(git:e7e05ae)
ps_implicit_methods.F
Go to the documentation of this file.
1 !--------------------------------------------------------------------------------------------------!
2 ! CP2K: A general program to perform molecular dynamics simulations !
3 ! Copyright 2000-2024 CP2K developers group <https://cp2k.org> !
4 ! !
5 ! SPDX-License-Identifier: GPL-2.0-or-later !
6 !--------------------------------------------------------------------------------------------------!
7 
8 ! **************************************************************************************************
9 !> \brief The implicit (generalized) Poisson solver
10 !> \par History
11 !> 06.2014 created [Hossein Bani-Hashemian]
12 !> 11.2015 - dealt with missing grid points of periodic grids while performing dct;
13 !> - revised solver for Neumann and mixed boundary setups.
14 !> \author Hossein Bani-Hashemian
15 ! **************************************************************************************************
17  USE bibliography, ONLY: banihashemian2016,&
18  cite_reference
21  cp_logger_type
22  USE dct, ONLY: &
25  USE dielectric_methods, ONLY: derive_fft,&
27  USE dielectric_types, ONLY: dielectric_type
30  USE kahan_sum, ONLY: accurate_sum
31  USE kinds, ONLY: dp,&
32  int_8
33  USE mathconstants, ONLY: fourpi,&
34  pi
35  USE ps_implicit_types, ONLY: mixed_bc,&
37  neumann_bc,&
38  periodic_bc,&
39  ps_implicit_type
40  USE pw_grid_types, ONLY: pw_grid_type
41  USE pw_methods, ONLY: pw_axpy,&
42  pw_copy,&
43  pw_integral_ab,&
44  pw_scale,&
45  pw_transfer,&
46  pw_zero
47  USE pw_poisson_types, ONLY: greens_fn_type,&
48  pw_poisson_parameter_type,&
49  pw_poisson_type
50  USE pw_pool_types, ONLY: pw_pool_create,&
52  pw_pool_type
53  USE pw_types, ONLY: pw_c1d_gs_type,&
54  pw_r3d_rs_type
55 #include "../base/base_uses.f90"
56 
57  IMPLICIT NONE
58  PRIVATE
59  CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'ps_implicit_methods'
60 
61  PUBLIC ps_implicit_create, &
66 
67  INTERFACE ps_implicit_compute_ehartree
68  MODULE PROCEDURE compute_ehartree_periodic_bc, &
69  compute_ehartree_mixed_bc
70  END INTERFACE ps_implicit_compute_ehartree
71 
72  REAL(dp), PRIVATE, PARAMETER :: large_error = 1.0e4_dp
73 
74 CONTAINS
75 
76 ! **************************************************************************************************
77 !> \brief Creates implicit Poisson solver environment
78 !> \param pw_pool pool of pw grid
79 !> \param poisson_params poisson_env parameters
80 !> \param dct_pw_grid discrete cosine transform (extended) grid
81 !> \param green green function for FFT based inverse Laplacian
82 !> \param ps_implicit_env implicit env to be created
83 !> \par History
84 !> 06.2014 created [Hossein Bani-Hashemian]
85 !> \author Mohammad Hossein Bani-Hashemian
86 ! **************************************************************************************************
87  SUBROUTINE ps_implicit_create(pw_pool, poisson_params, dct_pw_grid, green, ps_implicit_env)
88 
89  TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
90  TYPE(pw_poisson_parameter_type), INTENT(INOUT) :: poisson_params
91  TYPE(pw_grid_type), INTENT(IN), POINTER :: dct_pw_grid
92  TYPE(greens_fn_type), INTENT(IN), POINTER :: green
93  TYPE(ps_implicit_type), INTENT(INOUT), POINTER :: ps_implicit_env
94 
95  CHARACTER(LEN=*), PARAMETER :: routinen = 'ps_implicit_create'
96 
97  INTEGER :: boundary_condition, handle, j, &
98  n_contacts, neumann_directions
99  TYPE(pw_pool_type), POINTER :: pw_pool_xpndd
100 
101  CALL timeset(routinen, handle)
102 
103  CALL cite_reference(banihashemian2016)
104 
105  IF (.NOT. ASSOCIATED(ps_implicit_env)) THEN
106  ALLOCATE (ps_implicit_env)
107 
108  ps_implicit_env%do_dbc_cube = poisson_params%dbc_params%do_dbc_cube
109  boundary_condition = poisson_params%ps_implicit_params%boundary_condition
110  neumann_directions = poisson_params%ps_implicit_params%neumann_directions
111 
112 ! create dielectric
113  NULLIFY (ps_implicit_env%dielectric)
114  SELECT CASE (boundary_condition)
116  CALL dielectric_create(ps_implicit_env%dielectric, pw_pool, poisson_params%dielectric_params)
117  CASE (neumann_bc, mixed_bc)
118  CALL pw_pool_create(pw_pool_xpndd, pw_grid=dct_pw_grid)
119  CALL dielectric_create(ps_implicit_env%dielectric, pw_pool_xpndd, poisson_params%dielectric_params)
120  CALL pw_pool_release(pw_pool_xpndd)
121  END SELECT
122 
123 ! initial guess
124  NULLIFY (ps_implicit_env%initial_guess)
125 
126 ! v_eps
127  NULLIFY (ps_implicit_env%v_eps)
128  ALLOCATE (ps_implicit_env%v_eps)
129  CALL pw_pool%create_pw(ps_implicit_env%v_eps)
130  CALL pw_zero(ps_implicit_env%v_eps)
131 
132 ! constraint charge
133  NULLIFY (ps_implicit_env%cstr_charge)
134  SELECT CASE (boundary_condition)
135  CASE (mixed_periodic_bc)
136  ALLOCATE (ps_implicit_env%cstr_charge)
137  CALL pw_pool%create_pw(ps_implicit_env%cstr_charge)
138  CALL pw_zero(ps_implicit_env%cstr_charge)
139  CASE (mixed_bc)
140  CALL pw_pool_create(pw_pool_xpndd, pw_grid=dct_pw_grid)
141  ALLOCATE (ps_implicit_env%cstr_charge)
142  CALL pw_pool_xpndd%create_pw(ps_implicit_env%cstr_charge)
143  CALL pw_zero(ps_implicit_env%cstr_charge)
144  CALL pw_pool_release(pw_pool_xpndd)
145  END SELECT
146 
147 ! initialize energies
148  ps_implicit_env%ehartree = 0.0_dp
149  ps_implicit_env%electric_enthalpy = 0.0_dp
150 ! times called
151  ps_implicit_env%times_called = 0
152 
153 ! dct env
154  IF (boundary_condition .EQ. mixed_bc .OR. boundary_condition .EQ. neumann_bc) THEN
155  CALL dct_type_init(pw_pool%pw_grid, neumann_directions, ps_implicit_env%dct_env)
156  END IF
157 
158 ! prepare dirichlet bc
159  CALL dirichlet_boundary_region_setup(pw_pool, poisson_params, ps_implicit_env%contacts)
160  CALL ps_implicit_prepare_blocks(pw_pool, dct_pw_grid, green, poisson_params, ps_implicit_env)
161  ! release tiles if they are not supposed to be written into cube files
162  IF ((boundary_condition .EQ. mixed_periodic_bc .OR. boundary_condition .EQ. mixed_bc) .AND. &
163  (.NOT. poisson_params%dbc_params%do_dbc_cube)) THEN
164  n_contacts = SIZE(ps_implicit_env%contacts)
165  DO j = 1, n_contacts
166  CALL dbc_tile_release(ps_implicit_env%contacts(j)%dirichlet_bc, pw_pool)
167  END DO
168  END IF
169 
170  END IF
171 
172  CALL timestop(handle)
173 
174  END SUBROUTINE ps_implicit_create
175 
176 ! **************************************************************************************************
177 !> \brief implicit Poisson solver for periodic boundary conditions
178 !> \param poisson_env poisson environment
179 !> \param density electron density
180 !> \param v_new electrostatic potential
181 !> \param ehartree Hartree energy
182 !> \par History
183 !> 07.2014 created [Hossein Bani-Hashemian]
184 !> \author Mohammad Hossein Bani-Hashemian
185 ! **************************************************************************************************
186  SUBROUTINE implicit_poisson_solver_periodic(poisson_env, density, v_new, ehartree)
187 
188  TYPE(pw_poisson_type), INTENT(IN) :: poisson_env
189  TYPE(pw_r3d_rs_type), INTENT(IN) :: density
190  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: v_new
191  REAL(dp), INTENT(OUT), OPTIONAL :: ehartree
192 
193  CHARACTER(LEN=*), PARAMETER :: routinen = 'implicit_poisson_solver_periodic'
194 
195  INTEGER :: handle, iter, max_iter, outp_unit, &
196  times_called
197  LOGICAL :: reached_max_iter, reached_tol, &
198  use_zero_initial_guess
199  REAL(dp) :: nabs_error, omega, pres_error, tol
200  TYPE(dielectric_type), POINTER :: dielectric
201  TYPE(greens_fn_type), POINTER :: green
202  TYPE(ps_implicit_type), POINTER :: ps_implicit_env
203  TYPE(pw_pool_type), POINTER :: pw_pool
204  TYPE(pw_r3d_rs_type) :: g, pxqainvxres, qainvxres, res_new, &
205  res_old, v_old
206 
207  CALL timeset(routinen, handle)
208 
209  pw_pool => poisson_env%pw_pools(poisson_env%pw_level)%pool
210  dielectric => poisson_env%implicit_env%dielectric
211  green => poisson_env%green_fft
212  ps_implicit_env => poisson_env%implicit_env
213 
214  tol = poisson_env%parameters%ps_implicit_params%tol
215  omega = poisson_env%parameters%ps_implicit_params%omega
216  max_iter = poisson_env%parameters%ps_implicit_params%max_iter
217  use_zero_initial_guess = poisson_env%parameters%ps_implicit_params%zero_initial_guess
218  times_called = ps_implicit_env%times_called
219 
220 ! check if this is the first scf iteration
221  IF (times_called .EQ. 0) CALL ps_implicit_initial_guess_create(ps_implicit_env, pw_pool)
222 
223  CALL pw_pool%create_pw(g)
224  CALL pw_pool%create_pw(v_old)
225  CALL pw_pool%create_pw(res_old)
226  CALL pw_pool%create_pw(res_new)
227  CALL pw_pool%create_pw(qainvxres)
228  CALL pw_pool%create_pw(pxqainvxres)
229 
230  IF (use_zero_initial_guess) THEN
231  CALL pw_zero(v_old)
232  ELSE
233  CALL pw_copy(ps_implicit_env%initial_guess, v_old)
234  END IF
235 
236  g%array = fourpi*density%array/dielectric%eps%array
237 
238 ! res_old = g - \Delta(v_old) - P(v_old)
239  CALL apply_poisson_operator_fft(pw_pool, green, dielectric, v_old, res_old)
240  CALL pw_scale(res_old, -1.0_dp)
241  CALL pw_axpy(g, res_old)
242 
243 ! evaluate \Delta^-1(res_old)
244  CALL apply_inv_laplace_operator_fft(pw_pool, green, res_old, qainvxres)
245 
246  iter = 1
247  DO
248 
249 ! v_new = v_old + \omega * QAinvxres_old
250  CALL pw_scale(qainvxres, omega)
251  CALL pw_copy(qainvxres, v_new)
252  CALL pw_axpy(v_old, v_new)
253 
254 ! res_new = res_old - \omega * ( \Delta(QAinvxres_old) + P(QAinvxres_old) )
255 ! = (1 - \omega) * res_old - \omega * PxQAinvxres
256  CALL apply_p_operator(pw_pool, dielectric, qainvxres, pxqainvxres)
257  CALL pw_copy(pxqainvxres, res_new)
258  CALL pw_scale(res_new, -1.0_dp)
259  CALL pw_axpy(res_old, res_new, 1.0_dp - omega)
260 
261 ! compute the error
262  CALL ps_implicit_compute_error_fft(pw_pool, green, res_new, v_old, v_new, qainvxres, &
263  pres_error, nabs_error)
264 ! output
265  CALL ps_implicit_output(iter, pres_error, nabs_error, outp_unit)
266  IF (PRESENT(ehartree)) THEN
267  CALL ps_implicit_compute_ehartree(density, v_new, ehartree)
268  CALL ps_implicit_report_ehartree(ps_implicit_env, outp_unit, ehartree)
269  ps_implicit_env%ehartree = ehartree
270  ELSE
271  IF (outp_unit .GT. 0) WRITE (outp_unit, '(A1,/)')
272  END IF
273 
274  iter = iter + 1
275  reached_max_iter = iter .GT. max_iter
276  reached_tol = pres_error .LE. tol
277  IF (pres_error .GT. large_error) &
278  cpabort("Poisson solver did not converge.")
279  ps_implicit_env%times_called = ps_implicit_env%times_called + 1
280  IF (reached_max_iter .OR. reached_tol) EXIT
281 
282 ! v_old = v_new, res_old = res_new
283  CALL pw_copy(v_new, v_old)
284  CALL pw_copy(res_new, res_old)
285 
286  END DO
287  CALL ps_implicit_print_convergence_msg(iter, max_iter, outp_unit)
288 
289  IF ((times_called .NE. 0) .AND. (.NOT. use_zero_initial_guess)) &
290  CALL pw_copy(v_new, ps_implicit_env%initial_guess)
291 
292  IF (PRESENT(ehartree)) ehartree = ps_implicit_env%ehartree
293 ! compute the extra contribution to the Hamiltonian due to the presence of dielectric
294  block
295  TYPE(pw_r3d_rs_type) :: v_eps
296  v_eps%pw_grid => ps_implicit_env%v_eps%pw_grid
297  v_eps%array => ps_implicit_env%v_eps%array
298  CALL ps_implicit_compute_veps(pw_pool, dielectric, v_new, v_eps)
299  END block
300 
301  CALL pw_pool%give_back_pw(g)
302  CALL pw_pool%give_back_pw(v_old)
303  CALL pw_pool%give_back_pw(res_old)
304  CALL pw_pool%give_back_pw(res_new)
305  CALL pw_pool%give_back_pw(qainvxres)
306  CALL pw_pool%give_back_pw(pxqainvxres)
307 
308  CALL timestop(handle)
309 
310  END SUBROUTINE implicit_poisson_solver_periodic
311 
312 ! **************************************************************************************************
313 !> \brief implicit Poisson solver: zero-average solution of the Poisson equation
314 !> subject to homogeneous Neumann boundary conditions
315 !> \param poisson_env poisson environment
316 !> \param density electron density
317 !> \param v_new electrostatic potential
318 !> \param ehartree Hartree energy
319 !> \par History
320 !> 02.2015 created [Hossein Bani-Hashemian]
321 !> 11.2015 revised [Hossein Bani-Hashemian]
322 !> \author Mohammad Hossein Bani-Hashemian
323 ! **************************************************************************************************
324  SUBROUTINE implicit_poisson_solver_neumann(poisson_env, density, v_new, ehartree)
325 
326  TYPE(pw_poisson_type), INTENT(IN) :: poisson_env
327  TYPE(pw_r3d_rs_type), INTENT(IN) :: density
328  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: v_new
329  REAL(dp), INTENT(OUT), OPTIONAL :: ehartree
330 
331  CHARACTER(LEN=*), PARAMETER :: routinen = 'implicit_poisson_solver_neumann'
332 
333  INTEGER :: handle, iter, max_iter, &
334  neumann_directions, outp_unit, &
335  times_called
336  LOGICAL :: reached_max_iter, reached_tol, &
337  use_zero_initial_guess
338  REAL(dp) :: nabs_error, omega, pres_error, tol, &
339  vol_scfac
340  TYPE(dct_type), POINTER :: dct_env
341  TYPE(dielectric_type), POINTER :: dielectric
342  TYPE(greens_fn_type), POINTER :: green
343  TYPE(ps_implicit_type), POINTER :: ps_implicit_env
344  TYPE(pw_pool_type), POINTER :: pw_pool, pw_pool_xpndd
345  TYPE(pw_r3d_rs_type) :: density_xpndd, g, pxqainvxres, &
346  qainvxres, res_new, res_old, &
347  v_eps_xpndd, v_new_xpndd, v_old
348 
349  CALL timeset(routinen, handle)
350 
351  pw_pool => poisson_env%pw_pools(poisson_env%pw_level)%pool
352  dielectric => poisson_env%implicit_env%dielectric
353  green => poisson_env%green_fft
354  ps_implicit_env => poisson_env%implicit_env
355  dct_env => ps_implicit_env%dct_env
356 
357  tol = poisson_env%parameters%ps_implicit_params%tol
358  omega = poisson_env%parameters%ps_implicit_params%omega
359  max_iter = poisson_env%parameters%ps_implicit_params%max_iter
360  use_zero_initial_guess = poisson_env%parameters%ps_implicit_params%zero_initial_guess
361  neumann_directions = poisson_env%parameters%ps_implicit_params%neumann_directions
362  times_called = ps_implicit_env%times_called
363 
364  SELECT CASE (neumann_directions)
365  CASE (neumannxyz)
366  vol_scfac = 8.0_dp
367  CASE (neumannxy, neumannxz, neumannyz)
368  vol_scfac = 4.0_dp
369  CASE (neumannx, neumanny, neumannz)
370  vol_scfac = 2.0_dp
371  END SELECT
372 
373  CALL pw_pool_create(pw_pool_xpndd, pw_grid=poisson_env%dct_pw_grid)
374 
375 ! check if this is the first scf iteration
376  IF (times_called .EQ. 0) CALL ps_implicit_initial_guess_create(ps_implicit_env, pw_pool_xpndd)
377 
378  CALL pw_pool_xpndd%create_pw(g)
379  CALL pw_pool_xpndd%create_pw(v_old)
380  CALL pw_pool_xpndd%create_pw(res_old)
381  CALL pw_pool_xpndd%create_pw(res_new)
382  CALL pw_pool_xpndd%create_pw(qainvxres)
383  CALL pw_pool_xpndd%create_pw(pxqainvxres)
384  CALL pw_pool_xpndd%create_pw(density_xpndd)
385  CALL pw_pool_xpndd%create_pw(v_new_xpndd)
386  CALL pw_pool_xpndd%create_pw(v_eps_xpndd)
387 
388  IF (use_zero_initial_guess) THEN
389  CALL pw_zero(v_old)
390  ELSE
391  CALL pw_copy(ps_implicit_env%initial_guess, v_old)
392  END IF
393 
394  CALL pw_expand(neumann_directions, &
395  dct_env%recv_msgs_bnds, dct_env%dests_expand, dct_env%srcs_expand, &
396  dct_env%flipg_stat, dct_env%bounds_shftd, density, density_xpndd)
397  CALL pw_expand(neumann_directions, &
398  dct_env%recv_msgs_bnds, dct_env%dests_expand, dct_env%srcs_expand, &
399  dct_env%flipg_stat, dct_env%bounds_shftd, v_new, v_new_xpndd)
400 
401  g%array = fourpi*density_xpndd%array/dielectric%eps%array
402 
403 ! res_old = g - \Delta(v_old) - P(v_old)
404  CALL apply_poisson_operator_dct(pw_pool_xpndd, green, dielectric, v_old, res_old)
405  CALL pw_scale(res_old, -1.0_dp)
406  CALL pw_axpy(g, res_old)
407 
408 ! evaluate \Delta^-1(res_old)
409  CALL apply_inv_laplace_operator_dct(pw_pool_xpndd, green, res_old, qainvxres)
410 
411  iter = 1
412  DO
413 
414 ! v_new = v_old + \omega * QAinvxres_old
415  CALL pw_scale(qainvxres, omega)
416  CALL pw_copy(qainvxres, v_new_xpndd)
417  CALL pw_axpy(v_old, v_new_xpndd)
418 
419 ! res_new = res_old - \omega * ( \Delta(QAinvxres_old) + P(QAinvxres_old) )
420 ! = (1 - \omega) * res_old - \omega * PxQAinvxres
421  CALL apply_p_operator(pw_pool_xpndd, dielectric, qainvxres, pxqainvxres)
422  CALL pw_copy(pxqainvxres, res_new)
423  CALL pw_scale(res_new, -1.0_dp)
424  CALL pw_axpy(res_old, res_new, 1.0_dp - omega)
425 
426 ! compute the error
427  CALL ps_implicit_compute_error_dct(pw_pool_xpndd, green, res_new, v_old, v_new_xpndd, qainvxres, &
428  pres_error, nabs_error)
429 ! output
430  CALL ps_implicit_output(iter, pres_error, nabs_error, outp_unit)
431  IF (PRESENT(ehartree)) THEN
432  CALL ps_implicit_compute_ehartree(density_xpndd, v_new_xpndd, ehartree)
433  CALL ps_implicit_report_ehartree(ps_implicit_env, outp_unit, ehartree/vol_scfac)
434  ps_implicit_env%ehartree = ehartree/vol_scfac
435  ELSE
436  IF (outp_unit .GT. 0) WRITE (outp_unit, '(A1,/)')
437  END IF
438 
439  iter = iter + 1
440  reached_max_iter = iter .GT. max_iter
441  reached_tol = pres_error .LE. tol
442  IF (pres_error .GT. large_error) &
443  cpabort("Poisson solver did not converge.")
444  ps_implicit_env%times_called = ps_implicit_env%times_called + 1
445  IF (reached_max_iter .OR. reached_tol) EXIT
446 
447 ! v_old = v_new, res_old = res_new
448  CALL pw_copy(v_new_xpndd, v_old)
449  CALL pw_copy(res_new, res_old)
450 
451  END DO
452  CALL ps_implicit_print_convergence_msg(iter, max_iter, outp_unit)
453 
454  CALL pw_shrink(neumann_directions, dct_env%dests_shrink, dct_env%srcs_shrink, &
455  dct_env%bounds_local_shftd, v_new_xpndd, v_new)
456 
457  IF ((times_called .NE. 0) .AND. (.NOT. use_zero_initial_guess)) &
458  CALL pw_copy(v_new_xpndd, ps_implicit_env%initial_guess)
459 
460  IF (PRESENT(ehartree)) ehartree = ps_implicit_env%ehartree
461 ! compute the extra contribution to the Hamiltonian due to the presence of dielectric
462 ! veps has to be computed for the expanded data and then shrunk otherwise we loose accuracy
463  CALL ps_implicit_compute_veps(pw_pool_xpndd, dielectric, v_new_xpndd, v_eps_xpndd)
464  block
465  TYPE(pw_r3d_rs_type) :: v_eps
466  v_eps%pw_grid => ps_implicit_env%v_eps%pw_grid
467  v_eps%array => ps_implicit_env%v_eps%array
468  CALL pw_shrink(neumann_directions, dct_env%dests_shrink, dct_env%srcs_shrink, &
469  dct_env%bounds_local_shftd, v_eps_xpndd, v_eps)
470  END block
471 
472  CALL pw_pool_xpndd%give_back_pw(g)
473  CALL pw_pool_xpndd%give_back_pw(v_old)
474  CALL pw_pool_xpndd%give_back_pw(res_old)
475  CALL pw_pool_xpndd%give_back_pw(res_new)
476  CALL pw_pool_xpndd%give_back_pw(qainvxres)
477  CALL pw_pool_xpndd%give_back_pw(pxqainvxres)
478  CALL pw_pool_xpndd%give_back_pw(density_xpndd)
479  CALL pw_pool_xpndd%give_back_pw(v_new_xpndd)
480  CALL pw_pool_xpndd%give_back_pw(v_eps_xpndd)
481  CALL pw_pool_release(pw_pool_xpndd)
482 
483  CALL timestop(handle)
484 
485  END SUBROUTINE implicit_poisson_solver_neumann
486 
487 ! **************************************************************************************************
488 !> \brief implicit Poisson solver for mixed-periodic boundary conditions (periodic + Dirichlet)
489 !> \param poisson_env poisson environment
490 !> \param density electron density
491 !> \param v_new electrostatic potential
492 !> \param electric_enthalpy electric enthalpy
493 !> \par History
494 !> 07.2014 created [Hossein Bani-Hashemian]
495 !> \author Mohammad Hossein Bani-Hashemian
496 ! **************************************************************************************************
497  SUBROUTINE implicit_poisson_solver_mixed_periodic(poisson_env, density, v_new, electric_enthalpy)
498 
499  TYPE(pw_poisson_type), INTENT(IN) :: poisson_env
500  TYPE(pw_r3d_rs_type), INTENT(IN) :: density
501  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: v_new
502  REAL(dp), INTENT(OUT), OPTIONAL :: electric_enthalpy
503 
504  CHARACTER(LEN=*), PARAMETER :: routinen = 'implicit_poisson_solver_mixed_periodic'
505 
506  INTEGER :: data_size, handle, iter, j, lb1, lb2, lb3, max_iter, n_contacts, n_tiles_tot, ng, &
507  ngpts_local, nt, nt_tot, outp_unit, times_called, ub1, ub2, ub3
508  INTEGER(KIND=int_8) :: ngpts
509  INTEGER, DIMENSION(2, 3) :: bounds_local
510  INTEGER, DIMENSION(3) :: npts_local
511  LOGICAL :: reached_max_iter, reached_tol, &
512  use_zero_initial_guess
513  REAL(dp) :: axvbar_avg, ehartree, eta, g_avg, &
514  gminusaxvbar_avg, nabs_error, omega, &
515  pres_error, tol
516  REAL(dp), ALLOCATABLE, DIMENSION(:) :: btxlambda_new, btxlambda_old, bxv_bar, bxv_new, &
517  lambda0, lambda_new, lambda_newneta, lambda_old, qsxlambda, v_bar1d, v_d, v_new1d, w
518  REAL(dp), ALLOCATABLE, DIMENSION(:, :) :: b, bt, qs, rinv
519  REAL(dp), ALLOCATABLE, DIMENSION(:, :, :) :: btxlambda_new3d, btxlambda_old3d
520  TYPE(dielectric_type), POINTER :: dielectric
521  TYPE(greens_fn_type), POINTER :: green
522  TYPE(ps_implicit_type), POINTER :: ps_implicit_env
523  TYPE(pw_grid_type), POINTER :: pw_grid
524  TYPE(pw_pool_type), POINTER :: pw_pool
525  TYPE(pw_r3d_rs_type) :: axvbar, g, pxqainvxres, qainvxres, &
526  res_new, res_old, v_old
527 
528  CALL timeset(routinen, handle)
529 
530  pw_pool => poisson_env%pw_pools(poisson_env%pw_level)%pool
531  pw_grid => pw_pool%pw_grid
532  dielectric => poisson_env%implicit_env%dielectric
533  green => poisson_env%green_fft
534  ps_implicit_env => poisson_env%implicit_env
535 
536  ngpts_local = pw_grid%ngpts_local
537  ngpts = pw_grid%ngpts
538  npts_local = pw_grid%npts_local
539  bounds_local = pw_grid%bounds_local
540  tol = poisson_env%parameters%ps_implicit_params%tol
541  omega = poisson_env%parameters%ps_implicit_params%omega
542  max_iter = poisson_env%parameters%ps_implicit_params%max_iter
543  use_zero_initial_guess = poisson_env%parameters%ps_implicit_params%zero_initial_guess
544  times_called = ps_implicit_env%times_called
545 
546  n_contacts = SIZE(ps_implicit_env%contacts)
547  n_tiles_tot = 0
548  DO j = 1, n_contacts
549  n_tiles_tot = n_tiles_tot + ps_implicit_env%contacts(j)%dirichlet_bc%n_tiles
550  END DO
551 
552  IF (pw_grid%para%blocked) THEN
553  data_size = product(npts_local)
554  ELSE IF (pw_grid%para%ray_distribution) THEN
555  data_size = ngpts_local
556  ELSE ! parallel run with np = 1
557  data_size = product(npts_local)
558  END IF
559 
560 ! check if this is the first scf iteration
561  IF (times_called .EQ. 0) CALL ps_implicit_initial_guess_create(ps_implicit_env, pw_pool)
562 
563  ALLOCATE (b(n_tiles_tot, data_size))
564  ALLOCATE (bt(data_size, n_tiles_tot))
565  ALLOCATE (qs(n_tiles_tot, n_tiles_tot))
566  ALLOCATE (rinv(n_tiles_tot + 1, n_tiles_tot + 1))
567 
568  b(:, :) = ps_implicit_env%B
569  bt(:, :) = ps_implicit_env%Bt
570  qs(:, :) = ps_implicit_env%QS
571  rinv(:, :) = ps_implicit_env%Rinv
572  CALL get_voltage(poisson_env%parameters%dbc_params%time, ps_implicit_env%v_D, ps_implicit_env%osc_frac, &
573  ps_implicit_env%frequency, ps_implicit_env%phase, v_d)
574 
575  lb1 = bounds_local(1, 1); ub1 = bounds_local(2, 1)
576  lb2 = bounds_local(1, 2); ub2 = bounds_local(2, 2)
577  lb3 = bounds_local(1, 3); ub3 = bounds_local(2, 3)
578 
579  ALLOCATE (lambda0(n_tiles_tot), lambda_old(n_tiles_tot), lambda_new(n_tiles_tot))
580  ALLOCATE (btxlambda_old(data_size), btxlambda_new(data_size))
581  ALLOCATE (btxlambda_old3d(lb1:ub1, lb2:ub2, lb3:ub3), btxlambda_new3d(lb1:ub1, lb2:ub2, lb3:ub3))
582  ALLOCATE (qsxlambda(n_tiles_tot))
583  ALLOCATE (w(n_tiles_tot + 1))
584  ALLOCATE (lambda_newneta(n_tiles_tot + 1))
585  ALLOCATE (v_bar1d(data_size))
586  ALLOCATE (bxv_bar(n_tiles_tot))
587 
588  ALLOCATE (v_new1d(data_size))
589  ALLOCATE (bxv_new(n_tiles_tot))
590 
591  CALL pw_pool%create_pw(g)
592  CALL pw_pool%create_pw(v_old)
593  CALL pw_pool%create_pw(res_old)
594  CALL pw_pool%create_pw(res_new)
595  CALL pw_pool%create_pw(qainvxres)
596  CALL pw_pool%create_pw(pxqainvxres)
597  CALL pw_pool%create_pw(axvbar)
598 
599  IF (use_zero_initial_guess) THEN
600  CALL pw_zero(v_old)
601  lambda0 = 0.0_dp
602  ELSE
603  CALL pw_copy(ps_implicit_env%initial_guess, v_old)
604  lambda0(:) = ps_implicit_env%initial_lambda
605  END IF
606 
607  g%array = fourpi*density%array/dielectric%eps%array
608  g_avg = accurate_sum(g%array)/ngpts
609 
610  lambda_old(:) = lambda0
611 
612 ! res_old = g - \Delta(v_old) - P(v_old) - B^t * \lambda_old
613  CALL apply_poisson_operator_fft(pw_pool, green, dielectric, v_old, res_old)
614  CALL pw_scale(res_old, -1.0_dp)
615  CALL pw_axpy(g, res_old)
616  IF (data_size .NE. 0) THEN
617  CALL dgemv('N', data_size, n_tiles_tot, 1.0_dp, bt, data_size, lambda_old, 1, 0.0_dp, btxlambda_old, 1)
618  END IF
619  CALL convert_1dto3d(ps_implicit_env%idx_1dto3d, btxlambda_old, btxlambda_old3d)
620  res_old%array = res_old%array - btxlambda_old3d
621 
622 ! evaluate \Delta^-1(res_old)
623  CALL apply_inv_laplace_operator_fft(pw_pool, green, res_old, qainvxres)
624 
625  iter = 1
626  DO
627 
628 ! v_new (v_bar) = v_old + \omega * QAinvxres_old
629  CALL pw_scale(qainvxres, omega)
630  CALL pw_copy(qainvxres, v_new)
631  CALL pw_axpy(v_old, v_new)
632 
633 ! evaluate 1^t * (g - \Delta(\bar{v}) - P(\bar{v}))
634 ! = 1^t * (g - P(\bar{v}))
635  CALL apply_p_operator(pw_pool, dielectric, v_new, axvbar)
636  axvbar_avg = accurate_sum(axvbar%array)/ngpts
637  gminusaxvbar_avg = g_avg - axvbar_avg
638  CALL pw_grid%para%group%sum(gminusaxvbar_avg)
639 
640 ! evaluate Q_S * \lambda + v_D - B * \bar{v}
641  CALL dgemv('N', n_tiles_tot, n_tiles_tot, 1.0_dp, qs, n_tiles_tot, lambda_old, 1, 0.0_dp, qsxlambda, 1)
642  v_bar1d(ps_implicit_env%idx_1dto3d) = reshape(v_new%array, (/data_size/))
643  CALL dgemv('N', n_tiles_tot, data_size, 1.0_dp, b, n_tiles_tot, v_bar1d, 1, 0.0_dp, bxv_bar, 1)
644  CALL pw_grid%para%group%sum(bxv_bar)
645 ! solve R [\lambda; \eta] = [Q_S * \lambda + v_D - B * \bar{v}; 1^t * (g - \Delta(\bar{v}) - P(\bar{v}))]
646  w = 0.0_dp
647  w(:) = (/qsxlambda + v_d - bxv_bar, gminusaxvbar_avg/)
648  CALL dgemv('N', n_tiles_tot + 1, n_tiles_tot + 1, 1.0_dp, rinv, n_tiles_tot + 1, w, 1, 0.0_dp, lambda_newneta, 1)
649  lambda_new(:) = lambda_newneta(1:n_tiles_tot)
650  eta = lambda_newneta(n_tiles_tot + 1)
651 
652 ! v_new = v_bar + 1 * \eta
653  v_new%array = v_new%array + eta/ngpts
654 
655 ! evaluate B^t * \lambda_new
656  IF (data_size .NE. 0) THEN
657  CALL dgemv('N', data_size, n_tiles_tot, 1.0_dp, bt, data_size, lambda_new, 1, 0.0_dp, btxlambda_new, 1)
658  END IF
659  CALL convert_1dto3d(ps_implicit_env%idx_1dto3d, btxlambda_new, btxlambda_new3d)
660 
661 ! res_new = res_old - \omega * ( \Delta(QAinvxres_old) + P(QAinvxres_old) ) - B^t * ( \lambda_new - \lambda_old )
662 ! = (1 - \omega) * res_old - \omega * P(QAinvxres_old) - B^t * ( \lambda_new - \lambda_old )
663  CALL pw_zero(res_new)
664  CALL apply_p_operator(pw_pool, dielectric, qainvxres, pxqainvxres)
665  CALL pw_axpy(pxqainvxres, res_new, -1.0_dp)
666  CALL pw_axpy(res_old, res_new, 1.0_dp - omega)
667  res_new%array = res_new%array + btxlambda_old3d - btxlambda_new3d
668 
669 ! compute the error
670  CALL ps_implicit_compute_error_fft(pw_pool, green, res_new, v_old, v_new, qainvxres, &
671  pres_error, nabs_error)
672 ! output
673  CALL ps_implicit_output(iter, pres_error, nabs_error, outp_unit)
674  IF (PRESENT(electric_enthalpy)) THEN
675  CALL ps_implicit_compute_ehartree(dielectric, density, btxlambda_new3d, v_new, ehartree, electric_enthalpy)
676  CALL ps_implicit_report_ehartree(ps_implicit_env, outp_unit, ehartree)
677  ps_implicit_env%ehartree = ehartree
678  ps_implicit_env%electric_enthalpy = electric_enthalpy
679  ELSE
680  IF (outp_unit .GT. 0) WRITE (outp_unit, '(A1,/)')
681  END IF
682 
683 ! verbose output
684  IF (poisson_env%parameters%dbc_params%verbose_output) THEN
685  v_new1d(ps_implicit_env%idx_1dto3d) = reshape(v_new%array, (/data_size/))
686  CALL dgemv('N', n_tiles_tot, data_size, 1.0_dp, b, n_tiles_tot, v_new1d, 1, 0.0_dp, bxv_new, 1)
687  CALL pw_grid%para%group%sum(bxv_new)
688  IF (outp_unit .GT. 0) THEN
689  WRITE (outp_unit, '(T3,A,A)') "======== verbose ", repeat('=', 61)
690  WRITE (outp_unit, '(T20,A)') "Drgn tile vhartree lambda "
691  WRITE (outp_unit, '(T19,A)') repeat('-', 46)
692  nt_tot = 1
693  DO ng = 1, n_contacts
694  DO nt = 1, ps_implicit_env%contacts(ng)%dirichlet_bc%n_tiles
695  WRITE (outp_unit, '(T17,I6,5X,I6,3X,E13.4,E13.4)') ng, nt, bxv_new(nt_tot), lambda_new(nt_tot)
696  nt_tot = nt_tot + 1
697  END DO
698  END DO
699  WRITE (outp_unit, '(T3,A)') repeat('=', 78)
700  END IF
701  END IF
702 
703 ! check the convergence
704  iter = iter + 1
705  reached_max_iter = iter .GT. max_iter
706  reached_tol = pres_error .LE. tol
707  ps_implicit_env%times_called = ps_implicit_env%times_called + 1
708  IF (pres_error .GT. large_error) &
709  cpabort("Poisson solver did not converge.")
710  IF (reached_max_iter .OR. reached_tol) EXIT
711 
712 ! update
713  CALL pw_copy(v_new, v_old)
714  lambda_old(:) = lambda_new
715  CALL pw_copy(res_new, res_old)
716  btxlambda_old3d(:, :, :) = btxlambda_new3d
717 
718  END DO
719  CALL ps_implicit_print_convergence_msg(iter, max_iter, outp_unit)
720 
721  IF ((times_called .NE. 0) .AND. (.NOT. use_zero_initial_guess)) THEN
722  CALL pw_copy(v_new, ps_implicit_env%initial_guess)
723  ps_implicit_env%initial_lambda(:) = lambda_new
724  END IF
725 
726  ps_implicit_env%cstr_charge%array = btxlambda_new3d
727  IF (PRESENT(electric_enthalpy)) electric_enthalpy = ps_implicit_env%electric_enthalpy
728 ! compute the extra contribution to the Hamiltonian due to the presence of dielectric
729  block
730  TYPE(pw_r3d_rs_type) :: tmp
731  tmp%pw_grid => ps_implicit_env%v_eps%pw_grid
732  tmp%array => ps_implicit_env%v_eps%array
733  CALL ps_implicit_compute_veps(pw_pool, dielectric, v_new, tmp)
734  END block
735 
736  CALL pw_pool%give_back_pw(g)
737  CALL pw_pool%give_back_pw(v_old)
738  CALL pw_pool%give_back_pw(res_old)
739  CALL pw_pool%give_back_pw(res_new)
740  CALL pw_pool%give_back_pw(qainvxres)
741  CALL pw_pool%give_back_pw(pxqainvxres)
742  CALL pw_pool%give_back_pw(axvbar)
743 
744  CALL timestop(handle)
745 
747 
748 ! **************************************************************************************************
749 !> \brief implicit Poisson solver for mixed boundary conditions (Neumann + Dirichlet)
750 !> \param poisson_env poisson environment
751 !> \param density electron density
752 !> \param v_new electrostatic potential
753 !> \param electric_enthalpy electric enthalpy
754 !> \par History
755 !> 10.2014 created [Hossein Bani-Hashemian]
756 !> 11.2015 revised [Hossein Bani-Hashemian]
757 !> \author Mohammad Hossein Bani-Hashemian
758 ! **************************************************************************************************
759  SUBROUTINE implicit_poisson_solver_mixed(poisson_env, density, v_new, electric_enthalpy)
760 
761  TYPE(pw_poisson_type), INTENT(IN) :: poisson_env
762  TYPE(pw_r3d_rs_type), INTENT(IN) :: density
763  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: v_new
764  REAL(dp), INTENT(OUT), OPTIONAL :: electric_enthalpy
765 
766  CHARACTER(LEN=*), PARAMETER :: routinen = 'implicit_poisson_solver_mixed'
767 
768  INTEGER :: data_size, handle, iter, j, lb1, lb2, lb3, max_iter, n_contacts, n_tiles_tot, &
769  neumann_directions, ng, ngpts_local, nt, nt_tot, outp_unit, times_called, ub1, ub2, ub3
770  INTEGER(KIND=int_8) :: ngpts
771  INTEGER, DIMENSION(2, 3) :: bounds_local
772  INTEGER, DIMENSION(3) :: npts_local
773  LOGICAL :: reached_max_iter, reached_tol, &
774  use_zero_initial_guess
775  REAL(dp) :: axvbar_avg, ehartree, eta, g_avg, &
776  gminusaxvbar_avg, nabs_error, omega, &
777  pres_error, tol, vol_scfac
778  REAL(dp), ALLOCATABLE, DIMENSION(:) :: btxlambda_new, btxlambda_old, bxv_bar, bxv_new, &
779  lambda0, lambda_new, lambda_newneta, lambda_old, qsxlambda, v_bar1d, v_d, v_new1d, w
780  REAL(dp), ALLOCATABLE, DIMENSION(:, :) :: b, bt, qs, rinv
781  REAL(dp), ALLOCATABLE, DIMENSION(:, :, :) :: btxlambda_new3d, btxlambda_old3d
782  TYPE(dct_type), POINTER :: dct_env
783  TYPE(dielectric_type), POINTER :: dielectric
784  TYPE(greens_fn_type), POINTER :: green
785  TYPE(ps_implicit_type), POINTER :: ps_implicit_env
786  TYPE(pw_grid_type), POINTER :: dct_pw_grid, pw_grid
787  TYPE(pw_pool_type), POINTER :: pw_pool, pw_pool_xpndd
788  TYPE(pw_r3d_rs_type) :: axvbar, density_xpndd, g, pxqainvxres, &
789  qainvxres, res_new, res_old, &
790  v_eps_xpndd, v_new_xpndd, v_old
791 
792  CALL timeset(routinen, handle)
793 
794  pw_pool => poisson_env%pw_pools(poisson_env%pw_level)%pool
795  pw_grid => pw_pool%pw_grid
796  dielectric => poisson_env%implicit_env%dielectric
797  green => poisson_env%green_fft
798  ps_implicit_env => poisson_env%implicit_env
799  dct_env => ps_implicit_env%dct_env
800 
801  dct_pw_grid => poisson_env%dct_pw_grid
802  ngpts_local = dct_pw_grid%ngpts_local
803  ngpts = dct_pw_grid%ngpts
804  npts_local = dct_pw_grid%npts_local
805  bounds_local = dct_pw_grid%bounds_local
806  tol = poisson_env%parameters%ps_implicit_params%tol
807  omega = poisson_env%parameters%ps_implicit_params%omega
808  max_iter = poisson_env%parameters%ps_implicit_params%max_iter
809  use_zero_initial_guess = poisson_env%parameters%ps_implicit_params%zero_initial_guess
810  neumann_directions = poisson_env%parameters%ps_implicit_params%neumann_directions
811  times_called = ps_implicit_env%times_called
812 
813  SELECT CASE (neumann_directions)
814  CASE (neumannxyz)
815  vol_scfac = 8.0_dp
816  CASE (neumannxy, neumannxz, neumannyz)
817  vol_scfac = 4.0_dp
818  CASE (neumannx, neumanny, neumannz)
819  vol_scfac = 2.0_dp
820  END SELECT
821 
822  n_contacts = SIZE(ps_implicit_env%contacts)
823  n_tiles_tot = 0
824  DO j = 1, n_contacts
825  n_tiles_tot = n_tiles_tot + ps_implicit_env%contacts(j)%dirichlet_bc%n_tiles
826  END DO
827 
828  IF (dct_pw_grid%para%blocked) THEN
829  data_size = product(npts_local)
830  ELSE IF (dct_pw_grid%para%ray_distribution) THEN
831  data_size = ngpts_local
832  ELSE ! parallel run with np = 1
833  data_size = product(npts_local)
834  END IF
835 
836  CALL pw_pool_create(pw_pool_xpndd, pw_grid=dct_pw_grid)
837 
838 ! check if this is the first scf iteration
839  IF (times_called .EQ. 0) CALL ps_implicit_initial_guess_create(ps_implicit_env, pw_pool_xpndd)
840 
841  ALLOCATE (b(n_tiles_tot, data_size))
842  ALLOCATE (bt(data_size, n_tiles_tot))
843  ALLOCATE (qs(n_tiles_tot, n_tiles_tot))
844  ALLOCATE (rinv(n_tiles_tot + 1, n_tiles_tot + 1))
845 
846  b(:, :) = ps_implicit_env%B
847  bt(:, :) = ps_implicit_env%Bt
848  qs(:, :) = ps_implicit_env%QS
849  rinv(:, :) = ps_implicit_env%Rinv
850  CALL get_voltage(poisson_env%parameters%dbc_params%time, ps_implicit_env%v_D, ps_implicit_env%osc_frac, &
851  ps_implicit_env%frequency, ps_implicit_env%phase, v_d)
852 
853  lb1 = bounds_local(1, 1); ub1 = bounds_local(2, 1)
854  lb2 = bounds_local(1, 2); ub2 = bounds_local(2, 2)
855  lb3 = bounds_local(1, 3); ub3 = bounds_local(2, 3)
856 
857  ALLOCATE (lambda0(n_tiles_tot), lambda_old(n_tiles_tot), lambda_new(n_tiles_tot))
858  ALLOCATE (btxlambda_old(data_size), btxlambda_new(data_size))
859  ALLOCATE (btxlambda_old3d(lb1:ub1, lb2:ub2, lb3:ub3), btxlambda_new3d(lb1:ub1, lb2:ub2, lb3:ub3))
860  ALLOCATE (qsxlambda(n_tiles_tot))
861  ALLOCATE (w(n_tiles_tot + 1))
862  ALLOCATE (lambda_newneta(n_tiles_tot + 1))
863  ALLOCATE (v_bar1d(data_size))
864  ALLOCATE (bxv_bar(n_tiles_tot))
865 
866  ALLOCATE (v_new1d(data_size))
867  ALLOCATE (bxv_new(n_tiles_tot))
868 
869  CALL pw_pool_xpndd%create_pw(g)
870  CALL pw_pool_xpndd%create_pw(v_old)
871  CALL pw_pool_xpndd%create_pw(res_old)
872  CALL pw_pool_xpndd%create_pw(res_new)
873  CALL pw_pool_xpndd%create_pw(qainvxres)
874  CALL pw_pool_xpndd%create_pw(pxqainvxres)
875  CALL pw_pool_xpndd%create_pw(axvbar)
876  CALL pw_pool_xpndd%create_pw(density_xpndd)
877  CALL pw_pool_xpndd%create_pw(v_new_xpndd)
878  CALL pw_pool_xpndd%create_pw(v_eps_xpndd)
879 
880  IF (use_zero_initial_guess) THEN
881  CALL pw_zero(v_old)
882  lambda0 = 0.0_dp
883  ELSE
884  CALL pw_copy(ps_implicit_env%initial_guess, v_old)
885  lambda0(:) = ps_implicit_env%initial_lambda
886  END IF
887 
888  CALL pw_expand(neumann_directions, &
889  dct_env%recv_msgs_bnds, dct_env%dests_expand, dct_env%srcs_expand, &
890  dct_env%flipg_stat, dct_env%bounds_shftd, density, density_xpndd)
891  CALL pw_expand(neumann_directions, &
892  dct_env%recv_msgs_bnds, dct_env%dests_expand, dct_env%srcs_expand, &
893  dct_env%flipg_stat, dct_env%bounds_shftd, v_new, v_new_xpndd)
894 
895  g%array = fourpi*density_xpndd%array/dielectric%eps%array
896  g_avg = accurate_sum(g%array)/ngpts
897 
898  lambda_old(:) = lambda0
899 
900 ! res_old = g - \Delta(v_old) - P(v_old) - B^t * \lambda_old
901  CALL apply_poisson_operator_dct(pw_pool_xpndd, green, dielectric, v_old, res_old)
902  CALL pw_scale(res_old, -1.0_dp)
903  CALL pw_axpy(g, res_old)
904  IF (data_size .NE. 0) THEN
905  CALL dgemv('N', data_size, n_tiles_tot, 1.0_dp, bt, data_size, lambda_old, 1, 0.0_dp, btxlambda_old, 1)
906  END IF
907  CALL convert_1dto3d(ps_implicit_env%idx_1dto3d, btxlambda_old, btxlambda_old3d)
908  res_old%array = res_old%array - btxlambda_old3d
909 
910 ! evaluate \Delta^-1(res_old)
911  CALL apply_inv_laplace_operator_dct(pw_pool_xpndd, green, res_old, qainvxres)
912 
913  iter = 1
914  DO
915 
916 ! v_new (v_bar) = v_old + \omega * QAinvxres_old
917  CALL pw_scale(qainvxres, omega)
918  CALL pw_copy(qainvxres, v_new_xpndd)
919  CALL pw_axpy(v_old, v_new_xpndd)
920 
921 ! evaluate 1^t * (g - \Delta(\bar{v}) - P(\bar{v}))
922 ! = 1^t * (g - P(\bar{v}))
923  CALL apply_p_operator(pw_pool_xpndd, dielectric, v_new_xpndd, axvbar)
924  axvbar_avg = accurate_sum(axvbar%array)/ngpts
925  gminusaxvbar_avg = g_avg - axvbar_avg
926  CALL dct_pw_grid%para%group%sum(gminusaxvbar_avg)
927 
928 ! evaluate Q_S * \lambda + v_D - B * \bar{v}
929  CALL dgemv('N', n_tiles_tot, n_tiles_tot, 1.0_dp, qs, n_tiles_tot, lambda_old, 1, 0.0_dp, qsxlambda, 1)
930  v_bar1d(ps_implicit_env%idx_1dto3d) = reshape(v_new_xpndd%array, (/data_size/))
931  CALL dgemv('N', n_tiles_tot, data_size, 1.0_dp, b, n_tiles_tot, v_bar1d, 1, 0.0_dp, bxv_bar, 1)
932  CALL dct_pw_grid%para%group%sum(bxv_bar)
933 ! solve R [\lambda; \eta] = [Q_S * \lambda + v_D - B * \bar{v}; 1^t * (g - \Delta(\bar{v}) - P(\bar{v}))]
934  w = 0.0_dp
935  w(:) = (/qsxlambda + v_d - bxv_bar, gminusaxvbar_avg/)
936  CALL dgemv('N', n_tiles_tot + 1, n_tiles_tot + 1, 1.0_dp, rinv, n_tiles_tot + 1, w, 1, 0.0_dp, lambda_newneta, 1)
937  lambda_new(:) = lambda_newneta(1:n_tiles_tot)
938  eta = lambda_newneta(n_tiles_tot + 1)
939 
940 ! v_new = v_bar + 1 * \eta
941  v_new_xpndd%array = v_new_xpndd%array + eta/ngpts
942 
943 ! evaluate B^t * \lambda_new
944  IF (data_size .NE. 0) THEN
945  CALL dgemv('N', data_size, n_tiles_tot, 1.0_dp, bt, data_size, lambda_new, 1, 0.0_dp, btxlambda_new, 1)
946  END IF
947  CALL convert_1dto3d(ps_implicit_env%idx_1dto3d, btxlambda_new, btxlambda_new3d)
948 
949 ! res_new = res_old - \omega * ( \Delta(QAinvxres_old) + P(QAinvxres_old) ) - B^t * ( \lambda_new - \lambda_old )
950 ! = (1 - \omega) * res_old - \omega * P(QAinvxres_old) - B^t * ( \lambda_new - \lambda_old )
951  CALL pw_zero(res_new)
952  CALL apply_p_operator(pw_pool_xpndd, dielectric, qainvxres, pxqainvxres)
953  CALL pw_axpy(pxqainvxres, res_new, -1.0_dp)
954  CALL pw_axpy(res_old, res_new, 1.0_dp - omega)
955  res_new%array = res_new%array - btxlambda_new3d + btxlambda_old3d
956 
957 ! compute the error
958  CALL ps_implicit_compute_error_dct(pw_pool_xpndd, green, res_new, v_old, v_new_xpndd, qainvxres, &
959  pres_error, nabs_error)
960 ! output
961  CALL ps_implicit_output(iter, pres_error, nabs_error, outp_unit)
962  IF (PRESENT(electric_enthalpy)) THEN
963  CALL ps_implicit_compute_ehartree(dielectric, density_xpndd, btxlambda_new3d, v_new_xpndd, &
964  ehartree, electric_enthalpy)
965  CALL ps_implicit_report_ehartree(ps_implicit_env, outp_unit, ehartree/vol_scfac)
966  ps_implicit_env%ehartree = ehartree/vol_scfac
967  ps_implicit_env%electric_enthalpy = electric_enthalpy/vol_scfac
968  ELSE
969  IF (outp_unit .GT. 0) WRITE (outp_unit, '(A1,/)')
970  END IF
971 
972 ! verbose output
973  IF (poisson_env%parameters%dbc_params%verbose_output) THEN
974  v_new1d(ps_implicit_env%idx_1dto3d) = reshape(v_new_xpndd%array, (/data_size/))
975  CALL dgemv('N', n_tiles_tot, data_size, 1.0_dp, b, n_tiles_tot, v_new1d, 1, 0.0_dp, bxv_new, 1)
976  CALL pw_grid%para%group%sum(bxv_new)
977  IF (outp_unit .GT. 0) THEN
978  WRITE (outp_unit, '(T3,A)') "======== verbose "//repeat('=', 61)
979  WRITE (outp_unit, '(T20,A)') "Drgn tile vhartree lambda "
980  WRITE (outp_unit, '(T19,A)') repeat('-', 46)
981  nt_tot = 1
982  DO ng = 1, n_contacts
983  DO nt = 1, ps_implicit_env%contacts(ng)%dirichlet_bc%n_tiles
984  WRITE (outp_unit, '(T17,I6,5X,I6,3X,E13.4,E13.4)') ng, nt, bxv_new(nt_tot), lambda_new(nt_tot)
985  nt_tot = nt_tot + 1
986  END DO
987  END DO
988  WRITE (outp_unit, '(T3,A)') repeat('=', 78)
989  END IF
990  END IF
991 
992 ! check the convergence
993  iter = iter + 1
994  reached_max_iter = iter .GT. max_iter
995  reached_tol = pres_error .LE. tol
996  ps_implicit_env%times_called = ps_implicit_env%times_called + 1
997  IF (pres_error .GT. large_error) &
998  cpabort("Poisson solver did not converge.")
999  IF (reached_max_iter .OR. reached_tol) EXIT
1000 
1001 ! update
1002  CALL pw_copy(v_new_xpndd, v_old)
1003  lambda_old(:) = lambda_new
1004  CALL pw_copy(res_new, res_old)
1005  btxlambda_old3d(:, :, :) = btxlambda_new3d
1006 
1007  END DO
1008  CALL ps_implicit_print_convergence_msg(iter, max_iter, outp_unit)
1009 
1010  CALL pw_shrink(neumann_directions, dct_env%dests_shrink, dct_env%srcs_shrink, &
1011  dct_env%bounds_local_shftd, v_new_xpndd, v_new)
1012 
1013  IF ((times_called .NE. 0) .AND. (.NOT. use_zero_initial_guess)) THEN
1014  CALL pw_copy(v_new_xpndd, ps_implicit_env%initial_guess)
1015  ps_implicit_env%initial_lambda(:) = lambda_new
1016  END IF
1017 
1018  ps_implicit_env%cstr_charge%array = btxlambda_new3d
1019  IF (PRESENT(electric_enthalpy)) electric_enthalpy = ps_implicit_env%electric_enthalpy
1020 ! compute the extra contribution to the Hamiltonian due to the presence of dielectric
1021  CALL ps_implicit_compute_veps(pw_pool_xpndd, dielectric, v_new_xpndd, v_eps_xpndd)
1022  CALL pw_shrink(neumann_directions, dct_env%dests_shrink, dct_env%srcs_shrink, &
1023  dct_env%bounds_local_shftd, v_eps_xpndd, ps_implicit_env%v_eps)
1024 
1025  CALL pw_pool_xpndd%give_back_pw(g)
1026  CALL pw_pool_xpndd%give_back_pw(v_old)
1027  CALL pw_pool_xpndd%give_back_pw(res_old)
1028  CALL pw_pool_xpndd%give_back_pw(res_new)
1029  CALL pw_pool_xpndd%give_back_pw(qainvxres)
1030  CALL pw_pool_xpndd%give_back_pw(pxqainvxres)
1031  CALL pw_pool_xpndd%give_back_pw(axvbar)
1032  CALL pw_pool_xpndd%give_back_pw(density_xpndd)
1033  CALL pw_pool_xpndd%give_back_pw(v_new_xpndd)
1034  CALL pw_pool_xpndd%give_back_pw(v_eps_xpndd)
1035  CALL pw_pool_release(pw_pool_xpndd)
1036 
1037  CALL timestop(handle)
1038 
1039  END SUBROUTINE implicit_poisson_solver_mixed
1040 
1041 ! **************************************************************************************************
1042 !> \brief allocates and zeroises initial guess for implicit (iterative) Poisson solver
1043 !> \param ps_implicit_env the implicit env containing the initial guess
1044 !> \param pw_pool pool of pw grid
1045 !> \par History
1046 !> 06.2014 created [Hossein Bani-Hashemian]
1047 !> \author Mohammad Hossein Bani-Hashemian
1048 ! **************************************************************************************************
1049  SUBROUTINE ps_implicit_initial_guess_create(ps_implicit_env, pw_pool)
1050 
1051  TYPE(ps_implicit_type), INTENT(INOUT), POINTER :: ps_implicit_env
1052  TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1053 
1054  CHARACTER(LEN=*), PARAMETER :: routinen = 'ps_implicit_initial_guess_create'
1055 
1056  INTEGER :: handle, n_tiles_tot
1057 
1058  CALL timeset(routinen, handle)
1059 
1060  n_tiles_tot = SIZE(ps_implicit_env%v_D)
1061  NULLIFY (ps_implicit_env%initial_guess)
1062  ALLOCATE (ps_implicit_env%initial_guess)
1063  CALL pw_pool%create_pw(ps_implicit_env%initial_guess)
1064  CALL pw_zero(ps_implicit_env%initial_guess)
1065  ALLOCATE (ps_implicit_env%initial_lambda(n_tiles_tot))
1066  ps_implicit_env%initial_lambda = 0.0_dp
1067 
1068  CALL timestop(handle)
1069 
1070  END SUBROUTINE ps_implicit_initial_guess_create
1071 
1072 ! **************************************************************************************************
1073 !> \brief prepare blocks B, Bt, QS, R^-1, v_D
1074 !> \param pw_pool_orig original pw grid
1075 !> \param dct_pw_grid DCT (extended) grid
1076 !> \param green green functions for FFT based inverse Laplacian
1077 !> \param poisson_params paramaters of the poisson_env
1078 !> \param ps_implicit_env the implicit_env that stores the blocks
1079 !> \par History
1080 !> 10.2014 created [Hossein Bani-Hashemian]
1081 !> \author Mohammad Hossein Bani-Hashemian
1082 ! **************************************************************************************************
1083  SUBROUTINE ps_implicit_prepare_blocks(pw_pool_orig, dct_pw_grid, green, &
1084  poisson_params, ps_implicit_env)
1085 
1086  TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool_orig
1087  TYPE(pw_grid_type), INTENT(IN), POINTER :: dct_pw_grid
1088  TYPE(greens_fn_type), INTENT(IN) :: green
1089  TYPE(pw_poisson_parameter_type), INTENT(IN) :: poisson_params
1090  TYPE(ps_implicit_type), INTENT(INOUT), POINTER :: ps_implicit_env
1091 
1092  CHARACTER(LEN=*), PARAMETER :: routinen = 'ps_implicit_prepare_blocks'
1093 
1094  INTEGER :: data_size, handle, i, indx1, indx2, info, j, k, l, lb1, lb2, lb3, n_contacts, &
1095  n_tiles, n_tiles_tot, neumann_directions, ngpts_local, ub1, ub2, ub3, unit_nr
1096  INTEGER(KIND=int_8) :: ngpts
1097  INTEGER, ALLOCATABLE, DIMENSION(:) :: ipiv
1098  INTEGER, DIMENSION(2, 3) :: bounds, bounds_local
1099  INTEGER, DIMENSION(3) :: npts, npts_local
1100  LOGICAL :: done_preparing
1101  REAL(dp) :: tile_volume, vol_scfac
1102  REAL(dp), ALLOCATABLE, DIMENSION(:) :: bxunit_vec, test_vec, work_arr
1103  REAL(dp), ALLOCATABLE, DIMENSION(:, :) :: qainvxbt, r
1104  TYPE(cp_logger_type), POINTER :: logger
1105  TYPE(dct_type), POINTER :: dct_env
1106  TYPE(pw_grid_type), POINTER :: pw_grid_orig
1107  TYPE(pw_pool_type), POINTER :: pw_pool_xpndd
1108  TYPE(pw_r3d_rs_type) :: pw_in, pw_out
1109 
1110  CALL timeset(routinen, handle)
1111 
1112  pw_grid_orig => pw_pool_orig%pw_grid
1113 
1114  logger => cp_get_default_logger()
1115  IF (logger%para_env%is_source()) THEN
1116  unit_nr = cp_logger_get_default_unit_nr(logger, local=.true.)
1117  ELSE
1118  unit_nr = -1
1119  END IF
1120 
1121  SELECT CASE (poisson_params%ps_implicit_params%boundary_condition)
1122  CASE (mixed_bc)
1123 
1124  ngpts_local = dct_pw_grid%ngpts_local
1125  ngpts = dct_pw_grid%ngpts
1126  npts_local = dct_pw_grid%npts_local
1127  npts = dct_pw_grid%npts
1128  bounds_local = dct_pw_grid%bounds_local
1129  bounds = dct_pw_grid%bounds
1130  dct_env => ps_implicit_env%dct_env
1131 
1132  neumann_directions = poisson_params%ps_implicit_params%neumann_directions
1133 
1134  SELECT CASE (neumann_directions)
1135  CASE (neumannxyz)
1136  vol_scfac = 8.0_dp
1137  CASE (neumannxy, neumannxz, neumannyz)
1138  vol_scfac = 4.0_dp
1139  CASE (neumannx, neumanny, neumannz)
1140  vol_scfac = 2.0_dp
1141  END SELECT
1142 
1143 ! evaluate indices for converting 3D arrays into 1D arrays
1144  lb1 = bounds_local(1, 1); ub1 = bounds_local(2, 1)
1145  lb2 = bounds_local(1, 2); ub2 = bounds_local(2, 2)
1146  lb3 = bounds_local(1, 3); ub3 = bounds_local(2, 3)
1147 
1148  IF (dct_pw_grid%para%blocked) THEN
1149  data_size = product(npts_local)
1150  ELSE IF (dct_pw_grid%para%ray_distribution) THEN
1151  data_size = ngpts_local
1152  ELSE ! parallel run with np = 1
1153  data_size = product(npts_local)
1154  END IF
1155 
1156  ALLOCATE (ps_implicit_env%idx_1dto3d(data_size))
1157  l = 1
1158  ! Suppress OpenMP (at least the Intel compiler has an issue here)
1159  ! An automatic OpenMP parallelization of this loop might be tricky
1160  ! because of the l incrementation
1161 !$OMP PARALLEL IF(.FALSE.)
1162 !$OMP DO
1163  DO k = lb3, ub3
1164  DO j = lb2, ub2
1165  DO i = lb1, ub1
1166  ps_implicit_env%idx_1dto3d(l) = (i - lb1 + 1) + &
1167  (j - lb2)*npts_local(1) + &
1168  (k - lb3)*npts_local(1)*npts_local(2)
1169  l = l + 1
1170  END DO
1171  END DO
1172  END DO
1173 !$OMP END DO
1174 !$OMP END PARALLEL
1175 
1176  n_contacts = SIZE(ps_implicit_env%contacts)
1177  n_tiles_tot = 0
1178  DO j = 1, n_contacts
1179  n_tiles_tot = n_tiles_tot + ps_implicit_env%contacts(j)%dirichlet_bc%n_tiles
1180  END DO
1181 
1182  ALLOCATE (ps_implicit_env%B(n_tiles_tot, data_size))
1183  ALLOCATE (ps_implicit_env%Bt(data_size, n_tiles_tot))
1184  ALLOCATE (ps_implicit_env%QS(n_tiles_tot, n_tiles_tot))
1185  ALLOCATE (ps_implicit_env%Rinv(n_tiles_tot + 1, n_tiles_tot + 1))
1186  ALLOCATE (ps_implicit_env%v_D(n_tiles_tot))
1187  ALLOCATE (ps_implicit_env%osc_frac(n_tiles_tot))
1188  ALLOCATE (ps_implicit_env%frequency(n_tiles_tot))
1189  ALLOCATE (ps_implicit_env%phase(n_tiles_tot))
1190 
1191  ALLOCATE (qainvxbt(data_size, n_tiles_tot))
1192  ALLOCATE (bxunit_vec(n_tiles_tot))
1193  ALLOCATE (test_vec(n_tiles_tot))
1194  ALLOCATE (r(n_tiles_tot + 1, n_tiles_tot + 1))
1195  ALLOCATE (work_arr(n_tiles_tot + 1), ipiv(n_tiles_tot + 1)) ! LAPACK work and ipiv arrays
1196 
1197 ! prepare pw_pool for evaluating inverse Laplacian of tile_pw's using DCT
1198  CALL pw_pool_create(pw_pool_xpndd, pw_grid=dct_pw_grid)
1199 
1200 ! set up B, B^t, (\Delta^-1)*B^t
1201  indx1 = 1
1202  DO j = 1, n_contacts
1203  n_tiles = ps_implicit_env%contacts(j)%dirichlet_bc%n_tiles
1204  indx2 = indx1 + n_tiles - 1
1205  DO i = 1, n_tiles
1206 
1207  CALL pw_pool_xpndd%create_pw(pw_in)
1208  CALL pw_expand(neumann_directions, &
1209  dct_env%recv_msgs_bnds, dct_env%dests_expand, dct_env%srcs_expand, &
1210  dct_env%flipg_stat, dct_env%bounds_shftd, &
1211  ps_implicit_env%contacts(j)%dirichlet_bc%tiles(i)%tile%tile_pw, pw_in)
1212 
1213  tile_volume = ps_implicit_env%contacts(j)%dirichlet_bc%tiles(i)%tile%volume
1214  CALL pw_scale(pw_in, 1.0_dp/(vol_scfac*tile_volume)) ! normalize tile_pw
1215  ps_implicit_env%Bt(ps_implicit_env%idx_1dto3d, indx1 + i - 1) = reshape(pw_in%array, (/data_size/))
1216 
1217  CALL pw_pool_xpndd%create_pw(pw_out)
1218  CALL apply_inv_laplace_operator_dct(pw_pool_xpndd, green, pw_in, pw_out)
1219  qainvxbt(ps_implicit_env%idx_1dto3d, indx1 + i - 1) = reshape(pw_out%array, (/data_size/))
1220  ! the electrostatic potential has opposite sign by internal convention
1221  ps_implicit_env%v_D(indx1 + i - 1) = -1.0_dp*ps_implicit_env%contacts(j)%dirichlet_bc%v_D
1222  ps_implicit_env%osc_frac(indx1 + i - 1) = ps_implicit_env%contacts(j)%dirichlet_bc%osc_frac
1223  ps_implicit_env%frequency(indx1 + i - 1) = ps_implicit_env%contacts(j)%dirichlet_bc%frequency
1224  ps_implicit_env%phase(indx1 + i - 1) = ps_implicit_env%contacts(j)%dirichlet_bc%phase
1225 
1226  CALL pw_pool_xpndd%give_back_pw(pw_in)
1227  CALL pw_pool_xpndd%give_back_pw(pw_out)
1228  END DO
1229  indx1 = indx2 + 1
1230  END DO
1231  ps_implicit_env%B(:, :) = transpose(ps_implicit_env%Bt)
1232 
1233 ! evaluate QS = - B*(\Delta^-1)*B^t
1234  IF (data_size .NE. 0) THEN
1235  CALL dgemm('N', 'N', n_tiles_tot, n_tiles_tot, data_size, &
1236  -1.0_dp, ps_implicit_env%B, n_tiles_tot, qainvxbt, &
1237  data_size, 0.0_dp, ps_implicit_env%QS, n_tiles_tot)
1238  END IF
1239  CALL pw_grid_orig%para%group%sum(ps_implicit_env%QS)
1240 
1241 ! evaluate B*1
1242  bxunit_vec(:) = sum(ps_implicit_env%B, 2)/ngpts
1243  CALL pw_grid_orig%para%group%sum(bxunit_vec)
1244 ! set up R = [QS B*1; (B*1)^t 0]
1245  r = 0.0_dp
1246  r(1:n_tiles_tot, 1:n_tiles_tot) = ps_implicit_env%QS
1247  r(1:n_tiles_tot, n_tiles_tot + 1) = bxunit_vec
1248  r(n_tiles_tot + 1, 1:n_tiles_tot) = bxunit_vec
1249 ! evaluate R^(-1)
1250  ps_implicit_env%Rinv(:, :) = r
1251  CALL dgetrf(n_tiles_tot + 1, n_tiles_tot + 1, ps_implicit_env%Rinv, n_tiles_tot + 1, ipiv, info)
1252  IF (info .NE. 0) &
1253  CALL cp_abort(__location__, &
1254  "R is (nearly) singular! Either two Dirichlet constraints are identical or "// &
1255  "you need to reduce the number of tiles.")
1256  CALL dgetri(n_tiles_tot + 1, ps_implicit_env%Rinv, n_tiles_tot + 1, ipiv, work_arr, n_tiles_tot + 1, info)
1257  IF (info .NE. 0) &
1258  cpabort("Inversion of R failed!")
1259 
1260  DEALLOCATE (qainvxbt, bxunit_vec, r, work_arr, ipiv)
1261  CALL pw_pool_release(pw_pool_xpndd)
1262 
1263  done_preparing = .true.
1264  CALL pw_grid_orig%para%group%sum(done_preparing)
1265  IF ((unit_nr .GT. 0) .AND. done_preparing) THEN
1266  WRITE (unit_nr, "(T3,A,/,T3,A,/,A)") "POISSON| ... Done. ", repeat('-', 78)
1267  END IF
1268 
1269  CASE (mixed_periodic_bc)
1270 
1271  ngpts_local = pw_grid_orig%ngpts_local
1272  ngpts = pw_grid_orig%ngpts
1273  npts_local = pw_grid_orig%npts_local
1274  npts = pw_grid_orig%npts
1275  bounds_local = pw_grid_orig%bounds_local
1276  bounds = pw_grid_orig%bounds
1277  dct_env => ps_implicit_env%dct_env
1278 
1279 ! evaluate indices for converting 3D arrays into 1D arrays
1280  lb1 = bounds_local(1, 1); ub1 = bounds_local(2, 1)
1281  lb2 = bounds_local(1, 2); ub2 = bounds_local(2, 2)
1282  lb3 = bounds_local(1, 3); ub3 = bounds_local(2, 3)
1283 
1284  IF (pw_grid_orig%para%blocked) THEN
1285  data_size = product(npts_local)
1286  ELSE IF (pw_grid_orig%para%ray_distribution) THEN
1287  data_size = ngpts_local
1288  ELSE ! parallel run with np = 1
1289  data_size = product(npts_local)
1290  END IF
1291 
1292  ALLOCATE (ps_implicit_env%idx_1dto3d(data_size))
1293  l = 1
1294  ! Suppress OpenMP (at least the Intel compiler has an issue here)
1295  ! An automatic OpenMP parallelization of this loop might be tricky
1296  ! because of the l incrementation
1297 !$OMP PARALLEL IF(.FALSE.)
1298 !$OMP DO
1299  DO k = lb3, ub3
1300  DO j = lb2, ub2
1301  DO i = lb1, ub1
1302  ps_implicit_env%idx_1dto3d(l) = (i - lb1 + 1) + &
1303  (j - lb2)*npts_local(1) + &
1304  (k - lb3)*npts_local(1)*npts_local(2)
1305  l = l + 1
1306  END DO
1307  END DO
1308  END DO
1309 !$OMP END DO
1310 !$OMP END PARALLEL
1311 
1312  n_contacts = SIZE(ps_implicit_env%contacts)
1313  n_tiles_tot = 0
1314  DO j = 1, n_contacts
1315  n_tiles_tot = n_tiles_tot + ps_implicit_env%contacts(j)%dirichlet_bc%n_tiles
1316  END DO
1317 
1318  ALLOCATE (ps_implicit_env%B(n_tiles_tot, data_size))
1319  ALLOCATE (ps_implicit_env%Bt(data_size, n_tiles_tot))
1320  ALLOCATE (ps_implicit_env%QS(n_tiles_tot, n_tiles_tot))
1321  ALLOCATE (ps_implicit_env%Rinv(n_tiles_tot + 1, n_tiles_tot + 1))
1322  ALLOCATE (ps_implicit_env%v_D(n_tiles_tot))
1323  ALLOCATE (ps_implicit_env%osc_frac(n_tiles_tot))
1324  ALLOCATE (ps_implicit_env%frequency(n_tiles_tot))
1325  ALLOCATE (ps_implicit_env%phase(n_tiles_tot))
1326 
1327  ALLOCATE (qainvxbt(data_size, n_tiles_tot))
1328  ALLOCATE (bxunit_vec(n_tiles_tot))
1329  ALLOCATE (test_vec(n_tiles_tot))
1330  ALLOCATE (r(n_tiles_tot + 1, n_tiles_tot + 1))
1331  ALLOCATE (work_arr(n_tiles_tot + 1), ipiv(n_tiles_tot + 1))
1332 
1333 ! set up B, B^t, (\Delta^-1)*B^t
1334  indx1 = 1
1335  DO j = 1, n_contacts
1336  n_tiles = ps_implicit_env%contacts(j)%dirichlet_bc%n_tiles
1337  indx2 = indx1 + n_tiles - 1
1338  DO i = 1, n_tiles
1339  CALL pw_pool_orig%create_pw(pw_in)
1340  CALL pw_copy(ps_implicit_env%contacts(j)%dirichlet_bc%tiles(i)%tile%tile_pw, pw_in)
1341 
1342  tile_volume = ps_implicit_env%contacts(j)%dirichlet_bc%tiles(i)%tile%volume
1343  CALL pw_scale(pw_in, 1.0_dp/tile_volume) ! normalize tile_pw
1344  ps_implicit_env%Bt(ps_implicit_env%idx_1dto3d, indx1 + i - 1) = reshape(pw_in%array, (/data_size/))
1345 
1346  CALL pw_pool_orig%create_pw(pw_out)
1347  CALL apply_inv_laplace_operator_fft(pw_pool_orig, green, pw_in, pw_out)
1348  qainvxbt(ps_implicit_env%idx_1dto3d, indx1 + i - 1) = reshape(pw_out%array, (/data_size/))
1349  ! the electrostatic potential has opposite sign by internal convention
1350  ps_implicit_env%v_D(indx1 + i - 1) = -1.0_dp*ps_implicit_env%contacts(j)%dirichlet_bc%v_D
1351  ps_implicit_env%osc_frac(indx1 + i - 1) = ps_implicit_env%contacts(j)%dirichlet_bc%osc_frac
1352  ps_implicit_env%frequency(indx1 + i - 1) = ps_implicit_env%contacts(j)%dirichlet_bc%frequency
1353  ps_implicit_env%phase(indx1 + i - 1) = ps_implicit_env%contacts(j)%dirichlet_bc%phase
1354 
1355  CALL pw_pool_orig%give_back_pw(pw_in)
1356  CALL pw_pool_orig%give_back_pw(pw_out)
1357  END DO
1358  indx1 = indx2 + 1
1359  END DO
1360  ps_implicit_env%B(:, :) = transpose(ps_implicit_env%Bt)
1361 
1362 ! evaluate QS = - B*(\Delta^-1)*B^t
1363  IF (data_size .NE. 0) THEN
1364  CALL dgemm('N', 'N', n_tiles_tot, n_tiles_tot, data_size, &
1365  -1.0_dp, ps_implicit_env%B, n_tiles_tot, qainvxbt, &
1366  data_size, 0.0_dp, ps_implicit_env%QS, n_tiles_tot)
1367  END IF
1368  CALL pw_grid_orig%para%group%sum(ps_implicit_env%QS)
1369 
1370 ! evaluate B*1
1371  bxunit_vec(:) = sum(ps_implicit_env%B, 2)/ngpts
1372  CALL pw_grid_orig%para%group%sum(bxunit_vec)
1373 ! set up R = [QS B*1; (B*1)^t 0]
1374  r = 0.0_dp
1375  r(1:n_tiles_tot, 1:n_tiles_tot) = ps_implicit_env%QS
1376  r(1:n_tiles_tot, n_tiles_tot + 1) = bxunit_vec
1377  r(n_tiles_tot + 1, 1:n_tiles_tot) = bxunit_vec
1378 ! evaluate R^(-1)
1379  ps_implicit_env%Rinv(:, :) = r
1380  CALL dgetrf(n_tiles_tot + 1, n_tiles_tot + 1, ps_implicit_env%Rinv, n_tiles_tot + 1, ipiv, info)
1381  IF (info .NE. 0) &
1382  CALL cp_abort(__location__, &
1383  "R is (nearly) singular! Either two Dirichlet constraints are identical or "// &
1384  "you need to reduce the number of tiles.")
1385  CALL dgetri(n_tiles_tot + 1, ps_implicit_env%Rinv, n_tiles_tot + 1, ipiv, work_arr, n_tiles_tot + 1, info)
1386  IF (info .NE. 0) &
1387  cpabort("Inversion of R failed!")
1388 
1389  DEALLOCATE (qainvxbt, bxunit_vec, r, work_arr, ipiv)
1390 
1391  done_preparing = .true.
1392  CALL pw_grid_orig%para%group%sum(done_preparing)
1393  IF ((unit_nr .GT. 0) .AND. done_preparing) THEN
1394  WRITE (unit_nr, "(T3,A,/,T3,A,/,A)") "POISSON| ... Done. ", repeat('-', 78)
1395  END IF
1396 
1397  CASE (periodic_bc, neumann_bc)
1398 
1399  ALLOCATE (ps_implicit_env%idx_1dto3d(1))
1400  ALLOCATE (ps_implicit_env%B(1, 1))
1401  ALLOCATE (ps_implicit_env%Bt(1, 1))
1402  ALLOCATE (ps_implicit_env%QS(1, 1))
1403  ALLOCATE (ps_implicit_env%Rinv(1, 1))
1404  ALLOCATE (ps_implicit_env%v_D(1))
1405  ALLOCATE (ps_implicit_env%osc_frac(1))
1406  ALLOCATE (ps_implicit_env%frequency(1))
1407  ALLOCATE (ps_implicit_env%phase(1))
1408 
1409  ps_implicit_env%idx_1dto3d = 1
1410  ps_implicit_env%B = 0.0_dp
1411  ps_implicit_env%Bt = 0.0_dp
1412  ps_implicit_env%QS = 0.0_dp
1413  ps_implicit_env%Rinv = 0.0_dp
1414  ps_implicit_env%v_D = 0.0_dp
1415 
1416  CASE DEFAULT
1417  CALL cp_abort(__location__, &
1418  "Please specify the type of boundary conditions using the "// &
1419  "input file keyword BOUNDARY_CONDITIONS.")
1420  END SELECT
1421 
1422  CALL timestop(handle)
1423 
1424  END SUBROUTINE ps_implicit_prepare_blocks
1425 
1426 ! **************************************************************************************************
1427 !> \brief Evaluates the action of the operator P on a given matrix v, defined
1428 !> as: P(v) := - \nabla_r(\ln(\eps)) \cdot \nabla_r(v)
1429 !> \param pw_pool pool of pw grid
1430 !> \param dielectric dielectric_type containing eps
1431 !> \param v input matrix
1432 !> \param Pxv action of the operator P on v
1433 !> \par History
1434 !> 07.2014 created [Hossein Bani-Hashemian]
1435 !> \author Mohammad Hossein Bani-Hashemian
1436 ! **************************************************************************************************
1437  SUBROUTINE apply_p_operator(pw_pool, dielectric, v, Pxv)
1438 
1439  TYPE(pw_pool_type), POINTER :: pw_pool
1440  TYPE(dielectric_type), INTENT(IN), POINTER :: dielectric
1441  TYPE(pw_r3d_rs_type), INTENT(IN) :: v
1442  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: pxv
1443 
1444  CHARACTER(LEN=*), PARAMETER :: routinen = 'apply_P_operator'
1445 
1446  INTEGER :: handle, i
1447  TYPE(pw_r3d_rs_type), DIMENSION(3) :: dv
1448 
1449  CALL timeset(routinen, handle)
1450 
1451  DO i = 1, 3
1452  CALL pw_pool%create_pw(dv(i))
1453  END DO
1454 
1455  CALL derive_fft(v, dv, pw_pool)
1456  associate(dln_eps => dielectric%dln_eps)
1457  pxv%array = -(dv(1)%array*dln_eps(1)%array + &
1458  dv(2)%array*dln_eps(2)%array + &
1459  dv(3)%array*dln_eps(3)%array)
1460  END associate
1461 
1462  DO i = 1, 3
1463  CALL pw_pool%give_back_pw(dv(i))
1464  END DO
1465 
1466  CALL timestop(handle)
1467 
1468  END SUBROUTINE apply_p_operator
1469 
1470 ! **************************************************************************************************
1471 !> \brief Evaluates the action of the inverse of the Laplace operator on a given 3d matrix
1472 !> \param pw_pool pool of pw grid
1473 !> \param green green functions for FFT based inverse Laplacian
1474 !> \param pw_in pw_in (density)
1475 !> \param pw_out pw_out (potential)
1476 !> \par History
1477 !> 07.2014 created [Hossein Bani-Hashemian]
1478 !> \author Mohammad Hossein Bani-Hashemian
1479 ! **************************************************************************************************
1480  SUBROUTINE apply_inv_laplace_operator_fft(pw_pool, green, pw_in, pw_out)
1481 
1482  TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1483  TYPE(greens_fn_type), INTENT(IN) :: green
1484  TYPE(pw_r3d_rs_type), INTENT(IN) :: pw_in
1485  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: pw_out
1486 
1487  CHARACTER(LEN=*), PARAMETER :: routinen = 'apply_inv_laplace_operator_fft'
1488 
1489  INTEGER :: handle, ig, ng
1490  REAL(dp) :: prefactor
1491  TYPE(pw_c1d_gs_type) :: pw_in_gs
1492  TYPE(pw_grid_type), POINTER :: pw_grid
1493 
1494  CALL timeset(routinen, handle)
1495 
1496 ! here I divide by fourpi to cancel out the prefactor fourpi in influence_fn
1497  prefactor = 1.0_dp/fourpi
1498 
1499  pw_grid => pw_pool%pw_grid
1500  ng = SIZE(pw_grid%gsq)
1501 
1502  CALL pw_pool%create_pw(pw_in_gs)
1503 
1504  CALL pw_transfer(pw_in, pw_in_gs)
1505  DO ig = 1, ng
1506  pw_in_gs%array(ig) = prefactor*pw_in_gs%array(ig)*green%influence_fn%array(ig)
1507  END DO
1508  CALL pw_transfer(pw_in_gs, pw_out)
1509 
1510  CALL pw_pool%give_back_pw(pw_in_gs)
1511 
1512  CALL timestop(handle)
1513 
1514  END SUBROUTINE apply_inv_laplace_operator_fft
1515 
1516 ! **************************************************************************************************
1517 !> \brief Evaluates the action of the inverse of the Laplace operator on a given
1518 !> 3d matrix using DCT-I
1519 !> \param pw_pool pool of pw grid
1520 !> \param green the greens_fn_type data holding a valid dct_influence_fn
1521 !> \param pw_in pw_in (density)
1522 !> \param pw_out pw_out (potential)
1523 !> \par History
1524 !> 07.2014 created [Hossein Bani-Hashemian]
1525 !> 11.2015 revised [Hossein Bani-Hashemian]
1526 !> \author Mohammad Hossein Bani-Hashemian
1527 ! **************************************************************************************************
1528  SUBROUTINE apply_inv_laplace_operator_dct(pw_pool, green, pw_in, pw_out)
1529 
1530  TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1531  TYPE(greens_fn_type), INTENT(IN) :: green
1532  TYPE(pw_r3d_rs_type), INTENT(IN) :: pw_in
1533  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: pw_out
1534 
1535  CHARACTER(LEN=*), PARAMETER :: routinen = 'apply_inv_laplace_operator_dct'
1536 
1537  INTEGER :: handle, ig, ng
1538  REAL(dp) :: prefactor
1539  TYPE(pw_c1d_gs_type) :: pw_in_gs
1540  TYPE(pw_grid_type), POINTER :: pw_grid
1541 
1542  CALL timeset(routinen, handle)
1543 
1544 ! here I divide by fourpi to cancel out the prefactor fourpi in influence_fn
1545  prefactor = 1.0_dp/fourpi
1546 
1547  pw_grid => pw_pool%pw_grid
1548  ng = SIZE(pw_grid%gsq)
1549 
1550  CALL pw_pool%create_pw(pw_in_gs)
1551 
1552  CALL pw_transfer(pw_in, pw_in_gs)
1553  DO ig = 1, ng
1554  pw_in_gs%array(ig) = prefactor*pw_in_gs%array(ig)*green%dct_influence_fn%array(ig)
1555  END DO
1556  CALL pw_transfer(pw_in_gs, pw_out)
1557 
1558  CALL pw_pool%give_back_pw(pw_in_gs)
1559 
1560  CALL timestop(handle)
1561 
1562  END SUBROUTINE apply_inv_laplace_operator_dct
1563 
1564 ! **************************************************************************************************
1565 !> \brief Evaluates the action of the Laplace operator on a given 3d matrix
1566 !> \param pw_pool pool of pw grid
1567 !> \param green green functions for FFT based inverse Laplacian
1568 !> \param pw_in pw_in (potential)
1569 !> \param pw_out pw_out (density)
1570 !> \par History
1571 !> 07.2014 created [Hossein Bani-Hashemian]
1572 !> \author Mohammad Hossein Bani-Hashemian
1573 ! **************************************************************************************************
1574  SUBROUTINE apply_laplace_operator_fft(pw_pool, green, pw_in, pw_out)
1575 
1576  TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1577  TYPE(greens_fn_type), INTENT(IN) :: green
1578  TYPE(pw_r3d_rs_type), INTENT(IN) :: pw_in
1579  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: pw_out
1580 
1581  CHARACTER(LEN=*), PARAMETER :: routinen = 'apply_laplace_operator_fft'
1582 
1583  INTEGER :: g0_index, handle, ig, ng
1584  LOGICAL :: have_g0
1585  REAL(dp) :: prefactor
1586  TYPE(pw_c1d_gs_type) :: pw_in_gs
1587  TYPE(pw_grid_type), POINTER :: pw_grid
1588 
1589  CALL timeset(routinen, handle)
1590 
1591 ! here I multiply by fourpi to cancel out the prefactor fourpi in influence_fn
1592  prefactor = fourpi
1593 
1594  pw_grid => pw_pool%pw_grid
1595  ng = SIZE(pw_in%pw_grid%gsq)
1596  have_g0 = green%influence_fn%pw_grid%have_g0
1597 
1598  CALL pw_pool%create_pw(pw_in_gs)
1599 
1600  CALL pw_transfer(pw_in, pw_in_gs)
1601 
1602  IF (have_g0) THEN
1603  g0_index = green%influence_fn%pw_grid%first_gne0 - 1
1604  pw_in_gs%array(g0_index) = 0.0_dp
1605  END IF
1606  DO ig = green%influence_fn%pw_grid%first_gne0, ng
1607  pw_in_gs%array(ig) = prefactor*(pw_in_gs%array(ig)/green%influence_fn%array(ig))
1608  END DO
1609 
1610  CALL pw_transfer(pw_in_gs, pw_out)
1611 
1612  CALL pw_pool%give_back_pw(pw_in_gs)
1613 
1614  CALL timestop(handle)
1615 
1616  END SUBROUTINE apply_laplace_operator_fft
1617 
1618 ! **************************************************************************************************
1619 !> \brief Evaluates the action of the Laplace operator on a given 3d matrix using DCT-I
1620 !> \param pw_pool pool of pw grid
1621 !> \param green the greens_fn_type data holding a valid dct_influence_fn
1622 !> \param pw_in pw_in (potential)
1623 !> \param pw_out pw_out (density)
1624 !> \par History
1625 !> 07.2014 created [Hossein Bani-Hashemian]
1626 !> 11.2015 revised [Hossein Bani-Hashemian]
1627 !> \author Mohammad Hossein Bani-Hashemian
1628 ! **************************************************************************************************
1629  SUBROUTINE apply_laplace_operator_dct(pw_pool, green, pw_in, pw_out)
1630 
1631  TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1632  TYPE(greens_fn_type), INTENT(IN) :: green
1633  TYPE(pw_r3d_rs_type), INTENT(IN) :: pw_in
1634  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: pw_out
1635 
1636  CHARACTER(LEN=*), PARAMETER :: routinen = 'apply_laplace_operator_dct'
1637 
1638  INTEGER :: g0_index, handle, ig, ng
1639  LOGICAL :: have_g0
1640  REAL(dp) :: prefactor
1641  TYPE(pw_c1d_gs_type) :: pw_in_gs
1642  TYPE(pw_grid_type), POINTER :: pw_grid
1643 
1644  CALL timeset(routinen, handle)
1645 
1646 ! here I multiply by fourpi to cancel out the prefactor fourpi in influence_fn
1647  prefactor = fourpi
1648 
1649  pw_grid => pw_pool%pw_grid
1650  ng = SIZE(pw_in%pw_grid%gsq)
1651  have_g0 = green%dct_influence_fn%pw_grid%have_g0
1652 
1653  CALL pw_pool%create_pw(pw_in_gs)
1654 
1655  CALL pw_transfer(pw_in, pw_in_gs)
1656 
1657  IF (have_g0) THEN
1658  g0_index = green%dct_influence_fn%pw_grid%first_gne0 - 1
1659  pw_in_gs%array(g0_index) = 0.0_dp
1660  END IF
1661  DO ig = green%dct_influence_fn%pw_grid%first_gne0, ng
1662  pw_in_gs%array(ig) = prefactor*(pw_in_gs%array(ig)/green%dct_influence_fn%array(ig))
1663  END DO
1664 
1665  CALL pw_transfer(pw_in_gs, pw_out)
1666 
1667  CALL pw_pool%give_back_pw(pw_in_gs)
1668 
1669  CALL timestop(handle)
1670 
1671  END SUBROUTINE apply_laplace_operator_dct
1672 
1673 ! **************************************************************************************************
1674 !> \brief Evaluates the action of the generalized Poisson operator on a given 3d matrix.
1675 !> \param pw_pool pool of pw grid
1676 !> \param green green functions for FFT based inverse Laplacian
1677 !> \param dielectric dielectric environment
1678 !> \param v potential
1679 !> \param density density
1680 !> \par History
1681 !> 07.2014 created [Hossein Bani-Hashemian]
1682 !> \author Mohammad Hossein Bani-Hashemian
1683 ! **************************************************************************************************
1684  SUBROUTINE apply_poisson_operator_fft(pw_pool, green, dielectric, v, density)
1685 
1686  TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1687  TYPE(greens_fn_type), INTENT(IN) :: green
1688  TYPE(dielectric_type), INTENT(IN), POINTER :: dielectric
1689  TYPE(pw_r3d_rs_type), INTENT(IN) :: v
1690  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: density
1691 
1692  CHARACTER(LEN=*), PARAMETER :: routinen = 'apply_poisson_operator_fft'
1693 
1694  INTEGER :: handle
1695  TYPE(pw_r3d_rs_type) :: pxv
1696 
1697  CALL timeset(routinen, handle)
1698 
1699  CALL pw_pool%create_pw(pxv)
1700 
1701  CALL apply_p_operator(pw_pool, dielectric, v, pxv)
1702  CALL apply_laplace_operator_fft(pw_pool, green, v, density)
1703  CALL pw_axpy(pxv, density)
1704 
1705  CALL pw_pool%give_back_pw(pxv)
1706 
1707  CALL timestop(handle)
1708 
1709  END SUBROUTINE apply_poisson_operator_fft
1710 
1711 ! **************************************************************************************************
1712 !> \brief Evaluates the action of the generalized Poisson operator on a given
1713 !> 3d matrix using DCT-I.
1714 !> \param pw_pool pool of pw grid
1715 !> \param green the greens_fn_type data holding a valid dct_influence_fn
1716 !> \param dielectric dielectric environment
1717 !> \param v potential
1718 !> \param density density
1719 !> \par History
1720 !> 07.2014 created [Hossein Bani-Hashemian]
1721 !> 11.2015 revised [Hossein Bani-Hashemian]
1722 !> \author Mohammad Hossein Bani-Hashemian
1723 ! **************************************************************************************************
1724  SUBROUTINE apply_poisson_operator_dct(pw_pool, green, dielectric, v, density)
1725 
1726  TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1727  TYPE(greens_fn_type), INTENT(IN) :: green
1728  TYPE(dielectric_type), INTENT(IN), POINTER :: dielectric
1729  TYPE(pw_r3d_rs_type), INTENT(IN) :: v
1730  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: density
1731 
1732  CHARACTER(LEN=*), PARAMETER :: routinen = 'apply_poisson_operator_dct'
1733 
1734  INTEGER :: handle
1735  TYPE(pw_r3d_rs_type) :: pxv
1736 
1737  CALL timeset(routinen, handle)
1738 
1739  CALL pw_pool%create_pw(pxv)
1740 
1741  CALL apply_p_operator(pw_pool, dielectric, v, pxv)
1742  CALL apply_laplace_operator_dct(pw_pool, green, v, density)
1743  CALL pw_axpy(pxv, density)
1744 
1745  CALL pw_pool%give_back_pw(pxv)
1746 
1747  CALL timestop(handle)
1748 
1749  END SUBROUTINE apply_poisson_operator_dct
1750 
1751 ! **************************************************************************************************
1752 !> \brief Computes the extra contribution (v_eps)
1753 !> v_eps = - \frac{1}{8*\pi} * |\nabla_r(v)|^2 * \frac{d \eps}{d \rho}
1754 !> to the functional derivative of the Hartree energy wrt the density, being
1755 !> attributed to the dependency of the dielectric constant to the charge density.
1756 !> [see V. M. Sanchez, M. Sued, and D. A. Scherlis, J. Chem. Phys. 131, 174108 (2009)]
1757 !> \param pw_pool pool of the original plane-wave grid
1758 !> \param dielectric dielectric environment
1759 !> \param v Hartree potential
1760 !> \param v_eps v_eps
1761 !> \par History
1762 !> 08.2014 created [Hossein Bani-Hashemian]
1763 !> \author Mohammad Hossein Bani-Hashemian
1764 ! **************************************************************************************************
1765  SUBROUTINE ps_implicit_compute_veps(pw_pool, dielectric, v, v_eps)
1766 
1767  TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1768  TYPE(dielectric_type), INTENT(IN), POINTER :: dielectric
1769  TYPE(pw_r3d_rs_type), INTENT(IN) :: v
1770  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: v_eps
1771 
1772  CHARACTER(LEN=*), PARAMETER :: routinen = 'ps_implicit_compute_veps'
1773 
1774  INTEGER :: handle, i
1775  REAL(dp) :: eightpi
1776  TYPE(pw_r3d_rs_type) :: dv2
1777  TYPE(pw_r3d_rs_type), DIMENSION(3) :: dv
1778 
1779  CALL timeset(routinen, handle)
1780 
1781  eightpi = 2*fourpi
1782 
1783  CALL pw_pool%create_pw(dv2)
1784  DO i = 1, 3
1785  CALL pw_pool%create_pw(dv(i))
1786  END DO
1787 
1788  CALL derive_fft(v, dv, pw_pool)
1789 
1790 ! evaluate |\nabla_r(v)|^2
1791  dv2%array = dv(1)%array**2 + dv(2)%array**2 + dv(3)%array**2
1792 
1793  v_eps%array = -(1.0_dp/eightpi)*(dv2%array*dielectric%deps_drho%array)
1794 
1795  CALL pw_pool%give_back_pw(dv2)
1796  DO i = 1, 3
1797  CALL pw_pool%give_back_pw(dv(i))
1798  END DO
1799 
1800  CALL timestop(handle)
1801 
1802  END SUBROUTINE ps_implicit_compute_veps
1803 
1804 ! **************************************************************************************************
1805 !> \brief Computes the Hartree energy
1806 !> \param density electronic density
1807 !> \param v Hartree potential
1808 !> \param ehartree Hartree energy
1809 !> \par History
1810 !> 06.2015 created [Hossein Bani-Hashemian]
1811 !> \author Mohammad Hossein Bani-Hashemian
1812 ! **************************************************************************************************
1813  SUBROUTINE compute_ehartree_periodic_bc(density, v, ehartree)
1814 
1815  TYPE(pw_r3d_rs_type), INTENT(IN) :: density, v
1816  REAL(dp), INTENT(OUT) :: ehartree
1817 
1818  CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_ehartree_periodic_bc'
1819 
1820  INTEGER :: handle
1821 
1822  CALL timeset(routinen, handle)
1823 
1824 ! E_H = \frac{1}{2} * \int \rho * v dr
1825  ehartree = 0.5_dp*pw_integral_ab(density, v)
1826 
1827  CALL timestop(handle)
1828 
1829  END SUBROUTINE compute_ehartree_periodic_bc
1830 
1831 ! **************************************************************************************************
1832 !> \brief Computes the Hartree energy
1833 !> \param dielectric dielectric environment
1834 !> \param density electronic density
1835 !> \param Btxlambda B^t * \lambda (\lambda is the vector of Lagrange multipliers
1836 !> and B^t is the transpose of the boundary operator
1837 !> \param v Hartree potential
1838 !> \param ehartree Hartree energy
1839 !> \param electric_enthalpy electric enthalpy
1840 !> \par History
1841 !> 06.2015 created [Hossein Bani-Hashemian]
1842 !> \author Mohammad Hossein Bani-Hashemian
1843 ! **************************************************************************************************
1844  SUBROUTINE compute_ehartree_mixed_bc(dielectric, density, Btxlambda, v, ehartree, electric_enthalpy)
1845 
1846  TYPE(dielectric_type), INTENT(IN), POINTER :: dielectric
1847  TYPE(pw_r3d_rs_type), INTENT(IN) :: density
1848  REAL(dp), ALLOCATABLE, DIMENSION(:, :, :), &
1849  INTENT(IN) :: btxlambda
1850  TYPE(pw_r3d_rs_type), INTENT(IN) :: v
1851  REAL(dp), INTENT(OUT) :: ehartree, electric_enthalpy
1852 
1853  CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_ehartree_mixed_bc'
1854 
1855  INTEGER :: handle
1856  REAL(dp) :: dvol, ehartree_rho, ehartree_rho_cstr
1857  TYPE(pw_grid_type), POINTER :: pw_grid
1858 
1859  CALL timeset(routinen, handle)
1860 
1861  pw_grid => v%pw_grid
1862 
1863  dvol = pw_grid%dvol
1864 
1865 ! E_H = \frac{1}{2} * \int \rho * v dr + \frac{1}{8 \pi} * \int Btxlambda * v dr
1866 ! the sign of the second term depends on the sign chosen for the Lagrange multiplier
1867 ! term in the variational form
1868  ehartree_rho = accurate_sum(density%array*v%array)
1869  ehartree_rho_cstr = accurate_sum(dielectric%eps%array*btxlambda*v%array/fourpi)
1870  ehartree_rho = 0.5_dp*ehartree_rho*dvol
1871  ehartree_rho_cstr = 0.5_dp*ehartree_rho_cstr*dvol
1872  CALL pw_grid%para%group%sum(ehartree_rho)
1873  CALL pw_grid%para%group%sum(ehartree_rho_cstr)
1874  electric_enthalpy = ehartree_rho + ehartree_rho_cstr
1875  ehartree = ehartree_rho - ehartree_rho_cstr
1876 
1877  CALL timestop(handle)
1878 
1879  END SUBROUTINE compute_ehartree_mixed_bc
1880 
1881 ! **************************************************************************************************
1882 !> \brief Computes the (normalized) preconditioned residual norm error and the
1883 !> normalized absolute error
1884 !> \param pw_pool pool of the original plane-wave grid
1885 !> \param green greens functions for FFT based inverse Laplacian
1886 !> \param res_new residual
1887 !> \param v_old old v
1888 !> \param v_new new v
1889 !> \param QAinvxres_new Delta^-1(res_new)
1890 !> \param pres_error preconditioned residual norm error
1891 !> \param nabs_error normalized absolute error
1892 !> \par History
1893 !> 07.2014 created [Hossein Bani-Hashemian]
1894 !> \author Mohammad Hossein Bani-Hashemian
1895 ! **************************************************************************************************
1896  SUBROUTINE ps_implicit_compute_error_fft(pw_pool, green, res_new, v_old, v_new, &
1897  QAinvxres_new, pres_error, nabs_error)
1898 
1899  TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1900  TYPE(greens_fn_type), INTENT(IN) :: green
1901  TYPE(pw_r3d_rs_type), INTENT(IN) :: res_new, v_old, v_new
1902  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: qainvxres_new
1903  REAL(dp), INTENT(OUT) :: pres_error, nabs_error
1904 
1905  CHARACTER(LEN=*), PARAMETER :: routinen = 'ps_implicit_compute_error_fft'
1906 
1907  INTEGER :: handle
1908  REAL(dp) :: vol
1909 
1910  CALL timeset(routinen, handle)
1911 
1912  vol = pw_pool%pw_grid%vol
1913 
1914 ! evaluate \Delta^-1(res) = \Delta^-1 (g - \Delta(v_new) - P(v_new) + Bt \lambda)
1915  CALL apply_inv_laplace_operator_fft(pw_pool, green, res_new, qainvxres_new)
1916 ! (normalized) preconditioned residual norm error :
1917  pres_error = accurate_sum(qainvxres_new%array(:, :, :)**2)
1918  CALL pw_pool%pw_grid%para%group%sum(pres_error)
1919  pres_error = sqrt(pres_error)/vol
1920 
1921 ! normalized absolute error :
1922 ! nabs_error := \frac{\| v_old - v_new \|}{volume}
1923  nabs_error = accurate_sum(abs(v_old%array - v_new%array)**2)
1924  CALL pw_pool%pw_grid%para%group%sum(nabs_error)
1925  nabs_error = sqrt(nabs_error)/vol
1926 
1927  CALL timestop(handle)
1928 
1929  END SUBROUTINE ps_implicit_compute_error_fft
1930 
1931 ! **************************************************************************************************
1932 !> \brief Computes the (normalized) preconditioned residual norm error and the
1933 !> normalized absolute error
1934 !> \param pw_pool pool of the original plane-wave grid
1935 !> \param green the greens_fn_type data holding a valid dct_influence_fn
1936 !> \param res_new residual
1937 !> \param v_old old v
1938 !> \param v_new new v
1939 !> \param QAinvxres_new Delta^-1(res_new)
1940 !> \param pres_error preconditioned residual norm error
1941 !> \param nabs_error normalized absolute error
1942 !> \par History
1943 !> 07.2014 created [Hossein Bani-Hashemian]
1944 !> 11.2015 revised [Hossein Bani-Hashemian]
1945 !> \author Mohammad Hossein Bani-Hashemian
1946 ! **************************************************************************************************
1947  SUBROUTINE ps_implicit_compute_error_dct(pw_pool, green, res_new, v_old, v_new, &
1948  QAinvxres_new, pres_error, nabs_error)
1949 
1950  TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1951  TYPE(greens_fn_type), INTENT(IN) :: green
1952  TYPE(pw_r3d_rs_type), INTENT(IN) :: res_new, v_old, v_new
1953  TYPE(pw_r3d_rs_type), INTENT(INOUT) :: qainvxres_new
1954  REAL(dp), INTENT(OUT) :: pres_error, nabs_error
1955 
1956  CHARACTER(LEN=*), PARAMETER :: routinen = 'ps_implicit_compute_error_dct'
1957 
1958  INTEGER :: handle
1959  REAL(dp) :: vol
1960 
1961  CALL timeset(routinen, handle)
1962 
1963  vol = pw_pool%pw_grid%vol
1964 
1965 ! evaluate \Delta^-1(res) = \Delta^-1 (g - \Delta(v_new) - P(v_new) + Bt \lambda)
1966  CALL apply_inv_laplace_operator_dct(pw_pool, green, res_new, qainvxres_new)
1967 ! (normalized) preconditioned residual norm error :
1968  pres_error = accurate_sum(qainvxres_new%array(:, :, :)**2)
1969  CALL pw_pool%pw_grid%para%group%sum(pres_error)
1970  pres_error = sqrt(pres_error)/vol
1971 
1972 ! normalized absolute error :
1973 ! nabs_error := \frac{\| v_old - v_new \|}{volume}
1974  nabs_error = accurate_sum(abs(v_old%array - v_new%array)**2)
1975  CALL pw_pool%pw_grid%para%group%sum(nabs_error)
1976  nabs_error = sqrt(nabs_error)/vol
1977 
1978  CALL timestop(handle)
1979 
1980  END SUBROUTINE ps_implicit_compute_error_dct
1981 
1982 ! **************************************************************************************************
1983 !> \brief output of the implicit (iterative) Poisson solver
1984 !> \param iter current iteration
1985 !> \param pres_error preconditioned residual norm error
1986 !> \param nabs_error normalized absolute error
1987 !> \param outp_unit output unit
1988 !> \par History
1989 !> 07.2014 created [Hossein Bani-Hashemian]
1990 !> \author Mohammad Hossein Bani-Hashemian
1991 ! **************************************************************************************************
1992  SUBROUTINE ps_implicit_output(iter, pres_error, nabs_error, outp_unit)
1993 
1994  INTEGER, INTENT(IN) :: iter
1995  REAL(dp), INTENT(IN) :: pres_error, nabs_error
1996  INTEGER, INTENT(OUT) :: outp_unit
1997 
1998  CHARACTER(LEN=*), PARAMETER :: routinen = 'ps_implicit_output'
1999  INTEGER, PARAMETER :: low_print_level = 1
2000 
2001  INTEGER :: handle
2002  TYPE(cp_logger_type), POINTER :: logger
2003 
2004  CALL timeset(routinen, handle)
2005 
2006  logger => cp_get_default_logger()
2007  IF (logger%para_env%is_source()) THEN
2008  outp_unit = cp_logger_get_default_unit_nr(logger, local=.true.)
2009  ELSE
2010  outp_unit = -1
2011  END IF
2012 
2013  IF (logger%iter_info%print_level .GT. low_print_level) THEN
2014  IF ((outp_unit .GT. 0) .AND. (iter .EQ. 1)) THEN
2015  WRITE (outp_unit, '(T3,A)') &
2016  "POISSON| iter pres error nabs error E_hartree delta E"
2017  END IF
2018 
2019  IF (outp_unit .GT. 0) THEN
2020  WRITE (outp_unit, '(T3,A,I6,5X,E13.4,3X,E13.4)', advance='NO') &
2021  "POISSON| ", iter, pres_error, nabs_error
2022  END IF
2023  END IF
2024 
2025  CALL timestop(handle)
2026 
2027  END SUBROUTINE ps_implicit_output
2028 
2029 ! **************************************************************************************************
2030 !> \brief reports the Hartree energy in every iteration
2031 !> \param ps_implicit_env the implicit poisson solver environment
2032 !> \param outp_unit output unit
2033 !> \param ehartree Hartree energy
2034 !> \par History
2035 !> 07.2014 created [Hossein Bani-Hashemian]
2036 !> \author Mohammad Hossein Bani-Hashemian
2037 ! **************************************************************************************************
2038  SUBROUTINE ps_implicit_report_ehartree(ps_implicit_env, outp_unit, ehartree)
2039 
2040  TYPE(ps_implicit_type) :: ps_implicit_env
2041  INTEGER, INTENT(IN) :: outp_unit
2042  REAL(dp), INTENT(IN) :: ehartree
2043 
2044  CHARACTER(LEN=*), PARAMETER :: routinen = 'ps_implicit_report_ehartree'
2045  INTEGER, PARAMETER :: low_print_level = 1
2046 
2047  INTEGER :: handle
2048  TYPE(cp_logger_type), POINTER :: logger
2049 
2050  logger => cp_get_default_logger()
2051  CALL timeset(routinen, handle)
2052  IF (logger%iter_info%print_level .GT. low_print_level) THEN
2053  IF (outp_unit .GT. 0) WRITE (outp_unit, '(F19.10,E10.2)') &
2054  ehartree, ehartree - ps_implicit_env%ehartree
2055  END IF
2056  CALL timestop(handle)
2057 
2058  END SUBROUTINE ps_implicit_report_ehartree
2059 
2060 ! **************************************************************************************************
2061 !> \brief reports the final number of iteration
2062 !> \param iter the iteration number after exiting the main loop
2063 !> \param max_iter maximum number of iterations
2064 !> \param outp_unit output unit
2065 !> \par History
2066 !> 02.2016 created [Hossein Bani-Hashemian]
2067 !> \author Mohammad Hossein Bani-Hashemian
2068 ! **************************************************************************************************
2069  SUBROUTINE ps_implicit_print_convergence_msg(iter, max_iter, outp_unit)
2070 
2071  INTEGER, INTENT(IN) :: iter, max_iter, outp_unit
2072 
2073  CHARACTER(LEN=*), PARAMETER :: routinen = 'ps_implicit_print_convergence_msg'
2074 
2075  CHARACTER(LEN=12) :: msg
2076  INTEGER :: handle, last_iter
2077 
2078  CALL timeset(routinen, handle)
2079 
2080  last_iter = iter - 1
2081  IF (last_iter .EQ. 1) THEN
2082  msg = " iteration. "
2083  ELSE
2084  msg = " iterations."
2085  END IF
2086 
2087  IF (outp_unit .GT. 0) THEN
2088  IF (last_iter .EQ. max_iter) THEN
2089  WRITE (outp_unit, '(T3,A)') &
2090  "POISSON| No convergence achieved within the maximum number of iterations."
2091  END IF
2092  IF (last_iter .LT. max_iter) THEN
2093  WRITE (outp_unit, '(T3,A,I0,A)') &
2094  "POISSON| Poisson solver converged in ", last_iter, msg
2095  END IF
2096  END IF
2097  CALL timestop(handle)
2098 
2099  END SUBROUTINE ps_implicit_print_convergence_msg
2100 
2101 ! **************************************************************************************************
2102 !> \brief converts a 1D array to a 3D array (contiguous layout)
2103 !> \param idx_1dto3d mapping of indices
2104 !> \param arr1d input 1D array
2105 !> \param arr3d input 3D array
2106 ! **************************************************************************************************
2107  SUBROUTINE convert_1dto3d(idx_1dto3d, arr1d, arr3d)
2108 
2109  INTEGER, DIMENSION(:), INTENT(IN) :: idx_1dto3d
2110  REAL(dp), DIMENSION(:), INTENT(IN) :: arr1d
2111  REAL(dp), ALLOCATABLE, DIMENSION(:, :, :), &
2112  INTENT(INOUT) :: arr3d
2113 
2114  CHARACTER(LEN=*), PARAMETER :: routinen = 'convert_1dto3d'
2115 
2116  INTEGER :: handle, i, j, k, l, lb1, lb2, lb3, &
2117  npts1, npts2, npts3, ub1, ub2, ub3
2118 
2119  CALL timeset(routinen, handle)
2120 
2121  lb1 = lbound(arr3d, 1); ub1 = ubound(arr3d, 1)
2122  lb2 = lbound(arr3d, 2); ub2 = ubound(arr3d, 2)
2123  lb3 = lbound(arr3d, 3); ub3 = ubound(arr3d, 3)
2124 
2125  npts1 = ub1 - lb1 + 1
2126  npts2 = ub2 - lb2 + 1
2127  npts3 = ub3 - lb3 + 1
2128 
2129  DO l = 1, SIZE(idx_1dto3d)
2130  k = ((idx_1dto3d(l) - 1)/(npts1*npts2)) + lb3
2131  j = ((idx_1dto3d(l) - 1) - (k - lb3)*npts1*npts2)/npts1 + lb2
2132  i = idx_1dto3d(l) - ((j - lb2)*npts1 + (k - lb3)*npts1*npts2) + lb1 - 1
2133  arr3d(i, j, k) = arr1d(l)
2134  END DO
2135 
2136  CALL timestop(handle)
2137 
2138  END SUBROUTINE convert_1dto3d
2139 
2140 ! **************************************************************************************************
2141 !> \brief Returns the voltage of a tile. In case an alternating field is used, the oltage is a function of time
2142 !> \param time ...
2143 !> \param v_D ...
2144 !> \param osc_frac ...
2145 !> \param frequency ...
2146 !> \param phase ...
2147 !> \param v_D_new ...
2148 ! **************************************************************************************************
2149  SUBROUTINE get_voltage(time, v_D, osc_frac, frequency, phase, v_D_new)
2150 
2151  REAL(dp), INTENT(IN) :: time
2152  REAL(dp), DIMENSION(:), INTENT(IN) :: v_d, osc_frac, frequency, phase
2153  REAL(dp), ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: v_d_new
2154 
2155  CHARACTER(LEN=*), PARAMETER :: routinen = 'get_voltage'
2156 
2157  INTEGER :: handle, i
2158 
2159  CALL timeset(routinen, handle)
2160 
2161  ALLOCATE (v_d_new(SIZE(v_d)))
2162 
2163  DO i = 1, SIZE(v_d)
2164  v_d_new(i) = v_d(i)*(1 - osc_frac(i)) + &
2165  v_d(i)*osc_frac(i)*cos(2*pi*time*frequency(i) + phase(i))
2166  END DO
2167 
2168  CALL timestop(handle)
2169 
2170  END SUBROUTINE get_voltage
2171 
2172 END MODULE ps_implicit_methods
static void dgemm(const char transa, const char transb, const int m, const int n, const int k, const double alpha, const double *a, const int lda, const double *b, const int ldb, const double beta, double *c, const int ldc)
Convenient wrapper to hide Fortran nature of dgemm_, swapping a and b.
collects all references to literature in CP2K as new algorithms / method are included from literature...
Definition: bibliography.F:28
integer, save, public banihashemian2016
Definition: bibliography.F:43
various routines to log and control the output. The idea is that decisions about where to log should ...
recursive integer function, public cp_logger_get_default_unit_nr(logger, local, skip_not_ionode)
asks the default unit number of the given logger. try to use cp_logger_get_unit_nr
type(cp_logger_type) function, pointer, public cp_get_default_logger()
returns the default logger
the type I Discrete Cosine Transform (DCT-I)
Definition: dct.F:16
integer, parameter, public neumannx
Definition: dct.F:64
integer, parameter, public neumannxy
Definition: dct.F:64
subroutine, public dct_type_init(pw_grid, neumann_directions, dct_env)
Initializes a dct_type.
Definition: dct.F:84
integer, parameter, public neumannxz
Definition: dct.F:64
subroutine, public pw_shrink(neumann_directions, dests_shrink, srcs_shrink, bounds_local_shftd, pw_in, pw_shrinked)
shrinks an evenly symmetric pw_r3d_rs_type data to a pw_r3d_rs_type data that is 8 times smaller (the...
Definition: dct.F:702
integer, parameter, public neumannxyz
Definition: dct.F:64
integer, parameter, public neumannz
Definition: dct.F:64
integer, parameter, public neumannyz
Definition: dct.F:64
integer, parameter, public neumanny
Definition: dct.F:64
subroutine, public pw_expand(neumann_directions, recv_msgs_bnds, dests_expand, srcs_expand, flipg_stat, bounds_shftd, pw_in, pw_expanded)
expands a pw_r3d_rs_type data to an evenly symmetric pw_r3d_rs_type data that is 8 times larger than ...
Definition: dct.F:518
methods for evaluating the dielectric constant
subroutine, public dielectric_create(dielectric, pw_pool, dielectric_params)
allocates memory for a dielectric data type
subroutine, public derive_fft(f, df, pw_pool)
computes the derivative of a function using FFT
dielectric constant data type
subroutines for defining and creating Dirichlet type subdomains
subroutine, public dirichlet_boundary_region_setup(pw_pool, poisson_params, dbcs)
Sets up the Dirichlet boundary condition.
Dirichlet boundary condition data types.
subroutine, public dbc_tile_release(dbc, pw_pool)
releases tiles
sums arrays of real/complex numbers with much reduced round-off as compared to a naive implementation...
Definition: kahan_sum.F:29
Defines the basic variable types.
Definition: kinds.F:23
integer, parameter, public int_8
Definition: kinds.F:54
integer, parameter, public dp
Definition: kinds.F:34
Definition of mathematical constants and functions.
Definition: mathconstants.F:16
real(kind=dp), parameter, public pi
real(kind=dp), parameter, public fourpi
The implicit (generalized) Poisson solver.
subroutine, public implicit_poisson_solver_periodic(poisson_env, density, v_new, ehartree)
implicit Poisson solver for periodic boundary conditions
subroutine, public implicit_poisson_solver_mixed(poisson_env, density, v_new, electric_enthalpy)
implicit Poisson solver for mixed boundary conditions (Neumann + Dirichlet)
subroutine, public implicit_poisson_solver_neumann(poisson_env, density, v_new, ehartree)
implicit Poisson solver: zero-average solution of the Poisson equation subject to homogeneous Neumann...
subroutine, public implicit_poisson_solver_mixed_periodic(poisson_env, density, v_new, electric_enthalpy)
implicit Poisson solver for mixed-periodic boundary conditions (periodic + Dirichlet)
subroutine, public ps_implicit_create(pw_pool, poisson_params, dct_pw_grid, green, ps_implicit_env)
Creates implicit Poisson solver environment.
Types containing essential information for running implicit (iterative) Poisson solver.
integer, parameter, public neumann_bc
integer, parameter, public mixed_bc
integer, parameter, public mixed_periodic_bc
integer, parameter, public periodic_bc
functions related to the poisson solver on regular grids
Manages a pool of grids (to be used for example as tmp objects), but can also be used to instantiate ...
Definition: pw_pool_types.F:24
subroutine, public pw_pool_release(pool)
releases the given pool (see cp2k/doc/ReferenceCounting.html)
subroutine, public pw_pool_create(pool, pw_grid, max_cache)
creates a pool for pw