def make_same_mesh_connection(actx, to_discr, from_discr):
    from meshmode.discretization.connection.direct import (
        InterpolationBatch, DiscretizationConnectionElementGroup,
        DirectDiscretizationConnection)

    if from_discr.mesh is not to_discr.mesh:
        raise ValueError("from_discr and to_discr must be based on "
                         "the same mesh")

    groups = []
    for igrp, (fgrp, tgrp) in enumerate(zip(from_discr.groups,
                                            to_discr.groups)):
        all_elements = actx.freeze(
            actx.from_numpy(np.arange(fgrp.nelements, dtype=np.intp)))
        ibatch = InterpolationBatch(from_group_index=igrp,
                                    from_element_indices=all_elements,
                                    to_element_indices=all_elements,
                                    result_unit_nodes=tgrp.unit_nodes,
                                    to_element_face=None)

        groups.append(DiscretizationConnectionElementGroup([ibatch]))

    return DirectDiscretizationConnection(from_discr,
                                          to_discr,
                                          groups,
                                          is_surjective=True)
Example #2
0
def make_same_mesh_connection(to_discr, from_discr):
    from meshmode.discretization.connection.direct import (
        InterpolationBatch, DiscretizationConnectionElementGroup,
        DirectDiscretizationConnection)

    if from_discr.mesh is not to_discr.mesh:
        raise ValueError("from_discr and to_discr must be based on "
                         "the same mesh")

    assert to_discr.cl_context == from_discr.cl_context

    with cl.CommandQueue(to_discr.cl_context) as queue:
        groups = []
        for igrp, (fgrp,
                   tgrp) in enumerate(zip(from_discr.groups, to_discr.groups)):
            all_elements = cl.array.arange(queue,
                                           fgrp.nelements,
                                           dtype=np.intp).with_queue(None)
            ibatch = InterpolationBatch(from_group_index=igrp,
                                        from_element_indices=all_elements,
                                        to_element_indices=all_elements,
                                        result_unit_nodes=tgrp.unit_nodes,
                                        to_element_face=None)

            groups.append(DiscretizationConnectionElementGroup([ibatch]))

    return DirectDiscretizationConnection(from_discr,
                                          to_discr,
                                          groups,
                                          is_surjective=True)
Example #3
0
def _build_batches(queue, from_bins, to_bins, batch):
    from meshmode.discretization.connection.direct import \
            InterpolationBatch

    def to_device(x):
        return cl.array.to_device(queue, np.asarray(x))

    for ibatch, (from_bin, to_bin) in enumerate(zip(from_bins, to_bins)):
        yield InterpolationBatch(
            from_group_index=batch[ibatch].from_group_index,
            from_element_indices=to_device(from_bin),
            to_element_indices=to_device(to_bin),
            result_unit_nodes=batch[ibatch].result_unit_nodes,
            to_element_face=batch[ibatch].to_element_face)
Example #4
0
def _build_boundary_connection(queue, vol_discr, bdry_discr, connection_data,
                               per_face_groups):
    from meshmode.discretization.connection.direct import (
        InterpolationBatch, DiscretizationConnectionElementGroup,
        DirectDiscretizationConnection)

    ibdry_grp = 0
    batches = []

    connection_groups = []
    for igrp, vol_grp in enumerate(vol_discr.groups):
        mgrp = vol_grp.mesh_el_group

        for face_id in range(mgrp.nfaces):
            bdry_grp = bdry_discr.groups[ibdry_grp]
            data = connection_data[igrp, face_id]

            bdry_unit_nodes_01 = (bdry_grp.unit_nodes + 1) * 0.5
            result_unit_nodes = (np.dot(data.A, bdry_unit_nodes_01).T +
                                 data.b).T

            batches.append(
                InterpolationBatch(
                    from_group_index=igrp,
                    from_element_indices=cl.array.to_device(
                        queue,
                        data.group_source_element_indices).with_queue(None),
                    to_element_indices=cl.array.to_device(
                        queue,
                        data.group_target_element_indices).with_queue(None),
                    result_unit_nodes=result_unit_nodes,
                    to_element_face=face_id))

            is_last_face = face_id + 1 == mgrp.nfaces

            if per_face_groups or is_last_face:
                connection_groups.append(
                    DiscretizationConnectionElementGroup(batches))
                batches = []

                ibdry_grp += 1

    assert ibdry_grp == len(bdry_discr.groups)

    return DirectDiscretizationConnection(vol_discr,
                                          bdry_discr,
                                          connection_groups,
                                          is_surjective=True)
