예제 #1
0
    def mark_panels_for_refinement(self,
                                   places,
                                   dofdesc,
                                   tree,
                                   peer_lists,
                                   target_status,
                                   refine_flags,
                                   debug,
                                   wait_for=None):
        from pytential import bind, sym
        ambient_dim = places.ambient_dim

        # Round up level count--this gets included in the kernel as
        # a stack bound. Rounding avoids too many kernel versions.
        from pytools import div_ceil
        max_levels = 10 * div_ceil(tree.nlevels, 10)

        knl = self.code_container.refiner_for_failed_target_association(
            tree.dimensions, tree.coord_dtype, tree.box_id_dtype,
            peer_lists.peer_list_starts.dtype, tree.particle_id_dtype,
            max_levels)

        found_panel_to_refine = cl.array.zeros(self.queue, 1, np.int32)
        found_panel_to_refine.finish()

        # Perform a space invader query over the sources.
        source_slice = tree.user_source_ids[tree.qbx_user_source_slice]
        sources = [
            axis.with_queue(self.queue)[source_slice] for axis in tree.sources
        ]

        tunnel_radius_by_source = flatten(
            bind(places,
                 sym._close_target_tunnel_radii(ambient_dim, dofdesc=dofdesc))(
                     self.array_context))

        # see (TGTMARK) above for algorithm.

        box_to_search_dist, evt = self.code_container.space_invader_query()(
            self.queue,
            tree,
            sources,
            tunnel_radius_by_source,
            peer_lists,
            wait_for=wait_for)
        wait_for = [evt]

        evt = knl(*unwrap_args(
            tree, peer_lists, tree.box_to_qbx_source_starts,
            tree.box_to_qbx_source_lists, tree.qbx_panel_to_source_starts,
            tree.qbx_user_source_slice.start, tree.qbx_user_target_slice.start,
            tree.nqbxpanels, tree.sorted_target_ids, tunnel_radius_by_source,
            target_status, box_to_search_dist, refine_flags,
            found_panel_to_refine, *tree.sources),
                  range=slice(tree.nqbxtargets),
                  queue=self.queue,
                  wait_for=wait_for)

        if debug:
            refine_flags.finish()
            # Marked panel = 1, 0 otherwise
            marked_panel_count = cl.array.sum(refine_flags).get()
            logger.debug(
                "target association: {} panels flagged for refinement".format(
                    marked_panel_count))

        cl.wait_for_events([evt])

        return (found_panel_to_refine == 1).all().get()
