Ejemplo n.º 1
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
Ejemplo n.º 2
0
def test_nodal_face_mass_matrix(dim, order=3):
    from modepy.tools import unit_vertices
    all_verts = unit_vertices(dim).T

    basis = mp.simplex_onb(dim, order)

    np.set_printoptions(linewidth=200)

    from modepy.matrices import nodal_face_mass_matrix
    volume_nodes = mp.warp_and_blend_nodes(dim, order)
    face_nodes = mp.warp_and_blend_nodes(dim - 1, order)

    for iface in range(dim + 1):
        verts = np.hstack([all_verts[:, :iface], all_verts[:, iface + 1:]])

        fmm = nodal_face_mass_matrix(basis, volume_nodes, face_nodes, order,
                                     verts)
        fmm2 = nodal_face_mass_matrix(basis, volume_nodes, face_nodes,
                                      order + 1, verts)

        assert la.norm(fmm - fmm2, np.inf) < 1e-11

        fmm[np.abs(fmm) < 1e-13] = 0

        print(fmm)
        nnz = np.sum(np.abs(fmm) > 0)
        print(nnz)

    print(
        mp.mass_matrix(
            mp.simplex_onb(dim - 1, order),
            mp.warp_and_blend_nodes(dim - 1, order),
        ))
Ejemplo n.º 3
0
def test_nodal_face_mass_matrix(dim, order=3):
    from modepy.tools import unit_vertices
    all_verts = unit_vertices(dim).T

    basis = mp.simplex_onb(dim, order)

    np.set_printoptions(linewidth=200)

    from modepy.matrices import nodal_face_mass_matrix
    volume_nodes = mp.warp_and_blend_nodes(dim, order)
    face_nodes = mp.warp_and_blend_nodes(dim-1, order)

    for iface in range(dim+1):
        verts = np.hstack([all_verts[:, :iface], all_verts[:, iface+1:]])

        fmm = nodal_face_mass_matrix(basis, volume_nodes, face_nodes, order,
                verts)
        fmm2 = nodal_face_mass_matrix(basis, volume_nodes, face_nodes, order+1,
                verts)

        assert la.norm(fmm-fmm2, np.inf) < 1e-11

        fmm[np.abs(fmm) < 1e-13] = 0

        print(fmm)
        nnz = np.sum(np.abs(fmm) > 0)
        print(nnz)

    print(mp.mass_matrix(
        mp.simplex_onb(dim-1, order),
        mp.warp_and_blend_nodes(dim-1, order), ))
Ejemplo n.º 4
0
    def unit_nodes(self):
        dim = self.mesh_el_group.dim
        if self.order == 0:
            result = mp.warp_and_blend_nodes(dim, 1)
            result = np.mean(result, axis=1).reshape(-1, 1)
        else:
            result = mp.warp_and_blend_nodes(dim, self.order)

        dim2, nunit_nodes = result.shape
        assert dim2 == dim
        return result
Ejemplo n.º 5
0
def make_group_from_vertices(vertices, vertex_indices, order):
    el_vertices = vertices[:, vertex_indices]

    el_origins = el_vertices[:, :, 0][:, :, np.newaxis]
    # ambient_dim, nelements, nspan_vectors
    spanning_vectors = (
            el_vertices[:, :, 1:] - el_origins)

    nspan_vectors = spanning_vectors.shape[-1]
    dim = nspan_vectors

    # dim, nunit_nodes
    if dim <= 3:
        unit_nodes = mp.warp_and_blend_nodes(dim, order)
    else:
        unit_nodes = mp.equidistant_nodes(dim, order)

    unit_nodes_01 = 0.5 + 0.5*unit_nodes

    nodes = np.einsum(
            "si,des->dei",
            unit_nodes_01, spanning_vectors) + el_origins

    # make contiguous
    nodes = nodes.copy()

    from meshmode.mesh import SimplexElementGroup
    return SimplexElementGroup(
            order, vertex_indices, nodes,
            unit_nodes=unit_nodes)
Ejemplo n.º 6
0
    def unit_nodes(self):
        dim = self.mesh_el_group.dim
        result = mp.warp_and_blend_nodes(dim, self.order)

        dim2, nunit_nodes = result.shape
        assert dim2 == dim
        return result
Ejemplo n.º 7
0
    def unit_nodes(self):
        dim = self.mesh_el_group.dim
        result = mp.warp_and_blend_nodes(dim, self.order)

        dim2, nunit_nodes = result.shape
        assert dim2 == dim
        return result
