Exemple #1
0
def test_resampling_matrix(dims, shape_cls, ncoarse=5, nfine=10):
    shape = shape_cls(dims)

    coarse_space = mp.space_for_shape(shape, ncoarse)
    fine_space = mp.space_for_shape(shape, nfine)

    coarse_nodes = mp.edge_clustered_nodes_for_space(coarse_space, shape)
    coarse_basis = mp.basis_for_space(coarse_space, shape)

    fine_nodes = mp.edge_clustered_nodes_for_space(fine_space, shape)
    fine_basis = mp.basis_for_space(fine_space, shape)

    my_eye = np.dot(
            mp.resampling_matrix(fine_basis.functions, coarse_nodes, fine_nodes),
            mp.resampling_matrix(coarse_basis.functions, fine_nodes, coarse_nodes))

    assert la.norm(my_eye - np.eye(len(my_eye))) < 3e-13

    my_eye_least_squares = np.dot(
            mp.resampling_matrix(coarse_basis.functions, coarse_nodes, fine_nodes,
                least_squares_ok=True),
            mp.resampling_matrix(coarse_basis.functions, fine_nodes, coarse_nodes),
            )

    assert la.norm(my_eye_least_squares - np.eye(len(my_eye_least_squares))) < 4e-13
def test_resampling_matrix(dims):
    ncoarse = 5
    nfine = 10

    coarse_nodes = mp.warp_and_blend_nodes(dims, ncoarse)
    fine_nodes = mp.warp_and_blend_nodes(dims, nfine)

    coarse_basis = mp.simplex_onb(dims, ncoarse)
    fine_basis = mp.simplex_onb(dims, nfine)

    my_eye = np.dot(
        mp.resampling_matrix(fine_basis, coarse_nodes, fine_nodes),
        mp.resampling_matrix(coarse_basis, fine_nodes, coarse_nodes))

    assert la.norm(my_eye - np.eye(len(my_eye))) < 1e-13

    my_eye_least_squares = np.dot(
        mp.resampling_matrix(coarse_basis,
                             coarse_nodes,
                             fine_nodes,
                             least_squares_ok=True),
        mp.resampling_matrix(coarse_basis, fine_nodes, coarse_nodes),
    )

    assert la.norm(my_eye_least_squares -
                   np.eye(len(my_eye_least_squares))) < 4e-13
Exemple #3
0
def _test_node_vertex_consistency_simplex(mesh, mgrp, tol):
    if mgrp.nelements == 0:
        return True

    resampling_mat = mp.resampling_matrix(
            mp.simplex_best_available_basis(mgrp.dim, mgrp.order),
            mgrp.vertex_unit_coordinates().T,
            mgrp.unit_nodes)

    # dim, nelments, nnvertices
    map_vertices = np.einsum(
            "ij,dej->dei", resampling_mat, mgrp.nodes)

    grp_vertices = mesh.vertices[:, mgrp.vertex_indices]

    per_element_vertex_errors = np.sqrt(np.sum(
            np.sum((map_vertices - grp_vertices)**2, axis=0),
            axis=-1))

    if tol is None:
        tol = 1e3 * np.finfo(per_element_vertex_errors.dtype).eps

    from meshmode.mesh.processing import find_bounding_box

    bbox_min, bbox_max = find_bounding_box(mesh)
    size = la.norm(bbox_max-bbox_min)

    assert np.max(per_element_vertex_errors) < tol*size, \
            np.max(per_element_vertex_errors)

    return True
Exemple #4
0
def _test_node_vertex_consistency_simplex(mesh, mgrp):
    if mgrp.nelements == 0:
        return True

    resampling_mat = mp.resampling_matrix(
            mp.simplex_best_available_basis(mgrp.dim, mgrp.order),
            mgrp.vertex_unit_coordinates().T,
            mgrp.unit_nodes)

    # dim, nelments, nnvertices
    map_vertices = np.einsum(
            "ij,dej->dei", resampling_mat, mgrp.nodes)

    grp_vertices = mesh.vertices[:, mgrp.vertex_indices]

    per_element_vertex_errors = np.sqrt(np.sum(
            np.sum((map_vertices - grp_vertices)**2, axis=0),
            axis=-1))

    tol = 1e3 * np.finfo(per_element_vertex_errors.dtype).eps

    from meshmode.mesh.processing import find_bounding_box

    bbox_min, bbox_max = find_bounding_box(mesh)
    size = la.norm(bbox_max-bbox_min)

    assert np.max(per_element_vertex_errors) < tol*size, \
            np.max(per_element_vertex_errors)

    return True
    def _resample_matrix(self, actx: ArrayContext, to_group_index,
                         ibatch_index):
        import modepy as mp
        ibatch = self.groups[to_group_index].batches[ibatch_index]
        from_grp = self.from_discr.groups[ibatch.from_group_index]

        nfrom_unit_nodes = from_grp.unit_nodes.shape[1]
        if np.array_equal(from_grp.unit_nodes, ibatch.result_unit_nodes):
            # Nodes are exactly identical? We can 'interpolate' even when there
            # isn't a basis.

            result = np.eye(nfrom_unit_nodes)

        else:
            if len(from_grp.basis()) != nfrom_unit_nodes:
                from meshmode.discretization import NoninterpolatoryElementGroupError
                raise NoninterpolatoryElementGroupError(
                    "%s does not support interpolation because it is not "
                    "unisolvent (its unit node count does not match its "
                    "number of basis functions). Using connections requires "
                    "the ability to interpolate." % type(from_grp).__name__)

            result = mp.resampling_matrix(from_grp.basis(),
                                          ibatch.result_unit_nodes,
                                          from_grp.unit_nodes)

        return actx.freeze(actx.from_numpy(result))
Exemple #6
0
    def get_midpoints(group, tesselation, elements):
        """
        Compute the midpoints of the vertices of the specified elements.

        :arg group: An instance of :class:`meshmode.mesh.SimplexElementGroup`
        :arg tesselation: With attributes `ref_vertices`, `children`
        :arg elements: A list of (group-relative) element numbers

        :return: A :class:`dict` mapping element numbers to midpoint
            coordinates, with each value in the map having shape
            ``(ambient_dim, nmidpoints)``. The ordering of the midpoints
            follows their ordering in the tesselation (see also
            :meth:`SimplexResampler.get_vertex_pair_to_midpoint_order`)
        """
        assert len(group.vertex_indices[0]) == group.dim + 1

        # Get midpoints, converted to unit coordinates.
        midpoints = -1 + np.array([vertex for vertex in
                tesselation.ref_vertices if 1 in vertex], dtype=float)

        resamp_mat = mp.resampling_matrix(
            mp.simplex_best_available_basis(group.dim, group.order),
            midpoints.T,
            group.unit_nodes)

        resamp_midpoints = np.einsum("mu,deu->edm",
                                     resamp_mat,
                                     group.nodes[:, elements])

        return dict(zip(elements, resamp_midpoints))
