Exemple #1
0
def grad_simplex_onb(dims, n):
    """Return the gradients of the functions returned by :func:`simplex_onb`.

    :returns: a :class:`tuple` of functions, each of  which
        accepts arrays of shape *(dims, npts)*
        and returns a :class:`tuple` of length *dims* containing
        the derivatives along each axis as an array of size *npts*.
        'Scalar' evaluation, by passing just one vector of length *dims*,
        is also supported.

    See the following publications:

    * |proriol-ref|
    * |koornwinder-ref|
    * |dubiner-ref|

    .. versionchanged:: 2013.2

        Made return value a tuple, to make bases hashable.
    """
    from functools import partial
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam

    if dims == 1:
        return tuple(partial(grad_jacobi, 0, 0, i) for i in range(n+1))
    elif dims == 2:
        return tuple(partial(grad_pkdo_2d, order) for order in gnitstam(n, dims))
    elif dims == 3:
        return tuple(partial(grad_pkdo_3d, order) for order in gnitstam(n, dims))
    else:
        raise NotImplementedError("%d-dimensional bases" % dims)
Exemple #2
0
def grad_simplex_onb(dims, n):
    """Return the gradients of the functions returned by :func:`simplex_onb`.

    :returns: a :class:`tuple` of functions, each of  which
        accepts arrays of shape *(dims, npts)*
        and returns a :class:`tuple` of length *dims* containing
        the derivatives along each axis as an array of size *npts*.
        'Scalar' evaluation, by passing just one vector of length *dims*,
        is also supported.

    See the following publications:

    * |proriol-ref|
    * |koornwinder-ref|
    * |dubiner-ref|

    .. versionchanged:: 2013.2

        Made return value a tuple, to make bases hashable.
    """
    from functools import partial
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam

    if dims == 1:
        return tuple(partial(grad_jacobi, 0, 0, i) for i in range(n + 1))
    elif dims == 2:
        return tuple(
            partial(grad_pkdo_2d, order) for order in gnitstam(n, dims))
    elif dims == 3:
        return tuple(
            partial(grad_pkdo_3d, order) for order in gnitstam(n, dims))
    else:
        raise NotImplementedError("%d-dimensional bases" % dims)
Exemple #3
0
def test_tri_face_node_distribution():
    """Test whether the nodes on the faces of the triangle are distributed
    according to the same proportions on each face.

    If this is not the case, then reusing the same face mass matrix
    for each face would be invalid.
    """

    n = 8
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam
    node_tuples = list(gnitstam(n, 2))

    from modepy.nodes import warp_and_blend_nodes
    unodes = warp_and_blend_nodes(2, n, node_tuples)

    faces = [
            [i for i, nt in enumerate(node_tuples) if nt[0] == 0],
            [i for i, nt in enumerate(node_tuples) if nt[1] == 0],
            [i for i, nt in enumerate(node_tuples) if sum(nt) == n]
            ]

    projected_face_points = []
    for face_i in faces:
        start = unodes[:, face_i[0]]
        end = unodes[:, face_i[-1]]
        direction = end-start
        direction /= np.dot(direction, direction)
        pfp = np.array([np.dot(direction, unodes[:, i]-start) for i in face_i])
        projected_face_points.append(pfp)

    first_points = projected_face_points[0]
    for points in projected_face_points[1:]:
        error = la.norm(points-first_points, np.Inf)
        assert error < 1e-15
Exemple #4
0
def test_simplex_quadrature(quad_class, highest_order, dim):
    """Check that quadratures on simplices works as advertised"""
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam
    from modepy.tools import Monomial

    order = 1
    while True:
        try:
            quad = quad_class(order, dim)
        except mp.QuadratureRuleUnavailable:
            print(("UNAVAIL", quad_class, order))
            break

        if isinstance(quad_class, mp.VioreanuRokhlinSimplexQuadrature):
            assert (quad.weights > 0).all()

        if 0:
            import matplotlib.pyplot as pt
            pt.plot(quad.nodes[0], quad.nodes[1])
            pt.show()

        print((quad_class, order, quad.exact_to))
        for comb in gnitstam(quad.exact_to, dim):
            f = Monomial(comb)
            i_f = quad(f)
            ref = f.simplex_integral()
            err = abs(i_f - ref)
            assert err < 6e-15, (err, comb, i_f, ref)

        order += 1
        if highest_order is not None and order >= highest_order:
            break
Exemple #5
0
def fit_modal_decay(coeffs, dims, n, ignored_modes=1):
    """Fit a curve to the modal decay on each element.

    :arg coeffs: a array of shape *(num_elements, num_modes)* containing modal
        coefficients of the functions to be analyzed
    :arg dims: number of dimensions
    :arg ignored_modes: the number of modal coefficients to ignore at the
        beginning. The default value of '1' ignores the constant.
    :returns: a tuple *(expn, constant)* of arrays of length *num_elements*,
        where the modal decay is fit to the curve
        ``constant * total_degree**exponent``.

    ``-exponent-1`` can be used as a rough indicator of how many continuous
    derivatives the underlying function possesses.
    """

    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam

    mode_order_tuples = list(gnitstam(n, dims))

    coeffs_squared = skyline_pessimize(coeffs**2)

    mode_number_vector_int = make_mode_number_vector(
            mode_order_tuples, ignored_modes)
    mode_number_vector = mode_number_vector_int.astype(np.float64)
    weight_vector = np.ones_like(mode_number_vector)

    fit_mat = get_decay_fit_matrix(mode_number_vector, ignored_modes,
            weight_vector)

    el_norm_squared = np.sum(coeffs_squared, axis=-1)
    scaled_baseline = (
            create_decay_baseline(mode_number_vector, n)
            * el_norm_squared[:, np.newaxis])**2
    log_modal_coeffs = np.log(coeffs_squared[:, ignored_modes:] + scaled_baseline)/2

    assert fit_mat.shape[0] == 2  # exponent and log(constant)

    fit_values = np.dot(fit_mat, (weight_vector*log_modal_coeffs).T).T

    exponent = fit_values[:, 1]
    const = np.exp(fit_values[:, 0])

    if 0:
        import matplotlib.pyplot as pt
        pt.plot(log_modal_coeffs.flat, "o-")

        fit = np.log(const[:, np.newaxis]
                * mode_number_vector**exponent[:, np.newaxis])

        pt.plot(fit.flat)

        # plot_expt = np.zeros_like(log_modal_coeffs)
        # plot_expt[:] = exponent[:, np.newaxis]
        # pt.plot(plot_expt.flat)

        pt.show()

    return exponent, const
