LCOV - code coverage report
Current view: top level - src/common - reference_manager.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:2fce0f8) Lines: 104 125 83.2 %
Date: 2024-12-21 06:28:57 Functions: 8 13 61.5 %

          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                                   :: 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
      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      557450 :    SUBROUTINE cite_reference(key)
      85             :       INTEGER, INTENT(IN)                                :: key
      86             : 
      87      557450 :       IF (key < 1 .OR. key > max_reference) CPABORT("citation key out of range")
      88             : 
      89             :       ! set as cited
      90      557450 :       thebib(key)%ref%is_cited = .TRUE.
      91             : 
      92      557450 :    END SUBROUTINE
      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        9771 :    SUBROUTINE collect_citations_from_ranks(para_env)
     101             :       TYPE(mp_para_env_type), INTENT(IN)                 :: para_env
     102             : 
     103             :       INTEGER                                            :: i, t
     104             : 
     105     2667483 :       DO i = 1, nbib
     106     2657712 :          t = 0
     107     2657712 :          IF (thebib(i)%ref%is_cited) t = 1
     108     2657712 :          CALL para_env%max(t)
     109     2667483 :          thebib(i)%ref%is_cited = (t == 1)
     110             :       END DO
     111             : 
     112        9771 :    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     2495328 :    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     2495328 :       IF (nbib + 1 > max_reference) CPABORT("increase max_reference")
     143     2495328 :       nbib = nbib + 1
     144     2495328 :       key = nbib
     145             : 
     146     2495328 :       ALLOCATE (thebib(key)%ref)
     147             : 
     148             :       ! Copy authors.
     149     7485984 :       ALLOCATE (thebib(key)%ref%authors(SIZE(authors)))
     150    11183106 :       DO i = 1, SIZE(authors)
     151     8687778 :          CPASSERT(LEN_TRIM(authors(i)) <= default_string_length)
     152    11183106 :          thebib(key)%ref%authors(i) = authors(i)
     153             :       END DO
     154             : 
     155             :       ! Copy mandatory attributes.
     156     2495328 :       thebib(key)%ref%title = TRIM(title)
     157     2495328 :       thebib(key)%ref%source = TRIM(source)
     158     2495328 :       thebib(key)%ref%year = year
     159             : 
     160             :       ! Copy optional attributes.
     161     2495328 :       IF (PRESENT(volume)) THEN
     162     2412762 :          thebib(key)%ref%volume = TRIM(volume)
     163             :       END IF
     164     2495328 :       IF (PRESENT(pages)) THEN
     165     2449458 :          thebib(key)%ref%pages = TRIM(pages)
     166             :       END IF
     167     2495328 :       IF (PRESENT(doi)) THEN
     168     2458632 :          thebib(key)%ref%doi = TRIM(doi)
     169             :       END IF
     170             : 
     171             :       ! construct a citation_key
     172     2495328 :       author = authors(1)
     173     2495328 :       periodloc = INDEX(author, '.', back=.TRUE.)
     174     2495328 :       IF (periodloc > 0) author = author(periodloc + 1:)
     175     2495328 :       CPASSERT(LEN_TRIM(author) > 0)
     176     2495328 :       WRITE (citation_key, '(A,I4)') TRIM(author), year
     177             : 
     178             :       ! avoid special characters in names, just remove them
     179     2495328 :       mylen = LEN_TRIM(citation_key)
     180     2495328 :       ires = 0
     181    31631952 :       DO I = 1, mylen
     182    31631952 :        IF (INDEX("0123456789thequickbrownfoxjumpsoverthelazydogTHEQUICKBROWNFOXJUMPSOVERTHELAZYDOG", citation_key(i:i)) .NE. 0) THEN
     183    26503686 :             ires = ires + 1
     184    26503686 :             tmp = citation_key(i:i)
     185    26503686 :             citation_key(ires:ires) = tmp
     186             :          END IF
     187             :       END DO
     188     2495328 :       citation_key(ires + 1:) = ""
     189     2495328 :       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     2495328 :       mylen = LEN_TRIM(citation_key)
     193     2495328 :       key_a = citation_key(1:mylen)
     194     2495328 :       CALL uppercase(key_a)
     195     2495328 :       match = 0
     196   340612272 :       DO I = 1, nbib - 1
     197   338116944 :          key_b = thebib(I)%ref%citation_key(1:mylen)
     198   338116944 :          CALL uppercase(key_b)
     199   340612272 :          IF (key_a == key_b) match = match + 1
     200             :       END DO
     201     2495328 :       IF (match > 0) citation_key = citation_key(1:mylen)//CHAR(ICHAR('a') + match)
     202             : 
     203             :       ! finally store it
     204     2495328 :       thebib(key)%ref%citation_key = citation_key
     205             : 
     206     2495328 :    END SUBROUTINE add_reference
     207             : 
     208             : ! **************************************************************************************************
     209             : !> \brief deallocate the bibliography
     210             : !> \par History
     211             : !>      08.2007 Joost VandeVondele [ ]
     212             : ! **************************************************************************************************
     213        9174 :    SUBROUTINE remove_all_references()
     214             :       INTEGER                                            :: i
     215             : 
     216     2504502 :       DO i = 1, nbib
     217     2504502 :          DEALLOCATE (thebib(i)%ref)
     218             :       END DO
     219        9174 :    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        4981 :    SUBROUTINE print_cited_references(unit)
     229             :       INTEGER, INTENT(IN)                                :: unit
     230             : 
     231             :       INTEGER                                            :: i
     232        4981 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: irank, ival
     233             : 
     234       19924 :       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     1359813 :       DO i = 1, nbib
     239     1354832 :          irank(i) = i
     240     1359813 :          ival(i) = -thebib(i)%ref%year
     241             :       END DO
     242        4981 :       CALL sort(ival, nbib, irank)
     243             : 
     244     1359813 :       DO i = 1, nbib
     245     1359813 :          IF (thebib(irank(i))%ref%is_cited) THEN
     246       62402 :             CALL print_reference_journal(key=irank(i), unit=unit)
     247       62402 :             WRITE (unit, '(A)') ""
     248             :          END IF
     249             :       END DO
     250             : 
     251        4981 :    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       62402 :    SUBROUTINE print_reference_journal(key, unit)
     262             :       INTEGER, INTENT(IN)                                :: key, unit
     263             : 
     264       62402 :       CHARACTER(LEN=:), ALLOCATABLE                      :: text
     265             :       CHARACTER(LEN=default_string_length)               :: year_str
     266             :       INTEGER                                            :: iauthor
     267             : 
     268             :       ! Authors
     269       62402 :       text = thebib(key)%ref%authors(1)
     270      359287 :       DO iauthor = 2, SIZE(thebib(key)%ref%authors)
     271      359287 :          text = TRIM(text)//", "//thebib(key)%ref%authors(iauthor)
     272             :       END DO
     273       62402 :       CALL write_long_text(TRIM(text)//".", unit)
     274             : 
     275             :       ! Journal, volume, pages (year).
     276       62402 :       text = thebib(key)%ref%source
     277       62402 :       IF (ALLOCATED(thebib(key)%ref%volume)) THEN
     278       52066 :          text = text//" "//thebib(key)%ref%volume
     279             :       END IF
     280       62402 :       IF (ALLOCATED(thebib(key)%ref%pages)) THEN
     281       62395 :          text = TRIM(text)//", "//thebib(key)%ref%pages
     282             :       END IF
     283       62402 :       IF (thebib(key)%ref%year > 0) THEN
     284       62402 :          CALL integer_to_string(thebib(key)%ref%year, year_str)
     285       62402 :          text = TRIM(text)//" ("//TRIM(year_str)//")"
     286             :       END IF
     287       62402 :       CALL write_long_text(TRIM(text)//".", unit)
     288             : 
     289             :       ! Title
     290       62402 :       CALL write_long_text(thebib(key)%ref%title//".", unit)
     291             : 
     292             :       ! DOI
     293       62402 :       IF (ALLOCATED(thebib(key)%ref%doi)) THEN
     294       62030 :          WRITE (unit, '(T2,A)') "https://doi.org/"//TRIM(thebib(key)%ref%doi)
     295             :       END IF
     296             : 
     297       62402 :    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      187206 :    SUBROUTINE write_long_text(text, unit)
     355             :       CHARACTER(LEN=*), INTENT(IN)                       :: text
     356             :       INTEGER, INTENT(IN)                                :: unit
     357             : 
     358             :       INTEGER                                            :: a, b
     359             : 
     360      187206 :       a = 1; b = -1
     361      435971 :       DO WHILE (b < LEN(text))
     362      248765 :          b = next_linebreak(text, pos=a, rowlen=78)
     363      248765 :          WRITE (unit, '(T2,A)') text(a:b)
     364      248765 :          a = b + 1
     365             :       END DO
     366      187206 :    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      248765 :    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      248765 :       n = LEN_TRIM(text)
     384      248765 :       IF (n - pos <= rowlen) THEN
     385             :          ibreak = n ! remaining text shorter than line
     386             :       ELSE
     387       61559 :          i = INDEX(text(pos + 1:pos + 1 + rowlen), " ", BACK=.TRUE.)
     388       61559 :          IF (i == 0) THEN
     389           0 :             ibreak = pos + rowlen - 1 ! no space found, break mid-word
     390             :          ELSE
     391       61559 :             ibreak = pos + i ! break at space closest to rowlen
     392             :          END IF
     393             :       END IF
     394      248765 :    END FUNCTION next_linebreak
     395             : 
     396      473748 : END MODULE reference_manager

Generated by: LCOV version 1.15