Exemple #7
0
    def _resample_matrix(self, to_group_index, ibatch_index):
        import modepy as mp
        ibatch = self.groups[to_group_index].batches[ibatch_index]
        from_grp = self.from_discr.groups[ibatch.from_group_index]

        nfrom_unit_nodes = from_grp.unit_nodes.shape[1]
        if np.array_equal(from_grp.unit_nodes, ibatch.result_unit_nodes):
            # Nodes are exactly identical? We can 'interpolate' even when there
            # isn't a basis.

            result = np.eye(nfrom_unit_nodes)

        else:
            if len(from_grp.basis()) != nfrom_unit_nodes:
                from meshmode.discretization import NoninterpolatoryElementGroupError
                raise NoninterpolatoryElementGroupError(
                        "%s does not support interpolation because it is not "
                        "unisolvent (its unit node count does not match its "
                        "number of basis functions). Using connections requires "
                        "the ability to interpolate." % type(from_grp).__name__)

            result = mp.resampling_matrix(
                    from_grp.basis(),
                    ibatch.result_unit_nodes, from_grp.unit_nodes)

        with cl.CommandQueue(self.cl_context) as queue:
            return cl.array.to_device(queue, result).with_queue(None)
Exemple #8
0
def plot_element_values(n, nodes, values, resample_n=None,
        node_tuples=None, show_nodes=False):
    dims = len(nodes)

    orig_nodes = nodes
    orig_values = values

    if resample_n is not None:
        import modepy as mp
        basis = mp.simplex_onb(dims, n)
        fine_nodes = mp.equidistant_nodes(dims, resample_n)

        values = np.dot(mp.resampling_matrix(basis, fine_nodes, nodes), values)
        nodes = fine_nodes
        n = resample_n

    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam

    if dims == 1:
        import matplotlib.pyplot as pt
        pt.plot(nodes[0], values)
        if show_nodes:
            pt.plot(orig_nodes[0], orig_values, "x")
        pt.show()
    elif dims == 2:
        import mayavi.mlab as mlab
        mlab.triangular_mesh(
                nodes[0], nodes[1], values, submesh(list(gnitstam(n, 2))))
        if show_nodes:
            mlab.points3d(orig_nodes[0], orig_nodes[1], orig_values,
                    scale_factor=0.05)
        mlab.show()
    else:
        raise RuntimeError("unsupported dimensionality %d" % dims)
Exemple #9
0
    def get_tesselated_nodes(group, tesselation, elements):
        """
        Compute the nodes of the child elements according to the tesselation.

        :arg group: An instance of :class:`meshmode.mesh.SimplexElementGroup`
        :arg tesselation: With attributes `ref_vertices`, `children`
        :arg elements: A list of (group-relative) element numbers

        :return: A :class:`dict` mapping element numbers to node
            coordinates, with each value in the map having shape
            ``(ambient_dim, nchildren, nunit_nodes)``.
            The ordering of the child nodes follows the ordering
            of ``tesselation.children.``
        """
        assert len(group.vertex_indices[0]) == group.dim + 1

        from meshmode.mesh.refinement.utils import map_unit_nodes_to_children

        # Get child unit node coordinates.
        child_unit_nodes = np.hstack(
            list(map_unit_nodes_to_children(group.unit_nodes, tesselation)))

        resamp_mat = mp.resampling_matrix(
            mp.simplex_best_available_basis(group.dim, group.order),
            child_unit_nodes, group.unit_nodes)

        resamp_unit_nodes = np.einsum("cu,deu->edc", resamp_mat,
                                      group.nodes[:, elements])

        ambient_dim = len(group.nodes)
        nunit_nodes = len(group.unit_nodes[0])

        return dict((elem, resamp_unit_nodes[ielem].reshape((ambient_dim, -1,
                                                             nunit_nodes)))
                    for ielem, elem in enumerate(elements))
Exemple #10
0
def from_mesh_interp_matrix(grp: NodalElementGroupBase) -> np.ndarray:
    meg = grp.mesh_el_group
    meg_space = type(grp.space)(meg.dim, meg.order)

    return mp.resampling_matrix(
        mp.basis_for_space(meg_space, grp.shape).functions, grp.unit_nodes,
        meg.unit_nodes)
Exemple #11
0
    def get_midpoints(self, group, tesselation, elements):
        """
        Compute the midpoints of the vertices of the specified elements.

        :arg group: An instance of :class:`meshmode.mesh.SimplexElementGroup`
        :arg tesselation: With attributes `ref_vertices`, `children`
        :arg elements: A list of (group-relative) element numbers

        :return: A :class:`dict` mapping element numbers to midpoint
            coordinates, with each value in the map having shape
            ``(ambient_dim, nmidpoints)``. The ordering of the midpoints
            follows their ordering in the tesselation (see also
            :meth:`SimplexResampler.get_vertex_pair_to_midpoint_order`)
        """
        assert len(group.vertex_indices[0]) == group.dim + 1

        # Get midpoints, converted to unit coordinates.
        midpoints = -1 + np.array([vertex for vertex in
                tesselation.ref_vertices if 1 in vertex], dtype=float)

        resamp_mat = mp.resampling_matrix(
            mp.simplex_best_available_basis(group.dim, group.order),
            midpoints.T,
            group.unit_nodes)

        resamp_midpoints = np.einsum("mu,deu->edm",
                                     resamp_mat,
                                     group.nodes[:, elements])

        return dict(zip(elements, resamp_midpoints))
Exemple #12
0
def _build_new_group_table(from_conn, to_conn):
    def find_batch(nodes, gtb):
        for igrp, batches in enumerate(gtb):
            for ibatch, batch in enumerate(batches):
                if np.allclose(nodes, batch.result_unit_nodes):
                    return (igrp, ibatch)
        return (-1, -1)

    nfrom_groups = len(from_conn.groups)
    nto_groups = len(to_conn.groups)

    # construct a table from (old groups) -> (new groups)
    # NOTE: we try to reduce the number of new groups and batches by matching
    # the `result_unit_nodes` and only adding a new batch if necessary
    grp_to_grp = {}
    batch_info = [[] for i in range(nfrom_groups * nto_groups)]
    for (igrp, ibatch), (fgrp, fbatch) in _iterbatches(from_conn.groups):
        for (jgrp, jbatch), (tgrp, tbatch) in _iterbatches(to_conn.groups):
            # compute result_unit_nodes
            ffgrp = from_conn.from_discr.groups[fbatch.from_group_index]
            from_matrix = mp.resampling_matrix(
                    ffgrp.basis(),
                    fbatch.result_unit_nodes,
                    ffgrp.unit_nodes)
            result_unit_nodes = from_matrix.dot(ffgrp.unit_nodes.T)

            tfgrp = to_conn.from_discr.groups[tbatch.from_group_index]
            to_matrix = mp.resampling_matrix(
                    tfgrp.basis(),
                    tbatch.result_unit_nodes,
                    tfgrp.unit_nodes)
            result_unit_nodes = to_matrix.dot(result_unit_nodes).T

            # find new (group, batch)
            (igrp_new, ibatch_new) = find_batch(result_unit_nodes, batch_info)
            if igrp_new < 0:
                igrp_new = nto_groups * igrp + jgrp
                ibatch_new = len(batch_info[igrp_new])

                batch_info[igrp_new].append(_ConnectionBatchData(
                    from_group_index=fbatch.from_group_index,
                    result_unit_nodes=result_unit_nodes,
                    to_element_face=tbatch.to_element_face))

            grp_to_grp[igrp, ibatch, jgrp, jbatch] = (igrp_new, ibatch_new)

    return grp_to_grp, batch_info