Exemple #6
0
    def _vis_connectivity(self):
        """
        :return: an array of shape
            ``(vis_discr.nelements,nsubelements,primitive_element_size)``
        """
        # Assume that we're using modepy's default node ordering.

        from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
                as gnitstam, single_valued
        vis_order = single_valued(
                group.order for group in self.vis_discr.groups)
        node_tuples = list(gnitstam(vis_order, self.vis_discr.dim))

        from modepy.tools import submesh
        el_connectivity = np.array(
                submesh(node_tuples),
                dtype=np.intp)

        nelements = sum(group.nelements for group in self.vis_discr.groups)
        vis_connectivity = np.empty(
                (nelements,) + el_connectivity.shape, dtype=np.intp)

        el_nr_base = 0
        for group in self.vis_discr.groups:
            assert len(node_tuples) == group.nunit_nodes
            vis_connectivity[el_nr_base:el_nr_base+group.nelements] = (
                    np.arange(
                        el_nr_base*group.nunit_nodes,
                        (el_nr_base+group.nelements)*group.nunit_nodes,
                        group.nunit_nodes
                        )[:, np.newaxis, np.newaxis]
                    + el_connectivity)
            el_nr_base += group.nelements

        return vis_connectivity
Exemple #7
0
def test_hypercube_quadrature(cls, dim):
    from pytools import \
            generate_nonnegative_integer_tuples_summing_to_at_most as gnitstam
    from modepy.tools import Monomial

    def _check_monomial(quad, comb):
        f = Monomial(comb)
        int_approx = quad(f)
        int_exact = 2**dim * f.hypercube_integral()

        error = abs(int_approx - int_exact) / abs(int_exact)
        logger.info("%s: %.5e %.5e / rel error %.5e",
                comb, int_approx, int_exact, error)

        return error

    order = 1
    while True:
        try:
            quad = cls(order, dim)
        except mp.QuadratureRuleUnavailable:
            logger.info("UNAVAILABLE at order %d", order)
            break

        assert np.all(quad.weights > 0)

        logger.info("quadrature: %s %d %d", cls, order, quad.exact_to)
        for comb in gnitstam(quad.exact_to, dim):
            assert _check_monomial(quad, comb) < 5.0e-15

        comb = (0,) * (dim - 1) + (quad.exact_to + 1,)
        assert _check_monomial(quad, comb) > 5.0e-15

        order += 2
Exemple #8
0
def tesselatetet():
    node_tuples = list(gnitstam(2, 3))

    node_dict = {ituple: idx for idx, ituple in enumerate(node_tuples)}

    def try_add_tet(current, d1, d2, d3, d4):
        try:
            result.append((
                node_dict[add_tuples(current, d1)],
                node_dict[add_tuples(current, d2)],
                node_dict[add_tuples(current, d3)],
                node_dict[add_tuples(current, d4)],
            ))
        except KeyError:
            pass

    result = []

    if len(result) > 0:
        return [node_tuples, result]
    for current in node_tuples:
        # this is a tesselation of a cube into six tets.
        # subtets that fall outside of the master tet are simply not added.

        # positively oriented
        try_add_tet(current, (0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1))
        try_add_tet(current, (1, 0, 1), (1, 0, 0), (0, 0, 1), (0, 1, 0))
        try_add_tet(current, (1, 0, 1), (0, 1, 1), (0, 1, 0), (0, 0, 1))

        try_add_tet(current, (1, 0, 0), (0, 1, 0), (1, 0, 1), (1, 1, 0))
        try_add_tet(current, (0, 1, 1), (0, 1, 0), (1, 1, 0), (1, 0, 1))
        try_add_tet(current, (0, 1, 1), (1, 1, 1), (1, 0, 1), (1, 1, 0))

    return node_tuples, result
Exemple #9
0
def fit_modal_decay(coeffs, dims, n, ignored_modes=1):
    """Fit a curve to the modal decay on each element.

    :arg coeffs: a array of shape *(num_elements, num_modes)* containing modal
        coefficients of the functions to be analyzed
    :arg dims: number of dimensions
    :arg ignored_modes: the number of modal coefficients to ignore at the
        beginning. The default value of '1' ignores the constant.
    :returns: a tuple *(expn, constant)* of arrays of length *num_elements*,
        where the modal decay is fit to the curve
        ``constant * total_degree**exponent``.

    ``-exponent-1`` can be used as a rough indicator of how many continuous
    derivatives the underlying function possesses.
    """

    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam

    mode_order_tuples = list(gnitstam(n, dims))

    coeffs_squared = skyline_pessimize(coeffs**2)

    mode_number_vector_int = make_mode_number_vector(mode_order_tuples,
                                                     ignored_modes)
    mode_number_vector = mode_number_vector_int.astype(np.float64)
    weight_vector = np.ones_like(mode_number_vector)

    fit_mat = get_decay_fit_matrix(mode_number_vector, ignored_modes,
                                   weight_vector)

    el_norm_squared = np.sum(coeffs_squared, axis=-1)
    scaled_baseline = (create_decay_baseline(mode_number_vector, n) *
                       el_norm_squared[:, np.newaxis])**2
    log_modal_coeffs = np.log(coeffs_squared[:, ignored_modes:] +
                              scaled_baseline) / 2

    assert fit_mat.shape[0] == 2  # exponent and log(constant)

    fit_values = np.dot(fit_mat, (weight_vector * log_modal_coeffs).T).T

    exponent = fit_values[:, 1]
    const = np.exp(fit_values[:, 0])

    if 0:
        import matplotlib.pyplot as pt
        pt.plot(log_modal_coeffs.flat, "o-")

        fit = np.log(const[:, np.newaxis] *
                     mode_number_vector**exponent[:, np.newaxis])

        pt.plot(fit.flat)

        # plot_expt = np.zeros_like(log_modal_coeffs)
        # plot_expt[:] = exponent[:, np.newaxis]
        # pt.plot(plot_expt.flat)

        pt.show()

    return exponent, const
Exemple #10
0
def tesselatetri():
    result = []

    node_tuples = list(gnitstam(2, 2))
    node_dict = dict((ituple, idx) for idx, ituple in enumerate(node_tuples))

    def try_add_tri(current, d1, d2, d3):
        try:
            result.append((
                node_dict[add_tuples(current, d1)],
                node_dict[add_tuples(current, d2)],
                node_dict[add_tuples(current, d3)],
            ))
        except KeyError:
            pass

    if len(result) > 0:
        return [node_tuples, result]
    for current in node_tuples:
        # this is a tesselation of a square into two triangles.
        # subtriangles that fall outside of the master tet are simply not added.

        # positively oriented
        try_add_tri(current, (0, 0), (1, 0), (0, 1))
        try_add_tri(current, (1, 0), (1, 1), (0, 1))
    return node_tuples, result
