/*
    DFT++ is a density functional package developed by the research group
    of Professor Tomas Arias

    Copyright 1996-2003 Sohrab Ismail-Beigi

    This file is part of DFT++.

    DFT++ is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    DFT++ is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DFT++; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Please see the file CREDITS for a list of authors.

    For academic users, we request that publications using results obtained with
    this software reference

    "New algebraic formulation of density functional calculation," by Sohrab Ismail-Beigi
    and T.A. Arias, Computer Physics Communications 128:1-2, 1-45 (June 2000).

    and, if using the wavelet basis, further reference

    "Multiresolution analysis of electronic structure: semicardinal and wavelet bases,"
    T.A. Arias, Reviews of Modern Physics 71:1, 267-311 (January 1999).

    and 

    "Robust ab initio calculation of condensed matter: transparent convergence through
    semicardinal multiresolution analysis,'' I.P. Daykov, T.A. Arias, and
    Torkel D. Engeness, Physical Review Letters, 90:21, 216402 (May 2003).

    For your convenience, preprints of the above articles may be obtained from
    http://arXiv.org/abs/cond-mat/9909130, 9805262, and 0204411, respectively.
*/

#include "header.h"

// This is the generalized preconditioner, which wraps up 
// precond_inv_kinetic.
void K(ColumnBundle &c, Everything &e)
{
  /* Roll-over value for kinetic preconditioning:  2.0*average kinetic
   * energy per band. */
  real KErollover;
  if(e.energies.KE > 0)
    KErollover = 2.0*e.energies.KE/e.elecinfo.nelectrons;
  else
    KErollover = 2.0; // this takes care of the band structure case, where
                      // we don't know the KE per electron

  precond_inv_kinetic(c,KErollover);
}
  
/*
 * Multiply column_bundle by preconditioner.  In our case, we precond.
 * with the inverse kinetic part of Hsp.
 *
 * For each column of Y, we multiply the G'th component by
 * f(0.5*|k+G|^2/KErollover) (the kinetic energy) where
 * f(x) satisfies:
 *
 * f(x) = 1 + O(x^N)          for x << 1  (does nothing to low KE parts)
 *      = (1/x)*(1 + O(x^-N)) for x >> 1  (scale by 2*KErollover/|k+G|^2 for
 *                                               high KE components)
 *      = N/(N+1)             for x == 1
 *
 * f(x) = (1+x+x^2+x^3+...+x^(N-1))/(1+x+x^2+...+x^N)=(1-x^N)/(1-x^(N+1))
 * has this property, and it is used below.  Currently, N = 9 below.
 *
 * The function of KErollover is to determine the point where x=1, i.e.
 * the roll-over point for the function f(x).
 */
void precond_inv_kinetic(ColumnBundle &Y, real KErollover)
{
#ifdef DFT_PROFILING
  timerOn(33); // turn on precond timer
#endif // DFT_PROFILING

  int i,j;
  Basis *basis;
  Lattice *lattice;
  real kplusGx,kplusGy,kplusGz,kplusG2,kx,ky,kz;
  real GGTxx,GGTyy,GGTzz,GGTxy,GGTxz,GGTyz;
  real f,x;
  int nbasis,my_ncols;

  //  if (Y.basis == 0)
  //  die("K(ColumnBundle) called with Y.basis == 0\n");
  //basis = Y.basis;

  /* Let's go! */
  nbasis = Y.col_length;
  my_ncols = Y.my_ncols;
  basis = Y.col[0]->basis;
  lattice = Y.col[0]->basis_spec->lattice;
  kx = basis->qnum->kvec.v[0];
  ky = basis->qnum->kvec.v[1];
  kz = basis->qnum->kvec.v[2];
  GGTxx = lattice->GGT.m[0][0];
  GGTyy = lattice->GGT.m[1][1];
  GGTzz = lattice->GGT.m[2][2];
  GGTxy = lattice->GGT.m[0][1];
  GGTxz = lattice->GGT.m[0][2];
  GGTyz = lattice->GGT.m[1][2];
  for (j=0; j < nbasis; j++)
    {
      kplusGx = kx + basis->Gx[j];
      kplusGy = ky + basis->Gy[j];
      kplusGz = kz + basis->Gz[j];
      kplusG2 = kplusGx*kplusGx*GGTxx +
                kplusGy*kplusGy*GGTyy +
	        kplusGz*kplusGz*GGTzz +
		2*(kplusGx*kplusGy*GGTxy +
		   kplusGx*kplusGz*GGTxz +
		   kplusGy*kplusGz*GGTyz    );
      x = 0.5*kplusG2/KErollover;
      f = 1.0+x*(1.0+x*(1.0+x*(1.0+x*(1.0+x*(1.0+x*(1.0+x*(1.0+x)))))));
      f = f/(1.0+x*f);

      // Hide ncols.
      for (i=0; i < my_ncols; i++)
        Y.col[i]->data.d[j] *= f;
    }

#ifdef DFT_PROFILING
  timerOff(33); // turn off precond timer
#endif // DFT_PROFILING
}
