/* 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" // Calculate Pulay correction energy void calc_Epulay(Everything &everything) { Elecvars &evars = everything.elecvars; BasisSpec &spec = everything.basis_spec; Lattice &lattice = everything.lattice; Energies &ener = everything.energies; Ioninfo &ioninfo = everything.ioninfo; int sp; real NGidealperVol,NGactualperVol; NGidealperVol = sqrt(2.0)*pow(spec.Ecut,1.5)/(3.0*M_PI*M_PI); // get the average nbasis int q; NGactualperVol = 0.0; for(q = 0; q < evars.nstates; q++) NGactualperVol += (real)evars.states[0].basis.nbasis/lattice.unit_cell_volume; NGactualperVol /= (real)evars.nstates; ener.Epulay = 0.0; for (sp=0; sp < ioninfo.nspecies; sp++) ener.Epulay += ioninfo.species[sp].natoms* ioninfo.species[sp].pot.dEperNatoms_dNGperVol; ener.Epulay *= (NGidealperVol-NGactualperVol); } /* Local pseudopotential core energy */ void calc_Ecore(Everything &everything) { #ifdef DFT_PROFILING timerOn(23); // Turn on calc_Ecore timer #endif // DFT_PROFILING Elecinfo &einfo = everything.elecinfo; Lattice &lattice = everything.lattice; Ioninfo &ioninfo = everything.ioninfo; everything.energies.Ecore = Vlocpot_GzeroEnergy(einfo.nelectrons,lattice,ioninfo); #ifdef DFT_PROFILING timerOff(23); // Turn off calc_Ecore timer #endif // DFT_PROFILING } /* * Calculate non-local pseudopotential energy: * * Enl = sum_{spcies,ions,l,m,k,...} * { trace(M_lm*Vnl^C[k]*F[k]*(Vnl^C[k])^ } * * For the Kleinman-Bylander case, the filling in of the Vnl is * done in parallel (parallelization over atoms) using the thread above. */ void calc_Enl(Everything &everything) { #ifdef DFT_PROFILING timerOn(20); // Turn on calc_Enl timer #endif // DFT_PROFILING everything.energies.Enl=0; Elecinfo &einfo = everything.elecinfo; Elecvars &evars = everything.elecvars; Ioninfo &ioninfo = everything.ioninfo; int sp,lm,i,q; BlochState *states = evars.states; real Enl; Enl = (real)0.0; for (sp=0; sp < ioninfo.nspecies; sp++) for (lm=0; lm < ioninfo.species[sp].pot.nlm; lm++) { if (ioninfo.species[sp].pot.ngamma[lm] > 1) { dft_log(DFT_SILENCE, "\nMultiple-projectors: running slow calc_Enl!\n"); /* this is the slow way where we go one atom at a time... * the smarter way would be to somehow make a new class * which is a block-diagonal matrix class (a string of matrix * classes on the diagonal of a bigger one), where each * diagonal is just Mnl below, and to define an * block_diag_matrix*matrix (returning matrix) operator. * Then we can do what we do with the Kleinman-Bylander * below with minimal changes. */ Matrix VdagC(ioninfo.species[sp].pot.ngamma[lm],einfo.nbands); Matrix &Mnl = ioninfo.species[sp].pot.M[lm]; /* reference */ for (q=0; q < evars.nstates; q++) { ColumnBundle Vnl(ioninfo.species[sp].pot.ngamma[lm], &(states[q].basis),"local"); for (i=0; i < ioninfo.species[sp].natoms; i++) { Vnl_pseudo(sp,i,lm,&ioninfo,Vnl); VdagC = Vnl^states[q].C; Enl += REAL(states[q].w*trace(Mnl*VdagC*states[q].F*herm_adjoint(VdagC))); } } } /* Kleinman-Bylander: bunch up all local potentials for * the atoms of this species and state into a big column_bundle * and work on them instead (should be faster due to ^ and * * operators being block-multiplies, etc.) */ else { Matrix VdagC(ioninfo.species[sp].natoms,einfo.nbands); scalar Mnl = ioninfo.species[sp].pot.M[lm](0,0); for (q=0; q < evars.nstates; q++) { // Vnl is created as distributed column_bundle. // the dimension that's distributed is ioninfo.species[sp].natoms ColumnBundle Vnl(ioninfo.species[sp].natoms,&(states[q].basis), "distributed"); // Fill in Vnl with pseudopotential elements Vnl_pseudo_fill_matrix(&ioninfo,Vnl,sp,lm); // Now use Vnl! VdagC = Vnl^states[q].C; Enl += REAL(states[q].w*trace(Mnl*VdagC*states[q].F*herm_adjoint(VdagC))); } } } everything.energies.Enl = Enl; #ifdef DFT_PROFILING timerOff(20); // Turn off calc_Enl timer #endif // DFT_PROFILING }