예제 #2
0
def test_target_association(ctx_factory,
                            curve_name,
                            curve_f,
                            nelements,
                            visualize=False):
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    # {{{ generate lpot source

    order = 16

    # Make the curve mesh.
    mesh = make_curve_mesh(curve_f, np.linspace(0, 1, nelements + 1), order)

    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory
    factory = InterpolatoryQuadratureSimplexGroupFactory(order)
    discr = Discretization(actx, mesh, factory)

    lpot_source = QBXLayerPotentialSource(
        discr,
        qbx_order=order,  # not used in target association
        fine_order=order)
    places = GeometryCollection(lpot_source)

    # }}}

    # {{{ generate targets

    from pyopencl.clrandom import PhiloxGenerator
    rng = PhiloxGenerator(cl_ctx, seed=RNG_SEED)

    dd = places.auto_source.to_stage1()
    centers = dof_array_to_numpy(
        actx,
        bind(
            places,
            sym.interleaved_expansion_centers(lpot_source.ambient_dim,
                                              dofdesc=dd))(actx))

    density_discr = places.get_discretization(dd.geometry)

    noise = actx.to_numpy(
        rng.uniform(queue, density_discr.ndofs, dtype=np.float, a=0.01, b=1.0))

    tunnel_radius = dof_array_to_numpy(
        actx,
        bind(
            places,
            sym._close_target_tunnel_radii(lpot_source.ambient_dim,
                                           dofdesc=dd))(actx))

    def targets_from_sources(sign, dist, dim=2):
        nodes = dof_array_to_numpy(
            actx,
            bind(places, sym.nodes(dim,
                                   dofdesc=dd))(actx).as_vector(np.object))
        normals = dof_array_to_numpy(
            actx,
            bind(places, sym.normal(dim,
                                    dofdesc=dd))(actx).as_vector(np.object))
        return actx.from_numpy(nodes + normals * sign * dist)

    from pytential.target import PointsTarget
    int_targets = PointsTarget(targets_from_sources(-1, noise * tunnel_radius))
    ext_targets = PointsTarget(targets_from_sources(+1, noise * tunnel_radius))
    far_targets = PointsTarget(
        targets_from_sources(+1, FAR_TARGET_DIST_FROM_SOURCE))

    # Create target discretizations.
    target_discrs = (
        # On-surface targets, interior
        (density_discr, -1),
        # On-surface targets, exterior
        (density_discr, +1),
        # Interior close targets
        (int_targets, -2),
        # Exterior close targets
        (ext_targets, +2),
        # Far targets, should not need centers
        (far_targets, 0),
    )

    sizes = np.cumsum([discr.ndofs for discr, _ in target_discrs])

    (
        surf_int_slice,
        surf_ext_slice,
        vol_int_slice,
        vol_ext_slice,
        far_slice,
    ) = [slice(start, end) for start, end in zip(np.r_[0, sizes], sizes)]

    # }}}

    # {{{ run target associator and check

    from pytential.qbx.target_assoc import (TargetAssociationCodeContainer,
                                            associate_targets_to_qbx_centers)

    from pytential.qbx.utils import TreeCodeContainer
    code_container = TargetAssociationCodeContainer(actx,
                                                    TreeCodeContainer(actx))

    target_assoc = (associate_targets_to_qbx_centers(
        places,
        places.auto_source,
        code_container.get_wrangler(actx),
        target_discrs,
        target_association_tolerance=1e-10).get(queue=queue))

    expansion_radii = dof_array_to_numpy(
        actx,
        bind(
            places,
            sym.expansion_radii(lpot_source.ambient_dim,
                                granularity=sym.GRANULARITY_CENTER))(actx))
    from meshmode.dof_array import thaw
    surf_targets = dof_array_to_numpy(actx, thaw(actx, density_discr.nodes()))
    int_targets = actx.to_numpy(int_targets.nodes())
    ext_targets = actx.to_numpy(ext_targets.nodes())

    def visualize_curve_and_assoc():
        import matplotlib.pyplot as plt
        from meshmode.mesh.visualization import draw_curve

        draw_curve(density_discr.mesh)

        targets = int_targets
        tgt_slice = surf_int_slice

        plt.plot(centers[0], centers[1], "+", color="orange")
        ax = plt.gca()

        for tx, ty, tcenter in zip(targets[0, tgt_slice], targets[1,
                                                                  tgt_slice],
                                   target_assoc.target_to_center[tgt_slice]):
            if tcenter >= 0:
                ax.add_artist(
                    plt.Line2D(
                        (tx, centers[0, tcenter]),
                        (ty, centers[1, tcenter]),
                    ))

        ax.set_aspect("equal")
        plt.show()

    if visualize:
        visualize_curve_and_assoc()

    # Checks that the targets match with centers on the appropriate side and
    # within the allowable distance.
    def check_close_targets(centers, targets, true_side, target_to_center,
                            target_to_side_result, tgt_slice):
        targets_have_centers = (target_to_center >= 0).all()
        assert targets_have_centers

        assert (target_to_side_result == true_side).all()

        TOL = 1e-3
        dists = la.norm((targets.T - centers.T[target_to_center]), axis=1)
        assert (dists <= (1 + TOL) * expansion_radii[target_to_center]).all()

    # Center side order = -1, 1, -1, 1, ...
    target_to_center_side = 2 * (target_assoc.target_to_center % 2) - 1

    # interior surface
    check_close_targets(centers, surf_targets, -1,
                        target_assoc.target_to_center[surf_int_slice],
                        target_to_center_side[surf_int_slice], surf_int_slice)

    # exterior surface
    check_close_targets(centers, surf_targets, +1,
                        target_assoc.target_to_center[surf_ext_slice],
                        target_to_center_side[surf_ext_slice], surf_ext_slice)

    # interior volume
    check_close_targets(centers, int_targets, -1,
                        target_assoc.target_to_center[vol_int_slice],
                        target_to_center_side[vol_int_slice], vol_int_slice)

    # exterior volume
    check_close_targets(centers, ext_targets, +1,
                        target_assoc.target_to_center[vol_ext_slice],
                        target_to_center_side[vol_ext_slice], vol_ext_slice)

    # Checks that far targets are not assigned a center.
    assert (target_assoc.target_to_center[far_slice] == -1).all()
