////////////////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////////////////
//
// EnergyFunctional.C
//
////////////////////////////////////////////////////////////////////////////////
#include "EnergyFunctional.h"
#include "Sample.h"
#include "Species.h"
#include "Wavefunction.h"
#include "ChargeDensity.h"
#include "SlaterDet.h"
#include "Basis.h"
#include "FourierTransform.h"
#include "StructureFactor.h"
#include "XCOperator.h"
#include "NonLocalPotential.h"
#include "ConfinementPotential.h"
#include "Timer.h"
#include "blas.h"
#include
#include
#include // fill()
using namespace std;
////////////////////////////////////////////////////////////////////////////////
EnergyFunctional::EnergyFunctional( Sample& s, const ChargeDensity& cd)
: s_(s), cd_(cd)
{
const AtomSet& atoms = s_.atoms;
const Wavefunction& wf = s_.wf;
sigma_ekin.resize(6);
sigma_econf.resize(6);
sigma_eps.resize(6);
sigma_ehart.resize(6);
sigma_exc.resize(6);
sigma_enl.resize(6);
sigma_esr.resize(6);
sigma.resize(6);
vbasis_ = cd_.vbasis();
// define FT's on vbasis contexts
vft = cd_.vft();
int np0v = vft->np0();
int np1v = vft->np1();
int np2v = vft->np2();
v_r.resize(wf.nspin());
for ( int ispin = 0; ispin < wf.nspin(); ispin++ )
{
v_r[ispin].resize(vft->np012loc());
}
tmp_r.resize(vft->np012loc());
if ( s_.ctxt_.onpe0() )
{
cout << " EnergyFunctional: np0v,np1v,np2v: " << np0v << " "
<< np1v << " " << np2v << endl;
cout << " EnergyFunctional: vft->np012(): "
<< vft->np012() << endl;
}
const int ngloc = vbasis_->localsize();
nsp_ = atoms.nsp();
vps.resize(nsp_);
dvps.resize(nsp_);
rhops.resize(nsp_);
zv_.resize(nsp_);
rcps_.resize(nsp_);
na_.resize(nsp_);
namax_ = 0;
for ( int is = 0; is < nsp_; is++ )
{
vps[is].resize(ngloc);
dvps[is].resize(ngloc);
rhops[is].resize(ngloc);
if ( atoms.na(is) > namax_ ) namax_ = atoms.na(is);
}
xco = new XCOperator(s_, cd_);
nlp.resize(wf.nspin());
for ( int ispin = 0; ispin < wf.nspin(); ispin++ )
{
nlp[ispin].resize(wf.nkp());
for ( int ikp = 0; ikp < wf.nkp(); ikp++ )
{
nlp[ispin][ikp] = new NonLocalPotential(s_.atoms, *wf.sd(ispin,ikp));
}
}
vion_local_g.resize(ngloc);
dvion_local_g.resize(ngloc);
vlocal_g.resize(ngloc);
vtemp.resize(ngloc);
rhoelg.resize(ngloc);
rhogt.resize(ngloc);
rhopst.resize(ngloc);
tau0.resize(nsp_);
fion_esr.resize(nsp_);
fext.resize(nsp_);
ftmp.resize(3*namax_);
eself_ = 0.0;
for ( int is = 0; is < nsp_; is++ )
{
Species *s = atoms.species_list[is];
const int na = atoms.na(is);
tau0[is].resize(3*na);
fion_esr[is].resize(3*na);
fext[is].resize(3*na);
eself_ += na * s->eself();
na_[is] = na;
zv_[is] = s->zval();
rcps_[is] = s->rcps();
}
// FT for interpolation of wavefunctions on the fine grid
ft.resize(wf.nkp());
for ( int ikp = 0; ikp < wf.nkp(); ikp++ )
{
ft[ikp] = cd_.ft(ikp);
}
// Confinement potentials
cfp.resize(wf.nkp());
for ( int ikp = 0; ikp < wf.nkp(); ikp++ )
{
cfp[ikp] = 0;
const double facs = 2.0;
const double sigmas = 0.5;
cfp[ikp] =
new ConfinementPotential(s_.ctrl.ecuts,facs,sigmas,
wf.sd(0,ikp)->basis());
}
sf.init(tau0,*vbasis_);
cell_moved();
atoms_moved();
}
////////////////////////////////////////////////////////////////////////////////
EnergyFunctional::~EnergyFunctional(void)
{
delete xco;
for ( int ikp = 0; ikp < s_.wf.nkp(); ikp++ )
{
delete cfp[ikp];
for ( int ispin = 0; ispin < nlp.size(); ispin++ )
delete nlp[ispin][ikp];
}
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 EnergyFunctional::update_vhxc(bool compute_stress)
{
// called when the charge density has changed
// update Hartree and xc potentials using the charge density cd_
// compute Hartree and xc energies
const Wavefunction& wf = s_.wf;
const UnitCell& cell = wf.cell();
const double omega = cell.volume();
const double omega_inv = 1.0 / omega;
const double *const g2i = vbasis_->g2i_ptr();
const double fpi = 4.0 * M_PI;
const int ngloc = vbasis_->localsize();
double tsum[2];
// compute total electronic density: rhoelg = rho_up + rho_dn
if ( wf.nspin() == 1 )
{
for ( int ig = 0; ig < ngloc; ig++ )
{
rhoelg[ig] = omega_inv * cd_.rhog[0][ig];
}
}
else
{
for ( int ig = 0; ig < ngloc; ig++ )
{
rhoelg[ig] = omega_inv * ( cd_.rhog[0][ig] + cd_.rhog[1][ig] );
}
}
// update XC operator
// compute xc energy, update self-energy operator and potential
tmap["exc"].start();
for ( int ispin = 0; ispin < wf.nspin(); ispin++ )
fill(v_r[ispin].begin(),v_r[ispin].end(),0.0);
xco->update(v_r, compute_stress);
exc_ = xco->exc();
if ( compute_stress )
xco->compute_stress(sigma_exc);
tmap["exc"].stop();
// compute local potential energy:
// integral of el. charge times ionic local pot.
int len=2*ngloc,inc1=1;
tsum[0] = 2.0 * ddot(&len,(double*)&rhoelg[0],&inc1,
(double*)&vion_local_g[0],&inc1);
// remove double counting for G=0
if ( vbasis_->context().myrow() == 0 )
{
tsum[0] -= real(conj(rhoelg[0])*vion_local_g[0]);
}
tsum[0] *= omega; // tsum[0] contains eps
// Hartree energy
ehart_ = 0.0;
double ehsum = 0.0;
for ( int ig = 0; ig < ngloc; ig++ )
{
const complex tmp = rhoelg[ig] + rhopst[ig];
ehsum += norm(tmp) * g2i[ig];
rhogt[ig] = tmp;
}
// factor 1/2 from definition of Ehart cancels with half sum over G
// Note: rhogt[ig] includes a factor 1/Omega
// Factor omega in next line yields prefactor 4 pi / omega in
tsum[1] = omega * fpi * ehsum;
// tsum[1] contains ehart
vbasis_->context().dsum(2,1,&tsum[0],2);
eps_ = tsum[0];
ehart_ = tsum[1];
// compute vlocal_g = vion_local_g + vhart_g
// where vhart_g = 4 * pi * (rhoelg + rhopst) * g2i
for ( int ig = 0; ig < ngloc; ig++ )
{
vlocal_g[ig] = vion_local_g[ig] + fpi * rhogt[ig] * g2i[ig];
}
// FT to tmpr_r
vft->backward(&vlocal_g[0],&tmp_r[0]);
// add local potential in tmp_r to v_r[ispin][i]
// v_r contains the xc potential
const int size = tmp_r.size();
if ( wf.nspin() == 1 )
{
for ( int i = 0; i < size; i++ )
{
v_r[0][i] += real(tmp_r[i]);
}
}
else
{
for ( int i = 0; i < size; i++ )
{
const double vloc = real(tmp_r[i]);
v_r[0][i] += vloc;
v_r[1][i] += vloc;
}
}
}
////////////////////////////////////////////////////////////////////////////////
double EnergyFunctional::energy(bool compute_hpsi, Wavefunction& dwf,
bool compute_forces, vector >& fion,
bool compute_stress, valarray& sigma)
{
const bool debug_stress = compute_stress &&
s_.ctrl.debug.find("STRESS") != string::npos;
const double fpi = 4.0 * M_PI;
const Wavefunction& wf = s_.wf;
const UnitCell& cell(wf.cell());
const double omega = cell.volume();
const double omega_inv = 1.0 / omega;
const int ngloc = vbasis_->localsize();
const double *const g2i = vbasis_->g2i_ptr();
const bool use_confinement = s_.ctrl.ecuts > 0.0;
for ( int is = 0; is < fion.size(); is++ )
for ( int i = 0; i < fion[is].size(); i++ )
fion[is][i] = 0.0;
if ( compute_hpsi )
{
for ( int ispin = 0; ispin < wf.nspin(); ispin++ )
for ( int ikp = 0; ikp < wf.nkp(); ikp++ )
dwf.sd(ispin,ikp)->c().clear();
}
// kinetic energy
tmap["ekin"].start();
// compute ekin, confinement energy, stress from ekin and econf
// ekin = sum_G |c_G|^2 G^2
// econf = sum_G |c_G|^2 fstress[G]
// stress_ekin_ij = (1/Omega) sum_G |c_G|^2 * 2 * G_i * G_j
// stress_econf_ij = (1/Omega) sum_G |c_G|^2 * dfstress[G] * G_i * G_j
ekin_ = 0.0;
econf_ = 0.0;
sigma_ekin = 0.0;
sigma_econf = 0.0;
valarray sum(0.0,14), tsum(0.0,14);
for ( int ispin = 0; ispin < wf.nspin(); ispin++ )
{
for ( int ikp = 0; ikp < wf.nkp(); ikp++ )
{
const double weight = wf.weight(ikp);
const SlaterDet& sd = *(wf.sd(ispin,ikp));
const Basis& wfbasis = sd.basis();
const D3vector kp = wfbasis.kpoint();
// factor fac in next lines: 2.0 for G and -G (if basis is real) and
// 0.5 from 1/(2m)
const double fac = wfbasis.real() ? 1.0 : 0.5;
const ComplexMatrix& c = sd.c();
const Context& sdctxt = sd.context();
// compute psi2sum(G) = fac * sum_G occ(n) psi2(n,G)
const int ngwloc = wfbasis.localsize();
valarray psi2sum(ngwloc);
const complex* p = c.cvalptr();
const int mloc = c.mloc();
const int nloc = c.nloc();
// nn = global n index
const int nnbase = sdctxt.mycol() * c.nb();
const double * const occ = sd.occ_ptr(nnbase);
for ( int ig = 0; ig < ngwloc; ig++ )
{
double tmpsum = 0.0;
for ( int n = 0; n < nloc; n++ )
{
const double psi2 = norm(p[ig+n*mloc]);
tmpsum += fac * occ[n] * psi2;
}
psi2sum[ig] = tmpsum;
}
// accumulate contributions to ekin,econf,sigma_ekin,sigma_econf in tsum
const double *const kpg2 = wfbasis.kpg2_ptr();
const double *const kpg_x = wfbasis.kpgx_ptr(0);
const double *const kpg_y = wfbasis.kpgx_ptr(1);
const double *const kpg_z = wfbasis.kpgx_ptr(2);
tsum = 0.0;
for ( int ig = 0; ig < ngwloc; ig++ )
{
const double psi2s = psi2sum[ig];
// tsum[0]: ekin partial sum
tsum[0] += psi2s * kpg2[ig];
if ( compute_stress )
{
const double tkpgx = kpg_x[ig];
const double tkpgy = kpg_y[ig];
const double tkpgz = kpg_z[ig];
const double fac_ekin = 2.0 * psi2s;
tsum[1] += fac_ekin * tkpgx * tkpgx;
tsum[2] += fac_ekin * tkpgy * tkpgy;
tsum[3] += fac_ekin * tkpgz * tkpgz;
tsum[4] += fac_ekin * tkpgx * tkpgy;
tsum[5] += fac_ekin * tkpgy * tkpgz;
tsum[6] += fac_ekin * tkpgx * tkpgz;
}
// tsum[0-6] contains the contributions to
// ekin, sigma_ekin, from vector ig
} // ig
if ( use_confinement )
{
const valarray& fstress = cfp[ikp]->fstress();
const valarray& dfstress = cfp[ikp]->dfstress();
for ( int ig = 0; ig < ngwloc; ig++ )
{
const double psi2s = psi2sum[ig];
// tsum[7]: econf partial sum
tsum[7] += psi2s * fstress[ig];
if ( compute_stress )
{
const double tkpgx = kpg_x[ig];
const double tkpgy = kpg_y[ig];
const double tkpgz = kpg_z[ig];
const double fac_econf = psi2s * dfstress[ig];
tsum[8] += fac_econf * tkpgx * tkpgx;
tsum[9] += fac_econf * tkpgy * tkpgy;
tsum[10] += fac_econf * tkpgz * tkpgz;
tsum[11] += fac_econf * tkpgx * tkpgy;
tsum[12] += fac_econf * tkpgy * tkpgz;
tsum[13] += fac_econf * tkpgx * tkpgz;
}
// tsum[7-13] contains the contributions to
// econf,sigma_econf from vector ig
} // ig
}
sum += weight * tsum;
} // ikp
} // ispin
// sum contains the contributions to ekin, etc.. from this task
wf.context().dsum(14,1,&sum[0],14);
ekin_ = sum[0];
sigma_ekin[0] = sum[1];
sigma_ekin[1] = sum[2];
sigma_ekin[2] = sum[3];
sigma_ekin[3] = sum[4];
sigma_ekin[4] = sum[5];
sigma_ekin[5] = sum[6];
econf_ = sum[7];
sigma_econf[0] = sum[8];
sigma_econf[1] = sum[9];
sigma_econf[2] = sum[10];
sigma_econf[3] = sum[11];
sigma_econf[4] = sum[12];
sigma_econf[5] = sum[13];
sigma_ekin *= omega_inv;
sigma_econf *= omega_inv;
tmap["ekin"].stop();
// Stress from Eps
sigma_eps = 0.0;
if ( compute_stress )
{
tsum = 0.0;
const double *const gi = vbasis_->gi_ptr();
const double *const g_x = vbasis_->gx_ptr(0);
const double *const g_y = vbasis_->gx_ptr(1);
const double *const g_z = vbasis_->gx_ptr(2);
for ( int ig = 0; ig < ngloc; ig++ )
{
// factor of 2 in next line: G and -G
// note: gi[0] == 0.0 in next line (no division by zero)
const double fac = 2.0 * gi[ig] *
real( conj(rhoelg[ig]) * dvion_local_g[ig] );
const double tgx = g_x[ig];
const double tgy = g_y[ig];
const double tgz = g_z[ig];
tsum[0] += fac * tgx * tgx;
tsum[1] += fac * tgy * tgy;
tsum[2] += fac * tgz * tgz;
tsum[3] += fac * tgx * tgy;
tsum[4] += fac * tgy * tgz;
tsum[5] += fac * tgx * tgz;
}
vbasis_->context().dsum(6,1,&tsum[0],6);
sigma_eps[0] = eps_ * omega_inv + tsum[0];
sigma_eps[1] = eps_ * omega_inv + tsum[1];
sigma_eps[2] = eps_ * omega_inv + tsum[2];
sigma_eps[3] = tsum[3];
sigma_eps[4] = tsum[4];
sigma_eps[5] = tsum[5];
}
// Stress from Hartree energy
if ( compute_stress )
{
tsum = 0.0;
const double *const g_x = vbasis_->gx_ptr(0);
const double *const g_y = vbasis_->gx_ptr(1);
const double *const g_z = vbasis_->gx_ptr(2);
for ( int ig = 0; ig < ngloc; ig++ )
{
const double temp = norm(rhogt[ig]) * g2i[ig] * g2i[ig];
const double tgx = g_x[ig];
const double tgy = g_y[ig];
const double tgz = g_z[ig];
tsum[0] += temp * tgx * tgx;
tsum[1] += temp * tgy * tgy;
tsum[2] += temp * tgz * tgz;
tsum[3] += temp * tgx * tgy;
tsum[4] += temp * tgy * tgz;
tsum[5] += temp * tgx * tgz;
}
for ( int is = 0; is < nsp_; is++ )
{
// Factor 2 in next line: 2*real(x) = ( x + h.c. )
// Factor 0.5 in next line: definition of sigma
const double fac2 = 2.0 * 0.25 * rcps_[is]*rcps_[is];
complex *s = &sf.sfac[is][0];
for ( int ig = 0; ig < ngloc; ig++ )
{
const complex sg = s[ig];
const complex rg = rhogt[ig];
// next line: keep only real part
const double temp = fac2 *
( rg.real() * sg.real() +
rg.imag() * sg.imag() )
* rhops[is][ig] * g2i[ig];
const double tgx = g_x[ig];
const double tgy = g_y[ig];
const double tgz = g_z[ig];
tsum[0] += temp * tgx * tgx;
tsum[1] += temp * tgy * tgy;
tsum[2] += temp * tgz * tgz;
tsum[3] += temp * tgx * tgy;
tsum[4] += temp * tgy * tgz;
tsum[5] += temp * tgx * tgz;
}
}
vbasis_->context().dsum(6,1,&tsum[0],6);
// Factor in next line:
// factor 2 from G and -G
// factor fpi from definition of sigma
// no factor 1/Omega^2 (already included in rhogt[ig] above)
sigma_ehart[0] = ehart_ * omega_inv - 2.0 * fpi * tsum[0];
sigma_ehart[1] = ehart_ * omega_inv - 2.0 * fpi * tsum[1];
sigma_ehart[2] = ehart_ * omega_inv - 2.0 * fpi * tsum[2];
sigma_ehart[3] = - 2.0 * fpi * tsum[3];
sigma_ehart[4] = - 2.0 * fpi * tsum[4];
sigma_ehart[5] = - 2.0 * fpi * tsum[5];
} // compute_stress
// Non local energy and forces
tmap["nonlocal"].start();
// modify next loop to span only local ikp
enl_ = 0.0;
vector > fion_enl;
fion_enl.resize(nsp_);
for ( int is = 0; is < nsp_; is++ )
fion_enl[is].resize(3*na_[is]);
valarray sigma_enl_kp(6);
sigma_enl = 0.0;
for ( int ikp = 0; ikp < wf.nkp(); ikp++ )
{
for ( int ispin = 0; ispin < nlp.size(); ispin++ )
{
enl_ += wf.weight(ikp) * nlp[ispin][ikp]->energy(compute_hpsi,
*dwf.sd(ispin,ikp), compute_forces, fion_enl, compute_stress,
sigma_enl_kp);
if ( compute_forces )
for ( int is = 0; is < nsp_; is++ )
for ( int i = 0; i < 3*na_[is]; i++ )
fion[is][i] += wf.weight(ikp) * fion_enl[is][i];
if ( compute_stress )
sigma_enl += wf.weight(ikp) * sigma_enl_kp;
}
}
tmap["nonlocal"].stop();
ecoul_ = ehart_ + esr_ - eself_;
ets_ = 0.0;
if ( s_.ctrl.fermi_temp > 0.0 )
{
const double wf_entropy = wf.entropy();
const double boltz = 1.0 / ( 11605.0 * 2.0 * 13.6058 );
ets_ = - wf_entropy * s_.ctrl.fermi_temp * boltz;
}
etotal_ = ekin_ + econf_ + eps_ + enl_ + ecoul_ + exc_ + ets_ + eexf_;
if ( compute_hpsi )
{
tmap["hpsi"].start();
for ( int ispin = 0; ispin < wf.nspin(); ispin++ )
{
for ( int ikp = 0; ikp < wf.nkp(); ikp++ )
{
const SlaterDet& sd = *(wf.sd(ispin,ikp));
SlaterDet& sdp = *(dwf.sd(ispin,ikp));
const ComplexMatrix& c = sd.c();
const Basis& wfbasis = sd.basis();
ComplexMatrix& cp = dwf.sd(ispin,ikp)->c();
const int mloc = cp.mloc();
const double* kpg2 = wfbasis.kpg2_ptr();
const int ngwloc = wfbasis.localsize();
// Laplacian
if ( use_confinement )
{
for ( int n = 0; n < sd.nstloc(); n++ )
{
const valarray& fstress = cfp[ikp]->fstress();
for ( int ig = 0; ig < ngwloc; ig++ )
{
cp[ig+mloc*n] += 0.5 * ( kpg2[ig] + fstress[ig] ) *
c[ig+mloc*n];
}
}
}
else
{
for ( int n = 0; n < sd.nstloc(); n++ )
{
for ( int ig = 0; ig < ngwloc; ig++ )
{
cp[ig+mloc*n] += 0.5 * kpg2[ig] * c[ig+mloc*n];
}
}
}
// local potential
sd.rs_mul_add(*ft[ikp], &v_r[ispin][0], sdp);
} //ikp
} //ispin
// apply self-energy operator
xco->apply_self_energy(dwf);
tmap["hpsi"].stop();
} // if compute_hpsi
if ( compute_forces )
{
const int* idx = vbasis_->idx_ptr();
const double* gx0 = vbasis_->gx_ptr(0);
const double* gx1 = vbasis_->gx_ptr(1);
const double* gx2 = vbasis_->gx_ptr(2);
for ( int is = 0; is < nsp_; is++ )
{
for ( int ig = 0; ig < ngloc; ig++ )
{
double tmp = fpi * rhops[is][ig] * g2i[ig];
vtemp[ig] = tmp * conj(rhogt[ig]) + vps[is][ig] * conj(rhoelg[ig]);
}
memset((void*)&ftmp[0],0,3*namax_*sizeof(double));
// loop over atoms of species is
for ( int ia = 0; ia < na_[is]; ia++ )
{
double sum0=0.0,sum1=0.0,sum2=0.0;
double *c0 = sf.cos0_ptr(is,ia);
double *c1 = sf.cos1_ptr(is,ia);
double *c2 = sf.cos2_ptr(is,ia);
double *s0 = sf.sin0_ptr(is,ia);
double *s1 = sf.sin1_ptr(is,ia);
double *s2 = sf.sin2_ptr(is,ia);
for ( int ig = 0; ig < ngloc; ig++ )
{
// compute Exp[igr] in 3D as a product of 1D contributions
// complex teigr = ei0[kv[3*ig+0]] *
// ei1[kv[3*ig+1]] *
// ei2[kv[3*ig+2]];
const int iii = ig+ig+ig;
const int kx = idx[iii];
const int ky = idx[iii+1];
const int kz = idx[iii+2];
const double cos_a = c0[kx];
const double cos_b = c1[ky];
const double cos_c = c2[kz];
const double sin_a = s0[kx];
const double sin_b = s1[ky];
const double sin_c = s2[kz];
// Next line: exp(-i*gr) =
// (cos_a - I sin_a)*(cos_b - I sin_b)*(cos_c - I sin_c)
double teigr_re =
cos_a*cos_b*cos_c - sin_a*sin_b*cos_c -
sin_a*cos_b*sin_c - cos_a*sin_b*sin_c;
double teigr_im =
sin_a*sin_b*sin_c - sin_a*cos_b*cos_c -
cos_a*sin_b*cos_c - cos_a*cos_b*sin_c;
/* fion is real */
double tmp = teigr_re * vtemp[ig].imag() +
teigr_im * vtemp[ig].real();
sum0 += tmp * gx0[ig];
sum1 += tmp * gx1[ig];
sum2 += tmp * gx2[ig];
}
ftmp[3*ia] = sum0;
ftmp[3*ia+1] = sum1;
ftmp[3*ia+2] = sum2;
}
int len = 3*na_[is];
vbasis_->context().dsum(len,1,&ftmp[0],len);
double fac = -2.0 * omega;
for ( int i = 0; i < 3*na_[is]; i++ )
{
fion[is][i] += fion_esr[is][i] + fac * ftmp[i];
}
// add external forces
if ( s_.extforces.size() > 0 )
{
assert(fion.size()==fext.size());
assert(fion[is].size()==fext[is].size());
for ( int i = 0; i < fion[is].size(); i++ )
fion[is][i] += fext[is][i];
}
}
}
if ( compute_stress )
sigma = sigma_ekin + sigma_econf + sigma_eps + sigma_enl +
sigma_ehart + sigma_exc + sigma_esr;
if ( debug_stress && s_.ctxt_.onpe0() )
{
//const double gpa = 29421.5;
cout.setf(ios::fixed,ios::floatfield);
cout.setf(ios::right,ios::adjustfield);
cout << setprecision(8);
cout << " \n"
<< " " << setw(12)
<< sigma_ekin[0] << " \n"
<< " " << setw(12)
<< sigma_ekin[1] << " \n"
<< " " << setw(12)
<< sigma_ekin[2] << " \n"
<< " " << setw(12)
<< sigma_ekin[3] << " \n"
<< " " << setw(12)
<< sigma_ekin[4] << " \n"
<< " " << setw(12)
<< sigma_ekin[5] << " \n"
<< endl
<< " " << setw(12)
<< sigma_econf[0] << " \n"
<< " " << setw(12)
<< sigma_econf[1] << " \n"
<< " " << setw(12)
<< sigma_econf[2] << " \n"
<< " " << setw(12)
<< sigma_econf[3] << " \n"
<< " " << setw(12)
<< sigma_econf[4] << " \n"
<< " " << setw(12)
<< sigma_econf[5] << " \n"
<< endl
<< " " << setw(12)
<< sigma_eps[0] << " \n"
<< " " << setw(12)
<< sigma_eps[1] << " \n"
<< " " << setw(12)
<< sigma_eps[2] << " \n"
<< " " << setw(12)
<< sigma_eps[3] << " \n"
<< " " << setw(12)
<< sigma_eps[4] << " \n"
<< " " << setw(12)
<< sigma_eps[5] << " \n"
<< endl
<< " " << setw(12)
<< sigma_enl[0] << " \n"
<< " " << setw(12)
<< sigma_enl[1] << " \n"
<< " " << setw(12)
<< sigma_enl[2] << " \n"
<< " " << setw(12)
<< sigma_enl[3] << " \n"
<< " " << setw(12)
<< sigma_enl[4] << " \n"
<< " " << setw(12)
<< sigma_enl[5] << " \n"
<< endl
<< " " << setw(12)
<< sigma_ehart[0] << " \n"
<< " " << setw(12)
<< sigma_ehart[1] << " \n"
<< " " << setw(12)
<< sigma_ehart[2] << " \n"
<< " " << setw(12)
<< sigma_ehart[3] << " \n"
<< " " << setw(12)
<< sigma_ehart[4] << " \n"
<< " " << setw(12)
<< sigma_ehart[5] << " \n"
<< endl
<< " " << setw(12)
<< sigma_exc[0] << " \n"
<< " " << setw(12)
<< sigma_exc[1] << " \n"
<< " " << setw(12)
<< sigma_exc[2] << " \n"
<< " " << setw(12)
<< sigma_exc[3] << " \n"
<< " " << setw(12)
<< sigma_exc[4] << " \n"
<< " " << setw(12)
<< sigma_exc[5] << " \n"
<< endl
<< " " << setw(12)
<< sigma_esr[0] << " \n"
<< " " << setw(12)
<< sigma_esr[1] << " \n"
<< " " << setw(12)
<< sigma_esr[2] << " \n"
<< " " << setw(12)
<< sigma_esr[3] << " \n"
<< " " << setw(12)
<< sigma_esr[4] << " \n"
<< " " << setw(12)
<< sigma_esr[5] << " \n"
<< endl
<< " " << setw(12) << sigma[0] << " \n"
<< " " << setw(12) << sigma[1] << " \n"
<< " " << setw(12) << sigma[2] << " \n"
<< " " << setw(12) << sigma[3] << " \n"
<< " " << setw(12) << sigma[4] << " \n"
<< " " << setw(12) << sigma[5] << " \n"
<< " " << endl;
}
return etotal_;
}
////////////////////////////////////////////////////////////////////////////////
void EnergyFunctional::atoms_moved(void)
{
const AtomSet& atoms = s_.atoms;
int ngloc = vbasis_->localsize();
// fill tau0 with values in atom_list
atoms.get_positions(tau0);
sf.update(tau0,*vbasis_);
// compute Fourier coefficients of the local potential
memset( (void*)&vion_local_g[0], 0, 2*ngloc*sizeof(double) );
memset( (void*)&dvion_local_g[0], 0, 2*ngloc*sizeof(double) );
memset( (void*)&rhopst[0], 0, 2*ngloc*sizeof(double) );
for ( int is = 0; is < atoms.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];
vion_local_g[ig] += sg * vps[is][ig];
dvion_local_g[ig] += sg * dvps[is][ig];
}
}
// compute esr: pseudocharge repulsion energy
const UnitCell& cell = s_.wf.cell();
const double omega_inv = 1.0 / cell.volume();
// distance between opposite planes of the cell
const double d0 = 2.0 * M_PI / length(cell.b(0));
const double d1 = 2.0 * M_PI / length(cell.b(1));
const double d2 = 2.0 * M_PI / length(cell.b(2));
assert (d0!=0.0);
assert (d1!=0.0);
assert (d2!=0.0);
#ifdef DEBUG
cout << " EnergyFunctional: d0 = " << d0 << endl;
cout << " EnergyFunctional: d1 = " << d1 << endl;
cout << " EnergyFunctional: d2 = " << d2 << endl;
#endif
esr_ = 0.0;
sigma_esr = 0.0;
for ( int is = 0; is < nsp_; is++ )
for ( int i = 0; i < fion_esr[is].size(); i++ )
fion_esr[is][i] = 0.0;
for ( int is1 = 0; is1 < nsp_; is1++ )
{
for ( int is2 = is1; is2 < nsp_; is2++ )
{
double rcps12 = sqrt ( rcps_[is1]*rcps_[is1]+rcps_[is2]*rcps_[is2] );
// convergence criterion for lattice sums:
// fac * rcps12 < ncell * d
const double fac = 8.0;
const int ncell0 = (int) (fac * rcps12 / d0);
const int ncell1 = (int) (fac * rcps12 / d1);
const int ncell2 = (int) (fac * rcps12 / d2);
#ifdef DEBUG
const double mindist = min(min(d0,d1),d2);
cout << " EnergyFunctional: mindist/rcps12: " << mindist/rcps12 << endl;
cout << " EnergyFunctional: ncell[012]: "
<< ncell0 << " " << ncell1 << " " << ncell2 << endl;
#endif
for ( int ia1 = 0; ia1 < na_[is1]; ia1++ )
{
int ia2min = 0;
if ( is1 == is2 ) ia2min = ia1;
for ( int ia2 = ia2min; ia2 < na_[is2]; ia2++ )
{
const bool same_atom = ( is1==is2 && ia1==ia2 );
double x12 = tau0[is1][3*ia1+0] - tau0[is2][3*ia2+0];
double y12 = tau0[is1][3*ia1+1] - tau0[is2][3*ia2+1];
double z12 = tau0[is1][3*ia1+2] - tau0[is2][3*ia2+2];
D3vector v12(x12,y12,z12);
cell.fold_in_ws(v12);
// loop over neighboring cells
for ( int ic0 = -ncell0; ic0 <= ncell0; ic0++ )
for ( int ic1 = -ncell1; ic1 <= ncell1; ic1++ )
for ( int ic2 = -ncell2; ic2 <= ncell2; ic2++ )
{
if ( !same_atom || ic0!=0 || ic1!=0 || ic2!=0 )
{
double fac = 1.0;
if ( same_atom )
fac = 0.5;
D3vector s = ic0*cell.a(0) + ic1*cell.a(1) + ic2*cell.a(2);
x12 = v12.x + s.x;
y12 = v12.y + s.y;
z12 = v12.z + s.z;
double r12 = sqrt(x12*x12 + y12*y12 + z12*z12);
double arg = r12 / rcps12;
double esr_term = zv_[is1] * zv_[is2] * erfc(arg) / r12;
esr_ += fac * esr_term;
double desr_erfc = 2.0 * zv_[is1]*zv_[is2] *
exp(-arg*arg)/(rcps12*sqrt(M_PI));
// desrdr = (1/r) d Esr / dr
double desrdr = - fac * (esr_term+desr_erfc) / ( r12*r12 );
fion_esr[is1][3*ia1+0] -= desrdr * x12;
fion_esr[is2][3*ia2+0] += desrdr * x12;
fion_esr[is1][3*ia1+1] -= desrdr * y12;
fion_esr[is2][3*ia2+1] += desrdr * y12;
fion_esr[is1][3*ia1+2] -= desrdr * z12;
fion_esr[is2][3*ia2+2] += desrdr * z12;
sigma_esr[0] += desrdr * x12 * x12;
sigma_esr[1] += desrdr * y12 * y12;
sigma_esr[2] += desrdr * z12 * z12;
sigma_esr[3] += desrdr * x12 * y12;
sigma_esr[4] += desrdr * y12 * z12;
sigma_esr[5] += desrdr * x12 * z12;
}
}
}
}
}
}
sigma_esr *= - omega_inv;
// get external forces in fext
eexf_ = s_.extforces.energy(tau0,fext);
}
////////////////////////////////////////////////////////////////////////////////
void EnergyFunctional::cell_moved(void)
{
const Wavefunction& wf = s_.wf;
const UnitCell& cell = wf.cell();
// resize vbasis_
vbasis_->resize(cell,s_.wf.refcell(),4.0*s_.wf.ecut());
const int ngloc = vbasis_->localsize();
const double omega = cell.volume();
assert(omega != 0.0);
const double omega_inv = 1.0 / omega;
const AtomSet& atoms = s_.atoms;
for ( int is = 0; is < nsp_; is++ )
{
Species *s = atoms.species_list[is];
const double * const g = vbasis_->g_ptr();
double v,dv;
for ( int ig = 0; ig < ngloc; ig++ )
{
rhops[is][ig] = s->rhopsg(g[ig]) * omega_inv;
s->dvlocg(g[ig],v,dv);
vps[is][ig] = v * omega_inv;
dvps[is][ig] = dv * omega_inv;
}
}
// Update confinement potentials and non-local potentials
for ( int ikp = 0; ikp < wf.nkp(); ikp++ )
{
cfp[ikp]->update();
for ( int ispin = 0; ispin < nlp.size(); ispin++ )
nlp[ispin][ikp]->update_twnl();
}
}
////////////////////////////////////////////////////////////////////////////////
void EnergyFunctional::print(ostream& os) const
{
os.setf(ios::fixed,ios::floatfield);
os.setf(ios::right,ios::adjustfield);
os << setprecision(8);
os << " " << setw(15) << ekin() << " \n"
<< " " << setw(15) << econf() << " \n"
<< " " << setw(15) << eps() << " \n"
<< " " << setw(15) << enl() << " \n"
<< " " << setw(15) << ecoul() << " \n"
<< " " << setw(15) << exc() << " \n"
<< " " << setw(15) << esr() << " \n"
<< " " << setw(15) << eself() << " \n"
<< " " << setw(15) << ets() << " \n"
<< " " << setw(15) << eexf() << " \n"
<< " " << setw(15) << etotal() << " \n"
<< flush;
}
////////////////////////////////////////////////////////////////////////////////
ostream& operator<< ( ostream& os, const EnergyFunctional& e )
{
e.print(os);
return os;
}