//DIIS convergence acceleration according to Pulay: Chem. Phys. Lett. 73, 393 (1980); J. Comp. Chem. 3,556 (1982) #ifndef _DIIS_H_ #define _DIIS_H_ #include "vec.h" #include "smat.h" #include "mat.h" #include "sparsemat.h" #include "nonclass.h" #include "la_traits.h" #include "auxstorage.h" //Pulay memorial book remarks - for numerical stabilization small addition to diagonal #define DIISEPS 1e-9 // Typically, T is some solution vector in form of NRVec, NRMat, or NRSMat over double or complex fields // actually it can be anything what has operator=(const T&), clear(), dot() , axpy(), norm() and copyonwrite(), and LA_traits::normtype and elementtype // and get() and put() if external storage is requested template class DIIS { int dim; int aktdim; bool incore; int cyclicshift; //circular buffer of last dim vectors typedef typename LA_traits::elementtype Te; typedef typename LA_traits::elementtype Ue; NRSMat bmat; AuxStorage *st; AuxStorage *errst; T *stor; U *errstor; public: DIIS() {dim=0; st=NULL; stor=NULL; errst=NULL; errstor=NULL;}; //for array of diis DIIS(const int n, const bool core=1); void setup(const int n, const bool core=1); ~DIIS(); typename LA_traits::normtype extrapolate(T &vec, const U &errvec, bool verbose=false); //vec is input/output; returns square residual norm }; template DIIS::DIIS(const int n, const bool core) : dim(n), incore(core), bmat(n+1,n+1) { st=incore?NULL: new AuxStorage; errst=incore?NULL: new AuxStorage; stor= incore? new T[dim] : NULL; errstor= incore? new U[dim] : NULL; bmat= (Ue)0; for(int i=1; i<=n; ++i) bmat(0,i) = (Ue)-1; aktdim=cyclicshift=0; } template void DIIS::setup(const int n, const bool core) { dim=n; incore=core; bmat.resize(n+1); st=incore?NULL: new AuxStorage; errst=incore?NULL: new AuxStorage; stor= incore? new T[dim] : NULL; errstor= incore? new U[dim] : NULL; bmat= (Ue)0; for(int i=1; i<=n; ++i) bmat(0,i) = (Ue)-1; aktdim=cyclicshift=0; } template DIIS::~DIIS() { if(st) delete st; if(errst) delete errst; if(stor) delete[] stor; if(errstor) delete[] errstor; } template typename LA_traits::normtype DIIS::extrapolate(T &vec, const U &errvec, bool verbose) { if(!dim) laerror("attempt to extrapolate from uninitialized DIIS"); //if dim exceeded, shift if(aktdim==dim) { cyclicshift=(cyclicshift+1)%dim; for(int i=1; iput(vec,(aktdim-1+cyclicshift)%dim); errst->put(errvec,(aktdim-1+cyclicshift)%dim); } if(aktdim==1) return (typename LA_traits::normtype)1; //calculate overlaps of the new error with old ones typename LA_traits::normtype norm=errvec.norm(); bmat(aktdim,aktdim)= norm*norm + DIISEPS; if(incore) for(int i=1; iget(tmp,(i-1+cyclicshift)%dim); bmat(i,aktdim)= errvec.dot(tmp); } } //prepare rhs-solution vector NRVec rhs(dim+1); rhs= (Ue)0; rhs[0]= (Ue)-1; //solve for coefficients //@@@@@@ implement checking for bad condition number and eliminating old vectors //@@@ explicit solution - cf. remarks in Pulay memorial book { NRSMat amat=bmat; linear_solve(amat,rhs,NULL,aktdim+1); } if(verbose) cout <<"DIIS coefficients: "<get(tmp,(i-1+cyclicshift)%dim); vec.axpy(rhs[i],tmp); } } return norm; } #endif