LCOV - code coverage report
Current view: top level - src/pseudopotentials - upf_projector_integrals_unittest.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:123285f) Lines: 84.9 % 106 90
Test Date: 2026-11-04 00:58:17 Functions: 100.0 % 2 2

            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            2 : PROGRAM upf_projector_integrals_unittest
       9            2 :    USE ai_moments,                      ONLY: moment
      10              :    USE ai_overlap,                      ONLY: overlap
      11              :    USE atom_upf,                        ONLY: atom_read_upf,&
      12              :                                               atom_release_upf,&
      13              :                                               atom_upfpot_type
      14              :    USE cp_log_handling,                 ONLY: cp_logger_get_default_io_unit
      15              :    USE ieee_arithmetic,                 ONLY: ieee_is_finite
      16              :    USE kinds,                           ONLY: default_path_length,&
      17              :                                               dp
      18              :    USE libcp2k,                         ONLY: cp2k_finalize,&
      19              :                                               cp2k_init
      20              :    USE orbital_pointers,                ONLY: nco,&
      21              :                                               ncoset
      22              :    USE orbital_transformation_matrices, ONLY: orbtramat
      23              :    USE upf_projector_integrals,         ONLY: upf_projector_init,&
      24              :                                               upf_projector_overlap,&
      25              :                                               upf_projector_type
      26              : 
      27              :    IMPLICIT NONE
      28              :    LOGICAL :: source_root_argument
      29              : 
      30              :    INTEGER, PARAMETER :: la = 4, lb = 3, nder = 2, na = 5, nb = 5
      31              :    REAL(KIND=dp), PARAMETER :: alpha(na) = [0.04_dp, 0.3_dp, 1.2_dp, 5.0_dp, 24.0_dp]
      32              :    REAL(KIND=dp), PARAMETER :: eta(nb) = [0.3_dp, 0.7_dp, 1.1_dp, 2.2_dp, 0.4_dp]
      33              :    CHARACTER(LEN=default_path_length) :: filename
      34              :    INTEGER :: grid, nr, i, j, p, m, l, geometry, id, ld, ldai, first, shell, q, io_unit
      35              :    REAL(KIND=dp) :: dx, rac(3), origin(3), error, scale, expected_d
      36            4 :    REAL(KIND=dp), ALLOCATABLE :: sab(:, :), work(:, :, :), moments(:, :, :), integrals(:, :, :), reference(:, :, :)
      37          210 :    TYPE(atom_upfpot_type) :: pot
      38            2 :    TYPE(upf_projector_type) :: projectors
      39              : 
      40            2 :    CALL cp2k_init()
      41            2 :    io_unit = cp_logger_get_default_io_unit()
      42            2 :    pot%number_of_proj = nb
      43            2 :    ALLOCATE (pot%lbeta(nb))
      44           12 :    pot%lbeta(:) = [0, 1, 2, 3, 1]
      45            2 :    ALLOCATE (pot%dion(nb, nb))
      46           62 :    pot%dion = 0.0_dp
      47           12 :    DO i = 1, nb
      48           12 :       pot%dion(i, i) = 0.3_dp*i - 0.8_dp
      49              :    END DO
      50            2 :    pot%dion(2, 5) = 0.17_dp
      51            2 :    pot%dion(5, 2) = 0.17_dp
      52              : 
      53            8 :    DO grid = 1, 3
      54            6 :       nr = 2501
      55            6 :       IF (grid == 2) nr = 2500
      56           12 :       ALLOCATE (pot%r(nr), pot%rab(nr), pot%beta(nr, nb))
      57        15010 :       DO i = 1, nr
      58        15010 :          IF (grid == 3) THEN
      59         5002 :             dx = LOG(1.0_dp + 12.5_dp/0.001_dp)/(nr - 1)
      60         5002 :             pot%r(i) = 0.001_dp*(EXP((i - 1)*dx) - 1.0_dp)
      61         5002 :             pot%rab(i) = dx*(pot%r(i) + 0.001_dp)
      62              :          ELSE
      63        10002 :             pot%r(i) = (i - 1)*0.005_dp
      64        10002 :             pot%rab(i) = 0.005_dp
      65              :          END IF
      66              :       END DO
      67           36 :       DO i = 1, nb
      68        75056 :          pot%beta(:, i) = pot%r**pot%lbeta(i)*EXP(-eta(i)*pot%r**2)
      69              :       END DO
      70            6 :       CALL upf_projector_init(pot, la + nder, projectors)
      71              :       ! Independent expansion of D, including non-diagonal coupling of two p projectors.
      72            6 :       p = 0
      73           36 :       DO i = 1, nb
      74          150 :          DO m = -pot%lbeta(i), pot%lbeta(i)
      75          114 :             p = p + 1
      76          114 :             q = 0
      77          714 :             DO j = 1, nb
      78         2850 :                DO l = -pot%lbeta(j), pot%lbeta(j)
      79         2166 :                   q = q + 1
      80         2166 :                   expected_d = 0.0_dp
      81         2166 :                   IF (pot%lbeta(i) == pot%lbeta(j) .AND. m == l) expected_d = pot%dion(i, j)
      82         2736 :                   IF (ABS(projectors%d(p, q) - expected_d) > 1.e-15_dp) ERROR STOP "UPF D expansion failed"
      83              :                END DO
      84              :             END DO
      85              :          END DO
      86              :       END DO
      87            6 :       ld = MAX(na*ncoset(la), nb*ncoset(lb))
      88            6 :       ldai = ncoset(la + nder)
      89            0 :       ALLOCATE (sab(ld, ld*ncoset(nder)), work(ldai, ldai, ncoset(nder)), &
      90            0 :                 moments(na*ncoset(la), nb*ncoset(lb), ncoset(nder) - 1), &
      91            0 :                 integrals(na*ncoset(la), projectors%nproj, ncoset(nder)), &
      92          126 :                 reference(na*ncoset(la), projectors%nproj, ncoset(nder)))
      93           30 :       DO geometry = 1, 4
      94            6 :          SELECT CASE (geometry)
      95              :          CASE (1)
      96            6 :             rac = 0.0_dp
      97              :          CASE (2)
      98            6 :             rac = [0.2_dp, -0.5_dp, 0.7_dp]
      99              :          CASE (3)
     100            6 :             rac = [3.0_dp, 2.0_dp, -1.0_dp]
     101              :          CASE (4)
     102           24 :             rac = [-7.0_dp, 1.0_dp, 2.0_dp]
     103              :          END SELECT
     104           24 :          sab = 0.0_dp
     105              :          CALL overlap(la, 0, na, SPREAD(100.0_dp, 1, na), alpha, &
     106              :                       lb, 0, nb, SPREAD(100.0_dp, 1, nb), eta, rac, NORM2(rac), &
     107           96 :                       sab, nder, .TRUE., work, ldai)
     108          264 :          DO id = 1, ncoset(nder)
     109          240 :             p = 0
     110         1464 :             DO i = 1, nb
     111         1200 :                l = pot%lbeta(i)
     112         1200 :                first = (id - 1)*ld + (i - 1)*ncoset(lb) + ncoset(l - 1) + 1
     113              :                reference(:, p + 1:p + 2*l + 1, id) = &
     114     15830880 :                   MATMUL(sab(:na*ncoset(la), first:first + nco(l) - 1), TRANSPOSE(orbtramat(l)%slm))
     115         1440 :                p = p + 2*l + 1
     116              :             END DO
     117              :          END DO
     118           24 :          CALL upf_projector_overlap(projectors, la, alpha, rac, nder, integrals)
     119          264 :          DO id = 1, ncoset(nder)
     120       802800 :             scale = MAX(1.0_dp, MAXVAL(ABS(reference(:, :, id))))
     121       802800 :             error = MAXVAL(ABS(integrals(:, :, id) - reference(:, :, id)))/scale
     122          240 :             IF (io_unit > 0) WRITE (io_unit, '(A,3I4,ES16.7)') &
     123          120 :                "UPF radial Gaussian overlap/derivative:", grid, geometry, id, error
     124          264 :             IF (error > 1.e-10_dp) ERROR STOP "Numerical UPF overlaps disagree with analytic Gaussian integrals"
     125              :          END DO
     126              :          ! Independent moment implementation uses the OS recurrence with a fixed reference.
     127           24 :          origin = [0.4_dp, 0.6_dp, -0.3_dp]
     128              :          CALL moment(la, na, alpha, SPREAD(100.0_dp, 1, na), 0, &
     129           96 :                      lb, nb, eta, SPREAD(100.0_dp, 1, nb), nder, origin, origin + rac, moments)
     130          240 :          DO id = 2, ncoset(nder)
     131          216 :             p = 0
     132         1320 :             DO i = 1, nb
     133         1080 :                l = pot%lbeta(i)
     134         1080 :                shell = (i - 1)*ncoset(lb) + ncoset(l - 1) + 1
     135              :                reference(:, p + 1:p + 2*l + 1, id) = &
     136     14247792 :                   MATMUL(moments(:, shell:shell + nco(l) - 1, id - 1), TRANSPOSE(orbtramat(l)%slm))
     137         1296 :                p = p + 2*l + 1
     138              :             END DO
     139              :          END DO
     140           24 :          CALL upf_projector_overlap(projectors, la, alpha, rac, nder, integrals, moment_origin=origin)
     141       802824 :          scale = MAX(1.0_dp, MAXVAL(ABS(reference)))
     142       802824 :          error = MAXVAL(ABS(integrals - reference))/scale
     143           24 :          IF (io_unit > 0) WRITE (io_unit, '(A,2I4,ES16.7)') "UPF radial Gaussian moments:", grid, geometry, error
     144           30 :          IF (error > 1.e-10_dp) ERROR STOP "Numerical UPF moments disagree with analytic Gaussian integrals"
     145              :       END DO
     146            8 :       DEALLOCATE (pot%r, pot%rab, pot%beta, sab, work, moments, integrals, reference)
     147              :    END DO
     148            2 :    CALL atom_release_upf(pot)
     149              :    ! Optional library audit checks real radial meshes, angular channels, and
     150              :    ! successive cache replacement. Finiteness is not a physical accuracy test.
     151            4 :    DO i = 1, COMMAND_ARGUMENT_COUNT()
     152            2 :       CALL GET_COMMAND_ARGUMENT(i, filename)
     153              :       ! do_regtest.py passes the CP2K source root before any optional UPF audit files.
     154            2 :       IF (i == 1) THEN
     155            2 :          INQUIRE (FILE=TRIM(filename)//"/tests/UNIT_TESTS", EXIST=source_root_argument)
     156            2 :          IF (source_root_argument) CYCLE
     157              :       END IF
     158            0 :       CALL atom_read_upf(pot, TRIM(filename))
     159            0 :       IF (pot%number_of_proj > 0) THEN
     160            0 :          CALL upf_projector_init(pot, la + nder, projectors)
     161            0 :          IF (.NOT. ALL(ieee_is_finite(projectors%d))) ERROR STOP "Non-finite original UPF D matrix"
     162            0 :          ALLOCATE (integrals(na*ncoset(la), projectors%nproj, ncoset(nder)))
     163            0 :          DO geometry = 1, 2
     164            0 :             rac = 0.0_dp
     165            0 :             IF (geometry == 2) rac = [0.3_dp, -0.5_dp, 1.0_dp]
     166            0 :             CALL upf_projector_overlap(projectors, la, alpha, rac, nder, integrals)
     167            0 :             IF (.NOT. ALL(ieee_is_finite(integrals))) ERROR STOP "Non-finite native UPF projector integrals"
     168              :          END DO
     169            0 :          IF (io_unit > 0) WRITE (io_unit, '(A,1X,A,1X,I6,1X,ES16.7)') "UPF native integrals:", TRIM(filename), &
     170            0 :             projectors%nproj, MAXVAL(ABS(integrals))
     171            0 :          DEALLOCATE (integrals)
     172              :       END IF
     173            4 :       CALL atom_release_upf(pot)
     174              :    END DO
     175            2 :    CALL cp2k_finalize()
     176            2 : END PROGRAM upf_projector_integrals_unittest
        

Generated by: LCOV version 2.0-1