Example #1
0
def create_refined_connection(queue, discr, threshold=0.3):
    from meshmode.mesh.refinement import RefinerWithoutAdjacency
    from meshmode.discretization.connection import make_refinement_connection
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory

    flags = np.random.rand(discr.mesh.nelements) < threshold
    refiner = RefinerWithoutAdjacency(discr.mesh)
    refiner.refine(flags)

    discr_order = discr.groups[0].order
    connection = make_refinement_connection(refiner, discr,
            InterpolatoryQuadratureSimplexGroupFactory(discr_order))

    return connection
Example #2
0
def create_refined_connection(queue, discr, threshold=0.3):
    from meshmode.mesh.refinement import RefinerWithoutAdjacency
    from meshmode.discretization.connection import make_refinement_connection
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory

    flags = np.random.rand(discr.mesh.nelements) < threshold
    refiner = RefinerWithoutAdjacency(discr.mesh)
    refiner.refine(flags)

    discr_order = discr.groups[0].order
    connection = make_refinement_connection(refiner, discr,
            InterpolatoryQuadratureSimplexGroupFactory(discr_order))

    return connection
Example #3
0
def generate_icosphere(r, order, uniform_refinement_rounds=0):
    mesh = generate_icosahedron(r, order)

    if uniform_refinement_rounds:
        # These come out conforming, so we're OK to use the faster refiner.
        from meshmode.mesh.refinement import RefinerWithoutAdjacency
        refiner = RefinerWithoutAdjacency(mesh)
        for i in range(uniform_refinement_rounds):
            refiner.refine_uniformly()

        mesh = refiner.get_current_mesh()

    vertices = mesh.vertices * r / np.sqrt(np.sum(mesh.vertices**2, axis=0))
    grp, = mesh.groups
    grp = grp.copy(nodes=grp.nodes * r / np.sqrt(np.sum(grp.nodes**2, axis=0)))

    from meshmode.mesh import Mesh
    return Mesh(vertices, [grp], is_conforming=True)
Example #4
0
def refine_mesh_and_get_urchin_warper(order,
                                      m,
                                      n,
                                      est_rel_interp_tolerance,
                                      min_rad=0.2,
                                      uniform_refinement_rounds=0):
    """
    :returns: a tuple ``(refiner, warp_mesh)``, where *refiner* is
        a :class:`~meshmode.mesh.refinement.Refiner` (from which the unwarped mesh
        may be obtained), and whose
        :meth:`~meshmode.mesh.refinement.RefinerWithoutAdjacency.get_current_mesh`
        returns a locally-refined :class:`~meshmode.mesh.Mesh` of a sphere and
        *warp_mesh* is a callable taking and returning a mesh that warps the
        unwarped mesh into a smooth shape covered by a spherical harmonic of
        order *(m, n)*.
    :arg order: the polynomial order of the returned mesh
    :arg est_rel_interp_tolerance: a tolerance for the relative
        interpolation error estimates on the warped version of the mesh.

    .. versionadded: 2018.1
    """
    def sph_harm(m, n, pts):
        assert abs(m) <= n
        x, y, z = pts
        r = np.sqrt(np.sum(pts**2, axis=0))
        theta = np.arccos(z / r)
        phi = np.arctan2(y, x)

        import scipy.special as sps
        # Note: This matches the spherical harmonic
        # convention in the QBX3D paper:
        # https://arxiv.org/abs/1805.06106
        #
        # Numpy takes arguments in the order (theta, phi)
        # *and* swaps their meanings, so passing the
        # arguments swapped maintains the intended meaning.
        return sps.sph_harm(m, n, phi, theta)

    def map_coords(pts):
        r = np.sqrt(np.sum(pts**2, axis=0))

        sph = sph_harm(m, n, pts).real
        scaled = min_rad + (sph - lo) / (hi - lo)
        new_rad = scaled

        return pts * new_rad / r

    def warp_mesh(mesh, node_vertex_consistency_tolerance):
        groups = [grp.copy(nodes=map_coords(grp.nodes)) for grp in mesh.groups]

        from meshmode.mesh import Mesh
        return Mesh(
            map_coords(mesh.vertices),
            groups,
            node_vertex_consistency_tolerance=False,
            is_conforming=mesh.is_conforming,
        )

    unwarped_mesh = generate_icosphere(1, order=order)

    from meshmode.mesh.refinement import RefinerWithoutAdjacency

    # These come out conformal, so we're OK to use the faster refiner.
    refiner = RefinerWithoutAdjacency(unwarped_mesh)
    for i in range(uniform_refinement_rounds):
        refiner.refine_uniformly()

    nodes_sph = sph_harm(m, n, unwarped_mesh.groups[0].nodes).real
    lo = np.min(nodes_sph)
    hi = np.max(nodes_sph)
    del nodes_sph

    from functools import partial
    unwarped_mesh = warp_and_refine_until_resolved(
        refiner, partial(warp_mesh, node_vertex_consistency_tolerance=False),
        est_rel_interp_tolerance)

    return refiner, partial(
        warp_mesh, node_vertex_consistency_tolerance=est_rel_interp_tolerance)
