LCOV - code coverage report
Current view: top level - src/common - reference_manager.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:cccd2f3) Lines: 83.3 % 132 110
Test Date: 2026-05-06 07:07:47 Functions: 61.5 % 13 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 provides a uniform framework to add references to CP2K
      10              : !>      cite and output these
      11              : !> \note
      12              : !>      references need to be input using the ISI citation format, because it is
      13              : !>      uniform, easy to parse, and can be exported for example from web of science
      14              : !>      furthermore, it can be easily converted to and from using the bibutils tools
      15              : !>      a collection of easy to use conversion programs that can be found at
      16              : !>      http://www.scripps.edu/~cdputnam/software/bibutils/
      17              : !>      by Chris Putnam
      18              : !>
      19              : !>      see thebibliography.F on how to add references easily
      20              : !> \par History
      21              : !>      08.2007 [Joost VandeVondele]
      22              : !>      07.2024 [Ole Schuett]
      23              : !> \author Joost VandeVondele
      24              : ! **************************************************************************************************
      25              : MODULE reference_manager
      26              :    USE kinds,                           ONLY: default_string_length
      27              :    USE message_passing,                 ONLY: mp_para_env_type
      28              :    USE string_utilities,                ONLY: integer_to_string,&
      29              :                                               substitute_special_xml_tokens,&
      30              :                                               uppercase
      31              :    USE util,                            ONLY: sort
      32              : #include "../base/base_uses.f90"
      33              : 
      34              :    IMPLICIT NONE
      35              : 
      36              :    PUBLIC :: cite_reference
      37              :    PUBLIC :: collect_citations_from_ranks
      38              :    PUBLIC :: print_cited_references
      39              :    PUBLIC :: export_references_as_xml
      40              : 
      41              :    PUBLIC :: add_reference          ! use this one only in bibliography.F
      42              :    PUBLIC :: remove_all_references  ! use only in f77_interface.F
      43              :    PUBLIC :: get_citation_key       ! a string key describing the reference (e.g. Kohn1965b)
      44              : 
      45              :    PRIVATE
      46              : 
      47              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'reference_manager'
      48              : 
      49              :    ! maximum number of reference that can be added
      50              :    INTEGER, PARAMETER :: max_reference = 1024
      51              : 
      52              :    TYPE reference_type
      53              :       PRIVATE
      54              :       CHARACTER(LEN=default_string_length), DIMENSION(:), ALLOCATABLE :: authors
      55              :       CHARACTER(LEN=:), ALLOCATABLE                                   :: title
      56              :       CHARACTER(LEN=:), ALLOCATABLE                                   :: source
      57              :       CHARACTER(LEN=:), ALLOCATABLE                                   :: volume
      58              :       CHARACTER(LEN=:), ALLOCATABLE                                   :: pages
      59              :       INTEGER                                                         :: year = 0
      60              :       CHARACTER(LEN=:), ALLOCATABLE                                   :: doi
      61              :       ! has this reference been cited in the program run
      62              :       LOGICAL                                                         :: is_cited = .FALSE.
      63              :       ! this is a citation key for output in the reference lists
      64              :       CHARACTER(LEN=default_string_length)                            :: citation_key = ""
      65              :    END TYPE reference_type
      66              : 
      67              :    ! useful to build arrays
      68              :    TYPE reference_p_type
      69              :       TYPE(reference_type), POINTER :: ref => NULL()
      70              :    END TYPE reference_p_type
      71              : 
      72              :    ! the bibliography
      73              :    INTEGER, SAVE :: nbib = 0
      74              :    TYPE(reference_p_type), DIMENSION(max_reference) :: thebib
      75              : 
      76              : CONTAINS
      77              : 
      78              : ! **************************************************************************************************
      79              : !> \brief marks a given reference as cited.
      80              : !> \param key citation key as returned from add_reference
      81              : !> \par History
      82              : !>      XX.2007 created [ ]
      83              : ! **************************************************************************************************
      84       688849 :    SUBROUTINE cite_reference(key)
      85              :       INTEGER, INTENT(IN)                                :: key
      86              : 
      87       688849 :       IF (key < 1 .OR. key > max_reference) CPABORT("citation key out of range")
      88              : 
      89              :       ! set as cited
      90       688849 :       thebib(key)%ref%is_cited = .TRUE.
      91              : 
      92       688849 :    END SUBROUTINE cite_reference
      93              : 
      94              : ! **************************************************************************************************
      95              : !> \brief Checks for each reference if any mpi-rank has marked it for citation.
      96              : !> \param para_env ...
      97              : !> \par History
      98              : !>      12.2013 created [Ole Schuett]
      99              : ! **************************************************************************************************
     100        10419 :    SUBROUTINE collect_citations_from_ranks(para_env)
     101              :       TYPE(mp_para_env_type), INTENT(IN)                 :: para_env
     102              : 
     103              :       INTEGER                                            :: i, t
     104              : 
     105      3115281 :       DO i = 1, nbib
     106      3104862 :          t = 0
     107      3104862 :          IF (thebib(i)%ref%is_cited) t = 1
     108      3104862 :          CALL para_env%max(t)
     109      3115281 :          thebib(i)%ref%is_cited = (t == 1)
     110              :       END DO
     111              : 
     112        10419 :    END SUBROUTINE collect_citations_from_ranks
     113              : 
     114              : ! **************************************************************************************************
     115              : !> \brief add a reference to the bibliography
     116              : !> \param key output, this handle is needed to cite this reference later
     117              : !> \param authors ...
     118              : !> \param title ...
     119              : !> \param source ...
     120              : !> \param volume ...
     121              : !> \param pages ...
     122              : !> \param year ...
     123              : !> \param doi ...
     124              : !> \param citation_key ...
     125              : !> \par History
     126              : !>      08.2007 created [Joost VandeVondele]
     127              : !>      07.2024 complete rewrite [Ole Schuett]
     128              : !> \note
     129              : !>      - see bibliography.F for it use.
     130              : ! **************************************************************************************************
     131      5854508 :    SUBROUTINE add_reference(key, authors, title, source, volume, pages, year, doi, citation_key)
     132              :       INTEGER, INTENT(OUT)                               :: key
     133              :       CHARACTER(LEN=*), DIMENSION(:), INTENT(IN)         :: authors
     134              :       CHARACTER(LEN=*), INTENT(IN)                       :: title, source
     135              :       CHARACTER(LEN=*), INTENT(IN), OPTIONAL             :: volume, pages
     136              :       INTEGER, INTENT(IN)                                :: year
     137              :       CHARACTER(LEN=*), INTENT(IN), OPTIONAL             :: doi, citation_key
     138              : 
     139              :       CHARACTER                                          :: tmp
     140              :       CHARACTER(LEN=default_string_length)               :: author, citation_key_, key_a, key_b
     141              :       INTEGER                                            :: i, ires, match, mylen, periodloc
     142              : 
     143      2927254 :       IF (nbib + 1 > max_reference) CPABORT("increase max_reference")
     144      2927254 :       nbib = nbib + 1
     145      2927254 :       key = nbib
     146              : 
     147      2927254 :       ALLOCATE (thebib(key)%ref)
     148              : 
     149              :       ! Copy authors.
     150      8781762 :       ALLOCATE (thebib(key)%ref%authors(SIZE(authors)))
     151     13496802 :       DO i = 1, SIZE(authors)
     152     10569548 :          CPASSERT(LEN_TRIM(authors(i)) <= default_string_length)
     153     13496802 :          thebib(key)%ref%authors(i) = authors(i)
     154              :       END DO
     155              : 
     156              :       ! Copy mandatory attributes.
     157      2927254 :       thebib(key)%ref%title = TRIM(title)
     158      2927254 :       thebib(key)%ref%source = TRIM(source)
     159      2927254 :       thebib(key)%ref%year = year
     160              : 
     161              :       ! Copy optional attributes.
     162      2927254 :       IF (PRESENT(volume)) THEN
     163      2838847 :          thebib(key)%ref%volume = TRIM(volume)
     164              :       END IF
     165      2927254 :       IF (PRESENT(pages)) THEN
     166      2887962 :          thebib(key)%ref%pages = TRIM(pages)
     167              :       END IF
     168      2927254 :       IF (PRESENT(doi)) THEN
     169      2897785 :          thebib(key)%ref%doi = TRIM(doi)
     170              :       END IF
     171              : 
     172      2927254 :       IF (PRESENT(citation_key)) THEN
     173        19646 :          citation_key_ = citation_key
     174        19646 :          CPASSERT(LEN_TRIM(citation_key_) > 4)
     175              :       ELSE
     176              :          ! construct a citation_key
     177      2907608 :          author = authors(1)
     178      2907608 :          periodloc = INDEX(author, '.', back=.TRUE.)
     179      2907608 :          IF (periodloc > 0) author = author(periodloc + 1:)
     180      2907608 :          CPASSERT(LEN_TRIM(author) > 0)
     181      2907608 :          WRITE (citation_key_, '(A,I4)') TRIM(author), year
     182              : 
     183              :          ! avoid special characters in names, just remove them
     184      2907608 :          mylen = LEN_TRIM(citation_key_)
     185      2907608 :          ires = 0
     186     37199701 :          DO I = 1, mylen
     187     34292093 :             IF (INDEX("0123456789thequickbrownfoxjumpsoverthelazydogTHEQUICKBROWNFOXJUMPSOVERTHELAZYDOG", &
     188      2907608 :                       citation_key_(i:i)) /= 0) THEN
     189     31197848 :                ires = ires + 1
     190     31197848 :                tmp = citation_key_(i:i)
     191     31197848 :                citation_key_(ires:ires) = tmp
     192              :             END IF
     193              :          END DO
     194      2907608 :          citation_key_(ires + 1:) = ""
     195      2907608 :          CPASSERT(LEN_TRIM(citation_key_) > 4) ! At least one character of the author should be left.
     196              :       END IF
     197              : 
     198              :       ! avoid duplicates, search through the list for matches (case-insensitive)
     199      2927254 :       mylen = LEN_TRIM(citation_key_)
     200      2927254 :       key_a = citation_key_(1:mylen)
     201      2927254 :       CALL uppercase(key_a)
     202      2927254 :       match = 0
     203    437624473 :       DO I = 1, nbib - 1
     204    434697219 :          key_b = thebib(I)%ref%citation_key(1:mylen)
     205    434697219 :          CALL uppercase(key_b)
     206    437624473 :          IF (key_a == key_b) match = match + 1
     207              :       END DO
     208      2927254 :       IF (match > 0) THEN
     209       117876 :          IF (PRESENT(citation_key)) THEN
     210            0 :             CPABORT("explicit citation key already exists")
     211              :          ELSE
     212       117876 :             citation_key_ = citation_key_(1:mylen)//CHAR(ICHAR('a') + match)
     213              :          END IF
     214              :       END IF
     215              : 
     216              :       ! finally store it
     217      2927254 :       thebib(key)%ref%citation_key = citation_key_
     218              : 
     219      2927254 :    END SUBROUTINE add_reference
     220              : 
     221              : ! **************************************************************************************************
     222              : !> \brief deallocate the bibliography
     223              : !> \par History
     224              : !>      08.2007 Joost VandeVondele [ ]
     225              : ! **************************************************************************************************
     226         9822 :    SUBROUTINE remove_all_references()
     227              :       INTEGER                                            :: i
     228              : 
     229      2936778 :       DO i = 1, nbib
     230      2936778 :          DEALLOCATE (thebib(i)%ref)
     231              :       END DO
     232         9822 :    END SUBROUTINE remove_all_references
     233              : 
     234              : ! **************************************************************************************************
     235              : !> \brief printout of all cited references in the journal format sorted by publication year
     236              : !> \param unit ...
     237              : !> \par History
     238              : !>      08.2007 Joost VandeVondele
     239              : !>      07.2024 Ole Schuett
     240              : ! **************************************************************************************************
     241         5305 :    SUBROUTINE print_cited_references(unit)
     242              :       INTEGER, INTENT(IN)                                :: unit
     243              : 
     244              :       INTEGER                                            :: i
     245         5305 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: irank, ival
     246              : 
     247        21220 :       ALLOCATE (ival(nbib), irank(nbib))
     248              : 
     249              :       ! we'll sort the references wrt to the publication year
     250              :       ! the most recent first, publications without a year get last
     251      1586195 :       DO i = 1, nbib
     252      1580890 :          irank(i) = i
     253      1586195 :          ival(i) = -thebib(i)%ref%year
     254              :       END DO
     255         5305 :       CALL sort(ival, nbib, irank)
     256              : 
     257      1586195 :       DO i = 1, nbib
     258      1586195 :          IF (thebib(irank(i))%ref%is_cited) THEN
     259        69955 :             CALL print_reference_journal(key=irank(i), unit=unit)
     260        69955 :             WRITE (unit, '(A)') ""
     261              :          END IF
     262              :       END DO
     263              : 
     264         5305 :    END SUBROUTINE print_cited_references
     265              : 
     266              : ! **************************************************************************************************
     267              : !> \brief prints a reference in a journal style citation format,
     268              : !>      adding also a DOI link, which is convenient
     269              : !> \param key ...
     270              : !> \param unit ...
     271              : !> \par History
     272              : !>      08.2007 created [Joost VandeVondele]
     273              : ! **************************************************************************************************
     274        69955 :    SUBROUTINE print_reference_journal(key, unit)
     275              :       INTEGER, INTENT(IN)                                :: key, unit
     276              : 
     277        69955 :       CHARACTER(LEN=:), ALLOCATABLE                      :: text
     278              :       CHARACTER(LEN=default_string_length)               :: year_str
     279              :       INTEGER                                            :: iauthor
     280              : 
     281              :       ! Authors
     282        69955 :       text = thebib(key)%ref%authors(1)
     283       571526 :       DO iauthor = 2, SIZE(thebib(key)%ref%authors)
     284       571526 :          text = TRIM(text)//", "//thebib(key)%ref%authors(iauthor)
     285              :       END DO
     286        69955 :       CALL write_long_text(TRIM(text)//".", unit)
     287              : 
     288              :       ! Journal, volume, pages (year).
     289        69955 :       text = thebib(key)%ref%source
     290        69955 :       IF (ALLOCATED(thebib(key)%ref%volume)) THEN
     291        58886 :          text = text//" "//thebib(key)%ref%volume
     292              :       END IF
     293        69955 :       IF (ALLOCATED(thebib(key)%ref%pages)) THEN
     294        69949 :          text = TRIM(text)//", "//thebib(key)%ref%pages
     295              :       END IF
     296        69955 :       IF (thebib(key)%ref%year > 0) THEN
     297        69955 :          CALL integer_to_string(thebib(key)%ref%year, year_str)
     298        69955 :          text = TRIM(text)//" ("//TRIM(year_str)//")"
     299              :       END IF
     300        69955 :       CALL write_long_text(TRIM(text)//".", unit)
     301              : 
     302              :       ! Title
     303        69955 :       CALL write_long_text(thebib(key)%ref%title//".", unit)
     304              : 
     305              :       ! DOI
     306        69955 :       IF (ALLOCATED(thebib(key)%ref%doi)) THEN
     307        69498 :          WRITE (unit, '(T2,A)') "https://doi.org/"//TRIM(thebib(key)%ref%doi)
     308              :       END IF
     309              : 
     310        69955 :    END SUBROUTINE print_reference_journal
     311              : 
     312              : ! **************************************************************************************************
     313              : !> \brief Exports all references as XML.
     314              : !> \param unit ...
     315              : !> \author Ole Schuett
     316              : ! **************************************************************************************************
     317            0 :    SUBROUTINE export_references_as_xml(unit)
     318              :       INTEGER, INTENT(IN)                                :: unit
     319              : 
     320              :       INTEGER                                            :: i, j
     321              : 
     322            0 :       DO i = 1, nbib
     323            0 :          WRITE (unit, '(T2,A)') '<REFERENCE key="'//TRIM(thebib(i)%ref%citation_key)//'">'
     324              : 
     325              :          ! Authors
     326            0 :          DO j = 1, SIZE(thebib(i)%ref%authors)
     327            0 :             WRITE (unit, '(T3,A)') '<AUTHOR>'//TRIM(thebib(i)%ref%authors(j))//'</AUTHOR>'
     328              :          END DO
     329              : 
     330              :          ! Title and source.
     331            0 :          WRITE (unit, '(T3,A)') '<TITLE>'//thebib(i)%ref%title//'</TITLE>'
     332            0 :          WRITE (unit, '(T3,A)') '<SOURCE>'//thebib(i)%ref%source//'</SOURCE>'
     333              : 
     334              :          ! DOI, volume, pages, year, month.
     335            0 :          IF (ALLOCATED(thebib(i)%ref%doi)) &
     336            0 :             WRITE (unit, '(T3,A)') '<DOI>'//TRIM(substitute_special_xml_tokens(thebib(i)%ref%doi))//'</DOI>'
     337            0 :          IF (ALLOCATED(thebib(i)%ref%volume)) &
     338            0 :             WRITE (unit, '(T3,A)') '<VOLUME>'//thebib(i)%ref%volume//'</VOLUME>'
     339            0 :          IF (ALLOCATED(thebib(i)%ref%pages)) &
     340            0 :             WRITE (unit, '(T3,A)') '<PAGES>'//thebib(i)%ref%pages//'</PAGES>'
     341            0 :          IF (thebib(i)%ref%year > 0) &
     342            0 :             WRITE (unit, '(T3,A,I4.4,A)') '<YEAR>', thebib(i)%ref%year, '</YEAR>'
     343            0 :          WRITE (unit, '(T2,A)') '</REFERENCE>'
     344              :       END DO
     345              : 
     346            0 :    END SUBROUTINE export_references_as_xml
     347              : 
     348              : ! **************************************************************************************************
     349              : !> \brief ...
     350              : !> \param key ...
     351              : !> \return ...
     352              : ! **************************************************************************************************
     353            0 :    PURE FUNCTION get_citation_key(key) RESULT(res)
     354              :       INTEGER, INTENT(IN)                                :: key
     355              :       CHARACTER(LEN=default_string_length)               :: res
     356              : 
     357            0 :       res = thebib(key)%ref%citation_key
     358            0 :    END FUNCTION get_citation_key
     359              : 
     360              : ! **************************************************************************************************
     361              : !> \brief Helper routine for print_reference_journal()
     362              : !> \param text ...
     363              : !> \param unit ...
     364              : !> \return ...
     365              : !> \author Ole Schuett
     366              : ! **************************************************************************************************
     367       209865 :    SUBROUTINE write_long_text(text, unit)
     368              :       CHARACTER(LEN=*), INTENT(IN)                       :: text
     369              :       INTEGER, INTENT(IN)                                :: unit
     370              : 
     371              :       INTEGER                                            :: a, b
     372              : 
     373       209865 :       a = 1; b = -1
     374       518869 :       DO WHILE (b < LEN(text))
     375       309004 :          b = next_linebreak(text, pos=a, rowlen=78)
     376       309004 :          WRITE (unit, '(T2,A)') text(a:b)
     377       309004 :          a = b + 1
     378              :       END DO
     379       209865 :    END SUBROUTINE write_long_text
     380              : 
     381              : ! **************************************************************************************************
     382              : !> \brief Helper routine for write_long_text()
     383              : !> \param text ...
     384              : !> \param pos ...
     385              : !> \param rowlen ...
     386              : !> \return ...
     387              : !> \author Ole Schuett
     388              : ! **************************************************************************************************
     389       309004 :    FUNCTION next_linebreak(text, pos, rowlen) RESULT(ibreak)
     390              :       CHARACTER(LEN=*), INTENT(IN)                       :: text
     391              :       INTEGER, INTENT(IN)                                :: pos, rowlen
     392              :       INTEGER                                            :: ibreak
     393              : 
     394              :       INTEGER                                            :: i, n
     395              : 
     396       309004 :       n = LEN_TRIM(text)
     397       309004 :       IF (n - pos <= rowlen) THEN
     398              :          ibreak = n ! remaining text shorter than line
     399              :       ELSE
     400        99139 :          i = INDEX(text(pos + 1:pos + 1 + rowlen), " ", BACK=.TRUE.)
     401        99139 :          IF (i == 0) THEN
     402            0 :             ibreak = pos + rowlen - 1 ! no space found, break mid-word
     403              :          ELSE
     404        99139 :             ibreak = pos + i ! break at space closest to rowlen
     405              :          END IF
     406              :       END IF
     407       309004 :    END FUNCTION next_linebreak
     408              : 
     409       700361 : END MODULE reference_manager
        

Generated by: LCOV version 2.0-1