LCOV - code coverage report
Current view: top level - src/pw - cube_utils.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:262480d) Lines: 121 126 96.0 %
Date: 2024-11-22 07:00:40 Functions: 8 10 80.0 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2024 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \brief for a given dr()/dh(r) this will provide the bounds to be used if
      10             : !>      one wants to go over a sphere-subregion of given radius
      11             : !> \note
      12             : !>      the computation of the exact sphere radius is sensitive to roundoff (e.g.
      13             : !>      compiler optimization level) and hence this small roundoff can result in
      14             : !>      energy difference of about EPS_DEFAULT in QS energies (one gridpoint more or
      15             : !>      less in the density mapping)
      16             : !> \author Joost VandeVondele
      17             : ! **************************************************************************************************
      18             : MODULE cube_utils
      19             : 
      20             :    USE kinds,                           ONLY: dp
      21             :    USE realspace_grid_types,            ONLY: realspace_grid_desc_type
      22             : #include "../base/base_uses.f90"
      23             : 
      24             :    IMPLICIT NONE
      25             :    PRIVATE
      26             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cube_utils'
      27             : 
      28             :    PUBLIC :: cube_info_type
      29             : 
      30             :    PUBLIC :: init_cube_info, destroy_cube_info, &
      31             :              return_cube, return_cube_max_iradius, return_cube_nonortho, &
      32             :              compute_cube_center
      33             : 
      34             :    TYPE :: cube_ptr
      35             :       INTEGER, POINTER, DIMENSION(:) :: p => NULL()
      36             :    END TYPE cube_ptr
      37             : 
      38             :    TYPE :: cube_info_type
      39             :       INTEGER                      :: max_radius = 0.0_dp
      40             :       REAL(KIND=dp)              :: dr(3) = 0.0_dp, drmin = 0.0_dp
      41             :       REAL(KIND=dp)              :: dh(3, 3) = 0.0_dp
      42             :       REAL(KIND=dp)              :: dh_inv(3, 3) = 0.0_dp
      43             :       LOGICAL                      :: orthorhombic = .TRUE.
      44             :       INTEGER, POINTER             :: lb_cube(:, :) => NULL()
      45             :       INTEGER, POINTER             :: ub_cube(:, :) => NULL()
      46             :       TYPE(cube_ptr), POINTER, DIMENSION(:)  :: sphere_bounds => NULL()
      47             :       INTEGER, POINTER             :: sphere_bounds_count(:) => NULL()
      48             :       REAL(KIND=dp)              :: max_rad_ga = 0.0_dp
      49             :    END TYPE cube_info_type
      50             : 
      51             : CONTAINS
      52             : ! **************************************************************************************************
      53             : !> \brief unifies the computation of the cube center, so that differences in
      54             : !>        implementation, and thus roundoff and numerics can not lead to
      55             : !>        off-by-one errors (which can lead to out-of-bounds access with distributed grids).
      56             : !>        in principle, something similar would be needed for the computation of the cube bounds
      57             : !>
      58             : !> \param cube_center ...
      59             : !> \param rs_desc ...
      60             : !> \param zeta ...
      61             : !> \param zetb ...
      62             : !> \param ra ...
      63             : !> \param rab ...
      64             : !> \par History
      65             : !>      11.2008 created [Joost VandeVondele]
      66             : ! **************************************************************************************************
      67     7866849 :    SUBROUTINE compute_cube_center(cube_center, rs_desc, zeta, zetb, ra, rab)
      68             : 
      69             :       INTEGER, DIMENSION(3), INTENT(OUT)                 :: cube_center
      70             :       TYPE(realspace_grid_desc_type), POINTER            :: rs_desc
      71             :       REAL(KIND=dp), INTENT(IN)                          :: zeta, zetb, ra(3), rab(3)
      72             : 
      73             :       REAL(KIND=dp)                                      :: zetp
      74             :       REAL(KIND=dp), DIMENSION(3)                        :: rp
      75             : 
      76     7866849 :       zetp = zeta + zetb
      77    31467396 :       rp(:) = ra(:) + zetb/zetp*rab(:)
      78   133736433 :       cube_center(:) = FLOOR(MATMUL(rs_desc%dh_inv, rp))
      79             : 
      80     7866849 :    END SUBROUTINE compute_cube_center
      81             : 
      82             : ! **************************************************************************************************
      83             : !> \brief ...
      84             : !> \param info ...
      85             : !> \param radius ...
      86             : !> \param lb ...
      87             : !> \param ub ...
      88             : !> \param rp ...
      89             : ! **************************************************************************************************
      90      440489 :    SUBROUTINE return_cube_nonortho(info, radius, lb, ub, rp)
      91             : 
      92             :       TYPE(cube_info_type), INTENT(IN)                   :: info
      93             :       REAL(KIND=dp), INTENT(IN)                          :: radius
      94             :       INTEGER, INTENT(OUT)                               :: lb(3), ub(3)
      95             :       REAL(KIND=dp), INTENT(IN)                          :: rp(3)
      96             : 
      97             :       INTEGER                                            :: i, j, k
      98             :       REAL(KIND=dp)                                      :: point(3), res(3)
      99             : 
     100      440489 :       IF (radius > info%max_rad_ga) THEN
     101             :          !
     102             :          ! This is an important check. If the required radius for mapping the density is different
     103             :          ! from the actual computed one, (significant) errors can occur.
     104             :          ! This error can invariably be fixed by improving the computation of maxradius
     105             :          ! in the call to init_cube_info
     106             :          !
     107           0 :          WRITE (*, *) info%max_rad_ga, radius
     108           0 :          CPABORT("Called with too large radius.")
     109             :       END IF
     110             : 
     111             :       ! get the min/max indices of a cube that contains a sphere of the given radius around rp
     112             :       ! if the cell is very non-orthogonal this implies that many useless points are included
     113             :       ! this estimate can be improved (i.e. not box but sphere should be used)
     114     1761956 :       lb = HUGE(lb)
     115     1761956 :       ub = -HUGE(ub)
     116     1761956 :       DO i = -1, 1
     117     5726357 :          DO j = -1, 1
     118    17179071 :             DO k = -1, 1
     119    11893203 :                point(1) = rp(1) + i*radius
     120    11893203 :                point(2) = rp(2) + j*radius
     121    11893203 :                point(3) = rp(3) + k*radius
     122   154611639 :                res = MATMUL(info%dh_inv, point)
     123    47572812 :                lb = MIN(lb, FLOOR(res))
     124    51537213 :                ub = MAX(ub, CEILING(res))
     125             :             END DO
     126             :          END DO
     127             :       END DO
     128             : 
     129      440489 :    END SUBROUTINE return_cube_nonortho
     130             : 
     131             : ! **************************************************************************************************
     132             : !> \brief ...
     133             : !> \param info ...
     134             : !> \param radius ...
     135             : !> \param lb_cube ...
     136             : !> \param ub_cube ...
     137             : !> \param sphere_bounds ...
     138             : ! **************************************************************************************************
     139     7466533 :    SUBROUTINE return_cube(info, radius, lb_cube, ub_cube, sphere_bounds)
     140             : 
     141             :       TYPE(cube_info_type)                               :: info
     142             :       REAL(KIND=dp)                                      :: radius
     143             :       INTEGER                                            :: lb_cube(3), ub_cube(3)
     144             :       INTEGER, DIMENSION(:), POINTER                     :: sphere_bounds
     145             : 
     146             :       INTEGER                                            :: imr
     147             : 
     148     7466533 :       IF (info%orthorhombic) THEN
     149     7466533 :          imr = MAX(1, CEILING(radius/info%drmin))
     150     7466533 :          IF (imr .GT. info%max_radius) THEN
     151             :             !
     152             :             ! This is an important check. If the required radius for mapping the density is different
     153             :             ! from the actual computed one, (significant) errors can occur.
     154             :             ! This error can invariably be fixed by improving the computation of maxradius
     155             :             ! in the call to init_cube_info
     156             :             !
     157           0 :             CPABORT("Called with too large radius.")
     158             :          END IF
     159    29866132 :          lb_cube(:) = info%lb_cube(:, imr)
     160    29866132 :          ub_cube(:) = info%ub_cube(:, imr)
     161     7466533 :          sphere_bounds => info%sphere_bounds(imr)%p
     162             :       ELSE
     163             :          ! nothing yet, we should check the radius
     164             :       END IF
     165             : 
     166     7466533 :    END SUBROUTINE return_cube
     167             : 
     168             :    ! this is the integer max radius of the cube
     169             : ! **************************************************************************************************
     170             : !> \brief ...
     171             : !> \param info ...
     172             : !> \return ...
     173             : ! **************************************************************************************************
     174       28500 :    INTEGER FUNCTION return_cube_max_iradius(info)
     175             :       TYPE(cube_info_type)                               :: info
     176             : 
     177       28500 :       return_cube_max_iradius = info%max_radius
     178       28500 :    END FUNCTION return_cube_max_iradius
     179             : 
     180             : ! **************************************************************************************************
     181             : !> \brief ...
     182             : !> \param info ...
     183             : ! **************************************************************************************************
     184       29460 :    SUBROUTINE destroy_cube_info(info)
     185             :       TYPE(cube_info_type)                               :: info
     186             : 
     187             :       INTEGER                                            :: i
     188             : 
     189       29460 :       IF (info%orthorhombic) THEN
     190       27352 :          DEALLOCATE (info%lb_cube)
     191       27352 :          DEALLOCATE (info%ub_cube)
     192       27352 :          DEALLOCATE (info%sphere_bounds_count)
     193      449496 :          DO i = 1, info%max_radius
     194      449496 :             DEALLOCATE (info%sphere_bounds(i)%p)
     195             :          END DO
     196       27352 :          DEALLOCATE (info%sphere_bounds)
     197             :       ELSE
     198             :          ! no info to be deallocated
     199             :       END IF
     200       29460 :    END SUBROUTINE
     201             : 
     202             : ! **************************************************************************************************
     203             : !> \brief ...
     204             : !> \param info ...
     205             : !> \param dr ...
     206             : !> \param dh ...
     207             : !> \param dh_inv ...
     208             : !> \param ortho ...
     209             : !> \param max_radius ...
     210             : ! **************************************************************************************************
     211      883800 :    SUBROUTINE init_cube_info(info, dr, dh, dh_inv, ortho, max_radius)
     212             :       TYPE(cube_info_type), INTENT(OUT)                  :: info
     213             :       REAL(KIND=dp), INTENT(IN)                          :: dr(3), dh(3, 3), dh_inv(3, 3)
     214             :       LOGICAL, INTENT(IN)                                :: ortho
     215             :       REAL(KIND=dp), INTENT(IN)                          :: max_radius
     216             : 
     217             :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'init_cube_info'
     218             : 
     219             :       INTEGER                                            :: check_1, check_2, handle, i, igmin, imr, &
     220             :                                                             jg, jg2, jgmin, k, kg, kg2, kgmin, &
     221             :                                                             lb(3), ub(3)
     222             :       REAL(KIND=dp)                                      :: drmin, dxi, dy2, dyi, dz2, dzi, radius, &
     223             :                                                             radius2, rp(3)
     224             : 
     225       29460 :       CALL timeset(routineN, handle)
     226      117840 :       info%dr = dr
     227      382980 :       info%dh = dh
     228      382980 :       info%dh_inv = dh_inv
     229       29460 :       info%orthorhombic = ortho
     230       29460 :       info%max_rad_ga = max_radius
     231      147300 :       drmin = MINVAL(dr)
     232       29460 :       info%drmin = drmin
     233             : 
     234       29460 :       NULLIFY (info%lb_cube, info%ub_cube, &
     235       29460 :                info%sphere_bounds_count, info%sphere_bounds)
     236             : 
     237       29460 :       IF (.NOT. info%orthorhombic) THEN
     238             : 
     239        2108 :          rp = 0.0_dp
     240             :          !
     241             :          ! could still be wrong (maybe needs an additional +1 to account for off-gridpoint rp's)
     242             :          !
     243        2108 :          CALL return_cube_nonortho(info, max_radius, lb, ub, rp)
     244       16864 :          info%max_radius = MAX(MAXVAL(ABS(lb)), MAXVAL(ABS(ub)))
     245             : 
     246             :       ELSE
     247             : 
     248             :          ! this info is specialized to orthogonal grids
     249       27352 :          imr = CEILING((max_radius)/drmin)
     250       27352 :          info%max_radius = imr
     251       27352 :          dzi = 1.0_dp/dr(3)
     252       27352 :          dyi = 1.0_dp/dr(2)
     253       27352 :          dxi = 1.0_dp/dr(1)
     254       27352 :          dz2 = (dr(3))**2
     255       27352 :          dy2 = (dr(2))**2
     256             : 
     257             :          ALLOCATE (info%lb_cube(3, imr), info%ub_cube(3, imr), &
     258      667032 :                    info%sphere_bounds_count(imr), info%sphere_bounds(imr))
     259       27352 :          check_1 = 0
     260       27352 :          check_2 = 0
     261             : !       count and allocate
     262             : 
     263      449496 :          DO i = 1, imr
     264      422144 :             k = 1
     265      422144 :             radius = i*drmin
     266      422144 :             radius2 = radius**2
     267      422144 :             kgmin = do_and_hide_it_1(dzi, i, drmin, 0.0_dp, 0.0_dp, 0, 0)
     268      422144 :             k = k + 1
     269     6510972 :             DO kg = kgmin, 0
     270     6088828 :                kg2 = kg*kg
     271     6088828 :                jgmin = do_and_hide_it_1(dyi, i, drmin, dz2, 0.0_dp, kg2, 0)
     272     6088828 :                k = k + 1
     273   285223242 :                DO jg = jgmin, 0
     274   278712270 :                   jg2 = jg*jg
     275   278712270 :                   igmin = do_and_hide_it_1(dxi, i, drmin, dz2, dy2, kg2, jg2)
     276   278712270 :                   check_1 = MODULO((kgmin*97 + jgmin*37 + igmin*113)*check_1 + 1277, 9343)
     277   284801098 :                   k = k + 1
     278             :                END DO
     279             :             END DO
     280      422144 :             info%sphere_bounds_count(i) = k - 1
     281     1293784 :             ALLOCATE (info%sphere_bounds(i)%p(info%sphere_bounds_count(i)))
     282             :          END DO
     283             : 
     284             : !       init sphere_bounds array
     285             :          ! notice : as many points in lb_cube..0 as 1..ub_cube
     286      449496 :          DO i = 1, imr
     287      422144 :             k = 1
     288      422144 :             radius = i*drmin
     289     1688576 :             info%lb_cube(:, i) = -1
     290      422144 :             radius2 = radius**2
     291      422144 :             kgmin = do_and_hide_it_1(dzi, i, drmin, 0.0_dp, 0.0_dp, 0, 0)
     292      422144 :             info%lb_cube(3, i) = MIN(kgmin, info%lb_cube(3, i))
     293      422144 :             info%sphere_bounds(i)%p(k) = kgmin
     294      422144 :             k = k + 1
     295     6510972 :             DO kg = kgmin, 0
     296     6088828 :                kg2 = kg*kg
     297     6088828 :                jgmin = do_and_hide_it_1(dyi, i, drmin, dz2, 0.0_dp, kg2, 0)
     298     6088828 :                info%lb_cube(2, i) = MIN(jgmin, info%lb_cube(2, i))
     299     6088828 :                info%sphere_bounds(i)%p(k) = jgmin
     300     6088828 :                k = k + 1
     301   285223242 :                DO jg = jgmin, 0
     302   278712270 :                   jg2 = jg*jg
     303   278712270 :                   igmin = do_and_hide_it_1(dxi, i, drmin, dz2, dy2, kg2, jg2)
     304   278712270 :                   check_2 = MODULO((kgmin*97 + jgmin*37 + igmin*113)*check_2 + 1277, 9343)
     305   278712270 :                   info%lb_cube(1, i) = MIN(igmin, info%lb_cube(1, i))
     306   278712270 :                   info%sphere_bounds(i)%p(k) = igmin
     307   284801098 :                   k = k + 1
     308             :                END DO
     309             :             END DO
     310     1715928 :             info%ub_cube(:, i) = 1 - info%lb_cube(:, i)
     311             :          END DO
     312       27352 :          IF (check_1 .NE. check_2) THEN
     313           0 :             CPABORT("Irreproducible fp math caused memory corruption")
     314             :          END IF
     315             : 
     316             :       END IF
     317             : 
     318       29460 :       CALL timestop(handle)
     319             : 
     320       29460 :    END SUBROUTINE
     321             : 
     322             :    ! try to hide things from the optimizer, so that we get the same numbers,
     323             :    ! always (this solves the optimisation problems with the intel and nag compiler
     324             :    ! in which the counting loops and execution loops above are executed a different
     325             :    ! number of times, even at -O1
     326             : ! **************************************************************************************************
     327             : !> \brief ...
     328             : !> \param prefactor ...
     329             : !> \param i ...
     330             : !> \param drmin ...
     331             : !> \param dz2 ...
     332             : !> \param dy2 ...
     333             : !> \param kg2 ...
     334             : !> \param jg2 ...
     335             : !> \return ...
     336             : ! **************************************************************************************************
     337   570446484 :    FUNCTION do_and_hide_it_1(prefactor, i, drmin, dz2, dy2, kg2, jg2) RESULT(res)
     338             :       REAL(KIND=dp)                                      :: prefactor
     339             :       INTEGER                                            :: i
     340             :       REAL(KIND=dp)                                      :: drmin, dz2, dy2
     341             :       INTEGER                                            :: kg2, jg2, res
     342             : 
     343   570446484 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: buf
     344             : 
     345   570446484 :       ALLOCATE (buf(4))
     346   570446484 :       buf(1) = prefactor
     347   570446484 :       buf(2) = drmin
     348   570446484 :       buf(3) = dz2
     349   570446484 :       buf(4) = dy2
     350   570446484 :       res = do_and_hide_it_2(buf, i, jg2, kg2)
     351   570446484 :       DEALLOCATE (buf)
     352   570446484 :    END FUNCTION do_and_hide_it_1
     353             : 
     354             : ! **************************************************************************************************
     355             : !> \brief ...
     356             : !> \param buf ...
     357             : !> \param i ...
     358             : !> \param jg2 ...
     359             : !> \param kg2 ...
     360             : !> \return ...
     361             : ! **************************************************************************************************
     362   570446484 :    FUNCTION do_and_hide_it_2(buf, i, jg2, kg2) RESULT(res)
     363             :       REAL(KIND=dp), DIMENSION(:), POINTER               :: buf
     364             :       INTEGER                                            :: i, jg2, kg2, res
     365             : 
     366   570446484 :       buf(2) = (i*buf(2))**2
     367   570446484 :       res = CEILING(-0.1E-7_dp - buf(1)*SQRT(MAX(buf(2) - kg2*buf(3) - jg2*buf(4), 0.0_dp)))
     368   570446484 :    END FUNCTION do_and_hide_it_2
     369             : 
     370           0 : END MODULE cube_utils
     371             : 

Generated by: LCOV version 1.15