From c91c1498b692a202ddbd172f628ef4435b6f1775 Mon Sep 17 00:00:00 2001
From: Andreas Kloeckner <inform@tiker.net>
Date: Sun, 21 Jul 2013 22:47:51 -0400
Subject: [PATCH] Build, test M2M

---
 sumpy/expansion/multipole.py | 56 ++++++++++++++++++++++++++++++++----
 test/test_kernels.py         | 22 +++++++++++++-
 2 files changed, 72 insertions(+), 6 deletions(-)

diff --git a/sumpy/expansion/multipole.py b/sumpy/expansion/multipole.py
index 924055dc..425980e9 100644
--- a/sumpy/expansion/multipole.py
+++ b/sumpy/expansion/multipole.py
@@ -26,6 +26,9 @@ import sympy as sp  # noqa
 
 from sumpy.expansion import ExpansionBase, VolumeTaylorExpansionBase
 
+import logging
+logger = logging.getLogger(__name__)
+
 
 class MultipoleExpansionBase(ExpansionBase):
     pass
@@ -61,26 +64,69 @@ class VolumeTaylorMultipoleExpansion(
                             for iaxis, mi_i in enumerate(mi))
 
                     result[i] += (
-                        - 1/mi_factorial(derivative_mi)
-                        * mi_power(avec, derivative_mi)
+                        - mi_power(avec, derivative_mi)
                         * dir_vec[idim])
 
             return result
         else:
             return [
-                    mi_power(avec, mi) / mi_factorial(mi)
+                    mi_power(avec, mi)
                     for mi in self.get_coefficient_identifiers()]
 
     def evaluate(self, coeffs, bvec):
         ppkernel = self.kernel.postprocess_at_target(
                 self.kernel.get_expression(bvec), bvec)
 
-        from sumpy.tools import mi_derivative
+        from sumpy.tools import mi_derivative, mi_factorial
         return sum(
-                coeff
+                coeff / mi_factorial(mi)
                 * mi_derivative(ppkernel, bvec, mi)
                 for coeff, mi in zip(coeffs, self.get_coefficient_identifiers()))
 
+    def translate_from(self, src_expansion, src_coeff_exprs, dvec):
+        if not isinstance(src_expansion, type(self)):
+            raise RuntimeError("do not know how to translate %s to "
+                    "Taylor multipole expansion"
+                    % type(src_expansion).__name)
+
+        logger.info("building translation operator: %s(%d) -> %s(%d): start"
+                % (type(src_expansion).__name__,
+                    src_expansion.order,
+                    type(self).__name__,
+                    self.order))
+
+        src_mi_to_index = dict((mi, i) for i, mi in enumerate(
+            src_expansion.get_coefficient_identifiers()))
+
+        result = [0] * len(self.get_coefficient_identifiers())
+        from pytools import generate_nonnegative_integer_tuples_below as gnitb
+
+        for i, tgt_mi in enumerate(
+                self.get_coefficient_identifiers()):
+
+            tgt_mi_plus_one = tuple(mi_i + 1 for mi_i in tgt_mi)
+
+            for src_mi in gnitb(tgt_mi_plus_one):
+                try:
+                    src_index = src_mi_to_index[src_mi]
+                except KeyError:
+                    # tgt higher-order than source--dumb, but not life-threatening
+                    continue
+
+                contrib = src_coeff_exprs[src_index]
+
+                for idim in xrange(self.dim):
+                    n = tgt_mi[idim]
+                    k = src_mi[idim]
+                    assert n >= k
+                    contrib *= (sp.binomial(n, k)
+                            * dvec[idim]**(n-k))
+
+                result[i] += contrib
+
+        logger.info("building translation operator: done")
+        return result
+
 # }}}
 
 # vim: fdm=marker
diff --git a/test/test_kernels.py b/test/test_kernels.py
index d74c45ba..c4f08b1d 100644
--- a/test/test_kernels.py
+++ b/test/test_kernels.py
@@ -365,6 +365,7 @@ def test_translations(ctx_getter, knl):
 
         from sumpy import P2E, E2P, P2P, E2E
         p2m = P2E(ctx, m_expn, out_kernels)
+        m2m = E2E(ctx, m_expn, m_expn)
         m2l = E2E(ctx, m_expn, l_expn)
         l2l = E2E(ctx, l_expn, l_expn)
         l2p = E2P(ctx, l_expn, out_kernels)
@@ -391,11 +392,28 @@ def test_translations(ctx_getter, knl):
 
         # }}}
 
+        # {{{ apply M2M
+
+        m2m_target_boxes = np.array([1], dtype=np.int32)
+        m2m_src_box_starts = np.array([0, 1], dtype=np.int32)
+        m2m_src_box_lists = np.array([0], dtype=np.int32)
+
+        evt, (mpoles,) = m2m(queue,
+                src_expansions=mpoles,
+                target_boxes=m2m_target_boxes,
+                src_box_starts=m2m_src_box_starts,
+                src_box_lists=m2m_src_box_lists,
+                centers=centers,
+                #flags="print_hl_cl",
+                out_host=True, **extra_kwargs)
+
+        # }}}
+
         # {{{ apply M2L
 
         m2l_target_boxes = np.array([2], dtype=np.int32)
         m2l_src_box_starts = np.array([0, 1], dtype=np.int32)
-        m2l_src_box_lists = np.array([0], dtype=np.int32)
+        m2l_src_box_lists = np.array([1], dtype=np.int32)
 
         evt, (mpoles,) = m2l(queue,
                 src_expansions=mpoles,
@@ -456,6 +474,8 @@ def test_translations(ctx_getter, knl):
 
         err = la.norm((pot - pot_direct)/res**2)
 
+        err = err / (la.norm(pot_direct) / res**2)
+
         # }}}
 
         pconv_verifier.add_data_point(order, err)
-- 
GitLab