Program Listing for File mathutil.cpp

Return to documentation for file (/Users/robertshaw/devfiles/libecpint/src/lib/mathutil.cpp)

/*
 *      Copyright (c) 2020 Robert Shaw
 *      This file is a part of Libecpint.
 *
 *      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.
 */

#include "mathutil.hpp"
#include <iostream>

namespace libecpint {

    double FAC[MAX_FAC];
    double DFAC[MAX_DFAC];

    double pow_m2(const double z) { return 1.0/(z*z); }
    double pow_m1(const double z) { return 1.0/z; }
    double pow_0(const double z) { return 1.0; }
    double pow_1(const double z) { return z; }
    double pow_2(const double z) { return z*z; }
    double pow_3(const double z) { return z*z*z; }
    double pow_4(const double z) { double z2 = z*z; return z2*z2; }
    double pow_5(const double z) { double z2 = z*z; double z3 = z2*z; return z2*z3; };
    double pow_6(const double z) { double z2 = z*z; return z2*z2*z2; }
    double pow_7(const double z) { double z2 = z*z; double z3 = z*z2; return z3*z2*z2; }
    double pow_8(const double z) { double z2 = z*z; double z4 = z2*z2; return z4*z4; }
    double pow_9(const double z) { double z3 = z*z*z; return z3*z3*z3; }
    double pow_10(const double z) { double z2 = z*z; double z3 = z*z2; double z5= z2*z3; return z5*z5; }
    double pow_11(const double z) { double z2 = z*z; double z3 = z*z2; return z3*z3*z3*z2; }
    double pow_12(const double z) { double z3 = z*z*z; double z6 = z3*z3; return z6*z6; }
    double pow_13(const double z) { double z3 = z*z*z; double z6 = z3*z3; return z6*z6*z; }
    double pow_14(const double z) { double z2 = z*z; double z3 = z*z2; double z7 = z2*z2*z3; return z7*z7; }
    double pow_15(const double z) { double z2 = z*z; double z3 = z*z2; double z5 = z2*z3; return z5*z5*z5; }
    double pow_16(const double z) { double z2 = z*z; double z4 = z2*z2; double z8 = z4*z4; return z8*z8; }
    double pow_17(const double z) { double z2 = z*z; double z4 = z2*z2; double z8 = z4*z4; return z8*z8*z;}
    double pow_18(const double z) { double z3 = z*z*z; double z9 = z3*z3*z3; return z9*z9; }
    double pow_19(const double z) { double z3 = z*z*z; double z9 = z3*z3*z3; return z9*z9*z; }
    double pow_20(const double z) { double z2 = z*z; double z4 = z2*z2; double z8 = z4*z4; return z8*z8*z4; }

    void initFactorials() {
    #ifndef FAC_INIT
    #define FAC_INIT
            FAC[0] = 1.0;
            DFAC[0] = 1.0;
            DFAC[1] = 1.0;

            for (int i = 1; i < MAX_FAC; i++)  FAC[i] = double(i) * FAC[i-1];
            for (int i = 2; i < MAX_DFAC; i++) DFAC[i] = double(i) * DFAC[i-2];
    #endif
    }

    // Compute all the real spherical harmonics Slm(theta, phi) for l,m up to lmax
    // x = std::cos (theta)
    TwoIndex<double> realSphericalHarmonics(const int lmax, const double x, const double phi){
        TwoIndex<double> rshValues(lmax+1, 2*lmax+1, 0.0);

        if (lmax > 0) {
            // First calculate the associated Legendre polynomials, Plm(std::cos theta), ustd::sing the recursion relation
            // (l-m)Plm = x(2l - 1)P{l-1}m - (l+m-1)P{l-2}m
            // along with the zeroth order term
            // Pmm = (-1)^m (2m-1)!!(1-x^2)^{m/2}
            double x2 = x * x;
            double Plm[lmax+1][lmax+1];
            // First get all Pmm terms
            Plm[0][0] = 1.0;
            double sox2 = std::sqrt(std::max(0.0, 1.0 - x2));
            double ox2m = 1.0;
            for (int m = 1; m <= lmax; m++) {
                ox2m *= -sox2;
                Plm[m][m] = ox2m * DFAC[2*m-1];
            }

            // Then increment l for each m
            Plm[1][0] = x;
            Plm[0][1] = 0.0;
            for (int l = 2; l <= lmax; l++) {
                ox2m = x * (2*l - 1);
                for (int m = 0; m < l; m++) {
                    Plm[l][m] = ox2m * Plm[l-1][m] - (l + m - 1)*Plm[l-2][m];
                    Plm[l][m] /= ((double) (l -m));
                }
                Plm[l-1][l] = 0.0;
            }

            // Now we compute the spherical harmonics via
            // Slm(theta, phi) = Clm * Plm(std::cos(theta)) * std::cos(m * phi), m > 0
            // Sl{-m}(theta, phi) = Clm * Plm(std::cos(theta)) * std::sin(m * phi)
            // Sl0(theta, phi) = std::sqrt(2) * Cl0 * Pl0(std::cos(theta))
            // where Clm^2 = (2l + 1)*(l - m)! / (8*pi * (l+m)!)
            double osq4pi = 1.0 / std::sqrt(4.0 * M_PI);
            int sign;
            for (int l = 0; l <= lmax; l++) {
                rshValues(l, l) = osq4pi * std::sqrt(2.0 * l + 1.0) * Plm[l][0];
                sign = -1;
                for (int m = 1; m <= l; m++) {
                    ox2m = (2.0 * l + 1.0) * FAC[l-m] / FAC[l+m];
                    ox2m = sign * osq4pi * std::sqrt(2.0 * ox2m) * Plm[l][m];
                    rshValues(l, l+m) = ox2m * std::cos(m * phi);
                    rshValues(l, l-m) = ox2m * std::sin(m * phi);
                    sign *= -1;
                }
            }

        } else {
            rshValues(0, 0) = 1.0 / std::sqrt(4.0 * M_PI);
        }

        return rshValues;
    }

    double frobenius_norm(const TwoIndex<double>& mat) {
        return std::sqrt(std::inner_product(mat.data.begin(), mat.data.end(), mat.data.begin(), 0.0));
    }

}