Example #5
0
def _refine_qbx_stage2(lpot_source,
                       stage1_density_discr,
                       wrangler,
                       group_factory,
                       expansion_disturbance_tolerance=None,
                       force_stage2_uniform_refinement_rounds=None,
                       maxiter=None,
                       debug=None,
                       visualize=False):
    from meshmode.discretization.connection import ChainedDiscretizationConnection
    if lpot_source._disable_refinement:
        return stage1_density_discr, ChainedDiscretizationConnection(
            [], from_discr=stage1_density_discr)

    from meshmode.mesh.refinement import RefinerWithoutAdjacency
    refiner = RefinerWithoutAdjacency(stage1_density_discr.mesh)

    # TODO: Stop doing redundant checks by avoiding panels which no longer need
    # refinement.

    connections = []
    violated_criteria = []
    iter_violated_criteria = ["start"]
    niter = 0

    stage2_density_discr = stage1_density_discr
    while iter_violated_criteria:
        iter_violated_criteria = []
        niter += 1

        if niter > maxiter:
            _warn_max_iterations(violated_criteria,
                                 expansion_disturbance_tolerance)
            break

        places = _make_temporary_collection(
            lpot_source,
            stage1_density_discr=stage1_density_discr,
            stage2_density_discr=stage2_density_discr)

        # Build tree and auxiliary data.
        # FIXME: The tree should not have to be rebuilt at each iteration.
        tree = wrangler.build_tree(places,
                                   sources_list=[places.auto_source.geometry],
                                   use_stage2_discr=True)
        peer_lists = wrangler.find_peer_lists(tree)
        refine_flags = make_empty_refine_flags(wrangler.queue,
                                               stage2_density_discr)

        has_insufficient_quad_resolution = \
                wrangler.check_sufficient_source_quadrature_resolution(
                        stage2_density_discr, tree, peer_lists, refine_flags,
                        debug)
        if has_insufficient_quad_resolution:
            iter_violated_criteria.append("insufficient quadrature resolution")
            _visualize_refinement(wrangler.queue,
                                  stage2_density_discr,
                                  niter,
                                  2,
                                  "quad-resolution",
                                  refine_flags,
                                  visualize=visualize)

        if iter_violated_criteria:
            violated_criteria.append(" and ".join(iter_violated_criteria))

            conn = wrangler.refine(stage2_density_discr, refiner, refine_flags,
                                   group_factory, debug)
            stage2_density_discr = conn.to_discr
            connections.append(conn)

        del tree
        del refine_flags
        del peer_lists

    for _ in range(force_stage2_uniform_refinement_rounds):
        conn = wrangler.refine(
            stage2_density_discr, refiner,
            np.ones(stage2_density_discr.mesh.nelements, dtype=np.bool),
            group_factory, debug)
        stage2_density_discr = conn.to_discr
        connections.append(conn)

    conn = ChainedDiscretizationConnection(connections,
                                           from_discr=stage1_density_discr)

    return stage2_density_discr, conn
