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