From 94b286132982e165652696b226c2309344ef992c Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sun, 25 Jun 2017 03:04:25 -0500 Subject: [PATCH 1/2] Remove redudancies from code containers (closes #58 on gitlab). --- pytential/qbx/__init__.py | 13 ++++++++--- pytential/qbx/geometry.py | 17 +++++++------- pytential/qbx/refinement.py | 18 +++++---------- pytential/qbx/target_assoc.py | 18 +++++---------- pytential/qbx/utils.py | 42 ++++++++++++++++++++++++++++++++--- 5 files changed, 67 insertions(+), 41 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 16814fec..287189f5 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -273,17 +273,24 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): return conn + @property + @memoize_method + def tree_code_container(self): + from pytential.qbx.utils import TreeCodeContainer + return TreeCodeContainer(self.cl_context) + @property @memoize_method def refiner_code_container(self): from pytential.qbx.refinement import RefinerCodeContainer - return RefinerCodeContainer(self.cl_context) + return RefinerCodeContainer(self.cl_context, self.tree_code_container) @property @memoize_method def target_association_code_container(self): from pytential.qbx.target_assoc import TargetAssociationCodeContainer - return TargetAssociationCodeContainer(self.cl_context) + return TargetAssociationCodeContainer( + self.cl_context, self.tree_code_container) @memoize_method def with_refinement(self, target_order=None, kernel_length_scale=None, @@ -436,7 +443,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): def qbx_fmm_code_getter(self): from pytential.qbx.geometry import QBXFMMGeometryCodeGetter return QBXFMMGeometryCodeGetter(self.cl_context, self.ambient_dim, - debug=self.debug) + self.tree_code_container, debug=self.debug) # {{{ fmm-based execution diff --git a/pytential/qbx/geometry.py b/pytential/qbx/geometry.py index b08370df..4af69019 100644 --- a/pytential/qbx/geometry.py +++ b/pytential/qbx/geometry.py @@ -33,6 +33,9 @@ import loopy as lp from cgen import Enum +from pytential.qbx.utils import TreeCodeContainerMixin + + import logging logger = logging.getLogger(__name__) @@ -112,11 +115,13 @@ class target_state(Enum): # noqa FAILED = -2 -class QBXFMMGeometryCodeGetter(object): - def __init__(self, cl_context, ambient_dim, debug): +class QBXFMMGeometryCodeGetter(TreeCodeContainerMixin): + + def __init__(self, cl_context, ambient_dim, tree_code_container, debug): self.cl_context = cl_context self.ambient_dim = ambient_dim self.debug = debug + self.tree_code_container = tree_code_container @memoize_method def copy_targets_kernel(self): @@ -137,12 +142,6 @@ class QBXFMMGeometryCodeGetter(object): knl = lp.tag_array_axes(knl, "targets", "stride:auto, stride:1") return lp.tag_inames(knl, dict(dim="ilp")) - @property - @memoize_method - def build_tree(self): - from boxtree import TreeBuilder - return TreeBuilder(self.cl_context) - @property @memoize_method def build_traversal(self): @@ -513,7 +512,7 @@ class QBXFMMGeometryData(object): # # FIXME: Should investigate this further. - tree, _ = code_getter.build_tree(queue, + tree, _ = code_getter.build_tree()(queue, particles=lpot_src.fine_density_discr.nodes(), targets=target_info.targets, target_radii=target_radii, diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index 2ab598d8..a9db5fda 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -35,7 +35,8 @@ from pytools import memoize_method from boxtree.area_query import AreaQueryElementwiseTemplate from boxtree.tools import InlineBinarySearch from pytential.qbx.utils import ( - QBX_TREE_C_PREAMBLE, QBX_TREE_MAKO_DEFS, TreeWranglerBase) + QBX_TREE_C_PREAMBLE, QBX_TREE_MAKO_DEFS, TreeWranglerBase, + TreeCodeContainerMixin) import logging logger = logging.getLogger(__name__) @@ -200,10 +201,11 @@ SUFFICIENT_SOURCE_QUADRATURE_RESOLUTION_CHECKER = AreaQueryElementwiseTemplate( # {{{ code container -class RefinerCodeContainer(object): +class RefinerCodeContainer(TreeCodeContainerMixin): - def __init__(self, cl_context): + def __init__(self, cl_context, tree_code_container): self.cl_context = cl_context + self.tree_code_container = tree_code_container @memoize_method def expansion_disk_undisturbed_by_sources_checker( @@ -244,16 +246,6 @@ class RefinerCodeContainer(object): knl = lp.split_iname(knl, "panel", 128, inner_tag="l.0", outer_tag="g.0") return knl - @memoize_method - def tree_builder(self): - from boxtree.tree_build import TreeBuilder - return TreeBuilder(self.cl_context) - - @memoize_method - def peer_list_finder(self): - from boxtree.area_query import PeerListFinder - return PeerListFinder(self.cl_context) - def get_wrangler(self, queue): """ :arg queue: diff --git a/pytential/qbx/target_assoc.py b/pytential/qbx/target_assoc.py index 946fe96b..733e2f2f 100644 --- a/pytential/qbx/target_assoc.py +++ b/pytential/qbx/target_assoc.py @@ -37,7 +37,8 @@ from boxtree.area_query import AreaQueryElementwiseTemplate from boxtree.tools import InlineBinarySearch from cgen import Enum from pytential.qbx.utils import ( - QBX_TREE_C_PREAMBLE, QBX_TREE_MAKO_DEFS, TreeWranglerBase) + QBX_TREE_C_PREAMBLE, QBX_TREE_MAKO_DEFS, TreeWranglerBase, + TreeCodeContainerMixin) unwrap_args = AreaQueryElementwiseTemplate.unwrap_args @@ -324,10 +325,11 @@ class QBXTargetAssociation(DeviceDataRecord): pass -class TargetAssociationCodeContainer(object): +class TargetAssociationCodeContainer(TreeCodeContainerMixin): - def __init__(self, cl_context): + def __init__(self, cl_context, tree_code_container): self.cl_context = cl_context + self.tree_code_container = tree_code_container @memoize_method def target_marker(self, dimensions, coord_dtype, box_id_dtype, @@ -365,21 +367,11 @@ class TargetAssociationCodeContainer(object): max_levels, extra_type_aliases=(("particle_id_t", particle_id_dtype),)) - @memoize_method - def peer_list_finder(self): - from boxtree.area_query import PeerListFinder - return PeerListFinder(self.cl_context) - @memoize_method def space_invader_query(self): from boxtree.area_query import SpaceInvaderQueryBuilder return SpaceInvaderQueryBuilder(self.cl_context) - @memoize_method - def tree_builder(self): - from boxtree.tree_build import TreeBuilder - return TreeBuilder(self.cl_context) - def get_wrangler(self, queue): return TargetAssociationWrangler(self, queue) diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index edf92dfe..e39e0c6d 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -31,7 +31,7 @@ import numpy as np from boxtree.tree import Tree import pyopencl as cl import pyopencl.array # noqa -from pytools import memoize +from pytools import memoize, memoize_method import logging logger = logging.getLogger(__name__) @@ -137,13 +137,49 @@ def get_interleaved_radii(queue, lpot_source): # }}} -# {{{ peer list wrangler mixin +# {{{ tree code container + +class TreeCodeContainer(object): + + def __init__(self, cl_context): + self.cl_context = cl_context + + @memoize_method + def build_tree(self): + from boxtree.tree_build import TreeBuilder + return TreeBuilder(self.cl_context) + + @memoize_method + def peer_list_finder(self): + from boxtree.area_query import PeerListFinder + return PeerListFinder(self.cl_context) + +# }}} + + +# {{{ tree code container mixin + +class TreeCodeContainerMixin(object): + """Forwards requests for tree-related code to an inner code container named + self.tree_code_container. + """ + + def build_tree(self): + return self.tree_code_container.build_tree() + + def peer_list_finder(self): + return self.tree_code_container.peer_list_finder() + +# }}} + + +# {{{ tree wrangler base class class TreeWranglerBase(object): def build_tree(self, lpot_source, targets_list=(), use_base_fine_discr=False): - tb = self.code_container.tree_builder() + tb = self.code_container.build_tree() from pytential.qbx.utils import build_tree_with_qbx_metadata return build_tree_with_qbx_metadata( self.queue, tb, lpot_source, targets_list=targets_list, -- GitLab From b48f6871d0f9246b3bc226cf92f8050bba7d5b84 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 3 Jul 2017 15:04:53 -0500 Subject: [PATCH 2/2] a) Rename QBXFMMGeometryCodeGetter to QBXFMMGeometryCodeContainer. b) Add a reusable code cache class. --- pytential/qbx/__init__.py | 128 +++++++++++++++++++++++++------------- pytential/qbx/geometry.py | 32 +++++----- 2 files changed, 102 insertions(+), 58 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 0ba8e5d7..bcd675c7 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -79,7 +79,8 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): _expansion_stick_out_factor=0, performance_data_file=None, fmm_backend="sumpy", - target_stick_out_factor=_not_provided): + target_stick_out_factor=_not_provided, + code_cache=None): """ :arg fine_order: The total degree to which the (upsampled) underlying quadrature is exact. @@ -160,6 +161,11 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): self._expansion_stick_out_factor = _expansion_stick_out_factor self.performance_data_file = performance_data_file + if code_cache is None: + code_cache = CodeCache(self.cl_context, self.debug) + + self.code_cache = code_cache + def copy( self, density_discr=None, @@ -175,6 +181,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): debug=_not_provided, _refined_for_global_qbx=_not_provided, target_stick_out_factor=_not_provided, + code_cache=_not_provided, ): # {{{ argument processing @@ -232,7 +239,12 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): else self._expansion_stick_out_factor), performance_data_file=( performance_data_file or self.performance_data_file), - fmm_backend=self.fmm_backend) + fmm_backend=self.fmm_backend, + code_cache=( + # None is a valid value here + code_cache + if code_cache is not _not_provided + else self.code_cache)) # }}} @@ -274,23 +286,16 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): return conn @property - @memoize_method def tree_code_container(self): - from pytential.qbx.utils import TreeCodeContainer - return TreeCodeContainer(self.cl_context) + return self.code_cache.tree_code_container() @property - @memoize_method def refiner_code_container(self): - from pytential.qbx.refinement import RefinerCodeContainer - return RefinerCodeContainer(self.cl_context, self.tree_code_container) + return self.code_cache.refiner_code_container() @property - @memoize_method def target_association_code_container(self): - from pytential.qbx.target_assoc import TargetAssociationCodeContainer - return TargetAssociationCodeContainer( - self.cl_context, self.tree_code_container) + return self.code_cache.target_association_code_container() @memoize_method def with_refinement(self, target_order=None, kernel_length_scale=None, @@ -386,7 +391,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): """ from pytential.qbx.geometry import QBXFMMGeometryData - return QBXFMMGeometryData(self.qbx_fmm_code_getter, + return QBXFMMGeometryData(self.qbx_fmm_code_container, self, target_discrs_and_qbx_sides, target_association_tolerance=self.target_association_tolerance, debug=self.debug) @@ -439,44 +444,20 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): return func(queue, insn, bound_expr, evaluate_wrapper) @property - @memoize_method - def qbx_fmm_code_getter(self): - from pytential.qbx.geometry import QBXFMMGeometryCodeGetter - return QBXFMMGeometryCodeGetter(self.cl_context, self.ambient_dim, - self.tree_code_container, debug=self.debug) + def qbx_fmm_code_container(self): + return self.code_cache.fmm_geometry_code_container(self.ambient_dim) # {{{ fmm-based execution - @memoize_method def expansion_wrangler_code_container(self, base_kernel, out_kernels): mpole_expn_class = \ self.expansion_factory.get_multipole_expansion_class(base_kernel) local_expn_class = \ self.expansion_factory.get_local_expansion_class(base_kernel) - from functools import partial - fmm_mpole_factory = partial(mpole_expn_class, base_kernel) - fmm_local_factory = partial(local_expn_class, base_kernel) - qbx_local_factory = partial(local_expn_class, base_kernel) - - if self.fmm_backend == "sumpy": - from pytential.qbx.fmm import \ - QBXSumpyExpansionWranglerCodeContainer - return QBXSumpyExpansionWranglerCodeContainer( - self.cl_context, - fmm_mpole_factory, fmm_local_factory, qbx_local_factory, - out_kernels) - - elif self.fmm_backend == "fmmlib": - from pytential.qbx.fmmlib import \ - QBXFMMLibExpansionWranglerCodeContainer - return QBXFMMLibExpansionWranglerCodeContainer( - self.cl_context, - fmm_mpole_factory, fmm_local_factory, qbx_local_factory, - out_kernels) - - else: - raise ValueError("invalid FMM backend: %s" % self.fmm_backend) + return self.code_cache.expansion_wrangler_code_container( + self.fmm_backend, mpole_expn_class, local_expn_class, + local_expn_class, base_kernel, out_kernels) def exec_compute_potential_insn_fmm(self, queue, insn, bound_expr, evaluate): # {{{ build list of unique target discretizations used @@ -777,9 +758,72 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): # }}} +# {{{ built code cache + +class CodeCache(object): + + def __init__(self, cl_context, debug=False): + self.cl_context = cl_context + self.debug = debug + + @memoize_method + def tree_code_container(self): + from pytential.qbx.utils import TreeCodeContainer + return TreeCodeContainer(self.cl_context) + + @memoize_method + def refiner_code_container(self): + from pytential.qbx.refinement import RefinerCodeContainer + return RefinerCodeContainer( + self.cl_context, self.tree_code_container()) + + @memoize_method + def target_association_code_container(self): + from pytential.qbx.target_assoc import TargetAssociationCodeContainer + return TargetAssociationCodeContainer( + self.cl_context, self.tree_code_container()) + + @memoize_method + def fmm_geometry_code_container(self, ambient_dim): + from pytential.qbx.geometry import QBXFMMGeometryCodeContainer + return QBXFMMGeometryCodeContainer( + self.cl_context, ambient_dim, self.tree_code_container(), + debug=self.debug) + + @memoize_method + def expansion_wrangler_code_container(self, fmm_backend, mpole_expn_class, + local_expn_class, qbx_local_expn_class, base_kernel, out_kernels): + from functools import partial + fmm_mpole_factory = partial(mpole_expn_class, base_kernel) + fmm_local_factory = partial(local_expn_class, base_kernel) + qbx_local_factory = partial(qbx_local_expn_class, base_kernel) + + if fmm_backend == "sumpy": + from pytential.qbx.fmm import \ + QBXSumpyExpansionWranglerCodeContainer + return QBXSumpyExpansionWranglerCodeContainer( + self.cl_context, + fmm_mpole_factory, fmm_local_factory, qbx_local_factory, + out_kernels) + + elif fmm_backend == "fmmlib": + from pytential.qbx.fmmlib import \ + QBXFMMLibExpansionWranglerCodeContainer + return QBXFMMLibExpansionWranglerCodeContainer( + self.cl_context, + fmm_mpole_factory, fmm_local_factory, qbx_local_factory, + out_kernels) + + else: + raise ValueError("invalid FMM backend: %s" % fmm_backend) + +# }}} + + __all__ = ( QBXLayerPotentialSource, QBXTargetAssociationFailedException, + CodeCache ) # vim: fdm=marker diff --git a/pytential/qbx/geometry.py b/pytential/qbx/geometry.py index f8e2d21e..f11d5523 100644 --- a/pytential/qbx/geometry.py +++ b/pytential/qbx/geometry.py @@ -73,7 +73,7 @@ Enums of special values Geometry description code container ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. autoclass:: QBXFMMGeometryCodeGetter +.. autoclass:: QBXFMMGeometryCodeContainer :members: :undoc-members: @@ -106,7 +106,7 @@ class target_state(Enum): # noqa FAILED = -2 -class QBXFMMGeometryCodeGetter(TreeCodeContainerMixin): +class QBXFMMGeometryCodeContainer(TreeCodeContainerMixin): def __init__(self, cl_context, ambient_dim, tree_code_container, debug): self.cl_context = cl_context @@ -294,9 +294,9 @@ class QBXFMMGeometryData(object): .. rubric :: Attributes - .. attribute:: code_getter + .. attribute:: code_container - The :class:`QBXFMMGeometryCodeGetter` for this object. + The :class:`QBXFMMGeometryCodeContainer` for this object. .. attribute:: lpot_source @@ -341,7 +341,7 @@ class QBXFMMGeometryData(object): .. automethod:: plot() """ - def __init__(self, code_getter, lpot_source, + def __init__(self, code_container, lpot_source, target_discrs_and_qbx_sides, target_association_tolerance, debug): """ @@ -354,7 +354,7 @@ class QBXFMMGeometryData(object): potentially costly self-checks """ - self.code_getter = code_getter + self.code_container = code_container self.lpot_source = lpot_source self.target_discrs_and_qbx_sides = \ target_discrs_and_qbx_sides @@ -408,7 +408,7 @@ class QBXFMMGeometryData(object): def target_info(self): """Return a :class:`TargetInfo`. |cached|""" - code_getter = self.code_getter + code_container = self.code_container lpot_src = self.lpot_source with cl.CommandQueue(self.cl_context) as queue: @@ -424,14 +424,14 @@ class QBXFMMGeometryData(object): targets = cl.array.empty( self.cl_context, (lpot_src.ambient_dim, ntargets), self.coord_dtype) - code_getter.copy_targets_kernel()( + code_container.copy_targets_kernel()( queue, targets=targets[:, :self.ncenters], points=self.centers()) for start, (target_discr, _) in zip( target_discr_starts, self.target_discrs_and_qbx_sides): - code_getter.copy_targets_kernel()( + code_container.copy_targets_kernel()( queue, targets=targets[:, start:start+target_discr.nnodes], points=target_discr.nodes()) @@ -474,7 +474,7 @@ class QBXFMMGeometryData(object): |cached| """ - code_getter = self.code_getter + code_container = self.code_container lpot_src = self.lpot_source target_info = self.target_info() @@ -503,7 +503,7 @@ class QBXFMMGeometryData(object): # # FIXME: Should investigate this further. - tree, _ = code_getter.build_tree()(queue, + tree, _ = code_container.build_tree()(queue, particles=lpot_src.fine_density_discr.nodes(), targets=target_info.targets, target_radii=target_radii, @@ -532,7 +532,7 @@ class QBXFMMGeometryData(object): """ with cl.CommandQueue(self.cl_context) as queue: - trav, _ = self.code_getter.build_traversal(queue, self.tree(), + trav, _ = self.code_container.build_traversal(queue, self.tree(), debug=self.debug) if self.lpot_source._expansions_in_tree_have_extent: @@ -552,7 +552,7 @@ class QBXFMMGeometryData(object): trav = self.traversal() qbx_center_to_target_box_lookup = \ - self.code_getter.qbx_center_to_target_box_lookup( + self.code_container.qbx_center_to_target_box_lookup( # particle_id_dtype: tree.particle_id_dtype, # box_id_dtype: @@ -639,7 +639,7 @@ class QBXFMMGeometryData(object): center_is_used = cl.array.zeros(queue, self.ncenters, np.int8) - self.code_getter.pick_used_centers( + self.code_container.pick_used_centers( queue, center_is_used=center_is_used, target_to_center=tgt_assoc_result, @@ -730,7 +730,7 @@ class QBXFMMGeometryData(object): queue, tree_ttc.shape, tree_ttc.dtype) count = cl.array.empty(queue, 1, tree_ttc.dtype) - self.code_getter.filter_center_and_target_ids(tree_ttc.dtype)( + self.code_container.filter_center_and_target_ids(tree_ttc.dtype)( tree_ttc, filtered_tree_ttc, filtered_target_ids, count, queue=queue, size=len(tree_ttc)) @@ -740,7 +740,7 @@ class QBXFMMGeometryData(object): filtered_target_ids = filtered_target_ids[:count].copy() center_target_starts, targets_sorted_by_center, _ = \ - self.code_getter.key_value_sort(queue, + self.code_container.key_value_sort(queue, filtered_tree_ttc, filtered_target_ids, self.ncenters, tree_ttc.dtype) -- GitLab