diff --git a/sumpy/expansion/__init__.py b/sumpy/expansion/__init__.py index 647ebb01589a502c128a30aa597377c8fed1d0e4..f9ffc4f54dfd5427e8d47088872ce2a967a62032 100644 --- a/sumpy/expansion/__init__.py +++ b/sumpy/expansion/__init__.py @@ -29,6 +29,7 @@ import logging from pytools import memoize_method import sumpy.symbolic as sym from collections import defaultdict +from sumpy.tools import CoeffIdentifier, add_mi __doc__ = """ @@ -364,24 +365,23 @@ class LinearRecurrenceBasedExpansionTermsWrangler(ExpansionTermsWrangler): from pytools import ProcessLogger plog = ProcessLogger(logger, "compute recurrence for Taylor coefficients") - pde_dict = self.get_pde_dict() + pdes, iexpr, nexpr = self.get_pdes() pde_mat = [] mis = self.get_full_coefficient_identifiers() coeff_ident_enumerate_dict = dict((tuple(mi), i) for (i, mi) in enumerate(mis)) + offset = len(mis) for mi in mis: - for pde_mi, coeff in iteritems(pde_dict): - diff = np.array(mi, dtype=int) - pde_mi - if (diff >= 0).all(): - eq = [0]*len(mis) - for pde_mi2, coeff2 in iteritems(pde_dict): - c = tuple(pde_mi2 + diff) - if c not in coeff_ident_enumerate_dict: - break - eq[coeff_ident_enumerate_dict[c]] = coeff - else: - pde_mat.append(eq) + for pde_dict in pdes: + eq = [0]*(len(mis)*nexpr) + for ident, coeff in iteritems(pde_dict): + c = tuple(add_mi(ident.mi, mi)) + if c not in coeff_ident_enumerate_dict: + break + eq[offset*ident.iexpr + coeff_ident_enumerate_dict[c]] = coeff + else: + pde_mat.append(eq) if len(pde_mat) > 0: r""" @@ -398,8 +398,9 @@ class LinearRecurrenceBasedExpansionTermsWrangler(ExpansionTermsWrangler): :math:`S = N_{[r, :]}^{-T} N^T = N_{[r, :]}^{-T} N^T`. """ n = nullspace(pde_mat) + n = n[offset*iexpr:offset*(iexpr+1), :] idx = self.get_reduced_coeffs(n) - assert len(idx) >= n.shape[1] + n = n[:,:len(idx)] s = solve_symbolic(n.T[:, idx], n.T) stored_identifiers = [mis[i] for i in idx] else: @@ -422,10 +423,12 @@ class LinearRecurrenceBasedExpansionTermsWrangler(ExpansionTermsWrangler): return stored_identifiers, coeff_matrix - def get_pde_dict(self): + def get_pdes(self): r""" - Returns the PDE as a dictionary of (mi, coeff) such that mi - is the multi-index of the derivative and the PDE is represented by, + Returns a list of PDEs, expression number, number of expressions. + A PDE is a dictionary of (ident, coeff) such that ident is a + namedtuple of (mi, iexpr) where mi is the multi-index of the + derivative, iexpr is the expression number and the PDE is represented by, .. math:: @@ -487,15 +490,16 @@ class LaplaceExpansionTermsWrangler(LinearRecurrenceBasedExpansionTermsWrangler) init_arg_names = ("order", "dim", "max_mi") def __init__(self, order, dim, max_mi=None): - super(LaplaceExpansionTermsWrangler, self).__init__(order, dim, 1, max_mi) + super(LaplaceExpansionTermsWrangler, self).__init__(order=order, dim=dim, + deriv_multiplier=1, max_mi=max_mi) - def get_pde_dict(self): + def get_pdes(self): pde_dict = {} for d in range(self.dim): mi = [0]*self.dim mi[d] = 2 - pde_dict[tuple(mi)] = 1 - return pde_dict + pde_dict[CoeffIdentifier(tuple(mi), 0)] = 1 + return [pde_dict], 0, 1 def _get_reduced_coeffs(self, nullspace): idx = [] @@ -515,17 +519,17 @@ class HelmholtzExpansionTermsWrangler(LinearRecurrenceBasedExpansionTermsWrangle self.helmholtz_k_name = helmholtz_k_name multiplier = sym.Symbol(helmholtz_k_name) - super(HelmholtzExpansionTermsWrangler, self).__init__(order, dim, multiplier, - max_mi) + super(HelmholtzExpansionTermsWrangler, self).__init__(order=order, dim=dim, + deriv_multiplier=multiplier, max_mi=max_mi) - def get_pde_dict(self, **kwargs): + def get_pdes(self, **kwargs): pde_dict = {} for d in range(self.dim): mi = [0]*self.dim mi[d] = 2 - pde_dict[tuple(mi)] = 1 - pde_dict[tuple([0]*self.dim)] = 1 - return pde_dict + pde_dict[CoeffIdentifier(tuple(mi), 0)] = 1 + pde_dict[CoeffIdentifier(tuple([0]*self.dim), 0)] = 1 + return [pde_dict], 0, 1 def _get_reduced_coeffs(self, nullspace): idx = [] @@ -536,6 +540,46 @@ class HelmholtzExpansionTermsWrangler(LinearRecurrenceBasedExpansionTermsWrangle idx.append(i) return idx + +class StokesExpansionTermsWrangler(LinearRecurrenceBasedExpansionTermsWrangler): + + init_arg_names = ("order", "dim", "icomp", "viscosity_mu_name", "max_mi") + + def __init__(self, order, dim, icomp, viscosity_mu_name, max_mi=None): + self.viscosity_mu_name = viscosity_mu_name + self.icomp = icomp + multiplier = 1/sym.Symbol(viscosity_mu_name) + super(StokesExpansionTermsWrangler, self).__init__(order=order, dim=dim, + deriv_multiplier=multiplier, max_mi=max_mi) + + def get_pdes(self, **kwargs): + pdes = [] + # velocity + for eq in range(self.dim): + pde_dict = {} + for d in range(self.dim): + mi = [0]*self.dim + mi[d] = 2 + pde_dict[CoeffIdentifier(tuple(mi), eq)] = 1 + mi = [0]*self.dim + mi[eq] = 1 + pde_dict[CoeffIdentifier(tuple(mi), self.dim)] = -1 + pdes.append(pde_dict) + # pressure + pde_dict = {} + for d in range(self.dim): + mi = [0]*self.dim + mi[d] = 2 + pde_dict[CoeffIdentifier(tuple(mi), self.dim)] = 1 + pdes.append(pde_dict) + # divergence + pde_dict = {} + for d in range(self.dim): + mi = [0]*self.dim + mi[d] = 1 + pde_dict[CoeffIdentifier(tuple(mi), d)] = 1 + pdes.append(pde_dict) + return pdes, self.icomp, self.dim+1 # }}} @@ -612,6 +656,19 @@ class HelmholtzConformingVolumeTaylorExpansion(VolumeTaylorExpansionBase): helmholtz_k_name = kernel.get_base_kernel().helmholtz_k_name self.expansion_terms_wrangler_key = (order, kernel.dim, helmholtz_k_name) + +class StokesConformingVolumeTaylorExpansion(VolumeTaylorExpansionBase): + + expansion_terms_wrangler_class = StokesExpansionTermsWrangler + expansion_terms_wrangler_cache = {} + + # not user-facing, be strict about having to pass use_rscale + def __init__(self, kernel, order, use_rscale): + icomp = kernel.get_base_kernel().icomp + viscosity_mu_name = kernel.get_base_kernel().viscosity_mu_name + self.expansion_terms_wrangler_key = (order, kernel.dim, + icomp, viscosity_mu_name) + # }}} diff --git a/sumpy/expansion/local.py b/sumpy/expansion/local.py index cf94ebd674201178047cfee86acca0d74512c1ec..f080b7460029ce2f5325e0ed58f3d97611aa791c 100644 --- a/sumpy/expansion/local.py +++ b/sumpy/expansion/local.py @@ -27,7 +27,7 @@ import sumpy.symbolic as sym from sumpy.expansion import ( ExpansionBase, VolumeTaylorExpansion, LaplaceConformingVolumeTaylorExpansion, - HelmholtzConformingVolumeTaylorExpansion) + HelmholtzConformingVolumeTaylorExpansion, StokesConformingVolumeTaylorExpansion) class LocalExpansionBase(ExpansionBase): @@ -269,6 +269,16 @@ class HelmholtzConformingVolumeTaylorLocalExpansion( HelmholtzConformingVolumeTaylorExpansion.__init__( self, kernel, order, use_rscale) + +class StokesConformingVolumeTaylorLocalExpansion( + StokesConformingVolumeTaylorExpansion, + VolumeTaylorLocalExpansionBase): + + def __init__(self, kernel, order, use_rscale=None): + VolumeTaylorLocalExpansionBase.__init__(self, kernel, order, use_rscale) + StokesConformingVolumeTaylorExpansion.__init__( + self, kernel, order, use_rscale) + # }}} diff --git a/sumpy/expansion/multipole.py b/sumpy/expansion/multipole.py index 25488952e4b7425371ecf23aefa261c0c70900f5..07f2eeb6929c47b0437e954819ebc449fc7c0c27 100644 --- a/sumpy/expansion/multipole.py +++ b/sumpy/expansion/multipole.py @@ -28,7 +28,7 @@ import sumpy.symbolic as sym # noqa from sumpy.symbolic import vector_xreplace from sumpy.expansion import ( ExpansionBase, VolumeTaylorExpansion, LaplaceConformingVolumeTaylorExpansion, - HelmholtzConformingVolumeTaylorExpansion) + HelmholtzConformingVolumeTaylorExpansion, StokesConformingVolumeTaylorExpansion) import logging logger = logging.getLogger(__name__) @@ -220,6 +220,16 @@ class HelmholtzConformingVolumeTaylorMultipoleExpansion( HelmholtzConformingVolumeTaylorExpansion.__init__( self, kernel, order, use_rscale) + +class StokesConformingVolumeTaylorMultipoleExpansion( + StokesConformingVolumeTaylorExpansion, + VolumeTaylorMultipoleExpansionBase): + + def __init__(self, kernel, order, use_rscale=None): + VolumeTaylorMultipoleExpansionBase.__init__(self, kernel, order, use_rscale) + StokesConformingVolumeTaylorExpansion.__init__( + self, kernel, order, use_rscale) + # }}} diff --git a/sumpy/tools.py b/sumpy/tools.py index 32d14bcbfcf09f48322ff285d37ba5461b12560d..c13a04b6a73f858650e4235c9cb753d6b863c119 100644 --- a/sumpy/tools.py +++ b/sumpy/tools.py @@ -27,6 +27,7 @@ THE SOFTWARE. import six from six.moves import range, zip +from collections import namedtuple from pytools import memoize_method, memoize_in import numpy as np import sumpy.symbolic as sym @@ -729,4 +730,7 @@ def solve_symbolic(a, b): red = rref(big)[0] return red[:, big.shape[0]:] + +CoeffIdentifier = namedtuple('CoeffIdentifier', ['mi', 'iexpr']) + # vim: fdm=marker diff --git a/test/test_kernels.py b/test/test_kernels.py index 3fcb195bba0be9cb0b67a4822a45dec616d370aa..6a96d1ad8f44133b8b4bff7ba87078a5cded528a 100644 --- a/test/test_kernels.py +++ b/test/test_kernels.py @@ -37,13 +37,15 @@ from sumpy.expansion.multipole import ( VolumeTaylorMultipoleExpansion, H2DMultipoleExpansion, VolumeTaylorMultipoleExpansionBase, LaplaceConformingVolumeTaylorMultipoleExpansion, - HelmholtzConformingVolumeTaylorMultipoleExpansion) + HelmholtzConformingVolumeTaylorMultipoleExpansion, + StokesConformingVolumeTaylorMultipoleExpansion) from sumpy.expansion.local import ( VolumeTaylorLocalExpansion, H2DLocalExpansion, LaplaceConformingVolumeTaylorLocalExpansion, - HelmholtzConformingVolumeTaylorLocalExpansion) + HelmholtzConformingVolumeTaylorLocalExpansion, + StokesConformingVolumeTaylorLocalExpansion) from sumpy.kernel import (LaplaceKernel, HelmholtzKernel, AxisTargetDerivative, - DirectionalSourceDerivative) + DirectionalSourceDerivative, StokesletKernel) from pytools.convergence import PConvergenceVerifier import logging @@ -155,6 +157,8 @@ def test_p2e2p(ctx_getter, base_knl, expn_class, order, with_source_derivative): extra_kwargs["k"] = 0.2 * (0.707 + 0.707j) else: extra_kwargs["k"] = 0.2 + if isinstance(base_knl, StokesletKernel): + extra_kwargs["mu"] = 0.2 if with_source_derivative: knl = DirectionalSourceDerivative(base_knl, "dir_vec") @@ -342,7 +346,10 @@ def test_p2e2p(ctx_getter, base_knl, expn_class, order, with_source_derivative): (HelmholtzKernel(2), VolumeTaylorLocalExpansion, VolumeTaylorMultipoleExpansion), (HelmholtzKernel(2), HelmholtzConformingVolumeTaylorLocalExpansion, HelmholtzConformingVolumeTaylorMultipoleExpansion), - (HelmholtzKernel(2), H2DLocalExpansion, H2DMultipoleExpansion) + (HelmholtzKernel(2), H2DLocalExpansion, H2DMultipoleExpansion), + (StokesletKernel(2, 0, 0), VolumeTaylorLocalExpansion, VolumeTaylorMultipoleExpansion), + (StokesletKernel(2, 0, 0), StokesConformingVolumeTaylorLocalExpansion, + StokesConformingVolumeTaylorMultipoleExpansion), ]) def test_translations(ctx_getter, knl, local_expn_class, mpole_expn_class): logging.basicConfig(level=logging.INFO) @@ -363,6 +370,8 @@ def test_translations(ctx_getter, knl, local_expn_class, mpole_expn_class): extra_kwargs = {} if isinstance(knl, HelmholtzKernel): extra_kwargs["k"] = 0.05 + if isinstance(knl, StokesletKernel): + extra_kwargs["mu"] = 0.05 # Just to make sure things also work away from the origin origin = np.array([2, 1], np.float64) @@ -404,7 +413,7 @@ def test_translations(ctx_getter, knl, local_expn_class, mpole_expn_class): # FIXME: Embarrassing--but we run out of memory for higher orders. orders = [2, 3] else: - orders = [2, 3, 4] + orders = [3, 4, 5] nboxes = centers.shape[-1] def eval_at(e2p, source_box_nr, rscale):