-
Notifications
You must be signed in to change notification settings - Fork 234
fix(xc): implement proper Laplacian calculation for SCAN-L functional #7457
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 4 commits
0f94387
4a546e6
37ffa57
f0e94d4
f76b1d8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| #include "../xc_functional.h" | ||
| #include "../libxc_abacus.h" | ||
| #include "gtest/gtest.h" | ||
| #include "xctest.h" | ||
| #include "../exx_info.h" | ||
| #include <cmath> | ||
| #include <iostream> | ||
| #include <iomanip> | ||
|
|
||
| namespace ModuleBase | ||
| { | ||
| void WARNING_QUIT(const std::string &file,const std::string &description) {exit(1);} | ||
| void TITLE(const std::string &class_function_name,bool disable){}; | ||
| void TITLE(const std::string &class_name,const std::string &function_name,bool disable){}; | ||
| } | ||
|
|
||
| namespace GlobalV | ||
| { | ||
| std::string BASIS_TYPE = ""; | ||
| bool CAL_STRESS = false; | ||
| int CAL_FORCE = 0; | ||
| int NSPIN = 1; | ||
| } | ||
|
|
||
| namespace GlobalC | ||
| { | ||
| Exx_Info exx_info; | ||
| } | ||
|
|
||
| class XCTest_SCANL_Laplacian : public XCTest | ||
| { | ||
| protected: | ||
| double e_base, v1_base, v2_base, v3_base; | ||
| double e_modified, v1_modified, v2_modified, v3_modified; | ||
| double e_scaled, v1_scaled, v2_scaled, v3_scaled; | ||
|
|
||
| void SetUp() | ||
| { | ||
| XC_Functional::set_xc_type("MGGA_X_SCANL+MGGA_C_SCANL"); | ||
|
|
||
| const double rho = 0.17E+01; | ||
| const double grho = 0.81E-11; | ||
| const double tau = 0.02403590412; | ||
| const double lapl_base = 0.15E+01; | ||
| double hybrid_alpha = 0.0; | ||
|
|
||
| XC_Functional_Libxc::tau_xc( | ||
| XC_Functional::get_func_id(), | ||
| rho, grho, lapl_base, tau, | ||
| e_base, v1_base, v2_base, v3_base, hybrid_alpha | ||
| ); | ||
|
|
||
| XC_Functional_Libxc::tau_xc( | ||
| XC_Functional::get_func_id(), | ||
| rho, grho, lapl_base + 1.0, tau, | ||
| e_modified, v1_modified, v2_modified, v3_modified, hybrid_alpha | ||
| ); | ||
|
|
||
| XC_Functional_Libxc::tau_xc( | ||
| XC_Functional::get_func_id(), | ||
| rho, grho, 2.0 * lapl_base, tau, | ||
| e_scaled, v1_scaled, v2_scaled, v3_scaled, hybrid_alpha | ||
| ); | ||
| } | ||
| }; | ||
|
|
||
| TEST_F(XCTest_SCANL_Laplacian, laplacian_affects_energy) | ||
| { | ||
| EXPECT_NE(e_base, e_modified); | ||
| EXPECT_NE(e_base, e_scaled); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This only asserts the Laplacian changes the energy ( |
||
|
|
||
| std::cout << std::scientific << std::setprecision(15); | ||
| std::cout << "\n=== SCAN Laplacian Sensitivity Test ===" << std::endl; | ||
| std::cout << "Base Laplacian: " << 0.15E+01 << std::endl; | ||
| std::cout << " E_xc = " << e_base << std::endl; | ||
| std::cout << " dE/dlapl ≈ " << (e_modified - e_base) / 1.0 << std::endl; | ||
| std::cout << "Modified Laplacian:" << 0.15E+01 + 1.0 << std::endl; | ||
| std::cout << " E_xc = " << e_modified << std::endl; | ||
| std::cout << " Delta E = " << e_modified - e_base << std::endl; | ||
| std::cout << "Scaled Laplacian: " << 2.0 * 0.15E+01 << std::endl; | ||
| std::cout << " E_xc = " << e_scaled << std::endl; | ||
| std::cout << " Delta E = " << e_scaled - e_base << std::endl; | ||
| std::cout << "========================================" << std::endl; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -90,6 +90,8 @@ void XC_Functional::gradcorr( | |
| double* neg = nullptr; | ||
| double** vsave = nullptr; | ||
| double** vgg = nullptr; | ||
| double* lapl1 = nullptr; | ||
| double* lapl2 = nullptr; | ||
|
|
||
| // for spin unpolarized case, | ||
| // calculate the gradient of (rho_core+rho) in reciprocal space. | ||
|
|
@@ -118,6 +120,12 @@ void XC_Functional::gradcorr( | |
|
|
||
| XC_Functional::grad_rho( rhogsum1 , gdr1, rhopw, ucell->tpiba); | ||
|
|
||
| if(func_type == 3 || func_type == 5) | ||
| { | ||
| lapl1 = new double[rhopw->nrxx]; | ||
| XC_Functional::laplacian_rho(rhogsum1, lapl1, rhopw, ucell->tpiba); | ||
| } | ||
|
|
||
| // for spin polarized case; | ||
| // calculate the gradient of (rho_core+rho) in reciprocal space. | ||
| if(PARAM.inp.nspin==2) | ||
|
|
@@ -146,6 +154,12 @@ void XC_Functional::gradcorr( | |
| } | ||
|
|
||
| XC_Functional::grad_rho( rhogsum2 , gdr2, rhopw, ucell->tpiba); | ||
|
|
||
| if(func_type == 3 || func_type == 5) | ||
| { | ||
| lapl2 = new double[rhopw->nrxx]; | ||
| XC_Functional::laplacian_rho(rhogsum2, lapl2, rhopw, ucell->tpiba); | ||
| } | ||
| } | ||
|
|
||
| if(PARAM.inp.nspin == 4&&(PARAM.globalv.domag||PARAM.globalv.domag_z)) | ||
|
|
@@ -222,6 +236,20 @@ void XC_Functional::gradcorr( | |
|
|
||
| XC_Functional::grad_rho( rhogsum1 , gdr1, rhopw, ucell->tpiba); | ||
| XC_Functional::grad_rho( rhogsum2 , gdr2, rhopw, ucell->tpiba); | ||
|
|
||
| if(func_type == 3 || func_type == 5) | ||
| { | ||
| if(lapl1 == nullptr) | ||
| { | ||
| lapl1 = new double[rhopw->nrxx]; | ||
| } | ||
| XC_Functional::laplacian_rho(rhogsum1, lapl1, rhopw, ucell->tpiba); | ||
| if(lapl2 == nullptr) | ||
| { | ||
| lapl2 = new double[rhopw->nrxx]; | ||
| } | ||
| XC_Functional::laplacian_rho(rhogsum2, lapl2, rhopw, ucell->tpiba); | ||
| } | ||
| } | ||
|
|
||
| const double epsr = 1.0e-6; | ||
|
|
@@ -297,7 +325,8 @@ void XC_Functional::gradcorr( | |
| #ifdef __EXX | ||
| hybrid_alpha = GlobalC::exx_info.info_global.hybrid_alpha; | ||
| #endif | ||
| XC_Functional_Libxc::tau_xc( func_id, arho, grho2a, atau, sxc, v1xc, v2xc, v3xc, hybrid_alpha); | ||
| double lapl_val = (lapl1 != nullptr) ? lapl1[ir] : 0.0; | ||
| XC_Functional_Libxc::tau_xc( func_id, arho, grho2a, lapl_val, atau, sxc, v1xc, v2xc, v3xc, hybrid_alpha); | ||
| } | ||
| else | ||
| { | ||
|
|
@@ -366,10 +395,12 @@ void XC_Functional::gradcorr( | |
| #ifdef __EXX | ||
| hybrid_alpha = GlobalC::exx_info.info_global.hybrid_alpha; | ||
| #endif | ||
| double laplup_val = (lapl1 != nullptr) ? lapl1[ir] : 0.0; | ||
| double lapldw_val = (lapl2 != nullptr) ? lapl2[ir] : 0.0; | ||
| XC_Functional_Libxc::tau_xc_spin( | ||
| func_id, | ||
| rhotmp1[ir], rhotmp2[ir], gdr1[ir], gdr2[ir], | ||
| atau1, atau2, sxc, v1xcup, v1xcdw, v2xcup, v2xcdw, v2xcud, v3xcup, v3xcdw, hybrid_alpha); | ||
| laplup_val, lapldw_val, atau1, atau2, sxc, v1xcup, v1xcdw, v2xcup, v2xcdw, v2xcud, v3xcup, v3xcdw, hybrid_alpha); | ||
| } | ||
| else | ||
| { | ||
|
|
@@ -653,14 +684,18 @@ void XC_Functional::gradcorr( | |
| delete[] rhotmp2; | ||
| delete[] rhogsum2; | ||
| delete[] gdr2; | ||
| delete[] lapl2; | ||
| if(!is_stress) | ||
| { | ||
| delete[] h2; | ||
| } | ||
| delete[] lapl1; | ||
| } | ||
| if(PARAM.inp.nspin == 4 && (PARAM.globalv.domag||PARAM.globalv.domag_z)) | ||
| else if(PARAM.inp.nspin == 4 && (PARAM.globalv.domag||PARAM.globalv.domag_z)) | ||
| { | ||
| delete[] neg; | ||
| delete[] lapl1; | ||
| delete[] lapl2; | ||
| if(!is_stress) | ||
| { | ||
| for(int i=0; i<nspin0; i++) | ||
|
|
@@ -679,6 +714,10 @@ void XC_Functional::gradcorr( | |
| delete[] rhogsum2; | ||
| delete[] gdr2; | ||
| } | ||
| else | ||
| { | ||
| delete[] lapl1; | ||
| } | ||
|
|
||
| return; | ||
| } | ||
|
|
@@ -816,11 +855,42 @@ void XC_Functional::grad_dot( | |
| dh[ir] = aux[ir].real() * tpiba; | ||
| } | ||
|
|
||
| delete[] aux; | ||
| delete[] aux; | ||
| delete[] gaux; | ||
| return; | ||
| } | ||
|
|
||
|
|
||
| void XC_Functional::laplacian_rho( | ||
| const std::complex<double>* rhog, | ||
| double* lapl, | ||
| const ModulePW::PW_Basis* rho_basis, | ||
| const double tpiba) | ||
| { | ||
| std::complex<double>* lapl_tmp = new std::complex<double>[rho_basis->nmaxgr]; | ||
|
|
||
| for(int ir=0; ir<rho_basis->nrxx; ir++) | ||
| { | ||
| lapl[ir] = 0.0; | ||
| } | ||
|
|
||
| for(int i=0; i<3; i++) | ||
| { | ||
| for(int ig=0; ig<rho_basis->npw; ig++) | ||
| { | ||
| lapl_tmp[ig] = -rhog[ig] * rho_basis->gcar[ig][i] * rho_basis->gcar[ig][i]; | ||
| } | ||
| rho_basis->recip2real(lapl_tmp, lapl_tmp); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One small cleanup: this does 3 inverse FFTs; you can build |
||
| for(int ir=0; ir<rho_basis->nrxx; ir++) | ||
| { | ||
| lapl[ir] += lapl_tmp[ir].real() * tpiba * tpiba; | ||
| } | ||
| } | ||
|
|
||
| delete[] lapl_tmp; | ||
| } | ||
|
|
||
|
|
||
| void XC_Functional::noncolin_rho( | ||
| double *rhoout1, | ||
| double *rhoout2, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine as-is:
test_xc4uses SCAN, a τ-mGGA that ignores the Laplacian, so passinggrhohere doesn't affect the reference values. A one-line comment noting that would avoid confusion.