Exemple #11
0
def plot_element_values(n, nodes, values, resample_n=None,
        node_tuples=None, show_nodes=False):
    dims = len(nodes)

    orig_nodes = nodes
    orig_values = values

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

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

    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam

    if dims == 1:
        import matplotlib.pyplot as pt
        pt.plot(nodes[0], values)
        if show_nodes:
            pt.plot(orig_nodes[0], orig_values, "x")
        pt.show()
    elif dims == 2:
        import mayavi.mlab as mlab
        mlab.triangular_mesh(
                nodes[0], nodes[1], values, submesh(list(gnitstam(n, 2))))
        if show_nodes:
            mlab.points3d(orig_nodes[0], orig_nodes[1], orig_values,
                    scale_factor=0.05)
        mlab.show()
    else:
        raise RuntimeError("unsupported dimensionality %d" % dims)
Exemple #12
0
def test_tri_face_node_distribution():
    """Test whether the nodes on the faces of the triangle are distributed
    according to the same proportions on each face.

    If this is not the case, then reusing the same face mass matrix
    for each face would be invalid.
    """

    n = 8
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam
    node_tuples = list(gnitstam(n, 2))

    from modepy.nodes import warp_and_blend_nodes
    unodes = warp_and_blend_nodes(2, n, node_tuples)

    faces = [
            [i for i, nt in enumerate(node_tuples) if nt[0] == 0],
            [i for i, nt in enumerate(node_tuples) if nt[1] == 0],
            [i for i, nt in enumerate(node_tuples) if sum(nt) == n]
            ]

    projected_face_points = []
    for face_i in faces:
        start = unodes[:, face_i[0]]
        end = unodes[:, face_i[-1]]
        direction = end-start
        direction /= np.dot(direction, direction)
        pfp = np.array([np.dot(direction, unodes[:, i]-start) for i in face_i])
        projected_face_points.append(pfp)

    first_points = projected_face_points[0]
    for points in projected_face_points[1:]:
        error = la.norm(points-first_points, np.Inf)
        assert error < 1e-15
Exemple #13
0
    def lexicographic_node_tuples(self):
        from pytools import \
                generate_nonnegative_integer_tuples_summing_to_at_most as gnitstam
        result = list(gnitstam(self.order, self.dimensions))

        assert len(result) == self.node_count()
        return result
Exemple #14
0
def test_simplex_quadrature(quad_class, highest_order, dim):
    """Check that quadratures on simplices works as advertised"""
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam
    from modepy.tools import Monomial

    order = 1
    while True:
        try:
            quad = quad_class(order, dim)
        except mp.QuadratureRuleUnavailable:
            print(("UNAVAILABLE", quad_class, order))
            break

        if isinstance(quad_class, mp.VioreanuRokhlinSimplexQuadrature):
            assert (quad.weights > 0).all()

        if 0:
            import matplotlib.pyplot as pt
            pt.plot(quad.nodes[0], quad.nodes[1])
            pt.show()

        print((quad_class, order, quad.exact_to))
        for comb in gnitstam(quad.exact_to, dim):
            f = Monomial(comb)
            i_f = quad(f)
            ref = f.simplex_integral()
            err = abs(i_f - ref)
            assert err < 6e-15, (err, comb, i_f, ref)

        order += 1
        if highest_order is not None and order >= highest_order:
            break
Exemple #15
0
def equidistant_nodes(dims, n, node_tuples=None):
    """
    :arg dims: dimensionality of desired simplex
        (e.g. 1, 2 or 3, for interval, triangle or tetrahedron).
    :arg n: Desired maximum total polynomial degree to interpolate.
    :arg node_tuples: a list of tuples of integers indicating the node order.
        Use default order if *None*, see
        :func:`pytools.generate_nonnegative_integer_tuples_summing_to_at_most`.
    :returns: An array of shape *(dims, nnodes)* containing unit coordinates
        of the interpolation nodes. (see :ref:`tri-coords` and :ref:`tet-coords`)
    """

    if node_tuples is None:
        from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
                as gnitstam
        node_tuples = list(gnitstam(n, dims))
    else:
        try:
            from math import comb  # comb is v3.8+
            node_count = comb(n + dims, dims)
        except ImportError:
            from functools import reduce
            from operator import mul
            node_count = reduce(mul, range(n + 1, n + dims + 1), 1) \
                    // reduce(mul, range(1, dims + 1), 1)

        if len(node_tuples) != node_count:
            raise ValueError(
                "'node_tuples' list does not have the correct length")

    # shape: (2, nnodes)
    return (np.array(node_tuples, dtype=np.float64) / n * 2 - 1).T
Exemple #16
0
def tesselatetri():
    result = []

    node_tuples = list(gnitstam(2, 2))
    node_dict = dict(
          (ituple, idx)
          for idx, ituple in enumerate(node_tuples))

    def try_add_tri(current, d1, d2, d3):
        try:
            result.append((
                node_dict[add_tuples(current, d1)],
                node_dict[add_tuples(current, d2)],
                node_dict[add_tuples(current, d3)],
                ))
        except KeyError:
            pass

    if len(result) > 0:
        return [node_tuples, result]
    for current in node_tuples:
        # this is a tesselation of a square into two triangles.
        # subtriangles that fall outside of the master tet are simply not added.

        # positively oriented
        try_add_tri(current, (0, 0), (1, 0), (0, 1))
        try_add_tri(current, (1, 0), (1, 1), (0, 1))
    return node_tuples, result
Exemple #17
0
    def _vis_connectivity(self):
        """
        :return: an array of shape
            ``(vis_discr.nelements,nsubelements,primitive_element_size)``
        """
        # Assume that we're using modepy's default node ordering.

        from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
                as gnitstam, single_valued
        vis_order = single_valued(group.order
                                  for group in self.vis_discr.groups)
        node_tuples = list(gnitstam(vis_order, self.vis_discr.dim))

        from modepy.tools import submesh
        el_connectivity = np.array(submesh(node_tuples), dtype=np.intp)

        nelements = sum(group.nelements for group in self.vis_discr.groups)
        vis_connectivity = np.empty((nelements, ) + el_connectivity.shape,
                                    dtype=np.intp)

        el_nr_base = 0
        for group in self.vis_discr.groups:
            assert len(node_tuples) == group.nunit_nodes
            vis_connectivity[el_nr_base:el_nr_base + group.nelements] = (
                np.arange(el_nr_base * group.nunit_nodes,
                          (el_nr_base + group.nelements) * group.nunit_nodes,
                          group.nunit_nodes)[:, np.newaxis, np.newaxis] +
                el_connectivity)
            el_nr_base += group.nelements

        return vis_connectivity
