////////////////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////////////////
//
// BOSampleStepper.C
//
////////////////////////////////////////////////////////////////////////////////
#include "BOSampleStepper.h"
#include "EnergyFunctional.h"
#include "SlaterDet.h"
#include "Basis.h"
#include "WavefunctionStepper.h"
#include "SDWavefunctionStepper.h"
#include "PSDWavefunctionStepper.h"
#include "PSDAWavefunctionStepper.h"
#include "JDWavefunctionStepper.h"
#include "UserInterface.h"
#include "Preconditioner.h"
#include "SDIonicStepper.h"
#include "SDAIonicStepper.h"
#include "CGIonicStepper.h"
#include "MDIonicStepper.h"
#include "BMDIonicStepper.h"
#include "SDCellStepper.h"
#include "CGCellStepper.h"
#include "AndersonMixer.h"
#include "MLWFTransform.h"
#include "D3tensor.h"
#include
#include
using namespace std;
////////////////////////////////////////////////////////////////////////////////
BOSampleStepper::BOSampleStepper(Sample& s, int nitscf, int nite) :
SampleStepper(s), cd_(s.wf), ef_(s,cd_),
dwf(s.wf), wfv(s.wfv), nitscf_(nitscf), nite_(nite),
initial_atomic_density(false) {}
////////////////////////////////////////////////////////////////////////////////
BOSampleStepper::~BOSampleStepper()
{
for ( TimerMap::iterator i = tmap.begin(); i != tmap.end(); i++ )
{
double time = (*i).second.real();
double tmin = time;
double tmax = time;
s_.ctxt_.dmin(1,1,&tmin,1);
s_.ctxt_.dmax(1,1,&tmax,1);
if ( s_.ctxt_.myproc()==0 )
{
cout << ""
<< endl;
}
}
}
////////////////////////////////////////////////////////////////////////////////
void BOSampleStepper::initialize_density(void)
{
// initialize cd_ with a sum of atomic densities
double atom_radius[] =
{
0.00, 0.80, 0.59, 3.16, 2.12, // null H He Li Be
1.64, 1.27, 1.06, 0.91, 0.79, // B C N O F
0.72, 3.59, 2.74, 2.23, 2.10, // Ne Na Mg Al Si
1.85, 1.66, 1.49, 1.34, 4.59, // P S Cl Ar K
3.67, 3.48, 3.33, 3.23, 3.14, // Ca Sc Ti V Cr
3.04, 2.95, 2.87, 2.82, 2.74, // Mn Fe Co Ni Cu
2.68, 2.57, 2.36, 2.15, 1.95, // Zn Ga Ge As Se
1.78, 1.66, 5.01, 4.14, 4.01, // Br Kr Rb Sr Y
3.89, 3.74, 3.59, 3.46, 3.36, // Zr Nb Mo Tc Ru
3.27, 3.19, 3.12, 3.04, 2.95, // Rh Pd Ag Cd In
2.74, 2.51, 2.32, 2.17, 2.04, // Sn Sb Te I Xe
5.63, 4.78, 0.00, 0.00, 4.67, // Cs Ba La Ce Pr
3.89, 3.87, 4.50, 4.37, 4.40, // Nd Pm Sm Eu Gd
4.25, 4.31, 0.00, 4.27, 4.20, // Tb Dy Ho Er Tm
4.20, 4.10, 3.93, 3.78, 3.65, // Yb Lu Hf Ta W
3.55, 3.50, 3.40, 3.34, 3.29, // Re Os Ir Pt Au
3.23, 2.95, 2.91, 2.70, 2.55, // Hg Tl Pb Bi Po
4.00, 2.27, 4.00, 4.00, 4.00, // At Rn Fr Ra Ac
4.00, 4.00, 4.00, 4.00, 4.00, // Th Pa U Np Pu
4.00, 4.00, 4.00, 4.00, 4.00, // Am Cm Bk Cf Es
4.00, 4.00, 4.00, 4.00 // Fm Md No Lr
};
const AtomSet& atoms = s_.atoms;
const Basis* const vbasis = cd_.vbasis();
const int ngloc = vbasis->localsize();
vector > > rhops;
const int nsp = atoms.nsp();
rhops.resize(nsp);
vector > rhopst(ngloc);
const double * const g2 = vbasis->g2_ptr();
for ( int is = 0; is < nsp; is++ )
{
rhops[is].resize(ngloc);
Species *s = atoms.species_list[is];
const int zval = s->zval();
const int atomic_number = s->atomic_number();
assert(atomic_number < sizeof(atom_radius)/sizeof(double));
// scaling factor 2.0 in next line is empirically adjusted
double rc = 2.0 * atom_radius[atomic_number];
for ( int ig = 0; ig < ngloc; ig++ )
{
double arg = 0.25 * rc * rc * g2[ig];
rhops[is][ig] = zval * exp( -arg );
}
}
vector > tau0;
tau0.resize(nsp);
for ( int is = 0; is < nsp; is++ )
tau0.resize(3*atoms.na(is));
atoms.get_positions(tau0);
StructureFactor sf;
sf.init(tau0,*vbasis);
sf.update(tau0,*vbasis);
memset( (void*)&rhopst[0], 0, 2*ngloc*sizeof(double) );
for ( int is = 0; is < nsp; is++ )
{
complex *s = &sf.sfac[is][0];
for ( int ig = 0; ig < ngloc; ig++ )
{
const complex sg = s[ig];
rhopst[ig] += sg * rhops[is][ig];
}
}
// Initialize charge equally for both spins
cd_.rhog[0] = rhopst;
if ( cd_.rhog.size() == 2 )
{
assert(cd_.rhog[0].size()==cd_.rhog[1].size());
for ( int i = 0; i < cd_.rhog[0].size(); i++ )
{
cd_.rhog[0][i] = 0.5 * rhopst[i];
cd_.rhog[1][i] = 0.5 * rhopst[i];
}
}
initial_atomic_density = true;
}
////////////////////////////////////////////////////////////////////////////////
void BOSampleStepper::step(int niter)
{
const Context& ctxt = s_.ctxt_;
const bool onpe0 = ctxt.onpe0();
const bool anderson_charge_mixing = ( s_.ctrl.charge_mix_ndim > 0 );
// determine whether eigenvectors must be computed
// eigenvectors are computed if explicitly requested with wf_diag==T
// or if the SlaterDet has fractionally occupied states
const bool fractional_occ = (s_.wf.nel() != 2 * s_.wf.nst());
const bool compute_eigvec = fractional_occ || s_.ctrl.wf_diag == "T";
const bool compute_mlwf = s_.ctrl.wf_diag == "MLWF";
const bool compute_mlwfc = s_.ctrl.wf_diag == "MLWFC";
enum ortho_type { GRAM, LOWDIN, ORTHO_ALIGN, RICCATI };
AtomSet& atoms = s_.atoms;
Wavefunction& wf = s_.wf;
const int nspin = wf.nspin();
const double dt = s_.ctrl.dt;
const string wf_dyn = s_.ctrl.wf_dyn;
const string atoms_dyn = s_.ctrl.atoms_dyn;
const string cell_dyn = s_.ctrl.cell_dyn;
const bool extrapolate_wf = ( atoms_dyn == "MD" );
const bool ntc_extrapolation =
s_.ctrl.debug.find("NTC_EXTRAPOLATION") != string::npos;
const bool asp_extrapolation =
s_.ctrl.debug.find("ASP_EXTRAPOLATION") != string::npos;
Wavefunction* wfmm;
if ( extrapolate_wf && ( ntc_extrapolation || asp_extrapolation ) )
wfmm = new Wavefunction(wf);
// Next lines: special value of niter = 0: GS calculation only
const bool atoms_move = ( niter > 0 && atoms_dyn != "LOCKED" );
const bool compute_stress = ( s_.ctrl.stress == "ON" );
const bool cell_moves = ( niter > 0 && compute_stress &&
cell_dyn != "LOCKED" );
// GS-only calculation:
const bool gs_only = !atoms_move && !cell_moves;
Timer tm_iter;
Preconditioner prec(wf,ef_,s_.ctrl.ecutprec);
WavefunctionStepper* wf_stepper = 0;
if ( wf_dyn == "SD" )
{
const double emass = s_.ctrl.emass;
double dt2bye = (emass == 0.0) ? 0.5 / wf.ecut() : dt*dt/emass;
// divide dt2bye by facs coefficient if stress == ON
const double facs = 2.0;
if ( s_.ctrl.stress == "ON" )
{
dt2bye /= facs;
}
wf_stepper = new SDWavefunctionStepper(wf,dt2bye,tmap);
}
else if ( wf_dyn == "PSD" )
wf_stepper = new PSDWavefunctionStepper(wf,prec,tmap);
else if ( wf_dyn == "PSDA" )
wf_stepper = new PSDAWavefunctionStepper(wf,prec,tmap);
else if ( wf_dyn == "JD" )
wf_stepper = new JDWavefunctionStepper(wf,prec,ef_,tmap);
// wf_stepper == 0 indicates that wf_dyn == LOCKED
IonicStepper* ionic_stepper = 0;
if ( atoms_dyn == "SD" )
ionic_stepper = new SDIonicStepper(s_);
else if ( atoms_dyn == "SDA" )
ionic_stepper = new SDAIonicStepper(s_);
else if ( atoms_dyn == "CG" )
ionic_stepper = new CGIonicStepper(s_);
else if ( atoms_dyn == "MD" )
ionic_stepper = new MDIonicStepper(s_);
else if ( atoms_dyn == "BMD" )
ionic_stepper = new BMDIonicStepper(s_);
if ( ionic_stepper )
ionic_stepper->setup_constraints();
CellStepper* cell_stepper = 0;
if ( cell_dyn == "SD" )
cell_stepper = new SDCellStepper(s_);
else if ( cell_dyn == "CG" )
cell_stepper = new CGCellStepper(s_);
// Allocate wavefunction velocity if not available
if ( atoms_move && extrapolate_wf )
{
if ( s_.wfv == 0 )
{
s_.wfv = new Wavefunction(wf);
s_.wfv->clear();
}
}
vector mlwft(nspin);
if ( compute_mlwf || compute_mlwfc )
{
// MLWF can be computed at the gamma point only
// There must be a single k-point, and it must be gamma
if ( wf.nkp() > 1 || ( wf.nkp()==1 && wf.kpoint(0) != D3vector(0,0,0) ) )
{
if ( onpe0 )
{
cout << " BOSampleStepper::step: MLWF can be computed at k=0 only"
<< endl;
cout << " BOSampleStepper::step: cannot run" << endl;
}
return;
}
for ( int ispin = 0; ispin < nspin; ispin++ )
mlwft[ispin] = new MLWFTransform(*wf.sd(ispin,0));
}
// Charge mixing variables: include both spins in the same vector
if ( nspin > 1 ) assert(cd_.rhog[0].size()==cd_.rhog[1].size());
const int ng = cd_.rhog[0].size();
vector > rhog_in(nspin*ng), drhog(nspin*ng),
rhobar(nspin*ng), drhobar(nspin*ng);
const int anderson_ndim = s_.ctrl.charge_mix_ndim;
vector wkerker(ng), wls(ng);
// Anderson charge mixer: include both spins in the same vector
// Factor of 2: complex coeffs stored as double
MPI_Comm vcomm = cd_.vcomm();
AndersonMixer mixer(2*nspin*ng,anderson_ndim,&vcomm);
// compute Kerker preconditioning
// real space Kerker cutoff in a.u.
const double rc_Kerker = s_.ctrl.charge_mix_rcut;
const double *const g2 = cd_.vbasis()->g2_ptr();
// define q1 cutoff for row weighting of LS charge mixing
// Use rc1 = 3 a.u. default cutoff
double rc1 = 3.0;
// check if override from the debug variable
// use: set debug RC1
if ( s_.ctrl.debug.find("RC1") != string::npos )
{
istringstream is(s_.ctrl.debug);
string s;
is >> s >> rc1;
if ( onpe0 )
cout << " override rc1 value: rc1 = " << rc1 << endl;
assert(rc1 >= 0.0);
}
if ( rc1 != 0.0 )
{
const double q1 = 2.0 * M_PI / rc1;
for ( int i = 0; i < wls.size(); i++ )
{
if ( g2[i] != 0.0 )
wls[i] = sqrt(g2[i] / ( g2[i] + q1*q1 ));
else
wls[i] = 1.0;
}
}
else
{
for ( int i = 0; i < wls.size(); i++ )
wls[i] = 1.0;
}
if ( rc_Kerker > 0.0 )
{
const double q0_kerker = 2 * M_PI / rc_Kerker;
const double q0_kerker2 = q0_kerker * q0_kerker;
for ( int i = 0; i < wkerker.size(); i++ )
wkerker[i] = g2[i] / ( g2[i] + q0_kerker2 );
}
else
{
for ( int i = 0; i < wkerker.size(); i++ )
wkerker[i] = 1.0;
}
// Next line: special case of niter=0: compute GS only
for ( int iter = 0; iter < max(niter,1); iter++ )
{
// ionic iteration
tm_iter.start();
if ( onpe0 )
cout << "\n";
// compute energy and ionic forces using existing wavefunction
if ( !gs_only )
{
tmap["charge"].start();
cd_.update_density();
tmap["charge"].stop();
tmap["update_vhxc"].start();
ef_.update_vhxc(compute_stress);
tmap["update_vhxc"].stop();
const bool compute_forces = true;
tmap["energy"].start();
double energy =
ef_.energy(false,dwf,compute_forces,fion,compute_stress,sigma_eks);
tmap["energy"].stop();
double enthalpy = ef_.enthalpy();
if ( onpe0 )
{
cout << ef_;
if ( ef_.el_enth() )
cout << *ef_.el_enth();
}
if ( iter > 0 && ionic_stepper )
{
ionic_stepper->compute_v(energy,fion);
}
// at this point, positions r0, velocities v0 and forces fion are
// consistent
double ekin_ion = 0.0, temp_ion = 0.0;
if ( ionic_stepper )
{
ekin_ion = ionic_stepper->ekin();
temp_ion = ionic_stepper->temp();
}
// print positions, velocities and forces at time t0
if ( onpe0 )
{
cout << "" << endl;
cout << atoms.cell();
for ( int is = 0; is < atoms.atom_list.size(); is++ )
{
int i = 0;
for ( int ia = 0; ia < atoms.atom_list[is].size(); ia++ )
{
Atom* pa = atoms.atom_list[is][ia];
cout << " name() << "\""
<< " species=\"" << pa->species()
<< "\">\n"
<< " " << pa->position() << " \n"
<< " " << pa->velocity() << " \n"
<< " "
<< fion[is][i] << " "
<< fion[is][i+1] << " "
<< fion[is][i+2]
<< " \n";
cout << " " << endl;
i += 3;
}
}
cout << "" << endl;
cout << setprecision(6);
cout << " " << atoms.cell().a_norm(0)
<< " " << endl;
cout << " " << atoms.cell().a_norm(1)
<< " " << endl;
cout << " " << atoms.cell().a_norm(2)
<< " " << endl;
cout << setprecision(3) << " "
<< atoms.cell().alpha() << " " << endl;
cout << setprecision(3) << " "
<< atoms.cell().beta() << " " << endl;
cout << setprecision(3) << " "
<< atoms.cell().gamma() << " " << endl;
cout << setprecision(3) << " "
<< atoms.cell().volume() << " " << endl;
// include the kinetic energy of the stepper
// e.g. to include thermostat contributions
double ekin_stepper;
if ( ionic_stepper != 0 )
ekin_stepper = ionic_stepper->ekin_stepper();
cout << setprecision(8);
cout << " " << enthalpy+ekin_ion+ekin_stepper
<< " \n";
cout << " " << ekin_ion << " \n";
cout << " " << temp_ion << " \n";
}
if ( atoms_move )
{
if ( s_.constraints.size() > 0 )
{
s_.constraints.compute_forces(ionic_stepper->r0(), fion);
if ( onpe0 )
{
s_.constraints.list_constraints(cout);
}
}
// move atoms to new position: r0 <- r0 + v0*dt + dt2/m * fion
ionic_stepper->compute_r(energy,fion);
ef_.atoms_moved();
}
if ( compute_stress )
{
compute_sigma();
print_stress();
if ( cell_moves )
{
cell_stepper->compute_new_cell(enthalpy,sigma,fion);
// Update cell
cell_stepper->update_cell();
ef_.cell_moved();
ef_.atoms_moved(); // modifications of the cell also move ions
}
}
} // if !gs_only
// Recalculate ground state wavefunctions
#ifdef DEBUG
for ( int ispin = 0; ispin < nspin; ispin++ )
{
for ( int ikp = 0; ikp < s_.wf.nkp(); ikp++ )
{
double sum = s_.wf.sd(ispin,ikp)->empty_row_error() ;
if ( onpe0 )
{
cout.setf(ios::scientific,ios::floatfield);
cout << " sd empty row error: ispin="
<< ispin << " ikp=" << ikp << " " << sum << endl;
}
sum = s_.wf.sd(ispin,ikp)->g0_imag_error() ;
if ( onpe0 )
{
cout.setf(ios::scientific,ios::floatfield);
cout << " sd g0 imag error: ispin="
<< ispin << " ikp=" << ikp << " " << sum << endl;
}
}
}
#endif
// wavefunction extrapolation
if ( atoms_move && extrapolate_wf )
{
for ( int ispin = 0; ispin < nspin; ispin++ )
{
for ( int ikp = 0; ikp < s_.wf.nkp(); ikp++ )
{
if ( ntc_extrapolation )
{
double* c = (double*) s_.wf.sd(ispin,ikp)->c().cvalptr();
double* cv = (double*) s_.wfv->sd(ispin,ikp)->c().cvalptr();
double* cmm = (double*) wfmm->sd(ispin,ikp)->c().cvalptr();
const int mloc = s_.wf.sd(ispin,ikp)->c().mloc();
const int nloc = s_.wf.sd(ispin,ikp)->c().nloc();
const int len = 2*mloc*nloc;
if ( iter == 0 )
{
// copy c on cv
for ( int i = 0; i < len; i++ )
{
const double x = c[i];
const double v = cv[i];
// extrapolation using velocity in cv
c[i] = x + dt * v;
cv[i] = x;
}
tmap["gram"].start();
s_.wf.sd(ispin,ikp)->gram();
tmap["gram"].stop();
}
else if ( iter == 1 )
{
s_.wfv->align(s_.wf);
for ( int i = 0; i < len; i++ )
{
const double x = c[i];
const double xm = cv[i];
c[i] = 2.0 * x - xm;
cv[i] = x;
cmm[i] = xm;
}
tmap["gram"].start();
s_.wf.sd(ispin,ikp)->gram();
tmap["gram"].stop();
}
else
{
// align wf with wfmm before extrapolation
// s_.wf.align(*wfmm);
wfmm->align(s_.wf);
// extrapolate
for ( int i = 0; i < len; i++ )
{
const double x = c[i]; // current wf (scf converged) at t
const double xm = cv[i]; // extrapolated wf at t
const double xmm = cmm[i]; // extrapolated wf at t-dt
c[i] = 2.0 * x - xmm;
// save extrapolated value at t in cmm
cmm[i] = xm;
}
// orthogonalize the extrapolated value
tmap["gram"].start();
s_.wf.sd(ispin,ikp)->gram();
tmap["gram"].stop();
//tmap["lowdin"].start();
//s_.wf.sd(ispin,ikp)->lowdin();
//tmap["lowdin"].stop();
// c[i] now contains the extrapolated value
// save a copy in cv[i]
for ( int i = 0; i < len; i++ )
{
cv[i] = c[i];
}
}
// c[i] is now ready for electronic iterations
}
else if ( asp_extrapolation )
{
double* c = (double*) s_.wf.sd(ispin,ikp)->c().cvalptr();
double* cv = (double*) s_.wfv->sd(ispin,ikp)->c().cvalptr();
double* cmm = (double*) wfmm->sd(ispin,ikp)->c().cvalptr();
const int mloc = s_.wf.sd(ispin,ikp)->c().mloc();
const int nloc = s_.wf.sd(ispin,ikp)->c().nloc();
const int len = 2*mloc*nloc;
if ( iter == 0 )
{
for ( int i = 0; i < len; i++ )
{
const double x = c[i];
const double v = cv[i];
// extrapolation using velocity in cv
c[i] = x + dt * v;
cv[i] = x;
}
tmap["gram"].start();
s_.wf.sd(ispin,ikp)->gram();
tmap["gram"].stop();
}
else if ( iter == 1 )
{
//s_.wfv->align(s_.wf);
for ( int i = 0; i < len; i++ )
{
const double x = c[i];
const double xm = cv[i];
c[i] = 2.0 * x - xm;
cv[i] = x;
cmm[i] = xm;
}
tmap["gram"].start();
s_.wf.sd(ispin,ikp)->gram();
tmap["gram"].stop();
}
else
{
// align wf with wfmm before extrapolation
// s_.wf.align(*wfmm);
// wfmm->align(s_.wf);
// extrapolate
for ( int i = 0; i < len; i++ )
{
const double x = c[i]; // current wf (scf converged) at t
const double xm = cv[i]; // extrapolated wf at t
const double xmm = cmm[i]; // extrapolated wf at t-dt
const double asp_a1 = 0.5;
c[i] = 2.0 * x - xm +
asp_a1 * ( x - 2.0 * xm + xmm );
//c[i] = 2.5 * x - 2.0 * xm + 0.5 * xmm;
cmm[i] = xm;
cv[i] = x;
}
// orthogonalize the extrapolated value
tmap["gram"].start();
s_.wf.sd(ispin,ikp)->gram();
tmap["gram"].stop();
//tmap["lowdin"].start();
//s_.wf.sd(ispin,ikp)->lowdin();
//tmap["lowdin"].stop();
// c[i] now contains the extrapolated value
}
// c[i] is now ready for electronic iterations
}
else // normal extrapolation
{
double* c = (double*) s_.wf.sd(ispin,ikp)->c().cvalptr();
double* cv = (double*) s_.wfv->sd(ispin,ikp)->c().cvalptr();
const int mloc = s_.wf.sd(ispin,ikp)->c().mloc();
const int nloc = s_.wf.sd(ispin,ikp)->c().nloc();
const int len = 2*mloc*nloc;
if ( iter == 0 )
{
// copy c to cv
for ( int i = 0; i < len; i++ )
{
const double x = c[i];
const double v = cv[i];
c[i] = x + dt * v;
cv[i] = x;
}
//tmap["lowdin"].start();
//s_.wf.sd(ispin,ikp)->lowdin();
//tmap["lowdin"].stop();
tmap["gram"].start();
s_.wf.sd(ispin,ikp)->gram();
tmap["gram"].stop();
}
else
{
tmap["align"].start();
s_.wfv->align(s_.wf);
tmap["align"].stop();
// linear extrapolation
for ( int i = 0; i < len; i++ )
{
const double x = c[i];
const double xm = cv[i];
c[i] = 2.0 * x - xm;
cv[i] = x;
}
//tmap["ortho_align"].start();
//s_.wf.sd(ispin,ikp)->ortho_align(*s_.wfv->sd(ispin,ikp));
//tmap["ortho_align"].stop();
//tmap["riccati"].start();
//s_.wf.sd(ispin,ikp)->riccati(*s_.wfv->sd(ispin,ikp));
//tmap["riccati"].stop();
tmap["lowdin"].start();
s_.wf.sd(ispin,ikp)->lowdin();
tmap["lowdin"].stop();
}
}
}
}
} // atoms_move && extrapolate_wf
// do nitscf self-consistent iterations, each with nite electronic steps
if ( wf_stepper != 0 )
{
wf_stepper->preprocess();
if ( anderson_charge_mixing )
mixer.restart();
double ehart, ehart_m;
bool scf_converged = false;
int itscf = 0;
double etotal = 0.0, etotal_m = 0.0, etotal_mm = 0.0;
while ( !scf_converged && itscf < nitscf_ )
{
if ( nite_ > 0 && onpe0 )
cout << " BOSampleStepper: start scf iteration" << endl;
// compute new density in cd_.rhog
tmap["charge"].start();
if ( itscf==0 && initial_atomic_density )
cd_.update_rhor();
else
cd_.update_density();
tmap["charge"].stop();
// charge mixing
if ( nite_ > 0 )
{
if ( itscf == 0 )
{
// at first scf iteration, nothing to mix
// memorize rhog in rhog_in for next iteration
for ( int ispin = 0; ispin < nspin; ispin++ )
for ( int i = 0; i < ng; i++ )
rhog_in[i+ng*ispin] = cd_.rhog[ispin][i];
}
else
{
// itscf > 0
// compute unscreened correction drhog
for ( int ispin = 0; ispin < nspin; ispin++ )
{
for ( int i = 0; i < ng; i++ )
{
drhog[i+ng*ispin] = (cd_.rhog[ispin][i]-rhog_in[i+ng*ispin]);
}
}
const double alpha = s_.ctrl.charge_mix_coeff;
// Anderson acceleration
if ( anderson_charge_mixing )
{
// row weighting of LS calculation
for ( int ispin = 0; ispin < nspin; ispin++ )
{
for ( int i = 0; i < ng; i++ )
drhog[i+ng*ispin] /= wls[i];
}
const Context * const kpctxt = s_.wf.kpcontext();
if ( kpctxt->mycol() == 0 )
{
// use AndersonMixer on first column only and bcast results
mixer.update((double*)&rhog_in[0], (double*)&drhog[0],
(double*)&rhobar[0], (double*)&drhobar[0]);
const int n = 2*nspin*ng;
kpctxt->dbcast_send('r',n,1,(double*)&rhobar[0],n);
kpctxt->dbcast_send('r',n,1,(double*)&drhobar[0],n);
}
else
{
const int n = 2*nspin*ng;
kpctxt->dbcast_recv('r',n,1,(double*)&rhobar[0],n,-1,0);
kpctxt->dbcast_recv('r',n,1,(double*)&drhobar[0],n,-1,0);
}
for ( int ispin = 0; ispin < nspin; ispin++ )
{
for ( int i = 0; i < ng; i++ )
drhobar[i+ng*ispin] *= wls[i];
for ( int i = 0; i < ng; i++ )
rhog_in[i+ng*ispin] = rhobar[i+ng*ispin] +
alpha * drhobar[i+ng*ispin] * wkerker[i];
}
}
else
{
for ( int ispin = 0; ispin < nspin; ispin++ )
{
for ( int i = 0; i < ng; i++ )
rhog_in[i+ng*ispin] += alpha * drhog[i+ng*ispin] * wkerker[i];
}
}
for ( int ispin = 0; ispin < nspin; ispin++ )
{
for ( int i = 0; i < ng; i++ )
cd_.rhog[ispin][i] = rhog_in[i+ng*ispin];
}
cd_.update_rhor();
}
} // if nite_ > 0
tmap["update_vhxc"].start();
ef_.update_vhxc(compute_stress);
tmap["update_vhxc"].stop();
// reset stepper only if multiple non-selfconsistent steps
if ( nite_ > 0 ) wf_stepper->preprocess();
// non-self-consistent loop
// repeat until the change in eigenvalue_sum is smaller than a
// fraction of the change in Hartree energy in the last scf iteration
bool nonscf_converged = false;
if ( itscf > 0 )
ehart_m = ehart;
ehart = ef_.ehart();
double delta_ehart = 0.0;
if ( itscf > 0 )
delta_ehart = fabs(ehart - ehart_m);
// if ( onpe0 && nite_ > 0 )
// cout << " delta_ehart = " << delta_ehart << endl;
int ite = 0;
double energy, etotal_int;
double eigenvalue_sum, eigenvalue_sum_m = 0.0;
// if nite == 0: do 1 iteration, no screening in charge mixing
// if nite > 0: do nite iterations, use screening in charge mixing
//
while ( !nonscf_converged && ite < max(nite_,1) )
{
tmap["energy"].start();
energy = ef_.energy(true,dwf,false,fion,false,sigma_eks);
tmap["energy"].stop();
// compute the sum of eigenvalues (with fixed weight)
// to measure convergence of the subspace update
// compute trace of the Hamiltonian matrix Y^T H Y
// scalar product of Y and (HY): tr Y^T (HY) = sum_ij Y_ij (HY)_ij
// Note: since the hamiltonian is hermitian and dwf=H*wf
// the dot product in the following line is real
if ( ite > 0 )
eigenvalue_sum_m = eigenvalue_sum;
eigenvalue_sum = real(s_.wf.dot(dwf));
if ( onpe0 )
cout << " "
<< eigenvalue_sum << " " << endl;
tmap["wf_update"].start();
wf_stepper->update(dwf);
tmap["wf_update"].stop();
// compare delta_eig_sum only after first iteration
if ( ite > 0 )
{
double delta_eig_sum = fabs(eigenvalue_sum - eigenvalue_sum_m);
nonscf_converged |= (delta_eig_sum < 0.01 * delta_ehart);
#ifdef DEBUG
if ( onpe0 )
{
cout << " BOSampleStepper::step delta_eig_sum: "
<< delta_eig_sum << endl;
cout << " BOSampleStepper::step: delta_ehart: "
<< delta_ehart << endl;
}
#endif
}
ite++;
}
// subspace diagonalization
if ( compute_eigvec || s_.ctrl.wf_diag == "EIGVAL" )
{
tmap["energy"].start();
ef_.energy(true,dwf,false,fion,false,sigma_eks);
tmap["energy"].stop();
tmap["wfdiag"].start();
s_.wf.diag(dwf,compute_eigvec);
tmap["wfdiag"].stop();
if ( onpe0 )
{
cout << "" << endl;
// print eigenvalues
for ( int ispin = 0; ispin < wf.nspin(); ispin++ )
{
for ( int ikp = 0; ikp < wf.nkp(); ikp++ )
{
const int nst = wf.sd(ispin,ikp)->nst();
const double eVolt = 2.0 * 13.6058;
cout << " kpoint()
<< "\" weight=\""
<< setprecision(8)
<< wf.weight(ikp)
<< "\" n=\"" << nst << "\">" << endl;
for ( int i = 0; i < nst; i++ )
{
cout << setw(12) << setprecision(5)
<< wf.sd(ispin,ikp)->eig(i)*eVolt;
if ( i%5 == 4 ) cout << endl;
}
if ( nst%5 != 0 ) cout << endl;
cout << " " << endl;
}
}
cout << "" << endl;
}
}
// update occupation numbers if fractionally occupied states
// compute weighted sum of eigenvalues
// default value if no fractional occupation
double w_eigenvalue_sum = 2.0 * eigenvalue_sum;
if ( fractional_occ )
{
wf.update_occ(s_.ctrl.fermi_temp);
#if 0
if ( onpe0 )
{
cout << " Wavefunction entropy: " << wf_entropy << endl;
cout << " Entropy contribution to free energy: "
<< - wf_entropy * s_.ctrl.fermi_temp * boltz << endl;
}
#endif
w_eigenvalue_sum = 0.0;
for ( int ispin = 0; ispin < wf.nspin(); ispin++ )
{
for ( int ikp = 0; ikp < wf.nkp(); ikp++ )
{
const int nst = wf.sd(ispin,ikp)->nst();
const double wkp = wf.weight(ikp);
for ( int n = 0; n < nst; n++ )
{
const double occ = wf.sd(ispin,ikp)->occ(n);
w_eigenvalue_sum += wkp * occ * wf.sd(ispin,ikp)->eig(n);
}
}
}
}
// Harris-Foulkes estimate of the total energy
etotal_int = w_eigenvalue_sum - ef_.ehart_e() + ef_.ehart_p() +
ef_.esr() - ef_.eself() + ef_.dxc() + ef_.ets();
#ifdef DEBUG
if ( onpe0 )
{
cout << setprecision(8);
cout << "w_eigenvalue_sum = " << setw(15) << w_eigenvalue_sum << endl;
cout << "ef.dxc() = " << setw(15) << ef_.dxc() << endl;
cout << "ef.ehart() = " << setw(15) << ef_.ehart() << endl;
cout << "ef.ehart_e() = " << setw(15) << ef_.ehart_e() << endl;
cout << "ef.ehart_ep() = " << setw(15) << ef_.ehart_ep() << endl;
cout << "ef.esr() = " << setw(15) << ef_.esr() << endl;
}
#endif
if ( onpe0 )
{
cout.setf(ios::fixed,ios::floatfield);
cout.setf(ios::right,ios::adjustfield);
cout << " " << setprecision(8) << setw(15)
<< etotal_int << " \n";
}
etotal_mm = etotal_m;
etotal_m = etotal;
etotal = etotal_int;
if ( nite_ > 0 && onpe0 )
cout << " BOSampleStepper: end scf iteration" << endl;
// delta_etotal = interval containing etotal, etotal_m and etotal_mm
double delta_etotal = fabs(etotal - etotal_m);
delta_etotal = max(delta_etotal,fabs(etotal - etotal_mm));
delta_etotal = max(delta_etotal,fabs(etotal_m - etotal_mm));
scf_converged |= (delta_etotal < s_.ctrl.scf_tol);
itscf++;
} // while scf
if ( compute_mlwf || compute_mlwfc )
{
tmap["mlwf"].start();
for ( int ispin = 0; ispin < nspin; ispin++ )
{
mlwft[ispin]->update();
mlwft[ispin]->compute_transform();
}
if ( compute_mlwf )
{
for ( int ispin = 0; ispin < nspin; ispin++ )
{
SlaterDet& sd = *(wf.sd(ispin,0));
mlwft[ispin]->apply_transform(sd);
}
}
if ( onpe0 )
{
D3vector edipole_sum;
cout << "" << endl;
for ( int ispin = 0; ispin < nspin; ispin++ )
{
SlaterDet& sd = *(wf.sd(ispin,0));
cout << " " << endl;
double total_spread[6];
for ( int j = 0; j < 6; j++ )
total_spread[j] = 0.0;
for ( int i = 0; i < sd.nst(); i++ )
{
D3vector ctr = mlwft[ispin]->center(i);
double spi[6];
for (int j=0; j<3; j++)
{
spi[j] = mlwft[ispin]->spread2(i,j);
total_spread[j] += spi[j];
}
cout.setf(ios::fixed, ios::floatfield);
cout.setf(ios::right, ios::adjustfield);
cout << " "
<< endl;
}
cout << " ";
for ( int j = 0; j < 3; j++ )
cout << setw(10) << total_spread[j];
cout << " " << endl;
D3vector edipole = mlwft[ispin]->dipole();
cout << " " << edipole
<< " " << endl;
edipole_sum += edipole;
cout << " " << endl;
}
D3vector idipole = atoms.dipole();
cout << " " << idipole
<< " " << endl;
cout << " " << idipole + edipole_sum
<< " " << endl;
cout << " " << length(idipole + edipole_sum)
<< " " << endl;
cout << "" << endl;
}
tmap["mlwf"].stop();
}
// If GS calculation only, print energy and atomset at end of iterations
if ( gs_only )
{
tmap["charge"].start();
cd_.update_density();
tmap["charge"].stop();
tmap["update_vhxc"].start();
ef_.update_vhxc(compute_stress);
tmap["update_vhxc"].stop();
const bool compute_forces = true;
tmap["energy"].start();
ef_.energy(false,dwf,compute_forces,fion,compute_stress,sigma_eks);
tmap["energy"].stop();
if ( onpe0 )
{
cout << ef_;
if ( ef_.el_enth() )
cout << *ef_.el_enth();
cout << "" << endl;
cout << atoms.cell();
for ( int is = 0; is < atoms.atom_list.size(); is++ )
{
int i = 0;
for ( int ia = 0; ia < atoms.atom_list[is].size(); ia++ )
{
Atom* pa = atoms.atom_list[is][ia];
cout << " name() << "\""
<< " species=\"" << pa->species()
<< "\">\n"
<< " " << pa->position() << " \n"
<< " " << pa->velocity() << " \n"
<< " "
<< fion[is][i] << " "
<< fion[is][i+1] << " "
<< fion[is][i+2]
<< " \n";
cout << " " << endl;
i += 3;
}
}
cout << "" << endl;
cout << setprecision(6);
cout << " " << atoms.cell().a_norm(0)
<< " " << endl;
cout << " " << atoms.cell().a_norm(1)
<< " " << endl;
cout << " " << atoms.cell().a_norm(2)
<< " " << endl;
cout << setprecision(3) << " "
<< atoms.cell().alpha() << " " << endl;
cout << setprecision(3) << " "
<< atoms.cell().beta() << " " << endl;
cout << setprecision(3) << " "
<< atoms.cell().gamma() << " " << endl;
cout << setprecision(3) << " "
<< atoms.cell().volume() << " " << endl;
if ( compute_stress )
{
compute_sigma();
print_stress();
}
}
}
wf_stepper->postprocess();
}
else
{
// wf_stepper == 0, wf_dyn == LOCKED
// evaluate and print energy
tmap["charge"].start();
cd_.update_density();
tmap["charge"].stop();
tmap["update_vhxc"].start();
ef_.update_vhxc(compute_stress);
tmap["update_vhxc"].stop();
tmap["energy"].start();
ef_.energy(true,dwf,false,fion,false,sigma_eks);
tmap["energy"].stop();
if ( onpe0 )
{
cout << ef_;
if ( ef_.el_enth() )
cout << *ef_.el_enth();
}
}
if ( atoms_move )
s_.constraints.update_constraints(dt);
// execute commands in iter_cmd if defined
if ( !s_.ctrl.iter_cmd.empty() )
{
if ( iter % s_.ctrl.iter_cmd_period == 0 )
{
// command must be terminated with \n
istringstream cmdstream(s_.ctrl.iter_cmd + "\n");
s_.ui->processCmds(cmdstream,"[iter_cmd]",true);
}
}
// print iteration time
double time = tm_iter.real();
double tmin = time;
double tmax = time;
s_.ctxt_.dmin(1,1,&tmin,1);
s_.ctxt_.dmax(1,1,&tmax,1);
if ( onpe0 )
{
cout << " "
<< endl;
cout << "" << endl;
}
} // for iter
if ( atoms_move )
{
// compute ionic forces at last position to update velocities
// consistently with last position
tmap["charge"].start();
cd_.update_density();
tmap["charge"].stop();
tmap["update_vhxc"].start();
ef_.update_vhxc(compute_stress);
tmap["update_vhxc"].stop();
const bool compute_forces = true;
tmap["energy"].start();
double energy =
ef_.energy(false,dwf,compute_forces,fion,compute_stress,sigma_eks);
tmap["energy"].stop();
ionic_stepper->compute_v(energy,fion);
// positions r0 and velocities v0 are consistent
}
if ( atoms_move && extrapolate_wf )
{
// compute wavefunction velocity after last iteration
// s_.wfv contains the previous wavefunction
tmap["align"].start();
s_.wfv->align(s_.wf);
tmap["align"].stop();
for ( int ispin = 0; ispin < s_.wf.nspin(); ispin++ )
{
for ( int ikp = 0; ikp < s_.wf.nkp(); ikp++ )
{
double* c = (double*) s_.wf.sd(ispin,ikp)->c().cvalptr();
double* cm = (double*) s_.wfv->sd(ispin,ikp)->c().cvalptr();
const int mloc = s_.wf.sd(ispin,ikp)->c().mloc();
const int nloc = s_.wf.sd(ispin,ikp)->c().nloc();
const int len = 2*mloc*nloc;
const double dt_inv = 1.0 / dt;
if ( ntc_extrapolation )
{
double* cmm = (double*) wfmm->sd(ispin,ikp)->c().cvalptr();
for ( int i = 0; i < len; i++ )
{
const double x = c[i];
const double xmm = cmm[i];
cm[i] = dt_inv * ( x - xmm );
}
tmap["gram"].start();
s_.wf.sd(ispin,ikp)->gram();
tmap["gram"].stop();
}
else // normal extrapolation or asp_extrapolation
{
for ( int i = 0; i < len; i++ )
{
const double x = c[i];
const double xm = cm[i];
cm[i] = dt_inv * ( x - xm );
}
tmap["gram"].start();
s_.wf.sd(ispin,ikp)->gram();
tmap["gram"].stop();
}
}
}
// compute ionic forces at last position to update velocities
// consistently with last position
tmap["charge"].start();
cd_.update_density();
tmap["charge"].stop();
tmap["update_vhxc"].start();
ef_.update_vhxc(compute_stress);
tmap["update_vhxc"].stop();
const bool compute_forces = true;
tmap["energy"].start();
double energy =
ef_.energy(false,dwf,compute_forces,fion,compute_stress,sigma_eks);
tmap["energy"].stop();
ionic_stepper->compute_v(energy,fion);
// positions r0 and velocities v0 are consistent
}
else
{
// delete wavefunction velocities
if ( s_.wfv != 0 )
delete s_.wfv;
s_.wfv = 0;
}
for ( int ispin = 0; ispin < nspin; ispin++ )
{
delete mlwft[ispin];
}
// delete steppers
delete wf_stepper;
delete ionic_stepper;
delete cell_stepper;
if ( ntc_extrapolation || asp_extrapolation ) delete wfmm;
initial_atomic_density = false;
}