Skip to content

Commit 69a663f

Browse files
authored
Add optional DFT-D4 support (#7380)
* Add optional DFT-D4 support * Docs and tests * Install dftd4 from toolchain in GitHub test * Fix stress calculation * Add regtest * Add D4S model * Add citations
1 parent 3950da7 commit 69a663f

40 files changed

Lines changed: 940 additions & 27 deletions

.github/workflows/test.yml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ concurrency:
77
group: ${{ github.workflow }}-${{ github.ref }}
88
cancel-in-progress: true
99

10+
defaults:
11+
run:
12+
shell: bash
13+
1014
jobs:
1115
test:
1216
name: Test
@@ -32,12 +36,20 @@ jobs:
3236
- name: Install CI tools
3337
run: |
3438
sudo apt-get update
35-
sudo apt-get install -y ccache ca-certificates python-is-python3 python3-pip
39+
sudo apt-get install -y gfortran ccache ca-certificates python-is-python3 python3-pip
3640
sudo pip install clang-format clang-tidy
3741
42+
- name: Install dftd4 from toolchain
43+
run: |
44+
cd toolchain
45+
./install_abacus_toolchain_new.sh --with-dftd4=install --dry-run -j8
46+
./scripts/stage4/install_dftd4.sh
47+
cd ..
48+
3849
- name: Configure
3950
run: |
40-
cmake -B build -DBUILD_TESTING=ON -DENABLE_MLALGO=ON -DENABLE_LIBXC=ON -DENABLE_LIBRI=ON -DENABLE_GOOGLEBENCH=ON -DENABLE_RAPIDJSON=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DENABLE_FLOAT_FFTW=ON
51+
source toolchain/install/setup
52+
cmake -B build -DBUILD_TESTING=ON -DENABLE_MLALGO=ON -DENABLE_LIBXC=ON -DENABLE_LIBRI=ON -DENABLE_GOOGLEBENCH=ON -DENABLE_RAPIDJSON=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DENABLE_FLOAT_FFTW=ON -DENABLE_DFTD4=ON
4153
4254
# Temporarily removed because no one maintains this now.
4355
# And it will break the CI test workflow.

CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ option(USE_ELPA "Enable ELPA for LCAO" ON)
3434
option(ENABLE_LIBRI "Enable LibRI for hybrid functional" OFF)
3535
option(ENABLE_LIBCOMM "Enable LibComm" OFF)
3636
option(ENABLE_PEXSI "Enable PEXSI for LCAO" OFF)
37+
option(ENABLE_DFTD4 "Enable DFT-D4 dispersion correction" OFF)
3738

3839
option(BUILD_TESTING "Build unittests" OFF)
3940
option(DEBUG_INFO "Print message to debug" OFF)
@@ -236,6 +237,18 @@ if(ENABLE_COVERAGE)
236237
add_coverage(${ABACUS_BIN_NAME})
237238
endif()
238239

240+
if(ENABLE_DFTD4)
241+
# DFTD4 requires enabling C and Fortran to work
242+
enable_language(C)
243+
enable_language(Fortran)
244+
# Avoid custom-lapack fallback when resolving DFT-D4 dependencies
245+
find_package(BLAS REQUIRED)
246+
find_package(LAPACK REQUIRED)
247+
find_package(dftd4 4.2.0 REQUIRED)
248+
if(NOT TARGET dftd4::dftd4)
249+
message(FATAL_ERROR "DFT-D4 was found, but target dftd4::dftd4 is missing.")
250+
endif()
251+
endif()
239252

240253
macro(set_if_higher VARIABLE VALUE)
241254
if(${VARIABLE} LESS ${VALUE})

docs/CITATIONS.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,11 @@ The following references are required to be cited when using ABACUS. Specificall
4747
Sun, Liang, and Mohan Chen. "Machine learning based nonlocal kinetic energy density functional for simple metals and alloys." Physical Review B 109.11 (2024): 115135.
4848

4949
Sun, Liang, and Mohan Chen. "Multi-channel machine learning based nonlocal kinetic energy density functional for semiconductors." Electronic Structure 6.4 (2024): 045006.
50+
51+
- **If DFT-D4 dispersion correction is used:**
52+
53+
Eike Caldeweyher, Sebastian Ehlert, Andreas Hansen, Hagen Neugebauer, Sebastian Spicher, Christoph Bannwarth, and Stefan Grimme. "A generally applicable atomic-charge dependent London dispersion correction." The Journal of Chemical Physics 150, 154122 (2019). DOI: 10.1063/1.5090222.
54+
55+
Eike Caldeweyher, Jan-Michael Mewes, Sebastian Ehlert, and Stefan Grimme. "Extension and evaluation of the D4 London-dispersion model for periodic systems." Physical Chemistry Chemical Physics 22, 8499-8512 (2020). DOI: 10.1039/D0CP00502A.
56+
57+
Nikolay V. Tkachenko, Linus B. Dittmer, Rebecca Tomann, and Martin Head-Gordon. "Smooth D4S model." The Journal of Physical Chemistry Letters 15, 10629-10637 (2024). DOI: 10.1021/acs.jpclett.4c02653.

docs/advanced/input_files/input-main.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,8 @@
396396
- [sc\_scf\_thr](#sc_scf_thr)
397397
- [vdW correction](#vdw-correction)
398398
- [vdw\_method](#vdw_method)
399+
- [vdw\_d4\_xc](#vdw_d4_xc)
400+
- [vdw\_d4\_model](#vdw_d4_model)
399401
- [vdw\_s6](#vdw_s6)
400402
- [vdw\_s8](#vdw_s8)
401403
- [vdw\_a1](#vdw_a1)
@@ -3640,11 +3642,28 @@
36403642
- d2: Grimme's D2 dispersion correction method
36413643
- d3_0: Grimme's DFT-D3(0) dispersion correction method (zero-damping)
36423644
- d3_bj: Grimme's DFTD3(BJ) dispersion correction method (BJ-damping)
3645+
- d4: Grimme's DFT-D4 dispersion correction method using the external DFT-D4 library
36433646
- none: no vdW correction
36443647

36453648
> Note: ABACUS supports automatic setting of DFT-D3 parameters for common functionals. To benefit from this feature, please specify the parameter dft_functional explicitly, otherwise the autoset procedure will crash. If not satisfied with the built-in parameters, any manual setting on vdw_s6, vdw_s8, vdw_a1 and vdw_a2 will overwrite the automatic values.
3649+
3650+
> Note: DFT-D4 support requires ABACUS to be configured with ENABLE_DFTD4=ON and a CMake-installed dftd4 library exporting dftd4-config.cmake. DFT-D4 damping parameters are loaded from the external library.
36463651
- **Default**: none
36473652

3653+
### vdw_d4_xc
3654+
3655+
- **Type**: String
3656+
- **Availability**: *vdw_method is set to d4*
3657+
- **Description**: Functional name passed to the DFT-D4 library to load its internal damping parameters. If set to default, ABACUS infers the functional name from dft_functional or pseudopotential metadata.
3658+
- **Default**: default
3659+
3660+
### vdw_d4_model
3661+
3662+
- **Type**: String
3663+
- **Availability**: *vdw_method is set to d4*
3664+
- **Description**: DFT-D4 dispersion model used by the external DFT-D4 library. Available options are d4 for the standard D4 model and d4s for the smooth D4S model.
3665+
- **Default**: d4
3666+
36483667
### vdw_s6
36493668

36503669
- **Type**: String
@@ -3737,7 +3756,7 @@
37373756

37383757
- **Type**: String
37393758
- **Availability**: *vdw_cutoff_type is set to radius*
3740-
- **Description**: Defines the radius of the cutoff sphere when vdw_cutoff_type is set to radius. The default values depend on the chosen vdw_method.
3759+
- **Description**: Defines the cutoff radius when vdw_cutoff_type is set to radius. The default values depend on the chosen vdw_method. For DFT-D4, this controls the two-body dispersion cutoff, while the three-body cutoff is internally limited to the DFT-D4 default value of 40 Bohr.
37413760
- **Unit**: defined by vdw_radius_unit (default Bohr)
37423761

37433762
### vdw_radius_unit
@@ -3759,8 +3778,8 @@
37593778
### vdw_cn_thr
37603779

37613780
- **Type**: Real
3762-
- **Availability**: *vdw_method is set to d3_0 or d3_bj*
3763-
- **Description**: The cutoff radius when calculating coordination numbers.
3781+
- **Availability**: *vdw_method is set to d3_0, d3_bj, or d4*
3782+
- **Description**: The cutoff radius when calculating coordination numbers. The default is 40 Bohr for DFT-D3 and 30 Bohr for DFT-D4.
37643783
- **Default**: 40
37653784
- **Unit**: defined by vdw_cn_thr_unit (default: Bohr)
37663785

docs/advanced/install.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,25 @@ These two libraries are added as submodules in the [deps](https://github.com/dee
7777

7878
If you prefer using manually downloaded libraries, provide `-DLIBRI_DIR=${path to your LibRI folder} -DLIBCOMM_DIR=${path to your LibComm folder}`.
7979

80+
81+
## Build with DFT-D4 support
82+
83+
ABACUS can use the external [DFT-D4](https://github.com/dftd4/dftd4) library for Grimme's DFT-D4 dispersion correction. DFT-D4 support is optional and disabled by default.
84+
85+
DFT-D4 must be built and installed with CMake, so that ABACUS can locate it through the exported `dftd4-config.cmake` package. Meson-built installations, including the Conda package of `dftd4`, are not supported unless they provide a compatible CMake package file.
86+
87+
To build ABACUS with DFT-D4 support, pass `-DENABLE_DFTD4=ON` to CMake and provide `CMAKE_PREFIX_PATH` environment variable.
88+
89+
In the input file, enable DFT-D4 with:
90+
91+
```text
92+
vdw_method d4
93+
vdw_d4_xc pbe
94+
vdw_d4_model d4 # or d4s for the smooth D4S model
95+
```
96+
97+
If `vdw_d4_xc` is set to `default`, ABACUS will infer the functional name from `dft_functional` or pseudopotential metadata and pass it to the DFT-D4 library. The `vdw_d4_model` keyword selects the dispersion model inside the DFT-D4 library; the default is `d4`, while `d4s` enables the smooth D4S model.
98+
8099
## Build Unit Tests
81100

82101
To build tests for ABACUS, define `BUILD_TESTING` flag. You can also specify path to local installation of [Googletest](https://github.com/google/googletest) by setting `GTEST_DIR` flags. If not found in local, the configuration process will try to download it automatically.

docs/parameters.yaml

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3738,12 +3738,31 @@ parameters:
37383738
* d2: Grimme's D2 dispersion correction method
37393739
* d3_0: Grimme's DFT-D3(0) dispersion correction method (zero-damping)
37403740
* d3_bj: Grimme's DFTD3(BJ) dispersion correction method (BJ-damping)
3741+
* d4: Grimme's DFT-D4 dispersion correction method using the external DFT-D4 library
37413742
* none: no vdW correction
37423743
37433744
[NOTE] ABACUS supports automatic setting of DFT-D3 parameters for common functionals. To benefit from this feature, please specify the parameter dft_functional explicitly, otherwise the autoset procedure will crash. If not satisfied with the built-in parameters, any manual setting on vdw_s6, vdw_s8, vdw_a1 and vdw_a2 will overwrite the automatic values.
3745+
3746+
[NOTE] DFT-D4 support requires ABACUS to be configured with ENABLE_DFTD4=ON and a CMake-installed dftd4 library exporting `dftd4-config.cmake`. DFT-D4 damping parameters are loaded from the external library.
37443747
default_value: none
37453748
unit: ""
37463749
availability: ""
3750+
- name: vdw_d4_xc
3751+
category: vdW correction
3752+
type: String
3753+
description: |
3754+
Functional name passed to the DFT-D4 library to load its internal damping parameters. If set to default, ABACUS infers the functional name from dft_functional or pseudopotential metadata.
3755+
default_value: default
3756+
unit: ""
3757+
availability: vdw_method is set to d4
3758+
- name: vdw_d4_model
3759+
category: vdW correction
3760+
type: String
3761+
description: |
3762+
DFT-D4 dispersion model used by the external DFT-D4 library. Available options are d4 for the standard D4 model and d4s for the smooth D4S model.
3763+
default_value: d4
3764+
unit: ""
3765+
availability: vdw_method is set to d4
37473766
- name: vdw_s6
37483767
category: vdW correction
37493768
type: String
@@ -3852,7 +3871,7 @@ parameters:
38523871
category: vdW correction
38533872
type: String
38543873
description: |
3855-
Defines the radius of the cutoff sphere when vdw_cutoff_type is set to radius. The default values depend on the chosen vdw_method.
3874+
Defines the cutoff radius when vdw_cutoff_type is set to radius. The default values depend on the chosen vdw_method. For DFT-D4, this controls the two-body dispersion cutoff, while the three-body cutoff is internally limited to the DFT-D4 default value of 40 Bohr.
38563875
default_value: ""
38573876
unit: defined by vdw_radius_unit (default Bohr)
38583877
availability: vdw_cutoff_type is set to radius
@@ -3878,10 +3897,10 @@ parameters:
38783897
category: vdW correction
38793898
type: Real
38803899
description: |
3881-
The cutoff radius when calculating coordination numbers.
3900+
The cutoff radius when calculating coordination numbers. The default is 40 Bohr for DFT-D3 and 30 Bohr for DFT-D4.
38823901
default_value: "40"
38833902
unit: "defined by vdw_cn_thr_unit (default: Bohr)"
3884-
availability: vdw_method is set to d3_0 or d3_bj
3903+
availability: vdw_method is set to d3_0, d3_bj, or d4
38853904
- name: vdw_cn_thr_unit
38863905
category: vdW correction
38873906
type: String

source/source_estate/elecstate_print.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,11 @@ void print_etot(const Magnetism& magnet,
250250
titles.push_back("E_vdwD3");
251251
energies_Ry.push_back(elec.f_en.evdw);
252252
}
253+
else if (vdw_method == "d4")
254+
{
255+
titles.push_back("E_vdwD4");
256+
energies_Ry.push_back(elec.f_en.evdw);
257+
}
253258

254259
// mohan add 20251108
255260
if (PARAM.inp.dft_plus_u)
Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
1-
add_library(
2-
vdw
3-
OBJECT
4-
vdwd2_parameters.cpp
5-
vdwd3_parameters_tab.cpp
6-
vdwd3_parameters.cpp
7-
vdwd2.cpp
8-
vdwd3.cpp
9-
vdwd3_autoset_xcname.cpp
10-
vdwd3_autoset_xcparam.cpp
11-
vdw.cpp
1+
set(vdw_sources
2+
vdwd2_parameters.cpp
3+
vdwd3_parameters_tab.cpp
4+
vdwd3_parameters.cpp
5+
vdwd2.cpp
6+
vdwd3.cpp
7+
vdwd3_autoset_xcname.cpp
8+
vdwd3_autoset_xcparam.cpp
9+
vdw.cpp
1210
)
1311

12+
if(ENABLE_DFTD4)
13+
list(APPEND vdw_sources vdwd4.cpp)
14+
endif()
15+
16+
add_library(vdw OBJECT ${vdw_sources})
17+
18+
if(ENABLE_DFTD4)
19+
target_link_libraries(vdw PUBLIC dftd4::dftd4)
20+
target_compile_definitions(vdw PUBLIC __DFTD4)
21+
endif()
22+
1423
if(ENABLE_COVERAGE)
1524
add_coverage(vdw)
1625
endif()
@@ -19,4 +28,4 @@ if(BUILD_TESTING)
1928
if(ENABLE_MPI)
2029
add_subdirectory(test)
2130
endif()
22-
endif()
31+
endif()

source/source_hamilt/module_vdw/test/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,8 @@ AddTest(
1010
LIBS parameter ${math_libs} base device vdw
1111
SOURCES vdw_test.cpp
1212
)
13+
14+
if(ENABLE_DFTD4)
15+
target_compile_definitions(MODULE_HAMILT_vdwTest PRIVATE __DFTD4)
16+
target_link_libraries(MODULE_HAMILT_vdwTest dftd4::dftd4)
17+
endif()

source/source_hamilt/module_vdw/test/vdw_test.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
#include "source_hamilt/module_vdw/vdwd3_parameters.h"
1111
#include "source_hamilt/module_vdw/vdwd2.h"
1212
#include "source_hamilt/module_vdw/vdwd3.h"
13+
#ifdef __DFTD4
14+
#include "source_hamilt/module_vdw/vdwd4.h"
15+
#endif
1316
#include "source_hamilt/module_vdw/vdw.h"
1417
#undef private
1518

@@ -604,6 +607,111 @@ TEST_F(vdwd3abcTest, D3bjGetStress)
604607
EXPECT_NEAR(stress.e33, -3.4278442125590892e-05,1e-12);
605608
}
606609

610+
#ifdef __DFTD4
611+
612+
class vdwd4Test: public testing::Test
613+
{
614+
protected:
615+
UnitCell ucell;
616+
Input_para input;
617+
618+
void SetUp(){
619+
stru_ structure{std::vector<double>{0.5,0.5,0.0,0.5,0.0,0.5,0.0,0.5,0.5},
620+
std::vector<atomtype_>{atomtype_{"Si",
621+
std::vector<std::vector<double>>{
622+
{0., 0., 0.},
623+
{0.3, 0.25, 0.25}
624+
}}}};
625+
construct_ucell(structure,ucell);
626+
627+
input.vdw_method = "d4";
628+
input.vdw_d4_xc = "pbe";
629+
input.vdw_d4_model = "d4";
630+
input.vdw_cutoff_type = "radius";
631+
input.vdw_radius_unit = "Bohr";
632+
input.vdw_cutoff_radius = "60";
633+
input.vdw_cn_thr_unit = "Bohr";
634+
input.vdw_cn_thr = 30;
635+
}
636+
637+
void TearDown(){
638+
ClearUcell(ucell);
639+
}
640+
};
641+
642+
TEST_F(vdwd4Test, D4GetEnergy)
643+
{
644+
auto vdw_solver = vdw::make_vdw(ucell, input);
645+
double ene = vdw_solver->get_energy();
646+
EXPECT_NEAR(ene, -0.04998837990336073, 1E-10);
647+
}
648+
649+
TEST_F(vdwd4Test, D4GetForce)
650+
{
651+
auto vdw_solver = vdw::make_vdw(ucell, input);
652+
std::vector<ModuleBase::Vector3<double>> force = vdw_solver->get_force();
653+
EXPECT_NEAR(force[0].x, -0.0023357259921368717, 1e-12);
654+
EXPECT_NEAR(force[0].y, 0.0, 1e-12);
655+
EXPECT_NEAR(force[0].z, 0.0, 1e-12);
656+
EXPECT_NEAR(force[1].x, 0.0023357259921368730, 1e-12);
657+
EXPECT_NEAR(force[1].y, 0.0, 1e-12);
658+
EXPECT_NEAR(force[1].z, 0.0, 1e-12);
659+
}
660+
661+
TEST_F(vdwd4Test, D4GetStress)
662+
{
663+
auto vdw_solver = vdw::make_vdw(ucell, input);
664+
ModuleBase::Matrix3 stress = vdw_solver->get_stress();
665+
EXPECT_NEAR(stress.e11, 0.00015830384474877792, 1e-12);
666+
EXPECT_NEAR(stress.e12, 0.0, 1e-12);
667+
EXPECT_NEAR(stress.e13, 0.0, 1e-12);
668+
EXPECT_NEAR(stress.e21, 0.0, 1e-12);
669+
EXPECT_NEAR(stress.e22, 0.00016694998515968720, 1e-12);
670+
EXPECT_NEAR(stress.e23, -1.5500973166318808e-05, 1e-12);
671+
EXPECT_NEAR(stress.e31, 0.0, 1e-12);
672+
EXPECT_NEAR(stress.e32, -1.5500973166318808e-05, 1e-12);
673+
EXPECT_NEAR(stress.e33, 0.00016694998515968726, 1e-12);
674+
}
675+
676+
TEST_F(vdwd4Test, D4SGetEnergy)
677+
{
678+
input.vdw_d4_model = "d4s";
679+
auto vdw_solver = vdw::make_vdw(ucell, input);
680+
double ene = vdw_solver->get_energy();
681+
EXPECT_NEAR(ene, -0.05638517144755526, 1E-10);
682+
}
683+
684+
TEST_F(vdwd4Test, D4SGetForce)
685+
{
686+
input.vdw_d4_model = "d4s";
687+
auto vdw_solver = vdw::make_vdw(ucell, input);
688+
std::vector<ModuleBase::Vector3<double>> force = vdw_solver->get_force();
689+
EXPECT_NEAR(force[0].x, -0.005448661796788402, 1e-12);
690+
EXPECT_NEAR(force[0].y, 0.0, 1e-12);
691+
EXPECT_NEAR(force[0].z, 0.0, 1e-12);
692+
EXPECT_NEAR(force[1].x, 0.005448661796788397, 1e-12);
693+
EXPECT_NEAR(force[1].y, 0.0, 1e-12);
694+
EXPECT_NEAR(force[1].z, 0.0, 1e-12);
695+
}
696+
697+
TEST_F(vdwd4Test, D4SGetStress)
698+
{
699+
input.vdw_d4_model = "d4s";
700+
auto vdw_solver = vdw::make_vdw(ucell, input);
701+
ModuleBase::Matrix3 stress = vdw_solver->get_stress();
702+
EXPECT_NEAR(stress.e11, 0.00013831119855416262, 1e-12);
703+
EXPECT_NEAR(stress.e12, 0.0, 1e-12);
704+
EXPECT_NEAR(stress.e13, 0.0, 1e-12);
705+
EXPECT_NEAR(stress.e21, 0.0, 1e-12);
706+
EXPECT_NEAR(stress.e22, 0.00015770515797834415, 1e-12);
707+
EXPECT_NEAR(stress.e23, -3.862972112000666e-05, 1e-12);
708+
EXPECT_NEAR(stress.e31, 0.0, 1e-12);
709+
EXPECT_NEAR(stress.e32, -3.862972112000666e-05, 1e-12);
710+
EXPECT_NEAR(stress.e33, 0.00015770515797834423, 1e-12);
711+
}
712+
713+
#endif // __DFTD4
714+
607715
int main(int argc, char **argv)
608716
{
609717
MPI_Init(&argc, &argv);

0 commit comments

Comments
 (0)