LCOV - code coverage report
Current view: top level - src - qs_external_potential.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:9ccafda) Lines: 92.4 % 223 206
Test Date: 2026-10-15 00:55:44 Functions: 100.0 % 7 7

            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 Routines to handle an external electrostatic field
      10              : !>        The external field can be generic and is provided by user input
      11              : ! **************************************************************************************************
      12              : MODULE qs_external_potential
      13              :    USE atomic_kind_types,               ONLY: atomic_kind_type,&
      14              :                                               get_atomic_kind
      15              :    USE cell_types,                      ONLY: cell_type,&
      16              :                                               pbc
      17              :    USE cp_control_types,                ONLY: dft_control_type
      18              :    USE cp_log_handling,                 ONLY: cp_to_string
      19              :    USE cp_realspace_grid_cube,          ONLY: cp_cube_to_pw
      20              :    USE force_fields_util,               ONLY: get_generic_info
      21              :    USE fparser,                         ONLY: evalf,&
      22              :                                               evalfd,&
      23              :                                               finalizef,&
      24              :                                               initf,&
      25              :                                               parsef
      26              :    USE input_section_types,             ONLY: section_vals_get_subs_vals,&
      27              :                                               section_vals_type,&
      28              :                                               section_vals_val_get
      29              :    USE kinds,                           ONLY: default_path_length,&
      30              :                                               default_string_length,&
      31              :                                               dp,&
      32              :                                               int_8
      33              :    USE maxwell_solver_interface,        ONLY: maxwell_solver
      34              :    USE message_passing,                 ONLY: mp_comm_type
      35              :    USE particle_types,                  ONLY: particle_type
      36              :    USE pw_grid_types,                   ONLY: PW_MODE_LOCAL
      37              :    USE pw_methods,                      ONLY: pw_zero
      38              :    USE pw_types,                        ONLY: pw_r3d_rs_type
      39              :    USE qs_energy_types,                 ONLY: qs_energy_type
      40              :    USE qs_environment_types,            ONLY: get_qs_env,&
      41              :                                               qs_environment_type
      42              :    USE qs_force_types,                  ONLY: qs_force_type
      43              :    USE qs_kind_types,                   ONLY: get_qs_kind,&
      44              :                                               qs_kind_type
      45              :    USE string_utilities,                ONLY: compress
      46              : #include "./base/base_uses.f90"
      47              : 
      48              :    IMPLICIT NONE
      49              : 
      50              :    PRIVATE
      51              : 
      52              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_external_potential'
      53              : 
      54              : ! *** Public subroutines ***
      55              :    PUBLIC :: external_e_potential, &
      56              :              external_c_potential, &
      57              :              interpolate_external_potential
      58              : 
      59              : CONTAINS
      60              : 
      61              : ! **************************************************************************************************
      62              : !> \brief  Computes the external potential on the grid
      63              : !> \param qs_env ...
      64              : !> \date   12.2009
      65              : !> \author Teodoro Laino [tlaino]
      66              : ! **************************************************************************************************
      67        20384 :    SUBROUTINE external_e_potential(qs_env)
      68              : 
      69              :       TYPE(qs_environment_type), POINTER                 :: qs_env
      70              : 
      71              :       CHARACTER(len=*), PARAMETER :: routineN = 'external_e_potential'
      72              : 
      73              :       INTEGER                                            :: handle, i, j, k
      74              :       INTEGER(kind=int_8)                                :: npoints
      75              :       INTEGER, DIMENSION(2, 3)                           :: bo_global, bo_local
      76              :       REAL(kind=dp)                                      :: dvol, scaling_factor
      77        20384 :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:)           :: efunc, grid_p_i, grid_p_j, grid_p_k
      78        20384 :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :)        :: grid_p
      79              :       REAL(kind=dp), DIMENSION(3)                        :: dr
      80              :       TYPE(dft_control_type), POINTER                    :: dft_control
      81              :       TYPE(pw_r3d_rs_type), POINTER                      :: v_ee
      82              :       TYPE(section_vals_type), POINTER                   :: ext_pot_section, input
      83              : 
      84        20384 :       CALL timeset(routineN, handle)
      85        20384 :       CALL get_qs_env(qs_env, dft_control=dft_control)
      86        20384 :       IF (dft_control%apply_external_potential) THEN
      87           88 :          IF (dft_control%eval_external_potential) THEN
      88           16 :             CALL get_qs_env(qs_env, vee=v_ee)
      89           16 :             IF (dft_control%expot_control%maxwell_solver) THEN
      90            0 :                scaling_factor = dft_control%expot_control%scaling_factor
      91              :                CALL maxwell_solver(dft_control%maxwell_control, v_ee, &
      92              :                                    qs_env%sim_step, qs_env%sim_time, &
      93            0 :                                    scaling_factor)
      94            0 :                dft_control%eval_external_potential = .FALSE.
      95           16 :             ELSE IF (dft_control%expot_control%read_from_cube) THEN
      96            2 :                scaling_factor = dft_control%expot_control%scaling_factor
      97            2 :                CALL cp_cube_to_pw(v_ee, 'pot.cube', scaling_factor)
      98            2 :                dft_control%eval_external_potential = .FALSE.
      99              :             ELSE
     100           14 :                CALL get_qs_env(qs_env, input=input)
     101           14 :                ext_pot_section => section_vals_get_subs_vals(input, "DFT%EXTERNAL_POTENTIAL")
     102              : 
     103           56 :                dr = v_ee%pw_grid%dr
     104           14 :                dvol = v_ee%pw_grid%dvol
     105           14 :                CALL pw_zero(v_ee)
     106              : 
     107          140 :                bo_local = v_ee%pw_grid%bounds_local
     108          140 :                bo_global = v_ee%pw_grid%bounds
     109              : 
     110              :                npoints = INT(bo_local(2, 1) - bo_local(1, 1) + 1, kind=int_8)* &
     111              :                          INT(bo_local(2, 2) - bo_local(1, 2) + 1, kind=int_8)* &
     112           14 :                          INT(bo_local(2, 3) - bo_local(1, 3) + 1, kind=int_8)
     113           42 :                ALLOCATE (efunc(npoints))
     114           42 :                ALLOCATE (grid_p(3, npoints))
     115           42 :                ALLOCATE (grid_p_i(bo_local(1, 1):bo_local(2, 1)))
     116           42 :                ALLOCATE (grid_p_j(bo_local(1, 2):bo_local(2, 2)))
     117           42 :                ALLOCATE (grid_p_k(bo_local(1, 3):bo_local(2, 3)))
     118              : 
     119          378 :                DO i = bo_local(1, 1), bo_local(2, 1)
     120          378 :                   grid_p_i(i) = (i - bo_global(1, 1))*dr(1)
     121              :                END DO
     122          742 :                DO j = bo_local(1, 2), bo_local(2, 2)
     123          742 :                   grid_p_j(j) = (j - bo_global(1, 2))*dr(2)
     124              :                END DO
     125          742 :                DO k = bo_local(1, 3), bo_local(2, 3)
     126          742 :                   grid_p_k(k) = (k - bo_global(1, 3))*dr(3)
     127              :                END DO
     128              : 
     129              :                npoints = 0
     130          742 :                DO k = bo_local(1, 3), bo_local(2, 3)
     131        38934 :                   DO j = bo_local(1, 2), bo_local(2, 2)
     132      1047704 :                      DO i = bo_local(1, 1), bo_local(2, 1)
     133      1008784 :                         npoints = npoints + 1
     134      1008784 :                         grid_p(1, npoints) = grid_p_i(i)
     135      1008784 :                         grid_p(2, npoints) = grid_p_j(j)
     136      1046976 :                         grid_p(3, npoints) = grid_p_k(k)
     137              :                      END DO
     138              :                   END DO
     139              :                END DO
     140              : 
     141           14 :                DEALLOCATE (grid_p_i, grid_p_j, grid_p_k)
     142              : 
     143           14 :                CALL get_external_potential(grid_p, ext_pot_section, func=efunc)
     144              : 
     145           14 :                npoints = 0
     146          742 :                DO k = bo_local(1, 3), bo_local(2, 3)
     147        38934 :                   DO j = bo_local(1, 2), bo_local(2, 2)
     148      1047704 :                      DO i = bo_local(1, 1), bo_local(2, 1)
     149      1008784 :                         npoints = npoints + 1
     150      1046976 :                         v_ee%array(i, j, k) = v_ee%array(i, j, k) + efunc(npoints)
     151              :                      END DO
     152              :                   END DO
     153              :                END DO
     154              : 
     155           14 :                DEALLOCATE (grid_p, efunc)
     156              : 
     157           14 :                dft_control%eval_external_potential = .FALSE.
     158              :             END IF
     159              :          END IF
     160              :       END IF
     161        20384 :       CALL timestop(handle)
     162        20384 :    END SUBROUTINE external_e_potential
     163              : 
     164              : ! **************************************************************************************************
     165              : !> \brief  Computes the force and the energy due to the external potential on the cores
     166              : !> \param qs_env ...
     167              : !> \param calculate_forces ...
     168              : !> \date   12.2009
     169              : !> \author Teodoro Laino [tlaino]
     170              : ! **************************************************************************************************
     171        17378 :    SUBROUTINE external_c_potential(qs_env, calculate_forces)
     172              : 
     173              :       TYPE(qs_environment_type), POINTER                 :: qs_env
     174              :       LOGICAL, OPTIONAL                                  :: calculate_forces
     175              : 
     176              :       CHARACTER(len=*), PARAMETER :: routineN = 'external_c_potential'
     177              : 
     178              :       INTEGER                                            :: atom_a, handle, iatom, ikind, natom, &
     179              :                                                             nkind, nparticles
     180        17378 :       INTEGER, DIMENSION(:), POINTER                     :: list
     181              :       LOGICAL                                            :: my_force, pot_on_grid
     182              :       REAL(KIND=dp)                                      :: ee_core_ener, zeff
     183        17378 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: efunc
     184        17378 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: dfunc, r
     185        17378 :       TYPE(atomic_kind_type), DIMENSION(:), POINTER      :: atomic_kind_set
     186              :       TYPE(cell_type), POINTER                           :: cell
     187              :       TYPE(dft_control_type), POINTER                    :: dft_control
     188        17378 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     189              :       TYPE(pw_r3d_rs_type), POINTER                      :: v_ee
     190              :       TYPE(qs_energy_type), POINTER                      :: energy
     191        17378 :       TYPE(qs_force_type), DIMENSION(:), POINTER         :: force
     192        17378 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     193              :       TYPE(section_vals_type), POINTER                   :: ext_pot_section, input
     194              : 
     195        17378 :       CALL timeset(routineN, handle)
     196        17378 :       NULLIFY (dft_control)
     197              : 
     198              :       CALL get_qs_env(qs_env=qs_env, &
     199              :                       atomic_kind_set=atomic_kind_set, &
     200              :                       qs_kind_set=qs_kind_set, &
     201              :                       energy=energy, &
     202              :                       particle_set=particle_set, &
     203              :                       input=input, &
     204              :                       cell=cell, &
     205        17378 :                       dft_control=dft_control)
     206              : 
     207        17378 :       IF (dft_control%apply_external_potential .AND. .NOT. qs_env%mimic) THEN
     208              :          !ensure that external potential is loaded to grid
     209           82 :          IF (dft_control%eval_external_potential) THEN
     210            0 :             CALL external_e_potential(qs_env)
     211              :          END IF
     212           82 :          my_force = .FALSE.
     213           82 :          IF (PRESENT(calculate_forces)) my_force = calculate_forces
     214           82 :          ee_core_ener = 0.0_dp
     215           82 :          nkind = SIZE(atomic_kind_set)
     216              : 
     217              :          !check if external potential on grid has been loaded from a file instead of giving a function
     218           82 :          IF (dft_control%expot_control%read_from_cube .OR. &
     219              :              dft_control%expot_control%maxwell_solver) THEN
     220           24 :             CALL get_qs_env(qs_env, vee=v_ee)
     221           24 :             pot_on_grid = .TRUE.
     222              :          ELSE
     223           58 :             pot_on_grid = .FALSE.
     224           58 :             ext_pot_section => section_vals_get_subs_vals(input, "DFT%EXTERNAL_POTENTIAL")
     225              :          END IF
     226              : 
     227           82 :          nparticles = 0
     228          242 :          DO ikind = 1, SIZE(atomic_kind_set)
     229          160 :             CALL get_atomic_kind(atomic_kind_set(ikind), natom=natom)
     230          242 :             nparticles = nparticles + MAX(natom, 0)
     231              :          END DO
     232              : 
     233          246 :          ALLOCATE (efunc(nparticles))
     234          328 :          ALLOCATE (dfunc(3, nparticles), r(3, nparticles))
     235              : 
     236           82 :          nparticles = 0
     237          242 :          DO ikind = 1, SIZE(atomic_kind_set)
     238          160 :             CALL get_atomic_kind(atomic_kind_set(ikind), atom_list=list, natom=natom)
     239              : 
     240          480 :             DO iatom = 1, natom
     241          238 :                atom_a = list(iatom)
     242          238 :                nparticles = nparticles + 1
     243              :                !pbc returns r(i) in range [-cell%hmat(i,i)/2, cell%hmat(i,i)/2]
     244              :                !for periodic dimensions (assuming the cell is orthorombic).
     245              :                !This is not consistent with the potential on grid, where r(i) is
     246              :                !in range [0, cell%hmat(i,i)]
     247              :                !Use new pbc function with switch positive_range=.TRUE.
     248          398 :                r(:, nparticles) = pbc(particle_set(atom_a)%r(:), cell, positive_range=.TRUE.)
     249              :             END DO
     250              :          END DO
     251              : 
     252              :          !if potential is on grid, interpolate the value at r,
     253              :          !otherwise evaluate the given function
     254           82 :          IF (pot_on_grid) THEN
     255           96 :             DO iatom = 1, nparticles
     256              :                CALL interpolate_external_potential(r(:, iatom), v_ee, func=efunc(iatom), &
     257           96 :                                                    dfunc=dfunc(:, iatom), calc_derivatives=my_force)
     258              :             END DO
     259              :          ELSE
     260           58 :             CALL get_external_potential(r, ext_pot_section, func=efunc, dfunc=dfunc, calc_derivatives=my_force)
     261              :          END IF
     262              : 
     263           82 :          IF (my_force) THEN
     264           40 :             CALL get_qs_env(qs_env=qs_env, force=force)
     265              :          END IF
     266              : 
     267           82 :          nparticles = 0
     268          242 :          DO ikind = 1, SIZE(atomic_kind_set)
     269          160 :             CALL get_atomic_kind(atomic_kind_set(ikind), natom=natom)
     270          160 :             CALL get_qs_kind(qs_kind_set(ikind), zeff=zeff)
     271              : 
     272          480 :             DO iatom = 1, natom
     273          238 :                nparticles = nparticles + 1
     274              : 
     275          238 :                ee_core_ener = ee_core_ener + zeff*efunc(nparticles)
     276          398 :                IF (my_force) THEN
     277          464 :                   force(ikind)%eev(1:3, iatom) = dfunc(1:3, nparticles)*zeff
     278              :                END IF
     279              :             END DO
     280              :          END DO
     281           82 :          energy%ee_core = ee_core_ener
     282              : 
     283           82 :          DEALLOCATE (dfunc, r)
     284           82 :          DEALLOCATE (efunc)
     285              :       END IF
     286        17378 :       CALL timestop(handle)
     287        34756 :    END SUBROUTINE external_c_potential
     288              : 
     289              : ! **************************************************************************************************
     290              : !> \brief  Low level function for computing the potential and the derivatives
     291              : !> \param r                position in realspace for each grid-point
     292              : !> \param ext_pot_section ...
     293              : !> \param func             external potential at r
     294              : !> \param dfunc            derivative of the external potential at r
     295              : !> \param calc_derivatives Whether to calculate dfunc
     296              : !> \date   12.2009
     297              : !> \par History
     298              : !>      12.2009            created [tlaino]
     299              : !>      11.2014            reading external cube file added [Juha Ritala & Matt Watkins]
     300              : !> \author Teodoro Laino [tlaino]
     301              : ! **************************************************************************************************
     302           72 :    SUBROUTINE get_external_potential(r, ext_pot_section, func, dfunc, calc_derivatives)
     303              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: r
     304              :       TYPE(section_vals_type), POINTER                   :: ext_pot_section
     305              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT), OPTIONAL :: func
     306              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT), &
     307              :          OPTIONAL                                        :: dfunc
     308              :       LOGICAL, INTENT(IN), OPTIONAL                      :: calc_derivatives
     309              : 
     310              :       CHARACTER(len=*), PARAMETER :: routineN = 'get_external_potential'
     311              : 
     312              :       CHARACTER(LEN=default_path_length)                 :: coupling_function
     313              :       CHARACTER(LEN=default_string_length)               :: def_error, this_error
     314              :       CHARACTER(LEN=default_string_length), &
     315           72 :          DIMENSION(:), POINTER                           :: my_par
     316              :       INTEGER                                            :: handle, j
     317              :       INTEGER(kind=int_8)                                :: ipoint, npoints
     318              :       LOGICAL                                            :: check, my_force
     319              :       REAL(KIND=dp)                                      :: dedf, dx, err, lerr
     320           72 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: my_val
     321              : 
     322           72 :       CALL timeset(routineN, handle)
     323           72 :       NULLIFY (my_par, my_val)
     324           72 :       my_force = .FALSE.
     325           72 :       IF (PRESENT(calc_derivatives)) my_force = calc_derivatives
     326           72 :       check = PRESENT(dfunc) .EQV. PRESENT(calc_derivatives)
     327           72 :       CPASSERT(check)
     328           72 :       CALL section_vals_val_get(ext_pot_section, "DX", r_val=dx)
     329           72 :       CALL section_vals_val_get(ext_pot_section, "ERROR_LIMIT", r_val=lerr)
     330              :       CALL get_generic_info(ext_pot_section, "FUNCTION", coupling_function, my_par, my_val, &
     331          288 :                             input_variables=["X", "Y", "Z"], i_rep_sec=1)
     332           72 :       CALL initf(1)
     333           72 :       CALL parsef(1, TRIM(coupling_function), my_par)
     334              : 
     335           72 :       npoints = SIZE(r, 2, kind=int_8)
     336              : 
     337      1009022 :       DO ipoint = 1, npoints
     338      1008950 :          my_val(1) = r(1, ipoint)
     339      1008950 :          my_val(2) = r(2, ipoint)
     340      1008950 :          my_val(3) = r(3, ipoint)
     341              : 
     342      1008950 :          IF (PRESENT(func)) func(ipoint) = evalf(1, my_val)
     343      1009022 :          IF (my_force) THEN
     344          320 :             DO j = 1, 3
     345          240 :                dedf = evalfd(1, j, my_val, dx, err)
     346          240 :                IF (ABS(err) > lerr) THEN
     347            0 :                   WRITE (this_error, "(A,G12.6,A)") "(", err, ")"
     348            0 :                   WRITE (def_error, "(A,G12.6,A)") "(", lerr, ")"
     349            0 :                   CALL compress(this_error, .TRUE.)
     350            0 :                   CALL compress(def_error, .TRUE.)
     351              :                   CALL cp_warn(__LOCATION__, &
     352              :                                'ASSERTION (cond) failed at line '//cp_to_string(__LINE__)// &
     353              :                                ' Error '//TRIM(this_error)//' in computing numerical derivatives larger then'// &
     354            0 :                                TRIM(def_error)//' .')
     355              :                END IF
     356          320 :                dfunc(j, ipoint) = dedf
     357              :             END DO
     358              :          END IF
     359              :       END DO
     360           72 :       DEALLOCATE (my_par)
     361           72 :       DEALLOCATE (my_val)
     362           72 :       CALL finalizef()
     363           72 :       CALL timestop(handle)
     364           72 :    END SUBROUTINE get_external_potential
     365              : 
     366              : ! **************************************************************************************************
     367              : !> \brief                  subroutine that interpolates the value of the external
     368              : !>                         potential at position r based on the values on the realspace grid
     369              : !> \param r                 ...
     370              : !> \param grid             external potential pw grid, vee
     371              : !> \param func             value of vee at r
     372              : !> \param dfunc            derivatives of vee at r
     373              : !> \param calc_derivatives calc dfunc
     374              : ! **************************************************************************************************
     375           76 :    SUBROUTINE interpolate_external_potential(r, grid, func, dfunc, calc_derivatives)
     376              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: r
     377              :       TYPE(pw_r3d_rs_type), POINTER                      :: grid
     378              :       REAL(KIND=dp), INTENT(OUT), OPTIONAL               :: func, dfunc(3)
     379              :       LOGICAL, INTENT(IN), OPTIONAL                      :: calc_derivatives
     380              : 
     381              :       CHARACTER(len=*), PARAMETER :: routineN = 'interpolate_external_potential'
     382              : 
     383              :       INTEGER                                            :: buffer_i, buffer_j, buffer_k, &
     384              :                                                             data_source, fd_extra_point, handle, &
     385              :                                                             i, i_pbc, ip, j, j_pbc, k, k_pbc, &
     386              :                                                             my_rank, num_pe, tag
     387              :       INTEGER, DIMENSION(3)                              :: lbounds, lbounds_local, lower_inds, &
     388              :                                                             ubounds, ubounds_local, upper_inds
     389              :       LOGICAL                                            :: check, my_force
     390           76 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: bcast_buffer
     391              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :)     :: grid_buffer
     392           76 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :, :)  :: dgrid
     393              :       REAL(KIND=dp), DIMENSION(3)                        :: dr, subgrid_origin
     394              :       TYPE(mp_comm_type)                                 :: gid
     395              : 
     396           76 :       CALL timeset(routineN, handle)
     397           76 :       my_force = .FALSE.
     398           76 :       IF (PRESENT(calc_derivatives)) my_force = calc_derivatives
     399           76 :       check = PRESENT(dfunc) .EQV. PRESENT(calc_derivatives)
     400           76 :       CPASSERT(check)
     401              : 
     402           76 :       IF (my_force) THEN
     403           36 :          ALLOCATE (grid_buffer(0:3, 0:3, 0:3))
     404           36 :          ALLOCATE (bcast_buffer(0:3))
     405           36 :          ALLOCATE (dgrid(1:2, 1:2, 1:2, 3))
     406           36 :          fd_extra_point = 1
     407              :       ELSE
     408           40 :          ALLOCATE (grid_buffer(1:2, 1:2, 1:2))
     409           40 :          ALLOCATE (bcast_buffer(1:2))
     410           40 :          fd_extra_point = 0
     411              :       END IF
     412              : 
     413              :       ! The values of external potential on grid are distributed among the
     414              :       ! processes, so first we have to gather them up
     415           76 :       gid = grid%pw_grid%para%group
     416           76 :       my_rank = grid%pw_grid%para%group%mepos
     417           76 :       num_pe = grid%pw_grid%para%group%num_pe
     418           76 :       tag = 1
     419              : 
     420          304 :       dr = grid%pw_grid%dr
     421          304 :       lbounds = grid%pw_grid%bounds(1, :)
     422          304 :       ubounds = grid%pw_grid%bounds(2, :)
     423          304 :       lbounds_local = grid%pw_grid%bounds_local(1, :)
     424          304 :       ubounds_local = grid%pw_grid%bounds_local(2, :)
     425              : 
     426              :       ! Determine the indices of grid points that are needed
     427          304 :       lower_inds = lbounds + FLOOR(r/dr) - fd_extra_point
     428          304 :       upper_inds = lower_inds + 1 + 2*fd_extra_point
     429              : 
     430          300 :       DO i = lower_inds(1), upper_inds(1)
     431              :          ! If index is out of global bounds, assume periodic boundary conditions
     432          224 :          i_pbc = pbc_index(i, lbounds(1), ubounds(1))
     433          224 :          buffer_i = i - lower_inds(1) + 1 - fd_extra_point
     434         1036 :          DO j = lower_inds(2), upper_inds(2)
     435          736 :             j_pbc = pbc_index(j, lbounds(2), ubounds(2))
     436          736 :             buffer_j = j - lower_inds(2) + 1 - fd_extra_point
     437              : 
     438              :             ! Find the process that has the data for indices i_pbc and j_pbc
     439              :             ! and store the data to bcast_buffer. Assuming that each process has full z data
     440          960 :             IF (grid%pw_grid%para%mode /= PW_MODE_LOCAL) THEN
     441         1200 :                DO ip = 0, num_pe - 1
     442              :                   IF (grid%pw_grid%para%bo(1, 1, ip, 1) <= i_pbc - lbounds(1) + 1 .AND. &
     443              :                       grid%pw_grid%para%bo(2, 1, ip, 1) >= i_pbc - lbounds(1) + 1 .AND. &
     444         1200 :                       grid%pw_grid%para%bo(1, 2, ip, 1) <= j_pbc - lbounds(2) + 1 .AND. &
     445            0 :                       grid%pw_grid%para%bo(2, 2, ip, 1) >= j_pbc - lbounds(2) + 1) THEN
     446          736 :                      data_source = ip
     447          736 :                      EXIT
     448              :                   END IF
     449              :                END DO
     450          736 :                IF (my_rank == data_source) THEN
     451          368 :                   IF (lower_inds(3) >= lbounds(3) .AND. upper_inds(3) <= ubounds(3)) THEN
     452              :                      bcast_buffer(:) = &
     453         1680 :                         grid%array(i_pbc, j_pbc, lower_inds(3):upper_inds(3))
     454              :                   ELSE
     455            0 :                      DO k = lower_inds(3), upper_inds(3)
     456            0 :                         k_pbc = pbc_index(k, lbounds(3), ubounds(3))
     457            0 :                         buffer_k = k - lower_inds(3) + 1 - fd_extra_point
     458              :                         bcast_buffer(buffer_k) = &
     459            0 :                            grid%array(i_pbc, j_pbc, k_pbc)
     460              :                      END DO
     461              :                   END IF
     462              :                END IF
     463              :                ! data_source sends data to everyone
     464          736 :                CALL gid%bcast(bcast_buffer, data_source)
     465         3360 :                grid_buffer(buffer_i, buffer_j, :) = bcast_buffer
     466              :             ELSE
     467            0 :                grid_buffer(buffer_i, buffer_j, :) = grid%array(i_pbc, j_pbc, lower_inds(3):upper_inds(3))
     468              :             END IF
     469              :          END DO
     470              :       END DO
     471              : 
     472              :       ! Now that all the processes have local external potential data around r,
     473              :       ! interpolate the value at r
     474          304 :       subgrid_origin = (lower_inds - lbounds + fd_extra_point)*dr
     475           76 :       func = trilinear_interpolation(r, grid_buffer(1:2, 1:2, 1:2), subgrid_origin, dr)
     476              : 
     477              :       ! If the derivative of the potential is needed, approximate the derivative at grid
     478              :       ! points using finite differences, and then interpolate the value at r
     479           76 :       IF (my_force) THEN
     480           36 :          CALL d_finite_difference(grid_buffer, dr, dgrid)
     481          144 :          DO i = 1, 3
     482          144 :             dfunc(i) = trilinear_interpolation(r, dgrid(:, :, :, i), subgrid_origin, dr)
     483              :          END DO
     484           36 :          DEALLOCATE (dgrid)
     485              :       END IF
     486              : 
     487           76 :       DEALLOCATE (grid_buffer)
     488           76 :       CALL timestop(handle)
     489          152 :    END SUBROUTINE interpolate_external_potential
     490              : 
     491              : ! **************************************************************************************************
     492              : !> \brief       subroutine that uses finite differences to approximate the partial
     493              : !>              derivatives of the potential based on the given values on grid
     494              : !> \param grid  tiny bit of external potential vee
     495              : !> \param dr    step size for finite difference
     496              : !> \param dgrid derivatives of grid
     497              : ! **************************************************************************************************
     498           36 :    PURE SUBROUTINE d_finite_difference(grid, dr, dgrid)
     499              :       REAL(KIND=dp), DIMENSION(0:, 0:, 0:), INTENT(IN)   :: grid
     500              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: dr
     501              :       REAL(KIND=dp), DIMENSION(1:, 1:, 1:, :), &
     502              :          INTENT(OUT)                                     :: dgrid
     503              : 
     504              :       INTEGER                                            :: i, j, k
     505              : 
     506          108 :       DO i = 1, SIZE(dgrid, 1)
     507          252 :          DO j = 1, SIZE(dgrid, 2)
     508          504 :             DO k = 1, SIZE(dgrid, 3)
     509          288 :                dgrid(i, j, k, 1) = 0.5_dp*(grid(i + 1, j, k) - grid(i - 1, j, k))/dr(1)
     510          288 :                dgrid(i, j, k, 2) = 0.5_dp*(grid(i, j + 1, k) - grid(i, j - 1, k))/dr(2)
     511          432 :                dgrid(i, j, k, 3) = 0.5_dp*(grid(i, j, k + 1) - grid(i, j, k - 1))/dr(3)
     512              :             END DO
     513              :          END DO
     514              :       END DO
     515           36 :    END SUBROUTINE d_finite_difference
     516              : 
     517              : ! **************************************************************************************************
     518              : !> \brief             trilinear interpolation function that interpolates value at r based
     519              : !>                    on 2x2x2 grid points around r in subgrid
     520              : !> \param r           where to interpolate to
     521              : !> \param subgrid     part of external potential on a grid
     522              : !> \param origin      center of grid
     523              : !> \param dr          step size
     524              : !> \return interpolated value of external potential
     525              : ! **************************************************************************************************
     526          184 :    PURE FUNCTION trilinear_interpolation(r, subgrid, origin, dr) RESULT(value_at_r)
     527              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: r
     528              :       REAL(KIND=dp), DIMENSION(:, :, :), INTENT(IN)      :: subgrid
     529              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: origin, dr
     530              :       REAL(KIND=dp)                                      :: value_at_r
     531              : 
     532              :       REAL(KIND=dp), DIMENSION(3)                        :: norm_r, norm_r_rev
     533              : 
     534          736 :       norm_r = (r - origin)/dr
     535          736 :       norm_r_rev = 1 - norm_r
     536              :       value_at_r = subgrid(1, 1, 1)*PRODUCT(norm_r_rev) + &
     537              :                    subgrid(2, 1, 1)*norm_r(1)*norm_r_rev(2)*norm_r_rev(3) + &
     538              :                    subgrid(1, 2, 1)*norm_r_rev(1)*norm_r(2)*norm_r_rev(3) + &
     539              :                    subgrid(1, 1, 2)*norm_r_rev(1)*norm_r_rev(2)*norm_r(3) + &
     540              :                    subgrid(1, 2, 2)*norm_r_rev(1)*norm_r(2)*norm_r(3) + &
     541              :                    subgrid(2, 1, 2)*norm_r(1)*norm_r_rev(2)*norm_r(3) + &
     542              :                    subgrid(2, 2, 1)*norm_r(1)*norm_r(2)*norm_r_rev(3) + &
     543         1472 :                    subgrid(2, 2, 2)*PRODUCT(norm_r)
     544          184 :    END FUNCTION trilinear_interpolation
     545              : 
     546              : ! **************************************************************************************************
     547              : !> \brief          get a correct value for possible out of bounds index using periodic
     548              : !>                  boundary conditions
     549              : !> \param i ...
     550              : !> \param lowbound ...
     551              : !> \param upbound ...
     552              : !> \return ...
     553              : ! **************************************************************************************************
     554          960 :    ELEMENTAL FUNCTION pbc_index(i, lowbound, upbound)
     555              :       INTEGER, INTENT(IN)                                :: i, lowbound, upbound
     556              :       INTEGER                                            :: pbc_index
     557              : 
     558          960 :       IF (i < lowbound) THEN
     559            0 :          pbc_index = upbound + i - lowbound + 1
     560          960 :       ELSE IF (i > upbound) THEN
     561            0 :          pbc_index = lowbound + i - upbound - 1
     562              :       ELSE
     563              :          pbc_index = i
     564              :       END IF
     565          960 :    END FUNCTION pbc_index
     566              : 
     567              : END MODULE qs_external_potential
        

Generated by: LCOV version 2.0-1