Exemple #18
0
def warp_and_blend_nodes_2d(n, node_tuples=None):
    try:
        alpha = _alpha_opt_2d[n-1]
    except IndexError:
        alpha = 5/3

    if node_tuples is None:
        from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
                as gnitstam
        node_tuples = list(gnitstam(n, 2))
    else:
        if len(node_tuples) != (n+1)*(n+2)//2:
            raise ValueError("node_tuples list does not have the correct length")

    # shape: (2, nnodes)
    unit_nodes = (np.array(node_tuples, dtype=np.float64)/n*2 - 1).T

    from modepy.tools import (
            unit_to_barycentric,
            barycentric_to_equilateral,
            equilateral_to_unit)
    bary = unit_to_barycentric(unit_nodes)

    return equilateral_to_unit(
        barycentric_to_equilateral(bary)
        + _2d_equilateral_shift(n, bary, alpha))
Exemple #19
0
def warp_and_blend_nodes_2d(n, node_tuples=None):
    try:
        alpha = _alpha_opt_2d[n - 1]
    except IndexError:
        alpha = 5 / 3

    if node_tuples is None:
        from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
                as gnitstam
        node_tuples = list(gnitstam(n, 2))
    else:
        if len(node_tuples) != (n + 1) * (n + 2) // 2:
            raise ValueError(
                "node_tuples list does not have the correct length")

    # shape: (2, nnodes)
    unit_nodes = (np.array(node_tuples, dtype=np.float64) / n * 2 - 1).T

    from modepy.tools import (unit_to_barycentric, barycentric_to_equilateral,
                              equilateral_to_unit)
    bary = unit_to_barycentric(unit_nodes)

    return equilateral_to_unit(
        barycentric_to_equilateral(bary) +
        _2d_equilateral_shift(n, bary, alpha))
Exemple #20
0
def simplex_onb_with_mode_ids(dims, n):
    """Return a list of orthonormal basis functions in dimension *dims* of maximal
    total degree *n*.

    :returns: a tuple ``(mode_ids, basis)``, where *basis* is a class:`tuple`
        of functions, each of  which accepts arrays of shape *(dims, npts)* and
        return the function values as an array of size *npts*.  'Scalar'
        evaluation, by passing just one vector of length *dims*, is also supported.
        *mode_ids* is a tuple of the same length as *basis*, where each entry
        is a tuple of integers describing the order of the mode along the coordinate
        axes.

    See the following publications:

    * |proriol-ref|
    * |koornwinder-ref|
    * |dubiner-ref|

    ... versionadded: 2018.1
    """
    from functools import partial
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam

    if dims == 0:

        def zerod_basis(x):
            if len(x.shape) == 1:
                return 1
            else:
                return np.ones(x.shape[1])

        return ((0, ), ), (zerod_basis, )

    elif dims == 1:
        mode_ids = tuple(range(n + 1))
        return mode_ids, tuple(partial(jacobi, 0, 0, i) for i in mode_ids)
    elif dims == 2:
        mode_ids = tuple(gnitstam(n, dims))
        return mode_ids, tuple(partial(pkdo_2d, order) for order in mode_ids)
    elif dims == 3:
        mode_ids = tuple(gnitstam(n, dims))
        return mode_ids, tuple(partial(pkdo_3d, order) for order in mode_ids)
    else:
        raise NotImplementedError("%d-dimensional bases" % dims)
Exemple #21
0
    def get_full_coefficient_identifiers(self):
        """
        Returns identifiers for every coefficient in the complete expansion.
        """
        from pytools import (
            generate_nonnegative_integer_tuples_summing_to_at_most as gnitstam)

        res = sorted(gnitstam(self.order, self.dim), key=sum)
        return res
Exemple #22
0
def vtk_lagrange_simplex_node_tuples_to_permutation(node_tuples):
    order = max([sum(i) for i in node_tuples])
    dims = len(node_tuples[0])

    node_to_index = dict(
        (node_tuple, i) for i, node_tuple in enumerate(gnitstam(order, dims)))

    assert len(node_tuples) == len(node_to_index)
    return [node_to_index[v] for v in node_tuples]
Exemple #23
0
    def get_full_coefficient_identifiers(self):
        """
        Returns identifiers for every coefficient in the complete expansion.
        """
        from pytools import (
                generate_nonnegative_integer_tuples_summing_to_at_most
                as gnitstam)

        return sorted(gnitstam(self.order, self.kernel.dim), key=sum)
Exemple #24
0
def simplex_onb_with_mode_ids(dims, n):
    """Return a list of orthonormal basis functions in dimension *dims* of maximal
    total degree *n*.

    :returns: a tuple ``(mode_ids, basis)``, where *basis* is a class:`tuple`
        of functions, each of  which accepts arrays of shape *(dims, npts)* and
        return the function values as an array of size *npts*.  'Scalar'
        evaluation, by passing just one vector of length *dims*, is also supported.
        *mode_ids* is a tuple of the same length as *basis*, where each entry
        is a tuple of integers describing the order of the mode along the coordinate
        axes.

    See the following publications:

    * |proriol-ref|
    * |koornwinder-ref|
    * |dubiner-ref|

    ... versionadded: 2018.1
    """
    from functools import partial
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam

    if dims == 0:
        def zerod_basis(x):
            if len(x.shape) == 1:
                return 1
            else:
                return np.ones(x.shape[1])

        return ((0,),), (zerod_basis,)

    elif dims == 1:
        mode_ids = tuple(range(n+1))
        return mode_ids, tuple(partial(jacobi, 0, 0, i) for i in mode_ids)
    elif dims == 2:
        mode_ids = tuple(gnitstam(n, dims))
        return mode_ids, tuple(partial(pkdo_2d, order) for order in mode_ids)
    elif dims == 3:
        mode_ids = tuple(gnitstam(n, dims))
        return mode_ids, tuple(partial(pkdo_3d, order) for order in mode_ids)
    else:
        raise NotImplementedError("%d-dimensional bases" % dims)
Exemple #25
0
def simplex_onb(dims, n):
    """Return a list of orthonormal basis functions in dimension *dims* of maximal
    total degree *n*.

    :returns: a class:`tuple` of functions, each of  which
        accepts arrays of shape *(dims, npts)*
        and return the function values as an array of size *npts*.
        'Scalar' evaluation, by passing just one vector of length *dims*,
        is also supported.

    See the following publications:

    * |proriol-ref|
    * |koornwinder-ref|
    * |dubiner-ref|

    .. versionchanged:: 2013.2

        Made return value a tuple, to make bases hashable.
    """
    from functools import partial
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam

    if dims == 0:

        def zerod_basis(x):
            if len(x.shape) == 1:
                return 1
            else:
                return np.ones(x.shape[1])

        return (zerod_basis, )

    elif dims == 1:
        return tuple(partial(jacobi, 0, 0, i) for i in range(n + 1))
    elif dims == 2:
        return tuple(partial(pkdo_2d, order) for order in gnitstam(n, dims))
    elif dims == 3:
        return tuple(partial(pkdo_3d, order) for order in gnitstam(n, dims))
    else:
        raise NotImplementedError("%d-dimensional bases" % dims)
