From c04082c578a74678f9e38bda3817b226e9f52f1b Mon Sep 17 00:00:00 2001 From: Matt Wala <wala1@illinois.edu> Date: Fri, 19 Jul 2019 16:41:27 -0500 Subject: [PATCH] Delete broken/obsolete h2dterms and l2dterms wrappers, and replace with FMMLib-based expansion order finder Closes #39 - [ ] Point requirements.txt back to pyfmmlib master after pyfmmlib!15 is merged --- requirements.txt | 2 +- sumpy/expansion/level_to_order.py | 90 ++++++++++++++----------------- test/test_misc.py | 20 ++++++- 3 files changed, 59 insertions(+), 53 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8e48bc1c..ccf135bd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,4 @@ git+https://github.com/inducer/islpy git+https://github.com/inducer/pyopencl git+https://gitlab.tiker.net/inducer/boxtree git+https://github.com/inducer/loopy -git+https://github.com/inducer/pyfmmlib +git+https://gitlab.tiker.net/inducer/pyfmmlib@l3dterms-and-h3dterms diff --git a/sumpy/expansion/level_to_order.py b/sumpy/expansion/level_to_order.py index 8f012e55..5e29b861 100644 --- a/sumpy/expansion/level_to_order.py +++ b/sumpy/expansion/level_to_order.py @@ -23,73 +23,61 @@ THE SOFTWARE. """ __doc__ = """ -.. autofunction:: h2d_level_to_order_lookup -.. autofunction:: l2d_level_to_order_lookup - +.. autoclass:: FMMLibExpansionOrderFinder .. autoclass:: SimpleExpansionOrderFinder """ import numpy as np -def h2d_level_to_order_lookup(tree, helmholtz_k, epsilon): - """ - Compute a mapping from level number to expansion order, - Helmholtz 2D case. - - This wraps the function *h2dterms* from :mod:`pyfmmlib`. - - :arg tree: An instance of :class:`boxtree.Tree`. - :arg helmholtz_k: Helmholtz parameter - :arg epsilon: Precision - - :return: A :class:`numpy.array` of length `tree.nlevels` +class FMMLibExpansionOrderFinder(object): + r"""Return expansion orders that meet the tolerance for a given level + using routines wrapped from :mod:`pyfmmlib`. """ - if tree.dimensions != 2: - raise ValueError("tree must be 2d") - - orders = np.empty(tree.nlevels, dtype=int) - bbox_area = np.max( - tree.bounding_box[1] - tree.bounding_box[0]) ** 2 - - from pyfmmlib import h2dterms - for level in range(tree.nlevels): - nterms, ier = h2dterms(bbox_area / 2 ** level, helmholtz_k, epsilon) - if ier != 0: - raise RuntimeError( - "h2dterms returned error code {ier}".format(ier=ier)) - orders[level] = nterms - - return orders + def __init__(self, tol, extra_order=0): + """ + :arg tol: error tolerance + :arg extra_order: order increase to accommodate, say, the taking of + derivatives of the FMM expansions. + """ + self.tol = tol + self.extra_order = extra_order + def __call__(self, kernel, kernel_args, tree, level): + import pyfmmlib -def l2d_level_to_order_lookup(tree, epsilon): - """ - Compute a mapping from level number to expansion order, - Laplace 2D case. + from sumpy.kernel import LaplaceKernel, HelmholtzKernel - This wraps the function *l2dterms* from :mod:`pyfmmlib`. + assert isinstance(kernel, (LaplaceKernel, HelmholtzKernel)) + assert tree.dimensions in (2, 3) - :arg tree: An instance of :class:`boxtree.Tree`. - :arg epsilon: Precision + if isinstance(kernel, LaplaceKernel): + if tree.dimensions == 2: + nterms, ier = pyfmmlib.l2dterms(self.tol) + if ier: + raise RuntimeError("l2dterms returned error code '%d'" % ier) - :return: A :class:`numpy.array` of length `tree.nlevels` - """ + elif tree.dimensions == 3: + nterms, ier = pyfmmlib.l3dterms(self.tol) + if ier: + raise RuntimeError("l3dterms returned error code '%d'" % ier) - if tree.dimensions != 2: - raise ValueError("tree must be 2d") + elif isinstance(kernel, HelmholtzKernel): + helmholtz_k = dict(kernel_args)[kernel.helmholtz_k_name] + size = tree.root_extent / 2 ** level - from pyfmmlib import l2dterms - nterms, ier = l2dterms(epsilon) - if ier != 0: - raise RuntimeError( - "l2dterms returned error code {ier}".format(ier=ier)) + if tree.dimensions == 2: + nterms, ier = pyfmmlib.h2dterms(size, helmholtz_k, self.tol) + if ier: + raise RuntimeError("h2dterms returned error code '%d'" % ier) - orders = np.empty(tree.nlevels, dtype=int) - orders.fill(nterms) + elif tree.dimensions == 3: + nterms, ier = pyfmmlib.h3dterms(size, helmholtz_k, self.tol) + if ier: + raise RuntimeError("h3dterms returned error code '%d'" % ier) - return orders + return nterms + self.extra_order class SimpleExpansionOrderFinder(object): @@ -114,7 +102,7 @@ class SimpleExpansionOrderFinder(object): extra_order=1): """ :arg extra_order: order increase to accommodate, say, the taking of - oderivatives f the FMM expansions. + derivatives of the FMM expansions. """ self.tol = tol diff --git a/test/test_misc.py b/test/test_misc.py index 33e8535a..8866c289 100644 --- a/test/test_misc.py +++ b/test/test_misc.py @@ -124,7 +124,9 @@ class FakeTree: self.stick_out_factor = stick_out_factor -@pytest.mark.parametrize("knl", [LaplaceKernel(2), HelmholtzKernel(2)]) +@pytest.mark.parametrize("knl", [ + LaplaceKernel(2), HelmholtzKernel(2), + LaplaceKernel(3), HelmholtzKernel(3)]) def test_order_finder(knl): from sumpy.expansion.level_to_order import SimpleExpansionOrderFinder @@ -137,6 +139,22 @@ def test_order_finder(knl): print(orders) +@pytest.mark.parametrize("knl", [ + LaplaceKernel(2), HelmholtzKernel(2), + LaplaceKernel(3), HelmholtzKernel(3)]) +def test_fmmlib_order_finder(knl): + pytest.importorskip("pyfmmlib") + from sumpy.expansion.level_to_order import FMMLibExpansionOrderFinder + + ofind = FMMLibExpansionOrderFinder(1e-5) + + tree = FakeTree(knl.dim, 200, 0.5) + orders = [ + ofind(knl, frozenset([("k", 5)]), tree, level) + for level in range(30)] + print(orders) + + # {{{ expansion toys p2e2e2p test cases def approx_convergence_factor(orders, errors): -- GitLab