From 11f1697650e2458cbd10362ffea2939cb5472a1e Mon Sep 17 00:00:00 2001 From: Matt Wala <wala1@illinois.edu> Date: Fri, 19 May 2017 15:32:58 -0500 Subject: [PATCH] LineTaylorLocalExpansion: Reinstate the old derivative taking behavior when using sympy, so code generation doesn't blow up. Also add a test the uses LineTaylorLocalExpansion. --- sumpy/expansion/local.py | 33 ++++++++++--- test/test_qbx.py | 104 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 8 deletions(-) create mode 100644 test/test_qbx.py diff --git a/sumpy/expansion/local.py b/sumpy/expansion/local.py index f9ec106a..02a47fed 100644 --- a/sumpy/expansion/local.py +++ b/sumpy/expansion/local.py @@ -68,16 +68,33 @@ class LineTaylorLocalExpansion(LocalExpansionBase): line_kernel = self.kernel.get_expression(avec_line) - from sumpy.tools import MiDerivativeTaker, my_syntactic_subs - deriv_taker = MiDerivativeTaker(line_kernel, (tau,)) + from sumpy.symbolic import USE_SYMENGINE + + if USE_SYMENGINE: + from sumpy.tools import MiDerivativeTaker, my_syntactic_subs + deriv_taker = MiDerivativeTaker(line_kernel, (tau,)) + + return [my_syntactic_subs( + self.kernel.postprocess_at_target( + self.kernel.postprocess_at_source( + deriv_taker.diff(i), + avec), bvec), + {tau: 0}) + for i in self.get_coefficient_identifiers()] + else: + # Workaround for sympy. The automatic distribution after + # single-variable diff makes the expressions very large + # (https://github.com/sympy/sympy/issues/4596), so avoid doing + # single variable diff. + # + # See also https://gitlab.tiker.net/inducer/pytential/merge_requests/12 - return [my_syntactic_subs( - self.kernel.postprocess_at_target( + return [self.kernel.postprocess_at_target( self.kernel.postprocess_at_source( - deriv_taker.diff(i), - avec), bvec), - {tau: 0}) - for i in self.get_coefficient_identifiers()] + line_kernel.diff("tau", i), avec), + bvec) + .subs("tau", 0) + for i in self.get_coefficient_identifiers()] def evaluate(self, coeffs, bvec): from pytools import factorial diff --git a/test/test_qbx.py b/test/test_qbx.py new file mode 100644 index 00000000..a6c2319b --- /dev/null +++ b/test/test_qbx.py @@ -0,0 +1,104 @@ +from __future__ import division, absolute_import, print_function + +__copyright__ = "Copyright (C) 2017 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 +import sys + +import pyopencl as cl + +import logging +logger = logging.getLogger(__name__) + + +try: + import faulthandler +except ImportError: + pass +else: + faulthandler.enable() + + +def test_direct(ctx_getter): + # This evaluates a single layer potential on a circle. + logging.basicConfig(level=logging.INFO) + + ctx = ctx_getter() + queue = cl.CommandQueue(ctx) + + from sumpy.kernel import LaplaceKernel + lknl = LaplaceKernel(2) + + order = 12 + + from sumpy.qbx import LayerPotential + from sumpy.expansion.local import LineTaylorLocalExpansion + + lpot = LayerPotential(ctx, [LineTaylorLocalExpansion(lknl, order)]) + + mode_nr = 25 + + from pytools.convergence import EOCRecorder + + eocrec = EOCRecorder() + + for n in [200, 300, 400]: + t = np.linspace(0, 2 * np.pi, n, endpoint=False) + unit_circle = np.exp(1j * t) + unit_circle = np.array([unit_circle.real, unit_circle.imag]) + + sigma = np.cos(mode_nr * t) + eigval = 1/(2*mode_nr) + + result_ref = eigval * sigma + + h = 2 * np.pi / n + + targets = unit_circle + sources = unit_circle + + radius = 7 * h + centers = unit_circle * (1 - radius) + + strengths = (sigma * h,) + evt, (result_qbx,) = lpot(queue, targets, sources, centers, strengths) + + eocrec.add_data_point(h, np.max(np.abs(result_ref - result_qbx))) + + print(eocrec) + + slack = 1.5 + assert eocrec.order_estimate() > order - slack + + +# You can test individual routines by typing +# $ python test_kernels.py 'test_p2p(cl.create_some_context)' + +if __name__ == "__main__": + if len(sys.argv) > 1: + exec(sys.argv[1]) + else: + from py.test.cmdline import main + main([__file__]) + +# vim: fdm=marker -- GitLab