예제 #1
0
파일: op.py 프로젝트: nchristensen/grudge
    def get_ref_stiffness_transpose_mat(out_grp, in_grp):
        if in_grp == out_grp:
            from meshmode.discretization.poly_element import \
                mass_matrix, diff_matrices

            mmat = mass_matrix(out_grp)
            return actx.freeze(
                actx.from_numpy(
                    np.asarray(
                        [dmat.T @ mmat.T for dmat in diff_matrices(out_grp)])))

        from modepy import vandermonde
        basis = out_grp.basis_obj()
        vand = vandermonde(basis.functions, out_grp.unit_nodes)
        grad_vand = vandermonde(basis.gradients, in_grp.unit_nodes)
        vand_inv_t = np.linalg.inv(vand).T

        if not isinstance(grad_vand, tuple):
            # NOTE: special case for 1d
            grad_vand = (grad_vand, )

        weights = in_grp.quadrature_rule().weights
        return actx.freeze(
            actx.from_numpy(
                np.einsum("c,bz,acz->abc", weights, vand_inv_t,
                          grad_vand).copy()  # contigify the array
            ))
예제 #2
0
    def matrix(out_element_group, in_element_group):
        if out_element_group == in_element_group:
            return in_element_group.mass_matrix()

        from modepy import vandermonde
        vand = vandermonde(out_element_group.basis(), out_element_group.unit_nodes)
        o_vand = vandermonde(out_element_group.basis(), in_element_group.unit_nodes)
        vand_inv_t = np.linalg.inv(vand).T

        weights = in_element_group.weights
        return np.einsum("j,ik,jk->ij", weights, vand_inv_t, o_vand)
예제 #3
0
    def matrix(out_element_group, in_element_group):
        if out_element_group == in_element_group:
            from meshmode.discretization.poly_element import mass_matrix
            return mass_matrix(in_element_group)

        from modepy import vandermonde
        basis = out_element_group.basis_obj()
        vand = vandermonde(basis.functions, out_element_group.unit_nodes)
        o_vand = vandermonde(basis.functions, in_element_group.unit_nodes)
        vand_inv_t = np.linalg.inv(vand).T

        weights = in_element_group.quadrature_rule().weights
        return np.einsum("j,ik,jk->ij", weights, vand_inv_t, o_vand)
예제 #4
0
    def matrices(out_elem_grp, in_elem_grp):
        if in_elem_grp == out_elem_grp:
            assert in_elem_grp.is_orthogonal_basis()
            mmat = in_elem_grp.mass_matrix()
            return [dmat.T.dot(mmat.T) for dmat in in_elem_grp.diff_matrices()]

        from modepy import vandermonde
        vand = vandermonde(out_elem_grp.basis(), out_elem_grp.unit_nodes)
        grad_vand = vandermonde(out_elem_grp.grad_basis(), in_elem_grp.unit_nodes)
        vand_inv_t = np.linalg.inv(vand).T

        weights = in_elem_grp.weights

        return np.einsum('c,bz,acz->abc', weights, vand_inv_t, grad_vand)
예제 #5
0
def is_affine_simplex_group(group, abs_tol=None):
    if abs_tol is None:
        abs_tol = 1.0e-13

    if not isinstance(group, SimplexElementGroup):
        raise TypeError("expected a 'SimplexElementGroup' not '%s'" %
                type(group).__name__)

    # get matrices
    basis = mp.simplex_best_available_basis(group.dim, group.order)
    grad_basis = mp.grad_simplex_best_available_basis(group.dim, group.order)

    vinv = la.inv(mp.vandermonde(basis, group.unit_nodes))
    diff = mp.differentiation_matrices(basis, grad_basis, group.unit_nodes)
    if not isinstance(diff, tuple):
        diff = (diff,)

    # construct all second derivative matrices (including cross terms)
    from itertools import product
    mats = []
    for n in product(range(group.dim), repeat=2):
        if n[0] > n[1]:
            continue
        mats.append(vinv.dot(diff[n[0]].dot(diff[n[1]])))

    # check just the first element for a non-affine local-to-global mapping
    ddx_coeffs = np.einsum("aij,bj->abi", mats, group.nodes[:, 0, :])
    norm_inf = np.max(np.abs(ddx_coeffs))
    if norm_inf > abs_tol:
        return False

    # check all elements for a non-affine local-to-global mapping
    ddx_coeffs = np.einsum("aij,bcj->abci", mats, group.nodes)
    norm_inf = np.max(np.abs(ddx_coeffs))
    return norm_inf < abs_tol