Exemple #26
0
def estimate_lebesgue_constant(n, nodes, visualize=False):
    """Estimate the
    `Lebesgue constant
    <https://en.wikipedia.org/wiki/Lebesgue_constant_(interpolation)>`_
    of the *nodes* at polynomial order *n*.

    :arg nodes: an array of shape *(dims, nnodes)* as returned by
        :func:`modepy.warp_and_blend_nodes`.
    :arg visualize: visualize the function that gives rise to the
        returned Lebesgue constant. (2D only for now)
    :return: the Lebesgue constant, a scalar

    .. versionadded:: 2013.2
    """
    from modepy.matrices import vandermonde
    from modepy.modes import simplex_onb

    dims = len(nodes)
    basis = simplex_onb(dims, n)
    vdm = vandermonde(basis, nodes)

    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam
    huge_n = 30*n
    equi_node_tuples = list(gnitstam(huge_n, dims))
    tons_of_equi_nodes = (
            np.array(equi_node_tuples, dtype=np.float64)
            / huge_n * 2 - 1).T

    eq_vdm = vandermonde(basis, tons_of_equi_nodes)
    eq_to_out = la.solve(vdm.T, eq_vdm.T).T

    lebesgue_worst = np.sum(np.abs(eq_to_out), axis=1)
    lebesgue_constant = np.max(lebesgue_worst)

    if visualize:
        print("Lebesgue constant: %g" % lebesgue_constant)
        from modepy.tools import submesh

        import mayavi.mlab as mlab
        mlab.figure(bgcolor=(1, 1, 1))
        mlab.triangular_mesh(
                tons_of_equi_nodes[0],
                tons_of_equi_nodes[1],
                lebesgue_worst / lebesgue_constant,
                submesh(equi_node_tuples))

        x, y = np.mgrid[-1:1:20j, -1:1:20j]
        mlab.mesh(x, y, 0*x, representation="wireframe", color=(0.4, 0.4, 0.4),
                line_width=0.6)

        mlab.show()

    return lebesgue_constant
    def connectivity_for_element_group(self, grp):
        from pytools import (
                generate_nonnegative_integer_tuples_summing_to_at_most as gnitstam,
                generate_nonnegative_integer_tuples_below as gnitb)
        from meshmode.mesh import TensorProductElementGroup, SimplexElementGroup

        if isinstance(grp.mesh_el_group, SimplexElementGroup):
            node_tuples = list(gnitstam(grp.order, grp.dim))

            from modepy.tools import simplex_submesh
            el_connectivity = np.array(
                    simplex_submesh(node_tuples),
                    dtype=np.intp)

            vtk_cell_type = self.simplex_cell_types[grp.dim]

        elif isinstance(grp.mesh_el_group, TensorProductElementGroup):
            node_tuples = list(gnitb(grp.order+1, grp.dim))
            node_tuple_to_index = dict(
                    (nt, i) for i, nt in enumerate(node_tuples))

            def add_tuple(a, b):
                return tuple(ai+bi for ai, bi in zip(a, b))

            el_offsets = {
                    1: [(0,), (1,)],
                    2: [(0, 0), (1, 0), (1, 1), (0, 1)],
                    3: [
                        (0, 0, 0),
                        (1, 0, 0),
                        (1, 1, 0),
                        (0, 1, 0),
                        (0, 0, 1),
                        (1, 0, 1),
                        (1, 1, 1),
                        (0, 1, 1),
                        ]
                    }[grp.dim]

            el_connectivity = np.array([
                    [
                        node_tuple_to_index[add_tuple(origin, offset)]
                        for offset in el_offsets]
                    for origin in gnitb(grp.order, grp.dim)])

            vtk_cell_type = self.tensor_cell_types[grp.dim]

        else:
            raise NotImplementedError("visualization for element groups "
                    "of type '%s'" % type(grp.mesh_el_group).__name__)

        assert len(node_tuples) == grp.nunit_dofs
        return el_connectivity, vtk_cell_type
Exemple #28
0
    def get_full_coefficient_identifiers(self):
        """
        Returns identifiers for every coefficient in the complete expansion.
        """
        from pytools import (
            generate_nonnegative_integer_tuples_summing_to_at_most as gnitstam)

        res = sorted(gnitstam(self.order, self.dim), key=sum)

        if self.max_mi is None:
            return res

        return [
            mi for mi in res
            if all(mi[i] <= self.max_mi[i] for i in range(self.dim))
        ]
Exemple #29
0
def simplex_monomial_basis(dims, n):
    """Return a list of monomial basis functions in dimension *dims* of maximal
    total degree *n*.

    :returns: a class:`tuple` of functions, each of  which
        accepts arrays of shape *(dims, npts)*
        and return the function values as an array of size *npts*.
        'Scalar' evaluation, by passing just one vector of length *dims*,
        is also supported.

    .. versionadded:: 2016.1
    """

    from functools import partial
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam
    return tuple(partial(monomial, order) for order in gnitstam(n, dims))
Exemple #30
0
def grad_simplex_monomial_basis(dims, n):
    """Return the gradients of the functions returned by
    :func:`simplex_monomial_basis`.

    :returns: a :class:`tuple` of functions, each of  which
        accepts arrays of shape *(dims, npts)*
        and returns a :class:`tuple` of length *dims* containing
        the derivatives along each axis as an array of size *npts*.
        'Scalar' evaluation, by passing just one vector of length *dims*,
        is also supported.

    .. versionadded:: 2016.1
    """

    from functools import partial
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam
    return tuple(partial(grad_monomial, order) for order in gnitstam(n, dims))
Exemple #31
0
def grad_simplex_monomial_basis(dims, n):
    """Return the gradients of the functions returned by
    :func:`simplex_monomial_basis`.

    :returns: a :class:`tuple` of functions, each of  which
        accepts arrays of shape *(dims, npts)*
        and returns a :class:`tuple` of length *dims* containing
        the derivatives along each axis as an array of size *npts*.
        'Scalar' evaluation, by passing just one vector of length *dims*,
        is also supported.

    .. versionadded:: 2016.1
    """

    from functools import partial
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam
    return tuple(partial(grad_monomial, order) for order in gnitstam(n, dims))
