(git:d18deda)
Loading...
Searching...
No Matches
reference_manager.F
Go to the documentation of this file.
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! **************************************************************************************************
31 USE util, ONLY: sort
32#include "../base/base_uses.f90"
33
34 IMPLICIT NONE
35
36 PUBLIC :: cite_reference
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
76CONTAINS
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 SUBROUTINE cite_reference(key)
85 INTEGER, INTENT(IN) :: key
86
87 IF (key < 1 .OR. key > max_reference) cpabort("citation key out of range")
88
89 ! set as cited
90 thebib(key)%ref%is_cited = .true.
91
92 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 SUBROUTINE collect_citations_from_ranks(para_env)
101 TYPE(mp_para_env_type), INTENT(IN) :: para_env
102
103 INTEGER :: i, t
104
105 DO i = 1, nbib
106 t = 0
107 IF (thebib(i)%ref%is_cited) t = 1
108 CALL para_env%max(t)
109 thebib(i)%ref%is_cited = (t == 1)
110 END DO
111
112 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 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 IF (nbib + 1 > max_reference) cpabort("increase max_reference")
143 nbib = nbib + 1
144 key = nbib
145
146 ALLOCATE (thebib(key)%ref)
147
148 ! Copy authors.
149 ALLOCATE (thebib(key)%ref%authors(SIZE(authors)))
150 DO i = 1, SIZE(authors)
151 cpassert(len_trim(authors(i)) <= default_string_length)
152 thebib(key)%ref%authors(i) = authors(i)
153 END DO
154
155 ! Copy mandatory attributes.
156 thebib(key)%ref%title = trim(title)
157 thebib(key)%ref%source = trim(source)
158 thebib(key)%ref%year = year
159
160 ! Copy optional attributes.
161 IF (PRESENT(volume)) THEN
162 thebib(key)%ref%volume = trim(volume)
163 END IF
164 IF (PRESENT(pages)) THEN
165 thebib(key)%ref%pages = trim(pages)
166 END IF
167 IF (PRESENT(doi)) THEN
168 thebib(key)%ref%doi = trim(doi)
169 END IF
170
171 ! construct a citation_key
172 author = authors(1)
173 periodloc = index(author, '.', back=.true.)
174 IF (periodloc > 0) author = author(periodloc + 1:)
175 cpassert(len_trim(author) > 0)
176 WRITE (citation_key, '(A,I4)') trim(author), year
177
178 ! avoid special characters in names, just remove them
179 mylen = len_trim(citation_key)
180 ires = 0
181 DO i = 1, mylen
182 IF (index("0123456789thequickbrownfoxjumpsoverthelazydogTHEQUICKBROWNFOXJUMPSOVERTHELAZYDOG", citation_key(i:i)) .NE. 0) THEN
183 ires = ires + 1
184 tmp = citation_key(i:i)
185 citation_key(ires:ires) = tmp
186 END IF
187 END DO
188 citation_key(ires + 1:) = ""
189 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 mylen = len_trim(citation_key)
193 key_a = citation_key(1:mylen)
194 CALL uppercase(key_a)
195 match = 0
196 DO i = 1, nbib - 1
197 key_b = thebib(i)%ref%citation_key(1:mylen)
198 CALL uppercase(key_b)
199 IF (key_a == key_b) match = match + 1
200 END DO
201 IF (match > 0) citation_key = citation_key(1:mylen)//char(ichar('a') + match)
202
203 ! finally store it
204 thebib(key)%ref%citation_key = citation_key
205
206 END SUBROUTINE add_reference
207
208! **************************************************************************************************
209!> \brief deallocate the bibliography
210!> \par History
211!> 08.2007 Joost VandeVondele [ ]
212! **************************************************************************************************
214 INTEGER :: i
215
216 DO i = 1, nbib
217 DEALLOCATE (thebib(i)%ref)
218 END DO
219 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 SUBROUTINE print_cited_references(unit)
229 INTEGER, INTENT(IN) :: unit
230
231 INTEGER :: i
232 INTEGER, ALLOCATABLE, DIMENSION(:) :: irank, ival
233
234 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 DO i = 1, nbib
239 irank(i) = i
240 ival(i) = -thebib(i)%ref%year
241 END DO
242 CALL sort(ival, nbib, irank)
243
244 DO i = 1, nbib
245 IF (thebib(irank(i))%ref%is_cited) THEN
246 CALL print_reference_journal(key=irank(i), unit=unit)
247 WRITE (unit, '(A)') ""
248 END IF
249 END DO
250
251 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 SUBROUTINE print_reference_journal(key, unit)
262 INTEGER, INTENT(IN) :: key, unit
263
264 CHARACTER(LEN=:), ALLOCATABLE :: text
265 CHARACTER(LEN=default_string_length) :: year_str
266 INTEGER :: iauthor
267
268 ! Authors
269 text = thebib(key)%ref%authors(1)
270 DO iauthor = 2, SIZE(thebib(key)%ref%authors)
271 text = trim(text)//", "//thebib(key)%ref%authors(iauthor)
272 END DO
273 CALL write_long_text(trim(text)//".", unit)
274
275 ! Journal, volume, pages (year).
276 text = thebib(key)%ref%source
277 IF (ALLOCATED(thebib(key)%ref%volume)) THEN
278 text = text//" "//thebib(key)%ref%volume
279 END IF
280 IF (ALLOCATED(thebib(key)%ref%pages)) THEN
281 text = trim(text)//", "//thebib(key)%ref%pages
282 END IF
283 IF (thebib(key)%ref%year > 0) THEN
284 CALL integer_to_string(thebib(key)%ref%year, year_str)
285 text = trim(text)//" ("//trim(year_str)//")"
286 END IF
287 CALL write_long_text(trim(text)//".", unit)
288
289 ! Title
290 CALL write_long_text(thebib(key)%ref%title//".", unit)
291
292 ! DOI
293 IF (ALLOCATED(thebib(key)%ref%doi)) THEN
294 WRITE (unit, '(T2,A)') "https://doi.org/"//trim(thebib(key)%ref%doi)
295 END IF
296
297 END SUBROUTINE print_reference_journal
298
299! **************************************************************************************************
300!> \brief Exports all references as XML.
301!> \param unit ...
302!> \author Ole Schuett
303! **************************************************************************************************
305 INTEGER, INTENT(IN) :: unit
306
307 INTEGER :: i, j
308
309 DO i = 1, nbib
310 WRITE (unit, '(T2,A)') '<REFERENCE key="'//trim(thebib(i)%ref%citation_key)//'">'
311
312 ! Authors
313 DO j = 1, SIZE(thebib(i)%ref%authors)
314 WRITE (unit, '(T3,A)') '<AUTHOR>'//trim(thebib(i)%ref%authors(j))//'</AUTHOR>'
315 END DO
316
317 ! Title and source.
318 WRITE (unit, '(T3,A)') '<TITLE>'//thebib(i)%ref%title//'</TITLE>'
319 WRITE (unit, '(T3,A)') '<SOURCE>'//thebib(i)%ref%source//'</SOURCE>'
320
321 ! DOI, volume, pages, year, month.
322 IF (ALLOCATED(thebib(i)%ref%doi)) &
323 WRITE (unit, '(T3,A)') '<DOI>'//trim(substitute_special_xml_tokens(thebib(i)%ref%doi))//'</DOI>'
324 IF (ALLOCATED(thebib(i)%ref%volume)) &
325 WRITE (unit, '(T3,A)') '<VOLUME>'//thebib(i)%ref%volume//'</VOLUME>'
326 IF (ALLOCATED(thebib(i)%ref%pages)) &
327 WRITE (unit, '(T3,A)') '<PAGES>'//thebib(i)%ref%pages//'</PAGES>'
328 IF (thebib(i)%ref%year > 0) &
329 WRITE (unit, '(T3,A,I4.4,A)') '<YEAR>', thebib(i)%ref%year, '</YEAR>'
330 WRITE (unit, '(T2,A)') '</REFERENCE>'
331 END DO
332
333 END SUBROUTINE export_references_as_xml
334
335! **************************************************************************************************
336!> \brief ...
337!> \param key ...
338!> \return ...
339! **************************************************************************************************
340 PURE FUNCTION get_citation_key(key) RESULT(res)
341 INTEGER, INTENT(IN) :: key
342 CHARACTER(LEN=default_string_length) :: res
343
344 res = thebib(key)%ref%citation_key
345 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 SUBROUTINE write_long_text(text, unit)
355 CHARACTER(LEN=*), INTENT(IN) :: text
356 INTEGER, INTENT(IN) :: unit
357
358 INTEGER :: a, b
359
360 a = 1; b = -1
361 DO WHILE (b < len(text))
362 b = next_linebreak(text, pos=a, rowlen=78)
363 WRITE (unit, '(T2,A)') text(a:b)
364 a = b + 1
365 END DO
366 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 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 n = len_trim(text)
384 IF (n - pos <= rowlen) THEN
385 ibreak = n ! remaining text shorter than line
386 ELSE
387 i = index(text(pos + 1:pos + 1 + rowlen), " ", back=.true.)
388 IF (i == 0) THEN
389 ibreak = pos + rowlen - 1 ! no space found, break mid-word
390 ELSE
391 ibreak = pos + i ! break at space closest to rowlen
392 END IF
393 END IF
394 END FUNCTION next_linebreak
395
396END MODULE reference_manager
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public default_string_length
Definition kinds.F:57
Interface to the message passing library MPI.
provides a uniform framework to add references to CP2K cite and output these
pure character(len=default_string_length) function, public get_citation_key(key)
...
subroutine, public add_reference(key, authors, title, source, volume, pages, year, doi)
add a reference to the bibliography
subroutine, public remove_all_references()
deallocate the bibliography
subroutine, public collect_citations_from_ranks(para_env)
Checks for each reference if any mpi-rank has marked it for citation.
subroutine, public cite_reference(key)
marks a given reference as cited.
subroutine, public print_cited_references(unit)
printout of all cited references in the journal format sorted by publication year
subroutine, public export_references_as_xml(unit)
Exports all references as XML.
Utilities for string manipulations.
subroutine, public integer_to_string(inumber, string)
Converts an integer number to a string. The WRITE statement will return an error message,...
character(len=2 *len(inp_string)) function, public substitute_special_xml_tokens(inp_string)
Substitutes the five predefined XML entities: &, <, >, ', and ".
elemental subroutine, public uppercase(string)
Convert all lower case characters in a string to upper case.
All kind of helpful little routines.
Definition util.F:14
stores all the informations relevant to an mpi environment