예제 #6
0
    def matrices(out_elem_grp, in_elem_grp):
        if in_elem_grp == out_elem_grp:
            assert in_elem_grp.is_orthogonal_basis()
            mmat = in_elem_grp.mass_matrix()
            return [dmat.T.dot(mmat.T) for dmat in in_elem_grp.diff_matrices()]

        from modepy import vandermonde
        vand = vandermonde(out_elem_grp.basis(), out_elem_grp.unit_nodes)
        grad_vand = vandermonde(out_elem_grp.grad_basis(), in_elem_grp.unit_nodes)
        vand_inv_t = np.linalg.inv(vand).T

        if not isinstance(grad_vand, tuple):
            # NOTE: special case for 1d
            grad_vand = (grad_vand,)

        weights = in_elem_grp.weights
        return np.einsum("c,bz,acz->abc", weights, vand_inv_t, grad_vand)
예제 #7
0
def test_modal_coefficients_by_projection(actx_factory, quad_group_factory):
    group_cls = SimplexElementGroup
    modal_group_factory = ModalSimplexGroupFactory
    actx = actx_factory()
    order = 10
    m_order = 5

    # Make a regular rectangle mesh
    mesh = mgen.generate_regular_rect_mesh(a=(0, 0),
                                           b=(5, 3),
                                           npoints_per_axis=(10, 6),
                                           order=order,
                                           group_cls=group_cls)

    # Make discretizations
    nodal_disc = Discretization(actx, mesh, quad_group_factory(order))
    modal_disc = Discretization(actx, mesh, modal_group_factory(m_order))

    # Make connections one using quadrature projection
    nodal_to_modal_conn_quad = NodalToModalDiscretizationConnection(
        nodal_disc, modal_disc, allow_approximate_quad=True)

    def f(x):
        return 2 * actx.np.sin(5 * x)

    x_nodal = thaw(nodal_disc.nodes()[0], actx)
    nodal_f = f(x_nodal)

    # Compute modal coefficients we expect to get
    import modepy as mp

    grp, = nodal_disc.groups
    shape = mp.Simplex(grp.dim)
    space = mp.space_for_shape(shape, order=m_order)
    basis = mp.orthonormal_basis_for_space(space, shape)
    quad = grp.quadrature_rule()

    nodal_f_data = actx.to_numpy(nodal_f[0])
    vdm = mp.vandermonde(basis.functions, quad.nodes)
    w_diag = np.diag(quad.weights)

    modal_data = []
    for _, nodal_data in enumerate(nodal_f_data):
        # Compute modal data in each element: V.T * W * nodal_data
        elem_modal_f = np.dot(vdm.T, np.dot(w_diag, nodal_data))
        modal_data.append(elem_modal_f)

    modal_data = actx.from_numpy(np.asarray(modal_data))
    modal_f_expected = DOFArray(actx, data=(modal_data, ))

    # Map nodal coefficients using the quadrature-based projection
    modal_f_computed = nodal_to_modal_conn_quad(nodal_f)

    err = flat_norm(modal_f_expected - modal_f_computed)

    assert err <= 1e-13
예제 #8
0
파일: op.py 프로젝트: nchristensen/grudge
    def get_ref_mass_mat(out_grp, in_grp):
        if out_grp == in_grp:
            from meshmode.discretization.poly_element import mass_matrix

            return actx.freeze(
                actx.from_numpy(np.asarray(mass_matrix(out_grp), order="C")))

        from modepy import vandermonde
        basis = out_grp.basis_obj()
        vand = vandermonde(basis.functions, out_grp.unit_nodes)
        o_vand = vandermonde(basis.functions, in_grp.unit_nodes)
        vand_inv_t = np.linalg.inv(vand).T

        weights = in_grp.quadrature_rule().weights
        return actx.freeze(
            actx.from_numpy(
                np.asarray(np.einsum("j,ik,jk->ij", weights, vand_inv_t,
                                     o_vand),
                           order="C")))
