Skip to content

Commit 0f94387

Browse files
fix(xc): implement proper Laplacian calculation for SCAN-L functional
- Add laplacian_rho() function to calculate Laplacian in reciprocal space - Update tau_xc() and tau_xc_spin() to accept laplacian parameter - Fix v_xc_meta() batch path which used sigma as laplacian - Add laplacian computation in gradcorr() for mGGA functionals - Add unit test verifying laplacian parameter affects XC energy Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com> Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com>
1 parent 707f092 commit 0f94387

8 files changed

Lines changed: 209 additions & 8 deletions

File tree

source/source_hamilt/module_xc/libxc_abacus.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ namespace XC_Functional_Libxc
198198
const std::vector<int> &func_id,
199199
const double &rho,
200200
const double &grho,
201+
const double &lapl_rho,
201202
const double &atau,
202203
double &sxc,
203204
double &v1xc,
@@ -211,6 +212,8 @@ namespace XC_Functional_Libxc
211212
double rhodw,
212213
ModuleBase::Vector3<double> gdr1,
213214
ModuleBase::Vector3<double> gdr2,
215+
double laplup,
216+
double lapldw,
214217
double tauup,
215218
double taudw,
216219
double &sxc,

source/source_hamilt/module_xc/libxc_mgga_wrap.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ void XC_Functional_Libxc::tau_xc(
1717
const std::vector<int>& func_id,
1818
const double& rho,
1919
const double& grho,
20+
const double& lapl_rho,
2021
const double& atau,
2122
double& sxc,
2223
double& v1xc,
@@ -28,7 +29,6 @@ void XC_Functional_Libxc::tau_xc(
2829
double v1 = 0.0;
2930
double v2 = 0.0;
3031
double v3 = 0.0;
31-
double lapl_rho = grho;
3232
double vlapl_rho = 0.0;
3333
std::vector<xc_func_type> funcs = XC_Functional_Libxc::init_func(
3434
/* func_id = */ func_id,
@@ -68,6 +68,8 @@ void XC_Functional_Libxc::tau_xc_spin(
6868
double rhodw,
6969
ModuleBase::Vector3<double> gdr1,
7070
ModuleBase::Vector3<double> gdr2,
71+
double laplup,
72+
double lapldw,
7173
double tauup,
7274
double taudw,
7375
double& sxc,
@@ -119,7 +121,7 @@ void XC_Functional_Libxc::tau_xc_spin(
119121
double s = 0.0;
120122
std::array<double, 2> v1xc = {0.0, 0.0};
121123
std::array<double, 2> v3xc = {0.0, 0.0};
122-
std::array<double, 2> lapl = {0.0, 0.0};
124+
std::array<double, 2> lapl = {laplup, lapldw};
123125
std::array<double, 2> vlapl = {0.0, 0.0};
124126
std::array<double, 3> v2xc = {0.0, 0.0, 0.0};
125127
// call Libxc function: xc_mgga_exc_vxc

source/source_hamilt/module_xc/libxc_pot.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <xc.h>
1313

1414
#include <vector>
15+
#include <complex>
1516

1617
std::tuple<double,double,ModuleBase::matrix> XC_Functional_Libxc::v_xc_libxc( // Peize Lin update for nspin==4 at 2023.01.14
1718
const std::vector<int> &func_id,
@@ -237,6 +238,27 @@ std::tuple<double,double,ModuleBase::matrix,ModuleBase::matrix> XC_Functional_Li
237238
= XC_Functional_Libxc::cal_gdr(nspin, nrxx, rho, tpiba, chr);
238239
const std::vector<double> sigma = XC_Functional_Libxc::convert_sigma(gdr);
239240

241+
// compute laplacian for mGGA functionals
242+
std::vector<double> lapl(nrxx * nspin, 0.0);
243+
{
244+
std::vector<std::complex<double>> rhog_tmp(chr->rhopw->npw);
245+
std::vector<double> rhor(nrxx);
246+
for( int is=0; is<nspin; ++is )
247+
{
248+
for( int ir=0; ir<nrxx; ++ir )
249+
{
250+
rhor[ir] = rho[ir*nspin+is];
251+
}
252+
chr->rhopw->real2recip(rhor.data(), rhog_tmp.data());
253+
std::vector<double> lapl_spin(nrxx);
254+
XC_Functional::laplacian_rho(rhog_tmp.data(), lapl_spin.data(), chr->rhopw, tpiba);
255+
for( int ir=0; ir<nrxx; ++ir )
256+
{
257+
lapl[ir*nspin+is] = lapl_spin[ir];
258+
}
259+
}
260+
}
261+
240262
//converting kin_r
241263
std::vector<double> kin_r;
242264
kin_r.resize(nrxx*nspin);
@@ -315,7 +337,7 @@ std::tuple<double,double,ModuleBase::matrix,ModuleBase::matrix> XC_Functional_Li
315337
nrxx_thread,
316338
rho.data() + ir_start * nspin,
317339
sigma.data() + ir_start * ((1==nspin)?1:3),
318-
sigma.data() + ir_start * ((1==nspin)?1:3),
340+
lapl.data() + ir_start * nspin,
319341
kin_r.data() + ir_start * nspin,
320342
exc.data() + ir_start,
321343
vrho.data() + ir_start * nspin,

source/source_hamilt/module_xc/test/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,17 @@ AddTest(
8282
../../../source_base/module_fft/fft_bundle.cpp
8383
../../../source_base/module_fft/fft_cpu.cpp
8484
${FFT_SRC}
85+
)
86+
87+
AddTest(
88+
TARGET MODULE_HAMILT_XCTest_SCANL_LAPL
89+
LIBS parameter MPI::MPI_CXX Libxc::xc
90+
SOURCES test_xc6.cpp ../xc_functional.cpp ../xc_lda_wrap.cpp
91+
../xc_gga_wrap.cpp
92+
../libxc_setup.cpp
93+
../libxc_lda_wrap.cpp
94+
../libxc_gga_wrap.cpp
95+
../libxc_mgga_wrap.cpp
96+
../xc_gga_corr.cpp ../xc_lda_corr.cpp
97+
../xc_gga_exch.cpp ../xc_lda_exch.cpp ../xc_hcth.cpp
8598
)

source/source_hamilt/module_xc/test/test_xc4.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ class XCTest_SCAN : public XCTest
4747
{
4848
double e,v,v1,v2,v3;
4949
double hybrid_alpha = 0.0;
50-
XC_Functional_Libxc::tau_xc(XC_Functional::get_func_id(), rho[i],grho[i],tau[i],e,v1,v2,v3,hybrid_alpha);
50+
double lapl_rho = grho[i];
51+
XC_Functional_Libxc::tau_xc(XC_Functional::get_func_id(), rho[i],grho[i],lapl_rho,tau[i],e,v1,v2,v3,hybrid_alpha);
5152
e_.push_back(e);
5253
v1_.push_back(v1);
5354
v2_.push_back(v2);
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include "../xc_functional.h"
2+
#include "../libxc_abacus.h"
3+
#include "gtest/gtest.h"
4+
#include "xctest.h"
5+
#include "../exx_info.h"
6+
#include <cmath>
7+
#include <iostream>
8+
#include <iomanip>
9+
10+
namespace ModuleBase
11+
{
12+
void WARNING_QUIT(const std::string &file,const std::string &description) {exit(1);}
13+
void TITLE(const std::string &class_function_name,bool disable){};
14+
void TITLE(const std::string &class_name,const std::string &function_name,bool disable){};
15+
}
16+
17+
namespace GlobalV
18+
{
19+
std::string BASIS_TYPE = "";
20+
bool CAL_STRESS = false;
21+
int CAL_FORCE = 0;
22+
int NSPIN = 1;
23+
}
24+
25+
namespace GlobalC
26+
{
27+
Exx_Info exx_info;
28+
}
29+
30+
class XCTest_SCANL_Laplacian : public XCTest
31+
{
32+
protected:
33+
double e_base, v1_base, v2_base, v3_base;
34+
double e_modified, v1_modified, v2_modified, v3_modified;
35+
double e_scaled, v1_scaled, v2_scaled, v3_scaled;
36+
37+
void SetUp()
38+
{
39+
XC_Functional::set_xc_type("SCAN");
40+
41+
const double rho = 0.17E+01;
42+
const double grho = 0.81E-11;
43+
const double tau = 0.02403590412;
44+
const double lapl_base = 0.15E+01;
45+
double hybrid_alpha = 0.0;
46+
47+
XC_Functional_Libxc::tau_xc(
48+
XC_Functional::get_func_id(),
49+
rho, grho, lapl_base, tau,
50+
e_base, v1_base, v2_base, v3_base, hybrid_alpha
51+
);
52+
53+
XC_Functional_Libxc::tau_xc(
54+
XC_Functional::get_func_id(),
55+
rho, grho, lapl_base + 1.0, tau,
56+
e_modified, v1_modified, v2_modified, v3_modified, hybrid_alpha
57+
);
58+
59+
XC_Functional_Libxc::tau_xc(
60+
XC_Functional::get_func_id(),
61+
rho, grho, 2.0 * lapl_base, tau,
62+
e_scaled, v1_scaled, v2_scaled, v3_scaled, hybrid_alpha
63+
);
64+
}
65+
};
66+
67+
TEST_F(XCTest_SCANL_Laplacian, laplacian_affects_energy)
68+
{
69+
EXPECT_NE(e_base, e_modified);
70+
EXPECT_NE(e_base, e_scaled);
71+
72+
std::cout << std::scientific << std::setprecision(15);
73+
std::cout << "\n=== SCAN Laplacian Sensitivity Test ===" << std::endl;
74+
std::cout << "Base Laplacian: " << 0.15E+01 << std::endl;
75+
std::cout << " E_xc = " << e_base << std::endl;
76+
std::cout << " dE/dlapl ≈ " << (e_modified - e_base) / 1.0 << std::endl;
77+
std::cout << "Modified Laplacian:" << 0.15E+01 + 1.0 << std::endl;
78+
std::cout << " E_xc = " << e_modified << std::endl;
79+
std::cout << " Delta E = " << e_modified - e_base << std::endl;
80+
std::cout << "Scaled Laplacian: " << 2.0 * 0.15E+01 << std::endl;
81+
std::cout << " E_xc = " << e_scaled << std::endl;
82+
std::cout << " Delta E = " << e_scaled - e_base << std::endl;
83+
std::cout << "========================================" << std::endl;
84+
}

source/source_hamilt/module_xc/xc_functional.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,12 @@ class XC_Functional
229229
const ModulePW::PW_Basis* rho_basis,
230230
const double tpiba);
231231

232+
static void laplacian_rho(
233+
const std::complex<double>* rhog,
234+
double* lapl,
235+
const ModulePW::PW_Basis* rho_basis,
236+
const double tpiba);
237+
232238
static void noncolin_rho(
233239
double* rhoout1,
234240
double* rhoout2,

source/source_hamilt/module_xc/xc_grad.cpp

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ void XC_Functional::gradcorr(
9090
double* neg = nullptr;
9191
double** vsave = nullptr;
9292
double** vgg = nullptr;
93+
double* lapl1 = nullptr;
94+
double* lapl2 = nullptr;
9395

9496
// for spin unpolarized case,
9597
// calculate the gradient of (rho_core+rho) in reciprocal space.
@@ -118,6 +120,12 @@ void XC_Functional::gradcorr(
118120

119121
XC_Functional::grad_rho( rhogsum1 , gdr1, rhopw, ucell->tpiba);
120122

123+
if(func_type == 3 || func_type == 5)
124+
{
125+
lapl1 = new double[rhopw->nrxx];
126+
XC_Functional::laplacian_rho(rhogsum1, lapl1, rhopw, ucell->tpiba);
127+
}
128+
121129
// for spin polarized case;
122130
// calculate the gradient of (rho_core+rho) in reciprocal space.
123131
if(PARAM.inp.nspin==2)
@@ -146,6 +154,12 @@ void XC_Functional::gradcorr(
146154
}
147155

148156
XC_Functional::grad_rho( rhogsum2 , gdr2, rhopw, ucell->tpiba);
157+
158+
if(func_type == 3 || func_type == 5)
159+
{
160+
lapl2 = new double[rhopw->nrxx];
161+
XC_Functional::laplacian_rho(rhogsum2, lapl2, rhopw, ucell->tpiba);
162+
}
149163
}
150164

151165
if(PARAM.inp.nspin == 4&&(PARAM.globalv.domag||PARAM.globalv.domag_z))
@@ -222,6 +236,20 @@ void XC_Functional::gradcorr(
222236

223237
XC_Functional::grad_rho( rhogsum1 , gdr1, rhopw, ucell->tpiba);
224238
XC_Functional::grad_rho( rhogsum2 , gdr2, rhopw, ucell->tpiba);
239+
240+
if(func_type == 3 || func_type == 5)
241+
{
242+
if(lapl1 == nullptr)
243+
{
244+
lapl1 = new double[rhopw->nrxx];
245+
}
246+
XC_Functional::laplacian_rho(rhogsum1, lapl1, rhopw, ucell->tpiba);
247+
if(lapl2 == nullptr)
248+
{
249+
lapl2 = new double[rhopw->nrxx];
250+
}
251+
XC_Functional::laplacian_rho(rhogsum2, lapl2, rhopw, ucell->tpiba);
252+
}
225253
}
226254

227255
const double epsr = 1.0e-6;
@@ -297,7 +325,8 @@ void XC_Functional::gradcorr(
297325
#ifdef __EXX
298326
hybrid_alpha = GlobalC::exx_info.info_global.hybrid_alpha;
299327
#endif
300-
XC_Functional_Libxc::tau_xc( func_id, arho, grho2a, atau, sxc, v1xc, v2xc, v3xc, hybrid_alpha);
328+
double lapl_val = (lapl1 != nullptr) ? lapl1[ir] : 0.0;
329+
XC_Functional_Libxc::tau_xc( func_id, arho, grho2a, lapl_val, atau, sxc, v1xc, v2xc, v3xc, hybrid_alpha);
301330
}
302331
else
303332
{
@@ -366,10 +395,12 @@ void XC_Functional::gradcorr(
366395
#ifdef __EXX
367396
hybrid_alpha = GlobalC::exx_info.info_global.hybrid_alpha;
368397
#endif
398+
double laplup_val = (lapl1 != nullptr) ? lapl1[ir] : 0.0;
399+
double lapldw_val = (lapl2 != nullptr) ? lapl2[ir] : 0.0;
369400
XC_Functional_Libxc::tau_xc_spin(
370401
func_id,
371402
rhotmp1[ir], rhotmp2[ir], gdr1[ir], gdr2[ir],
372-
atau1, atau2, sxc, v1xcup, v1xcdw, v2xcup, v2xcdw, v2xcud, v3xcup, v3xcdw, hybrid_alpha);
403+
laplup_val, lapldw_val, atau1, atau2, sxc, v1xcup, v1xcdw, v2xcup, v2xcdw, v2xcud, v3xcup, v3xcdw, hybrid_alpha);
373404
}
374405
else
375406
{
@@ -653,14 +684,18 @@ void XC_Functional::gradcorr(
653684
delete[] rhotmp2;
654685
delete[] rhogsum2;
655686
delete[] gdr2;
687+
delete[] lapl2;
656688
if(!is_stress)
657689
{
658690
delete[] h2;
659691
}
692+
delete[] lapl1;
660693
}
661-
if(PARAM.inp.nspin == 4 && (PARAM.globalv.domag||PARAM.globalv.domag_z))
694+
else if(PARAM.inp.nspin == 4 && (PARAM.globalv.domag||PARAM.globalv.domag_z))
662695
{
663696
delete[] neg;
697+
delete[] lapl1;
698+
delete[] lapl2;
664699
if(!is_stress)
665700
{
666701
for(int i=0; i<nspin0; i++)
@@ -679,6 +714,10 @@ void XC_Functional::gradcorr(
679714
delete[] rhogsum2;
680715
delete[] gdr2;
681716
}
717+
else
718+
{
719+
delete[] lapl1;
720+
}
682721

683722
return;
684723
}
@@ -816,11 +855,42 @@ void XC_Functional::grad_dot(
816855
dh[ir] = aux[ir].real() * tpiba;
817856
}
818857

819-
delete[] aux;
858+
delete[] aux;
820859
delete[] gaux;
821860
return;
822861
}
823862

863+
864+
void XC_Functional::laplacian_rho(
865+
const std::complex<double>* rhog,
866+
double* lapl,
867+
const ModulePW::PW_Basis* rho_basis,
868+
const double tpiba)
869+
{
870+
std::complex<double>* lapl_tmp = new std::complex<double>[rho_basis->nmaxgr];
871+
872+
for(int ir=0; ir<rho_basis->nrxx; ir++)
873+
{
874+
lapl[ir] = 0.0;
875+
}
876+
877+
for(int i=0; i<3; i++)
878+
{
879+
for(int ig=0; ig<rho_basis->npw; ig++)
880+
{
881+
lapl_tmp[ig] = -rhog[ig] * rho_basis->gcar[ig][i] * rho_basis->gcar[ig][i];
882+
}
883+
rho_basis->recip2real(lapl_tmp, lapl_tmp);
884+
for(int ir=0; ir<rho_basis->nrxx; ir++)
885+
{
886+
lapl[ir] += lapl_tmp[ir].real() * tpiba * tpiba;
887+
}
888+
}
889+
890+
delete[] lapl_tmp;
891+
}
892+
893+
824894
void XC_Functional::noncolin_rho(
825895
double *rhoout1,
826896
double *rhoout2,

0 commit comments

Comments
 (0)