Exemple #13
0
def _build_new_group_table(from_conn, to_conn):
    def find_batch(nodes, gtb):
        for igrp, batches in enumerate(gtb):
            for ibatch, batch in enumerate(batches):
                if np.allclose(nodes, batch.result_unit_nodes):
                    return (igrp, ibatch)
        return (-1, -1)

    nfrom_groups = len(from_conn.groups)
    nto_groups = len(to_conn.groups)

    # construct a table from (old groups) -> (new groups)
    # NOTE: we try to reduce the number of new groups and batches by matching
    # the `result_unit_nodes` and only adding a new batch if necessary
    grp_to_grp = {}
    batch_info = [[] for i in range(nfrom_groups * nto_groups)]
    for (igrp, ibatch), (fgrp, fbatch) in _iterbatches(from_conn.groups):
        for (jgrp, jbatch), (tgrp, tbatch) in _iterbatches(to_conn.groups):
            # compute result_unit_nodes
            ffgrp = from_conn.from_discr.groups[fbatch.from_group_index]
            from_matrix = mp.resampling_matrix(
                    ffgrp.basis(),
                    fbatch.result_unit_nodes,
                    ffgrp.unit_nodes)
            result_unit_nodes = from_matrix.dot(ffgrp.unit_nodes.T)

            tfgrp = to_conn.from_discr.groups[tbatch.from_group_index]
            to_matrix = mp.resampling_matrix(
                    tfgrp.basis(),
                    tbatch.result_unit_nodes,
                    tfgrp.unit_nodes)
            result_unit_nodes = to_matrix.dot(result_unit_nodes).T

            # find new (group, batch)
            (igrp_new, ibatch_new) = find_batch(result_unit_nodes, batch_info)
            if igrp_new < 0:
                igrp_new = nto_groups * igrp + jgrp
                ibatch_new = len(batch_info[igrp_new])

                batch_info[igrp_new].append(_ConnectionBatchData(
                    from_group_index=fbatch.from_group_index,
                    result_unit_nodes=result_unit_nodes,
                    to_element_face=tbatch.to_element_face))

            grp_to_grp[igrp, ibatch, jgrp, jbatch] = (igrp_new, ibatch_new)

    return grp_to_grp, batch_info
Exemple #14
0
    def to_mesh_interp_matrix(grp) -> np.ndarray:
        import modepy as mp
        mat = mp.resampling_matrix(
                grp.basis_obj().functions,
                grp.mesh_el_group.unit_nodes,
                grp.unit_nodes)

        return actx.freeze(actx.from_numpy(mat))
Exemple #15
0
    def _resample_matrix(self, elgroup_index, ibatch_index):
        import modepy as mp
        ibatch = self.groups[elgroup_index].batches[ibatch_index]
        from_grp = self.from_discr.groups[elgroup_index]

        return mp.resampling_matrix(
                mp.simplex_onb(self.from_discr.dim, from_grp.order),
                ibatch.result_unit_nodes, from_grp.unit_nodes)
Exemple #16
0
    def from_mesh_interp_matrix(self):
        from modepy.modes import tensor_product_basis, jacobi
        from functools import partial
        meg = self.mesh_el_group

        basis = tensor_product_basis(
            self.dim,
            tuple(partial(jacobi, 0, 0, i) for i in range(meg.order + 1)))

        return mp.resampling_matrix(basis, self.unit_nodes, meg.unit_nodes)
Exemple #17
0
    def from_mesh_interp_matrix(self):
        from modepy.modes import tensor_product_basis, jacobi
        from functools import partial
        meg = self.mesh_el_group

        basis = tensor_product_basis(
                self.dim, tuple(
                    partial(jacobi, 0, 0, i)
                    for i in range(meg.order+1)))

        return mp.resampling_matrix(basis, self.unit_nodes, meg.unit_nodes)
Exemple #18
0
    def _resample_matrix(self, to_group_index, ibatch_index):
        import modepy as mp
        ibatch = self.groups[to_group_index].batches[ibatch_index]
        from_grp = self.from_discr.groups[ibatch.from_group_index]

        result = mp.resampling_matrix(
                from_grp.basis(),
                ibatch.result_unit_nodes, from_grp.unit_nodes)

        with cl.CommandQueue(self.cl_context) as queue:
            return cl.array.to_device(queue, result).with_queue(None)
Exemple #19
0
def test_resampling_matrix(dims):
    ncoarse = 5
    nfine = 10

    coarse_nodes = mp.warp_and_blend_nodes(dims, ncoarse)
    fine_nodes = mp.warp_and_blend_nodes(dims, nfine)

    coarse_basis = mp.simplex_onb(dims, ncoarse)
    fine_basis = mp.simplex_onb(dims, nfine)

    my_eye = np.dot(
            mp.resampling_matrix(fine_basis, coarse_nodes, fine_nodes),
            mp.resampling_matrix(coarse_basis, fine_nodes, coarse_nodes))

    assert la.norm(my_eye - np.eye(len(my_eye))) < 1e-13

    my_eye_least_squares = np.dot(
            mp.resampling_matrix(coarse_basis, coarse_nodes, fine_nodes,
                least_squares_ok=True),
            mp.resampling_matrix(coarse_basis, fine_nodes, coarse_nodes),
            )

    assert la.norm(my_eye_least_squares - np.eye(len(my_eye_least_squares))) < 4e-13