Example #6
0
def _refine_qbx_stage1(lpot_source,
                       density_discr,
                       wrangler,
                       group_factory,
                       kernel_length_scale=None,
                       scaled_max_curvature_threshold=None,
                       expansion_disturbance_tolerance=None,
                       maxiter=None,
                       debug=None,
                       visualize=False):
    from pytential import bind, sym
    from meshmode.discretization.connection import ChainedDiscretizationConnection
    if lpot_source._disable_refinement:
        return density_discr, ChainedDiscretizationConnection(
            [], from_discr=density_discr)

    from meshmode.mesh.refinement import RefinerWithoutAdjacency
    refiner = RefinerWithoutAdjacency(density_discr.mesh)

    # TODO: Stop doing redundant checks by avoiding panels which no longer need
    # refinement.

    connections = []
    violated_criteria = []
    iter_violated_criteria = ["start"]
    niter = 0

    actx = wrangler.array_context

    stage1_density_discr = density_discr
    while iter_violated_criteria:
        iter_violated_criteria = []
        niter += 1

        if niter > maxiter:
            _warn_max_iterations(violated_criteria,
                                 expansion_disturbance_tolerance)
            break

        refine_flags = make_empty_refine_flags(wrangler.queue,
                                               stage1_density_discr)

        if kernel_length_scale is not None:
            with ProcessLogger(
                    logger,
                    "checking kernel length scale to panel size ratio"):

                quad_resolution = bind(
                    stage1_density_discr,
                    sym._quad_resolution(
                        stage1_density_discr.ambient_dim,
                        dofdesc=sym.GRANULARITY_ELEMENT))(actx)

                violates_kernel_length_scale = \
                        wrangler.check_element_prop_threshold(
                                element_property=quad_resolution,
                                threshold=kernel_length_scale,
                                refine_flags=refine_flags, debug=debug)

                if violates_kernel_length_scale:
                    iter_violated_criteria.append("kernel length scale")
                    _visualize_refinement(actx,
                                          stage1_density_discr,
                                          niter,
                                          1,
                                          "kernel-length-scale",
                                          refine_flags,
                                          visualize=visualize)

        if scaled_max_curvature_threshold is not None:
            with ProcessLogger(logger,
                               "checking scaled max curvature threshold"):
                scaled_max_curv = bind(
                    stage1_density_discr,
                    sym.ElementwiseMax(sym._scaled_max_curvature(
                        stage1_density_discr.ambient_dim),
                                       dofdesc=sym.GRANULARITY_ELEMENT))(actx)

                violates_scaled_max_curv = \
                        wrangler.check_element_prop_threshold(
                                element_property=scaled_max_curv,
                                threshold=scaled_max_curvature_threshold,
                                refine_flags=refine_flags, debug=debug)

                if violates_scaled_max_curv:
                    iter_violated_criteria.append("curvature")
                    _visualize_refinement(wrangler.queue,
                                          stage1_density_discr,
                                          niter,
                                          1,
                                          "curvature",
                                          refine_flags,
                                          visualize=visualize)

        if not iter_violated_criteria:
            # Only start building trees once the simple length-based criteria
            # are happy.
            places = _make_temporary_collection(
                lpot_source, stage1_density_discr=stage1_density_discr)

            # Build tree and auxiliary data.
            # FIXME: The tree should not have to be rebuilt at each iteration.
            tree = wrangler.build_tree(
                places, sources_list=[places.auto_source.geometry])
            peer_lists = wrangler.find_peer_lists(tree)

            has_disturbed_expansions = \
                    wrangler.check_expansion_disks_undisturbed_by_sources(
                            stage1_density_discr, tree, peer_lists,
                            expansion_disturbance_tolerance,
                            refine_flags, debug)
            if has_disturbed_expansions:
                iter_violated_criteria.append("disturbed expansions")
                _visualize_refinement(wrangler.queue,
                                      stage1_density_discr,
                                      niter,
                                      1,
                                      "disturbed-expansions",
                                      refine_flags,
                                      visualize=visualize)

            del tree
            del peer_lists

        if iter_violated_criteria:
            violated_criteria.append(" and ".join(iter_violated_criteria))

            conn = wrangler.refine(stage1_density_discr, refiner, refine_flags,
                                   group_factory, debug)
            stage1_density_discr = conn.to_discr
            connections.append(conn)

        del refine_flags

    conn = ChainedDiscretizationConnection(connections,
                                           from_discr=density_discr)

    return stage1_density_discr, conn
