Skip to content

Commit 32e29f4

Browse files
linpeizedyzheng
authored andcommitted
Fix(module_xc): compute real density laplacian for meta-GGA functionals via reciprocal space
SCANL, R2SCANL, and REVSCAN require the density laplacian (∇²ρ) but v_xc_meta was passing |∇ρ|² (sigma) instead. This caused ~150 kbar stress error for Na with SCANL vs SCAN. Laplacian computation method: ∇²ρ(r) = -∑_G |G|² ρ(G) e^{iGr} - ρ(r) is Fourier-transformed to ρ(G) via real2recip() - Each G-component is multiplied by -|G|² (i.e. -gg[ig] * tpiba²) - Inverse FFT via recip2real() yields ∇²ρ(r) on the real-space grid - Only the real part is retained (imaginary part should be ~0 for real ρ) This is exact within the plane-wave basis, not numerical differentiation. Spin handling: - cal_lapl(nspin, nrxx, rho, tpiba2, chr) computes ∇²ρ for each spin channel: rho is interleaved [rho_up[0], rho_dw[0], rho_up[1], ...] and output lapl is [lapl_up[0], lapl_dw[0], lapl_up[1], ...] - nspin=1: single channel (spin-unpolarized), loop runs once for is=0 - nspin=2: separate laplacian for spin-up and spin-down channels, each computed independently from its own real-space density - nspin=4 (noncollinear): effective up/down densities are constructed via noncolin_rho() diagonalization of the spin density matrix, then ∇²ρ is computed for each effective channel Changes: - Add cal_lapl() in xc_functional_libxc_tools.cpp as reusable function for reciprocal-space Laplacian computation per spin channel - Fix v_xc_meta to call cal_lapl() and pass real laplacian to LibXC; handle vlapl output by computing ∇²(vlapl) via same G-space method - Fix tau_xc wrapper — added lapl_rho parameter, removed grho hack - Fix tau_xc_spin wrapper — initialize lapl array to zero instead of leaving uninitialized - Fix gradcorr stress/force paths — replace ~50 lines of duplicated inline Laplacian code with cal_lapl() calls; handles nspin=1/2/4 spin channel selection consistently with existing grad_rho pattern - Update test_xc4.cpp and test CMakeLists for new tau_xc signature
1 parent 4ef86d8 commit 32e29f4

11 files changed

Lines changed: 248 additions & 29 deletions