Exemple #20
0
def get_simplex_element_flip_matrix(order, unit_nodes, permutation=None):
    """
    Generate a resampling matrix that corresponds to a
    permutation of the barycentric coordinates being applied.
    The default permutation is to swap the
    first two barycentric coordinates.

    :param order: The order of the function space on the simplex,
                 (see second argument in
                  :fun:`modepy.simplex_best_available_basis`)
    :param unit_nodes: A np array of unit nodes with shape
                       *(dim, nunit_nodes)*
    :param permutation: Either *None*, or a tuple of shape
                        storing a permutation:
                        the *i*th barycentric coordinate gets mapped to
                        the *permutation[i]*th coordinate.

    :return: A numpy array of shape *(nunit_nodes, nunit_nodes)*
             which, when its transpose is right-applied
             to the matrix of nodes (shaped *(dim, nunit_nodes)*),
             corresponds to the permutation being applied
    """
    from modepy.tools import barycentric_to_unit, unit_to_barycentric

    bary_unit_nodes = unit_to_barycentric(unit_nodes)

    flipped_bary_unit_nodes = bary_unit_nodes.copy()
    if permutation is None:
        flipped_bary_unit_nodes[0, :] = bary_unit_nodes[1, :]
        flipped_bary_unit_nodes[1, :] = bary_unit_nodes[0, :]
    else:
        flipped_bary_unit_nodes[permutation, :] = bary_unit_nodes
    flipped_unit_nodes = barycentric_to_unit(flipped_bary_unit_nodes)

    dim = unit_nodes.shape[0]
    shape = mp.Simplex(dim)
    space = mp.PN(dim, order)
    basis = mp.basis_for_space(space, shape)
    flip_matrix = mp.resampling_matrix(basis.functions, flipped_unit_nodes,
                                       unit_nodes)

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

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

    return flip_matrix
Exemple #21
0
def test_resampling_matrix(dims, eltype):
    ncoarse = 5
    nfine = 10

    if eltype == "simplex":
        coarse_nodes = mp.warp_and_blend_nodes(dims, ncoarse)
        fine_nodes = mp.warp_and_blend_nodes(dims, nfine)

        coarse_basis = mp.simplex_onb(dims, ncoarse)
        fine_basis = mp.simplex_onb(dims, nfine)
    elif eltype == "tensor":
        coarse_nodes = mp.legendre_gauss_lobatto_tensor_product_nodes(
            dims, ncoarse)
        fine_nodes = mp.legendre_gauss_lobatto_tensor_product_nodes(
            dims, nfine)

        coarse_basis = mp.legendre_tensor_product_basis(dims, ncoarse)
        fine_basis = mp.legendre_tensor_product_basis(dims, nfine)
    else:
        raise ValueError(f"unknown element type: {eltype}")

    my_eye = np.dot(
        mp.resampling_matrix(fine_basis, coarse_nodes, fine_nodes),
        mp.resampling_matrix(coarse_basis, fine_nodes, coarse_nodes))

    assert la.norm(my_eye - np.eye(len(my_eye))) < 3e-13

    my_eye_least_squares = np.dot(
        mp.resampling_matrix(coarse_basis,
                             coarse_nodes,
                             fine_nodes,
                             least_squares_ok=True),
        mp.resampling_matrix(coarse_basis, fine_nodes, coarse_nodes),
    )

    assert la.norm(my_eye_least_squares -
                   np.eye(len(my_eye_least_squares))) < 4e-13
Exemple #22
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)
Exemple #23
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)
Exemple #24
0
def _(meg: _ModepyElementGroup, el_tess_info, elements):
    shape = meg._modepy_shape
    space = meg._modepy_space

    # get midpoints in reference coordinates
    midpoints = -1 + np.array(
        _get_ref_midpoints(shape, el_tess_info.ref_vertices))

    # resample midpoints to ambient coordinates
    resampling_mat = mp.resampling_matrix(
        mp.basis_for_space(space, shape).functions, midpoints.T,
        meg.unit_nodes)

    resampled_midpoints = np.einsum("mu,deu->edm", resampling_mat,
                                    meg.nodes[:, elements])

    return dict(zip(elements, resampled_midpoints))
Exemple #25
0
    def make_resampling_matrix(self, element_grp):
        """
            :arg element_grp: A
                :class:`meshmode.discretization.InterpolatoryElementGroupBase` whose
                basis functions span the same space as the FInAT element.
            :return: A matrix which resamples a function sampled at
                     the firedrake unit nodes to a function sampled at
                     *element_grp.unit_nodes()* (by matrix multiplication)
        """
        from meshmode.discretization import InterpolatoryElementGroupBase
        assert isinstance(element_grp, InterpolatoryElementGroupBase), \
            "element group must be an interpolatory element group so that" \
            " can redistribute onto its nodes"

        from modepy import resampling_matrix
        return resampling_matrix(element_grp.basis(),
                                 new_nodes=element_grp.unit_nodes,
                                 old_nodes=self.unit_nodes())
Exemple #26
0
    def _resample_matrix(self, to_group_index, ibatch_index):
        import modepy as mp
        ibatch = self.groups[to_group_index].batches[ibatch_index]
        from_grp = self.from_discr.groups[ibatch.from_group_index]

        if len(from_grp.basis()) != from_grp.unit_nodes.shape[1]:
            from meshmode.discretization import NoninterpolatoryElementGroupError
            raise NoninterpolatoryElementGroupError(
                "%s does not support interpolation because it is not "
                "unisolvent (its unit node count does not match its "
                "number of basis functions). Using connections requires "
                "the ability to interpolate." % type(from_grp).__name__)

        result = mp.resampling_matrix(from_grp.basis(),
                                      ibatch.result_unit_nodes,
                                      from_grp.unit_nodes)

        with cl.CommandQueue(self.cl_context) as queue:
            return cl.array.to_device(queue, result).with_queue(None)
