LCOV - code coverage report
Current view: top level - src/common - parallel_rng_types_unittest.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 92.8 % 111 103
Test Date: 2026-07-25 06:35:44 Functions: 100.0 % 4 4

            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 parallel_rng_types_TEST
       9            2 :    USE message_passing, ONLY: mp_world_finalize, &
      10              :                               mp_world_init, &
      11              :                               mp_comm_type
      12              :    USE kinds, ONLY: dp
      13              :    USE machine, ONLY: m_walltime, &
      14              :                       default_output_unit
      15              :    USE parallel_rng_types, ONLY: GAUSSIAN, &
      16              :                                  UNIFORM, &
      17              :                                  check_rng, &
      18              :                                  rng_stream_type, &
      19              :                                  rng_stream_type_from_record, &
      20              :                                  rng_name_length, &
      21              :                                  rng_record_length
      22              : 
      23              :    IMPLICIT NONE
      24              : 
      25              :    INTEGER                          :: i, nsamples, nargs, stat
      26              :    LOGICAL                          :: ionode
      27              :    REAL(KIND=dp)                    :: t, tend, tmax, tmin, tstart, tsum, tsum2
      28              :    TYPE(mp_comm_type) :: mpi_comm
      29              :    TYPE(rng_stream_type)            :: rng_stream
      30              :    CHARACTER(len=32)                :: arg
      31              : 
      32            2 :    nsamples = 1000
      33            2 :    nargs = command_argument_count()
      34              : 
      35            2 :    IF (nargs > 1) then
      36            0 :       ERROR STOP "Usage: parallel_rng_types_TEST [<int:nsamples>]"
      37              :    end if
      38              : 
      39            2 :    IF (nargs == 1) THEN
      40            2 :       CALL get_command_argument(1, arg)
      41            2 :       READ (arg, *, iostat=stat) nsamples
      42            2 :       IF (stat /= 0) then
      43            0 :          ERROR STOP "Usage: parallel_rng_types_TEST [<int:nsamples>]"
      44              :       end if
      45              :    END IF
      46              : 
      47            2 :    CALL mp_world_init(mpi_comm)
      48            2 :    ionode = mpi_comm%is_source()
      49              : 
      50            2 :    CALL check_rng(default_output_unit, ionode)
      51              : 
      52              :    ! Check performance
      53              : 
      54            2 :    IF (ionode) THEN
      55              :       WRITE (UNIT=default_output_unit, FMT="(/,/,T2,A,I10,A)") &
      56            1 :          "Check distributions using", nsamples, " random numbers:"
      57              :    END IF
      58              : 
      59              :    ! Test uniform distribution [0,1]
      60              : 
      61              :    rng_stream = rng_stream_type(name="Test uniform distribution [0,1]", &
      62              :                                 distribution_type=UNIFORM, &
      63            2 :                                 extended_precision=.TRUE.)
      64              : 
      65            2 :    IF (ionode) then
      66            1 :       CALL rng_stream%write(default_output_unit)
      67              :    end if
      68              : 
      69            2 :    tmax = -HUGE(0.0_dp)
      70            2 :    tmin = +HUGE(0.0_dp)
      71            2 :    tsum = 0.0_dp
      72            2 :    tsum2 = 0.0_dp
      73              : 
      74            2 :    tstart = m_walltime()
      75         2002 :    DO i = 1, nsamples
      76         2000 :       t = rng_stream%next()
      77         2000 :       tsum = tsum + t
      78         2000 :       tsum2 = tsum2 + t*t
      79         2000 :       IF (t > tmax) tmax = t
      80         2002 :       IF (t < tmin) tmin = t
      81              :    END DO
      82            2 :    tend = m_walltime()
      83              : 
      84            2 :    IF (ionode) THEN
      85            1 :       CALL rng_stream%write(default_output_unit, write_all=.TRUE.)
      86              :       WRITE (UNIT=default_output_unit, FMT="(/,(T4,A,F12.6))") &
      87            1 :          "Minimum: ", tmin, &
      88            1 :          "Maximum: ", tmax, &
      89            1 :          "Average: ", tsum/REAL(nsamples, KIND=dp), &
      90            1 :          "Variance:", tsum2/REAL(nsamples, KIND=dp), &
      91            2 :          "Time [s]:", tend - tstart
      92              :    END IF
      93              : 
      94              :    ! Test normal Gaussian distribution
      95              : 
      96              :    rng_stream = rng_stream_type(name="Test normal Gaussian distribution", &
      97              :                                 distribution_type=GAUSSIAN, &
      98            2 :                                 extended_precision=.TRUE.)
      99              : 
     100            2 :    IF (ionode) then
     101            1 :       CALL rng_stream%write(default_output_unit)
     102              :    end if
     103              : 
     104            2 :    tmax = -HUGE(0.0_dp)
     105            2 :    tmin = +HUGE(0.0_dp)
     106            2 :    tsum = 0.0_dp
     107            2 :    tsum2 = 0.0_dp
     108              : 
     109            2 :    tstart = m_walltime()
     110         2002 :    DO i = 1, nsamples
     111         2000 :       t = rng_stream%next()
     112         2000 :       tsum = tsum + t
     113         2000 :       tsum2 = tsum2 + t*t
     114         2000 :       IF (t > tmax) tmax = t
     115         2002 :       IF (t < tmin) tmin = t
     116              :    END DO
     117            2 :    tend = m_walltime()
     118              : 
     119            2 :    IF (ionode) THEN
     120            1 :       CALL rng_stream%write(default_output_unit)
     121              :       WRITE (UNIT=default_output_unit, FMT="(/,(T4,A,F12.6))") &
     122            1 :          "Minimum: ", tmin, &
     123            1 :          "Maximum: ", tmax, &
     124            1 :          "Average: ", tsum/REAL(nsamples, KIND=dp), &
     125            1 :          "Variance:", tsum2/REAL(nsamples, KIND=dp), &
     126            2 :          "Time [s]:", tend - tstart
     127              :    END IF
     128              : 
     129            2 :    IF (ionode) THEN
     130            1 :       CALL dump_reload_check()
     131            1 :       CALL shuffle_check()
     132              :    END IF
     133              : 
     134            2 :    CALL mp_world_finalize()
     135              : 
     136              : CONTAINS
     137              : ! **************************************************************************************************
     138              : !> \brief ...
     139              : ! **************************************************************************************************
     140            1 :    SUBROUTINE dump_reload_check()
     141              :       TYPE(rng_stream_type)            :: rng_stream
     142              :       CHARACTER(len=rng_record_length) :: rng_record
     143              :       REAL(KIND=dp), DIMENSION(3, 2)   :: ig, ig_orig, cg, cg_orig, bg, bg_orig
     144              :       CHARACTER(len=rng_name_length)   :: name, name_orig
     145              :       CHARACTER(len=*), PARAMETER      :: serialized_string = &
     146              :          "qtb_rng_gaussian                         1 F T F   0.0000000000000000E+00&
     147              :          &                12.0                12.0                12.0&
     148              :          &                12.0                12.0                12.0&
     149              :          &                12.0                12.0                12.0&
     150              :          &                12.0                12.0                12.0&
     151              :          &                12.0                12.0                12.0&
     152              :          &                12.0                12.0                12.0"
     153              : 
     154              :       WRITE (UNIT=default_output_unit, FMT="(/,/,T2,A)") &
     155            1 :          "Checking dump and load round trip:"
     156              : 
     157              :       rng_stream = rng_stream_type(name="Roundtrip for normal Gaussian distrib", &
     158              :                                    distribution_type=GAUSSIAN, &
     159            1 :                                    extended_precision=.TRUE.)
     160              : 
     161            1 :       CALL rng_stream%advance(7, 42)
     162            1 :       CALL rng_stream%get(ig=ig_orig, cg=cg_orig, bg=bg_orig, name=name_orig)
     163            1 :       CALL rng_stream%dump(rng_record)
     164              : 
     165            1 :       rng_stream = rng_stream_type_from_record(rng_record)
     166            1 :       CALL rng_stream%get(ig=ig, cg=cg, bg=bg, name=name)
     167              : 
     168              :       IF (ANY(ig /= ig_orig) .OR. ANY(cg /= cg_orig) .OR. ANY(bg /= bg_orig) &
     169           27 :           .OR. (name /= name_orig)) then
     170            0 :          ERROR STOP "Stream dump and load roundtrip failed"
     171              :       end if
     172              : 
     173              :       WRITE (UNIT=default_output_unit, FMT="(T4,A)") &
     174            1 :          "Roundtrip successful"
     175              : 
     176              :       WRITE (UNIT=default_output_unit, FMT="(/,/,T2,A)") &
     177            1 :          "Checking dumped format:"
     178              : 
     179            9 :       ig(:, :) = 12.0_dp
     180              :       rng_stream = rng_stream_type(name="qtb_rng_gaussian", &
     181              :                                    distribution_type=GAUSSIAN, &
     182              :                                    extended_precision=.TRUE., &
     183            1 :                                    seed=ig)
     184              : 
     185            1 :       CALL rng_stream%dump(rng_record)
     186              : 
     187              :       WRITE (UNIT=default_output_unit, FMT="(T4,A10,A433)") &
     188            1 :          "EXPECTED:", serialized_string
     189              : 
     190              :       WRITE (UNIT=default_output_unit, FMT="(T4,A10,A433)") &
     191            1 :          "GENERATED:", rng_record
     192              : 
     193            1 :       IF (rng_record /= serialized_string) then
     194            0 :          ERROR STOP "Serialized record does not match the expected output"
     195              :       end if
     196              : 
     197              :       WRITE (UNIT=default_output_unit, FMT="(T4,A)") &
     198            1 :          "Serialized record matches the expected output"
     199              : 
     200           25 :    END SUBROUTINE dump_reload_check
     201              : 
     202              : ! **************************************************************************************************
     203              : !> \brief ...
     204              : ! **************************************************************************************************
     205            1 :    SUBROUTINE shuffle_check()
     206              :       TYPE(rng_stream_type)              :: rng_stream
     207              : 
     208              :       INTEGER, PARAMETER                 :: sz = 20
     209              :       INTEGER, DIMENSION(1:sz)           :: arr, arr2, orig
     210              :       LOGICAL, DIMENSION(1:sz)           :: mask
     211              :       INTEGER :: idx
     212              :       REAL(KIND=dp), DIMENSION(3, 2), PARAMETER :: ig = 12.0_dp
     213              : 
     214              :       WRITE (UNIT=default_output_unit, FMT="(/,/,T2,A)", ADVANCE="no") &
     215            1 :          "Checking shuffle()"
     216              : 
     217            1 :       rng_stream = rng_stream_type(name="shuffle() check", seed=ig)
     218            1 :       orig = [(idx, idx=1, sz)]
     219              : 
     220            1 :       arr = orig
     221            1 :       CALL rng_stream%shuffle(arr)
     222              : 
     223            1 :       IF (ALL(arr == orig)) then
     224            0 :          ERROR STOP "shuffle failed: array was left untouched"
     225              :       end if
     226            1 :       WRITE (UNIT=default_output_unit, FMT="(A)", ADVANCE="no") "."
     227              : 
     228           21 :       IF (ANY(arr /= orig(arr))) then
     229            0 :          ERROR STOP "shuffle failed: the shuffled original is not the shuffled original"
     230              :       end if
     231            1 :       WRITE (UNIT=default_output_unit, FMT="(A)", ADVANCE="no") "."
     232              : 
     233              :       ! sort and compare to orig
     234           21 :       mask = .TRUE.
     235           21 :       DO idx = 1, size(orig)
     236          420 :          IF (MINVAL(arr, mask) /= orig(idx)) then
     237            0 :             ERROR STOP "shuffle failed: there is at least one unknown index"
     238              :          end if
     239          481 :          mask(MINLOC(arr, mask)) = .FALSE.
     240              :       END DO
     241            1 :       WRITE (UNIT=default_output_unit, FMT="(A)", ADVANCE="no") "."
     242              : 
     243            1 :       arr2 = orig
     244            1 :       CALL rng_stream%reset()
     245            1 :       CALL rng_stream%shuffle(arr2)
     246              : 
     247           21 :       IF (ANY(arr2 /= arr)) then
     248            0 :          ERROR STOP "shuffle failed: array was shuffled differently with same rng state"
     249              :       end if
     250            1 :       WRITE (UNIT=default_output_unit, FMT="(A)", ADVANCE="no") "."
     251              : 
     252              :       WRITE (UNIT=default_output_unit, FMT="(T4,A)") &
     253            1 :          " successful"
     254           27 :    END SUBROUTINE shuffle_check
     255              : END PROGRAM parallel_rng_types_TEST
     256              : ! vim: set ts=3 sw=3 tw=132 :
        

Generated by: LCOV version 2.0-1