Ejemplo n.º 8
0
def test_diff_matrix(dims, eltype):
    n = 5

    if eltype == "simplex":
        nodes = mp.warp_and_blend_nodes(dims, n)
        basis = mp.simplex_onb(dims, n)
        grad_basis = mp.grad_simplex_onb(dims, n)
    elif eltype == "tensor":
        nodes = mp.legendre_gauss_lobatto_tensor_product_nodes(dims, n)
        basis = mp.legendre_tensor_product_basis(dims, n)
        grad_basis = mp.grad_legendre_tensor_product_basis(dims, n)
    else:
        raise ValueError(f"unknown element type: {eltype}")

    diff_mat = mp.differentiation_matrices(basis, grad_basis, nodes)
    if isinstance(diff_mat, tuple):
        diff_mat = diff_mat[0]

    f = np.sin(nodes[0])

    df_dx = np.cos(nodes[0])
    df_dx_num = np.dot(diff_mat, f)

    error = la.norm(df_dx - df_dx_num) / la.norm(df_dx)
    logger.info("error: %.5e", error)
    assert error < 2.0e-4, error
Ejemplo n.º 9
0
def make_curve_mesh(curve_f, element_boundaries, order,
        unit_nodes=None,
        node_vertex_consistency_tolerance=None,
        return_parametrization_points=False):
    """
    :arg curve_f: A callable representing a parametrization for a curve,
        accepting a vector of point locations and returning
        an array of shape *(2, npoints)*.
    :arg element_boundaries: a vector of element boundary locations in
        :math:`[0,1]`, in order. 0 must be the first entry, 1 the
        last one.
    :arg unit_nodes: if given, the unit nodes to use. Must have shape
        ``(dim, nnoodes)``.
    :returns: a :class:`meshmode.mesh.Mesh`, or if *return_parametrization_points*
        is True, a tuple ``(mesh, par_points)``, where *par_points* is an array of
        parametrization points.
    """

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

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

    vertices = curve_f(element_boundaries)

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

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

    from meshmode.mesh import Mesh, SimplexElementGroup
    egroup = SimplexElementGroup(
            order,
            vertex_indices=np.vstack([
                np.arange(nelements, dtype=np.int32),
                np.arange(1, nelements+1, dtype=np.int32) % nelements,
                ]).T,
            nodes=nodes,
            unit_nodes=unit_nodes)

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

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

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

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

    vertices = curve_f(element_boundaries)

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

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

    from meshmode.mesh import Mesh, SimplexElementGroup
    egroup = SimplexElementGroup(
            order,
            vertex_indices=np.vstack([
                np.arange(nelements, dtype=np.int32),
                np.arange(1, nelements+1, dtype=np.int32) % nelements,
                ]).T,
            nodes=nodes,
            unit_nodes=unit_nodes)

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

    if return_parametrization_points:
        return mesh, t
    else:
        return mesh
Ejemplo n.º 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)
Ejemplo n.º 12
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)
Ejemplo n.º 13
0
    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)
Ejemplo n.º 14
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
Ejemplo n.º 15
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
Ejemplo n.º 16
0
def test_diff_matrix(dims):
    n = 5
    nodes = mp.warp_and_blend_nodes(dims, n)

    f = np.sin(nodes[0])
    df_dx = np.cos(nodes[0])

    diff_mat = mp.differentiation_matrices(mp.simplex_onb(dims, n),
                                           mp.grad_simplex_onb(dims, n), nodes)
    if isinstance(diff_mat, tuple):
        diff_mat = diff_mat[0]
    df_dx_num = np.dot(diff_mat, f)

    print(la.norm(df_dx - df_dx_num))
    assert la.norm(df_dx - df_dx_num) < 1e-3
Ejemplo n.º 17
0
def test_diff_matrix(dims):
    n = 5
    nodes = mp.warp_and_blend_nodes(dims, n)

    f = np.sin(nodes[0])
    df_dx = np.cos(nodes[0])

    diff_mat = mp.differentiation_matrices(
            mp.simplex_onb(dims, n),
            mp.grad_simplex_onb(dims, n),
            nodes)
    if isinstance(diff_mat, tuple):
        diff_mat = diff_mat[0]
    df_dx_num = np.dot(diff_mat, f)

    print((la.norm(df_dx-df_dx_num)))
    assert la.norm(df_dx-df_dx_num) < 1e-3
Ejemplo n.º 18
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
Ejemplo n.º 19
0
def test_diff_matrix_permutation(dims):
    order = 5
    space = mp.PN(dims, order)

    from pytools import \
            generate_nonnegative_integer_tuples_summing_to_at_most as gnitstam
    node_tuples = list(gnitstam(order, dims))

    simplex_onb = mp.orthonormal_basis_for_space(space, mp.Simplex(dims))
    nodes = np.array(mp.warp_and_blend_nodes(dims, order, node_tuples=node_tuples))
    diff_matrices = mp.differentiation_matrices(
            simplex_onb.functions, simplex_onb.gradients, nodes)

    for iref_axis in range(dims):
        perm = mp.diff_matrix_permutation(node_tuples, iref_axis)

        assert la.norm(
                diff_matrices[iref_axis]
                - diff_matrices[0][perm][:, perm]) < 1e-10