Exemple #27
0
    def flip_matrix(self):
        """
            :return: The matrix which should be applied to the
                     *(dim, nunitnodes)*-shaped array of nodes corresponding to
                     an element in order to change orientation - <-> +.

                     The matrix will be *(dim, dim)* and orthogonal with
                     *np.float64* type entries.
        """
        if self._flip_matrix is None:
            # This is very similar to :mod:`meshmode` in processing.py
            # the function :function:`from_simplex_element_group`, but
            # we needed to use firedrake nodes

            from modepy.tools import barycentric_to_unit, unit_to_barycentric

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

            bary_unit_nodes = unit_to_barycentric(self.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)

            from modepy import resampling_matrix, simplex_best_available_basis

            flip_matrix = resampling_matrix(
                simplex_best_available_basis(self.dim(),
                                             self.analog().degree),
                flipped_unit_nodes, self.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

            self._flip_matrix = flip_matrix

        return self._flip_matrix
Exemple #28
0
def _test_node_vertex_consistency_resampling(mesh, mgrp, tol):
    if mesh.vertices is None:
        return True

    if mgrp.nelements == 0:
        return True

    if isinstance(mgrp, _ModepyElementGroup):
        basis = mp.basis_for_space(mgrp._modepy_space, mgrp._modepy_shape).functions
    else:
        raise TypeError(f"unsupported group type: {type(mgrp).__name__}")

    resampling_mat = mp.resampling_matrix(
            basis,
            mgrp.vertex_unit_coordinates().T,
            mgrp.unit_nodes)

    # dim, nelments, nnvertices
    map_vertices = np.einsum(
            "ij,dej->dei", resampling_mat, mgrp.nodes)

    grp_vertices = mesh.vertices[:, mgrp.vertex_indices]

    per_element_vertex_errors = np.sqrt(np.sum(
            np.sum((map_vertices - grp_vertices)**2, axis=0),
            axis=-1))

    if tol is None:
        tol = 1e3 * np.finfo(per_element_vertex_errors.dtype).eps

    from meshmode.mesh.processing import find_bounding_box

    bbox_min, bbox_max = find_bounding_box(mesh)
    size = la.norm(bbox_max-bbox_min)

    max_el_vertex_error = np.max(per_element_vertex_errors)
    assert max_el_vertex_error < tol*size, max_el_vertex_error

    return True
Exemple #29
0
    def get_tesselated_nodes(group, tesselation, elements):
        """
        Compute the nodes of the child elements according to the tesselation.

        :arg group: An instance of :class:`meshmode.mesh.SimplexElementGroup`
        :arg tesselation: With attributes `ref_vertices`, `children`
        :arg elements: A list of (group-relative) element numbers

        :return: A :class:`dict` mapping element numbers to node
            coordinates, with each value in the map having shape
            ``(ambient_dim, nchildren, nunit_nodes)``.
            The ordering of the child nodes follows the ordering
            of ``tesselation.children.``
        """
        assert len(group.vertex_indices[0]) == group.dim + 1

        from meshmode.mesh.refinement.utils import map_unit_nodes_to_children

        # Get child unit node coordinates.
        child_unit_nodes = np.hstack(list(
            map_unit_nodes_to_children(group.unit_nodes, tesselation)))

        resamp_mat = mp.resampling_matrix(
            mp.simplex_best_available_basis(group.dim, group.order),
            child_unit_nodes,
            group.unit_nodes)

        resamp_unit_nodes = np.einsum("cu,deu->edc",
                                      resamp_mat,
                                      group.nodes[:, elements])

        ambient_dim = len(group.nodes)
        nunit_nodes = len(group.unit_nodes[0])

        return dict((elem,
            resamp_unit_nodes[ielem].reshape(
                 (ambient_dim, -1, nunit_nodes)))
            for ielem, elem in enumerate(elements))
Exemple #30
0
def _(meg: _ModepyElementGroup, el_tess_info, elements):
    shape = meg._modepy_shape
    space = meg._modepy_space

    # get child unit node coordinates.
    from meshmode.mesh.refinement.utils import map_unit_nodes_to_children
    child_unit_nodes = np.hstack(
        list(map_unit_nodes_to_children(meg, meg.unit_nodes, el_tess_info)))

    # resample child nodes to ambient coordinates
    resampling_mat = mp.resampling_matrix(
        mp.basis_for_space(space, shape).functions, child_unit_nodes,
        meg.unit_nodes)

    resampled_unit_nodes = np.einsum("cu,deu->edc", resampling_mat,
                                     meg.nodes[:, elements])

    ambient_dim = len(meg.nodes)
    nunit_nodes = len(meg.unit_nodes[0])

    return {
        el: resampled_unit_nodes[iel].reshape((ambient_dim, -1, nunit_nodes))
        for iel, el in enumerate(elements)
    }
Exemple #31
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
Exemple #32
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
Exemple #33
0
 def from_mesh_interp_matrix(self):
     meg = self.mesh_el_group
     meg_space = mp.QN(meg.dim, meg.order)
     return mp.resampling_matrix(
         mp.basis_for_space(meg_space, self._shape).functions,
         self.unit_nodes, meg.unit_nodes)
Exemple #34
0
def interpolate_from_meshmode(queue,
                              dof_vec,
                              elements_to_sources_lookup,
                              order="tree"):
    """Interpolate a DoF vector from :mod:`meshmode`.

    :arg dof_vec: a DoF vector representing a field in :mod:`meshmode`
        of shape ``(..., nnodes)``.
    :arg elements_to_sources_lookup: a :class:`ElementsToSourcesLookup`.
    :arg order: order of the output potential, either "tree" or "user".

    .. note:: This function currently supports meshes with just one element
        group. Also, the element group must be simplex-based.

    .. note:: This function does some heavy-lifting computation in Python,
        which we intend to optimize in the future. In particular, we plan
        to shift the batched linear solves and basis evaluations to
        :mod:`loopy`.

    TODO: make linear solvers available as :mod:`loopy` callables.
    TODO: make :mod:`modepy` emit :mod:`loopy` callables for basis evaluation.
    """
    if not isinstance(dof_vec, cl.array.Array):
        raise TypeError("non-array passed to interpolator")

    assert len(elements_to_sources_lookup.discr.groups) == 1
    assert len(elements_to_sources_lookup.discr.mesh.groups) == 1
    degroup = elements_to_sources_lookup.discr.groups[0]
    megroup = elements_to_sources_lookup.discr.mesh.groups[0]

    if not degroup.is_affine:
        raise ValueError(
            "interpolation requires global-to-local map, "
            "which is only available for affinely mapped elements")

    mesh = elements_to_sources_lookup.discr.mesh
    dim = elements_to_sources_lookup.discr.dim
    template_simplex = mesh.groups[0].vertex_unit_coordinates().T

    # -------------------------------------------------------
    # Inversely map source points with a global-to-local map.
    #
    # 1. For each element, solve for the affine map.
    #
    # 2. Apply the map to corresponding source points.
    #
    # This step computes `unit_sources`, the list of inversely
    # mapped source points.

    sources_in_element_starts = \
        elements_to_sources_lookup.sources_in_element_starts.get(queue)
    sources_in_element_lists = \
        elements_to_sources_lookup.sources_in_element_lists.get(queue)
    tree = elements_to_sources_lookup.tree.get(queue)

    unit_sources_host = make_obj_array(
        [np.zeros_like(srccrd) for srccrd in tree.sources])

    for iel in range(degroup.nelements):
        vertex_ids = megroup.vertex_indices[iel]
        vertices = mesh.vertices[:, vertex_ids]
        afa, afb = compute_affine_transform(vertices, template_simplex)

        beg = sources_in_element_starts[iel]
        end = sources_in_element_starts[iel + 1]
        source_ids_in_el = sources_in_element_lists[beg:end]
        sources_in_el = np.vstack(
            [tree.sources[iaxis][source_ids_in_el] for iaxis in range(dim)])

        ivmapped_el_sources = afa @ sources_in_el + afb.reshape([dim, 1])
        for iaxis in range(dim):
            unit_sources_host[iaxis][source_ids_in_el] = \
                ivmapped_el_sources[iaxis, :]

    unit_sources = make_obj_array(
        [cl.array.to_device(queue, usc) for usc in unit_sources_host])

    # -----------------------------------------------------
    # Carry out evaluations in the local (template) frames.
    #
    # 1. Assemble a resampling matrix for each element, with
    #    the basis functions and the local source points.
    #
    # 2. For each element, perform matvec on the resampling
    #    matrix and the local DoF coefficients.
    #
    # This step assumes `unit_sources` computed on device, so
    # that the previous step can be swapped with a kernel without
    # interrupting the followed computation.

    mapped_sources = np.vstack([usc.get(queue) for usc in unit_sources])

    basis_funcs = degroup.basis()

    arr_ctx = PyOpenCLArrayContext(queue)
    dof_vec_view = unflatten(arr_ctx, elements_to_sources_lookup.discr,
                             dof_vec)[0]
    dof_vec_view = dof_vec_view.get()

    sym_shape = dof_vec.shape[:-1]
    source_vec = np.zeros(sym_shape + (tree.nsources, ))

    for iel in range(degroup.nelements):
        beg = sources_in_element_starts[iel]
        end = sources_in_element_starts[iel + 1]
        source_ids_in_el = sources_in_element_lists[beg:end]
        mapped_sources_in_el = mapped_sources[:, source_ids_in_el]
        local_dof_vec = dof_vec_view[..., iel, :]

        # resampling matrix built from Vandermonde matrices
        import modepy as mp
        rsplm = mp.resampling_matrix(basis=basis_funcs,
                                     new_nodes=mapped_sources_in_el,
                                     old_nodes=degroup.unit_nodes)

        if len(sym_shape) == 0:
            local_coeffs = local_dof_vec
            source_vec[source_ids_in_el] = rsplm @ local_coeffs
        else:
            from pytools import indices_in_shape
            for sym_id in indices_in_shape(sym_shape):
                source_vec[sym_id + (source_ids_in_el, )] = \
                    rsplm @ local_dof_vec[sym_id]

    source_vec = cl.array.to_device(queue, source_vec)

    if order == "tree":
        pass  # no need to do anything
    elif order == "user":
        source_vec = source_vec[tree.sorted_target_ids]  # into user order
    else:
        raise ValueError(f"order must be 'tree' or 'user' (got {order}).")

    return source_vec
Exemple #35
0
def make_boundary_restriction(queue, discr, group_factory):
    """
    :return: a tuple ``(bdry_mesh, bdry_discr, connection)``
    """

    logger.info("building boundary connection: start")

    # {{{ build face_map

    # maps (igrp, el_grp, face_id) to a frozenset of vertex IDs
    face_map = {}

    for igrp, mgrp in enumerate(discr.mesh.groups):
        grp_face_vertex_indices = mgrp.face_vertex_indices()

        for iel_grp in range(mgrp.nelements):
            for fid, loc_face_vertices in enumerate(grp_face_vertex_indices):
                face_vertices = frozenset(
                        mgrp.vertex_indices[iel_grp, fvi]
                        for fvi in loc_face_vertices
                        )
                face_map.setdefault(face_vertices, []).append(
                        (igrp, iel_grp, fid))

    del face_vertices

    # }}}

    boundary_faces = [
            face_ids[0]
            for face_vertices, face_ids in six.iteritems(face_map)
            if len(face_ids) == 1]

    from pytools import flatten
    bdry_vertex_vol_nrs = sorted(set(flatten(six.iterkeys(face_map))))

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

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

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

    for igrp, grp in enumerate(discr.groups):
        mgrp = grp.mesh_el_group
        group_boundary_faces = [
                (ibface_el, ibface_face)
                for ibface_group, ibface_el, ibface_face in boundary_faces
                if ibface_group == igrp]

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

        # {{{ Preallocate arrays for mesh group

        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)

        # }}}

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

        # batch by face_id

        batch_base = 0

        for face_id in range(len(grp_face_vertex_indices)):
            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)

            new_el_numbers = np.arange(
                    batch_base,
                    batch_base + len(batch_boundary_el_numbers_in_grp))

            # {{{ 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 = (
                    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,
                    )

            batch_base += len(batch_boundary_el_numbers_in_grp)

        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)

    connection = _build_boundary_connection(
            queue, discr, bdry_discr, connection_data)

    logger.info("building boundary connection: done")

    return bdry_mesh, bdry_discr, connection
