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 get_interleaved_centers(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 int_centers = bind(lpot_source, sym.expansion_centers(lpot_source.ambient_dim, -1))(queue) ext_centers = bind(lpot_source, sym.expansion_centers(lpot_source.ambient_dim, +1))(queue) from pytential.symbolic.dof_connection import CenterGranularityConnection interleaver = CenterGranularityConnection(lpot_source.density_discr) return interleaver(queue, [int_centers, ext_centers])
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 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 test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, force_direct, visualize=False): logging.basicConfig(level=logging.INFO) print("ellipse_aspect: %s, mode_nr: %d, qbx_order: %d" % (ellipse_aspect, mode_nr, qbx_order)) cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) target_order = 8 from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from pytential.qbx import QBXLayerPotentialSource from pytools.convergence import EOCRecorder s_eoc_rec = EOCRecorder() d_eoc_rec = EOCRecorder() sp_eoc_rec = EOCRecorder() if ellipse_aspect != 1: nelements_values = [60, 100, 150, 200] else: nelements_values = [30, 70] # See # # [1] G. J. Rodin and O. Steinbach, "Boundary Element Preconditioners # for Problems Defined on Slender Domains", SIAM Journal on Scientific # Computing, Vol. 24, No. 4, pg. 1450, 2003. # https://dx.doi.org/10.1137/S1064827500372067 for nelements in nelements_values: mesh = make_curve_mesh(partial(ellipse, ellipse_aspect), np.linspace(0, 1, nelements + 1), target_order) fmm_order = 12 if force_direct: fmm_order = False pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource( pre_density_discr, 4 * target_order, qbx_order, fmm_order=fmm_order, _expansions_in_tree_have_extent=True, ) places = GeometryCollection(qbx) density_discr = places.get_discretization(places.auto_source.geometry) from meshmode.dof_array import thaw, flatten nodes = thaw(actx, density_discr.nodes()) if visualize: # plot geometry, centers, normals centers = bind(places, sym.expansion_centers(qbx.ambient_dim, +1))(actx) normals = bind(places, sym.normal(qbx.ambient_dim))(actx).as_vector(object) nodes_h = np.array( [actx.to_numpy(axis) for axis in flatten(nodes)]) centers_h = np.array( [actx.to_numpy(axis) for axis in flatten(centers)]) normals_h = np.array( [actx.to_numpy(axis) for axis in flatten(normals)]) pt.plot(nodes_h[0], nodes_h[1], "x-") pt.plot(centers_h[0], centers_h[1], "o") pt.quiver(nodes_h[0], nodes_h[1], normals_h[0], normals_h[1]) pt.gca().set_aspect("equal") pt.show() angle = actx.np.arctan2(nodes[1] * ellipse_aspect, nodes[0]) ellipse_fraction = ((1 - ellipse_aspect) / (1 + ellipse_aspect))**mode_nr # (2.6) in [1] J = actx.np.sqrt( # noqa actx.np.sin(angle)**2 + (1 / ellipse_aspect)**2 * actx.np.cos(angle)**2) from sumpy.kernel import LaplaceKernel lap_knl = LaplaceKernel(2) # {{{ single layer sigma_sym = sym.var("sigma") s_sigma_op = sym.S(lap_knl, sigma_sym, qbx_forced_limit=+1) sigma = actx.np.cos(mode_nr * angle) / J s_sigma = bind(places, s_sigma_op)(actx, sigma=sigma) # SIGN BINGO! :) s_eigval = 1 / (2 * mode_nr) * (1 + (-1)**mode_nr * ellipse_fraction) # (2.12) in [1] s_sigma_ref = s_eigval * J * sigma if 0: #pt.plot(s_sigma.get(), label="result") #pt.plot(s_sigma_ref.get(), label="ref") pt.plot(actx.to_numpy(flatten(s_sigma_ref - s_sigma)), label="err") pt.legend() pt.show() h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) s_err = (norm(density_discr, s_sigma - s_sigma_ref) / norm(density_discr, s_sigma_ref)) s_eoc_rec.add_data_point(h_max, s_err) # }}} # {{{ double layer d_sigma_op = sym.D(lap_knl, sigma_sym, qbx_forced_limit="avg") sigma = actx.np.cos(mode_nr * angle) d_sigma = bind(places, d_sigma_op)(actx, sigma=sigma) # SIGN BINGO! :) d_eigval = -(-1)**mode_nr * 1 / 2 * ellipse_fraction d_sigma_ref = d_eigval * sigma if 0: pt.plot(actx.to_numpy(flatten(d_sigma)), label="result") pt.plot(actx.to_numpy(flatten(d_sigma_ref)), label="ref") pt.legend() pt.show() if ellipse_aspect == 1: d_ref_norm = norm(density_discr, sigma) else: d_ref_norm = norm(density_discr, d_sigma_ref) d_err = (norm(density_discr, d_sigma - d_sigma_ref) / d_ref_norm) d_eoc_rec.add_data_point(h_max, d_err) # }}} if ellipse_aspect == 1: # {{{ S' sp_sigma_op = sym.Sp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg") sigma = actx.np.cos(mode_nr * angle) sp_sigma = bind(places, sp_sigma_op)(actx, sigma=sigma) sp_eigval = 0 sp_sigma_ref = sp_eigval * sigma sp_err = (norm(density_discr, sp_sigma - sp_sigma_ref) / norm(density_discr, sigma)) sp_eoc_rec.add_data_point(h_max, sp_err) # }}} print("Errors for S:") print(s_eoc_rec) required_order = qbx_order + 1 assert s_eoc_rec.order_estimate() > required_order - 1.5 print("Errors for D:") print(d_eoc_rec) required_order = qbx_order assert d_eoc_rec.order_estimate() > required_order - 1.5 if ellipse_aspect == 1: print("Errors for S':") print(sp_eoc_rec) required_order = qbx_order assert sp_eoc_rec.order_estimate() > required_order - 1.5
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)
assert np.allclose(r - pxyradii[i], 0.0, atol=1.0e-14) # }}} # {{{ 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)
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 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 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 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 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)
def _flat_centers(dofdesc, qbx_forced_limit): centers = bind(bound_expr.places, sym.expansion_centers( self.ambient_dim, qbx_forced_limit, dofdesc=dofdesc), )(actx) return freeze(flatten(centers, actx, leaf_class=DOFArray), actx)
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 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