LCOV - code coverage report
Current view: top level - src/pw - ps_implicit_methods.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 94.1 % 917 863
Test Date: 2026-07-25 06:35:44 Functions: 100.0 % 24 24

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

Generated by: LCOV version 2.0-1