forked from wannier-developers/wannier90
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathws_distance.F90
377 lines (325 loc) · 15.1 KB
/
ws_distance.F90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
!-*- mode: F90 -*-!
!------------------------------------------------------------!
! This file is distributed as part of the Wannier90 code and !
! under the terms of the GNU General Public License. See the !
! file `LICENSE' in the root directory of the Wannier90 !
! distribution, or http://www.gnu.org/copyleft/gpl.txt !
! !
! The webpage of the Wannier90 code is www.wannier.org !
! !
! The Wannier90 code is hosted on GitHub: !
! !
! https://github.com/wannier-developers/wannier90 !
!------------------------------------------------------------!
! !
! ws_distance:
! Original implementation by Lorenzo Paulatto, with later !
! modifications by Marco Gibertini, Dominik Gresch !
! and Giovanni Pizzi !
! !
!------------------------------------------------------------!
module w90_ws_distance
!! This module computes the optimal Wigner-Seitz cell around each Wannier
!! function to use for interpolation.
! Short documentation follows, for a longer explanation see the documentation
! of the use_ws_distance variable in the user guide.
!
! Some comments:
! 1. This computation is done independently on all processors (when run in
! parallel). I think this shouldn't do a problem as the math is fairly simple
! and uses data already broadcasted (integer values, and the
! wannier_centres), but if there is the risk of having different
! degeneracies or similar things on different MPI processors, we should
! probably think to do the math on node 0, and then broadcast results.
use w90_constants, only: dp
use w90_error
implicit none
private
public :: clean_ws_translate
public :: ws_translate_dist
public :: ws_write_vec
integer, parameter :: ndegenx = 8
!! max number of unit cells that can touch
!! in a single point (i.e. vertex of cube)
contains
!================================================!
subroutine ws_translate_dist(ws_distance, ws_region, num_wann, wannier_centres, real_lattice, &
mp_grid, nrpts, irvec, error, comm, force_recompute)
!================================================!
!! Find the supercell translation (i.e. the translation by a integer number of
!! supercell vectors, the supercell being defined by the mp_grid) that
!! minimizes the distance between two given Wannier functions, i and j,
!! the first in unit cell 0, the other in unit cell R.
!! I.e., we find the translation to put WF j in the Wigner-Seitz of WF i.
!! We also look for the number of equivalent translation, that happen when w_j,R
!! is on the edge of the WS of w_i,0. The results are stored in global
!! arrays wdist_ndeg, irdist_ws, crdist_ws.
!================================================!
use w90_utility, only: utility_cart_to_frac, utility_frac_to_cart, utility_inverse_mat
use w90_types, only: ws_region_type, ws_distance_type
implicit none
type(ws_distance_type), intent(inout) :: ws_distance
type(ws_region_type), intent(in) :: ws_region
type(w90_error_type), allocatable, intent(out) :: error
type(w90_comm_type), intent(in) :: comm
integer, intent(in) :: mp_grid(3)
integer, intent(in) :: num_wann
integer, intent(in) :: nrpts
integer, intent(in) :: irvec(:, :)
real(kind=dp), intent(in) :: real_lattice(3, 3)
real(kind=dp), intent(in) :: wannier_centres(:, :)
logical, optional, intent(in):: force_recompute ! set to true to force recomputing everything
! local variables
real(kind=dp) :: inv_lattice(3, 3)
integer :: iw, jw, ideg, ir, ierr
integer :: shifts(3, ndegenx)
real(DP) :: irvec_cart(3), tmp(3), tmp_frac(3), R_out(3, ndegenx)
! The subroutine does nothing if called more than once, which may
! not be the best thing if you invoke it while the WFs are moving
if (present(force_recompute)) then
if (force_recompute) then
call clean_ws_translate(ws_distance)
endif
endif
if (ws_distance%done) return
ws_distance%done = .true.
if (ndegenx*num_wann*nrpts <= 0) then
call set_error_fatal(error, "unexpected dimensions in ws_translate_dist", comm)
return
end if
allocate (ws_distance%irdist(3, ndegenx, num_wann, num_wann, nrpts), stat=ierr)
if (ierr /= 0) then
call set_error_alloc(error, 'Error in allocating irdist_ws in ws_translate_dist', comm)
return
endif
allocate (ws_distance%crdist(3, ndegenx, num_wann, num_wann, nrpts), stat=ierr)
if (ierr /= 0) then
call set_error_alloc(error, 'Error in allocating crdist_ws in ws_translate_dist', comm)
return
endif
allocate (ws_distance%ndeg(num_wann, num_wann, nrpts), stat=ierr)
if (ierr /= 0) then
call set_error_alloc(error, 'Error in allocating wcenter_ndeg in ws_translate_dist', comm)
return
endif
!translation_centre_frac = 0._dp
ws_distance%ndeg = 0
ws_distance%irdist = 0
ws_distance%crdist = 0
call utility_inverse_mat(real_lattice, inv_lattice)
do ir = 1, nrpts
do jw = 1, num_wann
do iw = 1, num_wann
call utility_frac_to_cart(REAL(irvec(:, ir), kind=dp), irvec_cart, real_lattice)
! function JW translated in the Wigner-Seitz around function IW
! and also find its degeneracy, and the integer shifts needed
! to identify it
! Note: the routine outputs R_out, but we don't really need it
! This is kept in case in the future we might want to use it
! R_out contains the actual vector between the two WFs. We
! calculate instead crdist_ws, that is the Bravais lattice vector
! between two supercell lattices, that is the only one we need
! later for interpolation etc.
call r_wz_sc(-wannier_centres(:, iw) &
+ (irvec_cart + wannier_centres(:, jw)), (/0._dp, 0._dp, 0._dp/), &
ws_distance%ndeg(iw, jw, ir), R_out, shifts, mp_grid, real_lattice, &
inv_lattice, ws_region%ws_search_size, ws_region%ws_distance_tol, &
error, comm)
if (allocated(error)) return
do ideg = 1, ws_distance%ndeg(iw, jw, ir)
ws_distance%irdist(:, ideg, iw, jw, ir) = irvec(:, ir) + shifts(:, ideg)
tmp_frac = REAL(ws_distance%irdist(:, ideg, iw, jw, ir), kind=dp)
CALL utility_frac_to_cart(tmp_frac, tmp, real_lattice)
ws_distance%crdist(:, ideg, iw, jw, ir) = tmp
enddo
enddo
enddo
enddo
end subroutine ws_translate_dist
!================================================!
subroutine R_wz_sc(R_in, R0, ndeg, R_out, shifts, mp_grid, real_lattice, inv_lattice, &
ws_search_size, ws_distance_tol, error, comm)
!================================================!
!! Put R_in in the Wigner-Seitz cell centered around R0,
!! and find all equivalent vectors to this (i.e., with same distance).
!! Return their coordinates and the degeneracy, as well as the integer
!! shifts needed to get the vector (these are always multiples of
!! the mp_grid, i.e. they are supercell displacements in the large supercell)
!================================================!
use w90_utility, only: utility_cart_to_frac, utility_frac_to_cart
implicit none
! arguments
integer, intent(in) :: mp_grid(3)
integer, intent(in) :: ws_search_size(3)
real(kind=dp), intent(in) :: real_lattice(3, 3)
real(kind=dp), intent(in) :: inv_lattice(3, 3)
real(kind=dp), intent(in) :: ws_distance_tol
real(DP), intent(in) :: R_in(3)
real(DP), intent(in) :: R0(3)
integer, intent(out) :: ndeg
real(DP), intent(out) :: R_out(3, ndegenx)
integer, intent(out) :: shifts(3, ndegenx)
type(w90_error_type), allocatable, intent(out) :: error
type(w90_comm_type), intent(in) :: comm
! local variables
real(DP) :: R(3), R_f(3), R_in_f(3), R_bz(3), mod2_R_bz
integer :: i, j, k
! init
ndeg = 0
R_out = 0._dp
shifts = 0
R_bz = R_in
mod2_R_bz = SUM((R_bz - R0)**2)
!
! take R_bz to cryst(frac) coord for translating
call utility_cart_to_frac(R_bz, R_in_f, inv_lattice)
! In this first loop, I just look for the shortest vector that I obtain
! by trying to displace the second Wannier function by all
! 'large-supercell' vectors
! The size of the supercell, controlled by ws_search_size,
! is incremented by one unit in order to account for WFs whose centre
! wanders away from the original reference unit cell
do i = -ws_search_size(1) - 1, ws_search_size(1) + 1
do j = -ws_search_size(2) - 1, ws_search_size(2) + 1
do k = -ws_search_size(3) - 1, ws_search_size(3) + 1
R_f = R_in_f + REAL((/i*mp_grid(1), j*mp_grid(2), k*mp_grid(3)/), &
kind=DP)
call utility_frac_to_cart(R_f, R, real_lattice)
if (SUM((R - R0)**2) < mod2_R_bz) then
R_bz = R
mod2_R_bz = SUM((R_bz - R0)**2)
! I start to set a first shift that is applied to get R_bz.
! Note: I reset these every time I find a smaller vector.
!
! At this stage, this is the same for all potentially degenerate
! points (hence the use of : in shifts(1,:), for instance)
! In the second loop below, this shift will be added to the
! additional shift that differs for each degenerate but
! equivalent point
shifts(1, :) = i*mp_grid(1)
shifts(2, :) = j*mp_grid(2)
shifts(3, :) = k*mp_grid(3)
endif
enddo
enddo
enddo
! Now, second loop to find the list of R_out that differ from R_in
! by a large-supercell lattice vector and are equally distant from R0
! (i.e. that are on the edges of the WS cell centered on R0)
! As above, the size of the supercell, controlled by ws_search_size,
! is incremented by one unit in order to account for WFs whose centre
! wanders away from the original reference unit cell
! I start from the last R_bz found
mod2_R_bz = SUM((R_bz - R0)**2)
! check if R0 and R_in are the same vector
if (mod2_R_bz < ws_distance_tol**2) then
ndeg = 1
R_out(:, 1) = R0
! I can safely return as 'shifts' is already set
return
endif
!
! take R_bz to cryst(frac) coord for translating
call utility_cart_to_frac(R_bz, R_in_f, inv_lattice)
do i = -ws_search_size(1) - 1, ws_search_size(1) + 1
do j = -ws_search_size(2) - 1, ws_search_size(2) + 1
do k = -ws_search_size(3) - 1, ws_search_size(3) + 1
r_f = r_in_f + real((/i*mp_grid(1), j*mp_grid(2), k*mp_grid(3)/), &
kind=DP)
call utility_frac_to_cart(R_f, R, real_lattice)
if (abs(sqrt(sum((r - r0)**2)) - sqrt(mod2_r_bz)) < ws_distance_tol) then
ndeg = ndeg + 1
if (ndeg > ndegenx) then
call set_error_fatal(error, "surprising ndeg, I wouldn't expect a degeneracy larger than 8...", comm)
return
end if
R_out(:, ndeg) = R
! I return/update also the shifts. Note that I have to sum these
! to the previous value since in this second loop I am using
! R_bz (from the first loop) as the 'central' reference point,
! that is already shifted by shift(:,ndeg)
shifts(1, ndeg) = shifts(1, ndeg) + i*mp_grid(1)
shifts(2, ndeg) = shifts(2, ndeg) + j*mp_grid(2)
shifts(3, ndeg) = shifts(3, ndeg) + k*mp_grid(3)
endif
enddo
enddo
enddo
!================================================!
end subroutine R_wz_sc
!================================================!
!================================================!
subroutine ws_write_vec(ws_distance, nrpts, irvec, num_wann, use_ws_distance, seedname, error, &
comm)
!================================================!
!! Write to file the lattice vectors of the superlattice
!! to be added to R vector in seedname_hr.dat, seedname_rmn.dat, etc.
!! in order to have the second Wannier function inside the WS cell
!! of the first one.
!================================================!
use w90_io, only: io_date
use w90_types, only: ws_distance_type
implicit none
type(ws_distance_type), intent(in) :: ws_distance
type(w90_error_type), allocatable, intent(out) :: error
integer, intent(in) :: num_wann
logical, intent(in) :: use_ws_distance
character(len=50), intent(in) :: seedname
type(w90_comm_type), intent(in) :: comm
integer, intent(in) :: nrpts
integer, intent(in) :: irvec(3, nrpts)
integer:: irpt, iw, jw, ideg, file_unit, ierr
character(len=100) :: header
character(len=9) :: cdate, ctime
call io_date(cdate, ctime)
open (newunit=file_unit, file=trim(seedname)//'_wsvec.dat', form='formatted', &
status='unknown', iostat=ierr)
if (ierr /= 0) then
call set_error_file(error, 'Error: ws_write_vec: problem opening file '//trim(seedname)//'_ws_vec.dat', comm)
return
endif
if (use_ws_distance) then
header = '## written on '//cdate//' at '//ctime//' with use_ws_distance=.true.'
write (file_unit, '(A)') trim(header)
do irpt = 1, nrpts
do iw = 1, num_wann
do jw = 1, num_wann
write (file_unit, '(5I5)') irvec(:, irpt), iw, jw
write (file_unit, '(I5)') ws_distance%ndeg(iw, jw, irpt)
do ideg = 1, ws_distance%ndeg(iw, jw, irpt)
write (file_unit, '(5I5,2F12.6,I5)') ws_distance%irdist(:, ideg, iw, jw, irpt) - &
irvec(:, irpt)
end do
end do
end do
end do
else
header = '## written on '//cdate//' at '//ctime//' with use_ws_distance=.false.'
write (file_unit, '(A)') trim(header)
do irpt = 1, nrpts
do iw = 1, num_wann
do jw = 1, num_wann
write (file_unit, '(5I5)') irvec(:, irpt), &
iw, jw
write (file_unit, '(I5)') 1
write (file_unit, '(3I5)') 0, 0, 0
end do
end do
end do
end if
close (file_unit)
!================================================!
end subroutine ws_write_vec
!================================================!
subroutine clean_ws_translate(ws_distance)
!================================================!
use w90_types, only: ws_distance_type
implicit none
type(ws_distance_type), intent(inout) :: ws_distance
ws_distance%done = .false.
if (allocated(ws_distance%irdist)) deallocate (ws_distance%irdist)
if (allocated(ws_distance%ndeg)) deallocate (ws_distance%ndeg)
if (allocated(ws_distance%crdist)) deallocate (ws_distance%crdist)
!================================================!
end subroutine clean_ws_translate
end module w90_ws_distance