From 591519914ae3ba3418212731d7b3e1974c7340d6 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Mon, 15 Jun 2020 13:19:46 -0500 Subject: [PATCH 01/75] Initial, incomplete adaptation to meshmode's ArrayContext --- pytential/__init__.py | 15 ++-- pytential/symbolic/execution.py | 145 +++++++++++++++++++++----------- 2 files changed, 106 insertions(+), 54 deletions(-) diff --git a/pytential/__init__.py b/pytential/__init__.py index 728ce196f..1f548deba 100644 --- a/pytential/__init__.py +++ b/pytential/__init__.py @@ -66,8 +66,8 @@ def _integral_op(discr): discr.ambient_dim, discr.dim, sym.var("integrand"))) -def integral(discr, queue, x): - return _integral_op(discr)(queue, integrand=x) +def integral(discr, x): + return _integral_op(discr)(integrand=x) @memoize_on_first_arg @@ -97,23 +97,26 @@ def _norm_inf_op(discr, num_components): return bind(discr, sym.NodeMax(max_arg)) -def norm(discr, queue, x, p=2): +def norm(discr, x, p=2): from pymbolic.geometric_algebra import MultiVector if isinstance(x, MultiVector): x = x.as_vector(np.object) + from meshmode.dof_array import DOFArray num_components = None - if isinstance(x, np.ndarray): + if (isinstance(x, np.ndarray) + and x.dtype.char == "O" + and not isinstance(x, DOFArray)): num_components, = x.shape if p == 2: norm_op = _norm_2_op(discr, num_components) from math import sqrt - return sqrt(norm_op(queue, integrand=x)) + return sqrt(norm_op(integrand=x)) elif p == np.inf or p == "inf": norm_op = _norm_inf_op(discr, num_components) - norm_res = norm_op(queue, arg=x) + norm_res = norm_op(arg=x) if isinstance(norm_res, np.ndarray): return max(norm_res) else: diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 9e3833f07..204b15e01 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -38,6 +38,9 @@ from loopy.version import MOST_RECENT_LANGUAGE_VERSION +from meshmode.array_context import ArrayContext +from meshmode.dof_array import DOFArray, thaw + from pytools import memoize_in from pytential import sym @@ -71,7 +74,7 @@ def mesh_el_view(mesh, group_nr, global_array): class EvaluationMapperBase(PymbolicEvaluationMapper): - def __init__(self, bound_expr, queue, context=None, + def __init__(self, bound_expr, actx, context=None, target_geometry=None, target_points=None, target_normals=None, target_tangents=None): if context is None: @@ -80,7 +83,13 @@ def __init__(self, bound_expr, queue, context=None, self.bound_expr = bound_expr self.places = bound_expr.places - self.queue = queue + self.array_context = actx + + from meshmode.array_context import PyOpenCLArrayContext + if not isinstance(actx, PyOpenCLArrayContext): + raise NotImplementedError("evaluation with non-PyOpenCL array contexts") + + self.queue = actx.queue # {{{ map_XXX @@ -105,10 +114,14 @@ def map_min(self, expr): expr) def map_node_sum(self, expr): - return cl.array.sum(self.rec(expr.operand)).get()[()] + return sum( + cl.array.sum(grp_ary).get()[()] + for grp_ary in self.rec(expr.operand)) def map_node_max(self, expr): - return cl.array.max(self.rec(expr.operand)).get()[()] + return max( + cl.array.max(grp_ary).get()[()] + for grp_ary in self.rec(expr.operand)) def _map_elementwise_reduction(self, reduction_name, expr): @memoize_in(self.places, "elementwise_node_"+reduction_name) @@ -154,6 +167,7 @@ def _reduce(nresult, knl, view): return result discr = self.places.get_discretization( + self.array_context, expr.dofdesc.geometry, expr.dofdesc.discr_stage) operand = self.rec(expr.operand) assert operand.shape == (discr.nnodes,) @@ -181,36 +195,34 @@ def map_elementwise_max(self, expr): def map_ones(self, expr): discr = self.places.get_discretization( + self.array_context, expr.dofdesc.geometry, expr.dofdesc.discr_stage) - result = (discr - .empty(queue=self.queue, dtype=discr.real_dtype) - .with_queue(self.queue)) + result = discr.empty(actx=self.array_context, dtype=discr.real_dtype) - result.fill(1) + for grp_ary in result: + grp_ary.fill(1) return result def map_node_coordinate_component(self, expr): discr = self.places.get_discretization( + self.array_context, expr.dofdesc.geometry, expr.dofdesc.discr_stage) - return discr.nodes()[expr.ambient_axis] \ - .with_queue(self.queue) + return thaw(self.array_context, discr.nodes()[expr.ambient_axis]) def map_num_reference_derivative(self, expr): discr = self.places.get_discretization( + self.array_context, expr.dofdesc.geometry, expr.dofdesc.discr_stage) from pytools import flatten ref_axes = flatten([axis] * mult for axis, mult in expr.ref_axes) - return discr.num_reference_derivative( - self.queue, - ref_axes, self.rec(expr.operand)) \ - .with_queue(self.queue) + return discr.num_reference_derivative(ref_axes, self.rec(expr.operand)) def map_q_weight(self, expr): discr = self.places.get_discretization( + self.array_context, expr.dofdesc.geometry, expr.dofdesc.discr_stage) - return discr.quad_weights(self.queue) \ - .with_queue(self.queue) + return thaw(self.array_context, discr.quad_weights()) def map_inverse(self, expr): bound_op_cache = self.bound_expr.places._get_cache("bound_op") @@ -306,8 +318,7 @@ def map_call(self, expr): if all(isinstance(arg, Number) for arg in args): return getattr(np, expr.function.name)(*args) else: - return getattr(cl.clmath, expr.function.name)( - *args, queue=self.queue) + return self.array_context.special_func(expr.function.name)(*args) else: return super(EvaluationMapperBase, self).map_call(expr) @@ -319,9 +330,9 @@ def map_call(self, expr): class EvaluationMapper(EvaluationMapperBase): - def __init__(self, bound_expr, queue, context=None, + def __init__(self, bound_expr, actx, context=None, timing_data=None): - EvaluationMapperBase.__init__(self, bound_expr, queue, context) + EvaluationMapperBase.__init__(self, bound_expr, actx, context) self.timing_data = timing_data def exec_compute_potential_insn(self, queue, insn, bound_expr, evaluate): @@ -354,13 +365,13 @@ class CostModelMapper(EvaluationMapperBase): data is collected. """ - def __init__(self, bound_expr, queue, context=None, + def __init__(self, bound_expr, actx, context=None, target_geometry=None, target_points=None, target_normals=None, target_tangents=None): if context is None: context = {} EvaluationMapperBase.__init__( - self, bound_expr, queue, context, + self, bound_expr, actx, context, target_geometry, target_points, target_normals, @@ -632,25 +643,8 @@ def __init__(self, places, auto_where=None): raise TypeError("Values in 'places' must be discretization, targets " "or layer potential sources.") - # check cl_context - from pytools import is_single_valued - cl_contexts = [] - for p in six.itervalues(self.places): - if isinstance(p, (PotentialSource, Discretization)): - cl_contexts.append(p.cl_context) - elif isinstance(p, TargetBase): - nodes = p.nodes()[0] - if isinstance(nodes, cl.array.Array) and nodes.queue is not None: - cl_contexts.append(nodes.queue.context) - else: - raise ValueError("unexpected value type in 'places'") - - if not is_single_valued(cl_contexts): - raise RuntimeError("All 'places' must have the same CL context.") - - self.cl_context = cl_contexts[0] - # check ambient_dim + from pytools import is_single_valued ambient_dims = [p.ambient_dim for p in six.itervalues(self.places)] if not is_single_valued(ambient_dims): raise RuntimeError("All 'places' must have the same ambient dimension.") @@ -710,7 +704,7 @@ def _add_conn_to_cache(self, conn, geometry, from_stage, to_stage): cache[key] = conn - def _get_qbx_discretization(self, geometry, discr_stage): + def _get_qbx_discretization(self, actx: ArrayContext, geometry, discr_stage): lpot_source = self.get_geometry(geometry) try: @@ -736,7 +730,7 @@ def get_connection(self, from_dd, to_dd): from pytential.symbolic.dof_connection import connection_from_dds return connection_from_dds(self, from_dd, to_dd) - def get_discretization(self, geometry, discr_stage=None): + def get_discretization(self, actx: ArrayContext, geometry, discr_stage=None): """ :arg dofdesc: a :class:`~pytential.symbolic.primitives.DOFDescriptor` specifying the desired discretization. @@ -747,6 +741,9 @@ def get_discretization(self, geometry, discr_stage=None): the corresponding :class:`~meshmode.discretization.Discretization` in its attributes instead. """ + if not isinstance(actx, ArrayContext): + raise TypeError("first argument must be an ArrayContext") + if discr_stage is None: discr_stage = sym.QBX_SOURCE_STAGE1 discr = self.get_geometry(geometry) @@ -755,7 +752,7 @@ def get_discretization(self, geometry, discr_stage=None): from pytential.source import LayerPotentialSourceBase if isinstance(discr, QBXLayerPotentialSource): - return self._get_qbx_discretization(geometry, discr_stage) + return self._get_qbx_discretization(actx, geometry, discr_stage) elif isinstance(discr, LayerPotentialSourceBase): return discr.density_discr else: @@ -872,7 +869,8 @@ def scipy_op(self, queue, arg_name, dtype, domains=None, **extra_args): return MatVecOp(self, queue, arg_name, dtype, total_dofs, starts_and_ends, extra_args) - def eval(self, queue, context=None, timing_data=None): + def eval(self, context=None, timing_data=None, + array_context: ArrayContext = None): """Evaluate the expression in *self*, using the :class:`pyopencl.CommandQueue` *queue* and the input variables given in the dictionary *context*. @@ -880,25 +878,76 @@ def eval(self, queue, context=None, timing_data=None): :arg timing_data: A dictionary into which timing data will be inserted during evaluation. (experimental) + :arg array_context: only needs to be supplied if no instances of + :class:`~meshmode.dof_array.DOFArray` with a + :class:`~meshmode.array_context.ArrayContext` + are supplied as part of *context*. :returns: the value of the expression, as a scalar, :class:`pyopencl.array.Array`, or an object array of these. """ if context is None: context = {} + + array_contexts = [] + if array_context is not None: + array_contexts.append(array_context) + del array_context + + # {{{ figure array context + + def look_for_array_contexts(ary): + if isinstance(ary, DOFArray): + if ary.array_context is not None: + array_contexts.append(ary.array_context) + elif isinstance(ary, np.ndarray) and ary.dtype.char == "O": + for idx in np.ndindex(ary.shape): + look_for_array_contexts(ary[idx]) + else: + pass + + for key, val in context.items(): + look_for_array_contexts(val) + + if array_contexts: + from pytools import is_single_valued + if not is_single_valued(array_contexts): + raise ValueError("arguments do not agree on an array context") + + array_context = array_contexts[0] + else: + array_context = None + + # }}} + exec_mapper = EvaluationMapper( - self, queue, context, timing_data=timing_data) + self, array_context, context, timing_data=timing_data) return self.code.execute(exec_mapper) - def __call__(self, queue, **args): + def __call__(self, *args, **kwargs): """Evaluate the expression in *self*, using the :class:`pyopencl.CommandQueue` *queue* and the input variables given in the dictionary *context*. :returns: the value of the expression, as a scalar, - :class:`pyopencl.array.Array`, or an object array of these. + :class:`meshmode.dof_array.DOFArray`, or an object array of + these. """ - return self.eval(queue, args) + array_context = None + if len(args) == 1: + array_context, = args + if not isinstance(array_context, ArrayContext): + raise TypeError("first positional argument must be of type " + "ArrayContext") + + elif not args: + pass + + else: + raise TypeError("More than one positional argument supplied. " + "None or an ArrayContext expected.") + + return self.eval(kwargs, array_context=array_context) def bind(places, expr, auto_where=None): From 66fc944064121c7507d317c22b401f43dacc5fae Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 17 Jun 2020 18:27:50 -0500 Subject: [PATCH 02/75] Improve ArrayContext processing in BoundOperator.{__call__,eval} --- pytential/symbolic/execution.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index 204b15e01..fdd5db5fd 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -25,6 +25,8 @@ THE SOFTWARE. """ +from typing import Optional + import six from six.moves import zip @@ -870,7 +872,7 @@ def scipy_op(self, queue, arg_name, dtype, domains=None, **extra_args): arg_name, dtype, total_dofs, starts_and_ends, extra_args) def eval(self, context=None, timing_data=None, - array_context: ArrayContext = None): + array_context: Optional[ArrayContext] = None): """Evaluate the expression in *self*, using the :class:`pyopencl.CommandQueue` *queue* and the input variables given in the dictionary *context*. @@ -889,13 +891,17 @@ def eval(self, context=None, timing_data=None, if context is None: context = {} + # {{{ figure array context + array_contexts = [] if array_context is not None: + if not isinstance(array_context, ArrayContext): + raise TypeError( + "first argument (if supplied) must be an ArrayContext") + array_contexts.append(array_context) del array_context - # {{{ figure array context - def look_for_array_contexts(ary): if isinstance(ary, DOFArray): if ary.array_context is not None: @@ -937,8 +943,8 @@ def __call__(self, *args, **kwargs): if len(args) == 1: array_context, = args if not isinstance(array_context, ArrayContext): - raise TypeError("first positional argument must be of type " - "ArrayContext") + raise TypeError("first positional argument (if given) " + "must be of type ArrayContext") elif not args: pass From 3dbc2c84cc3624628cd4d1fbe634e9fb29729361 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 17 Jun 2020 18:29:52 -0500 Subject: [PATCH 03/75] Track introduction of ArrayContext.np --- 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 fdd5db5fd..d19c8d67c 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -320,7 +320,7 @@ def map_call(self, expr): if all(isinstance(arg, Number) for arg in args): return getattr(np, expr.function.name)(*args) else: - return self.array_context.special_func(expr.function.name)(*args) + return getattr(self.array_context.np, expr.function.name)(*args) else: return super(EvaluationMapperBase, self).map_call(expr) From f0e5c54e978a20ab59da9d375d1ddb0501bd12b5 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Tue, 30 Jun 2020 18:35:12 -0500 Subject: [PATCH 04/75] symbolic.primitives: Fix the dimension check for a few 3D-surface-only operators --- pytential/symbolic/primitives.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 1799e09b4..bb5dfcbfa 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -759,7 +759,7 @@ 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: + if not (ambient_dim == 3 and dim == 2): raise NotImplementedError("only available for surfaces in 3D") pd_mat = parametrization_derivative_matrix(ambient_dim, dim, dofdesc) @@ -782,7 +782,7 @@ def second_fundamental_form(ambient_dim, dim=None, dofdesc=None): if dim is None: dim = ambient_dim - 1 - if ambient_dim != 3 and dim != 2: + if not (ambient_dim == 3 and dim == 2): raise NotImplementedError("only available for surfaces in 3D") r = nodes(ambient_dim, dofdesc=dofdesc).as_vector() @@ -814,7 +814,7 @@ def shape_operator(ambient_dim, dim=None, dofdesc=None): if dim is None: dim = ambient_dim - 1 - if ambient_dim != 3 and dim != 2: + if not (ambient_dim == 3 and dim == 2): raise NotImplementedError("only available for surfaces in 3D") # https://en.wikipedia.org/w/index.php?title=Differential_geometry_of_surfaces&oldid=833587563 From 273ba51a959b35dd007b39957a8d15765dca53ef Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Wed, 1 Jul 2020 22:07:20 -0500 Subject: [PATCH 05/75] CI: Disable tests, only keep examples for now --- .gitlab-ci.yml | 150 ++++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f9c12b45f..8c87a27dc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,42 +4,42 @@ # which skips the slow running tests. # * SKIP_EXAMPLES, if non-empty, can be used to skip the examples job. -Python 3 POCL: - script: - - export PY_EXE=python3 - - export PYOPENCL_TEST=portable:pthread - - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} - - export EXTRA_INSTALL="Cython pybind11 numpy scipy mako" - - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh - - ". ./build-and-test-py-project.sh" - tags: - - python3 - - pocl - - large-node - except: - - tags - artifacts: - reports: - junit: test/pytest.xml - -Python 3 Intel: - script: - - export PY_EXE=python3 - - source /opt/enable-intel-cl.sh - - export PYOPENCL_TEST="intel(r):pu" - - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} - - export EXTRA_INSTALL="Cython pybind11 numpy scipy mako" - - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh - - ". ./build-and-test-py-project.sh" - tags: - - python3 - - intel-cl-cpu - - large-node - except: - - tags - artifacts: - reports: - junit: test/pytest.xml +# Python 3 POCL: +# script: +# - export PY_EXE=python3 +# - export PYOPENCL_TEST=portable:pthread +# - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} +# - export EXTRA_INSTALL="Cython pybind11 numpy scipy mako" +# - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh +# - ". ./build-and-test-py-project.sh" +# tags: +# - python3 +# - pocl +# - large-node +# except: +# - tags +# artifacts: +# reports: +# junit: test/pytest.xml +# +# Python 3 Intel: +# script: +# - export PY_EXE=python3 +# - source /opt/enable-intel-cl.sh +# - export PYOPENCL_TEST="intel(r):pu" +# - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} +# - export EXTRA_INSTALL="Cython pybind11 numpy scipy mako" +# - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh +# - ". ./build-and-test-py-project.sh" +# tags: +# - python3 +# - intel-cl-cpu +# - large-node +# except: +# - tags +# artifacts: +# reports: +# junit: test/pytest.xml Python 3 POCL Examples: script: @@ -56,45 +56,45 @@ Python 3 POCL Examples: except: - tags -Python 3 Conda: - script: - - export SUMPY_FORCE_SYMBOLIC_BACKEND=symengine - - export CONDA_ENVIRONMENT=.test-conda-env-py3.yml - - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} - - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh - - ". ./build-and-test-py-project-within-miniconda.sh" - tags: - - linux - - large-node - except: - - tags - - artifacts: - reports: - junit: test/pytest.xml - -Python 3 Conda Apple: - script: - - export LC_ALL=en_US.UTF-8 - - export LANG=en_US.UTF-8 - - export CONDA_ENVIRONMENT=.test-conda-env-py3-macos.yml - - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} - - export CC=clang - # https://stackoverflow.com/q/60934005; https://reviews.llvm.org/D71579 - - export LDFLAGS="-mlinker-version=519" - - set -o xtrace - - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh - - ". ./build-and-test-py-project-within-miniconda.sh" - - tags: - - apple - except: - - tags - retry: 2 - - artifacts: - reports: - junit: test/pytest.xml +# Python 3 Conda: +# script: +# - export SUMPY_FORCE_SYMBOLIC_BACKEND=symengine +# - export CONDA_ENVIRONMENT=.test-conda-env-py3.yml +# - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} +# - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh +# - ". ./build-and-test-py-project-within-miniconda.sh" +# tags: +# - linux +# - large-node +# except: +# - tags +# +# artifacts: +# reports: +# junit: test/pytest.xml +# +# Python 3 Conda Apple: +# script: +# - export LC_ALL=en_US.UTF-8 +# - export LANG=en_US.UTF-8 +# - export CONDA_ENVIRONMENT=.test-conda-env-py3-macos.yml +# - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} +# - export CC=clang +# # https://stackoverflow.com/q/60934005; https://reviews.llvm.org/D71579 +# - export LDFLAGS="-mlinker-version=519" +# - set -o xtrace +# - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh +# - ". ./build-and-test-py-project-within-miniconda.sh" +# +# tags: +# - apple +# except: +# - tags +# retry: 2 +# +# artifacts: +# reports: +# junit: test/pytest.xml Documentation: script: From cf635e0b4ea9c899473f7b602df32f2954823a31 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Wed, 1 Jul 2020 22:31:09 -0500 Subject: [PATCH 06/75] Update requirements.txt --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 625deb28d..e5cac035f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,6 @@ git+https://github.com/inducer/pyopencl git+https://github.com/inducer/islpy git+https://github.com/inducer/loopy git+https://gitlab.tiker.net/inducer/boxtree -git+https://github.com/inducer/meshmode -git+https://gitlab.tiker.net/inducer/sumpy +git+https://gitlab.tiker.net/inducer/meshmode@array-context +git+https://gitlab.tiker.net/inducer/sumpy@layerpotential-obj-array git+https://gitlab.tiker.net/inducer/pyfmmlib From 459ec80e1423c2637f7ca2dd58df9f06ee7bdaa0 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 00:06:13 -0500 Subject: [PATCH 07/75] Port over examples --- examples/cost.py | 45 +-- examples/fmm-error.py | 26 +- examples/helmholtz-dirichlet.py | 32 ++- examples/laplace-dirichlet-3d.py | 30 +- examples/layerpot-3d.py | 30 +- examples/layerpot.py | 52 ++-- examples/scaling-study.py | 22 +- pytential/qbx/__init__.py | 152 ++++++---- pytential/qbx/direct.py | 6 +- pytential/qbx/fmm.py | 20 +- pytential/qbx/geometry.py | 259 +++++++++-------- pytential/qbx/refinement.py | 53 ++-- pytential/qbx/target_assoc.py | 33 ++- pytential/qbx/utils.py | 57 ++-- pytential/solve.py | 89 ++---- pytential/source.py | 85 +++--- pytential/symbolic/compiler.py | 26 +- pytential/symbolic/dof_connection.py | 119 ++++---- pytential/symbolic/execution.py | 267 ++++++++++-------- pytential/symbolic/pde/maxwell/__init__.py | 12 +- .../symbolic/pde/maxwell/generalized_debye.py | 16 +- pytential/symbolic/primitives.py | 43 +-- pytential/target.py | 3 + pytential/unregularized.py | 4 +- pytential/version.py | 2 +- setup.py | 2 +- 26 files changed, 815 insertions(+), 670 deletions(-) diff --git a/examples/cost.py b/examples/cost.py index 71c116804..0070aaa64 100644 --- a/examples/cost.py +++ b/examples/cost.py @@ -2,6 +2,8 @@ import pyopencl as cl import numpy as np +from meshmode.array_context import PyOpenCLArrayContext +from meshmode.dof_array import thaw from pytential import sym, bind from pytools import one @@ -26,7 +28,7 @@ TESTING_ARMS = (20,) -def starfish_lpot_source(queue, n_arms): +def starfish_lpot_source(actx, n_arms): from meshmode.discretization import Discretization from meshmode.discretization.poly_element import ( InterpolatoryQuadratureSimplexGroupFactory) @@ -39,7 +41,7 @@ def starfish_lpot_source(queue, n_arms): TARGET_ORDER) pre_density_discr = Discretization( - queue.context, mesh, + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(TARGET_ORDER)) lpot_kwargs = DEFAULT_LPOT_KWARGS.copy() @@ -60,14 +62,14 @@ def starfish_lpot_source(queue, n_arms): # }}} -def training_geometries(queue): +def training_geometries(actx): for n_arms in TRAINING_ARMS: - yield starfish_lpot_source(queue, n_arms) + yield starfish_lpot_source(actx, n_arms) -def test_geometries(queue): +def test_geometries(actx): for n_arms in TESTING_ARMS: - yield starfish_lpot_source(queue, n_arms) + yield starfish_lpot_source(actx, n_arms) def get_bound_op(places): @@ -79,15 +81,15 @@ def get_bound_op(places): return bind(places, op) -def get_test_density(queue, density_discr): - nodes = density_discr.nodes().with_queue(queue) - sigma = cl.clmath.sin(10 * nodes[0]) - +def get_test_density(actx, density_discr): + nodes = thaw(actx, density_discr.nodes()) + sigma = actx.np.sin(10 * nodes[0]) return sigma def calibrate_cost_model(ctx): queue = cl.CommandQueue(ctx) + actx = PyOpenCLArrayContext(queue) from pytential.qbx.cost import CostModel, estimate_calibration_params cost_model = CostModel() @@ -95,7 +97,7 @@ def calibrate_cost_model(ctx): model_results = [] timing_results = [] - for lpot_source in training_geometries(queue): + for lpot_source in training_geometries(actx): lpot_source = lpot_source.copy(cost_model=cost_model) from pytential import GeometryCollection @@ -103,16 +105,17 @@ def calibrate_cost_model(ctx): density_discr = places.get_discretization(places.auto_source.geometry) bound_op = get_bound_op(places) - sigma = get_test_density(queue, density_discr) + sigma = get_test_density(actx, density_discr) - cost_S = bound_op.get_modeled_cost(queue, sigma=sigma) + cost_S = bound_op.get_modeled_cost(actx, sigma=sigma) # Warm-up run. - bound_op.eval(queue, {"sigma": sigma}) + bound_op.eval({"sigma": sigma}, array_context=actx) for _ in range(RUNS): timing_data = {} - bound_op.eval(queue, {"sigma": sigma}, timing_data=timing_data) + bound_op.eval({"sigma": sigma}, array_context=actx, + timing_data=timing_data) model_results.append(one(cost_S.values())) timing_results.append(one(timing_data.values())) @@ -125,8 +128,9 @@ def calibrate_cost_model(ctx): def test_cost_model(ctx, cost_model): queue = cl.CommandQueue(ctx) + actx = PyOpenCLArrayContext(queue) - for lpot_source in test_geometries(queue): + for lpot_source in test_geometries(actx): lpot_source = lpot_source.copy(cost_model=cost_model) from pytential import GeometryCollection @@ -134,20 +138,21 @@ def test_cost_model(ctx, cost_model): density_discr = places.get_discretization(places.auto_source.geometry) bound_op = get_bound_op(places) - sigma = get_test_density(queue, density_discr) + sigma = get_test_density(actx, density_discr) - cost_S = bound_op.get_modeled_cost(queue, sigma=sigma) + cost_S = bound_op.get_modeled_cost(actx, sigma=sigma) model_result = ( one(cost_S.values()) .get_predicted_times(merge_close_lists=True)) # Warm-up run. - bound_op.eval(queue, {"sigma": sigma}) + bound_op.eval({"sigma": sigma}, array_context=actx) temp_timing_results = [] for _ in range(RUNS): timing_data = {} - bound_op.eval(queue, {"sigma": sigma}, timing_data=timing_data) + bound_op.eval({"sigma": sigma}, + array_context=actx, timing_data=timing_data) temp_timing_results.append(one(timing_data.values())) timing_result = {} diff --git a/examples/fmm-error.py b/examples/fmm-error.py index a6d19bb15..77281aa58 100644 --- a/examples/fmm-error.py +++ b/examples/fmm-error.py @@ -1,6 +1,8 @@ from __future__ import division import numpy as np import pyopencl as cl +from meshmode.array_context import PyOpenCLArrayContext +from meshmode.dof_array import thaw from meshmode.mesh.generation import ( # noqa make_curve_mesh, starfish, ellipse, drop) from sumpy.visualization import FieldPlotter @@ -13,6 +15,7 @@ def main(): cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) target_order = 16 qbx_order = 3 @@ -37,7 +40,7 @@ def main(): InterpolatoryQuadratureSimplexGroupFactory pre_density_discr = Discretization( - cl_ctx, mesh, + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) unaccel_qbx = QBXLayerPotentialSource( @@ -57,24 +60,27 @@ def main(): }) density_discr = places.get_discretization("unaccel_qbx") - nodes = density_discr.nodes().with_queue(queue) - angle = cl.clmath.atan2(nodes[1], nodes[0]) + nodes = thaw(actx, density_discr.nodes()) + angle = actx.np.atan2(nodes[1], nodes[0]) from pytential import bind, sym - #op = sym.d_dx(sym.S(kernel, sym.var("sigma")), qbx_forced_limit=None) - #op = sym.D(kernel, sym.var("sigma"), qbx_forced_limit=None) - op = sym.S(kernel, sym.var("sigma"), qbx_forced_limit=None) + kwargs = {"k": sym.var("k")} if k else {} + #op = sym.d_dx( + # sym.S(kernel, sym.var("sigma")), qbx_forced_limit=None, **kwargs) + #op = sym.D(kernel, sym.var("sigma"), qbx_forced_limit=None, **kwargs) + op = sym.S(kernel, sym.var("sigma"), qbx_forced_limit=None, **kwargs) - sigma = cl.clmath.cos(mode_nr*angle) + sigma = actx.np.cos(mode_nr*angle) if isinstance(kernel, HelmholtzKernel): - sigma = sigma.astype(np.complex128) + for i, elem in np.ndenumerate(sigma): + sigma[i] = elem.astype(np.complex128) fld_in_vol = bind(places, op, auto_where=("unaccel_qbx", "targets"))( - queue, sigma=sigma, k=k).get() + actx, sigma=sigma, k=k).get() fmm_fld_in_vol = bind(places, op, auto_where=("qbx", "targets"))( - queue, sigma=sigma, k=k).get() + actx, sigma=sigma, k=k).get() err = fmm_fld_in_vol-fld_in_vol diff --git a/examples/helmholtz-dirichlet.py b/examples/helmholtz-dirichlet.py index 75115da4a..aee8c3908 100644 --- a/examples/helmholtz-dirichlet.py +++ b/examples/helmholtz-dirichlet.py @@ -3,6 +3,7 @@ import pyopencl as cl import pyopencl.clmath # noqa +from meshmode.array_context import PyOpenCLArrayContext from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory @@ -29,6 +30,7 @@ def main(mesh_name="ellipse", visualize=False): cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) from meshmode.mesh.generation import ellipse, make_curve_mesh from functools import partial @@ -67,7 +69,7 @@ def main(mesh_name="ellipse", visualize=False): raise ValueError("unknown mesh name: {}".format(mesh_name)) pre_density_discr = Discretization( - cl_ctx, mesh, + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order)) from pytential.qbx import ( @@ -79,7 +81,7 @@ def main(mesh_name="ellipse", visualize=False): from sumpy.visualization import FieldPlotter fplot = FieldPlotter(np.zeros(2), extent=5, npoints=500) - targets = cl.array.to_device(queue, fplot.points) + targets = actx.from_numpy(fplot.points) from pytential import GeometryCollection places = GeometryCollection({ @@ -120,21 +122,22 @@ def main(mesh_name="ellipse", visualize=False): # {{{ fix rhs and solve - nodes = density_discr.nodes().with_queue(queue) + from meshmode.dof_array import thaw + nodes = thaw(actx, density_discr.nodes()) k_vec = np.array([2, 1]) k_vec = k * k_vec / la.norm(k_vec, 2) def u_incoming_func(x): - return cl.clmath.exp( + return actx.np.exp( 1j * (x[0] * k_vec[0] + x[1] * k_vec[1])) bc = -u_incoming_func(nodes) - bvp_rhs = bind(places, sqrt_w*sym.var("bc"))(queue, bc=bc) + bvp_rhs = bind(places, sqrt_w*sym.var("bc"))(actx, bc=bc) from pytential.solve import gmres gmres_result = gmres( - bound_op.scipy_op(queue, sigma_sym.name, dtype=np.complex128, k=k), + bound_op.scipy_op(actx, sigma_sym.name, dtype=np.complex128, k=k), bvp_rhs, tol=1e-8, progress=True, stall_iterations=0, hard_failure=True) @@ -152,15 +155,18 @@ def u_incoming_func(x): - sym.D(kernel, inv_sqrt_w_sigma, k=k_sym, **repr_kwargs)) u_incoming = u_incoming_func(targets) - ones_density = density_discr.zeros(queue) - ones_density.fill(1) + ones_density = density_discr.zeros(actx) + for elem in ones_density: + elem.fill(1) - indicator = bind(places, sym.D(LaplaceKernel(2), sigma_sym, **repr_kwargs))( - queue, sigma=ones_density).get() + indicator = actx.to_numpy( + bind(places, sym.D(LaplaceKernel(2), sigma_sym, **repr_kwargs))( + actx, sigma=ones_density)) try: - fld_in_vol = bind(places, representation_sym)( - queue, sigma=gmres_result.solution, k=k).get() + fld_in_vol = actx.to_numpy( + bind(places, representation_sym)( + actx, sigma=gmres_result.solution, k=k)) except QBXTargetAssociationFailedException as e: fplot.write_vtk_file("helmholtz-dirichlet-failed-targets.vts", [ ("failed", e.failed_target_flags.get(queue)) @@ -171,7 +177,7 @@ def u_incoming_func(x): fplot.write_vtk_file("helmholtz-dirichlet-potential.vts", [ ("potential", fld_in_vol), ("indicator", indicator), - ("u_incoming", u_incoming.get()), + ("u_incoming", actx.to_numpy(u_incoming)), ]) # }}} diff --git a/examples/laplace-dirichlet-3d.py b/examples/laplace-dirichlet-3d.py index 984f1de10..7f6bba458 100644 --- a/examples/laplace-dirichlet-3d.py +++ b/examples/laplace-dirichlet-3d.py @@ -3,6 +3,7 @@ import pyopencl as cl import pyopencl.clmath # noqa +from meshmode.array_context import PyOpenCLArrayContext from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory @@ -28,6 +29,7 @@ def main(mesh_name="torus", visualize=False): cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) if mesh_name == "torus": rout = 10 @@ -61,7 +63,7 @@ def main(mesh_name="torus", visualize=False): raise ValueError("unknown mesh name: {}".format(mesh_name)) pre_density_discr = Discretization( - cl_ctx, mesh, + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order)) from pytential.qbx import ( @@ -73,7 +75,7 @@ def main(mesh_name="torus", visualize=False): from sumpy.visualization import FieldPlotter fplot = FieldPlotter(np.zeros(3), extent=20, npoints=50) - targets = cl.array.to_device(queue, fplot.points) + targets = actx.from_numpy(fplot.points) from pytential import GeometryCollection places = GeometryCollection({ @@ -109,33 +111,39 @@ def main(mesh_name="torus", visualize=False): # {{{ fix rhs and solve - nodes = density_discr.nodes().with_queue(queue) + from meshmode.dof_array import thaw, flatten, unflatten + nodes = thaw(actx, density_discr.nodes()) source = np.array([rout, 0, 0]) def u_incoming_func(x): + from pytools.obj_array import obj_array_vectorize + x = obj_array_vectorize(actx.to_numpy, flatten(x)) + x = np.array(list(x)) # return 1/cl.clmath.sqrt( (x[0] - source[0])**2 # +(x[1] - source[1])**2 # +(x[2] - source[2])**2 ) - return 1.0/la.norm(x.get()-source[:, None], axis=0) + return 1.0/la.norm(x - source[:, None], axis=0) - bc = cl.array.to_device(queue, u_incoming_func(nodes)) + bc = unflatten(actx, + density_discr, + actx.from_numpy(u_incoming_func(nodes))) - bvp_rhs = bind(places, sqrt_w*sym.var("bc"))(queue, bc=bc) + bvp_rhs = bind(places, sqrt_w*sym.var("bc"))(actx, bc=bc) from pytential.solve import gmres gmres_result = gmres( - bound_op.scipy_op(queue, "sigma", dtype=np.float64), + bound_op.scipy_op(actx, "sigma", dtype=np.float64), bvp_rhs, tol=1e-14, progress=True, stall_iterations=0, hard_failure=True) sigma = bind(places, sym.var("sigma")/sqrt_w)( - queue, sigma=gmres_result.solution) + actx, sigma=gmres_result.solution) # }}} from meshmode.discretization.visualization import make_visualizer - bdry_vis = make_visualizer(queue, density_discr, 20) + bdry_vis = make_visualizer(actx, density_discr, 20) bdry_vis.write_vtk_file("laplace.vtu", [ ("sigma", sigma), ]) @@ -151,8 +159,8 @@ def u_incoming_func(x): + sym.D(kernel, inv_sqrt_w_sigma, **repr_kwargs)) try: - fld_in_vol = bind(places, representation_sym)( - queue, sigma=sigma).get() + fld_in_vol = actx.to_numpy( + bind(places, representation_sym)(actx, sigma=sigma)) except QBXTargetAssociationFailedException as e: fplot.write_vtk_file("laplace-dirichlet-3d-failed-targets.vts", [ ("failed", e.failed_target_flags.get(queue)), diff --git a/examples/layerpot-3d.py b/examples/layerpot-3d.py index ecace75de..78112858c 100644 --- a/examples/layerpot-3d.py +++ b/examples/layerpot-3d.py @@ -1,5 +1,5 @@ -from __future__ import division - +from meshmode.array_context import PyOpenCLArrayContext +from meshmode.dof_array import thaw import numpy as np import pyopencl as cl @@ -22,6 +22,7 @@ def main(mesh_name="ellipsoid"): cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) if mesh_name == "ellipsoid": cad_file_name = "geometries/ellipsoid.step" @@ -55,7 +56,7 @@ def main(mesh_name="ellipsoid"): InterpolatoryQuadratureSimplexGroupFactory density_discr = Discretization( - cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource(density_discr, 4*target_order, qbx_order, fmm_order=qbx_order + 3, @@ -71,8 +72,8 @@ def main(mesh_name="ellipsoid"): }, auto_where="qbx") density_discr = places.get_discretization("qbx") - nodes = density_discr.nodes().with_queue(queue) - angle = cl.clmath.atan2(nodes[1], nodes[0]) + nodes = thaw(actx, density_discr.nodes()) + angle = actx.np.atan2(nodes[1], nodes[0]) if k: kernel = HelmholtzKernel(3) @@ -83,18 +84,22 @@ def main(mesh_name="ellipsoid"): op = sym.D(kernel, sym.var("sigma"), qbx_forced_limit=None) #op = sym.S(kernel, sym.var("sigma"), qbx_forced_limit=None) - sigma = cl.clmath.cos(mode_nr*angle) + sigma = actx.np.cos(mode_nr*angle) if 0: - sigma = 0*angle + from meshmode.dof_array import flatten, unflatten + sigma = flatten(0 * angle) from random import randrange for i in range(5): sigma[randrange(len(sigma))] = 1 + sigma = unflatten(actx, density_discr, sigma) if isinstance(kernel, HelmholtzKernel): - sigma = sigma.astype(np.complex128) + for i, elem in np.ndenumerate(sigma): + sigma[i] = elem.astype(np.complex128) - fld_in_vol = bind(places, op, auto_where=("qbx", "targets"))( - queue, sigma=sigma, k=k).get() + fld_in_vol = actx.to_numpy( + bind(places, op, auto_where=("qbx", "targets"))( + actx, sigma=sigma, k=k)) #fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5) fplot.write_vtk_file("layerpot-3d-potential.vts", [ @@ -102,11 +107,10 @@ def main(mesh_name="ellipsoid"): ]) bdry_normals = bind(places, - sym.normal(density_discr.ambient_dim))(queue).as_vector(dtype=object) + sym.normal(density_discr.ambient_dim))(actx).as_vector(dtype=object) from meshmode.discretization.visualization import make_visualizer - bdry_vis = make_visualizer(queue, density_discr, target_order) - + bdry_vis = make_visualizer(actx, density_discr, target_order) bdry_vis.write_vtk_file("layerpot-3d-density.vtu", [ ("sigma", sigma), ("bdry_normals", bdry_normals), diff --git a/examples/layerpot.py b/examples/layerpot.py index e01a24eb8..4980be674 100644 --- a/examples/layerpot.py +++ b/examples/layerpot.py @@ -23,8 +23,8 @@ def main(curve_fn=starfish, visualize=True): - import logging - logging.basicConfig(level=logging.WARNING) # INFO for more progress info + #import logging + #logging.basicConfig(level=logging.WARNING) # INFO for more progress info cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) @@ -36,12 +36,15 @@ def main(curve_fn=starfish, visualize=True): target_order) from pytential.qbx import QBXLayerPotentialSource + from meshmode.array_context import PyOpenCLArrayContext from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory + actx = PyOpenCLArrayContext(queue) + pre_density_discr = Discretization( - cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource(pre_density_discr, 4*target_order, qbx_order, fmm_order=qbx_order+3, @@ -56,10 +59,12 @@ def main(curve_fn=starfish, visualize=True): "qbx": qbx, "targets": PointsTarget(targets_dev), }, auto_where="qbx") + density_discr = places.get_discretization("qbx") - nodes = density_discr.nodes().with_queue(queue) - angle = cl.clmath.atan2(nodes[1], nodes[0]) + from meshmode.dof_array import thaw + nodes = thaw(actx, density_discr.nodes()) + angle = actx.np.atan2(nodes[1], nodes[0]) if k: kernel = HelmholtzKernel(2) @@ -75,22 +80,26 @@ def op(**kwargs): return sym.D(kernel, sym.var("sigma"), **kwargs) #op = sym.S(kernel, sym.var("sigma"), qbx_forced_limit=None, **kwargs) - sigma = cl.clmath.cos(mode_nr*angle) + sigma = actx.np.cos(mode_nr*angle) if 0: - sigma = 0*angle + from meshmode.dof_array import flatten, unflatten + sigma = flatten(0 * angle) from random import randrange for i in range(5): sigma[randrange(len(sigma))] = 1 + sigma = unflatten(actx, density_discr, sigma) if isinstance(kernel, HelmholtzKernel): - sigma = sigma.astype(np.complex128) + for i, elem in np.ndenumerate(sigma): + sigma[i] = elem.astype(np.complex128) bound_bdry_op = bind(places, op()) if visualize: - fld_in_vol = bind(places, op( - source="qbx", - target="targets", - qbx_forced_limit=None))(queue, sigma=sigma, k=k).get() + fld_in_vol = actx.to_numpy( + bind(places, op( + source="qbx", + target="targets", + qbx_forced_limit=None))(actx, sigma=sigma, k=k)) if enable_mayavi: fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5) @@ -100,13 +109,9 @@ def op(**kwargs): ]) if 0: - def apply_op(density): - return bound_bdry_op( - queue, sigma=cl.array.to_device(queue, density), k=k).get() - + apply_op = bound_bdry_op.scipy_op(actx, "sigma", np.float64, k=k) from sumpy.tools import build_matrix - n = len(sigma) - mat = build_matrix(apply_op, dtype=np.float64, shape=(n, n)) + mat = build_matrix(apply_op) import matplotlib.pyplot as pt pt.imshow(mat) @@ -116,9 +121,16 @@ def apply_op(density): if enable_mayavi: # {{{ plot boundary field - fld_on_bdry = bound_bdry_op(queue, sigma=sigma, k=k).get() + from meshmode.dof_array import flatten + from pytools.obj_array import obj_array_vectorize + + fld_on_bdry = actx.to_numpy( + flatten(bound_bdry_op(actx, sigma=sigma, k=k))) + + nodes_host = obj_array_vectorize( + actx.to_numpy, + thaw(actx, density_discr.nodes())) - nodes_host = density_discr.nodes().get(queue=queue) mlab.points3d(nodes_host[0], nodes_host[1], fld_on_bdry.real, scale_factor=0.03) diff --git a/examples/scaling-study.py b/examples/scaling-study.py index 21a85019f..a14a5aed0 100644 --- a/examples/scaling-study.py +++ b/examples/scaling-study.py @@ -2,6 +2,7 @@ import pyopencl as cl import pyopencl.clmath # noqa +from meshmode.array_context import PyOpenCLArrayContext from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory @@ -58,11 +59,12 @@ def timing_run(nx, ny, visualize=False): cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) mesh = make_mesh(nx=nx, ny=ny, visualize=visualize) density_discr = Discretization( - cl_ctx, mesh, + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order)) from pytential.qbx import ( @@ -76,7 +78,7 @@ def timing_run(nx, ny, visualize=False): if visualize: from sumpy.visualization import FieldPlotter fplot = FieldPlotter(np.zeros(2), extent=5, npoints=1500) - targets = PointsTarget(cl.array.to_device(queue, fplot.points)) + targets = PointsTarget(actx.from_numpy(fplot.points)) places.update({ "plot-targets": targets, @@ -119,10 +121,12 @@ def timing_run(nx, ny, visualize=False): # {{{ fix rhs and solve mode_nr = 3 - nodes = density_discr.nodes().with_queue(queue) - angle = cl.clmath.atan2(nodes[1], nodes[0]) - sigma = cl.clmath.cos(mode_nr*angle) + from meshmode.dof_array import thaw + nodes = thaw(actx, density_discr.nodes()) + angle = actx.np.atan2(nodes[1], nodes[0]) + + sigma = actx.np.cos(mode_nr*angle) # }}} @@ -134,17 +138,17 @@ def timing_run(nx, ny, visualize=False): bound_op = bind(places, sym_op) print("FMM WARM-UP RUN 1: %5d elements" % mesh.nelements) - bound_op(queue, sigma=sigma, k=k) + bound_op(actx, sigma=sigma, k=k) queue.finish() print("FMM WARM-UP RUN 2: %5d elements" % mesh.nelements) - bound_op(queue, sigma=sigma, k=k) + bound_op(actx, sigma=sigma, k=k) queue.finish() from time import time t_start = time() - bound_op(queue, sigma=sigma, k=k) - queue.finish() + bound_op(actx, sigma=sigma, k=k) + actx.queue.finish() elapsed = time() - t_start print("FMM TIMING RUN: %5d elements -> %g s" diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index a23dd3d71..31930f859 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -from __future__ import division, absolute_import +from __future__ import annotations __copyright__ = "Copyright (C) 2013 Andreas Kloeckner" @@ -25,8 +24,11 @@ import six +from meshmode.array_context import PyOpenCLArrayContext +from meshmode.dof_array import flatten, thaw import numpy as np -from pytools import memoize_method +from pytools import memoize_method, memoize_in + from pytential.qbx.target_assoc import QBXTargetAssociationFailedException from pytential.source import LayerPotentialSourceBase @@ -221,6 +223,10 @@ def fmm_level_to_order(kernel, kernel_args, tree, level): # noqa pylint:disable # back if the layer potential source is ever copied. (such as # during refinement) + @property + def _setup_actx(self): + return self.density_discr._setup_actx + def copy( self, density_discr=None, @@ -335,24 +341,57 @@ def copy( # }}} + # {{{ code containers + @property - @memoize_method def tree_code_container(self): - from pytential.qbx.utils import TreeCodeContainer - return TreeCodeContainer(self.cl_context) + @memoize_in(self._setup_actx, ( + QBXLayerPotentialSource, "tree_code_container")) + def make_container(): + from pytential.qbx.utils import TreeCodeContainer + return TreeCodeContainer(self._setup_actx) + return make_container() @property - @memoize_method def refiner_code_container(self): - from pytential.qbx.refinement import RefinerCodeContainer - return RefinerCodeContainer(self.cl_context, self.tree_code_container) + @memoize_in(self._setup_actx, ( + QBXLayerPotentialSource, "refiner_code_container")) + def make_container(): + from pytential.qbx.refinement import RefinerCodeContainer + return RefinerCodeContainer( + self._setup_actx, self.tree_code_container) + return make_container() @property - @memoize_method def target_association_code_container(self): - from pytential.qbx.target_assoc import TargetAssociationCodeContainer - return TargetAssociationCodeContainer( - self.cl_context, self.tree_code_container) + @memoize_in(self._setup_actx, ( + QBXLayerPotentialSource, "target_association_code_container")) + def make_container(): + from pytential.qbx.target_assoc import TargetAssociationCodeContainer + return TargetAssociationCodeContainer( + self._setup_actx, self.tree_code_container) + return make_container() + + @property + def qbx_fmm_geometry_data_code_container(self): + @memoize_in(self._setup_actx, ( + QBXLayerPotentialSource, + "qbx_fmm_geometry_data_code_container")) + def make_container( + debug, ambient_dim, well_sep_is_n_away, + from_sep_smaller_crit): + from pytential.qbx.geometry import QBXFMMGeometryDataCodeContainer + return QBXFMMGeometryDataCodeContainer( + self._setup_actx, + ambient_dim, self.tree_code_container, debug, + _well_sep_is_n_away=well_sep_is_n_away, + _from_sep_smaller_crit=from_sep_smaller_crit) + + return make_container( + self.debug, self.ambient_dim, + self._well_sep_is_n_away, self._from_sep_smaller_crit) + + # }}} # {{{ internal API @@ -371,7 +410,7 @@ def qbx_fmm_geometry_data(self, places, name, from pytential.qbx.geometry import QBXFMMGeometryData return QBXFMMGeometryData(places, name, - self.qbx_fmm_code_getter, + self.qbx_fmm_geometry_data_code_container, target_discrs_and_qbx_sides, target_association_tolerance=self.target_association_tolerance, tree_kind=self._tree_kind, @@ -445,8 +484,8 @@ def drive_cost_model( cost_model_result = ( self.cost_model(wrangler, geo_data, kernel, kernel_arguments)) - from pytools.obj_array import with_object_array_or_scalar - output_placeholder = with_object_array_or_scalar( + from pytools.obj_array import obj_array_vectorize + output_placeholder = obj_array_vectorize( wrangler.finalize_potentials, wrangler.full_output_zeros() ) @@ -471,15 +510,6 @@ def _dispatch_compute_potential_insn(self, queue, insn, bound_expr, return func(queue, insn, bound_expr, evaluate, **extra_args) - @property - @memoize_method - def qbx_fmm_code_getter(self): - from pytential.qbx.geometry import QBXFMMGeometryCodeGetter - return QBXFMMGeometryCodeGetter(self.cl_context, self.ambient_dim, - self.tree_code_container, debug=self.debug, - _well_sep_is_n_away=self._well_sep_is_n_away, - _from_sep_smaller_crit=self._from_sep_smaller_crit) - # {{{ fmm-based execution @memoize_method @@ -542,8 +572,8 @@ def get_target_discrs_and_qbx_sides(self, insn, bound_expr): return target_name_and_side_to_number, tuple(target_discrs_and_qbx_sides) - def exec_compute_potential_insn_fmm(self, queue, insn, bound_expr, evaluate, - fmm_driver): + def exec_compute_potential_insn_fmm(self, actx: PyOpenCLArrayContext, + insn, bound_expr, evaluate, fmm_driver): """ :arg fmm_driver: A function that accepts four arguments: *wrangler*, *strength*, *geo_data*, *kernel*, *kernel_arguments* @@ -572,21 +602,23 @@ def exec_compute_potential_insn_fmm(self, queue, insn, bound_expr, evaluate, from pytential import bind, sym waa = bind(bound_expr.places, sym.weights_and_area_elements( - self.ambient_dim, dofdesc=insn.source))(queue) - strengths = waa * evaluate(insn.density).with_queue(queue) + self.ambient_dim, dofdesc=insn.source))(actx) + density = evaluate(insn.density) + strengths = waa * density + flat_strengths = flatten(strengths) out_kernels = tuple(knl for knl in insn.kernels) fmm_kernel = self.get_fmm_kernel(out_kernels) output_and_expansion_dtype = ( - self.get_fmm_output_and_expansion_dtype(fmm_kernel, strengths)) + self.get_fmm_output_and_expansion_dtype(fmm_kernel, flat_strengths)) kernel_extra_kwargs, source_extra_kwargs = ( self.get_fmm_expansion_wrangler_extra_kwargs( - queue, out_kernels, geo_data.tree().user_source_ids, + actx, out_kernels, geo_data.tree().user_source_ids, insn.kernel_arguments, evaluate)) wrangler = self.expansion_wrangler_code_container( fmm_kernel, out_kernels).get_wrangler( - queue, geo_data, output_and_expansion_dtype, + actx.queue, geo_data, output_and_expansion_dtype, self.qbx_order, self.fmm_level_to_order, source_extra_kwargs=source_extra_kwargs, @@ -594,7 +626,7 @@ def exec_compute_potential_insn_fmm(self, queue, insn, bound_expr, evaluate, _use_target_specific_qbx=self._use_target_specific_qbx) from pytential.qbx.geometry import target_state - if (geo_data.user_target_to_center().with_queue(queue) + if (actx.thaw(geo_data.user_target_to_center()) == target_state.FAILED).any().get(): raise RuntimeError("geometry has failed targets") @@ -610,20 +642,28 @@ def exec_compute_potential_insn_fmm(self, queue, insn, bound_expr, evaluate, # Execute global QBX. all_potentials_on_every_target, extra_outputs = ( fmm_driver( - wrangler, strengths, geo_data, fmm_kernel, kernel_extra_kwargs)) + wrangler, flat_strengths, geo_data, + fmm_kernel, kernel_extra_kwargs)) - result = [] + results = [] for o in insn.outputs: target_side_number = target_name_and_side_to_number[ o.target_name, o.qbx_forced_limit] + target_discr, _ = target_discrs_and_qbx_sides[target_side_number] target_slice = slice(*geo_data.target_info().target_discr_starts[ target_side_number:target_side_number+2]) - result.append((o.name, - all_potentials_on_every_target[o.kernel_index][target_slice])) + result = all_potentials_on_every_target[o.kernel_index][target_slice] - return result, extra_outputs + from meshmode.discretization import Discretization + if isinstance(target_discr, Discretization): + from meshmode.dof_array import unflatten + result = unflatten(actx, target_discr, result) + + results.append((o.name, result)) + + return results, extra_outputs # }}} @@ -681,7 +721,7 @@ def get_qbx_target_numberer(self, dtype): *count = item; """) - def exec_compute_potential_insn_direct(self, queue, insn, bound_expr, evaluate, + def exec_compute_potential_insn_direct(self, actx, insn, bound_expr, evaluate, return_timing_data): from pytential import bind, sym if return_timing_data: @@ -700,8 +740,9 @@ def exec_compute_potential_insn_direct(self, queue, insn, bound_expr, evaluate, kernel_args[arg_name] = evaluate(arg_expr) waa = bind(bound_expr.places, sym.weights_and_area_elements( - self.ambient_dim, dofdesc=insn.source))(queue) - strengths = waa * evaluate(insn.density).with_queue(queue) + self.ambient_dim, dofdesc=insn.source))(actx) + strengths = waa * evaluate(insn.density) + flat_strengths = flatten(strengths) source_discr = bound_expr.places.get_discretization( insn.source.geometry, insn.source.discr_stage) @@ -722,13 +763,13 @@ def exec_compute_potential_insn_direct(self, queue, insn, bound_expr, evaluate, assert abs(o.qbx_forced_limit) > 0 expansion_radii = bind(bound_expr.places, sym.expansion_radii( - self.ambient_dim, dofdesc=o.target_name))(queue) + self.ambient_dim, dofdesc=o.target_name))(actx) centers = bind(bound_expr.places, sym.expansion_centers( self.ambient_dim, o.qbx_forced_limit, - dofdesc=o.target_name))(queue) + dofdesc=o.target_name))(actx) evt, output_for_each_kernel = lpot_applier( - queue, target_discr.nodes(), + actx.queue, target_discr.nodes(), source_discr.nodes(), centers, [strengths], @@ -738,15 +779,20 @@ def exec_compute_potential_insn_direct(self, queue, insn, bound_expr, evaluate, else: # no on-disk kernel caching if p2p is None: - p2p = self.get_p2p(insn.kernels) + p2p = self.get_p2p(actx, insn.kernels) if lpot_applier_on_tgt_subset is None: lpot_applier_on_tgt_subset = self.get_lpot_applier_on_tgt_subset( insn.kernels) + queue = actx.queue + + from pytential.utils import flatten_if_needed + flat_targets = flatten_if_needed(actx, target_discr.nodes()) + flat_sources = flatten(thaw(actx, source_discr.nodes())) + evt, output_for_each_kernel = p2p(queue, - target_discr.nodes(), - source_discr.nodes(), - [strengths], **kernel_args) + flat_targets, flat_sources, + [flat_strengths], **kernel_args) qbx_forced_limit = o.qbx_forced_limit if qbx_forced_limit is None: @@ -794,11 +840,11 @@ def exec_compute_potential_insn_direct(self, queue, insn, bound_expr, evaluate, if qbx_tgt_count: lpot_applier_on_tgt_subset( queue, - targets=target_discr.nodes(), - sources=source_discr.nodes(), - centers=geo_data.centers(), - expansion_radii=geo_data.expansion_radii(), - strengths=[strengths], + targets=flat_targets, + sources=flat_sources, + centers=geo_data.flat_centers(), + expansion_radii=geo_data.flat_expansion_radii(), + strengths=[flat_strengths], qbx_tgt_numbers=qbx_tgt_numbers, qbx_center_numbers=qbx_center_numbers, **tgt_subset_kwargs) diff --git a/pytential/qbx/direct.py b/pytential/qbx/direct.py index eac3c7fc8..1bcee7c04 100644 --- a/pytential/qbx/direct.py +++ b/pytential/qbx/direct.py @@ -109,7 +109,11 @@ def get_kernel(self): def __call__(self, queue, targets, sources, centers, strengths, expansion_radii, **kwargs): - knl = self.get_cached_optimized_kernel() + from sumpy.tools import is_obj_array_like + knl = self.get_cached_optimized_kernel( + targets_is_obj_array=is_obj_array_like(targets), + sources_is_obj_array=is_obj_array_like(sources), + centers_is_obj_array=is_obj_array_like(centers)) for i, dens in enumerate(strengths): kwargs["strength_%d" % i] = dens diff --git a/pytential/qbx/fmm.py b/pytential/qbx/fmm.py index 8b9ea4292..5185376e4 100644 --- a/pytential/qbx/fmm.py +++ b/pytential/qbx/fmm.py @@ -225,8 +225,8 @@ def form_global_qbx_locals(self, src_weights): self.queue, global_qbx_centers=geo_data.global_qbx_centers(), qbx_center_to_target_box=geo_data.qbx_center_to_target_box(), - qbx_centers=geo_data.centers(), - qbx_expansion_radii=geo_data.expansion_radii(), + qbx_centers=geo_data.flat_centers(), + qbx_expansion_radii=geo_data.flat_expansion_radii(), source_box_starts=starts, source_box_lists=lists, @@ -268,8 +268,8 @@ def translate_box_multipoles_to_qbx_local(self, multipole_exps): ), centers=self.tree.box_centers, - qbx_centers=geo_data.centers(), - qbx_expansion_radii=geo_data.expansion_radii(), + qbx_centers=geo_data.flat_centers(), + qbx_expansion_radii=geo_data.flat_expansion_radii(), src_expansions=source_mpoles_view, src_base_ibox=source_level_start_ibox, @@ -321,8 +321,8 @@ def translate_box_local_to_qbx_local(self, local_exps): target_base_ibox=target_level_start_ibox, centers=self.tree.box_centers, - qbx_centers=geo_data.centers(), - qbx_expansion_radii=geo_data.expansion_radii(), + qbx_centers=geo_data.flat_centers(), + qbx_expansion_radii=geo_data.flat_expansion_radii(), expansions=target_locals_view, qbx_expansions=qbx_expansions, @@ -356,8 +356,8 @@ def eval_qbx_expansions(self, qbx_expansions): qbxl2p = self.code.qbxl2p(self.qbx_order) evt, pot_res = qbxl2p(self.queue, - qbx_centers=geo_data.centers(), - qbx_expansion_radii=geo_data.expansion_radii(), + qbx_centers=geo_data.flat_centers(), + qbx_expansion_radii=geo_data.flat_expansion_radii(), global_qbx_centers=geo_data.global_qbx_centers(), @@ -582,8 +582,8 @@ def reorder_and_finalize_potentials(x): # potential back into a CL array. return wrangler.finalize_potentials(x[tree.sorted_target_ids]) - from pytools.obj_array import with_object_array_or_scalar - result = with_object_array_or_scalar( + from pytools.obj_array import obj_array_vectorize + result = obj_array_vectorize( reorder_and_finalize_potentials, all_potentials_in_tree_order) # }}} diff --git a/pytential/qbx/geometry.py b/pytential/qbx/geometry.py index 7a991ddfa..4c9e124a9 100644 --- a/pytential/qbx/geometry.py +++ b/pytential/qbx/geometry.py @@ -28,6 +28,9 @@ import pyopencl as cl import pyopencl.array # noqa from pytools import memoize_method +from pytools.obj_array import obj_array_vectorize +from meshmode.array_context import PyOpenCLArrayContext +from meshmode.dof_array import flatten, thaw from boxtree.tools import DeviceDataRecord from boxtree.pyfmmlib_integration import FMMLibRotationDataInterface import loopy as lp @@ -76,7 +79,7 @@ Geometry description code container ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. autoclass:: QBXFMMGeometryCodeGetter +.. autoclass:: QBXFMMGeometryCodeContainer :members: :undoc-members: @@ -109,16 +112,21 @@ class target_state(Enum): # noqa FAILED = -2 -class QBXFMMGeometryCodeGetter(TreeCodeContainerMixin): - def __init__(self, cl_context, ambient_dim, tree_code_container, debug, +class QBXFMMGeometryDataCodeContainer(TreeCodeContainerMixin): + def __init__(self, actx: PyOpenCLArrayContext, ambient_dim, + tree_code_container, debug, _well_sep_is_n_away, _from_sep_smaller_crit): - self.cl_context = cl_context + self.array_context = actx self.ambient_dim = ambient_dim self.tree_code_container = tree_code_container self.debug = debug self._well_sep_is_n_away = _well_sep_is_n_away self._from_sep_smaller_crit = _from_sep_smaller_crit + @property + def cl_context(self): + return self.array_context.context + @memoize_method def copy_targets_kernel(self): knl = lp.make_kernel( @@ -324,7 +332,7 @@ class QBXFMMGeometryData(FMMLibRotationDataInterface): .. attribute:: code_getter - The :class:`QBXFMMGeometryCodeGetter` for this object. + The :class:`QBXFMMGeometryCodeContainer` for this object. .. attribute:: target_discrs_and_qbx_sides @@ -348,7 +356,7 @@ class QBXFMMGeometryData(FMMLibRotationDataInterface): .. rubric :: Expansion centers .. attribute:: ncenters - .. automethod:: centers() + .. automethod:: flat_centers() .. rubric :: Methods @@ -401,49 +409,54 @@ def __init__(self, places, source_dd, def ambient_dim(self): return self.lpot_source.ambient_dim - @property - def cl_context(self): - return self.lpot_source.cl_context - @property def coord_dtype(self): return self.lpot_source.density_discr.real_dtype + @property + def array_context(self): + return self.code_getter.array_context + + @property + def cl_context(self): + return self.code_getter.cl_context + # {{{ centers/radii @property def ncenters(self): - return len(self.centers()[0]) + return len(self.flat_centers()[0]) @memoize_method - def centers(self): - """ Return an object array of (interleaved) center coordinates. + def flat_centers(self): + """Return an object array of (interleaved) center coordinates. ``coord_t [ambient_dim][ncenters]`` """ from pytential import bind, sym - from pytools.obj_array import make_obj_array - with cl.CommandQueue(self.cl_context) as queue: - centers = bind(self.places, sym.interleaved_expansion_centers( + centers = bind(self.places, sym.interleaved_expansion_centers( self.ambient_dim, - dofdesc=self.source_dd.to_stage1()))(queue) - return make_obj_array([ax.with_queue(None) for ax in centers]) + dofdesc=self.source_dd.to_stage1()))(self.array_context) + return obj_array_vectorize(self.array_context.freeze, flatten(centers)) @memoize_method - def expansion_radii(self): - """Return an array of radii associated with the (interleaved) + def flat_expansion_radii(self): + """Return an array of radii associated with the (interleaved) expansion centers. ``coord_t [ncenters]`` """ from pytential import bind, sym - with cl.CommandQueue(self.cl_context) as queue: - return bind(self.places, sym.expansion_radii( - self.ambient_dim, - granularity=sym.GRANULARITY_CENTER, - dofdesc=self.source_dd.to_stage1()))(queue) + radii = bind(self.places, + sym.expansion_radii( + self.ambient_dim, + granularity=sym.GRANULARITY_CENTER, + dofdesc=self.source_dd.to_stage1()))( + self.array_context) + + return self.array_context.freeze(flatten(radii)) # }}} @@ -453,36 +466,41 @@ def expansion_radii(self): def target_info(self): """Return a :class:`TargetInfo`. |cached|""" - code_getter = self.code_getter - with cl.CommandQueue(self.cl_context) as queue: - ntargets = self.ncenters - target_discr_starts = [] + from pytential.utils import flatten_if_needed - for target_discr, qbx_side in self.target_discrs_and_qbx_sides: - target_discr_starts.append(ntargets) - ntargets += target_discr.nnodes + code_getter = self.code_getter + queue = self.array_context.queue + ntargets = self.ncenters + target_discr_starts = [] + for target_discr, qbx_side in self.target_discrs_and_qbx_sides: target_discr_starts.append(ntargets) + ntargets += target_discr.ndofs - targets = cl.array.empty( - self.cl_context, (self.ambient_dim, ntargets), - self.coord_dtype) + target_discr_starts.append(ntargets) + + targets = cl.array.empty( + self.cl_context, (self.ambient_dim, ntargets), + self.coord_dtype) + code_getter.copy_targets_kernel()( + queue, + targets=targets[:, :self.ncenters], + points=self.flat_centers()) + + for start, (target_discr, _) in zip( + target_discr_starts, self.target_discrs_and_qbx_sides): code_getter.copy_targets_kernel()( queue, - targets=targets[:, :self.ncenters], - points=self.centers()) + targets=targets[:, + start:start+target_discr.ndofs], + points=flatten_if_needed( + self.array_context, + target_discr.nodes())) - for start, (target_discr, _) in zip( - target_discr_starts, self.target_discrs_and_qbx_sides): - code_getter.copy_targets_kernel()( - queue, - targets=targets[:, start:start+target_discr.nnodes], - points=target_discr.nodes()) - - return TargetInfo( - targets=targets, - target_discr_starts=target_discr_starts, - ntargets=ntargets).with_queue(None) + return TargetInfo( + targets=targets, + target_discr_starts=target_discr_starts, + ntargets=ntargets).with_queue(None) def target_side_preferences(self): """Return one big array combining all the data from @@ -492,7 +510,7 @@ def target_side_preferences(self): tgt_info = self.target_info() - with cl.CommandQueue(self.cl_context) as queue: + with cl.CommandQueue(self.array_context.context) as queue: target_side_preferences = cl.array.empty( queue, tgt_info.ntargets, np.int8) target_side_preferences[:self.ncenters] = 0 @@ -500,7 +518,7 @@ def target_side_preferences(self): for tdstart, (target_discr, qbx_side) in \ zip(tgt_info.target_discr_starts, self.target_discrs_and_qbx_sides): - target_side_preferences[tdstart:tdstart+target_discr.nnodes] \ + target_side_preferences[tdstart:tdstart+target_discr.ndofs] \ = qbx_side return target_side_preferences.with_queue(None) @@ -521,52 +539,54 @@ def tree(self): lpot_source = self.lpot_source target_info = self.target_info() - with cl.CommandQueue(self.cl_context) as queue: - from pytential import sym - quad_stage2_discr = self.places.get_discretization( - self.source_dd.geometry, sym.QBX_SOURCE_QUAD_STAGE2) - - nsources = quad_stage2_discr.nnodes - nparticles = nsources + target_info.ntargets - - target_radii = None - if lpot_source._expansions_in_tree_have_extent: - target_radii = cl.array.zeros(queue, target_info.ntargets, - self.coord_dtype) - target_radii[:self.ncenters] = self.expansion_radii() - - refine_weights = cl.array.empty(queue, nparticles, dtype=np.int32) - - # Assign a weight of 1 to all sources, QBX centers, and conventional - # (non-QBX) targets. Assign a weight of 0 to all targets that need - # QBX centers. The potential at the latter targets is mediated - # entirely by the QBX center, so as a matter of evaluation cost, - # their location in the tree is irrelevant. - refine_weights[:-target_info.ntargets] = 1 - user_ttc = self.user_target_to_center().with_queue(queue) - refine_weights[-target_info.ntargets:] = ( - user_ttc == target_state.NO_QBX_NEEDED).astype(np.int32) - - refine_weights.finish() - - tree, _ = code_getter.build_tree()(queue, - particles=quad_stage2_discr.nodes(), - targets=target_info.targets, - target_radii=target_radii, - max_leaf_refine_weight=lpot_source._max_leaf_refine_weight, - refine_weights=refine_weights, - debug=self.debug, - stick_out_factor=lpot_source._expansion_stick_out_factor, - extent_norm=lpot_source._box_extent_norm, - kind=self.tree_kind) + queue = self.array_context.queue - if self.debug: - tgt_count_2 = cl.array.sum( - tree.box_target_counts_nonchild, queue=queue).get() + from pytential import sym + quad_stage2_discr = self.places.get_discretization( + self.source_dd.geometry, sym.QBX_SOURCE_QUAD_STAGE2) - assert (tree.ntargets == tgt_count_2), (tree.ntargets, tgt_count_2) + nsources = sum(grp.ndofs for grp in quad_stage2_discr.groups) + nparticles = nsources + target_info.ntargets - return tree + target_radii = None + if lpot_source._expansions_in_tree_have_extent: + target_radii = cl.array.zeros(queue, target_info.ntargets, + self.coord_dtype) + target_radii[:self.ncenters] = self.flat_expansion_radii() + + refine_weights = cl.array.empty(queue, nparticles, dtype=np.int32) + + # Assign a weight of 1 to all sources, QBX centers, and conventional + # (non-QBX) targets. Assign a weight of 0 to all targets that need + # QBX centers. The potential at the latter targets is mediated + # entirely by the QBX center, so as a matter of evaluation cost, + # their location in the tree is irrelevant. + refine_weights[:-target_info.ntargets] = 1 + user_ttc = self.user_target_to_center().with_queue(queue) + refine_weights[-target_info.ntargets:] = ( + user_ttc == target_state.NO_QBX_NEEDED).astype(np.int32) + + refine_weights.finish() + + tree, _ = code_getter.build_tree()(queue, + particles=flatten(thaw( + self.array_context, quad_stage2_discr.nodes())), + targets=target_info.targets, + target_radii=target_radii, + max_leaf_refine_weight=lpot_source._max_leaf_refine_weight, + refine_weights=refine_weights, + debug=self.debug, + stick_out_factor=lpot_source._expansion_stick_out_factor, + extent_norm=lpot_source._box_extent_norm, + kind=self.tree_kind) + + if self.debug: + tgt_count_2 = cl.array.sum( + tree.box_target_counts_nonchild, queue=queue).get() + + assert (tree.ntargets == tgt_count_2), (tree.ntargets, tgt_count_2) + + return tree # }}} @@ -761,31 +781,32 @@ def user_target_to_center(self): from pytential.target import PointsTarget - with cl.CommandQueue(self.cl_context) as queue: - target_side_prefs = (self - .target_side_preferences()[self.ncenters:].get(queue=queue)) - - target_discrs_and_qbx_sides = [( - PointsTarget(target_info.targets[:, self.ncenters:]), - target_side_prefs.astype(np.int32))] - - target_association_wrangler = ( - self.lpot_source.target_association_code_container - .get_wrangler(queue)) - - tgt_assoc_result = associate_targets_to_qbx_centers( - self.places, - self.source_dd, - target_association_wrangler, - target_discrs_and_qbx_sides, - target_association_tolerance=( - self.target_association_tolerance), - debug=self.debug) - - result = cl.array.empty(queue, target_info.ntargets, - tgt_assoc_result.target_to_center.dtype) - result[:self.ncenters].fill(target_state.NO_QBX_NEEDED) - result[self.ncenters:] = tgt_assoc_result.target_to_center + queue = self.array_context.queue + + target_side_prefs = (self + .target_side_preferences()[self.ncenters:].get(queue=queue)) + + target_discrs_and_qbx_sides = [( + PointsTarget(target_info.targets[:, self.ncenters:]), + target_side_prefs.astype(np.int32))] + + target_association_wrangler = ( + self.lpot_source.target_association_code_container + .get_wrangler(self.array_context)) + + tgt_assoc_result = associate_targets_to_qbx_centers( + self.places, + self.source_dd, + target_association_wrangler, + target_discrs_and_qbx_sides, + target_association_tolerance=( + self.target_association_tolerance), + debug=self.debug) + + result = cl.array.empty(queue, target_info.ntargets, + tgt_assoc_result.target_to_center.dtype) + result[:self.ncenters].fill(target_state.NO_QBX_NEEDED) + result[self.ncenters:] = tgt_assoc_result.target_to_center return result.with_queue(None) @@ -918,7 +939,7 @@ def plot(self, draw_circles=False, draw_center_numbers=False, # {{{ draw centers and circles - centers = self.centers() + centers = self.flat_centers() centers = [ centers[0].get(queue), centers[1].get(queue)] diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index b3c28ee6e..af48b2121 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import division, absolute_import, print_function +from __future__ import annotations __copyright__ = """ Copyright (C) 2013 Andreas Kloeckner @@ -29,6 +29,7 @@ import loopy as lp from loopy.version import MOST_RECENT_LANGUAGE_VERSION +from meshmode.array_context import PyOpenCLArrayContext import numpy as np import pyopencl as cl @@ -219,8 +220,8 @@ class RefinerCodeContainer(TreeCodeContainerMixin): - def __init__(self, cl_context, tree_code_container): - self.cl_context = cl_context + def __init__(self, actx: PyOpenCLArrayContext, tree_code_container): + self.array_context = actx self.tree_code_container = tree_code_container @memoize_method @@ -228,7 +229,7 @@ def expansion_disk_undisturbed_by_sources_checker( self, dimensions, coord_dtype, box_id_dtype, peer_list_idx_dtype, particle_id_dtype, max_levels): return EXPANSION_DISK_UNDISTURBED_BY_SOURCES_CHECKER.generate( - self.cl_context, + self.array_context.context, dimensions, coord_dtype, box_id_dtype, peer_list_idx_dtype, max_levels, extra_type_aliases=(("particle_id_t", particle_id_dtype),)) @@ -238,7 +239,7 @@ def sufficient_source_quadrature_resolution_checker( self, dimensions, coord_dtype, box_id_dtype, peer_list_idx_dtype, particle_id_dtype, max_levels): return SUFFICIENT_SOURCE_QUADRATURE_RESOLUTION_CHECKER.generate( - self.cl_context, + self.array_context.context, dimensions, coord_dtype, box_id_dtype, peer_list_idx_dtype, max_levels, extra_type_aliases=(("particle_id_t", particle_id_dtype),)) @@ -268,11 +269,11 @@ def element_prop_threshold_checker(self): knl = lp.split_iname(knl, "ielement", 128, inner_tag="l.0", outer_tag="g.0") return knl - def get_wrangler(self, queue): + def get_wrangler(self): """ :arg queue: """ - return RefinerWrangler(self, queue) + return RefinerWrangler(self.array_context, self) # }}} @@ -311,7 +312,10 @@ def check_expansion_disks_undisturbed_by_sources(self, from pytential import bind, sym center_danger_zone_radii = bind(stage1_density_discr, sym.expansion_radii(stage1_density_discr.ambient_dim, - granularity=sym.GRANULARITY_CENTER))(self.queue) + granularity=sym.GRANULARITY_CENTER))(self.array_context) + + from meshmode.dof_array import flatten + center_danger_zone_radii = flatten(center_danger_zone_radii) evt = knl( *unwrap_args( @@ -369,7 +373,11 @@ def check_sufficient_source_quadrature_resolution(self, dd = sym.as_dofdesc(sym.GRANULARITY_ELEMENT).to_stage2() source_danger_zone_radii_by_panel = bind(stage2_density_discr, sym._source_danger_zone_radii( - stage2_density_discr.ambient_dim, dofdesc=dd))(self.queue) + stage2_density_discr.ambient_dim, dofdesc=dd))( + self.array_context) + from meshmode.dof_array import flatten + source_danger_zone_radii_by_panel = flatten( + source_danger_zone_radii_by_panel) unwrap_args = AreaQueryElementwiseTemplate.unwrap_args evt = knl( @@ -407,6 +415,10 @@ def check_element_prop_threshold(self, element_property, threshold, refine_flags if debug: npanels_to_refine_prev = cl.array.sum(refine_flags).get() + from pytential.utils import flatten_if_needed + element_property = flatten_if_needed( + self.array_context, element_property) + evt, out = knl(self.queue, element_property=element_property, refine_flags=refine_flags, @@ -436,8 +448,10 @@ def refine(self, density_discr, refiner, refine_flags, factory, debug): with ProcessLogger(logger, "refine mesh"): refiner.refine(refine_flags) - from meshmode.discretization.connection import make_refinement_connection - conn = make_refinement_connection(refiner, density_discr, factory) + from meshmode.discretization.connection import ( + make_refinement_connection) + conn = make_refinement_connection( + self.array_context, refiner, density_discr, factory) return conn @@ -451,6 +465,7 @@ class RefinerNotConvergedWarning(UserWarning): def make_empty_refine_flags(queue, density_discr): + # FIXME: queue => actx """Return an array on the device suitable for use as element refine flags. :arg queue: An instance of :class:`pyopencl.CommandQueue`. @@ -487,6 +502,7 @@ def _warn_max_iterations(violated_criteria, expansion_disturbance_tolerance): def _visualize_refinement(queue, discr, niter, stage_nr, stage_name, flags, visualize=False): + # FIXME: queue => actx if not visualize: return @@ -529,7 +545,7 @@ def _make_quad_stage2_discr(lpot_source, stage2_density_discr): QuadratureSimplexGroupFactory return Discretization( - lpot_source.cl_context, + lpot_source._setup_actx, stage2_density_discr.mesh, QuadratureSimplexGroupFactory(lpot_source.fine_order), lpot_source.real_dtype) @@ -583,6 +599,8 @@ def _refine_qbx_stage1(lpot_source, density_discr, iter_violated_criteria = ["start"] niter = 0 + actx = wrangler.array_context + stage1_density_discr = density_discr while iter_violated_criteria: iter_violated_criteria = [] @@ -602,7 +620,7 @@ def _refine_qbx_stage1(lpot_source, density_discr, quad_resolution = bind(stage1_density_discr, sym._quad_resolution(stage1_density_discr.ambient_dim, - dofdesc=sym.GRANULARITY_ELEMENT))(wrangler.queue) + dofdesc=sym.GRANULARITY_ELEMENT))(actx) violates_kernel_length_scale = \ wrangler.check_element_prop_threshold( @@ -622,7 +640,7 @@ def _refine_qbx_stage1(lpot_source, density_discr, scaled_max_curv = bind(stage1_density_discr, sym.ElementwiseMax(sym._scaled_max_curvature( stage1_density_discr.ambient_dim), - dofdesc=sym.GRANULARITY_ELEMENT))(wrangler.queue) + dofdesc=sym.GRANULARITY_ELEMENT))(actx) violates_scaled_max_curv = \ wrangler.check_element_prop_threshold( @@ -764,7 +782,8 @@ def _refine_qbx_stage2(lpot_source, stage1_density_discr, def _refine_qbx_quad_stage2(lpot_source, stage2_density_discr): from meshmode.discretization.connection import make_same_mesh_connection discr = _make_quad_stage2_discr(lpot_source, stage2_density_discr) - conn = make_same_mesh_connection(discr, stage2_density_discr) + conn = make_same_mesh_connection( + lpot_source._setup_actx, discr, stage2_density_discr) return discr, conn @@ -898,7 +917,7 @@ def get_from_cache(from_ds, to_ds): # {{{ refine_geometry_collection -def refine_geometry_collection(queue, places, +def refine_geometry_collection(places, group_factory=None, refine_discr_stage=None, kernel_length_scale=None, @@ -945,7 +964,7 @@ def refine_geometry_collection(queue, places, continue _refine_for_global_qbx(places, dofdesc, - lpot_source.refiner_code_container.get_wrangler(queue), + lpot_source.refiner_code_container.get_wrangler(), group_factory=group_factory, kernel_length_scale=kernel_length_scale, scaled_max_curvature_threshold=scaled_max_curvature_threshold, diff --git a/pytential/qbx/target_assoc.py b/pytential/qbx/target_assoc.py index 39b226ab4..eda3dd8f3 100644 --- a/pytential/qbx/target_assoc.py +++ b/pytential/qbx/target_assoc.py @@ -36,6 +36,8 @@ from boxtree.area_query import AreaQueryElementwiseTemplate from boxtree.tools import InlineBinarySearch from cgen import Enum +from meshmode.array_context import PyOpenCLArrayContext +from meshmode.dof_array import flatten from pytential.qbx.utils import ( QBX_TREE_C_PREAMBLE, QBX_TREE_MAKO_DEFS, TreeWranglerBase, TreeCodeContainerMixin) @@ -444,10 +446,14 @@ class QBXTargetAssociation(DeviceDataRecord): class TargetAssociationCodeContainer(TreeCodeContainerMixin): - def __init__(self, cl_context, tree_code_container): - self.cl_context = cl_context + def __init__(self, actx: PyOpenCLArrayContext, tree_code_container): + self.array_context = actx self.tree_code_container = tree_code_container + @property + def cl_context(self): + return self.array_context.context + @memoize_method def target_marker(self, dimensions, coord_dtype, box_id_dtype, peer_list_idx_dtype, particle_id_dtype, max_levels): @@ -489,8 +495,8 @@ def space_invader_query(self): from boxtree.area_query import SpaceInvaderQueryBuilder return SpaceInvaderQueryBuilder(self.cl_context) - def get_wrangler(self, queue): - return TargetAssociationWrangler(self, queue) + def get_wrangler(self, actx: PyOpenCLArrayContext): + return TargetAssociationWrangler(actx, code_container=self) class TargetAssociationWrangler(TreeWranglerBase): @@ -521,9 +527,12 @@ def mark_targets(self, places, dofdesc, 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(places, sym._close_target_tunnel_radii(ambient_dim, dofdesc=dofdesc))( - self.queue) + self.array_context) + + tunnel_radius_by_source = flatten(tunnel_radius_by_source) # Target-marking algorithm (TGTMARK): # @@ -620,10 +629,13 @@ def find_centers(self, places, dofdesc, expansion_radii_by_center = bind(places, sym.expansion_radii( ambient_dim, granularity=sym.GRANULARITY_CENTER, - dofdesc=dofdesc))(self.queue) + dofdesc=dofdesc))(self.array_context) expansion_radii_by_center_with_tolerance = \ expansion_radii_by_center * (1 + target_association_tolerance) + expansion_radii_by_center_with_tolerance = flatten( + expansion_radii_by_center_with_tolerance) + # Idea: # # (1) Tag leaf boxes around centers with max distance to usable center. @@ -716,11 +728,12 @@ def mark_panels_for_refinement(self, places, dofdesc, 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(places, sym._close_target_tunnel_radii(ambient_dim, dofdesc=dofdesc))( - self.queue) + self.array_context) - # See (TGTMARK) above for algorithm. + # see (TGTMARK) above for algorithm. box_to_search_dist, evt = self.code_container.space_invader_query()( self.queue, @@ -731,10 +744,6 @@ def mark_panels_for_refinement(self, places, dofdesc, wait_for=wait_for) wait_for = [evt] - tunnel_radius_by_source = bind(places, - sym._close_target_tunnel_radii(ambient_dim, dofdesc=dofdesc))( - self.queue) - evt = knl( *unwrap_args( tree, peer_lists, diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index b872152a2..8d5970837 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import division, absolute_import, print_function +from __future__ import annotations __copyright__ = """ Copyright (C) 2016 Matt Wala @@ -28,6 +28,7 @@ import numpy as np from boxtree.tree import Tree +from meshmode.array_context import PyOpenCLArrayContext import pyopencl as cl import pyopencl.array # noqa from pytools import memoize_method @@ -72,23 +73,23 @@ class TreeCodeContainer(object): - def __init__(self, cl_context): - self.cl_context = cl_context + def __init__(self, actx: PyOpenCLArrayContext): + self.array_context = actx @memoize_method def build_tree(self): from boxtree.tree_build import TreeBuilder - return TreeBuilder(self.cl_context) + return TreeBuilder(self.array_context.context) @memoize_method def peer_list_finder(self): from boxtree.area_query import PeerListFinder - return PeerListFinder(self.cl_context) + return PeerListFinder(self.array_context.context) @memoize_method def particle_list_filter(self): from boxtree.tree import ParticleListFilter - return ParticleListFilter(self.cl_context) + return ParticleListFilter(self.array_context.context) # }}} @@ -116,9 +117,13 @@ def particle_list_filter(self): class TreeWranglerBase(object): - def __init__(self, code_container, queue): + def __init__(self, array_context: PyOpenCLArrayContext, code_container): self.code_container = code_container - self.queue = queue + self.array_context = array_context + + @property + def queue(self): + return self.array_context.queue def build_tree(self, places, targets_list=(), sources_list=(), use_stage2_discr=False): @@ -126,7 +131,7 @@ def build_tree(self, places, targets_list=(), sources_list=(), plfilt = self.code_container.particle_list_filter() return build_tree_with_qbx_metadata( - self.queue, places, tb, plfilt, + self.array_context, places, tb, plfilt, sources_list=sources_list, targets_list=targets_list, use_stage2_discr=use_stage2_discr) @@ -226,8 +231,8 @@ class TreeWithQBXMetadata(Tree): @log_process(logger) -def build_tree_with_qbx_metadata(queue, places, - tree_builder, particle_list_filter, +def build_tree_with_qbx_metadata(actx: PyOpenCLArrayContext, + places, tree_builder, particle_list_filter, sources_list=(), targets_list=(), use_stage2_discr=False): """Return a :class:`TreeWithQBXMetadata` built from the given layer @@ -240,7 +245,7 @@ def build_tree_with_qbx_metadata(queue, places, :class:`~pytential.symbolic.primitives.QBX_SOURCE_STAGE1`. * targets from ``targets_list``. - :arg queue: An instance of :class:`pyopencl.CommandQueue` + :arg actx: A :class:`PyOpenCLArrayContext` :arg places: An instance of :class:`~pytential.symbolic.execution.GeometryCollection`. :arg targets_list: A list of :class:`pytential.target.TargetBase` @@ -274,15 +279,20 @@ def build_tree_with_qbx_metadata(queue, places, def _make_centers(discr): return bind(discr, sym.interleaved_expansion_centers( - discr.ambient_dim))(queue) + discr.ambient_dim))(actx) stage1_density_discr = stage1_density_discrs[0] density_discr = density_discrs[0] - sources = density_discr.nodes() - centers = _make_centers(stage1_density_discr) - targets = (tgt.nodes() for tgt in targets_list) + from meshmode.dof_array import flatten, thaw + from pytential.utils import flatten_if_needed + sources = flatten(thaw(actx, density_discr.nodes())) + centers = flatten(_make_centers(stage1_density_discr)) + targets = [ + flatten_if_needed(actx, tgt.nodes()) + for tgt in targets_list] + queue = actx.queue particles = tuple( cl.array.concatenate(dim_coords, queue=queue) for dim_coords in zip(sources, centers, *targets)) @@ -330,10 +340,11 @@ def _make_centers(discr): flags[particle_slice].fill(1) flags.finish() + # FIXME: thaw box_to_class = ( particle_list_filter .filter_target_lists_in_user_order(queue, tree, flags) - .with_queue(queue)) + .with_queue(actx.queue)) if fixup: box_to_class.target_lists += fixup @@ -347,12 +358,14 @@ def _make_centers(discr): qbx_panel_to_source_starts = cl.array.empty( queue, npanels + 1, dtype=tree.particle_id_dtype) el_offset = 0 + node_nr_base = 0 for group in density_discr.groups: qbx_panel_to_source_starts[el_offset:el_offset + group.nelements] = \ - cl.array.arange(queue, group.node_nr_base, - group.node_nr_base + group.nnodes, - group.nunit_nodes, + cl.array.arange(queue, node_nr_base, + node_nr_base + group.ndofs, + group.nunit_dofs, dtype=tree.particle_id_dtype) + node_nr_base += group.ndofs el_offset += group.nelements qbx_panel_to_source_starts[-1] = nsources @@ -418,11 +431,11 @@ def ncenters(self): def centers(self): return np.array([ ci.get(queue=self.queue) - for ci in self.geo_data.centers()]) + for ci in self.geo_data.flat_centers()]) @memoize_method def expansion_radii(self): - return self.geo_data.expansion_radii().get(queue=self.queue) + return self.geo_data.flat_expansion_radii().get(queue=self.queue) @memoize_method def global_qbx_centers(self): diff --git a/pytential/solve.py b/pytential/solve.py index c2e932432..76d7958e5 100644 --- a/pytential/solve.py +++ b/pytential/solve.py @@ -22,9 +22,6 @@ THE SOFTWARE. """ - -from six.moves import range - __doc__ = """ .. autofunction:: gmres @@ -37,52 +34,22 @@ """ -def get_array_module(vec): - try: - from pyopencl.tools import array_module - from pytools.obj_array import is_obj_array - if is_obj_array(vec): - return array_module(vec[0]) - else: - return array_module(vec) - except ImportError: - return np - - -# {{{ block system support - -class VectorChopper(object): - def __init__(self, structured_vec): - from pytools.obj_array import is_obj_array - self.is_structured = is_obj_array(structured_vec) - self.array_module = get_array_module(structured_vec) - - if self.is_structured: - self.slices = [] - num_dofs = 0 - for entry in structured_vec: - if isinstance(entry, self.array_module.ndarray): - length = len(entry) - else: - length = 1 - - self.slices.append(slice(num_dofs, num_dofs+length)) - num_dofs += length - - def stack(self, vec): - if not self.is_structured: - return vec - - return self.array_module.hstack(vec) - - def chop(self, vec): - if not self.is_structured: - return vec - - from pytools.obj_array import make_obj_array - return make_obj_array([vec[slc] for slc in self.slices]) - -# }}} +import numpy as np +from numbers import Number +import pyopencl as cl +import pyopencl.array # noqa +from pytools.obj_array import obj_array_vectorize_n_args + + +def default_vdot(x, y): + if (isinstance(x, Number) + or (isinstance(x, np.ndarray) and x.dtype.char != "O")): + return np.vdot(x, y) + elif isinstance(x, cl.array.Array): + return cl.array.vdot(x, y).get() + else: + assert isinstance(x, np.ndarray) and x.dtype.char == "O" + return sum(obj_array_vectorize_n_args(default_vdot, x, y)) # {{{ gmres @@ -92,7 +59,6 @@ def chop(self, vec): # Necessary because SciPy gmres is not reentrant and thus does # not allow recursive solves. -import numpy as np from pytools import Record @@ -298,7 +264,7 @@ def __call__(self, resid): # {{{ entrypoint def gmres(op, rhs, restart=None, tol=None, x0=None, - inner_product=None, + inner_product=default_vdot, maxiter=None, hard_failure=None, no_progress_factor=None, stall_iterations=None, callback=None, progress=False, require_monotonicity=True): @@ -320,32 +286,20 @@ def gmres(op, rhs, restart=None, tol=None, x0=None, :return: a :class:`GMRESResult` """ - amod = get_array_module(rhs) - - chopper = VectorChopper(rhs) - stacked_rhs = chopper.stack(rhs) - - stacked_x0 = x0 - if stacked_x0 is not None: - stacked_x0 = chopper.stack(stacked_x0) - - if inner_product is None: - inner_product = amod.vdot - if callback is None: if progress: callback = ResidualPrinter(inner_product) else: callback = None - result = _gmres(op, stacked_rhs, restart=restart, tol=tol, x0=stacked_x0, + result = _gmres(op, rhs, restart=restart, tol=tol, x0=x0, dot=inner_product, maxiter=maxiter, hard_failure=hard_failure, no_progress_factor=no_progress_factor, stall_iterations=stall_iterations, callback=callback, require_monotonicity=require_monotonicity) - return result.copy(solution=chopper.chop(result.solution)) + return result # }}} @@ -367,10 +321,7 @@ def lu(op, rhs, show_spectrum=False): pt.plot(ev.real, ev.imag, "o") pt.show() - chopper = VectorChopper(rhs) - return chopper.chop( - la.solve(mat, - chopper.stack(rhs))) + return la.solve(mat, rhs) # }}} diff --git a/pytential/source.py b/pytential/source.py index 7ed794abf..475da21a3 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -26,7 +26,7 @@ import numpy as np # noqa: F401 import pyopencl as cl # noqa: F401 import six -from pytools import memoize_method +from pytools import memoize_in from sumpy.fmm import UnableToCollectTimingData @@ -53,6 +53,29 @@ class PotentialSource(object): def preprocess_optemplate(self, name, discretizations, expr): return expr + @property + def real_dtype(self): + raise NotImplementedError + + @property + def complex_dtype(self): + raise NotImplementedError + + def get_p2p(self, actx, kernels): + @memoize_in(actx, (PotentialSource, "p2p")) + def p2p(kernels): + from pytools import any + if any(knl.is_complex_valued for knl in kernels): + value_dtype = self.complex_dtype + else: + value_dtype = self.real_dtype + + from sumpy.p2p import P2P + return P2P(actx.context, + kernels, exclude_self=False, value_dtypes=value_dtype) + + return p2p(kernels) + # {{{ point potential source @@ -111,27 +134,11 @@ def op_group_features(self, expr): return result - @memoize_method - def get_p2p(self, kernels): - # needs to be separate method for caching - - from pytools import any - if any(knl.is_complex_valued for knl in kernels): - value_dtype = self.complex_dtype - else: - value_dtype = self.real_dtype - - from sumpy.p2p import P2P - p2p = P2P(self.cl_context, - kernels, exclude_self=False, value_dtypes=value_dtype) - - return p2p - - def cost_model_compute_potential_insn(self, queue, insn, bound_expr, + def cost_model_compute_potential_insn(self, actx, insn, bound_expr, evaluate, costs): raise NotImplementedError - def exec_compute_potential_insn(self, queue, insn, bound_expr, evaluate, + def exec_compute_potential_insn(self, actx, insn, bound_expr, evaluate, return_timing_data): if return_timing_data: from warnings import warn @@ -145,7 +152,7 @@ def exec_compute_potential_insn(self, queue, insn, bound_expr, evaluate, 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).copy() + strengths = evaluate(insn.density) # FIXME: Do this all at once result = [] @@ -155,10 +162,12 @@ def exec_compute_potential_insn(self, queue, insn, bound_expr, evaluate, # no on-disk kernel caching if p2p is None: - p2p = self.get_p2p(insn.kernels) + p2p = self.get_p2p(actx, insn.kernels) - evt, output_for_each_kernel = p2p(queue, - target_discr.nodes(), self._nodes, + from pytential.utils import flatten_if_needed + evt, output_for_each_kernel = p2p(actx.queue, + flatten_if_needed(actx, target_discr.nodes()), + self._nodes, [strengths], **kernel_args) result.append((o.name, output_for_each_kernel[o.kernel_index])) @@ -203,7 +212,7 @@ def dim(self): @property def cl_context(self): - return self.density_discr.cl_context + return self.density_discr._setup_actx.context @property def real_dtype(self): @@ -213,22 +222,6 @@ def real_dtype(self): def complex_dtype(self): return self.density_discr.complex_dtype - @memoize_method - def get_p2p(self, kernels): - # needs to be separate method for caching - - from pytools import any - if any(knl.is_complex_valued for knl in kernels): - value_dtype = self.density_discr.complex_dtype - else: - value_dtype = self.density_discr.real_dtype - - from sumpy.p2p import P2P - p2p = P2P(self.cl_context, - kernels, exclude_self=False, value_dtypes=value_dtype) - - return p2p - # {{{ fmm setup helpers def get_fmm_kernel(self, kernels): @@ -252,10 +245,12 @@ def get_fmm_output_and_expansion_dtype(self, base_kernel, strengths): return self.real_dtype def get_fmm_expansion_wrangler_extra_kwargs( - self, queue, out_kernels, tree_user_source_ids, arguments, evaluator): + self, actx, out_kernels, tree_user_source_ids, arguments, evaluator): # This contains things like the Helmholtz parameter k or # the normal directions for double layers. + queue = actx.queue + def reorder_sources(source_array): if isinstance(source_array, cl.array.Array): return (source_array @@ -269,15 +264,17 @@ def reorder_sources(source_array): source_extra_kwargs = {} from sumpy.tools import gather_arguments, gather_source_arguments - from pytools.obj_array import with_object_array_or_scalar + from pytools.obj_array import obj_array_vectorize + from pytential.utils import flatten_if_needed + for func, var_dict in [ (gather_arguments, kernel_extra_kwargs), (gather_source_arguments, source_extra_kwargs), ]: for arg in func(out_kernels): - var_dict[arg.name] = with_object_array_or_scalar( + var_dict[arg.name] = obj_array_vectorize( reorder_sources, - evaluator(arguments[arg.name])) + flatten_if_needed(actx, evaluator(arguments[arg.name]))) return kernel_extra_kwargs, source_extra_kwargs diff --git a/pytential/symbolic/compiler.py b/pytential/symbolic/compiler.py index 3fcfbf2ec..eb01532d9 100644 --- a/pytential/symbolic/compiler.py +++ b/pytential/symbolic/compiler.py @@ -270,13 +270,13 @@ def gen_expr_arrow(expr, target_node): for dep in insn.get_dependencies(): gen_expr_arrow(dep, node_names[insn]) - from pytools.obj_array import is_obj_array + code_res = code.result - if is_obj_array(code.result): - for subexp in code.result: + if isinstance(code_res, np.ndarray) and code_res.dtype.char == "O": + for subexp in code_res: gen_expr_arrow(subexp, "result") else: - gen_expr_arrow(code.result, "result") + gen_expr_arrow(code_res, "result") return "digraph dataflow {\n%s\n}\n" % "\n".join(result) @@ -331,7 +331,7 @@ def get_next_step(self, available_names, done_insns): discardable_vars = set(available_names) - needed_vars # {{{ make sure results do not get discarded - from pytools.obj_array import with_object_array_or_scalar + from pytools.obj_array import obj_array_vectorize from pytential.symbolic.mappers import DependencyMapper dm = DependencyMapper(composite_leaves=False) @@ -347,7 +347,7 @@ def remove_result_variable(result_expr): assert isinstance(var, Variable) discardable_vars.discard(var.name) - with_object_array_or_scalar(remove_result_variable, self.result) + obj_array_vectorize(remove_result_variable, self.result) # }}} return argmax2(available_insns), discardable_vars @@ -387,9 +387,9 @@ def execute(self, exec_mapper, pre_assign_check=None): done_insns.add(insn) assignments = ( - self.get_exec_function(insn, exec_mapper) - (exec_mapper.queue, insn, exec_mapper.bound_expr, - exec_mapper)) + self.get_exec_function(insn, exec_mapper)( + exec_mapper.array_context, + insn, exec_mapper.bound_expr, exec_mapper)) assignees = insn.get_assignees() for target, value in assignments: @@ -412,8 +412,8 @@ def execute(self, exec_mapper, pre_assign_check=None): raise RuntimeError("not all instructions are reachable" "--did you forget to pass a value for a placeholder?") - from pytools.obj_array import with_object_array_or_scalar - return with_object_array_or_scalar(exec_mapper, self.result) + from pytools.obj_array import obj_array_vectorize + return obj_array_vectorize(exec_mapper, self.result) # }}} @@ -480,8 +480,8 @@ def __call__(self, expr): # Put the toplevel expressions into variables as well. - from pytools.obj_array import with_object_array_or_scalar - result = with_object_array_or_scalar(self.assign_to_new_var, result) + from pytools.obj_array import obj_array_vectorize + result = obj_array_vectorize(self.assign_to_new_var, result) return Code(self.code, result) diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 9d23fb73d..745f927b0 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -27,12 +27,12 @@ """ import six -import pyopencl as cl -import pyopencl.array # noqa -from pytools import memoize +from meshmode.array_context import PyOpenCLArrayContext # noqa +from meshmode.dof_array import DOFArray +import numpy as np +from pytools import memoize_in import loopy as lp -from loopy.version import MOST_RECENT_LANGUAGE_VERSION __doc__ = """ @@ -69,7 +69,11 @@ def from_discr(self): def to_discr(self): return self.discr - def __call__(self, queue, vec): + @property + def array_context(self): + return self.discr._setup_actx + + def __call__(self, ary): raise NotImplementedError() @@ -85,61 +89,66 @@ class CenterGranularityConnection(GranularityConnection): 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"), - "..." - ], - name="node_interleaver_knl", - 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): + def interleave_dof_arrays(self, ary1, ary2): + if not isinstance(ary1, DOFArray) or not isinstance(ary2, DOFArray): + raise TypeError("non-array passed to connection") + + @memoize_in(self.array_context, + (CenterGranularityConnection, "interleave")) + def prg(): + from meshmode.array_context import make_loopy_program + return make_loopy_program( + """{[iel, idof]: 0<=iel 1 - do_split = len(self.starts_and_ends) > 1 - from pytools.obj_array import make_obj_array + def flatten(self, ary): + if not self._operator_uses_obj_array: + ary = [ary] - if do_split: - x = make_obj_array( - [x[start:end] for start, end in self.starts_and_ends]) + result = self.array_context.empty(self.total_dofs, self.dtype) + from pytential.utils import flatten_if_needed + for res_i, (start, end) in zip(ary, self.starts_and_ends): + result[start:end] = flatten_if_needed(self.array_context, res_i) + return result - args = self.extra_args.copy() - args[self.arg_name] = x - result = self.bound_expr(self.queue, **args) + def unflatten(self, ary): + components = [] + for discr, (start, end) in zip(self.discrs, self.starts_and_ends): + component = ary[start:end] + from meshmode.discretization import Discretization + if isinstance(discr, Discretization): + from meshmode.dof_array import unflatten + component = unflatten(self.array_context, discr, component) + components.append(component) + + if self._operator_uses_obj_array: + from pytools.obj_array import make_obj_array + return make_obj_array(components) + else: + return components[0] - if do_split: - # re-join what was split - joined_result = cl.array.empty(self.queue, self.total_dofs, - self.dtype) - for res_i, (start, end) in zip(result, self.starts_and_ends): - joined_result[start:end] = res_i - result = joined_result + def matvec(self, x): + # Three types of inputs are supported: + # * flat NumPy arrays + # => output is a flat NumPy array + # * flat PyOpenCL arrays + # => output is a flat PyOpenCL array + # * structured arrays (object arrays/DOFArrays) + # => output has same structure as input + if isinstance(x, np.ndarray) and x.dtype.char != "O": + x = self.array_context.from_numpy(x) + flat = True + host = True + assert x.shape == (self.total_dofs,) + elif isinstance(x, cl.array.Array): + flat = True + host = False + assert x.shape == (self.total_dofs,) + elif isinstance(x, np.ndarray) and x.dtype.char == "O": + flat = False + host = False + else: + raise ValueError("unsupported input type") + + args = self.extra_args.copy() + args[self.arg_name] = self.unflatten(x) if flat else x + result = self.bound_expr(self.array_context, **args) - if out_host: - result = result.get() + if flat: + result = self.flatten(result) + if host: + result = self.array_context.to_numpy(result) return result @@ -706,7 +725,7 @@ def _add_conn_to_cache(self, conn, geometry, from_stage, to_stage): cache[key] = conn - def _get_qbx_discretization(self, actx: ArrayContext, geometry, discr_stage): + def _get_qbx_discretization(self, geometry, discr_stage): lpot_source = self.get_geometry(geometry) try: @@ -715,12 +734,11 @@ def _get_qbx_discretization(self, actx: ArrayContext, geometry, discr_stage): from pytential import sym from pytential.qbx.refinement import _refine_for_global_qbx - with cl.CommandQueue(lpot_source.cl_context) as queue: - # NOTE: this adds the required discretizations to the cache - dofdesc = sym.DOFDescriptor(geometry, discr_stage) - _refine_for_global_qbx(self, dofdesc, - lpot_source.refiner_code_container.get_wrangler(queue), - _copy_collection=False) + # NOTE: this adds the required discretizations to the cache + dofdesc = sym.DOFDescriptor(geometry, discr_stage) + _refine_for_global_qbx(self, dofdesc, + lpot_source.refiner_code_container.get_wrangler(), + _copy_collection=False) discr = self._get_discr_from_cache(geometry, discr_stage) @@ -732,7 +750,7 @@ def get_connection(self, from_dd, to_dd): from pytential.symbolic.dof_connection import connection_from_dds return connection_from_dds(self, from_dd, to_dd) - def get_discretization(self, actx: ArrayContext, geometry, discr_stage=None): + def get_discretization(self, geometry, discr_stage=None): """ :arg dofdesc: a :class:`~pytential.symbolic.primitives.DOFDescriptor` specifying the desired discretization. @@ -743,9 +761,6 @@ def get_discretization(self, actx: ArrayContext, geometry, discr_stage=None): the corresponding :class:`~meshmode.discretization.Discretization` in its attributes instead. """ - if not isinstance(actx, ArrayContext): - raise TypeError("first argument must be an ArrayContext") - if discr_stage is None: discr_stage = sym.QBX_SOURCE_STAGE1 discr = self.get_geometry(geometry) @@ -754,7 +769,7 @@ def get_discretization(self, actx: ArrayContext, geometry, discr_stage=None): from pytential.source import LayerPotentialSourceBase if isinstance(discr, QBXLayerPotentialSource): - return self._get_qbx_discretization(actx, geometry, discr_stage) + return self._get_qbx_discretization(geometry, discr_stage) elif isinstance(discr, LayerPotentialSourceBase): return discr.density_discr else: @@ -828,7 +843,9 @@ def get_modeled_cost(self, queue, **args): self.code.execute(cost_model_mapper) return cost_model_mapper.get_modeled_cost() - def scipy_op(self, queue, arg_name, dtype, domains=None, **extra_args): + def scipy_op( + self, actx: PyOpenCLArrayContext, arg_name, dtype, + domains=None, **extra_args): """ :arg domains: a list of discretization identifiers or *None* values indicating the domains on which each component of the @@ -841,8 +858,7 @@ def scipy_op(self, queue, arg_name, dtype, domains=None, **extra_args): and returning :class:`pyopencl.array.Array` arrays. """ - from pytools.obj_array import is_obj_array - if is_obj_array(self.code.result): + if isinstance(self.code.result, np.ndarray): nresults = len(self.code.result) else: nresults = 1 @@ -851,15 +867,18 @@ def scipy_op(self, queue, arg_name, dtype, domains=None, **extra_args): self.places, domains, self.places.auto_target) total_dofs = 0 + discrs = [] starts_and_ends = [] for dom_name in domains: if dom_name is None: + discr = None size = 1 else: discr = self.places.get_discretization( dom_name.geometry, dom_name.discr_stage) - size = discr.nnodes + size = discr.ndofs + discrs.append(discr) starts_and_ends.append((total_dofs, total_dofs+size)) total_dofs += size @@ -868,11 +887,11 @@ def scipy_op(self, queue, arg_name, dtype, domains=None, **extra_args): # fair, since these operators are usually only used # for linear system solving, in which case the assumption # has to be true. - return MatVecOp(self, queue, - arg_name, dtype, total_dofs, starts_and_ends, extra_args) + return MatVecOp(self, actx, + arg_name, dtype, total_dofs, discrs, starts_and_ends, extra_args) def eval(self, context=None, timing_data=None, - array_context: Optional[ArrayContext] = None): + array_context: Optional[PyOpenCLArrayContext] = None): """Evaluate the expression in *self*, using the :class:`pyopencl.CommandQueue` *queue* and the input variables given in the dictionary *context*. @@ -882,7 +901,7 @@ def eval(self, context=None, timing_data=None, (experimental) :arg array_context: only needs to be supplied if no instances of :class:`~meshmode.dof_array.DOFArray` with a - :class:`~meshmode.array_context.ArrayContext` + :class:`~meshmode.array_context.PyOpenCLArrayContext` are supplied as part of *context*. :returns: the value of the expression, as a scalar, :class:`pyopencl.array.Array`, or an object array of these. @@ -895,9 +914,10 @@ def eval(self, context=None, timing_data=None, array_contexts = [] if array_context is not None: - if not isinstance(array_context, ArrayContext): + if not isinstance(array_context, PyOpenCLArrayContext): raise TypeError( - "first argument (if supplied) must be an ArrayContext") + "first argument (if supplied) must be a " + "PyOpenCLArrayContext") array_contexts.append(array_context) del array_context @@ -942,9 +962,9 @@ def __call__(self, *args, **kwargs): array_context = None if len(args) == 1: array_context, = args - if not isinstance(array_context, ArrayContext): + if not isinstance(array_context, PyOpenCLArrayContext): raise TypeError("first positional argument (if given) " - "must be of type ArrayContext") + "must be of type PyOpenCLArrayContext") elif not args: pass @@ -1036,13 +1056,14 @@ def build_matrix(queue, places, exprs, input_exprs, domains=None, context = {} from pytential import GeometryCollection - from pytools.obj_array import is_obj_array, make_obj_array if not isinstance(places, GeometryCollection): places = GeometryCollection(places, auto_where=auto_where) exprs = _prepare_expr(places, exprs, auto_where=auto_where) - if not is_obj_array(exprs): + if not (isinstance(exprs, np.ndarray) and exprs.dtype.char == "O"): + from pytools.obj_array import make_obj_array exprs = make_obj_array([exprs]) + try: input_exprs = list(input_exprs) except TypeError: diff --git a/pytential/symbolic/pde/maxwell/__init__.py b/pytential/symbolic/pde/maxwell/__init__.py index d89d393c4..f7148cfd0 100644 --- a/pytential/symbolic/pde/maxwell/__init__.py +++ b/pytential/symbolic/pde/maxwell/__init__.py @@ -61,7 +61,7 @@ def get_sym_maxwell_point_source(kernel, jxyz, k): # https://en.wikipedia.org/w/index.php?title=Maxwell%27s_equations&oldid=798940325#Alternative_formulations # (Vector calculus/Potentials/Any Gauge) # assumed time dependence exp(-1j*omega*t) - return sym.join_fields( + return sym.flat_obj_array( 1j*k*A, sym.curl(A)) @@ -108,7 +108,7 @@ def get_sym_maxwell_plane_wave(amplitude_vec, v, omega, e = amplitude_vec * sym.exp(1j*np.dot(n*omega, x)) - return sym.join_fields(e, c_inv * sym.cross(n, e)) + return sym.flat_obj_array(e, c_inv * sym.cross(n, e)) # }}} @@ -180,7 +180,7 @@ def scattered_volume_field(self, Jt, rho, qbx_forced_limit=None): E_scat = 1j*self.k*A - sym.grad(3, phi) H_scat = sym.curl(A) - return sym.join_fields(E_scat, H_scat) + return sym.flat_obj_array(E_scat, H_scat) # }}} @@ -248,13 +248,13 @@ def curl_S(dens, k): # sign flip included F4 = -sym.n_dot(mu1*H1-mu0*H0) + 0.5*(mu1+mu0)*u.rho_m # noqa pylint:disable=invalid-unary-operand-type - return sym.join_fields(F1, F2, F3, F4) + return sym.flat_obj_array(F1, F2, F3, F4) def rhs(self, Einc_xyz, Hinc_xyz): mu1 = self.mus[1] eps1 = self.epss[1] - return sym.join_fields( + return sym.flat_obj_array( xyz_to_tangential(sym.n_cross(Hinc_xyz)), sym.n_dot(eps1*Einc_xyz), xyz_to_tangential(sym.n_cross(Einc_xyz)), @@ -280,7 +280,7 @@ def curl_S(dens): E0 = 1j*k*eps*S(Jxyz) + mu*curl_S(Mxyz) - grad(S(u.rho_e)) H0 = -1j*k*mu*S(Mxyz) + eps*curl_S(Jxyz) + grad(S(u.rho_m)) - return sym.join_fields(E0, H0) + return sym.flat_obj_array(E0, H0) # }}} diff --git a/pytential/symbolic/pde/maxwell/generalized_debye.py b/pytential/symbolic/pde/maxwell/generalized_debye.py index 1b6ea8ee9..df7fd9f50 100644 --- a/pytential/symbolic/pde/maxwell/generalized_debye.py +++ b/pytential/symbolic/pde/maxwell/generalized_debye.py @@ -153,8 +153,8 @@ def volume_field_base(self, r, q, j): E = 1j*k*A - grad_phi - curl_S_volume(k, m) H = curl_S_volume(k, j) + 1j*k*Q - grad_psi - from pytools.obj_array import join_fields - return join_fields(E, H) + from pytools.obj_array import flat_obj_array + return flat_obj_array(E, H) def integral_equation(self, *args, **kwargs): nxnxE, ndotH = self.boundary_field(*args) @@ -178,8 +178,8 @@ def integral_equation(self, *args, **kwargs): E_minus_grad_phi = 1j*k*A - curl_S_volume(k, m) from hellskitchen.fmm import DifferenceKernel - from pytools.obj_array import join_fields - return join_fields( + from pytools.obj_array import flat_obj_array + return flat_obj_array( eh_op, # FIXME: These are inefficient. They compute a full volume field, # but only actually use the line part of it. @@ -262,10 +262,10 @@ def inv_rank_one_coeff(u): r_coeff = inv_rank_one_coeff(r_tilde) q_coeff = inv_rank_one_coeff(q_tilde) - from pytools.obj_array import join_fields + from pytools.obj_array import flat_obj_array factors = self.cluster_points() - fix = join_fields( + fix = flat_obj_array( factors[0]*s_ones*r_coeff, factors[1]*Ones()*q_coeff, ) @@ -376,10 +376,10 @@ def inv_rank_one_coeff(u): r_coeff = inv_rank_one_coeff(r_tilde) q_coeff = inv_rank_one_coeff(q_tilde) - from pytools.obj_array import join_fields + from pytools.obj_array import flat_obj_array factors = self.cluster_points() - fix = join_fields( + fix = flat_obj_array( factors[0]*s_ones*(r_coeff), factors[1]*Ones()*(q_coeff), ) diff --git a/pytential/symbolic/primitives.py b/pytential/symbolic/primitives.py index 1799e09b4..e3ab08214 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -36,7 +36,7 @@ from pymbolic.geometric_algebra.primitives import ( # noqa: F401 NablaComponent, DerivativeSource, Derivative as DerivativeBase) from pymbolic.primitives import make_sym_vector # noqa: F401 -from pytools.obj_array import make_obj_array, join_fields # noqa: F401 +from pytools.obj_array import make_obj_array, flat_obj_array # noqa: F401 from functools import partial @@ -53,28 +53,33 @@ Based on the mathematical quantity being represented, the following types of objects occur as part of a symbolic operator representation: -* If a quantity is a scalar, it is just a symbolic expression--that is, a nested - combination of placeholders (see below), arithmetic on them (see - :mod:`pymbolic.primitives`. These objects are created simply by doing - arithmetic on placeholders. +* If a quantity is a scalar, it is just a symbolic expression--that is, an + element of the set of formal expressions recursively generated by the + placeholders (see :ref:`placeholders`), constants, and arithmetic operations + on them (see :mod:`pymbolic.primitives`). Objects of this type are created + simply by doing arithmetic on placeholders and scalar constants. * If the quantity is "just a bunch of scalars" (like, say, rows in a system - of integral equations), the symbolic representation an object array. Each + of integral equations), the symbolic representation is an object array. Each element of the object array contains a symbolic expression. :func:`pytools.obj_array.make_obj_array` and - :func:`pytools.obj_array.join_fields` + :func:`pytools.obj_array.flat_obj_array` can help create those. * If it is a geometric quantity (that makes sense without explicit reference to coordinates), it is a :class:`pymbolic.geometric_algebra.MultiVector`. - This can be converted to an object array by calling : + This can be converted to an object array by calling: :meth:`pymbolic.geometric_algebra.MultiVector.as_vector`. -:mod:`pyopencl.array.Array` instances do not occur on the symbolic of -:mod:`pymbolic` at all. Those hold per-node degrees of freedom (and only -those), which is not visible as an array axis in symbolic code. (They're -visible only once evaluated.) +:class:`pyopencl.array.Array` and :class:`meshmode.dof_array.DOFArray` instances +hold per-node degrees of freedom (and only those). Such instances do *not* occur +on the symbolic side of :mod:`pytential` at all. They're only visible either as +bound inputs (see :func:`pytential.bind`) or outputs of evaluation. Which one is +used depends on the meaning of the data being represented. If the data is +associated with a :class:`~meshmode.discretization.Discretization`, then +:class:`~meshmode.dof_array.DOFArray` is used and otherwise +:class:`~pyopencl.array.Array` is used. DOF Description ^^^^^^^^^^^^^^^ @@ -93,6 +98,8 @@ .. autoclass:: DOFDescriptor .. autofunction:: as_dofdesc +.. _placeholders: + Placeholders ^^^^^^^^^^^^ @@ -472,12 +479,12 @@ def __call__(self, operand, *args, **kwargs): # return an object array of the operator applied to each of the # operands. - from pytools.obj_array import is_obj_array, with_object_array_or_scalar - if is_obj_array(operand): + from pytools.obj_array import obj_array_vectorize + if isinstance(operand, np.ndarray) and operand.dtype.char == "O": def make_op(operand_i): return self(operand_i, *args, **kwargs) - return with_object_array_or_scalar(make_op, operand) + return obj_array_vectorize(make_op, operand) else: return var.__call__(self, operand, *args, **kwargs) @@ -1333,12 +1340,12 @@ def dd_axis(axis, ambient_dim, operand): """Return the derivative along (XYZ) axis *axis* (in *ambient_dim*-dimensional space) of *operand*. """ - from pytools.obj_array import is_obj_array, with_object_array_or_scalar - if is_obj_array(operand): + from pytools.obj_array import obj_array_vectorize + if isinstance(operand, np.ndarray) and operand.dtype.char == "O": def dd_axis_comp(operand_i): return dd_axis(axis, ambient_dim, operand_i) - return with_object_array_or_scalar(dd_axis_comp, operand) + return obj_array_vectorize(dd_axis_comp, operand) d = Derivative() diff --git a/pytential/target.py b/pytential/target.py index e677bdb7a..fc5dc9bf9 100644 --- a/pytential/target.py +++ b/pytential/target.py @@ -73,4 +73,7 @@ def nnodes(self): for coord_ary in self._nodes: return coord_ary.shape[0] + # FIXME: Rename + ndofs = nnodes + # vim: foldmethod=marker diff --git a/pytential/unregularized.py b/pytential/unregularized.py index 6f0125cd5..51399d0e7 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -108,11 +108,11 @@ def exec_compute_potential_insn(self, queue, insn, bound_expr, evaluate, "Timing data collection not supported.", category=UnableToCollectTimingData) - from pytools.obj_array import with_object_array_or_scalar + from pytools.obj_array import obj_array_vectorize def evaluate_wrapper(expr): value = evaluate(expr) - return with_object_array_or_scalar(lambda x: x, value) + return obj_array_vectorize(lambda x: x, value) if self.fmm_level_to_order is False: func = self.exec_compute_potential_insn_direct diff --git a/pytential/version.py b/pytential/version.py index 0849c083e..aafdf685f 100644 --- a/pytential/version.py +++ b/pytential/version.py @@ -43,7 +43,7 @@ # }}} -VERSION = (2020, 1) +VERSION = (2020, 2) VERSION_TEXT = ".".join(str(i) for i in VERSION) PYTENTIAL_KERNEL_VERSION = (VERSION, _git_rev, 0) diff --git a/setup.py b/setup.py index ae80e32d5..89c01ca0a 100644 --- a/setup.py +++ b/setup.py @@ -119,7 +119,7 @@ def write_git_revision(package_name): "boxtree>=2019.1", "pymbolic>=2013.2", "loo.py>=2017.2", - "sumpy>=2013.1", + "sumpy>=2020.2", "cgen>=2013.1.2", "pyfmmlib>=2019.1.1", From 13067297a9728ae4fe0f782d2835cd92c6d78dac Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 00:13:10 -0500 Subject: [PATCH 08/75] Relax sumpy version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 89c01ca0a..ae80e32d5 100644 --- a/setup.py +++ b/setup.py @@ -119,7 +119,7 @@ def write_git_revision(package_name): "boxtree>=2019.1", "pymbolic>=2013.2", "loo.py>=2017.2", - "sumpy>=2020.2", + "sumpy>=2013.1", "cgen>=2013.1.2", "pyfmmlib>=2019.1.1", From c738c07950cb106f58b15ec7b803ea652d0d0a3e Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 00:18:40 -0500 Subject: [PATCH 09/75] Add utils.py --- pytential/utils.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 pytential/utils.py diff --git a/pytential/utils.py b/pytential/utils.py new file mode 100644 index 000000000..fb772c0fa --- /dev/null +++ b/pytential/utils.py @@ -0,0 +1,46 @@ +__copyright__ = """ +Copyright (C) 2020 Matt Wala +""" + +__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 +from meshmode.array_context import PyOpenCLArrayContext + + +def flatten_if_needed(actx: PyOpenCLArrayContext, ary: np.ndarray): + from pytools.obj_array import obj_array_vectorize_n_args + from meshmode.dof_array import DOFArray, thaw, flatten + + if (isinstance(ary, np.ndarray) + and ary.dtype.char == "O" + and not isinstance(ary, DOFArray)): + return obj_array_vectorize_n_args(flatten_if_needed, actx, ary) + + if not isinstance(ary, DOFArray): + return ary + + if ary.array_context is None: + ary = thaw(actx, ary) + + return flatten(ary) + +# vim: foldmethod=marker From c1678d12bce7223b8ada2ec7f2532a5062c5bfea Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 09:33:37 -0500 Subject: [PATCH 10/75] pylint fixes --- pytential/qbx/geometry.py | 2 +- pytential/unregularized.py | 10 +++++----- test/test_cost_model.py | 2 +- test/test_layer_pot.py | 6 +++--- test/test_layer_pot_identity.py | 2 +- test/test_maxwell.py | 6 +++--- test/test_symbolic.py | 4 ++-- test/test_tools.py | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pytential/qbx/geometry.py b/pytential/qbx/geometry.py index 4c9e124a9..1fa28a180 100644 --- a/pytential/qbx/geometry.py +++ b/pytential/qbx/geometry.py @@ -958,7 +958,7 @@ def plot(self, draw_circles=False, draw_center_numbers=False, if draw_circles: for icenter, (cx, cy, r) in enumerate(zip( centers[0], centers[1], - self.expansion_radii().get(queue))): + self.flat_expansion_radii().get(queue))): ax.add_artist( pt.Circle((cx, cy), r, fill=False, ls="dotted", lw=1)) diff --git a/pytential/unregularized.py b/pytential/unregularized.py index 51399d0e7..386bcc027 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -138,7 +138,7 @@ def preprocess_optemplate(self, name, discretizations, expr): from pytential.symbolic.mappers import UnregularizedPreprocessor return UnregularizedPreprocessor(name, discretizations)(expr) - def exec_compute_potential_insn_direct(self, queue, insn, bound_expr, evaluate): + def exec_compute_potential_insn_direct(self, actx, insn, bound_expr, evaluate): kernel_args = {} for arg_name, arg_expr in six.iteritems(insn.kernel_arguments): @@ -146,8 +146,8 @@ def exec_compute_potential_insn_direct(self, queue, insn, bound_expr, evaluate): from pytential import bind, sym waa = bind(bound_expr.places, sym.weights_and_area_elements( - self.ambient_dim, dofdesc=insn.source))(queue) - strengths = waa * evaluate(insn.density).with_queue(queue) + self.ambient_dim, dofdesc=insn.source))(actx) + strengths = waa * evaluate(insn.density) result = [] p2p = None @@ -157,9 +157,9 @@ def exec_compute_potential_insn_direct(self, queue, insn, bound_expr, evaluate): o.target_name.geometry, o.target_name.discr_stage) if p2p is None: - p2p = self.get_p2p(insn.kernels) + p2p = self.get_p2p(actx, insn.kernels) - evt, output_for_each_kernel = p2p(queue, + evt, output_for_each_kernel = p2p(actx, target_discr.nodes(), self.density_discr.nodes(), [strengths], **kernel_args) diff --git a/test/test_cost_model.py b/test/test_cost_model.py index 2ebac61c3..1edc7d9b3 100644 --- a/test/test_cost_model.py +++ b/test/test_cost_model.py @@ -123,7 +123,7 @@ def test_timing_data_gathering(ctx_factory): op_S = bind(places, sym_op_S) timing_data = {} - op_S.eval(queue, dict(sigma=sigma), timing_data=timing_data) + op_S.eval(dict(sigma=sigma), timing_data=timing_data, array_context=queue) assert timing_data print(timing_data) diff --git a/test/test_layer_pot.py b/test/test_layer_pot.py index c7eeb200c..25f19144b 100644 --- a/test/test_layer_pot.py +++ b/test/test_layer_pot.py @@ -268,7 +268,7 @@ def test_unregularized_with_ones_kernel(ctx_factory): sigma_sym = sym.var("sigma") op = sym.IntG(one_kernel_2d, sigma_sym, qbx_forced_limit=None) - sigma = cl.array.zeros(queue, discr.nnodes, dtype=float) + sigma = cl.array.zeros(queue, discr.ndofs, dtype=float) sigma.fill(1) sigma.finish() @@ -452,8 +452,8 @@ def nxcurlS(qbx_forced_limit): h_max = bind(places, sym.h_max(qbx.ambient_dim))(queue) err = ( - norm(density_discr, queue, jump_identity, np.inf) - / norm(density_discr, queue, density, np.inf)) + norm(density_discr, jump_identity, np.inf) + / norm(density_discr, density, np.inf)) print("ERROR", h_max, err) eoc_rec.add_data_point(h_max, err) diff --git a/test/test_layer_pot_identity.py b/test/test_layer_pot_identity.py index 7b0cbc65c..62c1c3950 100644 --- a/test/test_layer_pot_identity.py +++ b/test/test_layer_pot_identity.py @@ -390,7 +390,7 @@ def test_identity_convergence(ctx_factory, case, visualize=False): pt.plot(error) pt.show() - linf_error_norm = norm(density_discr, queue, error, p=np.inf) + linf_error_norm = norm(density_discr, error, p=np.inf) print("--->", key, linf_error_norm) h_max = bind(places, sym.h_max(qbx.ambient_dim))(queue) diff --git a/test/test_maxwell.py b/test/test_maxwell.py index 2e950193e..129cd217e 100644 --- a/test/test_maxwell.py +++ b/test/test_maxwell.py @@ -415,12 +415,12 @@ def eval_repr_at(tgt, source=None, target=None): pec_bc_e = sym.n_cross(bc_repr.e + inc_xyz_sym.e) pec_bc_h = sym.normal(3).as_vector().dot(bc_repr.h + inc_xyz_sym.h) - eh_bc_values = bind(places, sym.join_fields(pec_bc_e, pec_bc_h))( + eh_bc_values = bind(places, sym.flat_obj_array(pec_bc_e, pec_bc_h))( queue, jt=jt, rho=rho, inc_fld=inc_field_scat.field, **knl_kwargs) def scat_norm(f): - return norm(density_discr, queue, f, p=np.inf) + return norm(density_discr, f, p=np.inf) e_bc_residual = scat_norm(eh_bc_values[:3]) / scat_norm(inc_field_scat.e) h_bc_residual = scat_norm(eh_bc_values[3]) / scat_norm(inc_field_scat.h) @@ -484,7 +484,7 @@ def scat_norm(f): obs_repr = EHField(eval_repr_at(places, target="obs_discr")) def obs_norm(f): - return norm(obs_discr, queue, f, p=np.inf) + return norm(obs_discr, f, p=np.inf) rel_err_e = (obs_norm(inc_field_obs.e + obs_repr.e) / obs_norm(inc_field_obs.e)) diff --git a/test/test_symbolic.py b/test/test_symbolic.py index 37b86c12a..99c633c8f 100644 --- a/test/test_symbolic.py +++ b/test/test_symbolic.py @@ -62,7 +62,7 @@ def get_ellipse_with_ref_mean_curvature(cl_ctx, nelements, aspect=1): InterpolatoryQuadratureSimplexGroupFactory(order)) with cl.CommandQueue(cl_ctx) as queue: - nodes = discr.nodes().get(queue=queue) + nodes = discr.nodes() a = 1 b = 1/aspect @@ -83,7 +83,7 @@ def get_torus_with_ref_mean_curvature(cl_ctx, h): InterpolatoryQuadratureSimplexGroupFactory(order)) with cl.CommandQueue(cl_ctx) as queue: - nodes = discr.nodes().get(queue=queue) + nodes = discr.nodes() # copied from meshmode.mesh.generation.generate_torus a = r_major diff --git a/test/test_tools.py b/test/test_tools.py index d2f107848..b95c99e2f 100644 --- a/test/test_tools.py +++ b/test/test_tools.py @@ -83,7 +83,7 @@ def test_interpolatory_error_reporting(ctx_factory): vol_discr = Discretization(ctx, mesh, QuadratureSimplexGroupFactory(5)) - vol_x = vol_discr.nodes().with_queue(queue) + vol_x = vol_discr.nodes() # }}} @@ -93,7 +93,7 @@ def test_interpolatory_error_reporting(ctx_factory): one = rhs.copy() one.fill(1) with pytest.raises(TypeError): - print("AREA", integral(vol_discr, queue, one), 0.25**2*np.pi) + print("AREA", integral(vol_discr, one), 0.25**2*np.pi) def test_geometry_collection_caching(ctx_factory): From 657c35f659e8fdb50770b0ecab7a1b85473105e9 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 09:36:33 -0500 Subject: [PATCH 11/75] flake8 fixes --- test/test_symbolic.py | 4 ++-- test/test_tools.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_symbolic.py b/test/test_symbolic.py index 99c633c8f..6425c9a03 100644 --- a/test/test_symbolic.py +++ b/test/test_symbolic.py @@ -61,7 +61,7 @@ def get_ellipse_with_ref_mean_curvature(cl_ctx, nelements, aspect=1): discr = Discretization(cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(order)) - with cl.CommandQueue(cl_ctx) as queue: + with cl.CommandQueue(cl_ctx) as queue: # noqa nodes = discr.nodes() a = 1 @@ -82,7 +82,7 @@ def get_torus_with_ref_mean_curvature(cl_ctx, h): discr = Discretization(cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(order)) - with cl.CommandQueue(cl_ctx) as queue: + with cl.CommandQueue(cl_ctx) as queue: # noqa nodes = discr.nodes() # copied from meshmode.mesh.generation.generate_torus diff --git a/test/test_tools.py b/test/test_tools.py index b95c99e2f..53f91a000 100644 --- a/test/test_tools.py +++ b/test/test_tools.py @@ -62,7 +62,7 @@ def test_interpolatory_error_reporting(ctx_factory): logging.basicConfig(level=logging.INFO) ctx = ctx_factory() - queue = cl.CommandQueue(ctx) + queue = cl.CommandQueue(ctx) # noqa h = 0.2 from meshmode.mesh.io import generate_gmsh, FileSource From 51fa634328fb2a7b5c7241cee68cfa54204c57ea Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 09:40:10 -0500 Subject: [PATCH 12/75] requirements.txt: Point to revert-cffi-go-to-pybind branch of islpy --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e5cac035f..f78b6cfc9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ git+git://github.com/inducer/pymbolic sympy==1.1.1 git+https://github.com/inducer/modepy git+https://github.com/inducer/pyopencl -git+https://github.com/inducer/islpy +git+https://gitlab.tiker.net/inducer/islpy@revert-cffi-go-to-pybind git+https://github.com/inducer/loopy git+https://gitlab.tiker.net/inducer/boxtree git+https://gitlab.tiker.net/inducer/meshmode@array-context From 8a70c0df8bb0a6dda5f24b224157c948424ac543 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 10:03:24 -0500 Subject: [PATCH 13/75] Port over some tests --- .gitlab-ci.yml | 36 +++++++++++----------- setup.cfg | 1 + test/test_cost_model.py | 51 ++++++++++++++++++-------------- test/test_target_specific_qbx.py | 16 ++++++---- test/test_tools.py | 21 ++++++------- 5 files changed, 69 insertions(+), 56 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8c87a27dc..f9828212e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,24 +4,24 @@ # which skips the slow running tests. # * SKIP_EXAMPLES, if non-empty, can be used to skip the examples job. -# Python 3 POCL: -# script: -# - export PY_EXE=python3 -# - export PYOPENCL_TEST=portable:pthread -# - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} -# - export EXTRA_INSTALL="Cython pybind11 numpy scipy mako" -# - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh -# - ". ./build-and-test-py-project.sh" -# tags: -# - python3 -# - pocl -# - large-node -# except: -# - tags -# artifacts: -# reports: -# junit: test/pytest.xml -# +Python 3 POCL: + script: + - export PY_EXE=python3 + - export PYOPENCL_TEST=portable:pthread + - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} + - export EXTRA_INSTALL="Cython pybind11 numpy scipy mako" + - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh + - ". ./build-and-test-py-project.sh" + tags: + - python3 + - pocl + - large-node + except: + - tags + artifacts: + reports: + junit: test/pytest.xml + # Python 3 Intel: # script: # - export PY_EXE=python3 diff --git a/setup.cfg b/setup.cfg index a353f3f72..b59a3fd46 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,3 +9,4 @@ exclude= [tool:pytest] markers= slowtest: mark a test as slow +python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py diff --git a/test/test_cost_model.py b/test/test_cost_model.py index 1edc7d9b3..e99ce8672 100644 --- a/test/test_cost_model.py +++ b/test/test_cost_model.py @@ -26,6 +26,7 @@ import numpy.linalg as la # noqa from boxtree.tools import ConstantOneExpansionWrangler +from meshmode.array_context import PyOpenCLArrayContext import pyopencl as cl import pyopencl.clmath # noqa import pytest @@ -55,7 +56,7 @@ } -def get_lpot_source(queue, dim): +def get_lpot_source(actx: PyOpenCLArrayContext, dim): from meshmode.discretization import Discretization from meshmode.discretization.poly_element import ( InterpolatoryQuadratureSimplexGroupFactory) @@ -72,7 +73,7 @@ def get_lpot_source(queue, dim): raise ValueError("unsupported dimension: %d" % dim) pre_density_discr = Discretization( - queue.context, mesh, + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) lpot_kwargs = DEFAULT_LPOT_KWARGS.copy() @@ -91,9 +92,10 @@ def get_lpot_source(queue, dim): return lpot_source -def get_density(queue, discr): - nodes = discr.nodes().with_queue(queue) - return cl.clmath.sin(10 * nodes[0]) +def get_density(actx, discr): + from meshmode.dof_array import thaw + nodes = thaw(actx, discr.nodes()) + return actx.np.sin(10 * nodes[0]) # }}} @@ -108,13 +110,14 @@ def test_timing_data_gathering(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx, properties=cl.command_queue_properties.PROFILING_ENABLE) + actx = PyOpenCLArrayContext(queue) - lpot_source = get_lpot_source(queue, 2) + lpot_source = get_lpot_source(actx, 2) places = GeometryCollection(lpot_source) dofdesc = places.auto_source.to_stage1() density_discr = places.get_discretization(dofdesc.geometry) - sigma = get_density(queue, density_discr) + sigma = get_density(actx, density_discr) sigma_sym = sym.var("sigma") k_sym = LaplaceKernel(lpot_source.ambient_dim) @@ -123,7 +126,7 @@ def test_timing_data_gathering(ctx_factory): op_S = bind(places, sym_op_S) timing_data = {} - op_S.eval(dict(sigma=sigma), timing_data=timing_data, array_context=queue) + op_S.eval(dict(sigma=sigma), timing_data=timing_data, array_context=actx) assert timing_data print(timing_data) @@ -140,28 +143,29 @@ def test_cost_model(ctx_factory, dim, use_target_specific_qbx): """Test that cost model gathering can execute successfully.""" cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) - lpot_source = get_lpot_source(queue, dim).copy( + lpot_source = get_lpot_source(actx, dim).copy( _use_target_specific_qbx=use_target_specific_qbx, cost_model=CostModel()) places = GeometryCollection(lpot_source) density_discr = places.get_discretization(places.auto_source.geometry) - sigma = get_density(queue, density_discr) + sigma = get_density(actx, density_discr) sigma_sym = sym.var("sigma") k_sym = LaplaceKernel(lpot_source.ambient_dim) sym_op_S = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) op_S = bind(places, sym_op_S) - cost_S = op_S.get_modeled_cost(queue, sigma=sigma) + cost_S = op_S.get_modeled_cost(actx, sigma=sigma) assert len(cost_S) == 1 sym_op_S_plus_D = ( sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) + sym.D(k_sym, sigma_sym, qbx_forced_limit="avg")) op_S_plus_D = bind(places, sym_op_S_plus_D) - cost_S_plus_D = op_S_plus_D.get_modeled_cost(queue, sigma=sigma) + cost_S_plus_D = op_S_plus_D.get_modeled_cost(actx, sigma=sigma) assert len(cost_S_plus_D) == 2 # }}} @@ -173,17 +177,18 @@ def test_cost_model_metadata_gathering(ctx_factory): """Test that the cost model correctly gathers metadata.""" cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) from sumpy.expansion.level_to_order import SimpleExpansionOrderFinder fmm_level_to_order = SimpleExpansionOrderFinder(tol=1e-5) - lpot_source = get_lpot_source(queue, 2).copy( + lpot_source = get_lpot_source(actx, 2).copy( fmm_level_to_order=fmm_level_to_order) places = GeometryCollection(lpot_source) density_discr = places.get_discretization(places.auto_source.geometry) - sigma = get_density(queue, density_discr) + sigma = get_density(actx, density_discr) sigma_sym = sym.var("sigma") k_sym = HelmholtzKernel(2, "k") @@ -192,7 +197,7 @@ def test_cost_model_metadata_gathering(ctx_factory): sym_op_S = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1, k=sym.var("k")) op_S = bind(places, sym_op_S) - cost_S = one(op_S.get_modeled_cost(queue, sigma=sigma, k=k).values()) + cost_S = one(op_S.get_modeled_cost(actx, sigma=sigma, k=k).values()) geo_data = lpot_source.qbx_fmm_geometry_data( places, @@ -437,12 +442,13 @@ def test_cost_model_correctness(ctx_factory, dim, off_surface, """Check that computed cost matches that of a constant-one FMM.""" cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) cost_model = ( CostModel( translation_cost_model_factory=OpCountingTranslationCostModel)) - lpot_source = get_lpot_source(queue, dim).copy( + lpot_source = get_lpot_source(actx, dim).copy( cost_model=cost_model, _use_target_specific_qbx=use_target_specific_qbx) @@ -470,10 +476,10 @@ def test_cost_model_correctness(ctx_factory, dim, off_surface, sym_op_S = sym.S(k_sym, sigma_sym, qbx_forced_limit=qbx_forced_limit) op_S = bind(places, sym_op_S) - sigma = get_density(queue, density_discr) + sigma = get_density(actx, density_discr) from pytools import one - cost_S = one(op_S.get_modeled_cost(queue, sigma=sigma).values()) + cost_S = one(op_S.get_modeled_cost(actx, sigma=sigma).values()) # Run FMM with ConstantOneWrangler. This can't be done with pytential's # high-level interface, so call the FMM driver directly. @@ -487,7 +493,7 @@ def test_cost_model_correctness(ctx_factory, dim, off_surface, quad_stage2_density_discr = places.get_discretization( source_dd.geometry, sym.QBX_SOURCE_QUAD_STAGE2) - nnodes = quad_stage2_density_discr.nnodes + nnodes = quad_stage2_density_discr.ndofs src_weights = np.ones(nnodes) timing_data = {} @@ -538,13 +544,14 @@ def test_cost_model_order_varying_by_level(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) # {{{ constant level to order def level_to_order_constant(kernel, kernel_args, tree, level): return 1 - lpot_source = get_lpot_source(queue, 2).copy( + lpot_source = get_lpot_source(actx, 2).copy( cost_model=CostModel( calibration_params=CONSTANT_ONE_PARAMS), fmm_level_to_order=level_to_order_constant) @@ -556,11 +563,11 @@ def level_to_order_constant(kernel, kernel_args, tree, level): k_sym = LaplaceKernel(2) sym_op = sym.S(k_sym, sigma_sym, qbx_forced_limit=+1) - sigma = get_density(queue, density_discr) + sigma = get_density(actx, density_discr) cost_constant = one( bind(places, sym_op) - .get_modeled_cost(queue, sigma=sigma).values()) + .get_modeled_cost(actx, sigma=sigma).values()) # }}} diff --git a/test/test_target_specific_qbx.py b/test/test_target_specific_qbx.py index 551bb38c5..f4749260a 100644 --- a/test/test_target_specific_qbx.py +++ b/test/test_target_specific_qbx.py @@ -23,6 +23,7 @@ """ +from meshmode.array_context import PyOpenCLArrayContext import numpy as np import numpy.linalg as la # noqa import pyopencl as cl @@ -140,6 +141,7 @@ def test_target_specific_qbx(ctx_factory, op, helmholtz_k, qbx_order): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) target_order = 4 fmm_tol = 1e-3 @@ -152,7 +154,7 @@ def test_target_specific_qbx(ctx_factory, op, helmholtz_k, qbx_order): InterpolatoryQuadratureSimplexGroupFactory from pytential.qbx import QBXLayerPotentialSource pre_density_discr = Discretization( - cl_ctx, mesh, + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) from sumpy.expansion.level_to_order import SimpleExpansionOrderFinder @@ -174,12 +176,13 @@ def test_target_specific_qbx(ctx_factory, op, helmholtz_k, qbx_order): from pytential.qbx.refinement import refine_geometry_collection places = GeometryCollection(places, auto_where="qbx") - places = refine_geometry_collection(queue, places, + places = refine_geometry_collection(places, kernel_length_scale=kernel_length_scale) density_discr = places.get_discretization("qbx") - nodes = density_discr.nodes().with_queue(queue) - u_dev = clmath.sin(nodes[0]) + from meshmode.dof_array import thaw + nodes = thaw(actx, density_discr.nodes()) + u_dev = actx.np.sin(nodes[0]) if helmholtz_k == 0: kernel = LaplaceKernel(3) @@ -201,11 +204,12 @@ def test_target_specific_qbx(ctx_factory, op, helmholtz_k, qbx_order): expr = op(kernel, u_sym, qbx_forced_limit=-1, **kernel_kwargs) + from meshmode.dof_array import flatten bound_op = bind(places, expr) - pot_ref = bound_op(queue, u=u_dev, k=helmholtz_k).get() + pot_ref = actx.to_numpy(flatten(bound_op(actx, u=u_dev, k=helmholtz_k))) bound_op = bind(places, expr, auto_where="qbx_target_specific") - pot_tsqbx = bound_op(queue, u=u_dev, k=helmholtz_k).get() + pot_tsqbx = actx.to_numpy(flatten(bound_op(actx, u=u_dev, k=helmholtz_k))) assert np.allclose(pot_tsqbx, pot_ref, atol=1e-13, rtol=1e-13) diff --git a/test/test_tools.py b/test/test_tools.py index 53f91a000..cebabc730 100644 --- a/test/test_tools.py +++ b/test/test_tools.py @@ -24,6 +24,7 @@ from functools import partial +from meshmode.array_context import PyOpenCLArrayContext import pytest import numpy as np @@ -62,7 +63,8 @@ def test_interpolatory_error_reporting(ctx_factory): logging.basicConfig(level=logging.INFO) ctx = ctx_factory() - queue = cl.CommandQueue(ctx) # noqa + queue = cl.CommandQueue(ctx) + actx = PyOpenCLArrayContext(queue) h = 0.2 from meshmode.mesh.io import generate_gmsh, FileSource @@ -80,20 +82,18 @@ def test_interpolatory_error_reporting(ctx_factory): from meshmode.discretization.poly_element import \ QuadratureSimplexGroupFactory - vol_discr = Discretization(ctx, mesh, + vol_discr = Discretization(actx, mesh, QuadratureSimplexGroupFactory(5)) - vol_x = vol_discr.nodes() + from meshmode.dof_array import thaw + vol_x = thaw(actx, vol_discr.nodes()) # }}} from pytential import integral - rhs = 1 + 0*vol_x[0] - - one = rhs.copy() - one.fill(1) + one = 1 + 0*vol_x[0] with pytest.raises(TypeError): - print("AREA", integral(vol_discr, one), 0.25**2*np.pi) + print("AREA", integral(vol_discr, queue, one), 0.25**2*np.pi) def test_geometry_collection_caching(ctx_factory): @@ -103,6 +103,7 @@ def test_geometry_collection_caching(ctx_factory): # the `nodes` on each `discr_stage`. ctx = ctx_factory() queue = cl.CommandQueue(ctx) + actx = PyOpenCLArrayContext(queue) ndim = 2 nelements = 1024 @@ -128,7 +129,7 @@ def test_geometry_collection_caching(ctx_factory): mesh = affine_map(discrs[0].mesh, b=np.array([3 * k * radius, 0])) - discr = Discretization(ctx, mesh, + discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) discrs.append(discr) @@ -161,7 +162,7 @@ def test_geometry_collection_caching(ctx_factory): discr = places._get_discr_from_cache(sources[k], discr_stage) dofdesc = sym.DOFDescriptor(sources[k], discr_stage=discr_stage) - bind(places, sym.nodes(ndim, dofdesc=dofdesc))(queue) + bind(places, sym.nodes(ndim, dofdesc=dofdesc))(actx) discr = places._get_discr_from_cache(sources[k], discr_stage) assert discr is not None From 54a125ab160991303c3074aefafefab595106cfe Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 10:04:05 -0500 Subject: [PATCH 14/75] flake8 fix --- test/test_target_specific_qbx.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_target_specific_qbx.py b/test/test_target_specific_qbx.py index f4749260a..440e74862 100644 --- a/test/test_target_specific_qbx.py +++ b/test/test_target_specific_qbx.py @@ -27,7 +27,6 @@ import numpy as np import numpy.linalg as la # noqa import pyopencl as cl -import pyopencl.clmath as clmath import pytest from pyopencl.tools import ( # noqa pytest_generate_tests_for_pyopencl as pytest_generate_tests) From 36919c9ba0da78d45c417b2cc2427d8713639171 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 10:13:04 -0500 Subject: [PATCH 15/75] Improve test specificity --- test/test_tools.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/test_tools.py b/test/test_tools.py index cebabc730..89bfcd785 100644 --- a/test/test_tools.py +++ b/test/test_tools.py @@ -92,8 +92,9 @@ def test_interpolatory_error_reporting(ctx_factory): from pytential import integral one = 1 + 0*vol_x[0] - with pytest.raises(TypeError): - print("AREA", integral(vol_discr, queue, one), 0.25**2*np.pi) + from meshmode.discretization import NoninterpolatoryElementGroupError + with pytest.raises(NoninterpolatoryElementGroupError): + print("AREA", integral(vol_discr, one), 0.25**2*np.pi) def test_geometry_collection_caching(ctx_factory): From 16864eca15a19fbb4f9ede3c67ce9aebae2d16c3 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 13:02:20 -0500 Subject: [PATCH 16/75] Port test_symbolic.py --- setup.cfg | 2 +- test/test_symbolic.py | 71 +++++++++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/setup.cfg b/setup.cfg index b59a3fd46..155dc443e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,4 +9,4 @@ exclude= [tool:pytest] markers= slowtest: mark a test as slow -python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py +python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py, test_symbolic.py diff --git a/test/test_symbolic.py b/test/test_symbolic.py index 6425c9a03..a510596cd 100644 --- a/test/test_symbolic.py +++ b/test/test_symbolic.py @@ -39,6 +39,7 @@ from pyopencl.tools import ( # noqa pytest_generate_tests_for_pyopencl as pytest_generate_tests) +from meshmode.array_context import PyOpenCLArrayContext from meshmode.mesh.generation import ( # noqa ellipse, cloverleaf, starfish, drop, n_gon, qbx_peanut, WobblyCircle, make_curve_mesh) @@ -51,27 +52,27 @@ # {{{ discretization getters -def get_ellipse_with_ref_mean_curvature(cl_ctx, nelements, aspect=1): +def get_ellipse_with_ref_mean_curvature(actx, nelements, aspect=1): order = 4 mesh = make_curve_mesh( partial(ellipse, aspect), np.linspace(0, 1, nelements+1), order) - discr = Discretization(cl_ctx, mesh, + discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(order)) - with cl.CommandQueue(cl_ctx) as queue: # noqa - nodes = discr.nodes() + from meshmode.dof_array import thaw + nodes = thaw(actx, discr.nodes()) a = 1 b = 1/aspect - t = np.arctan2(nodes[1] * aspect, nodes[0]) + t = actx.np.atan2(nodes[1] * aspect, nodes[0]) - return discr, a*b / ((a*np.sin(t))**2 + (b*np.cos(t))**2)**(3/2) + return discr, a*b / ((a*actx.np.sin(t))**2 + (b*actx.np.cos(t))**2)**(3/2) -def get_torus_with_ref_mean_curvature(cl_ctx, h): +def get_torus_with_ref_mean_curvature(actx, h): order = 4 r_minor = 1.0 r_major = 3.0 @@ -79,20 +80,21 @@ def get_torus_with_ref_mean_curvature(cl_ctx, h): from meshmode.mesh.generation import generate_torus mesh = generate_torus(r_major, r_minor, n_major=h, n_minor=h, order=order) - discr = Discretization(cl_ctx, mesh, + discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(order)) - with cl.CommandQueue(cl_ctx) as queue: # noqa - nodes = discr.nodes() + from meshmode.dof_array import thaw + nodes = thaw(actx, discr.nodes()) # copied from meshmode.mesh.generation.generate_torus a = r_major b = r_minor - u = np.arctan2(nodes[1], nodes[0]) - rvec = np.array([np.cos(u), np.sin(u), np.zeros_like(u)]) - rvec = np.sum(nodes * rvec, axis=0) - a - cosv = np.cos(np.arctan2(nodes[2], rvec)) + u = actx.np.atan2(nodes[1], nodes[0]) + from pytools.obj_array import flat_obj_array + rvec = flat_obj_array(actx.np.cos(u), actx.np.sin(u), 0*u) + rvec = sum(nodes * rvec) - a + cosv = actx.np.cos(actx.np.atan2(nodes[2], rvec)) return discr, (a + 2.0 * b * cosv) / (2 * b * (a + b * cosv)) @@ -115,19 +117,20 @@ def test_mean_curvature(ctx_factory, discr_name, resolutions, discr_and_ref_mean_curvature_getter, visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) + actx = PyOpenCLArrayContext(queue) from pytools.convergence import EOCRecorder eoc = EOCRecorder() for r in resolutions: discr, ref_mean_curvature = \ - discr_and_ref_mean_curvature_getter(ctx, r) + discr_and_ref_mean_curvature_getter(actx, r) mean_curvature = bind( - discr, - sym.mean_curvature(discr.ambient_dim))(queue).get(queue) + discr, sym.mean_curvature(discr.ambient_dim))(actx) h = 1.0 / r - h_error = la.norm(mean_curvature - ref_mean_curvature, np.inf) + from meshmode.dof_array import flat_norm + h_error = flat_norm(mean_curvature - ref_mean_curvature, np.inf) eoc.add_data_point(h, h_error) print(eoc) @@ -142,13 +145,13 @@ def test_mean_curvature(ctx_factory, discr_name, resolutions, def test_tangential_onb(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) from meshmode.mesh.generation import generate_torus mesh = generate_torus(5, 2, order=3) discr = Discretization( - cl_ctx, mesh, - InterpolatoryQuadratureSimplexGroupFactory(3)) + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(3)) tob = sym.tangential_onb(mesh.ambient_dim) nvecs = tob.shape[1] @@ -157,8 +160,10 @@ def test_tangential_onb(ctx_factory): orth_check = bind(discr, sym.make_obj_array([ np.dot(tob[:, i], tob[:, j]) - (1 if i == j else 0) for i in range(nvecs) for j in range(nvecs)]) - )(queue) + )(actx) + from meshmode.dof_array import flatten + orth_check = flatten(orth_check) for i, orth_i in enumerate(orth_check): assert (cl.clmath.fabs(orth_i) < 1e-13).get().all() @@ -166,8 +171,9 @@ def test_tangential_onb(ctx_factory): orth_check = bind(discr, sym.make_obj_array([ np.dot(tob[:, i], sym.normal(mesh.ambient_dim).as_vector()) for i in range(nvecs)]) - )(queue) + )(actx) + orth_check = flatten(orth_check) for i, orth_i in enumerate(orth_check): assert (cl.clmath.fabs(orth_i) < 1e-13).get().all() @@ -227,6 +233,7 @@ def test_layer_potential_construction(lpot_class, ambient_dim=2): def test_interpolation(ctx_factory, name, source_discr_stage, target_granularity): ctx = ctx_factory() queue = cl.CommandQueue(ctx) + actx = PyOpenCLArrayContext(queue) nelements = 32 target_order = 7 @@ -245,7 +252,7 @@ def test_interpolation(ctx_factory, name, source_discr_stage, target_granularity mesh = make_curve_mesh(starfish, np.linspace(0.0, 1.0, nelements + 1), target_order) - discr = Discretization(ctx, mesh, + discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) from pytential.qbx import QBXLayerPotentialSource @@ -261,16 +268,22 @@ def test_interpolation(ctx_factory, name, source_discr_stage, target_granularity op_sym = sym.sin(sym.interp(from_dd, to_dd, sigma_sym)) bound_op = bind(places, op_sym, auto_where=where) - def nodes(stage): + from meshmode.dof_array import thaw, flatten, unflatten + + def discr_and_nodes(stage): density_discr = places.get_discretization(where.geometry, stage) - return density_discr.nodes().get(queue) + return density_discr, np.array([ + actx.to_numpy(flatten(axis)) + for axis in thaw(actx, density_discr.nodes())]) - target_nodes = nodes(sym.QBX_SOURCE_QUAD_STAGE2) - source_nodes = nodes(source_discr_stage) + _, target_nodes = discr_and_nodes(sym.QBX_SOURCE_QUAD_STAGE2) + source_discr, source_nodes = discr_and_nodes(source_discr_stage) - 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) + sigma_dev = unflatten( + actx, source_discr, + actx.from_numpy(la.norm(source_nodes, axis=0))) + sigma_target_interp = actx.to_numpy(flatten(bound_op(actx, sigma=sigma_dev))) if name in ("default", "default_explicit", "stage2", "quad"): error = la.norm(sigma_target_interp - sigma_target) / la.norm(sigma_target) From fddc8c4ba0e65ed7315ca79c08fb2b7c1776bb0b Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 13:05:56 -0500 Subject: [PATCH 17/75] Mark test_muller.py as OK --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 155dc443e..7a4d7d688 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,4 +9,4 @@ exclude= [tool:pytest] markers= slowtest: mark a test as slow -python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py, test_symbolic.py +python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py, test_symbolic.py, test_muller.py From 473de87c87231702e13bdd52b0b1654577745782 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 13:07:40 -0500 Subject: [PATCH 18/75] formatting --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 7a4d7d688..fbd2809cc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,4 +9,4 @@ exclude= [tool:pytest] markers= slowtest: mark a test as slow -python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py, test_symbolic.py, test_muller.py +python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py From bb6d56e3f7a67c096b9db771b7a2331d65d1ccef Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 13:22:31 -0500 Subject: [PATCH 19/75] Port test_global_qbx.py --- setup.cfg | 2 +- test/test_global_qbx.py | 127 +++++++++++++++++++++++++--------------- 2 files changed, 82 insertions(+), 47 deletions(-) diff --git a/setup.cfg b/setup.cfg index fbd2809cc..4e0403d50 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,4 +9,4 @@ exclude= [tool:pytest] markers= slowtest: mark a test as slow -python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py +python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py diff --git a/test/test_global_qbx.py b/test/test_global_qbx.py index 9ab5b6926..836508cd8 100644 --- a/test/test_global_qbx.py +++ b/test/test_global_qbx.py @@ -37,6 +37,7 @@ from pytential.qbx import QBXLayerPotentialSource from functools import partial +from meshmode.array_context import PyOpenCLArrayContext from meshmode.mesh.generation import ( # noqa ellipse, cloverleaf, starfish, drop, n_gon, qbx_peanut, make_curve_mesh, generate_icosphere, generate_torus) @@ -55,6 +56,22 @@ FAR_TARGET_DIST_FROM_SOURCE = 10 +# {{{ utils + +def dof_array_to_numpy(actx, ary): + """Converts DOFArrays (or object arrays of DOFArrays) to NumPy arrays. + Object arrays get turned into multidimensional arrays. + """ + from pytools.obj_array import obj_array_vectorize + from meshmode.dof_array import flatten + arr = obj_array_vectorize(actx.to_numpy, flatten(ary)) + if arr.dtype.char == "O": + arr = np.array(list(arr)) + return arr + +# }}} + + # {{{ source refinement checker class ElementInfo(RecordWithoutPickling): @@ -76,22 +93,23 @@ def iter_elements(discr): yield ElementInfo( element_nr=element_nr, discr_slice=slice(discr_nodes_idx, - discr_nodes_idx + discr_group.nunit_nodes)) + discr_nodes_idx + discr_group.nunit_dofs)) - discr_nodes_idx += discr_group.nunit_nodes + discr_nodes_idx += discr_group.nunit_dofs def run_source_refinement_test(ctx_factory, mesh, order, helmholtz_k=None, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) # {{{ initial geometry from meshmode.discretization import Discretization from meshmode.discretization.poly_element import ( InterpolatoryQuadratureSimplexGroupFactory) - discr = Discretization(cl_ctx, mesh, + discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(order)) lpot_source = QBXLayerPotentialSource(discr, @@ -107,7 +125,7 @@ def run_source_refinement_test(ctx_factory, mesh, order, expansion_disturbance_tolerance = 0.025 from pytential.qbx.refinement import refine_geometry_collection - places = refine_geometry_collection(queue, places, + places = refine_geometry_collection(places, kernel_length_scale=kernel_length_scale, expansion_disturbance_tolerance=expansion_disturbance_tolerance, visualize=visualize) @@ -116,27 +134,35 @@ def run_source_refinement_test(ctx_factory, mesh, order, dd = places.auto_source stage1_density_discr = places.get_discretization(dd.geometry) - stage1_density_nodes = stage1_density_discr.nodes().get(queue) + from meshmode.dof_array import thaw, flatten + + stage1_density_nodes = dof_array_to_numpy(actx, + thaw(actx, stage1_density_discr.nodes())) quad_stage2_density_discr = places.get_discretization( dd.geometry, sym.QBX_SOURCE_QUAD_STAGE2) - quad_stage2_density_nodes = quad_stage2_density_discr.nodes().get(queue) - - int_centers = bind(places, - sym.expansion_centers(lpot_source.ambient_dim, -1))(queue) - int_centers = np.array([axis.get(queue) for axis in int_centers]) - ext_centers = bind(places, - sym.expansion_centers(lpot_source.ambient_dim, +1))(queue) - ext_centers = np.array([axis.get(queue) for axis in ext_centers]) - - expansion_radii = bind(places, - sym.expansion_radii(lpot_source.ambient_dim))(queue).get() + quad_stage2_density_nodes = dof_array_to_numpy(actx, + thaw(actx, quad_stage2_density_discr.nodes())) + + int_centers = dof_array_to_numpy(actx, + bind(places, + sym.expansion_centers(lpot_source.ambient_dim, -1))(actx)) + ext_centers = dof_array_to_numpy(actx, + bind(places, + sym.expansion_centers(lpot_source.ambient_dim, +1))(actx)) + expansion_radii = dof_array_to_numpy(actx, + bind(places, sym.expansion_radii(lpot_source.ambient_dim))(actx)) dd = dd.copy(granularity=sym.GRANULARITY_ELEMENT) - source_danger_zone_radii = bind(places, sym._source_danger_zone_radii( - lpot_source.ambient_dim, dofdesc=dd.to_stage2()))(queue).get() - quad_res = bind(places, sym._quad_resolution( - lpot_source.ambient_dim, dofdesc=dd))(queue) + source_danger_zone_radii = dof_array_to_numpy(actx, + bind(places, + sym._source_danger_zone_radii( + lpot_source.ambient_dim, dofdesc=dd.to_stage2()))(actx)) + # FIXME: Why does _quad_resolution() return a host array? + quad_res = flatten( + bind(places, + sym._quad_resolution( + lpot_source.ambient_dim, dofdesc=dd))(actx)) # {{{ check if satisfying criteria @@ -236,6 +262,7 @@ def test_target_association(ctx_factory, curve_name, curve_f, nelements, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) # {{{ generate lpot source @@ -248,7 +275,7 @@ def test_target_association(ctx_factory, curve_name, curve_f, nelements, from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory factory = InterpolatoryQuadratureSimplexGroupFactory(order) - discr = Discretization(cl_ctx, mesh, factory) + discr = Discretization(actx, mesh, factory) lpot_source = QBXLayerPotentialSource(discr, qbx_order=order, # not used in target association @@ -263,20 +290,25 @@ def test_target_association(ctx_factory, curve_name, curve_f, nelements, rng = PhiloxGenerator(cl_ctx, seed=RNG_SEED) dd = places.auto_source.to_stage1() - centers = bind(places, sym.interleaved_expansion_centers( - lpot_source.ambient_dim, dofdesc=dd))(queue) - centers = np.array([ax.get(queue) for ax in centers]) - - tunnel_radius = bind(places, sym._close_target_tunnel_radii( - lpot_source.ambient_dim, dofdesc=dd))(queue) + centers = dof_array_to_numpy(actx, + bind(places, sym.interleaved_expansion_centers( + lpot_source.ambient_dim, dofdesc=dd))(actx)) density_discr = places.get_discretization(dd.geometry) - noise = rng.uniform(queue, density_discr.nnodes, dtype=np.float, a=0.01, b=1.0) + + noise = actx.dof_array_to_numpy( + rng.uniform(queue, density_discr.ndofs, dtype=np.float, a=0.01, b=1.0)) + + tunnel_radius = dof_array_to_numpy(actx, + bind(places, sym._close_target_tunnel_radii( + lpot_source.ambient_dim, dofdesc=dd))(actx)) def targets_from_sources(sign, dist, dim=2): - nodes = bind(places, sym.nodes(dim, dofdesc=dd))(queue) - normals = bind(places, sym.normal(dim, dofdesc=dd))(queue) - return (nodes + normals * sign * dist).as_vector(np.object) + nodes = dof_array_to_numpy(actx, + bind(places, sym.nodes(dim, dofdesc=dd))(actx).as_vector(np.object)) + normals = dof_array_to_numpy(actx, + bind(places, sym.normal(dim, dofdesc=dd))(actx).as_vector(np.object)) + return actx.from_numpy(nodes + normals * sign * dist) from pytential.target import PointsTarget int_targets = PointsTarget(targets_from_sources(-1, noise * tunnel_radius)) @@ -297,7 +329,7 @@ def targets_from_sources(sign, dist, dim=2): (far_targets, 0), ) - sizes = np.cumsum([discr.nnodes for discr, _ in target_discrs]) + sizes = np.cumsum([discr.ndofs for discr, _ in target_discrs]) (surf_int_slice, surf_ext_slice, @@ -315,23 +347,24 @@ def targets_from_sources(sign, dist, dim=2): from pytential.qbx.utils import TreeCodeContainer code_container = TargetAssociationCodeContainer( - cl_ctx, TreeCodeContainer(cl_ctx)) + actx, TreeCodeContainer(actx)) target_assoc = (associate_targets_to_qbx_centers( places, places.auto_source, - code_container.get_wrangler(queue), + code_container.get_wrangler(actx), target_discrs, target_association_tolerance=1e-10) .get(queue=queue)) - expansion_radii = bind(places, sym.expansion_radii( - lpot_source.ambient_dim, - granularity=sym.GRANULARITY_CENTER))(queue).get() - surf_targets = np.array( - [axis.get(queue) for axis in density_discr.nodes()]) - int_targets = np.array([axis.get(queue) for axis in int_targets.nodes()]) - ext_targets = np.array([axis.get(queue) for axis in ext_targets.nodes()]) + expansion_radii = dof_array_to_numpy(actx, + bind(places, sym.expansion_radii( + lpot_source.ambient_dim, + granularity=sym.GRANULARITY_CENTER))(actx)) + from meshmode.dof_array import thaw + surf_targets = dof_array_to_numpy(actx, thaw(actx, density_discr.nodes())) + int_targets = actx.to_numpy(int_targets.nodes()) + ext_targets = actx.to_numpy(ext_targets.nodes()) def visualize_curve_and_assoc(): import matplotlib.pyplot as plt @@ -416,6 +449,7 @@ def check_close_targets(centers, targets, true_side, def test_target_association_failure(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) # {{{ generate circle @@ -430,7 +464,7 @@ def test_target_association_failure(ctx_factory): from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory factory = InterpolatoryQuadratureSimplexGroupFactory(order) - discr = Discretization(cl_ctx, mesh, factory) + discr = Discretization(actx, mesh, factory) lpot_source = QBXLayerPotentialSource(discr, qbx_order=order, # not used in target association fine_order=order) @@ -444,8 +478,9 @@ def test_target_association_failure(ctx_factory): 2j * np.pi * np.linspace(0, 1, 500, endpoint=False)) from pytential.target import PointsTarget close_circle_target = ( - PointsTarget(cl.array.to_device( - queue, np.array([close_circle.real, close_circle.imag])))) + PointsTarget( + actx.from_numpy( + np.array([close_circle.real, close_circle.imag])))) targets = ( (close_circle_target, 0), @@ -458,13 +493,13 @@ def test_target_association_failure(ctx_factory): from pytential.qbx.utils import TreeCodeContainer code_container = TargetAssociationCodeContainer( - cl_ctx, TreeCodeContainer(cl_ctx)) + actx, TreeCodeContainer(actx)) with pytest.raises(QBXTargetAssociationFailedException): associate_targets_to_qbx_centers( places, places.auto_source, - code_container.get_wrangler(queue), + code_container.get_wrangler(actx), targets, target_association_tolerance=1e-10) From a7d8282880e127ce51fa7c104afd6452d83b8543 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 13:37:38 -0500 Subject: [PATCH 20/75] Misc fixes for test_global_qbx --- pytential/qbx/target_assoc.py | 12 +++++++----- pytential/qbx/utils.py | 2 +- test/test_global_qbx.py | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pytential/qbx/target_assoc.py b/pytential/qbx/target_assoc.py index eda3dd8f3..a25249768 100644 --- a/pytential/qbx/target_assoc.py +++ b/pytential/qbx/target_assoc.py @@ -733,6 +733,8 @@ def mark_panels_for_refinement(self, places, dofdesc, sym._close_target_tunnel_radii(ambient_dim, dofdesc=dofdesc))( self.array_context) + tunnel_radius_by_source = flatten(tunnel_radius_by_source) + # see (TGTMARK) above for algorithm. box_to_search_dist, evt = self.code_container.space_invader_query()( @@ -776,17 +778,17 @@ def mark_panels_for_refinement(self, places, dofdesc, return (found_panel_to_refine == 1).all().get() def make_target_flags(self, target_discrs_and_qbx_sides): - ntargets = sum(discr.nnodes for discr, _ in target_discrs_and_qbx_sides) + ntargets = sum(discr.ndofs for discr, _ in target_discrs_and_qbx_sides) target_flags = cl.array.empty(self.queue, ntargets, dtype=np.int32) offset = 0 for discr, flags in target_discrs_and_qbx_sides: if np.isscalar(flags): - target_flags[offset:offset + discr.nnodes].fill(flags) + target_flags[offset:offset + discr.ndofs].fill(flags) else: - assert len(flags) == discr.nnodes - target_flags[offset:offset + discr.nnodes] = flags - offset += discr.nnodes + assert len(flags) == discr.ndofs + target_flags[offset:offset + discr.ndofs] = flags + offset += discr.ndofs target_flags.finish() return target_flags diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index 8d5970837..433ce10e5 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -304,7 +304,7 @@ def _make_centers(discr): ncenters = len(centers[0]) # Each source gets an interior / exterior center. assert 2 * nsources == ncenters or use_stage2_discr - ntargets = sum(tgt.nnodes for tgt in targets_list) + ntargets = sum(tgt.ndofs for tgt in targets_list) # Slices qbx_user_source_slice = slice(0, nsources) diff --git a/test/test_global_qbx.py b/test/test_global_qbx.py index 836508cd8..6ea108ab2 100644 --- a/test/test_global_qbx.py +++ b/test/test_global_qbx.py @@ -296,7 +296,7 @@ def test_target_association(ctx_factory, curve_name, curve_f, nelements, density_discr = places.get_discretization(dd.geometry) - noise = actx.dof_array_to_numpy( + noise = actx.to_numpy( rng.uniform(queue, density_discr.ndofs, dtype=np.float, a=0.01, b=1.0)) tunnel_radius = dof_array_to_numpy(actx, From 55a2b362c6da8abe1cac7557f37b51253d6e368f Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 14:06:50 -0500 Subject: [PATCH 21/75] Port test_layer_pot_eigenvalues.py --- setup.cfg | 2 +- test/test_layer_pot_eigenvalues.py | 99 ++++++++++++++++-------------- 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/setup.cfg b/setup.cfg index 4e0403d50..f9d8c5ea1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,4 +9,4 @@ exclude= [tool:pytest] markers= slowtest: mark a test as slow -python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py +python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py test_layer_pot_eigenvalues.py diff --git a/test/test_layer_pot_eigenvalues.py b/test/test_layer_pot_eigenvalues.py index af2278ebe..b57979130 100644 --- a/test/test_layer_pot_eigenvalues.py +++ b/test/test_layer_pot_eigenvalues.py @@ -26,12 +26,12 @@ import numpy as np import numpy.linalg as la # noqa import pyopencl as cl -import pyopencl.clmath # noqa import pytest from pyopencl.tools import ( # noqa pytest_generate_tests_for_pyopencl as pytest_generate_tests) from functools import partial +from meshmode.array_context import PyOpenCLArrayContext from meshmode.mesh.generation import ( # noqa ellipse, cloverleaf, starfish, drop, n_gon, qbx_peanut, WobblyCircle, make_curve_mesh, NArmedStarfish) @@ -72,6 +72,7 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) target_order = 8 @@ -107,7 +108,7 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, fmm_order = False pre_density_discr = Discretization( - cl_ctx, mesh, + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource( pre_density_discr, 4*target_order, @@ -117,18 +118,19 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, places = GeometryCollection(qbx) density_discr = places.get_discretization(places.auto_source.geometry) - nodes = density_discr.nodes().with_queue(queue) + from meshmode.dof_array import thaw, flatten + nodes = thaw(actx, density_discr.nodes()) if visualize: # plot geometry, centers, normals centers = bind(places, - sym.expansion_centers(qbx.ambient_dim, +1))(queue) - normal = bind(places, - sym.normal(qbx.ambient_dim))(queue).as_vector(np.object) + sym.expansion_centers(qbx.ambient_dim, +1))(actx) + normals = bind(places, + sym.normal(qbx.ambient_dim))(actx).as_vector(np.object) - nodes_h = nodes.get() - centers_h = [centers[0].get(), centers[1].get()] - normals_h = [normal[0].get(), normal[1].get()] + nodes_h = np.array([actx.to_numpy(axis) for axis in flatten(nodes)]) + centers_h = np.array([actx.to_numpy(axis) for axis in flatten(centers)]) + normals_h = np.array([actx.to_numpy(axis) for axis in flatten(normals)]) pt.plot(nodes_h[0], nodes_h[1], "x-") pt.plot(centers_h[0], centers_h[1], "o") @@ -136,14 +138,14 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, pt.gca().set_aspect("equal") pt.show() - angle = cl.clmath.atan2(nodes[1]*ellipse_aspect, nodes[0]) + angle = actx.np.atan2(nodes[1]*ellipse_aspect, nodes[0]) ellipse_fraction = ((1-ellipse_aspect)/(1+ellipse_aspect))**mode_nr # (2.6) in [1] - J = cl.clmath.sqrt( # noqa - cl.clmath.sin(angle)**2 - + (1/ellipse_aspect)**2 * cl.clmath.cos(angle)**2) + J = actx.np.sqrt( # noqa + actx.np.sin(angle)**2 + + (1/ellipse_aspect)**2 * actx.np.cos(angle)**2) from sumpy.kernel import LaplaceKernel lap_knl = LaplaceKernel(2) @@ -153,8 +155,8 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, sigma_sym = sym.var("sigma") s_sigma_op = sym.S(lap_knl, sigma_sym, qbx_forced_limit=+1) - sigma = cl.clmath.cos(mode_nr*angle)/J - s_sigma = bind(places, s_sigma_op)(queue=queue, sigma=sigma) + sigma = actx.np.cos(mode_nr*angle)/J + s_sigma = bind(places, s_sigma_op)(actx, sigma=sigma) # SIGN BINGO! :) s_eigval = 1/(2*mode_nr) * (1 + (-1)**mode_nr * ellipse_fraction) @@ -165,14 +167,14 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, if 0: #pt.plot(s_sigma.get(), label="result") #pt.plot(s_sigma_ref.get(), label="ref") - pt.plot((s_sigma_ref - s_sigma).get(), label="err") + pt.plot(actx.to_numpy(flatten(s_sigma_ref - s_sigma)), label="err") pt.legend() pt.show() - h_max = bind(places, sym.h_max(qbx.ambient_dim))(queue) + h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) s_err = ( - norm(density_discr, queue, s_sigma - s_sigma_ref) - / norm(density_discr, queue, s_sigma_ref)) + norm(density_discr, s_sigma - s_sigma_ref) + / norm(density_discr, s_sigma_ref)) s_eoc_rec.add_data_point(h_max, s_err) # }}} @@ -181,8 +183,8 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, d_sigma_op = sym.D(lap_knl, sigma_sym, qbx_forced_limit="avg") - sigma = cl.clmath.cos(mode_nr*angle) - d_sigma = bind(places, d_sigma_op)(queue=queue, sigma=sigma) + sigma = actx.np.cos(mode_nr*angle) + d_sigma = bind(places, d_sigma_op)(actx, sigma=sigma) # SIGN BINGO! :) d_eigval = -(-1)**mode_nr * 1/2*ellipse_fraction @@ -190,18 +192,18 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, d_sigma_ref = d_eigval*sigma if 0: - pt.plot(d_sigma.get(), label="result") - pt.plot(d_sigma_ref.get(), label="ref") + pt.plot(actx.to_numpy(flatten(d_sigma)), label="result") + pt.plot(actx.to_numpy(flatten(d_sigma_ref)), label="ref") pt.legend() pt.show() if ellipse_aspect == 1: - d_ref_norm = norm(density_discr, queue, sigma) + d_ref_norm = norm(density_discr, sigma) else: - d_ref_norm = norm(density_discr, queue, d_sigma_ref) + d_ref_norm = norm(density_discr, d_sigma_ref) d_err = ( - norm(density_discr, queue, d_sigma - d_sigma_ref) + norm(density_discr, d_sigma - d_sigma_ref) / d_ref_norm) d_eoc_rec.add_data_point(h_max, d_err) @@ -212,15 +214,15 @@ def test_ellipse_eigenvalues(ctx_factory, ellipse_aspect, mode_nr, qbx_order, sp_sigma_op = sym.Sp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg") - sigma = cl.clmath.cos(mode_nr*angle) - sp_sigma = bind(places, sp_sigma_op)(queue=queue, sigma=sigma) + sigma = actx.np.cos(mode_nr*angle) + sp_sigma = bind(places, sp_sigma_op)(actx, sigma=sigma) sp_eigval = 0 sp_sigma_ref = sp_eigval*sigma sp_err = ( - norm(density_discr, queue, sp_sigma - sp_sigma_ref) - / norm(density_discr, queue, sigma)) + norm(density_discr, sp_sigma - sp_sigma_ref) + / norm(density_discr, sigma)) sp_eoc_rec.add_data_point(h_max, sp_err) # }}} @@ -261,6 +263,7 @@ def test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) target_order = 8 @@ -277,8 +280,8 @@ def test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, def rel_err(comp, ref): return ( - norm(density_discr, queue, comp - ref) - / norm(density_discr, queue, ref)) + norm(density_discr, comp - ref) + / norm(density_discr, ref)) for nrefinements in [0, 1]: from meshmode.mesh.generation import generate_icosphere @@ -292,7 +295,7 @@ def rel_err(comp, ref): mesh = refiner.get_current_mesh() pre_density_discr = Discretization( - cl_ctx, mesh, + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource( pre_density_discr, 4*target_order, @@ -301,14 +304,20 @@ def rel_err(comp, ref): ) places = GeometryCollection(qbx) - density_discr = places.get_discretization(places.auto_source.geometry) - nodes = density_discr.nodes().with_queue(queue) - r = cl.clmath.sqrt(nodes[0]**2 + nodes[1]**2 + nodes[2]**2) - phi = cl.clmath.acos(nodes[2]/r) - theta = cl.clmath.atan2(nodes[0], nodes[1]) + from meshmode.dof_array import flatten, unflatten, thaw - ymn = cl.array.to_device(queue, - special.sph_harm(mode_m, mode_n, theta.get(), phi.get())) + density_discr = places.get_discretization(places.auto_source.geometry) + nodes = thaw(actx, density_discr.nodes()) + r = actx.np.sqrt(nodes[0]*nodes[0] + nodes[1]*nodes[1] + nodes[2]*nodes[2]) + phi = actx.np.acos(nodes[2]/r) + theta = actx.np.atan2(nodes[0], nodes[1]) + + ymn = unflatten(actx, density_discr, + actx.from_numpy( + special.sph_harm( + mode_m, mode_n, + actx.to_numpy(flatten(theta)), + actx.to_numpy(flatten(phi))))) from sumpy.kernel import LaplaceKernel lap_knl = LaplaceKernel(3) @@ -317,10 +326,10 @@ def rel_err(comp, ref): s_sigma_op = bind(places, sym.S(lap_knl, sym.var("sigma"), qbx_forced_limit=+1)) - s_sigma = s_sigma_op(queue=queue, sigma=ymn) + s_sigma = s_sigma_op(actx, sigma=ymn) s_eigval = 1/(2*mode_n + 1) - h_max = bind(places, sym.h_max(qbx.ambient_dim))(queue) + h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) s_eoc_rec.add_data_point(h_max, rel_err(s_sigma, s_eigval*ymn)) # }}} @@ -329,7 +338,7 @@ def rel_err(comp, ref): d_sigma_op = bind(places, sym.D(lap_knl, sym.var("sigma"), qbx_forced_limit="avg")) - d_sigma = d_sigma_op(queue=queue, sigma=ymn) + d_sigma = d_sigma_op(actx, sigma=ymn) d_eigval = -1/(2*(2*mode_n + 1)) d_eoc_rec.add_data_point(h_max, rel_err(d_sigma, d_eigval*ymn)) @@ -339,7 +348,7 @@ def rel_err(comp, ref): sp_sigma_op = bind(places, sym.Sp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg")) - sp_sigma = sp_sigma_op(queue=queue, sigma=ymn) + sp_sigma = sp_sigma_op(actx, sigma=ymn) sp_eigval = -1/(2*(2*mode_n + 1)) sp_eoc_rec.add_data_point(h_max, rel_err(sp_sigma, sp_eigval*ymn)) @@ -350,7 +359,7 @@ def rel_err(comp, ref): dp_sigma_op = bind(places, sym.Dp(lap_knl, sym.var("sigma"), qbx_forced_limit="avg")) - dp_sigma = dp_sigma_op(queue=queue, sigma=ymn) + dp_sigma = dp_sigma_op(actx, sigma=ymn) dp_eigval = -(mode_n*(mode_n+1))/(2*mode_n + 1) dp_eoc_rec.add_data_point(h_max, rel_err(dp_sigma, dp_eigval*ymn)) From ce86dc69dfc64a156e52f1a8a84156e7a728c847 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 14:40:31 -0500 Subject: [PATCH 22/75] Point requirements.txt to sumpy master --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f78b6cfc9..d8fa9a6db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,5 +8,5 @@ git+https://gitlab.tiker.net/inducer/islpy@revert-cffi-go-to-pybind git+https://github.com/inducer/loopy git+https://gitlab.tiker.net/inducer/boxtree git+https://gitlab.tiker.net/inducer/meshmode@array-context -git+https://gitlab.tiker.net/inducer/sumpy@layerpotential-obj-array +git+https://gitlab.tiker.net/inducer/sumpy git+https://gitlab.tiker.net/inducer/pyfmmlib From b32792e8729ab39e841e72389648857ad17d5697 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 14:59:06 -0500 Subject: [PATCH 23/75] Fixes for test_layer_pot_eigenvalues --- pytential/qbx/__init__.py | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 31930f859..66c04b11f 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -25,7 +25,7 @@ import six from meshmode.array_context import PyOpenCLArrayContext -from meshmode.dof_array import flatten, thaw +from meshmode.dof_array import flatten, unflatten, thaw import numpy as np from pytools import memoize_method, memoize_in @@ -735,20 +735,23 @@ def exec_compute_potential_insn_direct(self, actx, insn, bound_expr, evaluate, p2p = None lpot_applier_on_tgt_subset = None + from pytential.utils import flatten_if_needed kernel_args = {} for arg_name, arg_expr in six.iteritems(insn.kernel_arguments): - kernel_args[arg_name] = evaluate(arg_expr) + kernel_args[arg_name] = flatten_if_needed(actx, evaluate(arg_expr)) waa = bind(bound_expr.places, sym.weights_and_area_elements( self.ambient_dim, dofdesc=insn.source))(actx) strengths = waa * evaluate(insn.density) + + from meshmode.discretization import Discretization flat_strengths = flatten(strengths) source_discr = bound_expr.places.get_discretization( insn.source.geometry, insn.source.discr_stage) # FIXME: Do this all at once - result = [] + results = [] for o in insn.outputs: source_dd = insn.source.copy(discr_stage=o.target_name.discr_stage) target_discr = bound_expr.places.get_discretization( @@ -769,13 +772,19 @@ def exec_compute_potential_insn_direct(self, actx, insn, bound_expr, evaluate, dofdesc=o.target_name))(actx) evt, output_for_each_kernel = lpot_applier( - actx.queue, target_discr.nodes(), - source_discr.nodes(), - centers, - [strengths], - expansion_radii=expansion_radii, + actx.queue, + flatten(thaw(actx, target_discr.nodes())), + flatten(thaw(actx, source_discr.nodes())), + flatten(centers), + [flat_strengths], + expansion_radii=flatten(expansion_radii), **kernel_args) - result.append((o.name, output_for_each_kernel[o.kernel_index])) + + result = output_for_each_kernel[o.kernel_index] + if isinstance(target_discr, Discretization): + result = unflatten(actx, target_discr, result) + + results.append((o.name, result)) else: # no on-disk kernel caching if p2p is None: @@ -786,7 +795,6 @@ def exec_compute_potential_insn_direct(self, actx, insn, bound_expr, evaluate, queue = actx.queue - from pytential.utils import flatten_if_needed flat_targets = flatten_if_needed(actx, target_discr.nodes()) flat_sources = flatten(thaw(actx, source_discr.nodes())) @@ -849,10 +857,14 @@ def exec_compute_potential_insn_direct(self, actx, insn, bound_expr, evaluate, qbx_center_numbers=qbx_center_numbers, **tgt_subset_kwargs) - result.append((o.name, output_for_each_kernel[o.kernel_index])) + result = output_for_each_kernel[o.kernel_index] + if isinstance(target_discr, Discretization): + result = unflatten(actx, target_discr, result) + + results.append((o.name, result)) timing_data = {} - return result, timing_data + return results, timing_data # }}} From 72a09b04750549d5d46f5b89d23a1f5b8fa5337c Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 15:30:01 -0500 Subject: [PATCH 24/75] Port test_layer_pot_identity --- test/test_layer_pot_identity.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/test/test_layer_pot_identity.py b/test/test_layer_pot_identity.py index 62c1c3950..b206e78de 100644 --- a/test/test_layer_pot_identity.py +++ b/test/test_layer_pot_identity.py @@ -32,6 +32,7 @@ pytest_generate_tests_for_pyopencl as pytest_generate_tests) from functools import partial +from meshmode.array_context import PyOpenCLArrayContext from meshmode.mesh.generation import ( # noqa ellipse, cloverleaf, starfish, drop, n_gon, qbx_peanut, WobblyCircle, NArmedStarfish, @@ -280,6 +281,7 @@ def test_identity_convergence(ctx_factory, case, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) # prevent cache 'splosion from sympy.core.cache import clear_cache @@ -314,7 +316,7 @@ def test_identity_convergence(ctx_factory, case, visualize=False): InterpolatoryQuadratureSimplexGroupFactory from pytential.qbx import QBXLayerPotentialSource pre_density_discr = Discretization( - cl_ctx, mesh, + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource( @@ -331,16 +333,18 @@ def test_identity_convergence(ctx_factory, case, visualize=False): from pytential.qbx.refinement import refine_geometry_collection kernel_length_scale = 5 / case.k if case.k else None - places = refine_geometry_collection(queue, places, + places = refine_geometry_collection(places, kernel_length_scale=kernel_length_scale) # {{{ compute values of a solution to the PDE density_discr = places.get_discretization(places.auto_source.geometry) - nodes_host = density_discr.nodes().get(queue) - normal = bind(places, sym.normal(d))(queue).as_vector(np.object) - normal_host = [normal[j].get() for j in range(d)] + from meshmode.dof_array import thaw, flatten, unflatten + nodes_host = [actx.to_numpy(axis) + for axis in flatten(thaw(actx, density_discr.nodes()))] + normal = bind(places, sym.normal(d))(actx).as_vector(np.object) + normal_host = [actx.to_numpy(axis)for axis in flatten(normal)] if k != 0: if d == 2: @@ -376,16 +380,18 @@ def test_identity_convergence(ctx_factory, case, visualize=False): # }}} - u_dev = cl.array.to_device(queue, u) - dn_u_dev = cl.array.to_device(queue, dn_u) - grad_u_dev = cl.array.to_device(queue, grad_u) + u_dev = unflatten(actx, density_discr, actx.from_numpy(u)) + dn_u_dev = unflatten(actx, density_discr, actx.from_numpy(dn_u)) + from pytools.obj_array import make_obj_array, obj_array_vectorize + grad_u_dev = unflatten(actx, density_discr, + obj_array_vectorize(actx.from_numpy, make_obj_array(grad_u))) key = (case.qbx_order, case.geometry.mesh_name, resolution, case.expr.zero_op_name) bound_op = bind(places, case.expr.get_zero_op(k_sym, **knl_kwargs)) error = bound_op( - queue, u=u_dev, dn_u=dn_u_dev, grad_u=grad_u_dev, k=case.k) + actx, u=u_dev, dn_u=dn_u_dev, grad_u=grad_u_dev, k=case.k) if 0: pt.plot(error) pt.show() @@ -393,14 +399,14 @@ def test_identity_convergence(ctx_factory, case, visualize=False): linf_error_norm = norm(density_discr, error, p=np.inf) print("--->", key, linf_error_norm) - h_max = bind(places, sym.h_max(qbx.ambient_dim))(queue) + h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) eoc_rec.add_data_point(h_max, linf_error_norm) if visualize: from meshmode.discretization.visualization import make_visualizer - bdry_vis = make_visualizer(queue, density_discr, target_order) + bdry_vis = make_visualizer(actx, density_discr, target_order) - bdry_normals = bind(places, sym.normal(mesh.ambient_dim))(queue)\ + bdry_normals = bind(places, sym.normal(mesh.ambient_dim))(actx)\ .as_vector(dtype=np.object) bdry_vis.write_vtk_file("source-%s.vtu" % resolution, [ From 7ce0165d251db4a0833535e71495789492168ac3 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 15:34:16 -0500 Subject: [PATCH 25/75] Actually run the tests in test_layer_pot_identity.py --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index f9d8c5ea1..d197df450 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,4 +9,4 @@ exclude= [tool:pytest] markers= slowtest: mark a test as slow -python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py test_layer_pot_eigenvalues.py +python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py test_layer_pot_eigenvalues.py test_layer_pot_identity.py From 25582e573f766138551e30e01fe775415da28d58 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 16:53:47 -0500 Subject: [PATCH 26/75] Port test_layer_pot.py --- pytential/qbx/__init__.py | 4 - pytential/source.py | 4 + pytential/unregularized.py | 167 ++++++++++++++++++++++--------------- setup.cfg | 2 +- test/test_layer_pot.py | 105 ++++++++++++----------- 5 files changed, 159 insertions(+), 123 deletions(-) diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 66c04b11f..5a9ddf7e9 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -223,10 +223,6 @@ def fmm_level_to_order(kernel, kernel_args, tree, level): # noqa pylint:disable # back if the layer potential source is ever copied. (such as # during refinement) - @property - def _setup_actx(self): - return self.density_discr._setup_actx - def copy( self, density_discr=None, diff --git a/pytential/source.py b/pytential/source.py index 475da21a3..09cbf3641 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -206,6 +206,10 @@ def __init__(self, density_discr): def ambient_dim(self): return self.density_discr.ambient_dim + @property + def _setup_actx(self): + return self.density_discr._setup_actx + @property def dim(self): return self.density_discr.dim diff --git a/pytential/unregularized.py b/pytential/unregularized.py index 386bcc027..4b0e896f0 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -27,6 +27,7 @@ import six +from meshmode.array_context import PyOpenCLArrayContext import numpy as np import loopy as lp @@ -99,8 +100,8 @@ def copy( density_discr=density_discr or self.density_discr, debug=debug if debug is not None else self.debug) - def exec_compute_potential_insn(self, queue, insn, bound_expr, evaluate, - return_timing_data): + def exec_compute_potential_insn(self, actx: PyOpenCLArrayContext, + insn, bound_expr, evaluate, return_timing_data): if return_timing_data: from warnings import warn from pytential.source import UnableToCollectTimingData @@ -119,7 +120,7 @@ def evaluate_wrapper(expr): else: func = self.exec_compute_potential_insn_fmm - return func(queue, insn, bound_expr, evaluate_wrapper) + return func(actx, insn, bound_expr, evaluate_wrapper) def op_group_features(self, expr): from sumpy.kernel import AxisTargetDerivativeRemover @@ -138,18 +139,23 @@ def preprocess_optemplate(self, name, discretizations, expr): from pytential.symbolic.mappers import UnregularizedPreprocessor return UnregularizedPreprocessor(name, discretizations)(expr) - def exec_compute_potential_insn_direct(self, actx, insn, bound_expr, evaluate): + def exec_compute_potential_insn_direct(self, actx: PyOpenCLArrayContext, + insn, bound_expr, evaluate): kernel_args = {} + from pytential.utils import flatten_if_needed + from meshmode.dof_array import flatten, thaw, unflatten + for arg_name, arg_expr in six.iteritems(insn.kernel_arguments): - kernel_args[arg_name] = evaluate(arg_expr) + kernel_args[arg_name] = flatten_if_needed(actx, evaluate(arg_expr)) from pytential import bind, sym waa = bind(bound_expr.places, sym.weights_and_area_elements( self.ambient_dim, dofdesc=insn.source))(actx) strengths = waa * evaluate(insn.density) + flat_strengths = flatten(strengths) - result = [] + results = [] p2p = None for o in insn.outputs: @@ -159,15 +165,20 @@ def exec_compute_potential_insn_direct(self, actx, insn, bound_expr, evaluate): if p2p is None: p2p = self.get_p2p(actx, insn.kernels) - evt, output_for_each_kernel = p2p(actx, - target_discr.nodes(), - self.density_discr.nodes(), - [strengths], **kernel_args) + evt, output_for_each_kernel = p2p(actx.queue, + flatten_if_needed(actx, target_discr.nodes()), + flatten(thaw(actx, self.density_discr.nodes())), + [flat_strengths], **kernel_args) + + from meshmode.discretization import Discretization + result = output_for_each_kernel[o.kernel_index] + if isinstance(target_discr, Discretization): + result = unflatten(actx, target_discr, result) - result.append((o.name, output_for_each_kernel[o.kernel_index])) + results.append((o.name, result)) timing_data = {} - return result, timing_data + return results, timing_data # {{{ fmm-based execution @@ -190,10 +201,9 @@ def expansion_wrangler_code_container(self, fmm_kernel, out_kernels): out_kernels) @property - @memoize_method def fmm_geometry_code_container(self): return _FMMGeometryCodeContainer( - self.cl_context, self.ambient_dim, self.debug) + self._setup_actx, self.ambient_dim, self.debug) def fmm_geometry_data(self, targets): return _FMMGeometryData( @@ -202,7 +212,8 @@ def fmm_geometry_data(self, targets): targets, self.debug) - def exec_compute_potential_insn_fmm(self, queue, insn, bound_expr, evaluate): + def exec_compute_potential_insn_fmm(self, actx: PyOpenCLArrayContext, + insn, bound_expr, evaluate): # {{{ gather unique target discretizations used target_name_to_index = {} @@ -227,8 +238,11 @@ def exec_compute_potential_insn_fmm(self, queue, insn, bound_expr, evaluate): from pytential import bind, sym waa = bind(bound_expr.places, sym.weights_and_area_elements( - self.ambient_dim, dofdesc=insn.source))(queue) - strengths = waa * evaluate(insn.density).with_queue(queue) + self.ambient_dim, dofdesc=insn.source))(actx) + strengths = waa * evaluate(insn.density) + + from meshmode.dof_array import flatten + flat_strengths = flatten(strengths) out_kernels = tuple(knl for knl in insn.kernels) fmm_kernel = self.get_fmm_kernel(out_kernels) @@ -236,12 +250,12 @@ def exec_compute_potential_insn_fmm(self, queue, insn, bound_expr, evaluate): self.get_fmm_output_and_expansion_dtype(fmm_kernel, strengths)) kernel_extra_kwargs, source_extra_kwargs = ( self.get_fmm_expansion_wrangler_extra_kwargs( - queue, out_kernels, geo_data.tree().user_source_ids, + actx, out_kernels, geo_data.tree().user_source_ids, insn.kernel_arguments, evaluate)) wrangler = self.expansion_wrangler_code_container( fmm_kernel, out_kernels).get_wrangler( - queue, + actx.queue, geo_data.tree(), output_and_expansion_dtype, self.fmm_level_to_order, @@ -252,25 +266,32 @@ def exec_compute_potential_insn_fmm(self, queue, insn, bound_expr, evaluate): from boxtree.fmm import drive_fmm all_potentials_on_every_tgt = drive_fmm( - geo_data.traversal(), wrangler, strengths, timing_data=None) + geo_data.traversal(), wrangler, flat_strengths, + timing_data=None) # {{{ postprocess fmm - result = [] + results = [] for o in insn.outputs: target_index = target_name_to_index[o.target_name] target_slice = slice(*geo_data.target_info().target_discr_starts[ target_index:target_index+2]) + target_discr = targets[target_index] + + result = all_potentials_on_every_tgt[o.kernel_index][target_slice] + + from meshmode.discretization import Discretization + if isinstance(target_discr, Discretization): + from meshmode.dof_array import unflatten + result = unflatten(actx, target_discr, result) - result.append( - (o.name, - all_potentials_on_every_tgt[o.kernel_index][target_slice])) + results.append((o.name, result)) # }}} timing_data = {} - return result, timing_data + return results, timing_data # }}} @@ -281,11 +302,15 @@ def exec_compute_potential_insn_fmm(self, queue, insn, bound_expr, evaluate): class _FMMGeometryCodeContainer(object): - def __init__(self, cl_context, ambient_dim, debug): - self.cl_context = cl_context + def __init__(self, actx, ambient_dim, debug): + self.array_context = actx self.ambient_dim = ambient_dim self.debug = debug + @property + def cl_context(self): + return self.array_context.context + @memoize_method def copy_targets_kernel(self): knl = lp.make_kernel( @@ -343,11 +368,15 @@ def __init__(self, lpot_source, code_getter, target_discrs, debug=True): @property def cl_context(self): - return self.lpot_source.cl_context + return self.code_getter.cl_context + + @property + def array_context(self): + return self.code_getter.array_context @property def coord_dtype(self): - return self.lpot_source.density_discr.nodes().dtype + return self.lpot_source.density_discr.real_dtype @property def ambient_dim(self): @@ -373,25 +402,29 @@ def tree(self): lpot_src = self.lpot_source target_info = self.target_info() - with cl.CommandQueue(self.cl_context) as queue: - nsources = lpot_src.density_discr.nnodes - nparticles = nsources + target_info.ntargets + queue = self.array_context.queue + + nsources = lpot_src.density_discr.ndofs + nparticles = nsources + target_info.ntargets - refine_weights = cl.array.zeros(queue, nparticles, dtype=np.int32) - refine_weights[:nsources] = 1 - refine_weights.finish() + refine_weights = cl.array.zeros(queue, nparticles, dtype=np.int32) + refine_weights[:nsources] = 1 + refine_weights.finish() - MAX_LEAF_REFINE_WEIGHT = 32 # noqa + MAX_LEAF_REFINE_WEIGHT = 32 # noqa - tree, _ = code_getter.build_tree(queue, - particles=lpot_src.density_discr.nodes(), - targets=target_info.targets, - max_leaf_refine_weight=MAX_LEAF_REFINE_WEIGHT, - refine_weights=refine_weights, - debug=self.debug, - kind="adaptive") + from meshmode.dof_array import thaw, flatten - return tree + tree, _ = code_getter.build_tree(queue, + particles=flatten( + thaw(self.array_context, lpot_src.density_discr.nodes())), + targets=target_info.targets, + max_leaf_refine_weight=MAX_LEAF_REFINE_WEIGHT, + refine_weights=refine_weights, + debug=self.debug, + kind="adaptive") + + return tree @memoize_method def target_info(self): @@ -399,31 +432,31 @@ def target_info(self): lpot_src = self.lpot_source target_discrs = self.target_discrs - with cl.CommandQueue(self.cl_context) as queue: - ntargets = 0 - target_discr_starts = [] - - for target_discr in target_discrs: - target_discr_starts.append(ntargets) - ntargets += target_discr.nnodes + ntargets = 0 + target_discr_starts = [] + for target_discr in target_discrs: target_discr_starts.append(ntargets) - - targets = cl.array.empty( - self.cl_context, - (lpot_src.ambient_dim, ntargets), - self.coord_dtype) - - for start, target_discr in zip(target_discr_starts, target_discrs): - code_getter.copy_targets_kernel()( - queue, - targets=targets[:, start:start+target_discr.nnodes], - points=target_discr.nodes()) - - return _TargetInfo( - targets=targets, - target_discr_starts=target_discr_starts, - ntargets=ntargets).with_queue(None) + ntargets += target_discr.ndofs + + target_discr_starts.append(ntargets) + + targets = self.array_context.empty( + (lpot_src.ambient_dim, ntargets), + self.coord_dtype) + + from pytential.utils import flatten_if_needed + for start, target_discr in zip(target_discr_starts, target_discrs): + code_getter.copy_targets_kernel()( + self.array_context.queue, + targets=targets[:, start:start+target_discr.ndofs], + points=flatten_if_needed( + self.array_context, target_discr.nodes())) + + return _TargetInfo( + targets=targets, + target_discr_starts=target_discr_starts, + ntargets=ntargets).with_queue(None) # }}} diff --git a/setup.cfg b/setup.cfg index d197df450..34699863c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,4 +9,4 @@ exclude= [tool:pytest] markers= slowtest: mark a test as slow -python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py test_layer_pot_eigenvalues.py test_layer_pot_identity.py +python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py test_layer_pot_eigenvalues.py test_layer_pot_identity.py test_layer_pot.py diff --git a/test/test_layer_pot.py b/test/test_layer_pot.py index 25f19144b..5e63232d6 100644 --- a/test/test_layer_pot.py +++ b/test/test_layer_pot.py @@ -26,12 +26,12 @@ import numpy as np import numpy.linalg as la # noqa import pyopencl as cl -import pyopencl.clmath # noqa import pytest from pyopencl.tools import ( # noqa pytest_generate_tests_for_pyopencl as pytest_generate_tests) from functools import partial +from meshmode.array_context import PyOpenCLArrayContext from meshmode.mesh.generation import ( # noqa ellipse, cloverleaf, starfish, drop, n_gon, qbx_peanut, WobblyCircle, make_curve_mesh, NArmedStarfish) @@ -51,6 +51,7 @@ def test_geometry(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) nelements = 30 order = 5 @@ -63,13 +64,13 @@ def test_geometry(ctx_factory): from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory - discr = Discretization(cl_ctx, mesh, + discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(order)) import pytential.symbolic.primitives as prim area_sym = prim.integral(2, 1, 1) - area = bind(discr, area_sym)(queue) + area = bind(discr, area_sym)(actx) err = abs(area-2*np.pi) print(err) @@ -86,6 +87,7 @@ def test_off_surface_eval(ctx_factory, use_fmm, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) # prevent cache 'splosion from sympy.core.cache import clear_cache @@ -109,7 +111,7 @@ def test_off_surface_eval(ctx_factory, use_fmm, visualize=False): InterpolatoryQuadratureSimplexGroupFactory pre_density_discr = Discretization( - cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource( pre_density_discr, 4*target_order, @@ -127,16 +129,16 @@ def test_off_surface_eval(ctx_factory, use_fmm, visualize=False): from sumpy.kernel import LaplaceKernel op = sym.D(LaplaceKernel(2), sym.var("sigma"), qbx_forced_limit=-2) - sigma = density_discr.zeros(queue) + 1 - fld_in_vol = bind(places, op)(queue, sigma=sigma) + sigma = density_discr.zeros(actx) + 1 + fld_in_vol = bind(places, op)(actx, sigma=sigma) fld_in_vol_exact = -1 - err = cl.clmath.fabs(fld_in_vol - fld_in_vol_exact) - linf_err = cl.array.max(err).get() + err = actx.np.fabs(fld_in_vol - fld_in_vol_exact) + linf_err = actx.to_numpy(err).max() print("l_inf error:", linf_err) if visualize: - fplot.show_scalar_in_matplotlib(fld_in_vol.get()) + fplot.show_scalar_in_matplotlib(actx.to_numpy(fld_in_vol)) import matplotlib.pyplot as pt pt.colorbar() pt.show() @@ -153,6 +155,7 @@ def test_off_surface_eval_vs_direct(ctx_factory, do_plot=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) # prevent cache 'splosion from sympy.core.cache import clear_cache @@ -172,7 +175,7 @@ def test_off_surface_eval_vs_direct(ctx_factory, do_plot=False): InterpolatoryQuadratureSimplexGroupFactory pre_density_discr = Discretization( - cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) direct_qbx = QBXLayerPotentialSource( pre_density_discr, 4*target_order, qbx_order, fmm_order=False, @@ -201,31 +204,30 @@ def test_off_surface_eval_vs_direct(ctx_factory, do_plot=False): from pytential.qbx import QBXTargetAssociationFailedException op = sym.D(LaplaceKernel(2), sym.var("sigma"), qbx_forced_limit=None) try: - direct_sigma = direct_density_discr.zeros(queue) + 1 + direct_sigma = direct_density_discr.zeros(actx) + 1 direct_fld_in_vol = bind(places, op, auto_where=("direct_qbx", "target"))( - queue, sigma=direct_sigma) + actx, sigma=direct_sigma) except QBXTargetAssociationFailedException as e: - fplot.show_scalar_in_matplotlib(e.failed_target_flags.get(queue)) + fplot.show_scalar_in_matplotlib(actx.to_numpy(e.failed_target_flags)) import matplotlib.pyplot as pt pt.show() raise - fmm_sigma = fmm_density_discr.zeros(queue) + 1 + fmm_sigma = fmm_density_discr.zeros(actx) + 1 fmm_fld_in_vol = bind(places, op, auto_where=("fmm_qbx", "target"))( - queue, sigma=fmm_sigma) + actx, sigma=fmm_sigma) - err = cl.clmath.fabs(fmm_fld_in_vol - direct_fld_in_vol) - - linf_err = cl.array.max(err).get() + err = actx.np.fabs(fmm_fld_in_vol - direct_fld_in_vol) + linf_err = actx.to_numpy(err).max() print("l_inf error:", linf_err) if do_plot: #fplot.show_scalar_in_mayavi(0.1*.get(queue)) fplot.write_vtk_file("potential.vts", [ - ("fmm_fld_in_vol", fmm_fld_in_vol.get(queue)), - ("direct_fld_in_vol", direct_fld_in_vol.get(queue)) + ("fmm_fld_in_vol", actx.to_numpy(fmm_fld_in_vol)), + ("direct_fld_in_vol", actx.to_numpy(direct_fld_in_vol)) ]) assert linf_err < 1e-3 @@ -235,10 +237,10 @@ def test_off_surface_eval_vs_direct(ctx_factory, do_plot=False): # {{{ unregularized tests - def test_unregularized_with_ones_kernel(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) nelements = 10 order = 8 @@ -251,7 +253,7 @@ def test_unregularized_with_ones_kernel(ctx_factory): from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory - discr = Discretization(cl_ctx, mesh, + discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(order)) from pytential.unregularized import UnregularizedLayerPotentialSource @@ -268,24 +270,24 @@ def test_unregularized_with_ones_kernel(ctx_factory): sigma_sym = sym.var("sigma") op = sym.IntG(one_kernel_2d, sigma_sym, qbx_forced_limit=None) - sigma = cl.array.zeros(queue, discr.ndofs, dtype=float) - sigma.fill(1) - sigma.finish() + sigma = discr.zeros(actx) + 1 result_self = bind(places, op, auto_where=places.auto_where)( - queue, sigma=sigma) + actx, sigma=sigma) result_nonself = bind(places, op, auto_where=(places.auto_source, "target_non_self"))( - queue, sigma=sigma) + actx, sigma=sigma) - assert np.allclose(result_self.get(), 2 * np.pi) - assert np.allclose(result_nonself.get(), 2 * np.pi) + from meshmode.dof_array import flatten + assert np.allclose(actx.to_numpy(flatten(result_self)), 2 * np.pi) + assert np.allclose(actx.to_numpy(result_nonself), 2 * np.pi) def test_unregularized_off_surface_fmm_vs_direct(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) nelements = 300 target_order = 8 @@ -303,7 +305,7 @@ def test_unregularized_off_surface_fmm_vs_direct(ctx_factory): InterpolatoryQuadratureSimplexGroupFactory density_discr = Discretization( - cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) direct = UnregularizedLayerPotentialSource( density_discr, fmm_order=False, @@ -311,7 +313,7 @@ def test_unregularized_off_surface_fmm_vs_direct(ctx_factory): fmm = direct.copy( fmm_level_to_order=lambda kernel, kernel_args, tree, level: fmm_order) - sigma = density_discr.zeros(queue) + 1 + sigma = density_discr.zeros(actx) + 1 fplot = FieldPlotter(np.zeros(2), extent=5, npoints=100) from pytential.target import PointsTarget @@ -332,13 +334,12 @@ def test_unregularized_off_surface_fmm_vs_direct(ctx_factory): direct_fld_in_vol = bind(places, op, auto_where=("unregularized_direct", "targets"))( - queue, sigma=sigma) + actx, sigma=sigma) fmm_fld_in_vol = bind(places, op, - auto_where=("unregularized_fmm", "targets"))(queue, sigma=sigma) - - err = cl.clmath.fabs(fmm_fld_in_vol - direct_fld_in_vol) + auto_where=("unregularized_fmm", "targets"))(actx, sigma=sigma) - linf_err = cl.array.max(err).get() + err = actx.np.fabs(fmm_fld_in_vol - direct_fld_in_vol) + linf_err = actx.to_numpy(err).max() print("l_inf error:", linf_err) assert linf_err < 5e-3 @@ -356,6 +357,7 @@ def test_3d_jump_relations(ctx_factory, relation, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) if relation == "div_s": target_order = 3 @@ -377,7 +379,7 @@ def test_3d_jump_relations(ctx_factory, relation, visualize=False): from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory pre_discr = Discretization( - cl_ctx, mesh, + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(3)) from pytential.qbx import QBXLayerPotentialSource @@ -401,8 +403,9 @@ def nxcurlS(qbx_forced_limit): sym.cse(sym.tangential_to_xyz(density_sym), "jxyz"), qbx_forced_limit=qbx_forced_limit))) - x, y, z = density_discr.nodes().with_queue(queue) - m = cl.clmath + from meshmode.dof_array import thaw + x, y, z = thaw(actx, density_discr.nodes()) + m = actx.np if relation == "nxcurls": density_sym = sym.make_sym_vector("density", 2) @@ -417,7 +420,7 @@ def nxcurlS(qbx_forced_limit): # an XYZ function and project it. density = bind(places, sym.xyz_to_tangential(sym.make_sym_vector("jxyz", 3)))( - queue, + actx, jxyz=sym.make_obj_array([ m.cos(0.5*x) * m.cos(0.5*y) * m.cos(0.5*z), m.sin(0.5*x) * m.cos(0.5*y) * m.sin(0.5*z), @@ -448,9 +451,9 @@ def nxcurlS(qbx_forced_limit): raise ValueError("unexpected value of 'relation': %s" % relation) bound_jump_identity = bind(places, jump_identity_sym) - jump_identity = bound_jump_identity(queue, density=density) + jump_identity = bound_jump_identity(actx, density=density) - h_max = bind(places, sym.h_max(qbx.ambient_dim))(queue) + h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) err = ( norm(density_discr, jump_identity, np.inf) / norm(density_discr, density, np.inf)) @@ -461,15 +464,15 @@ def nxcurlS(qbx_forced_limit): # {{{ visualization if visualize and relation == "nxcurls": - nxcurlS_ext = bind(places, nxcurlS(+1))(queue, density=density) - nxcurlS_avg = bind(places, nxcurlS("avg"))(queue, density=density) + nxcurlS_ext = bind(places, nxcurlS(+1))(actx, density=density) + nxcurlS_avg = bind(places, nxcurlS("avg"))(actx, density=density) jtxyz = bind(places, sym.tangential_to_xyz(density_sym))( - queue, density=density) + actx, density=density) from meshmode.discretization.visualization import make_visualizer - bdry_vis = make_visualizer(queue, qbx.density_discr, target_order+3) + bdry_vis = make_visualizer(actx, qbx.density_discr, target_order+3) - bdry_normals = bind(places, sym.normal(3))(queue)\ + bdry_normals = bind(places, sym.normal(3))(actx)\ .as_vector(dtype=object) bdry_vis.write_vtk_file("source-%s.vtu" % nel_factor, [ @@ -481,15 +484,15 @@ def nxcurlS(qbx_forced_limit): if visualize and relation == "sp": op = sym.Sp(knl, density_sym, qbx_forced_limit=+1) - sp_ext = bind(places, op)(queue, density=density) + sp_ext = bind(places, op)(actx, density=density) op = sym.Sp(knl, density_sym, qbx_forced_limit="avg") - sp_avg = bind(places, op)(queue, density=density) + sp_avg = bind(places, op)(actx, density=density) from meshmode.discretization.visualization import make_visualizer - bdry_vis = make_visualizer(queue, qbx.density_discr, target_order+3) + bdry_vis = make_visualizer(actx, qbx.density_discr, target_order+3) bdry_normals = bind(places, - sym.normal(3))(queue).as_vector(dtype=object) + sym.normal(3))(actx).as_vector(dtype=object) bdry_vis.write_vtk_file("source-%s.vtu" % nel_factor, [ ("density", density), From 01872e98236fd24119cefb6558c29a4158fefe0f Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 2 Jul 2020 22:05:03 -0500 Subject: [PATCH 27/75] port test_matrix to array-context --- pytential/symbolic/execution.py | 8 +- pytential/symbolic/matrix.py | 208 +++++++++++++++++++------------- test/test_matrix.py | 93 +++++++------- 3 files changed, 175 insertions(+), 134 deletions(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index e46f75095..4b36a56b8 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -1031,10 +1031,10 @@ def _bmat(blocks, dtypes): return result -def build_matrix(queue, places, exprs, input_exprs, domains=None, +def build_matrix(actx, places, exprs, input_exprs, domains=None, auto_where=None, context=None): """ - :arg queue: a :class:`pyopencl.CommandQueue`. + :arg actx: a :class:`~meshmode.array_context.ArrayContext`. :arg places: a :class:`~pytential.symbolic.execution.GeometryCollection`. Alternatively, any list or mapping that is a valid argument for its constructor can also be used. @@ -1085,7 +1085,7 @@ def build_matrix(queue, places, exprs, input_exprs, domains=None, domains[ibcol].geometry, domains[ibcol].discr_stage) mbuilder = MatrixBuilder( - queue, + actx, dep_expr=input_exprs[ibcol], other_dep_exprs=(input_exprs[:ibcol] + input_exprs[ibcol + 1:]), @@ -1102,7 +1102,7 @@ def build_matrix(queue, places, exprs, input_exprs, domains=None, if isinstance(block, np.ndarray): dtypes.append(block.dtype) - return cl.array.to_device(queue, _bmat(blocks, dtypes)) + return actx.from_numpy(_bmat(blocks, dtypes)) # }}} diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index d63f30549..12ec489a4 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -26,14 +26,13 @@ """ import numpy as np -import pyopencl as cl # noqa -import pyopencl.array # noqa import six from six.moves import intern from pytools import memoize_method from pytential.symbolic.mappers import EvaluationMapperBase +from pytential.utils import flatten_if_needed # {{{ helpers @@ -56,17 +55,44 @@ def _get_layer_potential_args(mapper, expr, include_args=None): and arg_name not in include_args): continue - kernel_args[arg_name] = mapper.rec(arg_expr) + kernel_args[arg_name] = flatten_if_needed(mapper.array_context, + mapper.rec(arg_expr) + ) return kernel_args + +def _unflatten_from_numpy(actx, ary, discr=None): + from pytools.obj_array import obj_array_vectorize + from meshmode.dof_array import unflatten + + if isinstance(ary, np.ndarray) and ary.dtype.char == "O": + ary = obj_array_vectorize(actx.from_numpy, ary) + else: + ary = actx.from_numpy(ary) + + if discr is None: + return ary + else: + return unflatten(actx, discr, ary) + + +def _flatten_to_numpy(actx, ary): + result = flatten_if_needed(actx, ary) + + from pytools.obj_array import obj_array_vectorize + if isinstance(result, np.ndarray) and ary.dtype.char == "O": + return obj_array_vectorize(actx.to_numpy, result) + else: + return actx.to_numpy(result) + # }}} # {{{ base classes for matrix builders class MatrixBuilderBase(EvaluationMapperBase): - def __init__(self, queue, dep_expr, other_dep_exprs, + def __init__(self, actx, dep_expr, other_dep_exprs, dep_source, dep_discr, places, context): """ :arg queue: a :class:`pyopencl.CommandQueue`. @@ -84,7 +110,7 @@ def __init__(self, queue, dep_expr, other_dep_exprs, """ super(MatrixBuilderBase, self).__init__(context=context) - self.queue = queue + self.array_context = actx self.dep_expr = dep_expr self.other_dep_exprs = other_dep_exprs self.dep_source = dep_source @@ -94,7 +120,7 @@ def __init__(self, queue, dep_expr, other_dep_exprs, # {{{ def get_dep_variable(self): - return np.eye(self.dep_discr.nnodes, dtype=np.float64) + return np.eye(self.dep_discr.ndofs, dtype=np.float64) def is_kind_vector(self, x): return len(x.shape) == 1 @@ -196,17 +222,25 @@ def map_num_reference_derivative(self, expr): if self.is_kind_matrix(rec_operand): raise NotImplementedError("derivatives") - rec_operand = cl.array.to_device(self.queue, rec_operand) + dofdesc = expr.dofdesc op = sym.NumReferenceDerivative( ref_axes=expr.ref_axes, operand=sym.var("u"), - dofdesc=expr.dofdesc) - return bind(self.places, op)(self.queue, u=rec_operand).get() + dofdesc=dofdesc) + + discr = self.places.get_discretization(dofdesc.geometry, dofdesc.discr_stage) + rec_operand = _unflatten_from_numpy(self.array_context, rec_operand, discr) + + return _flatten_to_numpy(self.array_context, + bind(self.places, op)(self.array_context, u=rec_operand) + ) def map_node_coordinate_component(self, expr): from pytential import bind, sym op = sym.NodeCoordinateComponent(expr.ambient_axis, dofdesc=expr.dofdesc) - return bind(self.places, op)(self.queue).get() + return _flatten_to_numpy(self.array_context, + bind(self.places, op)(self.array_context) + ) def map_call(self, expr): arg, = expr.parameters @@ -215,17 +249,13 @@ def map_call(self, expr): if isinstance(rec_arg, np.ndarray) and self.is_kind_matrix(rec_arg): raise RuntimeError("expression is nonlinear in variable") - if isinstance(rec_arg, np.ndarray): - rec_arg = cl.array.to_device(self.queue, rec_arg) - - from pytential import bind, sym - op = expr.function(sym.var("u")) - result = bind(self.places, op)(self.queue, u=rec_arg) - - if isinstance(result, cl.array.Array): - result = result.get() - - return result + from numbers import Number + if isinstance(rec_arg, Number): + return getattr(np, expr.function.name)(rec_arg) + else: + rec_arg = _unflatten_from_numpy(self.array_context, rec_arg) + result = getattr(self.array_context.np, expr.function.name)(rec_arg) + return _flatten_to_numpy(self.array_context, result) # }}} @@ -240,14 +270,14 @@ class MatrixBlockBuilderBase(MatrixBuilderBase): assume that each operator acts directly on the density. """ - def __init__(self, queue, dep_expr, other_dep_exprs, + def __init__(self, actx, dep_expr, other_dep_exprs, dep_source, dep_discr, places, index_set, context): """ :arg index_set: a :class:`sumpy.tools.MatrixBlockIndexRanges` class describing which blocks are going to be evaluated. """ - super(MatrixBlockBuilderBase, self).__init__(queue, + super(MatrixBlockBuilderBase, self).__init__(actx, dep_expr, other_dep_exprs, dep_source, dep_discr, places, context) self.index_set = index_set @@ -259,7 +289,7 @@ def _mat_mapper(self): # be computed on the full discretization, ignoring our index_set, # e.g the normal in a double layer potential - return MatrixBuilderBase(self.queue, + return MatrixBuilderBase(self.array_context, self.dep_expr, self.other_dep_exprs, self.dep_source, @@ -272,7 +302,7 @@ def _blk_mapper(self): # blk_mapper is used to recursively compute the density to # a layer potential operator to ensure there is no composition - return MatrixBlockBuilderBase(self.queue, + return MatrixBlockBuilderBase(self.array_context, self.dep_expr, self.other_dep_exprs, self.dep_source, @@ -302,9 +332,10 @@ def is_kind_matrix(self, x): # We'll cheat and build the matrix on the host. class MatrixBuilder(MatrixBuilderBase): - def __init__(self, queue, dep_expr, other_dep_exprs, + def __init__(self, actx, dep_expr, other_dep_exprs, dep_source, dep_discr, places, context): - super(MatrixBuilder, self).__init__(queue, dep_expr, other_dep_exprs, + super(MatrixBuilder, self).__init__( + actx, dep_expr, other_dep_exprs, dep_source, dep_discr, places, context) def map_interpolation(self, expr): @@ -313,13 +344,17 @@ def map_interpolation(self, expr): 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) + actx = self.array_context if isinstance(operand, (int, float, complex, np.number)): return operand elif isinstance(operand, np.ndarray) and operand.ndim == 1: conn = self.places.get_connection(expr.from_dd, expr.to_dd) - return conn(self.queue, - cl.array.to_device(self.queue, operand)).get(self.queue) + discr = self.places.get_discretization( + expr.from_dd.geometry, expr.from_dd.discr_stage) + + operand = _unflatten_from_numpy(actx, operand, discr) + return _flatten_to_numpy(actx, conn(operand)) elif isinstance(operand, np.ndarray) and operand.ndim == 2: cache = self.places._get_cache("direct_resampler") key = (expr.from_dd.geometry, @@ -333,8 +368,8 @@ def map_interpolation(self, expr): flatten_chained_connection conn = self.places.get_connection(expr.from_dd, expr.to_dd) - conn = flatten_chained_connection(self.queue, conn) - mat = conn.full_resample_matrix(self.queue).get(self.queue) + conn = flatten_chained_connection(actx, conn) + mat = actx.to_numpy(conn.full_resample_matrix(actx)) # FIXME: the resample matrix is slow to compute and very big # to store, so caching it may not be the best idea @@ -359,6 +394,7 @@ def map_int_g(self, expr): if not self.is_kind_matrix(rec_density): raise NotImplementedError("layer potentials on non-variables") + actx = self.array_context kernel = expr.kernel kernel_args = _get_layer_potential_args(self, expr) @@ -366,31 +402,31 @@ def map_int_g(self, expr): local_expn = LineTaylorLocalExpansion(kernel, lpot_source.qbx_order) from sumpy.qbx import LayerPotentialMatrixGenerator - mat_gen = LayerPotentialMatrixGenerator( - self.queue.context, (local_expn,)) + mat_gen = LayerPotentialMatrixGenerator(actx.context, (local_expn,)) assert abs(expr.qbx_forced_limit) > 0 from pytential import bind, sym radii = bind(self.places, sym.expansion_radii( source_discr.ambient_dim, - dofdesc=expr.target))(self.queue) + dofdesc=expr.target))(actx) centers = bind(self.places, sym.expansion_centers( source_discr.ambient_dim, expr.qbx_forced_limit, - dofdesc=expr.target))(self.queue) - - _, (mat,) = mat_gen(self.queue, - targets=target_discr.nodes(), - sources=source_discr.nodes(), - centers=centers, - expansion_radii=radii, + dofdesc=expr.target))(actx) + + from meshmode.dof_array import flatten, thaw + _, (mat,) = mat_gen(actx.queue, + targets=flatten(thaw(actx, target_discr.nodes())), + sources=flatten(thaw(actx, source_discr.nodes())), + centers=flatten(centers), + expansion_radii=flatten(radii), **kernel_args) - mat = mat.get() + mat = actx.to_numpy(mat) waa = bind(self.places, sym.weights_and_area_elements( source_discr.ambient_dim, - dofdesc=expr.source))(self.queue) - mat[:, :] *= waa.get(self.queue) + dofdesc=expr.source))(actx) + mat[:, :] *= actx.to_numpy(flatten(waa)) mat = mat.dot(rec_density) return mat @@ -401,9 +437,9 @@ def map_int_g(self, expr): # {{{ p2p matrix builder class P2PMatrixBuilder(MatrixBuilderBase): - def __init__(self, queue, dep_expr, other_dep_exprs, + def __init__(self, actx, dep_expr, other_dep_exprs, dep_source, dep_discr, places, context, exclude_self=True): - super(P2PMatrixBuilder, self).__init__(queue, + super(P2PMatrixBuilder, self).__init__(actx, dep_expr, other_dep_exprs, dep_source, dep_discr, places, context) @@ -430,25 +466,26 @@ def map_int_g(self, expr): kernel_args = kernel.get_args() + kernel.get_source_args() kernel_args = set(arg.loopy_arg.name for arg in kernel_args) + actx = self.array_context kernel_args = _get_layer_potential_args(self, expr, include_args=kernel_args) if self.exclude_self: - kernel_args["target_to_source"] = \ - cl.array.arange(self.queue, 0, target_discr.nnodes, dtype=np.int) + kernel_args["target_to_source"] = actx.from_numpy( + np.arange(0, target_discr.ndofs, dtype=np.int) + ) from sumpy.p2p import P2PMatrixGenerator - mat_gen = P2PMatrixGenerator( - self.queue.context, (kernel,), exclude_self=self.exclude_self) + mat_gen = P2PMatrixGenerator(actx.context, (kernel,), + exclude_self=self.exclude_self) - _, (mat,) = mat_gen(self.queue, - targets=target_discr.nodes(), - sources=source_discr.nodes(), + from meshmode.dof_array import flatten, thaw + _, (mat,) = mat_gen(actx.queue, + targets=flatten(thaw(actx, target_discr.nodes())), + sources=flatten(thaw(actx, source_discr.nodes())), **kernel_args) - mat = mat.get() - mat = mat.dot(rec_density) + return actx.to_numpy(mat).dot(rec_density) - return mat # }}} @@ -462,8 +499,9 @@ def __init__(self, queue, dep_expr, other_dep_exprs, dep_source, dep_discr, places, index_set, context) def get_dep_variable(self): - tgtindices = self.index_set.linear_row_indices.get(self.queue) - srcindices = self.index_set.linear_col_indices.get(self.queue) + queue = self.array_context.queue + tgtindices = self.index_set.linear_row_indices.get(queue) + srcindices = self.index_set.linear_col_indices.get(queue) return np.equal(tgtindices, srcindices).astype(np.float64) @@ -484,6 +522,7 @@ def map_int_g(self, expr): if not np.isscalar(rec_density): raise NotImplementedError + actx = self.array_context kernel = expr.kernel kernel_args = _get_layer_potential_args(self._mat_mapper, expr) @@ -491,34 +530,34 @@ def map_int_g(self, expr): local_expn = LineTaylorLocalExpansion(kernel, lpot_source.qbx_order) from sumpy.qbx import LayerPotentialMatrixBlockGenerator - mat_gen = LayerPotentialMatrixBlockGenerator( - self.queue.context, (local_expn,)) + mat_gen = LayerPotentialMatrixBlockGenerator(actx.context, (local_expn,)) assert abs(expr.qbx_forced_limit) > 0 from pytential import bind, sym radii = bind(self.places, sym.expansion_radii( source_discr.ambient_dim, - dofdesc=expr.target))(self.queue) + dofdesc=expr.target))(actx) centers = bind(self.places, sym.expansion_centers( source_discr.ambient_dim, expr.qbx_forced_limit, - dofdesc=expr.target))(self.queue) - - _, (mat,) = mat_gen(self.queue, - targets=target_discr.nodes(), - sources=source_discr.nodes(), - centers=centers, - expansion_radii=radii, + dofdesc=expr.target))(actx) + + from meshmode.dof_array import flatten, thaw + _, (mat,) = mat_gen(actx.queue, + targets=flatten(thaw(actx, target_discr.nodes())), + sources=flatten(thaw(actx, source_discr.nodes())), + centers=flatten(centers), + expansion_radii=flatten(radii), index_set=self.index_set, **kernel_args) waa = bind(self.places, sym.weights_and_area_elements( source_discr.ambient_dim, - dofdesc=expr.source))(self.queue) - mat *= waa[self.index_set.linear_col_indices] - mat = rec_density * mat.get(self.queue) + dofdesc=expr.source))(actx) + waa = flatten(waa) - return mat + mat *= waa[self.index_set.linear_col_indices] + return rec_density * actx.to_numpy(mat) class FarFieldBlockBuilder(MatrixBlockBuilderBase): @@ -530,8 +569,9 @@ def __init__(self, queue, dep_expr, other_dep_exprs, dep_source, dep_discr, self.exclude_self = exclude_self def get_dep_variable(self): - tgtindices = self.index_set.linear_row_indices.get(self.queue) - srcindices = self.index_set.linear_col_indices.get(self.queue) + queue = self.array_context.queue + tgtindices = self.index_set.linear_row_indices.get(queue) + srcindices = self.index_set.linear_col_indices.get(queue) return np.equal(tgtindices, srcindices).astype(np.float64) @@ -558,24 +598,26 @@ def map_int_g(self, expr): kernel_args = kernel.get_args() + kernel.get_source_args() kernel_args = set(arg.loopy_arg.name for arg in kernel_args) + actx = self.array_context kernel_args = _get_layer_potential_args(self._mat_mapper, expr, include_args=kernel_args) if self.exclude_self: - kernel_args["target_to_source"] = \ - cl.array.arange(self.queue, 0, target_discr.nnodes, dtype=np.int) + kernel_args["target_to_source"] = actx.from_numpy( + np.arange(0, target_discr.ndofs, dtype=np.int) + ) from sumpy.p2p import P2PMatrixBlockGenerator - mat_gen = P2PMatrixBlockGenerator( - self.queue.context, (kernel,), exclude_self=self.exclude_self) + mat_gen = P2PMatrixBlockGenerator(actx.context, (kernel,), + exclude_self=self.exclude_self) - _, (mat,) = mat_gen(self.queue, - targets=target_discr.nodes(), - sources=source_discr.nodes(), + from meshmode.dof_array import flatten, thaw + _, (mat,) = mat_gen(actx.queue, + targets=flatten(thaw(actx, target_discr.nodes())), + sources=flatten(thaw(actx, source_discr.nodes())), index_set=self.index_set, **kernel_args) - mat = rec_density * mat.get(self.queue) - return mat + return rec_density * actx.to_numpy(mat) # }}} diff --git a/test/test_matrix.py b/test/test_matrix.py index 12be496c5..5de6780a1 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -38,9 +38,10 @@ from sumpy.tools import BlockIndexRanges, MatrixBlockIndexRanges from sumpy.symbolic import USE_SYMENGINE -from pytential import sym +from pytential import bind, sym from pytential import GeometryCollection +from meshmode.array_context import PyOpenCLArrayContext from meshmode.mesh.generation import ( # noqa ellipse, NArmedStarfish, make_curve_mesh, generate_torus) @@ -55,7 +56,7 @@ pass -def _build_geometry(queue, +def _build_geometry(actx, ambient_dim=2, nelements=30, target_order=7, @@ -79,8 +80,7 @@ def _build_geometry(queue, from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory from pytential.qbx import QBXLayerPotentialSource - density_discr = Discretization( - queue.context, mesh, + density_discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource(density_discr, @@ -92,24 +92,24 @@ def _build_geometry(queue, return places, places.auto_source -def _build_block_index(queue, - discr, +def _build_block_index(actx, discr, nblks=10, factor=1.0, use_tree=True): - nnodes = discr.nnodes - max_particles_in_box = nnodes // nblks + max_particles_in_box = discr.ndofs // nblks # create index ranges from pytential.linalg.proxy import partition_by_nodes indices = partition_by_nodes(discr, - use_tree=use_tree, max_nodes_in_box=max_particles_in_box) + use_tree=use_tree, + max_nodes_in_box=max_particles_in_box) if abs(factor - 1.0) < 1.0e-14: return indices # randomly pick a subset of points - indices = indices.get(queue) + # FIXME: this needs porting in sumpy.tools.BlockIndexRanges + indices = indices.get(actx.queue) indices_ = np.empty(indices.nblocks, dtype=np.object) for i in range(indices.nblocks): @@ -120,13 +120,11 @@ def _build_block_index(queue, indices_[i] = np.sort( np.random.choice(iidx, size=isize, replace=False)) - ranges_ = cl.array.to_device(queue, - np.cumsum([0] + [r.shape[0] for r in indices_])) - indices_ = cl.array.to_device(queue, np.hstack(indices_)) + ranges_ = actx.from_numpy(np.cumsum([0] + [r.shape[0] for r in indices_])) + indices_ = actx.from_numpy(np.hstack(indices_)) - indices = BlockIndexRanges(discr.cl_context, - indices_.with_queue(None), - ranges_.with_queue(None)) + indices = BlockIndexRanges(actx.context, + actx.freeze(indices_), actx.freeze(ranges_)) return indices @@ -137,8 +135,8 @@ def _build_op(lpot_id, source=sym.DEFAULT_SOURCE, target=sym.DEFAULT_TARGET, qbx_forced_limit="avg"): - from sumpy.kernel import LaplaceKernel, HelmholtzKernel + if k: knl = HelmholtzKernel(ambient_dim) knl_kwargs = {"k": k} @@ -200,6 +198,7 @@ def _max_block_error(mat, blk, index_set): def test_matrix_build(ctx_factory, k, curve_f, lpot_id, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) # prevent cache 'splosion from sympy.core.cache import clear_cache @@ -215,8 +214,7 @@ def test_matrix_build(ctx_factory, k, curve_f, lpot_id, visualize=False): from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory - pre_density_discr = Discretization( - cl_ctx, mesh, + pre_density_discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) from pytential.qbx import QBXLayerPotentialSource @@ -228,7 +226,7 @@ def test_matrix_build(ctx_factory, k, curve_f, lpot_id, visualize=False): from pytential.qbx.refinement import refine_geometry_collection places = GeometryCollection(qbx) - places = refine_geometry_collection(queue, places, + places = refine_geometry_collection(places, kernel_length_scale=(5 / k if k else None)) source = places.auto_source.to_stage1() @@ -237,15 +235,14 @@ def test_matrix_build(ctx_factory, k, curve_f, lpot_id, visualize=False): op, u_sym, knl_kwargs = _build_op(lpot_id, k=k, source=places.auto_source, target=places.auto_target) - from pytential import bind bound_op = bind(places, op) from pytential.symbolic.execution import build_matrix - mat = build_matrix(queue, places, op, u_sym).get() + mat = build_matrix(actx, places, op, u_sym).get() if visualize: from sumpy.tools import build_matrix as build_matrix_via_matvec - mat2 = bound_op.scipy_op(queue, "u", dtype=mat.dtype, **knl_kwargs) + mat2 = bound_op.scipy_op(actx, "u", dtype=mat.dtype, **knl_kwargs) mat2 = build_matrix_via_matvec(mat2) print(la.norm((mat - mat2).real, "fro") / la.norm(mat2.real, "fro"), la.norm((mat - mat2).imag, "fro") / la.norm(mat2.imag, "fro")) @@ -267,23 +264,22 @@ def test_matrix_build(ctx_factory, k, curve_f, lpot_id, visualize=False): pt.colorbar() pt.show() - from sumpy.tools import vector_to_device, vector_from_device + from pytential.symbolic.matrix import _unflatten_from_numpy, _flatten_to_numpy np.random.seed(12) for i in range(5): if is_obj_array(u_sym): u = make_obj_array([ - np.random.randn(density_discr.nnodes) + np.random.randn(density_discr.ndofs) for _ in range(len(u_sym)) ]) else: - u = np.random.randn(density_discr.nnodes) + u = np.random.randn(density_discr.ndofs) + u_dev = _unflatten_from_numpy(actx, u, density_discr) - u_dev = vector_to_device(queue, u) res_matvec = np.hstack( - list(vector_from_device( - queue, bound_op(queue, u=u_dev)))) - - res_mat = mat.dot(np.hstack(list(u))) + _flatten_to_numpy(actx, bound_op(actx, u=u_dev)) + ) + res_mat = mat.dot(np.hstack(u)) abs_err = la.norm(res_mat - res_matvec, np.inf) rel_err = abs_err / la.norm(res_matvec, np.inf) @@ -299,6 +295,7 @@ def test_p2p_block_builder(ctx_factory, factor, ambient_dim, lpot_id, visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) + actx = PyOpenCLArrayContext(queue) # prevent cache explosion from sympy.core.cache import clear_cache @@ -312,7 +309,7 @@ def test_p2p_block_builder(ctx_factory, factor, ambient_dim, lpot_id, ) target_order = 2 if ambient_dim == 3 else 7 - places, dofdesc = _build_geometry(queue, + places, dofdesc = _build_geometry(actx, target_order=target_order, ambient_dim=ambient_dim, auto_where=place_ids) @@ -323,14 +320,14 @@ def test_p2p_block_builder(ctx_factory, factor, ambient_dim, lpot_id, dd = places.auto_source density_discr = places.get_discretization(dd.geometry, dd.discr_stage) - index_set = _build_block_index(queue, density_discr, factor=factor) + index_set = _build_block_index(actx, density_discr, factor=factor) index_set = MatrixBlockIndexRanges(ctx, index_set, index_set) from pytential.symbolic.execution import _prepare_expr expr = _prepare_expr(places, op) from pytential.symbolic.matrix import P2PMatrixBuilder - mbuilder = P2PMatrixBuilder(queue, + mbuilder = P2PMatrixBuilder(actx, dep_expr=u_sym, other_dep_exprs=[], dep_source=places.get_geometry(dd.geometry), @@ -341,7 +338,7 @@ def test_p2p_block_builder(ctx_factory, factor, ambient_dim, lpot_id, mat = mbuilder(expr) from pytential.symbolic.matrix import FarFieldBlockBuilder - mbuilder = FarFieldBlockBuilder(queue, + mbuilder = FarFieldBlockBuilder(actx, dep_expr=u_sym, other_dep_exprs=[], dep_source=places.get_geometry(dd.geometry), @@ -352,7 +349,7 @@ def test_p2p_block_builder(ctx_factory, factor, ambient_dim, lpot_id, exclude_self=True) blk = mbuilder(expr) - index_set = index_set.get(queue) + index_set = index_set.get(actx.queue) if visualize and ambient_dim == 2: blk_full = np.zeros_like(mat) mat_full = np.zeros_like(mat) @@ -381,6 +378,7 @@ def test_qbx_block_builder(ctx_factory, factor, ambient_dim, lpot_id, visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) + actx = PyOpenCLArrayContext(queue) # prevent cache explosion from sympy.core.cache import clear_cache @@ -394,7 +392,7 @@ def test_qbx_block_builder(ctx_factory, factor, ambient_dim, lpot_id, ) target_order = 2 if ambient_dim == 3 else 7 - places, _ = _build_geometry(queue, + places, _ = _build_geometry(actx, target_order=target_order, ambient_dim=ambient_dim, auto_where=place_ids) @@ -409,11 +407,11 @@ def test_qbx_block_builder(ctx_factory, factor, ambient_dim, lpot_id, dd = places.auto_source density_discr = places.get_discretization(dd.geometry, dd.discr_stage) - index_set = _build_block_index(queue, density_discr, factor=factor) + index_set = _build_block_index(actx, density_discr, factor=factor) index_set = MatrixBlockIndexRanges(ctx, index_set, index_set) from pytential.symbolic.matrix import NearFieldBlockBuilder - mbuilder = NearFieldBlockBuilder(queue, + mbuilder = NearFieldBlockBuilder(actx, dep_expr=u_sym, other_dep_exprs=[], dep_source=places.get_geometry(dd.geometry), @@ -424,7 +422,7 @@ def test_qbx_block_builder(ctx_factory, factor, ambient_dim, lpot_id, blk = mbuilder(expr) from pytential.symbolic.matrix import MatrixBuilder - mbuilder = MatrixBuilder(queue, + mbuilder = MatrixBuilder(actx, dep_expr=u_sym, other_dep_exprs=[], dep_source=places.get_geometry(dd.geometry), @@ -462,6 +460,7 @@ def test_build_matrix_places(ctx_factory, source_discr_stage, target_discr_stage, visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) + actx = PyOpenCLArrayContext(queue) # prevent cache explosion from sympy.core.cache import clear_cache @@ -476,7 +475,7 @@ def test_build_matrix_places(ctx_factory, ) # build test operators - places, _ = _build_geometry(queue, + places, _ = _build_geometry(actx, nelements=8, target_order=2, ambient_dim=2, @@ -493,7 +492,7 @@ def test_build_matrix_places(ctx_factory, dd = places.auto_source source_discr = places.get_discretization(dd.geometry, dd.discr_stage) - index_set = _build_block_index(queue, source_discr, factor=0.6) + index_set = _build_block_index(actx, source_discr, factor=0.6) index_set = MatrixBlockIndexRanges(ctx, index_set, index_set) from pytential.symbolic.execution import _prepare_expr @@ -501,7 +500,7 @@ def test_build_matrix_places(ctx_factory, # build full QBX matrix from pytential.symbolic.matrix import MatrixBuilder - mbuilder = MatrixBuilder(queue, + mbuilder = MatrixBuilder(actx, dep_expr=u_sym, other_dep_exprs=[], dep_source=places.get_geometry(dd.geometry), @@ -512,7 +511,7 @@ def test_build_matrix_places(ctx_factory, # build full p2p matrix from pytential.symbolic.matrix import P2PMatrixBuilder - mbuilder = P2PMatrixBuilder(queue, + mbuilder = P2PMatrixBuilder(actx, dep_expr=u_sym, other_dep_exprs=[], dep_source=places.get_geometry(dd.geometry), @@ -521,11 +520,11 @@ def test_build_matrix_places(ctx_factory, context={}) p2p_mat = mbuilder(op) - assert p2p_mat.shape == (target_discr.nnodes, source_discr.nnodes) + assert p2p_mat.shape == (target_discr.ndofs, source_discr.ndofs) # build block qbx and p2p matrices from pytential.symbolic.matrix import NearFieldBlockBuilder - mbuilder = NearFieldBlockBuilder(queue, + mbuilder = NearFieldBlockBuilder(actx, dep_expr=u_sym, other_dep_exprs=[], dep_source=places.get_geometry(dd.geometry), @@ -538,7 +537,7 @@ def test_build_matrix_places(ctx_factory, assert _max_block_error(qbx_mat, mat, index_set.get(queue)) < 1.0e-14 from pytential.symbolic.matrix import FarFieldBlockBuilder - mbuilder = FarFieldBlockBuilder(queue, + mbuilder = FarFieldBlockBuilder(actx, dep_expr=u_sym, other_dep_exprs=[], dep_source=places.get_geometry(dd.geometry), From c3077e9cf16edd73a80c611917edf68da140ad63 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 23:01:44 -0500 Subject: [PATCH 28/75] Port test_scalar_int_eq.py --- pytential/source.py | 15 ++++-- setup.cfg | 2 +- test/test_scalar_int_eq.py | 101 ++++++++++++++++++++----------------- 3 files changed, 67 insertions(+), 51 deletions(-) diff --git a/pytential/source.py b/pytential/source.py index 09cbf3641..db2781dba 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -91,8 +91,7 @@ class PointPotentialSource(PotentialSource): .. automethod:: exec_compute_potential_insn """ - def __init__(self, cl_context, nodes): - self.cl_context = cl_context + def __init__(self, nodes): self._nodes = nodes @property @@ -155,7 +154,7 @@ def exec_compute_potential_insn(self, actx, insn, bound_expr, evaluate, strengths = evaluate(insn.density) # FIXME: Do this all at once - result = [] + results = [] for o in insn.outputs: target_discr = bound_expr.places.get_discretization( o.target_name.geometry, o.target_name.discr_stage) @@ -170,10 +169,16 @@ def exec_compute_potential_insn(self, actx, insn, bound_expr, evaluate, self._nodes, [strengths], **kernel_args) - result.append((o.name, output_for_each_kernel[o.kernel_index])) + from meshmode.discretization import Discretization + result = output_for_each_kernel[o.kernel_index] + if isinstance(target_discr, Discretization): + from meshmode.dof_array import unflatten + result = unflatten(actx, target_discr, result) + + results.append((o.name, result)) timing_data = {} - return result, timing_data + return results, timing_data # }}} diff --git a/setup.cfg b/setup.cfg index 34699863c..35cae4c5a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,4 +9,4 @@ exclude= [tool:pytest] markers= slowtest: mark a test as slow -python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py test_layer_pot_eigenvalues.py test_layer_pot_identity.py test_layer_pot.py +python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py test_layer_pot_eigenvalues.py test_layer_pot_identity.py test_layer_pot.py test_scalar_int_eq.py diff --git a/test/test_scalar_int_eq.py b/test/test_scalar_int_eq.py index bc1973957..285705505 100644 --- a/test/test_scalar_int_eq.py +++ b/test/test_scalar_int_eq.py @@ -32,6 +32,7 @@ from pyopencl.tools import ( # noqa pytest_generate_tests_for_pyopencl as pytest_generate_tests) +from meshmode.array_context import PyOpenCLArrayContext from meshmode.mesh.generation import ( # noqa ellipse, cloverleaf, starfish, drop, n_gon, qbx_peanut, WobblyCircle, make_curve_mesh) @@ -436,7 +437,8 @@ def get_mesh(self, resolution, target_order): # {{{ test backend -def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): +def run_int_eq_test(actx: PyOpenCLArrayContext, + case, resolution, visualize=False): mesh = case.get_mesh(resolution, case.target_order) print("%d elements" % mesh.nelements) @@ -445,7 +447,7 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory pre_density_discr = Discretization( - cl_ctx, mesh, + actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(case.target_order)) source_order = 4*case.target_order @@ -495,7 +497,7 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): point_sources = make_circular_point_group( mesh.ambient_dim, 10, test_src_geo_radius, func=lambda x: x**1.5) - point_source = PointPotentialSource(cl_ctx, point_sources) + point_source = PointPotentialSource(point_sources) from pytential.target import PointsTarget test_targets = make_circular_point_group( @@ -554,7 +556,7 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): places = GeometryCollection(places) if case.use_refinement: from pytential.qbx.refinement import refine_geometry_collection - places = refine_geometry_collection(queue, places, + places = refine_geometry_collection(places, **refiner_extra_kwargs) dd = sym.as_dofdesc(sym.DEFAULT_SOURCE).to_stage1() @@ -573,15 +575,15 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): discr = places.get_discretization(dd.geometry, sym.QBX_SOURCE_QUAD_STAGE2) print("quad stage-2 elements have %d nodes" - % discr.groups[0].nunit_nodes) + % discr.groups[0].nunit_dofs) # }}} if hasattr(case, "visualize_geometry") and case.visualize_geometry: bdry_normals = bind(places, sym.normal(mesh.ambient_dim))( - queue).as_vector(dtype=np.object) + actx).as_vector(dtype=np.object) - bdry_vis = make_visualizer(queue, density_discr, case.target_order) + bdry_vis = make_visualizer(actx, density_discr, case.target_order) bdry_vis.write_vtk_file("geometry.vtu", [ ("normals", bdry_normals) ]) @@ -591,19 +593,24 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): if visualize: if mesh.ambient_dim == 2: # show geometry, centers, normals - nodes_h = density_discr.nodes().get(queue=queue) - normal = bind(places, sym.normal(2))(queue).as_vector(np.object) - + from meshmode.dof_array import thaw, flatten + nodes_h = [actx.to_numpy(axis) for axis in + flatten(thaw(actx, density_discr.nodes()))] + normal_h = [actx.to_numpy(axis) for axis in + flatten( + bind(places, sym.normal(2))(actx) + .as_vector(np.object))] + + import matplotlib.pyplot as pt pt.plot(nodes_h[0], nodes_h[1], "x-") - pt.quiver(nodes_h[0], nodes_h[1], - normal[0].get(queue), normal[1].get(queue)) + pt.quiver(nodes_h[0], nodes_h[1], normal_h[0], normal_h[1]) pt.gca().set_aspect("equal") pt.show() elif mesh.ambient_dim == 3: bdry_normals = bind(places, sym.normal(3))( queue).as_vector(dtype=object) - bdry_vis = make_visualizer(queue, density_discr, case.target_order+3) + bdry_vis = make_visualizer(actx, density_discr, case.target_order+3) bdry_vis.write_vtk_file("pre-solve-source-%s.vtu" % resolution, [ ("bdry_normals", bdry_normals), ]) @@ -654,7 +661,7 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): source_charges = source_charges.astype(dtype) assert np.sum(source_charges) < 1e-15 - source_charges_dev = cl.array.to_device(queue, source_charges) + source_charges_dev = actx.from_numpy(source_charges) # }}} @@ -666,27 +673,27 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): test_direct = bind(places, pot_src, auto_where=("point_source", "point_target"))( - queue, charges=source_charges_dev, **concrete_knl_kwargs) + actx, charges=source_charges_dev, **concrete_knl_kwargs) if case.bc_type == "dirichlet": bc = bind(places, pot_src, auto_where=("point_source", sym.DEFAULT_TARGET))( - queue, charges=source_charges_dev, **concrete_knl_kwargs) + actx, charges=source_charges_dev, **concrete_knl_kwargs) elif case.bc_type == "neumann": bc = bind(places, sym.normal_derivative( qbx.ambient_dim, pot_src, dofdesc=sym.DEFAULT_TARGET), auto_where=("point_source", sym.DEFAULT_TARGET))( - queue, charges=source_charges_dev, **concrete_knl_kwargs) + actx, charges=source_charges_dev, **concrete_knl_kwargs) elif case.bc_type == "clamped_plate": bc_u = bind((point_source, density_discr), pot_src)( - queue, charges=source_charges_dev, **concrete_knl_kwargs) + actx, charges=source_charges_dev, **concrete_knl_kwargs) bc_du = bind( (point_source, density_discr), sym.normal_derivative( qbx.ambient_dim, pot_src, dofdesc=sym.DEFAULT_TARGET) - )(queue, charges=source_charges_dev, **concrete_knl_kwargs) + )(actx, charges=source_charges_dev, **concrete_knl_kwargs) bc = [bc_u, bc_du] # }}} @@ -694,19 +701,19 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): # {{{ solve bound_op = bind(places, op_u) - rhs = bind(places, op.prepare_rhs(op.get_density_var("bc")))(queue, bc=bc) + rhs = bind(places, op.prepare_rhs(op.get_density_var("bc")))(actx, bc=bc) try: from pytential.solve import gmres gmres_result = gmres( - bound_op.scipy_op(queue, "u", dtype, **concrete_knl_kwargs), + bound_op.scipy_op(actx, "u", dtype, **concrete_knl_kwargs), rhs, tol=case.gmres_tol, progress=True, hard_failure=True, stall_iterations=50, no_progress_factor=1.05) except QBXTargetAssociationFailedException as e: - bdry_vis = make_visualizer(queue, density_discr, case.target_order+3) + bdry_vis = make_visualizer(actx, density_discr, case.target_order+3) bdry_vis.write_vtk_file("failed-targets-%s.vtu" % resolution, [ ("failed_targets", e.failed_target_flags), @@ -721,10 +728,11 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): # {{{ build matrix for spectrum check if 0: + # FIXME: Ensure this works from sumpy.tools import build_matrix mat = build_matrix( bound_op.scipy_op( - queue, arg_name="u", dtype=dtype, **concrete_knl_kwargs)) + actx, arg_name="u", dtype=dtype, **concrete_knl_kwargs)) w, v = la.eig(mat) if visualize: pt.imshow(np.log10(1e-20+np.abs(mat))) @@ -740,7 +748,7 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): op.representation(op.get_density_var("u")), auto_where=(sym.DEFAULT_SOURCE, "point_target")) - test_via_bdry = bound_tgt_op(queue, u=weighted_u, **concrete_knl_kwargs) + test_via_bdry = bound_tgt_op(actx, u=weighted_u, **concrete_knl_kwargs) err = test_via_bdry - test_direct @@ -784,11 +792,11 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): #print(bound_t_deriv_op.code) grad_from_src = bound_grad_op( - queue, u=weighted_u, **concrete_knl_kwargs) + actx, u=weighted_u, **concrete_knl_kwargs) grad_ref = bind(places, sym.grad(mesh.ambient_dim, pot_src), - auto_where=("point_source", "point_target"))(queue, + auto_where=("point_source", "point_target"))(actx, charges=source_charges_dev, **concrete_knl_kwargs) @@ -812,14 +820,16 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): sym.tangential_derivative(qbx.ambient_dim, pot), qbx_forced_limit=loc_sign)) - tang_deriv_from_src = bound_t_deriv_op( - queue, u=weighted_u, **concrete_knl_kwargs).as_scalar().get() + from meshmode.dof_array import flatten + tang_deriv_from_src = actx.to_numpy( + flatten(bound_t_deriv_op( + actx, u=weighted_u, **concrete_knl_kwargs).as_scalar())) - tang_deriv_ref = bind(places, + tang_deriv_ref = actx.to_numpy(flatten(bind(places, sym.tangential_derivative(qbx.ambient_dim, pot_src), - auto_where=("point_source", sym.DEFAULT_TARGET))(queue, + auto_where=("point_source", sym.DEFAULT_TARGET))(actx, charges=source_charges_dev, - **concrete_knl_kwargs).as_scalar().get() + **concrete_knl_kwargs).as_scalar())) if visualize: pt.plot(tang_deriv_ref.real) @@ -841,12 +851,12 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): if visualize: bdry_normals = bind(places, sym.normal(qbx.ambient_dim))( - queue).as_vector(dtype=np.object) + actx).as_vector(dtype=np.object) sym_sqrt_j = sym.sqrt_jac_q_weight(density_discr.ambient_dim) - u = bind(places, op.get_density_var("u") / sym_sqrt_j)(queue, u=weighted_u) + u = bind(places, op.get_density_var("u") / sym_sqrt_j)(actx, u=weighted_u) - bdry_vis = make_visualizer(queue, density_discr, case.target_order+3) + bdry_vis = make_visualizer(actx, density_discr, case.target_order+3) bdry_vis.write_vtk_file("source-%s.vtu" % resolution, [ ("u", u), ("bc", bc), @@ -857,7 +867,7 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): solved_pot = bind(places, op.representation(op.get_density_var("u")), auto_where=("qbx_target_tol", "plot_targets"))( - queue, u=weighted_u, k=case.k) + actx, u=weighted_u, k=getattr(case, "k", None)) except QBXTargetAssociationFailedException as e: fplot.write_vtk_file( "failed-targets.vts", @@ -866,23 +876,24 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): ]) raise - ones_density = density_discr.zeros(queue) - ones_density.fill(1) + ones_density = density_discr.zeros(actx) + 1 indicator = -sym.D(LaplaceKernel(qbx.ambient_dim), op.get_density_var("sigma"), qbx_forced_limit=None) indicator = bind(places, indicator, auto_where=("qbx_target_tol", "plot_targets"))( - queue, sigma=ones_density).get() - - solved_pot = solved_pot.get() + actx, sigma=ones_density) true_pot = bind(places, pot_src, auto_where=("point_source", "plot_targets"))( - queue, + actx, charges=source_charges_dev, - **concrete_knl_kwargs).get() + **concrete_knl_kwargs) + + solved_pot = actx.to_numpy(solved_pot) + true_pot = actx.to_numpy(true_pot) + indicator = actx.to_numpy(indicator) #fplot.show_scalar_in_mayavi(solved_pot.real, max_val=5) if case.prob_side == "scat": @@ -906,7 +917,7 @@ def run_int_eq_test(cl_ctx, queue, case, resolution, visualize=False): # }}} - h_max = bind(places, sym.h_max(qbx.ambient_dim))(queue) + h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) return dict( h_max=h_max, rel_err_2=rel_err_2, @@ -943,6 +954,7 @@ def test_integral_equation(ctx_factory, case, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) if USE_SYMENGINE and case.fmm_backend is None: pytest.skip("https://gitlab.tiker.net/inducer/sumpy/issues/25") @@ -959,8 +971,7 @@ def test_integral_equation(ctx_factory, case, visualize=False): have_error_data = False for resolution in case.resolutions: - result = run_int_eq_test(cl_ctx, queue, case, resolution, - visualize=visualize) + result = run_int_eq_test(actx, case, resolution, visualize=visualize) if result["rel_err_2"] is not None: have_error_data = True From 80ab289c30b1e854673cb6394bef421ed3304a58 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 2 Jul 2020 23:01:59 -0500 Subject: [PATCH 29/75] port test_linalg_proxy to array-context --- pytential/linalg/proxy.py | 403 +++++++++++++++++------------------ pytential/symbolic/matrix.py | 42 +--- pytential/utils.py | 25 +++ test/test_linalg_proxy.py | 107 +++++----- test/test_matrix.py | 10 +- 5 files changed, 297 insertions(+), 290 deletions(-) diff --git a/pytential/linalg/proxy.py b/pytential/linalg/proxy.py index ba2e2ea96..a87e0785e 100644 --- a/pytential/linalg/proxy.py +++ b/pytential/linalg/proxy.py @@ -26,10 +26,6 @@ import numpy as np import numpy.linalg as la -import pyopencl as cl -import pyopencl.array # noqa -from pyopencl.array import to_device - from pytools.obj_array import make_obj_array from pytools import memoize_method, memoize_in from sumpy.tools import BlockIndexRanges @@ -61,9 +57,7 @@ def _element_node_range(group, ielement): return np.arange(istart, iend) -def partition_by_nodes(discr, - use_tree=True, - max_nodes_in_box=None): +def partition_by_nodes(actx, discr, use_tree=True, max_nodes_in_box=None): """Generate equally sized ranges of nodes. The partition is created at the lowest level of granularity, i.e. nodes. This results in balanced ranges of points, but will split elements across different ranges. @@ -82,43 +76,45 @@ def partition_by_nodes(discr, # FIXME: this is just an arbitrary value max_nodes_in_box = 32 - with cl.CommandQueue(discr.cl_context) as queue: - if use_tree: - from boxtree import box_flags_enum - from boxtree import TreeBuilder + if use_tree: + from boxtree import box_flags_enum + from boxtree import TreeBuilder - builder = TreeBuilder(discr.cl_context) + builder = TreeBuilder(actx.context) - tree, _ = builder(queue, discr.nodes(), + from meshmode.dof_array import flatten, thaw + tree, _ = builder(actx.queue, + flatten(thaw(actx, discr.nodes())), max_particles_in_box=max_nodes_in_box) - tree = tree.get(queue) - leaf_boxes, = (tree.box_flags - & box_flags_enum.HAS_CHILDREN == 0).nonzero() + tree = tree.get(actx.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] - indices[i] = tree.user_source_ids[box_start:box_end] + 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] + indices[i] = tree.user_source_ids[box_start:box_end] - ranges = to_device(queue, - np.cumsum([0] + [box.shape[0] for box in indices])) - indices = to_device(queue, np.hstack(indices)) - else: - indices = cl.array.arange(queue, 0, discr.nnodes, - dtype=np.int) - ranges = cl.array.arange(queue, 0, discr.nnodes + 1, - discr.nnodes // max_nodes_in_box, - dtype=np.int) - assert ranges[-1] == discr.nnodes + ranges = actx.from_numpy( + np.cumsum([0] + [box.shape[0] for box in indices]) + ) + indices = actx.from_numpy(np.hstack(indices)) + else: + indices = actx.from_numpy(np.arange(0, discr.ndofs, dtype=np.int)) + ranges = actx.from_numpy(np.arange( + 0, + discr.ndofs + 1, + discr.ndofs // max_nodes_in_box, dtype=np.int)) - return BlockIndexRanges(discr.cl_context, - indices.with_queue(None), - ranges.with_queue(None)) + assert ranges[-1] == discr.ndofs + return BlockIndexRanges(actx.context, + actx.freeze(indices), actx.freeze(ranges)) -def partition_from_coarse(resampler, from_indices): + +def partition_from_coarse(actx, resampler, from_indices): """Generate a partition of nodes from an existing partition on a coarser discretization. The new partition is generated based on element refinement relationships in *resampler*, so the existing partition @@ -140,54 +136,51 @@ def partition_from_coarse(resampler, from_indices): if not hasattr(resampler, "groups"): raise ValueError("resampler must be a DirectDiscretizationConnection.") - with cl.CommandQueue(resampler.cl_context) as queue: - from_indices = from_indices.get(queue) - - # construct ranges - from_discr = resampler.from_discr - from_grp_ranges = np.cumsum( - [0] + [grp.nelements for grp in from_discr.mesh.groups]) - from_el_ranges = np.hstack([ - np.arange(grp.node_nr_base, grp.nnodes + 1, grp.nunit_nodes) - for grp in from_discr.groups]) - - # construct coarse element arrays in each from_range - el_indices = np.empty(from_indices.nblocks, dtype=np.object) - el_ranges = np.full(from_grp_ranges[-1], -1, dtype=np.int) - for i in range(from_indices.nblocks): - ifrom = from_indices.block_indices(i) - el_indices[i] = np.unique(np.digitize(ifrom, from_el_ranges)) - 1 - el_ranges[el_indices[i]] = i - el_indices = np.hstack(el_indices) - - # construct lookup table - to_el_table = [np.full(g.nelements, -1, dtype=np.int) - for g in resampler.to_discr.groups] - - for igrp, grp in enumerate(resampler.groups): - for batch in grp.batches: - to_el_table[igrp][batch.to_element_indices.get(queue)] = \ - from_grp_ranges[igrp] + batch.from_element_indices.get(queue) - - # construct fine node index list - indices = [np.empty(0, dtype=np.int) - for _ in range(from_indices.nblocks)] - for igrp in range(len(resampler.groups)): - to_element_indices = \ - np.where(np.isin(to_el_table[igrp], el_indices))[0] - - for i, j in zip(el_ranges[to_el_table[igrp][to_element_indices]], - to_element_indices): - indices[i] = np.hstack([indices[i], - _element_node_range(resampler.to_discr.groups[igrp], j)]) - - ranges = to_device(queue, - np.cumsum([0] + [b.shape[0] for b in indices])) - indices = to_device(queue, np.hstack(indices)) - - return BlockIndexRanges(resampler.cl_context, - indices.with_queue(None), - ranges.with_queue(None)) + from_indices = from_indices.get(actx.queue) + + # construct ranges + from_discr = resampler.from_discr + from_grp_ranges = np.cumsum( + [0] + [grp.nelements for grp in from_discr.mesh.groups]) + from_el_ranges = np.hstack([ + np.arange(grp.node_nr_base, grp.nnodes + 1, grp.nunit_nodes) + for grp in from_discr.groups]) + + # construct coarse element arrays in each from_range + el_indices = np.empty(from_indices.nblocks, dtype=np.object) + el_ranges = np.full(from_grp_ranges[-1], -1, dtype=np.int) + for i in range(from_indices.nblocks): + ifrom = from_indices.block_indices(i) + el_indices[i] = np.unique(np.digitize(ifrom, from_el_ranges)) - 1 + el_ranges[el_indices[i]] = i + el_indices = np.hstack(el_indices) + + # construct lookup table + to_el_table = [np.full(g.nelements, -1, dtype=np.int) + for g in resampler.to_discr.groups] + + for igrp, grp in enumerate(resampler.groups): + for batch in grp.batches: + to_el_table[igrp][actx.to_numpy(batch.to_element_indices)] = \ + from_grp_ranges[igrp] + actx.to_numpy(batch.from_element_indices) + + # construct fine node index list + indices = [np.empty(0, dtype=np.int) + for _ in range(from_indices.nblocks)] + for igrp in range(len(resampler.groups)): + to_element_indices = \ + np.where(np.isin(to_el_table[igrp], el_indices))[0] + + for i, j in zip(el_ranges[to_el_table[igrp][to_element_indices]], + to_element_indices): + indices[i] = np.hstack([indices[i], + _element_node_range(resampler.to_discr.groups[igrp], j)]) + + ranges = actx.from_numpy(np.cumsum([0] + [b.shape[0] for b in indices])) + indices = actx.from_numpy(np.hstack(indices)) + + return BlockIndexRanges(resampler.cl_context, + actx.freeze(indices), actx.freeze(ranges)) # }}} @@ -340,7 +333,7 @@ def get_kernel(self): """.format(radius_expr=radius_expr)], [ lp.GlobalArg("sources", None, - shape=(self.ambient_dim, "nsources")), + shape=(self.ambient_dim, "nsources"), dim_tags="sep,C"), lp.GlobalArg("center_int", None, shape=(self.ambient_dim, "nsources"), dim_tags="sep,C"), lp.GlobalArg("center_ext", None, @@ -367,11 +360,11 @@ def get_optimized_kernel(self): return knl - def __call__(self, queue, source_dd, indices, **kwargs): + def __call__(self, actx, source_dd, indices, **kwargs): """Generate proxy points for each given range of source points in the discretization in *source_dd*. - :arg queue: a :class:`pyopencl.CommandQueue`. + :arg actx: a :class:`~meshmode.array_context.ArrayContext`. :arg source_dd: a :class:`~pytential.symbolic.primitives.DOFDescriptor` for the discretization on which the proxy points are to be generated. @@ -397,47 +390,51 @@ def _affine_map(v, A, b): source_dd.geometry, source_dd.discr_stage) radii = bind(self.places, sym.expansion_radii( - self.ambient_dim, dofdesc=source_dd))(queue) + self.ambient_dim, dofdesc=source_dd))(actx) center_int = bind(self.places, sym.expansion_centers( - self.ambient_dim, -1, dofdesc=source_dd))(queue) + self.ambient_dim, -1, dofdesc=source_dd))(actx) center_ext = bind(self.places, sym.expansion_centers( - self.ambient_dim, +1, dofdesc=source_dd))(queue) + self.ambient_dim, +1, dofdesc=source_dd))(actx) + from meshmode.dof_array import flatten, thaw knl = self.get_kernel() - _, (centers_dev, radii_dev,) = knl(queue, - sources=discr.nodes(), - center_int=center_int, - center_ext=center_ext, - expansion_radii=radii, + _, (centers_dev, radii_dev,) = knl(actx.queue, + sources=flatten(thaw(actx, discr.nodes())), + center_int=flatten(center_int), + center_ext=flatten(center_ext), + expansion_radii=flatten(radii), srcindices=indices.indices, srcranges=indices.ranges, **kwargs) - centers = centers_dev.get() - radii = radii_dev.get() + from pytential.utils import flatten_to_numpy + centers = flatten_to_numpy(actx, centers_dev) + radii = flatten_to_numpy(actx, radii_dev) proxies = np.empty(indices.nblocks, dtype=np.object) for i in range(indices.nblocks): proxies[i] = _affine_map(self.ref_points, A=(radii[i] * np.eye(self.ambient_dim)), b=centers[:, i].reshape(-1, 1)) - pxyranges = cl.array.arange(queue, - 0, - proxies.shape[0] * proxies[0].shape[1] + 1, - proxies[0].shape[1], - dtype=indices.ranges.dtype) + pxyranges = actx.from_numpy(np.arange( + 0, + proxies.shape[0] * proxies[0].shape[1] + 1, + proxies[0].shape[1], + dtype=indices.ranges.dtype)) proxies = make_obj_array([ - cl.array.to_device(queue, np.hstack([p[idim] for p in proxies])) - for idim in range(self.ambient_dim)]) + actx.freeze(actx.from_numpy(np.hstack([p[idim] for p in proxies]))) + for idim in range(self.ambient_dim) + ]) centers = make_obj_array([ - centers_dev[idim].with_queue(queue).copy() - for idim in range(self.ambient_dim)]) + actx.freeze(centers_dev[idim]) + for idim in range(self.ambient_dim) + ]) assert pxyranges[-1] == proxies[0].shape[0] - return proxies, pxyranges, centers, radii_dev + return proxies, actx.freeze(pxyranges), centers, actx.freeze(radii_dev) -def gather_block_neighbor_points(discr, indices, pxycenters, pxyradii, - max_nodes_in_box=None): +def gather_block_neighbor_points(actx, discr, indices, pxycenters, pxyradii, + max_nodes_in_box=None): """Generate a set of neighboring points for each range of points in *discr*. Neighboring points of a range :math:`i` are defined as all the points inside the proxy ball :math:`i` that do not also @@ -455,79 +452,77 @@ def gather_block_neighbor_points(discr, indices, pxycenters, pxyradii, # FIXME: this is a fairly arbitrary value max_nodes_in_box = 32 - with cl.CommandQueue(discr.cl_context) as queue: - indices = indices.get(queue) - - # NOTE: this is constructed for multiple reasons: - # * TreeBuilder takes object arrays - # * `srcindices` can be a small subset of nodes, so this will save - # some work - # * `srcindices` may reorder the array returned by nodes(), so this - # makes sure that we have the same order in tree.user_source_ids - # and friends - sources = discr.nodes().get(queue) - sources = make_obj_array([ - cl.array.to_device(queue, sources[idim, indices.indices]) - for idim in range(discr.ambient_dim)]) - - # construct tree - from boxtree import TreeBuilder - builder = TreeBuilder(discr.cl_context) - tree, _ = builder(queue, sources, - max_particles_in_box=max_nodes_in_box) - - from boxtree.area_query import AreaQueryBuilder - builder = AreaQueryBuilder(discr.cl_context) - query, _ = builder(queue, tree, pxycenters, pxyradii) - - # find nodes inside each proxy ball - tree = tree.get(queue) - query = query.get(queue) - - if isinstance(pxycenters[0], cl.array.Array): - pxycenters = np.vstack([pxycenters[idim].get(queue) - for idim in range(discr.ambient_dim)]) - if isinstance(pxyradii, cl.array.Array): - pxyradii = pxyradii.get(queue) - - nbrindices = np.empty(indices.nblocks, dtype=np.object) - for iproxy in range(indices.nblocks): - # get list of boxes intersecting the current ball - istart = query.leaves_near_ball_starts[iproxy] - iend = query.leaves_near_ball_starts[iproxy + 1] - iboxes = query.leaves_near_ball_lists[istart:iend] - - # get nodes inside the boxes - istart = tree.box_source_starts[iboxes] - iend = istart + tree.box_source_counts_cumul[iboxes] - isources = np.hstack([np.arange(s, e) - for s, e in zip(istart, iend)]) - nodes = np.vstack([tree.sources[idim][isources] - for idim in range(discr.ambient_dim)]) - isources = tree.user_source_ids[isources] - - # get nodes inside the ball but outside the current range - center = pxycenters[:, iproxy].reshape(-1, 1) - radius = pxyradii[iproxy] - mask = ((la.norm(nodes - center, axis=0) < radius) - & ((isources < indices.ranges[iproxy]) - | (indices.ranges[iproxy + 1] <= isources))) - - nbrindices[iproxy] = indices.indices[isources[mask]] - - nbrranges = to_device(queue, - np.cumsum([0] + [n.shape[0] for n in nbrindices])) - nbrindices = to_device(queue, np.hstack(nbrindices)) - - return BlockIndexRanges(discr.cl_context, - nbrindices.with_queue(None), - nbrranges.with_queue(None)) - - -def gather_block_interaction_points(places, source_dd, indices, - radius_factor=None, - approx_nproxy=None, - max_nodes_in_box=None): + indices = indices.get(actx.queue) + + # NOTE: this is constructed for multiple reasons: + # * TreeBuilder takes object arrays + # * `srcindices` can be a small subset of nodes, so this will save + # some work + # * `srcindices` may reorder the array returned by nodes(), so this + # makes sure that we have the same order in tree.user_source_ids + # and friends + from pytential.utils import flatten_to_numpy + sources = flatten_to_numpy(actx, discr.nodes()) + sources = make_obj_array([ + actx.from_numpy(sources[idim][indices.indices]) + for idim in range(discr.ambient_dim)]) + + # construct tree + from boxtree import TreeBuilder + builder = TreeBuilder(actx.context) + tree, _ = builder(actx.queue, sources, + max_particles_in_box=max_nodes_in_box) + + from boxtree.area_query import AreaQueryBuilder + builder = AreaQueryBuilder(actx.context) + query, _ = builder(actx.queue, tree, pxycenters, pxyradii) + + # find nodes inside each proxy ball + tree = tree.get(actx.queue) + query = query.get(actx.queue) + + pxycenters = np.vstack([ + actx.to_numpy(pxycenters[idim]) + for idim in range(discr.ambient_dim) + ]) + pxyradii = actx.to_numpy(pxyradii) + + nbrindices = np.empty(indices.nblocks, dtype=np.object) + for iproxy in range(indices.nblocks): + # get list of boxes intersecting the current ball + istart = query.leaves_near_ball_starts[iproxy] + iend = query.leaves_near_ball_starts[iproxy + 1] + iboxes = query.leaves_near_ball_lists[istart:iend] + + # get nodes inside the boxes + istart = tree.box_source_starts[iboxes] + iend = istart + tree.box_source_counts_cumul[iboxes] + isources = np.hstack([np.arange(s, e) + for s, e in zip(istart, iend)]) + nodes = np.vstack([tree.sources[idim][isources] + for idim in range(discr.ambient_dim)]) + isources = tree.user_source_ids[isources] + + # get nodes inside the ball but outside the current range + center = pxycenters[:, iproxy].reshape(-1, 1) + radius = pxyradii[iproxy] + mask = ((la.norm(nodes - center, axis=0) < radius) + & ((isources < indices.ranges[iproxy]) + | (indices.ranges[iproxy + 1] <= isources))) + + nbrindices[iproxy] = indices.indices[isources[mask]] + + nbrranges = actx.from_numpy(np.cumsum([0] + [n.shape[0] for n in nbrindices])) + nbrindices = actx.from_numpy(np.hstack(nbrindices)) + + return BlockIndexRanges(actx.context, + actx.freeze(nbrindices), actx.freeze(nbrranges)) + + +def gather_block_interaction_points(actx, places, source_dd, indices, + radius_factor=None, + approx_nproxy=None, + max_nodes_in_box=None): """Generate sets of interaction points for each given range of indices in the *source* discretization. For each input range of indices, the corresponding output range of points is consists of: @@ -583,7 +578,7 @@ def knl(): """, [ lp.GlobalArg("sources", None, - shape=(lpot_source.ambient_dim, "nsources")), + shape=(lpot_source.ambient_dim, "nsources"), dim_tags="sep,C"), lp.GlobalArg("proxies", None, shape=(lpot_source.ambient_dim, "nproxies"), dim_tags="sep,C"), lp.GlobalArg("nbrindices", None, @@ -607,28 +602,28 @@ def knl(): return loopy_knl lpot_source = places.get_geometry(source_dd.geometry) - with cl.CommandQueue(lpot_source.cl_context) as queue: - generator = ProxyGenerator(places, - radius_factor=radius_factor, - approx_nproxy=approx_nproxy) - proxies, pxyranges, pxycenters, pxyradii = \ - generator(queue, source_dd, indices) - - discr = places.get_discretization(source_dd.geometry, source_dd.discr_stage) - neighbors = gather_block_neighbor_points(discr, - indices, pxycenters, pxyradii, - max_nodes_in_box=max_nodes_in_box) - - ranges = cl.array.zeros(queue, indices.nblocks + 1, dtype=np.int) - _, (nodes, ranges) = knl()(queue, - sources=discr.nodes(), - proxies=proxies, - pxyranges=pxyranges, - nbrindices=neighbors.indices, - nbrranges=neighbors.ranges, - ranges=ranges) - - return nodes.with_queue(None), ranges.with_queue(None) + generator = ProxyGenerator(places, + radius_factor=radius_factor, + approx_nproxy=approx_nproxy) + proxies, pxyranges, pxycenters, pxyradii = \ + generator(actx, source_dd, indices) + + discr = places.get_discretization(source_dd.geometry, source_dd.discr_stage) + neighbors = gather_block_neighbor_points(actx, discr, + indices, pxycenters, pxyradii, + max_nodes_in_box=max_nodes_in_box) + + from meshmode.dof_array import flatten, thaw + ranges = actx.zeros(indices.nblocks + 1, dtype=np.int) + _, (nodes, ranges) = knl()(actx.queue, + sources=flatten(thaw(actx, discr.nodes())), + proxies=proxies, + pxyranges=pxyranges, + nbrindices=neighbors.indices, + nbrranges=neighbors.ranges, + ranges=ranges) + + return actx.freeze(nodes), actx.freeze(ranges) # }}} diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index 12ec489a4..3ac09c00c 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -32,7 +32,8 @@ from pytools import memoize_method from pytential.symbolic.mappers import EvaluationMapperBase -from pytential.utils import flatten_if_needed +from pytential.utils import ( + flatten_if_needed, flatten_to_numpy, unflatten_from_numpy) # {{{ helpers @@ -61,31 +62,6 @@ def _get_layer_potential_args(mapper, expr, include_args=None): return kernel_args - -def _unflatten_from_numpy(actx, ary, discr=None): - from pytools.obj_array import obj_array_vectorize - from meshmode.dof_array import unflatten - - if isinstance(ary, np.ndarray) and ary.dtype.char == "O": - ary = obj_array_vectorize(actx.from_numpy, ary) - else: - ary = actx.from_numpy(ary) - - if discr is None: - return ary - else: - return unflatten(actx, discr, ary) - - -def _flatten_to_numpy(actx, ary): - result = flatten_if_needed(actx, ary) - - from pytools.obj_array import obj_array_vectorize - if isinstance(result, np.ndarray) and ary.dtype.char == "O": - return obj_array_vectorize(actx.to_numpy, result) - else: - return actx.to_numpy(result) - # }}} @@ -229,16 +205,16 @@ def map_num_reference_derivative(self, expr): dofdesc=dofdesc) discr = self.places.get_discretization(dofdesc.geometry, dofdesc.discr_stage) - rec_operand = _unflatten_from_numpy(self.array_context, rec_operand, discr) + rec_operand = unflatten_from_numpy(self.array_context, rec_operand, discr) - return _flatten_to_numpy(self.array_context, + return flatten_to_numpy(self.array_context, bind(self.places, op)(self.array_context, u=rec_operand) ) def map_node_coordinate_component(self, expr): from pytential import bind, sym op = sym.NodeCoordinateComponent(expr.ambient_axis, dofdesc=expr.dofdesc) - return _flatten_to_numpy(self.array_context, + return flatten_to_numpy(self.array_context, bind(self.places, op)(self.array_context) ) @@ -253,9 +229,9 @@ def map_call(self, expr): if isinstance(rec_arg, Number): return getattr(np, expr.function.name)(rec_arg) else: - rec_arg = _unflatten_from_numpy(self.array_context, rec_arg) + rec_arg = unflatten_from_numpy(self.array_context, rec_arg) result = getattr(self.array_context.np, expr.function.name)(rec_arg) - return _flatten_to_numpy(self.array_context, result) + return flatten_to_numpy(self.array_context, result) # }}} @@ -353,8 +329,8 @@ def map_interpolation(self, expr): discr = self.places.get_discretization( expr.from_dd.geometry, expr.from_dd.discr_stage) - operand = _unflatten_from_numpy(actx, operand, discr) - return _flatten_to_numpy(actx, conn(operand)) + operand = unflatten_from_numpy(actx, operand, discr) + return flatten_to_numpy(actx, conn(operand)) elif isinstance(operand, np.ndarray) and operand.ndim == 2: cache = self.places._get_cache("direct_resampler") key = (expr.from_dd.geometry, diff --git a/pytential/utils.py b/pytential/utils.py index fb772c0fa..be84f4168 100644 --- a/pytential/utils.py +++ b/pytential/utils.py @@ -43,4 +43,29 @@ def flatten_if_needed(actx: PyOpenCLArrayContext, ary: np.ndarray): return flatten(ary) + +def unflatten_from_numpy(actx, ary, discr=None): + from pytools.obj_array import obj_array_vectorize + from meshmode.dof_array import unflatten + + if isinstance(ary, np.ndarray) and ary.dtype.char == "O": + ary = obj_array_vectorize(actx.from_numpy, ary) + else: + ary = actx.from_numpy(ary) + + if discr is None: + return ary + else: + return unflatten(actx, discr, ary) + + +def flatten_to_numpy(actx, ary): + result = flatten_if_needed(actx, ary) + + from pytools.obj_array import obj_array_vectorize + if isinstance(result, np.ndarray) and ary.dtype.char == "O": + return obj_array_vectorize(actx.to_numpy, result) + else: + return actx.to_numpy(result) + # vim: foldmethod=marker diff --git a/test/test_linalg_proxy.py b/test/test_linalg_proxy.py index a44872906..8e485d6e9 100644 --- a/test/test_linalg_proxy.py +++ b/test/test_linalg_proxy.py @@ -29,6 +29,8 @@ import pyopencl.array # noqa from pytential import bind, sym + +from meshmode.array_context import PyOpenCLArrayContext from meshmode.mesh.generation import ( # noqa ellipse, NArmedStarfish, generate_torus, make_curve_mesh) @@ -41,9 +43,9 @@ from test_matrix import _build_geometry, _build_block_index -def _plot_partition_indices(queue, discr, indices, **kwargs): +def _plot_partition_indices(actx, discr, indices, **kwargs): import matplotlib.pyplot as pt - indices = indices.get(queue) + indices = indices.get(actx.queue) args = [ kwargs.get("method", "unknown"), @@ -57,12 +59,13 @@ def _plot_partition_indices(queue, discr, indices, **kwargs): pt.savefig("test_partition_{0}_{1}_{3}d_ranges_{2}.png".format(*args)) pt.clf() + from pytential.utils import flatten_to_numpy if discr.ambient_dim == 2: - sources = discr.nodes().get(queue) + sources = flatten_to_numpy(actx, discr.nodes()) pt.figure(figsize=(10, 8), dpi=300) - if indices.indices.shape[0] != discr.nnodes: + if indices.indices.shape[0] != discr.ndofs: pt.plot(sources[0], sources[1], 'ko', alpha=0.5) for i in range(indices.nblocks): isrc = indices.block_indices(i) @@ -80,17 +83,20 @@ def _plot_partition_indices(queue, discr, indices, **kwargs): return from meshmode.discretization.visualization import make_visualizer - marker = -42.0 * np.ones(discr.nnodes) + marker = -42.0 * np.ones(discr.ndofs) for i in range(indices.nblocks): isrc = indices.block_indices(i) marker[isrc] = 10.0 * (i + 1.0) - vis = make_visualizer(queue, discr, 10) + from meshmode.dof_array import unflatten + marker = unflatten(actx, discr, actx.from_numpy(marker)) + + vis = make_visualizer(actx, discr, 10) - filename = "test_partition_{0}_{1}_{3}d_{2}.png".format(*args) + filename = "test_partition_{0}_{1}_{3}d_{2}.vtu".format(*args) vis.write_vtk_file(filename, [ - ("marker", cl.array.to_device(queue, marker)) + ("marker", marker) ]) @@ -99,12 +105,14 @@ def _plot_partition_indices(queue, discr, indices, **kwargs): def test_partition_points(ctx_factory, use_tree, ambient_dim, visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) + actx = PyOpenCLArrayContext(queue) - places, dofdesc = _build_geometry(queue, ambient_dim=ambient_dim) - _build_block_index(queue, - places.get_discretization(dofdesc.geometry, dofdesc.discr_stage), - use_tree=use_tree, - factor=0.6) + places, dofdesc = _build_geometry(actx, ambient_dim=ambient_dim) + discr = places.get_discretization(dofdesc.geometry, dofdesc.discr_stage) + indices = _build_block_index(actx, discr, use_tree=use_tree, factor=0.6) + + if visualize: + _plot_partition_indices(actx, discr, indices, use_tree=use_tree) @pytest.mark.parametrize("ambient_dim", [2, 3]) @@ -112,24 +120,23 @@ def test_partition_points(ctx_factory, use_tree, ambient_dim, visualize=False): def test_proxy_generator(ctx_factory, ambient_dim, factor, visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) + actx = PyOpenCLArrayContext(queue) - places, dofdesc = _build_geometry(queue, ambient_dim=ambient_dim) + places, dofdesc = _build_geometry(actx, ambient_dim=ambient_dim) dofdesc = dofdesc.to_stage1() density_discr = places.get_discretization(dofdesc.geometry, dofdesc.discr_stage) - srcindices = _build_block_index(queue, - density_discr, - factor=factor) + srcindices = _build_block_index(actx, density_discr, factor=factor) from pytential.linalg.proxy import ProxyGenerator generator = ProxyGenerator(places) proxies, pxyranges, pxycenters, pxyradii = \ - generator(queue, dofdesc, srcindices) + generator(actx, dofdesc, srcindices) - proxies = np.vstack([p.get() for p in proxies]) - pxyranges = pxyranges.get() - pxycenters = np.vstack([c.get() for c in pxycenters]) - pxyradii = pxyradii.get() + proxies = np.vstack([actx.to_numpy(p) for p in proxies]) + pxyranges = actx.to_numpy(pxyranges) + pxycenters = np.vstack([actx.to_numpy(c) for c in pxycenters]) + pxyradii = actx.to_numpy(pxyradii) for i in range(srcindices.nblocks): ipxy = np.s_[pxyranges[i]:pxyranges[i + 1]] @@ -142,12 +149,14 @@ def test_proxy_generator(ctx_factory, ambient_dim, factor, visualize=False): if ambient_dim == 2: import matplotlib.pyplot as pt - density_nodes = density_discr.nodes().get(queue) - ci = bind(places, sym.expansion_centers(ambient_dim, -1))(queue) - ci = np.vstack([c.get(queue) for c in ci]) - ce = bind(places, sym.expansion_centers(ambient_dim, +1))(queue) - ce = np.vstack([c.get(queue) for c in ce]) - r = bind(places, sym.expansion_radii(ambient_dim))(queue).get() + from pytential.utils import flatten_to_numpy + density_nodes = np.vstack(flatten_to_numpy(actx, density_discr.nodes())) + ci = bind(places, sym.expansion_centers(ambient_dim, -1))(actx) + ci = np.vstack(flatten_to_numpy(actx, ci)) + ce = bind(places, sym.expansion_centers(ambient_dim, +1))(actx) + ce = np.vstack(flatten_to_numpy(actx, ce)) + r = bind(places, sym.expansion_radii(ambient_dim))(actx) + r = flatten_to_numpy(actx, r) for i in range(srcindices.nblocks): isrc = srcindices.block_indices(i) @@ -195,10 +204,10 @@ def test_proxy_generator(ctx_factory, ambient_dim, factor, visualize=False): b=pxycenters[:, i].reshape(-1)) mesh = merge_disjoint_meshes([mesh, density_discr.mesh]) - discr = Discretization(ctx, mesh, + discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(10)) - vis = make_visualizer(queue, discr, 10) + vis = make_visualizer(actx, discr, 10) filename = "test_proxy_generator_{}d_{:04}.vtu".format( ambient_dim, i) vis.write_vtk_file(filename, []) @@ -209,26 +218,25 @@ def test_proxy_generator(ctx_factory, ambient_dim, factor, visualize=False): def test_interaction_points(ctx_factory, ambient_dim, factor, visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) + actx = PyOpenCLArrayContext(queue) - places, dofdesc = _build_geometry(queue, ambient_dim=ambient_dim) + places, dofdesc = _build_geometry(actx, ambient_dim=ambient_dim) dofdesc = dofdesc.to_stage1() density_discr = places.get_discretization(dofdesc.geometry, dofdesc.discr_stage) - srcindices = _build_block_index(queue, - density_discr, - factor=factor) + srcindices = _build_block_index(actx, density_discr, factor=factor) # generate proxy points from pytential.linalg.proxy import ProxyGenerator generator = ProxyGenerator(places) - _, _, pxycenters, pxyradii = generator(queue, dofdesc, srcindices) + _, _, pxycenters, pxyradii = generator(actx, dofdesc, srcindices) from pytential.linalg.proxy import ( # noqa gather_block_neighbor_points, gather_block_interaction_points) - nbrindices = gather_block_neighbor_points(density_discr, + nbrindices = gather_block_neighbor_points(actx, density_discr, srcindices, pxycenters, pxyradii) - nodes, ranges = gather_block_interaction_points( + nodes, ranges = gather_block_interaction_points(actx, places, dofdesc, srcindices) srcindices = srcindices.get(queue) @@ -240,12 +248,13 @@ def test_interaction_points(ctx_factory, ambient_dim, factor, visualize=False): assert not np.any(np.isin(inbr, isrc)) + from pytential.utils import flatten_to_numpy if visualize: if ambient_dim == 2: import matplotlib.pyplot as pt - density_nodes = density_discr.nodes().get(queue) - nodes = nodes.get(queue) - ranges = ranges.get(queue) + density_nodes = flatten_to_numpy(actx, density_discr.nodes()) + nodes = flatten_to_numpy(actx, nodes) + ranges = actx.to_numpy(ranges) for i in range(srcindices.nblocks): isrc = srcindices.block_indices(i) @@ -255,14 +264,14 @@ def test_interaction_points(ctx_factory, ambient_dim, factor, visualize=False): pt.figure(figsize=(10, 8)) pt.plot(density_nodes[0], density_nodes[1], 'ko', ms=2.0, alpha=0.5) - pt.plot(density_nodes[0, srcindices.indices], - density_nodes[1, srcindices.indices], + pt.plot(density_nodes[0][srcindices.indices], + density_nodes[1][srcindices.indices], 'o', ms=2.0) - pt.plot(density_nodes[0, isrc], density_nodes[1, isrc], + pt.plot(density_nodes[0][isrc], density_nodes[1][isrc], 'o', ms=2.0) - pt.plot(density_nodes[0, inbr], density_nodes[1, inbr], + pt.plot(density_nodes[0][inbr], density_nodes[1][inbr], 'o', ms=2.0) - pt.plot(nodes[0, iall], nodes[1, iall], + pt.plot(nodes[0][iall], nodes[1][iall], 'x', ms=2.0) pt.xlim([-1.5, 1.5]) pt.ylim([-1.5, 1.5]) @@ -272,7 +281,7 @@ def test_interaction_points(ctx_factory, ambient_dim, factor, visualize=False): pt.clf() elif ambient_dim == 3: from meshmode.discretization.visualization import make_visualizer - marker = np.empty(density_discr.nnodes) + marker = np.empty(density_discr.ndofs) for i in range(srcindices.nblocks): isrc = srcindices.block_indices(i) @@ -282,9 +291,11 @@ def test_interaction_points(ctx_factory, ambient_dim, factor, visualize=False): marker[srcindices.indices] = 0.0 marker[isrc] = -42.0 marker[inbr] = +42.0 - marker_dev = cl.array.to_device(queue, marker) - vis = make_visualizer(queue, density_discr, 10) + from meshmode.dof_array import unflatten + marker_dev = unflatten(actx, density_discr, actx.from_numpy(marker)) + + vis = make_visualizer(actx, density_discr, 10) filename = "test_area_query_{}d_{:04}.vtu".format(ambient_dim, i) vis.write_vtk_file(filename, [ ("marker", marker_dev), diff --git a/test/test_matrix.py b/test/test_matrix.py index 5de6780a1..70a79d15f 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -100,7 +100,7 @@ def _build_block_index(actx, discr, # create index ranges from pytential.linalg.proxy import partition_by_nodes - indices = partition_by_nodes(discr, + indices = partition_by_nodes(actx, discr, use_tree=use_tree, max_nodes_in_box=max_particles_in_box) @@ -264,7 +264,7 @@ def test_matrix_build(ctx_factory, k, curve_f, lpot_id, visualize=False): pt.colorbar() pt.show() - from pytential.symbolic.matrix import _unflatten_from_numpy, _flatten_to_numpy + from pytential.utils import unflatten_from_numpy, flatten_to_numpy np.random.seed(12) for i in range(5): if is_obj_array(u_sym): @@ -274,10 +274,10 @@ def test_matrix_build(ctx_factory, k, curve_f, lpot_id, visualize=False): ]) else: u = np.random.randn(density_discr.ndofs) - u_dev = _unflatten_from_numpy(actx, u, density_discr) + u_dev = unflatten_from_numpy(actx, u, density_discr) res_matvec = np.hstack( - _flatten_to_numpy(actx, bound_op(actx, u=u_dev)) + flatten_to_numpy(actx, bound_op(actx, u=u_dev)) ) res_mat = mat.dot(np.hstack(u)) @@ -375,7 +375,7 @@ def test_p2p_block_builder(ctx_factory, factor, ambient_dim, lpot_id, @pytest.mark.parametrize("ambient_dim", [2, 3]) @pytest.mark.parametrize("lpot_id", [1, 2]) def test_qbx_block_builder(ctx_factory, factor, ambient_dim, lpot_id, - visualize=False): + visualize=True): ctx = ctx_factory() queue = cl.CommandQueue(ctx) actx = PyOpenCLArrayContext(queue) From 87df5814872fd5ed32ee0d93ccada2b6101f06b9 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 23:03:59 -0500 Subject: [PATCH 30/75] flake8 fixes --- test/test_scalar_int_eq.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/test_scalar_int_eq.py b/test/test_scalar_int_eq.py index 285705505..15be85715 100644 --- a/test/test_scalar_int_eq.py +++ b/test/test_scalar_int_eq.py @@ -601,14 +601,13 @@ def run_int_eq_test(actx: PyOpenCLArrayContext, bind(places, sym.normal(2))(actx) .as_vector(np.object))] - import matplotlib.pyplot as pt pt.plot(nodes_h[0], nodes_h[1], "x-") pt.quiver(nodes_h[0], nodes_h[1], normal_h[0], normal_h[1]) pt.gca().set_aspect("equal") pt.show() elif mesh.ambient_dim == 3: bdry_normals = bind(places, sym.normal(3))( - queue).as_vector(dtype=object) + actx).as_vector(dtype=object) bdry_vis = make_visualizer(actx, density_discr, case.target_order+3) bdry_vis.write_vtk_file("pre-solve-source-%s.vtu" % resolution, [ @@ -872,7 +871,7 @@ def run_int_eq_test(actx: PyOpenCLArrayContext, fplot.write_vtk_file( "failed-targets.vts", [ - ("failed_targets", e.failed_target_flags.get(queue)) + ("failed_targets", actx.to_numpy(e.failed_target_flags)) ]) raise From 9b4ce76386731fee9c1518fae92a3f400c92b3d3 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 23:06:19 -0500 Subject: [PATCH 31/75] Thaw failed_target_flags after QBXTargetAssociationFailedException --- test/test_layer_pot.py | 3 ++- test/test_scalar_int_eq.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/test_layer_pot.py b/test/test_layer_pot.py index 5e63232d6..9fa844355 100644 --- a/test/test_layer_pot.py +++ b/test/test_layer_pot.py @@ -209,7 +209,8 @@ def test_off_surface_eval_vs_direct(ctx_factory, do_plot=False): auto_where=("direct_qbx", "target"))( actx, sigma=direct_sigma) except QBXTargetAssociationFailedException as e: - fplot.show_scalar_in_matplotlib(actx.to_numpy(e.failed_target_flags)) + fplot.show_scalar_in_matplotlib( + actx.to_numpy(actx.thaw(e.failed_target_flags))) import matplotlib.pyplot as pt pt.show() raise diff --git a/test/test_scalar_int_eq.py b/test/test_scalar_int_eq.py index 15be85715..0f482cf98 100644 --- a/test/test_scalar_int_eq.py +++ b/test/test_scalar_int_eq.py @@ -715,7 +715,7 @@ def run_int_eq_test(actx: PyOpenCLArrayContext, bdry_vis = make_visualizer(actx, density_discr, case.target_order+3) bdry_vis.write_vtk_file("failed-targets-%s.vtu" % resolution, [ - ("failed_targets", e.failed_target_flags), + ("failed_targets", actx.thaw(e.failed_target_flags)), ]) raise @@ -871,7 +871,8 @@ def run_int_eq_test(actx: PyOpenCLArrayContext, fplot.write_vtk_file( "failed-targets.vts", [ - ("failed_targets", actx.to_numpy(e.failed_target_flags)) + ("failed_targets", actx.to_numpy( + actx.thaw(e.failed_target_flags))) ]) raise From fee617b1d6465efc7ed673e83e28b160ec484e1c Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 23:34:22 -0500 Subject: [PATCH 32/75] Port test_maxwell.py --- test/test_maxwell.py | 71 ++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/test/test_maxwell.py b/test/test_maxwell.py index 129cd217e..86866fc45 100644 --- a/test/test_maxwell.py +++ b/test/test_maxwell.py @@ -31,6 +31,7 @@ from pytential import bind, sym, norm +from meshmode.array_context import PyOpenCLArrayContext from sumpy.visualization import make_field_plotter_from_bbox # noqa from sumpy.point_calculus import CalculusPatch, frequency_domain_maxwell from sumpy.tools import vector_from_device @@ -77,7 +78,7 @@ def get_observation_mesh(self, target_order): else: return generate_icosphere(0.5, target_order) - def get_source(self, queue): + def get_source(self, actx): if self.is_interior: source_ctr = np.array([[0.35, 0.1, 0.15]]).T else: @@ -87,9 +88,7 @@ def get_source(self, queue): sources = source_ctr + source_rad*2*(np.random.rand(3, 10)-0.5) from pytential.source import PointPotentialSource - return PointPotentialSource( - queue.context, - cl.array.to_device(queue, sources)) + return PointPotentialSource(actx.from_numpy(sources)) class RoundedCubeTestCase(MaxwellTestCase): @@ -121,7 +120,7 @@ def get_observation_mesh(self, target_order): else: return generate_icosphere(0.5, target_order) - def get_source(self, queue): + def get_source(self, actx): if self.is_interior: source_ctr = np.array([[0.35, 0.1, 0.15]]).T else: @@ -131,9 +130,7 @@ def get_source(self, queue): sources = source_ctr + source_rad*2*(np.random.rand(3, 10)-0.5) from pytential.source import PointPotentialSource - return PointPotentialSource( - queue.context, - cl.array.to_device(queue, sources)) + return PointPotentialSource(actx.from_numpy(sources)) class ElliptiPlaneTestCase(MaxwellTestCase): @@ -168,7 +165,7 @@ def get_observation_mesh(self, target_order): else: return generate_icosphere(0.5, target_order) - def get_source(self, queue): + def get_source(self, actx): if self.is_interior: source_ctr = np.array([[0.35, 0.1, 0.15]]).T else: @@ -178,9 +175,7 @@ def get_source(self, queue): sources = source_ctr + source_rad*2*(np.random.rand(3, 10)-0.5) from pytential.source import PointPotentialSource - return PointPotentialSource( - queue.context, - cl.array.to_device(queue, sources)) + return PointPotentialSource(actx.from_numpy(sources)) # }}} @@ -228,6 +223,7 @@ def test_pec_mfie_extinction(ctx_factory, case, cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) np.random.seed(12) @@ -245,13 +241,17 @@ def test_pec_mfie_extinction(ctx_factory, case, get_sym_maxwell_plane_wave) mfie = PECChargeCurrentMFIEOperator() - test_source = case.get_source(queue) + test_source = case.get_source(actx) calc_patch = CalculusPatch(np.array([-3, 0, 0]), h=0.01) - calc_patch_tgt = PointsTarget(cl.array.to_device(queue, calc_patch.points)) + calc_patch_tgt = PointsTarget(actx.from_numpy(calc_patch.points)) rng = cl.clrandom.PhiloxGenerator(cl_ctx, seed=12) - src_j = rng.normal(queue, (3, test_source.nnodes), dtype=np.float64) + from pytools.obj_array import make_obj_array + # FIXME: default_offset=lp.auto + src_j = make_obj_array([ + rng.normal(actx.queue, (test_source.ndofs), dtype=np.float64) + for i in range(3)]) def eval_inc_field_at(places, source=None, target=None): if source is None: @@ -264,12 +264,12 @@ def eval_inc_field_at(places, source=None, target=None): amplitude_vec=np.array([1, 1, 1]), v=np.array([1, 0, 0]), omega=case.k), - auto_where=target)(queue) + auto_where=target)(actx) else: # point source return bind(places, get_sym_maxwell_point_source(mfie.kernel, j_sym, mfie.k), - auto_where=(source, target))(queue, j=src_j, k=case.k) + auto_where=(source, target))(actx, j=src_j, k=case.k) # }}} @@ -294,7 +294,7 @@ def eval_inc_field_at(places, source=None, target=None): observation_mesh = case.get_observation_mesh(case.target_order) pre_scat_discr = Discretization( - cl_ctx, scat_mesh, + actx, scat_mesh, InterpolatoryQuadratureSimplexGroupFactory(case.target_order)) qbx = QBXLayerPotentialSource( pre_scat_discr, fine_order=4*case.target_order, @@ -306,7 +306,7 @@ def eval_inc_field_at(places, source=None, target=None): scat_discr = qbx.density_discr obs_discr = Discretization( - cl_ctx, observation_mesh, + actx, observation_mesh, InterpolatoryQuadratureSimplexGroupFactory(case.target_order)) places.update({ @@ -324,7 +324,7 @@ def eval_inc_field_at(places, source=None, target=None): fplot = make_field_plotter_from_bbox( find_bounding_box(scat_discr.mesh), h=(0.05, 0.05, 0.3), extend_factor=0.3) - fplot_tgt = PointsTarget(cl.array.to_device(queue, fplot.points)) + fplot_tgt = PointsTarget(actx.from_numpy(fplot.points)) places.update({ "qbx_target_tol": qbx_tgt_tol, @@ -337,9 +337,9 @@ def eval_inc_field_at(places, source=None, target=None): # {{{ system solve - h_max = bind(places, sym.h_max(qbx.ambient_dim))(queue) + h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) - pde_test_inc = EHField(vector_from_device(queue, + pde_test_inc = EHField(vector_from_device(actx.queue, eval_inc_field_at(places, target="patch_target"))) source_maxwell_resids = [ @@ -356,7 +356,7 @@ def eval_inc_field_at(places, source=None, target=None): bound_j_op = bind(places, mfie.j_operator(loc_sign, jt_sym)) j_rhs = bind(places, mfie.j_rhs(inc_xyz_sym.h))( - queue, inc_fld=inc_field_scat.field, **knl_kwargs) + actx, inc_fld=inc_field_scat.field, **knl_kwargs) gmres_settings = dict( tol=case.gmres_tol, @@ -365,24 +365,24 @@ def eval_inc_field_at(places, source=None, target=None): stall_iterations=50, no_progress_factor=1.05) from pytential.solve import gmres gmres_result = gmres( - bound_j_op.scipy_op(queue, "jt", np.complex128, **knl_kwargs), + bound_j_op.scipy_op(actx, "jt", np.complex128, **knl_kwargs), j_rhs, **gmres_settings) jt = gmres_result.solution bound_rho_op = bind(places, mfie.rho_operator(loc_sign, rho_sym)) rho_rhs = bind(places, mfie.rho_rhs(jt_sym, inc_xyz_sym.e))( - queue, jt=jt, inc_fld=inc_field_scat.field, **knl_kwargs) + actx, jt=jt, inc_fld=inc_field_scat.field, **knl_kwargs) gmres_result = gmres( - bound_rho_op.scipy_op(queue, "rho", np.complex128, **knl_kwargs), + bound_rho_op.scipy_op(actx, "rho", np.complex128, **knl_kwargs), rho_rhs, **gmres_settings) rho = gmres_result.solution # }}} - jxyz = bind(places, sym.tangential_to_xyz(jt_sym))(queue, jt=jt) + jxyz = bind(places, sym.tangential_to_xyz(jt_sym))(actx, jt=jt) # {{{ volume eval @@ -393,9 +393,9 @@ def eval_repr_at(tgt, source=None, target=None): source = sym.DEFAULT_SOURCE return bind(places, sym_repr, auto_where=(source, target))( - queue, jt=jt, rho=rho, **knl_kwargs) + actx, jt=jt, rho=rho, **knl_kwargs) - pde_test_repr = EHField(vector_from_device(queue, + pde_test_repr = EHField(vector_from_device(actx.queue, eval_repr_at(places, target="patch_target"))) maxwell_residuals = [ @@ -416,7 +416,7 @@ def eval_repr_at(tgt, source=None, target=None): pec_bc_h = sym.normal(3).as_vector().dot(bc_repr.h + inc_xyz_sym.h) eh_bc_values = bind(places, sym.flat_obj_array(pec_bc_e, pec_bc_h))( - queue, jt=jt, rho=rho, inc_fld=inc_field_scat.field, + actx, jt=jt, rho=rho, inc_fld=inc_field_scat.field, **knl_kwargs) def scat_norm(f): @@ -435,11 +435,11 @@ def scat_norm(f): if visualize: from meshmode.discretization.visualization import make_visualizer - bdry_vis = make_visualizer(queue, scat_discr, case.target_order+3) + bdry_vis = make_visualizer(actx, scat_discr, case.target_order+3) bdry_normals = bind(places, sym.normal(3, dofdesc="scat_discr") - )(queue).as_vector(dtype=object) + )(actx).as_vector(dtype=object) bdry_vis.write_vtk_file("source-%s.vtu" % resolution, [ ("j", jxyz), @@ -459,12 +459,13 @@ def scat_norm(f): fplot.write_vtk_file( "failed-targets.vts", [ - ("failed_targets", e.failed_target_flags.get(queue)) + ("failed_targets", actx.to_numpy( + actx.thaw(e.failed_target_flags))), ]) raise - fplot_repr = EHField(vector_from_device(queue, fplot_repr)) - fplot_inc = EHField(vector_from_device(queue, + fplot_repr = EHField(vector_from_device(actx.queue, fplot_repr)) + fplot_inc = EHField(vector_from_device(actx.queue, eval_inc_field_at(places, target="plot_targets"))) fplot.write_vtk_file( From 21d641d87305884c12964f2865b8b7f8c4798170 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 23:34:44 -0500 Subject: [PATCH 33/75] Add test_maxwell.py to setup.cfg tests --- pytential/source.py | 3 +++ setup.cfg | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pytential/source.py b/pytential/source.py index db2781dba..549176d6e 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -113,6 +113,9 @@ def real_dtype(self): def nnodes(self): return self._nodes.shape[-1] + # FIXME: replace + ndofs = nnodes + @property def complex_dtype(self): return { diff --git a/setup.cfg b/setup.cfg index 35cae4c5a..bfd27f3e0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,4 +9,4 @@ exclude= [tool:pytest] markers= slowtest: mark a test as slow -python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py test_layer_pot_eigenvalues.py test_layer_pot_identity.py test_layer_pot.py test_scalar_int_eq.py +python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py test_layer_pot_eigenvalues.py test_layer_pot_identity.py test_layer_pot.py test_scalar_int_eq.py test_maxwell.py From d4a85856f412ee52a4e817aedfeb4e559856dbbe Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 2 Jul 2020 23:36:15 -0500 Subject: [PATCH 34/75] port test_stokes to array-context --- test/test_matrix.py | 2 +- test/test_stokes.py | 59 ++++++++++++++++++++++++++------------------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/test/test_matrix.py b/test/test_matrix.py index 70a79d15f..2b24b29ef 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -31,7 +31,7 @@ import numpy.linalg as la import pyopencl as cl -import pyopencl.array # noqa +import pyopencl.array from pytools.obj_array import make_obj_array, is_obj_array diff --git a/test/test_stokes.py b/test/test_stokes.py index 19167efe1..090770e59 100644 --- a/test/test_stokes.py +++ b/test/test_stokes.py @@ -25,9 +25,10 @@ import numpy as np import pyopencl as cl -import pyopencl.clmath # noqa +import pyopencl.clmath import pytest +from meshmode.array_context import PyOpenCLArrayContext from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory @@ -46,7 +47,7 @@ def run_exterior_stokes_2d(ctx_factory, nelements, mesh_order=4, target_order=4, qbx_order=4, - fmm_order=10, mu=1, circle_rad=1.5, visualize=False): + fmm_order=False, mu=1, circle_rad=1.5, visualize=False): # This program tests an exterior Stokes flow in 2D using the # compound representation given in Hsiao & Kress, @@ -57,6 +58,7 @@ def run_exterior_stokes_2d(ctx_factory, nelements, cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) + actx = PyOpenCLArrayContext(queue) ovsmp_target_order = 4*target_order @@ -68,8 +70,7 @@ def run_exterior_stokes_2d(ctx_factory, nelements, lambda t: circle_rad * ellipse(1, t), np.linspace(0, 1, nelements+1), target_order) - coarse_density_discr = Discretization( - cl_ctx, mesh, + coarse_density_discr = Discretization(actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) from pytential.qbx import QBXLayerPotentialSource @@ -111,8 +112,8 @@ def outside_circle(test_points, radius): density_discr = places.get_discretization(sym.DEFAULT_SOURCE) - normal = bind(places, sym.normal(2).as_vector())(queue) - path_length = bind(places, sym.integral(2, 1, 1))(queue) + normal = bind(places, sym.normal(2).as_vector())(actx) + path_length = bind(places, sym.integral(2, 1, 1))(actx) # }}} @@ -150,47 +151,52 @@ def outside_circle(test_points, radius): def fund_soln(x, y, loc, strength): #with direction (1,0) for point source - r = cl.clmath.sqrt((x - loc[0])**2 + (y - loc[1])**2) + r = actx.np.sqrt((x - loc[0])**2 + (y - loc[1])**2) scaling = strength/(4*np.pi*mu) - xcomp = (-cl.clmath.log(r) + (x - loc[0])**2/r**2) * scaling + xcomp = (-actx.np.log(r) + (x - loc[0])**2/r**2) * scaling ycomp = ((x - loc[0])*(y - loc[1])/r**2) * scaling return [xcomp, ycomp] def rotlet_soln(x, y, loc): - r = cl.clmath.sqrt((x - loc[0])**2 + (y - loc[1])**2) + r = actx.np.sqrt((x - loc[0])**2 + (y - loc[1])**2) xcomp = -(y - loc[1])/r**2 ycomp = (x - loc[0])/r**2 return [xcomp, ycomp] def fund_and_rot_soln(x, y, loc, strength): #with direction (1,0) for point source - r = cl.clmath.sqrt((x - loc[0])**2 + (y - loc[1])**2) + r = actx.np.sqrt((x - loc[0])**2 + (y - loc[1])**2) scaling = strength/(4*np.pi*mu) xcomp = ( - (-cl.clmath.log(r) + (x - loc[0])**2/r**2) * scaling + (-actx.np.log(r) + (x - loc[0])**2/r**2) * scaling - (y - loc[1])*strength*0.125/r**2 + 3.3) ycomp = ( ((x - loc[0])*(y - loc[1])/r**2) * scaling + (x - loc[0])*strength*0.125/r**2 + 1.5) - return [xcomp, ycomp] + return make_obj_array([xcomp, ycomp]) - nodes = density_discr.nodes().with_queue(queue) + from meshmode.dof_array import unflatten, flatten, thaw + nodes = flatten(thaw(actx, density_discr.nodes())) fund_soln_loc = np.array([0.5, -0.2]) strength = 100. - bc = fund_and_rot_soln(nodes[0], nodes[1], fund_soln_loc, strength) + bc = unflatten(actx, density_discr, + fund_and_rot_soln(nodes[0], nodes[1], fund_soln_loc, strength)) omega_sym = sym.make_sym_vector("omega", dim) u_A_sym_bdry = stokeslet_obj.apply( # noqa: N806 omega_sym, mu_sym, qbx_forced_limit=1) - omega = [ - cl.array.to_device(queue, (strength/path_length)*np.ones(len(nodes[0]))), - cl.array.to_device(queue, np.zeros(len(nodes[0])))] + from pytential.utils import unflatten_from_numpy + omega = unflatten_from_numpy(actx, make_obj_array([ + (strength/path_length)*np.ones(len(nodes[0])), + np.zeros(len(nodes[0])) + ]), density_discr) + bvp_rhs = bind(places, - sym.make_sym_vector("bc", dim) + u_A_sym_bdry)(queue, + sym.make_sym_vector("bc", dim) + u_A_sym_bdry)(actx, bc=bc, mu=mu, omega=omega) gmres_result = gmres( - bound_op.scipy_op(queue, "sigma", np.float64, mu=mu, normal=normal), + bound_op.scipy_op(actx, "sigma", np.float64, mu=mu, normal=normal), bvp_rhs, x0=bvp_rhs, tol=1e-9, progress=True, @@ -203,7 +209,7 @@ def fund_and_rot_soln(x, y, loc, strength): sigma = gmres_result.solution sigma_int_val_sym = sym.make_sym_vector("sigma_int_val", 2) - int_val = bind(places, sym.integral(2, 1, sigma_sym))(queue, sigma=sigma) + int_val = bind(places, sym.integral(2, 1, sigma_sym))(actx, sigma=sigma) int_val = -int_val/(2 * np.pi) print("int_val = ", int_val) @@ -217,7 +223,7 @@ def fund_and_rot_soln(x, y, loc, strength): - u_A_sym_vol + sigma_int_val_sym) where = (sym.DEFAULT_SOURCE, "point_target") - vel = bind(places, representation_sym, auto_where=where)(queue, + vel = bind(places, representation_sym, auto_where=where)(actx, sigma=sigma, mu=mu, normal=normal, @@ -226,7 +232,7 @@ def fund_and_rot_soln(x, y, loc, strength): print("@@@@@@@@") plot_vel = bind(places, representation_sym, - auto_where=(sym.DEFAULT_SOURCE, "plot_target"))(queue, + auto_where=(sym.DEFAULT_SOURCE, "plot_target"))(actx, sigma=sigma, mu=mu, normal=normal, @@ -240,8 +246,10 @@ def get_obj_array(obj_array): ]) exact_soln = fund_and_rot_soln( - cl.array.to_device(queue, eval_points[0]), cl.array.to_device( - queue, eval_points[1]), fund_soln_loc, strength) + actx.from_numpy(eval_points[0]), + actx.from_numpy(eval_points[1]), + fund_soln_loc, + strength) vel = get_obj_array(vel) err = vel-get_obj_array(exact_soln) @@ -289,7 +297,7 @@ def get_obj_array(obj_array): # }}} - h_max = bind(places, sym.h_max(qbx.ambient_dim))(queue) + h_max = bind(places, sym.h_max(qbx.ambient_dim))(actx) return h_max, l2_err @@ -301,6 +309,7 @@ def test_exterior_stokes_2d(ctx_factory, qbx_order=3): for nelements in [20, 50]: h_max, l2_err = run_exterior_stokes_2d(ctx_factory, nelements) eoc_rec.add_data_point(h_max, l2_err) + break print(eoc_rec) assert eoc_rec.order_estimate() >= qbx_order - 1 From 16b103d41db48423355e4d085341736a95d5c7c2 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 2 Jul 2020 23:45:18 -0500 Subject: [PATCH 35/75] enable tests in setup.cfg --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 34699863c..335a4a034 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,4 +9,4 @@ exclude= [tool:pytest] markers= slowtest: mark a test as slow -python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py test_layer_pot_eigenvalues.py test_layer_pot_identity.py test_layer_pot.py +python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py test_layer_pot_eigenvalues.py test_layer_pot_identity.py test_layer_pot.py test_linalg_proxy.py test_matrix.py test_stokes.py From 213ee646c5db073281c33315d7fca36be54b3cca Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 00:18:02 -0500 Subject: [PATCH 36/75] Allow tests marked as slowtest to run --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f9828212e..b6f4b2233 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,7 +8,7 @@ Python 3 POCL: script: - export PY_EXE=python3 - export PYOPENCL_TEST=portable:pthread - - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} + - # export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} # FIXME: Uncomment - export EXTRA_INSTALL="Cython pybind11 numpy scipy mako" - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh - ". ./build-and-test-py-project.sh" From b57224c42a99cb2f1c2c1241f2ea1a6463532959 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 00:20:25 -0500 Subject: [PATCH 37/75] Fix yaml syntax --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b6f4b2233..a9436d5b5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,7 +8,7 @@ Python 3 POCL: script: - export PY_EXE=python3 - export PYOPENCL_TEST=portable:pthread - - # export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} # FIXME: Uncomment + - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS} # ${PYTEST_ADDOPTS:--k-slowtest} # FIXME: Uncomment - export EXTRA_INSTALL="Cython pybind11 numpy scipy mako" - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh - ". ./build-and-test-py-project.sh" From 030c7c00e92bb467365dca715cee84152118bcd4 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 13:36:16 -0500 Subject: [PATCH 38/75] Document default_vdot() --- pytential/solve.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pytential/solve.py b/pytential/solve.py index 76d7958e5..3a717c67d 100644 --- a/pytential/solve.py +++ b/pytential/solve.py @@ -42,6 +42,8 @@ def default_vdot(x, y): + # vdot() implementation that is aware of scalars and host or + # PyOpenCL arrays. It also recurses down nested object arrays. if (isinstance(x, Number) or (isinstance(x, np.ndarray) and x.dtype.char != "O")): return np.vdot(x, y) @@ -240,11 +242,8 @@ def norm(x): # {{{ progress reporting class ResidualPrinter: - def __init__(self, inner_product=None): + def __init__(self, inner_product=default_vdot): self.count = 0 - if inner_product is None: - inner_product = np.vdot - self.inner_product = inner_product def __call__(self, resid): From af7ff8c93340b3aaa72b92ff1a0921206a5f3bbd Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 13:48:37 -0500 Subject: [PATCH 39/75] Improve the documentation in MatVecOp --- pytential/symbolic/execution.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pytential/symbolic/execution.py b/pytential/symbolic/execution.py index e46f75095..11b96566e 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -389,7 +389,11 @@ def get_modeled_cost(self): class MatVecOp(object): """A :class:`scipy.sparse.linalg.LinearOperator` work-alike. Exposes a :mod:`pytential` operator as a generic matrix operation, - i.e. given :math:`x`, compute :math:`Ax`. + i.e., given :math:`x`, compute :math:`Ax`. + + .. attribute:: shape + .. attribute:: dtype + .. automethod:: matvec """ def __init__(self, @@ -413,6 +417,8 @@ def _operator_uses_obj_array(self): return len(self.discrs) > 1 def flatten(self, ary): + # Return a flat version of *ary*. The returned value is suitable for + # use with solvers whose API expects a one-dimensional array. if not self._operator_uses_obj_array: ary = [ary] @@ -423,6 +429,7 @@ def flatten(self, ary): return result def unflatten(self, ary): + # Convert a flat version of *ary* into a structured version. components = [] for discr, (start, end) in zip(self.discrs, self.starts_and_ends): component = ary[start:end] From 0760994ca86725a0518af9b0a487d8b87b933f40 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 14:27:12 -0500 Subject: [PATCH 40/75] Remove unneeded FIXME --- test/test_global_qbx.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_global_qbx.py b/test/test_global_qbx.py index 6ea108ab2..53fc2e4ab 100644 --- a/test/test_global_qbx.py +++ b/test/test_global_qbx.py @@ -158,8 +158,7 @@ def run_source_refinement_test(ctx_factory, mesh, order, bind(places, sym._source_danger_zone_radii( lpot_source.ambient_dim, dofdesc=dd.to_stage2()))(actx)) - # FIXME: Why does _quad_resolution() return a host array? - quad_res = flatten( + quad_res = dof_array_to_numpy(actx, bind(places, sym._quad_resolution( lpot_source.ambient_dim, dofdesc=dd))(actx)) From 37e9404ae7c6cdf9fda299b16bbff5bda0cd1322 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 3 Jul 2020 07:56:05 -0500 Subject: [PATCH 41/75] remove stray debugging remnants --- test/test_matrix.py | 2 +- test/test_stokes.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_matrix.py b/test/test_matrix.py index 2b24b29ef..3c721760d 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -375,7 +375,7 @@ def test_p2p_block_builder(ctx_factory, factor, ambient_dim, lpot_id, @pytest.mark.parametrize("ambient_dim", [2, 3]) @pytest.mark.parametrize("lpot_id", [1, 2]) def test_qbx_block_builder(ctx_factory, factor, ambient_dim, lpot_id, - visualize=True): + visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) actx = PyOpenCLArrayContext(queue) diff --git a/test/test_stokes.py b/test/test_stokes.py index 090770e59..be2d20147 100644 --- a/test/test_stokes.py +++ b/test/test_stokes.py @@ -309,7 +309,6 @@ def test_exterior_stokes_2d(ctx_factory, qbx_order=3): for nelements in [20, 50]: h_max, l2_err = run_exterior_stokes_2d(ctx_factory, nelements) eoc_rec.add_data_point(h_max, l2_err) - break print(eoc_rec) assert eoc_rec.order_estimate() >= qbx_order - 1 From b5f1e9de98080880f9f751f28bb1a2629714469d Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 16:34:52 -0500 Subject: [PATCH 42/75] Remove another FIXME --- test/test_maxwell.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_maxwell.py b/test/test_maxwell.py index 86866fc45..0fc67b0bf 100644 --- a/test/test_maxwell.py +++ b/test/test_maxwell.py @@ -248,10 +248,9 @@ def test_pec_mfie_extinction(ctx_factory, case, rng = cl.clrandom.PhiloxGenerator(cl_ctx, seed=12) from pytools.obj_array import make_obj_array - # FIXME: default_offset=lp.auto src_j = make_obj_array([ rng.normal(actx.queue, (test_source.ndofs), dtype=np.float64) - for i in range(3)]) + for _ in range(3)]) def eval_inc_field_at(places, source=None, target=None): if source is None: From 740cd158e666c254721363fb08c9f42540878a6a Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 3 Jul 2020 23:53:11 +0200 Subject: [PATCH 43/75] Apply 1 suggestion(s) to 1 file(s) --- pytential/utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pytential/utils.py b/pytential/utils.py index be84f4168..599fec5cc 100644 --- a/pytential/utils.py +++ b/pytential/utils.py @@ -48,10 +48,7 @@ def unflatten_from_numpy(actx, ary, discr=None): from pytools.obj_array import obj_array_vectorize from meshmode.dof_array import unflatten - if isinstance(ary, np.ndarray) and ary.dtype.char == "O": - ary = obj_array_vectorize(actx.from_numpy, ary) - else: - ary = actx.from_numpy(ary) + ary = obj_array_vectorize(actx.from_numpy, ary) if discr is None: return ary From 67b122838db19f35137c319a9253dfc16719a959 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 3 Jul 2020 23:53:17 +0200 Subject: [PATCH 44/75] Apply 1 suggestion(s) to 1 file(s) --- pytential/utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pytential/utils.py b/pytential/utils.py index 599fec5cc..393e41d9b 100644 --- a/pytential/utils.py +++ b/pytential/utils.py @@ -60,9 +60,6 @@ def flatten_to_numpy(actx, ary): result = flatten_if_needed(actx, ary) from pytools.obj_array import obj_array_vectorize - if isinstance(result, np.ndarray) and ary.dtype.char == "O": - return obj_array_vectorize(actx.to_numpy, result) - else: - return actx.to_numpy(result) + return obj_array_vectorize(actx.to_numpy, result) # vim: foldmethod=marker From c09cfb9c5ad286250d884daf08f409160db75fd6 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 3 Jul 2020 23:53:25 +0200 Subject: [PATCH 45/75] Apply 1 suggestion(s) to 1 file(s) --- test/test_stokes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_stokes.py b/test/test_stokes.py index be2d20147..27563ef90 100644 --- a/test/test_stokes.py +++ b/test/test_stokes.py @@ -25,7 +25,6 @@ import numpy as np import pyopencl as cl -import pyopencl.clmath import pytest from meshmode.array_context import PyOpenCLArrayContext From 94a220d490053f7f089db46417e2817fbd04bf65 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 3 Jul 2020 18:29:16 -0500 Subject: [PATCH 46/75] switch argument order in unflatten_to_numpy --- pytential/symbolic/matrix.py | 6 +++--- pytential/utils.py | 3 +-- test/test_matrix.py | 2 +- test/test_stokes.py | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index 3ac09c00c..dc0eb3734 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -205,7 +205,7 @@ def map_num_reference_derivative(self, expr): dofdesc=dofdesc) discr = self.places.get_discretization(dofdesc.geometry, dofdesc.discr_stage) - rec_operand = unflatten_from_numpy(self.array_context, rec_operand, discr) + rec_operand = unflatten_from_numpy(self.array_context, discr, rec_operand) return flatten_to_numpy(self.array_context, bind(self.places, op)(self.array_context, u=rec_operand) @@ -229,7 +229,7 @@ def map_call(self, expr): if isinstance(rec_arg, Number): return getattr(np, expr.function.name)(rec_arg) else: - rec_arg = unflatten_from_numpy(self.array_context, rec_arg) + rec_arg = unflatten_from_numpy(self.array_context, None, rec_arg) result = getattr(self.array_context.np, expr.function.name)(rec_arg) return flatten_to_numpy(self.array_context, result) @@ -329,7 +329,7 @@ def map_interpolation(self, expr): discr = self.places.get_discretization( expr.from_dd.geometry, expr.from_dd.discr_stage) - operand = unflatten_from_numpy(actx, operand, discr) + operand = unflatten_from_numpy(actx, discr, operand) return flatten_to_numpy(actx, conn(operand)) elif isinstance(operand, np.ndarray) and operand.ndim == 2: cache = self.places._get_cache("direct_resampler") diff --git a/pytential/utils.py b/pytential/utils.py index 393e41d9b..f1e9f0d10 100644 --- a/pytential/utils.py +++ b/pytential/utils.py @@ -44,12 +44,11 @@ def flatten_if_needed(actx: PyOpenCLArrayContext, ary: np.ndarray): return flatten(ary) -def unflatten_from_numpy(actx, ary, discr=None): +def unflatten_from_numpy(actx, discr, ary): from pytools.obj_array import obj_array_vectorize from meshmode.dof_array import unflatten ary = obj_array_vectorize(actx.from_numpy, ary) - if discr is None: return ary else: diff --git a/test/test_matrix.py b/test/test_matrix.py index 3c721760d..ce429642d 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -274,7 +274,7 @@ def test_matrix_build(ctx_factory, k, curve_f, lpot_id, visualize=False): ]) else: u = np.random.randn(density_discr.ndofs) - u_dev = unflatten_from_numpy(actx, u, density_discr) + u_dev = unflatten_from_numpy(actx, density_discr, u) res_matvec = np.hstack( flatten_to_numpy(actx, bound_op(actx, u=u_dev)) diff --git a/test/test_stokes.py b/test/test_stokes.py index 27563ef90..ec7282a03 100644 --- a/test/test_stokes.py +++ b/test/test_stokes.py @@ -186,10 +186,10 @@ def fund_and_rot_soln(x, y, loc, strength): omega_sym, mu_sym, qbx_forced_limit=1) from pytential.utils import unflatten_from_numpy - omega = unflatten_from_numpy(actx, make_obj_array([ + omega = unflatten_from_numpy(actx, density_discr, make_obj_array([ (strength/path_length)*np.ones(len(nodes[0])), np.zeros(len(nodes[0])) - ]), density_discr) + ])) bvp_rhs = bind(places, sym.make_sym_vector("bc", dim) + u_A_sym_bdry)(actx, From 6b50ccf93c8387d3d1d23981a3f9fde6c4121010 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 19:05:41 -0500 Subject: [PATCH 47/75] Address more FIXMEs --- pytential/linalg/proxy.py | 10 +++++----- pytential/qbx/__init__.py | 2 +- pytential/qbx/refinement.py | 14 ++++++-------- pytential/source.py | 12 +++++------- pytential/symbolic/matrix.py | 6 +++--- pytential/target.py | 11 ++++------- test/test_cost_model.py | 6 +++--- test/test_linalg_proxy.py | 6 +++--- test/test_matrix.py | 10 +++++----- test/too_slow_test_helmholtz.py | 2 +- 10 files changed, 36 insertions(+), 43 deletions(-) diff --git a/pytential/linalg/proxy.py b/pytential/linalg/proxy.py index ba2e2ea96..631a74fa9 100644 --- a/pytential/linalg/proxy.py +++ b/pytential/linalg/proxy.py @@ -106,12 +106,12 @@ def partition_by_nodes(discr, np.cumsum([0] + [box.shape[0] for box in indices])) indices = to_device(queue, np.hstack(indices)) else: - indices = cl.array.arange(queue, 0, discr.nnodes, + indices = cl.array.arange(queue, 0, discr.ndofs, dtype=np.int) - ranges = cl.array.arange(queue, 0, discr.nnodes + 1, - discr.nnodes // max_nodes_in_box, + ranges = cl.array.arange(queue, 0, discr.ndofs + 1, + discr.ndofs // max_nodes_in_box, dtype=np.int) - assert ranges[-1] == discr.nnodes + assert ranges[-1] == discr.ndofs return BlockIndexRanges(discr.cl_context, indices.with_queue(None), @@ -148,7 +148,7 @@ def partition_from_coarse(resampler, from_indices): from_grp_ranges = np.cumsum( [0] + [grp.nelements for grp in from_discr.mesh.groups]) from_el_ranges = np.hstack([ - np.arange(grp.node_nr_base, grp.nnodes + 1, grp.nunit_nodes) + np.arange(grp.node_nr_base, grp.ndofs + 1, grp.nunit_nodes) for grp in from_discr.groups]) # construct coarse element arrays in each from_range diff --git a/pytential/qbx/__init__.py b/pytential/qbx/__init__.py index 5a9ddf7e9..db87e79ac 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -829,7 +829,7 @@ def exec_compute_potential_insn_direct(self, actx, insn, bound_expr, evaluate, if (o.qbx_forced_limit is not None and abs(o.qbx_forced_limit) == 1 - and qbx_tgt_count < target_discr.nnodes): + and qbx_tgt_count < target_discr.ndofs): raise RuntimeError("Did not find a matching QBX center " "for some targets") diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index af48b2121..78f329867 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -465,7 +465,6 @@ class RefinerNotConvergedWarning(UserWarning): def make_empty_refine_flags(queue, density_discr): - # FIXME: queue => actx """Return an array on the device suitable for use as element refine flags. :arg queue: An instance of :class:`pyopencl.CommandQueue`. @@ -500,9 +499,8 @@ def _warn_max_iterations(violated_criteria, expansion_disturbance_tolerance): RefinerNotConvergedWarning) -def _visualize_refinement(queue, discr, +def _visualize_refinement(actx: PyOpenCLArrayContext, discr, niter, stage_nr, stage_name, flags, visualize=False): - # FIXME: queue => actx if not visualize: return @@ -514,18 +512,18 @@ def _visualize_refinement(queue, discr, stage_name, np.sum(flags), discr.mesh.nelements, stage_nr) from meshmode.discretization.visualization import make_visualizer - vis = make_visualizer(queue, discr, 3) + vis = make_visualizer(actx, discr, 3) assert len(flags) == discr.mesh.nelements flags = flags.astype(np.bool) - nodes_flags = np.zeros(discr.nnodes) + nodes_flags = np.zeros(discr.ndofs) for grp in discr.groups: meg = grp.mesh_el_group grp.view(nodes_flags)[ flags[meg.element_nr_base:meg.nelements+meg.element_nr_base]] = 1 - nodes_flags = cl.array.to_device(queue, nodes_flags) + nodes_flags = actx.from_numpy(nodes_flags) vis_data = [ ("refine_flags", nodes_flags), ] @@ -533,7 +531,7 @@ def _visualize_refinement(queue, discr, if 0: from pytential import sym, bind bdry_normals = bind(discr, sym.normal(discr.ambient_dim))( - queue).as_vector(dtype=object) + actx).as_vector(dtype=object) vis_data.append(("bdry_normals", bdry_normals),) vis.write_vtk_file("refinement-%s-%03d.vtu" % (stage_name, niter), vis_data) @@ -630,7 +628,7 @@ def _refine_qbx_stage1(lpot_source, density_discr, if violates_kernel_length_scale: iter_violated_criteria.append("kernel length scale") - _visualize_refinement(wrangler.queue, stage1_density_discr, + _visualize_refinement(actx, stage1_density_discr, niter, 1, "kernel-length-scale", refine_flags, visualize=visualize) diff --git a/pytential/source.py b/pytential/source.py index 549176d6e..62ed25633 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -83,9 +83,9 @@ class PointPotentialSource(PotentialSource): """ .. attribute:: nodes - An :class:`pyopencl.array.Array` of shape ``[ambient_dim, nnodes]``. + An :class:`pyopencl.array.Array` of shape ``[ambient_dim, ndofs]``. - .. attribute:: nnodes + .. attribute:: ndofs .. automethod:: cost_model_compute_potential_insn .. automethod:: exec_compute_potential_insn @@ -110,11 +110,9 @@ def real_dtype(self): return self._nodes.dtype @property - def nnodes(self): - return self._nodes.shape[-1] - - # FIXME: replace - ndofs = nnodes + def ndofs(self): + for coord_ary in self._nodes: + return coord_ary.shape[0] @property def complex_dtype(self): diff --git a/pytential/symbolic/matrix.py b/pytential/symbolic/matrix.py index d63f30549..4fa07091c 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -94,7 +94,7 @@ def __init__(self, queue, dep_expr, other_dep_exprs, # {{{ def get_dep_variable(self): - return np.eye(self.dep_discr.nnodes, dtype=np.float64) + return np.eye(self.dep_discr.ndofs, dtype=np.float64) def is_kind_vector(self, x): return len(x.shape) == 1 @@ -434,7 +434,7 @@ def map_int_g(self, expr): expr, include_args=kernel_args) if self.exclude_self: kernel_args["target_to_source"] = \ - cl.array.arange(self.queue, 0, target_discr.nnodes, dtype=np.int) + cl.array.arange(self.queue, 0, target_discr.ndofs, dtype=np.int) from sumpy.p2p import P2PMatrixGenerator mat_gen = P2PMatrixGenerator( @@ -562,7 +562,7 @@ def map_int_g(self, expr): expr, include_args=kernel_args) if self.exclude_self: kernel_args["target_to_source"] = \ - cl.array.arange(self.queue, 0, target_discr.nnodes, dtype=np.int) + cl.array.arange(self.queue, 0, target_discr.ndofs, dtype=np.int) from sumpy.p2p import P2PMatrixBlockGenerator mat_gen = P2PMatrixBlockGenerator( diff --git a/pytential/target.py b/pytential/target.py index fc5dc9bf9..98f876b51 100644 --- a/pytential/target.py +++ b/pytential/target.py @@ -41,8 +41,8 @@ class TargetBase(object): .. attribute:: ambient_dim .. method:: nodes - Shape: ``[ambient_dim, nnodes]`` - .. attribute:: nnodes + Shape: ``[ambient_dim, ndofs]`` + .. attribute:: ndofs """ @@ -63,17 +63,14 @@ def preprocess_optemplate(self, name, discretizations, expr): return expr def nodes(self): - """Shape: ``[ambient_dim, nnodes]`` + """Shape: ``[ambient_dim, ndofs]`` """ return self._nodes @property - def nnodes(self): + def ndofs(self): for coord_ary in self._nodes: return coord_ary.shape[0] - # FIXME: Rename - ndofs = nnodes - # vim: foldmethod=marker diff --git a/test/test_cost_model.py b/test/test_cost_model.py index e99ce8672..42e03b2c4 100644 --- a/test/test_cost_model.py +++ b/test/test_cost_model.py @@ -493,15 +493,15 @@ def test_cost_model_correctness(ctx_factory, dim, off_surface, quad_stage2_density_discr = places.get_discretization( source_dd.geometry, sym.QBX_SOURCE_QUAD_STAGE2) - nnodes = quad_stage2_density_discr.ndofs - src_weights = np.ones(nnodes) + ndofs = quad_stage2_density_discr.ndofs + src_weights = np.ones(ndofs) timing_data = {} potential = drive_fmm(wrangler, src_weights, timing_data, traversal=wrangler.trav)[0][geo_data.ncenters:] # Check constant one wrangler for correctness. - assert (potential == nnodes).all() + assert (potential == ndofs).all() modeled_time = cost_S.get_predicted_times(merge_close_lists=True) diff --git a/test/test_linalg_proxy.py b/test/test_linalg_proxy.py index a44872906..d4a428dc5 100644 --- a/test/test_linalg_proxy.py +++ b/test/test_linalg_proxy.py @@ -62,7 +62,7 @@ def _plot_partition_indices(queue, discr, indices, **kwargs): pt.figure(figsize=(10, 8), dpi=300) - if indices.indices.shape[0] != discr.nnodes: + if indices.indices.shape[0] != discr.ndofs: pt.plot(sources[0], sources[1], 'ko', alpha=0.5) for i in range(indices.nblocks): isrc = indices.block_indices(i) @@ -80,7 +80,7 @@ def _plot_partition_indices(queue, discr, indices, **kwargs): return from meshmode.discretization.visualization import make_visualizer - marker = -42.0 * np.ones(discr.nnodes) + marker = -42.0 * np.ones(discr.ndofs) for i in range(indices.nblocks): isrc = indices.block_indices(i) @@ -272,7 +272,7 @@ def test_interaction_points(ctx_factory, ambient_dim, factor, visualize=False): pt.clf() elif ambient_dim == 3: from meshmode.discretization.visualization import make_visualizer - marker = np.empty(density_discr.nnodes) + marker = np.empty(density_discr.ndofs) for i in range(srcindices.nblocks): isrc = srcindices.block_indices(i) diff --git a/test/test_matrix.py b/test/test_matrix.py index 12be496c5..304848659 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -97,8 +97,8 @@ def _build_block_index(queue, nblks=10, factor=1.0, use_tree=True): - nnodes = discr.nnodes - max_particles_in_box = nnodes // nblks + ndofs = discr.ndofs + max_particles_in_box = ndofs // nblks # create index ranges from pytential.linalg.proxy import partition_by_nodes @@ -272,11 +272,11 @@ def test_matrix_build(ctx_factory, k, curve_f, lpot_id, visualize=False): for i in range(5): if is_obj_array(u_sym): u = make_obj_array([ - np.random.randn(density_discr.nnodes) + np.random.randn(density_discr.ndofs) for _ in range(len(u_sym)) ]) else: - u = np.random.randn(density_discr.nnodes) + u = np.random.randn(density_discr.ndofs) u_dev = vector_to_device(queue, u) res_matvec = np.hstack( @@ -521,7 +521,7 @@ def test_build_matrix_places(ctx_factory, context={}) p2p_mat = mbuilder(op) - assert p2p_mat.shape == (target_discr.nnodes, source_discr.nnodes) + assert p2p_mat.shape == (target_discr.ndofs, source_discr.ndofs) # build block qbx and p2p matrices from pytential.symbolic.matrix import NearFieldBlockBuilder diff --git a/test/too_slow_test_helmholtz.py b/test/too_slow_test_helmholtz.py index 9e9df21aa..6f858eaa8 100644 --- a/test/too_slow_test_helmholtz.py +++ b/test/too_slow_test_helmholtz.py @@ -370,7 +370,7 @@ def field_kind_to_string(field_kind): i_field += 0 from sumpy.kernel import LaplaceKernel - ones = (cl.array.empty(queue, (density_discr.nnodes,), dtype=np.float64) + ones = (cl.array.empty(queue, (density_discr.ndofs,), dtype=np.float64) .fill(1)) ind_func = - bind(places, sym.D(LaplaceKernel(2), sym.var("u")), auto_where=("qbx-low-order", "targets-plot"))( From 9303cc21232079679f2d96861016b7a0cdb89aab Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 19:06:25 -0500 Subject: [PATCH 48/75] Remove FIXME --- pytential/qbx/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pytential/qbx/utils.py b/pytential/qbx/utils.py index 433ce10e5..da804df5c 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -340,7 +340,6 @@ def _make_centers(discr): flags[particle_slice].fill(1) flags.finish() - # FIXME: thaw box_to_class = ( particle_list_filter .filter_target_lists_in_user_order(queue, tree, flags) From b51f070204bce939a2eb550247f5bae1e3ff5a9b Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 19:08:33 -0500 Subject: [PATCH 49/75] Address another FIXME --- test/test_scalar_int_eq.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_scalar_int_eq.py b/test/test_scalar_int_eq.py index 0f482cf98..82fca9ae1 100644 --- a/test/test_scalar_int_eq.py +++ b/test/test_scalar_int_eq.py @@ -727,7 +727,6 @@ def run_int_eq_test(actx: PyOpenCLArrayContext, # {{{ build matrix for spectrum check if 0: - # FIXME: Ensure this works from sumpy.tools import build_matrix mat = build_matrix( bound_op.scipy_op( From d476b0981b4ecf9a1af34cf43c86b31dedf9df91 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 19:20:51 -0500 Subject: [PATCH 50/75] flake8 fix --- test/test_global_qbx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_global_qbx.py b/test/test_global_qbx.py index 53fc2e4ab..6b8047b36 100644 --- a/test/test_global_qbx.py +++ b/test/test_global_qbx.py @@ -134,7 +134,7 @@ def run_source_refinement_test(ctx_factory, mesh, order, dd = places.auto_source stage1_density_discr = places.get_discretization(dd.geometry) - from meshmode.dof_array import thaw, flatten + from meshmode.dof_array import thaw stage1_density_nodes = dof_array_to_numpy(actx, thaw(actx, stage1_density_discr.nodes())) From 56373b87d5ad7326356cb8798888c9d767e1bbd8 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 4 Jul 2020 02:32:58 +0200 Subject: [PATCH 51/75] Apply 1 suggestion(s) to 1 file(s) --- test/test_stokes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_stokes.py b/test/test_stokes.py index ec7282a03..5ac2ab47a 100644 --- a/test/test_stokes.py +++ b/test/test_stokes.py @@ -46,7 +46,8 @@ def run_exterior_stokes_2d(ctx_factory, nelements, mesh_order=4, target_order=4, qbx_order=4, - fmm_order=False, mu=1, circle_rad=1.5, visualize=False): + fmm_order=False, # FIXME: FMM is slower than direct eval + mu=1, circle_rad=1.5, visualize=False): # This program tests an exterior Stokes flow in 2D using the # compound representation given in Hsiao & Kress, From 27c79226e02829f903f85d6803559c1abfb386eb Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 4 Jul 2020 02:33:10 +0200 Subject: [PATCH 52/75] Apply 1 suggestion(s) to 1 file(s) --- pytential/linalg/proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytential/linalg/proxy.py b/pytential/linalg/proxy.py index a87e0785e..42da45f6a 100644 --- a/pytential/linalg/proxy.py +++ b/pytential/linalg/proxy.py @@ -143,7 +143,7 @@ def partition_from_coarse(actx, resampler, from_indices): from_grp_ranges = np.cumsum( [0] + [grp.nelements for grp in from_discr.mesh.groups]) from_el_ranges = np.hstack([ - np.arange(grp.node_nr_base, grp.nnodes + 1, grp.nunit_nodes) + np.arange(grp.node_nr_base, grp.ndofs + 1, grp.nunit_dofs) for grp in from_discr.groups]) # construct coarse element arrays in each from_range From fb062bb3cf90659aa09ead912e71c1ac9f04c4a3 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 3 Jul 2020 19:44:23 -0500 Subject: [PATCH 53/75] remove partition_by_coarse --- pytential/linalg/proxy.py | 76 --------------------------------------- 1 file changed, 76 deletions(-) diff --git a/pytential/linalg/proxy.py b/pytential/linalg/proxy.py index 42da45f6a..0f79148e8 100644 --- a/pytential/linalg/proxy.py +++ b/pytential/linalg/proxy.py @@ -50,13 +50,6 @@ # {{{ point index partitioning -def _element_node_range(group, ielement): - istart = group.node_nr_base + group.nunit_nodes * ielement - iend = group.node_nr_base + group.nunit_nodes * (ielement + 1) - - return np.arange(istart, iend) - - def partition_by_nodes(actx, discr, use_tree=True, max_nodes_in_box=None): """Generate equally sized ranges of nodes. The partition is created at the lowest level of granularity, i.e. nodes. This results in balanced ranges @@ -113,75 +106,6 @@ def partition_by_nodes(actx, discr, use_tree=True, max_nodes_in_box=None): return BlockIndexRanges(actx.context, actx.freeze(indices), actx.freeze(ranges)) - -def partition_from_coarse(actx, resampler, from_indices): - """Generate a partition of nodes from an existing partition on a - coarser discretization. The new partition is generated based on element - refinement relationships in *resampler*, so the existing partition - needs to be created using :func:`partition_by_elements`, - since we assume that each range contains all the nodes in an element. - - The new partition will have the same number of ranges as the old partition. - The nodes inside each range in the new partition are all the nodes in - *resampler.to_discr* that were refined from elements in the same - range from *resampler.from_discr*. - - :arg resampler: a - :class:`meshmode.discretization.connection.DirectDiscretizationConnection`. - :arg from_indices: a :class:`sumpy.tools.BlockIndexRanges`. - - :return: a :class:`sumpy.tools.BlockIndexRanges`. - """ - - if not hasattr(resampler, "groups"): - raise ValueError("resampler must be a DirectDiscretizationConnection.") - - from_indices = from_indices.get(actx.queue) - - # construct ranges - from_discr = resampler.from_discr - from_grp_ranges = np.cumsum( - [0] + [grp.nelements for grp in from_discr.mesh.groups]) - from_el_ranges = np.hstack([ - np.arange(grp.node_nr_base, grp.ndofs + 1, grp.nunit_dofs) - for grp in from_discr.groups]) - - # construct coarse element arrays in each from_range - el_indices = np.empty(from_indices.nblocks, dtype=np.object) - el_ranges = np.full(from_grp_ranges[-1], -1, dtype=np.int) - for i in range(from_indices.nblocks): - ifrom = from_indices.block_indices(i) - el_indices[i] = np.unique(np.digitize(ifrom, from_el_ranges)) - 1 - el_ranges[el_indices[i]] = i - el_indices = np.hstack(el_indices) - - # construct lookup table - to_el_table = [np.full(g.nelements, -1, dtype=np.int) - for g in resampler.to_discr.groups] - - for igrp, grp in enumerate(resampler.groups): - for batch in grp.batches: - to_el_table[igrp][actx.to_numpy(batch.to_element_indices)] = \ - from_grp_ranges[igrp] + actx.to_numpy(batch.from_element_indices) - - # construct fine node index list - indices = [np.empty(0, dtype=np.int) - for _ in range(from_indices.nblocks)] - for igrp in range(len(resampler.groups)): - to_element_indices = \ - np.where(np.isin(to_el_table[igrp], el_indices))[0] - - for i, j in zip(el_ranges[to_el_table[igrp][to_element_indices]], - to_element_indices): - indices[i] = np.hstack([indices[i], - _element_node_range(resampler.to_discr.groups[igrp], j)]) - - ranges = actx.from_numpy(np.cumsum([0] + [b.shape[0] for b in indices])) - indices = actx.from_numpy(np.hstack(indices)) - - return BlockIndexRanges(resampler.cl_context, - actx.freeze(indices), actx.freeze(ranges)) - # }}} From 58d0dfa5c5c41ba801b66aad75545ccc67af6435 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 20:26:33 -0500 Subject: [PATCH 54/75] [ci skip] Rename default_vdot to structured_vdot --- pytential/solve.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pytential/solve.py b/pytential/solve.py index 3a717c67d..9a12cff47 100644 --- a/pytential/solve.py +++ b/pytential/solve.py @@ -41,7 +41,7 @@ from pytools.obj_array import obj_array_vectorize_n_args -def default_vdot(x, y): +def structured_vdot(x, y): # vdot() implementation that is aware of scalars and host or # PyOpenCL arrays. It also recurses down nested object arrays. if (isinstance(x, Number) @@ -51,7 +51,7 @@ def default_vdot(x, y): return cl.array.vdot(x, y).get() else: assert isinstance(x, np.ndarray) and x.dtype.char == "O" - return sum(obj_array_vectorize_n_args(default_vdot, x, y)) + return sum(obj_array_vectorize_n_args(structured_vdot, x, y)) # {{{ gmres @@ -242,7 +242,7 @@ def norm(x): # {{{ progress reporting class ResidualPrinter: - def __init__(self, inner_product=default_vdot): + def __init__(self, inner_product=structured_vdot): self.count = 0 self.inner_product = inner_product @@ -263,7 +263,7 @@ def __call__(self, resid): # {{{ entrypoint def gmres(op, rhs, restart=None, tol=None, x0=None, - inner_product=default_vdot, + inner_product=structured_vdot, maxiter=None, hard_failure=None, no_progress_factor=None, stall_iterations=None, callback=None, progress=False, require_monotonicity=True): From ef9c1c4c653608606f26f2146c9e183e97240a10 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sat, 4 Jul 2020 03:58:29 +0200 Subject: [PATCH 55/75] Undo setup.cfg changes --- setup.cfg | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index 52f59fb78..1a01ccc8a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,3 @@ - [flake8] ignore = E126,E127,E128,E123,E226,E241,E242,E265,E402,W503,N803,N806,N802,D102,D103 max-line-length=85 @@ -8,5 +7,4 @@ exclude= [tool:pytest] markers= - slowtest: mark a test as slow -python_files = test_cost_model.py test_tools.py test_target_specific_qbx.py test_symbolic.py test_muller.py test_global_qbx.py test_layer_pot_eigenvalues.py test_layer_pot_identity.py test_layer_pot.py test_scalar_int_eq.py test_maxwell.py test_linalg_proxy.py test_matrix.py test_stokes.py + slowtest: mark a test as slow \ No newline at end of file From c296c6d9c0ee3bdf06752adeee6fee7c205669e9 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 21:01:53 -0500 Subject: [PATCH 56/75] Tweak setup.cfg --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 1a01ccc8a..444d02d0d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,4 +7,4 @@ exclude= [tool:pytest] markers= - slowtest: mark a test as slow \ No newline at end of file + slowtest: mark a test as slow From 459f2f14c01f161a78300483098657bfb3c688d8 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 22:00:42 -0500 Subject: [PATCH 57/75] Undo changes to .gitlab-ci.yml --- .gitlab-ci.yml | 116 ++++++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a9436d5b5..f9c12b45f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,7 +8,7 @@ Python 3 POCL: script: - export PY_EXE=python3 - export PYOPENCL_TEST=portable:pthread - - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS} # ${PYTEST_ADDOPTS:--k-slowtest} # FIXME: Uncomment + - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} - export EXTRA_INSTALL="Cython pybind11 numpy scipy mako" - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh - ". ./build-and-test-py-project.sh" @@ -22,24 +22,24 @@ Python 3 POCL: reports: junit: test/pytest.xml -# Python 3 Intel: -# script: -# - export PY_EXE=python3 -# - source /opt/enable-intel-cl.sh -# - export PYOPENCL_TEST="intel(r):pu" -# - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} -# - export EXTRA_INSTALL="Cython pybind11 numpy scipy mako" -# - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh -# - ". ./build-and-test-py-project.sh" -# tags: -# - python3 -# - intel-cl-cpu -# - large-node -# except: -# - tags -# artifacts: -# reports: -# junit: test/pytest.xml +Python 3 Intel: + script: + - export PY_EXE=python3 + - source /opt/enable-intel-cl.sh + - export PYOPENCL_TEST="intel(r):pu" + - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} + - export EXTRA_INSTALL="Cython pybind11 numpy scipy mako" + - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh + - ". ./build-and-test-py-project.sh" + tags: + - python3 + - intel-cl-cpu + - large-node + except: + - tags + artifacts: + reports: + junit: test/pytest.xml Python 3 POCL Examples: script: @@ -56,45 +56,45 @@ Python 3 POCL Examples: except: - tags -# Python 3 Conda: -# script: -# - export SUMPY_FORCE_SYMBOLIC_BACKEND=symengine -# - export CONDA_ENVIRONMENT=.test-conda-env-py3.yml -# - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} -# - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh -# - ". ./build-and-test-py-project-within-miniconda.sh" -# tags: -# - linux -# - large-node -# except: -# - tags -# -# artifacts: -# reports: -# junit: test/pytest.xml -# -# Python 3 Conda Apple: -# script: -# - export LC_ALL=en_US.UTF-8 -# - export LANG=en_US.UTF-8 -# - export CONDA_ENVIRONMENT=.test-conda-env-py3-macos.yml -# - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} -# - export CC=clang -# # https://stackoverflow.com/q/60934005; https://reviews.llvm.org/D71579 -# - export LDFLAGS="-mlinker-version=519" -# - set -o xtrace -# - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh -# - ". ./build-and-test-py-project-within-miniconda.sh" -# -# tags: -# - apple -# except: -# - tags -# retry: 2 -# -# artifacts: -# reports: -# junit: test/pytest.xml +Python 3 Conda: + script: + - export SUMPY_FORCE_SYMBOLIC_BACKEND=symengine + - export CONDA_ENVIRONMENT=.test-conda-env-py3.yml + - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} + - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh + - ". ./build-and-test-py-project-within-miniconda.sh" + tags: + - linux + - large-node + except: + - tags + + artifacts: + reports: + junit: test/pytest.xml + +Python 3 Conda Apple: + script: + - export LC_ALL=en_US.UTF-8 + - export LANG=en_US.UTF-8 + - export CONDA_ENVIRONMENT=.test-conda-env-py3-macos.yml + - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} + - export CC=clang + # https://stackoverflow.com/q/60934005; https://reviews.llvm.org/D71579 + - export LDFLAGS="-mlinker-version=519" + - set -o xtrace + - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh + - ". ./build-and-test-py-project-within-miniconda.sh" + + tags: + - apple + except: + - tags + retry: 2 + + artifacts: + reports: + junit: test/pytest.xml Documentation: script: From e08ec0e6cdc986b7e790df332aef3fa53c13a0bd Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 22:21:31 -0500 Subject: [PATCH 58/75] Re-dangle Conda requirements files --- .test-conda-env-py3-macos.yml | 2 +- .test-conda-env-py3.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.test-conda-env-py3-macos.yml b/.test-conda-env-py3-macos.yml index 901576dcf..ba5a52e99 100644 --- a/.test-conda-env-py3-macos.yml +++ b/.test-conda-env-py3-macos.yml @@ -27,4 +27,4 @@ dependencies: - git+https://github.com/inducer/pymbolic - git+https://github.com/inducer/loopy - git+https://gitlab.tiker.net/inducer/sumpy - - git+https://github.com/inducer/meshmode + - git+https://gitlab.tiker.net/inducer/meshmode@array-context diff --git a/.test-conda-env-py3.yml b/.test-conda-env-py3.yml index 748855b09..fb8bb92d0 100644 --- a/.test-conda-env-py3.yml +++ b/.test-conda-env-py3.yml @@ -23,4 +23,4 @@ dependencies: - git+https://github.com/inducer/pymbolic - git+https://github.com/inducer/loopy - git+https://gitlab.tiker.net/inducer/sumpy - - git+https://github.com/inducer/meshmode + - git+https://gitlab.tiker.net/inducer/meshmode@array-context From 15b7c8df94822fbca9e688d8c5f494fa91dc4086 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sun, 5 Jul 2020 14:19:19 -0500 Subject: [PATCH 59/75] Fix CI failures on master due to recent Sumpy P2P API changes --- pytential/qbx/direct.py | 6 +++++- setup.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pytential/qbx/direct.py b/pytential/qbx/direct.py index eac3c7fc8..1bcee7c04 100644 --- a/pytential/qbx/direct.py +++ b/pytential/qbx/direct.py @@ -109,7 +109,11 @@ def get_kernel(self): def __call__(self, queue, targets, sources, centers, strengths, expansion_radii, **kwargs): - knl = self.get_cached_optimized_kernel() + from sumpy.tools import is_obj_array_like + knl = self.get_cached_optimized_kernel( + targets_is_obj_array=is_obj_array_like(targets), + sources_is_obj_array=is_obj_array_like(sources), + centers_is_obj_array=is_obj_array_like(centers)) for i, dens in enumerate(strengths): kwargs["strength_%d" % i] = dens diff --git a/setup.py b/setup.py index 6c4efcf85..8a3bd5f87 100644 --- a/setup.py +++ b/setup.py @@ -120,7 +120,7 @@ def write_git_revision(package_name): "boxtree>=2019.1", "pymbolic>=2013.2", "loo.py>=2017.2", - "sumpy>=2013.1", + "sumpy>=2020.2beta1", "cgen>=2013.1.2", "pyfmmlib>=2019.1.1", From a2a72733bb107d92fe18697db73c2d8d2fd617f9 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 12:33:52 -0500 Subject: [PATCH 60/75] Tweak comments in examples --- examples/fmm-error.py | 20 ++++++++++++++------ examples/layerpot.py | 4 ++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/examples/fmm-error.py b/examples/fmm-error.py index 77281aa58..ea50d70f6 100644 --- a/examples/fmm-error.py +++ b/examples/fmm-error.py @@ -46,7 +46,7 @@ def main(): unaccel_qbx = QBXLayerPotentialSource( pre_density_discr, fine_order=2*target_order, qbx_order=qbx_order, fmm_order=False, - target_association_tolerance=.05 + target_association_tolerance=.05, ) from pytential.target import PointsTarget @@ -64,11 +64,19 @@ def main(): angle = actx.np.atan2(nodes[1], nodes[0]) from pytential import bind, sym - kwargs = {"k": sym.var("k")} if k else {} - #op = sym.d_dx( - # sym.S(kernel, sym.var("sigma")), qbx_forced_limit=None, **kwargs) - #op = sym.D(kernel, sym.var("sigma"), qbx_forced_limit=None, **kwargs) - op = sym.S(kernel, sym.var("sigma"), qbx_forced_limit=None, **kwargs) + if k: + kernel_kwargs = {"k": sym.var("k")} + else: + kernel_kwargs = {} + + def get_op(): + kwargs = dict(qbx_forced_limit=None) + kwargs.update(kernel_kwargs) + # return sym.d_dx(2, sym.S(kernel, sym.var("sigma"), **kwargs)) + # return sym.D(kernel, sym.var("sigma"), **kwargs) + return sym.S(kernel, sym.var("sigma"), **kwargs) + + op = get_op() sigma = actx.np.cos(mode_nr*angle) diff --git a/examples/layerpot.py b/examples/layerpot.py index 4980be674..2dee4223f 100644 --- a/examples/layerpot.py +++ b/examples/layerpot.py @@ -23,8 +23,8 @@ def main(curve_fn=starfish, visualize=True): - #import logging - #logging.basicConfig(level=logging.WARNING) # INFO for more progress info + import logging + logging.basicConfig(level=logging.WARNING) # INFO for more progress info cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) From 0a298d9ad097c696ac8c6134cd38c5715488f85c Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 16:09:44 -0500 Subject: [PATCH 61/75] Fix getting host array --- examples/layerpot.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/examples/layerpot.py b/examples/layerpot.py index 2dee4223f..40a060fb4 100644 --- a/examples/layerpot.py +++ b/examples/layerpot.py @@ -121,25 +121,20 @@ def op(**kwargs): if enable_mayavi: # {{{ plot boundary field - from meshmode.dof_array import flatten - from pytools.obj_array import obj_array_vectorize + from pytential.utils import flatten_to_numpy - fld_on_bdry = actx.to_numpy( - flatten(bound_bdry_op(actx, sigma=sigma, k=k))) - - nodes_host = obj_array_vectorize( - actx.to_numpy, - thaw(actx, density_discr.nodes())) + fld_on_bdry = flatten_to_numpy( + actx, bound_bdry_op(actx, sigma=sigma, k=k)) + nodes_host = flatten_to_numpy(actx, density_discr.nodes()) mlab.points3d(nodes_host[0], nodes_host[1], fld_on_bdry.real, scale_factor=0.03) - # }}} - - if enable_mayavi: mlab.colorbar() mlab.show() + # }}} + if __name__ == "__main__": main() From db0a8c2db802bd05a14c21dc744e89330c9ca18a Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 16:10:03 -0500 Subject: [PATCH 62/75] Fix naming to QBXFMMGeometryDataCodeContainer --- pytential/qbx/geometry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pytential/qbx/geometry.py b/pytential/qbx/geometry.py index 1fa28a180..ace246b7a 100644 --- a/pytential/qbx/geometry.py +++ b/pytential/qbx/geometry.py @@ -79,7 +79,7 @@ Geometry description code container ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. autoclass:: QBXFMMGeometryCodeContainer +.. autoclass:: QBXFMMGeometryDataCodeContainer :members: :undoc-members: @@ -332,7 +332,7 @@ class QBXFMMGeometryData(FMMLibRotationDataInterface): .. attribute:: code_getter - The :class:`QBXFMMGeometryCodeContainer` for this object. + The :class:`QBXFMMGeometryDataCodeContainer` for this object. .. attribute:: target_discrs_and_qbx_sides From f1d71930e9cb06020cdf3f652434104216e0183c Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 16:11:05 -0500 Subject: [PATCH 63/75] Rename _FMMGeometryCodeContainer to _FMMGeometryDataCodeContainer --- pytential/unregularized.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pytential/unregularized.py b/pytential/unregularized.py index 4b0e896f0..b7cae228a 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -202,7 +202,7 @@ def expansion_wrangler_code_container(self, fmm_kernel, out_kernels): @property def fmm_geometry_code_container(self): - return _FMMGeometryCodeContainer( + return _FMMGeometryDataCodeContainer( self._setup_actx, self.ambient_dim, self.debug) def fmm_geometry_data(self, targets): @@ -300,7 +300,7 @@ def exec_compute_potential_insn_fmm(self, actx: PyOpenCLArrayContext, # {{{ fmm tools -class _FMMGeometryCodeContainer(object): +class _FMMGeometryDataCodeContainer(object): def __init__(self, actx, ambient_dim, debug): self.array_context = actx From f1281a0496ae6208b2cb47c7b3374e8d0da2ec3d Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 17:32:00 -0500 Subject: [PATCH 64/75] Update pytential/symbolic/execution.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Andreas Klöckner --- 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 ce8910e53..3beabc2c7 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -87,7 +87,7 @@ def __init__(self, bound_expr, actx: PyOpenCLArrayContext, context=None, self.array_context = actx if not isinstance(actx, PyOpenCLArrayContext): - raise NotImplementedError("evaluation with non-PyOpenCL array contexts") + raise NotImplementedError("evaluation with non-PyOpenCL array context") self.queue = actx.queue From bdb3cf2dac808a58ec6e3d4231f3afe314c0ad3d Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 17:32:25 -0500 Subject: [PATCH 65/75] Remove too_slow_test_helmholtz.py This file was not working and is now further out of date due to array context API changes --- test/too_slow_test_helmholtz.py | 455 -------------------------------- 1 file changed, 455 deletions(-) delete mode 100644 test/too_slow_test_helmholtz.py diff --git a/test/too_slow_test_helmholtz.py b/test/too_slow_test_helmholtz.py deleted file mode 100644 index 6f858eaa8..000000000 --- a/test/too_slow_test_helmholtz.py +++ /dev/null @@ -1,455 +0,0 @@ -from __future__ import division, absolute_import, print_function - -__copyright__ = "Copyright (C) 2014 Shidong Jiang, Andreas Kloeckner" - -__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 numpy.linalg as la -import pyopencl as cl -import pyopencl.array # noqa -import pyopencl.clmath # noqa - -import pytest - -from pytools.obj_array import make_obj_array - -from meshmode.discretization import Discretization -from meshmode.discretization.poly_element import \ - InterpolatoryQuadratureSimplexGroupFactory - -from six.moves import range - -from pytential import bind, sym, norm # noqa -from pytential.symbolic.pde.maxwell.waveguide import ( # noqa - DielectricSRep2DBoundaryOperator as SRep, - DielectricSDRep2DBoundaryOperator as SDRep) - - -from pyopencl.tools import ( # noqa - pytest_generate_tests_for_pyopencl as pytest_generate_tests) - -import logging -logger = logging.getLogger(__name__) - - -def run_dielectric_test(cl_ctx, queue, nelements, qbx_order, - op_class, mode, - k0=3, k1=2.9, mesh_order=10, - bdry_quad_order=None, bdry_ovsmp_quad_order=None, - use_l2_weighting=False, - fmm_order=None, visualize=False): - - if fmm_order is None: - fmm_order = qbx_order * 2 - if bdry_quad_order is None: - bdry_quad_order = mesh_order - if bdry_ovsmp_quad_order is None: - bdry_ovsmp_quad_order = 4*bdry_quad_order - - # {{{ geometries - - from meshmode.mesh.generation import ellipse, make_curve_mesh - from functools import partial - mesh = make_curve_mesh( - partial(ellipse, 3), - np.linspace(0, 1, nelements+1), - mesh_order) - - density_discr = Discretization( - cl_ctx, mesh, - InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order)) - - logger.info("%d elements" % mesh.nelements) - - from pytential.qbx import QBXLayerPotentialSource - qbx = QBXLayerPotentialSource( - density_discr, fine_order=bdry_ovsmp_quad_order, qbx_order=qbx_order, - fmm_order=fmm_order - ) - - from pytential.target import PointsTarget - targets_0 = PointsTarget(make_obj_array(list(np.array([ - [3.2 + t, -4] - for t in [0, 0.5, 1] - ]).T.copy()))) - targets_1 = PointsTarget(make_obj_array(list(np.array([ - [-0.3 * t, -0.2 * t] - for t in [0, 0.5, 1] - ]).T.copy()))) - - if visualize: - low_order_qbx, _ = QBXLayerPotentialSource( - density_discr, - fine_order=bdry_ovsmp_quad_order, qbx_order=2, - fmm_order=3, - ) - - from sumpy.visualization import FieldPlotter - fplot = FieldPlotter(np.zeros(2), extent=5, npoints=300) - targets_plot = PointsTarget(fplot.points) - - places = { - sym.DEFAULT_SOURCE: qbx, - sym.DEFAULT_TARGET: qbx.density_discr, - "targets0": targets_0, - "targets1": targets_1 - } - if visualize: - places.update({ - "qbx-low-order": low_order_qbx, - "targets-plot": targets_plot - }) - - from pytential import GeometryCollection - places = GeometryCollection(places) - - # }}} - - # from meshmode.discretization.visualization import make_visualizer - # bdry_vis = make_visualizer(queue, density_discr, 20) - - # {{{ solve bvp - - from sumpy.kernel import HelmholtzKernel, AxisTargetDerivative - kernel = HelmholtzKernel(2) - - beta = 2.5 - K0 = np.sqrt(k0**2-beta**2) # noqa - K1 = np.sqrt(k1**2-beta**2) # noqa - - pde_op = op_class( - mode, - k_vacuum=1, - domain_k_exprs=(k0, k1), - beta=beta, - interfaces=((0, 1, sym.DEFAULT_SOURCE),), - use_l2_weighting=use_l2_weighting) - - op_unknown_sym = pde_op.make_unknown("unknown") - - representation0_sym = pde_op.representation(op_unknown_sym, 0) - representation1_sym = pde_op.representation(op_unknown_sym, 1) - bound_pde_op = bind(places, pde_op.operator(op_unknown_sym)) - - e_factor = float(pde_op.ez_enabled) - h_factor = float(pde_op.hz_enabled) - - e_sources_0 = make_obj_array(list(np.array([ - [0.1, 0.2] - ]).T.copy())) - e_strengths_0 = np.array([1*e_factor]) - e_sources_1 = make_obj_array(list(np.array([ - [4, 4] - ]).T.copy())) - e_strengths_1 = np.array([1*e_factor]) - - h_sources_0 = make_obj_array(list(np.array([ - [0.2, 0.1] - ]).T.copy())) - h_strengths_0 = np.array([1*h_factor]) - h_sources_1 = make_obj_array(list(np.array([ - [4, 5] - ]).T.copy())) - h_strengths_1 = np.array([1*h_factor]) - - kernel_grad = [ - AxisTargetDerivative(i, kernel) for i in range(density_discr.ambient_dim)] - - from sumpy.p2p import P2P - pot_p2p = P2P(cl_ctx, [kernel], exclude_self=False) - pot_p2p_grad = P2P(cl_ctx, kernel_grad, exclude_self=False) - - normal = bind(places, sym.normal(qbx.ambient_dim))( - queue).as_vector(np.object) - tangent = bind(places, - sym.pseudoscalar(qbx.ambient_dim)/sym.area_element(qbx.ambient_dim))( - queue).as_vector(np.object) - - _, (E0,) = pot_p2p(queue, density_discr.nodes(), e_sources_0, [e_strengths_0], - out_host=False, k=K0) - _, (E1,) = pot_p2p(queue, density_discr.nodes(), e_sources_1, [e_strengths_1], - out_host=False, k=K1) - _, (grad0_E0, grad1_E0) = pot_p2p_grad( - queue, density_discr.nodes(), e_sources_0, [e_strengths_0], - out_host=False, k=K0) - _, (grad0_E1, grad1_E1) = pot_p2p_grad( - queue, density_discr.nodes(), e_sources_1, [e_strengths_1], - out_host=False, k=K1) - - _, (H0,) = pot_p2p(queue, density_discr.nodes(), h_sources_0, [h_strengths_0], - out_host=False, k=K0) - _, (H1,) = pot_p2p(queue, density_discr.nodes(), h_sources_1, [h_strengths_1], - out_host=False, k=K1) - _, (grad0_H0, grad1_H0) = pot_p2p_grad( - queue, density_discr.nodes(), h_sources_0, [h_strengths_0], - out_host=False, k=K0) - _, (grad0_H1, grad1_H1) = pot_p2p_grad( - queue, density_discr.nodes(), h_sources_1, [h_strengths_1], - out_host=False, k=K1) - - E0_dntarget = (grad0_E0*normal[0] + grad1_E0*normal[1]) # noqa - E1_dntarget = (grad0_E1*normal[0] + grad1_E1*normal[1]) # noqa - - H0_dntarget = (grad0_H0*normal[0] + grad1_H0*normal[1]) # noqa - H1_dntarget = (grad0_H1*normal[0] + grad1_H1*normal[1]) # noqa - - E0_dttarget = (grad0_E0*tangent[0] + grad1_E0*tangent[1]) # noqa - E1_dttarget = (grad0_E1*tangent[0] + grad1_E1*tangent[1]) # noqa - - H0_dttarget = (grad0_H0*tangent[0] + grad1_H0*tangent[1]) # noqa - H1_dttarget = (grad0_H1*tangent[0] + grad1_H1*tangent[1]) # noqa - - sqrt_w = bind(places, sym.sqrt_jac_q_weight(qbx.ambient_dim))(queue) - - bvp_rhs = np.zeros(len(pde_op.bcs), dtype=np.object) - for i_bc, terms in enumerate(pde_op.bcs): - for term in terms: - assert term.i_interface == 0 - if term.field_kind == pde_op.field_kind_e: - - if term.direction == pde_op.dir_none: - bvp_rhs[i_bc] += ( - term.coeff_outer * E0 - + term.coeff_inner * E1) - elif term.direction == pde_op.dir_normal: - bvp_rhs[i_bc] += ( - term.coeff_outer * E0_dntarget - + term.coeff_inner * E1_dntarget) - elif term.direction == pde_op.dir_tangential: - bvp_rhs[i_bc] += ( - term.coeff_outer * E0_dttarget - + term.coeff_inner * E1_dttarget) - else: - raise NotImplementedError("direction spec in RHS") - - elif term.field_kind == pde_op.field_kind_h: - if term.direction == pde_op.dir_none: - bvp_rhs[i_bc] += ( - term.coeff_outer * H0 - + term.coeff_inner * H1) - elif term.direction == pde_op.dir_normal: - bvp_rhs[i_bc] += ( - term.coeff_outer * H0_dntarget - + term.coeff_inner * H1_dntarget) - elif term.direction == pde_op.dir_tangential: - bvp_rhs[i_bc] += ( - term.coeff_outer * H0_dttarget - + term.coeff_inner * H1_dttarget) - else: - raise NotImplementedError("direction spec in RHS") - - if use_l2_weighting: - bvp_rhs[i_bc] *= sqrt_w - - scipy_op = bound_pde_op.scipy_op(queue, "unknown", - domains=[sym.DEFAULT_TARGET]*len(pde_op.bcs), K0=K0, K1=K1, - dtype=np.complex128) - - if mode == "tem" or op_class is SRep: - from sumpy.tools import vector_from_device, vector_to_device - from pytential.solve import lu - unknown = lu(scipy_op, vector_from_device(queue, bvp_rhs)) - unknown = vector_to_device(queue, unknown) - - else: - from pytential.solve import gmres - gmres_result = gmres(scipy_op, - bvp_rhs, tol=1e-14, progress=True, - hard_failure=True, stall_iterations=0) - - unknown = gmres_result.solution - - # }}} - - from sumpy.tools import vector_from_device - F0_tgt = bind(places, representation0_sym, - auto_where=(sym.DEFAULT_SOURCE, "targets0"))( - queue, unknown=unknown, K0=K0, K1=K1) - F0_tgt = vector_from_device(queue, F0_tgt) - - F1_tgt = bind(places, representation1_sym, - auto_where=(sym.DEFAULT_SOURCE, "targets1"))( - queue, unknown=unknown, K0=K0, K1=K1) - F1_tgt = vector_from_device(queue, F1_tgt) - - _, (E0_tgt_true,) = pot_p2p(queue, - targets_0.nodes(), e_sources_0, [e_strengths_0], - out_host=True, k=K0) - _, (E1_tgt_true,) = pot_p2p(queue, - targets_1.nodes(), e_sources_1, [e_strengths_1], - out_host=True, k=K1) - - _, (H0_tgt_true,) = pot_p2p(queue, - targets_0.nodes(), h_sources_0, [h_strengths_0], - out_host=True, k=K0) - _, (H1_tgt_true,) = pot_p2p(queue, - targets_1.nodes(), h_sources_1, [h_strengths_1], - out_host=True, k=K1) - - err_F0_total = 0 # noqa - err_F1_total = 0 # noqa - - i_field = 0 - - def vec_norm(ary): - return la.norm(ary.reshape(-1)) - - def field_kind_to_string(field_kind): - return {pde_op.field_kind_e: "E", pde_op.field_kind_h: "H"}[field_kind] - - for field_kind in pde_op.field_kinds: - if not pde_op.is_field_present(field_kind): - continue - - if field_kind == pde_op.field_kind_e: - F0_tgt_true = E0_tgt_true # noqa - F1_tgt_true = E1_tgt_true # noqa - elif field_kind == pde_op.field_kind_h: - F0_tgt_true = H0_tgt_true # noqa - F1_tgt_true = H1_tgt_true # noqa - else: - assert False - - abs_err_F0 = vec_norm(F0_tgt[i_field] - F0_tgt_true) # noqa - abs_err_F1 = vec_norm(F1_tgt[i_field] - F1_tgt_true) # noqa - - rel_err_F0 = abs_err_F0/vec_norm(F0_tgt_true) # noqa - rel_err_F1 = abs_err_F1/vec_norm(F1_tgt_true) # noqa - - err_F0_total = max(rel_err_F0, err_F0_total) # noqa - err_F1_total = max(rel_err_F1, err_F1_total) # noqa - - print("Abs Err %s0" % field_kind_to_string(field_kind), abs_err_F0) - print("Abs Err %s1" % field_kind_to_string(field_kind), abs_err_F1) - - print("Rel Err %s0" % field_kind_to_string(field_kind), rel_err_F0) - print("Rel Err %s1" % field_kind_to_string(field_kind), rel_err_F1) - - i_field += 1 - - if visualize: - fld0 = bind(places, representation0_sym, - auto_where=(sym.DEFAULT_SOURCE, "targets-plot"))( - queue, unknown=unknown, K0=K0) - fld1 = bind(places, representation1_sym, - auto_where=(sym.DEFAULT_SOURCE, "targets-plot"))( - queue, unknown=unknown, K1=K1) - - comp_fields = [] - i_field = 0 - for field_kind in pde_op.field_kinds: - if not pde_op.is_field_present(field_kind): - continue - - fld_str = field_kind_to_string(field_kind) - comp_fields.extend([ - ("%s_fld0" % fld_str, fld0[i_field].get()), - ("%s_fld1" % fld_str, fld1[i_field].get()), - ]) - - i_field += 0 - - from sumpy.kernel import LaplaceKernel - ones = (cl.array.empty(queue, (density_discr.ndofs,), dtype=np.float64) - .fill(1)) - ind_func = - bind(places, sym.D(LaplaceKernel(2), sym.var("u")), - auto_where=("qbx-low-order", "targets-plot"))( - queue, u=ones).get() - - _, (e_fld0_true,) = pot_p2p( - queue, fplot.points, e_sources_0, [e_strengths_0], - out_host=True, k=K0) - _, (e_fld1_true,) = pot_p2p( - queue, fplot.points, e_sources_1, [e_strengths_1], - out_host=True, k=K1) - _, (h_fld0_true,) = pot_p2p( - queue, fplot.points, h_sources_0, [h_strengths_0], - out_host=True, k=K0) - _, (h_fld1_true,) = pot_p2p( - queue, fplot.points, h_sources_1, [h_strengths_1], - out_host=True, k=K1) - - #fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5) - fplot.write_vtk_file( - "potential-n%d.vts" % nelements, - [ - ("e_fld0_true", e_fld0_true), - ("e_fld1_true", e_fld1_true), - ("h_fld0_true", h_fld0_true), - ("h_fld1_true", h_fld1_true), - ("ind", ind_func), - ] + comp_fields - ) - - return err_F0_total, err_F1_total - - -@pytest.mark.parametrize("qbx_order", [4]) -@pytest.mark.parametrize("op_class", [ - SRep, - SDRep, - ]) -@pytest.mark.parametrize("mode", [ - "te", - "tm", - "tem", - ]) -def test_dielectric(ctx_factory, qbx_order, op_class, mode, visualize=False): - cl_ctx = ctx_factory() - queue = cl.CommandQueue(cl_ctx) - - import logging - logging.basicConfig(level=logging.INFO) - - from pytools.convergence import EOCRecorder - eoc_rec = EOCRecorder() - - for nelements in [30, 50, 70]: - # prevent sympy cache 'splosion - from sympy.core.cache import clear_cache - clear_cache() - - errs = run_dielectric_test( - cl_ctx, queue, - nelements=nelements, qbx_order=qbx_order, - op_class=op_class, mode=mode, - visualize=visualize) - - eoc_rec.add_data_point(1/nelements, la.norm(list(errs), np.inf)) - - print(eoc_rec) - assert eoc_rec.order_estimate() > qbx_order - 0.5 - - -# You can test individual routines by typing -# $ python test_layer_pot.py 'test_routine()' - -if __name__ == "__main__": - import sys - if len(sys.argv) > 1: - exec(sys.argv[1]) - else: - from pytest import main - main([__file__]) - -# vim: fdm=marker From 10c3787dc991ea4c98cc250a50b2aeb897e3adb4 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 17:33:43 -0500 Subject: [PATCH 66/75] Make an interleave_dof_arrays underscored method --- pytential/symbolic/dof_connection.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pytential/symbolic/dof_connection.py b/pytential/symbolic/dof_connection.py index 745f927b0..4863652c6 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -89,7 +89,7 @@ class CenterGranularityConnection(GranularityConnection): def __init__(self, discr): super(CenterGranularityConnection, self).__init__(discr) - def interleave_dof_arrays(self, ary1, ary2): + def _interleave_dof_arrays(self, ary1, ary2): if not isinstance(ary1, DOFArray) or not isinstance(ary2, DOFArray): raise TypeError("non-array passed to connection") @@ -145,10 +145,10 @@ def __call__(self, arys): raise ValueError("cannot interleave arrays") if isinstance(arys[0], DOFArray): - return self.interleave_dof_arrays(*arys) + return self._interleave_dof_arrays(*arys) else: from pytools.obj_array import obj_array_vectorize_n_args - return obj_array_vectorize_n_args(self.interleave_dof_arrays, *arys) + return obj_array_vectorize_n_args(self._interleave_dof_arrays, *arys) # }}} From 499ffc3827f8bfaa9a6950c4d515cc931d30bf75 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Wed, 8 Jul 2020 15:23:43 -0500 Subject: [PATCH 67/75] Delete DielectricSRep2DBoundaryOperator and DielectricSDRep2DBoundaryOperator --- pytential/symbolic/pde/maxwell/waveguide.py | 635 -------------------- 1 file changed, 635 deletions(-) diff --git a/pytential/symbolic/pde/maxwell/waveguide.py b/pytential/symbolic/pde/maxwell/waveguide.py index 3a1d3a63d..aab6e9426 100644 --- a/pytential/symbolic/pde/maxwell/waveguide.py +++ b/pytential/symbolic/pde/maxwell/waveguide.py @@ -28,16 +28,9 @@ ^^^^^^^^^^^^^^^^^^^^^ .. autoclass:: SecondKindInfZMuellerOperator - -2D Dielectric (old-style) -^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. autoclass:: DielectricSRep2DBoundaryOperator -.. autoclass:: DielectricSDRep2DBoundaryOperator """ import numpy as np -from collections import namedtuple from six.moves import range from pytential import sym @@ -353,632 +346,4 @@ def a23_expr(phi): # }}} - -# {{{ old-style waveguide - -class Dielectric2DBoundaryOperatorBase(L2WeightedPDEOperator): - r""" - Solves the following system of BVPs on :math:`\mathbb{R}^2`, in which - a disjoint family of domains :math:`\Omega_i` is embedded: - - .. math:: - - \triangle E + (k_i^2-\beta^2) E = 0\quad \text{on $\Omega_i$}\\ - \triangle H + (k_i^2-\beta^2) H = 0\quad \text{on $\Omega_i$}\\ - [H] = 0 \text{ on $\partial \Omega_i$},\quad - [E] = 0 \text{ on $\partial \Omega_i$}\\ - \left[ \frac{k_0}{k^2-\beta^2} \partial_{\hat n}H\right] = 0 - \quad\text{on $\partial \Omega_i$},\quad\\ - \left[ \frac{k_0}{k^2-\beta^2} \partial_{\hat n}E\right] = 0 - \quad\text{on $\partial \Omega_i$} - - :math:`E` and :math:`H` are assumed to be of the form - - .. math:: - - E(x,y,z,t)=E(x,y)e^{i(\beta z-\omega t) - H(x,y,z,t)=H(x,y)e^{i(\beta z-\omega t) - - where :math:`[\cdot]` denotes the jump across an interface, and :math:`k` - (without an index) denotes the value of :math:`k` on either side of the - interface, for the purpose of computing the jump. :math:`\hat n` denotes - the unit normal of the interface. - - .. automethod:: make_unknown - .. automethod:: representation_outer - .. automethod:: representation_inner - .. automethod:: operator - """ - - field_kind_e = 0 - field_kind_h = 1 - field_kinds = [field_kind_e, field_kind_h] - - side_in = 0 - side_out = 1 - sides = [side_in, side_out] - side_to_sign = { - side_in: -1, - side_out: 1, - } - - dir_none = 0 - dir_normal = 1 - dir_tangential = 2 - - BCTermDescriptor = namedtuple("BCDescriptor", - "i_interface direction field_kind coeff_inner coeff_outer".split()) - - # {{{ constructor - - def __init__(self, mode, k_vacuum, domain_k_exprs, beta, - interfaces, use_l2_weighting=None): - """ - :attr mode: one of 'te', 'tm', 'tem' - :attr k_vacuum: A symbolic expression for the wave number in vacuum. - May be a string, which will be interpreted as a variable name. - :attr interfaces: a tuple of tuples - ``(outer_domain, inner_domain, interface_id)``, - where *outer_domain* and *inner_domain* are indices into - *domain_k_names*, - and *interface_id* is a symbolic name for the discretization of the - interface. 'outer' designates the side of the interface to which - the normal points. - :attr domain_k_exprs: a tuple of variable names of the Helmholtz - parameter *k*, to be used inside each part of the source geometry. - May also be a tuple of strings, which will be transformed into - variable references of the corresponding names. - :attr beta: A symbolic expression for the wave number in the :math:`z` - direction. May be a string, which will be interpreted as a variable - name. - """ - - if use_l2_weighting is None: - use_l2_weighting = False - - from sumpy.kernel import HelmholtzKernel - self.kernel = HelmholtzKernel(2, allow_evanescent=True) - - super(Dielectric2DBoundaryOperatorBase, self).__init__( - self.kernel, - use_l2_weighting=use_l2_weighting) - - if mode == "te": - self.ez_enabled = False - self.hz_enabled = True - elif mode == "tm": - self.ez_enabled = True - self.hz_enabled = False - elif mode == "tem": - self.ez_enabled = True - self.hz_enabled = True - else: - raise ValueError("invalid mode '%s'" % mode) - - self.interfaces = interfaces - - fk_e = self.field_kind_e - fk_h = self.field_kind_h - - dir_none = self.dir_none - dir_normal = self.dir_normal - dir_tangential = self.dir_tangential - - if isinstance(beta, str): - beta = sym.var(beta) - beta = sym.cse(beta, "beta") - - if isinstance(k_vacuum, str): - k_vacuum = sym.var(k_vacuum) - k_vacuum = sym.cse(k_vacuum, "k_vac") - - self.domain_k_exprs = [ - sym.var(k_expr) - if isinstance(k_expr, str) - else sym.cse(k_expr, "k%d" % idom) - for idom, k_expr in enumerate(domain_k_exprs)] - del domain_k_exprs - - # Note the case of k/K! - # "K" is the 2D Helmholtz parameter. - # "k" is the 3D Helmholtz parameter. - - self.domain_K_exprs = [ - sym.cse((k_expr**2-beta**2)**0.5, "K%d" % i) - for i, k_expr in enumerate(self.domain_k_exprs)] - - # {{{ build bc list - - # list of tuples, where each tuple consists of BCTermDescriptor instances - - all_bcs = [] - for i_interface, (outer_domain, inner_domain, _) in ( - enumerate(self.interfaces)): - k_outer = self.domain_k_exprs[outer_domain] - k_inner = self.domain_k_exprs[inner_domain] - - all_bcs += [ - ( # [E] = 0 - self.BCTermDescriptor( - i_interface=i_interface, - direction=dir_none, - field_kind=fk_e, - coeff_outer=1, - coeff_inner=-1), - ), - ( # [H] = 0 - self.BCTermDescriptor( - i_interface=i_interface, - direction=dir_none, - field_kind=fk_h, - coeff_outer=1, - coeff_inner=-1), - ), - ( - self.BCTermDescriptor( - i_interface=i_interface, - direction=dir_tangential, - field_kind=fk_e, - coeff_outer=beta/(k_outer**2-beta**2), - coeff_inner=-beta/(k_inner**2-beta**2)), - self.BCTermDescriptor( - i_interface=i_interface, - direction=dir_normal, - field_kind=fk_h, - coeff_outer=sym.cse(-k_vacuum/(k_outer**2-beta**2)), - coeff_inner=sym.cse(k_vacuum/(k_inner**2-beta**2))), - ), - ( - self.BCTermDescriptor( - i_interface=i_interface, - direction=dir_tangential, - field_kind=fk_h, - coeff_outer=beta/(k_outer**2-beta**2), - coeff_inner=-beta/(k_inner**2-beta**2)), - self.BCTermDescriptor( - i_interface=i_interface, - direction=dir_normal, - field_kind=fk_e, - coeff_outer=sym.cse( - (k_outer**2/k_vacuum)/(k_outer**2-beta**2)), - coeff_inner=sym.cse( - -(k_inner**2/k_vacuum) - / (k_inner**2-beta**2))) - ), - ] - - del k_outer - del k_inner - - self.bcs = [] - for bc in all_bcs: - any_significant_e = any( - term.field_kind == fk_e - and term.direction in [dir_normal, dir_none] - for term in bc) - any_significant_h = any( - term.field_kind == fk_h - and term.direction in [dir_normal, dir_none] - for term in bc) - is_necessary = ( - (self.ez_enabled and any_significant_e) - or (self.hz_enabled and any_significant_h)) - - # Only keep tangential modes for TEM. Otherwise, - # no jump in H already implies jump condition on - # tangential derivative. - is_tem = self.ez_enabled and self.hz_enabled - terms = tuple( - term - for term in bc - if term.direction != dir_tangential - or is_tem) - - if is_necessary: - self.bcs.append(terms) - - assert (len(all_bcs) - * (int(self.ez_enabled) + int(self.hz_enabled)) // 2 - == len(self.bcs)) - - # }}} - - # }}} - - def is_field_present(self, field_kind): - return ( - (field_kind == self.field_kind_e and self.ez_enabled) - or (field_kind == self.field_kind_h and self.hz_enabled)) - - def make_unknown(self, name): - num_densities = ( - 2 - * (int(self.ez_enabled) + int(self.hz_enabled)) - * len(self.interfaces)) - - assert num_densities == len(self.bcs) - - return sym.make_sym_vector(name, num_densities) - - def bc_term_to_operator_contrib(self, term, side, raw_potential_op, - density, discrete): - potential_op = raw_potential_op - - side_sign = self.side_to_sign[side] - - domain_outer, domain_inner, interface_id = \ - self.interfaces[term.i_interface] - if side == self.side_in: - K_expr = self.domain_K_exprs[domain_inner] # noqa - bc_coeff = term.coeff_inner - elif side == self.side_out: - K_expr = self.domain_K_exprs[domain_outer] # noqa - bc_coeff = term.coeff_outer - else: - raise ValueError("invalid value of 'side'") - - potential_op = potential_op( - self.kernel, density, source=interface_id, - k=K_expr) - - if term.direction == self.dir_none: - if raw_potential_op is sym.S: - jump_term = 0 - elif raw_potential_op is sym.D: - jump_term = (side_sign*0.5) * discrete - else: - assert False, raw_potential_op - elif term.direction == self.dir_normal: - potential_op = sym.normal_derivative( - 2, potential_op, dofdesc=interface_id) - - if raw_potential_op is sym.S: - # S' - jump_term = (-side_sign*0.5) * discrete - elif raw_potential_op is sym.D: - jump_term = 0 - else: - assert False, raw_potential_op - - elif term.direction == self.dir_tangential: - potential_op = sym.tangential_derivative( - raw_potential_op( - self.kernel, density, source=interface_id, - k=K_expr, qbx_forced_limit=side_sign), - interface_id).a.as_scalar() - - # Some of these may have jumps, but QBX does the dirty - # work here by directly computing the limit. - jump_term = 0 - - else: - raise ValueError("invalid direction") - - potential_op = ( - jump_term - + self.get_sqrt_weight(interface_id)*potential_op) - - del jump_term - - contrib = bc_coeff * potential_op - - if (raw_potential_op is sym.D - and term.direction == self.dir_normal): - # FIXME The hypersingular part should perhaps be - # treated specially to avoid cancellation. - pass - - return contrib - - -# {{{ single-layer representation - -class DielectricSRep2DBoundaryOperator(Dielectric2DBoundaryOperatorBase): - def _structured_unknown(self, unknown, with_l2_weights): - """ - :arg with_l2_weights: If True, return the 'bare' unknowns - that do not have the :math:`L^2` weights divided out. - Note: Those unknowns should *not* be interpreted as - point values of a density. - :returns: an array of unknowns, with the following index axes: - ``[side, field_kind, i_interface]``, where - ``side`` is o for the outside part and i for the interior part, - ``field_kind`` is 0 for the E-field and 1 for the H-field part, - ``i_interface`` is the number of the enclosed domain, starting from 0. - """ - result = np.zeros((2, 2, len(self.interfaces)), dtype=np.object) - sides = { - self.side_out: "o", - self.side_in: "i" - } - fields = { - self.field_kind_e: "E", - self.field_kind_h: "H" - } - - i_unknown = 0 - for side in self.sides: - for field_kind in self.field_kinds: - for i_interface in range(len(self.interfaces)): - - if self.is_field_present(field_kind): - dens = unknown[i_unknown] - i_unknown += 1 - else: - dens = 0 - - _, _, interface_id = self.interfaces[i_interface] - - if not with_l2_weights: - dens = sym.cse( - dens/self.get_sqrt_weight(interface_id), - "dens_{side}_{field}_{dom}".format( - side=sides[side], - field=fields[field_kind], - dom=i_interface)) - - result[side, field_kind, i_interface] = dens - - assert i_unknown == len(unknown) - return result - - def representation(self, unknown, i_domain, qbx_forced_limit=None): - """ - :return: a symbolic expression for the representation of the PDE solution - in domain number *i_domain*. - """ - unk = self._structured_unknown(unknown, with_l2_weights=False) - - result = [] - - for field_kind in self.field_kinds: - if not self.is_field_present(field_kind): - continue - - field_result = 0 - for i_interface, (i_domain_outer, i_domain_inner, interface_id) in ( - enumerate(self.interfaces)): - if i_domain_outer == i_domain: - side = self.side_out - elif i_domain_inner == i_domain: - side = self.side_in - else: - continue - - my_unk = unk[side, field_kind, i_interface] - if my_unk: - field_result += sym.S( - self.kernel, - my_unk, - source=interface_id, - k=self.domain_K_exprs[i_domain], - qbx_forced_limit=qbx_forced_limit) - - result.append(field_result) - - from pytools.obj_array import make_obj_array - return make_obj_array(result) - - def operator(self, unknown): - density_unk = self._structured_unknown(unknown, with_l2_weights=False) - discrete_unk = self._structured_unknown(unknown, with_l2_weights=True) - - result = [] - for bc in self.bcs: - op = 0 - - for side in self.sides: - for term in bc: - unk_index = (side, term.field_kind, term.i_interface) - density = density_unk[unk_index] - discrete = discrete_unk[unk_index] - - op += self.bc_term_to_operator_contrib( - term, side, sym.S, density, discrete) - - result.append(op) - - return np.array(result, dtype=np.object) - -# }}} - - -# {{{ single + double layer representation - -class DielectricSDRep2DBoundaryOperator(Dielectric2DBoundaryOperatorBase): - pot_kind_S = 0 # noqa: N815 - pot_kind_D = 1 # noqa: N815 - pot_kinds = [pot_kind_S, pot_kind_D] - potential_ops = { - pot_kind_S: sym.S, - pot_kind_D: sym.D, - } - - def __init__(self, mode, k_vacuum, domain_k_exprs, beta, - interfaces, use_l2_weighting=None): - - super(DielectricSDRep2DBoundaryOperator, self).__init__( - mode, k_vacuum, domain_k_exprs, beta, - interfaces, use_l2_weighting=use_l2_weighting) - - side_in = self.side_in - side_out = self.side_out - - def find_normal_derivative_bc_coeff(field_kind, i_interface, side): - result = 0 - for bc in self.bcs: - for term in bc: - if term.field_kind != field_kind: - continue - if term.i_interface != i_interface: - continue - if term.direction != self.dir_normal: - continue - - if side == side_in: - result += term.coeff_inner - elif side == side_out: - result += term.coeff_outer - else: - raise ValueError("invalid side") - - return result - - self.density_coeffs = np.zeros( - (len(self.pot_kinds), len(self.field_kinds), - len(self.interfaces), len(self.sides)), - dtype=np.object) - for field_kind in self.field_kinds: - for i_interface in range(len(self.interfaces)): - self.density_coeffs[ - self.pot_kind_S, field_kind, i_interface, side_in] = 1 - self.density_coeffs[ - self.pot_kind_S, field_kind, i_interface, side_out] = 1 - - # These need to satisfy - # - # [dens_coeff_D * bc_coeff * dn D] - # = dens_coeff_D_out * bc_coeff_out * (dn D) - # + dens_coeff_D_in * bc_coeff_in * dn D - # = 0 - # - # (because dn D is hypersingular, which we'd like to cancel out) - # - # NB: bc_coeff_{in,out} already contain the signs to realize - # the subtraction for the jump. (So the "+" above is as it - # should be.) - - dens_coeff_D_in = find_normal_derivative_bc_coeff( # noqa - field_kind, i_interface, side_out) - dens_coeff_D_out = - find_normal_derivative_bc_coeff( # noqa - field_kind, i_interface, side_in) - - self.density_coeffs[ - self.pot_kind_D, field_kind, i_interface, side_in] \ - = dens_coeff_D_in - self.density_coeffs[ - self.pot_kind_D, field_kind, i_interface, side_out] \ - = dens_coeff_D_out - - def _structured_unknown(self, unknown, with_l2_weights): - """ - :arg with_l2_weights: If True, return the 'bare' unknowns - that do not have the :math:`L^2` weights divided out. - Note: Those unknowns should *not* be interpreted as - point values of a density. - :returns: an array of unknowns, with the following index axes: - ``[pot_kind, field_kind, i_interface]``, where - ``pot_kind`` is 0 for the single-layer part and 1 for the double-layer - part, - ``field_kind`` is 0 for the E-field and 1 for the H-field part, - ``i_interface`` is the number of the enclosed domain, starting from 0. - """ - result = np.zeros((2, 2, len(self.interfaces)), dtype=np.object) - - i_unknown = 0 - for pot_kind in self.pot_kinds: - for field_kind in self.field_kinds: - for i_interface in range(len(self.interfaces)): - - if self.is_field_present(field_kind): - dens = unknown[i_unknown] - i_unknown += 1 - else: - dens = 0 - - _, _, interface_id = self.interfaces[i_interface] - - if not with_l2_weights: - dens = sym.cse( - dens/self.get_sqrt_weight(interface_id), - "dens_{pot}_{field}_{intf}".format( - pot={0: "S", 1: "D"}[pot_kind], - field={ - self.field_kind_e: "E", - self.field_kind_h: "H" - } - [field_kind], - intf=i_interface)) - - result[pot_kind, field_kind, i_interface] = dens - - assert i_unknown == len(unknown) - return result - - def representation(self, unknown, i_domain): - """ - :return: a symbolic expression for the representation of the PDE solution - in domain number *i_domain*. - """ - unk = self._structured_unknown(unknown, with_l2_weights=False) - - result = [] - - for field_kind in self.field_kinds: - if not self.is_field_present(field_kind): - continue - - field_result = 0 - for pot_kind in self.pot_kinds: - for i_interface, (i_domain_outer, i_domain_inner, interface_id) in ( - enumerate(self.interfaces)): - if i_domain_outer == i_domain: - side = self.side_out - elif i_domain_inner == i_domain: - side = self.side_in - else: - continue - - my_unk = unk[pot_kind, field_kind, i_interface] - if my_unk: - field_result += ( - self.density_coeffs[ - pot_kind, field_kind, i_interface, side] - * self.potential_ops[pot_kind]( - self.kernel, - my_unk, - source=interface_id, - k=self.domain_K_exprs[i_domain] - )) - - result.append(field_result) - - from pytools.obj_array import make_obj_array - return make_obj_array(result) - - def operator(self, unknown): - density_unk = self._structured_unknown(unknown, with_l2_weights=False) - discrete_unk = self._structured_unknown(unknown, with_l2_weights=True) - - result = [] - for bc in self.bcs: - op = 0 - - for pot_kind in self.pot_kinds: - for term in bc: - - for side in self.sides: - raw_potential_op = \ - self.potential_ops[pot_kind] - - unk_index = (pot_kind, term.field_kind, term.i_interface) - density = density_unk[unk_index] - discrete = discrete_unk[unk_index] - - op += ( - self.density_coeffs[ - pot_kind, term.field_kind, term.i_interface, - side] - * self.bc_term_to_operator_contrib( - term, side, raw_potential_op, density, discrete) - ) - - result.append(op) - - return np.array(result, dtype=np.object) - -# }}} - -# }}} - # vim: foldmethod=marker From 90056c14859e9a2cb5fb56c3e51ba9e0b2699706 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 10 Jul 2020 15:38:20 -0500 Subject: [PATCH 68/75] Move flatten() import to top of file, and don't use a separate assignment for calling flatten() --- pytential/qbx/refinement.py | 19 ++++++++----------- pytential/qbx/target_assoc.py | 23 +++++++++-------------- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index 78f329867..b0a3c28e0 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -30,6 +30,7 @@ import loopy as lp from loopy.version import MOST_RECENT_LANGUAGE_VERSION from meshmode.array_context import PyOpenCLArrayContext +from meshmode.dof_array import flatten import numpy as np import pyopencl as cl @@ -310,12 +311,10 @@ def check_expansion_disks_undisturbed_by_sources(self, unwrap_args = AreaQueryElementwiseTemplate.unwrap_args from pytential import bind, sym - center_danger_zone_radii = bind(stage1_density_discr, + center_danger_zone_radii = flatten( + bind(stage1_density_discr, sym.expansion_radii(stage1_density_discr.ambient_dim, - granularity=sym.GRANULARITY_CENTER))(self.array_context) - - from meshmode.dof_array import flatten - center_danger_zone_radii = flatten(center_danger_zone_radii) + granularity=sym.GRANULARITY_CENTER))(self.array_context)) evt = knl( *unwrap_args( @@ -371,13 +370,11 @@ def check_sufficient_source_quadrature_resolution(self, from pytential import bind, sym dd = sym.as_dofdesc(sym.GRANULARITY_ELEMENT).to_stage2() - source_danger_zone_radii_by_panel = bind(stage2_density_discr, - sym._source_danger_zone_radii( - stage2_density_discr.ambient_dim, dofdesc=dd))( - self.array_context) - from meshmode.dof_array import flatten source_danger_zone_radii_by_panel = flatten( - source_danger_zone_radii_by_panel) + bind(stage2_density_discr, + sym._source_danger_zone_radii( + stage2_density_discr.ambient_dim, dofdesc=dd) + (self.array_context))) unwrap_args = AreaQueryElementwiseTemplate.unwrap_args evt = knl( diff --git a/pytential/qbx/target_assoc.py b/pytential/qbx/target_assoc.py index a25249768..64aecae6d 100644 --- a/pytential/qbx/target_assoc.py +++ b/pytential/qbx/target_assoc.py @@ -528,11 +528,10 @@ def mark_targets(self, places, dofdesc, sources = [ axis.with_queue(self.queue)[source_slice] for axis in tree.sources] - tunnel_radius_by_source = bind(places, - sym._close_target_tunnel_radii(ambient_dim, dofdesc=dofdesc))( - self.array_context) - - tunnel_radius_by_source = flatten(tunnel_radius_by_source) + tunnel_radius_by_source = flatten( + bind(places, + sym._close_target_tunnel_radii(ambient_dim, dofdesc=dofdesc)) + (self.array_context)) # Target-marking algorithm (TGTMARK): # @@ -630,11 +629,8 @@ def find_centers(self, places, dofdesc, ambient_dim, granularity=sym.GRANULARITY_CENTER, dofdesc=dofdesc))(self.array_context) - expansion_radii_by_center_with_tolerance = \ - expansion_radii_by_center * (1 + target_association_tolerance) - expansion_radii_by_center_with_tolerance = flatten( - expansion_radii_by_center_with_tolerance) + expansion_radii_by_center * (1 + target_association_tolerance)) # Idea: # @@ -729,11 +725,10 @@ def mark_panels_for_refinement(self, places, dofdesc, sources = [ axis.with_queue(self.queue)[source_slice] for axis in tree.sources] - tunnel_radius_by_source = bind(places, - sym._close_target_tunnel_radii(ambient_dim, dofdesc=dofdesc))( - self.array_context) - - tunnel_radius_by_source = flatten(tunnel_radius_by_source) + tunnel_radius_by_source = flatten( + bind(places, + sym._close_target_tunnel_radii(ambient_dim, dofdesc=dofdesc)) + (self.array_context)) # see (TGTMARK) above for algorithm. From 0c63898a19a93f42eb0eff6a198fbcd82550a103 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 10 Jul 2020 15:48:47 -0500 Subject: [PATCH 69/75] pytential.source: Introduce _SumpyP2PMixin --- pytential/source.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pytential/source.py b/pytential/source.py index 62ed25633..19d90ae7a 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -62,7 +62,13 @@ def complex_dtype(self): raise NotImplementedError def get_p2p(self, actx, kernels): - @memoize_in(actx, (PotentialSource, "p2p")) + raise NotImplementedError + + +class _SumpyP2PMixin(object): + + def get_p2p(self, actx, kernels): + @memoize_in(actx, (_SumpyP2PMixin, "p2p")) def p2p(kernels): from pytools import any if any(knl.is_complex_valued for knl in kernels): @@ -72,14 +78,14 @@ def p2p(kernels): from sumpy.p2p import P2P return P2P(actx.context, - kernels, exclude_self=False, value_dtypes=value_dtype) + kernels, exclude_self=False, value_dtypes=value_dtype) return p2p(kernels) # {{{ point potential source -class PointPotentialSource(PotentialSource): +class PointPotentialSource(_SumpyP2PMixin, PotentialSource): """ .. attribute:: nodes @@ -186,7 +192,7 @@ def exec_compute_potential_insn(self, actx, insn, bound_expr, evaluate, # {{{ layer potential source -class LayerPotentialSourceBase(PotentialSource): +class LayerPotentialSourceBase(_SumpyP2PMixin, PotentialSource): """A discretization of a layer potential using panel-based geometry, with support for refinement and upsampling. From a86f7a977b153345ccbac9c715b22891e3dd89e0 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 10 Jul 2020 16:14:54 -0500 Subject: [PATCH 70/75] Fix incorrect parentheses placement --- pytential/qbx/refinement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pytential/qbx/refinement.py b/pytential/qbx/refinement.py index b0a3c28e0..dbcfe657b 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -373,8 +373,8 @@ def check_sufficient_source_quadrature_resolution(self, source_danger_zone_radii_by_panel = flatten( bind(stage2_density_discr, sym._source_danger_zone_radii( - stage2_density_discr.ambient_dim, dofdesc=dd) - (self.array_context))) + stage2_density_discr.ambient_dim, dofdesc=dd)) + (self.array_context)) unwrap_args = AreaQueryElementwiseTemplate.unwrap_args evt = knl( From 0f6471b2aebe3810bfb132d8221880530b646c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kl=C3=B6ckner?= Date: Sat, 11 Jul 2020 23:53:54 +0200 Subject: [PATCH 71/75] Re-dangle islpy back to master --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d8fa9a6db..6ce07e8a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ git+git://github.com/inducer/pymbolic sympy==1.1.1 git+https://github.com/inducer/modepy git+https://github.com/inducer/pyopencl -git+https://gitlab.tiker.net/inducer/islpy@revert-cffi-go-to-pybind +git+https://github.com/inducer/islpy git+https://github.com/inducer/loopy git+https://gitlab.tiker.net/inducer/boxtree git+https://gitlab.tiker.net/inducer/meshmode@array-context From 2fc4fb189f26efc4c63f3ec1cc1fa66642bbfbeb Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Sat, 11 Jul 2020 17:40:51 -0500 Subject: [PATCH 72/75] Move Apple CI to Github --- .github/workflows/ci.yml | 25 +++++++++++++++++++++---- .gitlab-ci.yml | 23 ----------------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5dc64f57c..ea924eb0d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/setup-python@v1 with: - python-version: '3.x' + python-version: '3.x' - name: "Main Script" run: | curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/prepare-and-run-flake8.sh @@ -32,7 +32,7 @@ jobs: - uses: actions/setup-python@v1 with: - python-version: '3.x' + python-version: '3.x' - name: "Main Script" run: | set -e @@ -51,18 +51,35 @@ jobs: . ./prepare-and-run-pylint.sh pytential test/test_*.py pytest3: - name: Pytest on Py3 + name: Pytest Linux runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: "Main Script" run: | - set -e grep -v symengine .test-conda-env-py3.yml > .test-conda-env.yml CONDA_ENVIRONMENT=.test-conda-env.yml curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh . ./build-and-test-py-project-within-miniconda.sh + pytest3mac: + name: Pytest Mac + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: "Main Script" + run: | + export LC_ALL=en_US.UTF-8 + export LANG=en_US.UTF-8 + export CONDA_ENVIRONMENT=.test-conda-env-py3-macos.yml + export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} + export CC=clang + # https://stackoverflow.com/q/60934005; https://reviews.llvm.org/D71579 + export LDFLAGS="-mlinker-version=519" + set -o xtrace + curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh + . ./build-and-test-py-project-within-miniconda.sh + pytest3symengine: name: Pytest on Py3 with SymEngine runs-on: ubuntu-latest diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f9c12b45f..dae0a27cd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -73,29 +73,6 @@ Python 3 Conda: reports: junit: test/pytest.xml -Python 3 Conda Apple: - script: - - export LC_ALL=en_US.UTF-8 - - export LANG=en_US.UTF-8 - - export CONDA_ENVIRONMENT=.test-conda-env-py3-macos.yml - - export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} - - export CC=clang - # https://stackoverflow.com/q/60934005; https://reviews.llvm.org/D71579 - - export LDFLAGS="-mlinker-version=519" - - set -o xtrace - - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh - - ". ./build-and-test-py-project-within-miniconda.sh" - - tags: - - apple - except: - - tags - retry: 2 - - artifacts: - reports: - junit: test/pytest.xml - Documentation: script: - EXTRA_INSTALL="Cython pybind11 numpy mako" From ba588a6591f1f98906ddc83d851e4216b483929c Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Sat, 11 Jul 2020 17:59:50 -0500 Subject: [PATCH 73/75] Try mac CI with normal conda env without mac workarounds --- .github/workflows/ci.yml | 6 ++---- .test-conda-env-py3-macos.yml | 30 ------------------------------ 2 files changed, 2 insertions(+), 34 deletions(-) delete mode 100644 .test-conda-env-py3-macos.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea924eb0d..35202a6cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,11 +71,9 @@ jobs: run: | export LC_ALL=en_US.UTF-8 export LANG=en_US.UTF-8 - export CONDA_ENVIRONMENT=.test-conda-env-py3-macos.yml + grep -v symengine .test-conda-env-py3.yml > .test-conda-env.yml + CONDA_ENVIRONMENT=.test-conda-env.yml export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} - export CC=clang - # https://stackoverflow.com/q/60934005; https://reviews.llvm.org/D71579 - export LDFLAGS="-mlinker-version=519" set -o xtrace curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project-within-miniconda.sh . ./build-and-test-py-project-within-miniconda.sh diff --git a/.test-conda-env-py3-macos.yml b/.test-conda-env-py3-macos.yml deleted file mode 100644 index ba5a52e99..000000000 --- a/.test-conda-env-py3-macos.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: test-conda-env-py3-macos -channels: -- conda-forge -- nodefaults -dependencies: -- git -- conda-forge::numpy -- conda-forge::sympy -- scipy -- pocl -- islpy -- pyopencl -- python>=3.6 -- python-symengine=0.6.0 -- pyfmmlib -# for OpenMP support in pyfmmlib -- libgfortran>=3.0.1 -- clangdev -- openmp -- cython -- gmsh - -- pip -- pip: - - git+https://github.com/inducer/pytools - - git+https://gitlab.tiker.net/inducer/boxtree - - git+https://github.com/inducer/pymbolic - - git+https://github.com/inducer/loopy - - git+https://gitlab.tiker.net/inducer/sumpy - - git+https://gitlab.tiker.net/inducer/meshmode@array-context From 753a54a0edbfcd2487dbba47013c567f1aad317a Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Sat, 11 Jul 2020 18:16:29 -0500 Subject: [PATCH 74/75] mac CI: Try and install compiler from conda --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35202a6cb..784c96778 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,6 +72,8 @@ jobs: export LC_ALL=en_US.UTF-8 export LANG=en_US.UTF-8 grep -v symengine .test-conda-env-py3.yml > .test-conda-env.yml + echo "- compiler" >> .test-conda-env.yml + echo "- openmp" >> .test-conda-env.yml CONDA_ENVIRONMENT=.test-conda-env.yml export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} set -o xtrace From 4e7f2db1d563f1b566184cc1ff76835c3cf88a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kl=C3=B6ckner?= Date: Sat, 11 Jul 2020 18:43:49 -0500 Subject: [PATCH 75/75] Apply suggestions from @isuruf to fix Github Mac CI Co-authored-by: Isuru Fernando --- .github/workflows/ci.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 784c96778..f483c67a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,8 +72,8 @@ jobs: export LC_ALL=en_US.UTF-8 export LANG=en_US.UTF-8 grep -v symengine .test-conda-env-py3.yml > .test-conda-env.yml - echo "- compiler" >> .test-conda-env.yml - echo "- openmp" >> .test-conda-env.yml + echo "- compilers" >> .test-conda-env.yml + echo "- llvm-openmp" >> .test-conda-env.yml CONDA_ENVIRONMENT=.test-conda-env.yml export PYTEST_ADDOPTS=${PYTEST_ADDOPTS:--k-slowtest} set -o xtrace @@ -93,4 +93,3 @@ jobs: . ./build-and-test-py-project-within-miniconda.sh # vim: sw=4 -