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 use xc_cam_oct_m, only: cam_null
46
47 implicit none
48 private
49 public :: &
58
59 integer, public, parameter :: &
60 POISSON_FFT_KERNEL_NONE = -1, &
67
68 type poisson_fft_t
69 ! Components are public by default
70 type(fourier_space_op_t) :: coulb
71 integer :: kernel
72 real(real64) :: soft_coulb_param
73 end type poisson_fft_t
74
75 real(real64), parameter :: TOL_VANISHING_Q = 1e-6_real64
76contains
77
78 subroutine poisson_fft_init(this, namespace, space, cube, kernel, soft_coulb_param, fullcube)
79 type(poisson_fft_t), intent(out) :: this
80 type(namespace_t), intent(in) :: namespace
81 class(space_t), intent(in) :: space
82 type(cube_t), intent(inout) :: cube
83 integer, intent(in) :: kernel
84 real(real64), optional, intent(in) :: soft_coulb_param
85 type(cube_t), optional, intent(in) :: fullcube
86
87 real(real64) :: qvector(1:space%dim)
88
89 push_sub(poisson_fft_init)
90
91 this%kernel = kernel
92 this%soft_coulb_param = optional_default(soft_coulb_param, m_zero)
93 qvector = m_zero
94 call this%coulb%init(space, qvector, cam_null)
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())
165 call poisson_fft_build_3d_1d(namespace, space, cube, coulb)
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
211 function poisson_fft_singularity_3d(coulb) result(singularity_term)
212 type(fourier_space_op_t), intent(in) :: coulb
213
214 real(real64) :: singularity_term
215
216 real(real64) :: inv_four_mu2
217
219
220 if (coulb%mu > m_epsilon) then
221 inv_four_mu2 = m_one/((m_two*coulb%mu)**2)
222 else
223 inv_four_mu2 = m_zero
224 end if
225
226 ! Screened short-range coulomb potential (erfc function)
227 if (coulb%mu > m_epsilon .and. abs(coulb%alpha) < m_epsilon) then
228 ! Analytical limit of 4pi/q^2 * (1-beta*exp(-|q|^2/4mu^2))
229 singularity_term = m_four * m_pi * inv_four_mu2 * coulb%beta
230 else ! We use the user-defined value of the singularity
231 ! Long-range screened singularity
232 if (abs(coulb%alpha) > m_epsilon) then
233 singularity_term = coulb%singularity*coulb%alpha + m_four * m_pi * inv_four_mu2 * coulb%beta
234 else
235 singularity_term = coulb%singularity
236 end if
237 ! 4pi/q^2 * alpha
238 end if
239
241
242 end function poisson_fft_singularity_3d
243
268 subroutine poisson_fft_build_3d_3d(cube, coulb)
269 type(cube_t), intent(in) :: cube
270 type(fourier_space_op_t), intent(inout) :: coulb
271
272 integer :: n1, n2, n3, lx, ly, lz, i
273 real(real64) :: modg2, modgyz, beta, modg2_cutoff, inv_four_mu2, ecut
274 real(real64) :: temp(3), diag_temp(3, 3), metric(3, 3), a(3, 3)
275 real(real64) :: q1, q2, q3, ux, uy, uz, a11, two_a12, two_a13, a22, two_a23, a33, four_a11
276 real(real64) :: singularity_term
277 real(real64), allocatable :: fft_coulb_fs(:,:,:)
278
280
281 if (coulb%mu > m_epsilon) then
282 inv_four_mu2 = m_one/((m_two*coulb%mu)**2)
283 else
284 inv_four_mu2 = m_zero
285 end if
286
287 n1 = max(1, cube%fs_n(1))
288 n2 = max(1, cube%fs_n(2))
289 n3 = max(1, cube%fs_n(3))
290
291 ! Define q+G = 0 term
292 singularity_term = poisson_fft_singularity_3d(coulb)
293
294 ! store the Fourier transform of the Coulomb interaction
295 safe_allocate(fft_coulb_fs(1:n1, 1:n2, 1:n3))
296
297 ! G vector cutoff
298 temp = m_two * m_pi / cube%length()
299 ecut = fft_get_ecut_from_box(cube%rs_n_global, cube%fs_istart, cube%latt, temp, 3, coulb%qq)
300
301 ! metric = B^T B
302 metric = matmul(transpose(cube%latt%klattice_primitive), cube%latt%klattice_primitive)
303
304 ! A = D (B^T B) D
305 diag_temp = m_zero
306 do i = 1, 3
307 diag_temp(i, i) = temp(i)
308 enddo
309 a = matmul(diag_temp, matmul(metric, diag_temp))
310 a11 = a(1, 1)
311 a22 = a(2, 2)
312 a33 = a(3, 3)
313 two_a12 = m_two * a(1, 2)
314 two_a13 = m_two * a(1, 3)
315 two_a23 = m_two * a(2, 3)
316 four_a11 = m_four * a11
317
318 q1 = coulb%qq(1)
319 q2 = coulb%qq(2)
320 q3 = coulb%qq(3)
321
322 modg2_cutoff = m_two * ecut * 1.001_real64
323
324 do lz = 1, n3
325 ! u=G+q (integer-mode coords)
326 uz = real(cube%fs_ifz(lz), real64) + q3
327
328 do ly = 1, n2
329 uy = real(cube%fs_ify(ly), real64) + q2
330 modgyz = a22*uy*uy + two_a23*uy*uz + a33*uz*uz
331 beta = two_a12*uy + two_a13*uz
332 if (modgyz - beta * beta / four_a11 > modg2_cutoff) then
333 fft_coulb_fs(1:n1, ly, lz) = m_zero
334 cycle
335 end if
336
337 do lx = 1, n1
338 ux = real(cube%fs_ifx(lx), real64) + q1
339 ! Cartesian |G + q|^2
340 modg2 = (a11*ux + beta)*ux + modgyz
341
342 if (modg2 > modg2_cutoff) then
343 fft_coulb_fs(lx, ly, lz) = m_zero
344 else if (modg2 > tol_vanishing_q) then
345 !Screened coulomb potential (erfc function)
346 if (coulb%mu > m_epsilon) then
347 if(abs(coulb%alpha) > m_epsilon) then ! CAM
348 fft_coulb_fs(lx, ly, lz) = m_four * m_pi / modg2 * (coulb%alpha + coulb%beta * exp(-modg2*inv_four_mu2))
349 else ! purely short-range screened
350 fft_coulb_fs(lx, ly, lz) = m_four * m_pi / modg2 * coulb%beta * (-expm1(-modg2 * inv_four_mu2))
351 end if
352 else
353 if (abs(coulb%alpha) > m_epsilon) then ! global screened hybrids
354 fft_coulb_fs(lx, ly, lz) = m_four * m_pi / modg2 * coulb%alpha
355 else ! Bare interaction
356 fft_coulb_fs(lx, ly, lz) = m_four * m_pi / modg2
357 end if
358 end if
359 else ! This is the term q+G = 0
360 fft_coulb_fs(lx, ly, lz) = singularity_term
361 end if
362
363 end do
364 end do
365 end do
366
367 call coulb%dset_op(cube, op_move = fft_coulb_fs)
368
369 safe_deallocate_a(fft_coulb_fs)
370
372
373 end subroutine poisson_fft_build_3d_3d
374
375
380 subroutine poisson_fft_build_3d_3d_hockney(cube, coulb, fullcube)
381 type(cube_t), intent(in) :: cube
382 type(fourier_space_op_t), intent(inout) :: coulb
383 type(cube_t), intent(in) :: fullcube
384
385 integer :: ix, iy, iz, ixx(3), db(3), nfs(3), nrs(3), nfs_s(3), nrs_s(3), dnrs(3)
386 real(real64) :: temp(3), modg2, weight
387 real(real64) :: gg(3)
388 real(real64), allocatable :: fft_Coulb_small_RS(:,:,:,:)
389 real(real64), allocatable :: fft_Coulb_RS(:,:,:,:)
390 complex(real64), allocatable :: fft_Coulb_small_FS(:,:,:,:)
391 complex(real64), allocatable :: fft_Coulb_FS(:,:,:,:)
392 integer, parameter :: howmany = 1
393
395
396 assert(abs(coulb%mu) < m_epsilon)
397 assert(cube%batch_capacity == 1)
398 assert(fullcube%batch_capacity == 1)
399
400 ! dimensions of large boxes
401 nfs(1:3) = fullcube%fs_n_global(1:3)
402 nrs(1:3) = fullcube%rs_n_global(1:3)
403
404 safe_allocate(fft_coulb_fs(1:nfs(1),1:nfs(2),1:nfs(3), 1:howmany))
405 safe_allocate(fft_coulb_rs(1:nrs(1),1:nrs(2),1:nrs(3), 1:howmany))
406
407 ! dimensions of small boxes x_s
408 nfs_s(1:3) = cube%fs_n_global(1:3)
409 nrs_s(1:3) = cube%rs_n_global(1:3)
410
411 safe_allocate(fft_coulb_small_fs(1:nfs_s(1),1:nfs_s(2),1:nfs_s(3), 1:howmany))
412 safe_allocate(fft_coulb_small_rs(1:nrs_s(1),1:nrs_s(2),1:nrs_s(3), 1:howmany))
413
414 ! build full periodic Coulomb potenital in Fourier space
415 fft_coulb_fs = m_zero
416
417 db(1:3) = fullcube%rs_n_global(1:3)
418 temp(1:3) = m_two*m_pi/(db(1:3)*cube%spacing(1:3))
419
420 do iz = 1, nfs(3)
421 ixx(3) = pad_feq(iz, db(3), .true.)
422 do iy = 1, nfs(2)
423 ixx(2) = pad_feq(iy, db(2), .true.)
424 do ix = 1, nfs(1)
425 ixx(1) = pad_feq(ix, db(1), .true.)
426
427 call fft_gg_transform(ixx, temp, 3, cube%latt, coulb%qq, gg, modg2)
428
429 if (abs(modg2) > tol_vanishing_q) then
430 fft_coulb_fs(ix, iy, iz, 1) = m_one/modg2
431 else
432 fft_coulb_fs(ix, iy, iz, 1) = m_zero
433 end if
434 end do
435 end do
436 end do
437
438 ! Full range hybrids weight
439 weight = m_four*m_pi
440 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
441
442 do iz = 1, nfs(3)
443 do iy = 1, nfs(2)
444 do ix = 1, nfs(1)
445 fft_coulb_fs(ix, iy, iz, 1) = weight*fft_coulb_fs(ix, iy, iz, 1)
446 end do
447 end do
448 end do
449
450 ! get periodic Coulomb potential in real space
451 call dfft_backward(fullcube%fft, fft_coulb_fs, fft_coulb_rs)
452
453 ! copy to small box by respecting this pattern
454 ! full periodic coulomb: |abc--------------------------xyz|
455 ! Hockney: |abcxyz|
456 dnrs = nrs - nrs_s
457
458 do iz = 1, nrs_s(3)
459 ixx(3) = iz
460 if (iz > nrs_s(3)/2+1) ixx(3) = ixx(3) + dnrs(3)
461 do iy = 1, nrs_s(2)
462 ixx(2) = iy
463 if (iy > nrs_s(2)/2+1) ixx(2) = ixx(2) + dnrs(2)
464 do ix = 1, nrs_s(1)
465 ixx(1) = ix
466 if (ix > nrs_s(1)/2+1) ixx(1) = ixx(1) + dnrs(1)
467 fft_coulb_small_rs(ix, iy, iz, 1) = fft_coulb_rs(ixx(1),ixx(2),ixx(3), 1)
468 end do
469 end do
470 end do
471 ! make Hockney kernel in Fourier space
472 call dfft_forward(cube%fft, fft_coulb_small_rs, fft_coulb_small_fs)
473 !dummy copy for type conversion
474 fft_coulb_small_rs(1:nfs_s(1),1:nfs_s(2),1:nfs_s(3),1:howmany) = &
475 real( fft_Coulb_small_FS(1:nfs_s(1),1:nfs_s(2),1:nfs_s(3),1:howmany), real64)
476
477
478 ! Restrict array to local part to support pfft
479 ! For FFTW this reduces simply to the full array
480 call coulb%dset_op(cube, &
481 fft_coulb_small_rs(cube%fs_istart(1):cube%fs_istart(1)+cube%fs_n(1), &
482 cube%fs_istart(2):cube%fs_istart(2)+cube%fs_n(2), &
483 cube%fs_istart(3):cube%fs_istart(3)+cube%fs_n(3), 1))
484
485 safe_deallocate_a(fft_coulb_fs)
486 safe_deallocate_a(fft_coulb_rs)
487 safe_deallocate_a(fft_coulb_small_fs)
488 safe_deallocate_a(fft_coulb_small_rs)
489
491
493
494 !-----------------------------------------------------------------
496 subroutine poisson_fft_build_3d_2d(namespace, cube, coulb)
497 type(namespace_t), intent(in) :: namespace
498 type(cube_t), intent(in) :: cube
499 type(fourier_space_op_t), intent(inout) :: coulb
500
501 integer :: ix, iy, iz, ixx(3), db(3)
502 integer :: lx, ly, lz, n1, n2, n3
503 real(real64) :: temp(3), modg2, ecut, weight
504 real(real64) :: gpar, gz, r_c, gg(3), default_r_c
505 real(real64), allocatable :: fft_coulb_FS(:,:,:)
506
508
509 db(1:3) = cube%rs_n_global(1:3)
510
511 assert(abs(coulb%mu) < m_epsilon)
512 ! Full range hybrids weight
513 weight = m_four*m_pi
514 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
515
516
517 !%Variable PoissonCutoffRadius
518 !%Type float
519 !%Section Hamiltonian::Poisson
520 !%Description
521 !% When <tt>PoissonSolver = fft</tt> and <tt>PoissonFFTKernel</tt> is neither <tt>multipole_corrections</tt>
522 !% nor <tt>fft_nocut</tt>,
523 !% this variable controls the distance after which the electron-electron interaction goes to zero.
524 !% A warning will be written if the value is too large and will cause spurious interactions between images.
525 !% The default is half of the FFT box max dimension in a finite direction.
526 !%End
527
528 default_r_c = db(3)*cube%spacing(3)/m_two
529 call get_cutoff(namespace, default_r_c, r_c)
530
531 n1 = max(1, cube%fs_n(1))
532 n2 = max(1, cube%fs_n(2))
533 n3 = max(1, cube%fs_n(3))
534 ! store the Fourier transform of the Coulomb interaction
535 safe_allocate(fft_coulb_fs(1:n1, 1:n2, 1:n3))
536 fft_coulb_fs = m_zero
537
538 temp(1:3) = m_two*m_pi/(db(1:3)*cube%spacing(1:3))
539
540 ecut = fft_get_ecut_from_box(cube%rs_n_global, cube%fs_istart, cube%latt, temp, 2, coulb%qq)
541
542 do lz = 1, n3
543 iz = cube%fs_istart(3) + lz - 1
544 ixx(3) = pad_feq(iz, db(3), .true.)
545 do ly = 1, n2
546 iy = cube%fs_istart(2) + ly - 1
547 ixx(2) = pad_feq(iy, db(2), .true.)
548 do lx = 1, n1
549 ix = cube%fs_istart(1) + lx - 1
550 ixx(1) = pad_feq(ix, db(1), .true.)
551
552 call fft_gg_transform(ixx, temp, 2, cube%latt, coulb%qq, gg, modg2)
553
554 if(sum(gg(1:2)**2) > m_two*ecut*1.001_real64) cycle
555
556 if (abs(modg2) > tol_vanishing_q) then
557 gz = abs(gg(3))
558 gpar = hypot(gg(1), gg(2))
559 ! note: if gpar = 0, then modg2 = gz**2
560 fft_coulb_fs(lx, ly, lz) = poisson_cutoff_3d_2d(gpar,gz,r_c)/modg2
561 else
562 fft_coulb_fs(lx, ly, lz) = -m_half*r_c**2
563 end if
564 fft_coulb_fs(lx, ly, lz) = weight*fft_coulb_fs(lx, ly, lz)
565 end do
566 end do
567
568 end do
569
570 call coulb%dset_op(cube, op_move = fft_coulb_fs)
571
572 safe_deallocate_a(fft_coulb_fs)
574 end subroutine poisson_fft_build_3d_2d
575 !-----------------------------------------------------------------
576
577
578 !-----------------------------------------------------------------
580 subroutine poisson_fft_build_3d_1d(namespace, space, cube, coulb)
581 type(namespace_t), intent(in) :: namespace
582 class(space_t), intent(in) :: space
583 type(cube_t), intent(in) :: cube
584 type(fourier_space_op_t), intent(inout) :: coulb
585
586 type(spline_t) :: cylinder_cutoff_f
587 real(real64), allocatable :: x(:), y(:)
588 integer :: ix, iy, iz, ixx(3), db(3), k, ngp
589 integer :: lx, ly, lz, n1, n2, n3, lxx(3)
590 real(real64) :: temp(3), modg2, xmax, weight
591 real(real64) :: gperp, gx, gy, gz, r_c, gg(3), default_r_c
592 real(real64), allocatable :: fft_coulb_FS(:,:,:)
593
595
596 assert(abs(coulb%mu) < m_epsilon)
597 ! Full range hybrids weight
598 weight = m_four*m_pi
599 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
600
601
602 db(1:3) = cube%rs_n_global(1:3)
603
604 default_r_c = maxval(db(2:3)*cube%spacing(2:3)/m_two)
605 call get_cutoff(namespace, default_r_c, r_c)
606
607 n1 = max(1, cube%fs_n(1))
608 n2 = max(1, cube%fs_n(2))
609 n3 = max(1, cube%fs_n(3))
610 ! store the Fourier transform of the Coulomb interaction
611 safe_allocate(fft_coulb_fs(1:n1, 1:n2, 1:n3))
612 fft_coulb_fs = m_zero
613
614 temp(1:3) = m_two*m_pi/(db(1:3)*cube%spacing(1:3))
615
616 if (.not. space%is_periodic()) then
617 ngp = 8*db(2)
618 safe_allocate(x(1:ngp))
619 safe_allocate(y(1:ngp))
620 end if
621
622 ! Note(Alex) This loop ordering results in bad memory access for fft_Coulb_FS
623 ! It should be refactored
624 do lx = 1, n1
625 ix = cube%fs_istart(1) + lx - 1
626 ixx(1) = pad_feq(ix, db(1), .true.)
627 lxx(1) = ixx(1) - cube%fs_istart(1) + 1
628 gx = temp(1)*ixx(1)
629
630 if (.not. space%is_periodic()) then
631 call spline_init(cylinder_cutoff_f)
632 xmax = norm2(temp(2:3)*db(2:3))/2
633 do k = 1, ngp
634 x(k) = (k-1)*(xmax/(ngp-1))
635 y(k) = poisson_cutoff_3d_1d_finite(gx, x(k), norm2(cube%latt%rlattice_primitive(:, 1)), &
636 maxval(norm2(cube%latt%rlattice_primitive(:, 2:3), dim=1)))
637 end do
638 call spline_fit(ngp, x, y, cylinder_cutoff_f, m_zero)
639 end if
640
641 do ly = 1, n2
642 iy = cube%fs_istart(2) + ly - 1
643 ixx(2) = pad_feq(iy, db(2), .true.)
644 lxx(2) = ixx(2) - cube%fs_istart(2) + 1
645 do lz = 1, n3
646 iz = cube%fs_istart(3) + lz - 1
647 ixx(3) = pad_feq(iz, db(3), .true.)
648 lxx(3) = ixx(3) - cube%fs_istart(3) + 1
649
650 call fft_gg_transform(ixx, temp, 1, cube%latt, coulb%qq, gg, modg2)
651
652 if (abs(modg2) > tol_vanishing_q) then
653 gperp = hypot(gg(2), gg(3))
654 if (space%periodic_dim == 1) then
655 if (gperp > r_c) then
656 fft_coulb_fs(lx, ly, lz) = m_zero
657 else
658 fft_coulb_fs(lx, ly, lz) = poisson_cutoff_3d_1d(abs(gx), gperp, r_c)/modg2
659 end if
660 else if (.not. space%is_periodic()) then
661 gy = gg(2)
662 gz = gg(3)
663 if ((gz >= m_zero) .and. (gy >= m_zero)) then
664 fft_coulb_fs(lx, ly, lz) = spline_eval(cylinder_cutoff_f, gperp)
665 end if
666 if ((gz >= m_zero) .and. (gy < m_zero)) then
667 fft_coulb_fs(lx, ly, lz) = fft_coulb_fs(lx, -lxx(2) + 1, lz)
668 end if
669 if ((gz < m_zero) .and. (gy >= m_zero)) then
670 fft_coulb_fs(lx, ly, lz) = fft_coulb_fs(lx, ly, -lxx(3) + 1)
671 end if
672 if ((gz < m_zero) .and. (gy < m_zero)) then
673 fft_coulb_fs(lx, ly, lz) = fft_coulb_fs(lx, -lxx(2) + 1, -lxx(3) + 1)
674 end if
675 end if
676
677 else
678 if (space%periodic_dim == 1) then
679 fft_coulb_fs(lx, ly, lz) = -(m_half*log(r_c) - m_fourth)*r_c**2
680 else if (.not. space%is_periodic()) then
681 fft_coulb_fs(lx, ly, lz) = poisson_cutoff_3d_1d_finite(m_zero, m_zero, &
682 norm2(cube%latt%rlattice_primitive(:, 1)), maxval(norm2(cube%latt%rlattice_primitive(:, 2:3), dim=1)))
683 end if
684
685 end if
686 fft_coulb_fs(lx, ly, lz) = weight*fft_coulb_fs(lx, ly, lz)
687 end do
688 end do
689
690 if (.not. space%is_periodic()) then
691 call spline_end(cylinder_cutoff_f)
692 end if
693 end do
694
695 call coulb%dset_op(cube, op_move = fft_coulb_fs)
696
697 safe_deallocate_a(fft_coulb_fs)
698 safe_deallocate_a(x)
699 safe_deallocate_a(y)
701 end subroutine poisson_fft_build_3d_1d
702 !-----------------------------------------------------------------
703
704
705 !-----------------------------------------------------------------
707 subroutine poisson_fft_build_3d_0d(namespace, cube, kernel, coulb, is_periodic)
708 type(namespace_t), intent(in) :: namespace
709 type(cube_t), intent(in) :: cube
710 integer, intent(in) :: kernel
711 type(fourier_space_op_t), intent(inout) :: coulb
712 logical, intent(in) :: is_periodic
713
714 integer :: ix, iy, iz, ixx(3), db(3), lx, ly, lz, n1, n2, n3
715 real(real64) :: temp(3), modg2, ecut, weight
716 real(real64) :: r_c, gg(3), default_r_c
717 real(real64), allocatable :: fft_coulb_FS(:,:,:)
718 real(real64) :: axis(3,3)
719
721
722 assert(abs(coulb%mu) < m_epsilon)
723 ! Full range hybrids weight
724 weight = m_four*m_pi
725 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
726
727
728 db(1:3) = cube%rs_n_global(1:3)
729
730 if (kernel /= poisson_fft_kernel_corrected) then
731
732 ! This is the real-space cutoff
733 do ix = 1, 3
734 axis(:,ix) = cube%latt%rlattice_primitive(:, ix) * cube%spacing(ix) * db(ix) / m_two
735 end do
736
737 default_r_c = m_huge
738 do ix = 1, 3
739 iy = mod(ix, 3)+1
740 iz = mod(ix+1, 3)+1
741
742 ! For orthogonal cells, this is determined by the size of the cube
743 if (.not. cube%latt%nonorthogonal) then
744 temp(1:3) = axis(:, ix)
745 default_r_c = min(default_r_c, norm2(temp(1:3)))
746 else
747 ! At the moment, this codepath is only called for DFT+U submesh Poisson solver
748 !
749 ! For non-orthogonal cells, the relevant length is the distance between two planes of
750 ! the parallelepiped. This ensures that we draw a sphere that touches the borders of the box,
751 ! thus avoiding contribution from periodic replicas
752 ! This distance is given by the usual formula of the distance from a point to a plan
753 temp = dcross_product(axis(:, iy), axis(:, iz))
754 temp = temp / norm2(temp)
755 default_r_c = min(default_r_c, dot_product(temp, axis(:, ix)-axis(:, iy)))
756 end if
757 end do
758 call get_cutoff(namespace, default_r_c, r_c)
759 end if
760
761 n1 = max(1, cube%fs_n(1))
762 n2 = max(1, cube%fs_n(2))
763 n3 = max(1, cube%fs_n(3))
764
765 ! store the fourier transform of the Coulomb interaction
766 ! store only the relevant part if PFFT is used
767 safe_allocate(fft_coulb_fs(1:n1,1:n2,1:n3))
768 fft_coulb_fs = m_zero
769
770 temp(1:3) = m_two*m_pi/(db(1:3)*cube%spacing(1:3))
771
772 ecut = fft_get_ecut_from_box(cube%rs_n_global, cube%fs_istart, cube%latt, temp, 3, coulb%qq)
773
774 do lz = 1, n3
775 iz = cube%fs_istart(3) + lz - 1
776 ixx(3) = pad_feq(iz, db(3), .true.)
777 do ly = 1, n2
778 iy = cube%fs_istart(2) + ly - 1
779 ixx(2) = pad_feq(iy, db(2), .true.)
780 do lx = 1, n1
781 ix = cube%fs_istart(1) + lx - 1
782 ixx(1) = pad_feq(ix, db(1), .true.)
783
784 call fft_gg_transform(ixx, temp, 0, cube%latt, coulb%qq, gg, modg2)
785
786 ! At the moment this is only done for periodic space, so for DFT+U Coulomb integrals
787 if(modg2 > m_two*ecut*1.001_real64 .and. is_periodic) cycle
788
789 if (abs(modg2) > tol_vanishing_q) then
790 select case (kernel)
792 fft_coulb_fs(lx, ly, lz) = weight*poisson_cutoff_3d_0d(sqrt(modg2),r_c)/modg2
794 fft_coulb_fs(lx, ly, lz) = weight/modg2
795 end select
796 else
797 select case (kernel)
799 fft_coulb_fs(lx, ly, lz) = weight*r_c**2/m_two
801 fft_coulb_fs(lx, ly, lz) = m_zero
802 end select
803 end if
804 end do
805 end do
806 end do
807
808 call coulb%dset_op(cube, fft_coulb_fs, in_device = (kernel /= poisson_fft_kernel_corrected))
809
810 safe_deallocate_a(fft_coulb_fs)
812 end subroutine poisson_fft_build_3d_0d
813 !-----------------------------------------------------------------
814
815
816 !-----------------------------------------------------------------
818 subroutine poisson_fft_build_2d_0d(namespace, cube, coulb)
819 type(namespace_t), intent(in) :: namespace
820 type(cube_t), intent(in) :: cube
821 type(fourier_space_op_t), intent(inout) :: coulb
822
823 type(spline_t) :: besselintf
824 integer :: i, ix, iy, ixx(2), db(2), npoints
825 real(real64) :: temp(2), vec, r_c, maxf, dk, default_r_c, weight
826 real(real64), allocatable :: x(:), y(:)
827 real(real64), allocatable :: fft_coulb_FS(:,:,:)
828
830
831 assert(abs(coulb%mu) < m_epsilon)
832 ! Full range hybrids weight
833 weight = m_one
834 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
835
836 db(1:2) = cube%rs_n_global(1:2)
837
838 default_r_c = maxval(db(1:2)*cube%spacing(1:2)/m_two)
839 call get_cutoff(namespace, default_r_c, r_c)
840
841 call spline_init(besselintf)
842
843 ! store the fourier transform of the Coulomb interaction
844 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
845 fft_coulb_fs = m_zero
846 temp(1:2) = m_two*m_pi/(db(1:2)*cube%spacing(1:2))
847
848 maxf = r_c * norm2(temp(1:2)*db(1:2))/2
849 dk = 0.25_real64 ! This seems to be reasonable.
850 npoints = nint(maxf/dk)
851 safe_allocate(x(1:npoints))
852 safe_allocate(y(1:npoints))
853 x(1) = m_zero
854 y(1) = m_zero
855 do i = 2, npoints
856 x(i) = (i-1) * maxf / (npoints-1)
857 y(i) = y(i-1) + poisson_cutoff_2d_0d(x(i-1), x(i))
858 end do
859 call spline_fit(npoints, x, y, besselintf, m_zero)
860
861 do iy = 1, cube%fs_n_global(2)
862 ixx(2) = pad_feq(iy, db(2), .true.)
863 do ix = 1, cube%fs_n_global(1)
864 ixx(1) = pad_feq(ix, db(1), .true.)
865 vec = norm2(temp(1:2)*ixx(1:2))
866 ! extra check to avoid extrapolation which leads to an error in gsl
867 if (vec*r_c >= x(npoints)) then
868 fft_coulb_fs(ix, iy, 1) = weight * y(npoints)
869 else if (vec > m_zero) then
870 fft_coulb_fs(ix, iy, 1) = weight * (m_two * m_pi / vec) * spline_eval(besselintf, vec*r_c)
871 else
872 fft_coulb_fs(ix, iy, 1) = weight * m_two * m_pi * r_c
873 end if
874 end do
875 end do
876
877 call coulb%dset_op(cube, op_move = fft_coulb_fs)
878
879 safe_deallocate_a(fft_coulb_fs)
880 safe_deallocate_a(x)
881 safe_deallocate_a(y)
882 call spline_end(besselintf)
884 end subroutine poisson_fft_build_2d_0d
885 !-----------------------------------------------------------------
886
887
888 !-----------------------------------------------------------------
890 subroutine poisson_fft_build_2d_1d(namespace, cube, coulb)
891 type(namespace_t), intent(in) :: namespace
892 type(cube_t), intent(in) :: cube
893 type(fourier_space_op_t), intent(inout) :: coulb
894
895 integer :: ix, iy, ixx(2), db(2)
896 real(real64) :: temp(2), r_c, gx, gy, default_r_c, weight
897 real(real64), allocatable :: fft_coulb_FS(:,:,:)
898
900
901 assert(abs(coulb%mu) < m_epsilon)
902 ! Full range hybrids weight
903 weight = m_one
904 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
905
906
907 db(1:2) = cube%rs_n_global(1:2)
908
909 default_r_c = db(2)*cube%spacing(2)/m_two
910 call get_cutoff(namespace, default_r_c, r_c)
911
912 ! store the fourier transform of the Coulomb interaction
913 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
914 fft_coulb_fs = m_zero
915 temp(1:2) = m_two*m_pi/(db(1:2)*cube%spacing(1:2))
916
917 ! First, the term ix = 0 => gx = 0.
918 fft_coulb_fs(1, 1, 1) = -m_four * r_c * (log(r_c)-m_one)
919 do iy = 2, cube%fs_n_global(2)
920 ixx(2) = pad_feq(iy, db(2), .true.)
921 gy = temp(2)*ixx(2)
922 fft_coulb_fs(1, iy, 1) = -m_four * poisson_cutoff_intcoslog(r_c, gy, m_one) * weight
923 end do
924
925 do ix = 2, cube%fs_n_global(1)
926 ixx(1) = pad_feq(ix, db(1), .true.)
927 gx = temp(1)*ixx(1)
928 do iy = 1, cube%fs_n_global(2)
929 ixx(2) = pad_feq(iy, db(2), .true.)
930 gy = temp(2)*ixx(2)
931 fft_coulb_fs(ix, iy, 1) = poisson_cutoff_2d_1d(gy, gx, r_c) * weight
932 end do
933 end do
934
935 call coulb%dset_op(cube, op_move = fft_coulb_fs)
936
937 safe_deallocate_a(fft_coulb_fs)
938
940 end subroutine poisson_fft_build_2d_1d
941 !-----------------------------------------------------------------
942
943
944 !-----------------------------------------------------------------
946 subroutine poisson_fft_build_2d_2d(cube, coulb)
947 type(cube_t), intent(in) :: cube
948 type(fourier_space_op_t), intent(inout) :: coulb
949
950 integer :: ix, iy, ixx(2), db(2)
951 real(real64) :: temp(2), vec, weight
952 real(real64), allocatable :: fft_coulb_FS(:,:,:)
953
955
956 assert(abs(coulb%mu) < m_epsilon)
957 ! Full range hybrids weight
958 weight = m_one
959 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
960
961 db(1:2) = cube%rs_n_global(1:2)
962
963 ! store the fourier transform of the Coulomb interaction
964 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
965 fft_coulb_fs = m_zero
966 temp(1:2) = m_two*m_pi/(db(1:2)*cube%spacing(1:2))
967
968 do iy = 1, cube%fs_n_global(2)
969 ixx(2) = pad_feq(iy, db(2), .true.)
970 do ix = 1, cube%fs_n_global(1)
971 ixx(1) = pad_feq(ix, db(1), .true.)
972 vec = sqrt((temp(1) * ixx(1))**2 + (temp(2) * ixx(2))**2)
973 if (vec > m_zero) fft_coulb_fs(ix, iy, 1) = m_two * m_pi / vec * weight
974 end do
975 end do
976
977 call coulb%dset_op(cube, op_move = fft_coulb_fs)
978
979 safe_deallocate_a(fft_coulb_fs)
981 end subroutine poisson_fft_build_2d_2d
982 !-----------------------------------------------------------------
983
984
985 !-----------------------------------------------------------------
986 subroutine poisson_fft_build_1d_1d(cube, coulb, poisson_soft_coulomb_param)
987 type(cube_t), intent(in) :: cube
988 type(fourier_space_op_t), intent(inout) :: coulb
989 real(real64), intent(in) :: poisson_soft_coulomb_param
990
991 integer :: ix, ixx
992 real(real64) :: g, weight
993 real(real64), allocatable :: fft_coulb_fs(:, :, :)
994
996
997 assert(abs(coulb%mu) < m_epsilon)
998 ! Full range hybrids weight
999 weight = m_one
1000 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
1001
1002 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
1003 fft_coulb_fs = m_zero
1004
1005 ! Fourier transform of Soft Coulomb interaction.
1006 do ix = 1, cube%fs_n_global(1)
1007 ixx = pad_feq(ix, cube%rs_n_global(1), .true.)
1008 g = (ixx + coulb%qq(1))*m_two*m_pi/abs(cube%latt%rlattice(1,1))
1009 if (abs(g) > tol_vanishing_q) then
1010 fft_coulb_fs(ix, 1, 1) = m_two * loct_bessel_k0(poisson_soft_coulomb_param*abs(g)) * weight
1011 else
1012 fft_coulb_fs(ix, 1, 1) = coulb%singularity * m_two * weight
1013 end if
1014 end do
1015
1016 call coulb%dset_op(cube, op_move = fft_coulb_fs)
1017 safe_deallocate_a(fft_coulb_fs)
1018
1020 end subroutine poisson_fft_build_1d_1d
1021 !-----------------------------------------------------------------
1022
1023
1024 !-----------------------------------------------------------------
1025 subroutine poisson_fft_build_1d_0d(namespace, cube, coulb, poisson_soft_coulomb_param)
1026 type(namespace_t), intent(in) :: namespace
1027 type(cube_t), intent(in) :: cube
1028 type(fourier_space_op_t), intent(inout) :: coulb
1029 real(real64), intent(in) :: poisson_soft_coulomb_param
1030
1031 integer :: box(1), ixx(1), ix
1032 real(real64) :: temp(1), g, r_c, default_r_c, weight
1033 real(real64), allocatable :: fft_coulb_fs(:, :, :)
1034
1035 push_sub(poisson_fft_build_1d_0d)
1036
1037 assert(abs(coulb%mu) < m_epsilon)
1038 ! Full range hybrids weight
1039 weight = m_one
1040 if(coulb%alpha > m_epsilon) weight = weight * coulb%alpha
1042 box(1:1) = cube%rs_n_global(1:1)
1043
1044 default_r_c = box(1)*cube%spacing(1)/m_two
1045 call get_cutoff(namespace, default_r_c, r_c)
1046
1047 safe_allocate(fft_coulb_fs(1:cube%fs_n_global(1), 1:cube%fs_n_global(2), 1:cube%fs_n_global(3)))
1048 fft_coulb_fs = m_zero
1049 temp(1:1) = m_two*m_pi/(box(1:1)*cube%spacing(1:1))
1050
1051 ! Fourier transform of Soft Coulomb interaction.
1052 do ix = 1, cube%fs_n_global(1)
1053 ixx(1) = pad_feq(ix, box(1), .true.)
1054 g = temp(1)*ixx(1)
1055 fft_coulb_fs(ix, 1, 1) = poisson_cutoff_1d_0d(g, poisson_soft_coulomb_param, r_c) * weight
1056 end do
1057
1058 call coulb%dset_op(cube, op_move = fft_coulb_fs)
1059 safe_deallocate_a(fft_coulb_fs)
1060
1062 end subroutine poisson_fft_build_1d_0d
1063 !-----------------------------------------------------------------
1064
1065
1066 !-----------------------------------------------------------------
1067 subroutine poisson_fft_end(this)
1068 type(poisson_fft_t), intent(inout) :: this
1069
1070 push_sub(poisson_fft_end)
1071
1072 call this%coulb%end()
1073
1074 pop_sub(poisson_fft_end)
1075 end subroutine poisson_fft_end
1076
1077#include "undef.F90"
1078#include "real.F90"
1079#include "poisson_fft_inc.F90"
1080#include "undef.F90"
1081#include "complex.F90"
1082#include "poisson_fft_inc.F90"
1083
1084
1085end module poisson_fft_oct_m
1086
1087!! Local Variables:
1088!! mode: f90
1089!! coding: utf-8
1090!! 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
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.
real(real64) function poisson_fft_singularity_3d(coulb)
Define the singularity correction for |q+G| = 0, for the 3D Coulomb kernel.
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
type(xc_cam_t), parameter, public cam_null
All CAM parameters set to zero.
Definition: xc_cam.F90:152
Definition of a Fourier Space Coulomb Kernel.
the basic spline datatype
Definition: splines.F90:156
int true(void)