Ejemplo n.º 20
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
Ejemplo n.º 21
0
def test_diff_matrix_permutation(dims):
    order = 5

    from pytools import \
            generate_nonnegative_integer_tuples_summing_to_at_most as gnitstam
    node_tuples = list(gnitstam(order, dims))

    simplex_onb = mp.simplex_onb(dims, order)
    grad_simplex_onb = mp.grad_simplex_onb(dims, order)
    nodes = np.array(
        mp.warp_and_blend_nodes(dims, order, node_tuples=node_tuples))
    diff_matrices = mp.differentiation_matrices(simplex_onb, grad_simplex_onb,
                                                nodes)

    for iref_axis in range(dims):
        perm = mp.diff_matrix_permutation(node_tuples, iref_axis)

        assert la.norm(diff_matrices[iref_axis] -
                       diff_matrices[0][perm][:, perm]) < 1e-10
Ejemplo n.º 22
0
    def __init__(self, order, vertex_indices, nodes,
            element_nr_base=None, node_nr_base=None,
            unit_nodes=None, dim=None):
        """
        :arg order: the maximum total degree used for interpolation.
        :arg nodes: ``[ambient_dim, nelements, nunit_nodes]``
            The nodes are assumed to be mapped versions of *unit_nodes*.
        :arg unit_nodes: ``[dim, nunit_nodes]``
            The unit nodes of which *nodes* is a mapped
            version. If unspecified, the nodes from
            :func:`modepy.warp_and_blend_nodes` for *dim*
            are assumed. These must be in unit coordinates
            as defined in :mod:`modepy`.
        :arg dim: only used if *unit_nodes* is None, to get
            the default unit nodes.

        Do not supply *element_nr_base* and *node_nr_base*, they will be
        automatically assigned.
        """

        if unit_nodes is None:
            if dim is None:
                raise TypeError("'dim' must be passed "
                        "if 'unit_nodes' is not passed")

            if dim <= 3:
                unit_nodes = mp.warp_and_blend_nodes(dim, order)
            else:
                unit_nodes = mp.equidistant_nodes(dim, order)

        dims = unit_nodes.shape[0]

        if vertex_indices is not None:
            if not issubclass(vertex_indices.dtype.type, np.integer):
                raise TypeError("vertex_indices must be integral")

            if vertex_indices.shape[-1] != dims+1:
                raise ValueError("vertex_indices has wrong number of vertices per "
                        "element. expected: %d, got: %d" % (dims+1,
                            vertex_indices.shape[-1]))

        super().__init__(order, vertex_indices, nodes,
                element_nr_base, node_nr_base, unit_nodes, dim)
Ejemplo n.º 23
0
def test_estimate_lebesgue_constant(dims, order, domain, visualize=False):
    logging.basicConfig(level=logging.INFO)

    if domain == "simplex":
        nodes = mp.warp_and_blend_nodes(dims, order)
    elif domain == "hypercube":
        from modepy.nodes import legendre_gauss_lobatto_tensor_product_nodes
        nodes = legendre_gauss_lobatto_tensor_product_nodes(dims, order)
    else:
        raise ValueError(f"unknown domain: '{domain}'")

    from modepy.tools import estimate_lebesgue_constant
    lebesgue_constant = estimate_lebesgue_constant(order, nodes, domain=domain)
    logger.info("%s-%d/%s: %.5e", domain, dims, order, lebesgue_constant)

    if not visualize:
        return

    from modepy.tools import _evaluate_lebesgue_function
    lebesgue, equi_node_tuples, equi_nodes = \
            _evaluate_lebesgue_function(order, nodes, domain)

    import matplotlib.pyplot as plt
    fig = plt.figure()
    ax = fig.gca()
    ax.grid()

    if dims == 1:
        ax.plot(equi_nodes[0], lebesgue)
        ax.set_xlabel("$x$")
        ax.set_ylabel(fr"$\lambda_{order}$")
    elif dims == 2:
        ax.plot(nodes[0], nodes[1], "ko")
        p = ax.tricontourf(equi_nodes[0], equi_nodes[1], lebesgue, levels=16)
        fig.colorbar(p)
        ax.set_xlim([-1.1, 1.1])
        ax.set_ylim([-1.1, 1.1])
        ax.set_aspect("equal")
    else:
        raise ValueError(f"unsupported dimension: {dims}")

    fig.savefig(f"estimate_lebesgue_constant_{domain}_{dims}_order_{order}")
