Skip to content

Commit 4f3ce3d

Browse files
thfroitzheimlmseidlermarvinfriede
authored
EEQ-BC Implementation (#56)
This PR contains the implementation of the EEQ-BC charge model (see [here](https://doi.org/10.1063/5.0268978)). The implementation contains full support for - electrostatic energy with gradients and stress tensor - atomic partial charges with gradients and lattice vector derivatives - both molecular and periodic calculations EEQ-BC is implemented as an alternative model. The general solution of the linear system remains unchanged. The new capacitance matrix is handled in the cache and modifies only the final A-matrix and b-vector. Additionally, we added explicit routines to calculate the EEQ and EEQ-BC charges, avoiding the through DFT-D4. --------- Co-authored-by: Polt <[email protected]> Co-authored-by: Leopold Seidler <[email protected]> Co-authored-by: Marvin Friede <[email protected]>
1 parent 5f95ef3 commit 4f3ce3d

27 files changed

+4930
-757
lines changed

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![CI](https://github.com/grimme-lab/multicharge/workflows/CI/badge.svg)](https://github.com/grimme-lab/multicharge/actions)
66
[![codecov](https://codecov.io/gh/grimme-lab/multicharge/branch/main/graph/badge.svg)](https://codecov.io/gh/grimme-lab/multicharge)
77

8-
Electronegativity equilibration model for atomic partial charges.
8+
Electronegativity equilibration models for atomic partial charges.
99

1010

1111
## Installation
@@ -135,6 +135,33 @@ multicharge.git = "https://github.com/grimme-lab/multicharge"
135135

136136
For an overview over all command line arguments use the ``--help`` argument or checkout the [``multicharge(1)``](man/multicharge.1.adoc) manpage.
137137

138+
## Citation
139+
140+
For the electronegativity equilibration model (EEQ):
141+
142+
Eike Caldeweyher, Sebastian Ehlert, Andreas Hansen, Hagen Neugebauer, Sebastian Spicher, Christoph Bannwarth and Stefan Grimme, *J. Chem Phys*, **2019**, 150, 154122.
143+
DOI: [10.1063/1.5090222](https://doi.org/10.1063/1.5090222)
144+
chemrxiv: [10.26434/chemrxiv.7430216](https://doi.org/10.26434/chemrxiv.7430216.v2)
145+
146+
... the EEQ extention to Fr, Ra, and the full Actinide series:
147+
148+
Lukas Wittmann, Igor Gordiy, Marvin Friede, Benjamin Helmich-Paris, Stefan Grimme, Andreas Hansen and Markus Bursch, *Phys. Chem. Chem. Phys.*, **2024**, 26, 21379-21394.
149+
DOI: [10.1039/D4CP01514B](10.1039/D4CP01514B)
150+
151+
... the periodic EEQ implementation:
152+
153+
Eike Caldeweyher, Jan-Michael Mewes, Sebastian Ehlert and Stefan Grimme, *Phys. Chem. Chem. Phys.*, **2020**, 22, 8499-8512.
154+
DOI: [10.1039/D0CP00502A](https://doi.org/10.1039/D0CP00502A)
155+
chemrxiv: [10.26434/chemrxiv.10299428](https://doi.org/10.26434/chemrxiv.10299428.v1)
156+
157+
<br>
158+
159+
For the bond capacity electronegativity equilibration charge model (EEQ<sub>BC</sub>):
160+
161+
Thomas Froitzheim, Marcel Müller, Andreas Hansen, and Stefan Grimme, *J. Chem. Phys.*, **2025**, 162, 214109.
162+
DOI: [10.1039/10.1063/5.0268978](https://doi.org/10.1063/5.0268978)
163+
chemrxiv: [10.26434/chemrxiv-2025-1nxwg](https://doi.org/10.26434/chemrxiv-2025-1nxwg)
164+
138165

139166
## License
140167

app/main.f90

Lines changed: 69 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,42 @@
1414
! limitations under the License.
1515

1616
program main
17-
use, intrinsic :: iso_fortran_env, only : output_unit, error_unit, input_unit
18-
use mctc_env, only : error_type, fatal_error, get_argument, wp
19-
use mctc_io, only : structure_type, read_structure, filetype, get_filetype
20-
use multicharge, only : mchrg_model_type, new_eeq2019_model, &
21-
& write_ascii_model, write_ascii_properties, write_ascii_results, &
22-
& get_multicharge_version
23-
use multicharge_output, only : json_results
17+
use, intrinsic :: iso_fortran_env, only: output_unit, error_unit, input_unit
18+
use mctc_env, only: error_type, fatal_error, get_argument, wp
19+
use mctc_io, only: structure_type, read_structure, filetype, get_filetype
20+
use mctc_cutoff, only: get_lattice_points
21+
use multicharge, only: mchrg_model_type, mchrg_model, new_eeq2019_model, &
22+
& new_eeqbc2025_model, get_multicharge_version, &
23+
& write_ascii_model, write_ascii_properties, write_ascii_results
24+
use multicharge_output, only: json_results
2425
implicit none
2526
character(len=*), parameter :: prog_name = "multicharge"
2627
character(len=*), parameter :: json_output = "multicharge.json"
2728

2829
character(len=:), allocatable :: input, chargeinput
2930
integer, allocatable :: input_format
30-
integer :: stat, unit
31+
integer :: stat, unit, model_id
3132
type(error_type), allocatable :: error
3233
type(structure_type) :: mol
33-
type(mchrg_model_type) :: model
34+
class(mchrg_model_type), allocatable :: model
3435
logical :: grad, json, exist
3536
real(wp), parameter :: cn_max = 8.0_wp, cutoff = 25.0_wp
36-
real(wp), allocatable :: cn(:), dcndr(:, :, :), dcndL(:, :, :)
37+
real(wp), allocatable :: cn(:), rcov(:), trans(:, :)
38+
real(wp), allocatable :: qloc(:)
39+
real(wp), allocatable :: dcndr(:, :, :), dcndL(:, :, :), dqlocdr(:, :, :), dqlocdL(:, :, :)
3740
real(wp), allocatable :: energy(:), gradient(:, :), sigma(:, :)
38-
real(wp), allocatable :: qvec(:), dqdr(:, :, :), dqdL(:, :, :)
41+
real(wp), allocatable :: qvec(:)
42+
real(wp), allocatable :: dqdr(:, :, :), dqdL(:, :, :)
3943
real(wp), allocatable :: charge
4044

41-
call get_arguments(input, input_format, grad, charge, json, error)
45+
call get_arguments(input, model_id, input_format, grad, charge, json, error)
4246
if (allocated(error)) then
4347
write(error_unit, '(a)') error%message
4448
error stop
4549
end if
4650

4751
if (input == "-") then
48-
if (.not.allocated(input_format)) input_format = filetype%xyz
52+
if (.not. allocated(input_format)) input_format = filetype%xyz
4953
call read_structure(mol, input_unit, input_format, error)
5054
else
5155
call read_structure(mol, input, error, input_format)
@@ -76,36 +80,47 @@ program main
7680
end if
7781
end if
7882

79-
call new_eeq2019_model(mol, model, error)
80-
if(allocated(error)) then
83+
if (model_id == mchrg_model%eeq2019) then
84+
call new_eeq2019_model(mol, model, error)
85+
else if (model_id == mchrg_model%eeqbc2025) then
86+
call new_eeqbc2025_model(mol, model, error)
87+
else
88+
call fatal_error(error, "Invalid model was choosen.")
89+
end if
90+
if (allocated(error)) then
8191
write(error_unit, '(a)') error%message
8292
error stop
8393
end if
8494

8595
call write_ascii_model(output_unit, mol, model)
8696

87-
allocate(cn(mol%nat))
88-
if (grad) then
89-
allocate(dcndr(3, mol%nat, mol%nat), dcndL(3, 3, mol%nat))
90-
end if
91-
92-
call model%ncoord%get_cn(mol, cn, dcndr, dcndL)
93-
9497
allocate(energy(mol%nat), qvec(mol%nat))
9598
energy(:) = 0.0_wp
99+
100+
allocate(cn(mol%nat), qloc(mol%nat))
96101
if (grad) then
97102
allocate(gradient(3, mol%nat), sigma(3, 3))
98103
gradient(:, :) = 0.0_wp
99104
sigma(:, :) = 0.0_wp
105+
106+
allocate(dqdr(3, mol%nat, mol%nat), dqdL(3, 3, mol%nat))
107+
dqdr(:, :, :) = 0.0_wp
108+
dqdL(:, :, :) = 0.0_wp
109+
110+
allocate(dcndr(3, mol%nat, mol%nat), dcndL(3, 3, mol%nat))
111+
allocate(dqlocdr(3, mol%nat, mol%nat), dqlocdL(3, 3, mol%nat))
100112
end if
101113

102-
call model%solve(mol, error, cn, dcndr, dcndL, energy, gradient, sigma, &
103-
& qvec, dqdr, dqdL)
114+
call get_lattice_points(mol%periodic, mol%lattice, model%ncoord%cutoff, trans)
115+
call model%ncoord%get_coordination_number(mol, trans, cn, dcndr, dcndL)
116+
call model%local_charge(mol, trans, qloc, dqlocdr, dqlocdL)
117+
call model%solve(mol, error, cn, qloc, dcndr, dcndL, dqlocdr, dqlocdL, &
118+
& energy, gradient, sigma, qvec, dqdr, dqdL)
119+
104120
if (allocated(error)) then
105121
write(error_unit, '(a)') error%message
106122
error stop
107123
end if
108-
109124

110125
call write_ascii_properties(output_unit, mol, model, cn, qvec)
111126
call write_ascii_results(output_unit, mol, energy, gradient, sigma)
@@ -115,12 +130,11 @@ program main
115130
call json_results(unit, " ", energy=sum(energy), gradient=gradient, charges=qvec, cn=cn)
116131
close(unit)
117132
write(output_unit, '(a)') &
118-
"[Info] JSON dump of results written to '"// json_output //"'"
133+
"[Info] JSON dump of results written to '"//json_output//"'"
119134
end if
120135

121136
contains
122137

123-
124138
subroutine help(unit)
125139
integer, intent(in) :: unit
126140

@@ -133,7 +147,8 @@ subroutine help(unit)
133147
"higher multipole moments", &
134148
""
135149

136-
write(unit, '(2x, a, t25, a)') &
150+
write(unit, '(2x, a, t35, a)') &
151+
"-m, -model, --model <model>", "Choose the charge model", &
137152
"-i, -input, --input <format>", "Hint for the format of the input file", &
138153
"-c, -charge, --charge <value>", "Set the molecular charge", &
139154
"-g, -grad, --grad", "Evaluate molecular gradient and virial", &
@@ -145,7 +160,6 @@ subroutine help(unit)
145160

146161
end subroutine help
147162

148-
149163
subroutine version(unit)
150164
integer, intent(in) :: unit
151165
character(len=:), allocatable :: version_string
@@ -156,12 +170,15 @@ subroutine version(unit)
156170

157171
end subroutine version
158172

159-
160-
subroutine get_arguments(input, input_format, grad, charge, json, error)
173+
subroutine get_arguments(input, model_id, input_format, grad, charge, &
174+
& json, error)
161175

162176
!> Input file name
163177
character(len=:), allocatable :: input
164178

179+
!> ID of choosen model type
180+
integer, intent(out) :: model_id
181+
165182
!> Input file format
166183
integer, allocatable, intent(out) :: input_format
167184

@@ -180,6 +197,7 @@ subroutine get_arguments(input, input_format, grad, charge, json, error)
180197
integer :: iarg, narg, iostat
181198
character(len=:), allocatable :: arg
182199

200+
model_id = mchrg_model%eeq2019
183201
grad = .false.
184202
json = .false.
185203
iarg = 0
@@ -195,24 +213,39 @@ subroutine get_arguments(input, input_format, grad, charge, json, error)
195213
call version(output_unit)
196214
stop
197215
case default
198-
if (.not.allocated(input)) then
216+
if (.not. allocated(input)) then
199217
call move_alloc(arg, input)
200218
cycle
201219
end if
202220
call fatal_error(error, "Too many positional arguments present")
203221
exit
222+
case("-m", "-model", "--model")
223+
iarg = iarg + 1
224+
call get_argument(iarg, arg)
225+
if (.not. allocated(arg)) then
226+
call fatal_error(error, "Missing argument for model")
227+
exit
228+
end if
229+
if (arg == "eeq2019" .or. arg == "eeq") then
230+
model_id = mchrg_model%eeq2019
231+
else if (arg == "eeqbc2025" .or. arg == "eeqbc") then
232+
model_id = mchrg_model%eeqbc2025
233+
else
234+
call fatal_error(error, "Invalid model")
235+
exit
236+
end if
204237
case("-i", "-input", "--input")
205238
iarg = iarg + 1
206239
call get_argument(iarg, arg)
207-
if (.not.allocated(arg)) then
240+
if (.not. allocated(arg)) then
208241
call fatal_error(error, "Missing argument for input format")
209242
exit
210243
end if
211244
input_format = get_filetype("."//arg)
212245
case("-c", "-charge", "--charge")
213246
iarg = iarg + 1
214247
call get_argument(iarg, arg)
215-
if (.not.allocated(arg)) then
248+
if (.not. allocated(arg)) then
216249
call fatal_error(error, "Missing argument for charge")
217250
exit
218251
end if
@@ -229,8 +262,8 @@ subroutine get_arguments(input, input_format, grad, charge, json, error)
229262
end select
230263
end do
231264

232-
if (.not.allocated(input)) then
233-
if (.not.allocated(error)) then
265+
if (.not. allocated(input)) then
266+
if (.not. allocated(error)) then
234267
call help(output_unit)
235268
error stop
236269
end if

man/multicharge.1.adoc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,21 @@ Electronegativity equilibration model for atomic partial charges.
1414

1515
== Options
1616

17+
*-m, -model, --model* _model_::
18+
Choose the charge model (eeq or eeqbc)
19+
1720
*-i, -input, --input* _format_::
1821
Hint for the format of the input file
1922

2023
*-c, -charge, --charge* _value_::
2124
Provide the molecular charge
2225

23-
*-j, -json, --json*::
24-
Provide output in JSON format to the file 'multicharge.json'
25-
2626
*-g, -grad, --grad*::
2727
Evaluate molecular gradient and virial
2828

29+
*-j, -json, --json*::
30+
Provide output in JSON format to the file 'multicharge.json'
31+
2932
*-v, -version, --version*::
3033
Print program version and exit
3134

src/multicharge.f90

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
! limitations under the License.
1515

1616
module multicharge
17-
use multicharge_cutoff, only : get_lattice_points
17+
use multicharge_charge, only : get_charges, get_eeq_charges, get_eeqbc_charges
1818
use multicharge_model, only : mchrg_model_type
1919
use multicharge_output, only : write_ascii_model, write_ascii_properties, &
2020
& write_ascii_results
21-
use multicharge_param, only : new_eeq2019_model
21+
use multicharge_param, only : new_eeq2019_model, new_eeqbc2025_model, mchrg_model
2222
use multicharge_version, only : get_multicharge_version
2323
implicit none
2424
public

src/multicharge/CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,23 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16+
add_subdirectory("model")
1617
add_subdirectory("param")
1718

1819
set(dir "${CMAKE_CURRENT_SOURCE_DIR}")
1920

2021
list(
2122
APPEND srcs
2223
"${dir}/blas.F90"
23-
"${dir}/cutoff.f90"
24+
"${dir}/charge.f90"
2425
"${dir}/ewald.f90"
2526
"${dir}/lapack.F90"
26-
"${dir}/model.F90"
27+
"${dir}/model.f90"
2728
"${dir}/output.f90"
2829
"${dir}/param.f90"
2930
"${dir}/version.f90"
3031
"${dir}/wignerseitz.f90"
32+
"${dir}/cache.f90"
3133
)
3234

3335
set(srcs "${srcs}" PARENT_SCOPE)

src/multicharge/cache.f90

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
! This file is part of multicharge.
2+
! SPDX-Identifier: Apache-2.0
3+
!
4+
! Licensed under the Apache License, Version 2.0 (the "License");
5+
! you may not use this file except in compliance with the License.
6+
! You may obtain a copy of the License at
7+
!
8+
! http://www.apache.org/licenses/LICENSE-2.0
9+
!
10+
! Unless required by applicable law or agreed to in writing, software
11+
! distributed under the License is distributed on an "AS IS" BASIS,
12+
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
! See the License for the specific language governing permissions and
14+
! limitations under the License.
15+
16+
!> @file multicharge/cache.f90
17+
!> Contains the cache baseclass for the charge models and a container for mutable cache data
18+
19+
!> Cache for charge models
20+
module multicharge_model_cache
21+
use mctc_env, only: wp
22+
use mctc_io, only: structure_type
23+
use multicharge_wignerseitz, only: wignerseitz_cell_type
24+
implicit none
25+
private
26+
27+
type, public :: cache_container
28+
!> Mutable data attribute
29+
class(*), allocatable :: raw
30+
end type cache_container
31+
32+
!> Cache for the charge model
33+
type, abstract, public :: model_cache
34+
!> Coordination number array
35+
real(wp), allocatable :: cn(:)
36+
!> Coordination number gradient w.r.t the positions
37+
real(wp), allocatable :: dcndr(:, :, :)
38+
!> Coordination number gradient w.r.t the lattice vectors
39+
real(wp), allocatable :: dcndL(:, :, :)
40+
!> Ewald separation parameter
41+
real(wp) :: alpha
42+
!> Wigner-Seitz cell
43+
type(wignerseitz_cell_type) :: wsc
44+
end type model_cache
45+
46+
end module multicharge_model_cache

0 commit comments

Comments
 (0)