From 591519914ae3ba3418212731d7b3e1974c7340d6 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Mon, 15 Jun 2020 13:19:46 -0500 Subject: [PATCH 01/76] 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 728ce196..1f548deb 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 9e3833f0..204b15e0 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -38,6 +38,9 @@ import pyopencl.clmath # noqa 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 @@ class EvaluationMapperBase(PymbolicEvaluationMapper): 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 @@ class EvaluationMapperBase(PymbolicEvaluationMapper): 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 @@ class EvaluationMapperBase(PymbolicEvaluationMapper): 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 @@ class EvaluationMapperBase(PymbolicEvaluationMapper): 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 @@ class EvaluationMapperBase(PymbolicEvaluationMapper): 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 @@ class EvaluationMapperBase(PymbolicEvaluationMapper): 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 @@ class GeometryCollection(object): 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 @@ class GeometryCollection(object): 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 @@ class GeometryCollection(object): 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 @@ class GeometryCollection(object): 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 @@ class GeometryCollection(object): 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 @@ class BoundExpression(object): 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 @@ class BoundExpression(object): :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): -- GitLab From 66fc944064121c7507d317c22b401f43dacc5fae Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 17 Jun 2020 18:27:50 -0500 Subject: [PATCH 02/76] 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 204b15e0..fdd5db5f 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -25,6 +25,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +from typing import Optional + import six from six.moves import zip @@ -870,7 +872,7 @@ class BoundExpression(object): 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 @@ class BoundExpression(object): 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 @@ class BoundExpression(object): 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 -- GitLab From 3dbc2c84cc3624628cd4d1fbe634e9fb29729361 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 17 Jun 2020 18:29:52 -0500 Subject: [PATCH 03/76] 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 fdd5db5f..d19c8d67 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -320,7 +320,7 @@ class EvaluationMapperBase(PymbolicEvaluationMapper): 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) -- GitLab From 273ba51a959b35dd007b39957a8d15765dca53ef Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Wed, 1 Jul 2020 22:07:20 -0500 Subject: [PATCH 04/76] 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 f9c12b45..8c87a27d 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: -- GitLab From cf635e0b4ea9c899473f7b602df32f2954823a31 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Wed, 1 Jul 2020 22:31:09 -0500 Subject: [PATCH 05/76] Update requirements.txt --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 625deb28..e5cac035 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 -- GitLab From 459ec80e1423c2637f7ca2dd58df9f06ee7bdaa0 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 00:06:13 -0500 Subject: [PATCH 06/76] 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 71c11680..0070aaa6 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 @@ TRAINING_ARMS = (10, 15, 25) 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 a6d19bb1..77281aa5 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 75115da4..aee8c390 100644 --- a/examples/helmholtz-dirichlet.py +++ b/examples/helmholtz-dirichlet.py @@ -3,6 +3,7 @@ import numpy.linalg as la 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 main(mesh_name="ellipse", visualize=False): - 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 main(mesh_name="ellipse", visualize=False): 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 984f1de1..7f6bba45 100644 --- a/examples/laplace-dirichlet-3d.py +++ b/examples/laplace-dirichlet-3d.py @@ -3,6 +3,7 @@ import numpy.linalg as la 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 main(mesh_name="torus", visualize=False): + 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 ecace75d..78112858 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 e01a24eb..4980be67 100644 --- a/examples/layerpot.py +++ b/examples/layerpot.py @@ -23,8 +23,8 @@ k = 0 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 main(curve_fn=starfish, visualize=True): 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 main(curve_fn=starfish, visualize=True): ]) 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 main(curve_fn=starfish, visualize=True): 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 21a85019..a14a5aed 100644 --- a/examples/scaling-study.py +++ b/examples/scaling-study.py @@ -2,6 +2,7 @@ import numpy as np 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 a23dd3d7..31930f85 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 @@ THE SOFTWARE. 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): # 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): # }}} + # {{{ 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): _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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): # 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): *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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 eac3c7fc..1bcee7c0 100644 --- a/pytential/qbx/direct.py +++ b/pytential/qbx/direct.py @@ -109,7 +109,11 @@ class LayerPotentialOnTargetAndCenterSubset(LayerPotentialBase): 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 8b9ea429..5185376e 100644 --- a/pytential/qbx/fmm.py +++ b/pytential/qbx/fmm.py @@ -225,8 +225,8 @@ QBXFMMGeometryData.non_qbx_box_target_lists`), 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 @@ QBXFMMGeometryData.non_qbx_box_target_lists`), ), 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 @@ QBXFMMGeometryData.non_qbx_box_target_lists`), 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 @@ QBXFMMGeometryData.non_qbx_box_target_lists`), 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 drive_fmm(expansion_wrangler, src_weights, timing_data=None, # 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 7a991ddf..4c9e124a 100644 --- a/pytential/qbx/geometry.py +++ b/pytential/qbx/geometry.py @@ -28,6 +28,9 @@ import numpy as np 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 @@ Enums of special values 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 @@ class QBXFMMGeometryData(FMMLibRotationDataInterface): 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 @@ class QBXFMMGeometryData(FMMLibRotationDataInterface): 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 @@ class QBXFMMGeometryData(FMMLibRotationDataInterface): 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 @@ class QBXFMMGeometryData(FMMLibRotationDataInterface): 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 @@ class QBXFMMGeometryData(FMMLibRotationDataInterface): 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 @@ class QBXFMMGeometryData(FMMLibRotationDataInterface): 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 @@ class QBXFMMGeometryData(FMMLibRotationDataInterface): # {{{ 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 b3c28ee6..af48b212 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 @@ THE SOFTWARE. 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 @@ SUFFICIENT_SOURCE_QUADRATURE_RESOLUTION_CHECKER = AreaQueryElementwiseTemplate( 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 @@ class RefinerCodeContainer(TreeCodeContainerMixin): 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 @@ class RefinerCodeContainer(TreeCodeContainerMixin): 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 @@ class RefinerCodeContainer(TreeCodeContainerMixin): 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 @@ class RefinerWrangler(TreeWranglerBase): 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 @@ class RefinerWrangler(TreeWranglerBase): 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 @@ class RefinerWrangler(TreeWranglerBase): 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 @@ class RefinerWrangler(TreeWranglerBase): 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 _refine_for_global_qbx(places, dofdesc, wrangler, # {{{ 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 39b226ab..eda3dd8f 100644 --- a/pytential/qbx/target_assoc.py +++ b/pytential/qbx/target_assoc.py @@ -36,6 +36,8 @@ from boxtree.tools import DeviceDataRecord 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 @@ class TargetAssociationCodeContainer(TreeCodeContainerMixin): 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 @@ class TargetAssociationWrangler(TreeWranglerBase): source_slice = tree.sorted_target_ids[tree.qbx_user_source_slice] sources = [ axis.with_queue(self.queue)[source_slice] for axis in tree.sources] + tunnel_radius_by_source = bind(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 @@ class TargetAssociationWrangler(TreeWranglerBase): 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 @@ class TargetAssociationWrangler(TreeWranglerBase): source_slice = tree.user_source_ids[tree.qbx_user_source_slice] sources = [ axis.with_queue(self.queue)[source_slice] for axis in tree.sources] + tunnel_radius_by_source = bind(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 @@ class TargetAssociationWrangler(TreeWranglerBase): 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 b872152a..8d597083 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 @@ THE SOFTWARE. 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 @@ QBX_TREE_MAKO_DEFS = r"""//CL:mako// 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 @@ class TreeCodeContainerMixin(object): 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 @@ class TreeWranglerBase(object): 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 @@ MAX_REFINE_WEIGHT = 64 @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 build_tree_with_qbx_metadata(queue, places, 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 build_tree_with_qbx_metadata(queue, places, 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 @@ class ToHostTransferredGeoDataWrapper(FMMLibRotationDataInterface): 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 c2e93243..76d7958e 100644 --- a/pytential/solve.py +++ b/pytential/solve.py @@ -22,9 +22,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ - -from six.moves import range - __doc__ = """ .. autofunction:: gmres @@ -37,52 +34,22 @@ __doc__ = """ """ -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 @@ class VectorChopper(object): # 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 @@ class ResidualPrinter: # {{{ 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 7ed794ab..475da21a 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -26,7 +26,7 @@ THE SOFTWARE. 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 @@ class PointPotentialSource(PotentialSource): 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 @@ class PointPotentialSource(PotentialSource): for arg_name, arg_expr in six.iteritems(insn.kernel_arguments): kernel_args[arg_name] = evaluate(arg_expr) - strengths = evaluate(insn.density).with_queue(queue).copy() + strengths = evaluate(insn.density) # FIXME: Do this all at once result = [] @@ -155,10 +162,12 @@ class PointPotentialSource(PotentialSource): # 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 @@ class LayerPotentialSourceBase(PotentialSource): @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 @@ class LayerPotentialSourceBase(PotentialSource): 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 @@ class LayerPotentialSourceBase(PotentialSource): 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 @@ class LayerPotentialSourceBase(PotentialSource): 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 3fcfbf2e..eb01532d 100644 --- a/pytential/symbolic/compiler.py +++ b/pytential/symbolic/compiler.py @@ -270,13 +270,13 @@ def dot_dataflow_graph(code, max_node_label_length=30, 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 @@ class Code(object): 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 @@ class Code(object): 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 @@ class Code(object): 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 @@ class Code(object): 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 @@ class OperatorCompiler(IdentityMapper): # 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 9d23fb73..745f927b 100644 --- a/pytential/symbolic/dof_connection.py +++ b/pytential/symbolic/dof_connection.py @@ -27,12 +27,12 @@ THE SOFTWARE. """ 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 @@ class GranularityConnection(object): 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 @@ class GeometryCollection(object): 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 @@ class GeometryCollection(object): 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 @@ class GeometryCollection(object): 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 @@ class GeometryCollection(object): 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 @@ class GeometryCollection(object): 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 @@ class BoundExpression(object): 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 @@ class BoundExpression(object): 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 @@ class BoundExpression(object): 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 @@ class BoundExpression(object): # 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 @@ class BoundExpression(object): (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 @@ class BoundExpression(object): 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 @@ class BoundExpression(object): 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 d89d393c..f7148cfd 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 @@ class PECChargeCurrentMFIEOperator: 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 @@ class MuellerAugmentedMFIEOperator(object): # 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 @@ class MuellerAugmentedMFIEOperator(object): 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 1b6ea8ee..df7fd9f5 100644 --- a/pytential/symbolic/pde/maxwell/generalized_debye.py +++ b/pytential/symbolic/pde/maxwell/generalized_debye.py @@ -153,8 +153,8 @@ class DebyeOperatorBase(object): 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 @@ class DebyeOperatorBase(object): 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 @@ class InvertingDebyeOperatorBase(DebyeOperatorBase): 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 @@ class NonInvertingDebyeOperator(DebyeOperatorBase): 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 1799e09b..e3ab0821 100644 --- a/pytential/symbolic/primitives.py +++ b/pytential/symbolic/primitives.py @@ -36,7 +36,7 @@ from pymbolic.geometric_algebra import MultiVector, componentwise 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 @@ Object types 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 @@ DOF Description .. autoclass:: DOFDescriptor .. autofunction:: as_dofdesc +.. _placeholders: + Placeholders ^^^^^^^^^^^^ @@ -472,12 +479,12 @@ class Function(var): # 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 e677bdb7..fc5dc9bf 100644 --- a/pytential/target.py +++ b/pytential/target.py @@ -73,4 +73,7 @@ class PointsTarget(TargetBase): 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 6f0125cd..51399d0e 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -108,11 +108,11 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): "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 0849c083..aafdf685 100644 --- a/pytential/version.py +++ b/pytential/version.py @@ -43,7 +43,7 @@ else: # }}} -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 ae80e32d..89c01ca0 100644 --- a/setup.py +++ b/setup.py @@ -119,7 +119,7 @@ setup(name="pytential", "boxtree>=2019.1", "pymbolic>=2013.2", "loo.py>=2017.2", - "sumpy>=2013.1", + "sumpy>=2020.2", "cgen>=2013.1.2", "pyfmmlib>=2019.1.1", -- GitLab From 13067297a9728ae4fe0f782d2835cd92c6d78dac Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 00:13:10 -0500 Subject: [PATCH 07/76] Relax sumpy version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 89c01ca0..ae80e32d 100644 --- a/setup.py +++ b/setup.py @@ -119,7 +119,7 @@ setup(name="pytential", "boxtree>=2019.1", "pymbolic>=2013.2", "loo.py>=2017.2", - "sumpy>=2020.2", + "sumpy>=2013.1", "cgen>=2013.1.2", "pyfmmlib>=2019.1.1", -- GitLab From c738c07950cb106f58b15ec7b803ea652d0d0a3e Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 00:18:40 -0500 Subject: [PATCH 08/76] 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 00000000..fb772c0f --- /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 -- GitLab From c1678d12bce7223b8ada2ec7f2532a5062c5bfea Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 09:33:37 -0500 Subject: [PATCH 09/76] 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 4c9e124a..1fa28a18 100644 --- a/pytential/qbx/geometry.py +++ b/pytential/qbx/geometry.py @@ -958,7 +958,7 @@ class QBXFMMGeometryData(FMMLibRotationDataInterface): 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 51399d0e..386bcc02 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -138,7 +138,7 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): 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 2ebac61c..1edc7d9b 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 c7eeb200..25f19144 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 test_3d_jump_relations(ctx_factory, relation, visualize=False): 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 7b0cbc65..62c1c395 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 2e950193..129cd217 100644 --- a/test/test_maxwell.py +++ b/test/test_maxwell.py @@ -415,12 +415,12 @@ def test_pec_mfie_extinction(ctx_factory, case, 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 test_pec_mfie_extinction(ctx_factory, case, 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 37b86c12..99c633c8 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 d2f10784..b95c99e2 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): -- GitLab From 657c35f659e8fdb50770b0ecab7a1b85473105e9 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 09:36:33 -0500 Subject: [PATCH 10/76] 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 99c633c8..6425c9a0 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 b95c99e2..53f91a00 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 -- GitLab From 51fa634328fb2a7b5c7241cee68cfa54204c57ea Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 09:40:10 -0500 Subject: [PATCH 11/76] 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 e5cac035..f78b6cfc 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 -- GitLab From 8a70c0df8bb0a6dda5f24b224157c948424ac543 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 10:03:24 -0500 Subject: [PATCH 12/76] 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 8c87a27d..f9828212 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 a353f3f7..b59a3fd4 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 1edc7d9b..e99ce867 100644 --- a/test/test_cost_model.py +++ b/test/test_cost_model.py @@ -26,6 +26,7 @@ import numpy as np 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 @@ DEFAULT_LPOT_KWARGS = { } -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 test_cost_model_order_varying_by_level(ctx_factory): 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 551bb38c..f4749260 100644 --- a/test/test_target_specific_qbx.py +++ b/test/test_target_specific_qbx.py @@ -23,6 +23,7 @@ THE SOFTWARE. """ +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 53f91a00..cebabc73 100644 --- a/test/test_tools.py +++ b/test/test_tools.py @@ -24,6 +24,7 @@ THE SOFTWARE. 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 -- GitLab From 54a125ab160991303c3074aefafefab595106cfe Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 10:04:05 -0500 Subject: [PATCH 13/76] 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 f4749260..440e7486 100644 --- a/test/test_target_specific_qbx.py +++ b/test/test_target_specific_qbx.py @@ -27,7 +27,6 @@ from meshmode.array_context import PyOpenCLArrayContext 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) -- GitLab From 36919c9ba0da78d45c417b2cc2427d8713639171 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 10:13:04 -0500 Subject: [PATCH 14/76] 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 cebabc73..89bfcd78 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): -- GitLab From 16864eca15a19fbb4f9ede3c67ce9aebae2d16c3 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 13:02:20 -0500 Subject: [PATCH 15/76] 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 b59a3fd4..155dc443 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 6425c9a0..a510596c 100644 --- a/test/test_symbolic.py +++ b/test/test_symbolic.py @@ -39,6 +39,7 @@ logger = logging.getLogger(__name__) 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 @@ from meshmode.discretization.poly_element import \ # {{{ 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) -- GitLab From fddc8c4ba0e65ed7315ca79c08fb2b7c1776bb0b Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 13:05:56 -0500 Subject: [PATCH 16/76] 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 155dc443..7a4d7d68 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 -- GitLab From 473de87c87231702e13bdd52b0b1654577745782 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 13:07:40 -0500 Subject: [PATCH 17/76] formatting --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 7a4d7d68..fbd2809c 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 -- GitLab From bb6d56e3f7a67c096b9db771b7a2331d65d1ccef Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 13:22:31 -0500 Subject: [PATCH 18/76] 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 fbd2809c..4e0403d5 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 9ab5b692..836508cd 100644 --- a/test/test_global_qbx.py +++ b/test/test_global_qbx.py @@ -37,6 +37,7 @@ from pyopencl.tools import pytest_generate_tests_for_pyopencl \ 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 @@ RNG_SEED = 10 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 test_target_association(ctx_factory, curve_name, curve_f, nelements, (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 test_target_association(ctx_factory, curve_name, curve_f, nelements, 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 test_target_association(ctx_factory, curve_name, curve_f, nelements, 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) -- GitLab From a7d8282880e127ce51fa7c104afd6452d83b8543 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 13:37:38 -0500 Subject: [PATCH 19/76] 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 eda3dd8f..a2524976 100644 --- a/pytential/qbx/target_assoc.py +++ b/pytential/qbx/target_assoc.py @@ -733,6 +733,8 @@ class TargetAssociationWrangler(TreeWranglerBase): 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 @@ class TargetAssociationWrangler(TreeWranglerBase): 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 8d597083..433ce10e 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -304,7 +304,7 @@ def build_tree_with_qbx_metadata(actx: PyOpenCLArrayContext, 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 836508cd..6ea108ab 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, -- GitLab From 55a2b362c6da8abe1cac7557f37b51253d6e368f Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 14:06:50 -0500 Subject: [PATCH 20/76] 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 4e0403d5..f9d8c5ea 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 af2278eb..b5797913 100644 --- a/test/test_layer_pot_eigenvalues.py +++ b/test/test_layer_pot_eigenvalues.py @@ -26,12 +26,12 @@ THE SOFTWARE. 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 test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, 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 test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, ) 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 test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, 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 test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, 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 test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, 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 test_sphere_eigenvalues(ctx_factory, mode_m, mode_n, qbx_order, 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)) -- GitLab From ce86dc69dfc64a156e52f1a8a84156e7a728c847 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 14:40:31 -0500 Subject: [PATCH 21/76] 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 f78b6cfc..d8fa9a6d 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 -- GitLab From b32792e8729ab39e841e72389648857ad17d5697 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 14:59:06 -0500 Subject: [PATCH 22/76] 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 31930f85..66c04b11 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -25,7 +25,7 @@ THE SOFTWARE. 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 # }}} -- GitLab From 72a09b04750549d5d46f5b89d23a1f5b8fa5337c Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 15:30:01 -0500 Subject: [PATCH 23/76] 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 62c1c395..b206e78d 100644 --- a/test/test_layer_pot_identity.py +++ b/test/test_layer_pot_identity.py @@ -32,6 +32,7 @@ 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, 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, [ -- GitLab From 7ce0165d251db4a0833535e71495789492168ac3 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 15:34:16 -0500 Subject: [PATCH 24/76] 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 f9d8c5ea..d197df45 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 -- GitLab From 25582e573f766138551e30e01fe775415da28d58 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 16:53:47 -0500 Subject: [PATCH 25/76] 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 66c04b11..5a9ddf7e 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -223,10 +223,6 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): # 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 475da21a..09cbf364 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -206,6 +206,10 @@ class LayerPotentialSourceBase(PotentialSource): 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 386bcc02..4b0e896f 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -27,6 +27,7 @@ THE SOFTWARE. import six +from meshmode.array_context import PyOpenCLArrayContext import numpy as np import loopy as lp @@ -99,8 +100,8 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): 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 @@ class _FMMGeometryData(object): @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 @@ class _FMMGeometryData(object): 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 @@ class _FMMGeometryData(object): 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 d197df45..34699863 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 25f19144..5e63232d 100644 --- a/test/test_layer_pot.py +++ b/test/test_layer_pot.py @@ -26,12 +26,12 @@ THE SOFTWARE. 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 @@ circle = partial(ellipse, 1) 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 test_3d_jump_relations(ctx_factory, relation, visualize=False): 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 test_3d_jump_relations(ctx_factory, relation, visualize=False): # 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 test_3d_jump_relations(ctx_factory, relation, visualize=False): 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 test_3d_jump_relations(ctx_factory, relation, visualize=False): # {{{ 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 test_3d_jump_relations(ctx_factory, relation, visualize=False): 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), -- GitLab From 01872e98236fd24119cefb6558c29a4158fefe0f Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 2 Jul 2020 22:05:03 -0500 Subject: [PATCH 26/76] 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 e46f7509..4b36a56b 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 d63f3054..12ec489a 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -26,14 +26,13 @@ THE SOFTWARE. """ 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 @@ class MatrixBuilderBase(EvaluationMapperBase): """ 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 @@ class MatrixBuilderBase(EvaluationMapperBase): # {{{ 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 @@ class MatrixBuilderBase(EvaluationMapperBase): 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 @@ class MatrixBuilderBase(EvaluationMapperBase): 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 @@ class MatrixBlockBuilderBase(MatrixBuilderBase): # 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 @@ class MatrixBlockBuilderBase(MatrixBuilderBase): # 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 @@ class MatrixBlockBuilderBase(MatrixBuilderBase): # 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 @@ class MatrixBuilder(MatrixBuilderBase): 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 @@ class MatrixBuilder(MatrixBuilderBase): 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 @@ class MatrixBuilder(MatrixBuilderBase): 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 @@ class MatrixBuilder(MatrixBuilderBase): 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 @@ class MatrixBuilder(MatrixBuilderBase): # {{{ 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 @@ class P2PMatrixBuilder(MatrixBuilderBase): 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 @@ class NearFieldBlockBuilder(MatrixBlockBuilderBase): 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 @@ class NearFieldBlockBuilder(MatrixBlockBuilderBase): 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 @@ class NearFieldBlockBuilder(MatrixBlockBuilderBase): 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 @@ class FarFieldBlockBuilder(MatrixBlockBuilderBase): 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 @@ class FarFieldBlockBuilder(MatrixBlockBuilderBase): 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 12be496c..5de6780a 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -38,9 +38,10 @@ from pytools.obj_array import make_obj_array, is_obj_array 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 @@ except ImportError: 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), -- GitLab From c3077e9cf16edd73a80c611917edf68da140ad63 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 23:01:44 -0500 Subject: [PATCH 27/76] 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 09cbf364..db2781db 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 @@ class PointPotentialSource(PotentialSource): 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 @@ class PointPotentialSource(PotentialSource): 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 34699863..35cae4c5 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 bc197395..28570550 100644 --- a/test/test_scalar_int_eq.py +++ b/test/test_scalar_int_eq.py @@ -32,6 +32,7 @@ import pytest 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 @@ class BetterplaneIntEqTestCase(IntEqTestCase): # {{{ 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 -- GitLab From 80ab289c30b1e854673cb6394bef421ed3304a58 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 2 Jul 2020 23:01:59 -0500 Subject: [PATCH 28/76] 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 ba2e2ea9..a87e0785 100644 --- a/pytential/linalg/proxy.py +++ b/pytential/linalg/proxy.py @@ -26,10 +26,6 @@ THE SOFTWARE. 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 @@ class ProxyGenerator(object): """.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 @@ class ProxyGenerator(object): 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 @@ class ProxyGenerator(object): 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 gather_block_interaction_points(places, source_dd, indices, """, [ 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 gather_block_interaction_points(places, source_dd, indices, 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 12ec489a..3ac09c00 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -32,7 +32,8 @@ from six.moves import intern 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 @@ class MatrixBuilderBase(EvaluationMapperBase): 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 @@ class MatrixBuilderBase(EvaluationMapperBase): 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 @@ class MatrixBuilder(MatrixBuilderBase): 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 fb772c0f..be84f416 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 a4487290..8e485d6e 100644 --- a/test/test_linalg_proxy.py +++ b/test/test_linalg_proxy.py @@ -29,6 +29,8 @@ import pyopencl as cl 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 pyopencl.tools import ( # noqa 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 5de6780a..70a79d15 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) -- GitLab From 87df5814872fd5ed32ee0d93ccada2b6101f06b9 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 23:03:59 -0500 Subject: [PATCH 29/76] 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 28570550..15be8571 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 -- GitLab From 9b4ce76386731fee9c1518fae92a3f400c92b3d3 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 23:06:19 -0500 Subject: [PATCH 30/76] 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 5e63232d..9fa84435 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 15be8571..0f482cf9 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 -- GitLab From fee617b1d6465efc7ed673e83e28b160ec484e1c Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 23:34:22 -0500 Subject: [PATCH 31/76] 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 129cd217..86866fc4 100644 --- a/test/test_maxwell.py +++ b/test/test_maxwell.py @@ -31,6 +31,7 @@ import pytest 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 @@ class SphereTestCase(MaxwellTestCase): 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 @@ class SphereTestCase(MaxwellTestCase): 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 @@ class RoundedCubeTestCase(MaxwellTestCase): 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 @@ class RoundedCubeTestCase(MaxwellTestCase): 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 @@ class ElliptiPlaneTestCase(MaxwellTestCase): 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 @@ class ElliptiPlaneTestCase(MaxwellTestCase): 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 test_pec_mfie_extinction(ctx_factory, case, 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 test_pec_mfie_extinction(ctx_factory, case, 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 test_pec_mfie_extinction(ctx_factory, case, 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 test_pec_mfie_extinction(ctx_factory, case, 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 test_pec_mfie_extinction(ctx_factory, case, # {{{ 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 test_pec_mfie_extinction(ctx_factory, case, 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 test_pec_mfie_extinction(ctx_factory, case, 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 test_pec_mfie_extinction(ctx_factory, case, 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 test_pec_mfie_extinction(ctx_factory, case, 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 test_pec_mfie_extinction(ctx_factory, case, 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 test_pec_mfie_extinction(ctx_factory, case, 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( -- GitLab From 21d641d87305884c12964f2865b8b7f8c4798170 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Thu, 2 Jul 2020 23:34:44 -0500 Subject: [PATCH 32/76] 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 db2781db..549176d6 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -113,6 +113,9 @@ class PointPotentialSource(PotentialSource): 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 35cae4c5..bfd27f3e 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 -- GitLab From d4a85856f412ee52a4e817aedfeb4e559856dbbe Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 2 Jul 2020 23:36:15 -0500 Subject: [PATCH 33/76] 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 70a79d15..2b24b29e 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -31,7 +31,7 @@ import numpy as np 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 19167efe..090770e5 100644 --- a/test/test_stokes.py +++ b/test/test_stokes.py @@ -25,9 +25,10 @@ THE SOFTWARE. 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 @@ import logging 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 run_exterior_stokes_2d(ctx_factory, nelements, 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 run_exterior_stokes_2d(ctx_factory, nelements, 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 run_exterior_stokes_2d(ctx_factory, nelements, 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 run_exterior_stokes_2d(ctx_factory, nelements, - 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 run_exterior_stokes_2d(ctx_factory, nelements, 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 run_exterior_stokes_2d(ctx_factory, nelements, ]) 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 run_exterior_stokes_2d(ctx_factory, nelements, # }}} - 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 -- GitLab From 16b103d41db48423355e4d085341736a95d5c7c2 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Thu, 2 Jul 2020 23:45:18 -0500 Subject: [PATCH 34/76] enable tests in setup.cfg --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 34699863..335a4a03 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 -- GitLab From 213ee646c5db073281c33315d7fca36be54b3cca Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 00:18:02 -0500 Subject: [PATCH 35/76] 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 f9828212..b6f4b223 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" -- GitLab From b57224c42a99cb2f1c2c1241f2ea1a6463532959 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 00:20:25 -0500 Subject: [PATCH 36/76] 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 b6f4b223..a9436d5b 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" -- GitLab From 030c7c00e92bb467365dca715cee84152118bcd4 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 13:36:16 -0500 Subject: [PATCH 37/76] 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 76d7958e..3a717c67 100644 --- a/pytential/solve.py +++ b/pytential/solve.py @@ -42,6 +42,8 @@ from pytools.obj_array import obj_array_vectorize_n_args 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 _gmres(A, b, restart=None, tol=None, x0=None, dot=None, # noqa # {{{ 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): -- GitLab From af7ff8c93340b3aaa72b92ff1a0921206a5f3bbd Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 13:48:37 -0500 Subject: [PATCH 38/76] 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 e46f7509..11b96566 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -389,7 +389,11 @@ class CostModelMapper(EvaluationMapperBase): 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 @@ class MatVecOp(object): 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 @@ class MatVecOp(object): 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] -- GitLab From 0760994ca86725a0518af9b0a487d8b87b933f40 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 14:27:12 -0500 Subject: [PATCH 39/76] 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 6ea108ab..53fc2e4a 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)) -- GitLab From 37e9404ae7c6cdf9fda299b16bbff5bda0cd1322 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 3 Jul 2020 07:56:05 -0500 Subject: [PATCH 40/76] 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 2b24b29e..3c721760 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 090770e5..be2d2014 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 -- GitLab From b5f1e9de98080880f9f751f28bb1a2629714469d Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 16:34:52 -0500 Subject: [PATCH 41/76] 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 86866fc4..0fc67b0b 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: -- GitLab From 740cd158e666c254721363fb08c9f42540878a6a Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 3 Jul 2020 23:53:11 +0200 Subject: [PATCH 42/76] 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 be84f416..599fec5c 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 -- GitLab From 67b122838db19f35137c319a9253dfc16719a959 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 3 Jul 2020 23:53:17 +0200 Subject: [PATCH 43/76] 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 599fec5c..393e41d9 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 -- GitLab From c09cfb9c5ad286250d884daf08f409160db75fd6 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 3 Jul 2020 23:53:25 +0200 Subject: [PATCH 44/76] 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 be2d2014..27563ef9 100644 --- a/test/test_stokes.py +++ b/test/test_stokes.py @@ -25,7 +25,6 @@ THE SOFTWARE. import numpy as np import pyopencl as cl -import pyopencl.clmath import pytest from meshmode.array_context import PyOpenCLArrayContext -- GitLab From 94a220d490053f7f089db46417e2817fbd04bf65 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 3 Jul 2020 18:29:16 -0500 Subject: [PATCH 45/76] 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 3ac09c00..dc0eb373 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -205,7 +205,7 @@ class MatrixBuilderBase(EvaluationMapperBase): 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 @@ class MatrixBuilderBase(EvaluationMapperBase): 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 @@ class MatrixBuilder(MatrixBuilderBase): 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 393e41d9..f1e9f0d1 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 3c721760..ce429642 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 27563ef9..ec7282a0 100644 --- a/test/test_stokes.py +++ b/test/test_stokes.py @@ -186,10 +186,10 @@ def run_exterior_stokes_2d(ctx_factory, nelements, 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, -- GitLab From 6b50ccf93c8387d3d1d23981a3f9fde6c4121010 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 19:05:41 -0500 Subject: [PATCH 46/76] 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 ba2e2ea9..631a74fa 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 5a9ddf7e..db87e79a 100644 --- a/pytential/qbx/__init__.py +++ b/pytential/qbx/__init__.py @@ -829,7 +829,7 @@ class QBXLayerPotentialSource(LayerPotentialSourceBase): 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 af48b212..78f32986 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 549176d6..62ed2563 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 @@ class PointPotentialSource(PotentialSource): 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 d63f3054..4fa07091 100644 --- a/pytential/symbolic/matrix.py +++ b/pytential/symbolic/matrix.py @@ -94,7 +94,7 @@ class MatrixBuilderBase(EvaluationMapperBase): # {{{ 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 @@ class P2PMatrixBuilder(MatrixBuilderBase): 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 @@ class FarFieldBlockBuilder(MatrixBlockBuilderBase): 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 fc5dc9bf..98f876b5 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 @@ class PointsTarget(TargetBase): 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 e99ce867..42e03b2c 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 a4487290..d4a428dc 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 12be496c..30484865 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 9e9df21a..6f858eaa 100644 --- a/test/too_slow_test_helmholtz.py +++ b/test/too_slow_test_helmholtz.py @@ -370,7 +370,7 @@ def run_dielectric_test(cl_ctx, queue, nelements, qbx_order, 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"))( -- GitLab From 9303cc21232079679f2d96861016b7a0cdb89aab Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 19:06:25 -0500 Subject: [PATCH 47/76] 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 433ce10e..da804df5 100644 --- a/pytential/qbx/utils.py +++ b/pytential/qbx/utils.py @@ -340,7 +340,6 @@ def build_tree_with_qbx_metadata(actx: PyOpenCLArrayContext, flags[particle_slice].fill(1) flags.finish() - # FIXME: thaw box_to_class = ( particle_list_filter .filter_target_lists_in_user_order(queue, tree, flags) -- GitLab From b51f070204bce939a2eb550247f5bae1e3ff5a9b Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 19:08:33 -0500 Subject: [PATCH 48/76] 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 0f482cf9..82fca9ae 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( -- GitLab From d476b0981b4ecf9a1af34cf43c86b31dedf9df91 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 19:20:51 -0500 Subject: [PATCH 49/76] 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 53fc2e4a..6b8047b3 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())) -- GitLab From 56373b87d5ad7326356cb8798888c9d767e1bbd8 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 4 Jul 2020 02:32:58 +0200 Subject: [PATCH 50/76] 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 ec7282a0..5ac2ab47 100644 --- a/test/test_stokes.py +++ b/test/test_stokes.py @@ -46,7 +46,8 @@ import logging 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, -- GitLab From 27c79226e02829f903f85d6803559c1abfb386eb Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Sat, 4 Jul 2020 02:33:10 +0200 Subject: [PATCH 51/76] 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 a87e0785..42da45f6 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 -- GitLab From fb062bb3cf90659aa09ead912e71c1ac9f04c4a3 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 3 Jul 2020 19:44:23 -0500 Subject: [PATCH 52/76] 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 42da45f6..0f79148e 100644 --- a/pytential/linalg/proxy.py +++ b/pytential/linalg/proxy.py @@ -50,13 +50,6 @@ Proxy Point Generation # {{{ 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)) - # }}} -- GitLab From 58d0dfa5c5c41ba801b66aad75545ccc67af6435 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 20:26:33 -0500 Subject: [PATCH 53/76] [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 3a717c67..9a12cff4 100644 --- a/pytential/solve.py +++ b/pytential/solve.py @@ -41,7 +41,7 @@ import pyopencl.array # noqa 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 _gmres(A, b, restart=None, tol=None, x0=None, dot=None, # noqa # {{{ 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 @@ class ResidualPrinter: # {{{ 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): -- GitLab From ef9c1c4c653608606f26f2146c9e183e97240a10 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Sat, 4 Jul 2020 03:58:29 +0200 Subject: [PATCH 54/76] Undo setup.cfg changes --- setup.cfg | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index 52f59fb7..1a01ccc8 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 -- GitLab From c296c6d9c0ee3bdf06752adeee6fee7c205669e9 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 21:01:53 -0500 Subject: [PATCH 55/76] Tweak setup.cfg --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 1a01ccc8..444d02d0 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 -- GitLab From 459f2f14c01f161a78300483098657bfb3c688d8 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 22:00:42 -0500 Subject: [PATCH 56/76] 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 a9436d5b..f9c12b45 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: -- GitLab From e08ec0e6cdc986b7e790df332aef3fa53c13a0bd Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 3 Jul 2020 22:21:31 -0500 Subject: [PATCH 57/76] 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 901576dc..ba5a52e9 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 748855b0..fb8bb92d 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 -- GitLab From a2a72733bb107d92fe18697db73c2d8d2fd617f9 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 12:33:52 -0500 Subject: [PATCH 58/76] 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 77281aa5..ea50d70f 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 4980be67..2dee4223 100644 --- a/examples/layerpot.py +++ b/examples/layerpot.py @@ -23,8 +23,8 @@ k = 0 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) -- GitLab From 0a298d9ad097c696ac8c6134cd38c5715488f85c Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 16:09:44 -0500 Subject: [PATCH 59/76] 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 2dee4223..40a060fb 100644 --- a/examples/layerpot.py +++ b/examples/layerpot.py @@ -121,25 +121,20 @@ def main(curve_fn=starfish, visualize=True): 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() -- GitLab From db0a8c2db802bd05a14c21dc744e89330c9ca18a Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 16:10:03 -0500 Subject: [PATCH 60/76] 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 1fa28a18..ace246b7 100644 --- a/pytential/qbx/geometry.py +++ b/pytential/qbx/geometry.py @@ -79,7 +79,7 @@ Enums of special values 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 -- GitLab From f1d71930e9cb06020cdf3f652434104216e0183c Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 16:11:05 -0500 Subject: [PATCH 61/76] 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 4b0e896f..b7cae228 100644 --- a/pytential/unregularized.py +++ b/pytential/unregularized.py @@ -202,7 +202,7 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): @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 @@ class UnregularizedLayerPotentialSource(LayerPotentialSourceBase): # {{{ fmm tools -class _FMMGeometryCodeContainer(object): +class _FMMGeometryDataCodeContainer(object): def __init__(self, actx, ambient_dim, debug): self.array_context = actx -- GitLab From f1281a0496ae6208b2cb47c7b3374e8d0da2ec3d Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 17:32:00 -0500 Subject: [PATCH 62/76] 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 ce8910e5..3beabc2c 100644 --- a/pytential/symbolic/execution.py +++ b/pytential/symbolic/execution.py @@ -87,7 +87,7 @@ class EvaluationMapperBase(PymbolicEvaluationMapper): 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 -- GitLab From bdb3cf2dac808a58ec6e3d4231f3afe314c0ad3d Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 17:32:25 -0500 Subject: [PATCH 63/76] 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 6f858eaa..00000000 --- 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 -- GitLab From 10c3787dc991ea4c98cc250a50b2aeb897e3adb4 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Mon, 6 Jul 2020 17:33:43 -0500 Subject: [PATCH 64/76] 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 745f927b..4863652c 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 @@ class CenterGranularityConnection(GranularityConnection): 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) # }}} -- GitLab From 499ffc3827f8bfaa9a6950c4d515cc931d30bf75 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Wed, 8 Jul 2020 15:23:43 -0500 Subject: [PATCH 65/76] 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 3a1d3a63..aab6e942 100644 --- a/pytential/symbolic/pde/maxwell/waveguide.py +++ b/pytential/symbolic/pde/maxwell/waveguide.py @@ -28,16 +28,9 @@ Second-Kind Waveguide ^^^^^^^^^^^^^^^^^^^^^ .. 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 @@ class SecondKindInfZMuellerOperator(L2WeightedPDEOperator): # }}} - -# {{{ 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 -- GitLab From 90056c14859e9a2cb5fb56c3e51ba9e0b2699706 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 10 Jul 2020 15:38:20 -0500 Subject: [PATCH 66/76] 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 78f32986..b0a3c28e 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -30,6 +30,7 @@ THE SOFTWARE. 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 @@ class RefinerWrangler(TreeWranglerBase): 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 @@ class RefinerWrangler(TreeWranglerBase): 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 a2524976..64aecae6 100644 --- a/pytential/qbx/target_assoc.py +++ b/pytential/qbx/target_assoc.py @@ -528,11 +528,10 @@ class TargetAssociationWrangler(TreeWranglerBase): 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 @@ class TargetAssociationWrangler(TreeWranglerBase): 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 @@ class TargetAssociationWrangler(TreeWranglerBase): 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. -- GitLab From 0c63898a19a93f42eb0eff6a198fbcd82550a103 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 10 Jul 2020 15:48:47 -0500 Subject: [PATCH 67/76] 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 62ed2563..19d90ae7 100644 --- a/pytential/source.py +++ b/pytential/source.py @@ -62,7 +62,13 @@ class PotentialSource(object): 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 @@ class PotentialSource(object): 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 @@ class PointPotentialSource(PotentialSource): # {{{ 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. -- GitLab From a86f7a977b153345ccbac9c715b22891e3dd89e0 Mon Sep 17 00:00:00 2001 From: Matt Wala Date: Fri, 10 Jul 2020 16:14:54 -0500 Subject: [PATCH 68/76] 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 b0a3c28e..dbcfe657 100644 --- a/pytential/qbx/refinement.py +++ b/pytential/qbx/refinement.py @@ -373,8 +373,8 @@ class RefinerWrangler(TreeWranglerBase): 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( -- GitLab 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 69/76] 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 d8fa9a6d..6ce07e8a 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 -- GitLab From 2fc4fb189f26efc4c63f3ec1cc1fa66642bbfbeb Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Sat, 11 Jul 2020 17:40:51 -0500 Subject: [PATCH 70/76] 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 5dc64f57..ea924eb0 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 f9c12b45..dae0a27c 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" -- GitLab From ba588a6591f1f98906ddc83d851e4216b483929c Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Sat, 11 Jul 2020 17:59:50 -0500 Subject: [PATCH 71/76] 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 ea924eb0..35202a6c 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 ba5a52e9..00000000 --- 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 -- GitLab From 753a54a0edbfcd2487dbba47013c567f1aad317a Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Sat, 11 Jul 2020 18:16:29 -0500 Subject: [PATCH 72/76] 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 35202a6c..784c9677 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 -- GitLab 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 73/76] 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 784c9677..f483c67a 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 - -- GitLab From 7575fe16878e16ec423f06086ccf8f83ec80b28e Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Sun, 12 Jul 2020 18:30:37 -0500 Subject: [PATCH 74/76] Simplify Pylint Github CI --- .github/workflows/ci.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f483c67a..a8284242 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,15 +29,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - - uses: actions/setup-python@v1 - with: - python-version: '3.x' - name: "Main Script" run: | - set -e - CONDA_ENVIRONMENT=.test-conda-env-py3-pylint.yml - cp .test-conda-env-py3.yml $CONDA_ENVIRONMENT + CONDA_ENVIRONMENT=.test-conda-env-py3.yml echo "- matplotlib" >> $CONDA_ENVIRONMENT echo "-------------------------------------------" cat $CONDA_ENVIRONMENT -- GitLab From e5bb53f3ed9aadfde88d5c94274f83be5120cafa Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Sun, 12 Jul 2020 18:31:21 -0500 Subject: [PATCH 75/76] Re-dangle req.txt back to github and meshmode master --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 6ce07e8a..09d1ec0d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ git+https://github.com/inducer/modepy 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://gitlab.tiker.net/inducer/meshmode@array-context -git+https://gitlab.tiker.net/inducer/sumpy -git+https://gitlab.tiker.net/inducer/pyfmmlib +git+https://github.com/inducer/boxtree +git+https://github.com/inducer/meshmode +git+https://github.com/inducer/sumpy +git+https://github.com/inducer/pyfmmlib -- GitLab From d3aa7c15f46e1671fe1bb7494934e10e8def61e5 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Sun, 12 Jul 2020 18:32:46 -0500 Subject: [PATCH 76/76] Re-dangle test conda env back to github and meshmode master --- .test-conda-env-py3.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.test-conda-env-py3.yml b/.test-conda-env-py3.yml index fb8bb92d..cb1f9027 100644 --- a/.test-conda-env-py3.yml +++ b/.test-conda-env-py3.yml @@ -22,5 +22,5 @@ dependencies: - 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 + - git+https://github.com/inducer/sumpy + - git+https://github.com/inducer/meshmode -- GitLab