예제 #3
0
    def mark_targets(self,
                     places,
                     dofdesc,
                     tree,
                     peer_lists,
                     target_status,
                     debug,
                     wait_for=None):
        from pytential import bind, sym
        ambient_dim = places.ambient_dim

        # Round up level count--this gets included in the kernel as
        # a stack bound. Rounding avoids too many kernel versions.
        from pytools import div_ceil
        max_levels = 10 * div_ceil(tree.nlevels, 10)

        knl = self.code_container.target_marker(
            tree.dimensions, tree.coord_dtype, tree.box_id_dtype,
            peer_lists.peer_list_starts.dtype, tree.particle_id_dtype,
            max_levels)

        found_target_close_to_panel = cl.array.zeros(self.queue, 1, np.int32)
        found_target_close_to_panel.finish()

        # Perform a space invader query over the sources.
        source_slice = tree.sorted_target_ids[tree.qbx_user_source_slice]
        sources = [
            axis.with_queue(self.queue)[source_slice] for axis in tree.sources
        ]

        tunnel_radius_by_source = flatten(
            bind(places,
                 sym._close_target_tunnel_radii(ambient_dim, dofdesc=dofdesc))(
                     self.array_context))

        # Target-marking algorithm (TGTMARK):
        #
        # (1) Use a space invader query to tag each leaf box that intersects with the
        # "near-source-detection tunnel" with the distance to the closest source.
        #
        # (2) Do an area query around all targets with the radius resulting
        # from the space invader query, enumerate sources in that vicinity.
        # If a source is found whose distance to the target is less than the
        # source's tunnel radius, mark that target as pending.
        # (or below: mark the source for refinement)

        # Note that this comment is referred to below by "TGTMARK". If you
        # remove this comment or change the algorithm here, make sure that
        # the reference below is still accurate.

        # Trade off for space-invaders vs directly tagging targets in
        # endangered boxes:
        #
        # (-) More complicated
        # (-) More actual work
        # (+) Taking the point of view of the targets could potentially lead to
        # more parallelism, if you think of the targets as unbounded while the
        # sources are fixed (which sort of makes sense, given that the number
        # of targets per box is not bounded).

        box_to_search_dist, evt = self.code_container.space_invader_query()(
            self.queue,
            tree,
            sources,
            tunnel_radius_by_source,
            peer_lists,
            wait_for=wait_for)
        wait_for = [evt]

        evt = knl(*unwrap_args(tree, peer_lists, tree.box_to_qbx_source_starts,
                               tree.box_to_qbx_source_lists,
                               tree.qbx_user_source_slice.start,
                               tree.qbx_user_target_slice.start,
                               tree.sorted_target_ids, tunnel_radius_by_source,
                               box_to_search_dist, target_status,
                               found_target_close_to_panel, *tree.sources),
                  range=slice(tree.nqbxtargets),
                  queue=self.queue,
                  wait_for=wait_for)

        if debug:
            target_status.finish()
            # Marked target = 1, 0 otherwise
            marked_target_count = cl.array.sum(target_status).get()
            logger.debug(
                "target association: {}/{} targets marked close to panels".
                format(marked_target_count, tree.nqbxtargets))

        cl.wait_for_events([evt])

        return (found_target_close_to_panel == 1).all().get()