LCOV - code coverage report
Current view: top level - src - cp_ddapc_methods.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:744416f) Lines: 92.2 % 451 416
Test Date: 2026-09-20 02:09:09 Functions: 100.0 % 10 10

            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 contains information regarding the decoupling/recoupling method of Bloechl
      10              : !> \author Teodoro Laino
      11              : ! **************************************************************************************************
      12              : MODULE cp_ddapc_methods
      13              :    USE cell_types,                      ONLY: cell_type
      14              :    USE cp_log_handling,                 ONLY: cp_logger_get_default_io_unit
      15              :    USE input_constants,                 ONLY: weight_type_mass,&
      16              :                                               weight_type_unit
      17              :    USE input_section_types,             ONLY: section_vals_type,&
      18              :                                               section_vals_val_get,&
      19              :                                               section_vals_val_set
      20              :    USE kahan_sum,                       ONLY: accurate_sum
      21              :    USE kinds,                           ONLY: dp
      22              :    USE mathconstants,                   ONLY: fourpi,&
      23              :                                               oorootpi,&
      24              :                                               pi,&
      25              :                                               twopi
      26              :    USE mathlib,                         ONLY: diamat_all,&
      27              :                                               invert_matrix
      28              :    USE message_passing,                 ONLY: mp_para_env_type
      29              :    USE particle_types,                  ONLY: particle_type
      30              :    USE pw_spline_utils,                 ONLY: Eval_Interp_Spl3_pbc
      31              :    USE pw_types,                        ONLY: pw_c1d_gs_type,&
      32              :                                               pw_r3d_rs_type
      33              :    USE spherical_harmonics,             ONLY: legendre
      34              : #include "./base/base_uses.f90"
      35              : 
      36              :    IMPLICIT NONE
      37              :    PRIVATE
      38              :    LOGICAL, PRIVATE, PARAMETER :: debug_this_module = .FALSE.
      39              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_ddapc_methods'
      40              :    PUBLIC :: ddapc_eval_gfunc, &
      41              :              build_b_vector, &
      42              :              build_der_b_vector, &
      43              :              build_A_matrix, &
      44              :              build_der_A_matrix_rows, &
      45              :              prep_g_dot_rvec_sin_cos, &
      46              :              cleanup_g_dot_rvec_sin_cos, &
      47              :              ddapc_eval_AmI, &
      48              :              ewald_ddapc_pot, &
      49              :              solvation_ddapc_pot
      50              : 
      51              : CONTAINS
      52              : 
      53              : ! **************************************************************************************************
      54              : !> \brief ...
      55              : !> \param gfunc ...
      56              : !> \param w ...
      57              : !> \param gcut ...
      58              : !> \param rho_tot_g ...
      59              : !> \param radii ...
      60              : ! **************************************************************************************************
      61          296 :    SUBROUTINE ddapc_eval_gfunc(gfunc, w, gcut, rho_tot_g, radii)
      62              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: gfunc
      63              :       REAL(kind=dp), DIMENSION(:), POINTER               :: w
      64              :       REAL(KIND=dp), INTENT(IN)                          :: gcut
      65              :       TYPE(pw_c1d_gs_type), INTENT(IN)                   :: rho_tot_g
      66              :       REAL(kind=dp), DIMENSION(:), POINTER               :: radii
      67              : 
      68              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'ddapc_eval_gfunc'
      69              : 
      70              :       INTEGER                                            :: e_dim, handle, ig, igauss, s_dim
      71              :       REAL(KIND=dp)                                      :: g2, gcut2, rc, rc2
      72              : 
      73          296 :       CALL timeset(routineN, handle)
      74          296 :       gcut2 = gcut*gcut
      75              :       !
      76          296 :       s_dim = rho_tot_g%pw_grid%first_gne0
      77          296 :       e_dim = rho_tot_g%pw_grid%ngpts_cut_local
      78         1184 :       ALLOCATE (gfunc(s_dim:e_dim, SIZE(radii)))
      79          888 :       ALLOCATE (w(s_dim:e_dim))
      80     73665770 :       gfunc = 0.0_dp
      81     24772666 :       w = 0.0_dp
      82         1136 :       DO igauss = 1, SIZE(radii)
      83          840 :          rc = radii(igauss)
      84          840 :          rc2 = rc*rc
      85       556592 :          DO ig = s_dim, e_dim
      86       556296 :             g2 = rho_tot_g%pw_grid%gsq(ig)
      87       556296 :             IF (g2 > gcut2) EXIT
      88       556296 :             gfunc(ig, igauss) = EXP(-g2*rc2/4.0_dp)
      89              :          END DO
      90              :       END DO
      91       186764 :       DO ig = s_dim, e_dim
      92       186764 :          g2 = rho_tot_g%pw_grid%gsq(ig)
      93       186764 :          IF (g2 > gcut2) EXIT
      94       186764 :          w(ig) = fourpi*(g2 - gcut2)**2/(g2*gcut2)
      95              :       END DO
      96          296 :       CALL timestop(handle)
      97          296 :    END SUBROUTINE ddapc_eval_gfunc
      98              : 
      99              : ! **************************************************************************************************
     100              : !> \brief Computes the B vector for the solution of the linear system
     101              : !> \param bv ...
     102              : !> \param gfunc ...
     103              : !> \param w ...
     104              : !> \param particle_set ...
     105              : !> \param radii ...
     106              : !> \param rho_tot_g ...
     107              : !> \param gcut ...
     108              : !> \par History
     109              : !>      08.2005 created [tlaino]
     110              : !> \author Teodoro Laino
     111              : ! **************************************************************************************************
     112         2588 :    SUBROUTINE build_b_vector(bv, gfunc, w, particle_set, radii, rho_tot_g, gcut)
     113              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT)         :: bv
     114              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: gfunc
     115              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: w
     116              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     117              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: radii
     118              :       TYPE(pw_c1d_gs_type), INTENT(IN)                   :: rho_tot_g
     119              :       REAL(KIND=dp), INTENT(IN)                          :: gcut
     120              : 
     121              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'build_b_vector'
     122              : 
     123              :       COMPLEX(KIND=dp)                                   :: phase
     124              :       INTEGER                                            :: e_dim, handle, idim, ig, igauss, igmax, &
     125              :                                                             iparticle, s_dim
     126              :       REAL(KIND=dp)                                      :: arg, g2, gcut2, gvec(3), rvec(3)
     127         2588 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: my_bv, my_bvw
     128              : 
     129         2588 :       CALL timeset(routineN, handle)
     130         2588 :       NULLIFY (my_bv, my_bvw)
     131         2588 :       gcut2 = gcut*gcut
     132         2588 :       s_dim = rho_tot_g%pw_grid%first_gne0
     133         2588 :       e_dim = rho_tot_g%pw_grid%ngpts_cut_local
     134         2588 :       igmax = 0
     135      1037996 :       DO ig = s_dim, e_dim
     136      1037996 :          g2 = rho_tot_g%pw_grid%gsq(ig)
     137      1037996 :          IF (g2 > gcut2) EXIT
     138      1037996 :          igmax = ig
     139              :       END DO
     140         2588 :       IF (igmax >= s_dim) THEN
     141         7764 :          ALLOCATE (my_bv(s_dim:igmax))
     142         5176 :          ALLOCATE (my_bvw(s_dim:igmax))
     143              :          !
     144         9884 :          DO iparticle = 1, SIZE(particle_set)
     145        29184 :             rvec = particle_set(iparticle)%r
     146      3512084 :             my_bv = 0.0_dp
     147      3512084 :             DO ig = s_dim, igmax
     148     14019152 :                gvec = rho_tot_g%pw_grid%g(:, ig)
     149     14019152 :                arg = DOT_PRODUCT(gvec, rvec)
     150      3504788 :                phase = CMPLX(COS(arg), -SIN(arg), KIND=dp)
     151      3512084 :                my_bv(ig) = w(ig)*REAL(CONJG(rho_tot_g%array(ig))*phase, KIND=dp)
     152              :             END DO
     153        29396 :             DO igauss = 1, SIZE(radii)
     154        19512 :                idim = (iparticle - 1)*SIZE(radii) + igauss
     155     10333500 :                DO ig = s_dim, igmax
     156     10333500 :                   my_bvw(ig) = my_bv(ig)*gfunc(ig, igauss)
     157              :                END DO
     158        26808 :                bv(idim) = accurate_sum(my_bvw)
     159              :             END DO
     160              :          END DO
     161         2588 :          DEALLOCATE (my_bvw)
     162         2588 :          DEALLOCATE (my_bv)
     163              :       ELSE
     164            0 :          DO iparticle = 1, SIZE(particle_set)
     165            0 :             DO igauss = 1, SIZE(radii)
     166            0 :                idim = (iparticle - 1)*SIZE(radii) + igauss
     167            0 :                bv(idim) = 0.0_dp
     168              :             END DO
     169              :          END DO
     170              :       END IF
     171         2588 :       CALL timestop(handle)
     172         2588 :    END SUBROUTINE build_b_vector
     173              : 
     174              : ! **************************************************************************************************
     175              : !> \brief Computes the A matrix for the solution of the linear system
     176              : !> \param Am ...
     177              : !> \param gfunc ...
     178              : !> \param w ...
     179              : !> \param particle_set ...
     180              : !> \param radii ...
     181              : !> \param rho_tot_g ...
     182              : !> \param gcut ...
     183              : !> \param g_dot_rvec_sin ...
     184              : !> \param g_dot_rvec_cos ...
     185              : !> \par History
     186              : !>      08.2005 created [tlaino]
     187              : !> \author Teodoro Laino
     188              : !> \note NB accept g_dot_rvec_* arrays
     189              : ! **************************************************************************************************
     190          296 :    SUBROUTINE build_A_matrix(Am, gfunc, w, particle_set, radii, rho_tot_g, gcut, g_dot_rvec_sin, g_dot_rvec_cos)
     191              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT)      :: Am
     192              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: gfunc
     193              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: w
     194              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     195              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: radii
     196              :       TYPE(pw_c1d_gs_type), INTENT(IN)                   :: rho_tot_g
     197              :       REAL(KIND=dp), INTENT(IN)                          :: gcut
     198              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: g_dot_rvec_sin, g_dot_rvec_cos
     199              : 
     200              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'build_A_matrix'
     201              : 
     202              :       INTEGER                                            :: e_dim, handle, idim1, idim2, ig, &
     203              :                                                             igauss1, igauss2, igmax, iparticle1, &
     204              :                                                             iparticle2, istart_g, s_dim
     205              :       REAL(KIND=dp)                                      :: g2, gcut2, tmp
     206          296 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: my_Am, my_Amw
     207          296 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :)     :: gfunc_sq
     208              : 
     209              : !NB precalculate as many things outside of the innermost loop as possible, in particular w(ig)*gfunc(ig,igauus1)*gfunc(ig,igauss2)
     210              : 
     211          296 :       CALL timeset(routineN, handle)
     212          296 :       gcut2 = gcut*gcut
     213          296 :       s_dim = rho_tot_g%pw_grid%first_gne0
     214          296 :       e_dim = rho_tot_g%pw_grid%ngpts_cut_local
     215          296 :       igmax = 0
     216       186764 :       DO ig = s_dim, e_dim
     217       186764 :          g2 = rho_tot_g%pw_grid%gsq(ig)
     218       186764 :          IF (g2 > gcut2) EXIT
     219       186764 :          igmax = ig
     220              :       END DO
     221          296 :       IF (igmax >= s_dim) THEN
     222          888 :          ALLOCATE (my_Am(s_dim:igmax))
     223          592 :          ALLOCATE (my_Amw(s_dim:igmax))
     224         1480 :          ALLOCATE (gfunc_sq(s_dim:igmax, SIZE(radii), SIZE(radii)))
     225              : 
     226         1136 :          DO igauss1 = 1, SIZE(radii)
     227         3608 :             DO igauss2 = 1, SIZE(radii)
     228      1665732 :                gfunc_sq(s_dim:igmax, igauss1, igauss2) = w(s_dim:igmax)*gfunc(s_dim:igmax, igauss1)*gfunc(s_dim:igmax, igauss2)
     229              :             END DO
     230              :          END DO
     231              : 
     232         1178 :          DO iparticle1 = 1, SIZE(particle_set)
     233         4912 :             DO iparticle2 = iparticle1, SIZE(particle_set)
     234      6964954 :                DO ig = s_dim, igmax
     235              :                   !NB replace explicit dot product and cosine with cos(A+B) formula - much faster
     236              :                   my_Am(ig) = (g_dot_rvec_cos(ig - s_dim + 1, iparticle1)*g_dot_rvec_cos(ig - s_dim + 1, iparticle2) + &
     237      6964954 :                                g_dot_rvec_sin(ig - s_dim + 1, iparticle1)*g_dot_rvec_sin(ig - s_dim + 1, iparticle2))
     238              :                END DO
     239        15638 :                DO igauss1 = 1, SIZE(radii)
     240        11022 :                   idim1 = (iparticle1 - 1)*SIZE(radii) + igauss1
     241        11022 :                   istart_g = 1
     242        11022 :                   IF (iparticle2 == iparticle1) istart_g = igauss1
     243        45158 :                   DO igauss2 = istart_g, SIZE(radii)
     244        30402 :                      idim2 = (iparticle2 - 1)*SIZE(radii) + igauss2
     245     60402252 :                      my_Amw(s_dim:igmax) = my_Am(s_dim:igmax)*gfunc_sq(s_dim:igmax, igauss1, igauss2)
     246              :                      !NB no loss of accuracy in my test cases
     247              :                      !tmp = accurate_sum(my_Amw)
     248     60402252 :                      tmp = SUM(my_Amw)
     249        30402 :                      Am(idim2, idim1) = tmp
     250        41424 :                      Am(idim1, idim2) = tmp
     251              :                   END DO
     252              :                END DO
     253              :             END DO
     254              :          END DO
     255          296 :          DEALLOCATE (gfunc_sq)
     256          296 :          DEALLOCATE (my_Amw)
     257          296 :          DEALLOCATE (my_Am)
     258              :       END IF
     259          296 :       CALL timestop(handle)
     260          296 :    END SUBROUTINE build_A_matrix
     261              : 
     262              : ! **************************************************************************************************
     263              : !> \brief Computes the derivative of B vector for the evaluation of the Pulay forces
     264              : !> \param dbv ...
     265              : !> \param gfunc ...
     266              : !> \param w ...
     267              : !> \param particle_set ...
     268              : !> \param radii ...
     269              : !> \param rho_tot_g ...
     270              : !> \param gcut ...
     271              : !> \param iparticle0 ...
     272              : !> \par History
     273              : !>      08.2005 created [tlaino]
     274              : !> \author Teodoro Laino
     275              : ! **************************************************************************************************
     276          420 :    SUBROUTINE build_der_b_vector(dbv, gfunc, w, particle_set, radii, rho_tot_g, gcut, iparticle0)
     277              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT)      :: dbv
     278              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: gfunc
     279              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: w
     280              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     281              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: radii
     282              :       TYPE(pw_c1d_gs_type), INTENT(IN)                   :: rho_tot_g
     283              :       REAL(KIND=dp), INTENT(IN)                          :: gcut
     284              :       INTEGER, INTENT(IN)                                :: iparticle0
     285              : 
     286              :       CHARACTER(len=*), PARAMETER :: routineN = 'build_der_b_vector'
     287              : 
     288              :       COMPLEX(KIND=dp)                                   :: dphase
     289              :       INTEGER                                            :: e_dim, handle, idim, ig, igauss, igmax, &
     290              :                                                             iparticle, s_dim
     291              :       REAL(KIND=dp)                                      :: arg, g2, gcut2
     292          420 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: my_dbvw
     293          420 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: my_dbv
     294              :       REAL(KIND=dp), DIMENSION(3)                        :: gvec, rvec
     295              : 
     296          420 :       CALL timeset(routineN, handle)
     297          420 :       gcut2 = gcut*gcut
     298          420 :       s_dim = rho_tot_g%pw_grid%first_gne0
     299          420 :       e_dim = rho_tot_g%pw_grid%ngpts_cut_local
     300          420 :       igmax = 0
     301       360282 :       DO ig = s_dim, e_dim
     302       360282 :          g2 = rho_tot_g%pw_grid%gsq(ig)
     303       360282 :          IF (g2 > gcut2) EXIT
     304       360282 :          igmax = ig
     305              :       END DO
     306          420 :       IF (igmax >= s_dim) THEN
     307         1260 :          ALLOCATE (my_dbv(3, s_dim:igmax))
     308         1260 :          ALLOCATE (my_dbvw(s_dim:igmax))
     309         1908 :          DO iparticle = 1, SIZE(particle_set)
     310         1488 :             IF (iparticle /= iparticle0) CYCLE
     311         1680 :             rvec = particle_set(iparticle)%r
     312       360282 :             DO ig = s_dim, igmax
     313      1439448 :                gvec = rho_tot_g%pw_grid%g(:, ig)
     314      1439448 :                arg = DOT_PRODUCT(gvec, rvec)
     315       359862 :                dphase = -CMPLX(SIN(arg), COS(arg), KIND=dp)
     316      1439868 :                my_dbv(:, ig) = w(ig)*REAL(CONJG(rho_tot_g%array(ig))*dphase, KIND=dp)*gvec(:)
     317              :             END DO
     318         3060 :             DO igauss = 1, SIZE(radii)
     319         1152 :                idim = (iparticle - 1)*SIZE(radii) + igauss
     320      1071630 :                DO ig = s_dim, igmax
     321      1071630 :                   my_dbvw(ig) = my_dbv(1, ig)*gfunc(ig, igauss)
     322              :                END DO
     323         1152 :                dbv(idim, 1) = accurate_sum(my_dbvw)
     324      1071630 :                DO ig = s_dim, igmax
     325      1071630 :                   my_dbvw(ig) = my_dbv(2, ig)*gfunc(ig, igauss)
     326              :                END DO
     327         1152 :                dbv(idim, 2) = accurate_sum(my_dbvw)
     328      1071630 :                DO ig = s_dim, igmax
     329      1071630 :                   my_dbvw(ig) = my_dbv(3, ig)*gfunc(ig, igauss)
     330              :                END DO
     331         1572 :                dbv(idim, 3) = accurate_sum(my_dbvw)
     332              :             END DO
     333              :          END DO
     334          420 :          DEALLOCATE (my_dbvw)
     335          420 :          DEALLOCATE (my_dbv)
     336              :       ELSE
     337            0 :          DO iparticle = 1, SIZE(particle_set)
     338            0 :             IF (iparticle /= iparticle0) CYCLE
     339            0 :             DO igauss = 1, SIZE(radii)
     340            0 :                idim = (iparticle - 1)*SIZE(radii) + igauss
     341            0 :                dbv(idim, 1:3) = 0.0_dp
     342              :             END DO
     343              :          END DO
     344              :       END IF
     345          420 :       CALL timestop(handle)
     346          420 :    END SUBROUTINE build_der_b_vector
     347              : 
     348              : ! **************************************************************************************************
     349              : !> \brief Computes the derivative of the A matrix for the evaluation of the
     350              : !>      Pulay forces
     351              : !> \param dAm ...
     352              : !> \param gfunc ...
     353              : !> \param w ...
     354              : !> \param particle_set ...
     355              : !> \param radii ...
     356              : !> \param rho_tot_g ...
     357              : !> \param gcut ...
     358              : !> \param iparticle0 ...
     359              : !> \param nparticles ...
     360              : !> \param g_dot_rvec_sin ...
     361              : !> \param g_dot_rvec_cos ...
     362              : !> \par History
     363              : !>      08.2005 created [tlaino]
     364              : !> \author Teodoro Laino
     365              : !> \note NB accept g_dot_rvec_* arrays
     366              : ! **************************************************************************************************
     367          444 :    SUBROUTINE build_der_A_matrix_rows(dAm, gfunc, w, particle_set, radii, &
     368          148 :                                       rho_tot_g, gcut, iparticle0, nparticles, g_dot_rvec_sin, g_dot_rvec_cos)
     369              :       REAL(KIND=dp), DIMENSION(:, :, :), INTENT(INOUT)   :: dAm
     370              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: gfunc
     371              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: w
     372              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     373              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: radii
     374              :       TYPE(pw_c1d_gs_type), INTENT(IN)                   :: rho_tot_g
     375              :       REAL(KIND=dp), INTENT(IN)                          :: gcut
     376              :       INTEGER, INTENT(IN)                                :: iparticle0, nparticles
     377              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: g_dot_rvec_sin, g_dot_rvec_cos
     378              : 
     379              :       CHARACTER(len=*), PARAMETER :: routineN = 'build_der_A_matrix_rows'
     380              : 
     381              :       INTEGER                                            :: e_dim, handle, ig, igauss2, igmax, &
     382              :                                                             iparticle1, iparticle2, s_dim
     383              :       REAL(KIND=dp)                                      :: g2, gcut2
     384              : 
     385              : !NB calculate derivatives for a block of particles, just the row parts (since derivative matrix is symmetric)
     386              : !NB Use DGEMM to speed up calculation, can't do accurate_sum() anymore because dgemm does the sum over g
     387              : 
     388              :       EXTERNAL DGEMM
     389          148 :       REAL(KIND=dp), ALLOCATABLE :: lhs(:, :), rhs(:, :)
     390              :       INTEGER :: Nr, Np, Ng, icomp, ipp
     391              : 
     392          148 :       CALL timeset(routineN, handle)
     393          148 :       gcut2 = gcut*gcut
     394          148 :       s_dim = rho_tot_g%pw_grid%first_gne0
     395          148 :       e_dim = rho_tot_g%pw_grid%ngpts_cut_local
     396          148 :       igmax = 0
     397       152182 :       DO ig = s_dim, e_dim
     398       152182 :          g2 = rho_tot_g%pw_grid%gsq(ig)
     399       152182 :          IF (g2 > gcut2) EXIT
     400       152182 :          igmax = ig
     401              :       END DO
     402              : 
     403          148 :       Nr = SIZE(radii)
     404          148 :       Np = SIZE(particle_set)
     405          148 :       Ng = igmax - s_dim + 1
     406          148 :       IF (igmax >= s_dim) THEN
     407          592 :          ALLOCATE (lhs(nparticles*Nr, Ng))
     408          592 :          ALLOCATE (rhs(Ng, Np*Nr))
     409              : 
     410              :          ! rhs with first term of sin(g.(rvec1-rvec2))
     411              :          ! rhs has all parts that depend on iparticle2
     412          568 :          DO iparticle2 = 1, Np
     413         1720 :             DO igauss2 = 1, Nr
     414      1072050 :                rhs(1:Ng, (iparticle2 - 1)*Nr + igauss2) = g_dot_rvec_sin(1:Ng, iparticle2)*gfunc(s_dim:igmax, igauss2)
     415              :             END DO
     416              :          END DO
     417          592 :          DO icomp = 1, 3
     418              :             ! create lhs, which has all parts that depend on iparticle1
     419         1704 :             DO ipp = 1, nparticles
     420         1260 :                iparticle1 = iparticle0 + ipp - 1
     421      1081290 :                DO ig = s_dim, igmax
     422              :                   lhs((ipp - 1)*Nr + 1:(ipp - 1)*Nr + Nr, ig - s_dim + 1) = w(ig)*rho_tot_g%pw_grid%g(icomp, ig)* &
     423      4292280 :                                                                           gfunc(ig, 1:Nr)*g_dot_rvec_cos(ig - s_dim + 1, iparticle1)
     424              :                END DO
     425              :             END DO ! ipp
     426              :             ! do main multiply
     427              :             CALL DGEMM('N', 'N', nparticles*Nr, Np*Nr, Ng, 1.0D0, lhs(1, 1), nparticles*Nr, rhs(1, 1), &
     428          444 :                        Ng, 0.0D0, dAm((iparticle0 - 1)*Nr + 1, 1, icomp), Np*Nr)
     429              :             ! do extra multiplies to compensate for missing factor of 2
     430         1852 :             DO ipp = 1, nparticles
     431         1260 :                iparticle1 = iparticle0 + ipp - 1
     432              :                CALL DGEMM('N', 'N', Nr, Nr, Ng, 1.0D0, lhs((ipp - 1)*Nr + 1, 1), nparticles*Nr, rhs(1, (iparticle1 - 1)*Nr + 1), &
     433         1704 :                           Ng, 1.0D0, dAm((iparticle1 - 1)*Nr + 1, (iparticle1 - 1)*Nr + 1, icomp), Np*Nr)
     434              :             END DO
     435              :             ! now extra columns to account for factor of 2 in some rhs columns
     436              :          END DO ! icomp
     437              : 
     438              :          ! rhs with second term of sin(g.(rvec1-rvec2))
     439              :          ! rhs has all parts that depend on iparticle2
     440          568 :          DO iparticle2 = 1, Np
     441         1720 :             DO igauss2 = 1, Nr
     442      1072050 :                rhs(1:Ng, (iparticle2 - 1)*Nr + igauss2) = -g_dot_rvec_cos(1:Ng, iparticle2)*gfunc(s_dim:igmax, igauss2)
     443              :             END DO
     444              :          END DO
     445          592 :          DO icomp = 1, 3
     446              :             ! create lhs, which has all parts that depend on iparticle1
     447         1704 :             DO ipp = 1, nparticles
     448         1260 :                iparticle1 = iparticle0 + ipp - 1
     449      1081290 :                DO ig = s_dim, igmax
     450              :                   lhs((ipp - 1)*Nr + 1:(ipp - 1)*Nr + Nr, ig - s_dim + 1) = w(ig)*rho_tot_g%pw_grid%g(icomp, ig)*gfunc(ig, 1:Nr)* &
     451      4292280 :                                                                             g_dot_rvec_sin(ig - s_dim + 1, iparticle1)
     452              :                END DO
     453              :             END DO
     454              :             ! do main multiply
     455              :             CALL DGEMM('N', 'N', nparticles*Nr, Np*Nr, Ng, 1.0D0, lhs(1, 1), nparticles*Nr, rhs(1, 1), &
     456          444 :                        Ng, 1.0D0, dAm((iparticle0 - 1)*Nr + 1, 1, icomp), Np*Nr)
     457              :             ! do extra multiples to compensate for missing factor of 2
     458         1852 :             DO ipp = 1, nparticles
     459         1260 :                iparticle1 = iparticle0 + ipp - 1
     460              :                CALL DGEMM('N', 'N', Nr, Nr, Ng, 1.0D0, lhs((ipp - 1)*Nr + 1, 1), nparticles*Nr, rhs(1, (iparticle1 - 1)*Nr + 1), &
     461         1704 :                           Ng, 1.0D0, dAm((iparticle1 - 1)*Nr + 1, (iparticle1 - 1)*Nr + 1, icomp), Np*Nr)
     462              :             END DO
     463              :          END DO
     464              : 
     465          148 :          DEALLOCATE (rhs)
     466          148 :          DEALLOCATE (lhs)
     467              :       ELSE
     468              :          ! Some ranks may not own G-vectors below the cutoff.
     469            0 :          dAm((iparticle0 - 1)*Nr + 1:(iparticle0 + nparticles - 1)*Nr, :, :) = 0.0_dp
     470              :       END IF
     471          148 :       CALL timestop(handle)
     472          148 :    END SUBROUTINE build_der_A_matrix_rows
     473              : 
     474              : ! **************************************************************************************************
     475              : !> \brief deallocate g_dot_rvec_* arrays
     476              : !> \param g_dot_rvec_sin ...
     477              : !> \param g_dot_rvec_cos ...
     478              : ! **************************************************************************************************
     479          444 :    SUBROUTINE cleanup_g_dot_rvec_sin_cos(g_dot_rvec_sin, g_dot_rvec_cos)
     480              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: g_dot_rvec_sin, g_dot_rvec_cos
     481              : 
     482          444 :       IF (ALLOCATED(g_dot_rvec_sin)) DEALLOCATE (g_dot_rvec_sin)
     483          444 :       IF (ALLOCATED(g_dot_rvec_cos)) DEALLOCATE (g_dot_rvec_cos)
     484          444 :    END SUBROUTINE cleanup_g_dot_rvec_sin_cos
     485              : 
     486              : ! **************************************************************************************************
     487              : !> \brief precompute sin(g.r) and cos(g.r) for quicker evaluations of sin(g.(r1-r2)) and cos(g.(r1-r2))
     488              : !> \param rho_tot_g ...
     489              : !> \param particle_set ...
     490              : !> \param gcut ...
     491              : !> \param g_dot_rvec_sin ...
     492              : !> \param g_dot_rvec_cos ...
     493              : ! **************************************************************************************************
     494          444 :    SUBROUTINE prep_g_dot_rvec_sin_cos(rho_tot_g, particle_set, gcut, g_dot_rvec_sin, g_dot_rvec_cos)
     495              :       TYPE(pw_c1d_gs_type), INTENT(IN)                   :: rho_tot_g
     496              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     497              :       REAL(KIND=dp), INTENT(IN)                          :: gcut
     498              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: g_dot_rvec_sin, g_dot_rvec_cos
     499              : 
     500              :       INTEGER                                            :: e_dim, ig, igmax, iparticle, s_dim
     501              :       REAL(KIND=dp)                                      :: g2, g_dot_rvec, gcut2, rvec(3)
     502              : 
     503          444 :       gcut2 = gcut*gcut
     504          444 :       s_dim = rho_tot_g%pw_grid%first_gne0
     505          444 :       e_dim = rho_tot_g%pw_grid%ngpts_cut_local
     506          444 :       igmax = 0
     507       338946 :       DO ig = s_dim, e_dim
     508       338946 :          g2 = rho_tot_g%pw_grid%gsq(ig)
     509       338946 :          IF (g2 > gcut2) EXIT
     510       338946 :          igmax = ig
     511              :       END DO
     512              : 
     513          444 :       IF (igmax >= s_dim) THEN
     514         1776 :          ALLOCATE (g_dot_rvec_sin(1:igmax - s_dim + 1, SIZE(particle_set)))
     515         1332 :          ALLOCATE (g_dot_rvec_cos(1:igmax - s_dim + 1, SIZE(particle_set)))
     516              : 
     517         1746 :          DO iparticle = 1, SIZE(particle_set)
     518         5208 :             rvec = particle_set(iparticle)%r
     519      1105232 :             DO ig = s_dim, igmax
     520      4413944 :                g_dot_rvec = DOT_PRODUCT(rho_tot_g%pw_grid%g(:, ig), rvec)
     521      1103486 :                g_dot_rvec_sin(ig - s_dim + 1, iparticle) = SIN(g_dot_rvec)
     522      1104788 :                g_dot_rvec_cos(ig - s_dim + 1, iparticle) = COS(g_dot_rvec)
     523              :             END DO
     524              :          END DO
     525              :       ELSE
     526              :          ! Pass valid zero-sized arrays to the downstream routines.
     527            0 :          ALLOCATE (g_dot_rvec_sin(0, SIZE(particle_set)))
     528            0 :          ALLOCATE (g_dot_rvec_cos(0, SIZE(particle_set)))
     529              :       END IF
     530              : 
     531          444 :    END SUBROUTINE prep_g_dot_rvec_sin_cos
     532              : 
     533              : ! **************************************************************************************************
     534              : !> \brief Computes the inverse AmI of the Am matrix
     535              : !> \param GAmI ...
     536              : !> \param c0 ...
     537              : !> \param gfunc ...
     538              : !> \param w ...
     539              : !> \param particle_set ...
     540              : !> \param gcut ...
     541              : !> \param rho_tot_g ...
     542              : !> \param radii ...
     543              : !> \param iw ...
     544              : !> \param Vol ...
     545              : !> \par History
     546              : !>      12.2005 created [tlaino]
     547              : !> \author Teodoro Laino
     548              : ! **************************************************************************************************
     549          296 :    SUBROUTINE ddapc_eval_AmI(GAmI, c0, gfunc, w, particle_set, gcut, &
     550              :                              rho_tot_g, radii, iw, Vol)
     551              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: GAmI
     552              :       REAL(KIND=dp), INTENT(OUT)                         :: c0
     553              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: gfunc
     554              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: w
     555              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     556              :       REAL(KIND=dp), INTENT(IN)                          :: gcut
     557              :       TYPE(pw_c1d_gs_type), INTENT(IN)                   :: rho_tot_g
     558              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: radii
     559              :       INTEGER, INTENT(IN)                                :: iw
     560              :       REAL(KIND=dp), INTENT(IN)                          :: Vol
     561              : 
     562              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'ddapc_eval_AmI'
     563              : 
     564              :       INTEGER                                            :: handle, ndim
     565              :       REAL(KIND=dp)                                      :: condition_number, inv_error
     566          296 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: AmE, cv
     567          296 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: Am, AmI, Amw, g_dot_rvec_cos, &
     568          296 :                                                             g_dot_rvec_sin
     569              : 
     570              : !NB for precomputation of sin(g.r) and cos(g.r)
     571              : 
     572          296 :       CALL timeset(routineN, handle)
     573          296 :       ndim = SIZE(particle_set)*SIZE(radii)
     574         1184 :       ALLOCATE (Am(ndim, ndim))
     575          888 :       ALLOCATE (AmI(ndim, ndim))
     576          888 :       ALLOCATE (GAmI(ndim, ndim))
     577          888 :       ALLOCATE (cv(ndim))
     578          296 :       Am = 0.0_dp
     579          296 :       AmI = 0.0_dp
     580         2834 :       cv = 1.0_dp/Vol
     581              :       !NB precompute sin(g.r) and cos(g.r) for faster evaluation of cos(g.(r1-r2)) in build_A_matrix()
     582          296 :       CALL prep_g_dot_rvec_sin_cos(rho_tot_g, particle_set, gcut, g_dot_rvec_sin, g_dot_rvec_cos)
     583          296 :       CALL build_A_matrix(Am, gfunc, w, particle_set, radii, rho_tot_g, gcut, g_dot_rvec_sin, g_dot_rvec_cos)
     584          296 :       CALL cleanup_g_dot_rvec_sin_cos(g_dot_rvec_sin, g_dot_rvec_cos)
     585        61100 :       Am(:, :) = Am(:, :)/(Vol*Vol)
     586          296 :       CALL rho_tot_g%pw_grid%para%group%sum(Am)
     587          296 :       IF (iw > 0) THEN
     588              :          ! Checking conditions numbers and eigenvalues
     589            0 :          ALLOCATE (Amw(ndim, ndim))
     590            0 :          ALLOCATE (AmE(ndim))
     591            0 :          Amw(:, :) = Am
     592            0 :          CALL diamat_all(Amw, AmE)
     593            0 :          condition_number = MAXVAL(ABS(AmE))/MINVAL(ABS(AmE))
     594            0 :          WRITE (iw, '(T3,A)') " Eigenvalues of Matrix A:"
     595            0 :          WRITE (iw, '(T3,4E15.8)') AmE
     596            0 :          WRITE (iw, '(T3,A,1E15.9)') " Condition number:", condition_number
     597            0 :          IF (condition_number > 1.0E12_dp) THEN
     598              :             WRITE (iw, FMT="(/,T2,A)") &
     599            0 :                "WARNING: high condition number => possibly ill-conditioned matrix"
     600              :          END IF
     601            0 :          DEALLOCATE (Amw)
     602            0 :          DEALLOCATE (AmE)
     603              :       END IF
     604          296 :       CALL invert_matrix(Am, AmI, inv_error, "N", improve=.FALSE.)
     605          296 :       IF (iw > 0) THEN
     606            0 :          WRITE (iw, '(T3,A,F15.9)') " Error inverting the A matrix: ", inv_error
     607              :       END IF
     608       122496 :       c0 = DOT_PRODUCT(cv, MATMUL(AmI, cv))
     609          296 :       DEALLOCATE (Am)
     610          296 :       DEALLOCATE (cv)
     611        61100 :       GAmI = AmI
     612          296 :       DEALLOCATE (AmI)
     613          296 :       CALL timestop(handle)
     614          592 :    END SUBROUTINE ddapc_eval_AmI
     615              : 
     616              : ! **************************************************************************************************
     617              : !> \brief Evaluates the Ewald term E2 and E3 energy term for the decoupling/coupling
     618              : !>      of periodic images
     619              : !> \param cp_para_env ...
     620              : !> \param coeff ...
     621              : !> \param factor ...
     622              : !> \param cell ...
     623              : !> \param multipole_section ...
     624              : !> \param particle_set ...
     625              : !> \param M ...
     626              : !> \param radii ...
     627              : !> \par History
     628              : !>      08.2005 created [tlaino]
     629              : !> \author Teodoro Laino
     630              : !> \note NB receive cp_para_env for parallelization
     631              : ! **************************************************************************************************
     632          224 :    RECURSIVE SUBROUTINE ewald_ddapc_pot(cp_para_env, coeff, factor, cell, multipole_section, &
     633              :                                         particle_set, M, radii)
     634              :       TYPE(mp_para_env_type), INTENT(IN)                 :: cp_para_env
     635              :       TYPE(pw_r3d_rs_type), INTENT(IN), POINTER          :: coeff
     636              :       REAL(KIND=dp), INTENT(IN)                          :: factor
     637              :       TYPE(cell_type), POINTER                           :: cell
     638              :       TYPE(section_vals_type), POINTER                   :: multipole_section
     639              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     640              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: M
     641              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: radii
     642              : 
     643              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'ewald_ddapc_pot'
     644              : 
     645              :       INTEGER :: ewmdim, handle, iaxis, idim, idim1, idim2, idimo, igauss1, igauss2, ip1, ip2, &
     646              :          iparticle1, iparticle2, istart_g, k1, k2, k3, n_rep, ndim, r1, r2, r3
     647              :       INTEGER, DIMENSION(3)                              :: gmax, image_cell, rmax, rmin
     648              :       LOGICAL                                            :: analyt
     649              :       REAL(KIND=dp) :: alpha, eps, ew_neut, fac, fac3, frac_radius, fs, g_ewald, galpha, gsq, &
     650              :          gsqi, ij_fac, my_val, r, r2tmp, r_ewald, rc1, rc12, rc2, rc22, rcut, rcut2, t1, tol, tol1
     651              :       REAL(KIND=dp), DIMENSION(3)                        :: g_index, gvec, ra, rvec, svec
     652          224 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: EwM
     653              : 
     654          224 :       NULLIFY (EwM)
     655          224 :       CALL timeset(routineN, handle)
     656          224 :       CPASSERT(.NOT. ASSOCIATED(M))
     657          224 :       CPASSERT(ASSOCIATED(radii))
     658         2240 :       rcut = MIN(NORM2(cell%hmat(:, 1)), NORM2(cell%hmat(:, 2)), NORM2(cell%hmat(:, 3)))/2.0_dp
     659          224 :       CALL section_vals_val_get(multipole_section, "RCUT", n_rep_val=n_rep)
     660          224 :       IF (n_rep == 1) CALL section_vals_val_get(multipole_section, "RCUT", r_val=rcut)
     661          224 :       CALL section_vals_val_get(multipole_section, "EWALD_PRECISION", r_val=eps)
     662          224 :       CALL section_vals_val_get(multipole_section, "ANALYTICAL_GTERM", l_val=analyt)
     663              :       ! The spline interpolation path is only valid for orthorhombic grids.
     664          224 :       analyt = analyt .OR. .NOT. cell%orthorhombic .OR. .NOT. ASSOCIATED(coeff)
     665          224 :       rcut2 = rcut**2
     666              :       !
     667              :       ! Setting-up parameters for Ewald summation
     668              :       !
     669          224 :       eps = MIN(ABS(eps), 0.5_dp)
     670          224 :       tol = SQRT(ABS(LOG(eps*rcut)))
     671          224 :       alpha = SQRT(ABS(LOG(eps*rcut*tol)))/rcut
     672          224 :       galpha = 1.0_dp/(4.0_dp*alpha*alpha)
     673          224 :       tol1 = SQRT(-LOG(eps*rcut*(2.0_dp*tol*alpha)**2))
     674          224 :       IF (cell%orthorhombic) THEN
     675          856 :          DO iaxis = 1, 3
     676          856 :             gmax(iaxis) = NINT(0.25_dp + cell%hmat(iaxis, iaxis)*alpha*tol1/pi)
     677              :          END DO
     678              :       ELSE
     679           40 :          DO iaxis = 1, 3
     680          130 :             gmax(iaxis) = CEILING(alpha*tol1*NORM2(cell%hmat(:, iaxis))/pi)
     681              :          END DO
     682              :       END IF
     683          224 :       fac = 1.e0_dp/cell%deth
     684          224 :       fac3 = fac*pi
     685          224 :       ew_neut = -fac*pi/alpha**2
     686              :       !
     687          224 :       ewmdim = SIZE(particle_set)*(SIZE(particle_set) + 1)/2
     688          224 :       ndim = SIZE(particle_set)*SIZE(radii)
     689          672 :       ALLOCATE (EwM(ewmdim))
     690          896 :       ALLOCATE (M(ndim, ndim))
     691        92216 :       M = 0.0_dp
     692              :       !
     693         5584 :       idim = 0
     694         5584 :       EwM = 0.0_dp
     695          972 :       DO iparticle1 = 1, SIZE(particle_set)
     696         6108 :          ip1 = (iparticle1 - 1)*SIZE(radii)
     697         6332 :          DO iparticle2 = 1, iparticle1
     698         5360 :             ij_fac = 1.0_dp
     699         5360 :             IF (iparticle1 == iparticle2) ij_fac = 0.5_dp
     700              : 
     701         5360 :             ip2 = (iparticle2 - 1)*SIZE(radii)
     702         5360 :             idim = idim + 1
     703              :             !NB parallelization, done here so indexing is right
     704         5360 :             IF (MOD(iparticle1, cp_para_env%num_pe) /= cp_para_env%mepos) CYCLE
     705              :             !
     706              :             ! Real-Space Contribution
     707              :             !
     708         2704 :             my_val = 0.0_dp
     709        10816 :             rvec = particle_set(iparticle1)%r - particle_set(iparticle2)%r
     710         2704 :             r_ewald = 0.0_dp
     711         2704 :             IF (iparticle1 /= iparticle2) THEN
     712         2314 :                ra = rvec
     713         9256 :                r2tmp = DOT_PRODUCT(ra, ra)
     714         2314 :                IF (r2tmp <= rcut2) THEN
     715         2226 :                   r = SQRT(r2tmp)
     716         2226 :                   t1 = erfc(alpha*r)/r
     717         2226 :                   r_ewald = t1
     718              :                END IF
     719              :             END IF
     720        35152 :             svec = MATMUL(cell%h_inv, rvec)
     721        10816 :             DO iaxis = 1, 3
     722        32448 :                frac_radius = rcut*NORM2(cell%h_inv(iaxis, :))
     723         8112 :                rmin(iaxis) = FLOOR(-svec(iaxis) - frac_radius)
     724        10816 :                rmax(iaxis) = CEILING(-svec(iaxis) + frac_radius)
     725              :             END DO
     726        16189 :             DO r1 = rmin(1), rmax(1)
     727        86728 :                DO r2 = rmin(2), rmax(2)
     728       467857 :                   DO r3 = rmin(3), rmax(3)
     729       383833 :                      IF ((r1 == 0) .AND. (r2 == 0) .AND. (r3 == 0)) CYCLE
     730      1524516 :                      image_cell = [r1, r2, r3]
     731      7241451 :                      ra = rvec + MATMUL(cell%hmat, REAL(image_cell, KIND=dp))
     732      1524516 :                      r2tmp = DOT_PRODUCT(ra, ra)
     733       451668 :                      IF (r2tmp <= rcut2) THEN
     734        47717 :                         r = SQRT(r2tmp)
     735        47717 :                         t1 = erfc(alpha*r)/r
     736        47717 :                         r_ewald = r_ewald + t1*ij_fac
     737              :                      END IF
     738              :                   END DO
     739              :                END DO
     740              :             END DO
     741              :             !
     742              :             ! G-space Contribution
     743              :             !
     744         2704 :             IF (analyt) THEN
     745          278 :                g_ewald = 0.0_dp
     746         3535 :                DO k1 = 0, gmax(1)
     747        88788 :                   DO k2 = -gmax(2), gmax(2)
     748      2516283 :                      DO k3 = -gmax(3), gmax(3)
     749      2427773 :                         IF (k1 == 0 .AND. k2 == 0 .AND. k3 == 0) CYCLE
     750      2427495 :                         fs = 2.0_dp; IF (k1 == 0) fs = 1.0_dp
     751      9709980 :                         g_index = [REAL(k1, KIND=dp), REAL(k2, KIND=dp), REAL(k3, KIND=dp)]
     752     12137475 :                         gvec = twopi*MATMUL(TRANSPOSE(cell%h_inv), g_index)
     753      9709980 :                         gsq = DOT_PRODUCT(gvec, gvec)
     754      2427495 :                         gsqi = fs/gsq
     755      2427495 :                         t1 = fac*gsqi*EXP(-galpha*gsq)
     756      9795511 :                         g_ewald = g_ewald + t1*COS(DOT_PRODUCT(gvec, rvec))
     757              :                      END DO
     758              :                   END DO
     759              :                END DO
     760              :             ELSE
     761         2426 :                g_ewald = Eval_Interp_Spl3_pbc(rvec, coeff)
     762              :             END IF
     763              :             !
     764              :             ! G-EWALD, R-EWALD
     765              :             !
     766         2704 :             g_ewald = r_ewald + fourpi*g_ewald
     767              :             !
     768              :             ! Self Contribution
     769              :             !
     770         2704 :             IF (iparticle1 == iparticle2) THEN
     771          390 :                g_ewald = g_ewald - 2.0_dp*alpha*oorootpi
     772              :             END IF
     773              :             !
     774         2704 :             IF (iparticle1 /= iparticle2) THEN
     775         2314 :                ra = rvec
     776         9256 :                r = NORM2(ra)
     777         2314 :                my_val = factor/r
     778              :             END IF
     779         6108 :             EwM(idim) = my_val - factor*g_ewald
     780              :          END DO ! iparticle2
     781              :       END DO ! iparticle1
     782              :       !NB sum over parallelized contributions of different nodes
     783        10944 :       CALL cp_para_env%sum(EwM)
     784          224 :       idim = 0
     785          972 :       DO iparticle2 = 1, SIZE(particle_set)
     786          748 :          ip2 = (iparticle2 - 1)*SIZE(radii)
     787          748 :          idimo = (iparticle2 - 1)
     788          748 :          idimo = idimo*(idimo + 1)/2
     789         3216 :          DO igauss2 = 1, SIZE(radii)
     790         2244 :             idim2 = ip2 + igauss2
     791         2244 :             rc2 = radii(igauss2)
     792         2244 :             rc22 = rc2*rc2
     793        19072 :             DO iparticle1 = 1, iparticle2
     794        16080 :                ip1 = (iparticle1 - 1)*SIZE(radii)
     795        16080 :                idim = idimo + iparticle1
     796        16080 :                istart_g = 1
     797        16080 :                IF (iparticle1 == iparticle2) istart_g = igauss2
     798        64320 :                DO igauss1 = istart_g, SIZE(radii)
     799        45996 :                   idim1 = ip1 + igauss1
     800        45996 :                   rc1 = radii(igauss1)
     801        45996 :                   rc12 = rc1*rc1
     802        45996 :                   M(idim1, idim2) = EwM(idim) - factor*ew_neut - factor*fac3*(rc12 + rc22)
     803        62076 :                   M(idim2, idim1) = M(idim1, idim2)
     804              :                END DO
     805              :             END DO
     806              :          END DO ! iparticle2
     807              :       END DO ! iparticle1
     808          224 :       DEALLOCATE (EwM)
     809          224 :       CALL timestop(handle)
     810          672 :    END SUBROUTINE ewald_ddapc_pot
     811              : 
     812              : ! **************************************************************************************************
     813              : !> \brief Evaluates the electrostatic potential due to a simple solvation model
     814              : !>      Spherical cavity in a dieletric medium
     815              : !> \param solvation_section ...
     816              : !> \param particle_set ...
     817              : !> \param M ...
     818              : !> \param radii ...
     819              : !> \par History
     820              : !>      08.2006 created [tlaino]
     821              : !> \author Teodoro Laino
     822              : ! **************************************************************************************************
     823           26 :    SUBROUTINE solvation_ddapc_pot(solvation_section, particle_set, M, radii)
     824              :       TYPE(section_vals_type), POINTER                   :: solvation_section
     825              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     826              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: M
     827              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: radii
     828              : 
     829              :       INTEGER :: i, idim, idim1, idim2, igauss1, igauss2, ip1, ip2, iparticle1, iparticle2, &
     830              :          istart_g, j, l, lmax, n_rep1, n_rep2, ndim, output_unit, weight
     831           26 :       INTEGER, DIMENSION(:), POINTER                     :: list
     832              :       LOGICAL                                            :: fixed_center
     833              :       REAL(KIND=dp)                                      :: center(3), eps_in, eps_out, factor, &
     834              :                                                             mass, mycos, r1, r2, Rs, rvec(3)
     835           26 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: pos, R0
     836           26 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: cost, LocP
     837              : 
     838           26 :       fixed_center = .FALSE.
     839           52 :       output_unit = cp_logger_get_default_io_unit()
     840           26 :       ndim = SIZE(particle_set)*SIZE(radii)
     841          104 :       ALLOCATE (M(ndim, ndim))
     842         1274 :       M = 0.0_dp
     843           26 :       eps_in = 1.0_dp
     844           26 :       CALL section_vals_val_get(solvation_section, "EPS_OUT", r_val=eps_out)
     845           26 :       CALL section_vals_val_get(solvation_section, "LMAX", i_val=lmax)
     846           26 :       CALL section_vals_val_get(solvation_section, "SPHERE%RADIUS", r_val=Rs)
     847           26 :       CALL section_vals_val_get(solvation_section, "SPHERE%CENTER%XYZ", n_rep_val=n_rep1)
     848           26 :       IF (n_rep1 /= 0) THEN
     849           24 :          CALL section_vals_val_get(solvation_section, "SPHERE%CENTER%XYZ", r_vals=R0)
     850           96 :          center = R0
     851              :       ELSE
     852              :          CALL section_vals_val_get(solvation_section, "SPHERE%CENTER%ATOM_LIST", &
     853            2 :                                    n_rep_val=n_rep2)
     854            2 :          IF (n_rep2 /= 0) THEN
     855            2 :             CALL section_vals_val_get(solvation_section, "SPHERE%CENTER%ATOM_LIST", i_vals=list)
     856            2 :             CALL section_vals_val_get(solvation_section, "SPHERE%CENTER%WEIGHT_TYPE", i_val=weight)
     857            2 :             ALLOCATE (R0(3))
     858              :             SELECT CASE (weight)
     859              :             CASE (weight_type_unit)
     860            8 :                R0 = 0.0_dp
     861            4 :                DO i = 1, SIZE(list)
     862           10 :                   R0 = R0 + particle_set(list(i))%r
     863              :                END DO
     864            8 :                R0 = R0/REAL(SIZE(list), KIND=dp)
     865              :             CASE (weight_type_mass)
     866            0 :                R0 = 0.0_dp
     867            0 :                mass = 0.0_dp
     868            0 :                DO i = 1, SIZE(list)
     869            0 :                   R0 = R0 + particle_set(list(i))%r*particle_set(list(i))%atomic_kind%mass
     870            0 :                   mass = mass + particle_set(list(i))%atomic_kind%mass
     871              :                END DO
     872            2 :                R0 = R0/mass
     873              :             END SELECT
     874            8 :             center = R0
     875            2 :             CALL section_vals_val_get(solvation_section, "SPHERE%CENTER%FIXED", l_val=fixed_center)
     876            4 :             IF (fixed_center) THEN
     877              :                CALL section_vals_val_set(solvation_section, "SPHERE%CENTER%XYZ", &
     878            2 :                                          r_vals_ptr=R0)
     879              :             ELSE
     880            0 :                DEALLOCATE (R0)
     881              :             END IF
     882              :          END IF
     883              :       END IF
     884           26 :       CPASSERT(n_rep1 /= 0 .OR. n_rep2 /= 0)
     885              :       ! Potential calculation
     886          104 :       ALLOCATE (LocP(0:lmax, SIZE(particle_set)))
     887           78 :       ALLOCATE (pos(SIZE(particle_set)))
     888          104 :       ALLOCATE (cost(SIZE(particle_set), SIZE(particle_set)))
     889              :       ! Determining the single atomic contribution to the dielectric dipole
     890           76 :       DO i = 1, SIZE(particle_set)
     891          200 :          rvec = particle_set(i)%r - center
     892          200 :          r2 = DOT_PRODUCT(rvec, rvec)
     893           50 :          r1 = SQRT(r2)
     894           50 :          IF (r1 >= Rs) THEN
     895            0 :             IF (output_unit > 0) THEN
     896            0 :                WRITE (output_unit, '(A,I6,A)') "Atom number :: ", i, " is out of the solvation sphere"
     897            0 :                WRITE (output_unit, '(2(A,F12.6))') "Distance from the center::", r1, " Radius of the sphere::", rs
     898              :             END IF
     899            0 :             CPABORT("Unable to evaluate electrostatic potential in solution")
     900              :          END IF
     901          250 :          LocP(:, i) = 0.0_dp
     902           50 :          IF (r1 /= 0.0_dp) THEN
     903          230 :             DO l = 0, lmax
     904              :                LocP(l, i) = (r1**l*REAL(l + 1, KIND=dp)*(eps_in - eps_out))/ &
     905          230 :                             (Rs**(2*l + 1)*eps_in*(REAL(l, KIND=dp)*eps_in + REAL(l + 1, KIND=dp)*eps_out))
     906              :             END DO
     907              :          ELSE
     908              :             ! limit for r->0
     909            4 :             LocP(0, i) = (eps_in - eps_out)/(Rs*eps_in*eps_out)
     910              :          END IF
     911           76 :          pos(i) = r1
     912              :       END DO
     913              :       ! Particle-Particle potential energy matrix
     914          198 :       cost = 0.0_dp
     915           76 :       DO i = 1, SIZE(particle_set)
     916          162 :          DO j = 1, i
     917           86 :             factor = 0.0_dp
     918           86 :             IF (pos(i)*pos(j) /= 0.0_dp) THEN
     919          296 :                mycos = DOT_PRODUCT(particle_set(i)%r - center, particle_set(j)%r - center)/(pos(i)*pos(j))
     920           74 :                IF (ABS(mycos) > 1.0_dp) mycos = SIGN(1.0_dp, mycos)
     921          370 :                DO l = 0, lmax
     922          370 :                   factor = factor + LocP(l, i)*pos(j)**l*legendre(mycos, l, 0)
     923              :                END DO
     924              :             ELSE
     925           12 :                factor = LocP(0, i)
     926              :             END IF
     927           86 :             cost(i, j) = factor
     928          136 :             cost(j, i) = factor
     929              :          END DO
     930              :       END DO
     931              :       ! Computes the full potential energy matrix
     932           26 :       idim = 0
     933           76 :       DO iparticle2 = 1, SIZE(particle_set)
     934           50 :          ip2 = (iparticle2 - 1)*SIZE(radii)
     935          226 :          DO igauss2 = 1, SIZE(radii)
     936          150 :             idim2 = ip2 + igauss2
     937          458 :             DO iparticle1 = 1, iparticle2
     938          258 :                ip1 = (iparticle1 - 1)*SIZE(radii)
     939          258 :                istart_g = 1
     940          258 :                IF (iparticle1 == iparticle2) istart_g = igauss2
     941         1032 :                DO igauss1 = istart_g, SIZE(radii)
     942          624 :                   idim1 = ip1 + igauss1
     943          624 :                   M(idim1, idim2) = cost(iparticle1, iparticle2)
     944          882 :                   M(idim2, idim1) = M(idim1, idim2)
     945              :                END DO
     946              :             END DO
     947              :          END DO
     948              :       END DO
     949           26 :       DEALLOCATE (cost)
     950           26 :       DEALLOCATE (pos)
     951           26 :       DEALLOCATE (LocP)
     952           52 :    END SUBROUTINE solvation_ddapc_pot
     953              : 
     954          296 : END MODULE cp_ddapc_methods
        

Generated by: LCOV version 2.0-1