Octopus
poisson_psolver.F90
Go to the documentation of this file.
1!! Copyright (C) 2013 J. Alberdi-Rodriguez
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
26 use global_oct_m
27 use, intrinsic :: iso_fortran_env
30 use mesh_oct_m
33 use parser_oct_m
35 use space_oct_m
37 ! Support for PSolver from BigDFT
38#ifdef HAVE_PSOLVER
40 use poisson_solver
41 use dictionaries, dict_set => set
42 use yaml_output, only: yaml_map
43 use wrapper_mpi, only: mpi_environment, mpi_environment_set
44 use at_domain
45#endif
46
47
48 implicit none
49
50 private
51 public :: &
59
61 private
62 type(fourier_space_op_t) :: coulb
63#ifdef HAVE_PSOLVER
64 type(coulomb_operator) :: kernel
65 type(dictionary), pointer :: inputs
66#endif
67
79 character(len = 1) :: geocode = "F"
89 character(len = 1), public :: datacode = "G"
90
91 integer :: isf_order
92 integer :: localnscatterarr(5)
93 real(real64) :: offset
94 end type poisson_psolver_t
95
96#ifdef HAVE_PSOLVER
97 logical, save :: flib_initialized = .false.
98#endif
99
100 real(real64), parameter :: TOL_VANISHING_Q = 1e-6_real64
101
102contains
103
104 !-----------------------------------------------------------------
105 subroutine poisson_psolver_init(this, namespace, space, cube, mu, qq, force_isolated)
106 type(poisson_psolver_t), intent(out) :: this
107 type(namespace_t), intent(in) :: namespace
108 class(space_t), intent(in) :: space
109 type(cube_t), intent(inout) :: cube
110 real(real64), intent(in) :: mu
111 real(real64), intent(in) :: qq(:)
112 logical, optional, intent(in) :: force_isolated
113
114 logical data_is_parallel
115#ifdef HAVE_PSOLVER
116 real(real64) :: alpha, beta, gamma
117 real(real64) :: modq2
118 type(mpi_environment) mpi_env
119 type(domain) :: dom
120#endif
121
122 push_sub(poisson_psolver_init)
123
124#ifdef HAVE_PSOLVER
125 if (.not. flib_initialized) then
126 call f_lib_initialize()
127 flib_initialized = .true.
128 end if
129 call dict_init(this%inputs)
130#endif
131
132 if (optional_default(force_isolated, .false.)) then
133 this%geocode = "F"
134 else
135 select case (space%periodic_dim)
136 case (0)
137 ! Free BC
138 this%geocode = "F"
139 case (1)
140 ! Wire BC
141 this%geocode = "W"
142 call messages_not_implemented("PSolver support for 1D periodic boundary conditions", namespace=namespace)
143 case (2)
144 ! Surface BC
145 this%geocode = "S"
146 call messages_not_implemented("PSolver support for 2D periodic boundary conditions", namespace=namespace)
147 case (3)
148 ! Periodic BC
149 this%geocode = "P"
150 call messages_experimental("PSolver support for 3D periodic boundary conditions", namespace=namespace)
151 end select
152 end if
154#ifdef HAVE_PSOLVER
155 !Verbosity switch
156 call dict_set(this%inputs//'setup'//'verbose', .false.)
157 !Order of the Interpolating Scaling Function family
158 call dict_set(this%inputs//'kernel'//'isf_order', 16)
159 !Mu screening parameter
160 call dict_set(this%inputs//'kernel'//'screening', mu)
161 !Calculation of the stress tensor
162 call dict_set(this%inputs//'kernel'//'stress_tensor', .false.)
163#else
164 this%isf_order = 16
165#endif
166
167 !%Variable PoissonSolverPSolverParallelData
168 !%Type logical
169 !%Section Hamiltonian::Poisson::PSolver
170 !%Default yes
171 !%Description
172 !% Indicates whether data is partitioned within the PSolver library.
173 !% If data is distributed among processes, Octopus uses parallel data-structures
174 !% and, thus, less memory.
175 !% If "yes", data is parallelized. The <i>z</i>-axis of the input vector
176 !% is split among the MPI processes.
177 !% If "no", entire input and output vector is saved in all the MPI processes.
178 !% If k-points parallelization is used, "no" must be selected.
179 !%End
180 call parse_variable(namespace, 'PoissonSolverPSolverParallelData', .true., data_is_parallel)
181 if (.not. cube%parallel_in_domains) then
182 data_is_parallel = .false.
183 end if
185 call messages_obsolete_variable(namespace, 'PoissonSolverISFParallelData', 'PoissonSolverPSolverParallelData')
187#ifdef HAVE_PSOLVER
188 if (data_is_parallel) then
189 call dict_set(this%inputs//'setup'//'global_data', .false.)
190 else
191 call dict_set(this%inputs//'setup'//'global_data', .true.)
192 end if
193#else
194 if (data_is_parallel) then
195 this%datacode = "D"
196 else
197 this%datacode = "G"
198 end if
199#endif
200
201#ifdef HAVE_PSOLVER
202 call dict_set(this%inputs//'setup'//'verbose', debug%info)
203
204 alpha = cube%latt%alpha*m_pi/(180.0_real64)
205 beta = cube%latt%beta*m_pi/(180.0_real64)
206 gamma = cube%latt%gamma*m_pi/(180.0_real64)
207
208 ! Previously, pkernel_init set the communicator used within PSolver to comm_world.
209 ! This can be overwritten by passing an optional argument of type(mpi_environment)
210 ! to pkernel_init(). This data type is defined within the wrapper_MPI module of
211 ! the Futile library. Futile is a prerequisit for PSolver.
212
213 ! TODO: check that cube%mpi_grp corresponds to the correct mpi group, when parallelizing, e.g., over systems !!
214
215 call mpi_environment_set(mpi_env, cube%mpi_grp%rank, cube%mpi_grp%size, cube%mpi_grp%comm%MPI_VAL, cube%mpi_grp%size)
216
217 dom=domain_new(units=atomic_units, bc=geocode_to_bc_enum(this%geocode),&
218 alpha_bc=alpha, beta_ac=beta, gamma_ab=gamma, acell=cube%rs_n_global*cube%spacing)
219
220 this%kernel = pkernel_init(cube%mpi_grp%rank, cube%mpi_grp%size, this%inputs, dom, cube%rs_n_global, &
221 cube%spacing, alpha_bc = alpha, beta_ac = beta, gamma_ab = gamma, mpi_env = mpi_env)
222
223 call pkernel_set(this%kernel, verbose=debug%info)
224
225 !G=0 component
226 modq2 = sum(qq(1:space%periodic_dim)**2)
227 if (modq2 > m_epsilon) then
228 this%offset = m_one/modq2
229 else
230 this%offset = m_zero
231 end if
232
233 !Screened coulomb potential (erfc function)
234 if (mu > m_epsilon) then
235 if (modq2 > m_epsilon) then
236 this%offset = this%offset*(m_one - exp(-modq2/((m_two*mu)**2)))
237 else
238 !Analytical limit of 1/|q|^2*(1-exp(-|q|^2/4mu^2))
239 this%offset = m_one/((m_two*mu)**2)
240 end if
241 end if
242 this%offset = this%offset*m_four*m_pi
243#endif
244
245 pop_sub(poisson_psolver_init)
246 end subroutine poisson_psolver_init
247
248 !-----------------------------------------------------------------
249 subroutine poisson_psolver_end(this)
250 type(poisson_psolver_t), intent(inout) :: this
251
252 push_sub(poisson_psolver_end)
253
254#ifdef HAVE_PSOLVER
255 call pkernel_free(this%kernel)
256 call f_lib_finalize()
257 call dict_free(this%inputs)
258#endif
259
260 pop_sub(poisson_psolver_end)
261 end subroutine poisson_psolver_end
262
263 !-----------------------------------------------------------------
264 subroutine poisson_psolver_reinit(this, space, cube, coulb, qq_in)
265 type(poisson_psolver_t), intent(inout) :: this
266 class(space_t), intent(in) :: space
267 type(cube_t), intent(inout) :: cube
268 type(fourier_space_op_t), intent(inout) :: coulb
269 real(real64), intent(in) :: qq_in(:)
270
271 real(real64) :: alpha, beta, gamma
272 real(real64) :: qq_abs(1:space%dim), qq(1:space%dim)
273 real(real64) :: modq2
274 integer :: idim
275#ifdef HAVE_PSOLVER
276 type(domain) :: dom
277#endif
278 push_sub(poisson_psolver_reinit)
279
280 ! We only support short range only or long-range only at the moment
281 if(coulb%mu > m_epsilon) then
282 assert(coulb%alpha < m_epsilon .or. coulb%beta < m_epsilon)
283 end if
284
285#ifdef HAVE_PSOLVER
286 call pkernel_free(this%kernel)
287#endif
288
289 !We might change the cell angles
290 alpha = cube%latt%alpha*m_pi/(180.0_real64)
291 beta = cube%latt%beta*m_pi/(180.0_real64)
292 gamma = cube%latt%gamma*m_pi/(180.0_real64)
293
294#ifdef HAVE_PSOLVER
295 call dict_set(this%inputs//'kernel'//'screening', coulb%mu)
296
297 dom=domain_new(units=atomic_units, bc=geocode_to_bc_enum(this%geocode),&
298 alpha_bc=alpha, beta_ac=beta, gamma_ab=gamma, acell=cube%rs_n_global*cube%spacing)
299
300 this%kernel = pkernel_init(cube%mpi_grp%rank, cube%mpi_grp%size, this%inputs,&
301 dom,cube%rs_n_global, cube%spacing, &
302 alpha_bc = alpha, beta_ac = beta, gamma_ab = gamma)
303
304 call pkernel_set(this%kernel, verbose=debug%info)
305#endif
306
307 !G=0 component
308 !We remove potential umklapp
309 do idim = 1, space%periodic_dim
310 qq(idim) = qq_in(idim) - anint(qq_in(idim) + m_half*1e-8_real64)
311 end do
312 qq(space%periodic_dim + 1:space%dim) = m_zero
313 call kpoints_to_absolute(cube%latt, qq, qq_abs)
314 modq2 = norm2(qq_abs)
315 if (modq2 > tol_vanishing_q) then
316 this%offset = m_four*m_pi/modq2
317 else
318 this%offset = coulb%singularity
319 end if
320
321 !Screened coulomb potential (erfc function)
322 if (coulb%mu > m_epsilon) then
323 if (modq2 > tol_vanishing_q) then
324 this%offset = this%offset*(m_one - exp(-modq2/((m_two*coulb%mu)**2)))
325 else
326 !Analytical limit of 1/|q|^2*(1-exp(-|q|^2/4mu^2))
327 this%offset = m_four*m_pi/((m_two*coulb%mu)**2)
328 end if
329 end if
330
332 end subroutine poisson_psolver_reinit
333
334 !-----------------------------------------------------------------
335 subroutine poisson_psolver_parallel_solve(this, mesh, cube, pot, rho, mesh_cube_map)
336 type(poisson_psolver_t), intent(in), target :: this
337 type(mesh_t), intent(in) :: mesh
338 type(cube_t), intent(in) :: cube
339 real(real64), intent(out) :: pot(:)
340 real(real64), intent(in) :: rho(:)
341 type(mesh_cube_parallel_map_t), intent(in) :: mesh_cube_map
342
343 type(cube_function_t) :: cf
344
345#ifdef HAVE_PSOLVER
346 type(coulomb_operator), pointer :: kernel_pointer
347 real(real64) :: hartree_energy
348 real(real64) :: offset
349 ! To be used only in the periodic case, ignored for other boundary conditions.
350#endif
351
355 real(real64), allocatable :: pot_ion(:,:,:)
356 character(len=3) :: quiet
357
359
360 call dcube_function_alloc_rs(cube, cf)
361 call dmesh_to_cube_parallel(mesh, rho, cube, cf, mesh_cube_map)
362 safe_allocate(pot_ion(1:cube%rs_n(1),1:cube%rs_n(2),1:cube%rs_n(3)))
363
364 quiet = "YES"
365 if (debug%info) quiet = "NO"
366
367#ifdef HAVE_PSOLVER
368 !The offset is the integral over space of the potential
369 !this%offset is the G=0 component of the (screened) Coulomb potential
370 !The G=0 component of the Hartree therefore needs to be
371 ! multiplied by the G=0 component of the density
372 if (this%offset > m_epsilon) then
373 offset = this%offset*dmf_integrate(mesh,rho)
374 end if
375#endif
376
377 call profiling_in("PSOLVER_LIBRARY")
378#ifdef HAVE_PSOLVER
379 kernel_pointer => this%kernel
380 call h_potential(this%datacode, kernel_pointer, cf%dRS, pot_ion, hartree_energy, offset, .false., quiet = quiet)
381#endif
382 call profiling_out("PSOLVER_LIBRARY")
383 safe_deallocate_a(pot_ion)
384
385 call dcube_to_mesh_parallel(cube, cf, mesh, pot, mesh_cube_map)
386
387 call dcube_function_free_rs(cube, cf)
388
390 end subroutine poisson_psolver_parallel_solve
391
392 !-----------------------------------------------------------------
393 subroutine poisson_psolver_global_solve(this, mesh, cube, pot, rho, sm)
394 type(poisson_psolver_t), intent(in), target :: this
395 type(mesh_t), intent(in) :: mesh
396 type(cube_t), intent(in) :: cube
397 real(real64), contiguous, intent(out) :: pot(:)
398 real(real64), contiguous, intent(in) :: rho(:)
399 type(submesh_t), optional, intent(in) :: sm
400
401 character(len=3) :: quiet
402 type(cube_function_t) :: cf
403
404#ifdef HAVE_PSOLVER
405 type(coulomb_operator), pointer :: kernel_pointer
406 real(real64) :: hartree_energy
407 real(real64) :: offset
408 ! To be used only in the periodic case, ignored for other boundary conditions.
409#endif
410
415 real(real64), allocatable :: pot_ion(:,:,:)
416
418
419 call dcube_function_alloc_rs(cube, cf)
421 if (present(sm)) then
422 call dsubmesh_to_cube(sm, rho, cube, cf)
423 else
424 call dmesh_to_cube(mesh, rho, cube, cf)
425 end if
426
427 safe_allocate(pot_ion(1:cube%rs_n(1), 1:cube%rs_n(2), 1:cube%rs_n(3)))
428
429 quiet = "YES"
430 if (debug%info) quiet = "NO"
431
432#ifdef HAVE_PSOLVER
433 !The offset is the integral over space of the potential
434 !this%offset is the G=0 component of the (screened) Coulomb potential
435 !The G=0 component of the Hartree therefore needs to be
436 ! multiplied by the G=0 component of the density
437 if (this%offset > m_epsilon) then
438 offset = this%offset*dmf_integrate(mesh, rho)
439 end if
440#endif
441
442 call profiling_in("PSOLVER_LIBRARY")
443
444#ifdef HAVE_PSOLVER
445 kernel_pointer => this%kernel
446 call h_potential(this%datacode, kernel_pointer, &
447 cf%dRS, pot_ion, hartree_energy, offset, .false., &
448 quiet = quiet) !optional argument
449#endif
450 safe_deallocate_a(pot_ion)
451
452 if (present(sm)) then
453 call dcube_to_submesh(cube, cf, sm, pot)
454 else
455 call dcube_to_mesh(cube, cf, mesh, pot)
456 end if
457
458 call dcube_function_free_rs(cube, cf)
459
461 end subroutine poisson_psolver_global_solve
462
463 subroutine poisson_psolver_get_dims(this, cube)
464 type(poisson_psolver_t), intent(inout) :: this
465 type(cube_t), intent(inout) :: cube
466
467#ifdef HAVE_PSOLVER
468
476 integer :: n3d
479 integer :: n3p
482 integer :: n3pi
488 integer :: i3xcsh
496 integer :: i3s
497
499 logical :: use_gradient
501 logical :: use_wb_corr
502#endif
503
505
506 ! Get the dimensions of the cube
507#ifdef HAVE_PSOLVER
508 use_gradient = .false.
509 use_wb_corr = .false.
510 call ps_dim4allocation(this%geocode, this%datacode, cube%mpi_grp%rank, cube%mpi_grp%size, &
511 cube%rs_n_global(1), cube%rs_n_global(2), cube%rs_n_global(3), &
512 use_gradient, use_wb_corr, &
513 0, n3d, n3p, n3pi, i3xcsh, i3s)
514 this%localnscatterarr(:) = (/ n3d, n3p, n3pi, i3xcsh, i3s /)
515#endif
516 cube%rs_n(1:2) = cube%rs_n_global(1:2)
517 cube%rs_n(3) = this%localnscatterarr(1)
518 cube%rs_istart(1:2) = 1
519 cube%rs_istart(3) = this%localnscatterarr(5)
520
521 !! With PSolver we don`t care about the Fourier space and its dimensions
522 !! We`ll put as in RS
523 cube%fs_n_global(1) = cube%rs_n_global(1)
524 cube%fs_n_global(2) = cube%rs_n_global(2)
525 cube%fs_n_global(3) = cube%rs_n_global(3)
526 cube%fs_n(1:2) = cube%rs_n_global(1:2)
527 cube%fs_n(3) = this%localnscatterarr(1)
528 cube%fs_istart(1:2) = 1
529 cube%fs_istart(3) = this%localnscatterarr(5)
530
532 end subroutine poisson_psolver_get_dims
533
534end module poisson_psolver_oct_m
535
536!! Local Variables:
537!! mode: f90
538!! coding: utf-8
539!! End:
double exp(double __x) __attribute__((__nothrow__
subroutine, public dmesh_to_cube(mesh, mf, cube, cf)
The next two subroutines convert a function between the normal mesh and the cube.
subroutine, public dcube_to_submesh(cube, cf, sm, mf)
subroutine, public dcube_to_mesh(cube, cf, mesh, mf)
subroutine, public dcube_function_alloc_rs(cube, cf, in_device, force_alloc)
Allocates locally the real space grid, if PFFT library is not used. Otherwise, it assigns the PFFT re...
subroutine, public dcube_function_free_rs(cube, cf)
Deallocates the real space grid.
subroutine, public dsubmesh_to_cube(sm, mf, cube, cf)
The next two subroutines convert a function between a submesh and the cube.
subroutine, public dmesh_to_cube_parallel(mesh, mf, cube, cf, map)
The next two subroutines convert a function between the normal mesh and the cube in parallel.
subroutine, public dcube_to_mesh_parallel(cube, cf, mesh, mf, map)
type(debug_t), save, public debug
Definition: debug.F90:154
real(real64), parameter, public m_two
Definition: global.F90:189
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_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
subroutine, public kpoints_to_absolute(latt, kin, kout)
Definition: kpoints.F90:1031
This module defines various routines, operating on mesh functions.
real(real64) function, public dmf_integrate(mesh, ff, mask, reduce)
Integrate a function on the mesh.
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:118
subroutine, public messages_not_implemented(feature, namespace)
Definition: messages.F90:1125
subroutine, public messages_obsolete_variable(namespace, name, rep)
Definition: messages.F90:1057
subroutine, public messages_experimental(name, namespace)
Definition: messages.F90:1097
subroutine, public poisson_psolver_global_solve(this, mesh, cube, pot, rho, sm)
subroutine, public poisson_psolver_parallel_solve(this, mesh, cube, pot, rho, mesh_cube_map)
subroutine, public poisson_psolver_end(this)
subroutine, public poisson_psolver_reinit(this, space, cube, coulb, qq_in)
subroutine, public poisson_psolver_get_dims(this, cube)
subroutine, public poisson_psolver_init(this, namespace, space, cube, mu, qq, force_isolated)
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
Definition: profiling.F90:623
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
Definition: profiling.F90:552
Describes mesh distribution to nodes.
Definition: mesh.F90:186
A submesh is a type of mesh, used for the projectors in the pseudopotentials It contains points on a ...
Definition: submesh.F90:175
int true(void)