예제 #9
0
    def estimate_resid(inner_n):
        nodes = mp.warp_and_blend_nodes(dims, inner_n)
        basis = mp.simplex_onb(dims, inner_n)
        vdm = mp.vandermonde(basis, nodes)

        f = test_func(nodes[0])
        coeffs = la.solve(vdm, f)

        from modepy.modal_decay import estimate_relative_expansion_residual
        return estimate_relative_expansion_residual(coeffs.reshape(1, -1),
                                                    dims, inner_n)
예제 #10
0
    def matrices(out_elem_grp, in_elem_grp):
        if in_elem_grp == out_elem_grp:
            assert in_elem_grp.is_orthonormal_basis()
            from meshmode.discretization.poly_element import (mass_matrix,
                                                              diff_matrices)
            mmat = mass_matrix(in_elem_grp)
            return [dmat.T.dot(mmat.T) for dmat in diff_matrices(in_elem_grp)]

        from modepy import vandermonde
        basis = out_elem_grp.basis_obj()
        vand = vandermonde(basis.functions, out_elem_grp.unit_nodes)
        grad_vand = vandermonde(basis.gradients, in_elem_grp.unit_nodes)
        vand_inv_t = np.linalg.inv(vand).T

        if not isinstance(grad_vand, tuple):
            # NOTE: special case for 1d
            grad_vand = (grad_vand, )

        weights = in_elem_grp.quadrature_rule().weights
        return np.einsum("c,bz,acz->abc", weights, vand_inv_t, grad_vand)
예제 #11
0
    def estimate_resid(inner_n):
        nodes = mp.warp_and_blend_nodes(dims, inner_n)
        basis = mp.simplex_onb(dims, inner_n)
        vdm = mp.vandermonde(basis, nodes)

        f = test_func(nodes[0])
        coeffs = la.solve(vdm, f)

        from modepy.modal_decay import estimate_relative_expansion_residual
        return estimate_relative_expansion_residual(
                coeffs.reshape(1, -1), dims, inner_n)
예제 #12
0
파일: test_tools.py 프로젝트: yjobic/modepy
    def estimate_resid(inner_n):
        nodes = mp.warp_and_blend_nodes(dims, inner_n)
        basis = mp.orthonormal_basis_for_space(
                mp.PN(dims, inner_n), mp.Simplex(dims))
        vdm = mp.vandermonde(basis.functions, nodes)

        f = test_func(nodes[0])
        coeffs = la.solve(vdm, f)

        from modepy.modal_decay import estimate_relative_expansion_residual
        return estimate_relative_expansion_residual(
                coeffs.reshape(1, -1), dims, inner_n)
예제 #13
0
def test_modal_decay(case_name, test_func, dims, n, expected_expn):
    nodes = mp.warp_and_blend_nodes(dims, n)
    basis = mp.simplex_onb(dims, n)
    vdm = mp.vandermonde(basis, nodes)

    f = test_func(nodes[0])
    coeffs = la.solve(vdm, f)

    if 0:
        from modepy.tools import plot_element_values
        plot_element_values(n, nodes, f, resample_n=70, show_nodes=True)

    from modepy.modal_decay import fit_modal_decay
    expn, _ = fit_modal_decay(coeffs.reshape(1, -1), dims, n)
    expn = expn[0]

    print(f"{case_name}: computed: {expn:g}, expected: {expected_expn:g}")
    assert abs(expn - expected_expn) < 0.1
예제 #14
0
def test_modal_coeffs_by_projection(dim):
    shape = mp.Simplex(dim)
    space = mp.space_for_shape(shape, order=5)
    basis = mp.orthonormal_basis_for_space(space, shape)

    quad = mp.XiaoGimbutasSimplexQuadrature(10, dim)
    assert quad.exact_to >= 2 * space.order

    modal_coeffs = np.random.randn(space.space_dim)
    vdm = mp.vandermonde(basis.functions, quad.nodes)

    evaluated = vdm @ modal_coeffs

    modal_coeffs_2 = vdm.T @ (evaluated * quad.weights)

    diff = modal_coeffs - modal_coeffs_2

    assert la.norm(diff, 2) < 3e-13