Example #5
0
def _build_boundary_connection(actx, vol_discr, bdry_discr, connection_data,
        per_face_groups):
    from meshmode.discretization.connection.direct import (
            InterpolationBatch,
            DiscretizationConnectionElementGroup,
            DirectDiscretizationConnection)

    ibdry_grp = 0
    batches = []

    connection_groups = []
    for igrp, vol_grp in enumerate(vol_discr.groups):
        mgrp = vol_grp.mesh_el_group

        for face_id in range(mgrp.nfaces):
            bdry_grp = bdry_discr.groups[ibdry_grp]
            data = connection_data[igrp, face_id]

            result_unit_nodes = data.face.map_to_volume(bdry_grp.unit_nodes)

            batches.append(
                InterpolationBatch(
                    from_group_index=igrp,
                    from_element_indices=actx.freeze(actx.from_numpy(
                        data.group_source_element_indices)),
                    to_element_indices=actx.freeze(actx.from_numpy(
                        data.group_target_element_indices)),
                    result_unit_nodes=result_unit_nodes,
                    to_element_face=face_id
                    ))

            is_last_face = face_id + 1 == mgrp.nfaces

            if per_face_groups or is_last_face:
                connection_groups.append(
                    DiscretizationConnectionElementGroup(batches))
                batches = []

                ibdry_grp += 1

    assert ibdry_grp == len(bdry_discr.groups)

    return DirectDiscretizationConnection(
            vol_discr, bdry_discr, connection_groups,
            is_surjective=True)
Example #6
0
def make_face_to_all_faces_embedding(faces_connection,
                                     all_faces_discr,
                                     from_discr=None):
    """Return a
    :class:`meshmode.discretization.connection.DiscretizationConnection`
    connecting a discretization containing some faces of a discretization
    to one containing all faces.

    :arg faces_connection: must be the (connection) result of calling
        :func:`meshmode.discretization.connection.make_face_restriction`
        with
        :class:`meshmode.discretization.connection.FACE_RESTR_INTERIOR`
        or a boundary tag.
    :arg all_faces_discr: must be the (discretization) result of calling
        :func:`meshmode.discretization.connection.make_face_restriction`
        with
        :class:`meshmode.discretization.connection.FACE_RESTR_ALL`
        for the same volume discretization as the one from which
        *faces_discr* was obtained.
    :arg from_discr: Allows substituting in a different origin
        discretization for the returned connection. This discretization
        must use the same mesh as ``faces_connection.to_discr``.
    """

    vol_discr = faces_connection.from_discr
    faces_discr = faces_connection.to_discr

    if from_discr is None:
        from_discr = faces_discr

    assert from_discr.mesh is faces_discr.mesh

    per_face_groups = (len(vol_discr.groups) != len(faces_discr.groups))

    if len(faces_discr.groups) != len(all_faces_discr.groups):
        raise ValueError("faces_discr and all_faces_discr must have the "
                         "same number of groups")
    if len(faces_connection.groups) != len(all_faces_discr.groups):
        raise ValueError("faces_connection and all_faces_discr must have the "
                         "same number of groups")

    from meshmode.discretization.connection import (
        DirectDiscretizationConnection, DiscretizationConnectionElementGroup,
        InterpolationBatch)

    i_faces_grp = 0

    with cl.CommandQueue(vol_discr.cl_context) as queue:
        groups = []
        for ivol_grp, vol_grp in enumerate(vol_discr.groups):
            batches = []

            nfaces = vol_grp.mesh_el_group.nfaces
            for iface in range(nfaces):
                all_faces_grp = all_faces_discr.groups[i_faces_grp]

                if per_face_groups:
                    assert len(
                        faces_connection.groups[i_faces_grp].batches) == 1
                else:
                    assert (len(
                        faces_connection.groups[i_faces_grp].batches) == nfaces
                            )

                assert np.array_equal(
                    from_discr.groups[i_faces_grp].unit_nodes,
                    all_faces_grp.unit_nodes)

                # {{{ find src_batch

                src_batches = faces_connection.groups[i_faces_grp].batches
                if per_face_groups:
                    src_batch, = src_batches
                else:
                    src_batch = src_batches[iface]
                del src_batches

                # }}}

                if per_face_groups:
                    to_element_indices = src_batch.from_element_indices
                else:
                    assert all_faces_grp.nelements == nfaces * vol_grp.nelements

                    to_element_indices = (
                        vol_grp.nelements * iface +
                        src_batch.from_element_indices.with_queue(queue)
                    ).with_queue(None)

                batches.append(
                    InterpolationBatch(
                        from_group_index=i_faces_grp,
                        from_element_indices=src_batch.to_element_indices,
                        to_element_indices=to_element_indices,
                        result_unit_nodes=all_faces_grp.unit_nodes,
                        to_element_face=None))

                is_last_face = iface + 1 == nfaces
                if per_face_groups or is_last_face:
                    groups.append(
                        DiscretizationConnectionElementGroup(batches=batches))
                    batches = []

                    i_faces_grp += 1

    return DirectDiscretizationConnection(from_discr,
                                          all_faces_discr,
                                          groups,
                                          is_surjective=False)
