From b7fb9c79b7e6f903ffdba33a06a9f65e08c2d33e Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 1 Jun 2019 20:39:47 -0500 Subject: [PATCH 01/96] move expansion_radii computation to qbx.utils --- pytential/qbx/__init__.py | 48 ++++++++++----------------------- pytential/qbx/utils.py | 34 +++++++++++++++++++++++ pytential/symbolic/execution.py | 2 +- 3 files changed, 49 insertions(+), 35 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index bc29d7fa..9cda4b23 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -492,10 +492,10 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): @memoize_method def _expansion_radii(self, last_dim_length): - with cl.CommandQueue(self.cl_context) as queue: - return (self._coarsest_quad_resolution(last_dim_length) - .with_queue(queue) - * 0.5 * self._dim_fudge_factor()).with_queue(None) + import pytential.qbx.utils as utils + return utils.get_expansion_radii(self, + last_dim_length=last_dim_length, + factor=0.5 * self._dim_fudge_factor()) # _expansion_radii should not be needed for the fine discretization @@ -514,11 +514,12 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): # - Setting this equal to half the expansion radius will not provide # a refinement 'buffer layer' at a 2x coarsening fringe. - with cl.CommandQueue(self.cl_context) as queue: - return ( - (self._stage2_coarsest_quad_resolution(last_dim_length) - .with_queue(queue)) - * 0.5 * 0.75 * self._dim_fudge_factor()).with_queue(None) + from pytential import sym + import pytential.qbx.utils as utils + return utils.get_expansion_radii(self, + last_dim_length=last_dim_length, + factor=0.5 * 0.75 * self._dim_fudge_factor(), + where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)) @memoize_method def _close_target_tunnel_radius(self, last_dim_length): @@ -535,19 +536,8 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): should be the same as the panel length. """ import pytential.qbx.utils as utils - from pytential import sym, bind - with cl.CommandQueue(self.cl_context) as queue: - maxstretch = bind( - self, - sym._simplex_mapping_max_stretch_factor( - self.ambient_dim) - )(queue) - - maxstretch = utils.to_last_dim_length( - self.density_discr, maxstretch, last_dim_length) - maxstretch = maxstretch.with_queue(None) - - return maxstretch + return utils.get_coarsest_resolution(self.density_discr, + last_dim_length) @memoize_method def _stage2_coarsest_quad_resolution(self, last_dim_length="npanels"): @@ -560,18 +550,8 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): raise NotImplementedError() import pytential.qbx.utils as utils - from pytential import sym, bind - with cl.CommandQueue(self.cl_context) as queue: - maxstretch = bind( - self, sym._simplex_mapping_max_stretch_factor( - self.ambient_dim, - where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)) - )(queue) - maxstretch = utils.to_last_dim_length( - self.stage2_density_discr, maxstretch, last_dim_length) - maxstretch = maxstretch.with_queue(None) - - return maxstretch + return utils.get_coarsest_resolution(self.stage2_density_discr, + last_dim_length) @memoize_method def qbx_fmm_geometry_data(self, target_discrs_and_qbx_sides): diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index c7d06247..1b36ee83 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -295,6 +295,40 @@ def element_centers_of_mass(discr): # }}} +# {{{ compute expansion radii + +def get_coarsest_resolution(discr, last_dim_length="npanels"): + from pytential import sym, bind + + with cl.CommandQueue(discr.cl_context) as queue: + sym_stretch_factor = \ + sym._simplex_mapping_max_stretch_factor(discr.ambient_dim) + stretch_factor = bind(discr, sym_stretch_factor)(queue) + stretch_factor = to_last_dim_length(discr, + stretch_factor, last_dim_length) + + return stretch_factor.with_queue(None) + + +def get_expansion_radii(lpot_src, + last_dim_length="npanels", factor=0.5, where=None): + from pytential import sym + if where is None or isinstance(where, sym.QBXSourceStage1): + discr = lpot_src.density_discr + elif isinstance(where, sym.QBXSourceStage2): + discr = lpot_src.stage2_density_discr + else: + from pytential.symbolic.mappers import stringify_where + raise ValueError('unsupported `where`: {}'.format( + stringify_where(where))) + + radii = get_coarsest_resolution(discr, last_dim_length) + with cl.CommandQueue(lpot_src.cl_context) as queue: + return (factor * radii.with_queue(queue)).with_queue(None) + +# }}} + + # {{{ compute center array def get_centers_on_side(lpot_src, sign): diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 9a9bcb05..b76ad47a 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -487,7 +487,7 @@ class GeometryCollection(object): def copy(self): return GeometryCollection( - self.places, + self.places.copy(), auto_where=self._default_place_ids) def get_cache(self, name): -- GitLab From 312249d8749b1a8b39a2893336548f53b4f192f1 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 8 Jun 2019 15:35:27 -0500 Subject: [PATCH 02/96] make an expression out of qbx expansion centers and radii --- pytential/qbx/__init__.py | 65 ++++++++++++++++++++++---------- pytential/qbx/utils.py | 34 ----------------- pytential/symbolic/execution.py | 8 ++++ pytential/symbolic/mappers.py | 18 +++++++++ pytential/symbolic/primitives.py | 63 +++++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+), 53 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 9cda4b23..adcb767e 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -348,7 +348,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): @memoize_method def weights_and_area_elements(self): - import pytential.symbolic.primitives as p + import pytential.symbolic.primitives as sym from pytential.symbolic.execution import bind with cl.CommandQueue(self.cl_context) as queue: # quad_stage2_density_discr is not guaranteed to be usable for @@ -359,10 +359,10 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): queue, bind( self.stage2_density_discr, - p.area_element(self.ambient_dim, self.dim) + sym.area_element(self.ambient_dim, self.dim) )(queue)) - qweight = bind(self.quad_stage2_density_discr, p.QWeight())(queue) + qweight = bind(self.quad_stage2_density_discr, sym.QWeight())(queue) return (area_element.with_queue(queue)*qweight).with_queue(None) @@ -490,12 +490,21 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): else: return 1 + def _expansion_radii_factor(self): + return 0.5 * self._dim_fudge_factor() + @memoize_method def _expansion_radii(self, last_dim_length): - import pytential.qbx.utils as utils - return utils.get_expansion_radii(self, - last_dim_length=last_dim_length, - factor=0.5 * self._dim_fudge_factor()) + import pytential.symbolic.primitives as sym + from pytential.symbolic.execution import bind + + with cl.CommandQueue(self.cl_context) as queue: + radii = bind(self, sym.qbx_expansion_radii( + self._expansion_radii_factor(), + self.ambient_dim, + last_dim_length=last_dim_length))(queue) + + return radii.with_queue(None) # _expansion_radii should not be needed for the fine discretization @@ -514,12 +523,17 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): # - Setting this equal to half the expansion radius will not provide # a refinement 'buffer layer' at a 2x coarsening fringe. - from pytential import sym - import pytential.qbx.utils as utils - return utils.get_expansion_radii(self, - last_dim_length=last_dim_length, - factor=0.5 * 0.75 * self._dim_fudge_factor(), - where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)) + import pytential.symbolic.primitives as sym + from pytential.symbolic.execution import bind + + with cl.CommandQueue(self.cl_context) as queue: + radii = bind(self, sym.qbx_expansion_radii( + 0.75 * self._expansion_radii_factor(), + self.ambient_dim, + last_dim_length=last_dim_length, + where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)))(queue) + + return radii.with_queue(None) @memoize_method def _close_target_tunnel_radius(self, last_dim_length): @@ -535,9 +549,15 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): mesh. In a 1D uniform mesh of uniform 'parametrization speed', it should be the same as the panel length. """ - import pytential.qbx.utils as utils - return utils.get_coarsest_resolution(self.density_discr, - last_dim_length) + + import pytential.symbolic.primitives as sym + from pytential.symbolic.execution import bind + + with cl.CommandQueue(self.cl_context) as queue: + stretch = sym._simplex_mapping_max_stretch_factor(self.ambient_dim) + r = bind(self, sym.LastDimLength(last_dim_length, stretch))(queue) + + return r.with_queue(None) @memoize_method def _stage2_coarsest_quad_resolution(self, last_dim_length="npanels"): @@ -549,9 +569,16 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): # Not technically required below, but no need to loosen for now. raise NotImplementedError() - import pytential.qbx.utils as utils - return utils.get_coarsest_resolution(self.stage2_density_discr, - last_dim_length) + import pytential.symbolic.primitives as sym + from pytential.symbolic.execution import bind + + with cl.CommandQueue(self.cl_context) as queue: + r = bind(self, sym.qbx_quad_resolution( + self.ambient_dim, + last_dim_length, + where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)))(queue) + + return r.with_queue(None) @memoize_method def qbx_fmm_geometry_data(self, target_discrs_and_qbx_sides): diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index 1b36ee83..c7d06247 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -295,40 +295,6 @@ def element_centers_of_mass(discr): # }}} -# {{{ compute expansion radii - -def get_coarsest_resolution(discr, last_dim_length="npanels"): - from pytential import sym, bind - - with cl.CommandQueue(discr.cl_context) as queue: - sym_stretch_factor = \ - sym._simplex_mapping_max_stretch_factor(discr.ambient_dim) - stretch_factor = bind(discr, sym_stretch_factor)(queue) - stretch_factor = to_last_dim_length(discr, - stretch_factor, last_dim_length) - - return stretch_factor.with_queue(None) - - -def get_expansion_radii(lpot_src, - last_dim_length="npanels", factor=0.5, where=None): - from pytential import sym - if where is None or isinstance(where, sym.QBXSourceStage1): - discr = lpot_src.density_discr - elif isinstance(where, sym.QBXSourceStage2): - discr = lpot_src.stage2_density_discr - else: - from pytential.symbolic.mappers import stringify_where - raise ValueError('unsupported `where`: {}'.format( - stringify_where(where))) - - radii = get_coarsest_resolution(discr, last_dim_length) - with cl.CommandQueue(lpot_src.cl_context) as queue: - return (factor * radii.with_queue(queue)).with_queue(None) - -# }}} - - # {{{ compute center array def get_centers_on_side(lpot_src, sign): diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index b76ad47a..f2403bc2 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -208,6 +208,14 @@ class EvaluationMapper(EvaluationMapperBase): else: raise TypeError("cannot interpolate `{}`".format(type(operand))) + def map_last_dim_length(self, expr): + discr = self.bound_expr.get_discretization(expr.where) + vec = self.rec(expr.operand) + + from pytential.qbx.utils import to_last_dim_length + return to_last_dim_length(discr, vec, expr.last_dim_length, + queue=self.queue) + # }}} def exec_assign(self, queue, insn, bound_expr, evaluate): diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 41545353..6fc08456 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -106,6 +106,11 @@ class IdentityMapper(IdentityMapperBase): def map_interpolation(self, expr): return type(expr)(expr.source, expr.target, self.rec(expr.operand)) + def map_last_dim_length(self, expr): + return type(expr)(expr.last_dim_length, + self.rec(expr.operand), + where=expr.where) + class CombineMapper(CombineMapperBase): def map_node_sum(self, expr): @@ -117,6 +122,7 @@ class CombineMapper(CombineMapperBase): map_elementwise_min = map_node_sum map_elementwise_max = map_node_sum map_interpolation = map_node_sum + map_last_dim_length = map_node_sum def map_int_g(self, expr): return self.combine( @@ -295,6 +301,14 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): return type(expr)(source, target, expr.operand) + def map_last_dim_length(self, expr): + where = expr.where + if where is None: + where = self.default_source + + return type(expr)(expr.last_dim_length, + self.rec(expr.operand), where=where) + def operand_rec(self, expr): return self.rec(expr) @@ -604,6 +618,10 @@ class StringifyMapper(BaseStringifyMapper): stringify_where(expr.target), self.rec(expr.operand, PREC_PRODUCT)) + def map_last_dim_length(self, expr, enclosing_prec): + return "Sample[%s](%s)" % (expr.last_dim_length, + self.rec(expr.operand, PREC_PRODUCT)) + # }}} diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index ec129369..9fefe558 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -786,6 +786,69 @@ def _scaled_max_curvature(ambient_dim, dim=None, where=None): # }}} +# {{{ qbx-specific geometry + +class LastDimLength(Expression): + init_arg_names = ("last_dim_length", "operand", "where") + + def __new__(cls, last_dim_length, operand, where=None): + if isinstance(operand, np.ndarray): + def make_op(operand_i): + return cls(last_dim_length, operand_i, where=where) + + return componentwise(make_op, operand) + else: + return Expression.__new__(cls) + + def __init__(self, last_dim_length, operand, where=None): + self.last_dim_length = last_dim_length + self.operand = operand + self.where = where + + def __getinitargs__(self): + return (self.last_dim_length, self.operand, self.where) + + mapper_method = intern("map_last_dim_length") + + +def qbx_quad_resolution(ambient_dim, last_dim_length="npanels", where=None): + stretch = _simplex_mapping_max_stretch_factor(ambient_dim, where=where) + return LastDimLength(last_dim_length, stretch, where=where) + + +def qbx_expansion_radii(factor, ambient_dim, + last_dim_length="nsources", where=None): + """Expansion radii. + + :arg factor: stick out factor for expansion radii. + """ + + stretch = _simplex_mapping_max_stretch_factor(ambient_dim, where=where) + radii = LastDimLength(last_dim_length, factor * stretch, where=where) + + return cse(radii, cse_scope.DISCRETIZATION) + + +def qbx_expansion_centers(factor, side, ambient_dim, dim=None, where=None): + """One-sided expansion centers. + + :arg factor: stick out factor for expansion radii. + :arg side: `+1` or `-1` expansion side, relative to the direction of + the normal vector. + """ + + x = nodes(ambient_dim, where=where) + normals = normal(ambient_dim, dim=dim, where=where) + radii = qbx_expansion_radii(factor, ambient_dim, + last_dim_length="nsources", where=where) + + centers = x + side * radii * normals + + return cse(centers.as_vector(), cse_scope.DISCRETIZATION) + +# }}} + + # {{{ operators class SingleScalarOperandExpression(Expression): -- GitLab From c741e8cae2231e2175b8df7cd996e42b3135f6d1 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 8 Jun 2019 15:49:49 -0500 Subject: [PATCH 03/96] simplify get_centers_on_side --- pytential/qbx/utils.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index c7d06247..cd130e41 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -298,15 +298,12 @@ def element_centers_of_mass(discr): # {{{ compute center array def get_centers_on_side(lpot_src, sign): - adim = lpot_src.density_discr.ambient_dim - dim = lpot_src.density_discr.dim - from pytential import sym, bind with cl.CommandQueue(lpot_src.cl_context) as queue: - nodes = bind(lpot_src.density_discr, sym.nodes(adim))(queue) - normals = bind(lpot_src.density_discr, sym.normal(adim, dim=dim))(queue) - expansion_radii = lpot_src._expansion_radii("nsources").with_queue(queue) - return (nodes + normals * sign * expansion_radii).as_vector(np.object) + return bind(lpot_src, sym.qbx_expansion_centers( + lpot_src._expansion_radii_factor(), + sign, + lpot_src.ambient_dim))(queue) # }}} -- GitLab From c7b9222e75bf330b20a02881013d534b47954acc Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 8 Jun 2019 19:14:01 -0500 Subject: [PATCH 04/96] remove usage of to_last_dim_length --- pytential/qbx/refinement.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index a80a8098..6075c152 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -615,15 +615,10 @@ def refine_for_global_qbx(lpot_source, wrangler, if scaled_max_curvature_threshold is not None: with ProcessLogger(logger, "checking scaled max curvature threshold"): - from pytential.qbx.utils import to_last_dim_length from pytential import sym, bind - scaled_max_curv = to_last_dim_length( - lpot_source.density_discr, - bind(lpot_source, - sym.ElementwiseMax( - sym._scaled_max_curvature( - lpot_source.density_discr.ambient_dim))) - (wrangler.queue), "npanels") + scaled_max_curv = bind(lpot_source, + sym.LastDimLength("npanels", sym._scaled_max_curvature( + lpot_source.ambient_dim)))(wrangler.queue) violates_scaled_max_curv = \ wrangler.check_element_prop_threshold( -- GitLab From 1e6ae94bea79dc1f1c0f497d33865356818d93de Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sun, 9 Jun 2019 13:19:18 -0500 Subject: [PATCH 05/96] qbx.utils: rename el_view to mesh_el_view to make it clearer --- pytential/qbx/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index cd130e41..733af6b8 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -250,7 +250,7 @@ def to_last_dim_length(discr, vec, last_dim_length, queue=None): knl(queue, nelements=group.nelements, a=group.view(vec), - result=el_view(discr, group_nr, result)) + result=mesh_el_view(discr.mesh, group_nr, result)) return result else: @@ -287,7 +287,7 @@ def element_centers_of_mass(discr): nelements=group.nelements, nunit_nodes=group.nunit_nodes, nodes=group.view(discr.nodes()), - panels=el_view(discr, group_nr, panels)) + panels=mesh_el_view(discr.mesh, group_nr, panels)) panels.finish() panels = panels.with_queue(None) return tuple(panels[d, :] for d in range(mesh.ambient_dim)) @@ -310,14 +310,14 @@ def get_centers_on_side(lpot_src, sign): # {{{ el_view -def el_view(discr, group_nr, global_array): +def mesh_el_view(mesh, group_nr, global_array): """Return a view of *global_array* of shape - ``(..., discr.groups[group_nr].nelements)`` + ``(..., mesh.groups[group_nr].nelements)`` where *global_array* is of shape ``(..., nelements)``, - where *nelements* is the global (per-discretization) node count. + where *nelements* is the global (per-mesh) element count. """ - group = discr.mesh.groups[group_nr] + group = mesh.groups[group_nr] return global_array[ ..., group.element_nr_base:group.element_nr_base + group.nelements] \ -- GitLab From cde1b5abef30e70b20ddae12cc1bd517bff65585 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sun, 9 Jun 2019 13:22:13 -0500 Subject: [PATCH 06/96] simplify imports --- pytential/qbx/__init__.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index adcb767e..608a2684 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -348,8 +348,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): @memoize_method def weights_and_area_elements(self): - import pytential.symbolic.primitives as sym - from pytential.symbolic.execution import bind + from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: # quad_stage2_density_discr is not guaranteed to be usable for # interpolation/differentiation. Use density_discr to find @@ -495,9 +494,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): @memoize_method def _expansion_radii(self, last_dim_length): - import pytential.symbolic.primitives as sym - from pytential.symbolic.execution import bind - + from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: radii = bind(self, sym.qbx_expansion_radii( self._expansion_radii_factor(), @@ -523,9 +520,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): # - Setting this equal to half the expansion radius will not provide # a refinement 'buffer layer' at a 2x coarsening fringe. - import pytential.symbolic.primitives as sym - from pytential.symbolic.execution import bind - + from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: radii = bind(self, sym.qbx_expansion_radii( 0.75 * self._expansion_radii_factor(), @@ -550,9 +545,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): should be the same as the panel length. """ - import pytential.symbolic.primitives as sym - from pytential.symbolic.execution import bind - + from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: stretch = sym._simplex_mapping_max_stretch_factor(self.ambient_dim) r = bind(self, sym.LastDimLength(last_dim_length, stretch))(queue) @@ -569,9 +562,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): # Not technically required below, but no need to loosen for now. raise NotImplementedError() - import pytential.symbolic.primitives as sym - from pytential.symbolic.execution import bind - + from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: r = bind(self, sym.qbx_quad_resolution( self.ambient_dim, -- GitLab From 3ec2a392aec7c373526afc0eaeb180c0798b1da9 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sun, 9 Jun 2019 14:16:06 -0500 Subject: [PATCH 07/96] rename LastDimLength --- pytential/qbx/__init__.py | 9 +++--- pytential/qbx/refinement.py | 2 +- pytential/symbolic/execution.py | 4 +-- pytential/symbolic/mappers.py | 14 ++++----- pytential/symbolic/primitives.py | 49 +++++++++++++++++++++----------- 5 files changed, 47 insertions(+), 31 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 608a2684..79860712 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -499,7 +499,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): radii = bind(self, sym.qbx_expansion_radii( self._expansion_radii_factor(), self.ambient_dim, - last_dim_length=last_dim_length))(queue) + granularity=last_dim_length))(queue) return radii.with_queue(None) @@ -525,7 +525,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): radii = bind(self, sym.qbx_expansion_radii( 0.75 * self._expansion_radii_factor(), self.ambient_dim, - last_dim_length=last_dim_length, + granularity=last_dim_length, where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)))(queue) return radii.with_queue(None) @@ -547,8 +547,9 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: - stretch = sym._simplex_mapping_max_stretch_factor(self.ambient_dim) - r = bind(self, sym.LastDimLength(last_dim_length, stretch))(queue) + r = bind(self, sym.qbx_quad_resolution( + self.ambient_dim, + last_dim_length))(queue) return r.with_queue(None) diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index 6075c152..6377920d 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -617,7 +617,7 @@ def refine_for_global_qbx(lpot_source, wrangler, "checking scaled max curvature threshold"): from pytential import sym, bind scaled_max_curv = bind(lpot_source, - sym.LastDimLength("npanels", sym._scaled_max_curvature( + sym.DOFGranularityConverter("npanels", sym._scaled_max_curvature( lpot_source.ambient_dim)))(wrangler.queue) violates_scaled_max_curv = \ diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index f2403bc2..14383cd3 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -208,12 +208,12 @@ class EvaluationMapper(EvaluationMapperBase): else: raise TypeError("cannot interpolate `{}`".format(type(operand))) - def map_last_dim_length(self, expr): + def map_dof_granularity_converter(self, expr): discr = self.bound_expr.get_discretization(expr.where) vec = self.rec(expr.operand) from pytential.qbx.utils import to_last_dim_length - return to_last_dim_length(discr, vec, expr.last_dim_length, + return to_last_dim_length(discr, vec, expr.granularity, queue=self.queue) # }}} diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 6fc08456..85c3743a 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -106,8 +106,8 @@ class IdentityMapper(IdentityMapperBase): def map_interpolation(self, expr): return type(expr)(expr.source, expr.target, self.rec(expr.operand)) - def map_last_dim_length(self, expr): - return type(expr)(expr.last_dim_length, + def map_dof_granularity_converter(self, expr): + return type(expr)(expr.granularity, self.rec(expr.operand), where=expr.where) @@ -122,7 +122,7 @@ class CombineMapper(CombineMapperBase): map_elementwise_min = map_node_sum map_elementwise_max = map_node_sum map_interpolation = map_node_sum - map_last_dim_length = map_node_sum + map_dof_granularity_converter = map_node_sum def map_int_g(self, expr): return self.combine( @@ -301,12 +301,12 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): return type(expr)(source, target, expr.operand) - def map_last_dim_length(self, expr): + def map_dof_granularity_converter(self, expr): where = expr.where if where is None: where = self.default_source - return type(expr)(expr.last_dim_length, + return type(expr)(expr.granularity, self.rec(expr.operand), where=where) def operand_rec(self, expr): @@ -618,8 +618,8 @@ class StringifyMapper(BaseStringifyMapper): stringify_where(expr.target), self.rec(expr.operand, PREC_PRODUCT)) - def map_last_dim_length(self, expr, enclosing_prec): - return "Sample[%s](%s)" % (expr.last_dim_length, + def map_dof_granularity_converter(self, expr, enclosing_prec): + return "DOF[%s](%s)" % (expr.granularity, self.rec(expr.operand, PREC_PRODUCT)) # }}} diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 9fefe558..a00de7f1 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -126,6 +126,11 @@ Discretization properties .. autofunction:: second_fundamental_form .. autofunction:: shape_operator +.. autoclass:: DOFGranularityConverter +.. autofunction:: qbx_quad_resolution +.. autofunction:: qbx_expansion_radii +.. autofunction:: qbx_expansion_centers + Elementary numerics ^^^^^^^^^^^^^^^^^^^ @@ -788,50 +793,60 @@ def _scaled_max_curvature(ambient_dim, dim=None, where=None): # {{{ qbx-specific geometry -class LastDimLength(Expression): - init_arg_names = ("last_dim_length", "operand", "where") +class DOFGranularityConverter(Expression): + """Converts a vector of DOFs to a vector of a desired granularity. + + .. attribute:: granularity + + New granularity of the DOF vector. Can be one of the following + strings: + + - 'nsources': keeps the granularity of the DOF vector. + - 'ncenters': for interleaved expansion centers. + - 'npanels': per-element. + """ + + init_arg_names = ("granularity", "operand", "where") - def __new__(cls, last_dim_length, operand, where=None): + def __new__(cls, granularity, operand, where=None): if isinstance(operand, np.ndarray): def make_op(operand_i): - return cls(last_dim_length, operand_i, where=where) + return cls(granularity, operand_i, where=where) return componentwise(make_op, operand) else: return Expression.__new__(cls) - def __init__(self, last_dim_length, operand, where=None): - self.last_dim_length = last_dim_length + def __init__(self, granularity, operand, where=None): + self.granularity = granularity self.operand = operand self.where = where def __getinitargs__(self): - return (self.last_dim_length, self.operand, self.where) + return (self.granularity, self.operand, self.where) - mapper_method = intern("map_last_dim_length") + mapper_method = intern("map_dof_granularity_converter") -def qbx_quad_resolution(ambient_dim, last_dim_length="npanels", where=None): +def qbx_quad_resolution(ambient_dim, granularity="npanels", where=None): stretch = _simplex_mapping_max_stretch_factor(ambient_dim, where=where) - return LastDimLength(last_dim_length, stretch, where=where) + return DOFGranularityConverter(granularity, stretch, where=where) def qbx_expansion_radii(factor, ambient_dim, - last_dim_length="nsources", where=None): - """Expansion radii. - + granularity="nsources", where=None): + """ :arg factor: stick out factor for expansion radii. """ stretch = _simplex_mapping_max_stretch_factor(ambient_dim, where=where) - radii = LastDimLength(last_dim_length, factor * stretch, where=where) + radii = DOFGranularityConverter(granularity, factor * stretch, where=where) return cse(radii, cse_scope.DISCRETIZATION) def qbx_expansion_centers(factor, side, ambient_dim, dim=None, where=None): - """One-sided expansion centers. - + """ :arg factor: stick out factor for expansion radii. :arg side: `+1` or `-1` expansion side, relative to the direction of the normal vector. @@ -840,7 +855,7 @@ def qbx_expansion_centers(factor, side, ambient_dim, dim=None, where=None): x = nodes(ambient_dim, where=where) normals = normal(ambient_dim, dim=dim, where=where) radii = qbx_expansion_radii(factor, ambient_dim, - last_dim_length="nsources", where=where) + granularity="nsources", where=where) centers = x + side * radii * normals -- GitLab From fc4551c8352fb47d9fcc7869776c210c14b4590b Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sun, 9 Jun 2019 14:51:09 -0500 Subject: [PATCH 08/96] flake8 fix --- pytential/qbx/refinement.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index 6377920d..0bc49b07 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -616,9 +616,10 @@ def refine_for_global_qbx(lpot_source, wrangler, with ProcessLogger(logger, "checking scaled max curvature threshold"): from pytential import sym, bind - scaled_max_curv = bind(lpot_source, - sym.DOFGranularityConverter("npanels", sym._scaled_max_curvature( - lpot_source.ambient_dim)))(wrangler.queue) + scaled_max_curv = bind(lpot_source, sym.DOFGranularityConverter( + "npanels", + sym._scaled_max_curvature( + lpot_source.ambient_dim)))(wrangler.queue) violates_scaled_max_curv = \ wrangler.check_element_prop_threshold( -- GitLab From 9d5aa6e889f7b684c73b6c99cafa7a6a7794fe41 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 14 Jun 2019 20:36:41 -0500 Subject: [PATCH 09/96] primitives: add a more comprehesive dof descriptor. --- pytential/symbolic/primitives.py | 139 +++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index a00de7f1..7f15d1a3 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -260,6 +260,145 @@ class QBXSourceQuadStage2(_QBXSource): of the layer potential source identified by :attr:`where`. """ + +class DOMAIN_TAG(object): # noqa + """General domain specifier + + .. attribute:: tag + """ + init_arg_names = ("tag",) + + def __init__(self, tag): + self.tag = tag + + def __hash__(self): + return hash((type(self), self.tag)) + + def __eq__(self, other): + return type(self) is type(other) and self.tag == other.tag + + def __ne__(self, other): + return not self.__eq__(other) + + def __getinitargs__(self): + return (self.tag,) + + def __repr__(self): + if isinstance(self.tag, str): + tag = self.tag + else: + tag = tag.__name__ if isinstance(tag, type) else type(tag).__name__ + + return "{}({})".format(type(self).__name__, tag) + + +class QBX_DOMAIN_STAGE1(DOMAIN_TAG): # noqa + """Specifier for + :attr:~pytential.qbx.QBXLayerPotentialSource.density_discr`.""" + pass + + +class QBX_DOMAIN_STAGE2(DOMAIN_TAG): # noqa + """Specifier for + :attr:~pytential.qbx.QBXLayerPotentialSource.stage2_density_discr`.""" + pass + + +class QBX_DOMAIN_QUAD_STAGE2(DOMAIN_TAG): # noqa + """Specifier for + :attr:~pytential.qbx.QBXLayerPotentialSource.quad_stage2_density_discr`.""" + pass + + +class QBX_DOF_NODE: # noqa + """DOFs are per-source node.""" + pass + + +class QBX_DOF_CENTER: # noqa + """DOFs interleaved per expansion center.""" + pass + + +class QBX_DOF_ELEMENT: # noqa + """DOFs per discretization element.""" + pass + + +class DOFDescriptor(object): + """Descriptor for degrees of freedom on a domain. + + .. attribute:: domain + + Describes the domain on which a DOF is defined. The domain contains + an internal tag for which exact + :class:`~pytential.source.PotentialSource`, + :class:`~pytential.target.TargetBase` or + :class:`~meshmode.discretization.Discretization` it refers to. + Can be a generic :class:`QBX_DOMAIN` or one of + :class:`QBX_DOMAIN_STAGE1`, :class:`QBX_DOMAIN_STAGE2` or + :class:`QBX_DOMAIN_QUAD_STAGE2`. + + .. attribute:: granularity + + Describes the level of granularity of the DOF. + Can be one of :class:`QBX_DOF_NODE`, :class:`QBX_DOF_CENTER` or + :class:`QBX_DOF_ELEMENT`. + + """ + + init_arg_names = ("domain", "granularity") + + def __init__(self, domain, granularity=None): + if (domain == DEFAULT_SOURCE + or domain == DEFAULT_TARGET + or isinstance(domain, str)): + domain = DOMAIN_TAG(domain) + + if granularity is None: + granularity = QBX_DOF_NODE + + if not (isinstance(domain, DOMAIN_TAG) + or isinstance(domain, QBX_DOMAIN_STAGE1) + or isinstance(domain, QBX_DOMAIN_STAGE2) + or isinstance(domain, QBX_DOMAIN_QUAD_STAGE2)): + raise ValueError('unknown domain tag: {}'.format(domain)) + + if not (granularity == QBX_DOF_NODE + or granularity == QBX_DOF_CENTER + or granularity == QBX_DOF_ELEMENT): + raise ValueError('unknown granularity: {}'.format(granularity)) + + self.domain = domain + self.granularity = granularity + + def __hash__(self): + return hash((type(self), self.domain, self.granularity)) + + def __eq__(self, other): + return (type(self) is type(other) + and self.domain == other.domain + and self.granularity == other.granularity) + + def __ne__(self, other): + return not self.__eq__(other) + + def __getinitargs__(self): + return (self.domain, self.granularity) + + def __repr__(self): + return '{}({}, {})'.format( + type(self).__name__, + repr(self.domain), + self.granularity.__name__) + + +def as_dofdesc(desc): + if isinstance(desc, DOFDescriptor): + return desc + return DOFDescriptor(desc) + + # }}} -- GitLab From e1872365ec3964d9176678ce2d1c31607c131561 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 15 Jun 2019 20:06:17 -0500 Subject: [PATCH 10/96] replace uses of _source_danger_zone_radii with a bind --- pytential/qbx/__init__.py | 3 +++ pytential/qbx/refinement.py | 9 +++++++-- test/test_global_qbx.py | 9 ++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 79860712..c0f20d60 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -492,6 +492,9 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): def _expansion_radii_factor(self): return 0.5 * self._dim_fudge_factor() + def _source_danger_zone_radii_factor(self): + return 0.75 * self._expansion_radii_factor() + @memoize_method def _expansion_radii(self, last_dim_length): from pytential import bind, sym diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index 0bc49b07..f94ac751 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -358,8 +358,13 @@ class RefinerWrangler(TreeWranglerBase): found_panel_to_refine = cl.array.zeros(self.queue, 1, np.int32) found_panel_to_refine.finish() - source_danger_zone_radii_by_panel = \ - lpot_source._source_danger_zone_radii("npanels") + from pytential import bind, sym + source_danger_zone_radii_by_panel = bind(lpot_source, + sym.qbx_expansion_radii( + lpot_source._source_danger_zone_radii_factor(), + lpot_source.ambient_dim, + granularity="npanels", + where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)))(self.queue) unwrap_args = AreaQueryElementwiseTemplate.unwrap_args evt = knl( diff --git a/test/test_global_qbx.py b/test/test_global_qbx.py index af340805..8589515a 100644 --- a/test/test_global_qbx.py +++ b/test/test_global_qbx.py @@ -42,6 +42,7 @@ from meshmode.mesh.generation import ( # noqa make_curve_mesh, generate_icosphere, generate_torus) from extra_curve_data import horseshoe +from pytential import bind, sym import logging logger = logging.getLogger(__name__) @@ -125,9 +126,11 @@ def run_source_refinement_test(ctx_factory, mesh, order, helmholtz_k=None): ext_centers = np.array([axis.get(queue) for axis in ext_centers]) expansion_radii = lpot_source._expansion_radii("nsources").get(queue) quad_res = lpot_source._coarsest_quad_resolution("npanels").get(queue) - source_danger_zone_radii = \ - lpot_source._source_danger_zone_radii("npanels").get(queue) - + source_danger_zone_radii = bind(lpot_source, sym.qbx_expansion_radii( + lpot_source._source_danger_zone_radii_factor(), + lpot_source.ambient_dim, + granularity="npanels", + where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)))(queue).get() # {{{ check if satisfying criteria def check_disk_undisturbed_by_sources(centers_panel, sources_panel): -- GitLab From 87915701e622641843fb477a5976d8afe05516ed Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 15 Jun 2019 20:14:36 -0500 Subject: [PATCH 11/96] replace uses of _coarsest_quad_resolution with a bind --- pytential/qbx/__init__.py | 37 +++++++++++++++++-------------------- pytential/qbx/refinement.py | 10 ++++++---- test/test_global_qbx.py | 4 +++- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index c0f20d60..03982b64 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -493,6 +493,19 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): return 0.5 * self._dim_fudge_factor() def _source_danger_zone_radii_factor(self): + # This should be the expression of the expansion radii, but + # + # - in reference to the stage 2 discretization + # - mutliplied by 0.75 because + # + # - Setting this equal to the expansion radii ensures that *every* + # stage 2 element will be refined, which is wasteful. + # (so this needs to be smaller than that) + # + + # - Setting this equal to half the expansion radius will not provide + # a refinement 'buffer layer' at a 2x coarsening fringe. + return 0.75 * self._expansion_radii_factor() @memoize_method @@ -510,18 +523,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): @memoize_method def _source_danger_zone_radii(self, last_dim_length="npanels"): - # This should be the expression of the expansion radii, but - # - # - in reference to the stage 2 discretization - # - mutliplied by 0.75 because - # - # - Setting this equal to the expansion radii ensures that *every* - # stage 2 element will be refined, which is wasteful. - # (so this needs to be smaller than that) - # - - # - Setting this equal to half the expansion radius will not provide - # a refinement 'buffer layer' at a 2x coarsening fringe. + raise RuntimeError("bind `qbx_expansion_radii` directly") from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: @@ -543,10 +545,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): @memoize_method def _coarsest_quad_resolution(self, last_dim_length="npanels"): - """This measures the quadrature resolution across the - mesh. In a 1D uniform mesh of uniform 'parametrization speed', it - should be the same as the panel length. - """ + raise RuntimeError("bind `qbx_quad_resolution` directly") from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: @@ -558,10 +557,8 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): @memoize_method def _stage2_coarsest_quad_resolution(self, last_dim_length="npanels"): - """This measures the quadrature resolution across the - mesh. In a 1D uniform mesh of uniform 'parametrization speed', it - should be the same as the panel length. - """ + raise RuntimeError("bind `qbx_quad_resolution` directly") + if last_dim_length != "npanels": # Not technically required below, but no need to loosen for now. raise NotImplementedError() diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index f94ac751..c815f332 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -404,7 +404,6 @@ class RefinerWrangler(TreeWranglerBase): evt, out = knl(self.queue, element_property=element_property, - # lpot_source._coarsest_quad_resolution("npanels")), refine_flags=refine_flags, refine_flags_updated=np.array(0), threshold=np.array(threshold), @@ -604,11 +603,14 @@ def refine_for_global_qbx(lpot_source, wrangler, with ProcessLogger(logger, "checking kernel length scale to panel size ratio"): + from pytential import bind, sym + quad_resolution = bind(lpot_source, sym.qbx_quad_resolution( + lpot_source.ambient_dim, + granularity="npanels"))(wrangler.queue) + violates_kernel_length_scale = \ wrangler.check_element_prop_threshold( - element_property=( - lpot_source._coarsest_quad_resolution( - "npanels")), + element_property=quad_resolution, threshold=kernel_length_scale, refine_flags=refine_flags, debug=debug) diff --git a/test/test_global_qbx.py b/test/test_global_qbx.py index 8589515a..21a2bd12 100644 --- a/test/test_global_qbx.py +++ b/test/test_global_qbx.py @@ -125,7 +125,9 @@ def run_source_refinement_test(ctx_factory, mesh, order, helmholtz_k=None): ext_centers = get_centers_on_side(lpot_source, +1) ext_centers = np.array([axis.get(queue) for axis in ext_centers]) expansion_radii = lpot_source._expansion_radii("nsources").get(queue) - quad_res = lpot_source._coarsest_quad_resolution("npanels").get(queue) + quad_res = bind(lpot_source, sym.qbx_quad_resolution( + lpot_source.ambient_dim, + granularity="npanels"))(queue) source_danger_zone_radii = bind(lpot_source, sym.qbx_expansion_radii( lpot_source._source_danger_zone_radii_factor(), lpot_source.ambient_dim, -- GitLab From 6cae0a9d22ca9080571964d0bb36a8de61202fee Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 15 Jun 2019 20:48:18 -0500 Subject: [PATCH 12/96] replace uses of _expansion_radii with a bind --- pytential/linalg/proxy.py | 8 +++++++- pytential/qbx/__init__.py | 11 ++++++++++- pytential/qbx/refinement.py | 6 +++++- pytential/qbx/target_assoc.py | 35 +++++++++++++++++++++++++---------- pytential/qbx/utils.py | 7 ++++++- pytential/symbolic/matrix.py | 10 ++++++++-- test/test_global_qbx.py | 21 ++++++++++++++------- test/test_linalg_proxy.py | 6 +++++- 8 files changed, 80 insertions(+), 24 deletions(-) diff --git a/pytential/linalg/proxy.py b/pytential/linalg/proxy.py index ffc3e0aa..0cf5301f 100644 --- a/pytential/linalg/proxy.py +++ b/pytential/linalg/proxy.py @@ -468,12 +468,18 @@ class ProxyGenerator(object): from pytential.qbx.utils import get_centers_on_side + from pytential import bind, sym + radii = bind(self.source, sym.qbx_expansion_radii( + self.source._expansion_radii_factor(), + self.source.ambient_dim, + granularity="nsources"))(queue) + knl = self.get_kernel() _, (centers_dev, radii_dev,) = knl(queue, sources=self.source.density_discr.nodes(), center_int=get_centers_on_side(self.source, -1), center_ext=get_centers_on_side(self.source, +1), - expansion_radii=self.source._expansion_radii("nsources"), + expansion_radii=radii, srcindices=indices.indices, srcranges=indices.ranges, **kwargs) centers = centers_dev.get() diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 03982b64..dbba4c36 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -467,8 +467,12 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): @property @memoize_method def h_max(self): + from pytential import bind, sym + with cl.CommandQueue(self.cl_context) as queue: - quad_res = self._coarsest_quad_resolution("npanels").with_queue(queue) + quad_res = bind(self, sym.qbx_quad_resolution( + self.ambient_dim, + granularity="npanels"))(queue) return cl.array.max(quad_res).get().item() # {{{ internal API @@ -508,6 +512,9 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): return 0.75 * self._expansion_radii_factor() + def _close_target_tunnel_radius_factor(self): + return 0.5 * self._expansion_radii_factor() + @memoize_method def _expansion_radii(self, last_dim_length): from pytential import bind, sym @@ -537,6 +544,8 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): @memoize_method def _close_target_tunnel_radius(self, last_dim_length): + raise RuntimeError("bind `qbx_expansion_radii` directly") + with cl.CommandQueue(self.cl_context) as queue: return ( self._expansion_radii(last_dim_length).with_queue(queue) diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index c815f332..74a2a1a4 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -304,7 +304,11 @@ class RefinerWrangler(TreeWranglerBase): found_panel_to_refine.finish() unwrap_args = AreaQueryElementwiseTemplate.unwrap_args - center_danger_zone_radii = lpot_source._expansion_radii("ncenters") + from pytential import bind, sym + center_danger_zone_radii = bind(lpot_source, sym.qbx_expansion_radii( + lpot_source._expansion_radii_factor(), + lpot_source.ambient_dim, + granularity="ncenters"))(self.queue) evt = knl( *unwrap_args( diff --git a/pytential/qbx/target_assoc.py b/pytential/qbx/target_assoc.py index 25170c5f..8c370dc1 100644 --- a/pytential/qbx/target_assoc.py +++ b/pytential/qbx/target_assoc.py @@ -452,12 +452,14 @@ class TargetAssociationWrangler(TreeWranglerBase): found_target_close_to_panel.finish() # Perform a space invader query over the sources. + from pytential import bind, sym source_slice = tree.sorted_target_ids[tree.qbx_user_source_slice] sources = [ axis.with_queue(self.queue)[source_slice] for axis in tree.sources] - tunnel_radius_by_source = ( - lpot_source._close_target_tunnel_radius("nsources") - .with_queue(self.queue)) + tunnel_radius_by_source = bind(lpot_source, sym.qbx_expansion_radii( + lpot_source._close_target_tunnel_radius_factor(), + lpot_source.ambient_dim, + granularity="nsources"))(self.queue) # Target-marking algorithm (TGTMARK): # @@ -493,7 +495,10 @@ class TargetAssociationWrangler(TreeWranglerBase): wait_for=wait_for) wait_for = [evt] - tunnel_radius_by_source = lpot_source._close_target_tunnel_radius("nsources") + tunnel_radius_by_source = bind(lpot_source, sym.qbx_expansion_radii( + lpot_source._close_target_tunnel_radius_factor(), + lpot_source.ambient_dim, + granularity="nsources"))(self.queue) evt = knl( *unwrap_args( @@ -544,13 +549,16 @@ class TargetAssociationWrangler(TreeWranglerBase): marked_target_count = int(cl.array.sum(target_status).get()) # Perform a space invader query over the centers. + from pytential import bind, sym center_slice = ( tree.sorted_target_ids[tree.qbx_user_center_slice] .with_queue(self.queue)) centers = [ axis.with_queue(self.queue)[center_slice] for axis in tree.sources] - expansion_radii_by_center = \ - lpot_source._expansion_radii("ncenters").with_queue(self.queue) + expansion_radii_by_center = bind(lpot_source, sym.qbx_expansion_radii( + lpot_source._expansion_radii_factor(), + lpot_source.ambient_dim, + granularity="ncenters"))(self.queue) expansion_radii_by_center_with_tolerance = \ expansion_radii_by_center * (1 + target_association_tolerance) @@ -625,12 +633,14 @@ class TargetAssociationWrangler(TreeWranglerBase): found_panel_to_refine.finish() # Perform a space invader query over the sources. + from pytential import bind, sym source_slice = tree.user_source_ids[tree.qbx_user_source_slice] sources = [ axis.with_queue(self.queue)[source_slice] for axis in tree.sources] - tunnel_radius_by_source = ( - lpot_source._close_target_tunnel_radius("nsources") - .with_queue(self.queue)) + tunnel_radius_by_source = bind(lpot_source, sym.qbx_expansion_radii( + lpot_source._close_target_tunnel_radius_factor(), + lpot_source.ambient_dim, + granularity="nsources"))(self.queue) # See (TGTMARK) above for algorithm. @@ -643,6 +653,11 @@ class TargetAssociationWrangler(TreeWranglerBase): wait_for=wait_for) wait_for = [evt] + tunnel_radius_by_source = bind(lpot_source, sym.qbx_expansion_radii( + lpot_source._close_target_tunnel_radius_factor(), + lpot_source.ambient_dim, + granularity="nsources"))(self.queue) + evt = knl( *unwrap_args( tree, peer_lists, @@ -653,7 +668,7 @@ class TargetAssociationWrangler(TreeWranglerBase): tree.qbx_user_target_slice.start, tree.nqbxpanels, tree.sorted_target_ids, - lpot_source._close_target_tunnel_radius("nsources"), + tunnel_radius_by_source, target_status, box_to_search_dist, refine_flags, diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index 54877c17..4149c38e 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -129,8 +129,13 @@ def get_interleaved_radii(queue, lpot_source): Return an array of shape (dim, ncenters) in which interior centers are placed next to corresponding exterior centers. """ + from pytential import bind, sym + knl = get_interleaver_kernel(lpot_source.density_discr.real_dtype) - radii = lpot_source._expansion_radii("nsources") + radii = bind(lpot_source, sym.qbx_expansion_radii( + lpot_source._expansion_radii_factor(), + lpot_source.ambient_dim, + granularity="nsources"))(queue) result = cl.array.empty(queue, len(radii) * 2, radii.dtype) evt, _ = knl(queue, src1=radii, src2=radii, dst=result) diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index 9d0c2b2f..024e31e4 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -156,11 +156,17 @@ def _get_centers_and_expansion_radii(queue, source, target_discr, qbx_forced_lim # NOTE: skip expensive target association from pytential.qbx.utils import get_centers_on_side centers = get_centers_on_side(source, qbx_forced_limit) - radii = source._expansion_radii('nsources') + radii = bind(source, sym.qbx_expansion_radii( + source._expansion_radii_factor(), + source.ambient_dim, + granularity="nsources"))(queue) else: from pytential.qbx.utils import get_interleaved_centers centers = get_interleaved_centers(queue, source) - radii = source._expansion_radii('ncenters') + radii = bind(source, sym.qbx_expansion_radii( + source._expansion_radii_factor(), + source.ambient_dim, + granularity="ncenters"))(queue) # NOTE: using a very small tolerance to make sure all the stage2 # targets are associated to a center. We can't use the user provided diff --git a/test/test_global_qbx.py b/test/test_global_qbx.py index 21a2bd12..75cf9a63 100644 --- a/test/test_global_qbx.py +++ b/test/test_global_qbx.py @@ -124,10 +124,13 @@ def run_source_refinement_test(ctx_factory, mesh, order, helmholtz_k=None): int_centers = np.array([axis.get(queue) for axis in int_centers]) ext_centers = get_centers_on_side(lpot_source, +1) ext_centers = np.array([axis.get(queue) for axis in ext_centers]) - expansion_radii = lpot_source._expansion_radii("nsources").get(queue) + expansion_radii = bind(lpot_source, sym.qbx_expansion_radii( + lpot_source._expansion_radii_factor(), + lpot_source.ambient_dim, + granularity="nsources"))(queue).get() quad_res = bind(lpot_source, sym.qbx_quad_resolution( - lpot_source.ambient_dim, - granularity="npanels"))(queue) + lpot_source.ambient_dim, + granularity="npanels"))(queue) source_danger_zone_radii = bind(lpot_source, sym.qbx_expansion_radii( lpot_source._source_danger_zone_radii_factor(), lpot_source.ambient_dim, @@ -263,8 +266,10 @@ def test_target_association(ctx_factory, curve_name, curve_f, nelements, rng = PhiloxGenerator(cl_ctx, seed=RNG_SEED) nsources = lpot_source.density_discr.nnodes noise = rng.uniform(queue, nsources, dtype=np.float, a=0.01, b=1.0) - tunnel_radius = \ - lpot_source._close_target_tunnel_radius("nsources").with_queue(queue) + tunnel_radius = bind(lpot_source, sym.qbx_expansion_radii( + lpot_source._close_target_tunnel_radius_factor(), + lpot_source.ambient_dim, + granularity="nsources"))(queue) def targets_from_sources(sign, dist): from pytential import sym, bind @@ -321,8 +326,10 @@ def test_target_association(ctx_factory, curve_name, curve_f, nelements, target_association_tolerance=1e-10) .get(queue=queue)) - expansion_radii = lpot_source._expansion_radii("ncenters").get(queue) - + expansion_radii = bind(lpot_source, sym.qbx_expansion_radii( + lpot_source._expansion_radii_factor(), + lpot_source.ambient_dim, + granularity="ncenters"))(queue).get() surf_targets = np.array( [axis.get(queue) for axis in lpot_source.density_discr.nodes()]) int_targets = np.array([axis.get(queue) for axis in int_targets.nodes()]) diff --git a/test/test_linalg_proxy.py b/test/test_linalg_proxy.py index e8a063ca..f6a415e3 100644 --- a/test/test_linalg_proxy.py +++ b/test/test_linalg_proxy.py @@ -31,6 +31,7 @@ import numpy.linalg as la import pyopencl as cl from pyopencl.array import to_device +from pytential import bind, sym from sumpy.tools import BlockIndexRanges from meshmode.mesh.generation import ( # noqa ellipse, NArmedStarfish, generate_torus, make_curve_mesh) @@ -281,7 +282,10 @@ def test_proxy_generator(ctx_factory, ndim, factor, visualize=False): ci = np.vstack([c.get(queue) for c in ci]) ce = get_centers_on_side(qbx, +1) ce = np.vstack([c.get(queue) for c in ce]) - r = qbx._expansion_radii("nsources").get(queue) + r = bind(qbx, sym.qbx_expansion_radii( + qbx._expansion_radii_factor(), + qbx.ambient_dim, + granularity="nsources"))(queue).get() for i in range(srcindices.nblocks): isrc = srcindices.block_indices(i) -- GitLab From f63a5c7ac61f15f90e013c1251fc505fdec807cf Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 15 Jun 2019 21:16:25 -0500 Subject: [PATCH 13/96] replace uses of get_centers_on_side with a bind --- pytential/linalg/proxy.py | 12 ++++++++---- pytential/qbx/__init__.py | 11 +++++++++-- pytential/qbx/utils.py | 12 ++++++++++-- pytential/symbolic/matrix.py | 18 ++++++++++-------- test/test_global_qbx.py | 20 +++++++++++++------- test/test_layer_pot_eigenvalues.py | 5 +++-- test/test_linalg_proxy.py | 9 ++++++--- 7 files changed, 59 insertions(+), 28 deletions(-) diff --git a/pytential/linalg/proxy.py b/pytential/linalg/proxy.py index 0cf5301f..154dd030 100644 --- a/pytential/linalg/proxy.py +++ b/pytential/linalg/proxy.py @@ -466,19 +466,23 @@ class ProxyGenerator(object): def _affine_map(v, A, b): return np.dot(A, v) + b - from pytential.qbx.utils import get_centers_on_side - from pytential import bind, sym radii = bind(self.source, sym.qbx_expansion_radii( self.source._expansion_radii_factor(), self.source.ambient_dim, granularity="nsources"))(queue) + center_int = bind(self.source, sym.qbx_expansion_centers( + self.source._expansion_radii_factor(), -1, + self.source.ambient_dim))(queue) + center_ext = bind(self.source, sym.qbx_expansion_centers( + self.source._expansion_radii_factor(), +1, + self.source.ambient_dim))(queue) knl = self.get_kernel() _, (centers_dev, radii_dev,) = knl(queue, sources=self.source.density_discr.nodes(), - center_int=get_centers_on_side(self.source, -1), - center_ext=get_centers_on_side(self.source, +1), + center_int=center_int, + center_ext=center_ext, expansion_radii=radii, srcindices=indices.indices, srcranges=indices.ranges, **kwargs) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index dbba4c36..88c9fead 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -846,7 +846,14 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): strengths = (evaluate(insn.density).with_queue(queue) * self.weights_and_area_elements()) - import pytential.qbx.utils as utils + from pytential import bind, sym + int_centers = bind(self, sym.qbx_expansion_centers( + self._expansion_radii_factor(), -1, + self.ambient_dim))(queue) + ext_centers = bind(self, sym.qbx_expansion_centers( + self._expansion_radii_factor(), +1, + self.ambient_dim))(queue) + centers = {-1: int_centers, 1: ext_centers} # FIXME: Do this all at once result = [] @@ -863,7 +870,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): evt, output_for_each_kernel = lpot_applier( queue, target_discr.nodes(), self.quad_stage2_density_discr.nodes(), - utils.get_centers_on_side(self, o.qbx_forced_limit), + centers[o.qbx_forced_limit], [strengths], expansion_radii=self._expansion_radii("nsources"), **kernel_args) diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index 4149c38e..98ac8372 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -102,9 +102,15 @@ 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 knl = get_interleaver_kernel(lpot_source.density_discr.real_dtype) - int_centers = get_centers_on_side(lpot_source, -1) - ext_centers = get_centers_on_side(lpot_source, +1) + + int_centers = bind(lpot_source, sym.qbx_expansion_centers( + lpot_source._expansion_radii_factor(), -1, + lpot_source.ambient_dim))(queue) + ext_centers = bind(lpot_source, sym.qbx_expansion_centers( + lpot_source._expansion_radii_factor(), +1, + lpot_source.ambient_dim))(queue) result = [] wait_for = [] @@ -303,6 +309,8 @@ def element_centers_of_mass(discr): # {{{ compute center array def get_centers_on_side(lpot_src, sign): + raise RuntimeError('bind `qbx_expansion_radii` directly') + from pytential import sym, bind with cl.CommandQueue(lpot_src.cl_context) as queue: return bind(lpot_src, sym.qbx_expansion_centers( diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index 024e31e4..e62704b1 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -154,19 +154,21 @@ def _get_centers_and_expansion_radii(queue, source, target_discr, qbx_forced_lim if source.density_discr is target_discr: # NOTE: skip expensive target association - from pytential.qbx.utils import get_centers_on_side - centers = get_centers_on_side(source, qbx_forced_limit) + centers = bind(source, sym.qbx_expansion_centers( + source._expansion_radii_factor(), + qbx_forced_limit, + source.ambient_dim))(queue) radii = bind(source, sym.qbx_expansion_radii( - source._expansion_radii_factor(), - source.ambient_dim, - granularity="nsources"))(queue) + source._expansion_radii_factor(), + source.ambient_dim, + granularity="nsources"))(queue) else: from pytential.qbx.utils import get_interleaved_centers centers = get_interleaved_centers(queue, source) radii = bind(source, sym.qbx_expansion_radii( - source._expansion_radii_factor(), - source.ambient_dim, - granularity="ncenters"))(queue) + source._expansion_radii_factor(), + source.ambient_dim, + granularity="ncenters"))(queue) # NOTE: using a very small tolerance to make sure all the stage2 # targets are associated to a center. We can't use the user provided diff --git a/test/test_global_qbx.py b/test/test_global_qbx.py index 75cf9a63..ee516b69 100644 --- a/test/test_global_qbx.py +++ b/test/test_global_qbx.py @@ -115,27 +115,33 @@ def run_source_refinement_test(ctx_factory, mesh, order, helmholtz_k=None): cl_ctx, TreeCodeContainer(cl_ctx)).get_wrangler(queue), factory, **refiner_extra_kwargs) - from pytential.qbx.utils import get_centers_on_side - discr_nodes = lpot_source.density_discr.nodes().get(queue) fine_discr_nodes = \ lpot_source.quad_stage2_density_discr.nodes().get(queue) - int_centers = get_centers_on_side(lpot_source, -1) + + int_centers = bind(lpot_source, sym.qbx_expansion_centers( + lpot_source._expansion_radii_factor(), -1, + lpot_source.ambient_dim))(queue) int_centers = np.array([axis.get(queue) for axis in int_centers]) - ext_centers = get_centers_on_side(lpot_source, +1) + ext_centers = bind(lpot_source, sym.qbx_expansion_centers( + lpot_source._expansion_radii_factor(), +1, + lpot_source.ambient_dim))(queue) ext_centers = np.array([axis.get(queue) for axis in ext_centers]) + expansion_radii = bind(lpot_source, sym.qbx_expansion_radii( lpot_source._expansion_radii_factor(), lpot_source.ambient_dim, granularity="nsources"))(queue).get() - quad_res = bind(lpot_source, sym.qbx_quad_resolution( - lpot_source.ambient_dim, - granularity="npanels"))(queue) source_danger_zone_radii = bind(lpot_source, sym.qbx_expansion_radii( lpot_source._source_danger_zone_radii_factor(), lpot_source.ambient_dim, granularity="npanels", where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)))(queue).get() + + quad_res = bind(lpot_source, sym.qbx_quad_resolution( + lpot_source.ambient_dim, + granularity="npanels"))(queue) + # {{{ check if satisfying criteria def check_disk_undisturbed_by_sources(centers_panel, sources_panel): diff --git a/test/test_layer_pot_eigenvalues.py b/test/test_layer_pot_eigenvalues.py index fdfdc937..fb07ce68 100644 --- a/test/test_layer_pot_eigenvalues.py +++ b/test/test_layer_pot_eigenvalues.py @@ -119,8 +119,9 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, if 0: # plot geometry, centers, normals - from pytential.qbx.utils import get_centers_on_side - centers = get_centers_on_side(qbx, 1) + centers = bind(qbx, sym.qbx_expansion_centers( + qbx._expansion_radii_factor(), +1, + qbx.ambient_dim))(queue) nodes_h = nodes.get() centers_h = [centers[0].get(), centers[1].get()] diff --git a/test/test_linalg_proxy.py b/test/test_linalg_proxy.py index f6a415e3..447b5a54 100644 --- a/test/test_linalg_proxy.py +++ b/test/test_linalg_proxy.py @@ -275,12 +275,15 @@ def test_proxy_generator(ctx_factory, ndim, factor, visualize=False): if visualize: if qbx.ambient_dim == 2: import matplotlib.pyplot as pt - from pytential.qbx.utils import get_centers_on_side density_nodes = qbx.density_discr.nodes().get(queue) - ci = get_centers_on_side(qbx, -1) + ci = bind(qbx, sym.qbx_expansion_centers( + qbx._expansion_radii_factor(), -1, + qbx.ambient_dim))(queue) ci = np.vstack([c.get(queue) for c in ci]) - ce = get_centers_on_side(qbx, +1) + ce = bind(qbx, sym.qbx_expansion_centers( + qbx._expansion_radii_factor(), +1, + qbx.ambient_dim))(queue) ce = np.vstack([c.get(queue) for c in ce]) r = bind(qbx, sym.qbx_expansion_radii( qbx._expansion_radii_factor(), -- GitLab From 1573b1e633af69ed9d0300b12ff0976201e25d42 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 15 Jun 2019 21:31:05 -0500 Subject: [PATCH 14/96] make _expansion_radii_factor and friends properties --- pytential/linalg/proxy.py | 6 +++--- pytential/qbx/__init__.py | 26 ++++++++++++++++++-------- pytential/qbx/refinement.py | 4 ++-- pytential/qbx/target_assoc.py | 10 +++++----- pytential/qbx/utils.py | 8 ++++---- pytential/symbolic/matrix.py | 6 +++--- test/test_global_qbx.py | 12 ++++++------ test/test_layer_pot_eigenvalues.py | 2 +- test/test_linalg_proxy.py | 6 +++--- 9 files changed, 45 insertions(+), 35 deletions(-) diff --git a/pytential/linalg/proxy.py b/pytential/linalg/proxy.py index 154dd030..ef0653fa 100644 --- a/pytential/linalg/proxy.py +++ b/pytential/linalg/proxy.py @@ -468,14 +468,14 @@ class ProxyGenerator(object): from pytential import bind, sym radii = bind(self.source, sym.qbx_expansion_radii( - self.source._expansion_radii_factor(), + self.source._expansion_radii_factor, self.source.ambient_dim, granularity="nsources"))(queue) center_int = bind(self.source, sym.qbx_expansion_centers( - self.source._expansion_radii_factor(), -1, + self.source._expansion_radii_factor, -1, self.source.ambient_dim))(queue) center_ext = bind(self.source, sym.qbx_expansion_centers( - self.source._expansion_radii_factor(), +1, + self.source._expansion_radii_factor, +1, self.source.ambient_dim))(queue) knl = self.get_kernel() diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 88c9fead..697c7c5e 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -487,15 +487,18 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): import pytential.qbx.utils as utils return utils.element_centers_of_mass(self.stage2_density_discr) + @property def _dim_fudge_factor(self): if self.density_discr.dim == 2: return 0.5 else: return 1 + @property def _expansion_radii_factor(self): - return 0.5 * self._dim_fudge_factor() + return 0.5 * self._dim_fudge_factor + @property def _source_danger_zone_radii_factor(self): # This should be the expression of the expansion radii, but # @@ -510,17 +513,20 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): # - Setting this equal to half the expansion radius will not provide # a refinement 'buffer layer' at a 2x coarsening fringe. - return 0.75 * self._expansion_radii_factor() + return 0.75 * self._expansion_radii_factor + @property def _close_target_tunnel_radius_factor(self): - return 0.5 * self._expansion_radii_factor() + return 0.5 * self._expansion_radii_factor @memoize_method def _expansion_radii(self, last_dim_length): + raise RuntimeError("bind `qbx_expansion_radii` directly") + from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: radii = bind(self, sym.qbx_expansion_radii( - self._expansion_radii_factor(), + self._expansion_radii_factor, self.ambient_dim, granularity=last_dim_length))(queue) @@ -535,7 +541,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: radii = bind(self, sym.qbx_expansion_radii( - 0.75 * self._expansion_radii_factor(), + 0.75 * self._expansion_radii_factor, self.ambient_dim, granularity=last_dim_length, where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)))(queue) @@ -847,11 +853,15 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): * self.weights_and_area_elements()) from pytential import bind, sym + expansion_radii = bind(self, sym.qbx_expansion_radii( + self._expansion_radii_factor, + self.ambient_dim, + granularity="nsources"))(queue) int_centers = bind(self, sym.qbx_expansion_centers( - self._expansion_radii_factor(), -1, + self._expansion_radii_factor, -1, self.ambient_dim))(queue) ext_centers = bind(self, sym.qbx_expansion_centers( - self._expansion_radii_factor(), +1, + self._expansion_radii_factor, +1, self.ambient_dim))(queue) centers = {-1: int_centers, 1: ext_centers} @@ -872,7 +882,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): self.quad_stage2_density_discr.nodes(), centers[o.qbx_forced_limit], [strengths], - expansion_radii=self._expansion_radii("nsources"), + expansion_radii=expansion_radii, **kernel_args) result.append((o.name, output_for_each_kernel[o.kernel_index])) else: diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index 74a2a1a4..37ae13dc 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -306,7 +306,7 @@ class RefinerWrangler(TreeWranglerBase): from pytential import bind, sym center_danger_zone_radii = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._expansion_radii_factor(), + lpot_source._expansion_radii_factor, lpot_source.ambient_dim, granularity="ncenters"))(self.queue) @@ -365,7 +365,7 @@ class RefinerWrangler(TreeWranglerBase): from pytential import bind, sym source_danger_zone_radii_by_panel = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._source_danger_zone_radii_factor(), + lpot_source._source_danger_zone_radii_factor, lpot_source.ambient_dim, granularity="npanels", where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)))(self.queue) diff --git a/pytential/qbx/target_assoc.py b/pytential/qbx/target_assoc.py index 8c370dc1..6a206313 100644 --- a/pytential/qbx/target_assoc.py +++ b/pytential/qbx/target_assoc.py @@ -457,7 +457,7 @@ class TargetAssociationWrangler(TreeWranglerBase): sources = [ axis.with_queue(self.queue)[source_slice] for axis in tree.sources] tunnel_radius_by_source = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._close_target_tunnel_radius_factor(), + lpot_source._close_target_tunnel_radius_factor, lpot_source.ambient_dim, granularity="nsources"))(self.queue) @@ -496,7 +496,7 @@ class TargetAssociationWrangler(TreeWranglerBase): wait_for = [evt] tunnel_radius_by_source = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._close_target_tunnel_radius_factor(), + lpot_source._close_target_tunnel_radius_factor, lpot_source.ambient_dim, granularity="nsources"))(self.queue) @@ -556,7 +556,7 @@ class TargetAssociationWrangler(TreeWranglerBase): centers = [ axis.with_queue(self.queue)[center_slice] for axis in tree.sources] expansion_radii_by_center = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._expansion_radii_factor(), + lpot_source._expansion_radii_factor, lpot_source.ambient_dim, granularity="ncenters"))(self.queue) expansion_radii_by_center_with_tolerance = \ @@ -638,7 +638,7 @@ class TargetAssociationWrangler(TreeWranglerBase): sources = [ axis.with_queue(self.queue)[source_slice] for axis in tree.sources] tunnel_radius_by_source = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._close_target_tunnel_radius_factor(), + lpot_source._close_target_tunnel_radius_factor, lpot_source.ambient_dim, granularity="nsources"))(self.queue) @@ -654,7 +654,7 @@ class TargetAssociationWrangler(TreeWranglerBase): wait_for = [evt] tunnel_radius_by_source = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._close_target_tunnel_radius_factor(), + lpot_source._close_target_tunnel_radius_factor, lpot_source.ambient_dim, granularity="nsources"))(self.queue) diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index 98ac8372..56b6e656 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -106,10 +106,10 @@ def get_interleaved_centers(queue, lpot_source): knl = get_interleaver_kernel(lpot_source.density_discr.real_dtype) int_centers = bind(lpot_source, sym.qbx_expansion_centers( - lpot_source._expansion_radii_factor(), -1, + lpot_source._expansion_radii_factor, -1, lpot_source.ambient_dim))(queue) ext_centers = bind(lpot_source, sym.qbx_expansion_centers( - lpot_source._expansion_radii_factor(), +1, + lpot_source._expansion_radii_factor, +1, lpot_source.ambient_dim))(queue) result = [] @@ -139,7 +139,7 @@ def get_interleaved_radii(queue, lpot_source): knl = get_interleaver_kernel(lpot_source.density_discr.real_dtype) radii = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._expansion_radii_factor(), + lpot_source._expansion_radii_factor, lpot_source.ambient_dim, granularity="nsources"))(queue) @@ -314,7 +314,7 @@ def get_centers_on_side(lpot_src, sign): from pytential import sym, bind with cl.CommandQueue(lpot_src.cl_context) as queue: return bind(lpot_src, sym.qbx_expansion_centers( - lpot_src._expansion_radii_factor(), + lpot_src._expansion_radii_factor, sign, lpot_src.ambient_dim))(queue) diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index e62704b1..231e3437 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -155,18 +155,18 @@ def _get_centers_and_expansion_radii(queue, source, target_discr, qbx_forced_lim if source.density_discr is target_discr: # NOTE: skip expensive target association centers = bind(source, sym.qbx_expansion_centers( - source._expansion_radii_factor(), + source._expansion_radii_factor, qbx_forced_limit, source.ambient_dim))(queue) radii = bind(source, sym.qbx_expansion_radii( - source._expansion_radii_factor(), + source._expansion_radii_factor, source.ambient_dim, granularity="nsources"))(queue) else: from pytential.qbx.utils import get_interleaved_centers centers = get_interleaved_centers(queue, source) radii = bind(source, sym.qbx_expansion_radii( - source._expansion_radii_factor(), + source._expansion_radii_factor, source.ambient_dim, granularity="ncenters"))(queue) diff --git a/test/test_global_qbx.py b/test/test_global_qbx.py index ee516b69..efb01053 100644 --- a/test/test_global_qbx.py +++ b/test/test_global_qbx.py @@ -120,20 +120,20 @@ def run_source_refinement_test(ctx_factory, mesh, order, helmholtz_k=None): lpot_source.quad_stage2_density_discr.nodes().get(queue) int_centers = bind(lpot_source, sym.qbx_expansion_centers( - lpot_source._expansion_radii_factor(), -1, + lpot_source._expansion_radii_factor, -1, lpot_source.ambient_dim))(queue) int_centers = np.array([axis.get(queue) for axis in int_centers]) ext_centers = bind(lpot_source, sym.qbx_expansion_centers( - lpot_source._expansion_radii_factor(), +1, + lpot_source._expansion_radii_factor, +1, lpot_source.ambient_dim))(queue) ext_centers = np.array([axis.get(queue) for axis in ext_centers]) expansion_radii = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._expansion_radii_factor(), + lpot_source._expansion_radii_factor, lpot_source.ambient_dim, granularity="nsources"))(queue).get() source_danger_zone_radii = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._source_danger_zone_radii_factor(), + lpot_source._source_danger_zone_radii_factor, lpot_source.ambient_dim, granularity="npanels", where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)))(queue).get() @@ -273,7 +273,7 @@ def test_target_association(ctx_factory, curve_name, curve_f, nelements, nsources = lpot_source.density_discr.nnodes noise = rng.uniform(queue, nsources, dtype=np.float, a=0.01, b=1.0) tunnel_radius = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._close_target_tunnel_radius_factor(), + lpot_source._close_target_tunnel_radius_factor, lpot_source.ambient_dim, granularity="nsources"))(queue) @@ -333,7 +333,7 @@ def test_target_association(ctx_factory, curve_name, curve_f, nelements, .get(queue=queue)) expansion_radii = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._expansion_radii_factor(), + lpot_source._expansion_radii_factor, lpot_source.ambient_dim, granularity="ncenters"))(queue).get() surf_targets = np.array( diff --git a/test/test_layer_pot_eigenvalues.py b/test/test_layer_pot_eigenvalues.py index fb07ce68..0c6de241 100644 --- a/test/test_layer_pot_eigenvalues.py +++ b/test/test_layer_pot_eigenvalues.py @@ -120,7 +120,7 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, # plot geometry, centers, normals centers = bind(qbx, sym.qbx_expansion_centers( - qbx._expansion_radii_factor(), +1, + qbx._expansion_radii_factor, +1, qbx.ambient_dim))(queue) nodes_h = nodes.get() diff --git a/test/test_linalg_proxy.py b/test/test_linalg_proxy.py index 447b5a54..385a37a4 100644 --- a/test/test_linalg_proxy.py +++ b/test/test_linalg_proxy.py @@ -278,15 +278,15 @@ def test_proxy_generator(ctx_factory, ndim, factor, visualize=False): density_nodes = qbx.density_discr.nodes().get(queue) ci = bind(qbx, sym.qbx_expansion_centers( - qbx._expansion_radii_factor(), -1, + qbx._expansion_radii_factor, -1, qbx.ambient_dim))(queue) ci = np.vstack([c.get(queue) for c in ci]) ce = bind(qbx, sym.qbx_expansion_centers( - qbx._expansion_radii_factor(), +1, + qbx._expansion_radii_factor, +1, qbx.ambient_dim))(queue) ce = np.vstack([c.get(queue) for c in ce]) r = bind(qbx, sym.qbx_expansion_radii( - qbx._expansion_radii_factor(), + qbx._expansion_radii_factor, qbx.ambient_dim, granularity="nsources"))(queue).get() -- GitLab From a8bcc94e640c178ca8ed7ab2b6826dd5925cb04b Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Wed, 26 Jun 2019 10:11:10 -0500 Subject: [PATCH 15/96] primitives: remove useless getinitargs --- pytential/symbolic/primitives.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 7f15d1a3..0e0b8e02 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -266,7 +266,6 @@ class DOMAIN_TAG(object): # noqa .. attribute:: tag """ - init_arg_names = ("tag",) def __init__(self, tag): self.tag = tag @@ -280,9 +279,6 @@ class DOMAIN_TAG(object): # noqa def __ne__(self, other): return not self.__eq__(other) - def __getinitargs__(self): - return (self.tag,) - def __repr__(self): if isinstance(self.tag, str): tag = self.tag -- GitLab From 3054252bf7830a97b10f51d8ae09779a75824a89 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Wed, 26 Jun 2019 17:15:40 +0200 Subject: [PATCH 16/96] Apply suggestion to pytential/symbolic/primitives.py --- pytential/symbolic/primitives.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 7f15d1a3..0f059239 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -326,7 +326,12 @@ class QBX_DOF_ELEMENT: # noqa class DOFDescriptor(object): - """Descriptor for degrees of freedom on a domain. + """A data structure specifying the meaning of a vector of degrees of freedom + that is handled by :mod:`pytential` (a "DOF vector"). In particular, using + :attr:`domain`, this data structure describes the geometric object on which + the (scalar) function described by the DOF vector exists. Using + :attr:`granularity`, the data structure describes how the geometric object + is discretized (e.g. conventional nodal data, per-element scalars, etc.) .. attribute:: domain -- GitLab From 0b3cd5c7b4ec2b9ce51dc199718892dac1c8a9ad Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Wed, 26 Jun 2019 17:17:14 +0200 Subject: [PATCH 17/96] Apply suggestion to pytential/symbolic/primitives.py --- pytential/symbolic/primitives.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 0f059239..a5d60077 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -991,7 +991,7 @@ def qbx_expansion_radii(factor, ambient_dim, def qbx_expansion_centers(factor, side, ambient_dim, dim=None, where=None): """ - :arg factor: stick out factor for expansion radii. + :arg factor: target confinement factor for expansion radii. :arg side: `+1` or `-1` expansion side, relative to the direction of the normal vector. """ -- GitLab From e411243a25dfe987ba98ca94a9bd0171412f207c Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Wed, 26 Jun 2019 10:28:29 -0500 Subject: [PATCH 18/96] primitives: use specific noqa ids --- pytential/symbolic/primitives.py | 38 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 0e0b8e02..3582107e 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -203,11 +203,11 @@ Pretty-printing expressions # {{{ 'where' specifiers -class DEFAULT_SOURCE: # noqa +class DEFAULT_SOURCE: # noqa: N801 pass -class DEFAULT_TARGET: # noqa +class DEFAULT_TARGET: # noqa: N801 pass @@ -261,7 +261,7 @@ class QBXSourceQuadStage2(_QBXSource): """ -class DOMAIN_TAG(object): # noqa +class DOMAIN_TAG(object): # noqa: N801 """General domain specifier .. attribute:: tag @@ -288,35 +288,35 @@ class DOMAIN_TAG(object): # noqa return "{}({})".format(type(self).__name__, tag) -class QBX_DOMAIN_STAGE1(DOMAIN_TAG): # noqa +class QBX_DOMAIN_STAGE1(DOMAIN_TAG): # noqa: N801 """Specifier for - :attr:~pytential.qbx.QBXLayerPotentialSource.density_discr`.""" + :attr:`pytential.qbx.QBXLayerPotentialSource.density_discr`.""" pass -class QBX_DOMAIN_STAGE2(DOMAIN_TAG): # noqa +class QBX_DOMAIN_STAGE2(DOMAIN_TAG): # noqa: N801 """Specifier for - :attr:~pytential.qbx.QBXLayerPotentialSource.stage2_density_discr`.""" + :attr:`pytential.qbx.QBXLayerPotentialSource.stage2_density_discr`.""" pass -class QBX_DOMAIN_QUAD_STAGE2(DOMAIN_TAG): # noqa +class QBX_DOMAIN_QUAD_STAGE2(DOMAIN_TAG): # noqa: N801 """Specifier for - :attr:~pytential.qbx.QBXLayerPotentialSource.quad_stage2_density_discr`.""" + :attr:`pytential.qbx.QBXLayerPotentialSource.quad_stage2_density_discr`.""" pass -class QBX_DOF_NODE: # noqa +class QBX_DOF_NODE: # noqa: N801 """DOFs are per-source node.""" pass -class QBX_DOF_CENTER: # noqa +class QBX_DOF_CENTER: # noqa: N801 """DOFs interleaved per expansion center.""" pass -class QBX_DOF_ELEMENT: # noqa +class QBX_DOF_ELEMENT: # noqa: N801 """DOFs per discretization element.""" pass @@ -398,7 +398,7 @@ def as_dofdesc(desc): # }}} -class cse_scope(cse_scope_base): # noqa +class cse_scope(cse_scope_base): # noqa: N801 DISCRETIZATION = "pytential_discretization" @@ -1512,11 +1512,11 @@ def int_g_dsource(ambient_dim, dsource, kernel, density, # {{{ geometric calculus -class _unspecified: # noqa +class _unspecified: # noqa: N801 pass -def S(kernel, density, # noqa +def S(kernel, density, qbx_forced_limit=_unspecified, source=None, target=None, kernel_arguments=None, **kwargs): @@ -1547,7 +1547,7 @@ def normal_derivative(ambient_dim, operand, dim=None, where=None): * d(operand)) -def Sp(kernel, *args, **kwargs): # noqa +def Sp(kernel, *args, **kwargs): where = kwargs.get("target") if "qbx_forced_limit" not in kwargs: warn("not specifying qbx_forced_limit on call to 'Sp' is deprecated, " @@ -1569,7 +1569,7 @@ def Sp(kernel, *args, **kwargs): # noqa dim=dim, where=where) -def Spp(kernel, *args, **kwargs): # noqa +def Spp(kernel, *args, **kwargs): ambient_dim = kwargs.get("ambient_dim") from sumpy.kernel import Kernel if ambient_dim is None and isinstance(kernel, Kernel): @@ -1586,7 +1586,7 @@ def Spp(kernel, *args, **kwargs): # noqa dim=dim, where=where) -def D(kernel, *args, **kwargs): # noqa +def D(kernel, *args, **kwargs): ambient_dim = kwargs.get("ambient_dim") from sumpy.kernel import Kernel if ambient_dim is None and isinstance(kernel, Kernel): @@ -1609,7 +1609,7 @@ def D(kernel, *args, **kwargs): # noqa kernel, *args, **kwargs).xproject(0) -def Dp(kernel, *args, **kwargs): # noqa +def Dp(kernel, *args, **kwargs): ambient_dim = kwargs.get("ambient_dim") from sumpy.kernel import Kernel if ambient_dim is None and isinstance(kernel, Kernel): -- GitLab From bae05a5fb66c638859c7bc709230d6e3d3a8b66b Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Wed, 26 Jun 2019 21:01:36 -0500 Subject: [PATCH 19/96] primitives: fix some remarks --- pytential/symbolic/primitives.py | 140 ++++++++++++++----------------- 1 file changed, 61 insertions(+), 79 deletions(-) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 50a83ec3..4eadc2f6 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -41,8 +41,10 @@ from functools import partial __doc__ = """ -.. |where-blurb| replace:: A symbolic name for a - :class:`pytential.discretization.Discretization` +.. |where-blurb| replace:: A symbolic name for a geometric object (such + as a :class:`~meshmode.discretization.Discretization`) or a + :class:`DOFDescriptor`. + Object types ^^^^^^^^^^^^ @@ -261,62 +263,35 @@ class QBXSourceQuadStage2(_QBXSource): """ -class DOMAIN_TAG(object): # noqa: N801 - """General domain specifier - - .. attribute:: tag +class QBX_SOURCE_STAGE1: # noqa: N801 + """Symbolic identifier for the base `stage1` discretization + :attr:`pytential.source.LayerPotentialSourceBase.density_discr`. """ - def __init__(self, tag): - self.tag = tag - - def __hash__(self): - return hash((type(self), self.tag)) - - def __eq__(self, other): - return type(self) is type(other) and self.tag == other.tag - - def __ne__(self, other): - return not self.__eq__(other) - - def __repr__(self): - if isinstance(self.tag, str): - tag = self.tag - else: - tag = tag.__name__ if isinstance(tag, type) else type(tag).__name__ - - return "{}({})".format(type(self).__name__, tag) - - -class QBX_DOMAIN_STAGE1(DOMAIN_TAG): # noqa: N801 - """Specifier for - :attr:`pytential.qbx.QBXLayerPotentialSource.density_discr`.""" - pass - - -class QBX_DOMAIN_STAGE2(DOMAIN_TAG): # noqa: N801 - """Specifier for - :attr:`pytential.qbx.QBXLayerPotentialSource.stage2_density_discr`.""" - pass +class QBX_SOURCE_STAGE2: # noqa: N801 + """Symbolic identifier for the `stage2` discretization + :attr:`pytential.source.LayerPotentialSourceBase.stage2_density_discr`. + """ -class QBX_DOMAIN_QUAD_STAGE2(DOMAIN_TAG): # noqa: N801 - """Specifier for - :attr:`pytential.qbx.QBXLayerPotentialSource.quad_stage2_density_discr`.""" +class QBX_SOURCE_QUAD_STAGE2: # noqa: N801 + """Symbolic identifier for the `stage2` discretization + :attr:`pytential.source.LayerPotentialSourceBase.quad_stage2_density_discr`. + """ pass -class QBX_DOF_NODE: # noqa: N801 +class GRANULARITY_NODE: # noqa: N801 """DOFs are per-source node.""" pass -class QBX_DOF_CENTER: # noqa: N801 +class GRANULARITY_CENTER: # noqa: N801 """DOFs interleaved per expansion center.""" pass -class QBX_DOF_ELEMENT: # noqa: N801 +class GRANULARITY_ELEMENT: # noqa: N801 """DOFs per discretization element.""" pass @@ -324,73 +299,80 @@ class QBX_DOF_ELEMENT: # noqa: N801 class DOFDescriptor(object): """A data structure specifying the meaning of a vector of degrees of freedom that is handled by :mod:`pytential` (a "DOF vector"). In particular, using - :attr:`domain`, this data structure describes the geometric object on which + :attr:`where`, this data structure describes the geometric object on which the (scalar) function described by the DOF vector exists. Using :attr:`granularity`, the data structure describes how the geometric object is discretized (e.g. conventional nodal data, per-element scalars, etc.) - .. attribute:: domain + .. attribute:: where - Describes the domain on which a DOF is defined. The domain contains - an internal tag for which exact + An identifier for the domain on which the DOFs exist. This can be a + simple string or another hashable identifier for the geometric object. + The geometric objects are generally subclasses of :class:`~pytential.source.PotentialSource`, :class:`~pytential.target.TargetBase` or - :class:`~meshmode.discretization.Discretization` it refers to. - Can be a generic :class:`QBX_DOMAIN` or one of - :class:`QBX_DOMAIN_STAGE1`, :class:`QBX_DOMAIN_STAGE2` or - :class:`QBX_DOMAIN_QUAD_STAGE2`. + :class:`~meshmode.discretization.Discretization`. + + .. attribute:: discr + + Specific to a :class:`pytential.source.LayerPotentialSourceBase`, + this discribes on which of the discretizations the + DOFs are defined. Can be one of :class:`QBX_SOURCE_STAGE1`, + :class:`QBX_SOURCE_STAGE2` or :class:`QBX_SOURCE_QUAD_STAGE2`. .. attribute:: granularity Describes the level of granularity of the DOF. - Can be one of :class:`QBX_DOF_NODE`, :class:`QBX_DOF_CENTER` or - :class:`QBX_DOF_ELEMENT`. + Can be one of :class:`GRANULARITY_NODE`, :class:`GRANULARITY_CENTER` or + :class:`GRANULARITY_ELEMENT`. """ - init_arg_names = ("domain", "granularity") - - def __init__(self, domain, granularity=None): - if (domain == DEFAULT_SOURCE - or domain == DEFAULT_TARGET - or isinstance(domain, str)): - domain = DOMAIN_TAG(domain) - + def __init__(self, where, discr=None, granularity=None): if granularity is None: - granularity = QBX_DOF_NODE + granularity = GRANULARITY_NODE - if not (isinstance(domain, DOMAIN_TAG) - or isinstance(domain, QBX_DOMAIN_STAGE1) - or isinstance(domain, QBX_DOMAIN_STAGE2) - or isinstance(domain, QBX_DOMAIN_QUAD_STAGE2)): - raise ValueError('unknown domain tag: {}'.format(domain)) + if discr is not None: + if not (discr is QBX_SOURCE_STAGE1 + or discr is QBX_SOURCE_STAGE2 + or discr is QBX_SOURCE_QUAD_STAGE2): + raise ValueError('unknown discr tag: "{}"'.format(discr)) - if not (granularity == QBX_DOF_NODE - or granularity == QBX_DOF_CENTER - or granularity == QBX_DOF_ELEMENT): - raise ValueError('unknown granularity: {}'.format(granularity)) + if not (granularity is GRANULARITY_NODE + or granularity is GRANULARITY_CENTER + or granularity is GRANULARITY_ELEMENT): + raise ValueError('unknown granularity: "{}"'.format(granularity)) - self.domain = domain + self.where = where + self.discr = discr self.granularity = granularity + def copy(self, where=None, discr=None, granularity=None): + return type(self)( + where=(self.where + if where is None else where), + granularity=(self.granularity + if granularity is None else granularity), + discr=(self.discr + if discr is None else discr)) + def __hash__(self): - return hash((type(self), self.domain, self.granularity)) + return hash((type(self), self.where, self.discr, self.granularity)) def __eq__(self, other): return (type(self) is type(other) - and self.domain == other.domain + and self.where == other.where + and self.discr == other.discr and self.granularity == other.granularity) def __ne__(self, other): return not self.__eq__(other) - def __getinitargs__(self): - return (self.domain, self.granularity) - def __repr__(self): - return '{}({}, {})'.format( + return '{}(where={}, discr={}, granularity={})'.format( type(self).__name__, - repr(self.domain), + self.where, + self.discr if self.discr is None else self.discr.__name__, self.granularity.__name__) -- GitLab From bb9a004a8e6402ae793ce3ec9d5e548065638544 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Wed, 26 Jun 2019 21:29:28 -0500 Subject: [PATCH 20/96] make use of dofdesc class --- pytential/symbolic/execution.py | 17 ++++---- pytential/symbolic/mappers.py | 66 +++++++++++++++++++------------- pytential/symbolic/primitives.py | 17 +++++++- 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 14383cd3..3a1d9425 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -473,25 +473,28 @@ class GeometryCollection(object): in its attributes instead. """ - if where in self.places: - discr = self.places[where] + dd = sym.as_dofdesc(where) + if dd.where in self.places: + discr = self.places[dd.where] else: - discr = self.places.get(getattr(where, 'where', None), None) + discr = None if discr is None: - raise KeyError('`where` not in the collection: {}'.format(where)) + raise KeyError('`where` not in the collection: {}'.format(dd.where)) from pytential.source import LayerPotentialSourceBase if isinstance(discr, LayerPotentialSourceBase): - return self._get_lpot_discretization(discr, where) + return self._get_lpot_discretization(discr, dd.where) else: return discr def __getitem__(self, where): - return self.places[where] + dd = sym.as_dofdesc(where) + return self.places[dd.where] def __contains__(self, where): - return where in self.places + dd = sym.as_dofdesc(where) + return dd.where in self.places def copy(self): return GeometryCollection( diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 85c3743a..8aa9ba48 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -220,50 +220,61 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): IdentityMapper.map_common_subexpression def map_ones(self, expr): - if expr.where is None: - return type(expr)(where=self.default_where) + dd = prim.as_dofdesc(expr.where) + if dd.where is None: + return type(expr)(where=dd.copy(where=self.default_where)) else: return expr map_q_weight = map_ones def map_parametrization_derivative_component(self, expr): - if expr.where is None: + dd = prim.as_dofdesc(expr.where) + if dd.where is None: return type(expr)( - expr.ambient_axis, expr.ref_axis, self.default_where) + expr.ambient_axis, expr.ref_axis, + dd.copy(where=self.default_where)) else: return expr def map_node_coordinate_component(self, expr): - if expr.where is None: + dd = prim.as_dofdesc(expr.where) + if dd.where is None: return type(expr)( - expr.ambient_axis, self.default_where) + expr.ambient_axis, + dd.copy(where=self.default_where)) else: return expr def map_num_reference_derivative(self, expr): - if expr.where is None: + dd = prim.as_dofdesc(expr.where) + if dd.where is None: return type(expr)( - expr.ref_axes, self.rec(expr.operand), self.default_where) + expr.ref_axes, self.rec(expr.operand), + dd.copy(where=self.default_where)) else: return expr def map_elementwise_sum(self, expr): - return type(expr)( - self.rec(expr.operand), - expr.where if expr.where is not None else self.default_where) + operand = self.rec(expr.operand) + dd = prim.as_dofdesc(expr.where) + + if dd.where is None: + return type(expr)(operand, dd.copy(where=self.default_where)) + else: + return type(expr)(operand, dd) map_elementwise_min = map_elementwise_sum map_elementwise_max = map_elementwise_sum def map_int_g(self, expr): - source = expr.source - target = expr.target + source = prim.as_dofdesc(expr.source) + target = prim.as_dofdesc(expr.target) - if source is None: - source = self.default_source - if target is None: - target = self.default_where + if source.where is None: + source = source.copy(where=self.default_source) + if target.where is None: + target = target.copy(where=self.default_where) return type(expr)( expr.kernel, @@ -275,10 +286,10 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): )) def map_inverse(self, expr): - where = expr.where + dd = prim.as_dofdesc(expr.where) - if where is None: - where = self.default_where + if dd.where is None: + dd = dd.copy(where=self.default_where) return type(expr)( # don't recurse into expression--it is a separate world that @@ -288,20 +299,21 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): dict([ (name, self.rec(name_expr)) for name, name_expr in six.iteritems(expr.extra_vars)]), - where) + dd) def map_interpolation(self, expr): - source = expr.source - target = expr.target + source = prim.as_dofdesc(expr.source) + target = prim.as_dofdesc(expr.target) - if source is None: - source = self.default_source - if target is None: - target = self.default_where + if source.where is None: + source = source.copy(where=self.default_source) + if target.where is None: + target = target.copy(where=self.default_where) return type(expr)(source, target, expr.operand) def map_dof_granularity_converter(self, expr): + # FIXME: this needs to be deleted where = expr.where if where is None: where = self.default_source diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 4eadc2f6..69d93c71 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -379,6 +379,21 @@ class DOFDescriptor(object): def as_dofdesc(desc): if isinstance(desc, DOFDescriptor): return desc + + # TODO: should be deleted once _QBXSource and friends are gone + if isinstance(desc, _QBXSource): + from warnings import warn + warn('using _QBXSource is deprecated, use DOFDescriptor instead.', + DeprecationWarning, stacklevel=2) + + if isinstance(desc, QBXSourceStage2): + discr = QBX_SOURCE_STAGE2 + elif isinstance(desc, QBXSourceQuadStage2): + discr = QBX_SOURCE_QUAD_STAGE2 + else: + discr = QBX_SOURCE_STAGE1 + return DOFDescriptor(desc.where, discr=discr) + return DOFDescriptor(desc) @@ -487,7 +502,7 @@ class DiscretizationProperty(Expression): :arg where: |where-blurb| """ - self.where = where + self.where = as_dofdesc(where) def __getinitargs__(self): return (self.where,) -- GitLab From e21a2edc1aa4f67bb480ad036f20a43f3b6ed019 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 29 Jun 2019 19:05:19 -0500 Subject: [PATCH 21/96] remove (most) geometry from QBXLayerPotentialSource --- pytential/qbx/__init__.py | 95 +++------------------------------------ 1 file changed, 5 insertions(+), 90 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 697c7c5e..46c1d555 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -487,105 +487,20 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): import pytential.qbx.utils as utils return utils.element_centers_of_mass(self.stage2_density_discr) - @property - def _dim_fudge_factor(self): - if self.density_discr.dim == 2: - return 0.5 - else: - return 1 - - @property - def _expansion_radii_factor(self): - return 0.5 * self._dim_fudge_factor - - @property - def _source_danger_zone_radii_factor(self): - # This should be the expression of the expansion radii, but - # - # - in reference to the stage 2 discretization - # - mutliplied by 0.75 because - # - # - Setting this equal to the expansion radii ensures that *every* - # stage 2 element will be refined, which is wasteful. - # (so this needs to be smaller than that) - # - - # - Setting this equal to half the expansion radius will not provide - # a refinement 'buffer layer' at a 2x coarsening fringe. - - return 0.75 * self._expansion_radii_factor - - @property - def _close_target_tunnel_radius_factor(self): - return 0.5 * self._expansion_radii_factor - - @memoize_method def _expansion_radii(self, last_dim_length): - raise RuntimeError("bind `qbx_expansion_radii` directly") - - from pytential import bind, sym - with cl.CommandQueue(self.cl_context) as queue: - radii = bind(self, sym.qbx_expansion_radii( - self._expansion_radii_factor, - self.ambient_dim, - granularity=last_dim_length))(queue) - - return radii.with_queue(None) + raise RuntimeError("bind `expansion_radii` directly") - # _expansion_radii should not be needed for the fine discretization - - @memoize_method def _source_danger_zone_radii(self, last_dim_length="npanels"): - raise RuntimeError("bind `qbx_expansion_radii` directly") - - from pytential import bind, sym - with cl.CommandQueue(self.cl_context) as queue: - radii = bind(self, sym.qbx_expansion_radii( - 0.75 * self._expansion_radii_factor, - self.ambient_dim, - granularity=last_dim_length, - where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)))(queue) - - return radii.with_queue(None) + raise RuntimeError("bind `_source_danger_zone_radii` directly") - @memoize_method def _close_target_tunnel_radius(self, last_dim_length): - raise RuntimeError("bind `qbx_expansion_radii` directly") - - with cl.CommandQueue(self.cl_context) as queue: - return ( - self._expansion_radii(last_dim_length).with_queue(queue) - * 0.5 - ).with_queue(None) + raise RuntimeError("bind `_close_target_tunnel_radii` directly") - @memoize_method def _coarsest_quad_resolution(self, last_dim_length="npanels"): - raise RuntimeError("bind `qbx_quad_resolution` directly") - - from pytential import bind, sym - with cl.CommandQueue(self.cl_context) as queue: - r = bind(self, sym.qbx_quad_resolution( - self.ambient_dim, - last_dim_length))(queue) - - return r.with_queue(None) + raise RuntimeError("bind `_quad_resolution` directly") - @memoize_method def _stage2_coarsest_quad_resolution(self, last_dim_length="npanels"): - raise RuntimeError("bind `qbx_quad_resolution` directly") - - if last_dim_length != "npanels": - # Not technically required below, but no need to loosen for now. - raise NotImplementedError() - - from pytential import bind, sym - with cl.CommandQueue(self.cl_context) as queue: - r = bind(self, sym.qbx_quad_resolution( - self.ambient_dim, - last_dim_length, - where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)))(queue) - - return r.with_queue(None) + raise RuntimeError("bind `_quad_resolution` directly") @memoize_method def qbx_fmm_geometry_data(self, target_discrs_and_qbx_sides): -- GitLab From 8857b984011e2ad24ec80b0ca66c264c905cb1c1 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 29 Jun 2019 19:08:57 -0500 Subject: [PATCH 22/96] move expansion radii factors to primitives and cleanup --- pytential/symbolic/primitives.py | 68 ++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 69d93c71..2ee02c4f 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -203,7 +203,7 @@ Pretty-printing expressions """ -# {{{ 'where' specifiers +# {{{ dof descriptors class DEFAULT_SOURCE: # noqa: N801 pass @@ -965,37 +965,63 @@ class DOFGranularityConverter(Expression): mapper_method = intern("map_dof_granularity_converter") -def qbx_quad_resolution(ambient_dim, granularity="npanels", where=None): - stretch = _simplex_mapping_max_stretch_factor(ambient_dim, where=where) - return DOFGranularityConverter(granularity, stretch, where=where) +def _expansion_radii_factor(ambient_dim): + dim_fudge_factor = 0.5 if ambient_dim == 2 else 1.0 + return 0.5 * dim_fudge_factor -def qbx_expansion_radii(factor, ambient_dim, - granularity="nsources", where=None): - """ - :arg factor: stick out factor for expansion radii. - """ +def _quad_resolution(ambient_dim, granularity=None, where=None): + source = as_dofdesc(where) + target = source.copy(granularity=granularity) - stretch = _simplex_mapping_max_stretch_factor(ambient_dim, where=where) - radii = DOFGranularityConverter(granularity, factor * stretch, where=where) + stretch = _simplex_mapping_max_stretch_factor(ambient_dim, where=source) + return Interpolation(source, target, operand) - return cse(radii, cse_scope.DISCRETIZATION) +def _source_danger_zone_radii(ambient_dim, granularity=None, where=None): + where = as_dofdesc(where) + if where.discr is None: + where = where.copy(discr=QBX_SOURCE_STAGE2) -def qbx_expansion_centers(factor, side, ambient_dim, dim=None, where=None): - """ - :arg factor: target confinement factor for expansion radii. - :arg side: `+1` or `-1` expansion side, relative to the direction of - the normal vector. - """ + # This should be the expression of the expansion radii, but + # + # - in reference to the stage 2 discretization + # - mutliplied by 0.75 because + # + # - Setting this equal to the expansion radii ensures that *every* + # stage 2 element will be refined, which is wasteful. + # (so this needs to be smaller than that) + # - Setting this equal to half the expansion radius will not provide + # a refinement 'buffer layer' at a 2x coarsening fringe. + + factor = 0.75 * _expansion_radii_factor(ambient_dim) + return factor * _quad_resolution(ambient_dim, + granularity=granularity, where=where) + + +def _close_target_tunnel_radii(ambient_dim, granularity=None, where=None): + factor = 0.5 * _expansion_radii_factor(ambient_dim) + + return factor * _quad_resolution(ambient_dim, + granularity=granularity, where=where) + + +def expansion_radii(ambient_dim, granularity=None, where=None): + factor = _expansion_radii_factor(ambient_dim) + + return cse(factor * _quad_resolution(ambient_dim, + granularity=granularity, where=where), cse_scope.DISCRETIZATION) + + +def expansion_centers(ambient_dim, side, dim=None, where=None): + where = as_dofdesc(where).with_granularity(granularity=GRANULARITY_NODE) x = nodes(ambient_dim, where=where) normals = normal(ambient_dim, dim=dim, where=where) - radii = qbx_expansion_radii(factor, ambient_dim, - granularity="nsources", where=where) + radii = expansion_radii(ambient_dim, + granularity=GRANULARITY_NODE, where=where) centers = x + side * radii * normals - return cse(centers.as_vector(), cse_scope.DISCRETIZATION) # }}} -- GitLab From e0c685cecd3ee5777789006b6bf4cb9e7e943a0b Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 29 Jun 2019 20:32:49 -0500 Subject: [PATCH 23/96] rename expansion_radii expressions --- pytential/linalg/proxy.py | 16 ++--- pytential/qbx/__init__.py | 16 ++--- pytential/qbx/refinement.py | 11 ++- pytential/qbx/target_assoc.py | 27 +++----- pytential/qbx/utils.py | 112 +++---------------------------- pytential/symbolic/matrix.py | 17 ++--- pytential/symbolic/primitives.py | 15 +++-- test/test_global_qbx.py | 34 ++++------ test/test_linalg_proxy.py | 13 +--- 9 files changed, 67 insertions(+), 194 deletions(-) diff --git a/pytential/linalg/proxy.py b/pytential/linalg/proxy.py index ef0653fa..e89053d9 100644 --- a/pytential/linalg/proxy.py +++ b/pytential/linalg/proxy.py @@ -467,16 +467,12 @@ class ProxyGenerator(object): return np.dot(A, v) + b from pytential import bind, sym - radii = bind(self.source, sym.qbx_expansion_radii( - self.source._expansion_radii_factor, - self.source.ambient_dim, - granularity="nsources"))(queue) - center_int = bind(self.source, sym.qbx_expansion_centers( - self.source._expansion_radii_factor, -1, - self.source.ambient_dim))(queue) - center_ext = bind(self.source, sym.qbx_expansion_centers( - self.source._expansion_radii_factor, +1, - self.source.ambient_dim))(queue) + 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, diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 46c1d555..8e860546 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -768,16 +768,12 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): * self.weights_and_area_elements()) from pytential import bind, sym - expansion_radii = bind(self, sym.qbx_expansion_radii( - self._expansion_radii_factor, - self.ambient_dim, - granularity="nsources"))(queue) - int_centers = bind(self, sym.qbx_expansion_centers( - self._expansion_radii_factor, -1, - self.ambient_dim))(queue) - ext_centers = bind(self, sym.qbx_expansion_centers( - self._expansion_radii_factor, +1, - self.ambient_dim))(queue) + expansion_radii = bind(self, + sym.expansion_radii(self.ambient_dim))(queue) + int_centers = bind(self, + sym.expansion_centers(self.ambient_dim, -1))(queue) + ext_centers = bind(self, + sym.expansion_centers(self.ambient_dim, +1))(queue) centers = {-1: int_centers, 1: ext_centers} # FIXME: Do this all at once diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index 37ae13dc..f51188e3 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -305,10 +305,9 @@ class RefinerWrangler(TreeWranglerBase): unwrap_args = AreaQueryElementwiseTemplate.unwrap_args from pytential import bind, sym - center_danger_zone_radii = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._expansion_radii_factor, + center_danger_zone_radii = bind(lpot_source, sym.expansion_radii( lpot_source.ambient_dim, - granularity="ncenters"))(self.queue) + granularity=sym.GRANULARITY_CENTER))(self.queue) evt = knl( *unwrap_args( @@ -364,11 +363,9 @@ class RefinerWrangler(TreeWranglerBase): from pytential import bind, sym source_danger_zone_radii_by_panel = bind(lpot_source, - sym.qbx_expansion_radii( - lpot_source._source_danger_zone_radii_factor, + sym._source_danger_zone_radii( lpot_source.ambient_dim, - granularity="npanels", - where=sym.QBXSourceStage2(sym.DEFAULT_SOURCE)))(self.queue) + granularity=sym.GRANULARITY_ELEMENT))(self.queue) unwrap_args = AreaQueryElementwiseTemplate.unwrap_args evt = knl( diff --git a/pytential/qbx/target_assoc.py b/pytential/qbx/target_assoc.py index 6a206313..14ea33ba 100644 --- a/pytential/qbx/target_assoc.py +++ b/pytential/qbx/target_assoc.py @@ -456,10 +456,8 @@ class TargetAssociationWrangler(TreeWranglerBase): source_slice = tree.sorted_target_ids[tree.qbx_user_source_slice] sources = [ axis.with_queue(self.queue)[source_slice] for axis in tree.sources] - tunnel_radius_by_source = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._close_target_tunnel_radius_factor, - lpot_source.ambient_dim, - granularity="nsources"))(self.queue) + tunnel_radius_by_source = bind(lpot_source, + sym._close_target_tunnel_radii(lpot_source.ambient_dim)(self.queue) # Target-marking algorithm (TGTMARK): # @@ -495,10 +493,8 @@ class TargetAssociationWrangler(TreeWranglerBase): wait_for=wait_for) wait_for = [evt] - tunnel_radius_by_source = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._close_target_tunnel_radius_factor, - lpot_source.ambient_dim, - granularity="nsources"))(self.queue) + tunnel_radius_by_source = bind(lpot_source, + sym._close_target_tunnel_radii(lpot_source.ambient_dim))(self.queue) evt = knl( *unwrap_args( @@ -556,9 +552,8 @@ class TargetAssociationWrangler(TreeWranglerBase): centers = [ axis.with_queue(self.queue)[center_slice] for axis in tree.sources] expansion_radii_by_center = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._expansion_radii_factor, lpot_source.ambient_dim, - granularity="ncenters"))(self.queue) + granularity=sym.GRANULARITY_CENTER))(self.queue) expansion_radii_by_center_with_tolerance = \ expansion_radii_by_center * (1 + target_association_tolerance) @@ -637,10 +632,8 @@ class TargetAssociationWrangler(TreeWranglerBase): source_slice = tree.user_source_ids[tree.qbx_user_source_slice] sources = [ axis.with_queue(self.queue)[source_slice] for axis in tree.sources] - tunnel_radius_by_source = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._close_target_tunnel_radius_factor, - lpot_source.ambient_dim, - granularity="nsources"))(self.queue) + tunnel_radius_by_source = bind(lpot_source, + sym._close_target_tunnel_radii(lpot_source.ambient_dim))(self.queue) # See (TGTMARK) above for algorithm. @@ -653,10 +646,8 @@ class TargetAssociationWrangler(TreeWranglerBase): wait_for=wait_for) wait_for = [evt] - tunnel_radius_by_source = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._close_target_tunnel_radius_factor, - lpot_source.ambient_dim, - granularity="nsources"))(self.queue) + tunnel_radius_by_source = bind(lpot_source, + sym._close_target_tunnel_radii(lpot_source.ambient_dim))(self.queue) evt = knl( *unwrap_args( diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index 56b6e656..d5555e9d 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -103,27 +103,13 @@ def get_interleaved_centers(queue, lpot_source): next to corresponding exterior centers. """ from pytential import bind, sym - knl = get_interleaver_kernel(lpot_source.density_discr.real_dtype) + 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) - int_centers = bind(lpot_source, sym.qbx_expansion_centers( - lpot_source._expansion_radii_factor, -1, - lpot_source.ambient_dim))(queue) - ext_centers = bind(lpot_source, sym.qbx_expansion_centers( - lpot_source._expansion_radii_factor, +1, - lpot_source.ambient_dim))(queue) - - result = [] - wait_for = [] - - for int_axis, ext_axis in zip(int_centers, ext_centers): - axis = cl.array.empty(queue, len(int_axis) * 2, int_axis.dtype) - evt, _ = knl(queue, src1=int_axis, src2=ext_axis, dst=axis) - result.append(axis) - wait_for.append(evt) - - cl.wait_for_events(wait_for) - - return result + interleaver = InterleaverConnection(lpot_source.density_discr) + return interleaver(queue, [int_centers, ext_centers]) # }}} @@ -137,17 +123,11 @@ def get_interleaved_radii(queue, lpot_source): """ from pytential import bind, sym - knl = get_interleaver_kernel(lpot_source.density_discr.real_dtype) - radii = bind(lpot_source, sym.qbx_expansion_radii( - lpot_source._expansion_radii_factor, - lpot_source.ambient_dim, - granularity="nsources"))(queue) + radii = bind(lpot_source, + sym.expansion_radii(lpot_source.ambient_dim))(queue) - result = cl.array.empty(queue, len(radii) * 2, radii.dtype) - evt, _ = knl(queue, src1=radii, src2=radii, dst=result) - evt.wait() - - return result + interleaver = InterleaverConnection(lpot_source.density_discr) + return interleaver(queue, radii) # }}} @@ -224,49 +204,6 @@ class TreeWranglerBase(object): # {{{ to_last_dim_length -def to_last_dim_length(discr, vec, last_dim_length, queue=None): - """Takes a :class:`pyopencl.array.Array` with a last axis that has the same - length as the number of discretization nodes in the discretization *discr* - and converts it so that the last axis has a length as specified by - *last_dim_length*. - """ - - queue = queue or vec.queue - - if last_dim_length == "nsources": - return vec - - elif last_dim_length == "ncenters": - knl = get_interleaver_kernel(vec.dtype) - _, (result,) = knl(queue, dstlen=2*discr.nnodes, src1=vec, src2=vec) - return result - - elif last_dim_length == "npanels": - knl = lp.make_kernel( - "{[i,k]: 0<=i Date: Sun, 30 Jun 2019 10:22:48 -0500 Subject: [PATCH 24/96] expand interpolation operator to also change granularities --- pytential/qbx/__init__.py | 4 +- pytential/qbx/refinement.py | 4 +- pytential/qbx/target_assoc.py | 4 +- pytential/qbx/utils.py | 161 ++++++++++++++++++++++++++++++- pytential/symbolic/execution.py | 48 +++------ pytential/symbolic/mappers.py | 14 +-- pytential/symbolic/primitives.py | 6 +- test/test_global_qbx.py | 8 +- test/test_symbolic.py | 56 +++++++---- 9 files changed, 227 insertions(+), 78 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 8e860546..328c4e8f 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -470,9 +470,9 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: - quad_res = bind(self, sym.qbx_quad_resolution( + quad_res = bind(self, sym._quad_resolution( self.ambient_dim, - granularity="npanels"))(queue) + granularity=sym.GRANULARITY_ELEMENT))(queue) return cl.array.max(quad_res).get().item() # {{{ internal API diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index f51188e3..53daffb9 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -605,9 +605,9 @@ def refine_for_global_qbx(lpot_source, wrangler, "checking kernel length scale to panel size ratio"): from pytential import bind, sym - quad_resolution = bind(lpot_source, sym.qbx_quad_resolution( + quad_resolution = bind(lpot_source, sym._quad_resolution( lpot_source.ambient_dim, - granularity="npanels"))(wrangler.queue) + granularity=sym.GRANULARITY_ELEMENT))(wrangler.queue) violates_kernel_length_scale = \ wrangler.check_element_prop_threshold( diff --git a/pytential/qbx/target_assoc.py b/pytential/qbx/target_assoc.py index 14ea33ba..01fd6eb6 100644 --- a/pytential/qbx/target_assoc.py +++ b/pytential/qbx/target_assoc.py @@ -457,7 +457,7 @@ class TargetAssociationWrangler(TreeWranglerBase): sources = [ axis.with_queue(self.queue)[source_slice] for axis in tree.sources] tunnel_radius_by_source = bind(lpot_source, - sym._close_target_tunnel_radii(lpot_source.ambient_dim)(self.queue) + sym._close_target_tunnel_radii(lpot_source.ambient_dim))(self.queue) # Target-marking algorithm (TGTMARK): # @@ -551,7 +551,7 @@ class TargetAssociationWrangler(TreeWranglerBase): .with_queue(self.queue)) centers = [ axis.with_queue(self.queue)[center_slice] for axis in tree.sources] - expansion_radii_by_center = bind(lpot_source, sym.qbx_expansion_radii( + expansion_radii_by_center = bind(lpot_source, sym.expansion_radii( lpot_source.ambient_dim, granularity=sym.GRANULARITY_CENTER))(self.queue) expansion_radii_by_center_with_tolerance = \ diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index d5555e9d..2494e136 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -31,8 +31,9 @@ import numpy as np from boxtree.tree import Tree import pyopencl as cl import pyopencl.array # noqa -from pytools import memoize, memoize_method +from pytools import memoize, memoize_in, memoize_method from loopy.version import MOST_RECENT_LANGUAGE_VERSION +from meshmode.discretization.connection import DiscretizationConnection import logging logger = logging.getLogger(__name__) @@ -70,6 +71,164 @@ QBX_TREE_MAKO_DEFS = r"""//CL:mako// # }}} +# {{{ connections + + +def mesh_el_view(mesh, group_nr, global_array): + """Return a view of *global_array* of shape + ``(..., mesh.groups[group_nr].nelements)`` + where *global_array* is of shape ``(..., nelements)``, + where *nelements* is the global (per-mesh) element count. + """ + + group = mesh.groups[group_nr] + + return global_array[ + ..., group.element_nr_base:group.element_nr_base + group.nelements] \ + .reshape( + global_array.shape[:-1] + + (group.nelements,)) + + +class InterleaverConnection(DiscretizationConnection): + def __init__(self, discr): + super(InterleaverConnection, self).__init__(discr, discr, + is_surjective=True) + + @memoize + def kernel(self, dtype): + knl = lp.make_kernel( + "[srclen, dstlen] -> {[i]: 0 <= i < srclen}", + """ + dst[2*i] = src1[i] + dst[2*i + 1] = src2[i] + """, + [ + lp.GlobalArg("src1", shape="srclen", dtype=dtype), + lp.GlobalArg("src2", shape="srclen", dtype=dtype), + lp.GlobalArg("dst", shape="dstlen", dtype=dtype), + "..." + ], + assumptions="2*srclen = dstlen", + lang_version=MOST_RECENT_LANGUAGE_VERSION, + ) + + knl = lp.split_iname(knl, "i", 128, + inner_tag="l.0", outer_tag="g.0") + return knl + + def __call__(self, queue, vecs): + if isinstance(vecs, cl.array.Array): + vecs = [[vecs], [vecs]] + elif isinstance(vecs, (list, tuple)): + assert len(vecs) == 2 + else: + raise ValueError('cannot interleave arrays') + + result = [] + for src1, src2 in zip(vecs[0], vecs[1]): + if not isinstance(src1, cl.array.Array) \ + or not isinstance(src2, cl.array.Array): + raise TypeError('non-array passed to connection') + + if src1.shape != (self.to_discr.nnodes,) \ + or src2.shape != (self.to_discr.nnodes,): + raise ValueError('invalid shape of incoming array') + + axis = cl.array.empty(queue, 2 * len(src1), src1.dtype) + self.kernel(src1.dtype)(queue, + src1=src1, src2=src2, dst=axis) + result.append(axis) + + # TODO: why is get_interleaved_something have an evt.wait? + + return result[0] if len(result) == 1 else result + + +class ElementSampleConnection(DiscretizationConnection): + def __init__(self, discr): + super(ElementSampleConnection, self).__init__(discr, discr, + is_surjective=True) + + def __call__(self, queue, vec): + @memoize_in(self, "subsample_to_elements_kernel") + def kernel(): + knl = lp.make_kernel( + "{[i, k]: 0 <= i < nelements}", + "result[i] = a[i, 0]", + [ + lp.GlobalArg("a", + shape=("nelements", "nunit_nodes"), dtype=None), + lp.ValueArg("nunit_nodes", dtype=np.int32), + "..." + ], + name="subsample_to_elements", + lang_version=MOST_RECENT_LANGUAGE_VERSION, + ) + + knl = lp.split_iname(knl, "i", 128, + inner_tag="l.0", outer_tag="g.0") + return knl + + result = cl.array.empty(queue, self.to_discr.mesh.nelements, vec.dtype) + for igrp, group in enumerate(self.to_discr.groups): + kernel()(queue, + a=group.view(vec), + result=mesh_el_view(self.to_discr.mesh, igrp, result)) + + return result + + +def connection_from_dds(lpot_source, source, target): + from pytential import sym + source = sym.as_dofdesc(source) + target = sym.as_dofdesc(target) + + if not ((source.where == sym.DEFAULT_SOURCE \ + and target.where == sym.DEFAULT_TARGET) \ + or source.where == target.where): + raise ValueError('cannot interpolate between different domains') + if source.granularity != sym.GRANULARITY_NODE: + raise ValueError('can only interpolate from `GRANULARITY_NODE`') + + connections = [] + from_discr = lpot_source.density_discr + if target.discr != source.discr: + if target.discr != sym.QBX_SOURCE_QUAD_STAGE2: + raise RuntimeError('can only interpolate to `QBX_SOURCE_QUAD_STAGE2`') + + if source.discr == sym.QBX_SOURCE_STAGE2: + from_discr = lpot_source.stage2_density_discr + connections.append(lpot_source.refined_interp_to_ovsmp_quad_connection) + elif source.discr == sym.QBX_SOURCE_QUAD_STAGE2: + from_discr = lpot_source.quad_stage2_density_discrd + else: + from_discr = lpot_source.density_discr + connections.append(lpot_source.resampler) + + if target.granularity != source.granularity: + if target.discr == sym.QBX_SOURCE_STAGE2: + discr = lpot_source.stage2_density_discr + elif target.discr == sym.QBX_SOURCE_QUAD_STAGE2: + discr = lpot_source.quad_stage2_density_discr + else: + discr = lpot_source.density_discr + + if not connections: + from_discr = discr + + if target.granularity == sym.GRANULARITY_CENTER: + connections.append(InterleaverConnection(discr)) + elif target.granularity == sym.GRANULARITY_ELEMENT: + connections.append(ElementSampleConnection(discr)) + + from meshmode.discretization.connection import ChainedDiscretizationConnection + return ChainedDiscretizationConnection(connections, from_discr=from_discr) + + +# }}} + + # {{{ interleaver kernel @memoize diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 3a1d9425..893d0881 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -174,35 +174,17 @@ class EvaluationMapper(EvaluationMapperBase): return source.map_quad_kernel_op(expr, self.bound_expr, self.rec) def map_interpolation(self, expr): - if not isinstance(expr.target, sym.QBXSourceQuadStage2): - raise RuntimeError("can only interpolate to quad_stage2 mesh") + from pytential.source import LayerPotentialSourceBase + from pytential.qbx.utils import connection_from_dds source = self.bound_expr.places[expr.source] - operand = self.rec(expr.operand) - - from pytential.source import LayerPotentialSourceBase - if isinstance(source, LayerPotentialSourceBase): - sym_source = expr.source - if not isinstance(sym_source, sym._QBXSource): - sym_source = sym.QBXSourceStage1(sym_source) - - if isinstance(sym_source, sym.QBXSourceStage1): - resampler = source.resampler - elif isinstance(sym_source, sym.QBXSourceStage2): - resampler = source.refined_interp_to_ovsmp_quad_connection - elif isinstance(sym_source, sym.QBXSourceQuadStage2): - resampler = lambda x, y: y # noqa - else: - from pytential.symbolic.mappers import stringify_where - raise ValueError( - "unknown `where` identifier in " - "interpolation source: {}".format( - stringify_where(sym_source))) - else: + if not isinstance(source, LayerPotentialSourceBase): raise TypeError("source must be a `LayerPotentialSourceBase`") + operand = self.rec(expr.operand) if isinstance(operand, cl.array.Array): - return resampler(self.queue, operand) + conn = connection_from_dds(source, expr.source, expr.target) + return conn(self.queue, operand) elif isinstance(operand, (int, float, complex, np.number)): return operand else: @@ -447,20 +429,14 @@ class GeometryCollection(object): self.caches = {} - def _get_lpot_discretization(self, lpot, where): - if not isinstance(where, sym._QBXSource): - where = sym.QBXSourceStage1(where) + def _get_lpot_discretization(self, lpot, dd): + dd = sym.as_dofdesc(dd) - if isinstance(where, sym.QBXSourceStage1): - return lpot.density_discr - if isinstance(where, sym.QBXSourceStage2): + if dd.discr == sym.QBX_SOURCE_STAGE2: return lpot.stage2_density_discr - if isinstance(where, sym.QBXSourceQuadStage2): + if dd.discr == sym.QBX_SOURCE_QUAD_STAGE2: return lpot.quad_stage2_density_discr - - from pytential.symbolic.mappers import stringify_where - raise ValueError('unknown `where` identifier: {}'.format( - stringify_where(where))) + return lpot.density_discr def get_discretization(self, where): """ @@ -484,7 +460,7 @@ class GeometryCollection(object): from pytential.source import LayerPotentialSourceBase if isinstance(discr, LayerPotentialSourceBase): - return self._get_lpot_discretization(discr, dd.where) + return self._get_lpot_discretization(discr, dd) else: return discr diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 8aa9ba48..67220666 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -51,7 +51,6 @@ from pymbolic.geometric_algebra.mapper import ( DerivativeSourceFinder as DerivativeSourceFinderBase, - GraphvizMapper as GraphvizMapperBase) import pytential.symbolic.primitives as prim @@ -256,7 +255,7 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): return expr def map_elementwise_sum(self, expr): - operand = self.rec(expr.operand) + operand = self.operand_rec(expr.operand) dd = prim.as_dofdesc(expr.where) if dd.where is None: @@ -310,7 +309,7 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): if target.where is None: target = target.copy(where=self.default_where) - return type(expr)(source, target, expr.operand) + return type(expr)(source, target, self.operand_rec(expr.operand)) def map_dof_granularity_converter(self, expr): # FIXME: this needs to be deleted @@ -439,20 +438,21 @@ class QBXInterpolationPreprocessor(IdentityMapper): self.places = places def map_int_g(self, expr): - if isinstance(expr.source, prim.QBXSourceQuadStage2): + dd = prim.as_dofdesc(expr.source) + if dd.discr == prim.QBX_SOURCE_QUAD_STAGE2: return expr from pytential.source import LayerPotentialSourceBase source = self.places[expr.source] if isinstance(source, LayerPotentialSourceBase): - stage2_source = prim.QBXSourceQuadStage2(prim.DEFAULT_SOURCE) + target_dd = dd.copy(discr=prim.QBX_SOURCE_QUAD_STAGE2) density = prim.Interpolation( - expr.source, stage2_source, self.rec(expr.density)) + dd, target_dd, self.rec(expr.density)) kernel_arguments = dict( (name, prim.Interpolation( - expr.source, stage2_source, self.rec(arg_expr))) + dd, target_dd, self.rec(arg_expr))) for name, arg_expr in expr.kernel_arguments.items()) expr = expr.copy( diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 68372437..7af90ff7 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -395,7 +395,7 @@ def as_dofdesc(desc): if desc == QBX_SOURCE_STAGE1 \ or desc == QBX_SOURCE_STAGE2 \ or desc == QBX_SOURCE_QUAD_STAGE2: - return DOFDescriptor(None, discr=discr) + return DOFDescriptor(None, discr=desc) return DOFDescriptor(desc) @@ -978,7 +978,7 @@ def _quad_resolution(ambient_dim, granularity=None, where=None): target = source.copy(granularity=granularity) stretch = _simplex_mapping_max_stretch_factor(ambient_dim, where=source) - return Interpolation(source, target, operand) + return Interpolation(source, target, stretch) def _source_danger_zone_radii(ambient_dim, granularity=None, where=None): @@ -1017,7 +1017,7 @@ def expansion_radii(ambient_dim, granularity=None, where=None): def expansion_centers(ambient_dim, side, dim=None, where=None): - where = as_dofdesc(where).with_granularity(granularity=GRANULARITY_NODE) + where = as_dofdesc(where) x = nodes(ambient_dim, where=where) normals = normal(ambient_dim, dim=dim, where=where) diff --git a/test/test_global_qbx.py b/test/test_global_qbx.py index b3f89a42..99e942bf 100644 --- a/test/test_global_qbx.py +++ b/test/test_global_qbx.py @@ -127,15 +127,15 @@ def run_source_refinement_test(ctx_factory, mesh, order, helmholtz_k=None): 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() + 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, granularity=sym.GRANULARITY_ELEMENT, where=sym.QBX_SOURCE_STAGE2))(queue).get() - quad_res = bind(lpot_source, sym.qbx_quad_resolution( + quad_res = bind(lpot_source, sym._quad_resolution( lpot_source.ambient_dim, - granularity="npanels"))(queue) + granularity=sym.GRANULARITY_ELEMENT))(queue) # {{{ check if satisfying criteria @@ -268,7 +268,7 @@ def test_target_association(ctx_factory, curve_name, curve_f, nelements, nsources = lpot_source.density_discr.nnodes noise = rng.uniform(queue, nsources, dtype=np.float, a=0.01, b=1.0) tunnel_radius = bind(lpot_source, - sym._close_target_tunnel_radii(lpot_source.ambient_dim)(queue) + sym._close_target_tunnel_radii(lpot_source.ambient_dim))(queue) def targets_from_sources(sign, dist): from pytential import sym, bind diff --git a/test/test_symbolic.py b/test/test_symbolic.py index d709f7f0..11ef6b10 100644 --- a/test/test_symbolic.py +++ b/test/test_symbolic.py @@ -201,13 +201,15 @@ def test_expr_pickling(): # }}} -@pytest.mark.parametrize("source", [ - sym.DEFAULT_SOURCE, - sym.QBXSourceStage1(sym.DEFAULT_SOURCE), - sym.QBXSourceStage2(sym.DEFAULT_SOURCE), - sym.QBXSourceQuadStage2(sym.DEFAULT_SOURCE) +@pytest.mark.parametrize(("source_discr", "target_granularity"), [ + (None, None), + (sym.QBX_SOURCE_STAGE1, sym.GRANULARITY_NODE), + (sym.QBX_SOURCE_STAGE2, sym.GRANULARITY_NODE), + (sym.QBX_SOURCE_STAGE2, sym.GRANULARITY_CENTER), + (sym.QBX_SOURCE_STAGE2, sym.GRANULARITY_ELEMENT), + (sym.QBX_SOURCE_QUAD_STAGE2, sym.GRANULARITY_NODE) ]) -def test_interpolation(ctx_factory, source): +def test_interpolation(ctx_factory, source_discr, target_granularity): ctx = ctx_factory() queue = cl.CommandQueue(ctx) @@ -227,25 +229,37 @@ def test_interpolation(ctx_factory, source): qbx_order=qbx_order, fmm_order=False).with_refinement() - target = sym.QBXSourceQuadStage2(sym.DEFAULT_SOURCE) + source = sym.DOFDescriptor(sym.DEFAULT_SOURCE, + discr=source_discr, + granularity=sym.GRANULARITY_NODE) + target = sym.DOFDescriptor(sym.DEFAULT_TARGET, + discr=sym.QBX_SOURCE_QUAD_STAGE2, + granularity=target_granularity) + sigma_sym = sym.var("sigma") op_sym = sym.sin(sym.Interpolation(source, target, sigma_sym)) - bound_op = bind(qbx, op_sym, auto_where=(source, sym.DEFAULT_TARGET)) + bound_op = bind(qbx, op_sym) - quad2_nodes = qbx.quad_stage2_density_discr.nodes().get(queue) - if isinstance(source, sym.QBXSourceStage2): - nodes = qbx.stage2_density_discr.nodes().get(queue) - elif isinstance(source, sym.QBXSourceQuadStage2): - nodes = quad2_nodes + target_nodes = qbx.quad_stage2_density_discr.nodes().get(queue) + if source_discr == sym.QBX_SOURCE_STAGE2: + source_nodes = qbx.stage2_density_discr.nodes().get(queue) + elif source_discr == sym.QBX_SOURCE_QUAD_STAGE2: + source_nodes = target_nodes else: - nodes = qbx.density_discr.nodes().get(queue) - - sigma_dev = cl.array.to_device(queue, la.norm(nodes, axis=0)) - sigma_quad2 = np.sin(la.norm(quad2_nodes, axis=0)) - sigma_quad2_interp = bound_op(queue, sigma=sigma_dev).get(queue) - - error = la.norm(sigma_quad2_interp - sigma_quad2) / la.norm(sigma_quad2) - assert error < 1.0e-10 + source_nodes = qbx.density_discr.nodes().get(queue) + + sigma_dev = cl.array.to_device(queue, la.norm(source_nodes, axis=0)) + sigma_target = np.sin(la.norm(target_nodes, axis=0)) + sigma_target_interp = bound_op(queue, sigma=sigma_dev).get(queue) + + if target.granularity == sym.GRANULARITY_NODE: + error = la.norm(sigma_target_interp - sigma_target) / la.norm(sigma_target) + assert error < 1.0e-10 + elif target.granularity == sym.GRANULARITY_CENTER: + assert len(sigma_target_interp) == 2 * len(sigma_target) + elif target.granularity == sym.GRANULARITY_ELEMENT: + nelements = qbx.quad_stage2_density_discr.mesh.nelements + assert len(sigma_target_interp) == nelements # You can test individual routines by typing -- GitLab From 9cd98efafd4dbdba40db44412ae07ffd6b7ed3ff Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sun, 30 Jun 2019 10:25:48 -0500 Subject: [PATCH 25/96] remove last_dim_length expression remnants --- pytential/qbx/utils.py | 30 --------------------------- pytential/symbolic/execution.py | 8 -------- pytential/symbolic/mappers.py | 19 ----------------- pytential/symbolic/primitives.py | 35 -------------------------------- 4 files changed, 92 deletions(-) diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index 2494e136..a7c1379d 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -229,31 +229,6 @@ def connection_from_dds(lpot_source, source, target): # }}} -# {{{ interleaver kernel - -@memoize -def get_interleaver_kernel(dtype): - # NOTE: Returned kernel needs dstlen or dst parameter - from pymbolic import var - knl = lp.make_kernel( - "[srclen,dstlen] -> {[i]: 0<=i Date: Mon, 1 Jul 2019 20:12:30 -0500 Subject: [PATCH 26/96] matrix: use dofdesc --- pytential/symbolic/execution.py | 12 +++++--- pytential/symbolic/matrix.py | 30 +++++++++--------- pytential/symbolic/primitives.py | 5 +++ test/test_matrix.py | 53 +++++++++++++++----------------- 4 files changed, 51 insertions(+), 49 deletions(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index d266242b..bd4ab689 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -395,6 +395,8 @@ class GeometryCollection(object): # NOTE: keeping this here to make sure auto_where unpacks into # just the two elements source_where, target_where = auto_where + source_where = sym.as_dofdesc(source_where) + target_where = sym.as_dofdesc(target_where) self._default_source_place = source_where self._default_target_place = target_where @@ -402,15 +404,15 @@ class GeometryCollection(object): self.places = {} if isinstance(places, LayerPotentialSourceBase): - self.places[source_where] = places - self.places[target_where] = \ + self.places[source_where.where] = places + self.places[target_where.where] = \ self._get_lpot_discretization(places, target_where) elif isinstance(places, (Discretization, TargetBase)): - self.places[target_where] = places + self.places[target_where.where] = places elif isinstance(places, tuple): source_discr, target_discr = places - self.places[source_where] = source_discr - self.places[target_where] = target_discr + self.places[source_where.where] = source_discr + self.places[target_where.where] = target_discr else: self.places = places.copy() diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index d24b67e5..88efdbad 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -81,10 +81,9 @@ def _get_layer_potential_args(mapper, expr, source): """ # skip resampling if source and target are the same - from pytential.symbolic.primitives import DEFAULT_SOURCE, DEFAULT_TARGET - if ((expr.source is not DEFAULT_SOURCE) - and (expr.target is not DEFAULT_TARGET) - and (isinstance(expr.source, type(expr.target)))): + source_dd = sym.as_dofdesc(expr.source) + target_dd = sym.as_dofdesc(expr.target) + if source_dd.discr == target_dd.discr: source = None kernel_args = {} @@ -405,13 +404,14 @@ class MatrixBuilder(MatrixBuilderBase): dep_source, dep_discr, places, context) def map_int_g(self, expr): - where_source = expr.source - if where_source is sym.DEFAULT_SOURCE: - where_source = sym.QBXSourceQuadStage2(expr.source) + source_dd = sym.as_dofdesc(expr.source) + target_dd = sym.as_dofdesc(expr.target) + if source_dd.discr is None: + source_dd = source_dd.copy(discr=sym.QBX_SOURCE_QUAD_STAGE2) - source = self.places[expr.source] - source_discr = self.places.get_discretization(where_source) - target_discr = self.places.get_discretization(expr.target) + lpot_source = self.places[source_dd] + source_discr = self.places.get_discretization(source_dd) + target_discr = self.places.get_discretization(target_dd) rec_density = self.rec(expr.density) if is_zero(rec_density): @@ -422,10 +422,10 @@ class MatrixBuilder(MatrixBuilderBase): raise NotImplementedError("layer potentials on non-variables") kernel = expr.kernel - kernel_args = _get_layer_potential_args(self, expr, source) + kernel_args = _get_layer_potential_args(self, expr, lpot_source) from sumpy.expansion.local import LineTaylorLocalExpansion - local_expn = LineTaylorLocalExpansion(kernel, source.qbx_order) + local_expn = LineTaylorLocalExpansion(kernel, lpot_source.qbx_order) from sumpy.qbx import LayerPotentialMatrixGenerator mat_gen = LayerPotentialMatrixGenerator( @@ -433,7 +433,7 @@ class MatrixBuilder(MatrixBuilderBase): assert abs(expr.qbx_forced_limit) > 0 centers, radii = _get_centers_and_expansion_radii(self.queue, - source, target_discr, expr.qbx_forced_limit) + lpot_source, target_discr, expr.qbx_forced_limit) _, (mat,) = mat_gen(self.queue, targets=target_discr.nodes(), @@ -443,14 +443,14 @@ class MatrixBuilder(MatrixBuilderBase): **kernel_args) mat = mat.get() - waa = _get_weights_and_area_elements(self.queue, source, source_discr) + waa = _get_weights_and_area_elements(self.queue, lpot_source, source_discr) mat[:, :] *= waa.get(self.queue) if target_discr.nnodes != source_discr.nnodes: # NOTE: we only resample sources assert target_discr.nnodes < source_discr.nnodes - resampler = source.direct_resampler + resampler = lpot_source.direct_resampler resample_mat = resampler.full_resample_matrix(self.queue).get(self.queue) mat = mat.dot(resample_mat) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index a9cd95a5..6f138a09 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -346,6 +346,11 @@ class DOFDescriptor(object): self.granularity = granularity def copy(self, where=None, discr=None, granularity=None): + if isinstance(where, DOFDescriptor): + discr = where.discr if discr is None else discr + granularity = where.granularity if granularity is None else granularity + where = where.where + return type(self)( where=(self.where if where is None else where), diff --git a/test/test_matrix.py b/test/test_matrix.py index d912b2e7..01db67e2 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -40,8 +40,6 @@ from meshmode.mesh.generation import ( # noqa ellipse, NArmedStarfish, make_curve_mesh, generate_torus) from pytential import bind, sym -from pytential.symbolic.primitives import DEFAULT_SOURCE, DEFAULT_TARGET -from pytential.symbolic.primitives import QBXSourceStage1, QBXSourceQuadStage2 import pytest from pyopencl.tools import ( # noqa @@ -289,7 +287,7 @@ def test_p2p_block_builder(ctx_factory, factor, ndim, lpot_id, from pytential.symbolic.execution import _prepare_expr, _prepare_domains places = GeometryCollection(qbx) expr = _prepare_expr(places, op) - domains = _prepare_domains(1, places, None, DEFAULT_SOURCE) + domains = _prepare_domains(1, places, None, sym.DEFAULT_SOURCE) from pytential.symbolic.matrix import P2PMatrixBuilder mbuilder = P2PMatrixBuilder(queue, @@ -356,20 +354,20 @@ def test_qbx_block_builder(ctx_factory, factor, ndim, lpot_id, # NOTE: NearFieldBlockBuilder only does stage1/stage1 or stage2/stage2, # so we need to hardcode the discr for MatrixBuilder too, since the # defaults are different - where = (QBXSourceStage1(DEFAULT_SOURCE), QBXSourceStage1(DEFAULT_TARGET)) + place_ids = (sym.QBX_SOURCE_STAGE1, sym.QBX_SOURCE_STAGE1) from pytential.symbolic.execution import GeometryCollection, _prepare_expr - places = GeometryCollection(qbx, auto_where=where) + places = GeometryCollection(qbx, auto_where=place_ids) expr = _prepare_expr(places, op) - density_discr = places.get_discretization(where[0]) + density_discr = places.get_discretization(place_ids[0]) index_set = _build_block_index(density_discr, factor=factor) from pytential.symbolic.matrix import NearFieldBlockBuilder mbuilder = NearFieldBlockBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[where[0]], - dep_discr=places.get_discretization(where[0]), + dep_source=places[place_ids[0]], + dep_discr=places.get_discretization(place_ids[0]), places=places, index_set=index_set, context={}) @@ -379,8 +377,8 @@ def test_qbx_block_builder(ctx_factory, factor, ndim, lpot_id, mbuilder = MatrixBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[where[0]], - dep_discr=places.get_discretization(where[0]), + dep_source=places[place_ids[0]], + dep_discr=places.get_discretization(place_ids[0]), places=places, context={}) mat = mbuilder(expr) @@ -408,13 +406,11 @@ def test_qbx_block_builder(ctx_factory, factor, ndim, lpot_id, assert _max_block_error(mat, blk, index_set) < 1.0e-14 -@pytest.mark.parametrize('place_id', - [(DEFAULT_SOURCE, DEFAULT_TARGET), - (QBXSourceStage1(DEFAULT_SOURCE), - QBXSourceStage1(DEFAULT_TARGET)), - (QBXSourceQuadStage2(DEFAULT_SOURCE), - QBXSourceQuadStage2(DEFAULT_TARGET))]) -def test_build_matrix_places(ctx_factory, place_id, visualize=False): +@pytest.mark.parametrize('place_ids', + [(None, None), + (sym.QBX_SOURCE_STAGE1, sym.QBX_SOURCE_STAGE1), + (sym.QBX_SOURCE_QUAD_STAGE2, sym.QBX_SOURCE_QUAD_STAGE2)]) +def test_build_matrix_places(ctx_factory, place_ids, visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) @@ -430,16 +426,15 @@ def test_build_matrix_places(ctx_factory, place_id, visualize=False): qbx_forced_limit=qbx_forced_limit) from pytential.symbolic.execution import GeometryCollection - places = GeometryCollection(qbx, auto_where=place_id) - source_discr = places.get_discretization(place_id[0]) - target_discr = places.get_discretization(place_id[1]) + places = GeometryCollection(qbx) + source_discr = places.get_discretization(place_ids[0]) + target_discr = places.get_discretization(place_ids[1]) index_set = _build_block_index(source_discr, factor=0.6) # build full QBX matrix from pytential.symbolic.execution import build_matrix - qbx_mat = build_matrix(queue, qbx, op, u_sym, - auto_where=place_id, domains=place_id[0]) + qbx_mat = build_matrix(queue, qbx, op, u_sym, auto_where=place_ids) qbx_mat = qbx_mat.get(queue) assert qbx_mat.shape == (target_discr.nnodes, source_discr.nnodes) @@ -452,8 +447,8 @@ def test_build_matrix_places(ctx_factory, place_id, visualize=False): mbuilder = P2PMatrixBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[place_id[0]], - dep_discr=places.get_discretization(place_id[0]), + dep_source=places[place_ids[0]], + dep_discr=places.get_discretization(place_ids[0]), places=places, context={}) p2p_mat = mbuilder(op) @@ -465,21 +460,21 @@ def test_build_matrix_places(ctx_factory, place_id, visualize=False): mbuilder = NearFieldBlockBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[place_id[0]], - dep_discr=places.get_discretization(place_id[0]), + dep_source=places[place_ids[0]], + dep_discr=places.get_discretization(place_ids[0]), places=places, index_set=index_set, context={}) mat = mbuilder(op) - if place_id[0] is not DEFAULT_SOURCE: + if place_ids[0] is not DEFAULT_SOURCE: assert _max_block_error(qbx_mat, mat, index_set.get(queue)) < 1.0e-14 from pytential.symbolic.matrix import FarFieldBlockBuilder mbuilder = FarFieldBlockBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[place_id[0]], - dep_discr=places.get_discretization(place_id[0]), + dep_source=places[place_ids[0]], + dep_discr=places.get_discretization(place_ids[0]), places=places, index_set=index_set, context={}, -- GitLab From 8f065b4c191b4db4773ca58a5a69bafbaa3100fe Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 1 Jul 2019 20:35:11 -0500 Subject: [PATCH 27/96] fix incorrect dim_fudge_factor copy pasting --- pytential/symbolic/execution.py | 14 +++++++------- pytential/symbolic/primitives.py | 32 ++++++++++++++++++-------------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index bd4ab689..130e64b7 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -395,8 +395,8 @@ class GeometryCollection(object): # NOTE: keeping this here to make sure auto_where unpacks into # just the two elements source_where, target_where = auto_where - source_where = sym.as_dofdesc(source_where) - target_where = sym.as_dofdesc(target_where) + # source_where = sym.as_dofdesc(source_where) + # target_where = sym.as_dofdesc(target_where) self._default_source_place = source_where self._default_target_place = target_where @@ -404,15 +404,15 @@ class GeometryCollection(object): self.places = {} if isinstance(places, LayerPotentialSourceBase): - self.places[source_where.where] = places - self.places[target_where.where] = \ + self.places[source_where] = places + self.places[target_where] = \ self._get_lpot_discretization(places, target_where) elif isinstance(places, (Discretization, TargetBase)): - self.places[target_where.where] = places + self.places[target_where] = places elif isinstance(places, tuple): source_discr, target_discr = places - self.places[source_where.where] = source_discr - self.places[target_where.where] = target_discr + self.places[source_where] = source_discr + self.places[target_where] = target_discr else: self.places = places.copy() diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 6f138a09..e1d1fb83 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -938,20 +938,24 @@ def _scaled_max_curvature(ambient_dim, dim=None, where=None): # {{{ qbx-specific geometry -def _expansion_radii_factor(ambient_dim): - dim_fudge_factor = 0.5 if ambient_dim == 2 else 1.0 +def _expansion_radii_factor(ambient_dim, dim): + if dim is None: + dim = ambient_dim - 1 + + dim_fudge_factor = 0.5 if dim == 2 else 1.0 return 0.5 * dim_fudge_factor -def _quad_resolution(ambient_dim, granularity=None, where=None): +def _quad_resolution(ambient_dim, dim=None, granularity=None, where=None): source = as_dofdesc(where) target = source.copy(granularity=granularity) - stretch = _simplex_mapping_max_stretch_factor(ambient_dim, where=source) + stretch = _simplex_mapping_max_stretch_factor(ambient_dim, + dim=dim, where=source) return Interpolation(source, target, stretch) -def _source_danger_zone_radii(ambient_dim, granularity=None, where=None): +def _source_danger_zone_radii(ambient_dim, dim=None, granularity=None, where=None): where = as_dofdesc(where) if where.discr is None: where = where.copy(discr=QBX_SOURCE_STAGE2) @@ -967,22 +971,22 @@ def _source_danger_zone_radii(ambient_dim, granularity=None, where=None): # - Setting this equal to half the expansion radius will not provide # a refinement 'buffer layer' at a 2x coarsening fringe. - factor = 0.75 * _expansion_radii_factor(ambient_dim) - return factor * _quad_resolution(ambient_dim, + factor = 0.75 * _expansion_radii_factor(ambient_dim, dim) + return factor * _quad_resolution(ambient_dim, dim=dim, granularity=granularity, where=where) -def _close_target_tunnel_radii(ambient_dim, granularity=None, where=None): - factor = 0.5 * _expansion_radii_factor(ambient_dim) +def _close_target_tunnel_radii(ambient_dim, dim=None, granularity=None, where=None): + factor = 0.5 * _expansion_radii_factor(ambient_dim, dim) - return factor * _quad_resolution(ambient_dim, + return factor * _quad_resolution(ambient_dim, dim=dim, granularity=granularity, where=where) -def expansion_radii(ambient_dim, granularity=None, where=None): - factor = _expansion_radii_factor(ambient_dim) +def expansion_radii(ambient_dim, dim=None, granularity=None, where=None): + factor = _expansion_radii_factor(ambient_dim, dim) - return cse(factor * _quad_resolution(ambient_dim, + return cse(factor * _quad_resolution(ambient_dim, dim=dim, granularity=granularity, where=where), cse_scope.DISCRETIZATION) @@ -991,7 +995,7 @@ def expansion_centers(ambient_dim, side, dim=None, where=None): x = nodes(ambient_dim, where=where) normals = normal(ambient_dim, dim=dim, where=where) - radii = expansion_radii(ambient_dim, + radii = expansion_radii(ambient_dim, dim=dim, granularity=GRANULARITY_NODE, where=where) centers = x + side * radii * normals -- GitLab From 4462fc21a934b4dab63e438f9198f35ec3e8cc29 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 1 Jul 2019 21:09:32 -0500 Subject: [PATCH 28/96] execution: clean up map_interpolation a bit --- pytential/qbx/utils.py | 24 +++++++++--------------- pytential/symbolic/execution.py | 14 ++++---------- pytential/unregularized.py | 11 +++++++++-- 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index a7c1379d..c9385a58 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -179,7 +179,7 @@ class ElementSampleConnection(DiscretizationConnection): return result -def connection_from_dds(lpot_source, source, target): +def connection_from_dds(places, source, target): from pytential import sym source = sym.as_dofdesc(source) target = sym.as_dofdesc(target) @@ -191,37 +191,31 @@ def connection_from_dds(lpot_source, source, target): if source.granularity != sym.GRANULARITY_NODE: raise ValueError('can only interpolate from `GRANULARITY_NODE`') + lpot_source = places[source] connections = [] - from_discr = lpot_source.density_discr if target.discr != source.discr: if target.discr != sym.QBX_SOURCE_QUAD_STAGE2: raise RuntimeError('can only interpolate to `QBX_SOURCE_QUAD_STAGE2`') if source.discr == sym.QBX_SOURCE_STAGE2: - from_discr = lpot_source.stage2_density_discr connections.append(lpot_source.refined_interp_to_ovsmp_quad_connection) elif source.discr == sym.QBX_SOURCE_QUAD_STAGE2: - from_discr = lpot_source.quad_stage2_density_discrd + pass else: - from_discr = lpot_source.density_discr connections.append(lpot_source.resampler) if target.granularity != source.granularity: - if target.discr == sym.QBX_SOURCE_STAGE2: - discr = lpot_source.stage2_density_discr - elif target.discr == sym.QBX_SOURCE_QUAD_STAGE2: - discr = lpot_source.quad_stage2_density_discr - else: - discr = lpot_source.density_discr - - if not connections: - from_discr = discr - + discr = places.get_discretization(target) if target.granularity == sym.GRANULARITY_CENTER: connections.append(InterleaverConnection(discr)) elif target.granularity == sym.GRANULARITY_ELEMENT: connections.append(ElementSampleConnection(discr)) + if not connections: + from_discr = places.get_discretization(source) + else: + from_discr = None + from meshmode.discretization.connection import ChainedDiscretizationConnection return ChainedDiscretizationConnection(connections, from_discr=from_discr) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 130e64b7..3f5cdb61 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -174,16 +174,12 @@ class EvaluationMapper(EvaluationMapperBase): return source.map_quad_kernel_op(expr, self.bound_expr, self.rec) def map_interpolation(self, expr): - from pytential.source import LayerPotentialSourceBase - from pytential.qbx.utils import connection_from_dds - - source = self.bound_expr.places[expr.source] - if not isinstance(source, LayerPotentialSourceBase): - raise TypeError("source must be a `LayerPotentialSourceBase`") - operand = self.rec(expr.operand) + if isinstance(operand, cl.array.Array): - conn = connection_from_dds(source, expr.source, expr.target) + from pytential.qbx.utils import connection_from_dds + + conn = connection_from_dds(self.bound_expr.places, expr.source, expr.target) return conn(self.queue, operand) elif isinstance(operand, (int, float, complex, np.number)): return operand @@ -395,8 +391,6 @@ class GeometryCollection(object): # NOTE: keeping this here to make sure auto_where unpacks into # just the two elements source_where, target_where = auto_where - # source_where = sym.as_dofdesc(source_where) - # target_where = sym.as_dofdesc(target_where) self._default_source_place = source_where self._default_target_place = target_where diff --git a/pytential/unregularized.py b/pytential/unregularized.py index 4bd67ae9..8d2e714c 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -113,8 +113,15 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): def quad_stage2_density_discr(self): return self.density_discr - def resampler(self, queue, f): - return f + @property + def resampler(self): + # NOTE: this is a no-op, but it returns a chained connection + # anyway to match the return type of QBXLayerPotentialSource.resampler + from meshmode.discretization.connection import \ + ChainedDiscretizationConnection + + return ChainedDiscretizationConnection([], + from_discr=self.density_discr) def with_refinement(self): raise NotImplementedError -- GitLab From 6175499ba2a5ddb4d454633978b1e731f9b3a1c0 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 1 Jul 2019 21:13:16 -0500 Subject: [PATCH 29/96] utils: add fixme --- pytential/qbx/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index c9385a58..cda8915a 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -195,6 +195,8 @@ def connection_from_dds(places, source, target): connections = [] if target.discr != source.discr: if target.discr != sym.QBX_SOURCE_QUAD_STAGE2: + # FIXME: can probably extend this to project from a QUAD_STAGE2 + # using L2ProjectionInverseDiscretizationConnection raise RuntimeError('can only interpolate to `QBX_SOURCE_QUAD_STAGE2`') if source.discr == sym.QBX_SOURCE_STAGE2: -- GitLab From 155eefafcb441e4385e2cc2a4567dd5e0e5e7a96 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Tue, 2 Jul 2019 09:22:11 -0500 Subject: [PATCH 30/96] propagate full dofdesc --- pytential/qbx/utils.py | 2 +- pytential/symbolic/execution.py | 13 ++++++++----- pytential/symbolic/mappers.py | 10 ++++++++-- pytential/symbolic/matrix.py | 16 +++++++--------- test/test_global_qbx.py | 3 +-- test/test_matrix.py | 15 ++++++++++++--- 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index cda8915a..dc86cd2c 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -195,7 +195,7 @@ def connection_from_dds(places, source, target): connections = [] if target.discr != source.discr: if target.discr != sym.QBX_SOURCE_QUAD_STAGE2: - # FIXME: can probably extend this to project from a QUAD_STAGE2 + # TODO: can probably extend this to project from a QUAD_STAGE2 # using L2ProjectionInverseDiscretizationConnection raise RuntimeError('can only interpolate to `QBX_SOURCE_QUAD_STAGE2`') diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 3f5cdb61..445bf23e 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -392,21 +392,24 @@ class GeometryCollection(object): # just the two elements source_where, target_where = auto_where + source_where = sym.as_dofdesc(source_where) + target_where = sym.as_dofdesc(target_where) + self._default_source_place = source_where self._default_target_place = target_where self._default_place_ids = (source_where, target_where) self.places = {} if isinstance(places, LayerPotentialSourceBase): - self.places[source_where] = places - self.places[target_where] = \ + self.places[source_where.where] = places + self.places[target_where.where] = \ self._get_lpot_discretization(places, target_where) elif isinstance(places, (Discretization, TargetBase)): - self.places[target_where] = places + self.places[target_where.where] = places elif isinstance(places, tuple): source_discr, target_discr = places - self.places[source_where] = source_discr - self.places[target_where] = target_discr + self.places[source_where.where] = source_discr + self.places[target_where.where] = target_discr else: self.places = places.copy() diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index e47281af..1bccee24 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -454,8 +454,14 @@ class QBXPreprocessor(IdentityMapper): self.places = places def map_int_g(self, expr): - source_discr = self.places.get_discretization(self.source_name) - target_discr = self.places.get_discretization(expr.target) + from pytential import sym + source_dd = sym.as_dofdesc(expr.source) + target_dd = sym.as_dofdesc(expr.target) + if source_dd.where != self.source_name: + return expr + + source_discr = self.places.get_discretization(source_dd) + target_discr = self.places.get_discretization(target_dd) if expr.qbx_forced_limit == 0: raise ValueError("qbx_forced_limit == 0 was a bad idea and " diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index 88efdbad..3dd03959 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -80,12 +80,6 @@ def _get_layer_potential_args(mapper, expr, source): :return: a mapping of kernel arguments evaluated by the *mapper*. """ - # skip resampling if source and target are the same - source_dd = sym.as_dofdesc(expr.source) - target_dd = sym.as_dofdesc(expr.target) - if source_dd.discr == target_dd.discr: - source = None - kernel_args = {} for arg_name, arg_expr in six.iteritems(expr.kernel_arguments): rec_arg = mapper.rec(arg_expr) @@ -168,7 +162,7 @@ def _get_centers_and_expansion_radii(queue, source, target_discr, qbx_forced_lim # targets are associated to a center. We can't use the user provided # source.target_association_tolerance here because it will likely be # way too small. - target_association_tolerance = 1.0e-1 + target_association_tolerance = 5.0e-1 from pytential.qbx.target_assoc import associate_targets_to_qbx_centers code_container = source.target_association_code_container @@ -422,7 +416,11 @@ class MatrixBuilder(MatrixBuilderBase): raise NotImplementedError("layer potentials on non-variables") kernel = expr.kernel - kernel_args = _get_layer_potential_args(self, expr, lpot_source) + if source_dd.discr == target_dd.discr: + # NOTE: passing None to avoid any resampling + kernel_args = _get_layer_potential_args(self, expr, None) + else: + kernel_args = _get_layer_potential_args(self, expr, lpot_source) from sumpy.expansion.local import LineTaylorLocalExpansion local_expn = LineTaylorLocalExpansion(kernel, lpot_source.qbx_order) @@ -446,7 +444,7 @@ class MatrixBuilder(MatrixBuilderBase): waa = _get_weights_and_area_elements(self.queue, lpot_source, source_discr) mat[:, :] *= waa.get(self.queue) - if target_discr.nnodes != source_discr.nnodes: + if source_dd.discr != target_dd.discr: # NOTE: we only resample sources assert target_discr.nnodes < source_discr.nnodes diff --git a/test/test_global_qbx.py b/test/test_global_qbx.py index 99e942bf..50b8212b 100644 --- a/test/test_global_qbx.py +++ b/test/test_global_qbx.py @@ -130,8 +130,7 @@ def run_source_refinement_test(ctx_factory, mesh, order, helmholtz_k=None): 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, - granularity=sym.GRANULARITY_ELEMENT, - where=sym.QBX_SOURCE_STAGE2))(queue).get() + granularity=sym.GRANULARITY_ELEMENT))(queue).get() quad_res = bind(lpot_source, sym._quad_resolution( lpot_source.ambient_dim, diff --git a/test/test_matrix.py b/test/test_matrix.py index 01db67e2..73a5ab11 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -354,7 +354,11 @@ def test_qbx_block_builder(ctx_factory, factor, ndim, lpot_id, # NOTE: NearFieldBlockBuilder only does stage1/stage1 or stage2/stage2, # so we need to hardcode the discr for MatrixBuilder too, since the # defaults are different - place_ids = (sym.QBX_SOURCE_STAGE1, sym.QBX_SOURCE_STAGE1) + source_dd = sym.DOFDescriptor(sym.DEFAULT_SOURCE, + discr=sym.QBX_SOURCE_STAGE1) + target_dd = sym.DOFDescriptor(sym.DEFAULT_TARGET, + discr=sym.QBX_SOURCE_STAGE1) + place_ids = (source_dd, target_dd) from pytential.symbolic.execution import GeometryCollection, _prepare_expr places = GeometryCollection(qbx, auto_where=place_ids) @@ -425,8 +429,13 @@ def test_build_matrix_places(ctx_factory, place_ids, visualize=False): op, u_sym, _ = _build_op(lpot_id=1, ndim=2, qbx_forced_limit=qbx_forced_limit) + place_ids = ( + sym.as_dofdesc(place_ids[0]).copy(where=sym.DEFAULT_SOURCE), + sym.as_dofdesc(place_ids[1]).copy(where=sym.DEFAULT_TARGET) + ) + from pytential.symbolic.execution import GeometryCollection - places = GeometryCollection(qbx) + places = GeometryCollection(qbx, auto_where=place_ids) source_discr = places.get_discretization(place_ids[0]) target_discr = places.get_discretization(place_ids[1]) @@ -466,7 +475,7 @@ def test_build_matrix_places(ctx_factory, place_ids, visualize=False): index_set=index_set, context={}) mat = mbuilder(op) - if place_ids[0] is not DEFAULT_SOURCE: + if place_ids[0].discr is not None: assert _max_block_error(qbx_mat, mat, index_set.get(queue)) < 1.0e-14 from pytential.symbolic.matrix import FarFieldBlockBuilder -- GitLab From 35fdcd21765761c3e9e1f54bc7d4bdcfd9ecd657 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Tue, 2 Jul 2019 09:37:08 -0500 Subject: [PATCH 31/96] remove a remaining last_dim_length expression --- pytential/qbx/refinement.py | 5 +++-- pytential/symbolic/primitives.py | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index 53daffb9..3062e1b7 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -624,8 +624,9 @@ def refine_for_global_qbx(lpot_source, wrangler, with ProcessLogger(logger, "checking scaled max curvature threshold"): from pytential import sym, bind - scaled_max_curv = bind(lpot_source, sym.DOFGranularityConverter( - "npanels", + scaled_max_curv = bind(lpot_source, sym.Interpolate( + sym.as_dofdesc(None), + sym.as_dofdesc(sym.GRANULARITY_ELEMENT), sym._scaled_max_curvature( lpot_source.ambient_dim)))(wrangler.queue) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index e1d1fb83..eec5a416 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -402,6 +402,11 @@ def as_dofdesc(desc): or desc == QBX_SOURCE_QUAD_STAGE2: return DOFDescriptor(None, discr=desc) + if desc == GRANULARITY_NODE \ + or desc == GRANULARITY_CENTER \ + or desc == GRANULARITY_ELEMENT: + return DOFDescriptor(None, granularity=desc) + return DOFDescriptor(desc) -- GitLab From a3cd52ae6f5efd91d0c488fbe6f5414fa1988356 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Tue, 2 Jul 2019 12:11:15 -0500 Subject: [PATCH 32/96] flake8 fixes --- pytential/qbx/utils.py | 4 ++-- pytential/symbolic/execution.py | 3 ++- pytential/symbolic/primitives.py | 8 +++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index dc86cd2c..c047e3cd 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -184,8 +184,8 @@ def connection_from_dds(places, source, target): source = sym.as_dofdesc(source) target = sym.as_dofdesc(target) - if not ((source.where == sym.DEFAULT_SOURCE \ - and target.where == sym.DEFAULT_TARGET) \ + if not ((source.where == sym.DEFAULT_SOURCE + and target.where == sym.DEFAULT_TARGET) or source.where == target.where): raise ValueError('cannot interpolate between different domains') if source.granularity != sym.GRANULARITY_NODE: diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 445bf23e..cd08ddb8 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -179,7 +179,8 @@ class EvaluationMapper(EvaluationMapperBase): if isinstance(operand, cl.array.Array): from pytential.qbx.utils import connection_from_dds - conn = connection_from_dds(self.bound_expr.places, expr.source, expr.target) + conn = connection_from_dds( + self.bound_expr.places, expr.source, expr.target) return conn(self.queue, operand) elif isinstance(operand, (int, float, complex, np.number)): return operand diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index eec5a416..46b94cf2 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -245,6 +245,7 @@ class QBXSourceStage1(_QBXSource): :attr:`pytential.qbx.QBXLayerPotentialSource.density_discr` of the layer potential source identified by :attr:`where`. """ + pass class QBXSourceStage2(_QBXSource): @@ -252,6 +253,7 @@ class QBXSourceStage2(_QBXSource): :attr:`pytential.qbx.QBXLayerPotentialSource.stage2_density_discr` of the layer potential source identified by :attr:`where`. """ + pass class QBXSourceQuadStage2(_QBXSource): @@ -259,20 +261,24 @@ class QBXSourceQuadStage2(_QBXSource): :attr:`pytential.qbx.QBXLayerPotentialSource.quad_stage2_density_discr` of the layer potential source identified by :attr:`where`. """ + pass class QBX_SOURCE_STAGE1: # noqa: N801 """Symbolic identifier for the base `stage1` discretization :attr:`pytential.source.LayerPotentialSourceBase.density_discr`. """ + pass + class QBX_SOURCE_STAGE2: # noqa: N801 """Symbolic identifier for the `stage2` discretization :attr:`pytential.source.LayerPotentialSourceBase.stage2_density_discr`. """ + pass -class QBX_SOURCE_QUAD_STAGE2: # noqa: N801 +class QBX_SOURCE_QUAD_STAGE2: # noqa: N801 """Symbolic identifier for the `stage2` discretization :attr:`pytential.source.LayerPotentialSourceBase.quad_stage2_density_discr`. """ -- GitLab From 47e9a777880f8497610a60d5c0f1338e2dced565 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 4 Jul 2019 15:48:02 -0500 Subject: [PATCH 33/96] do not overwrite granularity --- pytential/qbx/__init__.py | 11 ++++++----- pytential/qbx/refinement.py | 4 ++-- pytential/symbolic/mappers.py | 2 +- pytential/symbolic/matrix.py | 2 +- pytential/symbolic/primitives.py | 1 - 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 328c4e8f..2631c061 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -770,11 +770,12 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): from pytential import bind, sym expansion_radii = bind(self, sym.expansion_radii(self.ambient_dim))(queue) - int_centers = bind(self, - sym.expansion_centers(self.ambient_dim, -1))(queue) - ext_centers = bind(self, - sym.expansion_centers(self.ambient_dim, +1))(queue) - centers = {-1: int_centers, 1: ext_centers} + 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 = [] diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index 3062e1b7..b4eb73cb 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -627,8 +627,8 @@ def refine_for_global_qbx(lpot_source, wrangler, scaled_max_curv = bind(lpot_source, sym.Interpolate( sym.as_dofdesc(None), sym.as_dofdesc(sym.GRANULARITY_ELEMENT), - sym._scaled_max_curvature( - lpot_source.ambient_dim)))(wrangler.queue) + sym.ElementwiseMax(sym._scaled_max_curvature( + lpot_source.ambient_dim))))(wrangler.queue) violates_scaled_max_curv = \ wrangler.check_element_prop_threshold( diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 1bccee24..e7267cd1 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -249,7 +249,7 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): return expr def map_elementwise_sum(self, expr): - operand = self.operand_rec(expr.operand) + operand = self.rec(expr.operand) dd = prim.as_dofdesc(expr.where) if dd.where is None: diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index 3dd03959..ced75199 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -162,7 +162,7 @@ def _get_centers_and_expansion_radii(queue, source, target_discr, qbx_forced_lim # targets are associated to a center. We can't use the user provided # source.target_association_tolerance here because it will likely be # way too small. - target_association_tolerance = 5.0e-1 + target_association_tolerance = 1.0e-1 from pytential.qbx.target_assoc import associate_targets_to_qbx_centers code_container = source.target_association_code_container diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 46b94cf2..8ece0ebb 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -354,7 +354,6 @@ class DOFDescriptor(object): def copy(self, where=None, discr=None, granularity=None): if isinstance(where, DOFDescriptor): discr = where.discr if discr is None else discr - granularity = where.granularity if granularity is None else granularity where = where.where return type(self)( -- GitLab From 68f62c2a64ae3cd568ba87106b48b41af98b84ce Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 4 Jul 2019 16:33:54 -0500 Subject: [PATCH 34/96] fix rename in tests --- pytential/symbolic/execution.py | 3 --- test/test_layer_pot_eigenvalues.py | 5 ++--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index cd08ddb8..6cc052a4 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -445,9 +445,6 @@ class GeometryCollection(object): if dd.where in self.places: discr = self.places[dd.where] else: - discr = None - - if discr is None: raise KeyError('`where` not in the collection: {}'.format(dd.where)) from pytential.source import LayerPotentialSourceBase diff --git a/test/test_layer_pot_eigenvalues.py b/test/test_layer_pot_eigenvalues.py index 0c6de241..15dee8a3 100644 --- a/test/test_layer_pot_eigenvalues.py +++ b/test/test_layer_pot_eigenvalues.py @@ -119,9 +119,8 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, if 0: # plot geometry, centers, normals - centers = bind(qbx, sym.qbx_expansion_centers( - qbx._expansion_radii_factor, +1, - qbx.ambient_dim))(queue) + centers = bind(qbx, + sym.expansion_centers(qbx.ambient_dim, +1))(queue) nodes_h = nodes.get() centers_h = [centers[0].get(), centers[1].get()] -- GitLab From 2880acea9179e150d029fdd66f49fd112a50ab70 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 4 Jul 2019 17:02:46 -0500 Subject: [PATCH 35/96] fix default for interpolation in LocationTagger --- pytential/symbolic/mappers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index e7267cd1..8ff165a2 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -301,7 +301,7 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): if source.where is None: source = source.copy(where=self.default_source) if target.where is None: - target = target.copy(where=self.default_where) + target = target.copy(where=self.default_source) return type(expr)(source, target, self.operand_rec(expr.operand)) -- GitLab From 4ede38ed4be6e203dde7df3468718346b7f3ec32 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 4 Jul 2019 18:25:42 -0500 Subject: [PATCH 36/96] fix some mismatched dofdesc in matrix construction --- pytential/symbolic/matrix.py | 17 +++++++++-------- test/test_matrix.py | 4 ++-- test/test_symbolic.py | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index ced75199..5b60fb31 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -315,15 +315,16 @@ class MatrixBuilderBase(EvaluationMapperBase): if self.is_kind_matrix(rec_operand): raise NotImplementedError("derivatives") - where_discr = self.places[expr.where] - op = sym.NumReferenceDerivative(expr.ref_axes, sym.var("u")) - return bind(where_discr, op)( - self.queue, u=cl.array.to_device(self.queue, rec_operand)).get() + rec_operand = cl.array.to_device(self.queue, rec_operand) + op = sym.NumReferenceDerivative( + ref_axes=expr.ref_axes, + operand=sym.var("u"), + where=expr.where) + return bind(self.places, op)(self.queue, u=rec_operand).get() def map_node_coordinate_component(self, expr): - where_discr = self.places[expr.where] - op = sym.NodeCoordinateComponent(expr.ambient_axis) - return bind(where_discr, op)(self.queue).get() + op = sym.NodeCoordinateComponent(expr.ambient_axis, where=expr.where) + return bind(self.places, op)(self.queue).get() def map_call(self, expr): arg, = expr.parameters @@ -549,7 +550,7 @@ class NearFieldBlockBuilder(MatrixBlockBuilderBase): raise NotImplementedError() kernel = expr.kernel - kernel_args = _get_layer_potential_args(self.mat_mapper, expr, source) + kernel_args = _get_layer_potential_args(self.mat_mapper, expr, None) from sumpy.expansion.local import LineTaylorLocalExpansion local_expn = LineTaylorLocalExpansion(kernel, source.qbx_order) diff --git a/test/test_matrix.py b/test/test_matrix.py index 73a5ab11..1800cec2 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -355,9 +355,9 @@ def test_qbx_block_builder(ctx_factory, factor, ndim, lpot_id, # so we need to hardcode the discr for MatrixBuilder too, since the # defaults are different source_dd = sym.DOFDescriptor(sym.DEFAULT_SOURCE, - discr=sym.QBX_SOURCE_STAGE1) + discr=sym.QBX_SOURCE_STAGE2) target_dd = sym.DOFDescriptor(sym.DEFAULT_TARGET, - discr=sym.QBX_SOURCE_STAGE1) + discr=sym.QBX_SOURCE_STAGE2) place_ids = (source_dd, target_dd) from pytential.symbolic.execution import GeometryCollection, _prepare_expr diff --git a/test/test_symbolic.py b/test/test_symbolic.py index 11ef6b10..a0132702 100644 --- a/test/test_symbolic.py +++ b/test/test_symbolic.py @@ -232,7 +232,7 @@ def test_interpolation(ctx_factory, source_discr, target_granularity): source = sym.DOFDescriptor(sym.DEFAULT_SOURCE, discr=source_discr, granularity=sym.GRANULARITY_NODE) - target = sym.DOFDescriptor(sym.DEFAULT_TARGET, + target = sym.DOFDescriptor(sym.DEFAULT_SOURCE, discr=sym.QBX_SOURCE_QUAD_STAGE2, granularity=target_granularity) -- GitLab From 976340b6c0ec4dd926392f4ff9f6fd9af67b02fe Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 4 Jul 2019 21:09:13 -0500 Subject: [PATCH 37/96] remove _QBXSource and friends --- pytential/symbolic/primitives.py | 67 -------------------------------- 1 file changed, 67 deletions(-) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 8ece0ebb..f292d0ab 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -211,59 +211,6 @@ class DEFAULT_TARGET: # noqa: N801 pass -class _QBXSource(object): - """A symbolic 'where' specifier for the a density of a - :attr:`pytential.qbx.QBXLayerPotentialSource` - layer potential source identified by :attr:`where`. - - .. attribute:: where - - An identifier of a layer potential source, as used in - :func:`pytential.bind`. - - .. note:: - - This is not documented functionality and only intended for - internal use. - """ - - def __init__(self, where): - self.where = where - - def __hash__(self): - return hash((type(self), self.where)) - - def __eq__(self, other): - return type(self) is type(other) and self.where == other.where - - def __ne__(self, other): - return not self.__eq__(other) - - -class QBXSourceStage1(_QBXSource): - """An explicit symbolic 'where' specifier for the - :attr:`pytential.qbx.QBXLayerPotentialSource.density_discr` - of the layer potential source identified by :attr:`where`. - """ - pass - - -class QBXSourceStage2(_QBXSource): - """A symbolic 'where' specifier for the - :attr:`pytential.qbx.QBXLayerPotentialSource.stage2_density_discr` - of the layer potential source identified by :attr:`where`. - """ - pass - - -class QBXSourceQuadStage2(_QBXSource): - """A symbolic 'where' specifier for the - :attr:`pytential.qbx.QBXLayerPotentialSource.quad_stage2_density_discr` - of the layer potential source identified by :attr:`where`. - """ - pass - - class QBX_SOURCE_STAGE1: # noqa: N801 """Symbolic identifier for the base `stage1` discretization :attr:`pytential.source.LayerPotentialSourceBase.density_discr`. @@ -388,20 +335,6 @@ def as_dofdesc(desc): if isinstance(desc, DOFDescriptor): return desc - # TODO: should be deleted once _QBXSource and friends are gone - if isinstance(desc, _QBXSource): - from warnings import warn - warn('using _QBXSource is deprecated, use DOFDescriptor instead.', - DeprecationWarning, stacklevel=2) - - if isinstance(desc, QBXSourceStage2): - discr = QBX_SOURCE_STAGE2 - elif isinstance(desc, QBXSourceQuadStage2): - discr = QBX_SOURCE_QUAD_STAGE2 - else: - discr = QBX_SOURCE_STAGE1 - return DOFDescriptor(desc.where, discr=discr) - if desc == QBX_SOURCE_STAGE1 \ or desc == QBX_SOURCE_STAGE2 \ or desc == QBX_SOURCE_QUAD_STAGE2: -- GitLab From b5479b222351fd3820137559c7fbe8385eef2959 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 4 Jul 2019 21:09:38 -0500 Subject: [PATCH 38/96] make stringify_where support dofdesc --- pytential/symbolic/mappers.py | 52 ++++++++++++++++++-------------- pytential/symbolic/primitives.py | 4 +++ 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 8ff165a2..60158db2 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -512,27 +512,35 @@ class QBXPreprocessor(IdentityMapper): # {{{ stringifier def stringify_where(where): - if isinstance(where, prim.QBXSourceStage1): - return "stage1(%s)" % stringify_where(where.where) - if isinstance(where, prim.QBXSourceStage2): - return "stage2(%s)" % stringify_where(where.where) - if isinstance(where, prim.QBXSourceQuadStage2): - return "quad_stage2(%s)" % stringify_where(where.where) - - if where is None: - return "?" - elif where is prim.DEFAULT_SOURCE: - return "s" - elif where is prim.DEFAULT_TARGET: - return "t" + dd = prim.as_dofdesc(where) + + name = [] + if dd.where is None: + name.append("?") + elif dd.where is prim.DEFAULT_SOURCE: + name.append("s") + elif dd.where is prim.DEFAULT_TARGET: + name.append("t") else: - return str(where) + name.append(str(dd.where)) + + if dd.discr == prim.QBX_SOURCE_STAGE2: + name.append("stage2") + elif dd.discr == prim.QBX_SOURCE_QUAD_STAGE2: + name.append("quads2") + + if dd.granularity == prim.GRANULARITY_CENTER: + name.append("center") + elif dd.granularity == prim.GRANULARITY_ELEMENT: + name.append("panel") + + return "/".join(name) class StringifyMapper(BaseStringifyMapper): def map_ones(self, expr, enclosing_prec): - return "Ones.%s" % stringify_where(expr.where) + return "Ones[%s]" % stringify_where(expr.where) def map_inverse(self, expr, enclosing_prec): return "Solve(%s = %s {%s})" % ( @@ -549,17 +557,17 @@ class StringifyMapper(BaseStringifyMapper): set()) def map_elementwise_sum(self, expr, enclosing_prec): - return "ElwiseSum.%s(%s)" % ( + return "ElwiseSum[%s](%s)" % ( stringify_where(expr.where), self.rec(expr.operand, PREC_NONE)) def map_elementwise_min(self, expr, enclosing_prec): - return "ElwiseMin.%s(%s)" % ( + return "ElwiseMin[%s](%s)" % ( stringify_where(expr.where), self.rec(expr.operand, PREC_NONE)) def map_elementwise_max(self, expr, enclosing_prec): - return "ElwiseMax.%s(%s)" % ( + return "ElwiseMax[%s](%s)" % ( stringify_where(expr.where), self.rec(expr.operand, PREC_NONE)) @@ -570,7 +578,7 @@ class StringifyMapper(BaseStringifyMapper): return "NodeSum(%s)" % self.rec(expr.operand, PREC_NONE) def map_node_coordinate_component(self, expr, enclosing_prec): - return "x%d.%s" % (expr.ambient_axis, + return "x%d[%s]" % (expr.ambient_axis, stringify_where(expr.where)) def map_num_reference_derivative(self, expr, enclosing_prec): @@ -580,7 +588,7 @@ class StringifyMapper(BaseStringifyMapper): "d/dr%d^%d" % (axis, mult) for axis, mult in expr.ref_axes) - result = "%s.%s %s" % ( + result = "%s[%s] %s" % ( diff_op, stringify_where(expr.where), self.rec(expr.operand, PREC_PRODUCT), @@ -592,10 +600,10 @@ class StringifyMapper(BaseStringifyMapper): return result def map_parametrization_derivative(self, expr, enclosing_prec): - return "dx/dr.%s" % (stringify_where(expr.where)) + return "dx/dr[%s]" % (stringify_where(expr.where)) def map_q_weight(self, expr, enclosing_prec): - return "w_quad.%s" % stringify_where(expr.where) + return "w_quad[%s]" % stringify_where(expr.where) def _stringify_kernel_args(self, kernel_arguments): if not kernel_arguments: diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index f292d0ab..46189dec 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -330,6 +330,10 @@ class DOFDescriptor(object): self.discr if self.discr is None else self.discr.__name__, self.granularity.__name__) + def __str__(self): + from pytential.symbolic.mappers import stringify_where + return stringify_where(self) + def as_dofdesc(desc): if isinstance(desc, DOFDescriptor): -- GitLab From c7b2dd18bf56e6f616e5fd20bd5b553d3a69034c Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Wed, 10 Jul 2019 20:35:24 -0500 Subject: [PATCH 39/96] create new connections to transport between dof granularities --- pytential/qbx/utils.py | 179 +++----------------- pytential/symbolic/dofconnection.py | 249 ++++++++++++++++++++++++++++ pytential/symbolic/execution.py | 6 +- 3 files changed, 273 insertions(+), 161 deletions(-) create mode 100644 pytential/symbolic/dofconnection.py diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index c047e3cd..b14efe57 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -31,9 +31,8 @@ import numpy as np from boxtree.tree import Tree import pyopencl as cl import pyopencl.array # noqa -from pytools import memoize, memoize_in, memoize_method +from pytools import memoize_method from loopy.version import MOST_RECENT_LANGUAGE_VERSION -from meshmode.discretization.connection import DiscretizationConnection import logging logger = logging.getLogger(__name__) @@ -71,160 +70,6 @@ QBX_TREE_MAKO_DEFS = r"""//CL:mako// # }}} -# {{{ connections - - -def mesh_el_view(mesh, group_nr, global_array): - """Return a view of *global_array* of shape - ``(..., mesh.groups[group_nr].nelements)`` - where *global_array* is of shape ``(..., nelements)``, - where *nelements* is the global (per-mesh) element count. - """ - - group = mesh.groups[group_nr] - - return global_array[ - ..., group.element_nr_base:group.element_nr_base + group.nelements] \ - .reshape( - global_array.shape[:-1] - + (group.nelements,)) - - -class InterleaverConnection(DiscretizationConnection): - def __init__(self, discr): - super(InterleaverConnection, self).__init__(discr, discr, - is_surjective=True) - - @memoize - def kernel(self, dtype): - knl = lp.make_kernel( - "[srclen, dstlen] -> {[i]: 0 <= i < srclen}", - """ - dst[2*i] = src1[i] - dst[2*i + 1] = src2[i] - """, - [ - lp.GlobalArg("src1", shape="srclen", dtype=dtype), - lp.GlobalArg("src2", shape="srclen", dtype=dtype), - lp.GlobalArg("dst", shape="dstlen", dtype=dtype), - "..." - ], - assumptions="2*srclen = dstlen", - lang_version=MOST_RECENT_LANGUAGE_VERSION, - ) - - knl = lp.split_iname(knl, "i", 128, - inner_tag="l.0", outer_tag="g.0") - return knl - - def __call__(self, queue, vecs): - if isinstance(vecs, cl.array.Array): - vecs = [[vecs], [vecs]] - elif isinstance(vecs, (list, tuple)): - assert len(vecs) == 2 - else: - raise ValueError('cannot interleave arrays') - - result = [] - for src1, src2 in zip(vecs[0], vecs[1]): - if not isinstance(src1, cl.array.Array) \ - or not isinstance(src2, cl.array.Array): - raise TypeError('non-array passed to connection') - - if src1.shape != (self.to_discr.nnodes,) \ - or src2.shape != (self.to_discr.nnodes,): - raise ValueError('invalid shape of incoming array') - - axis = cl.array.empty(queue, 2 * len(src1), src1.dtype) - self.kernel(src1.dtype)(queue, - src1=src1, src2=src2, dst=axis) - result.append(axis) - - # TODO: why is get_interleaved_something have an evt.wait? - - return result[0] if len(result) == 1 else result - - -class ElementSampleConnection(DiscretizationConnection): - def __init__(self, discr): - super(ElementSampleConnection, self).__init__(discr, discr, - is_surjective=True) - - def __call__(self, queue, vec): - @memoize_in(self, "subsample_to_elements_kernel") - def kernel(): - knl = lp.make_kernel( - "{[i, k]: 0 <= i < nelements}", - "result[i] = a[i, 0]", - [ - lp.GlobalArg("a", - shape=("nelements", "nunit_nodes"), dtype=None), - lp.ValueArg("nunit_nodes", dtype=np.int32), - "..." - ], - name="subsample_to_elements", - lang_version=MOST_RECENT_LANGUAGE_VERSION, - ) - - knl = lp.split_iname(knl, "i", 128, - inner_tag="l.0", outer_tag="g.0") - return knl - - result = cl.array.empty(queue, self.to_discr.mesh.nelements, vec.dtype) - for igrp, group in enumerate(self.to_discr.groups): - kernel()(queue, - a=group.view(vec), - result=mesh_el_view(self.to_discr.mesh, igrp, result)) - - return result - - -def connection_from_dds(places, source, target): - from pytential import sym - source = sym.as_dofdesc(source) - target = sym.as_dofdesc(target) - - if not ((source.where == sym.DEFAULT_SOURCE - and target.where == sym.DEFAULT_TARGET) - or source.where == target.where): - raise ValueError('cannot interpolate between different domains') - if source.granularity != sym.GRANULARITY_NODE: - raise ValueError('can only interpolate from `GRANULARITY_NODE`') - - lpot_source = places[source] - connections = [] - if target.discr != source.discr: - if target.discr != sym.QBX_SOURCE_QUAD_STAGE2: - # TODO: can probably extend this to project from a QUAD_STAGE2 - # using L2ProjectionInverseDiscretizationConnection - raise RuntimeError('can only interpolate to `QBX_SOURCE_QUAD_STAGE2`') - - if source.discr == sym.QBX_SOURCE_STAGE2: - connections.append(lpot_source.refined_interp_to_ovsmp_quad_connection) - elif source.discr == sym.QBX_SOURCE_QUAD_STAGE2: - pass - else: - connections.append(lpot_source.resampler) - - if target.granularity != source.granularity: - discr = places.get_discretization(target) - if target.granularity == sym.GRANULARITY_CENTER: - connections.append(InterleaverConnection(discr)) - elif target.granularity == sym.GRANULARITY_ELEMENT: - connections.append(ElementSampleConnection(discr)) - - if not connections: - from_discr = places.get_discretization(source) - else: - from_discr = None - - from meshmode.discretization.connection import ChainedDiscretizationConnection - return ChainedDiscretizationConnection(connections, from_discr=from_discr) - - -# }}} - - # {{{ make interleaved centers def get_interleaved_centers(queue, lpot_source): @@ -238,7 +83,8 @@ def get_interleaved_centers(queue, lpot_source): ext_centers = bind(lpot_source, sym.expansion_centers(lpot_source.ambient_dim, +1))(queue) - interleaver = InterleaverConnection(lpot_source.density_discr) + from pytential.symbolic.dofconnection import CenterGranularityConnection + interleaver = CenterGranularityConnection(lpot_source.density_discr) return interleaver(queue, [int_centers, ext_centers]) # }}} @@ -256,7 +102,8 @@ def get_interleaved_radii(queue, lpot_source): radii = bind(lpot_source, sym.expansion_radii(lpot_source.ambient_dim))(queue) - interleaver = InterleaverConnection(lpot_source.density_discr) + from pytential.symbolic.dofconnection import CenterGranularityConnection + interleaver = CenterGranularityConnection(lpot_source.density_discr) return interleaver(queue, radii) # }}} @@ -334,6 +181,22 @@ class TreeWranglerBase(object): # {{{ element centers of mass +def mesh_el_view(mesh, group_nr, global_array): + """Return a view of *global_array* of shape + ``(..., mesh.groups[group_nr].nelements)`` + where *global_array* is of shape ``(..., nelements)``, + where *nelements* is the global (per-mesh) element count. + """ + + group = mesh.groups[group_nr] + + return global_array[ + ..., group.element_nr_base:group.element_nr_base + group.nelements] \ + .reshape( + global_array.shape[:-1] + + (group.nelements,)) + + def element_centers_of_mass(discr): knl = lp.make_kernel( """{[dim,k,i]: diff --git a/pytential/symbolic/dofconnection.py b/pytential/symbolic/dofconnection.py new file mode 100644 index 00000000..2f619b4b --- /dev/null +++ b/pytential/symbolic/dofconnection.py @@ -0,0 +1,249 @@ +# -*- coding: utf-8 -*- +from __future__ import division, absolute_import, print_function + +__copyright__ = """ +Copyright (C) 2019 Alexandru Fikl +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import numpy as np + +import pyopencl as cl +import pyopencl.array # noqa +from pytools import memoize + +import loopy as lp +from loopy.version import MOST_RECENT_LANGUAGE_VERSION + + +# {{{ granularity connections + +class GranularityConnection(object): + """Abstract interface for transporting a DOF between different levels + of granularity. + + .. attribute:: discr + .. automethod:: __call__ + """ + + def __init__(self, discr): + self.discr = discr + + def __call__(self, queue, vec): + raise NotImplementedError() + + +class CenterGranularityConnection(GranularityConnection): + """A :class:`GranularityConnection` used to transport from node data + (:class:`~pytential.symbolic.primitives.GRANULARITY_NODE`) to expansion + centers (:class:`~pytential.symbolic.primitives.GRANULARITY_CENTER`). + + .. attribute:: discr + .. automethod:: __call__ + """ + + def __init__(self, discr): + super(CenterGranularityConnection, self).__init__(discr) + + @memoize + def kernel(self): + knl = lp.make_kernel( + "[srclen, dstlen] -> {[i]: 0 <= i < srclen}", + """ + dst[2*i] = src1[i] + dst[2*i + 1] = src2[i] + """, + [ + lp.GlobalArg("src1", shape="srclen"), + lp.GlobalArg("src2", shape="srclen"), + lp.GlobalArg("dst", shape="dstlen"), + "..." + ], + assumptions="2*srclen = dstlen", + lang_version=MOST_RECENT_LANGUAGE_VERSION, + ) + + knl = lp.split_iname(knl, "i", 128, + inner_tag="l.0", outer_tag="g.0") + return knl + + def __call__(self, queue, vecs): + r""" + :arg vecs: a single :class:`pyopencl.array.Array` or a pair of + arrays. Given a pair of arrays :math:`x` and :math:`y`, they are + interleaved as :math:`[x_1, y_1, x_2, y_2, \ddots, x_n, y_n]`. + A single array is simply interleaved with itself. + """ + + if isinstance(vecs, cl.array.Array): + vecs = [[vecs], [vecs]] + elif isinstance(vecs, (list, tuple)): + assert len(vecs) == 2 + else: + raise ValueError('cannot interleave arrays') + + result = [] + for src1, src2 in zip(vecs[0], vecs[1]): + if not isinstance(src1, cl.array.Array) \ + or not isinstance(src2, cl.array.Array): + raise TypeError('non-array passed to connection') + + if src1.shape != (self.discr.nnodes,) \ + or src2.shape != (self.discr.nnodes,): + raise ValueError('invalid shape of incoming array') + + axis = cl.array.empty(queue, 2 * len(src1), src1.dtype) + self.kernel()(queue, + src1=src1, src2=src2, dst=axis) + result.append(axis) + + return result[0] if len(result) == 1 else result + + +class ElementGranularityConnection(GranularityConnection): + """A :class:`GranularityConnection` used to transport from node data + (:class:`~pytential.symbolic.primitives.GRANULARITY_NODE`) to expansion + center (:class:`~pytential.symbolic.primitives.GRANULARITY_ELEMENT`). + + .. attribute:: discr + .. automethod:: __call__ + """ + + def __init__(self, discr): + super(ElementGranularityConnection, self).__init__(discr) + + @memoize + def kernel(self): + knl = lp.make_kernel( + "{[i, k]: 0 <= i < nelements}", + "result[i] = a[i, 0]", + [ + lp.GlobalArg("a", + shape=("nelements", "nunit_nodes"), dtype=None), + lp.ValueArg("nunit_nodes", dtype=np.int32), + "..." + ], + name="subsample_to_elements", + lang_version=MOST_RECENT_LANGUAGE_VERSION, + ) + + knl = lp.split_iname(knl, "i", 128, + inner_tag="l.0", outer_tag="g.0") + return knl + + def __call__(self, queue, vec): + from pytential.qbx.utils import mesh_el_view + + result = cl.array.empty(queue, self.discr.mesh.nelements, vec.dtype) + for igrp, group in enumerate(self.discr.groups): + self.kernel()(queue, + a=group.view(vec), + result=mesh_el_view(self.discr.mesh, igrp, result)) + + return result + +# }}} + + +# {{{ dof connection + +class DOFConnection(object): + """A class used to transport between DOF types. + + .. attribute:: lpot_source + + A :class:`~pytential.source.LayerPotentialSourceBase`. + + .. attribute:: source + + A :class:`~pytential.symbolic.primitives.DOFDescriptor` for the + DOF type of the incoming array. + + .. attribute:: target + + A :class:`~pytential.symbolic.primitives.DOFDescriptor` for the + DOF type of the outgoing array. + + .. attribute:: connections + + A list of + :class:`~meshmode.discretization.connection.DiscretizationConnection`s + and :class:`GranularityConnection`s used to transport from the given + source to the target. + + .. automethod:: __call__ + """ + + def __init__(self, places, source, target): + from pytential import sym + source = sym.as_dofdesc(source) + target = sym.as_dofdesc(target) + + if not ((source.where == sym.DEFAULT_SOURCE + and target.where == sym.DEFAULT_TARGET) + or source.where == target.where): + raise ValueError('cannot interpolate between different domains') + if source.granularity != sym.GRANULARITY_NODE: + raise ValueError('can only interpolate from `GRANULARITY_NODE`') + + from pytential.symbolic.execution import GeometryCollection + if not isinstance(places, GeometryCollection): + places = GeometryCollection(places) + lpot_source = places[source] + + connections = [] + if target.discr != source.discr: + if target.discr != sym.QBX_SOURCE_QUAD_STAGE2: + # TODO: can probably extend this to project from a QUAD_STAGE2 + # using L2ProjectionInverseDiscretizationConnection + raise RuntimeError("can only interpolate to " + "`QBX_SOURCE_QUAD_STAGE2`") + + if source.discr == sym.QBX_SOURCE_STAGE2: + connections.append( + lpot_source.refined_interp_to_ovsmp_quad_connection) + elif source.discr == sym.QBX_SOURCE_QUAD_STAGE2: + pass + else: + connections.append(lpot_source.resampler) + + if target.granularity != source.granularity: + discr = places.get_discretization(target) + if target.granularity == sym.GRANULARITY_CENTER: + connections.append(CenterGranularityConnection(discr)) + elif target.granularity == sym.GRANULARITY_ELEMENT: + connections.append(ElementGranularityConnection(discr)) + + self.lpot_source = lpot_source + self.source = source + self.target = target + self.connections = connections + + def __call__(self, queue, vec): + for conn in self.connections: + vec = conn(queue, vec) + + return vec + +# }}} + +# vim: foldmethod=marker diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 6cc052a4..ff04b321 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -177,10 +177,10 @@ class EvaluationMapper(EvaluationMapperBase): operand = self.rec(expr.operand) if isinstance(operand, cl.array.Array): - from pytential.qbx.utils import connection_from_dds + from pytential.symbolic.dofconnection import DOFConnection - conn = connection_from_dds( - self.bound_expr.places, expr.source, expr.target) + conn = DOFConnection(self.bound_expr.places, + expr.source, expr.target) return conn(self.queue, operand) elif isinstance(operand, (int, float, complex, np.number)): return operand -- GitLab From 52bd13058f96ba3b3d54556c2f08611643b8135e Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Wed, 10 Jul 2019 20:36:15 -0500 Subject: [PATCH 40/96] remove unused private methods in QBXLayerPotentialSource --- pytential/qbx/__init__.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 2631c061..9f581ed5 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -487,21 +487,6 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): import pytential.qbx.utils as utils return utils.element_centers_of_mass(self.stage2_density_discr) - def _expansion_radii(self, last_dim_length): - raise RuntimeError("bind `expansion_radii` directly") - - def _source_danger_zone_radii(self, last_dim_length="npanels"): - raise RuntimeError("bind `_source_danger_zone_radii` directly") - - def _close_target_tunnel_radius(self, last_dim_length): - raise RuntimeError("bind `_close_target_tunnel_radii` directly") - - def _coarsest_quad_resolution(self, last_dim_length="npanels"): - raise RuntimeError("bind `_quad_resolution` directly") - - def _stage2_coarsest_quad_resolution(self, last_dim_length="npanels"): - raise RuntimeError("bind `_quad_resolution` directly") - @memoize_method def qbx_fmm_geometry_data(self, target_discrs_and_qbx_sides): """ -- GitLab From ba5ce68d1c3ed534f54b6e5c009a0886bf212c0f Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Wed, 10 Jul 2019 22:04:39 -0500 Subject: [PATCH 41/96] remove element_centers_of_mass --- pytential/linalg/proxy.py | 80 --------------------- pytential/qbx/__init__.py | 10 --- pytential/qbx/utils.py | 106 ++++------------------------ pytential/symbolic/dofconnection.py | 18 ++++- 4 files changed, 30 insertions(+), 184 deletions(-) diff --git a/pytential/linalg/proxy.py b/pytential/linalg/proxy.py index e89053d9..267600b2 100644 --- a/pytential/linalg/proxy.py +++ b/pytential/linalg/proxy.py @@ -119,85 +119,6 @@ def partition_by_nodes(discr, ranges.with_queue(None)) -def partition_by_elements(discr, - use_tree=True, - max_elements_in_box=None): - """Generate equally sized ranges of points. The partition is created at the - element level, so that all the nodes belonging to an element belong to - the same range. This can result in slightly larger differences in size - between the ranges, but can be very useful when the individual partitions - need to be resampled, integrated, etc. - - :arg discr: a :class:`meshmode.discretization.Discretization`. - :arg use_tree: if ``True``, node partitions are generated using a - :class:`boxtree.TreeBuilder`, which leads to geometrically close - points to belong to the same partition. If ``False``, a simple linear - partition is constructed. - :arg max_elements_in_box: passed to :class:`boxtree.TreeBuilder`. - - :return: a :class:`sumpy.tools.BlockIndexRanges`. - """ - - if max_elements_in_box is None: - # NOTE: keep in sync with partition_by_nodes - max_nodes_in_box = 32 - - nunit_nodes = int(np.mean([g.nunit_nodes for g in discr.groups])) - max_elements_in_box = max_nodes_in_box // nunit_nodes - - with cl.CommandQueue(discr.cl_context) as queue: - if use_tree: - from boxtree import box_flags_enum - from boxtree import TreeBuilder - - builder = TreeBuilder(discr.cl_context) - - from pytential.qbx.utils import element_centers_of_mass - elranges = np.cumsum([group.nelements for group in discr.mesh.groups]) - elcenters = element_centers_of_mass(discr) - - tree, _ = builder(queue, elcenters, - max_particles_in_box=max_elements_in_box) - - groups = discr.groups - tree = tree.get(queue) - leaf_boxes, = (tree.box_flags - & box_flags_enum.HAS_CHILDREN == 0).nonzero() - - indices = np.empty(len(leaf_boxes), dtype=np.object) - for i, ibox in enumerate(leaf_boxes): - box_start = tree.box_source_starts[ibox] - box_end = box_start + tree.box_source_counts_cumul[ibox] - - ielement = tree.user_source_ids[box_start:box_end] - igroup = np.digitize(ielement, elranges) - - indices[i] = np.hstack([_element_node_range(groups[j], k) - for j, k in zip(igroup, ielement)]) - else: - nelements = discr.mesh.nelements - elements = np.array_split(np.arange(0, nelements), - nelements // max_elements_in_box) - - elranges = np.cumsum([g.nelements for g in discr.groups]) - elgroups = [np.digitize(elements[i], elranges) - for i in range(len(elements))] - - indices = np.empty(len(elements), dtype=np.object) - for i in range(indices.shape[0]): - indices[i] = np.hstack([_element_node_range(discr.groups[j], k) - for j, k in zip(elgroups[i], elements[i])]) - - ranges = to_device(queue, - np.cumsum([0] + [b.shape[0] for b in indices])) - indices = to_device(queue, np.hstack(indices)) - assert ranges[-1] == discr.nnodes - - return BlockIndexRanges(discr.cl_context, - indices.with_queue(None), - ranges.with_queue(None)) - - def partition_from_coarse(resampler, from_indices): """Generate a partition of nodes from an existing partition on a coarser discretization. The new partition is generated based on element @@ -383,7 +304,6 @@ class ProxyGenerator(object): radius_expr = radius_expr.format(ratio=self.ratio) # NOTE: centers of mass are computed using a second-order approximation - # that currently matches what is in `element_centers_of_mass`. knl = lp.make_kernel([ "{[irange]: 0 <= irange < nranges}", "{[i]: 0 <= i < npoints}", diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 9f581ed5..399f11ef 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -477,16 +477,6 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): # {{{ internal API - @memoize_method - def _panel_centers_of_mass(self): - import pytential.qbx.utils as utils - return utils.element_centers_of_mass(self.density_discr) - - @memoize_method - def _stage2_panel_centers_of_mass(self): - import pytential.qbx.utils as utils - return utils.element_centers_of_mass(self.stage2_density_discr) - @memoize_method def qbx_fmm_geometry_data(self, target_discrs_and_qbx_sides): """ diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index b14efe57..9c7306b7 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -26,13 +26,11 @@ THE SOFTWARE. """ -import loopy as lp import numpy as np from boxtree.tree import Tree import pyopencl as cl import pyopencl.array # noqa from pytools import memoize_method -from loopy.version import MOST_RECENT_LANGUAGE_VERSION import logging logger = logging.getLogger(__name__) @@ -47,7 +45,6 @@ QBX_TREE_C_PREAMBLE = r"""//CL:mako// // have their own numbering starting at 0. These macros convert // the per-class numbering into the internal tree particle number. #define INDEX_FOR_CENTER_PARTICLE(i) (sorted_target_ids[center_offset + i]) -#define INDEX_FOR_PANEL_PARTICLE(i) (sorted_target_ids[panel_offset + i]) #define INDEX_FOR_SOURCE_PARTICLE(i) (sorted_target_ids[source_offset + i]) #define INDEX_FOR_TARGET_PARTICLE(i) (sorted_target_ids[target_offset + i]) @@ -179,66 +176,6 @@ class TreeWranglerBase(object): # }}} -# {{{ element centers of mass - -def mesh_el_view(mesh, group_nr, global_array): - """Return a view of *global_array* of shape - ``(..., mesh.groups[group_nr].nelements)`` - where *global_array* is of shape ``(..., nelements)``, - where *nelements* is the global (per-mesh) element count. - """ - - group = mesh.groups[group_nr] - - return global_array[ - ..., group.element_nr_base:group.element_nr_base + group.nelements] \ - .reshape( - global_array.shape[:-1] - + (group.nelements,)) - - -def element_centers_of_mass(discr): - knl = lp.make_kernel( - """{[dim,k,i]: - 0<=dim source relation - if use_stage2_discr: - density_discr = lpot_source.quad_stage2_density_discr - else: - density_discr = lpot_source.density_discr - qbx_panel_to_source_starts = cl.array.empty( queue, npanels + 1, dtype=tree.particle_id_dtype) el_offset = 0 @@ -470,7 +393,6 @@ def build_tree_with_qbx_metadata( qbx_panel_to_source_starts=qbx_panel_to_source_starts, qbx_panel_to_center_starts=qbx_panel_to_center_starts, qbx_user_source_slice=qbx_user_source_slice, - qbx_user_panel_slice=qbx_user_panel_slice, qbx_user_center_slice=qbx_user_center_slice, qbx_user_target_slice=qbx_user_target_slice, nqbxpanels=npanels, diff --git a/pytential/symbolic/dofconnection.py b/pytential/symbolic/dofconnection.py index 2f619b4b..3dcbe4f8 100644 --- a/pytential/symbolic/dofconnection.py +++ b/pytential/symbolic/dofconnection.py @@ -37,6 +37,22 @@ from loopy.version import MOST_RECENT_LANGUAGE_VERSION # {{{ granularity connections +def mesh_el_view(mesh, group_nr, global_array): + """Return a view of *global_array* of shape + ``(..., mesh.groups[group_nr].nelements)`` + where *global_array* is of shape ``(..., nelements)``, + where *nelements* is the global (per-mesh) element count. + """ + + group = mesh.groups[group_nr] + + return global_array[ + ..., group.element_nr_base:group.element_nr_base + group.nelements] \ + .reshape( + global_array.shape[:-1] + + (group.nelements,)) + + class GranularityConnection(object): """Abstract interface for transporting a DOF between different levels of granularity. @@ -151,8 +167,6 @@ class ElementGranularityConnection(GranularityConnection): return knl def __call__(self, queue, vec): - from pytential.qbx.utils import mesh_el_view - result = cl.array.empty(queue, self.discr.mesh.nelements, vec.dtype) for igrp, group in enumerate(self.discr.groups): self.kernel()(queue, -- GitLab From 5a23899efa7af886bfb23909ec4af06fedc18bd2 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Wed, 10 Jul 2019 22:21:46 -0500 Subject: [PATCH 42/96] fix proxy tests --- pytential/linalg/proxy.py | 4 +- test/test_linalg_proxy.py | 77 ++++----------------------------------- 2 files changed, 10 insertions(+), 71 deletions(-) diff --git a/pytential/linalg/proxy.py b/pytential/linalg/proxy.py index 267600b2..7d308ec3 100644 --- a/pytential/linalg/proxy.py +++ b/pytential/linalg/proxy.py @@ -45,7 +45,6 @@ Proxy Point Generation .. autoclass:: ProxyGenerator .. autofunction:: partition_by_nodes -.. autofunction:: partition_by_elements .. autofunction:: partition_from_coarse .. autofunction:: gather_block_neighbor_points @@ -330,7 +329,7 @@ class ProxyGenerator(object): (proxy_center[idim, irange] - center_ext[idim, srcindices[i + ioffset]]) ** 2)) + \ expansion_radii[srcindices[i + ioffset]]) - <> rqbx = if(rqbx_ext < rqbx_int, rqbx_int, rqbx_ext) + <> rqbx = rqbx_int if rqbx_ext < rqbx_int else rqbx_ext proxy_radius[irange] = {radius_expr} end @@ -355,6 +354,7 @@ class ProxyGenerator(object): lang_version=MOST_RECENT_LANGUAGE_VERSION) knl = lp.tag_inames(knl, "idim*:unr") + print(knl) return knl diff --git a/test/test_linalg_proxy.py b/test/test_linalg_proxy.py index a410ac88..ea2097eb 100644 --- a/test/test_linalg_proxy.py +++ b/test/test_linalg_proxy.py @@ -80,32 +80,17 @@ def _build_qbx_discr(queue, def _build_block_index(discr, nblks=10, factor=1.0, - method='elements', use_tree=True): - from pytential.linalg.proxy import ( - partition_by_nodes, partition_by_elements) + from pytential.linalg.proxy import partition_by_nodes - if method == 'elements': - factor = 1.0 - - if method == 'nodes': - nnodes = discr.nnodes - else: - nnodes = discr.mesh.nelements + nnodes = discr.nnodes max_particles_in_box = nnodes // nblks # create index ranges - if method == 'nodes': - indices = partition_by_nodes(discr, - use_tree=use_tree, - max_nodes_in_box=max_particles_in_box) - elif method == 'elements': - indices = partition_by_elements(discr, - use_tree=use_tree, - max_elements_in_box=max_particles_in_box) - else: - raise ValueError('unknown method: {}'.format(method)) + indices = partition_by_nodes(discr, + use_tree=use_tree, + max_nodes_in_box=max_particles_in_box) # randomly pick a subset of points if abs(factor - 1.0) > 1.0e-14: @@ -188,64 +173,18 @@ def _plot_partition_indices(queue, discr, indices, **kwargs): ]) -@pytest.mark.parametrize("method", ["nodes", "elements"]) @pytest.mark.parametrize("use_tree", [True, False]) @pytest.mark.parametrize("ndim", [2, 3]) -def test_partition_points(ctx_factory, method, use_tree, ndim, visualize=False): +def test_partition_points(ctx_factory, use_tree, ndim, visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) qbx = _build_qbx_discr(queue, ndim=ndim) _build_block_index(qbx.density_discr, - method=method, use_tree=use_tree, factor=0.6) -@pytest.mark.parametrize("use_tree", [True, False]) -@pytest.mark.parametrize("ndim", [2, 3]) -def test_partition_coarse(ctx_factory, use_tree, ndim, visualize=False): - ctx = ctx_factory() - queue = cl.CommandQueue(ctx) - - qbx = _build_qbx_discr(queue, ndim=ndim) - srcindices = _build_block_index(qbx.density_discr, - method="elements", use_tree=use_tree) - - if visualize: - discr = qbx.resampler.from_discr - _plot_partition_indices(queue, discr, srcindices, - method="elements", use_tree=use_tree, pid="stage1") - - from pytential.linalg.proxy import partition_from_coarse - resampler = qbx.direct_resampler - - t_start = time.time() - srcindices_ = partition_from_coarse(resampler, srcindices) - t_end = time.time() - if visualize: - print('Time: {:.5f}s'.format(t_end - t_start)) - - srcindices = srcindices.get(queue) - srcindices_ = srcindices_.get(queue) - - sources = resampler.from_discr.nodes().get(queue) - sources_ = resampler.to_discr.nodes().get(queue) - - for i in range(srcindices.nblocks): - isrc = srcindices.block_indices(i) - isrc_ = srcindices_.block_indices(i) - - for j in range(ndim): - assert np.min(sources_[j][isrc_]) <= np.min(sources[j][isrc]) - assert np.max(sources_[j][isrc_]) >= np.max(sources[j][isrc]) - - if visualize: - discr = resampler.to_discr - _plot_partition_indices(queue, discr, srcindices_, - method="elements", use_tree=use_tree, pid="stage2") - - @pytest.mark.parametrize("ndim", [2, 3]) @pytest.mark.parametrize("factor", [1.0, 0.6]) def test_proxy_generator(ctx_factory, ndim, factor, visualize=False): @@ -254,7 +193,7 @@ def test_proxy_generator(ctx_factory, ndim, factor, visualize=False): qbx = _build_qbx_discr(queue, ndim=ndim) srcindices = _build_block_index(qbx.density_discr, - method='nodes', factor=factor) + factor=factor) from pytential.linalg.proxy import ProxyGenerator generator = ProxyGenerator(qbx, ratio=1.1) @@ -346,7 +285,7 @@ def test_interaction_points(ctx_factory, ndim, factor, visualize=False): qbx = _build_qbx_discr(queue, ndim=ndim) srcindices = _build_block_index(qbx.density_discr, - method='nodes', factor=factor) + factor=factor) # generate proxy points from pytential.linalg.proxy import ProxyGenerator -- GitLab From 20a00cebb5b37e89a72e1441d8ed6918e8566e55 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Wed, 10 Jul 2019 22:41:55 -0500 Subject: [PATCH 43/96] flake8 fix --- test/test_linalg_proxy.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/test/test_linalg_proxy.py b/test/test_linalg_proxy.py index ea2097eb..5435e4c5 100644 --- a/test/test_linalg_proxy.py +++ b/test/test_linalg_proxy.py @@ -22,9 +22,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -import os -import time - import numpy as np import numpy.linalg as la @@ -165,9 +162,6 @@ def _plot_partition_indices(queue, discr, indices, **kwargs): vis = make_visualizer(queue, discr, 10) filename = "test_partition_{0}_{1}_{3}d_{2}.png".format(*args) - if os.path.isfile(filename): - os.remove(filename) - vis.write_vtk_file(filename, [ ("marker", cl.array.to_device(queue, marker)) ]) @@ -272,8 +266,6 @@ def test_proxy_generator(ctx_factory, ndim, factor, visualize=False): vis = make_visualizer(queue, discr, 10) filename = "test_proxy_generator_{}d_{:04}.vtu".format(ndim, i) - if os.path.isfile(filename): - os.remove(filename) vis.write_vtk_file(filename, []) @@ -356,9 +348,6 @@ def test_interaction_points(ctx_factory, ndim, factor, visualize=False): vis = make_visualizer(queue, qbx.density_discr, 10) filename = "test_area_query_{}d_{:04}.vtu".format(ndim, i) - if os.path.isfile(filename): - os.remove(filename) - vis.write_vtk_file(filename, [ ("marker", marker_dev), ]) -- GitLab From e1dae533ac78514f8fe3c94a6432256d61909d6f Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 15 Jul 2019 20:44:54 -0500 Subject: [PATCH 44/96] primitives: add h_max and weights_and_area_elements --- pytential/qbx/__init__.py | 23 +++++------------------ pytential/symbolic/primitives.py | 22 ++++++++++++++++++++++ pytential/unregularized.py | 24 ++++++++++-------------- 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 399f11ef..d5d1e0eb 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -350,20 +350,11 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): def weights_and_area_elements(self): from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: - # quad_stage2_density_discr is not guaranteed to be usable for - # interpolation/differentiation. Use density_discr to find - # area element instead, then upsample that. + waa = bind(self, sym.weights_and_area_elements( + self.ambient_dim, + where=sym.QBX_SOURCE_QUAD_STAGE2))(queue) - area_element = self.refined_interp_to_ovsmp_quad_connection( - queue, - bind( - self.stage2_density_discr, - sym.area_element(self.ambient_dim, self.dim) - )(queue)) - - qweight = bind(self.quad_stage2_density_discr, sym.QWeight())(queue) - - return (area_element.with_queue(queue)*qweight).with_queue(None) + return waa.with_queue(None) # }}} @@ -468,12 +459,8 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): @memoize_method def h_max(self): from pytential import bind, sym - with cl.CommandQueue(self.cl_context) as queue: - quad_res = bind(self, sym._quad_resolution( - self.ambient_dim, - granularity=sym.GRANULARITY_ELEMENT))(queue) - return cl.array.max(quad_res).get().item() + return bind(self, sym.h_max(self.ambient_dim))(queue) # {{{ internal API diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 46189dec..de22e2e5 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -948,6 +948,28 @@ def expansion_centers(ambient_dim, side, dim=None, where=None): centers = x + side * radii * normals return cse(centers.as_vector(), cse_scope.DISCRETIZATION) + +def h_max(ambient_dim, dim=None, where=None): + r = _quad_resolution(ambient_dim, dim=None, + granularity=GRANULARITY_ELEMENT, where=where) + + return cse(NodeMax(r), cse_scope.DISCRETIZATION) + + +def weights_and_area_elements(ambient_dim, dim=None, where=None): + where = as_dofdesc(where) + if where.discr == QBX_SOURCE_QUAD_STAGE2: + # quad_stage2_density_discr is not guaranteed to be usable for + # interpolation/differentiation. Use stage2_density_discr to find + # area elements instead, then upsample that. + source = where.copy(discr=QBX_SOURCE_STAGE2) + area = Interpolation(source, where, + area_element(ambient_dim, dim=dim, where=source)) + else: + area = area_element(ambient_dim, dim=dim, where=where) + + return cse(area * QWeight(where=where), cse_scope.DISCRETIZATION) + # }}} diff --git a/pytential/unregularized.py b/pytential/unregularized.py index 8d2e714c..ae24e2a7 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -90,24 +90,20 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): self.debug = debug @memoize_method - def weights_and_area_elements(self): - import pytential.symbolic.primitives as p - from pytential.symbolic.execution import bind + def h_max(self): + from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: - # quad_stage2_density_discr is not guaranteed to be usable for - # interpolation/differentiation. Use density_discr to find - # area element instead, then upsample that. + return bind(self, sym.h_max(self.ambient_dim))(queue) - area_element = self.resampler( - queue, - bind( - self.density_discr, - p.area_element(self.ambient_dim, self.dim) - )(queue)) + @memoize_method + def weights_and_area_elements(self): + from pytential import bind, sym + with cl.CommandQueue(self.cl_context) as queue: + waa = bind(self, + sym.weights_and_area_elements(self.ambient_dim))(queue) - qweight = bind(self.quad_stage2_density_discr, p.QWeight())(queue) + return waa.with_queue(None) - return (area_element.with_queue(queue)*qweight).with_queue(None) @property def quad_stage2_density_discr(self): -- GitLab From ffddb6b3be5c8baae90f035b94da38bb85834282 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 15 Jul 2019 20:45:47 -0500 Subject: [PATCH 45/96] execution: make sure array has a queue --- pytential/symbolic/execution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index ff04b321..e59cdcb4 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -181,7 +181,7 @@ class EvaluationMapper(EvaluationMapperBase): conn = DOFConnection(self.bound_expr.places, expr.source, expr.target) - return conn(self.queue, operand) + return conn(self.queue, operand).with_queue(self.queue) elif isinstance(operand, (int, float, complex, np.number)): return operand else: -- GitLab From ce33fd3e13d59e9b6e43f1c4ef815d7cf1140339 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 15 Jul 2019 20:46:19 -0500 Subject: [PATCH 46/96] mappers: put q_weight on source by default --- pytential/symbolic/execution.py | 5 ++++- pytential/symbolic/mappers.py | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index e59cdcb4..99fa1990 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -405,7 +405,10 @@ class GeometryCollection(object): self.places[source_where.where] = places self.places[target_where.where] = \ self._get_lpot_discretization(places, target_where) - elif isinstance(places, (Discretization, TargetBase)): + elif isinstance(places, Discretization): + self.places[source_where.where] = places + self.places[target_where.where] = places + elif isinstance(places, TargetBase): self.places[target_where.where] = places elif isinstance(places, tuple): source_discr, target_discr = places diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 60158db2..fd4759fe 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -219,7 +219,12 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): else: return expr - map_q_weight = map_ones + def map_q_weight(self, expr): + dd = prim.as_dofdesc(expr.where) + if dd.where is None: + return type(expr)(where=dd.copy(where=self.default_source)) + else: + return expr def map_parametrization_derivative_component(self, expr): dd = prim.as_dofdesc(expr.where) -- GitLab From 51aa30a23f823f7814f3d8225a48a56f446f10a7 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 15 Jul 2019 20:46:41 -0500 Subject: [PATCH 47/96] unregularized: add stage2_density_discr --- pytential/unregularized.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pytential/unregularized.py b/pytential/unregularized.py index ae24e2a7..55c3f1d0 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -104,6 +104,9 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): return waa.with_queue(None) + @property + def stage2_density_discr(self): + return self.density_discr @property def quad_stage2_density_discr(self): -- GitLab From f4fe9f39a5f2e4d6d376a51fcdcfbbef18d1d136 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 18 Jul 2019 19:19:10 -0500 Subject: [PATCH 48/96] remove explicit calls to QBXLayerPotentialSource.h_max --- pytential/qbx/__init__.py | 2 ++ test/test_layer_pot.py | 5 +++-- test/test_layer_pot_eigenvalues.py | 19 ++++++++++++------- test/test_layer_pot_identity.py | 3 ++- test/test_maxwell.py | 2 +- test/test_scalar_int_eq.py | 3 ++- test/test_stokes.py | 3 ++- 7 files changed, 24 insertions(+), 13 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index d5d1e0eb..0467362d 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -458,6 +458,8 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): @property @memoize_method def h_max(self): + raise RuntimeError('bind `h_max` directly') + from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: return bind(self, sym.h_max(self.ambient_dim))(queue) diff --git a/test/test_layer_pot.py b/test/test_layer_pot.py index c99f3073..2f2175ae 100644 --- a/test/test_layer_pot.py +++ b/test/test_layer_pot.py @@ -486,12 +486,13 @@ def test_3d_jump_relations(ctx_factory, relation, visualize=False): bound_jump_identity = bind(qbx, jump_identity_sym) jump_identity = bound_jump_identity(queue, density=density) + h_max = bind(qbx, sym.h_max(qbx.ambient_dim))(queue) err = ( norm(qbx, queue, jump_identity, np.inf) / norm(qbx, queue, density, np.inf)) - print("ERROR", qbx.h_max, err) + print("ERROR", h_max, err) - eoc_rec.add_data_point(qbx.h_max, err) + eoc_rec.add_data_point(h_max, err) # {{{ visualization diff --git a/test/test_layer_pot_eigenvalues.py b/test/test_layer_pot_eigenvalues.py index 15dee8a3..5737009c 100644 --- a/test/test_layer_pot_eigenvalues.py +++ b/test/test_layer_pot_eigenvalues.py @@ -164,10 +164,11 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, pt.legend() pt.show() + h_max = bind(qbx, sym.h_max(qbx.ambient_dim))(queue) s_err = ( norm(density_discr, queue, s_sigma - s_sigma_ref) / norm(density_discr, queue, s_sigma_ref)) - s_eoc_rec.add_data_point(qbx.h_max, s_err) + s_eoc_rec.add_data_point(h_max, s_err) # }}} @@ -198,7 +199,7 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, d_err = ( norm(density_discr, queue, d_sigma - d_sigma_ref) / d_ref_norm) - d_eoc_rec.add_data_point(qbx.h_max, d_err) + d_eoc_rec.add_data_point(h_max, d_err) # }}} @@ -217,7 +218,7 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, sp_err = ( norm(density_discr, queue, sp_sigma - sp_sigma_ref) / norm(density_discr, queue, sigma)) - sp_eoc_rec.add_data_point(qbx.h_max, sp_err) + sp_eoc_rec.add_data_point(h_max, sp_err) # }}} @@ -313,7 +314,9 @@ def test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, s_sigma_op = bind(qbx, sym.S(lap_knl, sym.var("sigma"), qbx_forced_limit=+1)) s_sigma = s_sigma_op(queue=queue, sigma=ymn) s_eigval = 1/(2*mode_n + 1) - s_eoc_rec.add_data_point(qbx.h_max, rel_err(s_sigma, s_eigval*ymn)) + + h_max = bind(qbx, sym.h_max(qbx.ambient_dim))(queue) + s_eoc_rec.add_data_point(h_max, rel_err(s_sigma, s_eigval*ymn)) # }}} @@ -323,7 +326,7 @@ def test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, sym.D(lap_knl, sym.var("sigma"), qbx_forced_limit="avg")) d_sigma = d_sigma_op(queue=queue, sigma=ymn) d_eigval = -1/(2*(2*mode_n + 1)) - d_eoc_rec.add_data_point(qbx.h_max, rel_err(d_sigma, d_eigval*ymn)) + d_eoc_rec.add_data_point(h_max, rel_err(d_sigma, d_eigval*ymn)) # }}} @@ -333,7 +336,8 @@ def test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, sym.Sp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg")) sp_sigma = sp_sigma_op(queue=queue, sigma=ymn) sp_eigval = -1/(2*(2*mode_n + 1)) - sp_eoc_rec.add_data_point(qbx.h_max, rel_err(sp_sigma, sp_eigval*ymn)) + + sp_eoc_rec.add_data_point(h_max, rel_err(sp_sigma, sp_eigval*ymn)) # }}} @@ -343,7 +347,8 @@ def test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, sym.Dp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg")) dp_sigma = dp_sigma_op(queue=queue, sigma=ymn) dp_eigval = -(mode_n*(mode_n+1))/(2*mode_n + 1) - dp_eoc_rec.add_data_point(qbx.h_max, rel_err(dp_sigma, dp_eigval*ymn)) + + dp_eoc_rec.add_data_point(h_max, rel_err(dp_sigma, dp_eigval*ymn)) # }}} diff --git a/test/test_layer_pot_identity.py b/test/test_layer_pot_identity.py index fa8b7a8f..d6d42651 100644 --- a/test/test_layer_pot_identity.py +++ b/test/test_layer_pot_identity.py @@ -382,7 +382,8 @@ def test_identity_convergence(ctx_factory, case, visualize=False): linf_error_norm = norm(density_discr, queue, error, p=np.inf) print("--->", key, linf_error_norm) - eoc_rec.add_data_point(qbx.h_max, linf_error_norm) + h_max = bind(qbx, sym.h_max(qbx.ambient_dim))(queue) + eoc_rec.add_data_point(h_max, linf_error_norm) if visualize: from meshmode.discretization.visualization import make_visualizer diff --git a/test/test_maxwell.py b/test/test_maxwell.py index 69bdc81e..5adee9d3 100644 --- a/test/test_maxwell.py +++ b/test/test_maxwell.py @@ -310,7 +310,7 @@ def test_pec_mfie_extinction(ctx_factory, case, visualize=False): case.fmm_tolerance), fmm_backend=case.fmm_backend ).with_refinement(_expansion_disturbance_tolerance=0.05) - h_max = qbx.h_max + h_max = bind(qbx, sym.h_max(qbx.ambient_dim))(queue) scat_discr = qbx.density_discr obs_discr = Discretization( diff --git a/test/test_scalar_int_eq.py b/test/test_scalar_int_eq.py index 811a912d..a884e7da 100644 --- a/test/test_scalar_int_eq.py +++ b/test/test_scalar_int_eq.py @@ -864,8 +864,9 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize): class Result(Record): pass + h_max = bind(qbx, sym.h_max(qbx.ambient_dim))(queue) return Result( - h_max=qbx.h_max, + h_max=h_max, rel_err_2=rel_err_2, rel_err_inf=rel_err_inf, rel_td_err_inf=rel_td_err_inf, diff --git a/test/test_stokes.py b/test/test_stokes.py index d8d5c821..45238e74 100644 --- a/test/test_stokes.py +++ b/test/test_stokes.py @@ -268,7 +268,8 @@ def run_exterior_stokes_2d(ctx_factory, nelements, # }}} - return qbx.h_max, l2_err + h_max = bind(qbx, sym.h_max(qbx.ambient_dim))(queue) + return h_max, l2_err @pytest.mark.slowtest -- GitLab From e6e2ce9fcd4f6150d28e617d4671813973c5fe87 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 18 Jul 2019 19:21:38 -0500 Subject: [PATCH 49/96] remove h_max from LayerPotentialSourceBase --- pytential/qbx/__init__.py | 9 --------- pytential/source.py | 1 - pytential/unregularized.py | 6 ------ 3 files changed, 16 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 0467362d..a29d20bd 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -455,15 +455,6 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): return lpot, connection - @property - @memoize_method - def h_max(self): - raise RuntimeError('bind `h_max` directly') - - from pytential import bind, sym - with cl.CommandQueue(self.cl_context) as queue: - return bind(self, sym.h_max(self.ambient_dim))(queue) - # {{{ internal API @memoize_method diff --git a/pytential/source.py b/pytential/source.py index e628fe21..84166996 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -183,7 +183,6 @@ class LayerPotentialSourceBase(PotentialSource): .. attribute:: dim .. attribute:: real_dtype .. attribute:: complex_dtype - .. attribute:: h_max .. rubric:: Execution diff --git a/pytential/unregularized.py b/pytential/unregularized.py index 55c3f1d0..131cfd91 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -89,12 +89,6 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): self.debug = debug - @memoize_method - def h_max(self): - from pytential import bind, sym - with cl.CommandQueue(self.cl_context) as queue: - return bind(self, sym.h_max(self.ambient_dim))(queue) - @memoize_method def weights_and_area_elements(self): from pytential import bind, sym -- GitLab From 83c932bfbe90c32352534259a2990998c2c86472 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 18 Jul 2019 20:05:41 -0500 Subject: [PATCH 50/96] clean up dofconnection a bit --- pytential/symbolic/dofconnection.py | 154 ++++++++++++++++++---------- pytential/symbolic/execution.py | 9 +- 2 files changed, 104 insertions(+), 59 deletions(-) diff --git a/pytential/symbolic/dofconnection.py b/pytential/symbolic/dofconnection.py index 3dcbe4f8..89e2f051 100644 --- a/pytential/symbolic/dofconnection.py +++ b/pytential/symbolic/dofconnection.py @@ -137,8 +137,8 @@ class CenterGranularityConnection(GranularityConnection): class ElementGranularityConnection(GranularityConnection): """A :class:`GranularityConnection` used to transport from node data - (:class:`~pytential.symbolic.primitives.GRANULARITY_NODE`) to expansion - center (:class:`~pytential.symbolic.primitives.GRANULARITY_ELEMENT`). + (:class:`~pytential.symbolic.primitives.GRANULARITY_NODE`) to per-element + data (:class:`~pytential.symbolic.primitives.GRANULARITY_ELEMENT`). .. attribute:: discr .. automethod:: __call__ @@ -183,81 +183,125 @@ class ElementGranularityConnection(GranularityConnection): class DOFConnection(object): """A class used to transport between DOF types. - .. attribute:: lpot_source + .. attribute:: connections - A :class:`~pytential.source.LayerPotentialSourceBase`. + A list of + :class:`~meshmode.discretization.connection.DiscretizationConnection`s + and :class:`GranularityConnection`s used to transport from the given + source to the target. - .. attribute:: source + .. attribute:: from_dd A :class:`~pytential.symbolic.primitives.DOFDescriptor` for the DOF type of the incoming array. - .. attribute:: target + .. attribute:: to_dd A :class:`~pytential.symbolic.primitives.DOFDescriptor` for the DOF type of the outgoing array. - .. attribute:: connections - - A list of - :class:`~meshmode.discretization.connection.DiscretizationConnection`s - and :class:`GranularityConnection`s used to transport from the given - source to the target. + .. attribute:: from_discr + .. attribute:: to_discr .. automethod:: __call__ """ - def __init__(self, places, source, target): - from pytential import sym - source = sym.as_dofdesc(source) - target = sym.as_dofdesc(target) - - if not ((source.where == sym.DEFAULT_SOURCE - and target.where == sym.DEFAULT_TARGET) - or source.where == target.where): - raise ValueError('cannot interpolate between different domains') - if source.granularity != sym.GRANULARITY_NODE: - raise ValueError('can only interpolate from `GRANULARITY_NODE`') - - from pytential.symbolic.execution import GeometryCollection - if not isinstance(places, GeometryCollection): - places = GeometryCollection(places) - lpot_source = places[source] - - connections = [] - if target.discr != source.discr: - if target.discr != sym.QBX_SOURCE_QUAD_STAGE2: - # TODO: can probably extend this to project from a QUAD_STAGE2 - # using L2ProjectionInverseDiscretizationConnection - raise RuntimeError("can only interpolate to " - "`QBX_SOURCE_QUAD_STAGE2`") - - if source.discr == sym.QBX_SOURCE_STAGE2: - connections.append( - lpot_source.refined_interp_to_ovsmp_quad_connection) - elif source.discr == sym.QBX_SOURCE_QUAD_STAGE2: - pass - else: - connections.append(lpot_source.resampler) - - if target.granularity != source.granularity: - discr = places.get_discretization(target) - if target.granularity == sym.GRANULARITY_CENTER: - connections.append(CenterGranularityConnection(discr)) - elif target.granularity == sym.GRANULARITY_ELEMENT: - connections.append(ElementGranularityConnection(discr)) - - self.lpot_source = lpot_source - self.source = source - self.target = target + def __init__(self, connections, from_dd=None, to_dd=None): + self.from_dd = from_dd + self.to_dd = to_dd self.connections = connections + from meshmode.discretization.connection import DiscretizationConnection + for conn in self.connections: + if not isinstance(conn, + (DiscretizationConnection, GranularityConnection)): + raise ValueError('unsupported connection type: {}' + .format(type(conn))) + + if self.connections: + conn = self.connections[0] + if isinstance(conn, DiscretizationConnection): + self.from_discr = conn.from_discr + elif isinstance(conn, GranularityConnection): + self.from_discr = conn.discr + + conn = self.connections[-1] + if isinstance(conn, DiscretizationConnection): + self.to_discr = conn.to_discr + elif isinstance(conn, GranularityConnection): + self.to_discr = conn.discr + def __call__(self, queue, vec): for conn in self.connections: vec = conn(queue, vec) return vec + +def connection_from_dds(places, from_dd, to_dd): + """ + :arg places: a :class:`~pytential.symbolic.execution.GeometryCollection` + or an argument taken by its constructor. + :arg from_dd: a descriptor for the incomming degrees of freedom. This + can be a :class:`~pytential.symbolic.primitives.DOFDescriptor` + or an identifier that can be transformed into one by + :func:`~pytential.symbolic.primitives.as_dofdesc`. + :arg to_dd: a descriptor for the outgoing degrees of freedom. + + :return: a :class:`DOFConnection` transporting between the two + of DOFs. + """ + + from pytential import sym + from_dd = sym.as_dofdesc(from_dd) + to_dd = sym.as_dofdesc(to_dd) + + from pytential.symbolic.execution import GeometryCollection + if not isinstance(places, GeometryCollection): + places = GeometryCollection(places) + from_discr = places[from_dd] + + if not ((from_dd.where in (sym.DEFAULT_SOURCE,) + and to_dd.where in (sym.DEFAULT_SOURCE, sym.DEFAULT_TARGET)) + or from_dd.where == to_dd.where): + raise ValueError("cannot interpolate between different domains") + + if from_dd.granularity != sym.GRANULARITY_NODE: + raise ValueError("can only interpolate from `GRANULARITY_NODE`") + + from pytential.qbx import QBXLayerPotentialSource + if (not isinstance(from_discr, QBXLayerPotentialSource) + and from_dd.discr != to_dd.discr): + raise ValueError("can only interpolate on a `QBXLayerPotentialSource`") + + connections = [] + if from_dd.discr != to_dd.discr: + if to_dd.discr != sym.QBX_SOURCE_QUAD_STAGE2: + # TODO: can probably extend this to project from a QUAD_STAGE2 + # using L2ProjectionInverseDiscretizationConnection + raise RuntimeError("can only interpolate to " + "`QBX_SOURCE_QUAD_STAGE2`") + + if from_dd.discr == sym.QBX_SOURCE_QUAD_STAGE2: + pass + elif from_dd.discr == sym.QBX_SOURCE_STAGE2: + connections.append( + from_discr.refined_interp_to_ovsmp_quad_connection) + else: + connections.append(from_discr.resampler) + + if from_dd.granularity != to_dd.granularity: + to_discr = places.get_discretization(to_dd) + + if to_dd.granularity == sym.GRANULARITY_NODE: + pass + elif to_dd.granularity == sym.GRANULARITY_CENTER: + connections.append(CenterGranularityConnection(to_discr)) + elif to_dd.granularity == sym.GRANULARITY_ELEMENT: + connections.append(ElementGranularityConnection(to_discr)) + + return DOFConnection(connections, from_dd=from_dd, to_dd=to_dd) + # }}} # vim: foldmethod=marker diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 99fa1990..e5e47294 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -53,6 +53,7 @@ class EvaluationMapper(EvaluationMapperBase): EvaluationMapperBase.__init__(self, context) self.bound_expr = bound_expr + self.places = bound_expr.places self.queue = queue # {{{ map_XXX @@ -156,7 +157,7 @@ class EvaluationMapper(EvaluationMapperBase): except KeyError: bound_op = bind( expr.expression, - self.bound_expr.places[expr.where], + self.places[expr.where], self.bound_expr.iprec) bound_op_cache[expr] = bound_op @@ -170,16 +171,16 @@ class EvaluationMapper(EvaluationMapperBase): return result def map_quad_kernel_op(self, expr): - source = self.bound_expr.places[expr.source] + source = self.places[expr.source] return source.map_quad_kernel_op(expr, self.bound_expr, self.rec) def map_interpolation(self, expr): operand = self.rec(expr.operand) if isinstance(operand, cl.array.Array): - from pytential.symbolic.dofconnection import DOFConnection + from pytential.symbolic.dofconnection import connection_from_dds - conn = DOFConnection(self.bound_expr.places, + conn = connection_from_dds(self.places, expr.source, expr.target) return conn(self.queue, operand).with_queue(self.queue) elif isinstance(operand, (int, float, complex, np.number)): -- GitLab From c1397659d1431b80b2a97a6c90277f208d155b1a Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 18 Jul 2019 20:17:15 -0500 Subject: [PATCH 51/96] add some more docs to dofconnections --- pytential/symbolic/dofconnection.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pytential/symbolic/dofconnection.py b/pytential/symbolic/dofconnection.py index 89e2f051..576a9ae9 100644 --- a/pytential/symbolic/dofconnection.py +++ b/pytential/symbolic/dofconnection.py @@ -104,8 +104,9 @@ class CenterGranularityConnection(GranularityConnection): def __call__(self, queue, vecs): r""" - :arg vecs: a single :class:`pyopencl.array.Array` or a pair of - arrays. Given a pair of arrays :math:`x` and :math:`y`, they are + :arg vecs: a single :class:`pyopencl.array.Array` or a pair of arrays. + :return: an interleaved array or list of :class:`pyopencl.array.Array`s. + If *vecs* was a pair of arrays :math:`(x, y)`, they are interleaved as :math:`[x_1, y_1, x_2, y_2, \ddots, x_n, y_n]`. A single array is simply interleaved with itself. """ @@ -167,6 +168,21 @@ class ElementGranularityConnection(GranularityConnection): return knl def __call__(self, queue, vec): + """ + :arg vec: a vector with degrees of freedom corresponding to + nodes in :attr:`discr`. + :return: an :class:`pyopencl.array.Array` of size + ``(discr.mesh.nelements,)``. The reduction is performed by + picking the first node in each element according to its + group numbering. + """ + + if not isinstance(vec, cl.array.Array): + raise TypeError('non-array passed to connection') + + if vec.shape != (self.discr.nnodes,): + raise ValueError('invalid shape of incoming array') + result = cl.array.empty(queue, self.discr.mesh.nelements, vec.dtype) for igrp, group in enumerate(self.discr.groups): self.kernel()(queue, -- GitLab From e532c299912b96f8a92bbfe1a455dde15e0bb53a Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 18 Jul 2019 21:05:44 -0500 Subject: [PATCH 52/96] clean up interpolation test a bit --- pytential/symbolic/execution.py | 72 +++++++++++++++++++++------------ test/test_symbolic.py | 31 +++++++------- 2 files changed, 64 insertions(+), 39 deletions(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index e5e47294..60de9f29 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -338,7 +338,7 @@ def _prepare_expr(places, expr): from pytential.symbolic.mappers import ( ToTargetTagger, DerivativeBinder) - expr = ToTargetTagger(*places._default_place_ids)(expr) + expr = ToTargetTagger(*places.auto_where)(expr) expr = DerivativeBinder()(expr) for name, place in six.iteritems(places.places): @@ -387,34 +387,40 @@ class GeometryCollection(object): from meshmode.discretization import Discretization from pytential.source import LayerPotentialSourceBase, PotentialSource - if auto_where is None: - source_where, target_where = sym.DEFAULT_SOURCE, sym.DEFAULT_TARGET + # {{{ define default source and target descriptors + + if isinstance(auto_where, (list, tuple)): + auto_source, auto_target = auto_where else: - # NOTE: keeping this here to make sure auto_where unpacks into - # just the two elements - source_where, target_where = auto_where + auto_source, auto_target = auto_where, None + + if auto_source is None: + auto_source = sym.DEFAULT_SOURCE + if auto_target is None: + auto_target = sym.DEFAULT_TARGET + + auto_source = sym.as_dofdesc(auto_source) + auto_target = sym.as_dofdesc(auto_target) + self.auto_where = (auto_source, auto_target) - source_where = sym.as_dofdesc(source_where) - target_where = sym.as_dofdesc(target_where) + # }}} - self._default_source_place = source_where - self._default_target_place = target_where - self._default_place_ids = (source_where, target_where) + # {{{ construct dict self.places = {} if isinstance(places, LayerPotentialSourceBase): - self.places[source_where.where] = places - self.places[target_where.where] = \ - self._get_lpot_discretization(places, target_where) + self.places[auto_source.where] = places + self.places[auto_target.where] = \ + self._get_lpot_discretization(places, auto_target) elif isinstance(places, Discretization): - self.places[source_where.where] = places - self.places[target_where.where] = places + self.places[auto_source.where] = places + self.places[auto_target.where] = places elif isinstance(places, TargetBase): - self.places[target_where.where] = places + self.places[auto_target.where] = places elif isinstance(places, tuple): source_discr, target_discr = places - self.places[source_where.where] = source_discr - self.places[target_where.where] = target_discr + self.places[auto_source.where] = source_discr + self.places[auto_target.where] = target_discr else: self.places = places.copy() @@ -423,8 +429,18 @@ class GeometryCollection(object): raise TypeError("Must pass discretization, targets or " "layer potential sources as 'places'.") + # }}} + self.caches = {} + @property + def auto_source(self): + return self.auto_where[0] + + @property + def auto_target(self): + return self.auto_where[1] + def _get_lpot_discretization(self, lpot, dd): dd = sym.as_dofdesc(dd) @@ -468,11 +484,17 @@ class GeometryCollection(object): def copy(self): return GeometryCollection( self.places.copy(), - auto_where=self._default_place_ids) + auto_where=self.auto_where) def get_cache(self, name): return self.caches.setdefault(name, {}) + def __repr__(self): + return repr(self.places) + + def __str__(self): + return str(self.places) + class BoundExpression(object): def __init__(self, places, sym_op_expr): @@ -495,7 +517,7 @@ class BoundExpression(object): *None* values indicating the domains on which each component of the solution vector lives. *None* values indicate that the component is a scalar. If *domains* is *None*, - :class:`pytential.symbolic.primitives.DEFAULT_TARGET`, is required + :class:`~pytential.symbolic.primitives.DEFAULT_TARGET` is required to be a key in :attr:`places`. """ @@ -505,8 +527,8 @@ class BoundExpression(object): else: nresults = 1 - domains = _prepare_domains(nresults, self.places, domains, - sym.DEFAULT_TARGET) + domains = _prepare_domains(nresults, + self.places, domains, self.places.auto_target) total_dofs = 0 starts_and_ends = [] @@ -622,8 +644,8 @@ def build_matrix(queue, places, exprs, input_exprs, domains=None, # not iterable, wrap in a list input_exprs = [input_exprs] - domains = _prepare_domains(len(input_exprs), places, domains, - places._default_source_place) + domains = _prepare_domains(len(input_exprs), + places, domains, places.auto_source) from pytential.symbolic.matrix import MatrixBuilder, is_zero nblock_rows = len(exprs) diff --git a/test/test_symbolic.py b/test/test_symbolic.py index a0132702..0db0a9d6 100644 --- a/test/test_symbolic.py +++ b/test/test_symbolic.py @@ -201,15 +201,15 @@ def test_expr_pickling(): # }}} -@pytest.mark.parametrize(("source_discr", "target_granularity"), [ - (None, None), - (sym.QBX_SOURCE_STAGE1, sym.GRANULARITY_NODE), - (sym.QBX_SOURCE_STAGE2, sym.GRANULARITY_NODE), - (sym.QBX_SOURCE_STAGE2, sym.GRANULARITY_CENTER), - (sym.QBX_SOURCE_STAGE2, sym.GRANULARITY_ELEMENT), - (sym.QBX_SOURCE_QUAD_STAGE2, sym.GRANULARITY_NODE) +@pytest.mark.parametrize(("name", "source_discr", "target_granularity"), [ + ("default", None, None), + ("default-explicit", sym.QBX_SOURCE_STAGE1, sym.GRANULARITY_NODE), + ("stage2", sym.QBX_SOURCE_STAGE2, sym.GRANULARITY_NODE), + ("stage2-center", sym.QBX_SOURCE_STAGE2, sym.GRANULARITY_CENTER), + ("stage2-element", sym.QBX_SOURCE_STAGE2, sym.GRANULARITY_ELEMENT), + ("quad", sym.QBX_SOURCE_QUAD_STAGE2, sym.GRANULARITY_NODE) ]) -def test_interpolation(ctx_factory, source_discr, target_granularity): +def test_interpolation(ctx_factory, name, source_discr, target_granularity): ctx = ctx_factory() queue = cl.CommandQueue(ctx) @@ -229,16 +229,17 @@ def test_interpolation(ctx_factory, source_discr, target_granularity): qbx_order=qbx_order, fmm_order=False).with_refinement() - source = sym.DOFDescriptor(sym.DEFAULT_SOURCE, + where = 'test-interpolation' + source = sym.DOFDescriptor(where, discr=source_discr, granularity=sym.GRANULARITY_NODE) - target = sym.DOFDescriptor(sym.DEFAULT_SOURCE, + target = sym.DOFDescriptor(where, discr=sym.QBX_SOURCE_QUAD_STAGE2, granularity=target_granularity) sigma_sym = sym.var("sigma") op_sym = sym.sin(sym.Interpolation(source, target, sigma_sym)) - bound_op = bind(qbx, op_sym) + bound_op = bind(qbx, op_sym, auto_where=where) target_nodes = qbx.quad_stage2_density_discr.nodes().get(queue) if source_discr == sym.QBX_SOURCE_STAGE2: @@ -252,14 +253,16 @@ def test_interpolation(ctx_factory, source_discr, target_granularity): sigma_target = np.sin(la.norm(target_nodes, axis=0)) sigma_target_interp = bound_op(queue, sigma=sigma_dev).get(queue) - if target.granularity == sym.GRANULARITY_NODE: + if name in ('default', 'default-explicit', 'stage2', 'quad'): error = la.norm(sigma_target_interp - sigma_target) / la.norm(sigma_target) assert error < 1.0e-10 - elif target.granularity == sym.GRANULARITY_CENTER: + elif name in ('stage2-center',): assert len(sigma_target_interp) == 2 * len(sigma_target) - elif target.granularity == sym.GRANULARITY_ELEMENT: + elif name in ('stage2-element',): nelements = qbx.quad_stage2_density_discr.mesh.nelements assert len(sigma_target_interp) == nelements + else: + raise ValueError('unknown test case name: {}'.format(name)) # You can test individual routines by typing -- GitLab From 6f74ef7e7330e07530cea9cf5208654648da9eb7 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 18 Jul 2019 21:39:45 -0500 Subject: [PATCH 53/96] only interpolate QBXLayerPotentialSources --- pytential/symbolic/dofconnection.py | 3 ++- pytential/symbolic/execution.py | 13 +++++++---- pytential/symbolic/mappers.py | 36 ++++++++++++++--------------- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/pytential/symbolic/dofconnection.py b/pytential/symbolic/dofconnection.py index 576a9ae9..7a8b7c02 100644 --- a/pytential/symbolic/dofconnection.py +++ b/pytential/symbolic/dofconnection.py @@ -94,6 +94,7 @@ class CenterGranularityConnection(GranularityConnection): lp.GlobalArg("dst", shape="dstlen"), "..." ], + name="node_interleaver_knl", assumptions="2*srclen = dstlen", lang_version=MOST_RECENT_LANGUAGE_VERSION, ) @@ -159,7 +160,7 @@ class ElementGranularityConnection(GranularityConnection): lp.ValueArg("nunit_nodes", dtype=np.int32), "..." ], - name="subsample_to_elements", + name="node_element_subsample", lang_version=MOST_RECENT_LANGUAGE_VERSION, ) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 60de9f29..9bfd2de7 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -384,8 +384,9 @@ class GeometryCollection(object): """ from pytential.target import TargetBase + from pytential.source import PotentialSource + from pytential.qbx import QBXLayerPotentialSource from meshmode.discretization import Discretization - from pytential.source import LayerPotentialSourceBase, PotentialSource # {{{ define default source and target descriptors @@ -408,11 +409,11 @@ class GeometryCollection(object): # {{{ construct dict self.places = {} - if isinstance(places, LayerPotentialSourceBase): + if isinstance(places, QBXLayerPotentialSource): self.places[auto_source.where] = places self.places[auto_target.where] = \ self._get_lpot_discretization(places, auto_target) - elif isinstance(places, Discretization): + elif isinstance(places, (Discretization, PotentialSource)): self.places[auto_source.where] = places self.places[auto_target.where] = places elif isinstance(places, TargetBase): @@ -467,9 +468,13 @@ class GeometryCollection(object): else: raise KeyError('`where` not in the collection: {}'.format(dd.where)) + from pytential.qbx import QBXLayerPotentialSource from pytential.source import LayerPotentialSourceBase - if isinstance(discr, LayerPotentialSourceBase): + + if isinstance(discr, QBXLayerPotentialSource): return self._get_lpot_discretization(discr, dd) + elif isinstance(discr, LayerPotentialSourceBase): + return discr.density_discr else: return discr diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index fd4759fe..426073b7 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -428,29 +428,27 @@ class QBXInterpolationPreprocessor(IdentityMapper): self.places = places def map_int_g(self, expr): - dd = prim.as_dofdesc(expr.source) - if dd.discr == prim.QBX_SOURCE_QUAD_STAGE2: + source_dd = prim.as_dofdesc(expr.source) + if source_dd.discr == prim.QBX_SOURCE_QUAD_STAGE2: return expr + lpot_source = self.places[expr.source] - from pytential.source import LayerPotentialSourceBase - source = self.places[expr.source] - - if isinstance(source, LayerPotentialSourceBase): - target_dd = dd.copy(discr=prim.QBX_SOURCE_QUAD_STAGE2) - - density = prim.Interpolation( - dd, target_dd, self.rec(expr.density)) - kernel_arguments = dict( - (name, prim.Interpolation( - dd, target_dd, self.rec(arg_expr))) - for name, arg_expr in expr.kernel_arguments.items()) + from pytential.qbx import QBXLayerPotentialSource + if not isinstance(lpot_source, QBXLayerPotentialSource): + return expr - expr = expr.copy( - kernel=expr.kernel, - density=density, - kernel_arguments=kernel_arguments) + target_dd = source_dd.copy(discr=prim.QBX_SOURCE_QUAD_STAGE2) + density = prim.Interpolation( + source_dd, target_dd, self.rec(expr.density)) + kernel_arguments = dict( + (name, prim.Interpolation( + source_dd, target_dd, self.rec(arg_expr))) + for name, arg_expr in expr.kernel_arguments.items()) - return expr + return expr.copy( + kernel=expr.kernel, + density=density, + kernel_arguments=kernel_arguments) class QBXPreprocessor(IdentityMapper): -- GitLab From 4ffc2963230eaae4e3862dd8363af1af4182798a Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 19 Jul 2019 21:17:46 -0500 Subject: [PATCH 54/96] primitives: set cse scope properly --- pytential/symbolic/primitives.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index de22e2e5..091c2925 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -934,7 +934,9 @@ def expansion_radii(ambient_dim, dim=None, granularity=None, where=None): factor = _expansion_radii_factor(ambient_dim, dim) return cse(factor * _quad_resolution(ambient_dim, dim=dim, - granularity=granularity, where=where), cse_scope.DISCRETIZATION) + granularity=granularity, where=where), + "expansion_radii", + cse_scope.DISCRETIZATION) def expansion_centers(ambient_dim, side, dim=None, where=None): @@ -946,14 +948,18 @@ def expansion_centers(ambient_dim, side, dim=None, where=None): granularity=GRANULARITY_NODE, where=where) centers = x + side * radii * normals - return cse(centers.as_vector(), cse_scope.DISCRETIZATION) + return cse(centers.as_vector(), + "expansion_centers", + cse_scope.DISCRETIZATION) def h_max(ambient_dim, dim=None, where=None): r = _quad_resolution(ambient_dim, dim=None, granularity=GRANULARITY_ELEMENT, where=where) - return cse(NodeMax(r), cse_scope.DISCRETIZATION) + return cse(NodeMax(r), + "h_max", + cse_scope.DISCRETIZATION) def weights_and_area_elements(ambient_dim, dim=None, where=None): @@ -968,7 +974,9 @@ def weights_and_area_elements(ambient_dim, dim=None, where=None): else: area = area_element(ambient_dim, dim=dim, where=where) - return cse(area * QWeight(where=where), cse_scope.DISCRETIZATION) + return cse(area * QWeight(where=where), + "weights_area_elements", + cse_scope.DISCRETIZATION) # }}} -- GitLab From 40f696c9069cc59552d19f2920883a38c0bf3e23 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sun, 21 Jul 2019 10:27:45 -0500 Subject: [PATCH 55/96] source: copy instead of multiplying by 1 --- pytential/source.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pytential/source.py b/pytential/source.py index 84166996..95568337 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -130,8 +130,7 @@ class PointPotentialSource(PotentialSource): 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()) + strengths = evaluate(insn.density).with_queue(queue).copy() # FIXME: Do this all at once result = [] @@ -205,6 +204,9 @@ class LayerPotentialSourceBase(PotentialSource): def resampler(self): raise NotImplementedError + def with_refinement(self): + raise NotImplementedError + @property def ambient_dim(self): return self.density_discr.ambient_dim -- GitLab From bb9efdea6f64eadad67c91aa9c9bfc9591f05c2f Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sun, 21 Jul 2019 10:28:03 -0500 Subject: [PATCH 56/96] unregularized: remove mentions of quad_state2_density_discr --- pytential/unregularized.py | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/pytential/unregularized.py b/pytential/unregularized.py index 131cfd91..66d92c5e 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -74,8 +74,7 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): if fmm_level_to_order is None: if fmm_order is not False: - def fmm_level_to_order(kernel, kernel_args, tree, level): # noqa pylint:disable=function-redefined - return fmm_order + fmm_level_to_order = lambda *args: fmm_order else: fmm_level_to_order = False @@ -98,27 +97,6 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): return waa.with_queue(None) - @property - def stage2_density_discr(self): - return self.density_discr - - @property - def quad_stage2_density_discr(self): - return self.density_discr - - @property - def resampler(self): - # NOTE: this is a no-op, but it returns a chained connection - # anyway to match the return type of QBXLayerPotentialSource.resampler - from meshmode.discretization.connection import \ - ChainedDiscretizationConnection - - return ChainedDiscretizationConnection([], - from_discr=self.density_discr) - - def with_refinement(self): - raise NotImplementedError - def copy( self, density_discr=None, @@ -182,7 +160,7 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): evt, output_for_each_kernel = p2p(queue, target_discr.nodes(), - self.quad_stage2_density_discr.nodes(), + self.density_discr.nodes(), [strengths], **kernel_args) result.append((o.name, output_for_each_kernel[o.kernel_index])) @@ -365,11 +343,11 @@ class _FMMGeometryData(object): @property def coord_dtype(self): - return self.lpot_source.quad_stage2_density_discr.nodes().dtype + return self.lpot_source.density_discr.nodes().dtype @property def ambient_dim(self): - return self.lpot_source.quad_stage2_density_discr.ambient_dim + return self.lpot_source.density_discr.ambient_dim @memoize_method def traversal(self): @@ -392,7 +370,7 @@ class _FMMGeometryData(object): target_info = self.target_info() with cl.CommandQueue(self.cl_context) as queue: - nsources = lpot_src.quad_stage2_density_discr.nnodes + nsources = lpot_src.density_discr.nnodes nparticles = nsources + target_info.ntargets refine_weights = cl.array.zeros(queue, nparticles, dtype=np.int32) @@ -402,7 +380,7 @@ class _FMMGeometryData(object): MAX_LEAF_REFINE_WEIGHT = 32 # noqa tree, _ = code_getter.build_tree(queue, - particles=lpot_src.quad_stage2_density_discr.nodes(), + particles=lpot_src.density_discr.nodes(), targets=target_info.targets, max_leaf_refine_weight=MAX_LEAF_REFINE_WEIGHT, refine_weights=refine_weights, -- GitLab From e932d133b4b3351d774296f452736b47112f7c37 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sun, 21 Jul 2019 10:34:18 -0500 Subject: [PATCH 57/96] small cleanups --- pytential/source.py | 3 +-- pytential/unregularized.py | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/pytential/source.py b/pytential/source.py index 95568337..f77b57be 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -152,8 +152,7 @@ class PointPotentialSource(PotentialSource): @memoize_method def weights_and_area_elements(self): with cl.CommandQueue(self.cl_context) as queue: - result = cl.array.empty(queue, self._nodes.shape[-1], - dtype=self.real_dtype) + result = cl.array.empty(queue, self.nnodes, dtype=self.real_dtype) result.fill(1) return result.with_queue(None) diff --git a/pytential/unregularized.py b/pytential/unregularized.py index 66d92c5e..0a8c7488 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -86,8 +86,6 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): expansion_factory = DefaultExpansionFactory() self.expansion_factory = expansion_factory - self.debug = debug - @memoize_method def weights_and_area_elements(self): from pytential import bind, sym @@ -201,7 +199,6 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): self.debug) def exec_compute_potential_insn_fmm(self, queue, insn, bound_expr, evaluate): - # {{{ gather unique target discretizations used target_name_to_index = {} -- GitLab From 13bdddf71810d57d065fa7b09f953885f6aeba52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kl=C3=B6ckner?= Date: Mon, 22 Jul 2019 22:47:11 +0200 Subject: [PATCH 58/96] Placate Flake8 --- pytential/unregularized.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytential/unregularized.py b/pytential/unregularized.py index 0a8c7488..77025c88 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -74,7 +74,8 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): if fmm_level_to_order is None: if fmm_order is not False: - fmm_level_to_order = lambda *args: fmm_order + def fmm_level_to_order(kernel, kernel_args, tree, level): # noqa pylint:disable=function-redefined + return fmm_order else: fmm_level_to_order = False -- GitLab From 1e044bceab0673f83fccc8fcdb176ecb854234ca Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Mon, 22 Jul 2019 17:03:26 -0500 Subject: [PATCH 59/96] Rename dofconnection -> dof_connection --- pytential/qbx/utils.py | 4 ++-- pytential/symbolic/{dofconnection.py => dof_connection.py} | 0 pytential/symbolic/execution.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename pytential/symbolic/{dofconnection.py => dof_connection.py} (100%) diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index 9c7306b7..a54f71dc 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -80,7 +80,7 @@ def get_interleaved_centers(queue, lpot_source): ext_centers = bind(lpot_source, sym.expansion_centers(lpot_source.ambient_dim, +1))(queue) - from pytential.symbolic.dofconnection import CenterGranularityConnection + from pytential.symbolic.dof_connection import CenterGranularityConnection interleaver = CenterGranularityConnection(lpot_source.density_discr) return interleaver(queue, [int_centers, ext_centers]) @@ -99,7 +99,7 @@ def get_interleaved_radii(queue, lpot_source): radii = bind(lpot_source, sym.expansion_radii(lpot_source.ambient_dim))(queue) - from pytential.symbolic.dofconnection import CenterGranularityConnection + from pytential.symbolic.dof_connection import CenterGranularityConnection interleaver = CenterGranularityConnection(lpot_source.density_discr) return interleaver(queue, radii) diff --git a/pytential/symbolic/dofconnection.py b/pytential/symbolic/dof_connection.py similarity index 100% rename from pytential/symbolic/dofconnection.py rename to pytential/symbolic/dof_connection.py diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 9bfd2de7..ef9542c1 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -178,7 +178,7 @@ class EvaluationMapper(EvaluationMapperBase): operand = self.rec(expr.operand) if isinstance(operand, cl.array.Array): - from pytential.symbolic.dofconnection import connection_from_dds + from pytential.symbolic.dof_connection import connection_from_dds conn = connection_from_dds(self.places, expr.source, expr.target) -- GitLab From b09be6f7b312353b7647b574e33e9038c3fe5018 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 22 Jul 2019 19:20:59 -0500 Subject: [PATCH 60/96] proxy: remove forgotten print statement --- pytential/linalg/proxy.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pytential/linalg/proxy.py b/pytential/linalg/proxy.py index 7d308ec3..9fd26658 100644 --- a/pytential/linalg/proxy.py +++ b/pytential/linalg/proxy.py @@ -354,8 +354,6 @@ class ProxyGenerator(object): lang_version=MOST_RECENT_LANGUAGE_VERSION) knl = lp.tag_inames(knl, "idim*:unr") - print(knl) - return knl @memoize_method -- GitLab From 67546acab5de63e58c891049ece46b025fda13fd Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 22 Jul 2019 19:48:48 -0500 Subject: [PATCH 61/96] small cleanups --- pytential/qbx/refinement.py | 4 ++-- pytential/symbolic/dof_connection.py | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index b4eb73cb..a011c280 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -625,8 +625,8 @@ def refine_for_global_qbx(lpot_source, wrangler, "checking scaled max curvature threshold"): from pytential import sym, bind scaled_max_curv = bind(lpot_source, sym.Interpolate( - sym.as_dofdesc(None), - sym.as_dofdesc(sym.GRANULARITY_ELEMENT), + sym.GRANULARITY_NODE, + sym.GRANULARITY_ELEMENT, sym.ElementwiseMax(sym._scaled_max_curvature( lpot_source.ambient_dim))))(wrangler.queue) diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 7a8b7c02..57b71c8d 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -2,6 +2,7 @@ from __future__ import division, absolute_import, print_function __copyright__ = """ +Copyright (C) 2016 Matt Wala Copyright (C) 2019 Alexandru Fikl """ @@ -64,6 +65,14 @@ class GranularityConnection(object): def __init__(self, discr): self.discr = discr + @property + def from_discr(self): + return self.discr + + @property + def to_discr(self): + return self.discr + def __call__(self, queue, vec): raise NotImplementedError() @@ -236,17 +245,8 @@ class DOFConnection(object): .format(type(conn))) if self.connections: - conn = self.connections[0] - if isinstance(conn, DiscretizationConnection): - self.from_discr = conn.from_discr - elif isinstance(conn, GranularityConnection): - self.from_discr = conn.discr - - conn = self.connections[-1] - if isinstance(conn, DiscretizationConnection): - self.to_discr = conn.to_discr - elif isinstance(conn, GranularityConnection): - self.to_discr = conn.discr + self.from_discr = self.connections[0].from_discr + self.to_discr = self.connections[-1].to_discr def __call__(self, queue, vec): for conn in self.connections: -- GitLab From e27a43cf4cd88fc8a223c25b190e73eeece434d0 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Wed, 24 Jul 2019 09:30:40 -0500 Subject: [PATCH 62/96] add mapper to upsample when differentiating on quad_stage2. The mapper looks for a num_reference_derivative on `quad_stage2` and replaces it by one on `stage2` wrapped inside an interpolation to `quad_stage2`. --- pytential/qbx/__init__.py | 9 +-- pytential/symbolic/execution.py | 11 ++-- pytential/symbolic/mappers.py | 59 +++++++++++++++--- pytential/symbolic/matrix.py | 102 +++++++++++++------------------ pytential/symbolic/primitives.py | 10 +-- test/test_matrix.py | 24 +++++--- 6 files changed, 119 insertions(+), 96 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index a29d20bd..c6479606 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -350,11 +350,12 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): def weights_and_area_elements(self): from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: - waa = bind(self, sym.weights_and_area_elements( - self.ambient_dim, - where=sym.QBX_SOURCE_QUAD_STAGE2))(queue) + where = sym.DOFDescriptor( + sym.DEFAULT_SOURCE, + discr=sym.QBX_SOURCE_QUAD_STAGE2) - return waa.with_queue(None) + return bind(self, sym.weights_and_area_elements( + self.ambient_dim, where=where))(queue).with_queue(None) # }}} diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 1a6c8873..0ed5d4d9 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -322,7 +322,7 @@ def _prepare_domains(nresults, places, domains, default_domain): return domains -def _prepare_expr(places, expr): +def _prepare_expr(places, expr, interpolate=True): """ :arg places: :class:`pytential.symbolic.execution.GeometryCollection`. :arg expr: a symbolic expression. @@ -332,7 +332,9 @@ def _prepare_expr(places, expr): from pytential.source import LayerPotentialSourceBase from pytential.symbolic.mappers import ( - ToTargetTagger, DerivativeBinder) + ToTargetTagger, + DerivativeBinder, + InterpolationPreprocessor) expr = ToTargetTagger(*places.auto_where)(expr) expr = DerivativeBinder()(expr) @@ -341,6 +343,8 @@ def _prepare_expr(places, expr): if isinstance(place, LayerPotentialSourceBase): expr = place.preprocess_optemplate(name, places, expr) + if interpolate: + expr = InterpolationPreprocessor(places)(expr) return expr # }}} @@ -566,10 +570,7 @@ def bind(places, expr, auto_where=None): if not isinstance(places, GeometryCollection): places = GeometryCollection(places, auto_where=auto_where) - - from pytential.symbolic.mappers import QBXInterpolationPreprocessor expr = _prepare_expr(places, expr) - expr = QBXInterpolationPreprocessor(places)(expr) return BoundExpression(places, expr) diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 426073b7..8ada3846 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -219,12 +219,7 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): else: return expr - def map_q_weight(self, expr): - dd = prim.as_dofdesc(expr.where) - if dd.where is None: - return type(expr)(where=dd.copy(where=self.default_source)) - else: - return expr + map_q_weight = map_ones def map_parametrization_derivative_component(self, expr): dd = prim.as_dofdesc(expr.where) @@ -423,17 +418,59 @@ class UnregularizedPreprocessor(IdentityMapper): # {{{ QBX preprocessor -class QBXInterpolationPreprocessor(IdentityMapper): - def __init__(self, places): +class InterpolationDiscretizationTagger(IdentityMapper): + def __init__(self, discr): + self.discr = discr + + def map_node_coordinate_component(self, expr): + dd = prim.as_dofdesc(expr.where) + if dd.discr == self.discr: + return expr + else: + return type(expr)( + expr.ambient_axis, + dd.copy(discr=self.discr)) + + def map_num_reference_derivative(self, expr): + dd = prim.as_dofdesc(expr.where) + if dd.discr == self.discr: + return expr + else: + return type(expr)( + expr.ref_axes, + self.rec(expr.operand), + dd.copy(discr=self.discr)) + + +class InterpolationPreprocessor(IdentityMapper): + def __init__(self, places, discr=None): self.places = places + self.discr = discr or prim.QBX_SOURCE_STAGE2 + self.tagger = InterpolationDiscretizationTagger(self.discr) + + def map_num_reference_derivative(self, expr): + target_dd = prim.as_dofdesc(expr.where) + if target_dd.discr != prim.QBX_SOURCE_QUAD_STAGE2: + return expr + + from pytential.qbx import QBXLayerPotentialSource + lpot_source = self.places[target_dd] + if not isinstance(lpot_source, QBXLayerPotentialSource): + return expr + + source_dd = target_dd.copy(discr=self.discr) + return prim.Interpolation( + source_dd, + target_dd, + self.rec(self.tagger(expr))) def map_int_g(self, expr): source_dd = prim.as_dofdesc(expr.source) if source_dd.discr == prim.QBX_SOURCE_QUAD_STAGE2: return expr - lpot_source = self.places[expr.source] from pytential.qbx import QBXLayerPotentialSource + lpot_source = self.places[source_dd] if not isinstance(lpot_source, QBXLayerPotentialSource): return expr @@ -448,7 +485,9 @@ class QBXInterpolationPreprocessor(IdentityMapper): return expr.copy( kernel=expr.kernel, density=density, - kernel_arguments=kernel_arguments) + kernel_arguments=kernel_arguments, + source=target_dd, + target=expr.target) class QBXPreprocessor(IdentityMapper): diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index 5b60fb31..f69ed44a 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -43,34 +43,6 @@ def is_zero(x): return isinstance(x, (int, float, complex, np.number)) and x == 0 -def _resample_arg(queue, source, x): - """ - :arg queue: a :class:`pyopencl.CommandQueue`. - :arg source: a :class:`pytential.source.LayerPotentialSourceBase` subclass. - If it is not a layer potential source, no resampling is done. - :arg x: a :class:`numpy.ndarray`. - - :return: a resampled :class:`numpy.ndarray` (see - :method:`pytential.source.LayerPotentialSourceBase.resampler`). - """ - - from pytential.source import LayerPotentialSourceBase - if not isinstance(source, LayerPotentialSourceBase): - return x - - if not isinstance(x, np.ndarray): - return x - - if len(x.shape) >= 2: - raise RuntimeError("matrix variables in kernel arguments") - - def resample(y): - return source.resampler(queue, cl.array.to_device(queue, y)).get(queue) - - from pytools.obj_array import with_object_array_or_scalar - return with_object_array_or_scalar(resample, x) - - def _get_layer_potential_args(mapper, expr, source): """ :arg mapper: a :class:`pytential.symbolic.matrix.MatrixBuilderBase`. @@ -82,8 +54,7 @@ def _get_layer_potential_args(mapper, expr, source): kernel_args = {} for arg_name, arg_expr in six.iteritems(expr.kernel_arguments): - rec_arg = mapper.rec(arg_expr) - kernel_args[arg_name] = _resample_arg(mapper.queue, source, rec_arg) + kernel_args[arg_name] = mapper.rec(arg_expr) return kernel_args @@ -106,9 +77,7 @@ def _get_kernel_args(mapper, kernel, expr, source): for arg_name, arg_expr in six.iteritems(expr.kernel_arguments): if arg_name not in inner_kernel_args: continue - - rec_arg = mapper.rec(arg_expr) - kernel_args[arg_name] = _resample_arg(mapper.queue, source, rec_arg) + kernel_args[arg_name] = mapper.rec(arg_expr) return kernel_args @@ -398,11 +367,32 @@ class MatrixBuilder(MatrixBuilderBase): super(MatrixBuilder, self).__init__(queue, dep_expr, other_dep_exprs, dep_source, dep_discr, places, context) + def map_interpolation(self, expr): + source_dd = sym.as_dofdesc(expr.source) + target_dd = sym.as_dofdesc(expr.target) + if target_dd.discr != sym.QBX_SOURCE_QUAD_STAGE2: + raise RuntimeError("can only interpolate to QBX_SOURCE_QUAD_STAGE2") + + operand = self.rec(expr.operand) + if isinstance(operand, (int, float, complex, np.number)): + return operand + elif isinstance(operand, np.ndarray) and operand.ndim == 1: + from pytential.symbolic.dof_connection import connection_from_dds + conn = connection_from_dds(self.places, + expr.source, expr.target) + + operand = cl.array.to_device(self.queue, operand) + return conn(self.queue, operand).get(self.queue) + elif isinstance(operand, np.ndarray) and operand.ndim == 2: + resampler = self.places[source_dd].direct_resampler + mat = resampler.full_resample_matrix(self.queue).get(self.queue) + return mat.dot(operand) + else: + raise RuntimeError('unknown operand type: {}'.format(type(operand))) + def map_int_g(self, expr): source_dd = sym.as_dofdesc(expr.source) target_dd = sym.as_dofdesc(expr.target) - if source_dd.discr is None: - source_dd = source_dd.copy(discr=sym.QBX_SOURCE_QUAD_STAGE2) lpot_source = self.places[source_dd] source_discr = self.places.get_discretization(source_dd) @@ -417,11 +407,7 @@ class MatrixBuilder(MatrixBuilderBase): raise NotImplementedError("layer potentials on non-variables") kernel = expr.kernel - if source_dd.discr == target_dd.discr: - # NOTE: passing None to avoid any resampling - kernel_args = _get_layer_potential_args(self, expr, None) - else: - kernel_args = _get_layer_potential_args(self, expr, lpot_source) + kernel_args = _get_layer_potential_args(self, expr, lpot_source) from sumpy.expansion.local import LineTaylorLocalExpansion local_expn = LineTaylorLocalExpansion(kernel, lpot_source.qbx_order) @@ -444,15 +430,6 @@ class MatrixBuilder(MatrixBuilderBase): waa = _get_weights_and_area_elements(self.queue, lpot_source, source_discr) mat[:, :] *= waa.get(self.queue) - - if source_dd.discr != target_dd.discr: - # NOTE: we only resample sources - assert target_discr.nnodes < source_discr.nnodes - - resampler = lpot_source.direct_resampler - resample_mat = resampler.full_resample_matrix(self.queue).get(self.queue) - mat = mat.dot(resample_mat) - mat = mat.dot(rec_density) return mat @@ -472,9 +449,12 @@ class P2PMatrixBuilder(MatrixBuilderBase): self.exclude_self = exclude_self def map_int_g(self, expr): - source = self.places[expr.source] - source_discr = self.places.get_discretization(expr.source) - target_discr = self.places.get_discretization(expr.target) + target_dd = sym.as_dofdesc(expr.target) + source_dd = sym.as_dofdesc(expr.source) + + source = self.places[source_dd] + source_discr = self.places.get_discretization(source_dd) + target_discr = self.places.get_discretization(target_dd) rec_density = self.rec(expr.density) if is_zero(rec_density): @@ -535,9 +515,12 @@ class NearFieldBlockBuilder(MatrixBlockBuilderBase): return np.equal(tgtindices, srcindices).astype(np.float64) def map_int_g(self, expr): - source = self.places[expr.source] - source_discr = self.places.get_discretization(expr.source) - target_discr = self.places.get_discretization(expr.target) + target_dd = sym.as_dofdesc(expr.target) + source_dd = sym.as_dofdesc(expr.source) + + source = self.places[source_dd] + source_discr = self.places.get_discretization(source_dd) + target_discr = self.places.get_discretization(target_dd) if source_discr is not target_discr: raise NotImplementedError() @@ -601,9 +584,12 @@ class FarFieldBlockBuilder(MatrixBlockBuilderBase): return np.equal(tgtindices, srcindices).astype(np.float64) def map_int_g(self, expr): - source = self.places[expr.source] - source_discr = self.places.get_discretization(expr.source) - target_discr = self.places.get_discretization(expr.target) + target_dd = sym.as_dofdesc(expr.target) + source_dd = sym.as_dofdesc(expr.source) + + source = self.places[source_dd] + source_discr = self.places.get_discretization(source_dd) + target_discr = self.places.get_discretization(target_dd) if source_discr is not target_discr: raise NotImplementedError() diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 091c2925..d82a8d2f 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -964,16 +964,8 @@ def h_max(ambient_dim, dim=None, where=None): def weights_and_area_elements(ambient_dim, dim=None, where=None): where = as_dofdesc(where) - if where.discr == QBX_SOURCE_QUAD_STAGE2: - # quad_stage2_density_discr is not guaranteed to be usable for - # interpolation/differentiation. Use stage2_density_discr to find - # area elements instead, then upsample that. - source = where.copy(discr=QBX_SOURCE_STAGE2) - area = Interpolation(source, where, - area_element(ambient_dim, dim=dim, where=source)) - else: - area = area_element(ambient_dim, dim=dim, where=where) + area = area_element(ambient_dim, dim=dim, where=where) return cse(area * QWeight(where=where), "weights_area_elements", cse_scope.DISCRETIZATION) diff --git a/test/test_matrix.py b/test/test_matrix.py index 1800cec2..c667535c 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -286,7 +286,7 @@ def test_p2p_block_builder(ctx_factory, factor, ndim, lpot_id, from pytential.symbolic.execution import GeometryCollection from pytential.symbolic.execution import _prepare_expr, _prepare_domains places = GeometryCollection(qbx) - expr = _prepare_expr(places, op) + expr = _prepare_expr(places, op, interpolate=False) domains = _prepare_domains(1, places, None, sym.DEFAULT_SOURCE) from pytential.symbolic.matrix import P2PMatrixBuilder @@ -362,7 +362,7 @@ def test_qbx_block_builder(ctx_factory, factor, ndim, lpot_id, from pytential.symbolic.execution import GeometryCollection, _prepare_expr places = GeometryCollection(qbx, auto_where=place_ids) - expr = _prepare_expr(places, op) + expr = _prepare_expr(places, op, interpolate=False) density_discr = places.get_discretization(place_ids[0]) index_set = _build_block_index(density_discr, factor=factor) @@ -441,17 +441,21 @@ def test_build_matrix_places(ctx_factory, place_ids, visualize=False): index_set = _build_block_index(source_discr, factor=0.6) - # build full QBX matrix - from pytential.symbolic.execution import build_matrix - qbx_mat = build_matrix(queue, qbx, op, u_sym, auto_where=place_ids) - qbx_mat = qbx_mat.get(queue) + from pytential.symbolic.execution import _prepare_expr + op = _prepare_expr(places, op, interpolate=False) - assert qbx_mat.shape == (target_discr.nnodes, source_discr.nnodes) + # build full QBX matrix + from pytential.symbolic.matrix import MatrixBuilder + mbuilder = MatrixBuilder(queue, + dep_expr=u_sym, + other_dep_exprs=[], + dep_source=places[place_ids[0]], + dep_discr=places.get_discretization(place_ids[0]), + places=places, + context={}) + qbx_mat = mbuilder(op) # build full p2p matrix - from pytential.symbolic.execution import _prepare_expr - op = _prepare_expr(places, op) - from pytential.symbolic.matrix import P2PMatrixBuilder mbuilder = P2PMatrixBuilder(queue, dep_expr=u_sym, -- GitLab From 73eb52c28ed06c560c995797784f59c4bdc3511a Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 25 Jul 2019 17:20:33 -0500 Subject: [PATCH 63/96] Doc improvements --- pytential/qbx/__init__.py | 9 ++++++++- pytential/symbolic/dof_connection.py | 4 +++- pytential/symbolic/primitives.py | 13 +++++++++---- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index a29d20bd..120de252 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -56,6 +56,13 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): .. attribute :: qbx_order .. attribute :: fmm_order + .. automethod :: __init__ + .. automethod :: with_refinement + .. automethod :: copy + + .. attribute :: stage2_density_discr + .. attribute :: quad_stage2_density_discr + See :ref:`qbxguts` for some information on the inner workings of this. """ @@ -379,7 +386,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): .. warning:: This always returns a - :class:`~meshmode.discretization.connection.DirectDiscretizationConnect`. + :class:`~meshmode.discretization.connection.DirectDiscretizationConnection`. In case the geometry has been refined multiple times, a direct connection can have a large number of groups and/or interpolation batches, making it scale significantly worse than diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 57b71c8d..856cec3d 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -207,7 +207,9 @@ class ElementGranularityConnection(GranularityConnection): # {{{ dof connection class DOFConnection(object): - """A class used to transport between DOF types. + """An interpolation operation for converting a DOF vector between + different DOF types, as described by + :class:`~pytential.symbolic.primitives.DOFDescriptor`. .. attribute:: connections diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 091c2925..334ae604 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -273,10 +273,10 @@ class DOFDescriptor(object): .. attribute:: granularity - Describes the level of granularity of the DOF. - Can be one of :class:`GRANULARITY_NODE`, :class:`GRANULARITY_CENTER` or - :class:`GRANULARITY_ELEMENT`. - + Describes the level of granularity of the DOF vector. + Can be one of :class:`GRANULARITY_NODE` (one DOF per node), + :class:`GRANULARITY_CENTER` (two DOFs per node, one per side) or + :class:`GRANULARITY_ELEMENT` (one DOF per element). """ def __init__(self, where, discr=None, granularity=None): @@ -894,6 +894,11 @@ def _expansion_radii_factor(ambient_dim, dim): def _quad_resolution(ambient_dim, dim=None, granularity=None, where=None): + """This measures the quadrature resolution across the + mesh. In a 1D uniform mesh of uniform 'parametrization speed', it + should be the same as the panel length. + """ + source = as_dofdesc(where) target = source.copy(granularity=granularity) -- GitLab From 2ca80265a1fc2013b63da3eb8394062e82c47de9 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 25 Jul 2019 17:21:04 -0500 Subject: [PATCH 64/96] Remove with_refinement from LayerPotentialSourceBase interface --- pytential/source.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pytential/source.py b/pytential/source.py index f77b57be..1a55878e 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -172,7 +172,6 @@ class LayerPotentialSourceBase(PotentialSource): .. attribute:: stage2_density_discr .. attribute:: quad_stage2_density_discr .. attribute:: resampler - .. method:: with_refinement .. rubric:: Discretization data @@ -203,9 +202,6 @@ class LayerPotentialSourceBase(PotentialSource): def resampler(self): raise NotImplementedError - def with_refinement(self): - raise NotImplementedError - @property def ambient_dim(self): return self.density_discr.ambient_dim -- GitLab From 9c52e7ce7f9564231b843736a93fe9dc7d85bcfe Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 25 Jul 2019 17:21:36 -0500 Subject: [PATCH 65/96] Interpolation.__new__: avoid creating no-op interpolation operators --- pytential/symbolic/primitives.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 334ae604..44441d6a 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -1228,6 +1228,12 @@ class Interpolation(Expression): init_arg_names = ("source", "target", "operand") def __new__(cls, source, target, operand): + source = as_dofdesc(source) + target = as_dofdesc(target) + + if source == target: + return operand + if isinstance(operand, np.ndarray): def make_op(operand_i): return cls(source, target, operand_i) -- GitLab From cdcf2f07509f305e8cfbdd5ac4c8b4aca2b6c726 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 25 Jul 2019 17:27:42 -0500 Subject: [PATCH 66/96] Remove support for interpolating to element granularity --- pytential/symbolic/dof_connection.py | 64 +++------------------------- 1 file changed, 5 insertions(+), 59 deletions(-) diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 856cec3d..594f8a84 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -26,8 +26,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -import numpy as np - import pyopencl as cl import pyopencl.array # noqa from pytools import memoize @@ -145,62 +143,6 @@ class CenterGranularityConnection(GranularityConnection): return result[0] if len(result) == 1 else result - -class ElementGranularityConnection(GranularityConnection): - """A :class:`GranularityConnection` used to transport from node data - (:class:`~pytential.symbolic.primitives.GRANULARITY_NODE`) to per-element - data (:class:`~pytential.symbolic.primitives.GRANULARITY_ELEMENT`). - - .. attribute:: discr - .. automethod:: __call__ - """ - - def __init__(self, discr): - super(ElementGranularityConnection, self).__init__(discr) - - @memoize - def kernel(self): - knl = lp.make_kernel( - "{[i, k]: 0 <= i < nelements}", - "result[i] = a[i, 0]", - [ - lp.GlobalArg("a", - shape=("nelements", "nunit_nodes"), dtype=None), - lp.ValueArg("nunit_nodes", dtype=np.int32), - "..." - ], - name="node_element_subsample", - lang_version=MOST_RECENT_LANGUAGE_VERSION, - ) - - knl = lp.split_iname(knl, "i", 128, - inner_tag="l.0", outer_tag="g.0") - return knl - - def __call__(self, queue, vec): - """ - :arg vec: a vector with degrees of freedom corresponding to - nodes in :attr:`discr`. - :return: an :class:`pyopencl.array.Array` of size - ``(discr.mesh.nelements,)``. The reduction is performed by - picking the first node in each element according to its - group numbering. - """ - - if not isinstance(vec, cl.array.Array): - raise TypeError('non-array passed to connection') - - if vec.shape != (self.discr.nnodes,): - raise ValueError('invalid shape of incoming array') - - result = cl.array.empty(queue, self.discr.mesh.nelements, vec.dtype) - for igrp, group in enumerate(self.discr.groups): - self.kernel()(queue, - a=group.view(vec), - result=mesh_el_view(self.discr.mesh, igrp, result)) - - return result - # }}} @@ -317,7 +259,11 @@ def connection_from_dds(places, from_dd, to_dd): elif to_dd.granularity == sym.GRANULARITY_CENTER: connections.append(CenterGranularityConnection(to_discr)) elif to_dd.granularity == sym.GRANULARITY_ELEMENT: - connections.append(ElementGranularityConnection(to_discr)) + raise ValueError("Creating a connection to element granularity " + "is not allowed. Use Elementwise{Max,Min,Sum}.") + else: + raise ValueError("invalid to_dd granularity: %s" + % to_dd.granularity) return DOFConnection(connections, from_dd=from_dd, to_dd=to_dd) -- GitLab From fc203bc92240845506587fa49841b28fb7c317c5 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 25 Jul 2019 17:45:58 -0500 Subject: [PATCH 67/96] connection_from_dds: Use 'is' to compare discretizations --- pytential/symbolic/dof_connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 594f8a84..496a0953 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -232,7 +232,7 @@ def connection_from_dds(places, from_dd, to_dd): from pytential.qbx import QBXLayerPotentialSource if (not isinstance(from_discr, QBXLayerPotentialSource) - and from_dd.discr != to_dd.discr): + and from_dd.discr is not to_dd.discr): raise ValueError("can only interpolate on a `QBXLayerPotentialSource`") connections = [] -- GitLab From c154eedb18ea8caa1a74b963098427fcfb6aebf5 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 25 Jul 2019 17:58:24 -0500 Subject: [PATCH 68/96] Minor doc tweak --- pytential/symbolic/primitives.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 44441d6a..16490ff8 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -258,7 +258,7 @@ class DOFDescriptor(object): .. attribute:: where An identifier for the domain on which the DOFs exist. This can be a - simple string or another hashable identifier for the geometric object. + simple string or any other hashable identifier for the geometric object. The geometric objects are generally subclasses of :class:`~pytential.source.PotentialSource`, :class:`~pytential.target.TargetBase` or -- GitLab From efa012e232f441f291771b8836d1a11bf1a4866c Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 25 Jul 2019 18:20:13 -0500 Subject: [PATCH 69/96] Fix error type in connection_from_dds --- pytential/symbolic/dof_connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 496a0953..060c9f9b 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -240,7 +240,7 @@ def connection_from_dds(places, from_dd, to_dd): if to_dd.discr != sym.QBX_SOURCE_QUAD_STAGE2: # TODO: can probably extend this to project from a QUAD_STAGE2 # using L2ProjectionInverseDiscretizationConnection - raise RuntimeError("can only interpolate to " + raise ValueError("can only interpolate to " "`QBX_SOURCE_QUAD_STAGE2`") if from_dd.discr == sym.QBX_SOURCE_QUAD_STAGE2: -- GitLab From d5dded58369a8f8f0fd4cf4450fd2a85eeee3336 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 26 Jul 2019 01:45:46 +0200 Subject: [PATCH 70/96] Apply suggestion to pytential/symbolic/execution.py --- pytential/symbolic/execution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 1a6c8873..fe9c0aab 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -475,7 +475,7 @@ class GeometryCollection(object): return discr def __getitem__(self, where): - dd = sym.as_dofdesc(where) + where = sym.as_dofdesc(where) return self.places[dd.where] def __contains__(self, where): -- GitLab From 6d6e018ef65ce395bbc4aa04efe6c4953b45dd55 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 25 Jul 2019 21:09:26 -0500 Subject: [PATCH 71/96] remove usage of ElementGranularityConnection This should make the tests pass (hopefully). Changes * made `map_elementwise_max` and friends do the subsampling, since it does not make sense for a generic vector to be subsampled like it was. * made sure everyone was tagged correctly for this to work. --- pytential/qbx/refinement.py | 13 ++--- pytential/symbolic/dof_connection.py | 19 +----- pytential/symbolic/execution.py | 86 ++++++++++++++++++++++------ pytential/symbolic/mappers.py | 65 +++++++++------------ pytential/symbolic/primitives.py | 4 +- test/test_global_qbx.py | 4 +- test/test_symbolic.py | 4 -- 7 files changed, 105 insertions(+), 90 deletions(-) diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index a011c280..83880aef 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -365,7 +365,7 @@ class RefinerWrangler(TreeWranglerBase): source_danger_zone_radii_by_panel = bind(lpot_source, sym._source_danger_zone_radii( lpot_source.ambient_dim, - granularity=sym.GRANULARITY_ELEMENT))(self.queue) + where=sym.GRANULARITY_ELEMENT))(self.queue) unwrap_args = AreaQueryElementwiseTemplate.unwrap_args evt = knl( @@ -607,7 +607,7 @@ def refine_for_global_qbx(lpot_source, wrangler, from pytential import bind, sym quad_resolution = bind(lpot_source, sym._quad_resolution( lpot_source.ambient_dim, - granularity=sym.GRANULARITY_ELEMENT))(wrangler.queue) + where=sym.GRANULARITY_ELEMENT))(wrangler.queue) violates_kernel_length_scale = \ wrangler.check_element_prop_threshold( @@ -624,11 +624,10 @@ def refine_for_global_qbx(lpot_source, wrangler, with ProcessLogger(logger, "checking scaled max curvature threshold"): from pytential import sym, bind - scaled_max_curv = bind(lpot_source, sym.Interpolate( - sym.GRANULARITY_NODE, - sym.GRANULARITY_ELEMENT, - sym.ElementwiseMax(sym._scaled_max_curvature( - lpot_source.ambient_dim))))(wrangler.queue) + scaled_max_curv = bind(lpot_source, + sym.ElementwiseMax( + sym._scaled_max_curvature(lpot_source.ambient_dim), + where=sym.GRANULARITY_ELEMENT))(wrangler.queue) violates_scaled_max_curv = \ wrangler.check_element_prop_threshold( diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 060c9f9b..0decc89e 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -36,22 +36,6 @@ from loopy.version import MOST_RECENT_LANGUAGE_VERSION # {{{ granularity connections -def mesh_el_view(mesh, group_nr, global_array): - """Return a view of *global_array* of shape - ``(..., mesh.groups[group_nr].nelements)`` - where *global_array* is of shape ``(..., nelements)``, - where *nelements* is the global (per-mesh) element count. - """ - - group = mesh.groups[group_nr] - - return global_array[ - ..., group.element_nr_base:group.element_nr_base + group.nelements] \ - .reshape( - global_array.shape[:-1] - + (group.nelements,)) - - class GranularityConnection(object): """Abstract interface for transporting a DOF between different levels of granularity. @@ -262,8 +246,7 @@ def connection_from_dds(places, from_dd, to_dd): raise ValueError("Creating a connection to element granularity " "is not allowed. Use Elementwise{Max,Min,Sum}.") else: - raise ValueError("invalid to_dd granularity: %s" - % to_dd.granularity) + raise ValueError("invalid to_dd granularity: %s" % to_dd.granularity) return DOFConnection(connections, from_dd=from_dd, to_dd=to_dd) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index fe9c0aab..d50ee92a 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -46,6 +46,23 @@ from pytential import sym # {{{ evaluation mapper + +def mesh_el_view(mesh, group_nr, global_array): + """Return a view of *global_array* of shape + ``(..., mesh.groups[group_nr].nelements)`` + where *global_array* is of shape ``(..., nelements)``, + where *nelements* is the global (per-mesh) element count. + """ + + group = mesh.groups[group_nr] + + return global_array[ + ..., group.element_nr_base:group.element_nr_base + group.nelements] \ + .reshape( + global_array.shape[:-1] + + (group.nelements,)) + + class EvaluationMapper(EvaluationMapperBase): def __init__(self, bound_expr, queue, context={}, target_geometry=None, @@ -85,30 +102,63 @@ class EvaluationMapper(EvaluationMapperBase): return cl.array.max(self.rec(expr.operand)).get()[()] def _map_elementwise_reduction(self, reduction_name, expr): - @memoize_in(self.bound_expr, "elementwise_"+reduction_name) - def knl(): + @memoize_in(self.places, "elementwise_node_"+reduction_name) + def node_knl(): import loopy as lp knl = lp.make_kernel( - "{[el, idof, jdof]: 0<=el Date: Thu, 25 Jul 2019 21:18:50 -0500 Subject: [PATCH 72/96] move stringify logic to DOFDescriptor --- pytential/symbolic/mappers.py | 24 +----------------------- pytential/symbolic/primitives.py | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 7e27b998..615ddc79 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -502,29 +502,7 @@ class QBXPreprocessor(IdentityMapper): # {{{ stringifier def stringify_where(where): - dd = prim.as_dofdesc(where) - - name = [] - if dd.where is None: - name.append("?") - elif dd.where is prim.DEFAULT_SOURCE: - name.append("s") - elif dd.where is prim.DEFAULT_TARGET: - name.append("t") - else: - name.append(str(dd.where)) - - if dd.discr == prim.QBX_SOURCE_STAGE2: - name.append("stage2") - elif dd.discr == prim.QBX_SOURCE_QUAD_STAGE2: - name.append("quads2") - - if dd.granularity == prim.GRANULARITY_CENTER: - name.append("center") - elif dd.granularity == prim.GRANULARITY_ELEMENT: - name.append("panel") - - return "/".join(name) + return str(prim.as_dofdesc(where)) class StringifyMapper(BaseStringifyMapper): diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 18977fc9..7c44c1a3 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -331,8 +331,27 @@ class DOFDescriptor(object): self.granularity.__name__) def __str__(self): - from pytential.symbolic.mappers import stringify_where - return stringify_where(self) + name = [] + if self.where is None: + name.append("?") + elif self.where is DEFAULT_SOURCE: + name.append("s") + elif self.where is DEFAULT_TARGET: + name.append("t") + else: + name.append(str(self.where)) + + if self.discr == QBX_SOURCE_STAGE2: + name.append("stage2") + elif self.discr == QBX_SOURCE_QUAD_STAGE2: + name.append("quads2") + + if self.granularity == GRANULARITY_CENTER: + name.append("center") + elif self.granularity == GRANULARITY_ELEMENT: + name.append("panel") + + return "/".join(name) def as_dofdesc(desc): -- GitLab From ef932be33d9135559fc30d4c50ca1e26a64b69dc Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 25 Jul 2019 21:20:23 -0500 Subject: [PATCH 73/96] execution: remove copy in GeometryCollection --- pytential/symbolic/execution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index d50ee92a..cad36268 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -534,7 +534,7 @@ class GeometryCollection(object): def copy(self): return GeometryCollection( - self.places.copy(), + self.places, auto_where=self.auto_where) def get_cache(self, name): -- GitLab From ea2e5b50b0d3f8018f10f591e8681c629c60092c Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 25 Jul 2019 21:28:13 -0500 Subject: [PATCH 74/96] compare using is instead of == --- pytential/symbolic/dof_connection.py | 16 +++++++--------- pytential/symbolic/mappers.py | 2 +- pytential/symbolic/primitives.py | 22 +++++++++++----------- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 0decc89e..d6c7a378 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -206,21 +206,19 @@ def connection_from_dds(places, from_dd, to_dd): places = GeometryCollection(places) from_discr = places[from_dd] - if not ((from_dd.where in (sym.DEFAULT_SOURCE,) - and to_dd.where in (sym.DEFAULT_SOURCE, sym.DEFAULT_TARGET)) - or from_dd.where == to_dd.where): + if from_dd.where != to_dd.where: raise ValueError("cannot interpolate between different domains") - if from_dd.granularity != sym.GRANULARITY_NODE: + if from_dd.granularity is not sym.GRANULARITY_NODE: raise ValueError("can only interpolate from `GRANULARITY_NODE`") - from pytential.qbx import QBXLayerPotentialSource - if (not isinstance(from_discr, QBXLayerPotentialSource) - and from_dd.discr is not to_dd.discr): - raise ValueError("can only interpolate on a `QBXLayerPotentialSource`") - connections = [] if from_dd.discr != to_dd.discr: + from pytential.qbx import QBXLayerPotentialSource + if not isinstance(from_discr, QBXLayerPotentialSource): + raise ValueError("can only interpolate on a " + "`QBXLayerPotentialSource`") + if to_dd.discr != sym.QBX_SOURCE_QUAD_STAGE2: # TODO: can probably extend this to project from a QUAD_STAGE2 # using L2ProjectionInverseDiscretizationConnection diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 615ddc79..0aca8940 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -416,7 +416,7 @@ class QBXInterpolationPreprocessor(IdentityMapper): def map_int_g(self, expr): source_dd = prim.as_dofdesc(expr.source) - if source_dd.discr == prim.QBX_SOURCE_QUAD_STAGE2: + if source_dd.discr is prim.QBX_SOURCE_QUAD_STAGE2: return expr lpot_source = self.places[expr.source] diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 7c44c1a3..71eed886 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -341,14 +341,14 @@ class DOFDescriptor(object): else: name.append(str(self.where)) - if self.discr == QBX_SOURCE_STAGE2: + if self.discr is QBX_SOURCE_STAGE2: name.append("stage2") - elif self.discr == QBX_SOURCE_QUAD_STAGE2: + elif self.discr is QBX_SOURCE_QUAD_STAGE2: name.append("quads2") - if self.granularity == GRANULARITY_CENTER: + if self.granularity is GRANULARITY_CENTER: name.append("center") - elif self.granularity == GRANULARITY_ELEMENT: + elif self.granularity is GRANULARITY_ELEMENT: name.append("panel") return "/".join(name) @@ -358,14 +358,14 @@ def as_dofdesc(desc): if isinstance(desc, DOFDescriptor): return desc - if desc == QBX_SOURCE_STAGE1 \ - or desc == QBX_SOURCE_STAGE2 \ - or desc == QBX_SOURCE_QUAD_STAGE2: + if desc is QBX_SOURCE_STAGE1 \ + or desc is QBX_SOURCE_STAGE2 \ + or desc is QBX_SOURCE_QUAD_STAGE2: return DOFDescriptor(None, discr=desc) - if desc == GRANULARITY_NODE \ - or desc == GRANULARITY_CENTER \ - or desc == GRANULARITY_ELEMENT: + if desc is GRANULARITY_NODE \ + or desc is GRANULARITY_CENTER \ + or desc is GRANULARITY_ELEMENT: return DOFDescriptor(None, granularity=desc) return DOFDescriptor(desc) @@ -988,7 +988,7 @@ def h_max(ambient_dim, dim=None, where=None): def weights_and_area_elements(ambient_dim, dim=None, where=None): where = as_dofdesc(where) - if where.discr == QBX_SOURCE_QUAD_STAGE2: + if where.discr is QBX_SOURCE_QUAD_STAGE2: # quad_stage2_density_discr is not guaranteed to be usable for # interpolation/differentiation. Use stage2_density_discr to find # area elements instead, then upsample that. -- GitLab From cefc7b6306030120eb36e0cc253c3f9078b1d9d3 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 25 Jul 2019 21:38:59 -0500 Subject: [PATCH 75/96] compare using is instead of == some more --- pytential/symbolic/dof_connection.py | 16 ++++++++-------- pytential/symbolic/execution.py | 4 ++-- test/test_symbolic.py | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index d6c7a378..34de41be 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -213,34 +213,34 @@ def connection_from_dds(places, from_dd, to_dd): raise ValueError("can only interpolate from `GRANULARITY_NODE`") connections = [] - if from_dd.discr != to_dd.discr: + if from_dd.discr is not to_dd.discr: from pytential.qbx import QBXLayerPotentialSource if not isinstance(from_discr, QBXLayerPotentialSource): raise ValueError("can only interpolate on a " "`QBXLayerPotentialSource`") - if to_dd.discr != sym.QBX_SOURCE_QUAD_STAGE2: + if to_dd.discr is not sym.QBX_SOURCE_QUAD_STAGE2: # TODO: can probably extend this to project from a QUAD_STAGE2 # using L2ProjectionInverseDiscretizationConnection raise ValueError("can only interpolate to " "`QBX_SOURCE_QUAD_STAGE2`") - if from_dd.discr == sym.QBX_SOURCE_QUAD_STAGE2: + if from_dd.discr is sym.QBX_SOURCE_QUAD_STAGE2: pass - elif from_dd.discr == sym.QBX_SOURCE_STAGE2: + elif from_dd.discr is sym.QBX_SOURCE_STAGE2: connections.append( from_discr.refined_interp_to_ovsmp_quad_connection) else: connections.append(from_discr.resampler) - if from_dd.granularity != to_dd.granularity: + if from_dd.granularity is not to_dd.granularity: to_discr = places.get_discretization(to_dd) - if to_dd.granularity == sym.GRANULARITY_NODE: + if to_dd.granularity is sym.GRANULARITY_NODE: pass - elif to_dd.granularity == sym.GRANULARITY_CENTER: + elif to_dd.granularity is sym.GRANULARITY_CENTER: connections.append(CenterGranularityConnection(to_discr)) - elif to_dd.granularity == sym.GRANULARITY_ELEMENT: + elif to_dd.granularity is sym.GRANULARITY_ELEMENT: raise ValueError("Creating a connection to element granularity " "is not allowed. Use Elementwise{Max,Min,Sum}.") else: diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index cad36268..2a97eae7 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -491,9 +491,9 @@ class GeometryCollection(object): def _get_lpot_discretization(self, lpot, dd): dd = sym.as_dofdesc(dd) - if dd.discr == sym.QBX_SOURCE_STAGE2: + if dd.discr is sym.QBX_SOURCE_STAGE2: return lpot.stage2_density_discr - if dd.discr == sym.QBX_SOURCE_QUAD_STAGE2: + if dd.discr is sym.QBX_SOURCE_QUAD_STAGE2: return lpot.quad_stage2_density_discr return lpot.density_discr diff --git a/test/test_symbolic.py b/test/test_symbolic.py index 5aeb601f..c1c130f0 100644 --- a/test/test_symbolic.py +++ b/test/test_symbolic.py @@ -241,9 +241,9 @@ def test_interpolation(ctx_factory, name, source_discr, target_granularity): bound_op = bind(qbx, op_sym, auto_where=where) target_nodes = qbx.quad_stage2_density_discr.nodes().get(queue) - if source_discr == sym.QBX_SOURCE_STAGE2: + if source_discr is sym.QBX_SOURCE_STAGE2: source_nodes = qbx.stage2_density_discr.nodes().get(queue) - elif source_discr == sym.QBX_SOURCE_QUAD_STAGE2: + elif source_discr is sym.QBX_SOURCE_QUAD_STAGE2: source_nodes = target_nodes else: source_nodes = qbx.density_discr.nodes().get(queue) -- GitLab From 5a68de01d8044cc418fbc3fa98263fc6cb931901 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 26 Jul 2019 19:38:54 -0500 Subject: [PATCH 76/96] primitives: deprecate where in favor of dofdesc --- pytential/qbx/__init__.py | 2 +- pytential/qbx/refinement.py | 6 +- pytential/symbolic/execution.py | 16 +- pytential/symbolic/mappers.py | 36 +- pytential/symbolic/matrix.py | 4 +- pytential/symbolic/pde/maxwell/__init__.py | 2 +- pytential/symbolic/pde/scalar.py | 5 +- pytential/symbolic/primitives.py | 396 +++++++++++++-------- test/test_global_qbx.py | 4 +- test/test_scalar_int_eq.py | 2 +- 10 files changed, 290 insertions(+), 183 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 120de252..5d193b74 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -359,7 +359,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): with cl.CommandQueue(self.cl_context) as queue: waa = bind(self, sym.weights_and_area_elements( self.ambient_dim, - where=sym.QBX_SOURCE_QUAD_STAGE2))(queue) + dofdesc=sym.QBX_SOURCE_QUAD_STAGE2))(queue) return waa.with_queue(None) diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index 83880aef..829e706c 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -365,7 +365,7 @@ class RefinerWrangler(TreeWranglerBase): source_danger_zone_radii_by_panel = bind(lpot_source, sym._source_danger_zone_radii( lpot_source.ambient_dim, - where=sym.GRANULARITY_ELEMENT))(self.queue) + dofdesc=sym.GRANULARITY_ELEMENT))(self.queue) unwrap_args = AreaQueryElementwiseTemplate.unwrap_args evt = knl( @@ -607,7 +607,7 @@ def refine_for_global_qbx(lpot_source, wrangler, from pytential import bind, sym quad_resolution = bind(lpot_source, sym._quad_resolution( lpot_source.ambient_dim, - where=sym.GRANULARITY_ELEMENT))(wrangler.queue) + dofdesc=sym.GRANULARITY_ELEMENT))(wrangler.queue) violates_kernel_length_scale = \ wrangler.check_element_prop_threshold( @@ -627,7 +627,7 @@ def refine_for_global_qbx(lpot_source, wrangler, scaled_max_curv = bind(lpot_source, sym.ElementwiseMax( sym._scaled_max_curvature(lpot_source.ambient_dim), - where=sym.GRANULARITY_ELEMENT))(wrangler.queue) + dofdesc=sym.GRANULARITY_ELEMENT))(wrangler.queue) violates_scaled_max_curv = \ wrangler.check_element_prop_threshold( diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 2a97eae7..1f0d8485 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -144,11 +144,11 @@ class EvaluationMapper(EvaluationMapperBase): return result - discr = self.bound_expr.get_discretization(expr.where) + discr = self.bound_expr.get_discretization(expr.dofdesc) operand = self.rec(expr.operand) assert operand.shape == (discr.nnodes,) - where = sym.as_dofdesc(expr.where) + where = sym.as_dofdesc(expr.dofdesc) if where.granularity is sym.GRANULARITY_NODE: return _reduce(discr.nnodes, node_knl(), @@ -170,7 +170,7 @@ class EvaluationMapper(EvaluationMapperBase): return self._map_elementwise_reduction("max", expr) def map_ones(self, expr): - discr = self.bound_expr.get_discretization(expr.where) + discr = self.bound_expr.get_discretization(expr.dofdesc) result = (discr .empty(queue=self.queue, dtype=discr.real_dtype) @@ -180,12 +180,12 @@ class EvaluationMapper(EvaluationMapperBase): return result def map_node_coordinate_component(self, expr): - discr = self.bound_expr.get_discretization(expr.where) + discr = self.bound_expr.get_discretization(expr.dofdesc) return discr.nodes()[expr.ambient_axis] \ .with_queue(self.queue) def map_num_reference_derivative(self, expr): - discr = self.bound_expr.get_discretization(expr.where) + discr = self.bound_expr.get_discretization(expr.dofdesc) from pytools import flatten ref_axes = flatten([axis] * mult for axis, mult in expr.ref_axes) @@ -195,7 +195,7 @@ class EvaluationMapper(EvaluationMapperBase): .with_queue(self.queue) def map_q_weight(self, expr): - discr = self.bound_expr.get_discretization(expr.where) + discr = self.bound_expr.get_discretization(expr.dofdesc) return discr.quad_weights(self.queue) \ .with_queue(self.queue) @@ -207,11 +207,11 @@ class EvaluationMapper(EvaluationMapperBase): except KeyError: bound_op = bind( expr.expression, - self.places[expr.where], + self.places[expr.dofdesc], self.bound_expr.iprec) bound_op_cache[expr] = bound_op - scipy_op = bound_op.scipy_op(expr.variable_name, expr.where, + scipy_op = bound_op.scipy_op(expr.variable_name, expr.dofdesc, **dict((var_name, self.rec(var_expr)) for var_name, var_expr in six.iteritems(expr.extra_vars))) diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 0aca8940..c46dac08 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -62,14 +62,14 @@ class IdentityMapper(IdentityMapperBase): map_node_max = map_node_sum def map_elementwise_sum(self, expr): - return type(expr)(self.rec(expr.operand), expr.where) + return type(expr)(self.rec(expr.operand), expr.dofdesc) map_elementwise_min = map_elementwise_sum map_elementwise_max = map_elementwise_sum def map_num_reference_derivative(self, expr): return type(expr)(expr.ref_axes, self.rec(expr.operand), - expr.where) + expr.dofdesc) # {{{ childless -- no need to rebuild @@ -92,7 +92,7 @@ class IdentityMapper(IdentityMapperBase): dict([ (name, self.rec(name_expr)) for name, name_expr in six.iteritems(expr.extra_vars)]), - expr.where) + expr.dofdesc) def map_int_g(self, expr): return expr.copy( @@ -177,7 +177,7 @@ class EvaluationMapper(EvaluationMapperBase): def map_num_reference_derivative(self, expr): return componentwise( lambda subexpr: type(expr)( - expr.ref_axes, self.rec(subexpr), expr.where), + expr.ref_axes, self.rec(subexpr), expr.dofdesc), expr.operand) def map_int_g(self, expr): @@ -223,7 +223,7 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): return dd def map_ones(self, expr): - return type(expr)(where=self._as_dofdesc(expr.where)) + return type(expr)(dofdesc=self._as_dofdesc(expr.dofdesc)) map_q_weight = map_ones @@ -231,23 +231,23 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): return type(expr)( expr.ambient_axis, expr.ref_axis, - self._as_dofdesc(expr.where)) + self._as_dofdesc(expr.dofdesc)) def map_node_coordinate_component(self, expr): return type(expr)( expr.ambient_axis, - self._as_dofdesc(expr.where)) + self._as_dofdesc(expr.dofdesc)) def map_num_reference_derivative(self, expr): return type(expr)( expr.ref_axes, self.rec(expr.operand), - self._as_dofdesc(expr.where)) + self._as_dofdesc(expr.dofdesc)) def map_elementwise_sum(self, expr): return type(expr)( self.rec(expr.operand), - self._as_dofdesc(expr.where)) + self._as_dofdesc(expr.dofdesc)) map_elementwise_min = map_elementwise_sum map_elementwise_max = map_elementwise_sum @@ -271,7 +271,7 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): )) def map_inverse(self, expr): - dd = prim.as_dofdesc(expr.where) + dd = prim.as_dofdesc(expr.dofdesc) if dd.where is None: dd = dd.copy(where=self.default_where) @@ -508,7 +508,7 @@ def stringify_where(where): class StringifyMapper(BaseStringifyMapper): def map_ones(self, expr, enclosing_prec): - return "Ones[%s]" % stringify_where(expr.where) + return "Ones[%s]" % stringify_where(expr.dofdesc) def map_inverse(self, expr, enclosing_prec): return "Solve(%s = %s {%s})" % ( @@ -526,17 +526,17 @@ class StringifyMapper(BaseStringifyMapper): def map_elementwise_sum(self, expr, enclosing_prec): return "ElwiseSum[%s](%s)" % ( - stringify_where(expr.where), + stringify_where(expr.dofdesc), self.rec(expr.operand, PREC_NONE)) def map_elementwise_min(self, expr, enclosing_prec): return "ElwiseMin[%s](%s)" % ( - stringify_where(expr.where), + stringify_where(expr.dofdesc), self.rec(expr.operand, PREC_NONE)) def map_elementwise_max(self, expr, enclosing_prec): return "ElwiseMax[%s](%s)" % ( - stringify_where(expr.where), + stringify_where(expr.dofdesc), self.rec(expr.operand, PREC_NONE)) def map_node_max(self, expr, enclosing_prec): @@ -547,7 +547,7 @@ class StringifyMapper(BaseStringifyMapper): def map_node_coordinate_component(self, expr, enclosing_prec): return "x%d[%s]" % (expr.ambient_axis, - stringify_where(expr.where)) + stringify_where(expr.dofdesc)) def map_num_reference_derivative(self, expr, enclosing_prec): diff_op = " ".join( @@ -558,7 +558,7 @@ class StringifyMapper(BaseStringifyMapper): result = "%s[%s] %s" % ( diff_op, - stringify_where(expr.where), + stringify_where(expr.dofdesc), self.rec(expr.operand, PREC_PRODUCT), ) @@ -568,10 +568,10 @@ class StringifyMapper(BaseStringifyMapper): return result def map_parametrization_derivative(self, expr, enclosing_prec): - return "dx/dr[%s]" % (stringify_where(expr.where)) + return "dx/dr[%s]" % (stringify_where(expr.dofdesc)) def map_q_weight(self, expr, enclosing_prec): - return "w_quad[%s]" % stringify_where(expr.where) + return "w_quad[%s]" % stringify_where(expr.dofdesc) def _stringify_kernel_args(self, kernel_arguments): if not kernel_arguments: diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index 5b60fb31..511e5f32 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -319,11 +319,11 @@ class MatrixBuilderBase(EvaluationMapperBase): op = sym.NumReferenceDerivative( ref_axes=expr.ref_axes, operand=sym.var("u"), - where=expr.where) + dofdesc=expr.dofdesc) return bind(self.places, op)(self.queue, u=rec_operand).get() def map_node_coordinate_component(self, expr): - op = sym.NodeCoordinateComponent(expr.ambient_axis, where=expr.where) + op = sym.NodeCoordinateComponent(expr.ambient_axis, dofdesc=expr.dofdesc) return bind(self.places, op)(self.queue).get() def map_call(self, expr): diff --git a/pytential/symbolic/pde/maxwell/__init__.py b/pytential/symbolic/pde/maxwell/__init__.py index a8dc0c42..0b038e95 100644 --- a/pytential/symbolic/pde/maxwell/__init__.py +++ b/pytential/symbolic/pde/maxwell/__init__.py @@ -96,7 +96,7 @@ def get_sym_maxwell_plane_wave(amplitude_vec, v, omega, epsilon=1, mu=1, where=N # NOTE: for complex, need to ensure real(n).dot(imag(n)) = 0 (7.15) - x = sym.nodes(3, where).as_vector() + x = sym.nodes(3, dofdesc=where).as_vector() v_mag_squared = sym.cse(np.dot(v, v), "v_mag_squared") n = v/sym.sqrt(v_mag_squared) diff --git a/pytential/symbolic/pde/scalar.py b/pytential/symbolic/pde/scalar.py index 840d8044..79e3f3d3 100644 --- a/pytential/symbolic/pde/scalar.py +++ b/pytential/symbolic/pde/scalar.py @@ -50,13 +50,14 @@ class L2WeightedPDEOperator(object): def get_weight(self, where=None): if self.use_l2_weighting: - return cse(area_element(self.kernel.dim, where=where)*QWeight(where)) + return cse(area_element(self.kernel.dim, dofdesc=where) * + QWeight(dofdesc=where)) else: return 1 def get_sqrt_weight(self, where=None): if self.use_l2_weighting: - return sqrt_jac_q_weight(self.kernel.dim, where=where) + return sqrt_jac_q_weight(self.kernel.dim, dofdesc=where) else: return 1 diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 71eed886..29926381 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -25,6 +25,7 @@ THE SOFTWARE. import six from six.moves import intern from warnings import warn +from functools import wraps import numpy as np from pymbolic.primitives import ( # noqa: F401,N813 @@ -41,9 +42,10 @@ from functools import partial __doc__ = """ -.. |where-blurb| replace:: A symbolic name for a geometric object (such - as a :class:`~meshmode.discretization.Discretization`) or a - :class:`DOFDescriptor`. +.. |dofdesc-blurb| replace:: A + :class:`~pytential.symbolic.primitives.DOFDescriptor` or a symbolic + name for a geometric object (such as a + :class:`~meshmode.discretization.Discretization`). Object types @@ -74,6 +76,23 @@ objects occur as part of a symbolic operator representation: those), which is not visible as an array axis in symbolic code. (They're visible only once evaluated.) +DOF Description +^^^^^^^^^^^^^^^ + +.. autoclass:: DEFAULT_SOURCE +.. autoclass:: DEFAULT_TARGET + +.. autoclass:: QBX_SOURCE_STAGE1 +.. autoclass:: QBX_SOURCE_STAGE2 +.. autoclass:: QBX_SOURCE_QUAD_STAGE2 + +.. autoclass:: GRANULARITY_NODE +.. autoclass:: GRANULARITY_CENTER +.. autoclass:: GRANULARITY_ELEMENT + +.. autoclass:: DOFDescriptor +.. autofunction:: as_dofdesc + Placeholders ^^^^^^^^^^^^ @@ -201,6 +220,23 @@ Pretty-printing expressions """ +def _deprecate_kwargs(oldkey, newkey): + def super_wrapper(func): + @wraps(func) + def wrapper(*args, **kwargs): + if oldkey in kwargs: + warn("using `{}` keyword is deprecated. " + "use `{}` instead".format(oldkey, newkey), + DeprecationWarning, stacklevel=2) + kwargs[newkey] = kwargs[oldkey] + del kwargs[oldkey] + + return func(*args, **kwargs) + return wrapper + + return super_wrapper + + # {{{ dof descriptors class DEFAULT_SOURCE: # noqa: N801 @@ -403,8 +439,9 @@ def make_sym_mv(name, num_components): return MultiVector(make_sym_vector(name, num_components)) -def make_sym_surface_mv(name, ambient_dim, dim, where=None): - par_grad = parametrization_derivative_matrix(ambient_dim, dim, where) +@_deprecate_kwargs('where', 'dofdesc') +def make_sym_surface_mv(name, ambient_dim, dim, dofdesc=None): + par_grad = parametrization_derivative_matrix(ambient_dim, dim, dofdesc) return sum( var("%s%d" % (name, i)) @@ -469,17 +506,24 @@ class DiscretizationProperty(Expression): further arguments. """ - init_arg_names = ("where",) + init_arg_names = ("dofdesc",) - def __init__(self, where=None): + @_deprecate_kwargs('where', 'dofdesc') + def __init__(self, dofdesc=None): """ - :arg where: |where-blurb| + :arg dofdesc: |dofdesc-blurb| """ - self.where = as_dofdesc(where) + self.dofdesc = as_dofdesc(dofdesc) + + @property + def where(self): + warn('`where` is deprecated. use `dofdesc` instead.', + DeprecationWarning, stacklevel=2) + return self.dofdesc def __getinitargs__(self): - return (self.where,) + return (self.dofdesc,) # {{{ discretization properties @@ -492,29 +536,31 @@ class QWeight(DiscretizationProperty): class NodeCoordinateComponent(DiscretizationProperty): - init_arg_names = ("ambient_axis", "where") + init_arg_names = ("ambient_axis", "dofdesc") - def __init__(self, ambient_axis, where=None): + @_deprecate_kwargs('where', 'dofdesc') + def __init__(self, ambient_axis, dofdesc=None): """ - :arg where: |where-blurb| + :arg dofdesc: |dofdesc-blurb| """ self.ambient_axis = ambient_axis - DiscretizationProperty.__init__(self, where) + DiscretizationProperty.__init__(self, dofdesc) def __getinitargs__(self): - return (self.ambient_axis, self.where) + return (self.ambient_axis, self.dofdesc) mapper_method = intern("map_node_coordinate_component") -def nodes(ambient_dim, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def nodes(ambient_dim, dofdesc=None): """Return a :class:`pymbolic.geometric_algebra.MultiVector` of node locations. """ return MultiVector( make_obj_array([ - NodeCoordinateComponent(i, where) + NodeCoordinateComponent(i, dofdesc) for i in range(ambient_dim)])) @@ -524,22 +570,24 @@ class NumReferenceDerivative(DiscretizationProperty): reference coordinates. """ - init_arg_names = ("ref_axes", "operand", "where") + init_arg_names = ("ref_axes", "operand", "dofdesc") - def __new__(cls, ref_axes=None, operand=None, where=None): + @_deprecate_kwargs('where', 'dofdesc') + def __new__(cls, ref_axes=None, operand=None, dofdesc=None): # If the constructor is handed a multivector object, return an # object array of the operator applied to each of the # coefficients in the multivector. if isinstance(operand, np.ndarray): def make_op(operand_i): - return cls(ref_axes, operand_i, where=where) + return cls(ref_axes, operand_i, dofdesc=dofdesc) return componentwise(make_op, operand) else: return DiscretizationProperty.__new__(cls) - def __init__(self, ref_axes, operand, where=None): + @_deprecate_kwargs('where', 'dofdesc') + def __init__(self, ref_axes, operand, dofdesc=None): """ :arg ref_axes: a :class:`tuple` of tuples indicating indices of coordinate axes of the reference element to the number of derivatives @@ -549,7 +597,7 @@ class NumReferenceDerivative(DiscretizationProperty): May also be a singile integer *i*, which is viewed as equivalent to ``((i, 1),)``. - :arg where: |where-blurb| + :arg dofdesc: |dofdesc-blurb| """ if isinstance(ref_axes, int): @@ -567,15 +615,16 @@ class NumReferenceDerivative(DiscretizationProperty): self.ref_axes = ref_axes self.operand = operand - DiscretizationProperty.__init__(self, where) + DiscretizationProperty.__init__(self, dofdesc) def __getinitargs__(self): - return (self.ref_axes, self.operand, self.where) + return (self.ref_axes, self.operand, self.dofdesc) mapper_method = intern("map_num_reference_derivative") -def reference_jacobian(func, output_dim, dim, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def reference_jacobian(func, output_dim, dim, dofdesc=None): """Return a :class:`np.array` representing the Jacobian of a vector function with respect to the reference coordinates. """ @@ -584,35 +633,38 @@ def reference_jacobian(func, output_dim, dim, where=None): for i in range(output_dim): func_component = func[i] for j in range(dim): - jac[i, j] = NumReferenceDerivative(j, func_component, where) + jac[i, j] = NumReferenceDerivative(j, func_component, dofdesc) return jac -def parametrization_derivative_matrix(ambient_dim, dim, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def parametrization_derivative_matrix(ambient_dim, dim, dofdesc=None): """Return a :class:`np.array` representing the derivative of the reference-to-global parametrization. """ return cse( reference_jacobian( - [NodeCoordinateComponent(i, where) for i in range(ambient_dim)], - ambient_dim, dim, where=where), + [NodeCoordinateComponent(i, dofdesc) for i in range(ambient_dim)], + ambient_dim, dim, dofdesc=dofdesc), "pd_matrix", cse_scope.DISCRETIZATION) -def parametrization_derivative(ambient_dim, dim, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def parametrization_derivative(ambient_dim, dim, dofdesc=None): """Return a :class:`pymbolic.geometric_algebra.MultiVector` representing the derivative of the reference-to-global parametrization. """ - par_grad = parametrization_derivative_matrix(ambient_dim, dim, where) + par_grad = parametrization_derivative_matrix(ambient_dim, dim, dofdesc) from pytools import product return product(MultiVector(vec) for vec in par_grad.T) -def pseudoscalar(ambient_dim, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def pseudoscalar(ambient_dim, dim=None, dofdesc=None): """ Same as the outer product of all parametrization derivative columns. """ @@ -620,34 +672,37 @@ def pseudoscalar(ambient_dim, dim=None, where=None): dim = ambient_dim - 1 return cse( - parametrization_derivative(ambient_dim, dim, where) + parametrization_derivative(ambient_dim, dim, dofdesc) .project_max_grade(), "pseudoscalar", cse_scope.DISCRETIZATION) -def area_element(ambient_dim, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def area_element(ambient_dim, dim=None, dofdesc=None): return cse( - sqrt(pseudoscalar(ambient_dim, dim, where).norm_squared()), + sqrt(pseudoscalar(ambient_dim, dim, dofdesc).norm_squared()), "area_element", cse_scope.DISCRETIZATION) -def sqrt_jac_q_weight(ambient_dim, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def sqrt_jac_q_weight(ambient_dim, dim=None, dofdesc=None): return cse( sqrt( - area_element(ambient_dim, dim, where) - * QWeight(where)), + area_element(ambient_dim, dim, dofdesc) + * QWeight(dofdesc)), "sqrt_jac_q_weight", cse_scope.DISCRETIZATION) -def normal(ambient_dim, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def normal(ambient_dim, dim=None, dofdesc=None): """Exterior unit normals.""" # Don't be tempted to add a sign here. As it is, it produces # exterior normals for positively oriented curves and surfaces. pder = ( - pseudoscalar(ambient_dim, dim, where) - / area_element(ambient_dim, dim, where)) + pseudoscalar(ambient_dim, dim, dofdesc) + / area_element(ambient_dim, dim, dofdesc)) return cse( # Dorst Section 3.7.2 pder << pder.I.inv(), @@ -655,7 +710,8 @@ def normal(ambient_dim, dim=None, where=None): scope=cse_scope.DISCRETIZATION) -def mean_curvature(ambient_dim, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def mean_curvature(ambient_dim, dim=None, dofdesc=None): """(Numerical) mean curvature.""" if dim is None: @@ -663,16 +719,16 @@ def mean_curvature(ambient_dim, dim=None, where=None): if ambient_dim == 2 and dim == 1: # https://en.wikipedia.org/wiki/Curvature#Local_expressions - xp, yp = parametrization_derivative_matrix(ambient_dim, dim, where) + xp, yp = parametrization_derivative_matrix(ambient_dim, dim, dofdesc) xpp, ypp = cse( - reference_jacobian([xp[0], yp[0]], ambient_dim, dim, where), + reference_jacobian([xp[0], yp[0]], ambient_dim, dim, dofdesc), "p2d_matrix", cse_scope.DISCRETIZATION) kappa = (xp[0]*ypp[0] - yp[0]*xpp[0]) / (xp[0]**2 + yp[0]**2)**(3/2) elif ambient_dim == 3 and dim == 2: # https://en.wikipedia.org/wiki/Mean_curvature#Surfaces_in_3D_space - s_op = shape_operator(ambient_dim, dim=dim, where=where) + s_op = shape_operator(ambient_dim, dim=dim, dofdesc=dofdesc) kappa = -0.5 * sum(s_op[i, i] for i in range(s_op.shape[0])) else: raise NotImplementedError('not available in {}D for {}D surfaces' @@ -681,21 +737,23 @@ def mean_curvature(ambient_dim, dim=None, where=None): return kappa -def first_fundamental_form(ambient_dim, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def first_fundamental_form(ambient_dim, dim=None, dofdesc=None): if dim is None: dim = ambient_dim - 1 if ambient_dim != 3 and dim != 2: raise NotImplementedError("only available for surfaces in 3D") - pd_mat = parametrization_derivative_matrix(ambient_dim, dim, where) + pd_mat = parametrization_derivative_matrix(ambient_dim, dim, dofdesc) return cse( np.dot(pd_mat.T, pd_mat), "fundform1") -def second_fundamental_form(ambient_dim, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def second_fundamental_form(ambient_dim, dim=None, dofdesc=None): """Compute the second fundamental form of a surface. This is in reference to the reference-to-global mapping in use for each element. @@ -710,17 +768,17 @@ def second_fundamental_form(ambient_dim, dim=None, where=None): if ambient_dim != 3 and dim != 2: raise NotImplementedError("only available for surfaces in 3D") - r = nodes(ambient_dim, where=where).as_vector() + r = nodes(ambient_dim, dofdesc=dofdesc).as_vector() # https://en.wikipedia.org/w/index.php?title=Second_fundamental_form&oldid=821047433#Classical_notation from functools import partial - d = partial(NumReferenceDerivative, where=where) + d = partial(NumReferenceDerivative, dofdesc=dofdesc) ruu = d(((0, 2),), r) ruv = d(((0, 1), (1, 1)), r) rvv = d(((1, 2),), r) - nrml = normal(ambient_dim, dim, where).as_vector() + nrml = normal(ambient_dim, dim, dofdesc).as_vector() ff2_l = cse(np.dot(ruu, nrml), "fundform2_L") ff2_m = cse(np.dot(ruv, nrml), "fundform2_M") @@ -734,7 +792,8 @@ def second_fundamental_form(ambient_dim, dim=None, where=None): return result -def shape_operator(ambient_dim, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def shape_operator(ambient_dim, dim=None, dofdesc=None): if dim is None: dim = ambient_dim - 1 @@ -742,8 +801,8 @@ def shape_operator(ambient_dim, dim=None, where=None): raise NotImplementedError("only available for surfaces in 3D") # https://en.wikipedia.org/w/index.php?title=Differential_geometry_of_surfaces&oldid=833587563 - (E, F), (F, G) = first_fundamental_form(ambient_dim, dim, where) - (e, f), (f, g) = second_fundamental_form(ambient_dim, dim, where) + (E, F), (F, G) = first_fundamental_form(ambient_dim, dim, dofdesc) + (e, f), (f, g) = second_fundamental_form(ambient_dim, dim, dofdesc) result = np.zeros((2, 2), dtype=object) result[0, 0] = e*G-f*F @@ -756,7 +815,8 @@ def shape_operator(ambient_dim, dim=None, where=None): "shape_operator") -def _panel_size(ambient_dim, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def _panel_size(ambient_dim, dim=None, dofdesc=None): # A broken quasi-1D approximation of 1D element size. Do not use. if dim is None: @@ -803,12 +863,13 @@ def _small_mat_eigenvalues(mat): "eigenvalue formula for %dx%d matrices" % (m, n)) +@_deprecate_kwargs('where', 'dofdesc') def _equilateral_parametrization_derivative_matrix(ambient_dim, dim=None, - where=None): + dofdesc=None): if dim is None: dim = ambient_dim - 1 - pder_mat = parametrization_derivative_matrix(ambient_dim, dim, where) + pder_mat = parametrization_derivative_matrix(ambient_dim, dim, dofdesc) # The above procedure works well only when the 'reference' end of the # mapping is in equilateral coordinates. @@ -821,7 +882,8 @@ def _equilateral_parametrization_derivative_matrix(ambient_dim, dim=None, "equilateral_pder_mat") -def _simplex_mapping_max_stretch_factor(ambient_dim, dim=None, where=None, +@_deprecate_kwargs('where', 'dofdesc') +def _simplex_mapping_max_stretch_factor(ambient_dim, dim=None, dofdesc=None, with_elementwise_max=True): """Return the largest factor by which the reference-to-global mapping stretches the bi-unit (i.e. :math:`[-1,1]`) reference @@ -843,7 +905,7 @@ def _simplex_mapping_max_stretch_factor(ambient_dim, dim=None, where=None, # reflects available quadrature resolution in that direction. equi_pder_mat = _equilateral_parametrization_derivative_matrix( - ambient_dim, dim, where) + ambient_dim, dim, dofdesc) # Compute eigenvalues of J^T to compute SVD. equi_pder_mat_jtj = cse( @@ -863,21 +925,22 @@ def _simplex_mapping_max_stretch_factor(ambient_dim, dim=None, where=None, result = Max(tuple(stretch_factors)) if with_elementwise_max: - result = ElementwiseMax(result, where=where) + result = ElementwiseMax(result, dofdesc=dofdesc) return cse(result, "mapping_max_stretch", cse_scope.DISCRETIZATION) -def _max_curvature(ambient_dim, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def _max_curvature(ambient_dim, dim=None, dofdesc=None): # An attempt at a 'max curvature' criterion. if dim is None: dim = ambient_dim - 1 if ambient_dim == 2: - return abs(mean_curvature(ambient_dim, dim, where=where)) + return abs(mean_curvature(ambient_dim, dim, dofdesc=dofdesc)) elif ambient_dim == 3: - shape_op = shape_operator(ambient_dim, dim, where=where) + shape_op = shape_operator(ambient_dim, dim, dofdesc=dofdesc) abs_principal_curvatures = [ abs(x) for x in _small_mat_eigenvalues(shape_op)] @@ -888,15 +951,17 @@ def _max_curvature(ambient_dim, dim=None, where=None): "dimensions" % ambient_dim) -def _scaled_max_curvature(ambient_dim, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def _scaled_max_curvature(ambient_dim, dim=None, dofdesc=None): """An attempt at a unit-less, scale-invariant quantity that characterizes 'how much curviness there is on an element'. Values seem to hover around 1 on typical meshes. Empirical evidence suggests that elements exceeding a threshold of about 0.8-1 will have high QBX truncation error. """ - return _max_curvature(ambient_dim, dim, where=where) * \ - _simplex_mapping_max_stretch_factor(ambient_dim, dim, where=where, + return _max_curvature(ambient_dim, dim, dofdesc=dofdesc) * \ + _simplex_mapping_max_stretch_factor(ambient_dim, dim, + dofdesc=dofdesc, with_elementwise_max=False) # }}} @@ -912,24 +977,27 @@ def _expansion_radii_factor(ambient_dim, dim): return 0.5 * dim_fudge_factor -def _quad_resolution(ambient_dim, dim=None, granularity=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def _quad_resolution(ambient_dim, dim=None, granularity=None, dofdesc=None): """This measures the quadrature resolution across the mesh. In a 1D uniform mesh of uniform 'parametrization speed', it should be the same as the panel length. """ - source = as_dofdesc(where) + source = as_dofdesc(dofdesc) target = source.copy(granularity=granularity) stretch = _simplex_mapping_max_stretch_factor(ambient_dim, - dim=dim, where=source) + dim=dim, dofdesc=source) return Interpolation(source, target, stretch) -def _source_danger_zone_radii(ambient_dim, dim=None, granularity=None, where=None): - where = as_dofdesc(where) - if where.discr is None: - where = where.copy(discr=QBX_SOURCE_STAGE2) +@_deprecate_kwargs('where', 'dofdesc') +def _source_danger_zone_radii(ambient_dim, dim=None, + granularity=None, dofdesc=None): + dofdesc = as_dofdesc(dofdesc) + if dofdesc.discr is None: + dofdesc = dofdesc.copy(discr=QBX_SOURCE_STAGE2) # This should be the expression of the expansion radii, but # @@ -944,32 +1012,34 @@ def _source_danger_zone_radii(ambient_dim, dim=None, granularity=None, where=Non factor = 0.75 * _expansion_radii_factor(ambient_dim, dim) return factor * _quad_resolution(ambient_dim, dim=dim, - granularity=granularity, where=where) + granularity=granularity, dofdesc=dofdesc) -def _close_target_tunnel_radii(ambient_dim, dim=None, granularity=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def _close_target_tunnel_radii(ambient_dim, dim=None, + granularity=None, dofdesc=None): factor = 0.5 * _expansion_radii_factor(ambient_dim, dim) return factor * _quad_resolution(ambient_dim, dim=dim, - granularity=granularity, where=where) + granularity=granularity, dofdesc=dofdesc) -def expansion_radii(ambient_dim, dim=None, granularity=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def expansion_radii(ambient_dim, dim=None, granularity=None, dofdesc=None): factor = _expansion_radii_factor(ambient_dim, dim) return cse(factor * _quad_resolution(ambient_dim, dim=dim, - granularity=granularity, where=where), + granularity=granularity, dofdesc=dofdesc), "expansion_radii", cse_scope.DISCRETIZATION) -def expansion_centers(ambient_dim, side, dim=None, where=None): - where = as_dofdesc(where) - - x = nodes(ambient_dim, where=where) - normals = normal(ambient_dim, dim=dim, where=where) +@_deprecate_kwargs('where', 'dofdesc') +def expansion_centers(ambient_dim, side, dim=None, dofdesc=None): + x = nodes(ambient_dim, dofdesc=dofdesc) + normals = normal(ambient_dim, dim=dim, dofdesc=dofdesc) radii = expansion_radii(ambient_dim, dim=dim, - granularity=GRANULARITY_NODE, where=where) + granularity=GRANULARITY_NODE, dofdesc=dofdesc) centers = x + side * radii * normals return cse(centers.as_vector(), @@ -977,28 +1047,30 @@ def expansion_centers(ambient_dim, side, dim=None, where=None): cse_scope.DISCRETIZATION) -def h_max(ambient_dim, dim=None, where=None): - where = as_dofdesc(where).copy(granularity=GRANULARITY_ELEMENT) - r = _quad_resolution(ambient_dim, dim=dim, where=where) +@_deprecate_kwargs('where', 'dofdesc') +def h_max(ambient_dim, dim=None, dofdesc=None): + dofdesc = as_dofdesc(dofdesc).copy(granularity=GRANULARITY_ELEMENT) + r = _quad_resolution(ambient_dim, dim=dim, dofdesc=dofdesc) return cse(NodeMax(r), "h_max", cse_scope.DISCRETIZATION) -def weights_and_area_elements(ambient_dim, dim=None, where=None): - where = as_dofdesc(where) - if where.discr is QBX_SOURCE_QUAD_STAGE2: +@_deprecate_kwargs('where', 'dofdesc') +def weights_and_area_elements(ambient_dim, dim=None, dofdesc=None): + dofdesc = as_dofdesc(dofdesc) + if dofdesc.discr is QBX_SOURCE_QUAD_STAGE2: # quad_stage2_density_discr is not guaranteed to be usable for # interpolation/differentiation. Use stage2_density_discr to find # area elements instead, then upsample that. - source = where.copy(discr=QBX_SOURCE_STAGE2) - area = Interpolation(source, where, - area_element(ambient_dim, dim=dim, where=source)) + source = dofdesc.copy(discr=QBX_SOURCE_STAGE2) + area = Interpolation(source, dofdesc, + area_element(ambient_dim, dim=dim, dofdesc=source)) else: - area = area_element(ambient_dim, dim=dim, where=where) + area = area_element(ambient_dim, dim=dim, dofdesc=dofdesc) - return cse(area * QWeight(where=where), + return cse(area * QWeight(dofdesc=dofdesc), "weights_area_elements", cse_scope.DISCRETIZATION) @@ -1043,38 +1115,47 @@ class NodeMax(SingleScalarOperandExpression): mapper_method = "map_node_max" -def integral(ambient_dim, dim, operand, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def integral(ambient_dim, dim, operand, dofdesc=None): """A volume integral of *operand*.""" return NodeSum( - area_element(ambient_dim, dim, where) - * QWeight(where) + area_element(ambient_dim, dim, dofdesc) + * QWeight(dofdesc) * operand) class SingleScalarOperandExpressionWithWhere(Expression): - init_arg_names = ("operand", "where") + init_arg_names = ("operand", "dofdesc") - def __new__(cls, operand=None, where=None): + @_deprecate_kwargs('where', 'dofdesc') + def __new__(cls, operand=None, dofdesc=None): # If the constructor is handed a multivector object, return an # object array of the operator applied to each of the # coefficients in the multivector. if isinstance(operand, (np.ndarray, MultiVector)): def make_op(operand_i): - return cls(operand_i, where) + return cls(operand_i, dofdesc) return componentwise(make_op, operand) else: return Expression.__new__(cls) - def __init__(self, operand, where=None): + @_deprecate_kwargs('where', 'dofdesc') + def __init__(self, operand, dofdesc=None): self.operand = operand - self.where = where + self.dofdesc = as_dofdesc(dofdesc) + + @property + def where(self): + warn('`where` is deprecated. use `dofdesc` instead.', + DeprecationWarning, stacklevel=2) + return self.dofdesc def __getinitargs__(self): - return (self.operand, self.where) + return (self.operand, self.dofdesc) class ElementwiseSum(SingleScalarOperandExpressionWithWhere): @@ -1106,54 +1187,71 @@ class Ones(Expression): discretization. """ - init_arg_names = ("where",) + init_arg_names = ("dofdesc",) - def __init__(self, where=None): - self.where = where + @_deprecate_kwargs('where', 'dofdesc') + def __init__(self, dofdesc=None): + self.dofdesc = as_dofdesc(dofdesc) + + @property + def where(self): + warn('`where` is deprecated. use `dofdesc` instead.', + DeprecationWarning, stacklevel=2) + return self.dofdesc def __getinitargs__(self): - return (self.where,) + return (self.dofdesc,) mapper_method = intern("map_ones") -def ones_vec(dim, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def ones_vec(dim, dofdesc=None): from pytools.obj_array import make_obj_array return MultiVector( - make_obj_array(dim*[Ones(where)])) + make_obj_array(dim*[Ones(dofdesc)])) -def area(ambient_dim, dim, where=None): - return cse(integral(ambient_dim, dim, Ones(where), where), "area", +@_deprecate_kwargs('where', 'dofdesc') +def area(ambient_dim, dim, dofdesc=None): + return cse(integral(ambient_dim, dim, Ones(dofdesc), dofdesc), "area", cse_scope.DISCRETIZATION) -def mean(ambient_dim, dim, operand, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def mean(ambient_dim, dim, operand, dofdesc=None): return ( - integral(ambient_dim, dim, operand, where) - / area(ambient_dim, dim, where)) + integral(ambient_dim, dim, operand, dofdesc) + / area(ambient_dim, dim, dofdesc)) class IterativeInverse(Expression): - init_arg_names = ("expression", "rhs", "variable_name", "extra_vars", "where") + init_arg_names = ("expression", "rhs", "variable_name", "extra_vars", "dofdesc") + @_deprecate_kwargs('where', 'dofdesc') def __init__(self, expression, rhs, variable_name, extra_vars={}, - where=None): + dofdesc=None): self.expression = expression self.rhs = rhs self.variable_name = variable_name self.extra_vars = extra_vars - self.where = where + self.dofdesc = as_dofdesc(dofdesc) + + @property + def where(self): + warn('`where` is deprecated. use `dofdesc` instead.', + DeprecationWarning, stacklevel=2) + return self.dofdesc def __getinitargs__(self): return (self.expression, self.rhs, self.variable_name, - self.extra_vars, self.where) + self.extra_vars, self.dofdesc) def get_hash(self): return hash((self.__class__,) + (self.expression, self.rhs, self.variable_name, - frozenset(six.iteritems(self.extra_vars)), self.where)) + frozenset(six.iteritems(self.extra_vars)), self.dofdesc)) mapper_method = intern("map_inverse") @@ -1541,10 +1639,11 @@ def S(kernel, density, kernel_arguments, **kwargs) -def tangential_derivative(ambient_dim, operand, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def tangential_derivative(ambient_dim, operand, dim=None, dofdesc=None): pder = ( - pseudoscalar(ambient_dim, dim, where) - / area_element(ambient_dim, dim, where)) + pseudoscalar(ambient_dim, dim, dofdesc) + / area_element(ambient_dim, dim, dofdesc)) # FIXME: Should be formula (3.25) in Dorst et al. d = Derivative() @@ -1552,15 +1651,16 @@ def tangential_derivative(ambient_dim, operand, dim=None, where=None): (d.dnabla(ambient_dim) * d(operand)) >> pder) -def normal_derivative(ambient_dim, operand, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def normal_derivative(ambient_dim, operand, dim=None, dofdesc=None): d = Derivative() return d.resolve( - (normal(ambient_dim, dim, where).scalar_product(d.dnabla(ambient_dim))) + (normal(ambient_dim, dim, dofdesc).scalar_product(d.dnabla(ambient_dim))) * d(operand)) def Sp(kernel, *args, **kwargs): - where = kwargs.get("target") + dofdesc = kwargs.get("target") if "qbx_forced_limit" not in kwargs: warn("not specifying qbx_forced_limit on call to 'Sp' is deprecated, " "defaulting to 'avg'", DeprecationWarning, stacklevel=2) @@ -1578,7 +1678,7 @@ def Sp(kernel, *args, **kwargs): return normal_derivative( ambient_dim, S(kernel, *args, **kwargs), - dim=dim, where=where) + dim=dim, dofdesc=dofdesc) def Spp(kernel, *args, **kwargs): @@ -1591,11 +1691,11 @@ def Spp(kernel, *args, **kwargs): "the kernel, or directly") dim = kwargs.pop("dim", None) - where = kwargs.get("target") + dofdesc = kwargs.get("target") return normal_derivative( ambient_dim, Sp(kernel, *args, **kwargs), - dim=dim, where=where) + dim=dim, dofdesc=dofdesc) def D(kernel, *args, **kwargs): @@ -1608,7 +1708,7 @@ def D(kernel, *args, **kwargs): "the kernel, or directly") dim = kwargs.pop("dim", None) - where = kwargs.get("source") + dofdesc = kwargs.get("source") if "qbx_forced_limit" not in kwargs: warn("not specifying qbx_forced_limit on call to 'D' is deprecated, " @@ -1617,7 +1717,7 @@ def D(kernel, *args, **kwargs): return int_g_dsource( ambient_dim, - normal(ambient_dim, dim, where), + normal(ambient_dim, dim, dofdesc), kernel, *args, **kwargs).xproject(0) @@ -1638,7 +1738,7 @@ def Dp(kernel, *args, **kwargs): return normal_derivative( ambient_dim, D(kernel, *args, **kwargs), - dim=dim, where=target) + dim=dim, dofdesc=target) # }}} @@ -1649,15 +1749,16 @@ def Dp(kernel, *args, **kwargs): # {{{ conventional vector calculus -def tangential_onb(ambient_dim, dim=None, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def tangential_onb(ambient_dim, dim=None, dofdesc=None): """Return a matrix of shape ``(ambient_dim, dim)`` with orthogonal columns - spanning the tangential space of the surface of *where*. + spanning the tangential space of the surface of *dofdesc*. """ if dim is None: dim = ambient_dim - 1 - pd_mat = parametrization_derivative_matrix(ambient_dim, dim, where) + pd_mat = parametrization_derivative_matrix(ambient_dim, dim, dofdesc) # {{{ Gram-Schmidt @@ -1676,30 +1777,34 @@ def tangential_onb(ambient_dim, dim=None, where=None): return orth_pd_mat -def xyz_to_tangential(xyz_vec, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def xyz_to_tangential(xyz_vec, dofdesc=None): ambient_dim = len(xyz_vec) - tonb = tangential_onb(ambient_dim, where=where) + tonb = tangential_onb(ambient_dim, dofdesc=dofdesc) return make_obj_array([ np.dot(tonb[:, i], xyz_vec) for i in range(ambient_dim - 1) ]) -def tangential_to_xyz(tangential_vec, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def tangential_to_xyz(tangential_vec, dofdesc=None): ambient_dim = len(tangential_vec) + 1 - tonb = tangential_onb(ambient_dim, where=where) + tonb = tangential_onb(ambient_dim, dofdesc=dofdesc) return sum( tonb[:, i] * tangential_vec[i] for i in range(ambient_dim - 1)) -def project_to_tangential(xyz_vec, where=None): +@_deprecate_kwargs('where', 'dofdesc') +def project_to_tangential(xyz_vec, dofdesc=None): return tangential_to_xyz( - cse(xyz_to_tangential(xyz_vec, where), where)) + cse(xyz_to_tangential(xyz_vec, dofdesc), dofdesc)) -def n_dot(vec, where=None): - nrm = normal(len(vec), where).as_vector() +@_deprecate_kwargs('where', 'dofdesc') +def n_dot(vec, dofdesc=None): + nrm = normal(len(vec), dofdesc).as_vector() return np.dot(nrm, vec) @@ -1716,8 +1821,9 @@ def cross(vec_a, vec_b): for i in range(3)]) -def n_cross(vec, where=None): - return cross(normal(3, where).as_vector(), vec) +@_deprecate_kwargs('where', 'dofdesc') +def n_cross(vec, dofdesc=None): + return cross(normal(3, dofdesc).as_vector(), vec) def div(vec): diff --git a/test/test_global_qbx.py b/test/test_global_qbx.py index 2af1edcc..ca276959 100644 --- a/test/test_global_qbx.py +++ b/test/test_global_qbx.py @@ -130,11 +130,11 @@ def run_source_refinement_test(ctx_factory, mesh, order, helmholtz_k=None): 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, - where=sym.GRANULARITY_ELEMENT))(queue).get() + dofdesc=sym.GRANULARITY_ELEMENT))(queue).get() quad_res = bind(lpot_source, sym._quad_resolution( lpot_source.ambient_dim, - where=sym.GRANULARITY_ELEMENT))(queue) + dofdesc=sym.GRANULARITY_ELEMENT))(queue) # {{{ check if satisfying criteria diff --git a/test/test_scalar_int_eq.py b/test/test_scalar_int_eq.py index a884e7da..2b3dbfa3 100644 --- a/test/test_scalar_int_eq.py +++ b/test/test_scalar_int_eq.py @@ -622,7 +622,7 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize): bc = bind( (point_source, density_discr), sym.normal_derivative( - qbx.ambient_dim, pot_src, where=sym.DEFAULT_TARGET) + qbx.ambient_dim, pot_src, dofdesc=sym.DEFAULT_TARGET) )(queue, charges=source_charges_dev, **concrete_knl_kwargs) # }}} -- GitLab From 851411e17278930f5df5e7ec995d3ee68ae200d5 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 26 Jul 2019 20:06:06 -0500 Subject: [PATCH 77/96] small cleanups --- pytential/qbx/__init__.py | 7 ++----- pytential/symbolic/mappers.py | 14 +++++++------- pytential/symbolic/matrix.py | 2 +- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 159b7b4b..f6975db2 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -357,12 +357,9 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): def weights_and_area_elements(self): from pytential import bind, sym with cl.CommandQueue(self.cl_context) as queue: - where = sym.DOFDescriptor( - sym.DEFAULT_SOURCE, - discr=sym.QBX_SOURCE_QUAD_STAGE2) - return bind(self, sym.weights_and_area_elements( - self.ambient_dim, where=where))(queue).with_queue(None) + self.ambient_dim, + where=sym.QBX_SOURCE_QUAD_STAGE2))(queue).with_queue(None) # }}} diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 21b64434..f1218ddb 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -416,7 +416,7 @@ class InterpolationDiscretizationTagger(IdentityMapper): def map_node_coordinate_component(self, expr): dd = prim.as_dofdesc(expr.where) - if dd.discr == self.discr: + if dd.discr is self.discr: return expr else: return type(expr)( @@ -425,7 +425,7 @@ class InterpolationDiscretizationTagger(IdentityMapper): def map_num_reference_derivative(self, expr): dd = prim.as_dofdesc(expr.where) - if dd.discr == self.discr: + if dd.discr is self.discr: return expr else: return type(expr)( @@ -435,14 +435,14 @@ class InterpolationDiscretizationTagger(IdentityMapper): class InterpolationPreprocessor(IdentityMapper): - def __init__(self, places, discr=None): + def __init__(self, places, from_discr=None): self.places = places - self.discr = discr or prim.QBX_SOURCE_STAGE2 - self.tagger = InterpolationDiscretizationTagger(self.discr) + self.from_discr = from_discr or prim.QBX_SOURCE_STAGE2 + self.tagger = InterpolationDiscretizationTagger(self.from_discr) def map_num_reference_derivative(self, expr): target_dd = prim.as_dofdesc(expr.where) - if target_dd.discr != prim.QBX_SOURCE_QUAD_STAGE2: + if target_dd.discr is not prim.QBX_SOURCE_QUAD_STAGE2: return expr from pytential.qbx import QBXLayerPotentialSource @@ -450,7 +450,7 @@ class InterpolationPreprocessor(IdentityMapper): if not isinstance(lpot_source, QBXLayerPotentialSource): return expr - source_dd = target_dd.copy(discr=self.discr) + source_dd = target_dd.copy(discr=self.from_discr) return prim.Interpolation( source_dd, target_dd, diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index f69ed44a..42f622f8 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -370,7 +370,7 @@ class MatrixBuilder(MatrixBuilderBase): def map_interpolation(self, expr): source_dd = sym.as_dofdesc(expr.source) target_dd = sym.as_dofdesc(expr.target) - if target_dd.discr != sym.QBX_SOURCE_QUAD_STAGE2: + if target_dd.discr is not sym.QBX_SOURCE_QUAD_STAGE2: raise RuntimeError("can only interpolate to QBX_SOURCE_QUAD_STAGE2") operand = self.rec(expr.operand) -- GitLab From ba7f2c6869e92bc1f6027ae987ca4b9495a7871f Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 26 Jul 2019 20:08:16 -0500 Subject: [PATCH 78/96] flake8 fix --- pytential/symbolic/pde/scalar.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pytential/symbolic/pde/scalar.py b/pytential/symbolic/pde/scalar.py index 79e3f3d3..032c68ee 100644 --- a/pytential/symbolic/pde/scalar.py +++ b/pytential/symbolic/pde/scalar.py @@ -50,8 +50,8 @@ class L2WeightedPDEOperator(object): def get_weight(self, where=None): if self.use_l2_weighting: - return cse(area_element(self.kernel.dim, dofdesc=where) * - QWeight(dofdesc=where)) + return cse(area_element(self.kernel.dim, dofdesc=where) + * QWeight(dofdesc=where)) else: return 1 -- GitLab From b9a1b0bd5a2405a4f1212811059e81e6ac19ad3a Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 26 Jul 2019 23:00:13 -0500 Subject: [PATCH 79/96] rename where to geometry in DOFDescriptor --- pytential/symbolic/dof_connection.py | 4 +- pytential/symbolic/execution.py | 52 ++++++++++++------------ pytential/symbolic/mappers.py | 61 ++++++++++++++-------------- pytential/symbolic/primitives.py | 44 ++++++++++---------- test/test_matrix.py | 10 +++-- test/test_symbolic.py | 6 ++- 6 files changed, 92 insertions(+), 85 deletions(-) diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 34de41be..4f6771e8 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -206,8 +206,8 @@ def connection_from_dds(places, from_dd, to_dd): places = GeometryCollection(places) from_discr = places[from_dd] - if from_dd.where != to_dd.where: - raise ValueError("cannot interpolate between different domains") + if from_dd.geometry != to_dd.geometry: + raise ValueError("cannot interpolate between different geometries") if from_dd.granularity is not sym.GRANULARITY_NODE: raise ValueError("can only interpolate from `GRANULARITY_NODE`") diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 2a97eae7..781555f6 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -456,18 +456,18 @@ class GeometryCollection(object): self.places = {} if isinstance(places, QBXLayerPotentialSource): - self.places[auto_source.where] = places - self.places[auto_target.where] = \ + self.places[auto_source.geometry] = places + self.places[auto_target.geometry] = \ self._get_lpot_discretization(places, auto_target) elif isinstance(places, (Discretization, PotentialSource)): - self.places[auto_source.where] = places - self.places[auto_target.where] = places + self.places[auto_source.geometry] = places + self.places[auto_target.geometry] = places elif isinstance(places, TargetBase): - self.places[auto_target.where] = places + self.places[auto_target.geometry] = places elif isinstance(places, tuple): source_discr, target_discr = places - self.places[auto_source.where] = source_discr - self.places[auto_target.where] = target_discr + self.places[auto_source.geometry] = source_discr + self.places[auto_target.geometry] = target_discr else: self.places = places.copy() @@ -488,49 +488,51 @@ class GeometryCollection(object): def auto_target(self): return self.auto_where[1] - def _get_lpot_discretization(self, lpot, dd): - dd = sym.as_dofdesc(dd) + def _get_lpot_discretization(self, lpot, dofdesc): + dofdesc = sym.as_dofdesc(dofdesc) - if dd.discr is sym.QBX_SOURCE_STAGE2: + if dofdesc.discr is sym.QBX_SOURCE_STAGE2: return lpot.stage2_density_discr - if dd.discr is sym.QBX_SOURCE_QUAD_STAGE2: + if dofdesc.discr is sym.QBX_SOURCE_QUAD_STAGE2: return lpot.quad_stage2_density_discr return lpot.density_discr - def get_discretization(self, where): + def get_discretization(self, dofdesc): """ - :arg where: location identifier. + :arg dofdesc: a :class:`~pytential.symbolic.primitives.DOFDescriptor` + specifying the desired discretization. :return: a geometry object in the collection corresponding to the - key *where*. If it is a + key *dofdesc*. If it is a :class:`~pytential.source.LayerPotentialSourceBase`, we look for the corresponding :class:`~meshmode.discretization.Discretization` in its attributes instead. """ - dd = sym.as_dofdesc(where) - if dd.where in self.places: - discr = self.places[dd.where] + dofdesc = sym.as_dofdesc(dofdesc) + if dofdesc.geometry in self.places: + discr = self.places[dofdesc.geometry] else: - raise KeyError('`where` not in the collection: {}'.format(dd.where)) + raise KeyError('geometry not in the collection: {}'.format( + dofdesc.geometry)) from pytential.qbx import QBXLayerPotentialSource from pytential.source import LayerPotentialSourceBase if isinstance(discr, QBXLayerPotentialSource): - return self._get_lpot_discretization(discr, dd) + return self._get_lpot_discretization(discr, dofdesc) elif isinstance(discr, LayerPotentialSourceBase): return discr.density_discr else: return discr - def __getitem__(self, where): - where = sym.as_dofdesc(where) - return self.places[where.where] + def __getitem__(self, dofdesc): + dofdesc = sym.as_dofdesc(dofdesc) + return self.places[dofdesc.geometry] - def __contains__(self, where): - where = sym.as_dofdesc(where) - return where.where in self.places + def __contains__(self, dofdesc): + dofdesc = sym.as_dofdesc(dofdesc) + return dofdesc.geometry in self.places def copy(self): return GeometryCollection( diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 0aca8940..1e8fa1cb 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -212,15 +212,16 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): map_common_subexpression_uncached = \ IdentityMapper.map_common_subexpression - def _as_dofdesc(self, where): - dd = prim.as_dofdesc(where) - if dd.where is None: - if dd.discr is None and dd.granularity is prim.GRANULARITY_NODE: - dd = dd.copy(where=self.default_where) + def _as_dofdesc(self, dofdesc): + dofdesc = prim.as_dofdesc(dofdesc) + if dofdesc.geometry is None: + if dofdesc.discr is None \ + and dofdesc.granularity is prim.GRANULARITY_NODE: + dofdesc = dofdesc.copy(geometry=self.default_where) else: - dd = dd.copy(where=self.default_source) + dofdesc = dofdesc.copy(geometry=self.default_source) - return dd + return dofdesc def map_ones(self, expr): return type(expr)(where=self._as_dofdesc(expr.where)) @@ -256,10 +257,10 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): source = prim.as_dofdesc(expr.source) target = prim.as_dofdesc(expr.target) - if source.where is None: - source = source.copy(where=self.default_source) - if target.where is None: - target = target.copy(where=self.default_where) + if source.geometry is None: + source = source.copy(geometry=self.default_source) + if target.geometry is None: + target = target.copy(geometry=self.default_where) return type(expr)( expr.kernel, @@ -271,10 +272,10 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): )) def map_inverse(self, expr): - dd = prim.as_dofdesc(expr.where) + dofdesc = prim.as_dofdesc(expr.where) - if dd.where is None: - dd = dd.copy(where=self.default_where) + if dofdesc.geometry is None: + dofdesc = dofdesc.copy(geometry=self.default_where) return type(expr)( # don't recurse into expression--it is a separate world that @@ -284,16 +285,16 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): dict([ (name, self.rec(name_expr)) for name, name_expr in six.iteritems(expr.extra_vars)]), - dd) + dofdesc) def map_interpolation(self, expr): source = prim.as_dofdesc(expr.source) target = prim.as_dofdesc(expr.target) - if source.where is None: - source = source.copy(where=self.default_source) - if target.where is None: - target = target.copy(where=self.default_source) + if source.geometry is None: + source = source.copy(geometry=self.default_source) + if target.geometry is None: + target = target.copy(geometry=self.default_source) return type(expr)(source, target, self.operand_rec(expr.operand)) @@ -415,21 +416,21 @@ class QBXInterpolationPreprocessor(IdentityMapper): self.places = places def map_int_g(self, expr): - source_dd = prim.as_dofdesc(expr.source) - if source_dd.discr is prim.QBX_SOURCE_QUAD_STAGE2: + source = prim.as_dofdesc(expr.source) + if source.discr is prim.QBX_SOURCE_QUAD_STAGE2: return expr - lpot_source = self.places[expr.source] + lpot_source = self.places[source] from pytential.qbx import QBXLayerPotentialSource if not isinstance(lpot_source, QBXLayerPotentialSource): return expr - target_dd = source_dd.copy(discr=prim.QBX_SOURCE_QUAD_STAGE2) + target = source.copy(discr=prim.QBX_SOURCE_QUAD_STAGE2) density = prim.Interpolation( - source_dd, target_dd, self.rec(expr.density)) + source, target, self.rec(expr.density)) kernel_arguments = dict( (name, prim.Interpolation( - source_dd, target_dd, self.rec(arg_expr))) + source, target, self.rec(arg_expr))) for name, arg_expr in expr.kernel_arguments.items()) return expr.copy( @@ -445,13 +446,13 @@ class QBXPreprocessor(IdentityMapper): def map_int_g(self, expr): from pytential import sym - source_dd = sym.as_dofdesc(expr.source) - target_dd = sym.as_dofdesc(expr.target) - if source_dd.where != self.source_name: + source = sym.as_dofdesc(expr.source) + target = sym.as_dofdesc(expr.target) + if source.geometry != self.source_name: return expr - source_discr = self.places.get_discretization(source_dd) - target_discr = self.places.get_discretization(target_dd) + source_discr = self.places.get_discretization(source) + target_discr = self.places.get_discretization(target) if expr.qbx_forced_limit == 0: raise ValueError("qbx_forced_limit == 0 was a bad idea and " diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 71eed886..31ebf6b7 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -250,14 +250,14 @@ class GRANULARITY_ELEMENT: # noqa: N801 class DOFDescriptor(object): """A data structure specifying the meaning of a vector of degrees of freedom that is handled by :mod:`pytential` (a "DOF vector"). In particular, using - :attr:`where`, this data structure describes the geometric object on which + :attr:`geometry`, this data structure describes the geometric object on which the (scalar) function described by the DOF vector exists. Using :attr:`granularity`, the data structure describes how the geometric object is discretized (e.g. conventional nodal data, per-element scalars, etc.) - .. attribute:: where + .. attribute:: geometry - An identifier for the domain on which the DOFs exist. This can be a + An identifier for the geometry on which the DOFs exist. This can be a simple string or any other hashable identifier for the geometric object. The geometric objects are generally subclasses of :class:`~pytential.source.PotentialSource`, @@ -279,7 +279,7 @@ class DOFDescriptor(object): :class:`GRANULARITY_ELEMENT` (one DOF per element). """ - def __init__(self, where, discr=None, granularity=None): + def __init__(self, geometry=None, discr=None, granularity=None): if granularity is None: granularity = GRANULARITY_NODE @@ -294,29 +294,29 @@ class DOFDescriptor(object): or granularity is GRANULARITY_ELEMENT): raise ValueError('unknown granularity: "{}"'.format(granularity)) - self.where = where + self.geometry = geometry self.discr = discr self.granularity = granularity - def copy(self, where=None, discr=None, granularity=None): - if isinstance(where, DOFDescriptor): - discr = where.discr if discr is None else discr - where = where.where + def copy(self, geometry=None, discr=None, granularity=None): + if isinstance(geometry, DOFDescriptor): + discr = geometry.discr if discr is None else discr + geometry = geometry.geometry return type(self)( - where=(self.where - if where is None else where), + geometry=(self.geometry + if geometry is None else geometry), granularity=(self.granularity if granularity is None else granularity), discr=(self.discr if discr is None else discr)) def __hash__(self): - return hash((type(self), self.where, self.discr, self.granularity)) + return hash((type(self), self.geometry, self.discr, self.granularity)) def __eq__(self, other): return (type(self) is type(other) - and self.where == other.where + and self.geometry == other.geometry and self.discr == other.discr and self.granularity == other.granularity) @@ -324,22 +324,22 @@ class DOFDescriptor(object): return not self.__eq__(other) def __repr__(self): - return '{}(where={}, discr={}, granularity={})'.format( + return '{}(geometry={}, discr={}, granularity={})'.format( type(self).__name__, - self.where, + self.geometry, self.discr if self.discr is None else self.discr.__name__, self.granularity.__name__) def __str__(self): name = [] - if self.where is None: + if self.geometry is None: name.append("?") - elif self.where is DEFAULT_SOURCE: + elif self.geometry is DEFAULT_SOURCE: name.append("s") - elif self.where is DEFAULT_TARGET: + elif self.geometry is DEFAULT_TARGET: name.append("t") else: - name.append(str(self.where)) + name.append(str(self.geometry)) if self.discr is QBX_SOURCE_STAGE2: name.append("stage2") @@ -361,14 +361,14 @@ def as_dofdesc(desc): if desc is QBX_SOURCE_STAGE1 \ or desc is QBX_SOURCE_STAGE2 \ or desc is QBX_SOURCE_QUAD_STAGE2: - return DOFDescriptor(None, discr=desc) + return DOFDescriptor(discr=desc) if desc is GRANULARITY_NODE \ or desc is GRANULARITY_CENTER \ or desc is GRANULARITY_ELEMENT: - return DOFDescriptor(None, granularity=desc) + return DOFDescriptor(granularity=desc) - return DOFDescriptor(desc) + return DOFDescriptor(geometry=desc) # }}} diff --git a/test/test_matrix.py b/test/test_matrix.py index 1800cec2..1d2f8d89 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -354,9 +354,11 @@ def test_qbx_block_builder(ctx_factory, factor, ndim, lpot_id, # NOTE: NearFieldBlockBuilder only does stage1/stage1 or stage2/stage2, # so we need to hardcode the discr for MatrixBuilder too, since the # defaults are different - source_dd = sym.DOFDescriptor(sym.DEFAULT_SOURCE, + source_dd = sym.DOFDescriptor( + geometry=sym.DEFAULT_SOURCE, discr=sym.QBX_SOURCE_STAGE2) - target_dd = sym.DOFDescriptor(sym.DEFAULT_TARGET, + target_dd = sym.DOFDescriptor( + geometry=sym.DEFAULT_TARGET, discr=sym.QBX_SOURCE_STAGE2) place_ids = (source_dd, target_dd) @@ -430,8 +432,8 @@ def test_build_matrix_places(ctx_factory, place_ids, visualize=False): qbx_forced_limit=qbx_forced_limit) place_ids = ( - sym.as_dofdesc(place_ids[0]).copy(where=sym.DEFAULT_SOURCE), - sym.as_dofdesc(place_ids[1]).copy(where=sym.DEFAULT_TARGET) + sym.as_dofdesc(place_ids[0]).copy(geometry=sym.DEFAULT_SOURCE), + sym.as_dofdesc(place_ids[1]).copy(geometry=sym.DEFAULT_TARGET) ) from pytential.symbolic.execution import GeometryCollection diff --git a/test/test_symbolic.py b/test/test_symbolic.py index c1c130f0..54fbb738 100644 --- a/test/test_symbolic.py +++ b/test/test_symbolic.py @@ -229,10 +229,12 @@ def test_interpolation(ctx_factory, name, source_discr, target_granularity): fmm_order=False).with_refinement() where = 'test-interpolation' - source = sym.DOFDescriptor(where, + source = sym.DOFDescriptor( + geometry=where, discr=source_discr, granularity=sym.GRANULARITY_NODE) - target = sym.DOFDescriptor(where, + target = sym.DOFDescriptor( + geometry=where, discr=sym.QBX_SOURCE_QUAD_STAGE2, granularity=target_granularity) -- GitLab From c1f90d026c3dd2fa04e6dd57ac943e9019fcec0f Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sun, 28 Jul 2019 10:39:49 -0500 Subject: [PATCH 80/96] rename DOFDescriptor.discr to discr_stage --- pytential/symbolic/dof_connection.py | 14 ++--- pytential/symbolic/execution.py | 8 +-- pytential/symbolic/mappers.py | 8 +-- pytential/symbolic/matrix.py | 8 +-- pytential/symbolic/primitives.py | 79 +++++++++++++++------------- test/test_matrix.py | 6 +-- test/test_symbolic.py | 12 ++--- 7 files changed, 69 insertions(+), 66 deletions(-) diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 4f6771e8..810d254f 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -213,21 +213,21 @@ def connection_from_dds(places, from_dd, to_dd): raise ValueError("can only interpolate from `GRANULARITY_NODE`") connections = [] - if from_dd.discr is not to_dd.discr: + if from_dd.discr_stage is not to_dd.discr_stage: from pytential.qbx import QBXLayerPotentialSource if not isinstance(from_discr, QBXLayerPotentialSource): raise ValueError("can only interpolate on a " "`QBXLayerPotentialSource`") - if to_dd.discr is not sym.QBX_SOURCE_QUAD_STAGE2: + if to_dd.discr_stage is not sym.QBX_SOURCE_QUAD_STAGE2: # TODO: can probably extend this to project from a QUAD_STAGE2 # using L2ProjectionInverseDiscretizationConnection raise ValueError("can only interpolate to " "`QBX_SOURCE_QUAD_STAGE2`") - if from_dd.discr is sym.QBX_SOURCE_QUAD_STAGE2: + if from_dd.discr_stage == sym.QBX_SOURCE_QUAD_STAGE2: pass - elif from_dd.discr is sym.QBX_SOURCE_STAGE2: + elif from_dd.discr_stage == sym.QBX_SOURCE_STAGE2: connections.append( from_discr.refined_interp_to_ovsmp_quad_connection) else: @@ -236,11 +236,11 @@ def connection_from_dds(places, from_dd, to_dd): if from_dd.granularity is not to_dd.granularity: to_discr = places.get_discretization(to_dd) - if to_dd.granularity is sym.GRANULARITY_NODE: + if to_dd.granularity == sym.GRANULARITY_NODE: pass - elif to_dd.granularity is sym.GRANULARITY_CENTER: + elif to_dd.granularity == sym.GRANULARITY_CENTER: connections.append(CenterGranularityConnection(to_discr)) - elif to_dd.granularity is sym.GRANULARITY_ELEMENT: + elif to_dd.granularity == sym.GRANULARITY_ELEMENT: raise ValueError("Creating a connection to element granularity " "is not allowed. Use Elementwise{Max,Min,Sum}.") else: diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 781555f6..acbbc50e 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -149,11 +149,11 @@ class EvaluationMapper(EvaluationMapperBase): assert operand.shape == (discr.nnodes,) where = sym.as_dofdesc(expr.where) - if where.granularity is sym.GRANULARITY_NODE: + if where.granularity == sym.GRANULARITY_NODE: return _reduce(discr.nnodes, node_knl(), lambda g, x: discr.groups[g].view(x)) - elif where.granularity is sym.GRANULARITY_ELEMENT: + elif where.granularity == sym.GRANULARITY_ELEMENT: return _reduce(discr.mesh.nelements, element_knl(), lambda g, x: mesh_el_view(discr.mesh, g, x)) @@ -491,9 +491,9 @@ class GeometryCollection(object): def _get_lpot_discretization(self, lpot, dofdesc): dofdesc = sym.as_dofdesc(dofdesc) - if dofdesc.discr is sym.QBX_SOURCE_STAGE2: + if dofdesc.discr_stage == sym.QBX_SOURCE_STAGE2: return lpot.stage2_density_discr - if dofdesc.discr is sym.QBX_SOURCE_QUAD_STAGE2: + if dofdesc.discr_stage == sym.QBX_SOURCE_QUAD_STAGE2: return lpot.quad_stage2_density_discr return lpot.density_discr diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 1e8fa1cb..3bc719d9 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -215,8 +215,8 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): def _as_dofdesc(self, dofdesc): dofdesc = prim.as_dofdesc(dofdesc) if dofdesc.geometry is None: - if dofdesc.discr is None \ - and dofdesc.granularity is prim.GRANULARITY_NODE: + if dofdesc.discr_stage is None \ + and dofdesc.granularity == prim.GRANULARITY_NODE: dofdesc = dofdesc.copy(geometry=self.default_where) else: dofdesc = dofdesc.copy(geometry=self.default_source) @@ -417,7 +417,7 @@ class QBXInterpolationPreprocessor(IdentityMapper): def map_int_g(self, expr): source = prim.as_dofdesc(expr.source) - if source.discr is prim.QBX_SOURCE_QUAD_STAGE2: + if source.discr_stage == prim.QBX_SOURCE_QUAD_STAGE2: return expr lpot_source = self.places[source] @@ -425,7 +425,7 @@ class QBXInterpolationPreprocessor(IdentityMapper): if not isinstance(lpot_source, QBXLayerPotentialSource): return expr - target = source.copy(discr=prim.QBX_SOURCE_QUAD_STAGE2) + target = source.copy(discr_stage=prim.QBX_SOURCE_QUAD_STAGE2) density = prim.Interpolation( source, target, self.rec(expr.density)) kernel_arguments = dict( diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index 5b60fb31..a970d545 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -401,8 +401,8 @@ class MatrixBuilder(MatrixBuilderBase): def map_int_g(self, expr): source_dd = sym.as_dofdesc(expr.source) target_dd = sym.as_dofdesc(expr.target) - if source_dd.discr is None: - source_dd = source_dd.copy(discr=sym.QBX_SOURCE_QUAD_STAGE2) + if source_dd.discr_stage is None: + source_dd = source_dd.copy(discr_stage=sym.QBX_SOURCE_QUAD_STAGE2) lpot_source = self.places[source_dd] source_discr = self.places.get_discretization(source_dd) @@ -417,7 +417,7 @@ class MatrixBuilder(MatrixBuilderBase): raise NotImplementedError("layer potentials on non-variables") kernel = expr.kernel - if source_dd.discr == target_dd.discr: + if source_dd.discr_stage == target_dd.discr_stage: # NOTE: passing None to avoid any resampling kernel_args = _get_layer_potential_args(self, expr, None) else: @@ -445,7 +445,7 @@ class MatrixBuilder(MatrixBuilderBase): waa = _get_weights_and_area_elements(self.queue, lpot_source, source_discr) mat[:, :] *= waa.get(self.queue) - if source_dd.discr != target_dd.discr: + if source_dd.discr_stage != target_dd.discr_stage: # NOTE: we only resample sources assert target_discr.nnodes < source_discr.nnodes diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 31ebf6b7..72362ed2 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -233,12 +233,12 @@ class QBX_SOURCE_QUAD_STAGE2: # noqa: N801 class GRANULARITY_NODE: # noqa: N801 - """DOFs are per-source node.""" + """DOFs are per node.""" pass class GRANULARITY_CENTER: # noqa: N801 - """DOFs interleaved per expansion center.""" + """DOFs interleaved per expansion center (two per node, one on each side).""" pass @@ -264,7 +264,7 @@ class DOFDescriptor(object): :class:`~pytential.target.TargetBase` or :class:`~meshmode.discretization.Discretization`. - .. attribute:: discr + .. attribute:: discr_stage Specific to a :class:`pytential.source.LayerPotentialSourceBase`, this discribes on which of the discretizations the @@ -279,28 +279,29 @@ class DOFDescriptor(object): :class:`GRANULARITY_ELEMENT` (one DOF per element). """ - def __init__(self, geometry=None, discr=None, granularity=None): + def __init__(self, geometry=None, discr_stage=None, granularity=None): if granularity is None: granularity = GRANULARITY_NODE - if discr is not None: - if not (discr is QBX_SOURCE_STAGE1 - or discr is QBX_SOURCE_STAGE2 - or discr is QBX_SOURCE_QUAD_STAGE2): - raise ValueError('unknown discr tag: "{}"'.format(discr)) + if discr_stage is not None: + if not (discr_stage == QBX_SOURCE_STAGE1 + or discr_stage == QBX_SOURCE_STAGE2 + or discr_stage == QBX_SOURCE_QUAD_STAGE2): + raise ValueError('unknown discr stage tag: "{}"'.format(discr_stage)) - if not (granularity is GRANULARITY_NODE - or granularity is GRANULARITY_CENTER - or granularity is GRANULARITY_ELEMENT): + if not (granularity == GRANULARITY_NODE + or granularity == GRANULARITY_CENTER + or granularity == GRANULARITY_ELEMENT): raise ValueError('unknown granularity: "{}"'.format(granularity)) self.geometry = geometry - self.discr = discr + self.discr_stage = discr_stage self.granularity = granularity - def copy(self, geometry=None, discr=None, granularity=None): + def copy(self, geometry=None, discr_stage=None, granularity=None): if isinstance(geometry, DOFDescriptor): - discr = geometry.discr if discr is None else discr + discr_stage = geometry.discr_stage \ + if discr_stage is None else discr_stage geometry = geometry.geometry return type(self)( @@ -308,47 +309,49 @@ class DOFDescriptor(object): if geometry is None else geometry), granularity=(self.granularity if granularity is None else granularity), - discr=(self.discr - if discr is None else discr)) + discr_stage=(self.discr_stage + if discr_stage is None else discr_stage)) def __hash__(self): - return hash((type(self), self.geometry, self.discr, self.granularity)) + return hash((type(self), + self.geometry, self.discr_stage, self.granularity)) def __eq__(self, other): return (type(self) is type(other) and self.geometry == other.geometry - and self.discr == other.discr + and self.discr_stage == other.discr_stage and self.granularity == other.granularity) def __ne__(self, other): return not self.__eq__(other) def __repr__(self): - return '{}(geometry={}, discr={}, granularity={})'.format( + return '{}(geometry={}, stage={}, granularity={})'.format( type(self).__name__, self.geometry, - self.discr if self.discr is None else self.discr.__name__, + self.discr_stage \ + if self.discr_stage is None else self.discr_stage.__name__, self.granularity.__name__) def __str__(self): name = [] if self.geometry is None: name.append("?") - elif self.geometry is DEFAULT_SOURCE: + elif self.geometry == DEFAULT_SOURCE: name.append("s") - elif self.geometry is DEFAULT_TARGET: + elif self.geometry == DEFAULT_TARGET: name.append("t") else: name.append(str(self.geometry)) - if self.discr is QBX_SOURCE_STAGE2: + if self.discr_stage == QBX_SOURCE_STAGE2: name.append("stage2") - elif self.discr is QBX_SOURCE_QUAD_STAGE2: + elif self.discr_stage == QBX_SOURCE_QUAD_STAGE2: name.append("quads2") - if self.granularity is GRANULARITY_CENTER: + if self.granularity == GRANULARITY_CENTER: name.append("center") - elif self.granularity is GRANULARITY_ELEMENT: + elif self.granularity == GRANULARITY_ELEMENT: name.append("panel") return "/".join(name) @@ -358,14 +361,14 @@ def as_dofdesc(desc): if isinstance(desc, DOFDescriptor): return desc - if desc is QBX_SOURCE_STAGE1 \ - or desc is QBX_SOURCE_STAGE2 \ - or desc is QBX_SOURCE_QUAD_STAGE2: - return DOFDescriptor(discr=desc) + if desc == QBX_SOURCE_STAGE1 \ + or desc == QBX_SOURCE_STAGE2 \ + or desc == QBX_SOURCE_QUAD_STAGE2: + return DOFDescriptor(discr_stage=desc) - if desc is GRANULARITY_NODE \ - or desc is GRANULARITY_CENTER \ - or desc is GRANULARITY_ELEMENT: + if desc == GRANULARITY_NODE \ + or desc == GRANULARITY_CENTER \ + or desc == GRANULARITY_ELEMENT: return DOFDescriptor(granularity=desc) return DOFDescriptor(geometry=desc) @@ -928,8 +931,8 @@ def _quad_resolution(ambient_dim, dim=None, granularity=None, where=None): def _source_danger_zone_radii(ambient_dim, dim=None, granularity=None, where=None): where = as_dofdesc(where) - if where.discr is None: - where = where.copy(discr=QBX_SOURCE_STAGE2) + if where.discr_stage is None: + where = where.copy(discr_stage=QBX_SOURCE_STAGE2) # This should be the expression of the expansion radii, but # @@ -988,11 +991,11 @@ def h_max(ambient_dim, dim=None, where=None): def weights_and_area_elements(ambient_dim, dim=None, where=None): where = as_dofdesc(where) - if where.discr is QBX_SOURCE_QUAD_STAGE2: + if where.discr_stage == QBX_SOURCE_QUAD_STAGE2: # quad_stage2_density_discr is not guaranteed to be usable for # interpolation/differentiation. Use stage2_density_discr to find # area elements instead, then upsample that. - source = where.copy(discr=QBX_SOURCE_STAGE2) + source = where.copy(discr_stage=QBX_SOURCE_STAGE2) area = Interpolation(source, where, area_element(ambient_dim, dim=dim, where=source)) else: diff --git a/test/test_matrix.py b/test/test_matrix.py index 1d2f8d89..9fe20fa2 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -356,10 +356,10 @@ def test_qbx_block_builder(ctx_factory, factor, ndim, lpot_id, # defaults are different source_dd = sym.DOFDescriptor( geometry=sym.DEFAULT_SOURCE, - discr=sym.QBX_SOURCE_STAGE2) + discr_stage=sym.QBX_SOURCE_STAGE2) target_dd = sym.DOFDescriptor( geometry=sym.DEFAULT_TARGET, - discr=sym.QBX_SOURCE_STAGE2) + discr_stage=sym.QBX_SOURCE_STAGE2) place_ids = (source_dd, target_dd) from pytential.symbolic.execution import GeometryCollection, _prepare_expr @@ -477,7 +477,7 @@ def test_build_matrix_places(ctx_factory, place_ids, visualize=False): index_set=index_set, context={}) mat = mbuilder(op) - if place_ids[0].discr is not None: + if place_ids[0].discr_stage is not None: assert _max_block_error(qbx_mat, mat, index_set.get(queue)) < 1.0e-14 from pytential.symbolic.matrix import FarFieldBlockBuilder diff --git a/test/test_symbolic.py b/test/test_symbolic.py index 54fbb738..8f550b67 100644 --- a/test/test_symbolic.py +++ b/test/test_symbolic.py @@ -201,14 +201,14 @@ def test_expr_pickling(): # }}} -@pytest.mark.parametrize(("name", "source_discr", "target_granularity"), [ +@pytest.mark.parametrize(("name", "source_discr_stage", "target_granularity"), [ ("default", None, None), ("default-explicit", sym.QBX_SOURCE_STAGE1, sym.GRANULARITY_NODE), ("stage2", sym.QBX_SOURCE_STAGE2, sym.GRANULARITY_NODE), ("stage2-center", sym.QBX_SOURCE_STAGE2, sym.GRANULARITY_CENTER), ("quad", sym.QBX_SOURCE_QUAD_STAGE2, sym.GRANULARITY_NODE) ]) -def test_interpolation(ctx_factory, name, source_discr, target_granularity): +def test_interpolation(ctx_factory, name, source_discr_stage, target_granularity): ctx = ctx_factory() queue = cl.CommandQueue(ctx) @@ -231,11 +231,11 @@ def test_interpolation(ctx_factory, name, source_discr, target_granularity): where = 'test-interpolation' source = sym.DOFDescriptor( geometry=where, - discr=source_discr, + discr_stage=source_discr_stage, granularity=sym.GRANULARITY_NODE) target = sym.DOFDescriptor( geometry=where, - discr=sym.QBX_SOURCE_QUAD_STAGE2, + discr_stage=sym.QBX_SOURCE_QUAD_STAGE2, granularity=target_granularity) sigma_sym = sym.var("sigma") @@ -243,9 +243,9 @@ def test_interpolation(ctx_factory, name, source_discr, target_granularity): bound_op = bind(qbx, op_sym, auto_where=where) target_nodes = qbx.quad_stage2_density_discr.nodes().get(queue) - if source_discr is sym.QBX_SOURCE_STAGE2: + if source_discr_stage == sym.QBX_SOURCE_STAGE2: source_nodes = qbx.stage2_density_discr.nodes().get(queue) - elif source_discr is sym.QBX_SOURCE_QUAD_STAGE2: + elif source_discr_stage == sym.QBX_SOURCE_QUAD_STAGE2: source_nodes = target_nodes else: source_nodes = qbx.density_discr.nodes().get(queue) -- GitLab From 46a63b62387bb2926178a39203575e6d7497fe5e Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sun, 28 Jul 2019 10:48:34 -0500 Subject: [PATCH 81/96] change some remaining where into dofdesc --- pytential/symbolic/pde/maxwell/__init__.py | 4 ++-- pytential/symbolic/pde/scalar.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pytential/symbolic/pde/maxwell/__init__.py b/pytential/symbolic/pde/maxwell/__init__.py index 0b038e95..f9c0b68e 100644 --- a/pytential/symbolic/pde/maxwell/__init__.py +++ b/pytential/symbolic/pde/maxwell/__init__.py @@ -70,7 +70,7 @@ def get_sym_maxwell_point_source(kernel, jxyz, k): # {{{ plane wave -def get_sym_maxwell_plane_wave(amplitude_vec, v, omega, epsilon=1, mu=1, where=None): +def get_sym_maxwell_plane_wave(amplitude_vec, v, omega, epsilon=1, mu=1, dofdesc=None): r"""Return a symbolic expression that, when bound to a :class:`pytential.source.PointPotentialSource` will yield a field satisfying Maxwell's equations. @@ -96,7 +96,7 @@ def get_sym_maxwell_plane_wave(amplitude_vec, v, omega, epsilon=1, mu=1, where=N # NOTE: for complex, need to ensure real(n).dot(imag(n)) = 0 (7.15) - x = sym.nodes(3, dofdesc=where).as_vector() + x = sym.nodes(3, dofdesc=dofdesc).as_vector() v_mag_squared = sym.cse(np.dot(v, v), "v_mag_squared") n = v/sym.sqrt(v_mag_squared) diff --git a/pytential/symbolic/pde/scalar.py b/pytential/symbolic/pde/scalar.py index 032c68ee..882b1b3c 100644 --- a/pytential/symbolic/pde/scalar.py +++ b/pytential/symbolic/pde/scalar.py @@ -48,16 +48,16 @@ class L2WeightedPDEOperator(object): warn("should use L2 weighting in %s" % type(self).__name__, stacklevel=3) - def get_weight(self, where=None): + def get_weight(self, dofdesc=None): if self.use_l2_weighting: - return cse(area_element(self.kernel.dim, dofdesc=where) - * QWeight(dofdesc=where)) + return cse(area_element(self.kernel.dim, dofdesc=dofdesc) + * QWeight(dofdesc=dofdesc)) else: return 1 - def get_sqrt_weight(self, where=None): + def get_sqrt_weight(self, dofdesc=None): if self.use_l2_weighting: - return sqrt_jac_q_weight(self.kernel.dim, dofdesc=where) + return sqrt_jac_q_weight(self.kernel.dim, dofdesc=dofdesc) else: return 1 -- GitLab From caf31175eefe0e43bab18a5cd2354301cfb479e5 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sun, 28 Jul 2019 13:28:31 -0500 Subject: [PATCH 82/96] docs and cleanup --- pytential/symbolic/execution.py | 7 ++- pytential/symbolic/mappers.py | 102 ++++++++++++++++++++----------- pytential/symbolic/matrix.py | 2 +- pytential/symbolic/primitives.py | 9 ++- test/test_matrix.py | 91 ++++++++++++++++----------- 5 files changed, 133 insertions(+), 78 deletions(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index f263ce0f..f1f9fba1 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -372,7 +372,7 @@ def _prepare_domains(nresults, places, domains, default_domain): return domains -def _prepare_expr(places, expr, interpolate=True): +def _prepare_expr(places, expr): """ :arg places: :class:`pytential.symbolic.execution.GeometryCollection`. :arg expr: a symbolic expression. @@ -393,8 +393,9 @@ def _prepare_expr(places, expr, interpolate=True): if isinstance(place, LayerPotentialSourceBase): expr = place.preprocess_optemplate(name, places, expr) - if interpolate: - expr = InterpolationPreprocessor(places)(expr) + # NOTE: only insert interpolation operators after the layer potential + # operators were preprocessed to avoid any confusion + expr = InterpolationPreprocessor(places)(expr) return expr # }}} diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index d02f1a07..f7f00480 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -152,7 +152,7 @@ class DependencyMapper(DependencyMapperBase, Collector): class EvaluationMapper(EvaluationMapperBase): """Unlike :mod:`pymbolic.mapper.evaluation.EvaluationMapper`, this class does evaluation mostly to get :class:`pymbolic.geometric_algebra.MultiVector` - instances to to do their thing, and perhaps to automatically kill terms + instances to do their thing, and perhaps to automatically kill terms that are multiplied by zero. Otherwise it intends to largely preserve the structure of the input expression. """ @@ -199,7 +199,7 @@ class EvaluationMapper(EvaluationMapperBase): expr.scope) -# {{{ target/source tagging +# {{{ dofdesc tagging class LocationTagger(CSECachingMapperMixin, IdentityMapper): """Used internally by :class:`ToTargetTagger`. Tags all src/target taggable @@ -315,6 +315,46 @@ class ToTargetTagger(LocationTagger): self.operand_rec = LocationTagger(default_source, default_source=default_source) + +class DiscretizationStageTagger(IdentityMapper): + """Descends into an expression tree and changes the + :attr:`~pytential.symbolic.primitives.DOFDescriptor.discr_stage` to + :attr:`discr_stage`. + + .. attribute:: discr_stage + + The new discretization for the DOFs in the expression. For valid + values, see + :attr:`~pytential.symbolic.primitives.DOFDescriptor.discr_stage`. + """ + + def __init__(self, discr_stage): + if not (discr_stage == prim.QBX_SOURCE_STAGE1 + or discr_stage == prim.QBX_SOURCE_STAGE2 + or discr_stage == prim.QBX_SOURCE_QUAD_STAGE2): + raise ValueError('unknown discr stage tag: "{}"'.format(discr_stage)) + + self.discr_stage = discr_stage + + def map_node_coordinate_component(self, expr): + dofdesc = prim.as_dofdesc(expr.where) + if dofdesc.discr_stage == self.discr_stage: + return expr + + return type(expr)( + expr.ambient_axis, + dofdesc.copy(discr_stage=self.discr_stage)) + + def map_num_reference_derivative(self, expr): + dofdesc = prim.as_dofdesc(expr.where) + if dofdesc.discr_stage == self.discr_stage: + return expr + + return type(expr)( + expr.ref_axes, + self.rec(expr.operand), + dofdesc.copy(discr_stage=self.discr_stage)) + # }}} @@ -409,41 +449,29 @@ class UnregularizedPreprocessor(IdentityMapper): # }}} -# {{{ QBX preprocessor - -class InterpolationDiscretizationTagger(IdentityMapper): - def __init__(self, discr): - self.discr = discr - - def map_node_coordinate_component(self, expr): - dd = prim.as_dofdesc(expr.where) - if dd.discr is self.discr: - return expr - else: - return type(expr)( - expr.ambient_axis, - dd.copy(discr=self.discr)) - - def map_num_reference_derivative(self, expr): - dd = prim.as_dofdesc(expr.where) - if dd.discr is self.discr: - return expr - else: - return type(expr)( - expr.ref_axes, - self.rec(expr.operand), - dd.copy(discr=self.discr)) - +# {{{ interpolation preprocessor class InterpolationPreprocessor(IdentityMapper): - def __init__(self, places, from_discr=None): + """Handle expressions that require upsampling or downsampling by inserting + a :class:`~pytential.symbolic.primitives.Interpolation`. This is used to + + * do differentiation on + :attr:`~pytential.source.LayerPotentialSource.quad_stage2_density_discr`, + by performing it on + :attr:`~pytential.source.LayerPotentialSource.stage2_density_discr` and + upsampling. + * upsample layer potential sources to + :attr:`~pytential.source.LayerPotentialSource.quad_stage2_density_discr`, + """ + + def __init__(self, places): self.places = places - self.from_discr = from_discr or prim.QBX_SOURCE_STAGE2 - self.tagger = InterpolationDiscretizationTagger(self.from_discr) + self.from_discr_stage = prim.QBX_SOURCE_STAGE2 + self.tagger = DiscretizationStageTagger(self.from_discr_stage) def map_num_reference_derivative(self, expr): target_dd = prim.as_dofdesc(expr.where) - if target_dd.discr is not prim.QBX_SOURCE_QUAD_STAGE2: + if target_dd.discr_stage != prim.QBX_SOURCE_QUAD_STAGE2: return expr from pytential.qbx import QBXLayerPotentialSource @@ -451,7 +479,7 @@ class InterpolationPreprocessor(IdentityMapper): if not isinstance(lpot_source, QBXLayerPotentialSource): return expr - source_dd = target_dd.copy(discr=self.from_discr) + source_dd = target_dd.copy(discr_stage=self.from_discr_stage) return prim.Interpolation( source_dd, target_dd, @@ -459,11 +487,11 @@ class InterpolationPreprocessor(IdentityMapper): def map_int_g(self, expr): source = prim.as_dofdesc(expr.source) - if source.discr_stage == prim.QBX_SOURCE_QUAD_STAGE2: + if source.discr_stage is not None: return expr from pytential.qbx import QBXLayerPotentialSource - lpot_source = self.places[source_dd] + lpot_source = self.places[source] if not isinstance(lpot_source, QBXLayerPotentialSource): return expr @@ -479,9 +507,13 @@ class InterpolationPreprocessor(IdentityMapper): kernel=expr.kernel, density=density, kernel_arguments=kernel_arguments, - source=target_dd, + source=target, target=expr.target) +# }}} + + +# {{{ QBX preprocessor class QBXPreprocessor(IdentityMapper): def __init__(self, source_name, places): diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index 42f622f8..a73e0c4d 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -370,7 +370,7 @@ class MatrixBuilder(MatrixBuilderBase): def map_interpolation(self, expr): source_dd = sym.as_dofdesc(expr.source) target_dd = sym.as_dofdesc(expr.target) - if target_dd.discr is not sym.QBX_SOURCE_QUAD_STAGE2: + if target_dd.discr_stage != sym.QBX_SOURCE_QUAD_STAGE2: raise RuntimeError("can only interpolate to QBX_SOURCE_QUAD_STAGE2") operand = self.rec(expr.operand) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 6a0e4cbb..aa730beb 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -326,12 +326,11 @@ class DOFDescriptor(object): return not self.__eq__(other) def __repr__(self): + discr_stage = self.discr_stage \ + if self.discr_stage is None else self.discr_stage.__name__, + granularity = self.granularity.__name__ return '{}(geometry={}, stage={}, granularity={})'.format( - type(self).__name__, - self.geometry, - self.discr_stage \ - if self.discr_stage is None else self.discr_stage.__name__, - self.granularity.__name__) + type(self).__name__, self.geometry, discr_stage, granularity) def __str__(self): name = [] diff --git a/test/test_matrix.py b/test/test_matrix.py index 16b691c2..64b001bb 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -122,6 +122,8 @@ def _build_block_index(discr, nblks=10, factor=1.0): def _build_op(lpot_id, k=0, ndim=2, + source=sym.DEFAULT_SOURCE, + target=sym.DEFAULT_TARGET, qbx_forced_limit="avg"): from sumpy.kernel import LaplaceKernel, HelmholtzKernel @@ -132,7 +134,10 @@ def _build_op(lpot_id, knl = LaplaceKernel(ndim) knl_kwargs = {} - lpot_kwargs = {"qbx_forced_limit": qbx_forced_limit} + lpot_kwargs = { + "qbx_forced_limit": qbx_forced_limit, + "source": source, + "target": target} lpot_kwargs.update(knl_kwargs) if lpot_id == 1: # scalar single-layer potential @@ -278,23 +283,33 @@ def test_p2p_block_builder(ctx_factory, factor, ndim, lpot_id, from sympy.core.cache import clear_cache clear_cache() + place_ids = ( + sym.DOFDescriptor( + geometry=sym.DEFAULT_SOURCE, + discr_stage=sym.QBX_SOURCE_STAGE1), + sym.DOFDescriptor( + geometry=sym.DEFAULT_TARGET, + discr_stage=sym.QBX_SOURCE_STAGE1), + ) target_order = 2 if ndim == 3 else 7 + qbx = _build_qbx_discr(queue, target_order=target_order, ndim=ndim) - op, u_sym, _ = _build_op(lpot_id, ndim=ndim) + op, u_sym, _ = _build_op(lpot_id, ndim=ndim, + source=place_ids[0], + target=place_ids[1]) index_set = _build_block_index(qbx.density_discr, factor=factor) from pytential.symbolic.execution import GeometryCollection - from pytential.symbolic.execution import _prepare_expr, _prepare_domains - places = GeometryCollection(qbx) - expr = _prepare_expr(places, op, interpolate=False) - domains = _prepare_domains(1, places, None, sym.DEFAULT_SOURCE) + from pytential.symbolic.execution import _prepare_expr + places = GeometryCollection(qbx, auto_where=place_ids) + expr = _prepare_expr(places, op) from pytential.symbolic.matrix import P2PMatrixBuilder mbuilder = P2PMatrixBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[domains[0]], - dep_discr=places.get_discretization(domains[0]), + dep_source=places[place_ids[0]], + dep_discr=places.get_discretization(place_ids[0]), places=places, context={}, exclude_self=True) @@ -304,8 +319,8 @@ def test_p2p_block_builder(ctx_factory, factor, ndim, lpot_id, mbuilder = FarFieldBlockBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[domains[0]], - dep_discr=places.get_discretization(domains[0]), + dep_source=places[place_ids[0]], + dep_discr=places.get_discretization(place_ids[0]), places=places, index_set=index_set, context={}, @@ -347,24 +362,25 @@ def test_qbx_block_builder(ctx_factory, factor, ndim, lpot_id, from sympy.core.cache import clear_cache clear_cache() + place_ids = ( + sym.DOFDescriptor( + geometry=sym.DEFAULT_SOURCE, + discr_stage=sym.QBX_SOURCE_STAGE2), + sym.DOFDescriptor( + geometry=sym.DEFAULT_TARGET, + discr_stage=sym.QBX_SOURCE_STAGE2), + ) target_order = 2 if ndim == 3 else 7 + qbx = _build_qbx_discr(queue, target_order=target_order, ndim=ndim) - op, u_sym, _ = _build_op(lpot_id, ndim=ndim) - - # NOTE: NearFieldBlockBuilder only does stage1/stage1 or stage2/stage2, - # so we need to hardcode the discr for MatrixBuilder too, since the - # defaults are different - source_dd = sym.DOFDescriptor( - geometry=sym.DEFAULT_SOURCE, - discr_stage=sym.QBX_SOURCE_STAGE2) - target_dd = sym.DOFDescriptor( - geometry=sym.DEFAULT_TARGET, - discr_stage=sym.QBX_SOURCE_STAGE2) - place_ids = (source_dd, target_dd) + op, u_sym, _ = _build_op(lpot_id, ndim=ndim, + source=place_ids[0], + target=place_ids[1], + qbx_forced_limit="avg") from pytential.symbolic.execution import GeometryCollection, _prepare_expr places = GeometryCollection(qbx, auto_where=place_ids) - expr = _prepare_expr(places, op, interpolate=False) + expr = _prepare_expr(places, op) density_discr = places.get_discretization(place_ids[0]) index_set = _build_block_index(density_discr, factor=factor) @@ -412,11 +428,11 @@ def test_qbx_block_builder(ctx_factory, factor, ndim, lpot_id, assert _max_block_error(mat, blk, index_set) < 1.0e-14 -@pytest.mark.parametrize('place_ids', - [(None, None), - (sym.QBX_SOURCE_STAGE1, sym.QBX_SOURCE_STAGE1), +@pytest.mark.parametrize(('source_discr_stage', 'target_discr_stage'), + [(sym.QBX_SOURCE_STAGE1, sym.QBX_SOURCE_STAGE1), (sym.QBX_SOURCE_QUAD_STAGE2, sym.QBX_SOURCE_QUAD_STAGE2)]) -def test_build_matrix_places(ctx_factory, place_ids, visualize=False): +def test_build_matrix_places(ctx_factory, + source_discr_stage, target_discr_stage, visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) @@ -424,18 +440,25 @@ def test_build_matrix_places(ctx_factory, place_ids, visualize=False): from sympy.core.cache import clear_cache clear_cache() + qbx_forced_limit = -1 + place_ids = ( + sym.DOFDescriptor( + geometry=sym.DEFAULT_SOURCE, + discr_stage=source_discr_stage), + sym.DOFDescriptor( + geometry=sym.DEFAULT_TARGET, + discr_stage=target_discr_stage), + ) + + # build test operators qbx = _build_qbx_discr(queue, nelements=8, target_order=2, ndim=2, curve_f=partial(ellipse, 1.0)) - qbx_forced_limit = -1 op, u_sym, _ = _build_op(lpot_id=1, ndim=2, + source=place_ids[0], + target=place_ids[1], qbx_forced_limit=qbx_forced_limit) - place_ids = ( - sym.as_dofdesc(place_ids[0]).copy(geometry=sym.DEFAULT_SOURCE), - sym.as_dofdesc(place_ids[1]).copy(geometry=sym.DEFAULT_TARGET) - ) - from pytential.symbolic.execution import GeometryCollection places = GeometryCollection(qbx, auto_where=place_ids) source_discr = places.get_discretization(place_ids[0]) @@ -444,7 +467,7 @@ def test_build_matrix_places(ctx_factory, place_ids, visualize=False): index_set = _build_block_index(source_discr, factor=0.6) from pytential.symbolic.execution import _prepare_expr - op = _prepare_expr(places, op, interpolate=False) + op = _prepare_expr(places, op) # build full QBX matrix from pytential.symbolic.matrix import MatrixBuilder -- GitLab From d1fb840f059c2d38cc6264ba17c55f0e75bfd2cf Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sun, 28 Jul 2019 13:30:20 -0500 Subject: [PATCH 83/96] flake8 --- pytential/symbolic/pde/maxwell/__init__.py | 3 ++- pytential/symbolic/primitives.py | 9 ++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pytential/symbolic/pde/maxwell/__init__.py b/pytential/symbolic/pde/maxwell/__init__.py index f9c0b68e..d89d393c 100644 --- a/pytential/symbolic/pde/maxwell/__init__.py +++ b/pytential/symbolic/pde/maxwell/__init__.py @@ -70,7 +70,8 @@ def get_sym_maxwell_point_source(kernel, jxyz, k): # {{{ plane wave -def get_sym_maxwell_plane_wave(amplitude_vec, v, omega, epsilon=1, mu=1, dofdesc=None): +def get_sym_maxwell_plane_wave(amplitude_vec, v, omega, + epsilon=1, mu=1, dofdesc=None): r"""Return a symbolic expression that, when bound to a :class:`pytential.source.PointPotentialSource` will yield a field satisfying Maxwell's equations. diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 6928a631..8bcd6ea3 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -362,12 +362,11 @@ class DOFDescriptor(object): return not self.__eq__(other) def __repr__(self): + discr_stage = self.discr_stage \ + if self.discr_stage is None else self.discr_stage.__name__, + granularity = self.granularity.__name__ return '{}(geometry={}, stage={}, granularity={})'.format( - type(self).__name__, - self.geometry, - self.discr_stage \ - if self.discr_stage is None else self.discr_stage.__name__, - self.granularity.__name__) + type(self).__name__, self.geometry, discr_stage, granularity) def __str__(self): name = [] -- GitLab From 53e71bb08480bb97f55373ef69e74b501cf8e901 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sun, 28 Jul 2019 13:32:21 -0500 Subject: [PATCH 84/96] flake8 --- pytential/symbolic/primitives.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 72362ed2..0d356588 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -326,12 +326,11 @@ class DOFDescriptor(object): return not self.__eq__(other) def __repr__(self): + discr_stage = self.discr_stage \ + if self.discr_stage is None else self.discr_stage.__name__, + granularity = self.granularity.__name__ return '{}(geometry={}, stage={}, granularity={})'.format( - type(self).__name__, - self.geometry, - self.discr_stage \ - if self.discr_stage is None else self.discr_stage.__name__, - self.granularity.__name__) + type(self).__name__, self.geometry, discr_stage, granularity) def __str__(self): name = [] -- GitLab From 3969146f3bd958fe6ad6ef2dc659b90a1c57dfb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kl=C3=B6ckner?= Date: Mon, 29 Jul 2019 05:10:27 +0200 Subject: [PATCH 85/96] Fix where->dofdesc missed in merge --- pytential/symbolic/primitives.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 330609ee..827be501 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -1061,7 +1061,7 @@ def h_max(ambient_dim, dim=None, dofdesc=None): @_deprecate_kwargs('where', 'dofdesc') def weights_and_area_elements(ambient_dim, dim=None, dofdesc=None): - area = area_element(ambient_dim, dim=dim, where=where) + area = area_element(ambient_dim, dim=dim, dofdesc=dofdesc) return cse(area * QWeight(dofdesc=dofdesc), "weights_area_elements", cse_scope.DISCRETIZATION) -- GitLab From ef2c4ac7c4e04cbe4a9b61713c88fa3d8ce7ce47 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Mon, 29 Jul 2019 21:43:07 -0500 Subject: [PATCH 86/96] limit usage of as_dofdesc --- pytential/symbolic/execution.py | 12 +++--- pytential/symbolic/mappers.py | 63 +++++++++++++++----------------- pytential/symbolic/matrix.py | 42 +++++++-------------- pytential/symbolic/primitives.py | 8 ++-- 4 files changed, 54 insertions(+), 71 deletions(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index af8f8031..9c16f54d 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -148,17 +148,17 @@ class EvaluationMapper(EvaluationMapperBase): operand = self.rec(expr.operand) assert operand.shape == (discr.nnodes,) - where = sym.as_dofdesc(expr.dofdesc) - if where.granularity == sym.GRANULARITY_NODE: + granularity = expr.dofdesc.granularity + if granularity == sym.GRANULARITY_NODE: return _reduce(discr.nnodes, node_knl(), lambda g, x: discr.groups[g].view(x)) - elif where.granularity == sym.GRANULARITY_ELEMENT: + elif granularity == sym.GRANULARITY_ELEMENT: return _reduce(discr.mesh.nelements, element_knl(), lambda g, x: mesh_el_view(discr.mesh, g, x)) else: - raise ValueError('unsupported granularity: %s' % where.granularity) + raise ValueError('unsupported granularity: %s' % granularity) def map_elementwise_sum(self, expr): return self._map_elementwise_reduction("sum", expr) @@ -368,7 +368,9 @@ def _prepare_domains(nresults, places, domains, default_domain): dom_name = domains return nresults * [dom_name] + domains = [sym.as_dofdesc(d) for d in domains] assert len(domains) == nresults + return domains @@ -494,8 +496,6 @@ class GeometryCollection(object): return self.auto_where[1] def _get_lpot_discretization(self, lpot, dofdesc): - dofdesc = sym.as_dofdesc(dofdesc) - if dofdesc.discr_stage == sym.QBX_SOURCE_STAGE2: return lpot.stage2_density_discr if dofdesc.discr_stage == sym.QBX_SOURCE_QUAD_STAGE2: diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index da2d449c..6414b5d5 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -212,8 +212,7 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): map_common_subexpression_uncached = \ IdentityMapper.map_common_subexpression - def _as_dofdesc(self, dofdesc): - dofdesc = prim.as_dofdesc(dofdesc) + def _default_dofdesc(self, dofdesc): if dofdesc.geometry is None: if dofdesc.discr_stage is None \ and dofdesc.granularity == prim.GRANULARITY_NODE: @@ -224,7 +223,7 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): return dofdesc def map_ones(self, expr): - return type(expr)(dofdesc=self._as_dofdesc(expr.dofdesc)) + return type(expr)(dofdesc=self._default_dofdesc(expr.dofdesc)) map_q_weight = map_ones @@ -232,33 +231,33 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): return type(expr)( expr.ambient_axis, expr.ref_axis, - self._as_dofdesc(expr.dofdesc)) + self._default_dofdesc(expr.dofdesc)) def map_node_coordinate_component(self, expr): return type(expr)( expr.ambient_axis, - self._as_dofdesc(expr.dofdesc)) + self._default_dofdesc(expr.dofdesc)) def map_num_reference_derivative(self, expr): return type(expr)( expr.ref_axes, self.rec(expr.operand), - self._as_dofdesc(expr.dofdesc)) + self._default_dofdesc(expr.dofdesc)) def map_elementwise_sum(self, expr): return type(expr)( self.rec(expr.operand), - self._as_dofdesc(expr.dofdesc)) + self._default_dofdesc(expr.dofdesc)) map_elementwise_min = map_elementwise_sum map_elementwise_max = map_elementwise_sum def map_int_g(self, expr): - source = prim.as_dofdesc(expr.source) - target = prim.as_dofdesc(expr.target) - + source = expr.source if source.geometry is None: source = source.copy(geometry=self.default_source) + + target = expr.target if target.geometry is None: target = target.copy(geometry=self.default_where) @@ -272,7 +271,7 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): )) def map_inverse(self, expr): - dofdesc = prim.as_dofdesc(expr.dofdesc) + dofdesc = expr.dofdesc if dofdesc.geometry is None: dofdesc = dofdesc.copy(geometry=self.default_where) @@ -287,11 +286,11 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): dofdesc) def map_interpolation(self, expr): - source = prim.as_dofdesc(expr.source) - target = prim.as_dofdesc(expr.target) - + source = expr.source if source.geometry is None: source = source.copy(geometry=self.default_source) + + target = expr.target if target.geometry is None: target = target.copy(geometry=self.default_source) @@ -336,7 +335,7 @@ class DiscretizationStageTagger(IdentityMapper): self.discr_stage = discr_stage def map_node_coordinate_component(self, expr): - dofdesc = prim.as_dofdesc(expr.where) + dofdesc = expr.dofdesc if dofdesc.discr_stage == self.discr_stage: return expr @@ -345,7 +344,7 @@ class DiscretizationStageTagger(IdentityMapper): dofdesc.copy(discr_stage=self.discr_stage)) def map_num_reference_derivative(self, expr): - dofdesc = prim.as_dofdesc(expr.where) + dofdesc = expr.dofdesc if dofdesc.discr_stage == self.discr_stage: return expr @@ -469,23 +468,23 @@ class InterpolationPreprocessor(IdentityMapper): self.tagger = DiscretizationStageTagger(self.from_discr_stage) def map_num_reference_derivative(self, expr): - target_dd = prim.as_dofdesc(expr.where) - if target_dd.discr_stage != prim.QBX_SOURCE_QUAD_STAGE2: + dofdesc = expr.dofdesc + if dofdesc.discr_stage != prim.QBX_SOURCE_QUAD_STAGE2: return expr from pytential.qbx import QBXLayerPotentialSource - lpot_source = self.places[target_dd] + lpot_source = self.places[dofdesc] if not isinstance(lpot_source, QBXLayerPotentialSource): return expr - source_dd = target_dd.copy(discr_stage=self.from_discr_stage) + from_dofdesc = dofdesc.copy(discr_stage=self.from_discr_stage) return prim.Interpolation( - source_dd, - target_dd, + from_dofdesc, + dofdesc, self.rec(self.tagger(expr))) def map_int_g(self, expr): - source = prim.as_dofdesc(expr.source) + source = expr.source if source.discr_stage is not None: return expr @@ -494,19 +493,20 @@ class InterpolationPreprocessor(IdentityMapper): if not isinstance(lpot_source, QBXLayerPotentialSource): return expr - target = source.copy(discr_stage=prim.QBX_SOURCE_QUAD_STAGE2) + from_dd = source + to_dd = from_dd.copy(discr_stage=prim.QBX_SOURCE_QUAD_STAGE2) density = prim.Interpolation( - source, target, self.rec(expr.density)) + from_dd, to_dd, self.rec(expr.density)) kernel_arguments = dict( (name, prim.Interpolation( - source, target, self.rec(arg_expr))) + from_dd, to_dd, self.rec(arg_expr))) for name, arg_expr in expr.kernel_arguments.items()) return expr.copy( kernel=expr.kernel, density=density, kernel_arguments=kernel_arguments, - source=target, + source=to_dd, target=expr.target) # }}} @@ -520,14 +520,11 @@ class QBXPreprocessor(IdentityMapper): self.places = places def map_int_g(self, expr): - from pytential import sym - source = sym.as_dofdesc(expr.source) - target = sym.as_dofdesc(expr.target) - if source.geometry != self.source_name: + if expr.source.geometry != self.source_name: return expr - source_discr = self.places.get_discretization(source) - target_discr = self.places.get_discretization(target) + source_discr = self.places.get_discretization(expr.source) + target_discr = self.places.get_discretization(expr.target) if expr.qbx_forced_limit == 0: raise ValueError("qbx_forced_limit == 0 was a bad idea and " diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index d163e39f..ff6c9822 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -368,9 +368,7 @@ class MatrixBuilder(MatrixBuilderBase): dep_source, dep_discr, places, context) def map_interpolation(self, expr): - source_dd = sym.as_dofdesc(expr.source) - target_dd = sym.as_dofdesc(expr.target) - if target_dd.discr_stage != sym.QBX_SOURCE_QUAD_STAGE2: + if expr.target.discr_stage != sym.QBX_SOURCE_QUAD_STAGE2: raise RuntimeError("can only interpolate to QBX_SOURCE_QUAD_STAGE2") operand = self.rec(expr.operand) @@ -384,19 +382,16 @@ class MatrixBuilder(MatrixBuilderBase): operand = cl.array.to_device(self.queue, operand) return conn(self.queue, operand).get(self.queue) elif isinstance(operand, np.ndarray) and operand.ndim == 2: - resampler = self.places[source_dd].direct_resampler + resampler = self.places[expr.source].direct_resampler mat = resampler.full_resample_matrix(self.queue).get(self.queue) return mat.dot(operand) else: raise RuntimeError('unknown operand type: {}'.format(type(operand))) def map_int_g(self, expr): - source_dd = sym.as_dofdesc(expr.source) - target_dd = sym.as_dofdesc(expr.target) - - lpot_source = self.places[source_dd] - source_discr = self.places.get_discretization(source_dd) - target_discr = self.places.get_discretization(target_dd) + lpot_source = self.places[expr.source] + source_discr = self.places.get_discretization(expr.source) + target_discr = self.places.get_discretization(expr.target) rec_density = self.rec(expr.density) if is_zero(rec_density): @@ -449,12 +444,9 @@ class P2PMatrixBuilder(MatrixBuilderBase): self.exclude_self = exclude_self def map_int_g(self, expr): - target_dd = sym.as_dofdesc(expr.target) - source_dd = sym.as_dofdesc(expr.source) - - source = self.places[source_dd] - source_discr = self.places.get_discretization(source_dd) - target_discr = self.places.get_discretization(target_dd) + source = self.places[expr.source] + source_discr = self.places.get_discretization(expr.source) + target_discr = self.places.get_discretization(expr.target) rec_density = self.rec(expr.density) if is_zero(rec_density): @@ -515,12 +507,9 @@ class NearFieldBlockBuilder(MatrixBlockBuilderBase): return np.equal(tgtindices, srcindices).astype(np.float64) def map_int_g(self, expr): - target_dd = sym.as_dofdesc(expr.target) - source_dd = sym.as_dofdesc(expr.source) - - source = self.places[source_dd] - source_discr = self.places.get_discretization(source_dd) - target_discr = self.places.get_discretization(target_dd) + source = self.places[expr.source] + source_discr = self.places.get_discretization(expr.source) + target_discr = self.places.get_discretization(expr.target) if source_discr is not target_discr: raise NotImplementedError() @@ -584,12 +573,9 @@ class FarFieldBlockBuilder(MatrixBlockBuilderBase): return np.equal(tgtindices, srcindices).astype(np.float64) def map_int_g(self, expr): - target_dd = sym.as_dofdesc(expr.target) - source_dd = sym.as_dofdesc(expr.source) - - source = self.places[source_dd] - source_discr = self.places.get_discretization(source_dd) - target_discr = self.places.get_discretization(target_dd) + source = self.places[expr.source] + source_discr = self.places.get_discretization(expr.source) + target_discr = self.places.get_discretization(expr.target) if source_discr is not target_discr: raise NotImplementedError() diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 827be501..8abe791c 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -1484,8 +1484,8 @@ class IntG(Expression): self.kernel = kernel self.density = density self.qbx_forced_limit = qbx_forced_limit - self.source = source - self.target = target + self.source = as_dofdesc(source) + self.target = as_dofdesc(target) self.kernel_arguments = kernel_arguments def copy(self, kernel=None, density=None, qbx_forced_limit=_NoArgSentinel, @@ -1494,8 +1494,8 @@ class IntG(Expression): density = density or self.density if qbx_forced_limit is _NoArgSentinel: qbx_forced_limit = self.qbx_forced_limit - source = source or self.source - target = target or self.target + source = as_dofdesc(source or self.source) + target = as_dofdesc(target or self.target) kernel_arguments = kernel_arguments or self.kernel_arguments return type(self)(kernel, density, qbx_forced_limit, source, target, kernel_arguments) -- GitLab From 23756477b7cb1647ff5ca7943998e7efdee16ee8 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 2 Aug 2019 19:40:50 -0500 Subject: [PATCH 87/96] use get_geometry instead of __getitem__ --- pytential/qbx/__init__.py | 2 +- pytential/symbolic/compiler.py | 5 +++-- pytential/symbolic/dof_connection.py | 2 +- pytential/symbolic/execution.py | 19 +++++++------------ pytential/symbolic/mappers.py | 4 ++-- pytential/symbolic/matrix.py | 10 +++++----- pytential/unregularized.py | 2 +- test/test_matrix.py | 16 ++++++++-------- 8 files changed, 28 insertions(+), 32 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 31b1d506..c6762642 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -576,7 +576,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): tgt_name_and_side_to_number[key] = \ len(target_discrs_and_qbx_sides) - target_discr = bound_expr.places[o.target_name] + target_discr = bound_expr.places.get_geometry(o.target_name) if isinstance(target_discr, LayerPotentialSourceBase): target_discr = target_discr.density_discr diff --git a/pytential/symbolic/compiler.py b/pytential/symbolic/compiler.py index cb9a9c1d..8f7ca490 100644 --- a/pytential/symbolic/compiler.py +++ b/pytential/symbolic/compiler.py @@ -224,7 +224,7 @@ class ComputePotentialInstruction(Instruction): ", ".join(args), "\n ".join(lines)) def get_exec_function(self, exec_mapper): - source = exec_mapper.bound_expr.places[self.source] + source = exec_mapper.bound_expr.places.get_geometry(self.source) return source.exec_compute_potential_insn def __hash__(self): @@ -440,8 +440,9 @@ class OperatorCompiler(IdentityMapper): def op_group_features(self, expr): from pytential.symbolic.primitives import hashable_kernel_args + lpot_source = self.places.get_geometry(expr.source) return ( - self.places[expr.source].op_group_features(expr) + lpot_source.op_group_features(expr) + hashable_kernel_args(expr.kernel_arguments)) @memoize_method diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 810d254f..98bc2f0c 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -204,7 +204,7 @@ def connection_from_dds(places, from_dd, to_dd): from pytential.symbolic.execution import GeometryCollection if not isinstance(places, GeometryCollection): places = GeometryCollection(places) - from_discr = places[from_dd] + from_discr = places.get_geometry(from_dd) if from_dd.geometry != to_dd.geometry: raise ValueError("cannot interpolate between different geometries") diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 9c16f54d..91e1c486 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -207,7 +207,7 @@ class EvaluationMapper(EvaluationMapperBase): except KeyError: bound_op = bind( expr.expression, - self.places[expr.dofdesc], + self.places.get_geometry(expr.dofdesc), self.bound_expr.iprec) bound_op_cache[expr] = bound_op @@ -359,9 +359,6 @@ def _prepare_domains(nresults, places, domains, default_domain): """ if domains is None: - if default_domain not in places: - raise RuntimeError("'domains is None' requires " - "default domain to be defined in places") dom_name = default_domain return nresults * [dom_name] elif not isinstance(domains, (list, tuple)): @@ -415,8 +412,10 @@ class GeometryCollection(object): of subsets of them, as well as related common subexpressions such as metric terms. - .. method:: __getitem__ .. method:: get_discretization + .. mathod:: get_geometry + .. method:: copy + .. method:: get_cache """ @@ -531,14 +530,10 @@ class GeometryCollection(object): else: return discr - def __getitem__(self, dofdesc): + def get_geometry(self, dofdesc): dofdesc = sym.as_dofdesc(dofdesc) return self.places[dofdesc.geometry] - def __contains__(self, dofdesc): - dofdesc = sym.as_dofdesc(dofdesc) - return dofdesc.geometry in self.places - def copy(self): return GeometryCollection( self.places, @@ -594,7 +589,7 @@ class BoundExpression(object): if dom_name is None: size = 1 else: - size = self.places[dom_name].nnodes + size = self.places.get_geometry(dom_name).nnodes starts_and_ends.append((total_dofs, total_dofs+size)) total_dofs += size @@ -714,7 +709,7 @@ def build_matrix(queue, places, exprs, input_exprs, domains=None, dep_expr=input_exprs[ibcol], other_dep_exprs=(input_exprs[:ibcol] + input_exprs[ibcol + 1:]), - dep_source=places[domains[ibcol]], + dep_source=places.get_geometry(domains[ibcol]), dep_discr=places.get_discretization(domains[ibcol]), places=places, context=context) diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 6414b5d5..31009367 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -473,7 +473,7 @@ class InterpolationPreprocessor(IdentityMapper): return expr from pytential.qbx import QBXLayerPotentialSource - lpot_source = self.places[dofdesc] + lpot_source = self.places.get_geometry(dofdesc) if not isinstance(lpot_source, QBXLayerPotentialSource): return expr @@ -489,7 +489,7 @@ class InterpolationPreprocessor(IdentityMapper): return expr from pytential.qbx import QBXLayerPotentialSource - lpot_source = self.places[source] + lpot_source = self.places.get_geometry(source) if not isinstance(lpot_source, QBXLayerPotentialSource): return expr diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index ff6c9822..e36c89ad 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -382,14 +382,14 @@ class MatrixBuilder(MatrixBuilderBase): operand = cl.array.to_device(self.queue, operand) return conn(self.queue, operand).get(self.queue) elif isinstance(operand, np.ndarray) and operand.ndim == 2: - resampler = self.places[expr.source].direct_resampler + resampler = self.places.get_geometry(expr.source).direct_resampler mat = resampler.full_resample_matrix(self.queue).get(self.queue) return mat.dot(operand) else: raise RuntimeError('unknown operand type: {}'.format(type(operand))) def map_int_g(self, expr): - lpot_source = self.places[expr.source] + lpot_source = self.places.get_geometry(expr.source) source_discr = self.places.get_discretization(expr.source) target_discr = self.places.get_discretization(expr.target) @@ -444,7 +444,7 @@ class P2PMatrixBuilder(MatrixBuilderBase): self.exclude_self = exclude_self def map_int_g(self, expr): - source = self.places[expr.source] + source = self.places.get_geometry(expr.source) source_discr = self.places.get_discretization(expr.source) target_discr = self.places.get_discretization(expr.target) @@ -507,7 +507,7 @@ class NearFieldBlockBuilder(MatrixBlockBuilderBase): return np.equal(tgtindices, srcindices).astype(np.float64) def map_int_g(self, expr): - source = self.places[expr.source] + source = self.places.get_geometry(expr.source) source_discr = self.places.get_discretization(expr.source) target_discr = self.places.get_discretization(expr.target) @@ -573,7 +573,7 @@ class FarFieldBlockBuilder(MatrixBlockBuilderBase): return np.equal(tgtindices, srcindices).astype(np.float64) def map_int_g(self, expr): - source = self.places[expr.source] + source = self.places.get_geometry(expr.source) source_discr = self.places.get_discretization(expr.source) target_discr = self.places.get_discretization(expr.target) diff --git a/pytential/unregularized.py b/pytential/unregularized.py index 77025c88..2ff748d1 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -212,7 +212,7 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): continue target_name_to_index[o.target_name] = len(targets) - targets.append(bound_expr.places[o.target_name]) + targets.append(bound_expr.places.get_geometry(o.target_name)) targets = tuple(targets) diff --git a/test/test_matrix.py b/test/test_matrix.py index 64b001bb..66441657 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -308,7 +308,7 @@ def test_p2p_block_builder(ctx_factory, factor, ndim, lpot_id, mbuilder = P2PMatrixBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[place_ids[0]], + dep_source=places.get_geometry(place_ids[0]), dep_discr=places.get_discretization(place_ids[0]), places=places, context={}, @@ -319,7 +319,7 @@ def test_p2p_block_builder(ctx_factory, factor, ndim, lpot_id, mbuilder = FarFieldBlockBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[place_ids[0]], + dep_source=places.get_geometry(place_ids[0]), dep_discr=places.get_discretization(place_ids[0]), places=places, index_set=index_set, @@ -388,7 +388,7 @@ def test_qbx_block_builder(ctx_factory, factor, ndim, lpot_id, mbuilder = NearFieldBlockBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[place_ids[0]], + dep_source=places.get_geometry(place_ids[0]), dep_discr=places.get_discretization(place_ids[0]), places=places, index_set=index_set, @@ -399,7 +399,7 @@ def test_qbx_block_builder(ctx_factory, factor, ndim, lpot_id, mbuilder = MatrixBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[place_ids[0]], + dep_source=places.get_geometry(place_ids[0]), dep_discr=places.get_discretization(place_ids[0]), places=places, context={}) @@ -474,7 +474,7 @@ def test_build_matrix_places(ctx_factory, mbuilder = MatrixBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[place_ids[0]], + dep_source=places.get_geometry(place_ids[0]), dep_discr=places.get_discretization(place_ids[0]), places=places, context={}) @@ -485,7 +485,7 @@ def test_build_matrix_places(ctx_factory, mbuilder = P2PMatrixBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[place_ids[0]], + dep_source=places.get_geometry(place_ids[0]), dep_discr=places.get_discretization(place_ids[0]), places=places, context={}) @@ -498,7 +498,7 @@ def test_build_matrix_places(ctx_factory, mbuilder = NearFieldBlockBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[place_ids[0]], + dep_source=places.get_geometry(place_ids[0]), dep_discr=places.get_discretization(place_ids[0]), places=places, index_set=index_set, @@ -511,7 +511,7 @@ def test_build_matrix_places(ctx_factory, mbuilder = FarFieldBlockBuilder(queue, dep_expr=u_sym, other_dep_exprs=[], - dep_source=places[place_ids[0]], + dep_source=places.get_geometry(place_ids[0]), dep_discr=places.get_discretization(place_ids[0]), places=places, index_set=index_set, -- GitLab From d9b0c83113ff1d1de5402869d00878ce8e10bda6 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 2 Aug 2019 20:02:19 -0500 Subject: [PATCH 88/96] update docs for ToTargetTagger --- pytential/symbolic/mappers.py | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 31009367..f9236c0e 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -202,9 +202,8 @@ class EvaluationMapper(EvaluationMapperBase): # {{{ dofdesc tagging class LocationTagger(CSECachingMapperMixin, IdentityMapper): - """Used internally by :class:`ToTargetTagger`. Tags all src/target taggable - leaves/operators as going back onto the source. - """ + """Used internally by :class:`ToTargetTagger`.""" + def __init__(self, default_where, default_source=prim.DEFAULT_SOURCE): self.default_source = default_source self.default_where = default_where @@ -286,25 +285,32 @@ class LocationTagger(CSECachingMapperMixin, IdentityMapper): dofdesc) def map_interpolation(self, expr): - source = expr.source - if source.geometry is None: - source = source.copy(geometry=self.default_source) + from_dd = expr.from_dd + if from_dd.geometry is None: + from_dd = from_dd.copy(geometry=self.default_source) - target = expr.target - if target.geometry is None: - target = target.copy(geometry=self.default_source) + to_dd = expr.to_dd + if to_dd.geometry is None: + to_dd = to_dd.copy(geometry=self.default_source) - return type(expr)(source, target, self.operand_rec(expr.operand)) + return type(expr)(from_dd, to_dd, self.operand_rec(expr.operand)) def operand_rec(self, expr): return self.rec(expr) class ToTargetTagger(LocationTagger): - """Descends into the expression tree, marking everything up to the first - layer potential operator as operating on the targets, and everything below - there as operating on the source. Also performs a consistency check such - that only 'target' and 'source' items are combined arithmetically. + """Descends into the expression tree, marking expressions based on two + heuristics: + + * everything up to the first layer potential operator is marked as + operating on the targets, and everything below there as operating on the + source. + * if an expression has a :class:`~pytential.symbolic.primitives.DOFDescriptor` + that requires a :class:`~pytential.source.LayerPotentialSourceBase` to be + used (e.g. by begin defined on + :class:`~pytential.symbolic.primitives.QBX_SOURCE_QUAD_STAGE2`), then + it is marked as operating on a source. """ def __init__(self, default_source, default_target): -- GitLab From 1d5c4d6f610f6f5ad2ed3e950d5864dbcafa6fc1 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 2 Aug 2019 20:02:37 -0500 Subject: [PATCH 89/96] interpolation: rename members to from_dd and to_dd for clarity --- pytential/symbolic/execution.py | 2 +- pytential/symbolic/mappers.py | 32 ++++++-------- pytential/symbolic/primitives.py | 76 +++++++++++++++++--------------- test/test_linalg_proxy.py | 2 - test/test_symbolic.py | 6 +-- 5 files changed, 57 insertions(+), 61 deletions(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 91e1c486..85f5a313 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -227,7 +227,7 @@ class EvaluationMapper(EvaluationMapperBase): from pytential.symbolic.dof_connection import connection_from_dds conn = connection_from_dds(self.places, - expr.source, expr.target) + expr.from_dd, expr.to_dd) return conn(self.queue, operand).with_queue(self.queue) elif isinstance(operand, (int, float, complex, np.number)): return operand diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index f9236c0e..3e1e0ffd 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -103,7 +103,7 @@ class IdentityMapper(IdentityMapperBase): )) def map_interpolation(self, expr): - return type(expr)(expr.source, expr.target, self.rec(expr.operand)) + return type(expr)(expr.from_dd, expr.to_dd, self.rec(expr.operand)) class CombineMapper(CombineMapperBase): @@ -474,38 +474,32 @@ class InterpolationPreprocessor(IdentityMapper): self.tagger = DiscretizationStageTagger(self.from_discr_stage) def map_num_reference_derivative(self, expr): - dofdesc = expr.dofdesc - if dofdesc.discr_stage != prim.QBX_SOURCE_QUAD_STAGE2: + to_dd = expr.dofdesc + if to_dd.discr_stage != prim.QBX_SOURCE_QUAD_STAGE2: return expr from pytential.qbx import QBXLayerPotentialSource - lpot_source = self.places.get_geometry(dofdesc) + lpot_source = self.places.get_geometry(to_dd) if not isinstance(lpot_source, QBXLayerPotentialSource): return expr - from_dofdesc = dofdesc.copy(discr_stage=self.from_discr_stage) - return prim.Interpolation( - from_dofdesc, - dofdesc, - self.rec(self.tagger(expr))) + from_dd = to_dd.copy(discr_stage=self.from_discr_stage) + return prim.interp(from_dd,to_dd, self.rec(self.tagger(expr))) def map_int_g(self, expr): - source = expr.source - if source.discr_stage is not None: + from_dd = expr.source + if from_dd.discr_stage is not None: return expr from pytential.qbx import QBXLayerPotentialSource - lpot_source = self.places.get_geometry(source) + lpot_source = self.places.get_geometry(from_dd) if not isinstance(lpot_source, QBXLayerPotentialSource): return expr - from_dd = source to_dd = from_dd.copy(discr_stage=prim.QBX_SOURCE_QUAD_STAGE2) - density = prim.Interpolation( - from_dd, to_dd, self.rec(expr.density)) + density = prim.interp(from_dd, to_dd, self.rec(expr.density)) kernel_arguments = dict( - (name, prim.Interpolation( - from_dd, to_dd, self.rec(arg_expr))) + (name, prim.interp(from_dd, to_dd, self.rec(arg_expr))) for name, arg_expr in expr.kernel_arguments.items()) return expr.copy( @@ -672,8 +666,8 @@ class StringifyMapper(BaseStringifyMapper): def map_interpolation(self, expr, enclosing_prec): return "Interp[%s->%s](%s)" % ( - stringify_where(expr.source), - stringify_where(expr.target), + stringify_where(expr.from_dd), + stringify_where(expr.to_dd), self.rec(expr.operand, PREC_PRODUCT)) # }}} diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 8abe791c..4df4ee4c 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -408,7 +408,6 @@ def as_dofdesc(desc): return DOFDescriptor(geometry=desc) - # }}} @@ -986,12 +985,12 @@ def _quad_resolution(ambient_dim, dim=None, granularity=None, dofdesc=None): should be the same as the panel length. """ - source = as_dofdesc(dofdesc) - target = source.copy(granularity=granularity) + from_dd = as_dofdesc(dofdesc) + to_dd = from_dd.copy(granularity=granularity) stretch = _simplex_mapping_max_stretch_factor(ambient_dim, - dim=dim, dofdesc=source) - return Interpolation(source, target, stretch) + dim=dim, dofdesc=from_dd) + return interp(from_dd, to_dd, stretch) @_deprecate_kwargs('where', 'dofdesc') @@ -1071,6 +1070,42 @@ def weights_and_area_elements(ambient_dim, dim=None, dofdesc=None): # {{{ operators +class Interpolation(Expression): + """Interpolate quantity from a DOF described by *from_dd* to a DOF + described by *to_dd*.""" + + init_arg_names = ("from_dd", "to_dd", "operand") + + def __new__(cls, from_dd, to_dd, operand): + from_dd = as_dofdesc(from_dd) + to_dd = as_dofdesc(to_dd) + + if from_dd == to_dd: + return operand + + if isinstance(operand, np.ndarray): + def make_op(operand_i): + return cls(from_dd, to_dd, operand_i) + + return componentwise(make_op, operand) + else: + return Expression.__new__(cls) + + def __init__(self, from_dd, to_dd, operand): + self.from_dd = as_dofdesc(from_dd) + self.to_dd = as_dofdesc(to_dd) + self.operand = operand + + def __getinitargs__(self): + return (self.from_dd, self.to_dd, self.operand) + + mapper_method = intern("map_interpolation") + + +def interp(from_dd, to_dd, operand): + return Interpolation(from_dd, to_dd, operand) + + class SingleScalarOperandExpression(Expression): init_arg_names = ("operand",) @@ -1331,37 +1366,6 @@ class _NoArgSentinel(object): pass -class Interpolation(Expression): - """Interpolate quantity from *source* to *target* discretization.""" - - init_arg_names = ("source", "target", "operand") - - def __new__(cls, source, target, operand): - source = as_dofdesc(source) - target = as_dofdesc(target) - - if source == target: - return operand - - if isinstance(operand, np.ndarray): - def make_op(operand_i): - return cls(source, target, operand_i) - - return componentwise(make_op, operand) - else: - return Expression.__new__(cls) - - def __init__(self, source, target, operand): - self.source = as_dofdesc(source) - self.target = as_dofdesc(target) - self.operand = operand - - def __getinitargs__(self): - return (self.source, self.target, self.operand) - - mapper_method = intern("map_interpolation") - - class IntG(Expression): r""" .. math:: diff --git a/test/test_linalg_proxy.py b/test/test_linalg_proxy.py index 5435e4c5..1707a666 100644 --- a/test/test_linalg_proxy.py +++ b/test/test_linalg_proxy.py @@ -338,8 +338,6 @@ def test_interaction_points(ctx_factory, ndim, factor, visualize=False): isrc = srcindices.block_indices(i) inbr = nbrindices.block_indices(i) - # TODO: some way to turn off some of the interpolations - # would help visualize this better. marker.fill(0.0) marker[srcindices.indices] = 0.0 marker[isrc] = -42.0 diff --git a/test/test_symbolic.py b/test/test_symbolic.py index 8f550b67..40e8382f 100644 --- a/test/test_symbolic.py +++ b/test/test_symbolic.py @@ -229,17 +229,17 @@ def test_interpolation(ctx_factory, name, source_discr_stage, target_granularity fmm_order=False).with_refinement() where = 'test-interpolation' - source = sym.DOFDescriptor( + from_dd = sym.DOFDescriptor( geometry=where, discr_stage=source_discr_stage, granularity=sym.GRANULARITY_NODE) - target = sym.DOFDescriptor( + to_dd = sym.DOFDescriptor( geometry=where, discr_stage=sym.QBX_SOURCE_QUAD_STAGE2, granularity=target_granularity) sigma_sym = sym.var("sigma") - op_sym = sym.sin(sym.Interpolation(source, target, sigma_sym)) + op_sym = sym.sin(sym.interp(from_dd, to_dd, sigma_sym)) bound_op = bind(qbx, op_sym, auto_where=where) target_nodes = qbx.quad_stage2_density_discr.nodes().get(queue) -- GitLab From 87fb856664bb05765eb225c4104f0a98169aa71e Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 2 Aug 2019 20:19:56 -0500 Subject: [PATCH 90/96] update some docs --- pytential/symbolic/execution.py | 6 +++--- pytential/symbolic/mappers.py | 2 +- pytential/symbolic/primitives.py | 9 ++++++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 85f5a313..48555f66 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -412,9 +412,9 @@ class GeometryCollection(object): of subsets of them, as well as related common subexpressions such as metric terms. - .. method:: get_discretization - .. mathod:: get_geometry - .. method:: copy + .. automethod:: get_discretization + .. automethod:: get_geometry + .. automethod:: copy .. method:: get_cache """ diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 3e1e0ffd..87198425 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -484,7 +484,7 @@ class InterpolationPreprocessor(IdentityMapper): return expr from_dd = to_dd.copy(discr_stage=self.from_discr_stage) - return prim.interp(from_dd,to_dd, self.rec(self.tagger(expr))) + return prim.interp(from_dd, to_dd, self.rec(self.tagger(expr))) def map_int_g(self, expr): from_dd = expr.source diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 4df4ee4c..c56417d8 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -149,6 +149,8 @@ Discretization properties .. autofunction:: expansion_radii .. autofunction:: expansion_centers +.. autofunction:: h_max +.. autofunction:: weights_and_area_elements Elementary numerics ^^^^^^^^^^^^^^^^^^^ @@ -169,6 +171,7 @@ Operators ^^^^^^^^^ .. autoclass:: Interpolation +.. autofunction:: interp Geometric Calculus (based on Geometric/Clifford Algebra) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -303,7 +306,7 @@ class DOFDescriptor(object): .. attribute:: discr_stage Specific to a :class:`pytential.source.LayerPotentialSourceBase`, - this discribes on which of the discretizations the + this describes on which of the discretizations the DOFs are defined. Can be one of :class:`QBX_SOURCE_STAGE1`, :class:`QBX_SOURCE_STAGE2` or :class:`QBX_SOURCE_QUAD_STAGE2`. @@ -1050,6 +1053,8 @@ def expansion_centers(ambient_dim, side, dim=None, dofdesc=None): @_deprecate_kwargs('where', 'dofdesc') def h_max(ambient_dim, dim=None, dofdesc=None): + """Defines a maximum element size in the discretization.""" + dofdesc = as_dofdesc(dofdesc).copy(granularity=GRANULARITY_ELEMENT) r = _quad_resolution(ambient_dim, dim=dim, dofdesc=dofdesc) @@ -1060,6 +1065,8 @@ def h_max(ambient_dim, dim=None, dofdesc=None): @_deprecate_kwargs('where', 'dofdesc') def weights_and_area_elements(ambient_dim, dim=None, dofdesc=None): + """Combines :func:`area_element` and :class:`QWeight`.""" + area = area_element(ambient_dim, dim=dim, dofdesc=dofdesc) return cse(area * QWeight(dofdesc=dofdesc), "weights_area_elements", -- GitLab From d7a6a0b728c27b5bd96629849f9f244423849f3c Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 2 Aug 2019 22:04:54 -0500 Subject: [PATCH 91/96] fix from previous rename --- pytential/qbx/utils.py | 3 +-- pytential/symbolic/matrix.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index a54f71dc..194c90fc 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -292,10 +292,8 @@ def build_tree_with_qbx_metadata( if use_stage2_discr: density_discr = lpot_source.quad_stage2_density_discr - npanels = lpot_source.stage2_density_discr.mesh.nelements else: density_discr = lpot_source.density_discr - npanels = lpot_source.density_discr.mesh.nelements sources = density_discr.nodes() centers = get_interleaved_centers(queue, lpot_source) @@ -307,6 +305,7 @@ def build_tree_with_qbx_metadata( # Counts nparticles = len(particles[0]) + npanels = density_discr.mesh.nelements nsources = len(sources[0]) ncenters = len(centers[0]) # Each source gets an interior / exterior center. diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index e36c89ad..69ac48c7 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -377,12 +377,12 @@ class MatrixBuilder(MatrixBuilderBase): elif isinstance(operand, np.ndarray) and operand.ndim == 1: from pytential.symbolic.dof_connection import connection_from_dds conn = connection_from_dds(self.places, - expr.source, expr.target) + expr.from_dd, expr.to_dd) operand = cl.array.to_device(self.queue, operand) return conn(self.queue, operand).get(self.queue) elif isinstance(operand, np.ndarray) and operand.ndim == 2: - resampler = self.places.get_geometry(expr.source).direct_resampler + resampler = self.places.get_geometry(expr.from_dd).direct_resampler mat = resampler.full_resample_matrix(self.queue).get(self.queue) return mat.dot(operand) else: -- GitLab From 483149e09d43a466d25fd0dc028665cd26744712 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 2 Aug 2019 23:04:35 -0500 Subject: [PATCH 92/96] another fix from previous rename --- pytential/symbolic/matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index 69ac48c7..9f9e0e4a 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -368,7 +368,7 @@ class MatrixBuilder(MatrixBuilderBase): dep_source, dep_discr, places, context) def map_interpolation(self, expr): - if expr.target.discr_stage != sym.QBX_SOURCE_QUAD_STAGE2: + if expr.to_dd.discr_stage != sym.QBX_SOURCE_QUAD_STAGE2: raise RuntimeError("can only interpolate to QBX_SOURCE_QUAD_STAGE2") operand = self.rec(expr.operand) -- GitLab From 88a06f5221556a3e2f7fde6b1cd916babc12a72f Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Mon, 12 Aug 2019 16:23:47 -0500 Subject: [PATCH 93/96] Typo/comment/doc/stringification fixes --- pytential/symbolic/dof_connection.py | 4 ++-- pytential/symbolic/execution.py | 11 +++++++---- pytential/symbolic/mappers.py | 2 +- pytential/symbolic/primitives.py | 9 ++++++++- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 98bc2f0c..031118d3 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -187,14 +187,14 @@ def connection_from_dds(places, from_dd, to_dd): """ :arg places: a :class:`~pytential.symbolic.execution.GeometryCollection` or an argument taken by its constructor. - :arg from_dd: a descriptor for the incomming degrees of freedom. This + :arg from_dd: a descriptor for the incoming degrees of freedom. This can be a :class:`~pytential.symbolic.primitives.DOFDescriptor` or an identifier that can be transformed into one by :func:`~pytential.symbolic.primitives.as_dofdesc`. :arg to_dd: a descriptor for the outgoing degrees of freedom. :return: a :class:`DOFConnection` transporting between the two - of DOFs. + kinds of DOF vectors. """ from pytential import sym diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 48555f66..5e21765d 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -46,7 +46,6 @@ from pytential import sym # {{{ evaluation mapper - def mesh_el_view(mesh, group_nr, global_array): """Return a view of *global_array* of shape ``(..., mesh.groups[group_nr].nelements)`` @@ -400,7 +399,7 @@ def _prepare_expr(places, expr): # }}} -# {{{ bound expression +# {{{ geometry collection class GeometryCollection(object): """A mapping from symbolic identifiers ("place IDs", typically strings) @@ -543,11 +542,15 @@ class GeometryCollection(object): return self.caches.setdefault(name, {}) def __repr__(self): - return repr(self.places) + return "%s(%s)" % (type(self).__name.__, repr(self.places)) def __str__(self): - return str(self.places) + return "%s(%s)" % (type(self).__name.__, str(self.places)) + +# }}} + +# {{{ bound expression class BoundExpression(object): def __init__(self, places, sym_op_expr): diff --git a/pytential/symbolic/mappers.py b/pytential/symbolic/mappers.py index 87198425..66c0bba0 100644 --- a/pytential/symbolic/mappers.py +++ b/pytential/symbolic/mappers.py @@ -308,7 +308,7 @@ class ToTargetTagger(LocationTagger): source. * if an expression has a :class:`~pytential.symbolic.primitives.DOFDescriptor` that requires a :class:`~pytential.source.LayerPotentialSourceBase` to be - used (e.g. by begin defined on + used (e.g. by being defined on :class:`~pytential.symbolic.primitives.QBX_SOURCE_QUAD_STAGE2`), then it is marked as operating on a source. """ diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index c56417d8..1f4fd485 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -893,7 +893,8 @@ def _simplex_mapping_max_stretch_factor(ambient_dim, dim=None, dofdesc=None, mapping stretches the bi-unit (i.e. :math:`[-1,1]`) reference element along any axis. - Returns a DOF vector that is elementwise constant. + If *map_elementwise_max* is True, returns a DOF vector that is elementwise + constant. """ if dim is None: @@ -986,6 +987,12 @@ def _quad_resolution(ambient_dim, dim=None, granularity=None, dofdesc=None): """This measures the quadrature resolution across the mesh. In a 1D uniform mesh of uniform 'parametrization speed', it should be the same as the panel length. + + In multiple dimensions (i.e. with multiple quadrature resolutions + depending on direction), this measure returns the coarsest of these resolution, + is invariant with respect to rotation of the global coordinate + system, and invariant with respect to vertex ordering of the reference + element. """ from_dd = as_dofdesc(dofdesc) -- GitLab From 587104cb489a5d52308f6d3a36002ccec62b7df7 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Mon, 12 Aug 2019 16:25:04 -0500 Subject: [PATCH 94/96] Add dof_connection.__doc__ (even if it's not rendered anywhere just yet) --- pytential/symbolic/dof_connection.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 031118d3..a4abbd0f 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -34,6 +34,19 @@ import loopy as lp from loopy.version import MOST_RECENT_LANGUAGE_VERSION +__doc__ = """ + +Connections +----------- + +.. autoclass:: GranularityConnection +.. autoclass:: CenterGranularityConnection +.. autoclass:: DOFConnection +.. autofunction:: connection_from_dds + +""" + + # {{{ granularity connections class GranularityConnection(object): -- GitLab From c27313b19ebe86f40d152e62a5b128fde49767fe Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Mon, 12 Aug 2019 16:26:24 -0500 Subject: [PATCH 95/96] Use 'is' for comparison of granularities and discr stages --- pytential/symbolic/dof_connection.py | 10 +++++----- pytential/symbolic/execution.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index a4abbd0f..8cc5d1e4 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -238,9 +238,9 @@ def connection_from_dds(places, from_dd, to_dd): raise ValueError("can only interpolate to " "`QBX_SOURCE_QUAD_STAGE2`") - if from_dd.discr_stage == sym.QBX_SOURCE_QUAD_STAGE2: + if from_dd.discr_stage is sym.QBX_SOURCE_QUAD_STAGE2: pass - elif from_dd.discr_stage == sym.QBX_SOURCE_STAGE2: + elif from_dd.discr_stage is sym.QBX_SOURCE_STAGE2: connections.append( from_discr.refined_interp_to_ovsmp_quad_connection) else: @@ -249,11 +249,11 @@ def connection_from_dds(places, from_dd, to_dd): if from_dd.granularity is not to_dd.granularity: to_discr = places.get_discretization(to_dd) - if to_dd.granularity == sym.GRANULARITY_NODE: + if to_dd.granularity is sym.GRANULARITY_NODE: pass - elif to_dd.granularity == sym.GRANULARITY_CENTER: + elif to_dd.granularity is sym.GRANULARITY_CENTER: connections.append(CenterGranularityConnection(to_discr)) - elif to_dd.granularity == sym.GRANULARITY_ELEMENT: + elif to_dd.granularity is sym.GRANULARITY_ELEMENT: raise ValueError("Creating a connection to element granularity " "is not allowed. Use Elementwise{Max,Min,Sum}.") else: diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 5e21765d..ec5b2674 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -148,11 +148,11 @@ class EvaluationMapper(EvaluationMapperBase): assert operand.shape == (discr.nnodes,) granularity = expr.dofdesc.granularity - if granularity == sym.GRANULARITY_NODE: + if granularity is sym.GRANULARITY_NODE: return _reduce(discr.nnodes, node_knl(), lambda g, x: discr.groups[g].view(x)) - elif granularity == sym.GRANULARITY_ELEMENT: + elif granularity is sym.GRANULARITY_ELEMENT: return _reduce(discr.mesh.nelements, element_knl(), lambda g, x: mesh_el_view(discr.mesh, g, x)) -- GitLab From bcb55016c5450c2f259a5187b48eb8d1d924fe3b Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Mon, 12 Aug 2019 20:28:22 -0500 Subject: [PATCH 96/96] Fix __name.__ typo --- pytential/symbolic/execution.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index ec5b2674..5a8f8b78 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -542,10 +542,10 @@ class GeometryCollection(object): return self.caches.setdefault(name, {}) def __repr__(self): - return "%s(%s)" % (type(self).__name.__, repr(self.places)) + return "%s(%s)" % (type(self).__name__, repr(self.places)) def __str__(self): - return "%s(%s)" % (type(self).__name.__, str(self.places)) + return "%s(%s)" % (type(self).__name__, str(self.places)) # }}} -- GitLab