Ejemplo n.º 24
0
    def __init__(self, order, vertex_indices, nodes,
            element_nr_base=None, node_nr_base=None,
            unit_nodes=None, dim=None):
        """
        :arg order: the mamximum total degree used for interpolation.
        :arg nodes: ``[ambient_dim, nelements, nunit_nodes]``
            The nodes are assumed to be mapped versions of *unit_nodes*.
        :arg unit_nodes: ``[dim, nunit_nodes]``
            The unit nodes of which *nodes* is a mapped
            version. If unspecified, the nodes from
            :func:`modepy.warp_and_blend_nodes` for *dim*
            are assumed. These must be in unit coordinates
            as defined in :mod:`modepy.nodes`.
        :arg dim: only used if *unit_nodes* is None, to get
            the default unit nodes.

        Do not supply *element_nr_base* and *node_nr_base*, they will be
        automatically assigned.
        """

        if not issubclass(vertex_indices.dtype.type, np.integer):
            raise TypeError("vertex_indices must be integral")

        if unit_nodes is None:
            if dim is None:
                raise TypeError("'dim' must be passed "
                        "if 'unit_nodes' is not passed")

            if dim <= 3:
                unit_nodes = mp.warp_and_blend_nodes(dim, order)
            else:
                unit_nodes = mp.equidistant_nodes(dim, order)

        dims = unit_nodes.shape[0]

        if vertex_indices.shape[-1] != dims+1:
            raise ValueError("vertex_indices has wrong number of vertices per "
                    "element. expected: %d, got: %d" % (dims+1,
                        vertex_indices.shape[-1]))

        super(SimplexElementGroup, self).__init__(order, vertex_indices, nodes,
                element_nr_base, node_nr_base, unit_nodes, dim)
Ejemplo n.º 25
0
def make_curve_mesh(curve_f, element_boundaries, order):
    """
    :arg curve_f: A callable representing a parametrization for a curve,
        accepting a vector of point locations and returning
        an array of shape *(2, npoints)*.
    :arg element_boundaries: a vector of element boundary locations in
        :math:`[0,1]`, in order. 0 must be the first entry, 1 the
        last one.
    :returns: a :class:`meshmode.mesh.Mesh`
    """

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

    unodes = mp.warp_and_blend_nodes(1, order)
    nodes_01 = 0.5*(unodes+1)

    vertices = curve_f(element_boundaries)

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

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

    from meshmode.mesh import Mesh, SimplexElementGroup
    egroup = SimplexElementGroup(
            order,
            vertex_indices=np.vstack([
                np.arange(nelements, dtype=np.int32),
                np.arange(1, nelements+1, dtype=np.int32) % nelements,
                ]).T,
            nodes=nodes,
            unit_nodes=unodes)

    return Mesh(
            vertices=vertices, groups=[egroup],
            nodal_adjacency=None,
            facial_adjacency_groups=None)
Ejemplo n.º 26
0
def make_group_from_vertices(vertices, vertex_indices, order,
        group_factory=None):
    # shape: (dim, nelements, nvertices)
    el_vertices = vertices[:, vertex_indices]

    from meshmode.mesh import SimplexElementGroup, TensorProductElementGroup

    if group_factory is None:
        group_factory = SimplexElementGroup

    if issubclass(group_factory, SimplexElementGroup):
        el_origins = el_vertices[:, :, 0][:, :, np.newaxis]
        # ambient_dim, nelements, nspan_vectors
        spanning_vectors = (
                el_vertices[:, :, 1:] - el_origins)

        nspan_vectors = spanning_vectors.shape[-1]
        dim = nspan_vectors

        # dim, nunit_nodes
        if dim <= 3:
            unit_nodes = mp.warp_and_blend_nodes(dim, order)
        else:
            unit_nodes = mp.equidistant_nodes(dim, order)

        unit_nodes_01 = 0.5 + 0.5*unit_nodes

        nodes = np.einsum(
                "si,des->dei",
                unit_nodes_01, spanning_vectors) + el_origins

    elif issubclass(group_factory, TensorProductElementGroup):
        nelements, nvertices = vertex_indices.shape

        dim = 0
        while True:
            if nvertices == 2**dim:
                break
            if nvertices < 2**dim:
                raise ValueError("invalid number of vertices for tensor-product "
                        "elements, must be power of two")
            dim += 1

        from modepy.quadrature.jacobi_gauss import legendre_gauss_lobatto_nodes
        from modepy.nodes import tensor_product_nodes
        unit_nodes = tensor_product_nodes(dim, legendre_gauss_lobatto_nodes(order))
        # shape: (dim, nnodes)
        unit_nodes_01 = 0.5 + 0.5*unit_nodes

        _, nnodes = unit_nodes.shape

        from pytools import generate_nonnegative_integer_tuples_below as gnitb
        id_tuples = list(gnitb(2, dim))
        assert len(id_tuples) == nvertices

        vdm = np.empty((nvertices, nvertices))
        for i, vertex_tuple in enumerate(id_tuples):
            for j, func_tuple in enumerate(id_tuples):
                vertex_ref = np.array(vertex_tuple, dtype=np.float64)
                vdm[i, j] = np.prod(vertex_ref**func_tuple)

        # shape: (dim, nelements, nvertices)
        coeffs = np.empty((dim, nelements, nvertices))
        for d in range(dim):
            coeffs[d] = la.solve(vdm, el_vertices[d].T).T

        vdm_nodes = np.zeros((nnodes, nvertices))
        for j, func_tuple in enumerate(id_tuples):
            vdm_nodes[:, j] = np.prod(
                    unit_nodes_01 ** np.array(func_tuple).reshape(-1, 1),
                    axis=0)

        nodes = np.einsum("ij,dej->dei", vdm_nodes, coeffs)

    else:
        raise ValueError("unsupported value for 'group_factory': %s"
                % group_factory)

    # make contiguous
    nodes = nodes.copy()

    return group_factory(
            order, vertex_indices, nodes,
            unit_nodes=unit_nodes)
