1 /*-------------------------------------------------------------------
2 Copyright 2013 Ravishankar Sundararaman
3
4 This file is part of JDFTx.
5
6 JDFTx is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 JDFTx is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with JDFTx. If not, see <http://www.gnu.org/licenses/>.
18 -------------------------------------------------------------------*/
19
20 #include <electronic/SpeciesInfo.h>
21 #include <electronic/SpeciesInfo_internal.h>
22 #include <electronic/Everything.h>
23 #include <electronic/ColumnBundle.h>
24 #include <core/LatticeUtils.h>
25
26 //------- SpeciesInfo functions related to atomic orbitals -------
27
28
29 //Overlap of two radial functions (assumes same grid configuration, but one grid could be shorter)
dot(const RadialFunctionR & X,const RadialFunctionR & Y)30 double dot(const RadialFunctionR& X, const RadialFunctionR& Y)
31 { size_t nr = std::min(X.f.size(), Y.f.size());
32 assert(X.r.size() >= nr);
33 assert(X.dr.size() >= nr);
34 double ret = 0.;
35 for(size_t i=0; i<nr; i++)
36 { const double& r = X.r[i];
37 const double& dr = X.dr[i];
38 ret += (r*r*dr) * (X.f[i] * Y.f[i]);
39 }
40 return ret;
41 }
42 //Accumulate radial functions (assumes same grid configuration, but X could be shorter)
axpy(double alpha,const RadialFunctionR & X,RadialFunctionR & Y)43 void axpy(double alpha, const RadialFunctionR& X, RadialFunctionR& Y)
44 { size_t nr = X.f.size();
45 assert(Y.f.size() >= nr);
46 for(size_t i=0; i<nr; i++) Y.f[i] += alpha * X.f[i];
47 }
48
49
50
accumulateAtomicDensity(ScalarFieldTildeArray & nTilde) const51 void SpeciesInfo::accumulateAtomicDensity(ScalarFieldTildeArray& nTilde) const
52 { //Collect list of distinct magnetizations and corresponding atom positions:
53 struct MomentDirection //list of atoms with same magnetic moment
54 { vector3<> Mhat;
55 std::vector< vector3<> > atpos;
56 };
57 struct MomentMagnitude //list of atoms with same magnetic moment magnitude
58 { double Mlength;
59 std::vector<MomentDirection> Mdirs;
60 };
61 std::vector<MomentMagnitude> Mmags;
62 if(initialMagneticMoments.size())
63 { for(unsigned a=0; a<atpos.size(); a++)
64 { const vector3<>& M = initialMagneticMoments[a];
65 double Mlength = M.length();
66 vector3<> Mhat = (Mlength ? 1./Mlength : 0.) * M;
67 bool foundLength = false;
68 for(MomentMagnitude& Mmag: Mmags) if(fabs(Mlength - Mmag.Mlength) < symmThreshold)
69 { bool foundDir = false;
70 for(MomentDirection& Mdir: Mmag.Mdirs) if((M - Mmag.Mlength*Mdir.Mhat).length_squared() < symmThresholdSq)
71 { Mdir.atpos.push_back(atpos[a]);
72 foundDir = true;
73 break;
74 }
75 if(!foundDir)
76 { MomentDirection Mdir;
77 Mdir.Mhat = Mhat;
78 Mdir.atpos.push_back(atpos[a]);
79 Mmag.Mdirs.push_back(Mdir);
80 }
81 foundLength = true;
82 break;
83 }
84 if(!foundLength)
85 { MomentDirection Mdir;
86 Mdir.Mhat = Mhat;
87 Mdir.atpos.push_back(atpos[a]);
88 MomentMagnitude Mmag;
89 Mmag.Mlength = Mlength;
90 Mmag.Mdirs.push_back(Mdir);
91 Mmags.push_back(Mmag);
92 }
93 }
94 }
95 else
96 { MomentDirection Mdir;
97 Mdir.atpos = atpos;
98 MomentMagnitude Mmag;
99 Mmag.Mlength = 0.;
100 Mmag.Mdirs.push_back(Mdir);
101 Mmags.push_back(Mmag);
102 }
103 //For each magnetization magnitude:
104 for(const MomentMagnitude& Mmag: Mmags)
105 { if(e->eInfo.nDensities == 1) assert(!Mmag.Mlength);
106 //Get the radial spin-densities in diagonal basis:
107 std::vector<RadialFunctionG> nRadial(Mmag.Mlength ? 2 : 1);
108 for(unsigned s=0; s<nRadial.size(); s++)
109 getAtom_nRadial(s, Mmag.Mlength, nRadial[s], false);
110 //Collect contributions for each direction with this magnitude:
111 for(const MomentDirection& Mdir: Mmag.Mdirs)
112 { //Compute structure factor for atoms with current magnetization vector:
113 ManagedArray<vector3<>> atposCur(Mdir.atpos);
114 ScalarFieldTilde SG; nullToZero(SG, e->gInfo);
115 callPref(getSG)(e->gInfo.S, Mdir.atpos.size(), atposCur.dataPref(), 1./e->gInfo.detR, SG->dataPref());
116 //Spin-densities in diagonal basis
117 std::vector<ScalarFieldTilde> nDiag;
118 for(const RadialFunctionG& nRad: nRadial)
119 nDiag.push_back(nRad * SG);
120 //Accumulate contributions:
121 if(nDiag.size()==1)
122 { if(nTilde.size()==1)
123 nTilde[0] += 2.*nDiag[0];
124 else
125 { nTilde[0] += nDiag[0];
126 nTilde[1] += nDiag[0];
127 }
128 }
129 else
130 { nTilde[0] += (0.5*(1.+Mdir.Mhat[2])) * nDiag[0] + (0.5*(1.-Mdir.Mhat[2])) * nDiag[1];
131 nTilde[1] += (0.5*(1.-Mdir.Mhat[2])) * nDiag[0] + (0.5*(1.+Mdir.Mhat[2])) * nDiag[1];
132 if(nTilde.size()==4) //noncollinear
133 { nTilde[2] += (0.5*Mdir.Mhat[0]) * (nDiag[0] - nDiag[1]); //Re(UpDn) = 0.5*Mx
134 nTilde[3] += (0.5*Mdir.Mhat[1]) * (nDiag[1] - nDiag[0]); //Im(UpDn) = -0.5*My
135 }
136 }
137 }
138 for(RadialFunctionG& nRad: nRadial) nRad.free();
139 }
140 }
141
accumulateAtomicPotential(ScalarFieldTilde & dTilde) const142 void SpeciesInfo::accumulateAtomicPotential(ScalarFieldTilde& dTilde) const
143 { //Get radial potential due to one atom:
144 RadialFunctionG dRadial;
145 getAtomPotential(dRadial);
146 //Gets tructure factor:
147 ScalarFieldTilde SG; nullToZero(SG, e->gInfo);
148 callPref(getSG)(e->gInfo.S, atpos.size(), atposManaged.dataPref(), 1./e->gInfo.detR, SG->dataPref());
149 //Accumulate contrbutions:
150 dTilde += dRadial * SG;
151 dRadial.free();
152 }
153
154 //Set atomic orbitals in column bundle from radial functions (almost same operation as setting Vnl)
setAtomicOrbitals(ColumnBundle & Y,bool applyO,int colOffset,const vector3<> * derivDir,const int stressDir) const155 void SpeciesInfo::setAtomicOrbitals(ColumnBundle& Y, bool applyO, int colOffset, const vector3<>* derivDir, const int stressDir) const
156 { if(!atpos.size()) return;
157 const auto& fRadial = applyO ? OpsiRadial : psiRadial; //!< select radial function set (psi or Opsi)
158 int nSpinCopies = 2/e->eInfo.qWeightSum;
159 int nOrbitalsPerAtom = 0;
160 for(int l=0; l<int(fRadial.size()); l++)
161 nOrbitalsPerAtom += nAtomicOrbitals(l)*(2*l+1)*nSpinCopies;
162 int iCol = colOffset;
163 for(int l=0; l<int(fRadial.size()); l++)
164 for(int n=0; n<nAtomicOrbitals(l); n++)
165 { setAtomicOrbitals(Y, applyO, n, l, iCol, nOrbitalsPerAtom, derivDir, stressDir);
166 iCol += (2*l+1)*nSpinCopies;
167 }
168 }
setAtomicOrbitals(ColumnBundle & psi,bool applyO,unsigned n,int l,int colOffset,int atomColStride,const vector3<> * derivDir,const int stressDir) const169 void SpeciesInfo::setAtomicOrbitals(ColumnBundle& psi, bool applyO, unsigned n, int l, int colOffset, int atomColStride, const vector3<>* derivDir, const int stressDir) const
170 { if(!atpos.size()) return;
171 assert(l < int(psiRadial.size()));
172 assert(int(n) < nAtomicOrbitals(l));
173 const auto& fRadial = applyO ? OpsiRadial : psiRadial; //!< select radial function set (psi or Opsi)
174 int nSpinCopies = 2/e->eInfo.qWeightSum;
175 int nOrbitalsPerAtom = (2*l+1)*nSpinCopies;
176 if(atomColStride) assert(atomColStride >= nOrbitalsPerAtom); else atomColStride = nOrbitalsPerAtom;
177 assert(psi.basis); assert(psi.qnum);
178 assert(colOffset + atomColStride*int(atpos.size()-1) + nOrbitalsPerAtom <= psi.nCols());
179 if(nSpinCopies>1) assert(psi.isSpinor()); //can have multiple spinor copies only in spinor mode
180 const Basis& basis = *psi.basis;
181 if(isRelativistic() && l>0)
182 { //find the two orbital indices corresponding to different j of same n
183 std::vector<int> pArr;
184 for(int j2=2*l-1; j2<=2*l+1; j2+=2)
185 { unsigned ns = 0;
186 for(unsigned p=0; p<psiRadial[l].size(); p++)
187 if(psi2j[l][p]==j2)
188 { if(ns==n) { pArr.push_back(p); break; }
189 ns++;
190 }
191 }
192 //Initialize a non-spinor ColumnBundle containing all m's for both j functions:
193 ColumnBundle V(atpos.size()*nOrbitalsPerAtom, basis.nbasis, &basis, psi.qnum, isGpuEnabled());
194 int iCol=0;
195 for(int p: pArr) for(int m=-l; m<=l; m++)
196 { size_t atomStride = V.colLength() * nOrbitalsPerAtom;
197 size_t offs = iCol * V.colLength();
198 callPref(Vnl)(basis.nbasis, atomStride, atpos.size(), l, m, psi.qnum->k, basis.iGarr.dataPref(),
199 e->gInfo.G, atposManaged.dataPref(), fRadial[l][p], V.dataPref()+offs, derivDir, stressDir);
200 iCol++;
201 }
202 //Transform the non-spinor ColumnBundle to the spinorial j eigenfunctions:
203 int N = nOrbitalsPerAtom;
204 matrix transform = zeroes(2*N, N);
205 transform.set(0,N, 0,2*l, getYlmToSpinAngleMatrix(l, 2*l-1));
206 transform.set(N,2*N, 2*l,N, getYlmToSpinAngleMatrix(l, 2*l+1));
207 for(size_t a=0; a<atpos.size(); a++)
208 psi.setSub(colOffset+a*atomColStride, V.getSub(a*N,(a+1)*N) * transform);
209 }
210 else
211 { int iCol = colOffset; //current column
212 for(int m=-l; m<=l; m++)
213 { //Set atomic orbitals for all atoms at specified (n,l,m):
214 size_t atomStride = psi.colLength() * atomColStride;
215 size_t offs = iCol * psi.colLength();
216 callPref(Vnl)(basis.nbasis, atomStride, atpos.size(), l, m, psi.qnum->k, basis.iGarr.dataPref(),
217 e->gInfo.G, atposManaged.dataPref(), fRadial[l][n], psi.dataPref()+offs, derivDir, stressDir);
218 if(nSpinCopies>1) //make copy for other spin
219 { complex* dataPtr = psi.dataPref()+offs;
220 for(size_t a=0; a<atpos.size(); a++)
221 { callPref(eblas_zero)(2*basis.nbasis, dataPtr+basis.nbasis);
222 callPref(eblas_copy)(dataPtr+3*basis.nbasis, dataPtr, basis.nbasis);
223 dataPtr += atomStride;
224 }
225 }
226 iCol += nSpinCopies;
227 }
228 }
229 }
nAtomicOrbitals() const230 int SpeciesInfo::nAtomicOrbitals() const
231 { int nOrbitals = 0;
232 if(isRelativistic())
233 { for(int l=0; l<int(psiRadial.size()); l++)
234 for(unsigned n=0; n<psiRadial[l].size(); n++)
235 nOrbitals += (psi2j[l][n]+1);
236 return nOrbitals * atpos.size();
237 }
238 else
239 { for(int l=0; l<int(psiRadial.size()); l++)
240 nOrbitals += (2*l+1)*psiRadial[l].size();
241 int nSpinCopies = 2/e->eInfo.qWeightSum;
242 return nOrbitals * atpos.size() * nSpinCopies;
243 }
244 }
lMaxAtomicOrbitals() const245 int SpeciesInfo::lMaxAtomicOrbitals() const
246 { return int(psiRadial.size()) - 1;
247 }
nAtomicOrbitals(int l) const248 int SpeciesInfo::nAtomicOrbitals(int l) const
249 { assert(l >= 0);
250 if(unsigned(l) >= psiRadial.size()) return -1; //signals end of l
251 return psiRadial[l].size() / ((isRelativistic() && l>0) ? 2 : 1); //principal quantum number reduced by a factor of 2 when psiRadial counts the j splitting
252 }
atomicOrbitalOffset(unsigned int iAtom,unsigned int n,int l,int m,int s) const253 int SpeciesInfo::atomicOrbitalOffset(unsigned int iAtom, unsigned int n, int l, int m, int s) const
254 { assert(iAtom < atpos.size());
255 assert(l >= 0); assert(unsigned(l) < psiRadial.size());
256 assert(s < e->eInfo.spinorLength());
257 assert(int(n) < nAtomicOrbitals(l));
258 int nSpinCopies = 2/e->eInfo.qWeightSum;
259 int iOrb = 0;
260 for(int L=0; L<int(psiRadial.size()); L++)
261 { int nCount_L = nAtomicOrbitals(L);
262 iOrb += nCount_L * (2*L+1)*nSpinCopies * iAtom; //orbitals from previous atoms
263 if(L<=l)
264 iOrb += (L==l ? n : nCount_L) * (2*L+1)*nSpinCopies; //orbitals from previous l,n at current atom
265 }
266 if(isRelativistic())
267 { int j2 = 2*l + (s ? -1 : +1); //2*j
268 int mj2 = 2*m + + (s ? -1 : +1); //2*m_j
269 assert(mj2 >= -j2); assert(mj2 <= j2);
270 if(s==0) iOrb += 2*l; //include orbitals from previous j at current n,l,atom (j = l-1/2 (accessed using s=1) is stored first)
271 return iOrb + (j2+mj2)/2; //include orbitals from previous m at current j,n,l,atom
272 }
273 else
274 { assert(m >= -l); assert(m <= l);
275 return iOrb + (l+m)*nSpinCopies + s; //include orbitals from previous m,s at current n,l,atom
276 }
277 }
278
estimateAtomEigs()279 void SpeciesInfo::estimateAtomEigs()
280 { if(!psiRadial.size()) return; //no orbitals
281
282 //Compute eigenvalues if unavailable
283 if(!atomEigs.size())
284 { std::map<int,int> invalidPsis; //some FHI pseudodpotentials store unbound projectors which need to be discarded for LCAO
285 logPrintf(" Approximate pseudo-atom eigenvalues: ");
286 atomEigs.resize(psiRadial.size());
287 double normFac = 1./e->gInfo.detR; //normalization factor in wavefunctions
288 for(unsigned l=0; l<psiRadial.size(); l++)
289 { for(unsigned p=0; p<psiRadial[l].size(); p++)
290 { const RadialFunctionR& psi = *(psiRadial[l][p].rFunc);
291 //Find points deep in the tail of the wavefunction, but still far above roundoff limit:
292 double r[2], e[2];
293 double psiThresh[2] = { 3e-7*normFac, 3e-6*normFac };
294 unsigned i = psi.r.size()-2;
295 for(int j=0; j<2; j++)
296 { for(; i>1; i--) if(fabs(psi.f[i]) > psiThresh[j]) break;
297 //Apply the Schrodinger operator with V = 0 (tail of a neutral atom)
298 r[j] = psi.r[i]; double rm = psi.r[i-1], rp = psi.r[i+1];
299 double Dfp = (psi.f[i+1]/psi.f[i] - 1.) / (rp-r[j]);
300 double Dfm = (psi.f[i-1]/psi.f[i] - 1.) / (rm-r[j]);
301 if(Dfp >= 0. || Dfm >= 0.) { e[j] = 0.; continue; } //must be an unbound state
302 e[j] = (-0.5/(r[j]*r[j])) * (l*(l+1) + (0.5/(rp-rm)) * (Dfp*(rp+r[j])*(rp+r[j]) - Dfm*(rm+r[j])*(rm+r[j])));
303 }
304 if(e[0] && e[1])
305 { double eCorrected = (r[0]*e[0] - r[1]*e[1]) / (r[0] - r[1]); //correct for remnant Z/r term in non-neutral atoms
306 atomEigs[l].push_back(eCorrected);
307 }
308 else //unbound state
309 { atomEigs[l].push_back(std::numeric_limits<double>::infinity()); //make sure it doesn't get occupied
310 invalidPsis[l]++;
311 }
312 }
313 //Print:
314 const char orbCode[] = "spdfgh";
315 if(isRelativistic() && l>0) //split by j
316 { int l2 = 2*l;
317 for(int j2=l2-1; j2<=l2+1; j2+=2)
318 { logPrintf(" %c%c (", orbCode[l], (j2<l2 ? '-' : '+'));
319 for(unsigned p=0; p<psiRadial[l].size(); p++)
320 if(psi2j[l][p]==j2) logPrintf(" %.2lg", atomEigs[l][p]);
321 logPrintf(" )");
322 }
323 }
324 else
325 { logPrintf(" %c (", orbCode[l]);
326 for(unsigned p=0; p<psiRadial[l].size(); p++)
327 logPrintf(" %.2lg", atomEigs[l][p]);
328 logPrintf(" )");
329 }
330 }
331 logPrintf("\n");
332 //Report removal of invalid psi's:
333 for(auto invalidPsi: invalidPsis)
334 logPrintf(" WARNING: encountered %d l=%d unbound projectors in atomic orbital set.\n", invalidPsi.second, invalidPsi.first);
335 }
336 }
337
getAtom_nRadial(int spin,double magneticMoment,RadialFunctionG & nRadial,bool forceNeutral) const338 void SpeciesInfo::getAtom_nRadial(int spin, double magneticMoment, RadialFunctionG& nRadial, bool forceNeutral) const
339 {
340 int spinCount = (e->eInfo.nDensities==1 ? 1 : 2);
341 assert(spin >= 0); assert(spin < spinCount);
342
343 //Determine occupations:
344 std::multimap< double, std::pair<unsigned,unsigned> > eigMap; //map from eigenvalues to (l,p)
345 std::vector<std::vector<double> > atomF(psiRadial.size());
346 for(unsigned l=0; l<psiRadial.size(); l++)
347 { atomF[l].resize(psiRadial[l].size());
348 for(unsigned p=0; p<psiRadial[l].size(); p++)
349 eigMap.insert(std::make_pair(atomEigs[l][p], std::make_pair(l,p)));
350 }
351 if(spinCount>1 && magneticMoment)
352 logPrintf("%s (M=%lg) pseudo-atom %s spin occupations: ", name.c_str(), magneticMoment, spin==0 ? "majority" : "minority");
353 else
354 logPrintf("%s pseudo-atom occupations: ", name.c_str());
355 double Nvalence = Z - initialOxidationState;
356 double N = 0.5*(Nvalence + (1-2*spin)*magneticMoment); //total electrons to fill
357 if(forceNeutral) N = 0.5*Z; //force neutral unpolarized atom
358 if(N < 0.)
359 die("Magnetic moment (%lg) exceeds pseudo-atom valence electron count (%lg) [per spin channel].\n", magneticMoment, Nvalence);
360 double Favail = N; //electrons yet to be filled
361 for(auto eigEntry: eigMap) //in ascending order of eigenvalues
362 { unsigned l = eigEntry.second.first;
363 unsigned p = eigEntry.second.second;
364 double capacity = isRelativistic() ? 0.5*(psi2j[l][p]+1) : (2*l+1);
365 atomF[l][p] = std::min(Favail, capacity);
366 Favail -= atomF[l][p];
367 }
368 if(Favail > 0.)
369 die("Insufficient atomic orbitals to occupy %lg electrons (%lg excess electrons) [per spin channel].\n", N, Favail);
370 double spinFactor = (spinCount>1 && magneticMoment) ? 1. : 2.; //if unpolarized, print total occupations over both spin channels
371 const char orbCode[] = "spdfgh";
372 for(unsigned l=0; l<psiRadial.size(); l++) if(psiRadial[l].size())
373 { if(isRelativistic() && l>0)
374 { int l2 = 2*l;
375 for(int j2=l2-1; j2<=l2+1; j2+=2)
376 { logPrintf(" %c%c (", orbCode[l], (j2<l2 ? '-' : '+'));
377 for(unsigned p=0; p<psiRadial[l].size(); p++)
378 if(psi2j[l][p]==j2) logPrintf(" %.2lg", atomF[l][p] * spinFactor);
379 logPrintf(" )");
380 }
381 }
382 else
383 { logPrintf(" %c (", orbCode[l]);
384 for(unsigned p=0; p<psiRadial[l].size(); p++)
385 logPrintf(" %.2lg", atomF[l][p] * spinFactor);
386 logPrintf(" )");
387 }
388 }
389 logPrintf("\n");
390
391 //Compute atom electron density: (NOTE: assumes all orbitals are on the same grid)
392 RadialFunctionR n = *psiRadial[0][0].rFunc; n.f.assign(n.r.size(), 0.); //same grid as orbitals, initialize to 0.
393 for(unsigned l=0; l<psiRadial.size(); l++)
394 { for(unsigned p=0; p<psiRadial[l].size(); p++)
395 { const RadialFunctionR& psi = *(psiRadial[l][p].rFunc);
396 for(unsigned i=0; i<n.r.size(); i++)
397 n.f[i] += atomF[l][p] * psi.f[i] * psi.f[i];
398 }
399 //Augmentation for ultrasofts:
400 if(Qint.size())
401 { for(unsigned p1=0; p1<VnlRadial[l].size(); p1++)
402 for(unsigned p2=0; p2<=p1; p2++)
403 { QijIndex qIndex = { (int)l, (int)p1, (int)l, (int)p2, 0 };
404 auto Qijl = Qradial.find(qIndex);
405 if(Qijl==Qradial.end()) continue; //no augmentation for this combination
406 //Collect density matrix element for this pair of projectors:
407 double matrixElem = 0.;
408 for(unsigned o=0; o<psiRadial[l].size(); o++)
409 matrixElem += atomF[l][o]
410 * dot(*psiRadial[l][o].rFunc, *VnlRadial[l][p1].rFunc)
411 * dot(*psiRadial[l][o].rFunc, *VnlRadial[l][p2].rFunc);
412 //Augment density:
413 axpy(matrixElem * (p1==p2 ? 1. : 2.), *Qijl->second.rFunc, n);
414 }
415 }
416 }
417 //Fix normalization:
418 double normFac = std::pow(e->gInfo.detR,2)/(4.*M_PI);
419 for(unsigned i=0; i<n.r.size(); i++) n.f[i] *= normFac;
420 //Transform density:
421 const double dG = e->gInfo.dGradial;
422 int nGridLoc = int(ceil(e->gInfo.GmaxGrid/dG))+5;
423 n.transform(0, dG, nGridLoc, nRadial);
424 }
425
getAtomPotential(RadialFunctionG & dRadial) const426 void SpeciesInfo::getAtomPotential(RadialFunctionG& dRadial) const
427 { //Get the radial density of a neutral atom:
428 RadialFunctionG nRadial;
429 logSuspend();
430 getAtom_nRadial(0,0, nRadial, true);
431 logResume();
432 RadialFunctionR n = *nRadial.rFunc;
433 nRadial.free();
434 //Calculate cumulative charge distribution:
435 std::vector<double> Qin(n.r.size());
436 double Qcur = 0.;
437 for(size_t i=0; i<Qin.size(); i++)
438 { double dQ = (2*n.f[i]) * (4*M_PI * n.r[i]*n.r[i] * n.dr[i]); //factor of 2 for spin
439 Qcur += 0.5*dQ;
440 Qin[i] = Qcur; //Trapezoidal rule
441 Qcur += 0.5*dQ;
442 }
443 //Calculate electrostatic potential:
444 RadialFunctionR d = n;
445 double phiCur = Z/n.r.back();
446 int iLast = int(Qin.size())-1;
447 for(int i=iLast; i>=0; i--)
448 { double rInv = n.r[i] ? 1./n.r[i] : 0.;
449 double dphi = Qin[i] * rInv*rInv * n.dr[i];
450 if(i<iLast)
451 phiCur += 0.5*dphi;
452 d.f[i] = phiCur - Z*rInv;
453 phiCur += 0.5*dphi;
454 }
455 //Pseudize potential:
456 //--- calculate pseudization radius
457 double Rvdw = 0.;
458 for(int i=iLast; i>=0; i--)
459 if(2.*n.f[i] > 0.01)
460 { Rvdw = 1.1 * n.r[i]; //DFT+D2 definition for convenience
461 break;
462 }
463 int iVdw = std::upper_bound(n.r.begin(), n.r.end(), Rvdw) - n.r.begin();
464 assert(iVdw < iLast);
465 Rvdw = n.r[iVdw]; //round to grid point
466 //--- pseudize electrostatic potential so that it is zero outside Rvdw
467 double d0 = d.f[iVdw]; //value
468 double d1 = (d.f[iVdw+1] - d.f[iVdw-1]) / log(d.r[iVdw+1]/d.r[iVdw-1]); //first derivative w.r.t x = r/Rvdw
469 double d2 = (d.f[iVdw+1] + d.f[iVdw-1] - 2.*d0) / std::pow(0.5*log(d.r[iVdw+1]/d.r[iVdw-1]),2) - d1; //second derivative w.r.t x = r/Rvdw
470 double a3 = 10.*d0 - 4.*d1 + 0.5*d2; //expansion x^3 (a3 + a4 x + a5 x^2) that is C2 continuous at 0 and Rvdw
471 double a4 = -15.*d0 + 7.*d1 - d2;
472 double a5 = 6.*d0 - 3.*d1 + 0.5*d2;
473 for(int i=0; i<iVdw; i++)
474 { double x = d.r[i] / Rvdw;
475 d.f[i] -= x*x*x*(a3 + x*(a4 + x*a5));
476 }
477 for(size_t i=iVdw; i<d.f.size(); i++)
478 d.f[i] = 0.;
479 //Add non-electrostatic part of Vloc:
480 RadialFunctionR& Vloc = *(VlocRadial.rFunc);
481 for(size_t i=0; i<d.f.size(); i++)
482 d.f[i] += Vloc.f[i];
483 //Transform potential:
484 const double dG = e->gInfo.dGradial;
485 int nGridLoc = int(ceil(e->gInfo.GmaxGrid/dG))+5;
486 d.transform(0, dG, nGridLoc, dRadial);
487 }
488