From a42be09a7b11c194f70100a91e0558b97d0f9fe7 Mon Sep 17 00:00:00 2001
From: Carl Poelking <cp605@cam.ac.uk>
Date: Tue, 20 Dec 2016 20:04:07 +0000
Subject: [PATCH] Tlmlm framework.

---
 src/soap/fieldtensor.cpp | 252 ++++++++++++++++++++++++++++++++++++++-
 src/soap/functions.cpp   |  64 ++++++++++
 src/soap/functions.hpp   |  10 ++
 3 files changed, 323 insertions(+), 3 deletions(-)

diff --git a/src/soap/fieldtensor.cpp b/src/soap/fieldtensor.cpp
index b8f946b..00b8d02 100644
--- a/src/soap/fieldtensor.cpp
+++ b/src/soap/fieldtensor.cpp
@@ -40,8 +40,169 @@ void AtomicSpectrumFT::registerPython() {
 // FieldTensorSpectrum
 // ===================
 
+std::vector<double> calculate_Alm(int L) {
+    // A_{lm} = \sqrt{(l+m)!*(l-m)!}
+    std::vector<double> out;
+    out.resize((L+1)*(L+1), 0.0);
+    for (int l = 0; l <= L; ++l) {
+        for (int m = -l; m <= L; ++m) {
+            out[l*l+l+m] = sqrt(factorial(l+m)*factorial(l-m));
+        }
+    }
+    return out;
+}
+
+std::vector<double> calculate_Blm(int L) {
+    // B_{lm} = A_{lm} / (2l-1)!!
+    std::vector<double> out;
+    out.resize((L+1)*(L+1), 0.0);
+    for (int l = 0; l <= L; ++l) {
+        for (int m = -l; m <= L; ++m) {
+            out[l*l+l+m] = sqrt(factorial(l+m)*factorial(l-m))/factorial2(2*l-1);
+        }
+    }
+    return out;
+}
+
+void calculate_Fl(double r, double a, int L, std::vector<double> &fl) {
+    // See Elking et al.: "Gaussian Multipole Model", JCTC (2010), Eq. 15a++
+    fl.clear();
+    fl.resize(L+1, 0.0);
+    if (r < 1e-10) { // TODO Define space quantum
+        double a2 = a*a;
+        double a_l = 1./a;
+        int two_l = 1;
+        int sign_l = -1;
+        double rsqrt_pi = 1./sqrt(M_PI);
+        for (int l = 0; l <= L; ++l) {
+            a_l *= a2;
+            two_l *= 2;
+            sign_l *= -1;
+            fl[l] = sign_l*two_l*a_l*rsqrt_pi/(2*l+1);
+        }
+    }
+    else {
+        // Initialise
+        fl[0] = std::erf(a*r)/r;
+        double r2 = r*r;
+        double a2 = a*a;
+        double r_sqrt_pi_exp_a2r2 = 1./sqrt(M_PI) * exp(-a2*r2);
+        double al = 1./a;
+        double tl = 1.;
+        // Compute
+        for (int l = 1; l <= L; ++l) {
+            al *= a2;
+            tl *= 2;
+            fl[l] = -1./r2 * ( (2*l-1)*fl[l-1] + pow(-1,l)*tl*al*r_sqrt_pi_exp_a2r2 );
+        }
+    }
+
+    return;
+}
+
+class Tlmlm
+{
+public:
+    typedef std::complex<double> dtype_t;
+    typedef ub::matrix< dtype_t > coeff_t;
+    typedef ub::zero_matrix< dtype_t > coeff_zero_t;
+
+    Tlmlm(int L) {
+        Alm = calculate_Alm(L);
+        Blm = calculate_Blm(L);
+    }
+
+    void computeTlmlm(vec d12, double r12, double a12, int L1, int L2, coeff_t &T) {
+        T.clear();
+        T = coeff_zero_t((L1+1)*(L1+1), (L2+1)*(L2+1));
+
+        int Lg = (L1 > L2) ? L1 : L2;
+        int L12 = L1+L2;
+        // Compute Rlm's
+        std::vector<std::complex<double>> Rlm;
+        calculate_solidharm_Rlm(d12, r12, Lg, Rlm);
+        // Compute Fl's
+        std::vector<double> Fl;
+        calculate_Fl(r12, a12, L12, Fl);
+
+        for (int l1 = 0; l1 <= L1; ++l1) {
+        for (int m1 = -l1; m1 <= l1; ++m1) {
+            int lm1 = l1*l1+l1+m1;
+        for (int l2 = 0; l2 <= L2; ++l2) {
+        for (int m2 = -l2; m2 <= l2; ++m2) {
+            int lm2 = l2*l2+l2+m2;
+
+            std::complex<double> tlm1lm2 = 0.0;
+            int lg = (l1 > l2) ? l1 : l2;
+
+            for (int l = 0; l <= lg; ++l) {
+            for (int m = -l; m <= l; ++m) {
+
+                if (std::abs(m2+m) > l2-l) continue;
+                if (std::abs(m1-m) > l1-l) continue;
+                int lm = l*l+l+m;
+                tlm1lm2 +=
+                    pow(-1, l1+m)*factorial2(2*l-1)/(Alm[lm]*Alm[lm])
+                  * std::conj( Rlm[ (l2-l)*(l2-l) + (l2-l) + m2+m ] )
+                  * std::conj( Rlm[ (l1-l)*(l1-l) + (l1-l) + m1-m ] )
+                  * Fl[ l1+l2-l ];
+
+            }} // l m
+
+            tlm1lm2 *=
+                Alm[lm1]*Alm[lm1] * Alm[lm2]*Alm[lm2]
+              / factorial2(2*l1-1)*factorial2(2*l2-1);
+
+             // TODO Assert .imag() == zero // TODO Does not actually need to be zero!?
+             T(lm1,lm2) = tlm1lm2;
+        }}}} // l1 m1 l2 m2
+        return;
+    }
+
+    double computeTl1m1l2m2(double d12, double r12, double a12, int l1, int l2) {
+
+    }
+private:
+    std::vector<double> Alm;
+    std::vector<double> Blm;
+};
+
+
+void calculate_r_dr_erfar_r(double r, double a, int L, bool normalise, std::vector<double> &cl) {
+    // Derivatives (1/r d/dr)^l (erf(ar)/r) for 0 <= l <= L
+    cl.clear();
+    cl.resize(L+1, 0.0);
+    // Initialise
+    cl[0] = std::erf(a*r)/r;
+    double r2 = r*r;
+    double a2 = a*a;
+    double r_sqrt_pi_exp_a2r2 = 1./sqrt(M_PI) * exp(-a2*r2);
+    double al = 1./a;
+    double tl = 1.;
+    // Compute
+    for (int l = 1; l <= L; ++l) {
+        al *= a2;
+        tl *= 2;
+        cl[l] = 1./r2 * ( (2*l-1)*cl[l-1] - tl*al*r_sqrt_pi_exp_a2r2 );
+    }
+
+    if (normalise) {
+        double rl = r;
+        cl[0] *= rl; // * factorial2(-1), which equals 1
+        for (int l = 1; l <= L; ++l) {
+            rl *= r2;
+            cl[l] *= rl / factorial2(2*l-1);
+        }
+    }
+
+    return;
+}
+
+
 FTSpectrum::FTSpectrum(Structure &structure, Options &options)
     : _structure(&structure), _options(&options) {
+    _cutoff = CutoffFunctionOutlet().create(_options->get<std::string>("radialcutoff.type"));
+	_cutoff->configure(*_options);
     return;
 }
 
@@ -50,10 +211,93 @@ FTSpectrum::~FTSpectrum() {
 }
 
 void FTSpectrum::compute() {
+    GLOG() << "Computing FTSpectrum ..." << std::endl;
+
+    int L = 3; // TODO
+    int S = 4;
+
+    // CREATE ATOMIC SPECTRA
+    for (auto it = _atomic_array.begin(); it != _atomic_array.end(); ++it) {
+        delete *it;
+    }
+    _atomic_array.clear();
+    for (auto pit = _structure->beginParticles(); pit != _structure->endParticles(); ++pit) {
+        atomic_t *new_atomic = new atomic_t(*pit, S);
+        _atomic_array.push_back(new_atomic);
+    }
+
+    for (auto it1 = beginAtomic(); it1 != endAtomic(); ++it1) {
+        atomic_t *a = *it1;
+        int sa = a->getTypeIdx();
+        vec ra = a->getCenter()->getPos();
+        for (auto it2 = beginAtomic(); it2 != endAtomic(); ++it2) {
+            atomic_t *b = *it2;
+            int sb = b->getTypeIdx();
+            vec rb = b->getCenter()->getPos();
+            // Apply weight function
+            vec dr_ab = _structure->connect(ra, rb);
+            double R_ab = soap::linalg::abs(dr_ab);
+            vec d_ab = dr_ab/R_ab;
+            if (! _cutoff->isWithinCutoff(R_ab)) continue;
+            double w_ab = _cutoff->calculateWeight(R_ab);
+            GLOG() << "    " << a->getCenter()->getId() << ":" << b->getCenter()->getId() << " R=" << R_ab << " w=" << w_ab << std::endl;
+            // Interact
+            // ...
+        }
+    }
 
-    int L = 3;
+    GLOG() << "Factorial2 ..." << std::endl;
+    for (int n = 0; n < 16; ++n) {
+        GLOG() << n << "!! = " << factorial2(n) << std::endl;
+    }
+
+    GLOG() << "Radial damping functions ..." << std::endl;
+    std::vector<double> cl;
+    calculate_Fl(0., 0.5, 5, cl);
+    for (int l = 0; l <= 4; ++l) {
+        GLOG() << l << " fl = " << cl[l] << std::endl;
+    }
+    calculate_Fl(0.5, 0.5, 5, cl);
+    for (int l = 0; l <= 4; ++l) {
+        GLOG() << l << " fl = " << cl[l] << std::endl;
+    }
+    calculate_Fl(4.5, 0.5, 5, cl);
+    for (int l = 0; l <= 4; ++l) {
+        GLOG() << l << " fl = " << cl[l] << std::endl;
+    }
+    calculate_Fl(10.5, 0.5, 5, cl);
+    for (int l = 0; l <= 4; ++l) {
+        GLOG() << l << " fl = " << cl[l] << std::endl;
+    }
+
+    GLOG() << "Solid harmonics Rlm ..." << std::endl;
+    std::vector<std::complex<double>> rlm;
+    double phi = 0.6;
+    double theta = 0.7;
+    double sp = std::sin(phi);
+    double st = std::sin(theta);
+    double cp = std::cos(phi);
+    double ct = std::cos(theta);
+    vec d = vec(st*cp, st*sp, ct);
+    double r = 0.5;
+    calculate_solidharm_Rlm(d, r, L, rlm);
+    for (int l = 0; l <= L; ++l) {
+        for (int m = -l; m <= l; ++m) {
+            int lm = l*l+l+m;
+            GLOG() << l << " " << m << " " << rlm[lm] << std::endl;
+        }
+    }
+    GLOG() << "r = 0 ..." << std::endl;
+    r = 0.;
+    calculate_solidharm_Rlm(d, r, L, rlm);
+    for (int l = 0; l <= L; ++l) {
+        for (int m = -l; m <= l; ++m) {
+            int lm = l*l+l+m;
+            GLOG() << l << " " << m << " " << rlm[lm] << std::endl;
+        }
+    }
 
-    GLOG() << "Legendre ..." << std::endl;
+    /*GLOG() << "Legendre ..." << std::endl;
     std::vector<double> plm;
     calculate_legendre_plm(L, 0.3, plm);
     for (int l = 0; l <= L; ++l) {
@@ -62,7 +306,6 @@ void FTSpectrum::compute() {
             GLOG() << l << " " << m << " " << plm[lm] << std::endl;
         }
     }
-
     GLOG() << "Factorial ..." << std::endl;
     for (int n = 0; n < 16; ++n) {
         GLOG() << n << "! = " << factorial(n) << std::endl;
@@ -87,6 +330,9 @@ void FTSpectrum::compute() {
         }
     }
 
+
+    */
+
     return;
 }
 
diff --git a/src/soap/functions.cpp b/src/soap/functions.cpp
index 0291b8b..d80c846 100644
--- a/src/soap/functions.cpp
+++ b/src/soap/functions.cpp
@@ -270,6 +270,70 @@ long int factorial(int x) {
     }
 }
 