Ejemplo n.º 27
0
n = 8

ball_radius = 0.05
link_radius = 0.02

from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
        as gnitstam
node_tuples = list(gnitstam(n, 3))
faces = [[nt for nt in node_tuples if nt[0] == 0],
         [nt for nt in node_tuples if nt[1] == 0],
         [nt for nt in node_tuples if nt[2] == 0],
         [nt for nt in node_tuples if sum(nt) == n]]

from modepy.tools import unit_to_barycentric, barycentric_to_equilateral
nodes = [(n[0], n[2], n[1]) for n in barycentric_to_equilateral(
    unit_to_barycentric(mp.warp_and_blend_nodes(3, n, node_tuples))).T]
id_to_node = dict(list(zip(node_tuples, nodes)))


def get_ball_radius(nid):
    in_faces = len([f for f in faces if nid in f])
    if in_faces >= 2:
        return ball_radius * 1.333
    else:
        return ball_radius


def get_ball_color(nid):
    in_faces = len([f for f in faces if nid in f])
    if in_faces >= 2:
        return (1, 0, 1)
Ejemplo n.º 28
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
Ejemplo n.º 29
0
def make_curve_mesh(curve_f,
                    element_boundaries,
                    order,
                    unit_nodes=None,
                    node_vertex_consistency_tolerance=None,
                    closed=True,
                    return_parametrization_points=False):
    """
    :arg curve_f: A callable representing a parametrization for a curve,
        accepting a vector of point locations and returning
        an array of shape *(2, npoints)*.
    :arg element_boundaries: a vector of element boundary locations in
        :math:`[0,1]`, in order. 0 must be the first entry, 1 the
        last one.
    :arg closed: if *True*, the curve is assumed closed and the first and
        last of the *element_boundaries* must match.
    :arg unit_nodes: if given, the unit nodes to use. Must have shape
        ``(dim, nnodes)``.
    :returns: a :class:`meshmode.mesh.Mesh`, or if *return_parametrization_points*
        is *True*, a tuple ``(mesh, par_points)``, where *par_points* is an array of
        parametrization points.
    """

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

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

    wrap = nelements
    if not closed:
        wrap += 1

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

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

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

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

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

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

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

    if return_parametrization_points:
        return mesh, t
    else:
        return mesh
Ejemplo n.º 30
0
from __future__ import absolute_import
import matplotlib.pyplot as pt
import numpy as np
import modepy as mp

dims = 3
n = 10

unit = mp.warp_and_blend_nodes(dims, n)

if 0:
    from modepy.tools import estimate_lebesgue_constant
    lebesgue = estimate_lebesgue_constant(n, unit, visualize=True)

from modepy.tools import unit_to_barycentric, barycentric_to_equilateral
equi = barycentric_to_equilateral(unit_to_barycentric(unit))

if dims == 2:
    pt.plot(equi[0], equi[1], "o")

    from modepy.tools import EQUILATERAL_VERTICES
    uv = list(EQUILATERAL_VERTICES[2])
    uv.append(uv[0])
    uv = np.array(uv)
    pt.plot(uv[:, 0], uv[:, 1], "")

    pt.gca().set_aspect("equal")
    pt.show()
