diff --git a/.pylintrc-local.yml b/.pylintrc-local.yml new file mode 100644 index 0000000000000000000000000000000000000000..d0095f0e8d86afb8db0f898e9c9e721249da5592 --- /dev/null +++ b/.pylintrc-local.yml @@ -0,0 +1,2 @@ +- arg: extension-pkg-whitelist + val: mayavi diff --git a/examples/curve-pot.py b/examples/curve-pot.py index 8d1a91a21d5bf543393942839e56d8f2e6332262..023bd7e19bb69b619db9003c2f30111ec23e80ba 100644 --- a/examples/curve-pot.py +++ b/examples/curve-pot.py @@ -25,11 +25,11 @@ def process_kernel(knl, what_operator): if what_operator == "S": pass elif what_operator == "S0": - from sumpy.kernel import TargetDerivative - target_knl = TargetDerivative(0, knl) + from sumpy.kernel import AxisTargetDerivative + target_knl = AxisTargetDerivative(0, knl) elif what_operator == "S1": - from sumpy.kernel import TargetDerivative - target_knl = TargetDerivative(1, knl) + from sumpy.kernel import AxisTargetDerivative + target_knl = AxisTargetDerivative(1, knl) elif what_operator == "D": from sumpy.kernel import DirectionalSourceDerivative source_knl = DirectionalSourceDerivative(knl) diff --git a/sumpy/codegen.py b/sumpy/codegen.py index ef7e059a10397e58819761e62473b858b1280695..147dc4992b81873b4ddb089b3f3c7a6ecce58cc9 100644 --- a/sumpy/codegen.py +++ b/sumpy/codegen.py @@ -667,8 +667,6 @@ def to_loopy_insns(assignments, vector_names=frozenset(), pymbolic_expr_maps=(), bik = BigIntegerKiller() cmr = ComplexRewriter(complex_dtype) - cmb_mapper = combine_mappers(bdr, btog, vcr, pwr, ssg, bik, cmr) - if 0: # https://github.com/inducer/sumpy/pull/40#issuecomment-852635444 cmb_mapper = combine_mappers(bdr, btog, vcr, pwr, ssg, bik, cmr) diff --git a/sumpy/e2e.py b/sumpy/e2e.py index 253f855928ee36b816f8e8a2b081d11c219db7a5..bcb02f126a0040329947104e9f9fe268a1efd38a 100644 --- a/sumpy/e2e.py +++ b/sumpy/e2e.py @@ -26,7 +26,7 @@ import sumpy.symbolic as sym import pymbolic from loopy.version import MOST_RECENT_LANGUAGE_VERSION -from sumpy.tools import KernelCacheWrapper, to_complex_dtype +from sumpy.tools import KernelCacheMixin, to_complex_dtype from pytools import memoize_method import logging @@ -48,7 +48,9 @@ Expansion-to-expansion # {{{ translation base class -class E2EBase(KernelCacheWrapper): +class E2EBase(KernelCacheMixin): + default_name = "e2e" + def __init__(self, ctx, src_expansion, tgt_expansion, name=None, device=None): """ @@ -130,6 +132,9 @@ class E2EBase(KernelCacheWrapper): self.tgt_expansion, ) + def get_kernel(self): + raise NotImplementedError + def get_optimized_kernel(self): # FIXME knl = self.get_kernel() diff --git a/sumpy/e2p.py b/sumpy/e2p.py index 0f0e1d166a3f3bae3157cbdc170cfd1d49e9d0d5..34bdd5f520cf077301b8f0d487bac87f346f8737 100644 --- a/sumpy/e2p.py +++ b/sumpy/e2p.py @@ -24,7 +24,7 @@ import numpy as np import loopy as lp import sumpy.symbolic as sym -from sumpy.tools import KernelCacheWrapper +from sumpy.tools import KernelCacheMixin from loopy.version import MOST_RECENT_LANGUAGE_VERSION @@ -42,7 +42,9 @@ Expansion-to-particle # {{{ E2P base class -class E2PBase(KernelCacheWrapper): +class E2PBase(KernelCacheMixin): + default_name = "e2p" + def __init__(self, ctx, expansion, kernels, name=None, device=None): """ diff --git a/sumpy/expansion/level_to_order.py b/sumpy/expansion/level_to_order.py index 5d00c5b01642a5c821ce0d0237a9a7d1313227aa..82db1aef2ed1f5253a8e39342ce6249fbb7f313e 100644 --- a/sumpy/expansion/level_to_order.py +++ b/sumpy/expansion/level_to_order.py @@ -48,38 +48,44 @@ class FMMLibExpansionOrderFinder: self.extra_order = extra_order def __call__(self, kernel, kernel_args, tree, level): - import pyfmmlib - + from pyfmmlib import ( # pylint: disable=no-name-in-module + l2dterms, l3dterms, h2dterms, h3dterms) from sumpy.kernel import LaplaceKernel, HelmholtzKernel - assert isinstance(kernel, (LaplaceKernel, HelmholtzKernel)) - assert tree.dimensions in (2, 3) - if isinstance(kernel, LaplaceKernel): if tree.dimensions == 2: - nterms, ier = pyfmmlib.l2dterms(self.tol) + nterms, ier = l2dterms(self.tol) if ier: raise RuntimeError(f"l2dterms returned error code '{ier}'") elif tree.dimensions == 3: - nterms, ier = pyfmmlib.l3dterms(self.tol) + nterms, ier = l3dterms(self.tol) if ier: raise RuntimeError(f"l3dterms returned error code '{ier}'") + else: + raise ValueError(f"unsupported dimension: {tree.dimensions}") + elif isinstance(kernel, HelmholtzKernel): helmholtz_k = dict(kernel_args)[kernel.helmholtz_k_name] size = tree.root_extent / 2 ** level if tree.dimensions == 2: - nterms, ier = pyfmmlib.h2dterms(size, helmholtz_k, self.tol) + nterms, ier = h2dterms(size, helmholtz_k, self.tol) if ier: raise RuntimeError(f"h2dterms returned error code '{ier}'") elif tree.dimensions == 3: - nterms, ier = pyfmmlib.h3dterms(size, helmholtz_k, self.tol) + nterms, ier = h3dterms(size, helmholtz_k, self.tol) if ier: raise RuntimeError(f"h3dterms returned error code '{ier}'") + else: + raise ValueError(f"unsupported dimension: {tree.dimensions}") + + else: + raise TypeError(f"unsupported kernel: '{type(kernel).__name__}'") + return nterms + self.extra_order diff --git a/sumpy/kernel.py b/sumpy/kernel.py index ba7282c03f08ddb5f322a9c880b1ac9caad4d351..70dd14e25abf3feda2f184237ead5f33f471cb33 100644 --- a/sumpy/kernel.py +++ b/sumpy/kernel.py @@ -20,6 +20,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +from typing import ClassVar, Tuple import loopy as lp import numpy as np @@ -111,7 +112,7 @@ class KernelArgument: # Needed for python2 return not self == other - def __hash__(self): + def __hash__(self): # pylint: disable=invalid-hash-returned return (type(self), self.loopy_arg) @@ -136,6 +137,8 @@ class Kernel: .. automethod:: get_source_args """ + init_arg_names: ClassVar[Tuple[str, ...]] + def __init__(self, dim): self.dim = dim @@ -164,6 +167,9 @@ class Kernel: key_hash.update(type(self).__name__.encode("utf8")) key_builder.rec(key_hash, self.__getinitargs__()) + def __getinitargs__(self): + return (self.dim,) + def __getstate__(self): return self.__getinitargs__() @@ -1083,6 +1089,7 @@ class _VectorIndexAdder(CSECachingMapperMixin, IdentityMapper): class DirectionalDerivative(DerivativeBase): + directional_kind: ClassVar[str] init_arg_names = ("inner_kernel", "dir_vec_name") def __init__(self, inner_kernel, dir_vec_name=None): @@ -1307,6 +1314,9 @@ class KernelMapper: class KernelCombineMapper(KernelMapper): + def combine(self, values): + raise NotImplementedError + def map_difference_kernel(self, kernel): return self.combine([ self.rec(kernel.kernel_plus), @@ -1408,7 +1418,7 @@ def to_kernel_and_args(kernel_like): if not isinstance(kernel_like, Kernel): if kernel_like == 0: - return LaplaceKernel(), {} + return LaplaceKernel(None), {} elif isinstance(kernel_like, str): return HelmholtzKernel(None), {"k": var(kernel_like)} else: diff --git a/sumpy/p2e.py b/sumpy/p2e.py index 177d0b586ba8c04be2c1e01955a79d7b53f042f1..21551aefadcdcba33c11f07374ea954c4882a2c0 100644 --- a/sumpy/p2e.py +++ b/sumpy/p2e.py @@ -24,7 +24,7 @@ import numpy as np import loopy as lp from loopy.version import MOST_RECENT_LANGUAGE_VERSION -from sumpy.tools import KernelCacheWrapper, KernelComputation +from sumpy.tools import KernelCacheMixin, KernelComputation import logging logger = logging.getLogger(__name__) @@ -43,12 +43,14 @@ Particle-to-Expansion # {{{ P2E base class -class P2EBase(KernelComputation, KernelCacheWrapper): +class P2EBase(KernelCacheMixin, KernelComputation): """Common input processing for kernel computations. .. automethod:: __init__ """ + default_name = "p2e" + def __init__(self, ctx, expansion, kernels=None, name=None, device=None, strength_usage=None): """ @@ -122,6 +124,9 @@ class P2EBase(KernelComputation, KernelCacheWrapper): return (type(self).__name__, self.name, self.expansion, tuple(self.source_kernels), tuple(self.strength_usage)) + def get_kernel(self): + raise NotImplementedError + def get_optimized_kernel(self, sources_is_obj_array, centers_is_obj_array): knl = self.get_kernel() diff --git a/sumpy/p2p.py b/sumpy/p2p.py index 83506a377dbd865bd1cf29b7de786aa8c0995264..abd5df0658a02a5da7be8762e4156c60419be19c 100644 --- a/sumpy/p2p.py +++ b/sumpy/p2p.py @@ -28,7 +28,7 @@ import loopy as lp from loopy.version import MOST_RECENT_LANGUAGE_VERSION from sumpy.tools import ( - KernelComputation, KernelCacheWrapper, is_obj_array_like) + KernelComputation, KernelCacheMixin, is_obj_array_like) __doc__ = """ @@ -50,7 +50,9 @@ Particle-to-particle # {{{ p2p base class -class P2PBase(KernelComputation, KernelCacheWrapper): +class P2PBase(KernelCacheMixin, KernelComputation): + default_name = "p2p" + def __init__(self, ctx, target_kernels, exclude_self, strength_usage=None, value_dtypes=None, name=None, device=None, source_kernels=None): """ diff --git a/sumpy/point_calculus.py b/sumpy/point_calculus.py index b80ab51f06adda4c62fbfdd48b478794a45ed39e..2e17c41a1c11300e62a540a7306cd5371bece4e0 100644 --- a/sumpy/point_calculus.py +++ b/sumpy/point_calculus.py @@ -120,7 +120,7 @@ class CalculusPatch: """ from pytools import indices_in_shape - from scipy.special import eval_chebyt + from scipy.special import eval_chebyt # pylint: disable=no-name-in-module def eval_basis(ind, x): result = 1 @@ -240,8 +240,10 @@ class CalculusPatch: :returns: a scalar. """ f_values = f_values.reshape(*self._pshape) + zero_eval_vec_1d = self._zero_eval_vec_1d + for _ in range(self.dim): - f_values = self._zero_eval_vec_1d.dot(f_values) + f_values = zero_eval_vec_1d @ f_values return f_values diff --git a/sumpy/qbx.py b/sumpy/qbx.py index de2598881fc067dbe24e1b4e8f61b6510f2a3849..f5e505706ce2f80d375a030537a122eafb853aa5 100644 --- a/sumpy/qbx.py +++ b/sumpy/qbx.py @@ -32,7 +32,7 @@ from pytools import memoize_method from pymbolic import parse, var from sumpy.tools import ( - KernelComputation, KernelCacheWrapper, is_obj_array_like) + KernelComputation, KernelCacheMixin, is_obj_array_like) import logging logger = logging.getLogger(__name__) @@ -66,7 +66,9 @@ def stringify_expn_index(i): # {{{ base class -class LayerPotentialBase(KernelComputation, KernelCacheWrapper): +class LayerPotentialBase(KernelCacheMixin, KernelComputation): + default_name = "qbx" + def __init__(self, ctx, expansion, strength_usage=None, value_dtypes=None, name=None, device=None, source_kernels=None, target_kernels=None): diff --git a/sumpy/symbolic.py b/sumpy/symbolic.py index ab4c25dcf9c64ccee990acc0c702cb4988dd0576..01adb6e834685a297b99ae1fa5677043c505ac0a 100644 --- a/sumpy/symbolic.py +++ b/sumpy/symbolic.py @@ -41,6 +41,8 @@ import pymbolic.primitives as prim import logging logger = logging.getLogger(__name__) +USE_SYMENGINE = False + # {{{ symbolic backend @@ -48,23 +50,24 @@ def _find_symbolic_backend(): global USE_SYMENGINE try: - import symengine # noqa + import symengine # noqa: F401 symengine_found = True + symengine_error = None except ImportError as import_error: symengine_found = False symengine_error = import_error - ALLOWED_BACKENDS = ("sympy", "symengine") # noqa - BACKEND_ENV_VAR = "SUMPY_FORCE_SYMBOLIC_BACKEND" # noqa + allowed_backends = ("sympy", "symengine") + backend_env_var = "SUMPY_FORCE_SYMBOLIC_BACKEND" import os - backend = os.environ.get(BACKEND_ENV_VAR) + backend = os.environ.get(backend_env_var) if backend is not None: - if backend not in ALLOWED_BACKENDS: + if backend not in allowed_backends: raise RuntimeError( - f"{BACKEND_ENV_VAR} value is unrecognized: '{backend}' " + f"{backend_env_var} value is unrecognized: '{backend}' " "(allowed values are {})".format( - ", ".join(f"'{val}'" for val in ALLOWED_BACKENDS)) + ", ".join(f"'{val}'" for val in allowed_backends)) ) if backend == "symengine" and not symengine_found: @@ -123,17 +126,15 @@ def _coeff_isneg(a): return a.is_Number and a.is_negative -have_unevaluated_expr = False -if not USE_SYMENGINE: +if USE_SYMENGINE: + def UnevaluatedExpr(x): # noqa: F811, N802 + return x +else: try: from sympy import UnevaluatedExpr - have_unevaluated_expr = True except ImportError: - pass - -if not have_unevaluated_expr: - def UnevaluatedExpr(x): # noqa - return x + def UnevaluatedExpr(x): # noqa: F811, N802 + return x if USE_SYMENGINE: @@ -287,7 +288,7 @@ class PymbolicToSympyMapper(PymbolicToSympyMapperBase): class SympyToPymbolicMapper(SympyToPymbolicMapperBase): - def map_Symbol(self, expr): # noqa + def map_Symbol(self, expr): # noqa: N802 if expr.name.startswith(SpatialConstant.prefix): return SpatialConstant.from_sympy(expr) return SympyToPymbolicMapperBase.map_Symbol(self, expr) @@ -334,7 +335,8 @@ class _BesselOrHankel(sympy.Function): def fdiff(self, argindex=1): if argindex in (1, 3): # we are not differentiating w.r.t order or nderivs - raise ValueError() + raise ValueError(f"invalid argindex: {argindex}") + order, z, nderivs = self.args return self.func(order, z, nderivs+1) @@ -351,10 +353,10 @@ _SympyBesselJ = BesselJ _SympyHankel1 = Hankel1 if USE_SYMENGINE: - def BesselJ(*args): # noqa: N802 + def BesselJ(*args): # noqa: N802 # pylint: disable=function-redefined return sym.sympify(_SympyBesselJ(*args)) - def Hankel1(*args): # noqa: N802 + def Hankel1(*args): # noqa: N802 # pylint: disable=function-redefined return sym.sympify(_SympyHankel1(*args)) # vim: fdm=marker diff --git a/sumpy/tools.py b/sumpy/tools.py index d3b61b8d1fa0702cdfa2861ff08b5610a0c9d96d..6505fa370e6b8d06565382ff365b7abc0f8aaace 100644 --- a/sumpy/tools.py +++ b/sumpy/tools.py @@ -54,7 +54,7 @@ import pyopencl as cl import pyopencl.array as cla import loopy as lp -from typing import Dict, Tuple, Any +from typing import Dict, Tuple, Any, ClassVar import logging logger = logging.getLogger(__name__) @@ -579,6 +579,8 @@ class ScalingAssignmentTag(Tag): class KernelComputation: """Common input processing for kernel computations.""" + default_name: ClassVar[str] = "unknown" + def __init__(self, ctx, target_kernels, source_kernels, strength_usage, value_dtypes, name, device=None): """ @@ -722,7 +724,7 @@ class OrderedSet(MutableSet): # }}} -class KernelCacheWrapper: +class KernelCacheMixin: @memoize_method def get_cached_optimized_kernel(self, **kwargs): from sumpy import code_cache, CACHING_ENABLED, OPT_ENABLED @@ -769,6 +771,9 @@ class KernelCacheWrapper: knl, within=ObjTagged(ScalingAssignmentTag())) +KernelCacheWrapper = KernelCacheMixin + + def is_obj_array_like(ary): return ( isinstance(ary, (tuple, list)) @@ -1242,4 +1247,29 @@ def run_opencl_fft(fft_app, queue, input_vec, inverse=False, wait_for=None): # }}} + +# {{{ deprecations + +_depr_name_to_replacement_and_obj = { + "KernelCacheWrapper": ("KernelCacheMixin", 2023), + } + +if sys.version_info >= (3, 7): + def __getattr__(name): + replacement_and_obj = _depr_name_to_replacement_and_obj.get(name, None) + if replacement_and_obj is not None: + replacement, obj, year = replacement_and_obj + from warnings import warn + warn(f"'sumpy.tools.{name}' is deprecated. " + f"Use '{replacement}' instead. " + f"'sumpy.tools.{name}' will continue to work until {year}.", + DeprecationWarning, stacklevel=2) + return obj + else: + raise AttributeError(name) +else: + KernelCacheWrapper = KernelCacheMixin + +# }}} + # vim: fdm=marker diff --git a/sumpy/toys.py b/sumpy/toys.py index ded4a54e2e071113ad87c865fb4b59e8f91af70a..be3bf843a4d754309b9a4051504e937674c6c4a6 100644 --- a/sumpy/toys.py +++ b/sumpy/toys.py @@ -697,7 +697,7 @@ class SchematicVisitor: elif expn_style == "circle": draw_circle(psource.center, psource.radius, fill=None) else: - raise ValueError(f"unknown expn_style: {self.expn_style}") + raise ValueError(f"unknown expn_style: {expn_style}") if psource.derived_from is None: return diff --git a/sumpy/visualization.py b/sumpy/visualization.py index c5ce571095c9bfcd4e05e72ad127f02ecb424059..258745982d7011492de11b71d6643352beaf4639 100644 --- a/sumpy/visualization.py +++ b/sumpy/visualization.py @@ -141,7 +141,7 @@ class FieldPlotter: if max_val is not None: squeezed_fld[squeezed_fld > max_val] = max_val - squeezed_fld[squeezed_fld < -max_val] = -max_val + squeezed_fld[squeezed_fld < -max_val] = -max_val # pylint: disable=E1130 squeezed_fld = squeezed_fld[..., ::-1] @@ -165,7 +165,8 @@ class FieldPlotter: def show_vector_in_mayavi(self, fld, do_show=True, **kwargs): c = self.points - from mayavi import mlab + from mayavi import mlab # pylint: disable=import-error + mlab.quiver3d(c[0], c[1], c[2], fld[0], fld[1], fld[2], **kwargs) @@ -181,7 +182,7 @@ class FieldPlotter: def show_scalar_in_mayavi(self, fld, max_val=None, **kwargs): if max_val is not None: fld[fld > max_val] = max_val - fld[fld < -max_val] = -max_val + fld[fld < -max_val] = -max_val # pylint: disable=E1130 if len(fld.shape) == 1: fld = fld.reshape(self.nd_points.shape[1:]) @@ -189,7 +190,7 @@ class FieldPlotter: nd_points = self.nd_points.squeeze()[self._get_nontrivial_dims()] squeezed_fld = fld.squeeze() - from mayavi import mlab + from mayavi import mlab # pylint: disable=import-error mlab.surf(nd_points[0], nd_points[1], squeezed_fld, **kwargs) # vim: foldmethod=marker