Example #7
0
def _make_cross_face_batches(queue, tgt_bdry_discr, src_bdry_discr, i_tgt_grp,
                             i_src_grp, tgt_bdry_element_indices,
                             src_bdry_element_indices):

    # FIXME: This should view-then-transfer
    # (but PyOpenCL doesn't do non-contiguous transfers for now).
    tgt_bdry_nodes = (tgt_bdry_discr.groups[i_tgt_grp].view(
        tgt_bdry_discr.nodes().get(queue=queue))[:, tgt_bdry_element_indices])

    # FIXME: This should view-then-transfer
    # (but PyOpenCL doesn't do non-contiguous transfers for now).
    src_bdry_nodes = (src_bdry_discr.groups[i_src_grp].view(
        src_bdry_discr.nodes().get(queue=queue))[:, src_bdry_element_indices])

    tol = 1e4 * np.finfo(tgt_bdry_nodes.dtype).eps

    src_mesh_grp = src_bdry_discr.mesh.groups[i_src_grp]
    src_grp = src_bdry_discr.groups[i_src_grp]

    dim = src_grp.dim
    ambient_dim, nelements, ntgt_unit_nodes = tgt_bdry_nodes.shape
    assert tgt_bdry_nodes.shape == src_bdry_nodes.shape

    # {{{ invert face map (using Gauss-Newton)

    initial_guess = np.mean(src_mesh_grp.vertex_unit_coordinates(), axis=0)
    src_unit_nodes = np.empty((dim, nelements, ntgt_unit_nodes))
    src_unit_nodes[:] = initial_guess.reshape(-1, 1, 1)

    import modepy as mp
    vdm = mp.vandermonde(src_grp.basis(), src_grp.unit_nodes)
    inv_t_vdm = la.inv(vdm.T)
    nsrc_funcs = len(src_grp.basis())

    def apply_map(unit_nodes):
        # unit_nodes: (dim, nelements, ntgt_unit_nodes)

        # basis_at_unit_nodes
        basis_at_unit_nodes = np.empty(
            (nsrc_funcs, nelements, ntgt_unit_nodes))

        for i, f in enumerate(src_grp.basis()):
            basis_at_unit_nodes[i] = (f(unit_nodes.reshape(dim, -1)).reshape(
                nelements, ntgt_unit_nodes))

        intp_coeffs = np.einsum("fj,jet->fet", inv_t_vdm, basis_at_unit_nodes)

        # If we're interpolating 1, we had better get 1 back.
        one_deviation = np.abs(np.sum(intp_coeffs, axis=0) - 1)
        assert (one_deviation < tol).all(), np.max(one_deviation)

        return np.einsum("fet,aef->aet", intp_coeffs, src_bdry_nodes)

    def get_map_jacobian(unit_nodes):
        # unit_nodes: (dim, nelements, ntgt_unit_nodes)

        # basis_at_unit_nodes
        dbasis_at_unit_nodes = np.empty(
            (dim, nsrc_funcs, nelements, ntgt_unit_nodes))

        for i, df in enumerate(src_grp.grad_basis()):
            df_result = df(unit_nodes.reshape(dim, -1))

            for rst_axis, df_r in enumerate(df_result):
                dbasis_at_unit_nodes[rst_axis, i] = (df_r.reshape(
                    nelements, ntgt_unit_nodes))

        dintp_coeffs = np.einsum("fj,rjet->rfet", inv_t_vdm,
                                 dbasis_at_unit_nodes)

        return np.einsum("rfet,aef->raet", dintp_coeffs, src_bdry_nodes)

    # {{{ test map applier and jacobian

    if 0:
        u = src_unit_nodes
        f = apply_map(u)
        for h in [1e-1, 1e-2]:
            du = h * np.random.randn(*u.shape)

            f_2 = apply_map(u + du)

            jf = get_map_jacobian(u)

            f2_2 = f + np.einsum("raet,ret->aet", jf, du)

            print(h, la.norm((f_2 - f2_2).ravel()))

    # }}}

    # {{{ visualize initial guess

    if 0:
        import matplotlib.pyplot as pt
        guess = apply_map(src_unit_nodes)
        goals = tgt_bdry_nodes

        from meshmode.discretization.visualization import draw_curve
        pt.figure(0)
        draw_curve(tgt_bdry_discr)
        pt.figure(1)
        draw_curve(src_bdry_discr)
        pt.figure(2)

        pt.plot(guess[0].reshape(-1), guess[1].reshape(-1), "or")
        pt.plot(goals[0].reshape(-1), goals[1].reshape(-1), "og")
        pt.plot(src_bdry_nodes[0].reshape(-1), src_bdry_nodes[1].reshape(-1),
                "xb")
        pt.show()

    # }}}

    logger.info("make_opposite_face_connection: begin gauss-newton")

    niter = 0
    while True:
        resid = apply_map(src_unit_nodes) - tgt_bdry_nodes

        df = get_map_jacobian(src_unit_nodes)
        df_inv_resid = np.empty_like(src_unit_nodes)

        # For the 1D/2D accelerated versions, we'll use the normal
        # equations and Cramer's rule. If you're looking for high-end
        # numerics, look no further than meshmode.

        if dim == 1:
            # A is df.T
            ata = np.einsum("iket,jket->ijet", df, df)
            atb = np.einsum("iket,ket->iet", df, resid)

            df_inv_resid = atb / ata[0, 0]

        elif dim == 2:
            # A is df.T
            ata = np.einsum("iket,jket->ijet", df, df)
            atb = np.einsum("iket,ket->iet", df, resid)

            det = ata[0, 0] * ata[1, 1] - ata[0, 1] * ata[1, 0]

            df_inv_resid = np.empty_like(src_unit_nodes)
            df_inv_resid[0] = 1 / det * (ata[1, 1] * atb[0] -
                                         ata[1, 0] * atb[1])
            df_inv_resid[1] = 1 / det * (-ata[0, 1] * atb[0] +
                                         ata[0, 0] * atb[1])

        else:
            # The boundary of a 3D mesh is 2D, so that's the
            # highest-dimensional case we genuinely care about.
            #
            # This stinks, performance-wise, because it's not vectorized.
            # But we'll only hit it for boundaries of 4+D meshes, in which
            # case... good luck. :)
            for e in range(nelements):
                for t in range(ntgt_unit_nodes):
                    df_inv_resid[:, e, t], _, _, _ = \
                            la.lstsq(df[:, :, e, t].T, resid[:, e, t])

        src_unit_nodes = src_unit_nodes - df_inv_resid

        # {{{ visualize next guess

        if 0:
            import matplotlib.pyplot as pt
            guess = apply_map(src_unit_nodes)
            goals = tgt_bdry_nodes

            pt.plot(guess[0].reshape(-1), guess[1].reshape(-1), "rx")
            pt.plot(goals[0].reshape(-1), goals[1].reshape(-1), "go")
            pt.show()

        # }}}

        max_resid = np.max(np.abs(resid))
        logger.debug("gauss-newton residual: %g" % max_resid)

        if max_resid < tol:
            logger.info("make_opposite_face_connection: gauss-newton: done, "
                        "final residual: %g" % max_resid)
            break

        niter += 1
        if niter > 10:
            raise RuntimeError(
                "Gauss-Newton (for finding opposite-face reference "
                "coordinates) did not converge")

    # }}}

    # {{{ find groups of src_unit_nodes

    def to_dev(ary):
        return cl.array.to_device(queue, ary, array_queue=None)

    done_elements = np.zeros(nelements, dtype=np.bool)
    while True:
        todo_elements, = np.where(~done_elements)
        if not len(todo_elements):
            return

        template_unit_nodes = src_unit_nodes[:, todo_elements[0], :]

        unit_node_dist = np.max(np.max(
            np.abs(src_unit_nodes[:, todo_elements, :] -
                   template_unit_nodes.reshape(dim, 1, -1)),
            axis=2),
                                axis=0)

        close_els = todo_elements[unit_node_dist < tol]
        done_elements[close_els] = True

        unit_node_dist = np.max(np.max(
            np.abs(src_unit_nodes[:, todo_elements, :] -
                   template_unit_nodes.reshape(dim, 1, -1)),
            axis=2),
                                axis=0)

        from meshmode.discretization.connection.direct import InterpolationBatch
        yield InterpolationBatch(
            from_group_index=i_src_grp,
            from_element_indices=to_dev(src_bdry_element_indices[close_els]),
            to_element_indices=to_dev(tgt_bdry_element_indices[close_els]),
            result_unit_nodes=template_unit_nodes,
            to_element_face=None)