elif dims == 3:
    import mayavi.mlab as mlab
    mlab.points3d(
Ejemplo n.º 31
0
from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
        as gnitstam
node_tuples = list(gnitstam(n, 3))
faces = [
        [nt for nt in node_tuples if nt[0] == 0],
        [nt for nt in node_tuples if nt[1] == 0],
        [nt for nt in node_tuples if nt[2] == 0],
        [nt for nt in node_tuples if sum(nt) == n]
        ]

from modepy.tools import unit_to_barycentric, barycentric_to_equilateral
nodes = [(n[0],n[2], n[1]) for n in
        barycentric_to_equilateral(
            unit_to_barycentric(
                mp.warp_and_blend_nodes(3, n, node_tuples))).T]
id_to_node = dict(list(zip(node_tuples, nodes)))

def get_ball_radius(nid):
    in_faces = len([f for f in faces if nid in f])
    if in_faces >= 2:
        return ball_radius * 1.333
    else:
        return ball_radius

def get_ball_color(nid):
    in_faces = len([f for f in faces if nid in f])
    if in_faces >= 2:
        return (1,0,1)
    else:
        return (0,0,1)
Ejemplo n.º 32
0
import numpy as np
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)
Ejemplo n.º 33
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
Ejemplo n.º 34
0
import numpy as np
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
Ejemplo n.º 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
Ejemplo n.º 36
0
def test_sanity_single_element(ctx_getter, dim, order, visualize=False):
    pytest.importorskip("pytential")

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

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

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

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

    mesh = Mesh(vertices, [mg],
                nodal_adjacency=None,
                facial_adjacency_groups=None)

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

    # {{{ volume calculation check

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

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

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

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

    assert rel_vol_err < 1e-12

    # }}}

    # {{{ boundary discretization

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

    # }}}

    # {{{ visualizers

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

    # }}}

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

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

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

    assert normal_outward_check.get().all(), normal_outward_check.get()
