LCOV - code coverage report
Current view: top level - src/common - reference_manager.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:96bff0e) Lines: 122 149 81.9 %
Date: 2024-07-27 06:51:10 Functions: 7 12 58.3 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2024 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \brief 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                                   :: issue
      59             :       CHARACTER(LEN=:), ALLOCATABLE                                   :: pages
      60             :       INTEGER                                                         :: year = 0
      61             :       INTEGER                                                         :: month = 0
      62             :       INTEGER                                                         :: day = 0
      63             :       CHARACTER(LEN=:), ALLOCATABLE                                   :: doi
      64             :       ! has this reference been cited in the program run
      65             :       LOGICAL                                                         :: is_cited = .FALSE.
      66             :       ! this is a citation key for output in the reference lists
      67             :       CHARACTER(LEN=default_string_length)                            :: citation_key = ""
      68             :    END TYPE reference_type
      69             : 
      70             :    ! useful to build arrays
      71             :    TYPE reference_p_type
      72             :       TYPE(reference_type), POINTER :: ref => NULL()
      73             :    END TYPE
      74             : 
      75             :    ! the bibliography
      76             :    INTEGER, SAVE :: nbib = 0
      77             :    TYPE(reference_p_type), DIMENSION(max_reference) :: thebib
      78             : 
      79             : CONTAINS
      80             : 
      81             : ! **************************************************************************************************
      82             : !> \brief marks a given reference as cited.
      83             : !> \param key citation key as returned from add_reference
      84             : !> \par History
      85             : !>      XX.2007 created [ ]
      86             : ! **************************************************************************************************
      87      535761 :    SUBROUTINE cite_reference(key)
      88             :       INTEGER, INTENT(IN)                                :: key
      89             : 
      90      535761 :       IF (key < 1 .OR. key > max_reference) CPABORT("citation key out of range")
      91             : 
      92             :       ! set as cited
      93      535761 :       thebib(key)%ref%is_cited = .TRUE.
      94             : 
      95      535761 :    END SUBROUTINE
      96             : 
      97             : ! **************************************************************************************************
      98             : !> \brief Checks for each reference if any mpi-rank has marked it for citation.
      99             : !> \param para_env ...
     100             : !> \par History
     101             : !>      12.2013 created [Ole Schuett]
     102             : ! **************************************************************************************************
     103        9009 :    SUBROUTINE collect_citations_from_ranks(para_env)
     104             :       TYPE(mp_para_env_type), INTENT(IN)                 :: para_env
     105             : 
     106             :       INTEGER                                            :: i, t
     107             : 
     108     2450448 :       DO i = 1, nbib
     109     2441439 :          t = 0
     110     2441439 :          IF (thebib(i)%ref%is_cited) t = 1
     111     2441439 :          CALL para_env%max(t)
     112     2450448 :          thebib(i)%ref%is_cited = (t == 1)
     113             :       END DO
     114             : 
     115        9009 :    END SUBROUTINE collect_citations_from_ranks
     116             : 
     117             : ! **************************************************************************************************
     118             : !> \brief add a reference to the bibliography
     119             : !> \param key output, this handle is needed to cite this reference later
     120             : !> \param authors ...
     121             : !> \param title ...
     122             : !> \param source ...
     123             : !> \param volume ...
     124             : !> \param issue ...
     125             : !> \param pages ...
     126             : !> \param year ...
     127             : !> \param month ...
     128             : !> \param day ...
     129             : !> \param doi ...
     130             : !> \par History
     131             : !>      08.2007 created [Joost VandeVondele]
     132             : !>      07.2024 complete rewrite [Ole Schuett]
     133             : !> \note
     134             : !>      - see bibliography.F for it use.
     135             : ! **************************************************************************************************
     136     2279652 :    SUBROUTINE add_reference(key, authors, title, source, volume, issue, pages, year, month, day, doi)
     137             :       INTEGER, INTENT(OUT)                               :: key
     138             :       CHARACTER(LEN=*), DIMENSION(:), INTENT(IN)         :: authors
     139             :       CHARACTER(LEN=*), INTENT(IN)                       :: title, source
     140             :       CHARACTER(LEN=*), INTENT(IN), OPTIONAL             :: volume, issue, pages
     141             :       INTEGER, INTENT(IN)                                :: year
     142             :       INTEGER, INTENT(IN), OPTIONAL                      :: month, day
     143             :       CHARACTER(LEN=*), INTENT(IN), OPTIONAL             :: doi
     144             : 
     145             :       CHARACTER                                          :: tmp
     146             :       CHARACTER(LEN=default_string_length)               :: author, citation_key, key_a, key_b
     147             :       INTEGER                                            :: commaloc, i, ires, match, mylen
     148             : 
     149     2279652 :       IF (nbib + 1 > max_reference) CPABORT("increase max_reference")
     150     2279652 :       nbib = nbib + 1
     151     2279652 :       key = nbib
     152             : 
     153     2279652 :       ALLOCATE (thebib(key)%ref)
     154             : 
     155             :       ! Copy authors.
     156     6838956 :       ALLOCATE (thebib(key)%ref%authors(SIZE(authors)))
     157    10161696 :       DO i = 1, SIZE(authors)
     158     7882044 :          CPASSERT(LEN_TRIM(authors(i)) <= default_string_length)
     159    10161696 :          thebib(key)%ref%authors(i) = authors(i)
     160             :       END DO
     161             : 
     162             :       ! Copy mandatory attributes.
     163     2279652 :       thebib(key)%ref%title = TRIM(title)
     164     2279652 :       thebib(key)%ref%source = TRIM(source)
     165     2279652 :       thebib(key)%ref%year = year
     166             : 
     167             :       ! Copy optional attributes.
     168     2279652 :       IF (PRESENT(volume)) THEN
     169     2195532 :          thebib(key)%ref%volume = TRIM(volume)
     170             :       END IF
     171     2279652 :       IF (PRESENT(issue)) THEN
     172     1732872 :          thebib(key)%ref%issue = TRIM(issue)
     173             :       END IF
     174     2279652 :       IF (PRESENT(pages)) THEN
     175     1926348 :          thebib(key)%ref%pages = TRIM(pages)
     176             :       END IF
     177     2279652 :       IF (PRESENT(month)) THEN
     178     1261800 :          thebib(key)%ref%month = month
     179             :       END IF
     180     2279652 :       IF (PRESENT(day)) THEN
     181      849612 :          thebib(key)%ref%day = day
     182             :       END IF
     183     2279652 :       IF (PRESENT(doi)) THEN
     184     2246004 :          thebib(key)%ref%doi = TRIM(doi)
     185             :       END IF
     186             : 
     187             :       ! construct a citation_key
     188     2279652 :       author = authors(1)
     189     2279652 :       commaloc = INDEX(author, ',')
     190     2279652 :       IF (commaloc > 0) author = author(1:commaloc - 1)
     191     2279652 :       CPASSERT(LEN_TRIM(author) > 0)
     192     2279652 :       WRITE (citation_key, '(A,I4)') TRIM(author), year
     193             : 
     194             :       ! avoid special characters in names, just remove them
     195     2279652 :       mylen = LEN_TRIM(citation_key)
     196     2279652 :       ires = 0
     197    26691276 :       DO I = 1, mylen
     198    26691276 :        IF (INDEX("0123456789thequickbrownfoxjumpsoverthelazydogTHEQUICKBROWNFOXJUMPSOVERTHELAZYDOG", citation_key(i:i)) .NE. 0) THEN
     199    24285444 :             ires = ires + 1
     200    24285444 :             tmp = citation_key(i:i)
     201    24285444 :             citation_key(ires:ires) = tmp
     202             :          END IF
     203             :       END DO
     204     2279652 :       citation_key(ires + 1:) = ""
     205     2279652 :       CPASSERT(LEN_TRIM(citation_key) > 4) ! At least one character of the author should be left.
     206             : 
     207             :       ! avoid duplicates, search through the list for matches (case-insensitive)
     208     2279652 :       mylen = LEN_TRIM(citation_key)
     209     2279652 :       key_a = citation_key(1:mylen)
     210     2279652 :       CALL uppercase(key_a)
     211     2279652 :       match = 0
     212   310032672 :       DO I = 1, nbib - 1
     213   307753020 :          key_b = thebib(I)%ref%citation_key(1:mylen)
     214   307753020 :          CALL uppercase(key_b)
     215   310032672 :          IF (key_a == key_b) match = match + 1
     216             :       END DO
     217     2279652 :       IF (match > 0) citation_key = citation_key(1:mylen)//CHAR(ICHAR('a') + match)
     218             : 
     219             :       ! finally store it
     220     2279652 :       thebib(key)%ref%citation_key = citation_key
     221             : 
     222     2279652 :    END SUBROUTINE add_reference
     223             : 
     224             : ! **************************************************************************************************
     225             : !> \brief deallocate the bibliography
     226             : !> \par History
     227             : !>      08.2007 Joost VandeVondele [ ]
     228             : ! **************************************************************************************************
     229        8412 :    SUBROUTINE remove_all_references()
     230             :       INTEGER                                            :: i
     231             : 
     232     2288064 :       DO i = 1, nbib
     233     2288064 :          DEALLOCATE (thebib(i)%ref)
     234             :       END DO
     235        8412 :    END SUBROUTINE remove_all_references
     236             : 
     237             : ! **************************************************************************************************
     238             : !> \brief printout of all cited references in the journal format sorted by publication year
     239             : !> \param unit ...
     240             : !> \par History
     241             : !>      08.2007 Joost VandeVondele
     242             : !>      07.2024 Ole Schuett
     243             : ! **************************************************************************************************
     244        4600 :    SUBROUTINE print_cited_references(unit)
     245             :       INTEGER, INTENT(IN)                                :: unit
     246             : 
     247             :       INTEGER                                            :: i
     248        4600 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: irank, ival
     249             : 
     250       18400 :       ALLOCATE (ival(nbib), irank(nbib))
     251             : 
     252             :       ! we'll sort the references wrt to the publication year
     253             :       ! the most recent first, publications without a year get last
     254     1251200 :       DO i = 1, nbib
     255     1246600 :          irank(i) = i
     256     1251200 :          ival(i) = -get_epoch(thebib(i)%ref)
     257             :       END DO
     258        4600 :       CALL sort(ival, nbib, irank)
     259             : 
     260     1251200 :       DO i = 1, nbib
     261     1251200 :          IF (thebib(irank(i))%ref%is_cited) THEN
     262       57307 :             CALL print_reference_journal(key=irank(i), unit=unit)
     263       57307 :             WRITE (unit, '(A)') ""
     264             :          END IF
     265             :       END DO
     266             : 
     267        4600 :    END SUBROUTINE print_cited_references
     268             : 
     269             : ! **************************************************************************************************
     270             : !> \brief prints a reference in a journal style citation format,
     271             : !>      adding also a DOI link, which is convenient
     272             : !> \param key ...
     273             : !> \param unit ...
     274             : !> \par History
     275             : !>      08.2007 created [Joost VandeVondele]
     276             : ! **************************************************************************************************
     277       57307 :    SUBROUTINE print_reference_journal(key, unit)
     278             :       INTEGER, INTENT(IN)                                :: key, unit
     279             : 
     280             :       CHARACTER(LEN=4*default_string_length)             :: journal
     281             :       CHARACTER(LEN=default_string_length)               :: author, year_str
     282             :       INTEGER                                            :: iauthor, ipos_line, ititle, jtitle
     283             : 
     284             :       ! write the author list
     285       57307 :       WRITE (unit, '(T2,A)', ADVANCE="NO") ""
     286       57307 :       ipos_line = 2
     287      381882 :       DO iauthor = 1, SIZE(thebib(key)%ref%authors)
     288      324575 :          author = thebib(key)%ref%authors(iauthor)
     289      324575 :          IF (ipos_line + LEN_TRIM(author) > 71) THEN
     290       38103 :             WRITE (unit, '(A)') ";"
     291       38103 :             WRITE (unit, '(T2,A)', ADVANCE="NO") ""
     292       38103 :             ipos_line = 2
     293             :          ELSE
     294      286472 :             IF (iauthor .NE. 1) WRITE (unit, '(A)', ADVANCE="NO") "; "
     295      286472 :             ipos_line = ipos_line + 2
     296             :          END IF
     297      324575 :          WRITE (unit, '(A)', ADVANCE="NO") TRIM(author)
     298      381882 :          ipos_line = ipos_line + LEN_TRIM(author)
     299             :       END DO
     300       57307 :       IF (iauthor > 0) THEN
     301       57307 :          WRITE (unit, '(A)', ADVANCE="NO") ". "
     302       57307 :          ipos_line = ipos_line + 2
     303             :       END IF
     304             : 
     305             :       ! Journal, volume (issue), pages (year).
     306       57307 :       journal = thebib(key)%ref%source
     307       57307 :       IF (ALLOCATED(thebib(key)%ref%volume)) THEN
     308       47737 :          journal = TRIM(journal)//", "//thebib(key)%ref%volume
     309       47737 :          IF (ALLOCATED(thebib(key)%ref%issue)) THEN
     310       46637 :             journal = TRIM(journal)//" ("//thebib(key)%ref%issue//")"
     311             :          END IF
     312             :       END IF
     313       57307 :       IF (ALLOCATED(thebib(key)%ref%pages)) THEN
     314       48419 :          journal = TRIM(journal)//", "//thebib(key)%ref%pages
     315             :       END IF
     316       57307 :       IF (thebib(key)%ref%year > 0) THEN
     317       57307 :          CALL integer_to_string(thebib(key)%ref%year, year_str)
     318       57307 :          journal = TRIM(journal)//" ("//TRIM(year_str)//")."
     319             :       END IF
     320       57307 :       IF (ipos_line + LEN_TRIM(journal) > 71) THEN
     321       52997 :          WRITE (unit, '(A)') ""
     322       52997 :          WRITE (unit, '(T2,A)', ADVANCE="NO") ""
     323       52997 :          ipos_line = 2
     324             :       END IF
     325       57307 :       IF (ipos_line + LEN_TRIM(journal) > 71) THEN
     326        4618 :          WRITE (unit, '(A)') TRIM(journal(1:69))
     327        4618 :          WRITE (unit, '(T2,A)', ADVANCE="NO") TRIM(journal(70:))
     328             :       ELSE
     329       52689 :          WRITE (unit, '(A)', ADVANCE="NO") TRIM(journal)
     330             :       END IF
     331       57307 :       WRITE (unit, '(T2,A)') ""
     332             : 
     333             :       ! Title
     334      154498 :       DO ititle = 1, LEN(thebib(key)%ref%title), 70
     335       97191 :          IF (ititle .NE. 1) WRITE (unit, '(A)') ""
     336       97191 :          jtitle = MIN(ititle + 69, LEN(thebib(key)%ref%title))
     337      154498 :          WRITE (unit, '(T2,A)', ADVANCE="NO") thebib(key)%ref%title(ititle:jtitle)
     338             :       END DO
     339       57307 :       IF (ititle > 0) WRITE (unit, '(A)') "."
     340             : 
     341             :       ! DOI
     342       57307 :       IF (ALLOCATED(thebib(key)%ref%doi)) THEN
     343       56940 :          WRITE (unit, '(T2,A)') "https://doi.org/"//TRIM(thebib(key)%ref%doi)
     344             :       END IF
     345             : 
     346       57307 :    END SUBROUTINE print_reference_journal
     347             : 
     348             : ! **************************************************************************************************
     349             : !> \brief Exports all references as XML.
     350             : !> \param unit ...
     351             : !> \author Ole Schuett
     352             : ! **************************************************************************************************
     353           0 :    SUBROUTINE export_references_as_xml(unit)
     354             :       INTEGER, INTENT(IN)                                :: unit
     355             : 
     356             :       INTEGER                                            :: i, j
     357             : 
     358           0 :       DO i = 1, nbib
     359           0 :          WRITE (unit, '(T2,A)') '<REFERENCE key="'//TRIM(thebib(i)%ref%citation_key)//'">'
     360             : 
     361             :          ! Authors
     362           0 :          DO j = 1, SIZE(thebib(i)%ref%authors)
     363           0 :             WRITE (unit, '(T3,A)') '<AUTHOR>'//TRIM(thebib(i)%ref%authors(j))//'</AUTHOR>'
     364             :          END DO
     365             : 
     366             :          ! Title and source.
     367           0 :          WRITE (unit, '(T3,A)') '<TITLE>'//thebib(i)%ref%title//'</TITLE>'
     368           0 :          WRITE (unit, '(T3,A)') '<SOURCE>'//thebib(i)%ref%source//'</SOURCE>'
     369             : 
     370             :          ! DOI, volume, issue, pages, year, month.
     371           0 :          IF (ALLOCATED(thebib(i)%ref%doi)) &
     372           0 :             WRITE (unit, '(T3,A)') '<DOI>'//TRIM(substitute_special_xml_tokens(thebib(i)%ref%doi))//'</DOI>'
     373           0 :          IF (ALLOCATED(thebib(i)%ref%volume)) &
     374           0 :             WRITE (unit, '(T3,A)') '<VOLUME>'//thebib(i)%ref%volume//'</VOLUME>'
     375           0 :          IF (ALLOCATED(thebib(i)%ref%issue)) &
     376           0 :             WRITE (unit, '(T3,A)') '<ISSUE>'//thebib(i)%ref%issue//'</ISSUE>'
     377           0 :          IF (ALLOCATED(thebib(i)%ref%pages)) &
     378           0 :             WRITE (unit, '(T3,A)') '<PAGES>'//thebib(i)%ref%pages//'</PAGES>'
     379           0 :          IF (thebib(i)%ref%year > 0) &
     380           0 :             WRITE (unit, '(T3,A,I4.4,A)') '<YEAR>', thebib(i)%ref%year, '</YEAR>'
     381           0 :          IF (thebib(i)%ref%month > 0) &
     382           0 :             WRITE (unit, '(T3,A,I2.2,A)') '<MONTH>', thebib(i)%ref%month, '</MONTH>'
     383           0 :          IF (thebib(i)%ref%day > 0) &
     384           0 :             WRITE (unit, '(T3,A,I2.2,A)') '<DAY>', thebib(i)%ref%day, '</DAY>'
     385             : 
     386           0 :          WRITE (unit, '(T2,A)') '</REFERENCE>'
     387             :       END DO
     388             : 
     389           0 :    END SUBROUTINE export_references_as_xml
     390             : 
     391             : ! **************************************************************************************************
     392             : !> \brief ...
     393             : !> \param key ...
     394             : !> \return ...
     395             : ! **************************************************************************************************
     396           0 :    PURE FUNCTION get_citation_key(key) RESULT(res)
     397             :       INTEGER, INTENT(IN)                                :: key
     398             :       CHARACTER(LEN=default_string_length)               :: res
     399             : 
     400           0 :       res = thebib(key)%ref%citation_key
     401           0 :    END FUNCTION get_citation_key
     402             : 
     403             : ! **************************************************************************************************
     404             : !> \brief This returns something epoch like, but can only be used to order the records
     405             : !>        missing years, months, days are implied zero(1900)
     406             : !> \param ref ...
     407             : !> \return ...
     408             : ! **************************************************************************************************
     409     1246600 :    PURE FUNCTION get_epoch(ref) RESULT(epoch)
     410             :       TYPE(reference_type), INTENT(IN)                   :: ref
     411             :       INTEGER                                            :: epoch
     412             : 
     413     1246600 :       epoch = ref%day + 31*ref%month + 12*31*(ref%year - 1900)
     414             : 
     415     1246600 :    END FUNCTION get_epoch
     416             : 
     417           0 : END MODULE reference_manager

Generated by: LCOV version 1.15