def export_mesh_to_firedrake(mesh, group_nr=None, comm=None):
    r"""
    Create a firedrake mesh corresponding to one
    :class:`~meshmode.mesh.Mesh`'s
    :class:`~meshmode.mesh.SimplexElementGroup`.

    :param mesh: A :class:`~meshmode.mesh.Mesh` to convert with
        at least one :class:`~meshmode.mesh.SimplexElementGroup`.
        'mesh.is_conforming' must evaluate to *True*.
        'mesh' must have vertices supplied, i.e.
        'mesh.vertices' must not be *None*.
    :param group_nr: The group number to be converted into a firedrake
        mesh. The corresponding group must be of type
        :class:`~meshmode.mesh.SimplexElementGroup`. If *None* and
        *mesh* has only one group, that group is used. Otherwise,
        a *ValueError* is raised.
    :param comm: The communicator to build the dmplex mesh on

    :return: A tuple *(fdrake_mesh, fdrake_cell_ordering, perm2cell)*
        where

        * *fdrake_mesh* is a :mod:`firedrake`
          `firedrake.mesh.MeshGeometry` corresponding to
          *mesh*
        * *fdrake_cell_ordering* is a numpy array whose *i*\ th
          element in *mesh* (i.e. the *i*\ th element in
          *mesh.groups[group_nr].vertex_indices*) corresponds to the
          *fdrake_cell_ordering[i]*\ th :mod:`firedrake` cell
        * *perm2cell* is a dictionary, mapping tuples to
          1-D numpy arrays of meshmode element indices.
          Each meshmode element index
          appears in exactly one of these arrays. The corresponding
          tuple describes how firedrake reordered the local vertex
          indices on that cell. In particular, if *c*
          is in the list *perm2cell[p]* for a tuple *p*, then
          the *p[i]*\ th local vertex of the *fdrake_cell_ordering[c]*\ th
          firedrake cell corresponds to the *i*\ th local vertex
          of the *c*\ th meshmode element.

    .. warning::
        Currently, no custom boundary tags are exported along with the mesh.
        :mod:`firedrake` seems to only allow one marker on each facet, whereas
        :mod:`meshmode` allows many.
    """
    if not isinstance(mesh, Mesh):
        raise TypeError("'mesh' must of type meshmode.mesh.Mesh,"
                        " not '%s'." % type(mesh))
    if group_nr is None:
        if len(mesh.groups) != 1:
            raise ValueError("'group_nr' is *None* but 'mesh' has "
                             "more than one group.")
        group_nr = 0
    if not isinstance(group_nr, int):
        raise TypeError("Expecting 'group_nr' to be of type int, not "
                        f"'{type(group_nr)}'")
    if group_nr < 0 or group_nr >= len(mesh.groups):
        raise ValueError("'group_nr' is an invalid group index:"
                         f" '{group_nr}' fails to satisfy "
                         f"0 <= {group_nr} < {len(mesh.groups)}")
    if not isinstance(mesh.groups[group_nr], SimplexElementGroup):
        raise TypeError("Expecting 'mesh.groups[group_nr]' to be of type "
                        "meshmode.mesh.SimplexElementGroup, not "
                        f"'{type(mesh.groups[group_nr])}'")
    if mesh.vertices is None:
        raise ValueError("'mesh' has no vertices "
                         "('mesh.vertices' is *None*)")
    if not mesh.is_conforming:
        raise ValueError(f"'mesh.is_conforming' is {mesh.is_conforming} "
                         "instead of *True*. Converting non-conforming "
                         " meshes to Firedrake is not supported")

    # Get the vertices and vertex indices of the requested group
    with ProcessLogger(logger, "Obtaining vertices from selected group"):
        group = mesh.groups[group_nr]
        fd2mm_indices = np.unique(group.vertex_indices.flatten())
        coords = mesh.vertices[:, fd2mm_indices].T
        mm2fd_indices = dict(zip(fd2mm_indices, np.arange(np.size(fd2mm_indices))))
        cells = np.vectorize(mm2fd_indices.__getitem__)(group.vertex_indices)

    # Get a dmplex object and then a mesh topology
    with ProcessLogger(logger, "Building dmplex object and MeshTopology"):
        if comm is None:
            from pyop2.mpi import COMM_WORLD
            comm = COMM_WORLD
        # FIXME : not sure how to get around the private accesses
        import firedrake.mesh as fd_mesh
        plex = fd_mesh._from_cell_list(group.dim, cells, coords, comm)
        # Nb : One might be tempted to pass reorder=False and thereby save some
        #      hassle in exchange for forcing firedrake to have slightly
        #      less efficient caching. Unfortunately, that only prevents
        #      the cells from being reordered, and does not prevent the
        #      vertices from being (locally) reordered on each cell...
        #      the tl;dr is we don't actually save any hassle
        top = fd_mesh.Mesh(plex, dim=mesh.ambient_dim)  # mesh topology
        top.init()

    # Get new element ordering:
    with ProcessLogger(logger, "Determining permutations applied"
                       " to local vertex numbers"):
        c_start, c_end = top._topology_dm.getHeightStratum(0)
        cell_index_mm2fd = np.vectorize(top._cell_numbering.getOffset)(
            np.arange(c_start, c_end))
        v_start, v_end = top._topology_dm.getDepthStratum(0)

        # Firedrake goes crazy reordering local vertex numbers,
        # we've got to work to figure out what changes they made.
        #
        # *perm2cells* will map permutations of local vertex numbers to
        #              the list of all the meshmode cells
        #              which firedrake reordered according to that permutation
        #
        #              Permutations on *n* vertices are stored as a tuple
        #              containing all of the integers *0*, *1*, *2*, ..., *n-1*
        #              exactly once. A permutation *p*
        #              represents relabeling the *i*\ th local vertex
        #              of a meshmode element as the *p[i]*\ th local vertex
        #              in the corresponding firedrake cell.
        #
        #              *perm2cells[p]* is a list of all the meshmode element indices
        #              for which *p* represents the reordering applied by firedrake
        perm2cells = {}
        for mm_cell_id, dmp_ids in enumerate(top.cell_closure[cell_index_mm2fd]):
            # look at order of vertices in firedrake cell
            vert_dmp_ids = \
                dmp_ids[np.logical_and(v_start <= dmp_ids, dmp_ids < v_end)]
            fdrake_order = vert_dmp_ids - v_start
            # get original order
            mm_order = mesh.groups[group_nr].vertex_indices[mm_cell_id]
            # want permutation p so that mm_order[p] = fdrake_order
            # To do so, look at permutations acting by composition.
            #
            # mm_order \circ argsort(mm_order) =
            #     fdrake_order \circ argsort(fdrake_order)
            # so
            # mm_order \circ argsort(mm_order) \circ inv(argsort(fdrake_order))
            #  = fdrake_order
            #
            # argsort acts as an inverse, so the desired permutation is:
            perm = tuple(np.argsort(mm_order)[np.argsort(np.argsort(fdrake_order))])
            perm2cells.setdefault(perm, [])
            perm2cells[perm].append(mm_cell_id)

        # Make perm2cells map to numpy arrays instead of lists
        perm2cells = {perm: np.array(cells)
                      for perm, cells in perm2cells.items()}

    # Now make a coordinates function
    with ProcessLogger(logger, "Building firedrake function "
                       "space for mesh coordinates"):
        from firedrake import VectorFunctionSpace, Function
        coords_fspace = VectorFunctionSpace(top, "CG", group.order,
                                            dim=mesh.ambient_dim)
        coords = Function(coords_fspace)

    # get firedrake unit nodes and map onto meshmode reference element
    fd_ref_cell_to_mm = get_affine_reference_simplex_mapping(group.dim, True)
    fd_unit_nodes = get_finat_element_unit_nodes(coords_fspace.finat_element)
    fd_unit_nodes = fd_ref_cell_to_mm(fd_unit_nodes)

    basis = simplex_best_available_basis(group.dim, group.order)
    resampling_mat = resampling_matrix(basis,
                                       new_nodes=fd_unit_nodes,
                                       old_nodes=group.unit_nodes)
    # Store the meshmode data resampled to firedrake unit nodes
    # (but still in meshmode order)
    resampled_group_nodes = np.matmul(group.nodes, resampling_mat.T)

    # Now put the nodes in the right local order
    # nodes is shaped *(ambient dim, nelements, nunit nodes)*
    with ProcessLogger(logger, "Storing meshmode mesh coordinates"
                       " in firedrake nodal order"):
        from meshmode.mesh.processing import get_simplex_element_flip_matrix
        for perm, cells in perm2cells.items():
            flip_mat = get_simplex_element_flip_matrix(group.order,
                                                       fd_unit_nodes,
                                                       perm)
            flip_mat = np.rint(flip_mat).astype(np.int32)
            resampled_group_nodes[:, cells, :] = \
                np.matmul(resampled_group_nodes[:, cells, :], flip_mat.T)

    # store resampled data in right cell ordering
    with ProcessLogger(logger, "resampling mesh coordinates to "
                       "firedrake unit nodes"):
        reordered_cell_node_list = coords_fspace.cell_node_list[cell_index_mm2fd]
        coords.dat.data[reordered_cell_node_list, :] = \
            resampled_group_nodes.transpose((1, 2, 0))

    return fd_mesh.Mesh(coords), cell_index_mm2fd, perm2cells