Ejemplo n.º 37
0
def run(actx, *,
        ambient_dim: int = 3,
        resolution: int = None,
        target_order: int = 4,
        tmax: float = 1.0,
        timestep: float = 1.0e-2,
        group_factory_name: str = "warp_and_blend",
        visualize: bool = True):
    if ambient_dim not in (2, 3):
        raise ValueError(f"unsupported dimension: {ambient_dim}")

    mesh_order = target_order
    radius = 1.0

    # {{{ geometry

    # {{{ element groups

    import modepy as mp
    import meshmode.discretization.poly_element as poly

    # NOTE: picking the same unit nodes for the mesh and the discr saves
    # a bit of work when reconstructing after a time step

    if group_factory_name == "warp_and_blend":
        group_factory_cls = poly.PolynomialWarpAndBlendGroupFactory

        unit_nodes = mp.warp_and_blend_nodes(ambient_dim - 1, mesh_order)
    elif group_factory_name == "quadrature":
        group_factory_cls = poly.InterpolatoryQuadratureSimplexGroupFactory

        if ambient_dim == 2:
            unit_nodes = mp.LegendreGaussQuadrature(
                    mesh_order, force_dim_axis=True).nodes
        else:
            unit_nodes = mp.VioreanuRokhlinSimplexQuadrature(mesh_order, 2).nodes
    else:
        raise ValueError(f"unknown group factory: '{group_factory_name}'")

    # }}}

    # {{{ discretization

    import meshmode.mesh.generation as gen
    if ambient_dim == 2:
        nelements = 8192 if resolution is None else resolution
        mesh = gen.make_curve_mesh(
                lambda t: radius * gen.ellipse(1.0, t),
                np.linspace(0.0, 1.0, nelements + 1),
                order=mesh_order,
                unit_nodes=unit_nodes)
    else:
        nrounds = 4 if resolution is None else resolution
        mesh = gen.generate_icosphere(radius,
                uniform_refinement_rounds=nrounds,
                order=mesh_order,
                unit_nodes=unit_nodes)

    from meshmode.discretization import Discretization
    discr0 = Discretization(actx, mesh, group_factory_cls(target_order))

    logger.info("ndofs:     %d", discr0.ndofs)
    logger.info("nelements: %d", discr0.mesh.nelements)

    # }}}

    if visualize:
        from meshmode.discretization.visualization import make_visualizer
        vis = make_visualizer(actx, discr0,
                vis_order=target_order,
                # NOTE: setting this to True will add some unnecessary
                # resampling in Discretization.nodes for the vis_discr underneath
                force_equidistant=False)

    # }}}

    # {{{ ode

    def velocity_field(nodes, alpha=1.0):
        return make_obj_array([
            alpha * nodes[0], -alpha * nodes[1], 0.0 * nodes[0]
            ][:ambient_dim])

    def source(t, x):
        discr = reconstruct_discr_from_nodes(actx, discr0, x)
        u = velocity_field(thaw(discr.nodes(), actx))

        # {{{

        # NOTE: these are just here because this was at some point used to
        # profile some more operators (turned out well!)

        from meshmode.discretization import num_reference_derivative
        x = thaw(discr.nodes()[0], actx)
        gradx = sum(
                num_reference_derivative(discr, (i,), x)
                for i in range(discr.dim))
        intx = sum(actx.np.sum(xi * wi) for xi, wi in zip(x, discr.quad_weights()))

        assert gradx is not None
        assert intx is not None

        # }}}

        return u

    # }}}

    # {{{ evolve

    maxiter = int(tmax // timestep) + 1
    dt = tmax / maxiter + 1.0e-15

    x = thaw(discr0.nodes(), actx)
    t = 0.0

    if visualize:
        filename = f"moving-geometry-{0:09d}.vtu"
        plot_solution(actx, vis, filename, discr0, t, x)

    for n in range(1, maxiter + 1):
        x = advance(actx, dt, t, x, source)
        t += dt

        if visualize:
            discr = reconstruct_discr_from_nodes(actx, discr0, x)
            vis = make_visualizer(actx, discr, vis_order=target_order)
            # vis = vis.copy_with_same_connectivity(actx, discr)

            filename = f"moving-geometry-{n:09d}.vtu"
            plot_solution(actx, vis, filename, discr, t, x)

        logger.info("[%05d/%05d] t = %.5e/%.5e dt = %.5e",
                n, maxiter, t, tmax, dt)
Ejemplo n.º 38
0
def test_sanity_single_element(ctx_getter, dim, order, visualize=False):
    pytest.importorskip("pytential")

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

    from modepy.tools import UNIT_VERTICES
    vertices = UNIT_VERTICES[dim].T.copy()

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

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

    mesh = Mesh(vertices, [mg])

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

    # {{{ volume calculation check

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

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

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

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

    assert rel_vol_err < 1e-12

    # }}}

    # {{{ boundary discretization

    from meshmode.discretization.connection import make_boundary_restriction
    bdry_mesh, bdry_discr, bdry_connection = make_boundary_restriction(
            queue, vol_discr, PolynomialWarpAndBlendGroupFactory(order + 3))

    # }}}

    # {{{ visualizers

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

    # }}}

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

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

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

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

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

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

    wrap = nelements
    if not closed:
        wrap += 1

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

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

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

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

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

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

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

    if return_parametrization_points:
        return mesh, t
    else:
        return mesh
Ejemplo n.º 40
0
def make_group_from_vertices(vertices, vertex_indices, order,
        group_factory=None):
    # shape: (dim, nelements, nvertices)
    el_vertices = vertices[:, vertex_indices]

    from meshmode.mesh import SimplexElementGroup, TensorProductElementGroup

    if group_factory is None:
        group_factory = SimplexElementGroup

    if issubclass(group_factory, SimplexElementGroup):
        el_origins = el_vertices[:, :, 0][:, :, np.newaxis]
        # ambient_dim, nelements, nspan_vectors
        spanning_vectors = (
                el_vertices[:, :, 1:] - el_origins)

        nspan_vectors = spanning_vectors.shape[-1]
        dim = nspan_vectors

        # dim, nunit_nodes
        if dim <= 3:
            unit_nodes = mp.warp_and_blend_nodes(dim, order)
        else:
            unit_nodes = mp.equidistant_nodes(dim, order)

        unit_nodes_01 = 0.5 + 0.5*unit_nodes

        nodes = np.einsum(
                "si,des->dei",
                unit_nodes_01, spanning_vectors) + el_origins

    elif issubclass(group_factory, TensorProductElementGroup):
        nelements, nvertices = vertex_indices.shape

        dim = 0
        while True:
            if nvertices == 2**dim:
                break
            if nvertices < 2**dim:
                raise ValueError("invalid number of vertices for tensor-product "
                        "elements, must be power of two")
            dim += 1

        from modepy.quadrature.jacobi_gauss import legendre_gauss_lobatto_nodes
        from modepy.nodes import tensor_product_nodes
        unit_nodes = tensor_product_nodes(dim, legendre_gauss_lobatto_nodes(order))
        # shape: (dim, nnodes)
        unit_nodes_01 = 0.5 + 0.5*unit_nodes

        _, nnodes = unit_nodes.shape

        from pytools import generate_nonnegative_integer_tuples_below as gnitb
        id_tuples = list(gnitb(2, dim))
        assert len(id_tuples) == nvertices

        vdm = np.empty((nvertices, nvertices))
        for i, vertex_tuple in enumerate(id_tuples):
            for j, func_tuple in enumerate(id_tuples):
                vertex_ref = np.array(vertex_tuple, dtype=np.float64)
                vdm[i, j] = np.prod(vertex_ref**func_tuple)

        # shape: (dim, nelements, nvertices)
        coeffs = np.empty((dim, nelements, nvertices))
        for d in range(dim):
            coeffs[d] = la.solve(vdm, el_vertices[d].T).T

        vdm_nodes = np.zeros((nnodes, nvertices))
        for j, func_tuple in enumerate(id_tuples):
            vdm_nodes[:, j] = np.prod(
                    unit_nodes_01 ** np.array(func_tuple).reshape(-1, 1),
                    axis=0)

        nodes = np.einsum("ij,dej->dei", vdm_nodes, coeffs)

    else:
        raise ValueError("unsupported value for 'group_factory': %s"
                % group_factory)

    # make contiguous
    nodes = nodes.copy()

    return group_factory(
            order, vertex_indices, nodes,
            unit_nodes=unit_nodes)
Ejemplo n.º 41
0
def make_group_from_vertices(vertices,
                             vertex_indices,
                             order,
                             group_cls=None,
                             unit_nodes=None,
                             group_factory=None):
    if group_factory is not None:
        from warnings import warn
        warn("'group_factory' is deprecated, use 'group_cls' instead",
             DeprecationWarning,
             stacklevel=2)

        if group_cls is not None:
            raise ValueError("cannot set both 'group_cls' and 'group_factory'")

        group_cls = group_factory

    # shape: (ambient_dim, nelements, nvertices)
    ambient_dim = vertices.shape[0]
    el_vertices = vertices[:, vertex_indices]

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

    if issubclass(group_cls, SimplexElementGroup):
        if order < 1:
            raise ValueError("can't represent simplices with mesh order < 1")

        el_origins = el_vertices[:, :, 0][:, :, np.newaxis]
        # ambient_dim, nelements, nspan_vectors
        spanning_vectors = (el_vertices[:, :, 1:] - el_origins)

        nspan_vectors = spanning_vectors.shape[-1]
        dim = nspan_vectors

        # dim, nunit_nodes
        if unit_nodes is None:
            if dim <= 3:
                unit_nodes = mp.warp_and_blend_nodes(dim, order)
            else:
                unit_nodes = mp.equidistant_nodes(dim, order)

        unit_nodes_01 = 0.5 + 0.5 * unit_nodes

        nodes = np.einsum("si,des->dei", unit_nodes_01,
                          spanning_vectors) + el_origins

    elif issubclass(group_cls, TensorProductElementGroup):
        nelements, nvertices = vertex_indices.shape

        dim = nvertices.bit_length() - 1
        if nvertices != 2**dim:
            raise ValueError("invalid number of vertices for tensor-product "
                             "elements, must be power of two")

        if unit_nodes is None:
            from modepy.quadrature.jacobi_gauss import legendre_gauss_lobatto_nodes
            unit_nodes = mp.tensor_product_nodes(
                dim, legendre_gauss_lobatto_nodes(order))

        # shape: (dim, nnodes)
        unit_nodes_01 = 0.5 + 0.5 * unit_nodes
        _, nnodes = unit_nodes.shape

        from pytools import generate_nonnegative_integer_tuples_below as gnitb
        id_tuples = list(gnitb(2, dim))
        assert len(id_tuples) == nvertices

        vdm = np.empty((nvertices, nvertices))
        for i, vertex_tuple in enumerate(id_tuples):
            for j, func_tuple in enumerate(id_tuples):
                vertex_ref = np.array(vertex_tuple, dtype=np.float64)
                vdm[i, j] = np.prod(vertex_ref**func_tuple)

        # shape: (ambient_dim, nelements, nvertices)
        coeffs = np.empty((ambient_dim, nelements, nvertices))
        for d in range(ambient_dim):
            coeffs[d] = la.solve(vdm, el_vertices[d].T).T

        vdm_nodes = np.zeros((nnodes, nvertices))
        for j, func_tuple in enumerate(id_tuples):
            vdm_nodes[:,
                      j] = np.prod(unit_nodes_01**np.array(func_tuple).reshape(
                          -1, 1),
                                   axis=0)

        nodes = np.einsum("ij,dej->dei", vdm_nodes, coeffs)
    else:
        raise ValueError(f"unsupported value for 'group_cls': {group_cls}")

    # make contiguous
    nodes = nodes.copy()

    return group_cls(order, vertex_indices, nodes, unit_nodes=unit_nodes)
Ejemplo n.º 42
0
import matplotlib.pyplot as pt
import numpy as np
import modepy as mp

nodes = mp.warp_and_blend_nodes(2, 10)
pt.plot(nodes[0], nodes[1], "x")

tri = np.array([(-1, -1), (1, -1), (-1, 1), (-1, -1)]).T
pt.plot(nodes[0], nodes[1], "x")
pt.plot(tri[0], tri[1], "-b")
pt.gca().set_aspect("equal")
pt.show()
Ejemplo n.º 43
0
import matplotlib.pyplot as pt
import numpy as np
import modepy as mp

dims = 3
n = 10

unit = mp.warp_and_blend_nodes(dims, n)

if 0:
    from modepy.tools import estimate_lebesgue_constant
    lebesgue = estimate_lebesgue_constant(n, unit, visualize=True)

from modepy.tools import unit_to_barycentric, barycentric_to_equilateral
equi = barycentric_to_equilateral(unit_to_barycentric(unit))

if dims == 2:
    pt.plot(equi[0], equi[1], "o")

    from modepy.tools import EQUILATERAL_VERTICES
    uv = list(EQUILATERAL_VERTICES[2])
    uv.append(uv[0])
    uv = np.array(uv)
    pt.plot(uv[:, 0], uv[:, 1], "")

    pt.gca().set_aspect("equal")
    pt.show()
elif dims == 3:
    import mayavi.mlab as mlab
    mlab.points3d(equi[0], equi[1], equi[2])
    mlab.orientation_axes()