Skip to content

Commit fa8a108

Browse files
authored
Support initial charge and moment for FHI-aims I/O
1 parent 012b603 commit fa8a108

5 files changed

Lines changed: 121 additions & 5 deletions

File tree

doc/format-aims.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ The format is identified by:
2828
| `atom` | Cartesian coordinates in Ångström |
2929
| `atom_frac` | Fractional coordinates (periodic systems) |
3030
| `lattice_vector` | Lattice vector (3 reals, one per line) |
31+
| `initial_charge` | Global initial total molecular charge |
32+
| `initial_moment` | Global initial spin moment / unpaired-electron count |
33+
34+
The reader and writer support the optional global `initial_charge` and `initial_moment` keywords for preserving molecular charge and spin information in `geometry.in` files.
3135

3236
### Atom Specification
3337

src/mctc/io/read/aims.f90

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ module mctc_io_read_aims
2020
use mctc_io_symbols, only : symbol_length, to_number
2121
use mctc_io_structure, only : structure_type, new
2222
use mctc_io_utils, only : next_line, token_type, next_token, io_error, filename, &
23-
read_next_token, to_string
23+
read_next_token, to_lower, to_string
2424
implicit none
2525
private
2626

@@ -44,7 +44,7 @@ subroutine read_aims(mol, unit, error)
4444
integer :: stat, pos, lnum, ilt, iat
4545
type(token_type) :: token
4646
character(len=:), allocatable :: line
47-
real(wp) :: x, y, z
47+
real(wp) :: x, y, z, charge, moment
4848
character(len=symbol_length), allocatable :: sym(:)
4949
real(wp), allocatable :: xyz(:, :), abc(:, :), lattice(:, :)
5050
logical :: is_frac, periodic(3)
@@ -58,6 +58,8 @@ subroutine read_aims(mol, unit, error)
5858
iat = 0
5959
ilt = 0
6060
periodic(:) = .false.
61+
charge = 0.0_wp
62+
moment = 0.0_wp
6163

6264
lnum = 0
6365
stat = 0
@@ -68,7 +70,7 @@ subroutine read_aims(mol, unit, error)
6870
if (line(1:1) == "#") cycle
6971

7072
call next_token(line, pos, token)
71-
select case(line(token%first:token%last))
73+
select case(to_lower(line(token%first:token%last)))
7274
case("atom", "atom_frac")
7375
is_frac = token%last - token%first + 1 > 4
7476
call read_next_token(line, pos, token, x, stat)
@@ -127,6 +129,22 @@ subroutine read_aims(mol, unit, error)
127129
if (.not.allocated(lattice)) allocate(lattice(3, 3), source=0.0_wp)
128130
lattice(:, ilt) = [x, y, z] * aatoau
129131

132+
case("initial_charge")
133+
call read_next_token(line, pos, token, charge, stat)
134+
if (stat /= 0) then
135+
call io_error(error, "Cannot read initial charge", &
136+
& line, token, filename(unit), lnum, "expected real value")
137+
exit
138+
end if
139+
140+
case("initial_moment")
141+
call read_next_token(line, pos, token, moment, stat)
142+
if (stat /= 0) then
143+
call io_error(error, "Cannot read initial moment", &
144+
& line, token, filename(unit), lnum, "expected real value")
145+
exit
146+
end if
147+
130148
case default
131149
call io_error(error, "Unexpected keyword found", &
132150
& line, token, filename(unit), lnum, "invalid in this context")
@@ -149,7 +167,8 @@ subroutine read_aims(mol, unit, error)
149167
periodic(:ilt) = .true.
150168
end if
151169

152-
call new(mol, sym(:iat), xyz, lattice=lattice, periodic=periodic)
170+
call new(mol, sym(:iat), xyz, charge=charge, uhf=nint(moment), &
171+
& lattice=lattice, periodic=periodic)
153172

154173
end subroutine read_aims
155174

src/mctc/io/write/aims.f90

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ subroutine write_aims(self, unit)
3434
integer :: iat, ilt
3535
logical :: expo
3636

37+
if (self%charge /= 0.0_wp) then
38+
write(unit, '(a, 1x, es24.14)') "initial_charge", self%charge
39+
end if
40+
if (self%uhf /= 0) then
41+
write(unit, '(a, 1x, es24.14)') "initial_moment", real(self%uhf, wp)
42+
end if
43+
3744
expo = maxval(self%xyz) > 1.0e+5 .or. minval(self%xyz) < -1.0e+5
3845
if (expo) then
3946
do iat = 1, self%nat

