LCOV - code coverage report
Current view: top level - src/common - reference_manager.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:936074a) Lines: 83.2 % 125 104
Test Date: 2025-12-04 06:27:48 Functions: 61.5 % 13 8

            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 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       580506 :    SUBROUTINE cite_reference(key)
      85              :       INTEGER, INTENT(IN)                                :: key
      86              : 
      87       580506 :       IF (key < 1 .OR. key > max_reference) CPABORT("citation key out of range")
      88              : 
      89              :       ! set as cited
      90       580506 :       thebib(key)%ref%is_cited = .TRUE.
      91              : 
      92       580506 :    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         9881 :    SUBROUTINE collect_citations_from_ranks(para_env)
     101              :       TYPE(mp_para_env_type), INTENT(IN)                 :: para_env
     102              : 
     103              :       INTEGER                                            :: i, t
     104              : 
     105      2816085 :       DO i = 1, nbib
     106      2806204 :          t = 0
     107      2806204 :          IF (thebib(i)%ref%is_cited) t = 1
     108      2806204 :          CALL para_env%max(t)
     109      2816085 :          thebib(i)%ref%is_cited = (t == 1)
     110              :       END DO
     111              : 
     112         9881 :    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              : !> \par History
     125              : !>      08.2007 created [Joost VandeVondele]
     126              : !>      07.2024 complete rewrite [Ole Schuett]
     127              : !> \note
     128              : !>      - see bibliography.F for it use.
     129              : ! **************************************************************************************************
     130      2636656 :    SUBROUTINE add_reference(key, authors, title, source, volume, pages, year, doi)
     131              :       INTEGER, INTENT(OUT)                               :: key
     132              :       CHARACTER(LEN=*), DIMENSION(:), INTENT(IN)         :: authors
     133              :       CHARACTER(LEN=*), INTENT(IN)                       :: title, source
     134              :       CHARACTER(LEN=*), INTENT(IN), OPTIONAL             :: volume, pages
     135              :       INTEGER, INTENT(IN)                                :: year
     136              :       CHARACTER(LEN=*), INTENT(IN), OPTIONAL             :: doi
     137              : 
     138              :       CHARACTER                                          :: tmp
     139              :       CHARACTER(LEN=default_string_length)               :: author, citation_key, key_a, key_b
     140              :       INTEGER                                            :: i, ires, match, mylen, periodloc
     141              : 
     142      2636656 :       IF (nbib + 1 > max_reference) CPABORT("increase max_reference")
     143      2636656 :       nbib = nbib + 1
     144      2636656 :       key = nbib
     145              : 
     146      2636656 :       ALLOCATE (thebib(key)%ref)
     147              : 
     148              :       ! Copy authors.
     149      7909968 :       ALLOCATE (thebib(key)%ref%authors(SIZE(authors)))
     150     11846384 :       DO i = 1, SIZE(authors)
     151      9209728 :          CPASSERT(LEN_TRIM(authors(i)) <= default_string_length)
     152     11846384 :          thebib(key)%ref%authors(i) = authors(i)
     153              :       END DO
     154              : 
     155              :       ! Copy mandatory attributes.
     156      2636656 :       thebib(key)%ref%title = TRIM(title)
     157      2636656 :       thebib(key)%ref%source = TRIM(source)
     158      2636656 :       thebib(key)%ref%year = year
     159              : 
     160              :       ! Copy optional attributes.
     161      2636656 :       IF (PRESENT(volume)) THEN
     162      2553100 :          thebib(key)%ref%volume = TRIM(volume)
     163              :       END IF
     164      2636656 :       IF (PRESENT(pages)) THEN
     165      2590236 :          thebib(key)%ref%pages = TRIM(pages)
     166              :       END IF
     167      2636656 :       IF (PRESENT(doi)) THEN
     168      2599520 :          thebib(key)%ref%doi = TRIM(doi)
     169              :       END IF
     170              : 
     171              :       ! construct a citation_key
     172      2636656 :       author = authors(1)
     173      2636656 :       periodloc = INDEX(author, '.', back=.TRUE.)
     174      2636656 :       IF (periodloc > 0) author = author(periodloc + 1:)
     175      2636656 :       CPASSERT(LEN_TRIM(author) > 0)
     176      2636656 :       WRITE (citation_key, '(A,I4)') TRIM(author), year
     177              : 
     178              :       ! avoid special characters in names, just remove them
     179      2636656 :       mylen = LEN_TRIM(citation_key)
     180      2636656 :       ires = 0
     181     33635932 :       DO I = 1, mylen
     182     33635932 :          IF (INDEX("0123456789thequickbrownfoxjumpsoverthelazydogTHEQUICKBROWNFOXJUMPSOVERTHELAZYDOG", citation_key(i:i)) /= 0) THEN
     183     28186224 :             ires = ires + 1
     184     28186224 :             tmp = citation_key(i:i)
     185     28186224 :             citation_key(ires:ires) = tmp
     186              :          END IF
     187              :       END DO
     188      2636656 :       citation_key(ires + 1:) = ""
     189      2636656 :       CPASSERT(LEN_TRIM(citation_key) > 4) ! At least one character of the author should be left.
     190              : 
     191              :       ! avoid duplicates, search through the list for matches (case-insensitive)
     192      2636656 :       mylen = LEN_TRIM(citation_key)
     193      2636656 :       key_a = citation_key(1:mylen)
     194      2636656 :       CALL uppercase(key_a)
     195      2636656 :       match = 0
     196    375723480 :       DO I = 1, nbib - 1
     197    373086824 :          key_b = thebib(I)%ref%citation_key(1:mylen)
     198    373086824 :          CALL uppercase(key_b)
     199    375723480 :          IF (key_a == key_b) match = match + 1
     200              :       END DO
     201      2636656 :       IF (match > 0) citation_key = citation_key(1:mylen)//CHAR(ICHAR('a') + match)
     202              : 
     203              :       ! finally store it
     204      2636656 :       thebib(key)%ref%citation_key = citation_key
     205              : 
     206      2636656 :    END SUBROUTINE add_reference
     207              : 
     208              : ! **************************************************************************************************
     209              : !> \brief deallocate the bibliography
     210              : !> \par History
     211              : !>      08.2007 Joost VandeVondele [ ]
     212              : ! **************************************************************************************************
     213         9284 :    SUBROUTINE remove_all_references()
     214              :       INTEGER                                            :: i
     215              : 
     216      2645940 :       DO i = 1, nbib
     217      2645940 :          DEALLOCATE (thebib(i)%ref)
     218              :       END DO
     219         9284 :    END SUBROUTINE remove_all_references
     220              : 
     221              : ! **************************************************************************************************
     222              : !> \brief printout of all cited references in the journal format sorted by publication year
     223              : !> \param unit ...
     224              : !> \par History
     225              : !>      08.2007 Joost VandeVondele
     226              : !>      07.2024 Ole Schuett
     227              : ! **************************************************************************************************
     228         5036 :    SUBROUTINE print_cited_references(unit)
     229              :       INTEGER, INTENT(IN)                                :: unit
     230              : 
     231              :       INTEGER                                            :: i
     232         5036 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: irank, ival
     233              : 
     234        20144 :       ALLOCATE (ival(nbib), irank(nbib))
     235              : 
     236              :       ! we'll sort the references wrt to the publication year
     237              :       ! the most recent first, publications without a year get last
     238      1435260 :       DO i = 1, nbib
     239      1430224 :          irank(i) = i
     240      1435260 :          ival(i) = -thebib(i)%ref%year
     241              :       END DO
     242         5036 :       CALL sort(ival, nbib, irank)
     243              : 
     244      1435260 :       DO i = 1, nbib
     245      1435260 :          IF (thebib(irank(i))%ref%is_cited) THEN
     246        63645 :             CALL print_reference_journal(key=irank(i), unit=unit)
     247        63645 :             WRITE (unit, '(A)') ""
     248              :          END IF
     249              :       END DO
     250              : 
     251         5036 :    END SUBROUTINE print_cited_references
     252              : 
     253              : ! **************************************************************************************************
     254              : !> \brief prints a reference in a journal style citation format,
     255              : !>      adding also a DOI link, which is convenient
     256              : !> \param key ...
     257              : !> \param unit ...
     258              : !> \par History
     259              : !>      08.2007 created [Joost VandeVondele]
     260              : ! **************************************************************************************************
     261        63645 :    SUBROUTINE print_reference_journal(key, unit)
     262              :       INTEGER, INTENT(IN)                                :: key, unit
     263              : 
     264        63645 :       CHARACTER(LEN=:), ALLOCATABLE                      :: text
     265              :       CHARACTER(LEN=default_string_length)               :: year_str
     266              :       INTEGER                                            :: iauthor
     267              : 
     268              :       ! Authors
     269        63645 :       text = thebib(key)%ref%authors(1)
     270       366270 :       DO iauthor = 2, SIZE(thebib(key)%ref%authors)
     271       366270 :          text = TRIM(text)//", "//thebib(key)%ref%authors(iauthor)
     272              :       END DO
     273        63645 :       CALL write_long_text(TRIM(text)//".", unit)
     274              : 
     275              :       ! Journal, volume, pages (year).
     276        63645 :       text = thebib(key)%ref%source
     277        63645 :       IF (ALLOCATED(thebib(key)%ref%volume)) THEN
     278        53193 :          text = text//" "//thebib(key)%ref%volume
     279              :       END IF
     280        63645 :       IF (ALLOCATED(thebib(key)%ref%pages)) THEN
     281        63639 :          text = TRIM(text)//", "//thebib(key)%ref%pages
     282              :       END IF
     283        63645 :       IF (thebib(key)%ref%year > 0) THEN
     284        63645 :          CALL integer_to_string(thebib(key)%ref%year, year_str)
     285        63645 :          text = TRIM(text)//" ("//TRIM(year_str)//")"
     286              :       END IF
     287        63645 :       CALL write_long_text(TRIM(text)//".", unit)
     288              : 
     289              :       ! Title
     290        63645 :       CALL write_long_text(thebib(key)%ref%title//".", unit)
     291              : 
     292              :       ! DOI
     293        63645 :       IF (ALLOCATED(thebib(key)%ref%doi)) THEN
     294        63267 :          WRITE (unit, '(T2,A)') "https://doi.org/"//TRIM(thebib(key)%ref%doi)
     295              :       END IF
     296              : 
     297        63645 :    END SUBROUTINE print_reference_journal
     298              : 
     299              : ! **************************************************************************************************
     300              : !> \brief Exports all references as XML.
     301              : !> \param unit ...
     302              : !> \author Ole Schuett
     303              : ! **************************************************************************************************
     304            0 :    SUBROUTINE export_references_as_xml(unit)
     305              :       INTEGER, INTENT(IN)                                :: unit
     306              : 
     307              :       INTEGER                                            :: i, j
     308              : 
     309            0 :       DO i = 1, nbib
     310            0 :          WRITE (unit, '(T2,A)') '<REFERENCE key="'//TRIM(thebib(i)%ref%citation_key)//'">'
     311              : 
     312              :          ! Authors
     313            0 :          DO j = 1, SIZE(thebib(i)%ref%authors)
     314            0 :             WRITE (unit, '(T3,A)') '<AUTHOR>'//TRIM(thebib(i)%ref%authors(j))//'</AUTHOR>'
     315              :          END DO
     316              : 
     317              :          ! Title and source.
     318            0 :          WRITE (unit, '(T3,A)') '<TITLE>'//thebib(i)%ref%title//'</TITLE>'
     319            0 :          WRITE (unit, '(T3,A)') '<SOURCE>'//thebib(i)%ref%source//'</SOURCE>'
     320              : 
     321              :          ! DOI, volume, pages, year, month.
     322            0 :          IF (ALLOCATED(thebib(i)%ref%doi)) &
     323            0 :             WRITE (unit, '(T3,A)') '<DOI>'//TRIM(substitute_special_xml_tokens(thebib(i)%ref%doi))//'</DOI>'
     324            0 :          IF (ALLOCATED(thebib(i)%ref%volume)) &
     325            0 :             WRITE (unit, '(T3,A)') '<VOLUME>'//thebib(i)%ref%volume//'</VOLUME>'
     326            0 :          IF (ALLOCATED(thebib(i)%ref%pages)) &
     327            0 :             WRITE (unit, '(T3,A)') '<PAGES>'//thebib(i)%ref%pages//'</PAGES>'
     328            0 :          IF (thebib(i)%ref%year > 0) &
     329            0 :             WRITE (unit, '(T3,A,I4.4,A)') '<YEAR>', thebib(i)%ref%year, '</YEAR>'
     330            0 :          WRITE (unit, '(T2,A)') '</REFERENCE>'
     331              :       END DO
     332              : 
     333            0 :    END SUBROUTINE export_references_as_xml
     334              : 
     335              : ! **************************************************************************************************
     336              : !> \brief ...
     337              : !> \param key ...
     338              : !> \return ...
     339              : ! **************************************************************************************************
     340            0 :    PURE FUNCTION get_citation_key(key) RESULT(res)
     341              :       INTEGER, INTENT(IN)                                :: key
     342              :       CHARACTER(LEN=default_string_length)               :: res
     343              : 
     344            0 :       res = thebib(key)%ref%citation_key
     345            0 :    END FUNCTION get_citation_key
     346              : 
     347              : ! **************************************************************************************************
     348              : !> \brief Helper routine for print_reference_journal()
     349              : !> \param text ...
     350              : !> \param unit ...
     351              : !> \return ...
     352              : !> \author Ole Schuett
     353              : ! **************************************************************************************************
     354       190935 :    SUBROUTINE write_long_text(text, unit)
     355              :       CHARACTER(LEN=*), INTENT(IN)                       :: text
     356              :       INTEGER, INTENT(IN)                                :: unit
     357              : 
     358              :       INTEGER                                            :: a, b
     359              : 
     360       190935 :       a = 1; b = -1
     361       444783 :       DO WHILE (b < LEN(text))
     362       253848 :          b = next_linebreak(text, pos=a, rowlen=78)
     363       253848 :          WRITE (unit, '(T2,A)') text(a:b)
     364       253848 :          a = b + 1
     365              :       END DO
     366       190935 :    END SUBROUTINE write_long_text
     367              : 
     368              : ! **************************************************************************************************
     369              : !> \brief Helper routine for write_long_text()
     370              : !> \param text ...
     371              : !> \param pos ...
     372              : !> \param rowlen ...
     373              : !> \return ...
     374              : !> \author Ole Schuett
     375              : ! **************************************************************************************************
     376       253848 :    FUNCTION next_linebreak(text, pos, rowlen) RESULT(ibreak)
     377              :       CHARACTER(LEN=*), INTENT(IN)                       :: text
     378              :       INTEGER, INTENT(IN)                                :: pos, rowlen
     379              :       INTEGER                                            :: ibreak
     380              : 
     381              :       INTEGER                                            :: i, n
     382              : 
     383       253848 :       n = LEN_TRIM(text)
     384       253848 :       IF (n - pos <= rowlen) THEN
     385              :          ibreak = n ! remaining text shorter than line
     386              :       ELSE
     387        62913 :          i = INDEX(text(pos + 1:pos + 1 + rowlen), " ", BACK=.TRUE.)
     388        62913 :          IF (i == 0) THEN
     389            0 :             ibreak = pos + rowlen - 1 ! no space found, break mid-word
     390              :          ELSE
     391        62913 :             ibreak = pos + i ! break at space closest to rowlen
     392              :          END IF
     393              :       END IF
     394       253848 :    END FUNCTION next_linebreak
     395              : 
     396       483102 : END MODULE reference_manager
        

Generated by: LCOV version 2.0-1