Exemple #32
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
Exemple #33
0
def simplex_monomial_basis_with_mode_ids(dims, n):
    """Return a list of monomial basis functions in dimension *dims* of maximal
    total degree *n*.

    :returns: a tuple ``(mode_ids, basis)``, where *basis* is a class:`tuple`
        of functions, each of  which accepts arrays of shape *(dims, npts)* and
        return the function values as an array of size *npts*.  'Scalar'
        evaluation, by passing just one vector of length *dims*, is also supported.
        *mode_ids* is a tuple of the same length as *basis*, where each entry
        is a tuple of integers describing the order of the mode along the coordinate
        axes.

    .. versionadded:: 2018.1
    """
    from functools import partial
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam
    mode_ids = tuple(gnitstam(n, dims))
    return mode_ids, tuple(partial(monomial, order) for order in mode_ids)
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
Exemple #35
0
def simplex_monomial_basis_with_mode_ids(dims, n):
    """Return a list of monomial basis functions in dimension *dims* of maximal
    total degree *n*.

    :returns: a tuple ``(mode_ids, basis)``, where *basis* is a class:`tuple`
        of functions, each of  which accepts arrays of shape *(dims, npts)* and
        return the function values as an array of size *npts*.  'Scalar'
        evaluation, by passing just one vector of length *dims*, is also supported.
        *mode_ids* is a tuple of the same length as *basis*, where each entry
        is a tuple of integers describing the order of the mode along the coordinate
        axes.

    .. versionadded:: 2018.1
    """
    from functools import partial
    from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
            as gnitstam
    mode_ids = tuple(gnitstam(n, dims))
    return mode_ids, tuple(partial(monomial, order) for order in mode_ids)
Exemple #36
0
def equidistant_nodes(dims, n, node_tuples=None):
    """
    :arg dims: dimensionality of desired simplex
        (e.g. 1, 2 or 3, for interval, triangle or tetrahedron).
    :arg n: Desired maximum total polynomial degree to interpolate.
    :arg node_tuples: a list of tuples of integers indicating the node order.
        Use default order if *None*, see
        :func:`pytools.generate_nonnegative_integer_tuples_summing_to_at_most`.
    :returns: An array of shape *(dims, nnodes)* containing unit coordinates
        of the interpolation nodes. (see :ref:`tri-coords` and :ref:`tet-coords`)
    """

    if node_tuples is None:
        from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
                as gnitstam
        node_tuples = list(gnitstam(n, dims))
    else:
        if len(node_tuples) != (n+1)*(n+2)//2:
            raise ValueError("node_tuples list does not have the correct length")

    # shape: (2, nnodes)
    return (np.array(node_tuples, dtype=np.float64)/n*2 - 1).T
Exemple #37
0
def equidistant_nodes(dims, n, node_tuples=None):
    """
    :arg dims: dimensionality of desired simplex
        (e.g. 1, 2 or 3, for interval, triangle or tetrahedron).
    :arg n: Desired maximum total polynomial degree to interpolate.
    :arg node_tuples: a list of tuples of integers indicating the node order.
        Use default order if *None*, see
        :func:`pytools.generate_nonnegative_integer_tuples_summing_to_at_most`.
    :returns: An array of shape *(dims, nnodes)* containing unit coordinates
        of the interpolation nodes. (see :ref:`tri-coords` and :ref:`tet-coords`)
    """

    if node_tuples is None:
        from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
                as gnitstam
        node_tuples = list(gnitstam(n, dims))
    else:
        if len(node_tuples) != (n + 1) * (n + 2) // 2:
            raise ValueError(
                "node_tuples list does not have the correct length")

    # shape: (2, nnodes)
    return (np.array(node_tuples, dtype=np.float64) / n * 2 - 1).T
Exemple #38
0
from pov import Sphere, Cylinder, File, Union, Texture, Pigment, \
        Camera, LightSource, Plane, Background, Finish
import numpy as np
import modepy as mp
import six
from six.moves import range
from six.moves import zip

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):
Exemple #39
0
def as_scalar_pde(pde, vec_idx):
    r"""
    Returns a scalar PDE that is satisfied by the *vec_idx* component
    of *pde*.

    :arg pde: An instance of :class:`LinearPDESystemOperator`
    :arg vec_idx: the index of the vector-valued function that we
                  want as a scalar PDE
    """
    from sumpy.tools import nullspace

    indices = set()
    for eq in pde.eqs:
        for deriv_ident in eq.keys():
            indices.add(deriv_ident.vec_idx)

    # this is already a scalar pde
    if len(indices) == 1 and list(indices)[0] == vec_idx:
        return pde

    from pytools import ProcessLogger
    plog = ProcessLogger(logger, "computing single PDE for multiple PDEs")

    from pytools import (
            generate_nonnegative_integer_tuples_summing_to_at_most
            as gnitstam)

    dim = pde.total_dims

    # slowly increase the order of the derivatives that we take of the
    # system of PDEs. Once we reach the order of the scalar PDE, this
    # loop will break
    for order in range(2, 100):
        mis = sorted(gnitstam(order, dim), key=sum)

        pde_mat = []
        coeff_ident_enumerate_dict = dict((tuple(mi), i) for
                                            (i, mi) in enumerate(mis))
        offset = len(mis)

        # Create a matrix of equations that are derivatives of the
        # original system of PDEs
        for mi in mis:
            for pde_dict in pde.eqs:
                eq = [0]*(len(mis)*(max(indices)+1))
                for ident, coeff in pde_dict.items():
                    c = tuple(add_mi(ident.mi, mi))
                    if c not in coeff_ident_enumerate_dict:
                        break
                    idx = offset*ident.vec_idx + coeff_ident_enumerate_dict[c]
                    eq[idx] = coeff
                else:
                    pde_mat.append(eq)

        if len(pde_mat) == 0:
            continue

        # Get the nullspace of the matrix and get the rows related to this
        # vec_idx
        n = nullspace(pde_mat)[offset*vec_idx:offset*(vec_idx+1), :]
        indep_row = find_linear_relationship(n)
        if len(indep_row) > 0:
            pde_dict = {}
            mult = indep_row[max(indep_row.keys())]
            for k, v in indep_row.items():
                pde_dict[DerivativeIdentifier(mis[k], 0)] = v / mult
            plog.done()
            return LinearPDESystemOperator(pde.dim, pmap(pde_dict))

    plog.done()
    assert False
import numpy as np
from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
        as gnitstam