Example #8
0
def _build_interpolation_batches_for_group(queue, group_idx,
                                           coarse_discr_group,
                                           fine_discr_group, record):
    r"""
    To map between discretizations, we sort each of the fine mesh
    elements into an interpolation batch.  Which batch they go
    into is determined by where the refined unit nodes live
    relative to the coarse reference element.

    For instance, consider the following refinement::

         ______      ______
        |\     |    |\    e|
        | \    |    |d\    |
        |  \   |    |__\   |
        |   \  | => |\c|\  |
        |    \ |    |a\|b\ |
        |     \|    |  |  \|
         ‾‾‾‾‾‾      ‾‾‾‾‾‾

    Here, the discretization unit nodes for elements a,b,c,d,e
    will each have different positions relative to the reference
    element, so each element gets its own batch. On the other
    hand, for::

         ______      ______
        |\     |    |\ f|\e|
        | \    |    |d\ |g\|
        |  \   |    |__\|__|
        |   \  | => |\c|\  |
        |    \ |    |a\|b\h|
        |     \|    |  |  \|
         ‾‾‾‾‾‾      ‾‾‾‾‾‾

    the pairs {a,e}, {b,f}, {c,g}, {d,h} can share interpolation
    batches because their unit nodes are mapped from the same part
    of the reference element.
    """
    from meshmode.discretization.connection.direct import InterpolationBatch

    num_children = len(record.tesselation.children) \
                   if record.tesselation else 0
    from_bins = [[] for i in range(1 + num_children)]
    to_bins = [[] for i in range(1 + num_children)]
    for elt_idx, refinement_result in enumerate(record.element_mapping):
        if len(refinement_result) == 1:
            # Not refined -> interpolates to self
            from_bins[0].append(elt_idx)
            to_bins[0].append(refinement_result[0])
        else:
            assert len(refinement_result) == num_children
            # Refined -> interpolates to children
            for from_bin, to_bin, child_idx in zip(from_bins[1:], to_bins[1:],
                                                   refinement_result):
                from_bin.append(elt_idx)
                to_bin.append(child_idx)

    fine_unit_nodes = fine_discr_group.unit_nodes
    from meshmode.mesh.refinement.utils import map_unit_nodes_to_children
    mapped_unit_nodes = map_unit_nodes_to_children(fine_unit_nodes,
                                                   record.tesselation)

    from itertools import chain
    for from_bin, to_bin, unit_nodes in zip(
            from_bins, to_bins, chain([fine_unit_nodes], mapped_unit_nodes)):
        if not from_bin:
            continue
        yield InterpolationBatch(
            from_group_index=group_idx,
            from_element_indices=cl.array.to_device(queue,
                                                    np.asarray(from_bin)),
            to_element_indices=cl.array.to_device(queue, np.asarray(to_bin)),
            result_unit_nodes=unit_nodes,
            to_element_face=None)