Octopus
poisson_fft.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2011 M. Marques, A. Castro, A. Rubio, G. Bertsch, M. Oliveira
2!!
3!! This program is free software; you can redistribute it and/or modify
4!! it under the terms of the GNU General Public License as published by
5!! the Free Software Foundation; either version 2, or (at your option)
6!! any later version.
7!!
8!! This program is distributed in the hope that it will be useful,
9!! but WITHOUT ANY WARRANTY; without even the implied warranty of
10!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11!! GNU General Public License for more details.
12!!
13!! You should have received a copy of the GNU General Public License
14!! along with this program; if not, write to the Free Software
15!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16!! 02110-1301, USA.
17!!
18
19#include "global.h"
20
23 use cube_oct_m
24 use debug_oct_m
25 use fft_oct_m
27 use global_oct_m
28 use, intrinsic :: iso_fortran_env
31 use math_oct_m
33 use mesh_oct_m
36 use parser_oct_m
39 use space_oct_m
42 use unit_oct_m
44
45 implicit none
46 private
47 public :: &
54
55 integer, public, parameter :: &
56 POISSON_FFT_KERNEL_NONE = -1, &
63
64 type poisson_fft_t
65 ! Components are public by default
66 type(fourier_space_op_t) :: coulb
67 integer :: kernel
68 real(real64) :: soft_coulb_param
69 end type poisson_fft_t
70
71 real(real64), parameter :: TOL_VANISHING_Q = 1e-6_real64
72contains
73
74 subroutine poisson_fft_init(this, namespace, space, cube, kernel, soft_coulb_param, fullcube)
75 type(poisson_fft_t), intent(out) :: this
76 type(namespace_t), intent(in) :: namespace
77 class(space_t), intent(in) :: space
78 type(cube_t), intent(inout) :: cube
79 integer, intent(in) :: kernel
80 real(real64), optional, intent(in) :: soft_coulb_param
81 type(cube_t), optional, intent(in) :: fullcube
82
83 push_sub(poisson_fft_init)
84
85 this%kernel = kernel
86 this%soft_coulb_param = optional_default(soft_coulb_param, m_zero)
87
88 safe_allocate(this%coulb%qq(1:space%dim))
89 this%coulb%qq(1:space%periodic_dim) = m_zero
90 this%coulb%qq(space%periodic_dim+1:space%dim) = 1e-5_real64
91 this%coulb%singularity = m_zero
92 this%coulb%mu = m_zero
93 this%coulb%alpha = m_zero
94 this%coulb%beta = m_zero
95
96 call poisson_fft_get_kernel(namespace, space, cube, this%coulb, kernel, soft_coulb_param, fullcube)
97
98 pop_sub(poisson_fft_init)
99 end subroutine poisson_fft_init
100
101 subroutine poisson_fft_get_kernel(namespace, space, cube, coulb, kernel, soft_coulb_param, fullcube)
102 type(namespace_t), intent(in) :: namespace
103 class(space_t), intent(in) :: space
104 type(cube_t), intent(in) :: cube
105 type(fourier_space_op_t), intent(inout) :: coulb
106 integer, intent(in) :: kernel
107 real(real64), optional, intent(in) :: soft_coulb_param
108 type(cube_t), optional, intent(in) :: fullcube
109
110 push_sub(poisson_fft_get_kernel)
111
112 if (coulb%mu > m_epsilon) then
113 if (space%dim /= 3 .or. kernel /= poisson_fft_kernel_nocut) then
114 message(1) = "The screened Coulomb potential is only implemented in 3D for PoissonFFTKernel=fft_nocut."
115 call messages_fatal(1, namespace=namespace)
116 end if
117 end if
118
119
120 if (kernel == poisson_fft_kernel_hockney) then
121 if (.not. present(fullcube)) then
122 message(1) = "Hockney's FFT-kernel needs cube of full unit cell "
123 call messages_fatal(1, namespace=namespace)
124 else
125 if (.not. allocated(fullcube%fft)) then
126 message(1) = "Hockney's FFT-kernel needs PoissonSolver=fft"
127 call messages_fatal(1, namespace=namespace)
128 end if
129 end if
130 end if
131
132
133 select case (space%dim)
134 case (1)
135 assert(present(soft_coulb_param))
136 select case (kernel)
138 call poisson_fft_build_1d_0d(namespace, cube, coulb, soft_coulb_param)
140 call poisson_fft_build_1d_1d(cube, coulb, soft_coulb_param)
141 case default
142 message(1) = "Invalid Poisson FFT kernel for 1D."
143 call messages_fatal(1, namespace=namespace)
144 end select
145
146 case (2)
147 select case (kernel)
149 call poisson_fft_build_2d_0d(namespace, cube, coulb)
151 call poisson_fft_build_2d_1d(namespace, cube, coulb)
153 call poisson_fft_build_2d_2d(cube, coulb)
154 case default
155 message(1) = "Invalid Poisson FFT kernel for 2D."
156 call messages_fatal(1, namespace=namespace)
157 end select
158
159 case (3)
160 select case (kernel)
162 call poisson_fft_build_3d_0d(namespace, cube, kernel, coulb, space%is_periodic())
163
165 call poisson_fft_build_3d_1d(namespace, space, cube, coulb)
166
168 call poisson_fft_build_3d_2d(namespace, cube, coulb)
169
171 call poisson_fft_build_3d_3d(cube, coulb)
172
174 call poisson_fft_build_3d_3d_hockney(cube, coulb, fullcube)
175
176 case default
177 message(1) = "Invalid Poisson FFT kernel for 3D."
178 call messages_fatal(1, namespace=namespace)
179 end select
180 end select
181
183 end subroutine poisson_fft_get_kernel
184
185 !-----------------------------------------------------------------
186
187 subroutine get_cutoff(namespace, default_r_c, r_c)
188 type(namespace_t), intent(in) :: namespace
189 real(real64), intent(in) :: default_r_c
190 real(real64), intent(out) :: r_c
191
192 push_sub(get_cutoff)
193
194 call parse_variable(namespace, 'PoissonCutoffRadius', default_r_c, r_c, units_inp%length)
195
196 call messages_write('Info: Poisson Cutoff Radius =')
197 call messages_write(r_c, units = units_out%length, fmt = '(f6.1)')
198 call messages_info()
199
200 if (r_c > default_r_c + m_epsilon) then
201 call messages_write('Poisson cutoff radius is larger than cell size.', new_line = .true.)
202 call messages_write('You can see electrons in neighboring cell(s).')
203 call messages_warning()
204 end if
205
206 pop_sub(get_cutoff)
207 end subroutine get_cutoff
208
209 !-----------------------------------------------------------------
210 subroutine poisson_fft_build_3d_3d(cube, coulb)
211 type(cube_t), intent(in) :: cube
212 type(fourier_space_op_t), intent(inout) :: coulb
213
214 integer :: ix, iy, iz, ixx(3), db(3), n1, n2, n3, lx, ly, lz
215 real(real64) :: temp(3), modg2, inv_four_mu2, ecut
216 real(real64) :: gg(3)
217 real(real64), allocatable :: fft_coulb_fs(:,:,:)
218
220
221 db(1:3) = cube%rs_n_global(1:3)
222
223 if (coulb%mu > m_epsilon) then
224 inv_four_mu2 = m_one/((m_two*coulb%mu)**2)
225 else
226 inv_four_mu2 = m_zero
227 end if
228
229 n1 = max(1, cube%fs_n(1))
230 n2 = max(1, cube%fs_n(2))
231 n3 = max(1, cube%fs_n(3))
232
233 ! store the Fourier transform of the Coulomb interaction
234 safe_allocate(fft_coulb_fs(1:n1, 1:n2, 1:n3))
235 fft_coulb_fs = m_zero
236
237 temp(1:3) = m_two*m_pi/(db(1:3)*cube%spacing(1:3))
238
239 ecut = fft_get_ecut_from_box(cube%rs_n_global, cube%fs_istart, cube%latt, temp, 3, coulb%qq)
240
241 do lx = 1, n1
242 ix = cube%fs_istart(1) + lx - 1
243 ixx(1) = pad_feq(ix, db(1), .true.)
244 do ly = 1, n2
245 iy = cube%fs_istart(2) + ly - 1
246 ixx(2) = pad_feq(iy, db(2), .true.)
247 do lz = 1, n3
248 iz = cube%fs_istart(3) + lz - 1
249 ixx(3) = pad_feq(iz, db(3), .true.)
250
251 call fft_gg_transform(ixx, temp, 3, cube%latt, coulb%qq, gg, modg2)
252
253 if(modg2 > m_two*ecut*1.001_real64) cycle
254
255 !HH not very elegant
256 if (cube%fft%library.eq.fftlib_nfft) modg2=cube%Lfs(ix,1)**2+cube%Lfs(iy,2)**2+cube%Lfs(iz,3)**2
257
258 if (modg2 > tol_vanishing_q) then
259 !Screened coulomb potential (erfc function)
260 if (coulb%mu > m_epsilon) then
261 if(abs(coulb%alpha) > m_epsilon) then ! CAM
262 fft_coulb_fs(lx, ly, lz) = m_four*m_pi/modg2*(coulb%alpha + coulb%beta*exp(-modg2*inv_four_mu2))
263 else ! purely short-range screened
264 fft_coulb_fs(lx, ly, lz) = m_four*m_pi/modg2*coulb%beta*(m_one-exp(-modg2*inv_four_mu2))
265 end if
266 else
267 if (abs(coulb%alpha) > m_epsilon) then ! global screened hybrids
268 fft_coulb_fs(lx, ly, lz) = m_four*m_pi/modg2*coulb%alpha
269 else ! Bare interaction
270 fft_coulb_fs(lx, ly, lz) = m_four*m_pi/modg2
271 end if
272 end if
273 else ! This is the term q+G = 0
274 !Screened short-range coulomb potential (erfc function)
275 if(coulb%mu > m_epsilon .and. abs(coulb%alpha) < m_epsilon) then
276 !Analytical limit of 4pi/q^2 * (1-beta*exp(-|q|^2/4mu^2))
277 fft_coulb_fs(lx, ly, lz) = m_four*m_pi*inv_four_mu2 * coulb%beta
278 else !We use the user-defined value of the singularity
279 !Long-range screened singularity
280 if(abs(coulb%alpha) > m_epsilon) then
281 fft_coulb_fs(lx, ly, lz) = coulb%singularity*coulb%alpha + m_four*m_pi*inv_four_mu2 * coulb%beta
282 else
283 fft_coulb_fs(lx, ly, lz) = coulb%singularity
284 end if
285 ! 4pi/q^2 * alpha
286 end if
287 end if
288
289 end do
290 end do
291
292 end do
293
294 call dfourier_space_op_init(coulb, cube, fft_coulb_fs)
295
296 safe_deallocate_a(fft_coulb_fs)
298 end subroutine poisson_fft_build_3d_3d
299 !-----------------------------------------------------------------
300
301 !-----------------------------------------------------------------
306 subroutine poisson_fft_build_3d_3d_hockney(cube, coulb, fullcube)
307 type(cube_t), intent(in) :: cube
308 type(fourier_space_op_t), intent(inout) :: coulb
309 type(cube_t), intent(in) :: fullcube
310
311 integer :: ix, iy, iz, ixx(3), db(3), nfs(3), nrs(3), nfs_s(3), nrs_s(3)
312 real(real64) :: temp(3), modg2, weight
313 real(real64) :: gg(3)
314 real(real64), allocatable :: fft_Coulb_small_RS(:,:,:)
315 real(real64), allocatable :: fft_Coulb_RS(:,:,:)
316 complex(real64), allocatable :: fft_Coulb_small_FS(:,:,:), fft_Coulb_FS(:,:,:)
317
319
320 assert(abs(coulb%mu) < m_epsilon)
321
322 ! dimensions of large boxes
323 nfs(1:3) = fullcube%fs_n_global(1:3)
324 nrs(1:3) = fullcube%rs_n_global(1:3)
325
326 safe_allocate(fft_coulb_fs(1:nfs(1),1:nfs(2),1:nfs(3)))
327 safe_allocate(fft_coulb_rs(1:nrs(1),1:nrs(2),1:nrs(3)))
328
329 ! dimensions of small boxes x_s
330 nfs_s(1:3) = cube%fs_n_global(1:3)
331 nrs_s(1:3) = cube%rs_n_global(1:3)
332
333 safe_allocate(fft_coulb_small_fs(1:nfs_s(1),1:nfs_s(2),1:nfs_s(3)))
334 safe_allocate(fft_coulb_small_rs(1:nrs_s(1),1:nrs_s(2),1:nrs_s(3)))
335
336 ! build full periodic Coulomb potenital in Fourier space
337 fft_coulb_fs = m_zero
338
339 db(1:3) = fullcube%rs_n_global(1:3)
340 temp(1:3) = m_two*m_pi/(db(1:3)*cube%spacing(1:3))
341
342 do ix = 1, nfs(1)
343 ixx(1) = pad_feq(ix, db(1), .true.)
344 do iy = 1, nfs(2)
345 ixx(2) = pad_feq(iy, db(2), .true.)
346 do iz = 1, nfs(3)
347 ixx(3) = pad_feq(iz, db(3), .true.)
348
349 call fft_gg_transform(ixx, temp, 3, cube%latt, coulb%qq, gg, modg2)
350
351 if (abs(modg2) > tol_vanishing_q) then
352 fft_coulb_fs(ix, iy, iz) = m_one/modg2
353 else
354 fft_coulb_fs(ix, iy, iz) = m_zero
355 end if
356 end do
357 end do
358 end do
359
360 ! Full range hybrids weight
361 weight = m_four*m_pi
362 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
363
364 do iz = 1, nfs(3)
365 do iy = 1, nfs(2)
366 do ix = 1, nfs(1)
367 fft_coulb_fs(ix, iy, iz) = weight*fft_coulb_fs(ix, iy, iz)
368 end do
369 end do
370 end do
371
372 ! get periodic Coulomb potential in real space
373 call dfft_backward(fullcube%fft, fft_coulb_fs, fft_coulb_rs)
374
375 ! copy to small box by respecting this pattern
376 ! full periodic coulomb: |abc--------------------------xyz|
377 ! Hockney: |abcxyz|
378 do ix = 1, nrs_s(1)
379 if (ix.le.nrs_s(1)/2+1) then
380 ixx(1)=ix
381 else
382 ixx(1) = nrs(1) - nrs_s(1)+ix
383 end if
384 do iy = 1, nrs_s(2)
385 if (iy.le.nrs_s(2)/2+1) then
386 ixx(2)=iy
387 else
388 ixx(2) = nrs(2) - nrs_s(2)+iy
389 end if
390 do iz = 1, nrs_s(3)
391 if (iz.le.nrs_s(3)/2+1) then
392 ixx(3)=iz
393 else
394 ixx(3) = nrs(3) - nrs_s(3)+iz
395 end if
396 fft_coulb_small_rs(ix,iy,iz) = fft_coulb_rs(ixx(1),ixx(2),ixx(3))
397 end do
398 end do
399 end do
400 ! make Hockney kernel in Fourier space
401 call dfft_forward(cube%fft, fft_coulb_small_rs, fft_coulb_small_fs)
402 !dummy copy for type conversion
403 fft_coulb_small_rs(1:nfs_s(1),1:nfs_s(2),1:nfs_s(3)) = &
404 real( fft_Coulb_small_FS(1:nfs_s(1),1:nfs_s(2),1:nfs_s(3)), real64)
405
406
407 ! Restrict array to local part to support pfft
408 ! For FFTW this reduces simply to the full array
409 call dfourier_space_op_init(coulb, cube, &
410 fft_coulb_small_rs(cube%fs_istart(1):cube%fs_istart(1)+cube%fs_n(1), &
411 cube%fs_istart(2):cube%fs_istart(2)+cube%fs_n(2), &
412 cube%fs_istart(3):cube%fs_istart(3)+cube%fs_n(3)))
413
414 safe_deallocate_a(fft_coulb_fs)
415 safe_deallocate_a(fft_coulb_rs)
416 safe_deallocate_a(fft_coulb_small_fs)
417 safe_deallocate_a(fft_coulb_small_rs)
418
420
422
423 !-----------------------------------------------------------------
425 subroutine poisson_fft_build_3d_2d(namespace, cube, coulb)
426 type(namespace_t), intent(in) :: namespace
427 type(cube_t), intent(in) :: cube
428 type(fourier_space_op_t), intent(inout) :: coulb
429
430 integer :: ix, iy, iz, ixx(3), db(3)
431 integer :: lx, ly, lz, n1, n2, n3
432 real(real64) :: temp(3), modg2, ecut, weight
433 real(real64) :: gpar, gz, r_c, gg(3), default_r_c
434 real(real64), allocatable :: fft_coulb_FS(:,:,:)
435
437
438 db(1:3) = cube%rs_n_global(1:3)
439
440 assert(abs(coulb%mu) < m_epsilon)
441 ! Full range hybrids weight
442 weight = m_four*m_pi
443 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
444
445
446 !%Variable PoissonCutoffRadius
447 !%Type float
448 !%Section Hamiltonian::Poisson
449 !%Description
450 !% When <tt>PoissonSolver = fft</tt> and <tt>PoissonFFTKernel</tt> is neither <tt>multipole_corrections</tt>
451 !% nor <tt>fft_nocut</tt>,
452 !% this variable controls the distance after which the electron-electron interaction goes to zero.
453 !% A warning will be written if the value is too large and will cause spurious interactions between images.
454 !% The default is half of the FFT box max dimension in a finite direction.
455 !%End
456
457 default_r_c = db(3)*cube%spacing(3)/m_two
458 call get_cutoff(namespace, default_r_c, r_c)
459
460 n1 = max(1, cube%fs_n(1))
461 n2 = max(1, cube%fs_n(2))
462 n3 = max(1, cube%fs_n(3))
463 ! store the Fourier transform of the Coulomb interaction
464 safe_allocate(fft_coulb_fs(1:n1, 1:n2, 1:n3))
465 fft_coulb_fs = m_zero
466
467 temp(1:3) = m_two*m_pi/(db(1:3)*cube%spacing(1:3))
468
469 ecut = fft_get_ecut_from_box(cube%rs_n_global, cube%fs_istart, cube%latt, temp, 2, coulb%qq)
470
471 do lx = 1, n1
472 ix = cube%fs_istart(1) + lx - 1
473 ixx(1) = pad_feq(ix, db(1), .true.)
474 do ly = 1, n2
475 iy = cube%fs_istart(2) + ly - 1
476 ixx(2) = pad_feq(iy, db(2), .true.)
477 do lz = 1, n3
478 iz = cube%fs_istart(3) + lz - 1
479 ixx(3) = pad_feq(iz, db(3), .true.)
480
481 call fft_gg_transform(ixx, temp, 2, cube%latt, coulb%qq, gg, modg2)
482
483 if(sum(gg(1:2)**2) > m_two*ecut*1.001_real64) cycle
484
485 if (abs(modg2) > tol_vanishing_q) then
486 gz = abs(gg(3))
487 gpar = hypot(gg(1), gg(2))
488 ! note: if gpar = 0, then modg2 = gz**2
489 fft_coulb_fs(lx, ly, lz) = poisson_cutoff_3d_2d(gpar,gz,r_c)/modg2
490 else
491 fft_coulb_fs(lx, ly, lz) = -m_half*r_c**2
492 end if
493 fft_coulb_fs(lx, ly, lz) = weight*fft_coulb_fs(lx, ly, lz)
494 end do
495 end do
496
497 end do
498
499 call dfourier_space_op_init(coulb, cube, fft_coulb_fs)
500
501 safe_deallocate_a(fft_coulb_fs)
503 end subroutine poisson_fft_build_3d_2d
504 !-----------------------------------------------------------------
505
506
507 !-----------------------------------------------------------------
509 subroutine poisson_fft_build_3d_1d(namespace, space, cube, coulb)
510 type(namespace_t), intent(in) :: namespace
511 class(space_t), intent(in) :: space
512 type(cube_t), intent(in) :: cube
513 type(fourier_space_op_t), intent(inout) :: coulb
514
515 type(spline_t) :: cylinder_cutoff_f
516 real(real64), allocatable :: x(:), y(:)
517 integer :: ix, iy, iz, ixx(3), db(3), k, ngp
518 integer :: lx, ly, lz, n1, n2, n3, lxx(3)
519 real(real64) :: temp(3), modg2, xmax, weight
520 real(real64) :: gperp, gx, gy, gz, r_c, gg(3), default_r_c
521 real(real64), allocatable :: fft_coulb_FS(:,:,:)
522
524
525 assert(abs(coulb%mu) < m_epsilon)
526 ! Full range hybrids weight
527 weight = m_four*m_pi
528 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
529
530
531 db(1:3) = cube%rs_n_global(1:3)
532
533 default_r_c = maxval(db(2:3)*cube%spacing(2:3)/m_two)
534 call get_cutoff(namespace, default_r_c, r_c)
535
536 n1 = max(1, cube%fs_n(1))
537 n2 = max(1, cube%fs_n(2))
538 n3 = max(1, cube%fs_n(3))
539 ! store the Fourier transform of the Coulomb interaction
540 safe_allocate(fft_coulb_fs(1:n1, 1:n2, 1:n3))
541 fft_coulb_fs = m_zero
542
543 temp(1:3) = m_two*m_pi/(db(1:3)*cube%spacing(1:3))
544
545 if (.not. space%is_periodic()) then
546 ngp = 8*db(2)
547 safe_allocate(x(1:ngp))
548 safe_allocate(y(1:ngp))
549 end if
550
551
552 do lx = 1, n1
553 ix = cube%fs_istart(1) + lx - 1
554 ixx(1) = pad_feq(ix, db(1), .true.)
555 lxx(1) = ixx(1) - cube%fs_istart(1) + 1
556 gx = temp(1)*ixx(1)
557
558 if (.not. space%is_periodic()) then
559 call spline_init(cylinder_cutoff_f)
560 xmax = norm2(temp(2:3)*db(2:3))/2
561 do k = 1, ngp
562 x(k) = (k-1)*(xmax/(ngp-1))
563 y(k) = poisson_cutoff_3d_1d_finite(gx, x(k), norm2(cube%latt%rlattice_primitive(:, 1)), &
564 maxval(norm2(cube%latt%rlattice_primitive(:, 2:3), dim=1)))
565 end do
566 call spline_fit(ngp, x, y, cylinder_cutoff_f, m_zero)
567 end if
568
569 do ly = 1, n2
570 iy = cube%fs_istart(2) + ly - 1
571 ixx(2) = pad_feq(iy, db(2), .true.)
572 lxx(2) = ixx(2) - cube%fs_istart(2) + 1
573 do lz = 1, n3
574 iz = cube%fs_istart(3) + lz - 1
575 ixx(3) = pad_feq(iz, db(3), .true.)
576 lxx(3) = ixx(3) - cube%fs_istart(3) + 1
577
578 call fft_gg_transform(ixx, temp, 1, cube%latt, coulb%qq, gg, modg2)
579
580 if (abs(modg2) > tol_vanishing_q) then
581 gperp = hypot(gg(2), gg(3))
582 if (space%periodic_dim == 1) then
583 if (gperp > r_c) then
584 fft_coulb_fs(lx, ly, lz) = m_zero
585 else
586 fft_coulb_fs(lx, ly, lz) = poisson_cutoff_3d_1d(abs(gx), gperp, r_c)/modg2
587 end if
588 else if (.not. space%is_periodic()) then
589 gy = gg(2)
590 gz = gg(3)
591 if ((gz >= m_zero) .and. (gy >= m_zero)) then
592 fft_coulb_fs(lx, ly, lz) = spline_eval(cylinder_cutoff_f, gperp)
593 end if
594 if ((gz >= m_zero) .and. (gy < m_zero)) then
595 fft_coulb_fs(lx, ly, lz) = fft_coulb_fs(lx, -lxx(2) + 1, lz)
596 end if
597 if ((gz < m_zero) .and. (gy >= m_zero)) then
598 fft_coulb_fs(lx, ly, lz) = fft_coulb_fs(lx, ly, -lxx(3) + 1)
599 end if
600 if ((gz < m_zero) .and. (gy < m_zero)) then
601 fft_coulb_fs(lx, ly, lz) = fft_coulb_fs(lx, -lxx(2) + 1, -lxx(3) + 1)
602 end if
603 end if
604
605 else
606 if (space%periodic_dim == 1) then
607 fft_coulb_fs(lx, ly, lz) = -(m_half*log(r_c) - m_fourth)*r_c**2
608 else if (.not. space%is_periodic()) then
609 fft_coulb_fs(lx, ly, lz) = poisson_cutoff_3d_1d_finite(m_zero, m_zero, &
610 norm2(cube%latt%rlattice_primitive(:, 1)), maxval(norm2(cube%latt%rlattice_primitive(:, 2:3), dim=1)))
611 end if
612
613 end if
614 fft_coulb_fs(lx, ly, lz) = weight*fft_coulb_fs(lx, ly, lz)
615 end do
616 end do
617
618 if (.not. space%is_periodic()) then
619 call spline_end(cylinder_cutoff_f)
620 end if
621 end do
622
623 call dfourier_space_op_init(coulb, cube, fft_coulb_fs)
624
625 safe_deallocate_a(fft_coulb_fs)
626 safe_deallocate_a(x)
627 safe_deallocate_a(y)
629 end subroutine poisson_fft_build_3d_1d
630 !-----------------------------------------------------------------
631
632
633 !-----------------------------------------------------------------
635 subroutine poisson_fft_build_3d_0d(namespace, cube, kernel, coulb, is_periodic)
636 type(namespace_t), intent(in) :: namespace
637 type(cube_t), intent(in) :: cube
638 integer, intent(in) :: kernel
639 type(fourier_space_op_t), intent(inout) :: coulb
640 logical, intent(in) :: is_periodic
641
642 integer :: ix, iy, iz, ixx(3), db(3), lx, ly, lz, n1, n2, n3
643 real(real64) :: temp(3), modg2, ecut, weight
644 real(real64) :: r_c, gg(3), default_r_c
645 real(real64), allocatable :: fft_coulb_FS(:,:,:)
646 real(real64) :: axis(3,3)
647
649
650 assert(abs(coulb%mu) < m_epsilon)
651 ! Full range hybrids weight
652 weight = m_four*m_pi
653 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
654
655
656 db(1:3) = cube%rs_n_global(1:3)
657
658 if (kernel /= poisson_fft_kernel_corrected) then
659
660 ! This is the real-space cutoff
661 do ix = 1, 3
662 axis(:,ix) = cube%latt%rlattice_primitive(:, ix) * cube%spacing(ix) * db(ix) / m_two
663 end do
664
665 default_r_c = m_huge
666 do ix = 1, 3
667 iy = mod(ix, 3)+1
668 iz = mod(ix+1, 3)+1
669
670 ! For orthogonal cells, this is determined by the size of the cube
671 if (.not. cube%latt%nonorthogonal) then
672 temp(1:3) = axis(:, ix)
673 default_r_c = min(default_r_c, norm2(temp(1:3)))
674 else
675 ! At the moment, this codepath is only called for DFT+U submesh Poisson solver
676 !
677 ! For non-orthogonal cells, the relevant length is the distance between two planes of
678 ! the parallelepiped. This ensures that we draw a sphere that touches the borders of the box,
679 ! thus avoiding contribution from periodic replicas
680 ! This distance is given by the usual formula of the distance from a point to a plan
681 temp = dcross_product(axis(:, iy), axis(:, iz))
682 temp = temp / norm2(temp)
683 default_r_c = min(default_r_c, dot_product(temp, axis(:, ix)-axis(:, iy)))
684 end if
685 end do
686 call get_cutoff(namespace, default_r_c, r_c)
687 end if
688
689 n1 = max(1, cube%fs_n(1))
690 n2 = max(1, cube%fs_n(2))
691 n3 = max(1, cube%fs_n(3))
692
693 ! store the fourier transform of the Coulomb interaction
694 ! store only the relevant part if PFFT is used
695 safe_allocate(fft_coulb_fs(1:n1,1:n2,1:n3))
696 fft_coulb_fs = m_zero
697
698 temp(1:3) = m_two*m_pi/(db(1:3)*cube%spacing(1:3))
699
700 ecut = fft_get_ecut_from_box(cube%rs_n_global, cube%fs_istart, cube%latt, temp, 3, coulb%qq)
701
702 do lx = 1, n1
703 ix = cube%fs_istart(1) + lx - 1
704 ixx(1) = pad_feq(ix, db(1), .true.)
705 do ly = 1, n2
706 iy = cube%fs_istart(2) + ly - 1
707 ixx(2) = pad_feq(iy, db(2), .true.)
708 do lz = 1, n3
709 iz = cube%fs_istart(3) + lz - 1
710 ixx(3) = pad_feq(iz, db(3), .true.)
711
712 call fft_gg_transform(ixx, temp, 0, cube%latt, coulb%qq, gg, modg2)
713
714 !HH
715 if (cube%fft%library.eq.fftlib_nfft) then
716 modg2=cube%Lfs(ix,1)**2+cube%Lfs(iy,2)**2+cube%Lfs(iz,3)**2
717 r_c = default_r_c*m_two
718 end if
719
720 ! At the moment this is only done for periodic space, so for DFT+U Coulomb integrals
721 if(modg2 > m_two*ecut*1.001_real64 .and. is_periodic) cycle
722
723 if (abs(modg2) > tol_vanishing_q) then
724 select case (kernel)
726 fft_coulb_fs(lx, ly, lz) = weight*poisson_cutoff_3d_0d(sqrt(modg2),r_c)/modg2
728 fft_coulb_fs(lx, ly, lz) = weight/modg2
729 end select
730 else
731 select case (kernel)
733 fft_coulb_fs(lx, ly, lz) = weight*r_c**2/m_two
735 fft_coulb_fs(lx, ly, lz) = m_zero
736 end select
737 end if
738 end do
739 end do
740 end do
741
742 call dfourier_space_op_init(coulb, cube, fft_coulb_fs, in_device = (kernel /= poisson_fft_kernel_corrected))
743
744 safe_deallocate_a(fft_coulb_fs)
746 end subroutine poisson_fft_build_3d_0d
747 !-----------------------------------------------------------------
748
749
750 !-----------------------------------------------------------------
752 subroutine poisson_fft_build_2d_0d(namespace, cube, coulb)
753 type(namespace_t), intent(in) :: namespace
754 type(cube_t), intent(in) :: cube
755 type(fourier_space_op_t), intent(inout) :: coulb
756
757 type(spline_t) :: besselintf
758 integer :: i, ix, iy, ixx(2), db(2), npoints
759 real(real64) :: temp(2), vec, r_c, maxf, dk, default_r_c, weight
760 real(real64), allocatable :: x(:), y(:)
761 real(real64), allocatable :: fft_coulb_FS(:,:,:)
762
764
765 assert(abs(coulb%mu) < m_epsilon)
766 ! Full range hybrids weight
767 weight = m_one
768 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
769
770 db(1:2) = cube%rs_n_global(1:2)
771
772 default_r_c = maxval(db(1:2)*cube%spacing(1:2)/m_two)
773 call get_cutoff(namespace, default_r_c, r_c)
774
775 call spline_init(besselintf)
776
777 ! store the fourier transform of the Coulomb interaction
778 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
779 fft_coulb_fs = m_zero
780 temp(1:2) = m_two*m_pi/(db(1:2)*cube%spacing(1:2))
781
782 maxf = r_c * norm2(temp(1:2)*db(1:2))/2
783 dk = 0.25_real64 ! This seems to be reasonable.
784 npoints = nint(maxf/dk)
785 safe_allocate(x(1:npoints))
786 safe_allocate(y(1:npoints))
787 x(1) = m_zero
788 y(1) = m_zero
789 do i = 2, npoints
790 x(i) = (i-1) * maxf / (npoints-1)
791 y(i) = y(i-1) + poisson_cutoff_2d_0d(x(i-1), x(i))
792 end do
793 call spline_fit(npoints, x, y, besselintf, m_zero)
794
795 do iy = 1, cube%fs_n_global(2)
796 ixx(2) = pad_feq(iy, db(2), .true.)
797 do ix = 1, cube%fs_n_global(1)
798 ixx(1) = pad_feq(ix, db(1), .true.)
799 vec = norm2(temp(1:2)*ixx(1:2))
800 ! extra check to avoid extrapolation which leads to an error in gsl
801 if (vec*r_c >= x(npoints)) then
802 fft_coulb_fs(ix, iy, 1) = weight * y(npoints)
803 else if (vec > m_zero) then
804 fft_coulb_fs(ix, iy, 1) = weight * (m_two * m_pi / vec) * spline_eval(besselintf, vec*r_c)
805 else
806 fft_coulb_fs(ix, iy, 1) = weight * m_two * m_pi * r_c
807 end if
808 end do
809 end do
810
811 call dfourier_space_op_init(coulb, cube, fft_coulb_fs)
812
813 safe_deallocate_a(fft_coulb_fs)
814 safe_deallocate_a(x)
815 safe_deallocate_a(y)
816 call spline_end(besselintf)
818 end subroutine poisson_fft_build_2d_0d
819 !-----------------------------------------------------------------
820
821
822 !-----------------------------------------------------------------
824 subroutine poisson_fft_build_2d_1d(namespace, cube, coulb)
825 type(namespace_t), intent(in) :: namespace
826 type(cube_t), intent(in) :: cube
827 type(fourier_space_op_t), intent(inout) :: coulb
828
829 integer :: ix, iy, ixx(2), db(2)
830 real(real64) :: temp(2), r_c, gx, gy, default_r_c, weight
831 real(real64), allocatable :: fft_coulb_FS(:,:,:)
832
834
835 assert(abs(coulb%mu) < m_epsilon)
836 ! Full range hybrids weight
837 weight = m_one
838 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
839
840
841 db(1:2) = cube%rs_n_global(1:2)
842
843 default_r_c = db(2)*cube%spacing(2)/m_two
844 call get_cutoff(namespace, default_r_c, r_c)
846 ! store the fourier transform of the Coulomb interaction
847 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
848 fft_coulb_fs = m_zero
849 temp(1:2) = m_two*m_pi/(db(1:2)*cube%spacing(1:2))
850
851 ! First, the term ix = 0 => gx = 0.
852 fft_coulb_fs(1, 1, 1) = -m_four * r_c * (log(r_c)-m_one)
853 do iy = 2, cube%fs_n_global(2)
854 ixx(2) = pad_feq(iy, db(2), .true.)
855 gy = temp(2)*ixx(2)
856 fft_coulb_fs(1, iy, 1) = -m_four * poisson_cutoff_intcoslog(r_c, gy, m_one) * weight
857 end do
858
859 do ix = 2, cube%fs_n_global(1)
860 ixx(1) = pad_feq(ix, db(1), .true.)
861 gx = temp(1)*ixx(1)
862 do iy = 1, db(2)
863 ixx(2) = pad_feq(iy, db(2), .true.)
864 gy = temp(2)*ixx(2)
865 fft_coulb_fs(ix, iy, 1) = poisson_cutoff_2d_1d(gy, gx, r_c) * weight
866 end do
867 end do
868
869 call dfourier_space_op_init(coulb, cube, fft_coulb_fs)
870
871 safe_deallocate_a(fft_coulb_fs)
872
874 end subroutine poisson_fft_build_2d_1d
875 !-----------------------------------------------------------------
876
877
878 !-----------------------------------------------------------------
880 subroutine poisson_fft_build_2d_2d(cube, coulb)
881 type(cube_t), intent(in) :: cube
882 type(fourier_space_op_t), intent(inout) :: coulb
883
884 integer :: ix, iy, ixx(2), db(2)
885 real(real64) :: temp(2), vec, weight
886 real(real64), allocatable :: fft_coulb_FS(:,:,:)
887
889
890 assert(abs(coulb%mu) < m_epsilon)
891 ! Full range hybrids weight
892 weight = m_one
893 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
894
895 db(1:2) = cube%rs_n_global(1:2)
896
897 ! store the fourier transform of the Coulomb interaction
898 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
899 fft_coulb_fs = m_zero
900 temp(1:2) = m_two*m_pi/(db(1:2)*cube%spacing(1:2))
901
902 do iy = 1, cube%fs_n_global(2)
903 ixx(2) = pad_feq(iy, db(2), .true.)
904 do ix = 1, cube%fs_n_global(1)
905 ixx(1) = pad_feq(ix, db(1), .true.)
906 vec = sqrt((temp(1) * ixx(1))**2 + (temp(2) * ixx(2))**2)
907 if (vec > m_zero) fft_coulb_fs(ix, iy, 1) = m_two * m_pi / vec * weight
908 end do
909 end do
910
911 call dfourier_space_op_init(coulb, cube, fft_coulb_fs)
912
913 safe_deallocate_a(fft_coulb_fs)
915 end subroutine poisson_fft_build_2d_2d
916 !-----------------------------------------------------------------
918
919 !-----------------------------------------------------------------
920 subroutine poisson_fft_build_1d_1d(cube, coulb, poisson_soft_coulomb_param)
921 type(cube_t), intent(in) :: cube
922 type(fourier_space_op_t), intent(inout) :: coulb
923 real(real64), intent(in) :: poisson_soft_coulomb_param
924
925 integer :: ix, ixx
926 real(real64) :: g
927 real(real64), allocatable :: fft_coulb_fs(:, :, :), weight
928
930
931 assert(abs(coulb%mu) < m_epsilon)
932 ! Full range hybrids weight
933 weight = m_one
934 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
935
936 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
937 fft_coulb_fs = m_zero
938
939 ! Fourier transform of Soft Coulomb interaction.
940 do ix = 1, cube%fs_n_global(1)
941 ixx = pad_feq(ix, cube%rs_n_global(1), .true.)
942 g = (ixx + coulb%qq(1))*m_two*m_pi/abs(cube%latt%rlattice(1,1))
943 if (abs(g) > tol_vanishing_q) then
944 fft_coulb_fs(ix, 1, 1) = m_two * loct_bessel_k0(poisson_soft_coulomb_param*abs(g)) * weight
945 else
946 fft_coulb_fs(ix, 1, 1) = coulb%singularity * m_two * weight
947 end if
948 end do
949
950 call dfourier_space_op_init(coulb, cube, fft_coulb_fs)
951 safe_deallocate_a(fft_coulb_fs)
952
954 end subroutine poisson_fft_build_1d_1d
955 !-----------------------------------------------------------------
956
957
958 !-----------------------------------------------------------------
959 subroutine poisson_fft_build_1d_0d(namespace, cube, coulb, poisson_soft_coulomb_param)
960 type(namespace_t), intent(in) :: namespace
961 type(cube_t), intent(in) :: cube
962 type(fourier_space_op_t), intent(inout) :: coulb
963 real(real64), intent(in) :: poisson_soft_coulomb_param
964
965 integer :: box(1), ixx(1), ix
966 real(real64) :: temp(1), g, r_c, default_r_c, weight
967 real(real64), allocatable :: fft_coulb_fs(:, :, :)
968
970
971 assert(abs(coulb%mu) < m_epsilon)
972 ! Full range hybrids weight
973 weight = m_one
974 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
975
976 box(1:1) = cube%rs_n_global(1:1)
977
978 default_r_c = box(1)*cube%spacing(1)/m_two
979 call get_cutoff(namespace, default_r_c, r_c)
980
981 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
982 fft_coulb_fs = m_zero
983 temp(1:1) = m_two*m_pi/(box(1:1)*cube%spacing(1:1))
984
985 ! Fourier transform of Soft Coulomb interaction.
986 do ix = 1, cube%fs_n_global(1)
987 ixx(1) = pad_feq(ix, box(1), .true.)
988 g = temp(1)*ixx(1)
989 fft_coulb_fs(ix, 1, 1) = poisson_cutoff_1d_0d(g, poisson_soft_coulomb_param, r_c) * weight
990 end do
991
992 call dfourier_space_op_init(coulb, cube, fft_coulb_fs)
993 safe_deallocate_a(fft_coulb_fs)
994
996 end subroutine poisson_fft_build_1d_0d
997 !-----------------------------------------------------------------
998
999
1000 !-----------------------------------------------------------------
1001 subroutine poisson_fft_end(this)
1002 type(poisson_fft_t), intent(inout) :: this
1003
1004 push_sub(poisson_fft_end)
1005
1006 call fourier_space_op_end(this%coulb)
1007
1008 pop_sub(poisson_fft_end)
1009 end subroutine poisson_fft_end
1010
1011#include "undef.F90"
1012#include "real.F90"
1013#include "poisson_fft_inc.F90"
1014#include "undef.F90"
1015#include "complex.F90"
1016#include "poisson_fft_inc.F90"
1017
1018
1019end module poisson_fft_oct_m
1020
1021!! Local Variables:
1022!! mode: f90
1023!! coding: utf-8
1024!! End:
Some operations may be done for one spline-function, or for an array of them.
Definition: splines.F90:179
double hypot(double __x, double __y) __attribute__((__nothrow__
double log(double __x) __attribute__((__nothrow__
double exp(double __x) __attribute__((__nothrow__
Fast Fourier Transform module. This module provides a single interface that works with different FFT ...
Definition: fft.F90:118
real(real64) function, public fft_get_ecut_from_box(box_dim, fs_istart, latt, gspacing, periodic_dim, qq)
Given an fft box (fixed by the real-space grid), it returns the cutoff energy of the sphere that fits...
Definition: fft.F90:1245
subroutine, public fft_gg_transform(gg_in, temp, periodic_dim, latt, qq, gg, modg2)
Definition: fft.F90:1200
pure integer function, public pad_feq(ii, nn, mode)
convert between array index and G-vector
Definition: fft.F90:1105
integer, parameter, public fftlib_nfft
Definition: fft.F90:182
subroutine, public fourier_space_op_end(this)
subroutine, public dfourier_space_op_init(this, cube, op, in_device)
real(real64), parameter, public m_two
Definition: global.F90:189
real(real64), parameter, public m_huge
Definition: global.F90:205
real(real64), parameter, public m_zero
Definition: global.F90:187
real(real64), parameter, public m_four
Definition: global.F90:191
real(real64), parameter, public m_pi
some mathematical constants
Definition: global.F90:185
real(real64), parameter, public m_fourth
Definition: global.F90:196
real(real64), parameter, public m_epsilon
Definition: global.F90:203
real(real64), parameter, public m_half
Definition: global.F90:193
real(real64), parameter, public m_one
Definition: global.F90:188
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:115
pure real(real64) function, dimension(1:3), public dcross_product(a, b)
Definition: math.F90:1833
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:118
subroutine, public messages_warning(no_lines, all_nodes, namespace)
Definition: messages.F90:543
subroutine, public messages_info(no_lines, iunit, verbose_limit, stress, all_nodes, namespace)
Definition: messages.F90:624
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:160
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:420
real(real64) function, public poisson_cutoff_3d_2d(p, z, r)
real(real64) function, public poisson_cutoff_3d_1d(x, p, rmax)
real(real64) function, public poisson_cutoff_3d_0d(x, r)
integer, parameter, public poisson_fft_kernel_hockney
subroutine poisson_fft_build_2d_0d(namespace, cube, coulb)
A. Castro et al., Phys. Rev. B 80, 033102 (2009)
subroutine poisson_fft_build_3d_1d(namespace, space, cube, coulb)
C. A. Rozzi et al., Phys. Rev. B 73, 205119 (2006), Table I.
subroutine poisson_fft_build_3d_2d(namespace, cube, coulb)
C. A. Rozzi et al., Phys. Rev. B 73, 205119 (2006), Table I.
integer, parameter, public poisson_fft_kernel_nocut
integer, parameter, public poisson_fft_kernel_cyl
subroutine poisson_fft_build_2d_1d(namespace, cube, coulb)
A. Castro et al., Phys. Rev. B 80, 033102 (2009)
subroutine poisson_fft_build_1d_0d(namespace, cube, coulb, poisson_soft_coulomb_param)
subroutine, public zpoisson_fft_solve(this, mesh, cube, pot, rho, mesh_cube_map, average_to_zero, kernel, sm)
subroutine poisson_fft_build_3d_0d(namespace, cube, kernel, coulb, is_periodic)
C. A. Rozzi et al., Phys. Rev. B 73, 205119 (2006), Table I.
subroutine get_cutoff(namespace, default_r_c, r_c)
subroutine poisson_fft_build_3d_3d(cube, coulb)
subroutine poisson_fft_build_1d_1d(cube, coulb, poisson_soft_coulomb_param)
subroutine, public poisson_fft_get_kernel(namespace, space, cube, coulb, kernel, soft_coulb_param, fullcube)
subroutine, public poisson_fft_end(this)
subroutine poisson_fft_build_2d_2d(cube, coulb)
A. Castro et al., Phys. Rev. B 80, 033102 (2009)
subroutine, public poisson_fft_init(this, namespace, space, cube, kernel, soft_coulb_param, fullcube)
integer, parameter, public poisson_fft_kernel_pla
integer, parameter, public poisson_fft_kernel_corrected
integer, parameter, public poisson_fft_kernel_sph
subroutine, public dpoisson_fft_solve(this, mesh, cube, pot, rho, mesh_cube_map, average_to_zero, kernel, sm)
subroutine poisson_fft_build_3d_3d_hockney(cube, coulb, fullcube)
Kernel for Hockneys algorithm that solves the poisson equation in a small box while respecting the pe...
subroutine, public spline_fit(nrc, rofi, ffit, spl, threshold)
Definition: splines.F90:413
real(real64) function, public spline_eval(spl, x)
Definition: splines.F90:441
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
Definition: unit.F90:132
This module defines the unit system, used for input and output.
type(unit_system_t), public units_out
type(unit_system_t), public units_inp
the units systems for reading and writing
the basic spline datatype
Definition: splines.F90:156
int true(void)