# prepare plot and eval nodes on triangle
dims = 2
node_n = 40
node_tuples = list(gnitstam(node_n, dims))
plot_nodes = np.array(node_tuples, dtype=np.float64) / node_n
eval_nodes = 2 * (plot_nodes - 0.5).T

# get triangle submesh
from modepy.tools import submesh
tri_subtriangles = np.array(submesh(node_tuples))

# evaluate each basis function, build global tri mesh
node_count = 0
all_nodes = []
all_triangles = []
all_values = []

from modepy.modes import simplex_onb

p = 3
stretch_factor = 1.5

for (i, j), basis_func in zip(
        gnitstam(p, dims),
        simplex_onb(dims, p),
):
Exemple #41
0
    def _vis_connectivity(self):
        """
        :return: a list of :class:`_VisConnectivityGroup` instances.
        """
        # Assume that we're using modepy's default node ordering.

        from pytools import (
            generate_nonnegative_integer_tuples_summing_to_at_most as gnitstam,
            generate_nonnegative_integer_tuples_below as gnitb)
        from meshmode.mesh import TensorProductElementGroup, SimplexElementGroup

        result = []

        from pyvisfile.vtk import (VTK_LINE, VTK_TRIANGLE, VTK_TETRA, VTK_QUAD,
                                   VTK_HEXAHEDRON)

        subel_nr_base = 0

        for group in self.vis_discr.groups:
            if isinstance(group.mesh_el_group, SimplexElementGroup):
                node_tuples = list(gnitstam(group.order, group.dim))

                from modepy.tools import submesh
                el_connectivity = np.array(submesh(node_tuples), dtype=np.intp)
                vtk_cell_type = {
                    1: VTK_LINE,
                    2: VTK_TRIANGLE,
                    3: VTK_TETRA,
                }[group.dim]

            elif isinstance(group.mesh_el_group, TensorProductElementGroup):
                node_tuples = list(gnitb(group.order + 1, group.dim))
                node_tuple_to_index = dict(
                    (nt, i) for i, nt in enumerate(node_tuples))

                def add_tuple(a, b):
                    return tuple(ai + bi for ai, bi in zip(a, b))

                el_offsets = {
                    1: [(0, ), (1, )],
                    2: [(0, 0), (1, 0), (1, 1), (0, 1)],
                    3: [
                        (0, 0, 0),
                        (1, 0, 0),
                        (1, 1, 0),
                        (0, 1, 0),
                        (0, 0, 1),
                        (1, 0, 1),
                        (1, 1, 1),
                        (0, 1, 1),
                    ]
                }[group.dim]

                el_connectivity = np.array([[
                    node_tuple_to_index[add_tuple(origin, offset)]
                    for offset in el_offsets
                ] for origin in gnitb(group.order, group.dim)])

                vtk_cell_type = {
                    1: VTK_LINE,
                    2: VTK_QUAD,
                    3: VTK_HEXAHEDRON,
                }[group.dim]

            else:
                raise NotImplementedError("visualization for element groups "
                                          "of type '%s'" %
                                          type(group.mesh_el_group).__name__)

            assert len(node_tuples) == group.nunit_nodes
            vis_connectivity = (
                group.node_nr_base +
                np.arange(0, group.nelements * group.nunit_nodes,
                          group.nunit_nodes)[:, np.newaxis, np.newaxis] +
                el_connectivity).astype(np.intp)

            vgrp = _VisConnectivityGroup(vis_connectivity=vis_connectivity,
                                         vtk_cell_type=vtk_cell_type,
                                         subelement_nr_base=subel_nr_base)
            result.append(vgrp)

            subel_nr_base += vgrp.nsubelements

        return result
Exemple #42
0
from pov import Sphere, Cylinder, File, Union, Texture, Pigment, \
        Camera, LightSource, Plane, Background, Finish
import numpy as np
import modepy as mp
import six
from six.moves import range
from six.moves import zip

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
Exemple #43
0
def warp_and_blend_nodes_3d(n, node_tuples=None):
    try:
        alpha = _alpha_opt_3d[n - 1]
    except IndexError:
        alpha = 1.

    if node_tuples is None:
        from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
                as gnitstam
        node_tuples = list(gnitstam(n, 3))
    else:
        if len(node_tuples) != (n + 1) * (n + 2) * (n + 3) // 6:
            raise ValueError(
                "node_tuples list does not have the correct length")

    # shape: (3, nnodes)
    unit_nodes = (np.array(node_tuples, dtype=np.float64) / n * 2 - 1).T

    from modepy.tools import (unit_to_barycentric, barycentric_to_equilateral,
                              equilateral_to_unit, EQUILATERAL_VERTICES)
    bary = unit_to_barycentric(unit_nodes)
    equi = barycentric_to_equilateral(bary)

    equi_vertices = EQUILATERAL_VERTICES[3]

    # total number of nodes and tolerance
    tol = 1e-8

    shift = np.zeros_like(equi)

    for i1, i2, i3, i4, vertex_step in [
        (0, 1, 2, 3, -1),
        (1, 2, 3, 0, -1),
        (2, 3, 0, 1, -1),
        (3, 0, 1, 2, -1),
    ]:

        vi2, vi3, vi4 = [(i1 + vertex_step * i) % 4 for i in range(1, 4)]

        # all vertices have the same distance from the origin
        tangent1 = equi_vertices[vi3] - equi_vertices[vi4]
        tangent1 /= la.norm(tangent1)

        tangent2 = equi_vertices[vi2] - equi_vertices[vi3]
        tangent2 -= np.dot(tangent1, tangent2) * tangent1

        tangent2 /= la.norm(tangent2)

        sub_bary = bary[[i2, i3, i4]]
        warp1, warp2 = _2d_equilateral_shift(n, sub_bary, alpha)

        l1 = bary[i1]
        l2, l3, l4 = sub_bary

        blend = l2 * l3 * l4

        denom = (l2 + 0.5 * l1) * (l3 + 0.5 * l1) * (l4 + 0.5 * l1)
        denom_ok = denom > tol

        blend[denom_ok] = ((1 + (alpha * l1[denom_ok])**2) * blend[denom_ok] /
                           denom[denom_ok])

        shift = shift + (blend * warp1)[np.newaxis, :] * tangent1[:,
                                                                  np.newaxis]
        shift = shift + (blend * warp2)[np.newaxis, :] * tangent2[:,
                                                                  np.newaxis]

        is_face = (l1 < tol) & ((l2 > tol) | (l3 > tol) | (l4 > tol))

        # assign face warp separately
        shift[:, is_face] = (
            +warp1[is_face][np.newaxis, :] * tangent1[:, np.newaxis] +
            warp2[is_face][np.newaxis, :] * tangent2[:, np.newaxis])

    return equilateral_to_unit(equi + shift)