예제 #15
0
def test_modal_decay(case_name, test_func, dims, n, expected_expn):
    nodes = mp.warp_and_blend_nodes(dims, n)
    basis = mp.simplex_onb(dims, n)
    vdm = mp.vandermonde(basis, nodes)

    f = test_func(nodes[0])
    coeffs = la.solve(vdm, f)

    if 0:
        from modepy.tools import plot_element_values
        plot_element_values(n, nodes, f, resample_n=70,
                show_nodes=True)

    from modepy.modal_decay import fit_modal_decay
    expn, _ = fit_modal_decay(coeffs.reshape(1, -1), dims, n)
    expn = expn[0]

    print(("%s: computed: %g, expected: %g" % (case_name, expn, expected_expn)))
    assert abs(expn-expected_expn) < 0.1
예제 #16
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)
예제 #17
0
 def vandermonde_matrix(grp):
     from modepy import vandermonde
     vdm = vandermonde(grp.basis_obj().functions, grp.unit_nodes)
     return actx.from_numpy(vdm)
예제 #18
0
def _make_cross_face_batches(
        queue, vol_discr, bdry_discr,
        i_tgt_grp, i_src_grp,
        i_face_tgt,
        adj_grp,
        vbc_tgt_grp_face_batch, src_grp_el_lookup):

    # {{{ index wrangling

    # Assert that the adjacency group and the restriction
    # interpolation batch and the adjacency group have the same
    # element ordering.

    adj_grp_tgt_flags = adj_grp.element_faces == i_face_tgt

    assert (
            np.array_equal(
                adj_grp.elements[adj_grp_tgt_flags],
                vbc_tgt_grp_face_batch.from_element_indices
                .get(queue=queue)))

    # find to_element_indices

    to_bdry_element_indices = (
            vbc_tgt_grp_face_batch.to_element_indices
            .get(queue=queue))

    # find from_element_indices

    from_vol_element_indices = adj_grp.neighbors[adj_grp_tgt_flags]
    from_element_faces = adj_grp.neighbor_faces[adj_grp_tgt_flags]

    from_bdry_element_indices = src_grp_el_lookup[
            from_vol_element_indices, from_element_faces]

    # }}}

    # {{{ visualization (for debugging)

    if 0:
        print("TVE", adj_grp.elements[adj_grp_tgt_flags])
        print("TBE", to_bdry_element_indices)
        print("FVE", from_vol_element_indices)
        from meshmode.mesh.visualization import draw_2d_mesh
        import matplotlib.pyplot as pt
        draw_2d_mesh(vol_discr.mesh, draw_element_numbers=True,
                set_bounding_box=True,
                draw_vertex_numbers=False,
                draw_face_numbers=True,
                fill=None)
        pt.figure()

        draw_2d_mesh(bdry_discr.mesh, draw_element_numbers=True,
                set_bounding_box=True,
                draw_vertex_numbers=False,
                draw_face_numbers=True,
                fill=None)

        pt.show()
    # }}}

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

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

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

    from_mesh_grp = bdry_discr.mesh.groups[i_src_grp]
    from_grp = bdry_discr.groups[i_src_grp]

    dim = from_grp.dim
    ambient_dim, nelements, nto_unit_nodes = to_bdry_nodes.shape

    initial_guess = np.mean(from_mesh_grp.vertex_unit_coordinates(), axis=0)
    from_unit_nodes = np.empty((dim, nelements, nto_unit_nodes))
    from_unit_nodes[:] = initial_guess.reshape(-1, 1, 1)

    import modepy as mp
    from_vdm = mp.vandermonde(from_grp.basis(), from_grp.unit_nodes)
    from_inv_t_vdm = la.inv(from_vdm.T)
    from_nfuncs = len(from_grp.basis())

    # (ambient_dim, nelements, nfrom_unit_nodes)
    from_bdry_nodes = (
            # FIXME: This should view-then-transfer (but PyOpenCL doesn't do
            # non-contiguous transfers for now).
            bdry_discr.groups[i_src_grp].view(
                bdry_discr.nodes().get(queue=queue))
            [:, from_bdry_element_indices])

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

        # basis_at_unit_nodes
        basis_at_unit_nodes = np.empty((from_nfuncs, nelements, nto_unit_nodes))

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

        intp_coeffs = np.einsum("fj,jet->fet", from_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, from_bdry_nodes)

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

        # basis_at_unit_nodes
        dbasis_at_unit_nodes = np.empty(
                (dim, from_nfuncs, nelements, nto_unit_nodes))

        for i, df in enumerate(from_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, nto_unit_nodes))

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

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

    # {{{ test map applier and jacobian

    if 0:
        u = from_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(from_unit_nodes)
        goals = to_bdry_nodes

        from meshmode.discretization.visualization import draw_curve
        draw_curve(bdry_discr)

        pt.plot(guess[0].reshape(-1), guess[1].reshape(-1), "or")
        pt.plot(goals[0].reshape(-1), goals[1].reshape(-1), "og")
        pt.plot(from_bdry_nodes[0].reshape(-1), from_bdry_nodes[1].reshape(-1), "o",
                color="purple")
        pt.show()

    # }}}

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

    niter = 0
    while True:
        resid = apply_map(from_unit_nodes) - to_bdry_nodes

        df = get_map_jacobian(from_unit_nodes)
        df_inv_resid = np.empty_like(from_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(from_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(nto_unit_nodes):
                    df_inv_resid[:, e, t], _, _, _ = \
                            la.lstsq(df[:, :, e, t].T, resid[:, e, t])

        from_unit_nodes = from_unit_nodes - df_inv_resid

        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 from_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 = from_unit_nodes[:, todo_elements[0], :]

        unit_node_dist = np.max(np.max(np.abs(
                from_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(
                from_unit_nodes[:, todo_elements, :]
                -
                template_unit_nodes.reshape(dim, 1, -1)),
                axis=2), axis=0)

        from meshmode.discretization.connection import InterpolationBatch
        yield InterpolationBatch(
                from_group_index=i_src_grp,
                from_element_indices=to_dev(from_bdry_element_indices[close_els]),
                to_element_indices=to_dev(to_bdry_element_indices[close_els]),
                result_unit_nodes=template_unit_nodes,
                to_element_face=None)
예제 #19
0
n = 17 # use this total degree
dimensions = 2

# Get a basis of orthonormal functions, and their derivatives.

basis = mp.simplex_onb(dimensions, n)
grad_basis = mp.grad_simplex_onb(dimensions, n)

nodes = mp.warp_and_blend_nodes(dimensions, n)
x, y = nodes

# We want to compute the x derivative of this function:

f = np.sin(5*x+y)
df_dx = 5*np.cos(5*x+y)

# The (generalized) Vandermonde matrix transforms coefficients into
# nodal values. So we can find basis coefficients by applying its
# inverse:

f_coefficients = np.linalg.solve(
        mp.vandermonde(basis, nodes), f)

# Now linearly combine the (x-)derivatives of the basis using
# f_coefficients to compute the numerical derivatives.

df_dx_num = np.dot(
        mp.vandermonde(grad_basis, nodes)[0], f_coefficients)

assert np.linalg.norm(df_dx - df_dx_num) < 1e-5
예제 #20
0
파일: derivative.py 프로젝트: userjjb/DbX
import modepy as mp

n = 17  # use this total degree
dimensions = 2

# Get a basis of orthonormal functions, and their derivatives.

basis = mp.simplex_onb(dimensions, n)
grad_basis = mp.grad_simplex_onb(dimensions, n)

nodes = mp.warp_and_blend_nodes(dimensions, n)
x, y = nodes

# We want to compute the x derivative of this function:

f = np.sin(5 * x + y)
df_dx = 5 * np.cos(5 * x + y)

# The (generalized) Vandermonde matrix transforms coefficients into
# nodal values. So we can find basis coefficients by applying its
# inverse:

f_coefficients = np.linalg.solve(mp.vandermonde(basis, nodes), f)

# Now linearly combine the (x-)derivatives of the basis using
# f_coefficients to compute the numerical derivatives.

df_dx_num = np.dot(mp.vandermonde(grad_basis, nodes)[0], f_coefficients)

assert np.linalg.norm(df_dx - df_dx_num) < 1e-5
예제 #21
0
    def __call__(self, ary):
        if not isinstance(ary, DOFArray):
            raise TypeError("non-array passed to discretization connection")

        if ary.shape != (len(self.from_discr.groups), ):
            raise ValueError("invalid shape of incoming resampling data")

        actx = ary.array_context

        @memoize_in(actx, (L2ProjectionInverseDiscretizationConnection,
                           "conn_projection_knl"))
        def kproj():
            return make_loopy_program(
                [
                    "{[iel]: 0 <= iel < nelements}",
                    "{[idof_quad]: 0 <= idof_quad < n_from_nodes}"
                ],
                """
                for iel
                    <> element_dot = sum(idof_quad,
                                ary[from_element_indices[iel], idof_quad]
                                * basis[idof_quad] * weights[idof_quad])

                    result[to_element_indices[iel], ibasis] = \
                            result[to_element_indices[iel], ibasis] + element_dot
                end
                """, [
                    lp.GlobalArg("ary",
                                 None,
                                 shape=("n_from_elements", "n_from_nodes")),
                    lp.GlobalArg(
                        "result", None, shape=("n_to_elements", "n_to_nodes")),
                    lp.GlobalArg("basis", None, shape="n_from_nodes"),
                    lp.GlobalArg("weights", None, shape="n_from_nodes"),
                    lp.ValueArg("n_from_elements", np.int32),
                    lp.ValueArg("n_to_elements", np.int32),
                    lp.ValueArg("n_to_nodes", np.int32),
                    lp.ValueArg("ibasis", np.int32), "..."
                ],
                name="conn_projection_knl")

        @memoize_in(actx, (L2ProjectionInverseDiscretizationConnection,
                           "conn_evaluation_knl"))
        def keval():
            return make_loopy_program(
                [
                    "{[iel]: 0 <= iel < nelements}",
                    "{[idof]: 0 <= idof < n_to_nodes}",
                    "{[ibasis]: 0 <= ibasis < n_to_nodes}"
                ],
                """
                    result[iel, idof] = result[iel, idof] + \
                        sum(ibasis, vdm[idof, ibasis] * coefficients[iel, ibasis])
                """, [
                    lp.GlobalArg("coefficients",
                                 None,
                                 shape=("nelements", "n_to_nodes")), "..."
                ],
                name="conn_evaluate_knl")

        # compute weights on each refinement of the reference element
        weights = self._batch_weights(actx)

        # perform dot product (on reference element) to get basis coefficients
        c = self.to_discr.zeros(actx, dtype=ary.entry_dtype)

        for igrp, (tgrp, cgrp) in enumerate(
                zip(self.to_discr.groups, self.conn.groups)):
            for ibatch, batch in enumerate(cgrp.batches):
                sgrp = self.from_discr.groups[batch.from_group_index]

                for ibasis, basis_fn in enumerate(sgrp.basis()):
                    basis = actx.from_numpy(
                        basis_fn(batch.result_unit_nodes).flatten())

                    # NOTE: batch.*_element_indices are reversed here because
                    # they are from the original forward connection, but
                    # we are going in reverse here. a bit confusing, but
                    # saves on recreating the connection groups and batches.
                    actx.call_loopy(
                        kproj(),
                        ibasis=ibasis,
                        ary=ary[sgrp.index],
                        basis=basis,
                        weights=weights[igrp, ibatch],
                        result=c[igrp],
                        from_element_indices=batch.to_element_indices,
                        to_element_indices=batch.from_element_indices)

        # evaluate at unit_nodes to get the vector on to_discr
        result = self.to_discr.zeros(actx, dtype=ary.entry_dtype)
        for igrp, grp in enumerate(self.to_discr.groups):
            from modepy import vandermonde
            vdm = actx.from_numpy(vandermonde(grp.basis(), grp.unit_nodes))
            actx.call_loopy(keval(),
                            result=result[grp.index],
                            vdm=vdm,
                            coefficients=c[grp.index])

        return result