Example #7
0
def refine_for_global_qbx(lpot_source,
                          wrangler,
                          group_factory,
                          kernel_length_scale=None,
                          force_stage2_uniform_refinement_rounds=None,
                          scaled_max_curvature_threshold=None,
                          debug=None,
                          maxiter=None,
                          visualize=None,
                          expansion_disturbance_tolerance=None,
                          refiner=None):
    """
    Entry point for calling the refiner.

    :arg lpot_source: An instance of :class:`QBXLayerPotentialSource`.

    :arg wrangler: An instance of :class:`RefinerWrangler`.

    :arg group_factory: An instance of
        :class:`meshmode.mesh.discretization.ElementGroupFactory`. Used for
        discretizing the coarse refined mesh.

    :arg kernel_length_scale: The kernel length scale, or *None* if not
        applicable. All panels are refined to below this size.

    :arg maxiter: The maximum number of refiner iterations.

    :returns: A tuple ``(lpot_source, *conn*)`` where ``lpot_source`` is the
        refined layer potential source, and ``conn`` is a
        :class:`meshmode.discretization.connection.DiscretizationConnection`
        going from the original mesh to the refined mesh.
    """

    if maxiter is None:
        maxiter = 10

    if debug is None:
        # FIXME: Set debug=False by default once everything works.
        debug = True

    if expansion_disturbance_tolerance is None:
        expansion_disturbance_tolerance = 0.025

    if force_stage2_uniform_refinement_rounds is None:
        force_stage2_uniform_refinement_rounds = 0

    # TODO: Stop doing redundant checks by avoiding panels which no longer need
    # refinement.

    from meshmode.mesh.refinement import RefinerWithoutAdjacency
    from meshmode.discretization.connection import (
        ChainedDiscretizationConnection, make_same_mesh_connection)

    if refiner is not None:
        assert refiner.get_current_mesh() == lpot_source.density_discr.mesh
    else:
        # We may be handed a mesh that's already non-conforming, we don't rely
        # on adjacency, and the no-adjacency refiner is faster.
        refiner = RefinerWithoutAdjacency(lpot_source.density_discr.mesh)

    connections = []

    # {{{ first stage refinement

    def visualize_refinement(niter, stage_nr, stage_name, flags):
        if not visualize:
            return

        if stage_nr == 1:
            discr = lpot_source.density_discr
        elif stage_nr == 2:
            discr = lpot_source.stage2_density_discr
        else:
            raise ValueError("unexpected stage number")

        flags = flags.get()
        logger.info("for stage %s: splitting %d/%d stage-%d elements",
                    stage_name, np.sum(flags), discr.mesh.nelements, stage_nr)

        from meshmode.discretization.visualization import make_visualizer
        vis = make_visualizer(wrangler.queue, discr, 3)

        assert len(flags) == discr.mesh.nelements

        flags = flags.astype(np.bool)
        nodes_flags = np.zeros(discr.nnodes)
        for grp in discr.groups:
            meg = grp.mesh_el_group
            grp.view(nodes_flags)[flags[meg.element_nr_base:meg.nelements +
                                        meg.element_nr_base]] = 1

        nodes_flags = cl.array.to_device(wrangler.queue, nodes_flags)
        vis_data = [
            ("refine_flags", nodes_flags),
        ]

        if 0:
            from pytential import sym, bind
            bdry_normals = bind(discr, sym.normal(discr.ambient_dim))(
                wrangler.queue).as_vector(dtype=object)
            vis_data.append(("bdry_normals", bdry_normals), )

        vis.write_vtk_file("refinement-%s-%03d.vtu" % (stage_name, niter),
                           vis_data)

    def warn_max_iterations():
        from warnings import warn
        warn(
            "QBX layer potential source refiner did not terminate "
            "after %d iterations (the maximum). "
            "You may pass 'visualize=True' to with_refinement() "
            "to see what area of the geometry is causing trouble. "
            "If the issue is disturbance of expansion disks, you may "
            "pass a slightly increased value (currently: %g) for "
            "_expansion_disturbance_tolerance in with_refinement(). "
            "As a last resort, "
            "you may use Python's warning filtering mechanism to "
            "not treat this warning as an error. "
            "The criteria triggering refinement in each iteration "
            "were: %s. " %
            (len(violated_criteria), expansion_disturbance_tolerance,
             ", ".join("%d: %s" % (i + 1, vc_text)
                       for i, vc_text in enumerate(violated_criteria))),
            RefinerNotConvergedWarning)

    violated_criteria = []
    iter_violated_criteria = ["start"]

    niter = 0

    while iter_violated_criteria:
        iter_violated_criteria = []
        niter += 1

        if niter > maxiter:
            warn_max_iterations()
            break

        refine_flags = make_empty_refine_flags(wrangler.queue, lpot_source)

        if kernel_length_scale is not None:
            with ProcessLogger(
                    logger,
                    "checking kernel length scale to panel size ratio"):

                from pytential import bind, sym
                quad_resolution = bind(
                    lpot_source,
                    sym._quad_resolution(lpot_source.ambient_dim,
                                         dofdesc=sym.GRANULARITY_ELEMENT))(
                                             wrangler.queue)

                violates_kernel_length_scale = \
                        wrangler.check_element_prop_threshold(
                                element_property=quad_resolution,
                                threshold=kernel_length_scale,
                                refine_flags=refine_flags, debug=debug)

                if violates_kernel_length_scale:
                    iter_violated_criteria.append("kernel length scale")
                    visualize_refinement(niter, 1, "kernel-length-scale",
                                         refine_flags)

        if scaled_max_curvature_threshold is not None:
            with ProcessLogger(logger,
                               "checking scaled max curvature threshold"):
                from pytential import sym, bind
                scaled_max_curv = bind(
                    lpot_source,
                    sym.ElementwiseMax(
                        sym._scaled_max_curvature(lpot_source.ambient_dim),
                        dofdesc=sym.GRANULARITY_ELEMENT))(wrangler.queue)

                violates_scaled_max_curv = \
                        wrangler.check_element_prop_threshold(
                                element_property=scaled_max_curv,
                                threshold=scaled_max_curvature_threshold,
                                refine_flags=refine_flags, debug=debug)

                if violates_scaled_max_curv:
                    iter_violated_criteria.append("curvature")
                    visualize_refinement(niter, 1, "curvature", refine_flags)

        if not iter_violated_criteria:
            # Only start building trees once the simple length-based criteria
            # are happy.

            # Build tree and auxiliary data.
            # FIXME: The tree should not have to be rebuilt at each iteration.
            tree = wrangler.build_tree(lpot_source)
            peer_lists = wrangler.find_peer_lists(tree)

            has_disturbed_expansions = \
                    wrangler.check_expansion_disks_undisturbed_by_sources(
                            lpot_source, tree, peer_lists,
                            expansion_disturbance_tolerance,
                            refine_flags, debug)
            if has_disturbed_expansions:
                iter_violated_criteria.append("disturbed expansions")
                visualize_refinement(niter, 1, "disturbed-expansions",
                                     refine_flags)

            del tree
            del peer_lists

        if iter_violated_criteria:
            violated_criteria.append(" and ".join(iter_violated_criteria))

            conn = wrangler.refine(lpot_source.density_discr, refiner,
                                   refine_flags, group_factory, debug)
            connections.append(conn)
            lpot_source = lpot_source.copy(density_discr=conn.to_discr)

        del refine_flags

    # }}}

    # {{{ second stage refinement

    iter_violated_criteria = ["start"]
    niter = 0
    fine_connections = []

    stage2_density_discr = lpot_source.density_discr

    while iter_violated_criteria:
        iter_violated_criteria = []
        niter += 1

        if niter > maxiter:
            warn_max_iterations()
            break

        # Build tree and auxiliary data.
        # FIXME: The tree should not have to be rebuilt at each iteration.
        tree = wrangler.build_tree(lpot_source, use_stage2_discr=True)
        peer_lists = wrangler.find_peer_lists(tree)
        refine_flags = make_empty_refine_flags(wrangler.queue,
                                               lpot_source,
                                               use_stage2_discr=True)

        has_insufficient_quad_res = \
                wrangler.check_sufficient_source_quadrature_resolution(
                        lpot_source, tree, peer_lists, refine_flags, debug)
        if has_insufficient_quad_res:
            iter_violated_criteria.append("insufficient quadrature resolution")
            visualize_refinement(niter, 2, "quad-resolution", refine_flags)

        if iter_violated_criteria:
            violated_criteria.append(" and ".join(iter_violated_criteria))

            conn = wrangler.refine(stage2_density_discr, refiner, refine_flags,
                                   group_factory, debug)
            stage2_density_discr = conn.to_discr
            fine_connections.append(conn)
            lpot_source = lpot_source.copy(
                to_refined_connection=ChainedDiscretizationConnection(
                    fine_connections))

        del tree
        del refine_flags
        del peer_lists

    for round in range(force_stage2_uniform_refinement_rounds):
        conn = wrangler.refine(
            stage2_density_discr, refiner,
            np.ones(stage2_density_discr.mesh.nelements, dtype=np.bool),
            group_factory, debug)
        stage2_density_discr = conn.to_discr
        fine_connections.append(conn)
        lpot_source = lpot_source.copy(
            to_refined_connection=ChainedDiscretizationConnection(
                fine_connections))

    # }}}

    lpot_source = lpot_source.copy(debug=debug, _refined_for_global_qbx=True)

    if len(connections) == 0:
        # FIXME: This is inefficient
        connection = make_same_mesh_connection(lpot_source.density_discr,
                                               lpot_source.density_discr)
    else:
        connection = ChainedDiscretizationConnection(connections)

    return lpot_source, connection