source/module_esolver/esolver_ks.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ void ESolver_KS<T, Device>::before_all_runners(UnitCell& ucell, const Input_para
185185
{
186186
XC_Functional::set_xc_type(ucell.atoms[0].ncpp.xc_func);
187187
}
188+
GlobalV::ofs_running<<XC_Functional::output_info()<<std::endl;
188189
ModuleBase::GlobalFunc::DONE(GlobalV::ofs_running, "SETUP UNITCELL");
189190

190191
//! 4) setup the charge mixing parameters

source/module_hamilt_general/module_xc/test/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ AddTest(
1111
TARGET XCTest_HSE
1212
LIBS parameter MPI::MPI_CXX Libxc::xc # required by global.h; for details, `remove_definitions(-D__MPI)`.
1313
SOURCES test_xc1.cpp ../xc_functional.cpp ../xc_functional_libxc.cpp
14+
../xc_functional_libxc_tools.cpp
1415
)
1516

1617

@@ -36,6 +37,7 @@ AddTest(
3637
../xc_functional_libxc_wrapper_xc.cpp
3738
../xc_functional_libxc_wrapper_gcxc.cpp
3839
../xc_functional_libxc_wrapper_tauxc.cpp
40+
../xc_functional_libxc_tools.cpp
3941
../xc_funct_corr_gga.cpp ../xc_funct_corr_lda.cpp ../xc_funct_exch_gga.cpp
4042
../xc_funct_exch_lda.cpp ../xc_funct_hcth.cpp
4143
../../../module_base/matrix.cpp
@@ -57,6 +59,7 @@ AddTest(
5759
../xc_functional_libxc_wrapper_xc.cpp
5860
../xc_functional_libxc_wrapper_gcxc.cpp
5961
../xc_functional_libxc_wrapper_tauxc.cpp
62+
../xc_functional_libxc_tools.cpp
6063
../xc_funct_corr_gga.cpp ../xc_funct_corr_lda.cpp
6164
../xc_funct_exch_gga.cpp ../xc_funct_exch_lda.cpp ../xc_funct_hcth.cpp
6265
)

source/module_hamilt_general/module_xc/test/test_xc4.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ class XCTest_SCAN : public XCTest
4444
for(int i=0;i<5;i++)
4545
{
4646
double e,v,v1,v2,v3;
47-
XC_Functional_Libxc::tau_xc(XC_Functional::get_func_id(), rho[i],grho[i],tau[i],e,v1,v2,v3);
47+
// SCAN does not depend on laplacian, pass 0.0
48+
XC_Functional_Libxc::tau_xc(XC_Functional::get_func_id(), rho[i],grho[i],0.0,tau[i],e,v1,v2,v3);
4849
e_.push_back(e);
4950
v1_.push_back(v1);
5051
v2_.push_back(v2);

source/module_hamilt_general/module_xc/xc_functional.cpp

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,37 @@ int XC_Functional::get_func_type()
3434
{
3535
return func_type;
3636
}
37+
3738
void XC_Functional::set_xc_first_loop(const UnitCell& ucell)
3839
{
40+
ModuleBase::TITLE("XC_Functional", "set_xc_first_loop");
3941
/** In the special "two-level" calculation case,
4042
the first scf iteration only calculate the functional without exact
4143
exchange. but in "nscf" calculation, there is no need of "two-level"
4244
method. */
43-
if (ucell.atoms[0].ncpp.xc_func == "HF" || ucell.atoms[0].ncpp.xc_func == "HSE"
44-
|| ucell.atoms[0].ncpp.xc_func == "PBE0"|| ucell.atoms[0].ncpp.xc_func == "LC_PBE"
45-
|| ucell.atoms[0].ncpp.xc_func == "LC_WPBE" || ucell.atoms[0].ncpp.xc_func == "LRC_WPBEH"
46-
|| ucell.atoms[0].ncpp.xc_func == "CAM_PBEH") {
45+
if (ucell.atoms[0].ncpp.xc_func == "HF" || ucell.atoms[0].ncpp.xc_func == "PBE0" || ucell.atoms[0].ncpp.xc_func == "HSE")
46+
{
4747
XC_Functional::set_xc_type("pbe");
4848
}
49-
else if (ucell.atoms[0].ncpp.xc_func == "SCAN0") {
50-
XC_Functional::set_xc_type("scan");
49+
else if ( ucell.atoms[0].ncpp.xc_func == "LC_PBE" || ucell.atoms[0].ncpp.xc_func == "LC_WPBE"
50+
|| ucell.atoms[0].ncpp.xc_func == "LRC_WPBEH" || ucell.atoms[0].ncpp.xc_func == "CAM_PBEH" )
51+
{
52+
XC_Functional::set_xc_type("pbe");
5153
}
52-
else if (ucell.atoms[0].ncpp.xc_func == "B3LYP") {
54+
// added by jghan, 2024-07-07
55+
else if ( ucell.atoms[0].ncpp.xc_func == "MULLER" || ucell.atoms[0].ncpp.xc_func == "POWER"
56+
|| ucell.atoms[0].ncpp.xc_func == "WP22" || ucell.atoms[0].ncpp.xc_func == "CWP22" )
57+
{
58+
XC_Functional::set_xc_type("pbe");
59+
}
60+
else if (ucell.atoms[0].ncpp.xc_func == "B3LYP")
61+
{
5362
XC_Functional::set_xc_type("blyp");
5463
}
64+
else if (ucell.atoms[0].ncpp.xc_func == "SCAN0")
65+
{
66+
XC_Functional::set_xc_type("scan");
67+
}
5568
}
5669

5770
// The setting values of functional id according to the index in LIBXC
@@ -346,4 +359,44 @@ void XC_Functional::set_xc_type(const std::string xc_func_in)
346359
use_libxc = false;
347360
#endif
348361

362+
}
363+
364+
365+
std::string XC_Functional::output_info()
366+
{
367+
#ifdef USE_LIBXC
368+
if(use_libxc)
369+
{
370+
std::stringstream ss;
371+
ss<<" Libxc v"<<xc_version_string()<<std::endl;
372+
ss<<"\t"<<xc_reference()<<std::endl;
373+
374+
std::vector<xc_func_type> funcs = XC_Functional_Libxc::init_func(func_id, XC_UNPOLARIZED);
375+
for(const auto &func : funcs)
376+
{
377+
const xc_func_info_type *info = xc_func_get_info(&func);
378+
ss<<" XC: "<<xc_func_info_get_name(info)<<std::endl;
379+
for(int i=0; i<XC_MAX_REFERENCES; ++i)
380+
{
381+
const func_reference_type *ref = xc_func_info_get_references(func.info, i);
382+
if(ref)
383+
ss<<"\t"<<xc_func_reference_get_ref(ref)<<std::endl;
384+
}
385+
}
386+
XC_Functional_Libxc::finish_func(funcs);
387+
return ss.str();
388+
}
389+
else
390+
{
391+
std::string s = " XC:\t";
392+
for(const auto &id: func_id)
393+
s += std::string(xc_functional_get_name(id))+"\t";
394+
return s;
395+
}
396+
#else
397+
std::string s = " XC:\t";
398+
for(const auto &id: func_id)
399+
s += std::to_string(id)+"\t";
400+
return s;
401+
#endif
349402
}

source/module_hamilt_general/module_xc/xc_functional.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class XC_Functional
8888

8989
public:
9090
static std::vector<int> get_func_id() { return func_id; }
91+
static std::string output_info();
9192

9293
//-------------------
9394
// xc_functional_wrapper_xc.cpp

source/module_hamilt_general/module_xc/xc_functional_gradcorr.cpp

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ void XC_Functional::gradcorr(double &etxc, double &vtxc, ModuleBase::matrix &v,
6767
ModuleBase::Vector3<double>* gdr2 = nullptr;
6868
ModuleBase::Vector3<double>* h1 = nullptr;
6969
ModuleBase::Vector3<double>* h2 = nullptr;
70+
double* lapl1 = nullptr;
71+
double* lapl2 = nullptr;
7072
double* neg = nullptr;
7173
double** vsave = nullptr;
7274
double** vgg = nullptr;
@@ -95,6 +97,27 @@ void XC_Functional::gradcorr(double &etxc, double &vtxc, ModuleBase::matrix &v,
9597

9698
XC_Functional::grad_rho( rhogsum1 , gdr1, rhopw, ucell->tpiba);
9799

100+
XC_Functional::grad_rho( rhogsum1 , gdr1, rhopw, ucell->tpiba);
101+
102+
// compute laplacian of total density (rho[0] + rho_core) for nspin=1,2
103+
// and as the "up" channel equivalent for nspin=4 noncollinear case
104+
{
105+
std::vector<double> rho_total(rhopw->nrxx);
106+
#ifdef _OPENMP
107+
#pragma omp parallel for schedule(static, 1024)
108+
#endif
109+
for(int ir=0; ir<rhopw->nrxx; ++ir)
110+
rho_total[ir] = rhotmp1[ir];
111+
std::vector<double> lapl_all = XC_Functional_Libxc::cal_lapl(
112+
1, rhopw->nrxx, rho_total, ucell->tpiba2, chr);
113+
lapl1 = new double[rhopw->nrxx];
114+
#ifdef _OPENMP
115+
#pragma omp parallel for schedule(static, 1024)
116+
#endif
117+
for(int ir=0; ir<rhopw->nrxx; ++ir)
118+
lapl1[ir] = lapl_all[ir];
119+
}
120+
98121
// for spin polarized case;
99122
// calculate the gradient of (rho_core+rho) in reciprocal space.
100123
if(PARAM.inp.nspin==2)
@@ -120,6 +143,24 @@ void XC_Functional::gradcorr(double &etxc, double &vtxc, ModuleBase::matrix &v,
120143
if(!is_stress) { h2 = new ModuleBase::Vector3<double>[rhopw->nrxx]; }
121144

122145
XC_Functional::grad_rho( rhogsum2 , gdr2, rhopw, ucell->tpiba);
146+
147+
// compute laplacian of spin-down density (rho[1] + rho_core)
148+
{
149+
std::vector<double> rho_dw(rhopw->nrxx);
150+
#ifdef _OPENMP
151+
#pragma omp parallel for schedule(static, 1024)
152+
#endif
153+
for(int ir=0; ir<rhopw->nrxx; ++ir)
154+
rho_dw[ir] = rhotmp2[ir];
155+
std::vector<double> lapl_all = XC_Functional_Libxc::cal_lapl(
156+
1, rhopw->nrxx, rho_dw, ucell->tpiba2, chr);
157+
lapl2 = new double[rhopw->nrxx];
158+
#ifdef _OPENMP
159+
#pragma omp parallel for schedule(static, 1024)
160+
#endif
161+
for(int ir=0; ir<rhopw->nrxx; ++ir)
162+
lapl2[ir] = lapl_all[ir];
163+
}
123164
}
124165

125166
if(PARAM.inp.nspin == 4&&(PARAM.globalv.domag||PARAM.globalv.domag_z))
@@ -189,6 +230,40 @@ void XC_Functional::gradcorr(double &etxc, double &vtxc, ModuleBase::matrix &v,
189230
XC_Functional::grad_rho( rhogsum1 , gdr1, rhopw, ucell->tpiba);
190231
XC_Functional::grad_rho( rhogsum2 , gdr2, rhopw, ucell->tpiba);
191232

233+
// re-compute laplacians after noncollinear rho update
234+
{
235+
std::vector<double> rho_up(rhopw->nrxx);
236+
#ifdef _OPENMP
237+
#pragma omp parallel for schedule(static, 1024)
238+
#endif
239+
for(int ir=0; ir<rhopw->nrxx; ++ir)
240+
rho_up[ir] = rhotmp1[ir];
241+
std::vector<double> lapl_up = XC_Functional_Libxc::cal_lapl(
242+
1, rhopw->nrxx, rho_up, ucell->tpiba2, chr);
243+
lapl1 = new double[rhopw->nrxx];
244+
#ifdef _OPENMP
245+
#pragma omp parallel for schedule(static, 1024)
246+
#endif
247+
for(int ir=0; ir<rhopw->nrxx; ++ir)
248+
lapl1[ir] = lapl_up[ir];
249+
}
250+
{
251+
std::vector<double> rho_dw(rhopw->nrxx);
252+
#ifdef _OPENMP
253+
#pragma omp parallel for schedule(static, 1024)
254+
#endif
255+
for(int ir=0; ir<rhopw->nrxx; ++ir)
256+
rho_dw[ir] = rhotmp2[ir];
257+
std::vector<double> lapl_dw = XC_Functional_Libxc::cal_lapl(
258+
1, rhopw->nrxx, rho_dw, ucell->tpiba2, chr);
259+
lapl2 = new double[rhopw->nrxx];
260+
#ifdef _OPENMP
261+
#pragma omp parallel for schedule(static, 1024)
262+
#endif
263+
for(int ir=0; ir<rhopw->nrxx; ++ir)
264+
lapl2[ir] = lapl_dw[ir];
265+
}
266+
192267
}
193268

194269
const double epsr = 1.0e-6;
@@ -251,7 +326,7 @@ void XC_Functional::gradcorr(double &etxc, double &vtxc, ModuleBase::matrix &v,
251326
{
252327
double v3xc;
253328
double atau = chr->kin_r[0][ir]/2.0;
254-
XC_Functional_Libxc::tau_xc( func_id, arho, grho2a, atau, sxc, v1xc, v2xc, v3xc);
329+
XC_Functional_Libxc::tau_xc( func_id, arho, grho2a, lapl1[ir], atau, sxc, v1xc, v2xc, v3xc);
255330
}
256331
else
257332
{
@@ -582,6 +657,7 @@ void XC_Functional::gradcorr(double &etxc, double &vtxc, ModuleBase::matrix &v,
582657
delete[] rhotmp1;
583658
delete[] rhogsum1;
584659
delete[] gdr1;
660+
delete[] lapl1;
585661
if(!is_stress) { delete[] h1;
586662
}
587663

@@ -590,6 +666,7 @@ void XC_Functional::gradcorr(double &etxc, double &vtxc, ModuleBase::matrix &v,
590666
delete[] rhotmp2;
591667
delete[] rhogsum2;
592668
delete[] gdr2;
669+
delete[] lapl2;
593670
if(!is_stress) { delete[] h2;
594671
}
595672
}
@@ -609,6 +686,7 @@ void XC_Functional::gradcorr(double &etxc, double &vtxc, ModuleBase::matrix &v,
609686
delete[] rhotmp2;
610687
delete[] rhogsum2;
611688
delete[] gdr2;
689+
delete[] lapl2;
612690
}
613691

614692
return;

source/module_hamilt_general/module_xc/xc_functional_libxc.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ namespace XC_Functional_Libxc
8282
extern std::vector<double> convert_sigma(
8383
const std::vector<std::vector<ModuleBase::Vector3<double>>> &gdr);
8484

85+
// calculating laplacian of density: ∇²ρ(r)
86+
extern std::vector<double> cal_lapl(
87+
const int nspin,
88+
const std::size_t nrxx,
89+
const std::vector<double> &rho,
90+
const double tpiba2,
91+
const Charge* const chr);
92+
8593
// sgn for threshold mask
8694
extern std::vector<double> cal_sgn(
8795
const double rho_threshold,
@@ -166,7 +174,7 @@ namespace XC_Functional_Libxc
166174
// wrapper for the mGGA functionals
167175
extern void tau_xc(
168176
const std::vector<int> &func_id,
169-
const double &rho, const double &grho, const double &atau, double &sxc,
177+
const double &rho, const double &grho, const double &lapl_rho, const double &atau, double &sxc,
170178
double &v1xc, double &v2xc, double &v3xc);
171179

172180
extern void tau_xc_spin(

source/module_hamilt_general/module_xc/xc_functional_libxc_tools.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,46 @@ XC_Functional_Libxc::cal_gdr(
8282
return gdr;
8383
}
8484

85+
// calculating laplacian of density: ∇²ρ(r)
86+
std::vector<double> XC_Functional_Libxc::cal_lapl(
87+
const int nspin,
88+
const std::size_t nrxx,
89+
const std::vector<double> &rho,
90+
const double tpiba2,
91+
const Charge* const chr)
92+
{
93+
std::vector<double> lapl(nspin * nrxx, 0.0);
94+
for( int is=0; is!=nspin; ++is )
95+
{
96+
std::vector<double> rhor(nrxx);
97+
#ifdef _OPENMP
98+
#pragma omp parallel for schedule(static, 1024)
99+
#endif
100+
for(std::size_t ir=0; ir<nrxx; ++ir)
101+
rhor[ir] = rho[ir*nspin+is];
102+
103+
std::vector<std::complex<double>> rhog(chr->rhopw->npw);
104+
chr->rhopw->real2recip(rhor.data(), rhog.data());
105+
106+
// ∇²ρ(r) = -∑_G |G|² ρ(G) e^{iGr}
107+
std::vector<std::complex<double>> rhog_lapl(chr->rhopw->npw);
108+
#ifdef _OPENMP
109+
#pragma omp parallel for schedule(static, 1024)
110+
#endif
111+
for(int ig=0; ig<chr->rhopw->npw; ++ig)
112+
rhog_lapl[ig] = -rhog[ig] * chr->rhopw->gg[ig] * tpiba2;
113+
114+
std::vector<std::complex<double>> aux(chr->rhopw->nmaxgr);
115+
chr->rhopw->recip2real(rhog_lapl.data(), aux.data());
116+
#ifdef _OPENMP
117+
#pragma omp parallel for schedule(static, 1024)
118+
#endif
119+
for(std::size_t ir=0; ir<nrxx; ++ir)
120+
lapl[is * nrxx + ir] = aux[ir].real();
121+
} // end for(is)
122+
return lapl;
123+
}
124+
85125
// converting grho (abacus=>libxc)
86126
std::vector<double> XC_Functional_Libxc::convert_sigma(
87127
const std::vector<std::vector<ModuleBase::Vector3<double>>> &gdr)

0 commit comments

Comments
 (0)