def test_target_association_failure(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) # {{{ generate circle order = 5 nelements = 40 # Make the curve mesh. curve_f = partial(ellipse, 1) 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 and check close_circle = 0.999 * np.exp( 2j * np.pi * np.linspace(0, 1, 500, endpoint=False)) from pytential.target import PointsTarget close_circle_target = (PointsTarget( actx.from_numpy(np.array([close_circle.real, close_circle.imag])))) targets = ((close_circle_target, 0), ) from pytential.qbx.target_assoc import (TargetAssociationCodeContainer, associate_targets_to_qbx_centers, QBXTargetAssociationFailedException ) from pytential.qbx.utils import TreeCodeContainer code_container = TargetAssociationCodeContainer(actx, TreeCodeContainer(actx)) with pytest.raises(QBXTargetAssociationFailedException): associate_targets_to_qbx_centers(places, places.auto_source, code_container.get_wrangler(actx), targets, target_association_tolerance=1e-10)
def test_target_association_failure(ctx_getter): cl_ctx = ctx_getter() queue = cl.CommandQueue(cl_ctx) # {{{ generate circle order = 5 nelements = 40 # Make the curve mesh. curve_f = partial(ellipse, 1) 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(cl_ctx, mesh, factory) lpot_source = QBXLayerPotentialSource(discr, qbx_order=order, # not used in target association fine_order=order) # }}} # {{{ generate targets and check close_circle = 0.999 * np.exp( 2j * np.pi * np.linspace(0, 1, 500, endpoint=False)) from pytential.target import PointsTarget close_circle_target = ( PointsTarget(cl.array.to_device( queue, np.array([close_circle.real, close_circle.imag])))) targets = ( (close_circle_target, 0), ) from pytential.qbx.target_assoc import ( TargetAssociationCodeContainer, associate_targets_to_qbx_centers, QBXTargetAssociationFailedException) from pytential.qbx.utils import TreeCodeContainer code_container = TargetAssociationCodeContainer( cl_ctx, TreeCodeContainer(cl_ctx)) with pytest.raises(QBXTargetAssociationFailedException): associate_targets_to_qbx_centers( lpot_source, code_container.get_wrangler(queue), targets, target_association_tolerance=1e-10)
def _get_centers_and_expansion_radii(queue, source, target_discr, qbx_forced_limit): """ :arg queue: a :class:`pyopencl.CommandQueue`. :arg source: a :class:`pytential.source.LayerPotentialSourceBase`. :arg target_discr: a :class:`meshmode.discretization.Discretization`. :arg qbx_forced_limit: an integer (*+1* or *-1*). :return: a tuple of `(centers, radii)` for each node in *target_discr*. """ if source.density_discr is target_discr: # NOTE: skip expensive target association centers = bind( source, sym.expansion_centers(source.ambient_dim, qbx_forced_limit))(queue) radii = bind(source, sym.expansion_radii(source.ambient_dim))(queue) else: from pytential.qbx.utils import get_interleaved_centers centers = get_interleaved_centers(queue, source) radii = bind( source, sym.expansion_radii(source.ambient_dim, granularity=sym.GRANULARITY_CENTER))(queue) # NOTE: using a very small tolerance to make sure all the stage2 # targets are associated to a center. We can't use the user provided # source.target_association_tolerance here because it will likely be # way too small. target_association_tolerance = 1.0e-1 from pytential.qbx.target_assoc import associate_targets_to_qbx_centers code_container = source.target_association_code_container assoc = associate_targets_to_qbx_centers( source, code_container.get_wrangler(queue), [(target_discr, qbx_forced_limit)], target_association_tolerance=target_association_tolerance) centers = [ cl.array.take(c, assoc.target_to_center, queue=queue) for c in centers ] radii = cl.array.take(radii, assoc.target_to_center, queue=queue) return centers, radii
def user_target_to_center(self): """Find which QBX center, if any, is to be used for each target. :attr:`target_state.NO_QBX_NEEDED` if none. :attr:`target_state.FAILED` if a center needs to be used, but none was found. See :meth:`center_to_tree_targets` for the reverse look-up table. Shape: ``[ntargets]`` of :attr:`boxtree.Tree.particle_id_dtype`, with extra values from :class:`target_state` allowed. Targets occur in user order. """ from pytential.qbx.target_assoc import associate_targets_to_qbx_centers target_info = self.target_info() from pytential.target import PointsTarget queue = self.array_context.queue target_side_prefs = ( self.target_side_preferences()[self.ncenters:].get(queue=queue)) target_discrs_and_qbx_sides = [ (PointsTarget(target_info.targets[:, self.ncenters:]), target_side_prefs.astype(np.int32)) ] target_association_wrangler = ( self.lpot_source.target_association_code_container.get_wrangler( self.array_context)) tgt_assoc_result = associate_targets_to_qbx_centers( self.places, self.source_dd, target_association_wrangler, target_discrs_and_qbx_sides, target_association_tolerance=(self.target_association_tolerance), debug=self.debug) result = cl.array.empty(queue, target_info.ntargets, tgt_assoc_result.target_to_center.dtype) result[:self.ncenters].fill(target_state.NO_QBX_NEEDED) result[self.ncenters:] = tgt_assoc_result.target_to_center return result.with_queue(None)
def user_target_to_center(self): """Find which QBX center, if any, is to be used for each target. :attr:`target_state.NO_QBX_NEEDED` if none. :attr:`target_state.FAILED` if a center needs to be used, but none was found. See :meth:`center_to_tree_targets` for the reverse look-up table. Shape: ``[ntargets]`` of :attr:`boxtree.Tree.particle_id_dtype`, with extra values from :class:`target_state` allowed. Targets occur in user order. """ from pytential.qbx.target_assoc import associate_targets_to_qbx_centers tgt_info = self.target_info() from pytential.target import PointsTarget with cl.CommandQueue(self.cl_context) as queue: target_side_prefs = (self .target_side_preferences()[self.ncenters:].get(queue=queue)) target_discrs_and_qbx_sides = [( PointsTarget(tgt_info.targets[:, self.ncenters:]), target_side_prefs.astype(np.int32))] target_association_wrangler = ( self.lpot_source.target_association_code_container .get_wrangler(queue)) tgt_assoc_result = associate_targets_to_qbx_centers( self.lpot_source, target_association_wrangler, target_discrs_and_qbx_sides, target_association_tolerance=( self.target_association_tolerance)) result = cl.array.empty(queue, tgt_info.ntargets, tgt_assoc_result.target_to_center.dtype) result[:self.ncenters].fill(target_state.NO_QBX_NEEDED) result[self.ncenters:] = tgt_assoc_result.target_to_center return result.with_queue(None)
def _get_centers_and_expansion_radii(queue, source, target_discr, qbx_forced_limit): """ :arg queue: a :class:`pyopencl.CommandQueue`. :arg source: a :class:`pytential.source.LayerPotentialSourceBase`. :arg target_discr: a :class:`meshmode.discretization.Discretization`. :arg qbx_forced_limit: an integer (*+1* or *-1*). :return: a tuple of `(centers, radii)` for each node in *target_discr*. """ if source.density_discr is target_discr: # NOTE: skip expensive target association from pytential.qbx.utils import get_centers_on_side centers = get_centers_on_side(source, qbx_forced_limit) radii = source._expansion_radii('nsources') else: from pytential.qbx.utils import get_interleaved_centers centers = get_interleaved_centers(queue, source) radii = source._expansion_radii('ncenters') # NOTE: using a very small tolerance to make sure all the stage2 # targets are associated to a center. We can't use the user provided # source.target_association_tolerance here because it will likely be # way too small. target_association_tolerance = 1.0e-1 from pytential.qbx.target_assoc import associate_targets_to_qbx_centers code_container = source.target_association_code_container assoc = associate_targets_to_qbx_centers( source, code_container.get_wrangler(queue), [(target_discr, qbx_forced_limit)], target_association_tolerance=target_association_tolerance) centers = [cl.array.take(c, assoc.target_to_center, queue=queue) for c in centers] radii = cl.array.take(radii, assoc.target_to_center, queue=queue) return centers, radii
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()
def test_target_association(ctx_getter, curve_name, curve_f, nelements, visualize=False): cl_ctx = ctx_getter() queue = cl.CommandQueue(cl_ctx) # {{{ 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(cl_ctx, mesh, factory) lpot_source, conn = QBXLayerPotentialSource(discr, qbx_order=order, # not used in target association fine_order=order).with_refinement() del discr from pytential.qbx.utils import get_interleaved_centers centers = np.array([ax.get(queue) for ax in get_interleaved_centers(queue, lpot_source)]) # }}} # {{{ generate targets from pyopencl.clrandom import PhiloxGenerator rng = PhiloxGenerator(cl_ctx, seed=RNG_SEED) nsources = lpot_source.density_discr.nnodes noise = rng.uniform(queue, nsources, dtype=np.float, a=0.01, b=1.0) tunnel_radius = \ lpot_source._close_target_tunnel_radius("nsources").with_queue(queue) def targets_from_sources(sign, dist): from pytential import sym, bind dim = 2 nodes = bind(lpot_source.density_discr, sym.nodes(dim))(queue) normals = bind(lpot_source.density_discr, sym.normal(dim))(queue) return (nodes + normals * sign * dist).as_vector(np.object) 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 (lpot_source.density_discr, -1), # On-surface targets, exterior (lpot_source.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.nnodes 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( cl_ctx, TreeCodeContainer(cl_ctx)) target_assoc = (associate_targets_to_qbx_centers( lpot_source, code_container.get_wrangler(queue), target_discrs, target_association_tolerance=1e-10) .get(queue=queue)) expansion_radii = lpot_source._expansion_radii("ncenters").get(queue) surf_targets = np.array( [axis.get(queue) for axis in lpot_source.density_discr.nodes()]) int_targets = np.array([axis.get(queue) for axis in int_targets.nodes()]) ext_targets = np.array([axis.get(queue) for axis in ext_targets.nodes()]) def visualize_curve_and_assoc(): import matplotlib.pyplot as plt from meshmode.mesh.visualization import draw_curve draw_curve(lpot_source.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()