#include #include #include #include #include "green.h" #include #include "global.h" /******************************************************************************* * The class of Green is designed to evaluate the LDOS via the Green's Function * method. The meaning of input/output parameters are as follows: * * ntm (input, value) total number of atoms in system * sdim (input, value) dimension of the system; usually 3 * niter (input, value) maximum iterations during Lanczos diagonalization * min (input, value) minimum value for the angular frequency * max (input, value) maximum value for the angular frequency * ndos (input, value) total number of points in LDOS * eps (input, value) epson that govens the width of delta-function * Hessian (input, pointer of pointer) mass-weighted force constant matrix, of * dimension [natom*sysdim][natm*sysdim]; it is actually * the dynamical matrix at gamma point * itm (input, value) index of the atom to evaluate local phonon DOS, from 0 * lpdos (output, array) double array of size (ndos, sdim) ******************************************************************************* * References: * 1. Z. Tang and N. R. Aluru, Phys. Rev. B 74, 235441 (2006). * 2. C. Hudon, R. Meyer, and L.J. Lewis, Phys. Rev. B 76, 045409 (2007). * 3. L.T. Kong and L.J. Lewis, Phys. Rev. B 77, 165422 (2008). * * NOTE: The real-space Green's function method is not expected to work accurately * for small systems, say, system (unit cell) less than 500 atoms. *******************************************************************************/ /*------------------------------------------------------------------------------ * Constructor is used as the main driver *----------------------------------------------------------------------------*/ Green::Green(const int ntm, const int sdim, const int niter, const double min, const double max, const int ndos, const double eps, double **Hessian, const int itm, double **lpdos) { const double tpi = 8.*atan(1.); natom = ntm; sysdim = sdim; nit = niter; epson = eps; wmin = min*tpi; wmax = max*tpi; nw = ndos + (ndos+1)%2; H = Hessian; iatom = itm; ldos = lpdos; memory = new Memory(); if (natom < 1 || iatom < 0 || iatom >= natom){ printf("\nError: Wrong number of total atoms or wrong index of interested atom!\n"); return; } ndim = natom * sysdim; if (nit < 1){printf("\nError: Wrong input of maximum iterations!\n"); return;} if (nit > ndim){printf("\nError: # Lanczos iterations is not expected to exceed the degree of freedom!\n"); return;} if (nw < 1){printf("\nError: Wrong input of points in LDOS!\n"); return;} // initialize variables and allocate local memories dw = (wmax - wmin)/double(nw-1); memory->create(alpha, sysdim,nit, "Green_Green:alpha"); memory->create(beta, sysdim,nit+1,"Green_Green:beta"); //memory->create(ldos, nw,sysdim, "Green_Green:ldos"); // use Lanczos algorithm to diagonalize the Hessian Lanczos(); // Get the inverser of the treated hessian by continued fractional method Recursion(); return; } /*------------------------------------------------------------------------------ * Deconstructor is used to free memory *----------------------------------------------------------------------------*/ Green::~Green() { H = NULL; ldos = NULL; memory->destroy(alpha); memory->destroy(beta); delete memory; return; } /*------------------------------------------------------------------------------ * Private method to diagonalize a matrix by the Lanczos algorithm *----------------------------------------------------------------------------*/ void Green::Lanczos() { double *vp, *v, *w, *ptr; vp = new double [ndim]; v = new double [ndim]; w = new double [ndim]; int ipos = iatom*sysdim; // Loop over dimension for (int idim = 0; idim < sysdim; ++idim){ beta[idim][0] = 0.; for (int i = 0; i < ndim; ++i) vp[i] = v[i] = 0.; v[ipos+idim] = 1.; // Loop on fraction levels for (int i = 0; i < nit; ++i){ double sum_a = 0.; for (int j = 0; j < ndim; ++j){ double sumHv = 0.; for (int k = 0; k < ndim; ++k) sumHv += H[j][k]*v[k]; w[j] = sumHv - beta[idim][i]*vp[j]; sum_a += w[j]*v[j]; } alpha[idim][i] = sum_a; for (int k = 0; k < ndim; ++k) w[k] -= alpha[idim][i]*v[k]; double gamma = 0.; for (int k = 0; k < ndim; ++k) gamma += w[k]*v[k]; for (int k = 0; k < ndim; ++k) w[k] -= gamma*v[k]; double sum_b = 0.; for (int k = 0; k < ndim; ++k) sum_b += w[k]*w[k]; beta[idim][i+1] = sqrt(sum_b); ptr = vp; vp = v; v = ptr; double tmp = 1./beta[idim][i+1]; for (int k = 0; k < ndim; ++k) v[k] = w[k] * tmp; } } ptr = NULL; delete []vp; delete []v; delete []w; return; } /*------------------------------------------------------------------------------ * Private method to compute the LDOS via the recusive method for system with * many atoms *----------------------------------------------------------------------------*/ void Green::Recursion() { // local variables double *alpha_inf, *beta_inf, *xmin, *xmax; alpha_inf = new double [sysdim]; beta_inf = new double [sysdim]; xmin = new double [sysdim]; xmax = new double [sysdim]; int nave = nit/4; for (int idim = 0; idim < sysdim; ++idim){ alpha_inf[idim] = beta_inf[idim] = 0.; for (int i = nit-nave; i < nit; ++i){ alpha_inf[idim] += alpha[idim][i]; beta_inf[idim] += beta[idim][i+1]; } alpha_inf[idim] /= double(nave); beta_inf[idim] /= double(nave); xmin[idim] = alpha_inf[idim] - 2.*beta_inf[idim]; xmax[idim] = alpha_inf[idim] + 2.*beta_inf[idim]; } std::complex Z, z_m_a, r_x, rec_x, rec_x_inv; double sr, si; double w = wmin; for (int i = 0; i < nw; ++i){ double a = w*w, ax, bx; Z = std::complex(w*w, epson); for (int idim = 0; idim < sysdim; ++idim){ double two_b = 2.*beta_inf[idim]*beta_inf[idim]; double rtwob = 1./two_b; z_m_a = Z - alpha_inf[idim]*alpha_inf[idim]; if ( a < xmin[idim] ){ r_x = sqrt(-2.*two_b + z_m_a); ax = std::real(r_x) * rtwob; bx = std::imag(r_x) * rtwob; } else if (a > xmax[idim]) { r_x = sqrt(-2.*two_b + z_m_a); ax = -std::real(r_x) * rtwob; bx = -std::imag(r_x) * rtwob; } else { r_x = sqrt(2.*two_b - z_m_a); ax = std::imag(r_x) * rtwob; bx = -std::real(r_x) * rtwob; } sr = (a - alpha_inf[idim])*rtwob + ax; si = epson * rtwob + bx; rec_x = std::complex (sr, si); for (int j = 0; j < nit; ++j){ rec_x_inv = Z - alpha[idim][nit-j-1] - beta[idim][nit-j]*beta[idim][nit-j]*rec_x; rec_x = 1./rec_x_inv; } ldos[i][idim] = std::imag(rec_x)*w; } w += dw; } delete []alpha_inf; delete []beta_inf; delete []xmin; delete []xmax; return; } /*------------------------------------------------------------------------------ * Private method to compute the LDOS via the recusive method for system with * a few atoms (less than NMAX) *----------------------------------------------------------------------------*/ void Green::recursion() { // local variables std::complex Z, rec_x, rec_x_inv; std::complex cunit = std::complex(0.,1.); double w = wmin; for (int i = 0; i < nw; ++i){ Z = std::complex(w*w, epson); for (int idim = 0; idim < sysdim; ++idim){ rec_x = std::complex(0.,0.); for (int j = 0; j < nit; ++j){ rec_x_inv = Z - alpha[idim][nit-j-1] - beta[idim][nit-j]*beta[idim][nit-j]*rec_x; rec_x = 1./rec_x_inv; } ldos[i][idim] = std::imag(rec_x)*w; } w += dw; } return; } /*------------------------------------------------------------------------------*/