
.. _program_listing_file__Users_robertshaw_devfiles_libecpint_src_lib_gaussquad.cpp:

Program Listing for File gaussquad.cpp
======================================

|exhale_lsh| :ref:`Return to documentation for file <file__Users_robertshaw_devfiles_libecpint_src_lib_gaussquad.cpp>` (``/Users/robertshaw/devfiles/libecpint/src/lib/gaussquad.cpp``)

.. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS

.. code-block:: 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 "gaussquad.hpp"
   #include <cmath>
   #include <cassert>
   #include <iostream>
   
   namespace libecpint {
   
       // Constructor
       GCQuadrature::GCQuadrature() {
           // Currently does nothing
       }
   
   
       GCQuadrature::GCQuadrature(const GCQuadrature &other) {
           maxN = other.maxN;
           M = other.M;
           I = other.I;
           t = other.t;
           x = other.x;
           w = other.w;
       }
   
       // Initialise the quadrature grid
       // As described in both Perez92 and Perez93
       void GCQuadrature::initGrid(const int points, const GCTYPE _t) {
           t = _t;
       
           // Initialise parameters for grid
           int p;
           if (t == ONEPOINT) { // Perez92 one point method
               // We need the number of points to be of the form
               // 2^p - 1 for some power p. 
               p = (int) floor(log(points + 1)/log(2));
               maxN = pow(2, p) - 1;
           } else if (t == TWOPOINT) { // Perez93 two point method
               // Here we need it instead to be of the form
               // 3 * 2^p - 1 for some p.
               p = (int) floor(log((points + 2)/3.0)/log(2));
               maxN = 3*pow(2, p) - 1;
           }
           M = (maxN-1)/2; // Midpoint
       
           // initialise arrays
           x.assign(maxN, 0.0);
           w.assign(maxN, 0.0);
       
           // At the midpoint, M, x[M] = 0 and w[M] = 1
           x[M] = 0.0; w[M] = 1.0;
           // The rest of the abscissae and weights are then given by:
           // z_i = i*Pi / (maxN + 1), s_i = sin(z_i), c_i = cos(z_i)
           // x_i = 1 + 2/(3*pi) * [ (3 + 2*s_i^2) * c_i * s_i - 3z_i]
           // (3(maxN + 1)/16) * w_i = s_i^4
           // We then note that s_(i+1) = c_1 s_i + s_1 c_i
           // and c_(i+1) = c_1 c_i - s_1 s_i
           // with z_(i+1) = z_i + z_1
           // Clearly s_(maxN + 1 - i) = s_i
           // and c_(maxN + 1 - i) = -c_i
           // Therefore x_(maxN + 1 - i) = -x_i
           // and w_(maxN + 1 - i) = w_i
           // Meaning that we only have to calculate half the weights and abscissae
           double z1 = M_PI / ((double)(maxN + 1));
           double c1 = cos(z1); double s1 = sin(z1);
           double zi, si, ci, zi1, si1, ci1; //z_i, s_i, c_i, z_(i+1), s_(i+1), c_(i+1)
           zi1 = z1; si1 = s1; ci1 = c1;
           double o23pi = 2.0 / (3.0 * M_PI); // Convenient
           double s2; //si * si
           for (int n = 0; n < M; n++) {
               // First update zi, si, ci
               zi = zi1;
               si = si1;
               ci = ci1;
               s2 = si * si;
           
               // Now determine the w and x values
               w[maxN - 1 - n] = w[n] = s2 * s2;
               x[n] = 1 + o23pi * ( (3.0 + 2.0 * s2) * ci * si - 3.0*zi );
               x[maxN - 1 - n] = x[n];
               x[n] = -x[n];
           
               // Then update zi1, si1, ci1
               zi1 = zi + z1;
               si1 = c1 * si + s1 * ci;
               ci1 = c1 * ci - s1 * si;
           }
       
           /*std::cout << maxN << " " << M << " " << start << " " << end << "\n";
           for (int q = 0; q < maxN; q++) std::cout << x[q] << " " << w[q] << "\n";*/
       }
   
       // Perform the GC integration on the function f
       std::pair<double, bool> GCQuadrature::integrate(
           std::function<double(double, const double*, int)> &f, const double *params,
           const double tolerance, const int start, const int end) const {
           bool converged = false; // 0 for converged, -1 for not converged
   
           double I = 0;
           if (t == ONEPOINT) {
               // Perez92 Case
               // Integration proceeds in the sequence T_1, T_3, T_7, ..., T_{maxN}
               // where T_m = (3(m+1)/16)I_m
               // by using the fact that T_{2m + 1} = T_{m} + sum_{k = 0}^m w_{2k+1}f(x_{2k+1})
               // The indices in terms of the maxN indices are given by 
               // 2k + 1 = (2k + 1) * M / 2^n = (2k + 1) * p
               // and checking convergence via whether
               // (T_{2m + 1} - 2T_m)^2 <= |T_{2m+1} - 4T_{(m-1)/2}| x tolerance
               double Tn, T2n1, Tn12; // T_n, T_{2n+1} and 4T_{(n-1)/2}
           
               // Initialise values, 
               // Single point integration would use midpoint, M
               Tn = w[M]*f(x[M], params, M);
               Tn12 = 2.0 * Tn;
           
               // Main loop
               int n = 1;
               double dT; // T_{2n+1} - 2T_n
               int ix; // Index needs to be calculated to know which points to use
               int p = (M+1) / 2; // M / 2^n 
               while (n < maxN && !converged) {
                   // Compute T_{2n+1}
                   T2n1 = Tn + sumTerms(f, params, n, start, end, p, 2);
               
                   // Check convergence
                   dT = T2n1 - 2.0*Tn;
                   n = 2*n + 1;
                   if (dT*dT <= fabs(T2n1 - Tn12)*tolerance) {
                       converged = true;  
                   } else {
                       Tn12 = 4.0 * Tn; 
                       Tn = T2n1;
                       p /= 2; 
                   }
               }
               // Finalise the integral
               I = 16.0 * T2n1 / (3.0*(n + 1.0));
           
           } else if (t == TWOPOINT) {
               // Perez93 case
               // We instead proceed along T_2, T_5, T_11, ..., T_{2m + 1} where maxN = 2m+1
               // but also compute T_1, T_3, ..., T_n, T_{2n+1} etc. as before,
               // where m + 1 = 3/2(n+1), so as to get better error control
               // To do this, we use that 
               // T_{2m+1} = T_m + T_n - T_{(n-1)/2} + sum_{i=0}^{(m-2)/3} [w_{6i+1}f(x_{6i+1}) + w_{6i+5}f(x_{6i+5})]
               // along with the same results as before. 
               // The algorithm proceeds by calculating one in the two-point sequence,
               // using an error of |I_{2m+1} - I_m|, then calculates one in the one-point sequence
               // and uses an error of |I_m - I_n|, to check convergence.
               double Tn, Tm, T2n1, T2m1, Tn12;
           
               // Initialise values
               Tn12 = 0.0; 
               Tn = w[M]*f(x[M], params, M);
               int M2 = (maxN - 2)/3; //Index of first point in twopoint sequence
               Tm = w[M2]*f(x[M2], params, M2) + w[maxN - M2 - 1]*f(x[maxN - M2 - 1], params, maxN - M2 - 1);
               int p = (M+1) / 2; // as before
               M2 = (M2 + 1)/2; 
               int ix; 
               int n = 1; int m = 2;
               double error;
            
               while(m < maxN && !converged) {
                   // Propagate the two-point sequence first 
                   T2m1 = Tm + Tn - Tn12 + sumTerms(f, params, (2*m - 1)/3, start, end, M2, 3);
               
                   // Check convergence
                   error = 16.0 * fabs(0.5*T2m1 - Tm) / (3.0 * (m + 1)); 
                   if (error > tolerance) {
                       // Propagate the one-point sequence
                       T2n1 = Tn + sumTerms(f, params, n, start, end, p, 2);
                   
                       // Check convergence again
                       error = 16.0 * fabs(2.0*T2m1 - 3.0*T2n1) / (18.0 * (n+1) );
                       m = 2 * m + 1;
                       n = 2 * n + 1;
                       if ( error < tolerance) {
                           converged = true; 
                       } else {
                           Tn12 = Tn;
                           Tn = T2n1;
                           Tm = T2m1; 
                           p /= 2;
                           M2 /= 2; 
                       }
                   } else {
                       m = 2 * m + 1;
                       converged = true; 
                   }
               }
               // Finalise the integral
               I = 16.0 * T2m1 / (3.0 * (m + 1.0));
           }
       
           return {I, converged};
       }
   
       // Worker function to do the additional sum terms when going from I_n to I_{2n+1}
       double GCQuadrature::sumTerms(
           const std::function<double(double, const double*, int)> &f,
           const double *p, const int limit, const int start, const int end,
           const int shift, const int skip) const {
           assert(start >= 0 && start < maxN);
           assert(end >= 0 && end < maxN);
           assert(end >= start);
   
           double value = 0.0;
           int ix; 
           for (int i = 0; i <= limit; i+=2) { 
               ix = (skip*i + 1)*shift - 1;
               if (ix >= start)
                   value += w[ix] * f(x[ix], p, ix);
           
               ix = maxN - ix - 1; 
               if (ix <= end)
                   value += w[ix] * f(x[ix], p, ix);
           }
           return value;
       }
   
       // The GC integrations above are over the interval [-1, 1] and thus need to be transformed
       // to the interval[0, infty), or [rmin, rmax]. We do this by the logarithmic transformation from Krack98
       // or the linear mapping of Flores06, respectively.  
       void GCQuadrature::transformZeroInf() {
           double ln2 = log(2.0);
           double xt;
       
           for (int i = 0; i < maxN; i++) {
               xt = 1.0 - log(1.0-x[i])/ln2;
               w[i] = w[i]/(ln2 * (1.0 - x[i]));
               x[i] = xt;
           }
       }
   
       void GCQuadrature::transformRMinMax(const double z, const double p) {
           double osz = 1.0 / sqrt(z);
       
           // Determine interval
           double rmin = p - 7.0 * osz;
           rmin = rmin > 0 ? rmin : 0.0;
           double rmax = p + 9.0 * osz;
       
           // Find the relative and absolute midpoints 
           double rmid = 0.5*(rmax - rmin); // Midpoint of interval relative to rmin
           double amid = rmid + rmin; // Midpoint of interval
       
           // Transform weights and abscissae by linearly transforming
           // both are scaled by rmid, and the abscissae are translated by amid
           for (int i = 0; i < maxN; i++) {
               x[i] = rmid * x[i] + amid;
               w[i] *= rmid;
           }
       }
       
       void GCQuadrature::untransformRMinMax(const double z, const double p) {
           double osz = 1.0 / sqrt(z);
       
           // Determine interval
           double rmin = p - 7.0 * osz;
           rmin = rmin > 0 ? rmin : 0.0;
           double rmax = p + 9.0 * osz;
       
           // Find the relative and absolute midpoints 
           double rmid = 0.5*(rmax - rmin); // Midpoint of interval relative to rmin
           double amid = rmid + rmin; // Midpoint of interval
       
           // Transform weights and abscissae by linearly transforming
           // both are scaled by rmid, and the abscissae are translated by amid
           for (int i = 0; i < maxN; i++) {
               x[i] = (x[i] - amid) / rmid;
               w[i] /= rmid;
           }
       }
   
   }
