LCOV - code coverage report
Current view: top level - src/swarm - glbopt_history.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:4c33f95) Lines: 100 120 83.3 %
Date: 2025-01-30 06:53:08 Functions: 9 14 64.3 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2025 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \brief History of minima, calculates, stores and compares fingerprints of minima.
      10             : !>        Used by Minima Hopping and Minima Crawling.
      11             : !> \author Ole Schuett
      12             : ! **************************************************************************************************
      13             : MODULE glbopt_history
      14             :    USE input_section_types,             ONLY: section_vals_type,&
      15             :                                               section_vals_val_get
      16             :    USE kinds,                           ONLY: dp
      17             : #include "../base/base_uses.f90"
      18             : 
      19             :    IMPLICIT NONE
      20             :    PRIVATE
      21             : 
      22             :    TYPE history_fingerprint_type
      23             :       PRIVATE
      24             :       REAL(KIND=dp)                            :: Epot = 0.0
      25             :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: goedecker
      26             :    END TYPE history_fingerprint_type
      27             : 
      28             :    TYPE history_entry_type
      29             :       TYPE(history_fingerprint_type), POINTER :: p => Null()
      30             :       INTEGER                                 :: id = -1
      31             :    END TYPE history_entry_type
      32             : 
      33             :    TYPE history_type
      34             :       PRIVATE
      35             :       TYPE(history_entry_type), DIMENSION(:), POINTER :: entries => Null()
      36             :       INTEGER                              :: length = 0
      37             :       INTEGER                              :: iw = -1
      38             :       REAL(KIND=dp)                        :: E_precision = 0.0
      39             :       REAL(KIND=dp)                        :: FP_precision = 0.0
      40             :    END TYPE history_type
      41             : 
      42             :    PUBLIC :: history_type, history_fingerprint_type
      43             :    PUBLIC :: history_init, history_finalize
      44             :    PUBLIC :: history_add, history_lookup
      45             :    PUBLIC :: history_fingerprint
      46             :    PUBLIC :: history_fingerprint_match
      47             : 
      48             :    LOGICAL, PARAMETER                     :: debug = .FALSE.
      49             :    INTEGER, PARAMETER                     :: history_grow_unit = 1000
      50             : CONTAINS
      51             : 
      52             : ! **************************************************************************************************
      53             : !> \brief Initializes a history.
      54             : !> \param history ...
      55             : !> \param history_section ...
      56             : !> \param iw ...
      57             : !> \author Ole Schuett
      58             : ! **************************************************************************************************
      59           3 :    SUBROUTINE history_init(history, history_section, iw)
      60             :       TYPE(history_type), INTENT(INOUT)                  :: history
      61             :       TYPE(section_vals_type), POINTER                   :: history_section
      62             :       INTEGER                                            :: iw
      63             : 
      64        3003 :       ALLOCATE (history%entries(history_grow_unit))
      65           3 :       history%iw = iw
      66             :       CALL section_vals_val_get(history_section, "ENERGY_PRECISION", &
      67           3 :                                 r_val=history%E_precision)
      68             :       CALL section_vals_val_get(history_section, "FINGERPRINT_PRECISION", &
      69           3 :                                 r_val=history%FP_precision)
      70             : 
      71           3 :       IF (iw > 0) THEN
      72             :          WRITE (iw, '(A,T66,E15.3)') &
      73           3 :             " GLBOPT| History energy precision", history%E_precision
      74             :          WRITE (iw, '(A,T66,E15.3)') &
      75           3 :             " GLBOPT| History fingerprint precision", history%FP_precision
      76             :       END IF
      77           3 :    END SUBROUTINE history_init
      78             : 
      79             : ! **************************************************************************************************
      80             : !> \brief Calculates a fingerprint for a given configuration.
      81             : !> \param Epot ...
      82             : !> \param pos ...
      83             : !> \return ...
      84             : !> \author Ole Schuett
      85             : ! **************************************************************************************************
      86         102 :    FUNCTION history_fingerprint(Epot, pos) RESULT(fp)
      87             :       REAL(KIND=dp), INTENT(IN)                          :: Epot
      88             :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: pos
      89             :       TYPE(history_fingerprint_type)                     :: fp
      90             : 
      91             :       INTEGER                                            :: handle
      92          51 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: tmp
      93             : 
      94          51 :       CALL timeset("glbopt_history_fingerprint", handle)
      95             : 
      96          51 :       NULLIFY (tmp)
      97          51 :       fp%Epot = Epot
      98          51 :       CALL goedecker_fingerprint(pos, tmp)
      99             : 
     100             :       !copy pointer to allocatable
     101         153 :       ALLOCATE (fp%goedecker(SIZE(tmp)))
     102         561 :       fp%goedecker(:) = tmp
     103          51 :       DEALLOCATE (tmp)
     104             : 
     105          51 :       CALL timestop(handle)
     106          51 :    END FUNCTION history_fingerprint
     107             : 
     108             : ! **************************************************************************************************
     109             : !> \brief Helper routine for history_fingerprint.
     110             : !>        Calculates a fingerprint based on inter-atomic distances.
     111             : !> \param pos ...
     112             : !> \param res ...
     113             : !> \author Stefan Goedecker
     114             : ! **************************************************************************************************
     115          51 :    SUBROUTINE goedecker_fingerprint(pos, res)
     116             :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: pos
     117             :       REAL(KIND=dp), DIMENSION(:), POINTER               :: res
     118             : 
     119             :       INTEGER                                            :: i, info, j, N
     120             :       REAL(KIND=dp)                                      :: d2, t
     121          51 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: matrix, work
     122             :       REAL(KIND=dp), DIMENSION(3)                        :: d
     123             : 
     124          51 :       IF (ASSOCIATED(res)) CPABORT("goedecker_fingerprint: res already allocated")
     125          51 :       N = SIZE(pos)/3 ! number of atoms
     126             : 
     127         306 :       ALLOCATE (matrix(N, N), work(N, N))
     128         561 :       DO i = 1, N
     129         510 :          matrix(i, i) = 1.0
     130        2856 :          DO j = i + 1, N
     131        9180 :             d = pos(3*i - 2:3*i) - pos(3*j - 2:3*j)
     132        9180 :             d2 = SUM(d**2)
     133        2295 :             t = EXP(-0.5*d2)
     134        2295 :             matrix(i, j) = t
     135        2805 :             matrix(j, i) = t
     136             :          END DO
     137             :       END DO
     138         153 :       ALLOCATE (res(N))
     139             :       ! matrix values are garbage on exit because of jobz='N'
     140          51 :       CALL dsyev('N', 'U', N, matrix, N, res, work, N**2, info)
     141          51 :       IF (info /= 0) CPABORT("goedecker_fingerprint: DSYEV failed")
     142          51 :    END SUBROUTINE goedecker_fingerprint
     143             : 
     144             : ! **************************************************************************************************
     145             : !> \brief Checks if two given fingerprints match.
     146             : !> \param history ...
     147             : !> \param fp1 ...
     148             : !> \param fp2 ...
     149             : !> \return ...
     150             : !> \author Ole Schuett
     151             : ! **************************************************************************************************
     152          11 :    FUNCTION history_fingerprint_match(history, fp1, fp2) RESULT(res)
     153             :       TYPE(history_type), INTENT(IN)                     :: history
     154             :       TYPE(history_fingerprint_type), INTENT(IN)         :: fp1, fp2
     155             :       LOGICAL                                            :: res
     156             : 
     157             :       res = (ABS(fp1%Epot - fp2%Epot) < history%E_precision) .AND. &
     158          11 :             (fingerprint_distance(fp1, fp2) < history%fp_precision)
     159             : 
     160          11 :    END FUNCTION history_fingerprint_match
     161             : 
     162             : ! **************************************************************************************************
     163             : !> \brief Helper routine for history_fingerprint_match
     164             : !>        Calculates the distance between two given fingerprints.
     165             : !> \param fp1 ...
     166             : !> \param fp2 ...
     167             : !> \return ...
     168             : !> \author Stefan Goedecker
     169             : ! **************************************************************************************************
     170          34 :    PURE FUNCTION fingerprint_distance(fp1, fp2) RESULT(res)
     171             :       TYPE(history_fingerprint_type), INTENT(IN)         :: fp1, fp2
     172             :       REAL(KIND=dp)                                      :: res
     173             : 
     174         374 :       res = SQRT(SUM((fp1%goedecker - fp2%goedecker)**2)/SIZE(fp1%goedecker))
     175          34 :    END FUNCTION fingerprint_distance
     176             : 
     177             : ! **************************************************************************************************
     178             : !> \brief Addes a new fingerprints to the history.
     179             : !>        Optionally, an abitrary id can be stored alongside the fingerprint.
     180             : !> \param history ...
     181             : !> \param fingerprint ...
     182             : !> \param id ...
     183             : !> \author Ole Schuett
     184             : ! **************************************************************************************************
     185          18 :    SUBROUTINE history_add(history, fingerprint, id)
     186             :       TYPE(history_type), INTENT(INOUT)                  :: history
     187             :       TYPE(history_fingerprint_type), INTENT(IN)         :: fingerprint
     188             :       INTEGER, INTENT(IN), OPTIONAL                      :: id
     189             : 
     190             :       INTEGER                                            :: handle, i, k, n
     191          18 :       TYPE(history_entry_type), DIMENSION(:), POINTER    :: tmp
     192             : 
     193          18 :       CALL timeset("glbopt_history_add", handle)
     194             : 
     195          18 :       n = SIZE(history%entries)
     196          18 :       IF (n == history%length) THEN
     197             :          ! grow history%entries array
     198           0 :          tmp => history%entries
     199           0 :          ALLOCATE (history%entries(n + history_grow_unit))
     200           0 :          history%entries(1:n) = tmp(:)
     201           0 :          DEALLOCATE (tmp)
     202           0 :          n = n + history_grow_unit
     203             :       END IF
     204             : 
     205          18 :       k = interpolation_search(history, fingerprint%Epot)
     206             : 
     207             :       !history%entries(k+1:) = history%entries(k:n-1)
     208             :       !Workaround for an XLF bug - pointer array copy does
     209             :       !not work correctly
     210       17975 :       DO i = n, k + 1, -1
     211       17975 :          history%entries(i) = history%entries(i - 1)
     212             :       END DO
     213             : 
     214          18 :       ALLOCATE (history%entries(k)%p)
     215          18 :       history%entries(k)%p = fingerprint
     216          18 :       IF (PRESENT(id)) &
     217          11 :          history%entries(k)%id = id
     218          18 :       history%length = history%length + 1
     219             : 
     220             :       IF (debug) THEN
     221             :          ! check history for correct order
     222             :          DO k = 1, history%length
     223             :             !WRITE(*,*) "history: ", k, "Epot",history%entries(k)%p%Epot
     224             :             IF (k > 1) THEN
     225             :                IF (history%entries(k - 1)%p%Epot > history%entries(k)%p%Epot) &
     226             :                   CPABORT("history_add: history in wrong order")
     227             :             END IF
     228             :          END DO
     229             :       END IF
     230             : 
     231          18 :       CALL timestop(handle)
     232          18 :    END SUBROUTINE history_add
     233             : 
     234             : ! **************************************************************************************************
     235             : !> \brief Checks if a given fingerprints is contained in the history.
     236             : !> \param history ...
     237             : !> \param fingerprint ...
     238             : !> \param found ...
     239             : !> \param id ...
     240             : !> \author Ole Schuett
     241             : ! **************************************************************************************************
     242          48 :    SUBROUTINE history_lookup(history, fingerprint, found, id)
     243             :       TYPE(history_type), INTENT(IN)                     :: history
     244             :       TYPE(history_fingerprint_type), INTENT(IN)         :: fingerprint
     245             :       LOGICAL, INTENT(OUT)                               :: found
     246             :       INTEGER, INTENT(OUT), OPTIONAL                     :: id
     247             : 
     248             :       INTEGER                                            :: found_i, handle, i, k, k_max, k_min
     249             :       REAL(KIND=dp)                                      :: best_match, dist, Epot
     250             : 
     251          48 :       CALL timeset("glbopt_history_lookup", handle)
     252             : 
     253          48 :       found = .FALSE.
     254          48 :       IF (PRESENT(id)) id = -1
     255          48 :       best_match = HUGE(1.0_dp)
     256             : 
     257          48 :       IF (history%length > 0) THEN
     258          45 :          Epot = fingerprint%Epot
     259          45 :          k = interpolation_search(history, fingerprint%Epot)
     260             : 
     261          71 :          DO k_min = k - 1, 1, -1
     262          71 :             IF (history%entries(k_min)%p%Epot < Epot - history%E_precision) EXIT
     263             :          END DO
     264             : 
     265          50 :          DO k_max = k, history%length
     266          50 :             IF (history%entries(k_max)%p%Epot > Epot + history%E_precision) EXIT
     267             :          END DO
     268             : 
     269          45 :          k_min = MAX(k_min + 1, 1)
     270          45 :          k_max = MIN(k_max - 1, history%length)
     271             : 
     272             :          IF (debug) found_i = -1
     273             : 
     274          76 :          DO i = k_min, k_max
     275          31 :             dist = fingerprint_distance(fingerprint, history%entries(i)%p)
     276             :             !WRITE(*,*) "entry ", i, " dist: ",dist
     277          76 :             IF (dist < history%fp_precision .AND. dist < best_match) THEN
     278          30 :                best_match = dist
     279          30 :                found = .TRUE.
     280          30 :                IF (PRESENT(id)) id = history%entries(i)%id
     281             :                IF (debug) found_i = i
     282             :             END IF
     283             :          END DO
     284             : 
     285             :          IF (debug) CALL verify_history_lookup(history, fingerprint, found_i)
     286             :       END IF
     287             : 
     288          48 :       CALL timestop(handle)
     289             : 
     290          48 :    END SUBROUTINE history_lookup
     291             : 
     292             : ! **************************************************************************************************
     293             : !> \brief Helper routine for history_lookup
     294             : !> \param history ...
     295             : !> \param Efind ...
     296             : !> \return ...
     297             : !> \author Ole Schuett
     298             : ! **************************************************************************************************
     299          63 :    FUNCTION interpolation_search(history, Efind) RESULT(res)
     300             :       TYPE(history_type), INTENT(IN)                     :: history
     301             :       REAL(KIND=dp), INTENT(IN)                          :: Efind
     302             :       INTEGER                                            :: res
     303             : 
     304             :       INTEGER                                            :: high, low, mid
     305             :       REAL(KIND=dp)                                      :: slope
     306             : 
     307          63 :       low = 1
     308          63 :       high = history%length
     309             : 
     310         157 :       DO WHILE (low < high)
     311             :          !linear interpolation
     312          94 :          slope = REAL(high - low, KIND=dp)/(history%entries(high)%p%Epot - history%entries(low)%p%Epot)
     313          94 :          mid = low + INT(slope*(Efind - history%entries(low)%p%Epot))
     314          94 :          mid = MIN(MAX(mid, low), high)
     315             : 
     316         157 :          IF (history%entries(mid)%p%Epot < Efind) THEN
     317          46 :             low = mid + 1
     318             :          ELSE
     319          48 :             high = mid - 1
     320             :          END IF
     321             :       END DO
     322             : 
     323          63 :       IF (0 < low .AND. low <= history%length) THEN
     324          60 :          IF (Efind > history%entries(low)%p%Epot) low = low + 1
     325             :       END IF
     326             : 
     327          63 :       res = low
     328          63 :    END FUNCTION interpolation_search
     329             : 
     330             : ! **************************************************************************************************
     331             : !> \brief Debugging routine, performs a slow (but robust) linear search.
     332             : !> \param history ...
     333             : !> \param fingerprint ...
     334             : !> \param found_i_ref ...
     335             : !> \author Ole Schuett
     336             : ! **************************************************************************************************
     337           0 :    SUBROUTINE verify_history_lookup(history, fingerprint, found_i_ref)
     338             :       TYPE(history_type), INTENT(IN)                     :: history
     339             :       TYPE(history_fingerprint_type), INTENT(IN)         :: fingerprint
     340             :       INTEGER, INTENT(IN)                                :: found_i_ref
     341             : 
     342             :       INTEGER                                            :: found_i, i
     343             :       REAL(KIND=dp)                                      :: best_fp_match, Epot_dist, fp_dist
     344             : 
     345           0 :       found_i = -1
     346           0 :       best_fp_match = HUGE(1.0_dp)
     347             : 
     348           0 :       DO i = 1, history%length
     349           0 :          Epot_dist = ABS(fingerprint%Epot - history%entries(i)%p%Epot)
     350           0 :          IF (Epot_dist > history%E_precision) CYCLE
     351           0 :          fp_dist = fingerprint_distance(fingerprint, history%entries(i)%p)
     352             :          !WRITE(*,*) "entry ", i, " dist: ",dist
     353           0 :          IF (fp_dist < history%fp_precision .AND. fp_dist < best_fp_match) THEN
     354           0 :             best_fp_match = fp_dist
     355           0 :             found_i = i
     356             :          END IF
     357             :       END DO
     358             : 
     359           0 :       IF (found_i /= found_i_ref) THEN
     360           0 :          WRITE (*, *) found_i, found_i_ref
     361           0 :          CPABORT("verify_history_lookup failed")
     362             :       END IF
     363             : 
     364           0 :    END SUBROUTINE verify_history_lookup
     365             : 
     366             : ! **************************************************************************************************
     367             : !> \brief Finalizes a history.
     368             : !> \param history ...
     369             : !> \author Ole Schuett
     370             : ! **************************************************************************************************
     371           3 :    SUBROUTINE history_finalize(history)
     372             :       TYPE(history_type)                                 :: history
     373             : 
     374             :       INTEGER                                            :: i
     375             : 
     376          21 :       DO i = 1, history%length
     377          18 :          IF (ASSOCIATED(history%entries(i)%p)) &
     378          21 :             DEALLOCATE (history%entries(i)%p)
     379             :       END DO
     380             : 
     381           3 :       DEALLOCATE (history%entries)
     382             : 
     383           3 :    END SUBROUTINE history_finalize
     384             : 
     385           0 : END MODULE glbopt_history

Generated by: LCOV version 1.15