Exemple #37
0
    def __init__(self, discr, fdrake_fspace, mm2fd_node_mapping, group_nr=None):
        """
        :param discr: A :class:`meshmode.discretization.Discretization`
        :param fdrake_fspace: A
            :class:`firedrake.functionspaceimpl.WithGeometry`.
            Must use ufl family ``"Discontinuous Lagrange"``.
        :param mm2fd_node_mapping: Used as attribute :attr:`mm2fd_node_mapping`.
            A 2-D numpy integer array with the same dtype as
            ``fdrake_fspace.cell_node_list.dtype``
        :param group_nr: The index of the group in *discr* which is
            being connected to *fdrake_fspace*. The group must be a
            :class:`~meshmode.discretization.InterpolatoryElementGroupBase`
            of the same topological dimension as *fdrake_fspace*.
            If *discr* has only one group, *group_nr=None* may be supplied.

        :raises TypeError: If any input arguments are of the wrong type,
            if the designated group is of the wrong type,
            or if *fdrake_fspace* is of the wrong family.
        :raises ValueError: If
            *mm2fd_node_mapping* is of the wrong shape
            or dtype, if *group_nr* is an invalid index, or
            if *group_nr* is *None* when *discr* has more than one group.
        """
        # {{{ Validate input
        if not isinstance(discr, Discretization):
            raise TypeError("'discr' must be of type "
                            "meshmode.discretization.Discretization, "
                            "not '%s'`." % type(discr))
        from firedrake.functionspaceimpl import WithGeometry
        if not isinstance(fdrake_fspace, WithGeometry):
            raise TypeError("'fdrake_fspace' must be of type "
                            "firedrake.functionspaceimpl.WithGeometry, "
                            "not '%s'." % type(fdrake_fspace))
        if not isinstance(mm2fd_node_mapping, np.ndarray):
            raise TypeError("'mm2fd_node_mapping' must be of type "
                            "numpy.ndarray, "
                            "not '%s'." % type(mm2fd_node_mapping))
        if not isinstance(group_nr, int) and group_nr is not None:
            raise TypeError("'group_nr' must be of type int or be "
                            "*None*, not of type '%s'." % type(group_nr))
        # Convert group_nr to an integer if *None*
        if group_nr is None:
            if len(discr.groups) != 1:
                raise ValueError("'group_nr' is *None* but 'discr' "
                                 "has '%s' != 1 groups." % len(discr.groups))
            group_nr = 0
        # store element_grp as variable for convenience
        element_grp = discr.groups[group_nr]

        if group_nr < 0 or group_nr >= len(discr.groups):
            raise ValueError("'group_nr' has value '%s', which an invalid "
                             "index into list 'discr.groups' of length '%s'."
                             % (group_nr, len(discr.groups)))
        if not isinstance(element_grp, InterpolatoryElementGroupBase):
            raise TypeError("'discr.groups[group_nr]' must be of type "
                            "InterpolatoryElementGroupBase"
                            ", not '%s'." % type(element_grp))
        if fdrake_fspace.ufl_element().family() != "Discontinuous Lagrange":
            raise TypeError("'fdrake_fspace.ufl_element().family()' must be"
                            "'Discontinuous Lagrange', not "
                            f"'{fdrake_fspace.ufl_element().family()}'")
        if mm2fd_node_mapping.shape != (element_grp.nelements,
                                        element_grp.nunit_dofs):
            raise ValueError("'mm2fd_node_mapping' must be of shape ",
                             "(%s,), not '%s'"
                             % ((discr.groups[group_nr].ndofs,),
                                mm2fd_node_mapping.shape))
        if mm2fd_node_mapping.dtype != fdrake_fspace.cell_node_list.dtype:
            raise ValueError("'mm2fd_node_mapping' must have dtype "
                             "%s, not '%s'" % (fdrake_fspace.cell_node_list.dtype,
                                             mm2fd_node_mapping.dtype))
        if np.size(np.unique(mm2fd_node_mapping)) != np.size(mm2fd_node_mapping):
            raise ValueError("'mm2fd_node_mapping' must have unique entries; "
                             "no two meshmode nodes may be associated to the "
                             "same Firedrake node")
        # }}}

        # Get meshmode unit nodes
        mm_unit_nodes = element_grp.unit_nodes
        # get firedrake unit nodes and map onto meshmode reference element
        tdim = fdrake_fspace.mesh().topological_dimension()
        fd_ref_cell_to_mm = get_affine_reference_simplex_mapping(tdim, True)
        fd_unit_nodes = get_finat_element_unit_nodes(fdrake_fspace.finat_element)
        fd_unit_nodes = fd_ref_cell_to_mm(fd_unit_nodes)

        # compute and store resampling matrices
        self._resampling_mat_fd2mm = resampling_matrix(element_grp.basis(),
                                                       new_nodes=mm_unit_nodes,
                                                       old_nodes=fd_unit_nodes)
        self._resampling_mat_mm2fd = resampling_matrix(element_grp.basis(),
                                                       new_nodes=fd_unit_nodes,
                                                       old_nodes=mm_unit_nodes)

        # Store input
        self.discr = discr
        self.group_nr = group_nr
        self.mm2fd_node_mapping = mm2fd_node_mapping
        self._mesh_geometry = fdrake_fspace.mesh()
        self._ufl_element = fdrake_fspace.ufl_element()
Exemple #38
0
 def resampling_matrix(self):
     meg = self.mesh_el_group
     return mp.resampling_matrix(
             mp.simplex_onb(self.dim, meg.order),
             self.unit_nodes, meg.unit_nodes)
Exemple #39
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)

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

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

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

    return connection
Exemple #40
0
def to_mesh_interp_matrix(grp: NodalElementGroupBase) -> np.ndarray:
    return mp.resampling_matrix(grp.basis_obj().functions,
                                grp.mesh_el_group.unit_nodes, grp.unit_nodes)
Exemple #41
0
 def from_mesh_interp_matrix(self):
     meg = self.mesh_el_group
     return mp.resampling_matrix(
         mp.simplex_best_available_basis(meg.dim, meg.order),
         self.unit_nodes, meg.unit_nodes)
 def from_mesh_interp_matrix(self):
     meg = self.mesh_el_group
     return mp.resampling_matrix(self.basis(), self.unit_nodes,
                                 meg.unit_nodes)
Exemple #43
0
 def from_mesh_interp_matrix(self):
     meg = self.mesh_el_group
     return mp.resampling_matrix(
             mp.simplex_best_available_basis(meg.dim, meg.order),
             self.unit_nodes,
             meg.unit_nodes)