def check_expansion_disks_undisturbed_by_sources(self, stage1_density_discr, tree, peer_lists, expansion_disturbance_tolerance, refine_flags, debug, wait_for=None): # Avoid generating too many kernels. from pytools import div_ceil max_levels = MAX_LEVELS_INCREMENT * div_ceil( tree.nlevels, MAX_LEVELS_INCREMENT) knl = self.code_container.expansion_disk_undisturbed_by_sources_checker( tree.dimensions, tree.coord_dtype, tree.box_id_dtype, peer_lists.peer_list_starts.dtype, tree.particle_id_dtype, max_levels) if debug: npanels_to_refine_prev = cl.array.sum(refine_flags).get() found_panel_to_refine = cl.array.zeros(self.queue, 1, np.int32) found_panel_to_refine.finish() unwrap_args = AreaQueryElementwiseTemplate.unwrap_args from pytential import bind, sym center_danger_zone_radii = flatten( bind(stage1_density_discr, sym.expansion_radii(stage1_density_discr.ambient_dim, granularity=sym.GRANULARITY_CENTER))(self.array_context)) 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_panel_to_center_starts, tree.qbx_user_source_slice.start, tree.qbx_user_center_slice.start, tree.sorted_target_ids, center_danger_zone_radii, expansion_disturbance_tolerance, tree.nqbxpanels, refine_flags, found_panel_to_refine, *tree.sources), range=slice(tree.nqbxcenters), queue=self.queue, wait_for=wait_for) cl.wait_for_events([evt]) if debug: npanels_to_refine = cl.array.sum(refine_flags).get() if npanels_to_refine > npanels_to_refine_prev: logger.debug("refiner: found {} panel(s) to refine".format( npanels_to_refine - npanels_to_refine_prev)) return found_panel_to_refine.get()[0] == 1
def __call__(self, actx: PyOpenCLArrayContext, source_dd, indices: BlockIndexRanges, **kwargs) -> BlockProxyPoints: from pytential import bind, sym source_dd = sym.as_dofdesc(source_dd) radii = bind(self.places, sym.expansion_radii(self.ambient_dim, dofdesc=source_dd))(actx) center_int = bind( self.places, sym.expansion_centers(self.ambient_dim, -1, dofdesc=source_dd))(actx) center_ext = bind( self.places, sym.expansion_centers(self.ambient_dim, +1, dofdesc=source_dd))(actx) return super().__call__(actx, source_dd, indices, expansion_radii=flatten(radii, actx), center_int=flatten(center_int, actx, leaf_class=DOFArray), center_ext=flatten(center_ext, actx, leaf_class=DOFArray), **kwargs)
def map_int_g(self, expr): lpot_source = self.places.get_geometry(expr.source.geometry) source_discr = self.places.get_discretization(expr.source.geometry, expr.source.discr_stage) target_discr = self.places.get_discretization(expr.target.geometry, expr.target.discr_stage) if source_discr is not target_discr: raise NotImplementedError rec_density = self._blk_mapper.rec(expr.density) if is_zero(rec_density): return 0 if not np.isscalar(rec_density): raise NotImplementedError actx = self.array_context kernel = expr.kernel kernel_args = _get_layer_potential_args(self._mat_mapper, expr) from sumpy.expansion.local import LineTaylorLocalExpansion local_expn = LineTaylorLocalExpansion(kernel, lpot_source.qbx_order) from sumpy.qbx import LayerPotentialMatrixBlockGenerator mat_gen = LayerPotentialMatrixBlockGenerator(actx.context, (local_expn, )) assert abs(expr.qbx_forced_limit) > 0 from pytential import bind, sym radii = bind( self.places, sym.expansion_radii(source_discr.ambient_dim, dofdesc=expr.target))(actx) centers = bind( self.places, sym.expansion_centers(source_discr.ambient_dim, expr.qbx_forced_limit, dofdesc=expr.target))(actx) from meshmode.dof_array import flatten, thaw _, (mat, ) = mat_gen(actx.queue, targets=flatten(thaw(actx, target_discr.nodes())), sources=flatten(thaw(actx, source_discr.nodes())), centers=flatten(centers), expansion_radii=flatten(radii), index_set=self.index_set, **kernel_args) waa = bind( self.places, sym.weights_and_area_elements(source_discr.ambient_dim, dofdesc=expr.source))(actx) waa = flatten(waa) mat *= waa[self.index_set.linear_col_indices] return rec_density * actx.to_numpy(mat)
def map_int_g(self, expr): lpot_source = self.places.get_geometry(expr.source.geometry) source_discr = self.places.get_discretization(expr.source.geometry, expr.source.discr_stage) target_discr = self.places.get_discretization(expr.target.geometry, expr.target.discr_stage) rec_density = self.rec(expr.density) if is_zero(rec_density): return 0 assert isinstance(rec_density, np.ndarray) if not self.is_kind_matrix(rec_density): raise NotImplementedError("layer potentials on non-variables") actx = self.array_context kernel = expr.kernel kernel_args = _get_layer_potential_args(self, expr) from sumpy.expansion.local import LineTaylorLocalExpansion local_expn = LineTaylorLocalExpansion(kernel, lpot_source.qbx_order) from sumpy.qbx import LayerPotentialMatrixGenerator mat_gen = LayerPotentialMatrixGenerator(actx.context, (local_expn, )) assert abs(expr.qbx_forced_limit) > 0 from pytential import bind, sym radii = bind( self.places, sym.expansion_radii(source_discr.ambient_dim, dofdesc=expr.target))(actx) centers = bind( self.places, sym.expansion_centers(source_discr.ambient_dim, expr.qbx_forced_limit, dofdesc=expr.target))(actx) from meshmode.dof_array import flatten, thaw _, (mat, ) = mat_gen(actx.queue, targets=flatten(thaw(actx, target_discr.nodes())), sources=flatten(thaw(actx, source_discr.nodes())), centers=flatten(centers), expansion_radii=flatten(radii), **kernel_args) mat = actx.to_numpy(mat) waa = bind( self.places, sym.weights_and_area_elements(source_discr.ambient_dim, dofdesc=expr.source))(actx) mat[:, :] *= actx.to_numpy(flatten(waa)) mat = mat.dot(rec_density) return mat
def get_interleaved_radii(queue, lpot_source): """ Return an array of shape (dim, ncenters) in which interior centers are placed next to corresponding exterior centers. """ from pytential import bind, sym radii = bind(lpot_source, sym.expansion_radii(lpot_source.ambient_dim))(queue) from pytential.symbolic.dof_connection import CenterGranularityConnection interleaver = CenterGranularityConnection(lpot_source.density_discr) return interleaver(queue, radii)
def flat_expansion_radii(self): """Return an array of radii associated with the (interleaved) expansion centers. ``coord_t [ncenters]`` """ from pytential import bind, sym radii = bind( self.places, sym.expansion_radii(self.ambient_dim, granularity=sym.GRANULARITY_CENTER, dofdesc=self.source_dd.to_stage1()))( self.array_context) return self.array_context.freeze(flatten(radii))
def exec_compute_potential_insn_direct(self, queue, insn, bound_expr, evaluate, return_timing_data): if return_timing_data: from pytential.source import UnableToCollectTimingData from warnings import warn warn("Timing data collection not supported.", category=UnableToCollectTimingData) lpot_applier = self.get_lpot_applier(insn.kernels) p2p = None lpot_applier_on_tgt_subset = None kernel_args = {} for arg_name, arg_expr in six.iteritems(insn.kernel_arguments): kernel_args[arg_name] = evaluate(arg_expr) strengths = (evaluate(insn.density).with_queue(queue) * self.weights_and_area_elements()) from pytential import bind, sym expansion_radii = bind(self, sym.expansion_radii(self.ambient_dim))(queue) centers = { -1: bind(self, sym.expansion_centers(self.ambient_dim, -1))(queue), +1: bind(self, sym.expansion_centers(self.ambient_dim, +1))(queue) } # FIXME: Do this all at once result = [] for o in insn.outputs: target_discr = bound_expr.get_discretization(o.target_name) is_self = self.density_discr is target_discr if is_self: # QBXPreprocessor is supposed to have taken care of this assert o.qbx_forced_limit is not None assert abs(o.qbx_forced_limit) > 0 evt, output_for_each_kernel = lpot_applier( queue, target_discr.nodes(), self.quad_stage2_density_discr.nodes(), centers[o.qbx_forced_limit], [strengths], expansion_radii=expansion_radii, **kernel_args) result.append((o.name, output_for_each_kernel[o.kernel_index])) else: # no on-disk kernel caching if p2p is None: p2p = self.get_p2p(insn.kernels) if lpot_applier_on_tgt_subset is None: lpot_applier_on_tgt_subset = self.get_lpot_applier_on_tgt_subset( insn.kernels) evt, output_for_each_kernel = p2p( queue, target_discr.nodes(), self.quad_stage2_density_discr.nodes(), [strengths], **kernel_args) qbx_forced_limit = o.qbx_forced_limit if qbx_forced_limit is None: qbx_forced_limit = 0 geo_data = self.qbx_fmm_geometry_data( target_discrs_and_qbx_sides=((target_discr, qbx_forced_limit), )) # center-related info is independent of targets # First ncenters targets are the centers tgt_to_qbx_center = ( geo_data.user_target_to_center()[geo_data.ncenters:].copy( queue=queue).with_queue(queue)) qbx_tgt_numberer = self.get_qbx_target_numberer( tgt_to_qbx_center.dtype) qbx_tgt_count = cl.array.empty(queue, (), np.int32) qbx_tgt_numbers = cl.array.empty_like(tgt_to_qbx_center) qbx_tgt_numberer(tgt_to_qbx_center, qbx_tgt_numbers, qbx_tgt_count, queue=queue) qbx_tgt_count = int(qbx_tgt_count.get()) if (o.qbx_forced_limit is not None and abs(o.qbx_forced_limit) == 1 and qbx_tgt_count < target_discr.nnodes): raise RuntimeError("Did not find a matching QBX center " "for some targets") qbx_tgt_numbers = qbx_tgt_numbers[:qbx_tgt_count] qbx_center_numbers = tgt_to_qbx_center[qbx_tgt_numbers] qbx_center_numbers.finish() tgt_subset_kwargs = kernel_args.copy() for i, res_i in enumerate(output_for_each_kernel): tgt_subset_kwargs["result_%d" % i] = res_i if qbx_tgt_count: lpot_applier_on_tgt_subset( queue, targets=target_discr.nodes(), sources=self.quad_stage2_density_discr.nodes(), centers=geo_data.centers(), expansion_radii=geo_data.expansion_radii(), strengths=[strengths], qbx_tgt_numbers=qbx_tgt_numbers, qbx_center_numbers=qbx_center_numbers, **tgt_subset_kwargs) result.append((o.name, output_for_each_kernel[o.kernel_index])) timing_data = {} return result, timing_data
def run_source_refinement_test(ctx_factory, mesh, order, helmholtz_k=None, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) # {{{ initial geometry from meshmode.discretization import Discretization from meshmode.discretization.poly_element import ( InterpolatoryQuadratureSimplexGroupFactory) discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(order)) lpot_source = QBXLayerPotentialSource( discr, qbx_order=order, # not used in refinement fine_order=order) places = GeometryCollection(lpot_source) # }}} # {{{ refined geometry kernel_length_scale = 5 / helmholtz_k if helmholtz_k else None expansion_disturbance_tolerance = 0.025 from pytential.qbx.refinement import refine_geometry_collection places = refine_geometry_collection( places, kernel_length_scale=kernel_length_scale, expansion_disturbance_tolerance=expansion_disturbance_tolerance, visualize=visualize) # }}} dd = places.auto_source stage1_density_discr = places.get_discretization(dd.geometry) from meshmode.dof_array import thaw stage1_density_nodes = dof_array_to_numpy( actx, thaw(actx, stage1_density_discr.nodes())) quad_stage2_density_discr = places.get_discretization( dd.geometry, sym.QBX_SOURCE_QUAD_STAGE2) quad_stage2_density_nodes = dof_array_to_numpy( actx, thaw(actx, quad_stage2_density_discr.nodes())) int_centers = dof_array_to_numpy( actx, bind(places, sym.expansion_centers(lpot_source.ambient_dim, -1))(actx)) ext_centers = dof_array_to_numpy( actx, bind(places, sym.expansion_centers(lpot_source.ambient_dim, +1))(actx)) expansion_radii = dof_array_to_numpy( actx, bind(places, sym.expansion_radii(lpot_source.ambient_dim))(actx)) dd = dd.copy(granularity=sym.GRANULARITY_ELEMENT) source_danger_zone_radii = dof_array_to_numpy( actx, bind( places, sym._source_danger_zone_radii(lpot_source.ambient_dim, dofdesc=dd.to_stage2()))(actx)) quad_res = dof_array_to_numpy( actx, bind(places, sym._quad_resolution(lpot_source.ambient_dim, dofdesc=dd))(actx)) # {{{ check if satisfying criteria def check_disk_undisturbed_by_sources(centers_panel, sources_panel): if centers_panel.element_nr == sources_panel.element_nr: # Same panel return my_int_centers = int_centers[:, centers_panel.discr_slice] my_ext_centers = ext_centers[:, centers_panel.discr_slice] all_centers = np.append(my_int_centers, my_ext_centers, axis=-1) nodes = stage1_density_nodes[:, sources_panel.discr_slice] # =distance(centers of panel 1, panel 2) dist = (la.norm( (all_centers[..., np.newaxis] - nodes[:, np.newaxis, ...]).T, axis=-1).min()) # Criterion: # A center cannot be closer to another panel than to its originating # panel. rad = expansion_radii[centers_panel.discr_slice] assert (dist >= rad * (1-expansion_disturbance_tolerance)).all(), \ (dist, rad, centers_panel.element_nr, sources_panel.element_nr) def check_sufficient_quadrature_resolution(centers_panel, sources_panel): dz_radius = source_danger_zone_radii[sources_panel.element_nr] my_int_centers = int_centers[:, centers_panel.discr_slice] my_ext_centers = ext_centers[:, centers_panel.discr_slice] all_centers = np.append(my_int_centers, my_ext_centers, axis=-1) nodes = quad_stage2_density_nodes[:, sources_panel.discr_slice] # =distance(centers of panel 1, panel 2) dist = (la.norm( (all_centers[..., np.newaxis] - nodes[:, np.newaxis, ...]).T, axis=-1).min()) # Criterion: # The quadrature contribution from each panel is as accurate # as from the center's own source panel. assert dist >= dz_radius, \ (dist, dz_radius, centers_panel.element_nr, sources_panel.element_nr) def check_quad_res_to_helmholtz_k_ratio(panel): # Check wavenumber to panel size ratio. assert quad_res[panel.element_nr] * helmholtz_k <= 5 for i, panel_1 in enumerate(iter_elements(stage1_density_discr)): for panel_2 in iter_elements(stage1_density_discr): check_disk_undisturbed_by_sources(panel_1, panel_2) for panel_2 in iter_elements(quad_stage2_density_discr): check_sufficient_quadrature_resolution(panel_1, panel_2) if helmholtz_k is not None: check_quad_res_to_helmholtz_k_ratio(panel_1)
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 run_source_refinement_test(actx_factory, mesh, order, helmholtz_k=None, surface_name="surface", visualize=False): actx = actx_factory() # {{{ initial geometry from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureGroupFactory discr = Discretization(actx, mesh, InterpolatoryQuadratureGroupFactory(order)) lpot_source = QBXLayerPotentialSource( discr, qbx_order=order, # not used in refinement fine_order=order) places = GeometryCollection(lpot_source) logger.info("nelements: %d", discr.mesh.nelements) logger.info("ndofs: %d", discr.ndofs) # }}} # {{{ refined geometry def _visualize_quad_resolution(_places, dd, suffix): if dd.discr_stage is None: vis_discr = lpot_source.density_discr else: vis_discr = _places.get_discretization(dd.geometry, dd.discr_stage) stretch = bind(_places, sym._simplex_mapping_max_stretch_factor( _places.ambient_dim, with_elementwise_max=False), auto_where=dd)(actx) from meshmode.discretization.visualization import make_visualizer vis = make_visualizer(actx, vis_discr, order, force_equidistant=True) vis.write_vtk_file( f"global-qbx-source-refinement-{surface_name}-{order}-{suffix}.vtu", [("stretch", stretch)], overwrite=True, use_high_order=True) kernel_length_scale = 5 / helmholtz_k if helmholtz_k else None expansion_disturbance_tolerance = 0.025 from pytential.qbx.refinement import refine_geometry_collection places = refine_geometry_collection( places, kernel_length_scale=kernel_length_scale, expansion_disturbance_tolerance=expansion_disturbance_tolerance, visualize=False) if visualize: dd = places.auto_source _visualize_quad_resolution(places, dd.copy(discr_stage=None), "original") _visualize_quad_resolution(places, dd.to_stage1(), "stage1") _visualize_quad_resolution(places, dd.to_stage2(), "stage2") # }}} dd = places.auto_source ambient_dim = places.ambient_dim stage1_density_discr = places.get_discretization(dd.geometry) stage1_density_nodes = actx.to_numpy( flatten(stage1_density_discr.nodes(), actx)).reshape(ambient_dim, -1) quad_stage2_density_discr = places.get_discretization( dd.geometry, sym.QBX_SOURCE_QUAD_STAGE2) quad_stage2_density_nodes = actx.to_numpy( flatten(quad_stage2_density_discr.nodes(), actx)).reshape(ambient_dim, -1) int_centers = actx.to_numpy( flatten( bind(places, sym.expansion_centers(ambient_dim, -1))(actx), actx)).reshape(ambient_dim, -1) ext_centers = actx.to_numpy( flatten( bind(places, sym.expansion_centers(ambient_dim, +1))(actx), actx)).reshape(ambient_dim, -1) expansion_radii = actx.to_numpy( flatten(bind(places, sym.expansion_radii(ambient_dim))(actx), actx)) dd = dd.copy(granularity=sym.GRANULARITY_ELEMENT) source_danger_zone_radii = actx.to_numpy( flatten( bind( places, sym._source_danger_zone_radii(ambient_dim, dofdesc=dd.to_stage2()))(actx), actx)) quad_res = actx.to_numpy( flatten( bind(places, sym._quad_resolution(ambient_dim, dofdesc=dd))(actx), actx)) # {{{ check if satisfying criteria def check_disk_undisturbed_by_sources(centers_element, sources_element): if centers_element.index == sources_element.index: # Same element return my_int_centers = int_centers[:, centers_element.discr_slice] my_ext_centers = ext_centers[:, centers_element.discr_slice] all_centers = np.append(my_int_centers, my_ext_centers, axis=-1) nodes = stage1_density_nodes[:, sources_element.discr_slice] # =distance(centers of element 1, element 2) dist = (la.norm( (all_centers[..., np.newaxis] - nodes[:, np.newaxis, ...]).T, axis=-1).min()) # Criterion: # A center cannot be closer to another element than to its originating # element. rad = expansion_radii[centers_element.discr_slice] assert np.all( dist >= rad * (1 - expansion_disturbance_tolerance)), (dist, rad, centers_element.index, sources_element.index) def check_sufficient_quadrature_resolution(centers_element, sources_element): dz_radius = source_danger_zone_radii[sources_element.index] my_int_centers = int_centers[:, centers_element.discr_slice] my_ext_centers = ext_centers[:, centers_element.discr_slice] all_centers = np.append(my_int_centers, my_ext_centers, axis=-1) nodes = quad_stage2_density_nodes[:, sources_element.discr_slice] # =distance(centers of element 1, element 2) dist = (la.norm( (all_centers[..., np.newaxis] - nodes[:, np.newaxis, ...]).T, axis=-1).min()) # Criterion: # The quadrature contribution from each element is as accurate # as from the center's own source element. assert dist >= dz_radius, \ (dist, dz_radius, centers_element.index, sources_element.index) def check_quad_res_to_helmholtz_k_ratio(element): # Check wavenumber to element size ratio. assert quad_res[element.index] * helmholtz_k <= 5 for element_1 in iter_elements(stage1_density_discr): for element_2 in iter_elements(stage1_density_discr): check_disk_undisturbed_by_sources(element_1, element_2) for element_2 in iter_elements(quad_stage2_density_discr): check_sufficient_quadrature_resolution(element_1, element_2) if helmholtz_k is not None: check_quad_res_to_helmholtz_k_ratio(element_1)
# {{{ visualization srcindices = srcindices.get(queue) if visualize: ambient_dim = places.ambient_dim if ambient_dim == 2: import matplotlib.pyplot as pt from pytential.utils import flatten_to_numpy density_nodes = np.vstack( flatten_to_numpy(actx, density_discr.nodes())) ci = bind(places, sym.expansion_centers(ambient_dim, -1))(actx) ci = np.vstack(flatten_to_numpy(actx, ci)) ce = bind(places, sym.expansion_centers(ambient_dim, +1))(actx) ce = np.vstack(flatten_to_numpy(actx, ce)) r = bind(places, sym.expansion_radii(ambient_dim))(actx) r = flatten_to_numpy(actx, r) for i in range(srcindices.nblocks): isrc = srcindices.block_indices(i) ipxy = np.s_[pxyranges[i]:pxyranges[i + 1]] pt.figure(figsize=(10, 8)) axis = pt.gca() for j in isrc: c = pt.Circle(ci[:, j], r[j], color='k', alpha=0.1) axis.add_artist(c) c = pt.Circle(ce[:, j], r[j], color='k', alpha=0.1) axis.add_artist(c) pt.plot(density_nodes[0],
def __call__(self, actx, source_dd, indices, **kwargs): """Generate proxy points for each given range of source points in the discretization in *source_dd*. :arg actx: a :class:`~meshmode.array_context.ArrayContext`. :arg source_dd: a :class:`~pytential.symbolic.primitives.DOFDescriptor` for the discretization on which the proxy points are to be generated. :arg indices: a :class:`sumpy.tools.BlockIndexRanges`. :return: a tuple of ``(proxies, pxyranges, pxycenters, pxyranges)``, where each element is a :class:`pyopencl.array.Array`. The sizes of the arrays are as follows: ``pxycenters`` is of size ``(2, nranges)``, ``pxyradii`` is of size ``(nranges,)``, ``pxyranges`` is of size ``(nranges + 1,)`` and ``proxies`` is of size ``(2, nranges * nproxy)``. The proxy points in a range :math:`i` can be obtained by a slice ``proxies[pxyranges[i]:pxyranges[i + 1]]`` and are all at a distance ``pxyradii[i]`` from the range center ``pxycenters[i]``. """ def _affine_map(v, A, b): return np.dot(A, v) + b from pytential import bind, sym source_dd = sym.as_dofdesc(source_dd) discr = self.places.get_discretization( source_dd.geometry, source_dd.discr_stage) radii = bind(self.places, sym.expansion_radii( self.ambient_dim, dofdesc=source_dd))(actx) center_int = bind(self.places, sym.expansion_centers( self.ambient_dim, -1, dofdesc=source_dd))(actx) center_ext = bind(self.places, sym.expansion_centers( self.ambient_dim, +1, dofdesc=source_dd))(actx) from meshmode.dof_array import flatten, thaw knl = self.get_kernel() _, (centers_dev, radii_dev,) = knl(actx.queue, sources=flatten(thaw(actx, discr.nodes())), center_int=flatten(center_int), center_ext=flatten(center_ext), expansion_radii=flatten(radii), srcindices=indices.indices, srcranges=indices.ranges, **kwargs) from pytential.utils import flatten_to_numpy centers = flatten_to_numpy(actx, centers_dev) radii = flatten_to_numpy(actx, radii_dev) proxies = np.empty(indices.nblocks, dtype=np.object) for i in range(indices.nblocks): proxies[i] = _affine_map(self.ref_points, A=(radii[i] * np.eye(self.ambient_dim)), b=centers[:, i].reshape(-1, 1)) pxyranges = actx.from_numpy(np.arange( 0, proxies.shape[0] * proxies[0].shape[1] + 1, proxies[0].shape[1], dtype=indices.ranges.dtype)) proxies = make_obj_array([ actx.freeze(actx.from_numpy(np.hstack([p[idim] for p in proxies]))) for idim in range(self.ambient_dim) ]) centers = make_obj_array([ actx.freeze(centers_dev[idim]) for idim in range(self.ambient_dim) ]) assert pxyranges[-1] == proxies[0].shape[0] return proxies, actx.freeze(pxyranges), centers, actx.freeze(radii_dev)
def __call__(self, queue, indices, **kwargs): """Generate proxy points for each given range of source points in the discretization in :attr:`source`. :arg queue: a :class:`pyopencl.CommandQueue`. :arg indices: a :class:`sumpy.tools.BlockIndexRanges`. :return: a tuple of ``(proxies, pxyranges, pxycenters, pxyranges)``, where each element is a :class:`pyopencl.array.Array`. The sizes of the arrays are as follows: ``pxycenters`` is of size ``(2, nranges)``, ``pxyradii`` is of size ``(nranges,)``, ``pxyranges`` is of size ``(nranges + 1,)`` and ``proxies`` is of size ``(2, nranges * nproxy)``. The proxy points in a range :math:`i` can be obtained by a slice ``proxies[pxyranges[i]:pxyranges[i + 1]]`` and are all at a distance ``pxyradii[i]`` from the range center ``pxycenters[i]``. """ def _affine_map(v, A, b): return np.dot(A, v) + b from pytential import bind, sym radii = bind(self.source, sym.expansion_radii(self.source.ambient_dim))(queue) center_int = bind(self.source, sym.expansion_centers(self.source.ambient_dim, -1))(queue) center_ext = bind(self.source, sym.expansion_centers(self.source.ambient_dim, +1))(queue) knl = self.get_kernel() _, ( centers_dev, radii_dev, ) = knl(queue, sources=self.source.density_discr.nodes(), center_int=center_int, center_ext=center_ext, expansion_radii=radii, srcindices=indices.indices, srcranges=indices.ranges, **kwargs) centers = centers_dev.get() radii = radii_dev.get() proxies = np.empty(indices.nblocks, dtype=np.object) for i in range(indices.nblocks): proxies[i] = _affine_map(self.ref_points, A=(radii[i] * np.eye(self.ambient_dim)), b=centers[:, i].reshape(-1, 1)) pxyranges = cl.array.arange(queue, 0, proxies.shape[0] * proxies[0].shape[1] + 1, proxies[0].shape[1], dtype=indices.ranges.dtype) proxies = make_obj_array([ cl.array.to_device(queue, np.hstack([p[idim] for p in proxies])) for idim in range(self.ambient_dim) ]) centers = make_obj_array([ centers_dev[idim].with_queue(queue).copy() for idim in range(self.ambient_dim) ]) assert pxyranges[-1] == proxies[0].shape[0] return proxies, pxyranges, centers, radii_dev
def map_int_g(self, expr): lpot_source = self.places.get_geometry(expr.source.geometry) source_discr = self.places.get_discretization(expr.source.geometry, expr.source.discr_stage) target_discr = self.places.get_discretization(expr.target.geometry, expr.target.discr_stage) if source_discr is not target_discr: raise NotImplementedError result = 0 for kernel, density in zip(expr.source_kernels, expr.densities): rec_density = self._blk_mapper.rec(density) if is_zero(rec_density): continue if not np.isscalar(rec_density): raise NotImplementedError actx = self.array_context kernel_args = _get_layer_potential_args(actx, self.places, expr, context=self.context) local_expn = lpot_source.get_expansion_for_qbx_direct_eval( kernel.get_base_kernel(), (expr.target_kernel, )) from pytential.linalg import make_index_blockwise_product tgtindices, srcindices = make_index_blockwise_product( actx, self.index_set) from sumpy.qbx import LayerPotentialMatrixSubsetGenerator mat_gen = LayerPotentialMatrixSubsetGenerator( actx.context, local_expn, source_kernels=(kernel, ), target_kernels=(expr.target_kernel, )) assert abs(expr.qbx_forced_limit) > 0 from pytential import bind, sym radii = bind( self.places, sym.expansion_radii(source_discr.ambient_dim, dofdesc=expr.target))(actx) centers = bind( self.places, sym.expansion_centers(source_discr.ambient_dim, expr.qbx_forced_limit, dofdesc=expr.target))(actx) _, (mat, ) = mat_gen(actx.queue, targets=flatten(target_discr.nodes(), actx, leaf_class=DOFArray), sources=flatten(source_discr.nodes(), actx, leaf_class=DOFArray), centers=flatten(centers, actx, leaf_class=DOFArray), expansion_radii=flatten(radii, actx), tgtindices=tgtindices, srcindices=srcindices, **kernel_args) waa = flatten( bind( self.places, sym.weights_and_area_elements(source_discr.ambient_dim, dofdesc=expr.source))(actx), actx) mat *= waa[srcindices] result += actx.to_numpy(mat) * rec_density return result
def _flat_expansion_radii(dofdesc): radii = bind( bound_expr.places, sym.expansion_radii(self.ambient_dim, dofdesc=dofdesc), )(actx) return freeze(flatten(radii, actx), actx)
def plot_proxy_geometry(actx, places, indices, pxy=None, nbrindices=None, with_qbx_centers=False, suffix=None): dofdesc = places.auto_source discr = places.get_discretization(dofdesc.geometry, dofdesc.discr_stage) ambient_dim = places.ambient_dim if suffix is None: suffix = f"{ambient_dim}d" suffix = suffix.replace(".", "_") import matplotlib.pyplot as pt pt.figure(figsize=(10, 8), dpi=300) pt.plot(np.diff(indices.ranges)) pt.savefig(f"test_proxy_geometry_{suffix}_ranges") pt.clf() if ambient_dim == 2: sources = actx.to_numpy(flatten(discr.nodes(), actx)).reshape(ambient_dim, -1) if pxy is not None: proxies = np.stack(pxy.points) pxycenters = np.stack(pxy.centers) pxyranges = pxy.indices.ranges if with_qbx_centers: ci = actx.to_numpy( flatten( bind(places, sym.expansion_centers(ambient_dim, -1))(actx), actx)).reshape(ambient_dim, -1) ce = actx.to_numpy( flatten( bind(places, sym.expansion_centers(ambient_dim, +1))(actx), actx)).reshape(ambient_dim, -1) r = actx.to_numpy( flatten( bind(places, sym.expansion_radii(ambient_dim))(actx), actx)) fig = pt.figure(figsize=(10, 8), dpi=300) if indices.indices.shape[0] != discr.ndofs: pt.plot(sources[0], sources[1], "ko", ms=2.0, alpha=0.5) for i in range(indices.nblocks): isrc = indices.block_indices(i) pt.plot(sources[0, isrc], sources[1, isrc], "o", ms=2.0) if with_qbx_centers: ax = pt.gca() for j in isrc: c = pt.Circle(ci[:, j], r[j], color="k", alpha=0.1) ax.add_artist(c) c = pt.Circle(ce[:, j], r[j], color="k", alpha=0.1) ax.add_artist(c) if pxy is not None: ipxy = np.s_[pxyranges[i]:pxyranges[i + 1]] pt.plot(proxies[0, ipxy], proxies[1, ipxy], "o", ms=2.0) if nbrindices is not None: inbr = nbrindices.block_indices(i) pt.plot(sources[0, inbr], sources[1, inbr], "o", ms=2.0) pt.xlim([-2, 2]) pt.ylim([-2, 2]) pt.gca().set_aspect("equal") pt.savefig(f"test_proxy_geometry_{suffix}") pt.close(fig) elif ambient_dim == 3: from meshmode.discretization.visualization import make_visualizer marker = -42.0 * np.ones(discr.ndofs) for i in range(indices.nblocks): isrc = indices.block_indices(i) marker[isrc] = 10.0 * (i + 1.0) template_ary = thaw(discr.nodes()[0], actx) marker_dev = unflatten(template_ary, actx.from_numpy(marker), actx) vis = make_visualizer(actx, discr) vis.write_vtk_file(f"test_proxy_geometry_{suffix}.vtu", [("marker", marker_dev)], overwrite=False) if nbrindices: for i in range(indices.nblocks): isrc = indices.block_indices(i) inbr = nbrindices.block_indices(i) marker.fill(0.0) marker[indices.indices] = 0.0 marker[isrc] = -42.0 marker[inbr] = +42.0 marker_dev = unflatten(template_ary, actx.from_numpy(marker), actx) vis.write_vtk_file( f"test_proxy_geometry_{suffix}_neighbor_{i:04d}.vtu", [("marker", marker_dev)], overwrite=False) if pxy: # NOTE: this does not plot the actual proxy points, just sphere # with the same center and radius as the proxy balls from meshmode.mesh.processing import (affine_map, merge_disjoint_meshes) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from meshmode.mesh.generation import generate_sphere ref_mesh = generate_sphere(1, 4, uniform_refinement_rounds=1) pxycenters = np.stack(pxy.centers) for i in range(indices.nblocks): mesh = affine_map(ref_mesh, A=pxy.radii[i], b=pxycenters[:, i].reshape(-1)) mesh = merge_disjoint_meshes([mesh, discr.mesh]) discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(4)) vis = make_visualizer(actx, discr) filename = f"test_proxy_geometry_{suffix}_block_{i:04d}.vtu" vis.write_vtk_file(filename, [], overwrite=False) else: raise ValueError
def find_centers(self, places, dofdesc, tree, peer_lists, target_status, target_flags, target_assoc, target_association_tolerance, 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.center_finder( tree.dimensions, tree.coord_dtype, tree.box_id_dtype, peer_lists.peer_list_starts.dtype, tree.particle_id_dtype, max_levels) if debug: target_status.finish() marked_target_count = int(cl.array.sum(target_status).get()) # Perform a space invader query over the centers. center_slice = ( tree.sorted_target_ids[tree.qbx_user_center_slice].with_queue( self.queue)) centers = [ axis.with_queue(self.queue)[center_slice] for axis in tree.sources ] expansion_radii_by_center = bind( places, sym.expansion_radii(ambient_dim, granularity=sym.GRANULARITY_CENTER, dofdesc=dofdesc))(self.array_context) expansion_radii_by_center_with_tolerance = flatten( expansion_radii_by_center * (1 + target_association_tolerance)) # Idea: # # (1) Tag leaf boxes around centers with max distance to usable center. # (2) Area query from targets with those radii to find closest eligible # center in terms of relative distance. box_to_search_dist, evt = self.code_container.space_invader_query()( self.queue, tree, centers, expansion_radii_by_center_with_tolerance, peer_lists, wait_for=wait_for) wait_for = [evt] def make_target_field(fill_val, dtype=tree.coord_dtype): arr = cl.array.empty(self.queue, tree.nqbxtargets, dtype) arr.fill(fill_val) wait_for.extend(arr.events) return arr target_to_center_plus = make_target_field(-1, np.int32) target_to_center_minus = make_target_field(-1, np.int32) min_dist_to_center_plus = make_target_field(np.inf) min_dist_to_center_minus = make_target_field(np.inf) min_rel_dist_to_center_plus = make_target_field(np.inf) min_rel_dist_to_center_minus = make_target_field(np.inf) evt = knl(*unwrap_args( tree, peer_lists, tree.box_to_qbx_center_starts, tree.box_to_qbx_center_lists, tree.qbx_user_center_slice.start, tree.qbx_user_target_slice.start, tree.sorted_target_ids, expansion_radii_by_center_with_tolerance, box_to_search_dist, target_flags, target_status, target_assoc.target_to_center, target_to_center_plus, target_to_center_minus, min_dist_to_center_plus, min_dist_to_center_minus, min_rel_dist_to_center_plus, min_rel_dist_to_center_minus, *tree.sources), range=slice(tree.nqbxtargets), queue=self.queue, wait_for=wait_for) if debug: target_status.finish() # Associated target = 2, marked target = 1 ntargets_associated = (int(cl.array.sum(target_status).get()) - marked_target_count) assert ntargets_associated >= 0 logger.debug( "target association: {} targets were assigned centers".format( ntargets_associated)) cl.wait_for_events([evt])
def test_proxy_generator(ctx_factory, ndim, factor, visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) qbx = _build_qbx_discr(queue, ndim=ndim) srcindices = _build_block_index(qbx.density_discr, factor=factor) from pytential.linalg.proxy import ProxyGenerator generator = ProxyGenerator(qbx, ratio=1.1) proxies, pxyranges, pxycenters, pxyradii = generator(queue, srcindices) proxies = np.vstack([p.get() for p in proxies]) pxyranges = pxyranges.get() pxycenters = np.vstack([c.get() for c in pxycenters]) pxyradii = pxyradii.get() for i in range(srcindices.nblocks): ipxy = np.s_[pxyranges[i]:pxyranges[i + 1]] r = la.norm(proxies[:, ipxy] - pxycenters[:, i].reshape(-1, 1), axis=0) assert np.allclose(r - pxyradii[i], 0.0, atol=1.0e-14) srcindices = srcindices.get(queue) if visualize: if qbx.ambient_dim == 2: import matplotlib.pyplot as pt density_nodes = qbx.density_discr.nodes().get(queue) ci = bind(qbx, sym.expansion_centers(qbx.ambient_dim, -1))(queue) ci = np.vstack([c.get(queue) for c in ci]) ce = bind(qbx, sym.expansion_centers(qbx.ambient_dim, +1))(queue) ce = np.vstack([c.get(queue) for c in ce]) r = bind(qbx, sym.expansion_radii(qbx.ambient_dim))(queue).get() for i in range(srcindices.nblocks): isrc = srcindices.block_indices(i) ipxy = np.s_[pxyranges[i]:pxyranges[i + 1]] pt.figure(figsize=(10, 8)) axis = pt.gca() for j in isrc: c = pt.Circle(ci[:, j], r[j], color='k', alpha=0.1) axis.add_artist(c) c = pt.Circle(ce[:, j], r[j], color='k', alpha=0.1) axis.add_artist(c) pt.plot(density_nodes[0], density_nodes[1], 'ko', ms=2.0, alpha=0.5) pt.plot(density_nodes[0, srcindices.indices], density_nodes[1, srcindices.indices], 'o', ms=2.0) pt.plot(density_nodes[0, isrc], density_nodes[1, isrc], 'o', ms=2.0) pt.plot(proxies[0, ipxy], proxies[1, ipxy], 'o', ms=2.0) pt.xlim([-1.5, 1.5]) pt.ylim([-1.5, 1.5]) filename = "test_proxy_generator_{}d_{:04}.png".format(ndim, i) pt.savefig(filename, dpi=300) pt.clf() else: from meshmode.discretization.visualization import make_visualizer from meshmode.mesh.processing import ( # noqa affine_map, merge_disjoint_meshes) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from meshmode.mesh.generation import generate_icosphere ref_mesh = generate_icosphere(1, generator.nproxy) # NOTE: this does not plot the actual proxy points for i in range(srcindices.nblocks): mesh = affine_map(ref_mesh, A=(pxyradii[i] * np.eye(ndim)), b=pxycenters[:, i].reshape(-1)) mesh = merge_disjoint_meshes([mesh, qbx.density_discr.mesh]) discr = Discretization( ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(10)) vis = make_visualizer(queue, discr, 10) filename = "test_proxy_generator_{}d_{:04}.vtu".format(ndim, i) vis.write_vtk_file(filename, [])
def run_source_refinement_test(ctx_factory, mesh, order, helmholtz_k=None): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import ( InterpolatoryQuadratureSimplexGroupFactory) factory = InterpolatoryQuadratureSimplexGroupFactory(order) discr = Discretization(cl_ctx, mesh, factory) from pytential.qbx.refinement import (RefinerCodeContainer, refine_for_global_qbx) from pytential.qbx.utils import TreeCodeContainer lpot_source = QBXLayerPotentialSource( discr, qbx_order=order, # not used in refinement fine_order=order) del discr expansion_disturbance_tolerance = 0.025 refiner_extra_kwargs = { "expansion_disturbance_tolerance": expansion_disturbance_tolerance, } if helmholtz_k is not None: refiner_extra_kwargs["kernel_length_scale"] = 5 / helmholtz_k lpot_source, conn = refine_for_global_qbx( lpot_source, RefinerCodeContainer(cl_ctx, TreeCodeContainer(cl_ctx)).get_wrangler(queue), factory, **refiner_extra_kwargs) discr_nodes = lpot_source.density_discr.nodes().get(queue) fine_discr_nodes = \ lpot_source.quad_stage2_density_discr.nodes().get(queue) int_centers = bind(lpot_source, sym.expansion_centers(lpot_source.ambient_dim, -1))(queue) int_centers = np.array([axis.get(queue) for axis in int_centers]) ext_centers = bind(lpot_source, sym.expansion_centers(lpot_source.ambient_dim, +1))(queue) ext_centers = np.array([axis.get(queue) for axis in ext_centers]) expansion_radii = bind(lpot_source, sym.expansion_radii( lpot_source.ambient_dim))(queue).get() source_danger_zone_radii = bind( lpot_source, sym._source_danger_zone_radii( lpot_source.ambient_dim, dofdesc=sym.GRANULARITY_ELEMENT))(queue).get() quad_res = bind( lpot_source, sym._quad_resolution(lpot_source.ambient_dim, dofdesc=sym.GRANULARITY_ELEMENT))(queue) # {{{ check if satisfying criteria def check_disk_undisturbed_by_sources(centers_panel, sources_panel): if centers_panel.element_nr == sources_panel.element_nr: # Same panel return my_int_centers = int_centers[:, centers_panel.discr_slice] my_ext_centers = ext_centers[:, centers_panel.discr_slice] all_centers = np.append(my_int_centers, my_ext_centers, axis=-1) nodes = discr_nodes[:, sources_panel.discr_slice] # =distance(centers of panel 1, panel 2) dist = (la.norm( (all_centers[..., np.newaxis] - nodes[:, np.newaxis, ...]).T, axis=-1).min()) # Criterion: # A center cannot be closer to another panel than to its originating # panel. rad = expansion_radii[centers_panel.discr_slice] assert (dist >= rad * (1-expansion_disturbance_tolerance)).all(), \ (dist, rad, centers_panel.element_nr, sources_panel.element_nr) def check_sufficient_quadrature_resolution(centers_panel, sources_panel): dz_radius = source_danger_zone_radii[sources_panel.element_nr] my_int_centers = int_centers[:, centers_panel.discr_slice] my_ext_centers = ext_centers[:, centers_panel.discr_slice] all_centers = np.append(my_int_centers, my_ext_centers, axis=-1) nodes = fine_discr_nodes[:, sources_panel.discr_slice] # =distance(centers of panel 1, panel 2) dist = (la.norm( (all_centers[..., np.newaxis] - nodes[:, np.newaxis, ...]).T, axis=-1).min()) # Criterion: # The quadrature contribution from each panel is as accurate # as from the center's own source panel. assert dist >= dz_radius, \ (dist, dz_radius, centers_panel.element_nr, sources_panel.element_nr) def check_quad_res_to_helmholtz_k_ratio(panel): # Check wavenumber to panel size ratio. assert quad_res[panel.element_nr] * helmholtz_k <= 5 for i, panel_1 in enumerate(iter_elements(lpot_source.density_discr)): for panel_2 in iter_elements(lpot_source.density_discr): check_disk_undisturbed_by_sources(panel_1, panel_2) for panel_2 in iter_elements(lpot_source.quad_stage2_density_discr): check_sufficient_quadrature_resolution(panel_1, panel_2) if helmholtz_k is not None: check_quad_res_to_helmholtz_k_ratio(panel_1)
def exec_compute_potential_insn_direct(self, actx, insn, bound_expr, evaluate, return_timing_data): from pytential import bind, sym if return_timing_data: from pytential.source import UnableToCollectTimingData from warnings import warn warn("Timing data collection not supported.", category=UnableToCollectTimingData) lpot_applier = self.get_lpot_applier(insn.target_kernels, insn.source_kernels) p2p = None lpot_applier_on_tgt_subset = None from pytential.utils import flatten_if_needed kernel_args = {} for arg_name, arg_expr in insn.kernel_arguments.items(): kernel_args[arg_name] = flatten_if_needed(actx, evaluate(arg_expr)) waa = bind( bound_expr.places, sym.weights_and_area_elements(self.ambient_dim, dofdesc=insn.source))(actx) strength_vecs = [waa * evaluate(density) for density in insn.densities] from meshmode.discretization import Discretization flat_strengths = [flatten(strength) for strength in strength_vecs] source_discr = bound_expr.places.get_discretization( insn.source.geometry, insn.source.discr_stage) # FIXME: Do this all at once results = [] for o in insn.outputs: source_dd = insn.source.copy(discr_stage=o.target_name.discr_stage) target_discr = bound_expr.places.get_discretization( o.target_name.geometry, o.target_name.discr_stage) density_discr = bound_expr.places.get_discretization( source_dd.geometry, source_dd.discr_stage) is_self = density_discr is target_discr if is_self: # QBXPreprocessor is supposed to have taken care of this assert o.qbx_forced_limit is not None assert abs(o.qbx_forced_limit) > 0 expansion_radii = bind( bound_expr.places, sym.expansion_radii(self.ambient_dim, dofdesc=o.target_name))(actx) centers = bind( bound_expr.places, sym.expansion_centers(self.ambient_dim, o.qbx_forced_limit, dofdesc=o.target_name))(actx) evt, output_for_each_kernel = lpot_applier( actx.queue, flatten(thaw(actx, target_discr.nodes())), flatten(thaw(actx, source_discr.nodes())), flatten(centers), flat_strengths, expansion_radii=flatten(expansion_radii), **kernel_args) result = output_for_each_kernel[o.target_kernel_index] if isinstance(target_discr, Discretization): result = unflatten(actx, target_discr, result) results.append((o.name, result)) else: # no on-disk kernel caching if p2p is None: p2p = self.get_p2p(actx, insn.target_kernels, insn.source_kernels) if lpot_applier_on_tgt_subset is None: lpot_applier_on_tgt_subset = self.get_lpot_applier_on_tgt_subset( insn.target_kernels, insn.source_kernels) queue = actx.queue flat_targets = flatten_if_needed(actx, target_discr.nodes()) flat_sources = flatten(thaw(actx, source_discr.nodes())) evt, output_for_each_kernel = p2p(queue, flat_targets, flat_sources, flat_strengths, **kernel_args) qbx_forced_limit = o.qbx_forced_limit if qbx_forced_limit is None: qbx_forced_limit = 0 target_discrs_and_qbx_sides = ((target_discr, qbx_forced_limit), ) geo_data = self.qbx_fmm_geometry_data( bound_expr.places, insn.source.geometry, target_discrs_and_qbx_sides=target_discrs_and_qbx_sides) # center-related info is independent of targets # First ncenters targets are the centers tgt_to_qbx_center = ( geo_data.user_target_to_center()[geo_data.ncenters:].copy( queue=queue).with_queue(queue)) qbx_tgt_numberer = self.get_qbx_target_numberer( tgt_to_qbx_center.dtype) qbx_tgt_count = cl.array.empty(queue, (), np.int32) qbx_tgt_numbers = cl.array.empty_like(tgt_to_qbx_center) qbx_tgt_numberer(tgt_to_qbx_center, qbx_tgt_numbers, qbx_tgt_count, queue=queue) qbx_tgt_count = int(qbx_tgt_count.get()) if (o.qbx_forced_limit is not None and abs(o.qbx_forced_limit) == 1 and qbx_tgt_count < target_discr.ndofs): raise RuntimeError("Did not find a matching QBX center " "for some targets") qbx_tgt_numbers = qbx_tgt_numbers[:qbx_tgt_count] qbx_center_numbers = tgt_to_qbx_center[qbx_tgt_numbers] qbx_center_numbers.finish() tgt_subset_kwargs = kernel_args.copy() for i, res_i in enumerate(output_for_each_kernel): tgt_subset_kwargs[f"result_{i}"] = res_i if qbx_tgt_count: lpot_applier_on_tgt_subset( queue, targets=flat_targets, sources=flat_sources, centers=geo_data.flat_centers(), expansion_radii=geo_data.flat_expansion_radii(), strengths=flat_strengths, qbx_tgt_numbers=qbx_tgt_numbers, qbx_center_numbers=qbx_center_numbers, **tgt_subset_kwargs) result = output_for_each_kernel[o.target_kernel_index] if isinstance(target_discr, Discretization): result = unflatten(actx, target_discr, result) results.append((o.name, result)) timing_data = {} return results, timing_data
def map_int_g(self, expr): lpot_source = self.places.get_geometry(expr.source.geometry) source_discr = self.places.get_discretization(expr.source.geometry, expr.source.discr_stage) target_discr = self.places.get_discretization(expr.target.geometry, expr.target.discr_stage) result = 0 for kernel, density in zip(expr.source_kernels, expr.densities): rec_density = self.rec(density) if is_zero(rec_density): continue assert isinstance(rec_density, np.ndarray) if not self.is_kind_matrix(rec_density): raise NotImplementedError("layer potentials on non-variables") actx = self.array_context kernel_args = _get_layer_potential_args(actx, self.places, expr, context=self.context) local_expn = lpot_source.get_expansion_for_qbx_direct_eval( kernel.get_base_kernel(), (expr.target_kernel, )) from sumpy.qbx import LayerPotentialMatrixGenerator mat_gen = LayerPotentialMatrixGenerator( actx.context, expansion=local_expn, source_kernels=(kernel, ), target_kernels=(expr.target_kernel, )) assert abs(expr.qbx_forced_limit) > 0 from pytential import bind, sym radii = bind( self.places, sym.expansion_radii(source_discr.ambient_dim, dofdesc=expr.target))(actx) centers = bind( self.places, sym.expansion_centers(source_discr.ambient_dim, expr.qbx_forced_limit, dofdesc=expr.target))(actx) _, (mat, ) = mat_gen(actx.queue, targets=flatten(target_discr.nodes(), actx, leaf_class=DOFArray), sources=flatten(source_discr.nodes(), actx, leaf_class=DOFArray), centers=flatten(centers, actx, leaf_class=DOFArray), expansion_radii=flatten(radii, actx), **kernel_args) mat = actx.to_numpy(mat) waa = bind( self.places, sym.weights_and_area_elements(source_discr.ambient_dim, dofdesc=expr.source))(actx) mat[:, :] *= actx.to_numpy(flatten(waa, actx)) result += mat @ rec_density return result