Example #8
0
def refine_mesh_and_get_urchin_warper(order, m, n, est_rel_interp_tolerance,
        min_rad=0.2, uniform_refinement_rounds=0):
    """
    :returns: a tuple ``(refiner, warp_mesh)``, where *refiner* is
        a :class:`meshmode.refinement.Refiner` (from which the unwarped mesh
        may be obtained), and whose
        :meth:`meshmode.refinement.Refiner.get_current_mesh` returns a
        locally-refined :class:`meshmode.mesh.Mesh` of a sphere and *warp_mesh*
        is a callable taking and returning a mesh that warps the unwarped mesh
        into a smooth shape govered by a spherical harmonic of order *(m, n)*.
    :arg order: the polynomial order of the returned mesh
    :arg est_rel_interp_tolerance: a tolerance for the relative
        interpolation error estimates on the warped version of the mesh.

    .. versionadded: 2018.1
    """

    def sph_harm(m, n, pts):
        assert abs(m) <= n
        x, y, z = pts
        r = np.sqrt(np.sum(pts**2, axis=0))
        theta = np.arccos(z/r)
        phi = np.arctan2(y, x)

        import scipy.special as sps
        # Note: This matches the spherical harmonic
        # convention in the QBX3D paper:
        # https://arxiv.org/abs/1805.06106
        #
        # Numpy takes arguments in the order (theta, phi)
        # *and* swaps their meanings, so passing the
        # arguments swapped maintains the intended meaning.
        return sps.sph_harm(m, n, phi, theta)

    def map_coords(pts):
        r = np.sqrt(np.sum(pts**2, axis=0))

        sph = sph_harm(m, n, pts).real
        scaled = min_rad + (sph - lo)/(hi-lo)
        new_rad = scaled

        return pts * new_rad / r

    def warp_mesh(mesh, node_vertex_consistency_tolerance):
        groups = [grp.copy(nodes=map_coords(grp.nodes)) for grp in mesh.groups]

        from meshmode.mesh import Mesh
        return Mesh(
                map_coords(mesh.vertices),
                groups,
                node_vertex_consistency_tolerance=False,
                is_conforming=mesh.is_conforming,
                )

    unwarped_mesh = generate_icosphere(1, order=order)

    from meshmode.mesh.refinement import RefinerWithoutAdjacency

    # These come out conformal, so we're OK to use the faster refiner.
    refiner = RefinerWithoutAdjacency(unwarped_mesh)
    for i in range(uniform_refinement_rounds):
        refiner.refine_uniformly()

    nodes_sph = sph_harm(m, n, unwarped_mesh.groups[0].nodes).real
    lo = np.min(nodes_sph)
    hi = np.max(nodes_sph)
    del nodes_sph

    from functools import partial
    unwarped_mesh = warp_and_refine_until_resolved(
                refiner,
                partial(warp_mesh, node_vertex_consistency_tolerance=False),
                est_rel_interp_tolerance)

    return refiner, partial(
            warp_mesh,
            node_vertex_consistency_tolerance=est_rel_interp_tolerance)