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