LCOV - code coverage report
Current view: top level - src/aobasis - ai_cartesian_operators.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:123285f) Lines: 97.9 % 97 95
Test Date: 2026-11-04 00:58:17 Functions: 88.9 % 9 8

            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_pair, 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_operator_pair
      32              :       MODULE PROCEDURE apply_cartesian_operator_pair_real, apply_cartesian_operator_pair_complex
      33              :    END INTERFACE apply_cartesian_operator_pair
      34              : 
      35              :    INTERFACE apply_cartesian_1d
      36              :       MODULE PROCEDURE apply_cartesian_1d_real, apply_cartesian_1d_complex
      37              :    END INTERFACE apply_cartesian_1d
      38              : 
      39              : CONTAINS
      40              : 
      41              : ! **************************************************************************************************
      42              : !> \brief Construct base, raised, lowered and degree indices for a Cartesian ladder.
      43              : !> \param lmax highest output shell or one-dimensional degree
      44              : !> \param lmin lowest output shell (zero for one-dimensional tables)
      45              : !> \param axis Cartesian axis, or zero for a one-dimensional table
      46              : !> \param map rows contain base, raised, lowered and degree indices
      47              : !> \param source_lmin optional lowest input shell; defaults to lmin for Cartesian blocks
      48              : ! **************************************************************************************************
      49    152590623 :    SUBROUTINE cartesian_ladder_map(lmax, lmin, axis, map, source_lmin)
      50              :       INTEGER, INTENT(IN)                                :: lmax, lmin, axis
      51              :       INTEGER, DIMENSION(:, :), INTENT(OUT)              :: map
      52              :       INTEGER, INTENT(IN), OPTIONAL                      :: source_lmin
      53              : 
      54              :       INTEGER                                            :: i, ico, lowered(3), offset, power(3), &
      55              :                                                             raised(3), source_offset
      56              : 
      57    152590623 :       IF (axis == 0) THEN
      58    122582140 :          DO i = 0, lmax
      59    427842752 :             map(:, i + 1) = [i + 1, i + 2, MAX(1, i), i]
      60              :          END DO
      61              :       ELSE
      62    106323636 :          offset = ncoset(lmin - 1)
      63    106323636 :          source_offset = offset
      64    106323636 :          IF (PRESENT(source_lmin)) source_offset = ncoset(source_lmin - 1)
      65    567330225 :          DO ico = offset + 1, ncoset(lmax)
      66   1844026356 :             power = indco(:, ico)
      67    461006589 :             raised = power
      68    461006589 :             lowered = power
      69    461006589 :             raised(axis) = raised(axis) + 1
      70    461006589 :             lowered(axis) = MAX(0, lowered(axis) - 1)
      71              :             map(:, ico - offset) = [ico - source_offset, &
      72              :                                     coset(raised(1), raised(2), raised(3)) - source_offset, &
      73   2411356581 :                                     MAX(1, coset(lowered(1), lowered(2), lowered(3)) - source_offset), power(axis)]
      74              :          END DO
      75              :       END IF
      76    152590623 :    END SUBROUTINE cartesian_ladder_map
      77              : 
      78              :    #:for suffix, integral_type in [('real', 'REAL'), ('complex', 'COMPLEX')]
      79              : ! **************************************************************************************************
      80              : !> \brief Apply one coordinate or basis-center derivative ladder to indexed integrals.
      81              : !> \param source integral block including the raised functions
      82              : !> \param values transformed integral block
      83              : !> \param map base, raised, lowered and degree indices
      84              : !> \param derivative select a basis-center derivative instead of coordinate multiplication
      85              : !> \param exponent exponent of the differentiated Gaussian
      86              : !> \param origin displacement of the Gaussian center from the coordinate reference point
      87              : !> \param right apply the map to columns rather than rows
      88              : ! **************************************************************************************************
      89    440903700 :       PURE SUBROUTINE cartesian_ladder_${suffix}$ (source, values, map, derivative, exponent, origin, right)
      90              :          ${integral_type}$ (KIND=dp), DIMENSION(:, :), INTENT(IN)  :: source
      91              :          ${integral_type}$ (KIND=dp), DIMENSION(:, :), INTENT(OUT) :: values
      92              :          INTEGER, DIMENSION(:, :), INTENT(IN)               :: map
      93              :          LOGICAL, INTENT(IN)                                :: derivative
      94              :          REAL(KIND=dp), INTENT(IN)                          :: exponent, origin
      95              :          LOGICAL, INTENT(IN)                                :: right
      96              : 
      97              :          INTEGER                                            :: i, j, second_index
      98    881807400 :          REAL(KIND=dp)                                      :: scale, second_scale(SIZE(map, 2))
      99              : 
     100    440903700 :          IF (derivative) THEN
     101    439150056 :             scale = 2.0_dp*exponent
     102   1759218687 :             second_scale = -REAL(map(4, :), dp)
     103              :             second_index = 3
     104              :          ELSE
     105      1753644 :             scale = 1.0_dp
     106      4786998 :             second_scale = origin
     107              :             second_index = 1
     108              :          END IF
     109              : 
     110              :          ! Keep the first (contiguous) dimension innermost on both sides.
     111    440903700 :          IF (right) THEN
     112    906334554 :             DO j = 1, SIZE(map, 2)
     113   3904870353 :                DO i = 1, SIZE(values, 1)
     114              :                   values(i, j) = scale*source(i, map(2, j)) + &
     115   3696870393 :                                  second_scale(j)*source(i, map(second_index, j))
     116              :                END DO
     117              :             END DO
     118              :          ELSE
     119    893316069 :             DO j = 1, SIZE(values, 2)
     120   3384782178 :                DO i = 1, SIZE(map, 2)
     121              :                   values(i, j) = scale*source(map(2, i), j) + &
     122   3151878438 :                                  second_scale(i)*source(map(second_index, i), j)
     123              :                END DO
     124              :             END DO
     125              :          END IF
     126    440903700 :       END SUBROUTINE cartesian_ladder_${suffix}$
     127              : 
     128              : ! **************************************************************************************************
     129              : !> \brief Apply an ordered pair of coordinate or derivative ladders to integral blocks.
     130              : !>        Maps describe source-to-work and work-to-output transformations. The first result
     131              : !>        is reused for all requested second axes. On the same basis function the resulting
     132              : !>        operator product is O_first O_second: the second operator acts on the Gaussian first.
     133              : !>        A derivative represents minus the electronic gradient, equal to a basis-center
     134              : !>        derivative on an unmodified Gaussian. Exponents and supplied origins are fixed
     135              : !>        coefficients; total center derivatives of coordinate prefactors require their
     136              : !>        parameter dependence to be accounted for by the caller.
     137              : !> \param source integral block including all required raised and lowered functions
     138              : !> \param values transformed components in the order specified by components
     139              : !> \param map_first first maps, indexed by map entry, output function and axis
     140              : !> \param map_second second maps into work, with the same axis convention
     141              : !> \param components first and second map-axis indices; use a single map axis for a 1D table
     142              : !> \param derivative select a derivative instead of coordinate multiplication for each step
     143              : !> \param exponents Gaussian exponents for the two steps, unused for coordinate multiplication
     144              : !> \param right select columns instead of rows for each step
     145              : !> \param work intermediate block, retained after the last requested first axis
     146              : !> \param origin optional center-to-reference displacements indexed by map axis and step; defaults to zero
     147              : ! **************************************************************************************************
     148     80405740 :       PURE SUBROUTINE apply_cartesian_operator_pair_${suffix}$ (source, values, map_first, map_second, &
     149     40202870 :                                                                 components, derivative, exponents, right, work, origin)
     150              :          ${integral_type}$ (KIND=dp), DIMENSION(:, :), INTENT(IN)       :: source
     151              :          ${integral_type}$ (KIND=dp), DIMENSION(:, :, :), INTENT(OUT)   :: values
     152              :          INTEGER, DIMENSION(:, :, :), INTENT(IN)                     :: map_first, map_second
     153              :          INTEGER, DIMENSION(:, :), INTENT(IN)                        :: components
     154              :          REAL(KIND=dp), DIMENSION(2), INTENT(IN)                       :: exponents
     155              :          LOGICAL, DIMENSION(2), INTENT(IN)                             :: derivative, right
     156              :          ${integral_type}$ (KIND=dp), DIMENSION(:, :), INTENT(INOUT)    :: work
     157              :          REAL(KIND=dp), DIMENSION(3, 2), INTENT(IN), OPTIONAL          :: origin
     158              : 
     159              :          INTEGER :: axis, icomp
     160              :          REAL(KIND=dp) :: center(3, 2)
     161              : 
     162     40202870 :          center = 0.0_dp
     163     40202870 :          IF (PRESENT(origin)) center = origin
     164     80642792 :          DO axis = 1, SIZE(map_first, 3)
     165     40973976 :             IF (.NOT. ANY(components(1, :) == axis)) CYCLE
     166              :             CALL cartesian_ladder(source, work, map_first(:, :, axis), &
     167     40439922 :                                   derivative(1), exponents(1), center(axis, 1), right(1))
     168    123213566 :             DO icomp = 1, SIZE(components, 2)
     169     42570774 :                IF (components(1, icomp) /= axis) CYCLE
     170              :                CALL cartesian_ladder(work, values(:, :, icomp), map_second(:, :, components(2, icomp)), &
     171     83010696 :                                      derivative(2), exponents(2), center(components(2, icomp), 2), right(2))
     172              :             END DO
     173              :          END DO
     174     40202870 :       END SUBROUTINE apply_cartesian_operator_pair_${suffix}$
     175              :    #:endfor
     176              : 
     177              : ! **************************************************************************************************
     178              : !> \brief Apply a Cartesian operator on either side of a primitive integral block.
     179              : !>        The input includes one extra shell on both sides. Coordinate multiplication
     180              : !>        uses the supplied shell-range layout; derivative inputs extend the lowest
     181              : !>        shell by one on the selected side for lowering. Screened primitive blocks
     182              : !>        must be zero in the input, including their raised and lowered functions.
     183              : !> \param la_max highest output shell on A
     184              : !> \param la_min lowest output shell on A
     185              : !> \param npgfa number of primitives on A
     186              : !> \param lb_max highest output shell on B
     187              : !> \param lb_min lowest output shell on B
     188              : !> \param npgfb number of primitives on B
     189              : !> \param ab input integral block with raised shells
     190              : !> \param values three Cartesian components in the output shell-range layout
     191              : !> \param right act on the ket rather than the bra
     192              : !> \param derivative select a basis-center derivative
     193              : !> \param exponents Gaussian exponents on the selected side, required for derivatives
     194              : !> \param origin displacement of the selected center from the coordinate reference point
     195              : ! **************************************************************************************************
     196      5213474 :    SUBROUTINE apply_cartesian_operator(la_max, la_min, npgfa, lb_max, lb_min, npgfb, &
     197      5213474 :                                        ab, values, right, derivative, exponents, origin)
     198              :       INTEGER, INTENT(IN)                                :: la_max, la_min, npgfa, lb_max, lb_min, &
     199              :                                                             npgfb
     200              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: ab
     201              :       REAL(KIND=dp), DIMENSION(:, :, :), INTENT(OUT)     :: values
     202              :       LOGICAL, INTENT(IN)                                :: right, derivative
     203              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN), OPTIONAL  :: exponents
     204              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN), OPTIONAL  :: origin
     205              : 
     206              :       INTEGER                                            :: axis, ia, iap, ib, ibp, ipgf, jpgf, n, &
     207              :                                                             na, nap, nb, nbp, source_min_a, &
     208              :                                                             source_min_b
     209      5213474 :       INTEGER, ALLOCATABLE                               :: map(:, :, :)
     210              :       LOGICAL                                            :: valid_shape
     211              :       REAL(KIND=dp)                                      :: center(3), exponent
     212              : 
     213      5213474 :       na = ncoset(la_max) - ncoset(la_min - 1)
     214      5213474 :       nb = ncoset(lb_max) - ncoset(lb_min - 1)
     215      5213474 :       source_min_a = la_min
     216      5213474 :       source_min_b = lb_min
     217      5213474 :       IF (derivative) THEN
     218      5212277 :          IF (right) THEN
     219      5169079 :             source_min_b = MAX(0, lb_min - 1)
     220              :          ELSE
     221        43198 :             source_min_a = MAX(0, la_min - 1)
     222              :          END IF
     223              :       END IF
     224      5213474 :       nap = ncoset(la_max + 1) - ncoset(source_min_a - 1)
     225      5213474 :       nbp = ncoset(lb_max + 1) - ncoset(source_min_b - 1)
     226      5213474 :       IF (derivative) THEN
     227      5212277 :          CPASSERT(PRESENT(exponents))
     228              :       END IF
     229     15640422 :       valid_shape = ALL(SHAPE(ab) >= [nap*npgfa, nbp*npgfb])
     230            0 :       CPASSERT(valid_shape)
     231     20853896 :       valid_shape = ALL(SHAPE(values) >= [na*npgfa, nb*npgfb, 3])
     232            0 :       CPASSERT(valid_shape)
     233      5213474 :       center = 0.0_dp
     234      5213474 :       IF (PRESENT(origin)) center = origin
     235      5213474 :       n = MERGE(nb, na, right)
     236     20853896 :       ALLOCATE (map(4, n, 3))
     237     20853896 :       DO axis = 1, 3
     238     20853896 :          IF (right) THEN
     239     15508092 :             CALL cartesian_ladder_map(lb_max, lb_min, axis, map(:, :, axis), source_min_b)
     240              :          ELSE
     241       132330 :             CALL cartesian_ladder_map(la_max, la_min, axis, map(:, :, axis), source_min_a)
     242              :          END IF
     243              :       END DO
     244              : 
     245   1202207294 :       values = 0.0_dp
     246     16385728 :       DO ipgf = 1, npgfa
     247     11172254 :          ia = (ipgf - 1)*na
     248     11172254 :          iap = (ipgf - 1)*nap
     249     45843562 :          DO jpgf = 1, npgfb
     250     29457834 :             ib = (jpgf - 1)*nb
     251     29457834 :             ibp = (jpgf - 1)*nbp
     252     29457834 :             exponent = 0.0_dp
     253     29711406 :             IF (derivative) exponent = exponents(MERGE(jpgf, ipgf, right))
     254    129003590 :             DO axis = 1, 3
     255              :                CALL cartesian_ladder(ab(iap + 1:iap + nap, ibp + 1:ibp + nbp), &
     256              :                                      values(ia + 1:ia + na, ib + 1:ib + nb, axis), map(:, :, axis), &
     257    117831336 :                                      derivative, exponent, center(axis), right)
     258              :             END DO
     259              :          END DO
     260              :       END DO
     261      5213474 :    END SUBROUTINE apply_cartesian_operator
     262              : 
     263              :    #:for suffix, integral_type in [('real', 'REAL'), ('complex', 'COMPLEX')]
     264              : ! **************************************************************************************************
     265              : !> \brief Apply the common ladder to separable one-dimensional integral tables.
     266              : !> \param source input tables indexed by the degrees on A and B and the Cartesian axis
     267              : !> \param values transformed tables; entries outside the requested range are untouched
     268              : !> \param la_max highest output degree on A
     269              : !> \param lb_max highest output degree on B
     270              : !> \param right act on B rather than A
     271              : !> \param derivative select a basis-center derivative
     272              : !> \param exponent Gaussian exponent on the selected side
     273              : !> \param origin displacement of that center from the coordinate reference point
     274              : ! **************************************************************************************************
     275     36687303 :       SUBROUTINE apply_cartesian_1d_${suffix}$ (source, values, la_max, lb_max, right, derivative, exponent, origin)
     276              :          ${integral_type}$ (KIND=dp), DIMENSION(0:, 0:, :), INTENT(IN)    :: source
     277              :          ${integral_type}$ (KIND=dp), DIMENSION(0:, 0:, :), INTENT(INOUT) :: values
     278              :          INTEGER, INTENT(IN)                                :: la_max, lb_max
     279              :          LOGICAL, INTENT(IN)                                :: right, derivative
     280              :          REAL(KIND=dp), INTENT(IN)                          :: exponent, origin(3)
     281              : 
     282     36687303 :          INTEGER :: axis, map(4, MERGE(lb_max, la_max, right) + 1)
     283              : 
     284     72810752 :          CALL cartesian_ladder_map(MERGE(lb_max, la_max, right), 0, 0, map)
     285    146749212 :          DO axis = 1, 3
     286              :             CALL cartesian_ladder(source(:, :, axis), values(0:la_max, 0:lb_max, axis), map, &
     287    146749212 :                                   derivative, exponent, origin(axis), right)
     288              :          END DO
     289     36687303 :       END SUBROUTINE apply_cartesian_1d_${suffix}$
     290              :    #:endfor
     291              : 
     292              : ! **************************************************************************************************
     293              : !> \brief Form requested coordinate powers from separable Cartesian integral tables.
     294              : !>        Only requested powers are stored. With the origin on A, each power is a
     295              : !>        direct shift of the bra degree; other origins use the binomial expansion.
     296              : !> \param source input tables with enough raised bra degrees for the largest power
     297              : !> \param values tables indexed by bra degree, ket degree, requested power and axis
     298              : !> \param la_max highest output degree on A
     299              : !> \param lb_max highest output degree on B
     300              : !> \param powers nonnegative coordinate powers to evaluate
     301              : !> \param origin displacement of A from the coordinate reference point
     302              : ! **************************************************************************************************
     303      1462329 :    SUBROUTINE cartesian_polynomial_1d(source, values, la_max, lb_max, powers, origin)
     304              :       REAL(KIND=dp), DIMENSION(0:, 0:, :), INTENT(IN)    :: source
     305              :       REAL(KIND=dp), DIMENSION(0:, 0:, 0:, :), &
     306              :          INTENT(OUT)                                     :: values
     307              :       INTEGER, INTENT(IN)                                :: la_max, lb_max, powers(0:)
     308              :       REAL(KIND=dp), INTENT(IN)                          :: origin(3)
     309              : 
     310              :       INTEGER                                            :: axis, i, k, p
     311              :       REAL(KIND=dp)                                      :: coefficient
     312              : 
     313      5849316 :       DO axis = 1, 3
     314     17905656 :          DO i = 0, SIZE(powers) - 1
     315     12056340 :             p = powers(i)
     316     16443327 :             IF (ABS(origin(axis)) <= 0.0_dp) THEN
     317      7194888 :                values(:, :, i, axis) = source(p:p + la_max, 0:lb_max, axis)
     318              :             ELSE
     319    113336541 :                values(:, :, i, axis) = 0.0_dp
     320     32960719 :                DO k = 0, p
     321     21654423 :                   coefficient = binomial_gen(REAL(p, dp), k)*origin(axis)**(p - k)
     322              :                   values(:, :, i, axis) = values(:, :, i, axis) + &
     323    232379941 :                                           coefficient*source(k:k + la_max, 0:lb_max, axis)
     324              :                END DO
     325              :             END IF
     326              :          END DO
     327              :       END DO
     328      1462329 :    END SUBROUTINE cartesian_polynomial_1d
     329              : 
     330              : END MODULE ai_cartesian_operators
        

Generated by: LCOV version 2.0-1