+const int FACTORIAL2_CACHE_SIZE = 16;
+const long int FACTORIAL2_CACHE[] = {
+    1,
+    1, 2, 3,
+    8, 15, 48,
+    105, 384, 945,
+    3840, 10395, 46080,
+    135135, 645120, 2027025 };
+
+long int factorial2(int x) {
+    if (x < 0) {
+        assert(x == -1);
+        return 1;
+    }
+    else if (x < FACTORIAL2_CACHE_SIZE) {
+        return FACTORIAL2_CACHE[x];
+    }
+    else {
+        assert(false && "Computing factorial from scratch - not desirable");
+        long int s = 1;
+        for (int n = x; n > 0; n -= 2) {
+            s *= n;
+        }
+        return s;
+    }
+}
+
+void calculate_solidharm_Rlm(
+    vec d,
+    double r,
+    int L,
+    std::vector<std::complex<double>> &rlm) {
+    // Initialise
+    rlm.clear();
+    rlm.resize((L+1)*(L+1), 0.0);
+    // Treat r=0
+    if (r < 1e-10) { // TODO Define SPACE-QUANTUM
+        //throw soap::base::APIError("Rlm(r=0) disabled by design: Handle r=0-case manually.");
+        rlm[0] = 1.;
+        return;
+    }
+    // Proceed with r != 0: Compute Associated Legendre Polynomials
+    std::vector<double> plm;
+	double theta = acos(d.getZ());
+	double phi = atan2(d.getY(), d.getX());
+	if (phi < 0.) phi += 2*M_PI; // <- Shift [-pi, -0] to [pi, 2*pi]
+	calculate_legendre_plm(L, d.getZ(), plm);
+    // Add radial component, phase factors, normalization
+    for (int l = 0; l <= L; ++l) {
+        for (int m = 0; m <= l; ++m) {
+            rlm[l*l+l+m] =
+                  pow(-1, l-m)
+                * pow(r, l)
+                / factorial(l+m)
+                * std::exp(std::complex<double>(0.,m*phi))
+                * plm[l*(l+1)/2+m];
+        }
+        for (int m = -l; m < 0; ++m) {
+            rlm[l*l+l+m] = pow(-1, m)*std::conj(rlm[l*l+l-m]);
+        }
+    }
+    return;
+}
+
 void calculate_solidharm_rlm_ilm(
         vec d,
         double r,
diff --git a/src/soap/functions.hpp b/src/soap/functions.hpp
index 8f75bf4..541b868 100644
--- a/src/soap/functions.hpp
+++ b/src/soap/functions.hpp
@@ -72,6 +72,16 @@ extern const int FACTORIAL_CACHE_SIZE;
 extern const long int FACTORIAL_CACHE[];
 long int factorial(int n);
 
+extern const int FACTORIAL2_CACHE_SIZE;
+extern const long int FACTORIAL2_CACHE[];
+long int factorial2(int n);
+
+void calculate_solidharm_Rlm(
+        vec d,
+        double r,
+        int L,
+        std::vector<std::complex<double>> &rlm);
+
 void calculate_solidharm_rlm_ilm(
         vec d,
         double r,
-- 
GitLab