Exemple #44
0
from __future__ import absolute_import
import numpy as np
from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
        as gnitstam
from six.moves import zip

# prepare plot and eval nodes on triangle
dims = 2
node_n = 40
node_tuples = list(gnitstam(node_n, dims))
plot_nodes = np.array(node_tuples, dtype=np.float64) / node_n
eval_nodes = 2*(plot_nodes - 0.5).T

# get triangle submesh
from modepy.tools import submesh
tri_subtriangles = np.array(submesh(node_tuples))

# evaluate each basis function, build global tri mesh
node_count = 0
all_nodes = []
all_triangles = []
all_values = []

from modepy.modes import simplex_onb

p = 3
stretch_factor = 1.5

for (i, j), basis_func in zip(
        gnitstam(p, dims),
        simplex_onb(dims, p),
Exemple #45
0
def warp_and_blend_nodes_3d(n, node_tuples=None):
    try:
        alpha = _alpha_opt_3d[n-1]
    except IndexError:
        alpha = 1.

    if node_tuples is None:
        from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \
                as gnitstam
        node_tuples = list(gnitstam(n, 3))
    else:
        if len(node_tuples) != (n+1)*(n+2)*(n+3)//6:
            raise ValueError("node_tuples list does not have the correct length")

    # shape: (3, nnodes)
    unit_nodes = (np.array(node_tuples, dtype=np.float64)/n*2 - 1).T

    from modepy.tools import (
            unit_to_barycentric,
            barycentric_to_equilateral,
            equilateral_to_unit,
            EQUILATERAL_VERTICES)
    bary = unit_to_barycentric(unit_nodes)
    equi = barycentric_to_equilateral(bary)

    equi_vertices = EQUILATERAL_VERTICES[3]

    # total number of nodes and tolerance
    tol = 1e-8

    shift = np.zeros_like(equi)

    for i1, i2, i3, i4, vertex_step in [
            (0, 1, 2, 3, -1),
            (1, 2, 3, 0, -1),
            (2, 3, 0, 1, -1),
            (3, 0, 1, 2, -1),
            ]:

        vi2, vi3, vi4 = [(i1 + vertex_step*i) % 4 for i in range(1, 4)]

        # all vertices have the same distance from the origin
        tangent1 = equi_vertices[vi3] - equi_vertices[vi4]
        tangent1 /= la.norm(tangent1)

        tangent2 = equi_vertices[vi2] - equi_vertices[vi3]
        tangent2 -= np.dot(tangent1, tangent2)*tangent1

        tangent2 /= la.norm(tangent2)

        sub_bary = bary[[i2, i3, i4]]
        warp1, warp2 = _2d_equilateral_shift(n, sub_bary, alpha)

        l1 = bary[i1]
        l2, l3, l4 = sub_bary

        blend = l2*l3*l4

        denom = (l2+0.5*l1)*(l3+0.5*l1)*(l4+0.5*l1)
        denom_ok = denom > tol

        blend[denom_ok] = (
                (1+(alpha*l1[denom_ok])**2)
                * blend[denom_ok]
                / denom[denom_ok])

        shift = shift + (blend*warp1)[np.newaxis, :] * tangent1[:, np.newaxis]
        shift = shift + (blend*warp2)[np.newaxis, :] * tangent2[:, np.newaxis]

        is_face = (l1 < tol) & ((l2 > tol) | (l3 > tol) | (l4 > tol))

        # assign face warp separately
        shift[:, is_face] = (
                + warp1[is_face][np.newaxis, :] * tangent1[:, np.newaxis]
                + warp2[is_face][np.newaxis, :] * tangent2[:, np.newaxis]
                )

    return equilateral_to_unit(equi + shift)
Exemple #46
0
    def _vis_connectivity(self):
        """
        :return: a list of :class:`_VisConnectivityGroup` instances.
        """
        # Assume that we're using modepy's default node ordering.

        from pytools import (
                generate_nonnegative_integer_tuples_summing_to_at_most as gnitstam,
                generate_nonnegative_integer_tuples_below as gnitb)
        from meshmode.mesh import TensorProductElementGroup, SimplexElementGroup

        result = []

        from pyvisfile.vtk import (
                VTK_LINE, VTK_TRIANGLE, VTK_TETRA,
                VTK_QUAD, VTK_HEXAHEDRON)

        subel_nr_base = 0

        for group in self.vis_discr.groups:
            if isinstance(group.mesh_el_group, SimplexElementGroup):
                node_tuples = list(gnitstam(group.order, group.dim))

                from modepy.tools import submesh
                el_connectivity = np.array(
                        submesh(node_tuples),
                        dtype=np.intp)
                vtk_cell_type = {
                        1: VTK_LINE,
                        2: VTK_TRIANGLE,
                        3: VTK_TETRA,
                        }[group.dim]

            elif isinstance(group.mesh_el_group, TensorProductElementGroup):
                node_tuples = list(gnitb(group.order+1, group.dim))
                node_tuple_to_index = dict(
                        (nt, i) for i, nt in enumerate(node_tuples))

                def add_tuple(a, b):
                    return tuple(ai+bi for ai, bi in zip(a, b))

                el_offsets = {
                        1: [(0,), (1,)],
                        2: [(0, 0), (1, 0), (1, 1), (0, 1)],
                        3: [
                            (0, 0, 0),
                            (1, 0, 0),
                            (1, 1, 0),
                            (0, 1, 0),
                            (0, 0, 1),
                            (1, 0, 1),
                            (1, 1, 1),
                            (0, 1, 1),
                            ]
                        }[group.dim]

                el_connectivity = np.array([
                        [
                            node_tuple_to_index[add_tuple(origin, offset)]
                            for offset in el_offsets]
                        for origin in gnitb(group.order, group.dim)])

                vtk_cell_type = {
                        1: VTK_LINE,
                        2: VTK_QUAD,
                        3: VTK_HEXAHEDRON,
                        }[group.dim]

            else:
                raise NotImplementedError("visualization for element groups "
                        "of type '%s'" % type(group.mesh_el_group).__name__)

            assert len(node_tuples) == group.nunit_nodes
            vis_connectivity = (
                    group.node_nr_base + np.arange(
                        0, group.nelements*group.nunit_nodes, group.nunit_nodes
                        )[:, np.newaxis, np.newaxis]
                    + el_connectivity).astype(np.intp)

            vgrp = _VisConnectivityGroup(
                vis_connectivity=vis_connectivity,
                vtk_cell_type=vtk_cell_type,
                subelement_nr_base=subel_nr_base)
            result.append(vgrp)

            subel_nr_base += vgrp.nsubelements

        return result