//////////////////////////////////////////////////////////////////////////////// // // 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 . // //////////////////////////////////////////////////////////////////////////////// // // MDWavefunctionStepper.C // //////////////////////////////////////////////////////////////////////////////// // $Id: MDWavefunctionStepper.C,v 1.12 2008-09-08 15:56:18 fgygi Exp $ #include "MDWavefunctionStepper.h" #include "Wavefunction.h" #include "SlaterDet.h" #include "Sample.h" #include using namespace std; //////////////////////////////////////////////////////////////////////////////// MDWavefunctionStepper::MDWavefunctionStepper(Wavefunction& wf, Wavefunction *wfv, double dt, double dt2bye, TimerMap& tmap) : wfv_(wfv), dt_(dt), dt2bye_(dt2bye), WavefunctionStepper(wf,tmap) { assert(wfv!=0); } //////////////////////////////////////////////////////////////////////////////// void MDWavefunctionStepper::update(Wavefunction& dwf) { // Verlet update of wf using force dwf and wfm stored in *wfv for ( int ispin = 0; ispin < wf_.nspin(); ispin++ ) { for ( int ikp = 0; ikp < wf_.nkp(); ikp++ ) { tmap_["md_update_wf"].start(); // Verlet update of wf // cp = c + (c - cm) - dt2/m * hpsi // This is implemented (for each coefficient) as: // cp = 2*c - cm - dt2bye * hpsi // cm = c // c = cp SlaterDet* sd = wf_.sd(ispin,ikp); double* cptr = (double*) sd->c().valptr(); double* cptrm = (double*) wfv_->sd(ispin,ikp)->c().valptr(); const double* dcptr = (const double*) dwf.sd(ispin,ikp)->c().cvalptr(); const int mloc = sd->c().mloc(); const int nloc = sd->c().nloc(); for ( int n = 0; n < nloc; n++ ) { // note: double mloc length for complex indices double* c = &cptr[2*mloc*n]; double* cm = &cptrm[2*mloc*n]; const double* dc = &dcptr[2*mloc*n]; for ( int i = 0; i < mloc; i++ ) { const double ctmp = c[2*i]; const double ctmp1 = c[2*i+1]; const double cmtmp = cm[2*i]; const double cmtmp1 = cm[2*i+1]; const double dctmp = dc[2*i]; const double dctmp1 = dc[2*i+1]; const double cptmp = 2.0*ctmp - cmtmp - dt2bye_ * dctmp; const double cptmp1= 2.0*ctmp1 - cmtmp1 - dt2bye_ * dctmp1; c[2*i] = cptmp; c[2*i+1] = cptmp1; cm[2*i] = ctmp; cm[2*i+1] = ctmp1; } } tmap_["md_update_wf"].stop(); tmap_["riccati"].start(); wf_.sd(ispin,ikp)->riccati(*(wfv_->sd(ispin,ikp))); tmap_["riccati"].stop(); } } ekin_em_ = ekin_ep_; ekin_ep_ = ekin_eh(); } //////////////////////////////////////////////////////////////////////////////// void MDWavefunctionStepper::compute_wfm(Wavefunction& dwf) { // Compute wfm for first MD step using wf, wfv and dwf (= Hpsi) // Replace then wfv by wfm // Compute cm using c and wavefunction velocity // cm = c - dt * v - 0.5 * dt2/m * hpsi // replace wfv by wfm const double half_dt2bye = 0.5 * dt2bye_; for ( int ispin = 0; ispin < wf_.nspin(); ispin++ ) { for ( int ikp = 0; ikp < wf_.nkp(); ikp++ ) { SlaterDet* sd = wf_.sd(ispin,ikp); double* cptr = (double*) sd->c().valptr(); double* cptrv = (double*) wfv_->sd(ispin,ikp)->c().valptr(); const double* dcptr = (const double*) dwf.sd(ispin,ikp)->c().cvalptr(); const int mloc = sd->c().mloc(); const int nloc = sd->c().nloc(); for ( int n = 0; n < nloc; n++ ) { // note: double mloc length for complex indices double* c = &cptr[2*mloc*n]; double* cv = &cptrv[2*mloc*n]; const double* dc = &dcptr[2*mloc*n]; for ( int i = 0; i < mloc; i++ ) { const double ctmp = c[2*i]; const double ctmp1 = c[2*i+1]; const double cvtmp = cv[2*i]; const double cvtmp1 = cv[2*i+1]; const double dctmp = dc[2*i]; const double dctmp1 = dc[2*i+1]; cv[2*i] = ctmp - dt_ * cvtmp - half_dt2bye * dctmp; cv[2*i+1] = ctmp1 - dt_ * cvtmp1 - half_dt2bye * dctmp1; } } tmap_["riccati"].start(); wfv_->sd(ispin,ikp)->riccati(*wf_.sd(ispin,ikp)); tmap_["riccati"].stop(); } } ekin_em_ = 0.0; ekin_ep_ = ekin_eh(); // Note: ekin_ep is a first-order approximation of ekin_e using wf and wfm // only. This makes ekin_e consistent with the following update() call // Note: *wfv_ now contains wf(t-dt) } //////////////////////////////////////////////////////////////////////////////// void MDWavefunctionStepper::compute_wfv(Wavefunction& dwf) { // Compute wfv = (wf - wfm)/dt - 0.5*dtbye*dwf assert(dt_!=0.0); for ( int ispin = 0; ispin < wf_.nspin(); ispin++ ) { for ( int ikp = 0; ikp < wf_.nkp(); ikp++ ) { // compute final velocity wfv // v = ( c - cm ) / dt - 0.5 * dt/m * hpsi // Note: At this point, *wfv_ contains wf(t-dt) // hpsi must be orthogonal to the subspace spanned by c // compute descent direction H psi - psi (psi^T H psi) SlaterDet* sd = wf_.sd(ispin,ikp); if ( sd->basis().real() ) { // proxy real matrices c, cp DoubleMatrix c(sd->c()); DoubleMatrix cp(dwf.sd(ispin,ikp)->c()); DoubleMatrix a(c.context(),c.n(),c.n(),c.nb(),c.nb()); // factor 2.0 in next line: G and -G a.gemm('t','n',2.0,c,cp,0.0); // rank-1 update correction a.ger(-1.0,c,0,cp,0); // cp = cp - c * a cp.gemm('n','n',-1.0,c,a,1.0); } else { ComplexMatrix& c = sd->c(); ComplexMatrix& cp = dwf.sd(ispin,ikp)->c(); ComplexMatrix a(c.context(),c.n(),c.n(),c.nb(),c.nb()); a.gemm('c','n',1.0,c,cp,0.0); // cp = cp - c * a cp.gemm('n','n',-1.0,c,a,1.0); } const double dt_inv = 1.0/dt_; const double half_dtbye = 0.5 * dt2bye_ / dt_; double* cptr = (double*) sd->c().valptr(); double* cptrv = (double*) wfv_->sd(ispin,ikp)->c().valptr(); const double* dcptr = (const double*) dwf.sd(ispin,ikp)->c().cvalptr(); const int mloc = sd->c().mloc(); const int nloc = sd->c().nloc(); for ( int n = 0; n < nloc; n++ ) { // note: double mloc length for complex indices double* c = &cptr[2*mloc*n]; double* cv = &cptrv[2*mloc*n]; const double* dc = &dcptr[2*mloc*n]; for ( int i = 0; i < mloc; i++ ) { const double ctmp = c[2*i]; const double ctmp1 = c[2*i+1]; const double cmtmp = cv[2*i]; const double cmtmp1 = cv[2*i+1]; const double dctmp = dc[2*i]; const double dctmp1 = dc[2*i+1]; cv[2*i] = ( ctmp - cmtmp ) * dt_inv - half_dtbye * dctmp; cv[2*i+1] = ( ctmp1 - cmtmp1 ) * dt_inv - half_dtbye * dctmp1; } } // Note: *wfv_ now contains the wavefunction velocity } } } //////////////////////////////////////////////////////////////////////////////// double MDWavefunctionStepper::ekin_eh(void) { // compute ekin at time t - 0.5*dt using wf and wfm tmap_["ekin_e"].start(); double ekin_e = 0.0; // assume that wfv contains wfm for ( int ispin = 0; ispin < wf_.nspin(); ispin++ ) { for ( int ikp = 0; ikp < wf_.nkp(); ikp++ ) { const double weight = wf_.weight(ikp); SlaterDet* sd = wf_.sd(ispin,ikp); double* cptr = (double*) sd->c().valptr(); double* cptrm = (double*) wfv_->sd(ispin,ikp)->c().valptr(); const int mloc = sd->c().mloc(); const int nloc = sd->c().nloc(); // compute electronic kinetic energy at time t-1/2 const bool onrow0 = ( wf_.context().myrow() == 0 ); const vector& occ = sd->occ(); for ( int n = 0; n < nloc; n++ ) { const int nglobal = sd->c().j(0,n); const double occn = occ[nglobal]; // note: double mloc length for complex indices double* c = &cptr[2*mloc*n]; double* cm = &cptrm[2*mloc*n]; double tmpsum = 0.0; if ( sd->basis().real() && onrow0 ) { // correct for double counting of G=0 element // i=0 coefficient is real, count only real part const double ctmp = c[0]; const double cmtmp = cm[0]; tmpsum -= 0.5 * (ctmp - cmtmp)*(ctmp - cmtmp); } for ( int i = 0; i < mloc; i++ ) { const double ctmp = c[2*i]; const double ctmp1 = c[2*i+1]; const double cmtmp = cm[2*i]; const double cmtmp1 = cm[2*i+1]; tmpsum += (ctmp -cmtmp )*(ctmp -cmtmp ) + (ctmp1-cmtmp1)*(ctmp1-cmtmp1); } if ( sd->basis().real() ) // Note: 2 in next line: from (G,-G) ekin_e += weight * ( 2.0 * occn / dt2bye_ ) * tmpsum; else ekin_e += weight * ( occn / dt2bye_ ) * tmpsum; } } } wf_.context().dsum(1,1,&ekin_e,1); tmap_["ekin_e"].stop(); return ekin_e; }