LCOV - code coverage report
Current view: top level - src - gw_ri_rs_utils.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:92574dc) Lines: 90.1 % 111 100
Test Date: 2026-09-24 01:27:39 Functions: 100.0 % 5 5

            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 Common setup operations used by the periodic and non-periodic GW RI-RS implementations.
      10              : ! **************************************************************************************************
      11              : MODULE gw_ri_rs_utils
      12              :    USE atomic_kind_types,               ONLY: get_atomic_kind_set
      13              :    USE basis_set_types,                 ONLY: gto_basis_set_type
      14              :    USE cell_types,                      ONLY: cell_type,&
      15              :                                               pbc
      16              :    USE kinds,                           ONLY: dp
      17              :    USE orbital_pointers,                ONLY: indco,&
      18              :                                               ncoset
      19              :    USE particle_types,                  ONLY: particle_type
      20              :    USE physcon,                         ONLY: angstrom
      21              :    USE post_scf_bandstructure_types,    ONLY: post_scf_bandstructure_type
      22              :    USE qs_kind_types,                   ONLY: get_qs_kind,&
      23              :                                               qs_kind_type
      24              : #include "./base/base_uses.f90"
      25              : 
      26              :    IMPLICIT NONE
      27              :    PRIVATE
      28              : 
      29              :    PUBLIC :: evaluate_ao_basis_on_points, evaluate_ao_on_points, filter_grid_to_voronoi, &
      30              :              get_rirs_cluster_atoms, precompute_ri_rs_radii
      31              : 
      32              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'gw_ri_rs_utils'
      33              : 
      34              : CONTAINS
      35              : 
      36              : ! **************************************************************************************************
      37              : !> \brief Compute per-atom AO and RI basis radii from the most diffuse Gaussian
      38              : !>        primitive in the AO ("ORB") and RI auxiliary ("RI_AUX") basis sets.
      39              : !>        Stores results in bs_env%ri_rs%radius_ao_per_atom(:) and
      40              : !>        bs_env%ri_rs%radius_ri_per_atom(:) and prints a per-atom table.
      41              : !>        Radius: r_kind = sqrt(-log(eps)/alpha_min_kind), with eps = eps_filter.
      42              : !> \param bs_env Band-structure environment containing GW parameters.
      43              : ! **************************************************************************************************
      44           48 :    SUBROUTINE precompute_ri_rs_radii(bs_env)
      45              :       TYPE(post_scf_bandstructure_type), POINTER         :: bs_env
      46              : 
      47              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'precompute_ri_rs_radii'
      48              :       REAL(KIND=dp), PARAMETER :: min_exponent_for_radius = 1.0E-3_dp
      49              : 
      50              :       INTEGER                                            :: handle, i, iatom, ikind, j, natom, nkind
      51           48 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: kind_of
      52              :       REAL(KIND=dp)                                      :: eps
      53           48 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: alpha_min_ao_kind, alpha_min_ri_kind
      54           48 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: zet_ao, zet_ri
      55              : 
      56           48 :       CALL timeset(routineN, handle)
      57              : 
      58           48 :       CPASSERT(ASSOCIATED(bs_env%ri_rs%atomic_kind_set))
      59           48 :       nkind = SIZE(bs_env%ri_rs%atomic_kind_set)
      60           48 :       natom = bs_env%n_atom
      61           48 :       eps = bs_env%eps_filter
      62              : 
      63          192 :       ALLOCATE (alpha_min_ao_kind(nkind), alpha_min_ri_kind(nkind))
      64          118 :       alpha_min_ao_kind = HUGE(1.0_dp)
      65          118 :       alpha_min_ri_kind = HUGE(1.0_dp)
      66              : 
      67          118 :       DO ikind = 1, nkind
      68           70 :          zet_ao => bs_env%basis_set_AO(ikind)%gto_basis_set%zet
      69           70 :          zet_ri => bs_env%basis_set_RI(ikind)%gto_basis_set%zet
      70          236 :          DO i = 1, SIZE(zet_ao, 1)
      71          546 :             DO j = 1, SIZE(zet_ao, 2)
      72          476 :                IF (zet_ao(i, j) > min_exponent_for_radius) THEN
      73          310 :                   alpha_min_ao_kind(ikind) = MIN(alpha_min_ao_kind(ikind), zet_ao(i, j))
      74              :                END IF
      75              :             END DO
      76              :          END DO
      77          188 :          DO i = 1, SIZE(zet_ri, 1)
      78          674 :             DO j = 1, SIZE(zet_ri, 2)
      79          604 :                IF (zet_ri(i, j) > min_exponent_for_radius) THEN
      80          534 :                   alpha_min_ri_kind(ikind) = MIN(alpha_min_ri_kind(ikind), zet_ri(i, j))
      81              :                END IF
      82              :             END DO
      83              :          END DO
      84              :       END DO
      85              : 
      86           48 :       CALL get_atomic_kind_set(atomic_kind_set=bs_env%ri_rs%atomic_kind_set, kind_of=kind_of)
      87              : 
      88          144 :       ALLOCATE (bs_env%ri_rs%radius_ao_per_atom(natom))
      89           96 :       ALLOCATE (bs_env%ri_rs%radius_ri_per_atom(natom))
      90          166 :       DO iatom = 1, natom
      91          118 :          ikind = kind_of(iatom)
      92          118 :          bs_env%ri_rs%radius_ao_per_atom(iatom) = SQRT(-LOG(eps)/alpha_min_ao_kind(ikind))
      93          166 :          bs_env%ri_rs%radius_ri_per_atom(iatom) = SQRT(-LOG(eps)/alpha_min_ri_kind(ikind))
      94              :       END DO
      95              : 
      96           48 :       IF (bs_env%unit_nr > 0) THEN
      97           24 :          WRITE (bs_env%unit_nr, '(T2,A)') 'RI-RS basis radii (Å):'
      98           24 :          WRITE (bs_env%unit_nr, '(T4,A6,2X,A4,2A14)') 'Kind', 'Elem', 'r_AO (Å)', 'r_RI (Å)'
      99           59 :          DO ikind = 1, nkind
     100              :             WRITE (bs_env%unit_nr, '(T4,I6,2X,A4,2F14.4)') &
     101           35 :                ikind, &
     102           35 :                bs_env%ri_rs%atomic_kind_set(ikind)%element_symbol, &
     103           35 :                SQRT(-LOG(eps)/alpha_min_ao_kind(ikind))*angstrom, &
     104           94 :                SQRT(-LOG(eps)/alpha_min_ri_kind(ikind))*angstrom
     105              :          END DO
     106           24 :          WRITE (bs_env%unit_nr, '(A)') ' '
     107              :       END IF
     108              : 
     109           48 :       DEALLOCATE (alpha_min_ao_kind, alpha_min_ri_kind, kind_of)
     110              : 
     111           48 :       CALL timestop(handle)
     112              : 
     113           48 :    END SUBROUTINE precompute_ri_rs_radii
     114              : 
     115              : !> \brief Retain source-grid points inside the Voronoi volume of one atom.
     116              : !> \param points Source-grid coordinates on entry and Voronoi-filtered coordinates on return.
     117              : !> \param icenter_atom Atom whose Voronoi volume is retained.
     118              : !> \param particle_set Molecular atom positions.
     119              : !> \param mask Optional membership mask; if present, leave points unchanged.
     120              : !> \param atom_indices Optional subset of nuclei defining the Voronoi partition.
     121              : ! **************************************************************************************************
     122           30 :    SUBROUTINE filter_grid_to_voronoi(points, icenter_atom, particle_set, mask, atom_indices)
     123              :       REAL(KIND=dp), ALLOCATABLE, INTENT(INOUT)          :: points(:, :)
     124              :       INTEGER, INTENT(IN)                                :: icenter_atom
     125              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     126              :       LOGICAL, INTENT(OUT), OPTIONAL                     :: mask(:)
     127              :       INTEGER, INTENT(IN), OPTIONAL                      :: atom_indices(:)
     128              : 
     129              :       INTEGER                                            :: iatom, iatom_index, ipoint, n_atoms, &
     130              :                                                             n_keep
     131           30 :       LOGICAL, ALLOCATABLE                               :: keep(:)
     132              :       REAL(KIND=dp)                                      :: displacement(3), distance2, max_radius2, &
     133              :                                                             tolerance
     134           30 :       REAL(KIND=dp), ALLOCATABLE                         :: filtered_points(:, :)
     135              : 
     136         5055 :       ALLOCATE (keep(SIZE(points, 2)), SOURCE=.TRUE.)
     137        19890 :       max_radius2 = MAXVAL(SUM(points**2, DIM=1))
     138           30 :       n_atoms = SIZE(particle_set)
     139           30 :       IF (PRESENT(atom_indices)) n_atoms = SIZE(atom_indices)
     140          120 :       DO iatom_index = 1, n_atoms
     141           90 :          IF (PRESENT(atom_indices)) THEN
     142            0 :             iatom = atom_indices(iatom_index)
     143            0 :             CPASSERT(iatom >= 1 .AND. iatom <= SIZE(particle_set))
     144              :          ELSE
     145              :             iatom = iatom_index
     146              :          END IF
     147           90 :          IF (iatom == icenter_atom) CYCLE
     148          240 :          displacement(:) = particle_set(iatom)%r - particle_set(icenter_atom)%r
     149          240 :          distance2 = SUM(displacement**2)
     150              :          ! |R_B-R_A| > 2 max_l|r_l-R_A| cannot cut this finite point set.
     151           60 :          IF (distance2 > 4.0_dp*max_radius2) CYCLE
     152           48 :          tolerance = 32.0_dp*EPSILON(1.0_dp)*MAX(1.0_dp, distance2)
     153         9929 :          DO ipoint = 1, SIZE(points, 2)
     154         9851 :             IF (.NOT. keep(ipoint)) CYCLE
     155              :             ! |r_l-R_B|² - |r_l-R_A|² = |R_B-R_A|² - 2(r_l-R_A)·(R_B-R_A).
     156        28934 :             IF (2.0_dp*DOT_PRODUCT(points(:, ipoint), displacement) > distance2 + tolerance) THEN
     157         3009 :                keep(ipoint) = .FALSE.
     158         4202 :             ELSE IF (iatom < icenter_atom) THEN
     159         5516 :                IF (ABS(2.0_dp*DOT_PRODUCT(points(:, ipoint), displacement) - distance2) <= tolerance) THEN
     160           49 :                   keep(ipoint) = .FALSE.
     161              :                END IF
     162              :             END IF
     163              :          END DO
     164              :       END DO
     165           30 :       IF (PRESENT(mask)) THEN
     166           30 :          CPASSERT(SIZE(mask) == SIZE(keep))
     167         4995 :          mask(:) = keep
     168           30 :          RETURN
     169              :       END IF
     170            0 :       n_keep = COUNT(keep)
     171            0 :       ALLOCATE (filtered_points(3, n_keep))
     172            0 :       IF (n_keep > 0) filtered_points(:, :) = RESHAPE(PACK(points, SPREAD(keep, 1, 3)), [3, n_keep])
     173            0 :       CALL MOVE_ALLOC(filtered_points, points)
     174           60 :    END SUBROUTINE filter_grid_to_voronoi
     175              : 
     176              : ! **************************************************************************************************
     177              : !> \brief Form C_A from nuclei within the specified radius, in global atom order.
     178              : !> \param particle_set Molecular nuclei.
     179              : !> \param cell Simulation cell.
     180              : !> \param icenter_atom Central atom A.
     181              : !> \param radius Cluster radius.
     182              : !> \param atom_indices Cluster atom indices including A.
     183              : ! **************************************************************************************************
     184           30 :    SUBROUTINE get_rirs_cluster_atoms(particle_set, cell, icenter_atom, radius, atom_indices)
     185              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     186              :       TYPE(cell_type), POINTER                           :: cell
     187              :       INTEGER, INTENT(IN)                                :: icenter_atom
     188              :       REAL(KIND=dp), INTENT(IN)                          :: radius
     189              :       INTEGER, ALLOCATABLE, INTENT(OUT)                  :: atom_indices(:)
     190              : 
     191              :       INTEGER                                            :: iatom, ncluster
     192           30 :       INTEGER, ALLOCATABLE                               :: work(:)
     193              : 
     194           90 :       ALLOCATE (work(SIZE(particle_set)))
     195          120 :       ncluster = 0
     196          120 :       DO iatom = 1, SIZE(particle_set)
     197          750 :          IF (SUM(pbc(particle_set(iatom)%r - particle_set(icenter_atom)%r, cell)**2) <= radius**2) THEN
     198           90 :             ncluster = ncluster + 1
     199          180 :             work(ncluster) = iatom
     200              :          END IF
     201              :       END DO
     202          180 :       ALLOCATE (atom_indices(ncluster), SOURCE=work(:ncluster))
     203           30 :    END SUBROUTINE get_rirs_cluster_atoms
     204              : 
     205              : ! **************************************************************************************************
     206              : !> \brief Evaluate contracted spherical AOs, and optionally their grid-coordinate derivatives.
     207              : !>
     208              : !> For atom A and displacement d(alpha)=r_l(alpha)-R_A(alpha), the value is
     209              : !>
     210              : !>   ϕ_μ(r_l) = Σ_(p,c) S_(pc,μ) d_x^lx d_y^ly d_z^lz exp(-ζ_p |d|^2).
     211              : !>
     212              : !> The optional derivative is evaluated analytically as
     213              : !>
     214              : !>   d ϕ_μ(r_l)/d r_(l,α)
     215              : !>     = sum_(p,c) S_(pc,mu) exp(-zeta_p |d|^2)
     216              : !>       [d polynomial_c/d d_alpha - 2 zeta_p d_alpha polynomial_c].
     217              : !>
     218              : !> \param phi AO values, accumulated into phi(l,mu).
     219              : !> \param grid_points Cartesian grid points, indexed (alpha,l).
     220              : !> \param iatom Source atom whose contracted AOs are evaluated.
     221              : !> \param particle_set Molecular particles.
     222              : !> \param qs_kind_set Quickstep atomic kinds containing the ORB bases.
     223              : !> \param cell Simulation cell used for the minimum-image displacement.
     224              : !> \param dphi Optional AO derivatives, accumulated into dphi(alpha,l,mu).
     225              : !> \param cutoff_squared Optional squared AO cutoff radius.
     226              : ! **************************************************************************************************
     227          136 :    SUBROUTINE evaluate_ao_on_points(phi, grid_points, iatom, particle_set, qs_kind_set, cell, &
     228          136 :                                     dphi, cutoff_squared)
     229              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT)      :: phi
     230              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: grid_points
     231              :       INTEGER, INTENT(IN)                                :: iatom
     232              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     233              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     234              :       TYPE(cell_type), POINTER                           :: cell
     235              :       REAL(KIND=dp), DIMENSION(:, :, :), INTENT(INOUT), &
     236              :          OPTIONAL                                        :: dphi
     237              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: cutoff_squared
     238              : 
     239              :       CHARACTER(len=*), PARAMETER :: routineN = 'evaluate_ao_on_points'
     240              : 
     241              :       INTEGER                                            :: handle, ikind
     242              :       TYPE(gto_basis_set_type), POINTER                  :: basis
     243              : 
     244          136 :       CALL timeset(routineN, handle)
     245          136 :       CPASSERT(SIZE(grid_points, 1) == 3)
     246          136 :       CPASSERT(SIZE(phi, 1) == SIZE(grid_points, 2))
     247          136 :       IF (PRESENT(dphi)) THEN
     248            0 :          CPASSERT(SIZE(dphi, 1) == 3)
     249            0 :          CPASSERT(SIZE(dphi, 2) == SIZE(phi, 1))
     250            0 :          CPASSERT(SIZE(dphi, 3) == SIZE(phi, 2))
     251              :       END IF
     252              : 
     253          136 :       ikind = particle_set(iatom)%atomic_kind%kind_number
     254          136 :       CALL get_qs_kind(qs_kind_set(ikind), basis_set=basis, basis_type='ORB')
     255          136 :       IF (.NOT. ASSOCIATED(basis)) THEN
     256            0 :          CALL timestop(handle)
     257            0 :          RETURN
     258              :       END IF
     259              :       CALL evaluate_ao_basis_on_points(phi, grid_points, basis, &
     260          272 :                                        particle_set(iatom)%r, cell, dphi, cutoff_squared)
     261          136 :       CALL timestop(handle)
     262              :    END SUBROUTINE evaluate_ao_on_points
     263              : 
     264              : ! **************************************************************************************************
     265              : !> \brief Evaluate one explicitly supplied contracted Gaussian basis on Cartesian points.
     266              : !> \param phi AO values, accumulated into phi(l,mu).
     267              : !> \param grid_points Cartesian grid points, indexed (alpha,l).
     268              : !> \param basis Contracted Gaussian basis to evaluate.
     269              : !> \param source_position Centre of the basis.
     270              : !> \param cell Simulation cell used for the minimum-image displacement.
     271              : !> \param dphi Optional AO derivatives, accumulated into dphi(alpha,l,mu).
     272              : !> \param cutoff_squared Optional squared AO cutoff radius.
     273              : ! **************************************************************************************************
     274         1392 :    SUBROUTINE evaluate_ao_basis_on_points(phi, grid_points, basis, source_position, cell, &
     275         1392 :                                           dphi, cutoff_squared)
     276              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT)      :: phi
     277              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: grid_points
     278              :       TYPE(gto_basis_set_type), POINTER                  :: basis
     279              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: source_position
     280              :       TYPE(cell_type), POINTER                           :: cell
     281              :       REAL(KIND=dp), DIMENSION(:, :, :), INTENT(INOUT), &
     282              :          OPTIONAL                                        :: dphi
     283              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: cutoff_squared
     284              : 
     285              :       INTEGER                                            :: first_sgf, ialpha, ico, iend_co, ipgf, &
     286              :                                                             ipoint, irow, iset, isgf, ishell, &
     287              :                                                             istart_co, l, last_sgf, lx, ly, lz, &
     288              :                                                             n_cart_total
     289              :       REAL(KIND=dp)                                      :: exponent, exponential, polynomial, &
     290              :                                                             polynomial_derivative(3), radius2, &
     291              :                                                             relative(3), weight
     292              : 
     293         1392 :       CPASSERT(ASSOCIATED(basis))
     294         1392 :       CPASSERT(SIZE(grid_points, 1) == 3)
     295         1392 :       CPASSERT(SIZE(phi, 1) == SIZE(grid_points, 2))
     296         1392 :       IF (PRESENT(dphi)) THEN
     297         1152 :          CPASSERT(SIZE(dphi, 1) == 3)
     298         1152 :          CPASSERT(SIZE(dphi, 2) == SIZE(phi, 1))
     299         1152 :          CPASSERT(SIZE(dphi, 3) == SIZE(phi, 2))
     300              :       END IF
     301              : 
     302              :       !$OMP PARALLEL DO DEFAULT(NONE) SCHEDULE(STATIC) &
     303              :       !$OMP SHARED(phi, dphi, grid_points, cell, basis, source_position, cutoff_squared, ncoset, indco) &
     304              :       !$OMP PRIVATE(ipoint, relative, radius2, iset, n_cart_total, ishell, l, istart_co, &
     305              :       !$OMP         iend_co, first_sgf, last_sgf, ipgf, exponent, exponential, isgf, ico, &
     306         1392 :       !$OMP         irow, weight, lx, ly, lz, polynomial, polynomial_derivative, ialpha)
     307              :       DO ipoint = 1, SIZE(grid_points, 2)
     308              :          relative = pbc(grid_points(:, ipoint) - source_position, cell)
     309              :          radius2 = DOT_PRODUCT(relative, relative)
     310              :          IF (PRESENT(cutoff_squared)) THEN
     311              :             IF (radius2 > cutoff_squared) CYCLE
     312              :          END IF
     313              : 
     314              :          DO iset = 1, basis%nset
     315              :             n_cart_total = ncoset(basis%lmax(iset))
     316              :             DO ishell = 1, basis%nshell(iset)
     317              :                l = basis%l(ishell, iset)
     318              :                istart_co = ncoset(l - 1) + 1
     319              :                iend_co = ncoset(l)
     320              :                first_sgf = basis%first_sgf(ishell, iset)
     321              :                last_sgf = basis%last_sgf(ishell, iset)
     322              :                DO ipgf = 1, basis%npgf(iset)
     323              :                   exponent = basis%zet(ipgf, iset)
     324              :                   exponential = EXP(-exponent*radius2)
     325              :                   DO isgf = first_sgf, last_sgf
     326              :                      DO ico = istart_co, iend_co
     327              :                         irow = (ipgf - 1)*n_cart_total + ico
     328              :                         weight = basis%sphi(irow, isgf)
     329              :                         lx = indco(1, ico)
     330              :                         ly = indco(2, ico)
     331              :                         lz = indco(3, ico)
     332              :                         polynomial = relative(1)**lx*relative(2)**ly*relative(3)**lz
     333              :                         phi(ipoint, isgf) = phi(ipoint, isgf) + weight*polynomial*exponential
     334              : 
     335              :                         IF (PRESENT(dphi)) THEN
     336              :                            polynomial_derivative = 0.0_dp
     337              :                            IF (lx > 0) polynomial_derivative(1) = REAL(lx, dp)*relative(1)**(lx - 1)* &
     338              :                                                                   relative(2)**ly*relative(3)**lz
     339              :                            IF (ly > 0) polynomial_derivative(2) = REAL(ly, dp)*relative(1)**lx* &
     340              :                                                                   relative(2)**(ly - 1)*relative(3)**lz
     341              :                            IF (lz > 0) polynomial_derivative(3) = REAL(lz, dp)*relative(1)**lx* &
     342              :                                                                   relative(2)**ly*relative(3)**(lz - 1)
     343              :                            DO ialpha = 1, 3
     344              :                               dphi(ialpha, ipoint, isgf) = dphi(ialpha, ipoint, isgf) + weight*exponential* &
     345              :                                                            (polynomial_derivative(ialpha) - &
     346              :                                                             2.0_dp*exponent*relative(ialpha)*polynomial)
     347              :                            END DO
     348              :                         END IF
     349              :                      END DO
     350              :                   END DO
     351              :                END DO
     352              :             END DO
     353              :          END DO
     354              :       END DO
     355              :       !$OMP END PARALLEL DO
     356         1392 :    END SUBROUTINE evaluate_ao_basis_on_points
     357              : 
     358              : END MODULE gw_ri_rs_utils
        

Generated by: LCOV version 2.0-1