LCOV - code coverage report
Current view: top level - src/aobasis - ai_cartesian_operators.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:9ccafda) Lines: 97.7 % 86 84
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 Coordinate and basis-center derivative ladders for Cartesian Gaussian integrals.
      10              : !>        Derivatives act on a basis function with the integral operator held fixed.
      11              : !>        Electronic-coordinate derivatives have the opposite sign.
      12              : ! **************************************************************************************************
      13              : MODULE ai_cartesian_operators
      14              :    USE kinds, ONLY: dp
      15              :    USE mathlib, ONLY: binomial_gen
      16              :    USE orbital_pointers, ONLY: coset, &
      17              :                                indco, &
      18              :                                ncoset
      19              : #include "../base/base_uses.f90"
      20              : 
      21              :    IMPLICIT NONE
      22              :    PRIVATE
      23              : 
      24              :    PUBLIC :: cartesian_ladder_map, cartesian_ladder, apply_cartesian_operator, apply_cartesian_1d, &
      25              :              cartesian_polynomial_1d
      26              : 
      27              :    INTERFACE cartesian_ladder
      28              :       MODULE PROCEDURE cartesian_ladder_real, cartesian_ladder_complex
      29              :    END INTERFACE cartesian_ladder
      30              : 
      31              :    INTERFACE apply_cartesian_1d
      32              :       MODULE PROCEDURE apply_cartesian_1d_real, apply_cartesian_1d_complex
      33              :    END INTERFACE apply_cartesian_1d
      34              : 
      35              : CONTAINS
      36              : 
      37              : ! **************************************************************************************************
      38              : !> \brief Construct base, raised, lowered and degree indices for a Cartesian ladder.
      39              : !> \param lmax highest output shell or one-dimensional degree
      40              : !> \param lmin lowest output shell (zero for one-dimensional tables)
      41              : !> \param axis Cartesian axis, or zero for a one-dimensional table
      42              : !> \param map rows contain base, raised, lowered and degree indices
      43              : !> \param source_lmin optional lowest input shell; defaults to lmin for Cartesian blocks
      44              : ! **************************************************************************************************
      45     80900877 :    SUBROUTINE cartesian_ladder_map(lmax, lmin, axis, map, source_lmin)
      46              :       INTEGER, INTENT(IN)                                :: lmax, lmin, axis
      47              :       INTEGER, DIMENSION(:, :), INTENT(OUT)              :: map
      48              :       INTEGER, INTENT(IN), OPTIONAL                      :: source_lmin
      49              : 
      50              :       INTEGER                                            :: i, ico, lowered(3), offset, power(3), &
      51              :                                                             raised(3), source_offset
      52              : 
      53     80900877 :       IF (axis == 0) THEN
      54    172714981 :          DO i = 0, lmax
      55    610356725 :             map(:, i + 1) = [i + 1, i + 2, MAX(1, i), i]
      56              :          END DO
      57              :       ELSE
      58     17596332 :          offset = ncoset(lmin - 1)
      59     17596332 :          source_offset = offset
      60     17596332 :          IF (PRESENT(source_lmin)) source_offset = ncoset(source_lmin - 1)
      61     81686925 :          DO ico = offset + 1, ncoset(lmax)
      62    256362372 :             power = indco(:, ico)
      63     64090593 :             raised = power
      64     64090593 :             lowered = power
      65     64090593 :             raised(axis) = raised(axis) + 1
      66     64090593 :             lowered(axis) = MAX(0, lowered(axis) - 1)
      67              :             map(:, ico - offset) = [ico - source_offset, &
      68              :                                     coset(raised(1), raised(2), raised(3)) - source_offset, &
      69    338049297 :                                     MAX(1, coset(lowered(1), lowered(2), lowered(3)) - source_offset), power(axis)]
      70              :          END DO
      71              :       END IF
      72     80900877 :    END SUBROUTINE cartesian_ladder_map
      73              : 
      74              :    #:for suffix, integral_type in [('real', 'REAL'), ('complex', 'COMPLEX')]
      75              : ! **************************************************************************************************
      76              : !> \brief Apply one coordinate or basis-center derivative ladder to indexed integrals.
      77              : !> \param source integral block including the raised functions
      78              : !> \param values transformed integral block
      79              : !> \param map base, raised, lowered and degree indices
      80              : !> \param derivative select a basis-center derivative instead of coordinate multiplication
      81              : !> \param exponent exponent of the differentiated Gaussian
      82              : !> \param origin displacement of the Gaussian center from the coordinate reference point
      83              : !> \param right apply the map to columns rather than rows
      84              : ! **************************************************************************************************
      85    283823646 :       PURE SUBROUTINE cartesian_ladder_${suffix}$ (source, values, map, derivative, exponent, origin, right)
      86              :          ${integral_type}$ (KIND=dp), DIMENSION(:, :), INTENT(IN)  :: source
      87              :          ${integral_type}$ (KIND=dp), DIMENSION(:, :), INTENT(OUT) :: values
      88              :          INTEGER, DIMENSION(:, :), INTENT(IN)               :: map
      89              :          LOGICAL, INTENT(IN)                                :: derivative
      90              :          REAL(KIND=dp), INTENT(IN)                          :: exponent, origin
      91              :          LOGICAL, INTENT(IN)                                :: right
      92              : 
      93              :          INTEGER                                            :: i, j, second_index
      94    567647292 :          REAL(KIND=dp)                                      :: scale, second_scale(SIZE(map, 2))
      95              : 
      96    283823646 :          IF (derivative) THEN
      97    282153618 :             scale = 2.0_dp*exponent
      98    902893935 :             second_scale = -REAL(map(4, :), dp)
      99              :             second_index = 3
     100              :          ELSE
     101      1670028 :             scale = 1.0_dp
     102      4493268 :             second_scale = origin
     103              :             second_index = 1
     104              :          END IF
     105              : 
     106              :          ! Keep the first (contiguous) dimension innermost on both sides.
     107    283823646 :          IF (right) THEN
     108    477413865 :             DO j = 1, SIZE(map, 2)
     109   1651479486 :                DO i = 1, SIZE(values, 1)
     110              :                   values(i, j) = scale*source(i, map(2, j)) + &
     111   1522136931 :                                  second_scale(j)*source(i, map(second_index, j))
     112              :                END DO
     113              :             END DO
     114              :          ELSE
     115    464096466 :             DO j = 1, SIZE(values, 2)
     116   1131452649 :                DO i = 1, SIZE(map, 2)
     117              :                   values(i, j) = scale*source(map(2, i), j) + &
     118    976971558 :                                  second_scale(i)*source(map(second_index, i), j)
     119              :                END DO
     120              :             END DO
     121              :          END IF
     122    283823646 :       END SUBROUTINE cartesian_ladder_${suffix}$
     123              :    #:endfor
     124              : 
     125              : ! **************************************************************************************************
     126              : !> \brief Apply a Cartesian operator on either side of a primitive integral block.
     127              : !>        The input includes one extra shell on both sides. Coordinate multiplication
     128              : !>        uses the supplied shell-range layout; derivative inputs extend the lowest
     129              : !>        shell by one on the selected side for lowering. Screened primitive blocks
     130              : !>        must be zero in the input, including their raised and lowered functions.
     131              : !> \param la_max highest output shell on A
     132              : !> \param la_min lowest output shell on A
     133              : !> \param npgfa number of primitives on A
     134              : !> \param lb_max highest output shell on B
     135              : !> \param lb_min lowest output shell on B
     136              : !> \param npgfb number of primitives on B
     137              : !> \param ab input integral block with raised shells
     138              : !> \param values three Cartesian components in the output shell-range layout
     139              : !> \param right act on the ket rather than the bra
     140              : !> \param derivative select a basis-center derivative
     141              : !> \param exponents Gaussian exponents on the selected side, required for derivatives
     142              : !> \param origin displacement of the selected center from the coordinate reference point
     143              : ! **************************************************************************************************
     144      5226074 :    SUBROUTINE apply_cartesian_operator(la_max, la_min, npgfa, lb_max, lb_min, npgfb, &
     145      5226074 :                                        ab, values, right, derivative, exponents, origin)
     146              :       INTEGER, INTENT(IN)                                :: la_max, la_min, npgfa, lb_max, lb_min, &
     147              :                                                             npgfb
     148              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: ab
     149              :       REAL(KIND=dp), DIMENSION(:, :, :), INTENT(OUT)     :: values
     150              :       LOGICAL, INTENT(IN)                                :: right, derivative
     151              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN), OPTIONAL  :: exponents
     152              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN), OPTIONAL  :: origin
     153              : 
     154              :       INTEGER                                            :: axis, ia, iap, ib, ibp, ipgf, jpgf, n, &
     155              :                                                             na, nap, nb, nbp, source_min_a, &
     156              :                                                             source_min_b
     157      5226074 :       INTEGER, ALLOCATABLE                               :: map(:, :, :)
     158              :       LOGICAL                                            :: valid_shape
     159              :       REAL(KIND=dp)                                      :: center(3), exponent
     160              : 
     161      5226074 :       na = ncoset(la_max) - ncoset(la_min - 1)
     162      5226074 :       nb = ncoset(lb_max) - ncoset(lb_min - 1)
     163      5226074 :       source_min_a = la_min
     164      5226074 :       source_min_b = lb_min
     165      5226074 :       IF (derivative) THEN
     166      5224877 :          IF (right) THEN
     167      5175379 :             source_min_b = MAX(0, lb_min - 1)
     168              :          ELSE
     169        49498 :             source_min_a = MAX(0, la_min - 1)
     170              :          END IF
     171              :       END IF
     172      5226074 :       nap = ncoset(la_max + 1) - ncoset(source_min_a - 1)
     173      5226074 :       nbp = ncoset(lb_max + 1) - ncoset(source_min_b - 1)
     174      5226074 :       IF (derivative) THEN
     175      5224877 :          CPASSERT(PRESENT(exponents))
     176              :       END IF
     177     15678222 :       valid_shape = ALL(SHAPE(ab) >= [nap*npgfa, nbp*npgfb])
     178            0 :       CPASSERT(valid_shape)
     179     20904296 :       valid_shape = ALL(SHAPE(values) >= [na*npgfa, nb*npgfb, 3])
     180            0 :       CPASSERT(valid_shape)
     181      5226074 :       center = 0.0_dp
     182      5226074 :       IF (PRESENT(origin)) center = origin
     183      5226074 :       n = MERGE(nb, na, right)
     184     20904296 :       ALLOCATE (map(4, n, 3))
     185     20904296 :       DO axis = 1, 3
     186     20904296 :          IF (right) THEN
     187     15526992 :             CALL cartesian_ladder_map(lb_max, lb_min, axis, map(:, :, axis), source_min_b)
     188              :          ELSE
     189       151230 :             CALL cartesian_ladder_map(la_max, la_min, axis, map(:, :, axis), source_min_a)
     190              :          END IF
     191              :       END DO
     192              : 
     193   1205277500 :       values = 0.0_dp
     194     16433944 :       DO ipgf = 1, npgfa
     195     11207870 :          ia = (ipgf - 1)*na
     196     11207870 :          iap = (ipgf - 1)*nap
     197     45993760 :          DO jpgf = 1, npgfb
     198     29559816 :             ib = (jpgf - 1)*nb
     199     29559816 :             ibp = (jpgf - 1)*nbp
     200     29559816 :             exponent = 0.0_dp
     201     29864379 :             IF (derivative) exponent = exponents(MERGE(jpgf, ipgf, right))
     202    129447134 :             DO axis = 1, 3
     203              :                CALL cartesian_ladder(ab(iap + 1:iap + nap, ibp + 1:ibp + nbp), &
     204              :                                      values(ia + 1:ia + na, ib + 1:ib + nb, axis), map(:, :, axis), &
     205    118239264 :                                      derivative, exponent, center(axis), right)
     206              :             END DO
     207              :          END DO
     208              :       END DO
     209      5226074 :    END SUBROUTINE apply_cartesian_operator
     210              : 
     211              :    #:for suffix, integral_type in [('real', 'REAL'), ('complex', 'COMPLEX')]
     212              : ! **************************************************************************************************
     213              : !> \brief Apply the common ladder to separable one-dimensional integral tables.
     214              : !> \param source input tables indexed by the degrees on A and B and the Cartesian axis
     215              : !> \param values transformed tables; entries outside the requested range are untouched
     216              : !> \param la_max highest output degree on A
     217              : !> \param lb_max highest output degree on B
     218              : !> \param right act on B rather than A
     219              : !> \param derivative select a basis-center derivative
     220              : !> \param exponent Gaussian exponent on the selected side
     221              : !> \param origin displacement of that center from the coordinate reference point
     222              : ! **************************************************************************************************
     223     63304545 :       SUBROUTINE apply_cartesian_1d_${suffix}$ (source, values, la_max, lb_max, right, derivative, exponent, origin)
     224              :          ${integral_type}$ (KIND=dp), DIMENSION(0:, 0:, :), INTENT(IN)    :: source
     225              :          ${integral_type}$ (KIND=dp), DIMENSION(0:, 0:, :), INTENT(INOUT) :: values
     226              :          INTEGER, INTENT(IN)                                :: la_max, lb_max
     227              :          LOGICAL, INTENT(IN)                                :: right, derivative
     228              :          REAL(KIND=dp), INTENT(IN)                          :: exponent, origin(3)
     229              : 
     230     63304545 :          INTEGER :: axis, map(4, MERGE(lb_max, la_max, right) + 1)
     231              : 
     232    112735566 :          CALL cartesian_ladder_map(MERGE(lb_max, la_max, right), 0, 0, map)
     233    253218180 :          DO axis = 1, 3
     234              :             CALL cartesian_ladder(source(:, :, axis), values(0:la_max, 0:lb_max, axis), map, &
     235    253218180 :                                   derivative, exponent, origin(axis), right)
     236              :          END DO
     237     63304545 :       END SUBROUTINE apply_cartesian_1d_${suffix}$
     238              :    #:endfor
     239              : 
     240              : ! **************************************************************************************************
     241              : !> \brief Form requested coordinate powers from separable Cartesian integral tables.
     242              : !>        Only requested powers are stored. With the origin on A, each power is a
     243              : !>        direct shift of the bra degree; other origins use the binomial expansion.
     244              : !> \param source input tables with enough raised bra degrees for the largest power
     245              : !> \param values tables indexed by bra degree, ket degree, requested power and axis
     246              : !> \param la_max highest output degree on A
     247              : !> \param lb_max highest output degree on B
     248              : !> \param powers nonnegative coordinate powers to evaluate
     249              : !> \param origin displacement of A from the coordinate reference point
     250              : ! **************************************************************************************************
     251      1472390 :    SUBROUTINE cartesian_polynomial_1d(source, values, la_max, lb_max, powers, origin)
     252              :       REAL(KIND=dp), DIMENSION(0:, 0:, :), INTENT(IN)    :: source
     253              :       REAL(KIND=dp), DIMENSION(0:, 0:, 0:, :), &
     254              :          INTENT(OUT)                                     :: values
     255              :       INTEGER, INTENT(IN)                                :: la_max, lb_max, powers(0:)
     256              :       REAL(KIND=dp), INTENT(IN)                          :: origin(3)
     257              : 
     258              :       INTEGER                                            :: axis, i, k, p
     259              :       REAL(KIND=dp)                                      :: coefficient
     260              : 
     261      5889560 :       DO axis = 1, 3
     262     18006266 :          DO i = 0, SIZE(powers) - 1
     263     12116706 :             p = powers(i)
     264     16533876 :             IF (ABS(origin(axis)) <= 0.0_dp) THEN
     265      7135766 :                values(:, :, i, axis) = source(p:p + la_max, 0:lb_max, axis)
     266              :             ELSE
     267    114026149 :                values(:, :, i, axis) = 0.0_dp
     268     33125099 :                DO k = 0, p
     269     21753051 :                   coefficient = binomial_gen(REAL(p, dp), k)*origin(axis)**(p - k)
     270              :                   values(:, :, i, axis) = values(:, :, i, axis) + &
     271    233480105 :                                           coefficient*source(k:k + la_max, 0:lb_max, axis)
     272              :                END DO
     273              :             END IF
     274              :          END DO
     275              :       END DO
     276      1472390 :    END SUBROUTINE cartesian_polynomial_1d
     277              : 
     278              : END MODULE ai_cartesian_operators
        

Generated by: LCOV version 2.0-1