test/test_read_aims.f90

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ subroutine collect_read_aims(testsuite)
4040
& new_unittest("valid5-aims", test_valid5_aims), &
4141
& new_unittest("valid6-aims", test_valid6_aims), &
4242
& new_unittest("valid7-aims", test_valid7_aims), &
43+
& new_unittest("valid8-aims", test_valid8_aims), &
44+
& new_unittest("valid9-aims", test_valid9_aims), &
4345
& new_unittest("invalid1-aims", test_invalid1_aims, should_fail=.true.), &
4446
& new_unittest("invalid2-aims", test_invalid2_aims, should_fail=.true.), &
4547
& new_unittest("invalid3-aims", test_invalid3_aims, should_fail=.true.), &
@@ -355,6 +357,61 @@ end subroutine test_valid7_aims
355357

356358

357359

360+
subroutine test_valid8_aims(error)
361+
362+
!> Error handling
363+
type(error_type), allocatable, intent(out) :: error
364+
365+
type(structure_type) :: struc
366+
integer :: unit
367+
368+
open(status='scratch', newunit=unit)
369+
write(unit, '(a)') &
370+
"initial_charge 1.0", &
371+
"initial_moment 2.0", &
372+
"atom 0.0 0.0 0.0 H", &
373+
"atom 0.0 0.0 0.0 He"
374+
rewind(unit)
375+
376+
call read_aims(struc, unit, error)
377+
close(unit)
378+
if (allocated(error)) return
379+
380+
call check(error, struc%charge, 1.0_wp, "Charge does not match")
381+
if (allocated(error)) return
382+
call check(error, struc%uhf, 2, "Spin information does not match")
383+
if (allocated(error)) return
384+
385+
end subroutine test_valid8_aims
386+
387+
388+
subroutine test_valid9_aims(error)
389+
390+
!> Error handling
391+
type(error_type), allocatable, intent(out) :: error
392+
393+
type(structure_type) :: struc
394+
integer :: unit
395+
396+
open(status='scratch', newunit=unit)
397+
write(unit, '(a)') &
398+
"initial_charge -1.0", &
399+
"initial_moment -2.0", &
400+
"atom 0.0 0.0 0.0 H"
401+
rewind(unit)
402+
403+
call read_aims(struc, unit, error)
404+
close(unit)
405+
if (allocated(error)) return
406+
407+
call check(error, struc%charge, -1.0_wp, "Charge does not match")
408+
if (allocated(error)) return
409+
call check(error, struc%uhf, -2, "Spin information does not match")
410+
if (allocated(error)) return
411+
412+
end subroutine test_valid9_aims
413+
414+
358415
subroutine test_invalid1_aims(error)
359416

360417
!> Error handling

test/test_write_aims.f90

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
! limitations under the License.
1414

1515
module test_write_aims
16+
use mctc_env, only : wp
1617
use mctc_env_testing, only : new_unittest, unittest_type, error_type, check
1718
use testsuite_structure, only : get_structure
1819
use mctc_io_write_aims
@@ -35,7 +36,8 @@ subroutine collect_write_aims(testsuite)
3536

3637
testsuite = [ &
3738
& new_unittest("valid1-aims", test_valid1_aims), &
38-
& new_unittest("valid2-aims", test_valid2_aims) &
39+
& new_unittest("valid2-aims", test_valid2_aims), &
40+
& new_unittest("valid3-aims", test_valid3_aims) &
3941
& ]
4042

4143
end subroutine collect_write_aims
@@ -97,4 +99,31 @@ subroutine test_valid2_aims(error)
9799
end subroutine test_valid2_aims
98100

99101

102+
subroutine test_valid3_aims(error)
103+
104+
!> Error handling
105+
type(error_type), allocatable, intent(out) :: error
106+
107+
type(structure_type) :: struc
108+
integer :: unit
109+
110+
call new(struc, [6, 1], ["C", "H"], reshape([0.0_wp, 0.0_wp, 0.0_wp, 0.5_wp, 0.0_wp, 0.0_wp], [3, 2]), &
111+
& charge=1.0_wp, uhf=2)
112+
113+
open(status='scratch', newunit=unit)
114+
call write_aims(struc, unit)
115+
rewind(unit)
116+
117+
call read_aims(struc, unit, error)
118+
close(unit)
119+
if (allocated(error)) return
120+
121+
call check(error, struc%charge, 1.0_wp, "Charge does not match")
122+
if (allocated(error)) return
123+
call check(error, struc%uhf, 2, "Spin information does not match")
124+
if (allocated(error)) return
125+
126+
end subroutine test_valid3_aims
127+
128+
100129
end module test_write_aims

0 commit comments

Comments
 (0)