//////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2008 The Regents of the University of California // // This file is part of Qbox // // Qbox is distributed 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. // See the file COPYING in the root directory of this distribution // or . // //////////////////////////////////////////////////////////////////////////////// // // Wavefunction.C // //////////////////////////////////////////////////////////////////////////////// #include "Wavefunction.h" #include "SlaterDet.h" #include "jacobi.h" #include "SharedFilePtr.h" #include #include #include using namespace std; //////////////////////////////////////////////////////////////////////////////// Wavefunction::Wavefunction(const Context& ctxt) : ctxt_(ctxt), nel_(0), nempty_(0), nspin_(1), deltaspin_(0), ecut_(0.0), nrowmax_(32) { // create a default wavefunction: one k point, k=0 kpoint_.resize(1); kpoint_[0] = D3vector(0,0,0); weight_.resize(1); weight_[0] = 1.0; compute_nst(); create_contexts(); allocate(); } //////////////////////////////////////////////////////////////////////////////// Wavefunction::Wavefunction(const Wavefunction& wf) : ctxt_(wf.ctxt_), nel_(wf.nel_), nempty_(wf.nempty_), nspin_(wf.nspin_), deltaspin_(wf.deltaspin_), nrowmax_(wf.nrowmax_), cell_(wf.cell_), refcell_(wf.refcell_), ecut_(wf.ecut_), weight_(wf.weight_), kpoint_(wf.kpoint_) { // Create a Wavefunction using the dimensions of the argument compute_nst(); // Next lines: do special allocation of contexts to ensure that // contexts are same as those of wf spincontext_ = new Context(*wf.spincontext_); kpcontext_ = new Context(*wf.kpcontext_); sdcontext_ = new Context(*wf.sdcontext_); allocate(); resize(cell_,refcell_,ecut_); init(); } //////////////////////////////////////////////////////////////////////////////// Wavefunction::~Wavefunction() { deallocate(); delete spincontext_; delete kpcontext_; delete sdcontext_; } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::allocate(void) { // create SlaterDets using kpoint list const int nkp = kpoint_.size(); sd_.resize(nspin_); for ( int ispin = 0; ispin < nspin_; ispin++ ) { sd_[ispin].resize(nkp); for ( int ikp = 0; ikp < nkp; ikp++ ) { sd_[ispin][ikp] = new SlaterDet(*sdcontext_,kpoint_[ikp]); } } } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::deallocate(void) { for ( int ispin = 0; ispin < sd_.size(); ispin++ ) { for ( int ikp = 0; ikp < sd_[ispin].size(); ikp++ ) { delete sd_[ispin][ikp]; } sd_[ispin].resize(0); } } //////////////////////////////////////////////////////////////////////////////// int Wavefunction::nkp(void) const { return kpoint_.size(); } //////////////////////////////////////////////////////////////////////////////// int Wavefunction::nel() const { return nel_; } // total number of electrons //////////////////////////////////////////////////////////////////////////////// int Wavefunction::nst() const { if ( nspin_ == 1 ) return nst_[0]; else return nst_[0]+nst_[1]; } //////////////////////////////////////////////////////////////////////////////// int Wavefunction::nst(int ispin) const { assert(ispin >= 0 && ispin < 2); return nst_[ispin]; } //////////////////////////////////////////////////////////////////////////////// int Wavefunction::nempty() const { return nempty_; } //////////////////////////////////////////////////////////////////////////////// int Wavefunction::nspin() const { return nspin_; } //////////////////////////////////////////////////////////////////////////////// int Wavefunction::deltaspin() const { return deltaspin_; } //////////////////////////////////////////////////////////////////////////////// double Wavefunction::entropy(void) const { double sum = 0.0; for ( int ispin = 0; ispin < nspin(); ispin++ ) { for ( int ikp = 0; ikp < nkp(); ikp++ ) { sum += weight_[ikp] * sd(ispin,ikp)->entropy(nspin_); } } return sum; } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::resize(const UnitCell& cell, const UnitCell& refcell, double ecut) { try { // resize all SlaterDets using cell, refcell, ecut and nst_[ispin] for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { sd_[ispin][ikp]->resize(cell,refcell,ecut,nst_[ispin]); } } cell_ = cell; refcell_ = refcell; ecut_ = ecut; } catch ( const SlaterDetException& sdex ) { cout << " Wavefunction: SlaterDetException during resize: " << endl << sdex.msg << endl; // no resize took place return; } catch ( bad_alloc ) { cout << " Wavefunction: insufficient memory for resize operation" << endl; return; } } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::init(void) { // initialize all SlaterDets with lowest energy plane waves for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { sd_[ispin][ikp]->init(); } } } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::clear(void) { // initialize all SlaterDets with zero for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { sd_[ispin][ikp]->c().clear(); } } } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::reset(void) { // reset to single kpoint, ecut=0 deallocate(); nel_ = 0; nempty_ = 0; nspin_ = 1; deltaspin_ = 0; kpoint_.resize(1); kpoint_[0] = D3vector(0,0,0); weight_.resize(1); weight_[0] = 1.0; compute_nst(); allocate(); } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::compute_nst(void) { // recompute nst from nel_, deltaspin_, nempty_ nst_.resize(nspin_); if ( nspin_ == 1 ) { nst_[0] = ( nel_ + 1 ) / 2 + nempty_; } else { // nspin == 2 nst_[0] = ( nel_ + 1 ) / 2 + deltaspin_ + nempty_; nst_[1] = nel_ / 2 - deltaspin_ + nempty_; } } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::set_nel(int nel) { if ( nel == nel_ ) return; if ( nel < 0 ) { cout << " Wavefunction::set_nel: nel < 0" << endl; return; } nel_ = nel; compute_nst(); resize(cell_,refcell_,ecut_); } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::set_nempty(int nempty) { if ( nempty == nempty_ ) return; if ( nempty < 0 ) { cout << " Wavefunction::set_nempty: negative value" << endl; return; } nempty_ = nempty; compute_nst(); update_occ(0.0); resize(cell_,refcell_,ecut_); } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::set_nspin(int nspin) { assert(nspin==1 || nspin==2); if ( nspin == nspin_ ) return; deallocate(); nspin_ = nspin; compute_nst(); allocate(); resize(cell_,refcell_,ecut_); init(); update_occ(0.0); } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::set_deltaspin(int deltaspin) { if ( deltaspin == deltaspin_ ) return; // check if value of deltaspin would result in nst[1] < 0 // nst_[1] = nel_ / 2 - deltaspin_ + nempty_; if ( nel_ / 2 - deltaspin + nempty_ < 0 ) { if ( ctxt_.onpe0() ) { cout << " Wavefunction::set_deltaspin: nel+nempty too small" << endl; cout << " Wavefunction::set_deltaspin: cannot set deltaspin" << endl; return; } } deallocate(); deltaspin_ = deltaspin; compute_nst(); allocate(); resize(cell_,refcell_,ecut_); init(); update_occ(0.0); } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::create_contexts(void) { // determine dimensions of sdcontext assert(nrowmax_>0); const int size = ctxt_.size(); int npr = nrowmax_; while ( size%npr != 0 ) npr--; // npr now divides size int npc = size/npr; spincontext_ = new Context(ctxt_.comm(),npr,npc); kpcontext_ = new Context(*spincontext_); sdcontext_ = new Context(*kpcontext_); } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::set_nrowmax(int n) { if ( n > ctxt_.size() ) { if ( ctxt_.onpe0() ) cout << " Wavefunction::set_nrowmax: nrowmax > ctxt_.size()" << endl; return; } deallocate(); delete spincontext_; delete kpcontext_; delete sdcontext_; nrowmax_ = n; create_contexts(); compute_nst(); allocate(); resize(cell_,refcell_,ecut_); init(); } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::add_kpoint(D3vector kpoint, double weight) { for ( int i = 0; i < kpoint_.size(); i++ ) { if ( length(kpoint - kpoint_[i]) < 1.e-6 ) { if ( ctxt_.onpe0() ) cout << " Wavefunction::add_kpoint: kpoint already defined" << endl; return; } } kpoint_.push_back(kpoint); weight_.push_back(weight); const int nkp = kpoint_.size(); sd_.resize(nspin_); for ( int ispin = 0; ispin < nspin_; ispin++ ) { sd_[ispin].push_back(new SlaterDet(*sdcontext_,kpoint_[nkp-1])); sd_[ispin][nkp-1]->resize(cell_,refcell_,ecut_,nst_[ispin]); } if ( nspin_ == 1 ) { sd_[0][nkp-1]->update_occ(nel_,nspin_); } else if ( nspin_ == 2 ) { const int nocc_up = (nel_+1)/2+deltaspin_; const int nocc_dn = nel_/2 - deltaspin_; sd_[0][nkp-1]->update_occ(nocc_up,nspin_); sd_[1][nkp-1]->update_occ(nocc_dn,nspin_); } else { // incorrect value of nspin_ assert(false); } } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::del_kpoint(D3vector kpoint) { bool found = false; vector::iterator pk = kpoint_.begin(); vector::iterator pw = weight_.begin(); while ( !found && pk != kpoint_.end() ) { if ( length(kpoint - *pk) < 1.e-6 ) { found = true; } else { pk++; pw++; } } if ( !found ) { if ( ctxt_.onpe0() ) cout << " Wavefunction::del_kpoint: no such kpoint" << endl; return; } deallocate(); kpoint_.erase(pk); weight_.erase(pw); allocate(); resize(cell_,refcell_,ecut_); init(); update_occ(0.0); } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::randomize(double amplitude) { for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { sd_[ispin][ikp]->randomize(amplitude); } } } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::update_occ(double temp) { // update occupation numbers using eigenvalues in SlaterDet if ( temp == 0.0 ) { // zero temperature if ( nspin_ == 1 ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { sd_[0][ikp]->update_occ(nel_,nspin_); } } else if ( nspin_ == 2 ) { const int nocc_up = (nel_+1)/2+deltaspin_; const int nocc_dn = nel_/2 - deltaspin_; for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { sd_[0][ikp]->update_occ(nocc_up,nspin_); sd_[1][ikp]->update_occ(nocc_dn,nspin_); } } else { // incorrect value of nspin_ assert(false); } } else { // finite temperature const double eVolt = 0.036749023; // 1 eV in Hartree const int maxiter = 500; // loop to find value of mu double mu = 0.0; double dmu = 2.0 * eVolt; const double totalcharge = (double) nel_; enum direction { up, down }; direction dir = up; double rhosum = 0.0; for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { sd_[ispin][ikp]->update_occ(nspin_,mu,temp); rhosum += weight_[ikp] * sd_[ispin][ikp]->total_charge(); } } int niter = 0; while ( niter < maxiter && fabs(rhosum - nel_) > 1.e-10 ) { niter++; if ( rhosum < totalcharge ) { if ( dir == down ) dmu /= 2.0; mu += dmu; dir = up; } else { if ( dir == up ) dmu /= 2.0; mu -= dmu; dir = down; } rhosum = 0.0; for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { sd_[ispin][ikp]->update_occ(nspin_,mu,temp); rhosum += weight_[ikp] * sd_[ispin][ikp]->total_charge(); } } } if ( niter == maxiter ) { cout << "Wavefunction::update_occ: mu did not converge in " << maxiter << " iterations" << endl; ctxt_.abort(1); } if ( ctxt_.onpe0() ) { cout << " Wavefunction::update_occ: sum = " << rhosum << endl; cout << " Wavefunction::update_occ: mu = " << setprecision(4) << mu / eVolt << " eV" << endl; cout.setf(ios::right,ios::adjustfield); cout.setf(ios::fixed,ios::floatfield); cout << " Wavefunction::update_occ: occupation numbers" << endl; for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { cout << " k = " << kpoint_[ikp] << endl; for ( int n = 0; n < sd_[ispin][ikp]->nst(); n++ ) { cout << setw(7) << setprecision(4) << sd_[ispin][ikp]->occ(n); if ( ( n%10 ) == 9 ) cout << endl; } if ( sd_[ispin][ikp]->nst() % 10 != 0 ) cout << endl; } } } } } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::gram(void) { for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { sd_[ispin][ikp]->gram(); } } } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::riccati(Wavefunction& wf) { assert(wf.context() == ctxt_); for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { sd_[ispin][ikp]->riccati(*wf.sd_[ispin][ikp]); } } } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::align(Wavefunction& wf) { assert(wf.context() == ctxt_); for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { sd_[ispin][ikp]->align(*wf.sd_[ispin][ikp]); } } } //////////////////////////////////////////////////////////////////////////////// complex Wavefunction::dot(const Wavefunction& wf) const { assert(wf.context() == ctxt_); complex sum = 0.0; for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { sum += sd_[ispin][ikp]->dot(*wf.sd_[ispin][ikp]); } } return sum; } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::diag(Wavefunction& dwf, bool eigvec) { // subspace diagonalization of <*this | dwf> // if eigvec==true, eigenvectors are computed and stored in *this, dwf is // overwritten for ( int ispin = 0; ispin < nspin(); ispin++ ) { if ( nst_[ispin] > 0 ) { for ( int ikp = 0; ikp < nkp(); ikp++ ) { // compute eigenvalues if ( sd(ispin,ikp)->basis().real() ) { // proxy real matrices c, cp DoubleMatrix c(sd(ispin,ikp)->c()); DoubleMatrix cp(dwf.sd(ispin,ikp)->c()); DoubleMatrix h(c.context(),c.n(),c.n(),c.nb(),c.nb()); // factor 2.0 in next line: G and -G h.gemm('t','n',2.0,c,cp,0.0); // rank-1 update correction h.ger(-1.0,c,0,cp,0); // cout << " Hamiltonian at k = " << sd(ispin,ikp)->kpoint() // << endl; // cout << h; #if 1 valarray w(h.m()); if ( eigvec ) { DoubleMatrix z(c.context(),c.n(),c.n(),c.nb(),c.nb()); h.syev('l',w,z); //h.syevx('l',w,z,1.e-6); cp = c; c.gemm('n','n',1.0,cp,z,0.0); } else { h.syev('l',w); } #else vector w(h.m()); DoubleMatrix z(c.context(),c.n(),c.n(),c.nb(),c.nb()); const int maxsweep = 30; int nsweep = jacobi(maxsweep,1.e-6,h,z,w); if ( eigvec ) { cp = c; c.gemm('n','n',1.0,cp,z,0.0); } #endif // set eigenvalues in SlaterDet sd(ispin,ikp)->set_eig(w); } else { ComplexMatrix& c(sd(ispin,ikp)->c()); ComplexMatrix& cp(dwf.sd(ispin,ikp)->c()); ComplexMatrix h(c.context(),c.n(),c.n(),c.nb(),c.nb()); h.gemm('c','n',1.0,c,cp,0.0); // cout << " Hamiltonian at k = " // << sd(ispin,ikp)->kpoint() << endl; // cout << h; valarray w(h.m()); if ( eigvec ) { #if DEBUG ComplexMatrix hcopy(h); #endif ComplexMatrix z(c.context(),c.n(),c.n(),c.nb(),c.nb()); h.heev('l',w,z); cp = c; c.gemm('n','n',1.0,cp,z,0.0); #if DEBUG // check that z contains eigenvectors of h // diagonal matrix with eigenvalues on diagonal ComplexMatrix d(c.context(),c.n(),c.n(),c.nb(),c.nb()); // the following test works only on one task assert(ctxt_.size()==1); for ( int i = 0; i < d.m(); i++ ) d[i+d.n()*i] = w[i]; ComplexMatrix dz(c.context(),c.n(),c.n(),c.nb(),c.nb()); dz.gemm('n','c',1.0,d,z,0.0); ComplexMatrix zdz(c.context(),c.n(),c.n(),c.nb(),c.nb()); zdz.gemm('n','n',1.0,z,dz,0.0); // zdz should be equal to hcopy zdz -= hcopy; cout << " heev: norm of error: " << zdz.nrm2() << endl; #endif } else { h.heev('l',w); } // set eigenvalues in SlaterDet sd(ispin,ikp)->set_eig(w); } } } } } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::print(ostream& os, string encoding, string tag) const { if ( ctxt_.onpe0() ) { os << "<" << tag << " ecut=\"" << ecut_ << "\"" << " nspin=\"" << nspin_ << "\"" << " nel=\"" << nel_ << "\"" << " nempty=\"" << nempty_ << "\">" << endl; os << setprecision(10); os << "" << endl; os << "basis().np(0) << "\"" << " ny=\"" << sd_[0][0]->basis().np(1) << "\"" << " nz=\"" << sd_[0][0]->basis().np(2) << "\"/>" << endl; } for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) sd_[ispin][ikp]->print(os,encoding,weight_[ikp],ispin,nspin_); } if ( ctxt_.onpe0() ) os << "" << endl; } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::write(SharedFilePtr& sfp, string encoding, string tag) const { sfp.sync(); if ( ctxt_.onpe0() ) { ostringstream os; os << "<" << tag << " ecut=\"" << ecut_ << "\"" << " nspin=\"" << nspin_ << "\"" << " nel=\"" << nel_ << "\"" << " nempty=\"" << nempty_ << "\">" << endl; os << setprecision(10); os << "" << endl; if ( refcell_.volume() != 0.0 ) { os << "" << endl; } os << "basis().np(0) << "\"" << " ny=\"" << sd_[0][0]->basis().np(1) << "\"" << " nz=\"" << sd_[0][0]->basis().np(2) << "\"/>" << endl; string str(os.str()); int len = str.size(); #if USE_MPI MPI_Status status; int err = MPI_File_write_at(sfp.file(),sfp.mpi_offset(),(void*)str.c_str(), len,MPI_CHAR,&status); if ( err != 0 ) cout << " Wavefunction::write: error in MPI_File_write" << endl; sfp.advance(len); #else sfp.file().write(str.c_str(),len); #endif } for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { sd_[ispin][ikp]->write(sfp,encoding,weight_[ikp],ispin,nspin_); } } sfp.sync(); if ( ctxt_.onpe0() ) { ostringstream os; os << "" << endl; string str(os.str()); int len = str.size(); #if USE_MPI MPI_Status status; int err = MPI_File_write_at(sfp.file(),sfp.mpi_offset(),(void*)str.c_str(), len,MPI_CHAR,&status); if ( err != 0 ) cout << " Wavefunction::write: error in MPI_File_write" << endl; sfp.advance(len); #else sfp.file().write(str.c_str(),len); #endif } } //////////////////////////////////////////////////////////////////////////////// void Wavefunction::info(ostream& os, string tag) const { if ( ctxt_.onpe0() ) { os << "<" << tag << " ecut=\"" << ecut_ << "\"" << " nspin=\"" << nspin_ << "\"" << " nel=\"" << nel_ << "\"" << " nempty=\"" << nempty_ << "\">" << endl; os.setf(ios::fixed,ios::floatfield); os << "" << endl; os << " reciprocal lattice vectors" << endl << setprecision(6) << " " << cell_.b(0) << endl << " " << cell_.b(1) << endl << " " << cell_.b(2) << endl; os << "" << endl; os << "basis().np(0) << "\"" << " ny=\"" << sd_[0][0]->basis().np(1) << "\"" << " nz=\"" << sd_[0][0]->basis().np(2) << "\"/>" << endl; } for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { if ( ctxt_.onpe0() ) cout << " kpoint: " << kpoint_[ikp] << " weight: " << weight_[ikp] << endl; sd_[ispin][ikp]->info(os); } } if ( ctxt_.onpe0() ) os << "" << endl; } //////////////////////////////////////////////////////////////////////////////// ostream& operator<<(ostream& os, const Wavefunction& wf) { wf.print(os,"text","wavefunction"); return os; } //////////////////////////////////////////////////////////////////////////////// Wavefunction& Wavefunction::operator=(const Wavefunction& wf) { if ( this == &wf ) return *this; assert(ctxt_ == wf.ctxt_); assert(nel_ == wf.nel_); assert(nempty_== wf.nempty_); assert(nspin_ == wf.nspin_); assert(nrowmax_ == wf.nrowmax_); assert(deltaspin_ == wf.deltaspin_); assert(refcell_ == wf.refcell_); assert(ecut_ == wf.ecut_); cell_ = wf.cell_; weight_ = wf.weight_; kpoint_ = wf.kpoint_; assert(*spincontext_ == *wf.spincontext_); assert(*kpcontext_ == *wf.kpcontext_); assert(*sdcontext_ == *wf.sdcontext_); for ( int ispin = 0; ispin < nspin_; ispin++ ) { for ( int ikp = 0; ikp < kpoint_.size(); ikp++ ) { *sd_[ispin][ikp] = *wf.sd_[ispin][ikp]; } } return *this; }