Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
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])
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
    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
Exemplo n.º 5
0
    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
Exemplo n.º 7
0
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)
Exemplo n.º 8
0
        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)
Exemplo n.º 9
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)
Exemplo n.º 10
0
    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
Exemplo n.º 11
0
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
Exemplo n.º 12
0
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, [])
Exemplo n.º 13
0
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)
Exemplo n.º 14
0
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)
Exemplo n.º 15
0
 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)
Exemplo n.º 16
0
    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
Exemplo n.º 17
0
    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
Exemplo n.º 18
0
    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