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
22 use accel_oct_m
24 use cube_oct_m
25 use debug_oct_m
26 use fft_oct_m
28 use global_oct_m
29 use, intrinsic :: iso_fortran_env
32 use math_oct_m
34 use mesh_oct_m
37 use parser_oct_m
40 use space_oct_m
43 use unit_oct_m
45
46 implicit none
47 private
48 public :: &
57
58 integer, public, parameter :: &
59 POISSON_FFT_KERNEL_NONE = -1, &
66
67 type poisson_fft_t
68 ! Components are public by default
69 type(fourier_space_op_t) :: coulb
70 integer :: kernel
71 real(real64) :: soft_coulb_param
72 end type poisson_fft_t
73
74 real(real64), parameter :: TOL_VANISHING_Q = 1e-6_real64
75contains
76
77 subroutine poisson_fft_init(this, namespace, space, cube, kernel, soft_coulb_param, fullcube)
78 type(poisson_fft_t), intent(out) :: this
79 type(namespace_t), intent(in) :: namespace
80 class(space_t), intent(in) :: space
81 type(cube_t), intent(inout) :: cube
82 integer, intent(in) :: kernel
83 real(real64), optional, intent(in) :: soft_coulb_param
84 type(cube_t), optional, intent(in) :: fullcube
85
86 push_sub(poisson_fft_init)
87
88 this%kernel = kernel
89 this%soft_coulb_param = optional_default(soft_coulb_param, m_zero)
90
91 safe_allocate(this%coulb%qq(1:space%dim))
92 this%coulb%qq(1:space%periodic_dim) = m_zero
93 this%coulb%qq(space%periodic_dim+1:space%dim) = 1e-5_real64
94 this%coulb%singularity = m_zero
95 this%coulb%mu = m_zero
96 this%coulb%alpha = m_zero
97 this%coulb%beta = m_zero
98
99 call poisson_fft_get_kernel(namespace, space, cube, this%coulb, kernel, soft_coulb_param, fullcube)
100
101 pop_sub(poisson_fft_init)
102 end subroutine poisson_fft_init
103
104 subroutine poisson_fft_get_kernel(namespace, space, cube, coulb, kernel, soft_coulb_param, fullcube)
105 type(namespace_t), intent(in) :: namespace
106 class(space_t), intent(in) :: space
107 type(cube_t), intent(in) :: cube
108 type(fourier_space_op_t), intent(inout) :: coulb
109 integer, intent(in) :: kernel
110 real(real64), optional, intent(in) :: soft_coulb_param
111 type(cube_t), optional, intent(in) :: fullcube
112
113 push_sub(poisson_fft_get_kernel)
114
115 if (coulb%mu > m_epsilon) then
116 if (space%dim /= 3 .or. kernel /= poisson_fft_kernel_nocut) then
117 message(1) = "The screened Coulomb potential is only implemented in 3D for PoissonFFTKernel=fft_nocut."
118 call messages_fatal(1, namespace=namespace)
119 end if
120 end if
121
122
123 if (kernel == poisson_fft_kernel_hockney) then
124 if (.not. present(fullcube)) then
125 message(1) = "Hockney's FFT-kernel needs cube of full unit cell "
126 call messages_fatal(1, namespace=namespace)
127 else
128 if (.not. allocated(fullcube%fft)) then
129 message(1) = "Hockney's FFT-kernel needs PoissonSolver=fft"
130 call messages_fatal(1, namespace=namespace)
131 end if
132 end if
133 end if
134
135
136 select case (space%dim)
137 case (1)
138 assert(present(soft_coulb_param))
139 select case (kernel)
141 call poisson_fft_build_1d_0d(namespace, cube, coulb, soft_coulb_param)
143 call poisson_fft_build_1d_1d(cube, coulb, soft_coulb_param)
144 case default
145 message(1) = "Invalid Poisson FFT kernel for 1D."
146 call messages_fatal(1, namespace=namespace)
147 end select
148
149 case (2)
150 select case (kernel)
152 call poisson_fft_build_2d_0d(namespace, cube, coulb)
154 call poisson_fft_build_2d_1d(namespace, cube, coulb)
156 call poisson_fft_build_2d_2d(cube, coulb)
157 case default
158 message(1) = "Invalid Poisson FFT kernel for 2D."
159 call messages_fatal(1, namespace=namespace)
160 end select
161
162 case (3)
163 select case (kernel)
165 call poisson_fft_build_3d_0d(namespace, cube, kernel, coulb, space%is_periodic())
168 call poisson_fft_build_3d_1d(namespace, space, cube, coulb)
171 call poisson_fft_build_3d_2d(namespace, cube, coulb)
174 call poisson_fft_build_3d_3d(cube, coulb)
175
177 call poisson_fft_build_3d_3d_hockney(cube, coulb, fullcube)
178
179 case default
180 message(1) = "Invalid Poisson FFT kernel for 3D."
181 call messages_fatal(1, namespace=namespace)
182 end select
183 end select
184
186 end subroutine poisson_fft_get_kernel
187
188 !-----------------------------------------------------------------
189
190 subroutine get_cutoff(namespace, default_r_c, r_c)
191 type(namespace_t), intent(in) :: namespace
192 real(real64), intent(in) :: default_r_c
193 real(real64), intent(out) :: r_c
194
195 push_sub(get_cutoff)
196
197 call parse_variable(namespace, 'PoissonCutoffRadius', default_r_c, r_c, units_inp%length)
198
199 call messages_write('Info: Poisson Cutoff Radius =')
200 call messages_write(r_c, units = units_out%length, fmt = '(f6.1)')
201 call messages_info()
202
203 if (r_c > default_r_c + m_epsilon) then
204 call messages_write('Poisson cutoff radius is larger than cell size.', new_line = .true.)
205 call messages_write('You can see electrons in neighboring cell(s).')
206 call messages_warning()
207 end if
208
209 pop_sub(get_cutoff)
210 end subroutine get_cutoff
211
212
237 subroutine poisson_fft_build_3d_3d(cube, coulb)
238 type(cube_t), intent(in) :: cube
239 type(fourier_space_op_t), intent(inout) :: coulb
240
241 integer :: n1, n2, n3, lx, ly, lz, i
242 real(real64) :: modg2, modgyz, beta, modg2_cutoff, inv_four_mu2, ecut
243 real(real64) :: temp(3), diag_temp(3, 3), metric(3, 3), a(3, 3)
244 real(real64) :: q1, q2, q3, ux, uy, uz, a11, two_a12, two_a13, a22, two_a23, a33, four_a11
245 real(real64) :: singularity_term
246 real(real64), allocatable :: fft_coulb_fs(:,:,:)
247
249
250 if (coulb%mu > m_epsilon) then
251 inv_four_mu2 = m_one/((m_two*coulb%mu)**2)
252 else
253 inv_four_mu2 = m_zero
254 end if
255
256 n1 = max(1, cube%fs_n(1))
257 n2 = max(1, cube%fs_n(2))
258 n3 = max(1, cube%fs_n(3))
259
260 ! Define q+G = 0 term
261 ! Screened short-range coulomb potential (erfc function)
262 if(coulb%mu > m_epsilon .and. abs(coulb%alpha) < m_epsilon) then
263 ! Analytical limit of 4pi/q^2 * (1-beta*exp(-|q|^2/4mu^2))
264 singularity_term = m_four * m_pi * inv_four_mu2 * coulb%beta
265 else ! We use the user-defined value of the singularity
266 ! Long-range screened singularity
267 if(abs(coulb%alpha) > m_epsilon) then
268 singularity_term = coulb%singularity*coulb%alpha + m_four*m_pi*inv_four_mu2 * coulb%beta
269 else
270 singularity_term = coulb%singularity
271 end if
272 ! 4pi/q^2 * alpha
273 end if
274
275 ! store the Fourier transform of the Coulomb interaction
276 safe_allocate(fft_coulb_fs(1:n1, 1:n2, 1:n3))
277
278 ! G vector cutoff
279 temp(1:3) = m_two * m_pi / (cube%rs_n_global(1:3) * cube%spacing(1:3))
280 ecut = fft_get_ecut_from_box(cube%rs_n_global, cube%fs_istart, cube%latt, temp, 3, coulb%qq)
281
282 ! metric = B^T B
283 metric = matmul(transpose(cube%latt%klattice_primitive), cube%latt%klattice_primitive)
284
285 ! A = D (B^T B) D
286 diag_temp = m_zero
287 do i = 1, 3
288 diag_temp(i, i) = temp(i)
289 enddo
290 a = matmul(diag_temp, matmul(metric, diag_temp))
291 a11 = a(1, 1)
292 a22 = a(2, 2)
293 a33 = a(3, 3)
294 two_a12 = m_two * a(1, 2)
295 two_a13 = m_two * a(1, 3)
296 two_a23 = m_two * a(2, 3)
297 four_a11 = m_four * a11
298
299 q1 = coulb%qq(1)
300 q2 = coulb%qq(2)
301 q3 = coulb%qq(3)
302
303 modg2_cutoff = m_two * ecut * 1.001_real64
304
305 do lz = 1, n3
306 ! u=G+q (integer-mode coords)
307 uz = real(cube%fs_ifz(lz), real64) + q3
308
309 do ly = 1, n2
310 uy = real(cube%fs_ify(ly), real64) + q2
311 modgyz = a22*uy*uy + two_a23*uy*uz + a33*uz*uz
312 beta = two_a12*uy + two_a13*uz
313 if (modgyz - beta * beta / four_a11 > modg2_cutoff) then
314 fft_coulb_fs(1:n1, ly, lz) = m_zero
315 cycle
316 end if
317
318 do lx = 1, n1
319 ux = real(cube%fs_ifx(lx), real64) + q1
320 ! Cartesian |G + q|^2
321 modg2 = (a11*ux + beta)*ux + modgyz
322
323 if (modg2 > modg2_cutoff) then
324 fft_coulb_fs(lx, ly, lz) = m_zero
325 else if (modg2 > tol_vanishing_q) then
326 !Screened coulomb potential (erfc function)
327 if (coulb%mu > m_epsilon) then
328 if(abs(coulb%alpha) > m_epsilon) then ! CAM
329 fft_coulb_fs(lx, ly, lz) = m_four * m_pi / modg2 * (coulb%alpha + coulb%beta * exp(-modg2*inv_four_mu2))
330 else ! purely short-range screened
331 fft_coulb_fs(lx, ly, lz) = m_four * m_pi / modg2 * coulb%beta * (-expm1(-modg2 * inv_four_mu2))
332 end if
333 else
334 if (abs(coulb%alpha) > m_epsilon) then ! global screened hybrids
335 fft_coulb_fs(lx, ly, lz) = m_four * m_pi / modg2 * coulb%alpha
336 else ! Bare interaction
337 fft_coulb_fs(lx, ly, lz) = m_four * m_pi / modg2
338 end if
339 end if
340 else ! This is the term q+G = 0
341 fft_coulb_fs(lx, ly, lz) = singularity_term
342 end if
343
344 end do
345 end do
346 end do
347
348 call dfourier_space_op_init(coulb, cube, op_move = fft_coulb_fs)
349
350 safe_deallocate_a(fft_coulb_fs)
351
353
354 end subroutine poisson_fft_build_3d_3d
355
356
361 subroutine poisson_fft_build_3d_3d_hockney(cube, coulb, fullcube)
362 type(cube_t), intent(in) :: cube
363 type(fourier_space_op_t), intent(inout) :: coulb
364 type(cube_t), intent(in) :: fullcube
365
366 integer :: ix, iy, iz, ixx(3), db(3), nfs(3), nrs(3), nfs_s(3), nrs_s(3), dnrs(3)
367 real(real64) :: temp(3), modg2, weight
368 real(real64) :: gg(3)
369 real(real64), allocatable :: fft_Coulb_small_RS(:,:,:,:)
370 real(real64), allocatable :: fft_Coulb_RS(:,:,:,:)
371 complex(real64), allocatable :: fft_Coulb_small_FS(:,:,:,:)
372 complex(real64), allocatable :: fft_Coulb_FS(:,:,:,:)
373 integer, parameter :: howmany = 1
374
376
377 assert(abs(coulb%mu) < m_epsilon)
378 assert(cube%batch_capacity == 1)
379 assert(fullcube%batch_capacity == 1)
380
381 ! dimensions of large boxes
382 nfs(1:3) = fullcube%fs_n_global(1:3)
383 nrs(1:3) = fullcube%rs_n_global(1:3)
384
385 safe_allocate(fft_coulb_fs(1:nfs(1),1:nfs(2),1:nfs(3), 1:howmany))
386 safe_allocate(fft_coulb_rs(1:nrs(1),1:nrs(2),1:nrs(3), 1:howmany))
387
388 ! dimensions of small boxes x_s
389 nfs_s(1:3) = cube%fs_n_global(1:3)
390 nrs_s(1:3) = cube%rs_n_global(1:3)
391
392 safe_allocate(fft_coulb_small_fs(1:nfs_s(1),1:nfs_s(2),1:nfs_s(3), 1:howmany))
393 safe_allocate(fft_coulb_small_rs(1:nrs_s(1),1:nrs_s(2),1:nrs_s(3), 1:howmany))
394
395 ! build full periodic Coulomb potenital in Fourier space
396 fft_coulb_fs = m_zero
397
398 db(1:3) = fullcube%rs_n_global(1:3)
399 temp(1:3) = m_two*m_pi/(db(1:3)*cube%spacing(1:3))
400
401 do iz = 1, nfs(3)
402 ixx(3) = pad_feq(iz, db(3), .true.)
403 do iy = 1, nfs(2)
404 ixx(2) = pad_feq(iy, db(2), .true.)
405 do ix = 1, nfs(1)
406 ixx(1) = pad_feq(ix, db(1), .true.)
407
408 call fft_gg_transform(ixx, temp, 3, cube%latt, coulb%qq, gg, modg2)
409
410 if (abs(modg2) > tol_vanishing_q) then
411 fft_coulb_fs(ix, iy, iz, 1) = m_one/modg2
412 else
413 fft_coulb_fs(ix, iy, iz, 1) = m_zero
414 end if
415 end do
416 end do
417 end do
418
419 ! Full range hybrids weight
420 weight = m_four*m_pi
421 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
422
423 do iz = 1, nfs(3)
424 do iy = 1, nfs(2)
425 do ix = 1, nfs(1)
426 fft_coulb_fs(ix, iy, iz, 1) = weight*fft_coulb_fs(ix, iy, iz, 1)
427 end do
428 end do
429 end do
430
431 ! get periodic Coulomb potential in real space
432 call dfft_backward(fullcube%fft, fft_coulb_fs, fft_coulb_rs)
433
434 ! copy to small box by respecting this pattern
435 ! full periodic coulomb: |abc--------------------------xyz|
436 ! Hockney: |abcxyz|
437 dnrs = nrs - nrs_s
438
439 do iz = 1, nrs_s(3)
440 ixx(3) = iz
441 if (iz > nrs_s(3)/2+1) ixx(3) = ixx(3) + dnrs(3)
442 do iy = 1, nrs_s(2)
443 ixx(2) = iy
444 if (iy > nrs_s(2)/2+1) ixx(2) = ixx(2) + dnrs(2)
445 do ix = 1, nrs_s(1)
446 ixx(1) = ix
447 if (ix > nrs_s(1)/2+1) ixx(1) = ixx(1) + dnrs(1)
448 fft_coulb_small_rs(ix, iy, iz, 1) = fft_coulb_rs(ixx(1),ixx(2),ixx(3), 1)
449 end do
450 end do
451 end do
452 ! make Hockney kernel in Fourier space
453 call dfft_forward(cube%fft, fft_coulb_small_rs, fft_coulb_small_fs)
454 !dummy copy for type conversion
455 fft_coulb_small_rs(1:nfs_s(1),1:nfs_s(2),1:nfs_s(3),1:howmany) = &
456 real( fft_Coulb_small_FS(1:nfs_s(1),1:nfs_s(2),1:nfs_s(3),1:howmany), real64)
457
458
459 ! Restrict array to local part to support pfft
460 ! For FFTW this reduces simply to the full array
461 call dfourier_space_op_init(coulb, cube, &
462 fft_coulb_small_rs(cube%fs_istart(1):cube%fs_istart(1)+cube%fs_n(1), &
463 cube%fs_istart(2):cube%fs_istart(2)+cube%fs_n(2), &
464 cube%fs_istart(3):cube%fs_istart(3)+cube%fs_n(3), 1))
465
466 safe_deallocate_a(fft_coulb_fs)
467 safe_deallocate_a(fft_coulb_rs)
468 safe_deallocate_a(fft_coulb_small_fs)
469 safe_deallocate_a(fft_coulb_small_rs)
470
472
474
475 !-----------------------------------------------------------------
477 subroutine poisson_fft_build_3d_2d(namespace, cube, coulb)
478 type(namespace_t), intent(in) :: namespace
479 type(cube_t), intent(in) :: cube
480 type(fourier_space_op_t), intent(inout) :: coulb
481
482 integer :: ix, iy, iz, ixx(3), db(3)
483 integer :: lx, ly, lz, n1, n2, n3
484 real(real64) :: temp(3), modg2, ecut, weight
485 real(real64) :: gpar, gz, r_c, gg(3), default_r_c
486 real(real64), allocatable :: fft_coulb_FS(:,:,:)
487
489
490 db(1:3) = cube%rs_n_global(1:3)
491
492 assert(abs(coulb%mu) < m_epsilon)
493 ! Full range hybrids weight
494 weight = m_four*m_pi
495 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
496
497
498 !%Variable PoissonCutoffRadius
499 !%Type float
500 !%Section Hamiltonian::Poisson
501 !%Description
502 !% When <tt>PoissonSolver = fft</tt> and <tt>PoissonFFTKernel</tt> is neither <tt>multipole_corrections</tt>
503 !% nor <tt>fft_nocut</tt>,
504 !% this variable controls the distance after which the electron-electron interaction goes to zero.
505 !% A warning will be written if the value is too large and will cause spurious interactions between images.
506 !% The default is half of the FFT box max dimension in a finite direction.
507 !%End
508
509 default_r_c = db(3)*cube%spacing(3)/m_two
510 call get_cutoff(namespace, default_r_c, r_c)
511
512 n1 = max(1, cube%fs_n(1))
513 n2 = max(1, cube%fs_n(2))
514 n3 = max(1, cube%fs_n(3))
515 ! store the Fourier transform of the Coulomb interaction
516 safe_allocate(fft_coulb_fs(1:n1, 1:n2, 1:n3))
517 fft_coulb_fs = m_zero
518
519 temp(1:3) = m_two*m_pi/(db(1:3)*cube%spacing(1:3))
520
521 ecut = fft_get_ecut_from_box(cube%rs_n_global, cube%fs_istart, cube%latt, temp, 2, coulb%qq)
522
523 do lz = 1, n3
524 iz = cube%fs_istart(3) + lz - 1
525 ixx(3) = pad_feq(iz, db(3), .true.)
526 do ly = 1, n2
527 iy = cube%fs_istart(2) + ly - 1
528 ixx(2) = pad_feq(iy, db(2), .true.)
529 do lx = 1, n1
530 ix = cube%fs_istart(1) + lx - 1
531 ixx(1) = pad_feq(ix, db(1), .true.)
532
533 call fft_gg_transform(ixx, temp, 2, cube%latt, coulb%qq, gg, modg2)
534
535 if(sum(gg(1:2)**2) > m_two*ecut*1.001_real64) cycle
536
537 if (abs(modg2) > tol_vanishing_q) then
538 gz = abs(gg(3))
539 gpar = hypot(gg(1), gg(2))
540 ! note: if gpar = 0, then modg2 = gz**2
541 fft_coulb_fs(lx, ly, lz) = poisson_cutoff_3d_2d(gpar,gz,r_c)/modg2
542 else
543 fft_coulb_fs(lx, ly, lz) = -m_half*r_c**2
544 end if
545 fft_coulb_fs(lx, ly, lz) = weight*fft_coulb_fs(lx, ly, lz)
546 end do
547 end do
548
549 end do
550
551 call dfourier_space_op_init(coulb, cube, op_move = fft_coulb_fs)
552
553 safe_deallocate_a(fft_coulb_fs)
555 end subroutine poisson_fft_build_3d_2d
556 !-----------------------------------------------------------------
557
558
559 !-----------------------------------------------------------------
561 subroutine poisson_fft_build_3d_1d(namespace, space, cube, coulb)
562 type(namespace_t), intent(in) :: namespace
563 class(space_t), intent(in) :: space
564 type(cube_t), intent(in) :: cube
565 type(fourier_space_op_t), intent(inout) :: coulb
566
567 type(spline_t) :: cylinder_cutoff_f
568 real(real64), allocatable :: x(:), y(:)
569 integer :: ix, iy, iz, ixx(3), db(3), k, ngp
570 integer :: lx, ly, lz, n1, n2, n3, lxx(3)
571 real(real64) :: temp(3), modg2, xmax, weight
572 real(real64) :: gperp, gx, gy, gz, r_c, gg(3), default_r_c
573 real(real64), allocatable :: fft_coulb_FS(:,:,:)
574
576
577 assert(abs(coulb%mu) < m_epsilon)
578 ! Full range hybrids weight
579 weight = m_four*m_pi
580 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
581
582
583 db(1:3) = cube%rs_n_global(1:3)
584
585 default_r_c = maxval(db(2:3)*cube%spacing(2:3)/m_two)
586 call get_cutoff(namespace, default_r_c, r_c)
587
588 n1 = max(1, cube%fs_n(1))
589 n2 = max(1, cube%fs_n(2))
590 n3 = max(1, cube%fs_n(3))
591 ! store the Fourier transform of the Coulomb interaction
592 safe_allocate(fft_coulb_fs(1:n1, 1:n2, 1:n3))
593 fft_coulb_fs = m_zero
594
595 temp(1:3) = m_two*m_pi/(db(1:3)*cube%spacing(1:3))
596
597 if (.not. space%is_periodic()) then
598 ngp = 8*db(2)
599 safe_allocate(x(1:ngp))
600 safe_allocate(y(1:ngp))
601 end if
602
603 ! Note(Alex) This loop ordering results in bad memory access for fft_Coulb_FS
604 ! It should be refactored
605 do lx = 1, n1
606 ix = cube%fs_istart(1) + lx - 1
607 ixx(1) = pad_feq(ix, db(1), .true.)
608 lxx(1) = ixx(1) - cube%fs_istart(1) + 1
609 gx = temp(1)*ixx(1)
610
611 if (.not. space%is_periodic()) then
612 call spline_init(cylinder_cutoff_f)
613 xmax = norm2(temp(2:3)*db(2:3))/2
614 do k = 1, ngp
615 x(k) = (k-1)*(xmax/(ngp-1))
616 y(k) = poisson_cutoff_3d_1d_finite(gx, x(k), norm2(cube%latt%rlattice_primitive(:, 1)), &
617 maxval(norm2(cube%latt%rlattice_primitive(:, 2:3), dim=1)))
618 end do
619 call spline_fit(ngp, x, y, cylinder_cutoff_f, m_zero)
620 end if
621
622 do ly = 1, n2
623 iy = cube%fs_istart(2) + ly - 1
624 ixx(2) = pad_feq(iy, db(2), .true.)
625 lxx(2) = ixx(2) - cube%fs_istart(2) + 1
626 do lz = 1, n3
627 iz = cube%fs_istart(3) + lz - 1
628 ixx(3) = pad_feq(iz, db(3), .true.)
629 lxx(3) = ixx(3) - cube%fs_istart(3) + 1
630
631 call fft_gg_transform(ixx, temp, 1, cube%latt, coulb%qq, gg, modg2)
632
633 if (abs(modg2) > tol_vanishing_q) then
634 gperp = hypot(gg(2), gg(3))
635 if (space%periodic_dim == 1) then
636 if (gperp > r_c) then
637 fft_coulb_fs(lx, ly, lz) = m_zero
638 else
639 fft_coulb_fs(lx, ly, lz) = poisson_cutoff_3d_1d(abs(gx), gperp, r_c)/modg2
640 end if
641 else if (.not. space%is_periodic()) then
642 gy = gg(2)
643 gz = gg(3)
644 if ((gz >= m_zero) .and. (gy >= m_zero)) then
645 fft_coulb_fs(lx, ly, lz) = spline_eval(cylinder_cutoff_f, gperp)
646 end if
647 if ((gz >= m_zero) .and. (gy < m_zero)) then
648 fft_coulb_fs(lx, ly, lz) = fft_coulb_fs(lx, -lxx(2) + 1, lz)
649 end if
650 if ((gz < m_zero) .and. (gy >= m_zero)) then
651 fft_coulb_fs(lx, ly, lz) = fft_coulb_fs(lx, ly, -lxx(3) + 1)
652 end if
653 if ((gz < m_zero) .and. (gy < m_zero)) then
654 fft_coulb_fs(lx, ly, lz) = fft_coulb_fs(lx, -lxx(2) + 1, -lxx(3) + 1)
655 end if
656 end if
657
658 else
659 if (space%periodic_dim == 1) then
660 fft_coulb_fs(lx, ly, lz) = -(m_half*log(r_c) - m_fourth)*r_c**2
661 else if (.not. space%is_periodic()) then
662 fft_coulb_fs(lx, ly, lz) = poisson_cutoff_3d_1d_finite(m_zero, m_zero, &
663 norm2(cube%latt%rlattice_primitive(:, 1)), maxval(norm2(cube%latt%rlattice_primitive(:, 2:3), dim=1)))
664 end if
665
666 end if
667 fft_coulb_fs(lx, ly, lz) = weight*fft_coulb_fs(lx, ly, lz)
668 end do
669 end do
670
671 if (.not. space%is_periodic()) then
672 call spline_end(cylinder_cutoff_f)
673 end if
674 end do
675
676 call dfourier_space_op_init(coulb, cube, op_move = fft_coulb_fs)
677
678 safe_deallocate_a(fft_coulb_fs)
679 safe_deallocate_a(x)
680 safe_deallocate_a(y)
682 end subroutine poisson_fft_build_3d_1d
683 !-----------------------------------------------------------------
684
685
686 !-----------------------------------------------------------------
688 subroutine poisson_fft_build_3d_0d(namespace, cube, kernel, coulb, is_periodic)
689 type(namespace_t), intent(in) :: namespace
690 type(cube_t), intent(in) :: cube
691 integer, intent(in) :: kernel
692 type(fourier_space_op_t), intent(inout) :: coulb
693 logical, intent(in) :: is_periodic
694
695 integer :: ix, iy, iz, ixx(3), db(3), lx, ly, lz, n1, n2, n3
696 real(real64) :: temp(3), modg2, ecut, weight
697 real(real64) :: r_c, gg(3), default_r_c
698 real(real64), allocatable :: fft_coulb_FS(:,:,:)
699 real(real64) :: axis(3,3)
700
702
703 assert(abs(coulb%mu) < m_epsilon)
704 ! Full range hybrids weight
705 weight = m_four*m_pi
706 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
707
708
709 db(1:3) = cube%rs_n_global(1:3)
710
711 if (kernel /= poisson_fft_kernel_corrected) then
712
713 ! This is the real-space cutoff
714 do ix = 1, 3
715 axis(:,ix) = cube%latt%rlattice_primitive(:, ix) * cube%spacing(ix) * db(ix) / m_two
716 end do
717
718 default_r_c = m_huge
719 do ix = 1, 3
720 iy = mod(ix, 3)+1
721 iz = mod(ix+1, 3)+1
722
723 ! For orthogonal cells, this is determined by the size of the cube
724 if (.not. cube%latt%nonorthogonal) then
725 temp(1:3) = axis(:, ix)
726 default_r_c = min(default_r_c, norm2(temp(1:3)))
727 else
728 ! At the moment, this codepath is only called for DFT+U submesh Poisson solver
729 !
730 ! For non-orthogonal cells, the relevant length is the distance between two planes of
731 ! the parallelepiped. This ensures that we draw a sphere that touches the borders of the box,
732 ! thus avoiding contribution from periodic replicas
733 ! This distance is given by the usual formula of the distance from a point to a plan
734 temp = dcross_product(axis(:, iy), axis(:, iz))
735 temp = temp / norm2(temp)
736 default_r_c = min(default_r_c, dot_product(temp, axis(:, ix)-axis(:, iy)))
737 end if
738 end do
739 call get_cutoff(namespace, default_r_c, r_c)
740 end if
741
742 n1 = max(1, cube%fs_n(1))
743 n2 = max(1, cube%fs_n(2))
744 n3 = max(1, cube%fs_n(3))
745
746 ! store the fourier transform of the Coulomb interaction
747 ! store only the relevant part if PFFT is used
748 safe_allocate(fft_coulb_fs(1:n1,1:n2,1:n3))
749 fft_coulb_fs = m_zero
750
751 temp(1:3) = m_two*m_pi/(db(1:3)*cube%spacing(1:3))
752
753 ecut = fft_get_ecut_from_box(cube%rs_n_global, cube%fs_istart, cube%latt, temp, 3, coulb%qq)
754
755 do lz = 1, n3
756 iz = cube%fs_istart(3) + lz - 1
757 ixx(3) = pad_feq(iz, db(3), .true.)
758 do ly = 1, n2
759 iy = cube%fs_istart(2) + ly - 1
760 ixx(2) = pad_feq(iy, db(2), .true.)
761 do lx = 1, n1
762 ix = cube%fs_istart(1) + lx - 1
763 ixx(1) = pad_feq(ix, db(1), .true.)
764
765 call fft_gg_transform(ixx, temp, 0, cube%latt, coulb%qq, gg, modg2)
766
767 ! At the moment this is only done for periodic space, so for DFT+U Coulomb integrals
768 if(modg2 > m_two*ecut*1.001_real64 .and. is_periodic) cycle
769
770 if (abs(modg2) > tol_vanishing_q) then
771 select case (kernel)
773 fft_coulb_fs(lx, ly, lz) = weight*poisson_cutoff_3d_0d(sqrt(modg2),r_c)/modg2
775 fft_coulb_fs(lx, ly, lz) = weight/modg2
776 end select
777 else
778 select case (kernel)
780 fft_coulb_fs(lx, ly, lz) = weight*r_c**2/m_two
782 fft_coulb_fs(lx, ly, lz) = m_zero
783 end select
784 end if
785 end do
786 end do
787 end do
788
789 call dfourier_space_op_init(coulb, cube, fft_coulb_fs, in_device = (kernel /= poisson_fft_kernel_corrected))
790
791 safe_deallocate_a(fft_coulb_fs)
793 end subroutine poisson_fft_build_3d_0d
794 !-----------------------------------------------------------------
795
796
797 !-----------------------------------------------------------------
799 subroutine poisson_fft_build_2d_0d(namespace, cube, coulb)
800 type(namespace_t), intent(in) :: namespace
801 type(cube_t), intent(in) :: cube
802 type(fourier_space_op_t), intent(inout) :: coulb
803
804 type(spline_t) :: besselintf
805 integer :: i, ix, iy, ixx(2), db(2), npoints
806 real(real64) :: temp(2), vec, r_c, maxf, dk, default_r_c, weight
807 real(real64), allocatable :: x(:), y(:)
808 real(real64), allocatable :: fft_coulb_FS(:,:,:)
809
811
812 assert(abs(coulb%mu) < m_epsilon)
813 ! Full range hybrids weight
814 weight = m_one
815 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
816
817 db(1:2) = cube%rs_n_global(1:2)
818
819 default_r_c = maxval(db(1:2)*cube%spacing(1:2)/m_two)
820 call get_cutoff(namespace, default_r_c, r_c)
821
822 call spline_init(besselintf)
823
824 ! store the fourier transform of the Coulomb interaction
825 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
826 fft_coulb_fs = m_zero
827 temp(1:2) = m_two*m_pi/(db(1:2)*cube%spacing(1:2))
828
829 maxf = r_c * norm2(temp(1:2)*db(1:2))/2
830 dk = 0.25_real64 ! This seems to be reasonable.
831 npoints = nint(maxf/dk)
832 safe_allocate(x(1:npoints))
833 safe_allocate(y(1:npoints))
834 x(1) = m_zero
835 y(1) = m_zero
836 do i = 2, npoints
837 x(i) = (i-1) * maxf / (npoints-1)
838 y(i) = y(i-1) + poisson_cutoff_2d_0d(x(i-1), x(i))
839 end do
840 call spline_fit(npoints, x, y, besselintf, m_zero)
841
842 do iy = 1, cube%fs_n_global(2)
843 ixx(2) = pad_feq(iy, db(2), .true.)
844 do ix = 1, cube%fs_n_global(1)
845 ixx(1) = pad_feq(ix, db(1), .true.)
846 vec = norm2(temp(1:2)*ixx(1:2))
847 ! extra check to avoid extrapolation which leads to an error in gsl
848 if (vec*r_c >= x(npoints)) then
849 fft_coulb_fs(ix, iy, 1) = weight * y(npoints)
850 else if (vec > m_zero) then
851 fft_coulb_fs(ix, iy, 1) = weight * (m_two * m_pi / vec) * spline_eval(besselintf, vec*r_c)
852 else
853 fft_coulb_fs(ix, iy, 1) = weight * m_two * m_pi * r_c
854 end if
855 end do
856 end do
857
858 call dfourier_space_op_init(coulb, cube, op_move = fft_coulb_fs)
859
860 safe_deallocate_a(fft_coulb_fs)
861 safe_deallocate_a(x)
862 safe_deallocate_a(y)
863 call spline_end(besselintf)
865 end subroutine poisson_fft_build_2d_0d
866 !-----------------------------------------------------------------
867
868
869 !-----------------------------------------------------------------
871 subroutine poisson_fft_build_2d_1d(namespace, cube, coulb)
872 type(namespace_t), intent(in) :: namespace
873 type(cube_t), intent(in) :: cube
874 type(fourier_space_op_t), intent(inout) :: coulb
875
876 integer :: ix, iy, ixx(2), db(2)
877 real(real64) :: temp(2), r_c, gx, gy, default_r_c, weight
878 real(real64), allocatable :: fft_coulb_FS(:,:,:)
879
881
882 assert(abs(coulb%mu) < m_epsilon)
883 ! Full range hybrids weight
884 weight = m_one
885 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
886
887
888 db(1:2) = cube%rs_n_global(1:2)
889
890 default_r_c = db(2)*cube%spacing(2)/m_two
891 call get_cutoff(namespace, default_r_c, r_c)
892
893 ! store the fourier transform of the Coulomb interaction
894 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
895 fft_coulb_fs = m_zero
896 temp(1:2) = m_two*m_pi/(db(1:2)*cube%spacing(1:2))
897
898 ! First, the term ix = 0 => gx = 0.
899 fft_coulb_fs(1, 1, 1) = -m_four * r_c * (log(r_c)-m_one)
900 do iy = 2, cube%fs_n_global(2)
901 ixx(2) = pad_feq(iy, db(2), .true.)
902 gy = temp(2)*ixx(2)
903 fft_coulb_fs(1, iy, 1) = -m_four * poisson_cutoff_intcoslog(r_c, gy, m_one) * weight
904 end do
905
906 do ix = 2, cube%fs_n_global(1)
907 ixx(1) = pad_feq(ix, db(1), .true.)
908 gx = temp(1)*ixx(1)
909 do iy = 1, cube%fs_n_global(2)
910 ixx(2) = pad_feq(iy, db(2), .true.)
911 gy = temp(2)*ixx(2)
912 fft_coulb_fs(ix, iy, 1) = poisson_cutoff_2d_1d(gy, gx, r_c) * weight
913 end do
914 end do
915
916 call dfourier_space_op_init(coulb, cube, op_move = fft_coulb_fs)
917
918 safe_deallocate_a(fft_coulb_fs)
919
921 end subroutine poisson_fft_build_2d_1d
922 !-----------------------------------------------------------------
923
924
925 !-----------------------------------------------------------------
927 subroutine poisson_fft_build_2d_2d(cube, coulb)
928 type(cube_t), intent(in) :: cube
929 type(fourier_space_op_t), intent(inout) :: coulb
930
931 integer :: ix, iy, ixx(2), db(2)
932 real(real64) :: temp(2), vec, weight
933 real(real64), allocatable :: fft_coulb_FS(:,:,:)
934
936
937 assert(abs(coulb%mu) < m_epsilon)
938 ! Full range hybrids weight
939 weight = m_one
940 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
941
942 db(1:2) = cube%rs_n_global(1:2)
943
944 ! store the fourier transform of the Coulomb interaction
945 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
946 fft_coulb_fs = m_zero
947 temp(1:2) = m_two*m_pi/(db(1:2)*cube%spacing(1:2))
948
949 do iy = 1, cube%fs_n_global(2)
950 ixx(2) = pad_feq(iy, db(2), .true.)
951 do ix = 1, cube%fs_n_global(1)
952 ixx(1) = pad_feq(ix, db(1), .true.)
953 vec = sqrt((temp(1) * ixx(1))**2 + (temp(2) * ixx(2))**2)
954 if (vec > m_zero) fft_coulb_fs(ix, iy, 1) = m_two * m_pi / vec * weight
955 end do
956 end do
957
958 call dfourier_space_op_init(coulb, cube, op_move = fft_coulb_fs)
959
960 safe_deallocate_a(fft_coulb_fs)
962 end subroutine poisson_fft_build_2d_2d
963 !-----------------------------------------------------------------
964
965
966 !-----------------------------------------------------------------
967 subroutine poisson_fft_build_1d_1d(cube, coulb, poisson_soft_coulomb_param)
968 type(cube_t), intent(in) :: cube
969 type(fourier_space_op_t), intent(inout) :: coulb
970 real(real64), intent(in) :: poisson_soft_coulomb_param
971
972 integer :: ix, ixx
973 real(real64) :: g, weight
974 real(real64), allocatable :: fft_coulb_fs(:, :, :)
975
977
978 assert(abs(coulb%mu) < m_epsilon)
979 ! Full range hybrids weight
980 weight = m_one
981 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
982
983 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
984 fft_coulb_fs = m_zero
985
986 ! Fourier transform of Soft Coulomb interaction.
987 do ix = 1, cube%fs_n_global(1)
988 ixx = pad_feq(ix, cube%rs_n_global(1), .true.)
989 g = (ixx + coulb%qq(1))*m_two*m_pi/abs(cube%latt%rlattice(1,1))
990 if (abs(g) > tol_vanishing_q) then
991 fft_coulb_fs(ix, 1, 1) = m_two * loct_bessel_k0(poisson_soft_coulomb_param*abs(g)) * weight
992 else
993 fft_coulb_fs(ix, 1, 1) = coulb%singularity * m_two * weight
994 end if
995 end do
996
997 call dfourier_space_op_init(coulb, cube, op_move = fft_coulb_fs)
998 safe_deallocate_a(fft_coulb_fs)
999
1001 end subroutine poisson_fft_build_1d_1d
1002 !-----------------------------------------------------------------
1003
1004
1005 !-----------------------------------------------------------------
1006 subroutine poisson_fft_build_1d_0d(namespace, cube, coulb, poisson_soft_coulomb_param)
1007 type(namespace_t), intent(in) :: namespace
1008 type(cube_t), intent(in) :: cube
1009 type(fourier_space_op_t), intent(inout) :: coulb
1010 real(real64), intent(in) :: poisson_soft_coulomb_param
1011
1012 integer :: box(1), ixx(1), ix
1013 real(real64) :: temp(1), g, r_c, default_r_c, weight
1014 real(real64), allocatable :: fft_coulb_fs(:, :, :)
1015
1016 push_sub(poisson_fft_build_1d_0d)
1017
1018 assert(abs(coulb%mu) < m_epsilon)
1019 ! Full range hybrids weight
1020 weight = m_one
1021 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
1023 box(1:1) = cube%rs_n_global(1:1)
1024
1025 default_r_c = box(1)*cube%spacing(1)/m_two
1026 call get_cutoff(namespace, default_r_c, r_c)
1027
1028 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
1029 fft_coulb_fs = m_zero
1030 temp(1:1) = m_two*m_pi/(box(1:1)*cube%spacing(1:1))
1031
1032 ! Fourier transform of Soft Coulomb interaction.
1033 do ix = 1, cube%fs_n_global(1)
1034 ixx(1) = pad_feq(ix, box(1), .true.)
1035 g = temp(1)*ixx(1)
1036 fft_coulb_fs(ix, 1, 1) = poisson_cutoff_1d_0d(g, poisson_soft_coulomb_param, r_c) * weight
1037 end do
1038
1039 call dfourier_space_op_init(coulb, cube, op_move = fft_coulb_fs)
1040 safe_deallocate_a(fft_coulb_fs)
1041
1043 end subroutine poisson_fft_build_1d_0d
1044 !-----------------------------------------------------------------
1045
1046
1047 !-----------------------------------------------------------------
1048 subroutine poisson_fft_end(this)
1049 type(poisson_fft_t), intent(inout) :: this
1050
1051 push_sub(poisson_fft_end)
1052
1053 call fourier_space_op_end(this%coulb)
1054
1055 pop_sub(poisson_fft_end)
1056 end subroutine poisson_fft_end
1057
1058#include "undef.F90"
1059#include "real.F90"
1060#include "poisson_fft_inc.F90"
1061#include "undef.F90"
1062#include "complex.F90"
1063#include "poisson_fft_inc.F90"
1064
1065
1066end module poisson_fft_oct_m
1067
1068!! Local Variables:
1069!! mode: f90
1070!! coding: utf-8
1071!! 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:120
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:1055
pure integer function, public pad_feq(ii, nn, mode)
convert between array index and G-vector
Definition: fft.F90:914
pure subroutine, public fft_gg_transform(gg_in, temp, periodic_dim, latt, qq, gg, modg2)
Convert FFT grid index into the Cartesian reciprocal-space vector .
Definition: fft.F90:1010
subroutine, public fourier_space_op_end(this)
subroutine, public dfourier_space_op_init(this, cube, op, in_device, op_move)
The operator can be provided either through op (which is copied) or through op_move (an allocatable t...
real(real64), parameter, public m_two
Definition: global.F90:202
real(real64), parameter, public m_huge
Definition: global.F90:218
real(real64), parameter, public m_zero
Definition: global.F90:200
real(real64), parameter, public m_four
Definition: global.F90:204
real(real64), parameter, public m_pi
some mathematical constants
Definition: global.F90:198
real(real64), parameter, public m_fourth
Definition: global.F90:209
real(real64), parameter, public m_epsilon
Definition: global.F90:216
real(real64), parameter, public m_half
Definition: global.F90:206
real(real64), parameter, public m_one
Definition: global.F90:201
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:117
pure real(real64) function, dimension(1:3), public dcross_product(a, b)
Definition: math.F90:1909
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
subroutine, public messages_warning(no_lines, all_nodes, namespace)
Definition: messages.F90:525
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:162
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:410
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:594
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, public dpoisson_fft_solve_batch(this, mesh, cube, pot, rho, mesh_cube_map, average_to_zero, kernel, sm, pot_buffer, rho_buffer, count)
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)
Compute the Coulomb kernel in reciprocal space, for a 3D FFT grid.
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
subroutine, public zpoisson_fft_solve_batch(this, mesh, cube, pot, rho, mesh_cube_map, average_to_zero, kernel, sm, pot_buffer, rho_buffer, count)
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:134
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)