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 754741 : SUBROUTINE cite_reference(key)
85 : INTEGER, INTENT(IN) :: key
86 :
87 754741 : IF (key < 1 .OR. key > max_reference) CPABORT("citation key out of range")
88 :
89 : ! set as cited
90 754741 : thebib(key)%ref%is_cited = .TRUE.
91 :
92 754741 : 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 11087 : SUBROUTINE collect_citations_from_ranks(para_env)
101 : TYPE(mp_para_env_type), INTENT(IN) :: para_env
102 :
103 : INTEGER :: i, t
104 :
105 3536753 : DO i = 1, nbib
106 3525666 : t = 0
107 3525666 : IF (thebib(i)%ref%is_cited) t = 1
108 3525666 : CALL para_env%max(t)
109 3536753 : thebib(i)%ref%is_cited = (t == 1)
110 : END DO
111 :
112 11087 : 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 6669096 : 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 3334548 : IF (nbib + 1 > max_reference) CPABORT("increase max_reference")
144 3334548 : nbib = nbib + 1
145 3334548 : key = nbib
146 :
147 3334548 : ALLOCATE (thebib(key)%ref)
148 :
149 : ! Copy authors.
150 10003644 : ALLOCATE (thebib(key)%ref%authors(SIZE(authors)))
151 15288588 : DO i = 1, SIZE(authors)
152 11954040 : CPASSERT(LEN_TRIM(authors(i)) <= default_string_length)
153 15288588 : thebib(key)%ref%authors(i) = authors(i)
154 : END DO
155 :
156 : ! Copy mandatory attributes.
157 3334548 : thebib(key)%ref%title = TRIM(title)
158 3334548 : thebib(key)%ref%source = TRIM(source)
159 3334548 : thebib(key)%ref%year = year
160 :
161 : ! Copy optional attributes.
162 3334548 : IF (PRESENT(volume)) THEN
163 3240174 : thebib(key)%ref%volume = TRIM(volume)
164 : END IF
165 3334548 : IF (PRESENT(pages)) THEN
166 3292604 : thebib(key)%ref%pages = TRIM(pages)
167 : END IF
168 3334548 : IF (PRESENT(doi)) THEN
169 3303090 : thebib(key)%ref%doi = TRIM(doi)
170 : END IF
171 :
172 3334548 : IF (PRESENT(citation_key)) THEN
173 41944 : citation_key_ = citation_key
174 41944 : CPASSERT(LEN_TRIM(citation_key_) > 4)
175 : ELSE
176 : ! construct a citation_key
177 3292604 : author = authors(1)
178 3292604 : periodloc = INDEX(author, '.', back=.TRUE.)
179 3292604 : IF (periodloc > 0) author = author(periodloc + 1:)
180 3292604 : CPASSERT(LEN_TRIM(author) > 0)
181 3292604 : WRITE (citation_key_, '(A,I4)') TRIM(author), year
182 :
183 : ! avoid special characters in names, just remove them
184 3292604 : mylen = LEN_TRIM(citation_key_)
185 3292604 : ires = 0
186 42143234 : DO I = 1, mylen
187 38850630 : IF (INDEX("0123456789thequickbrownfoxjumpsoverthelazydogTHEQUICKBROWNFOXJUMPSOVERTHELAZYDOG", &
188 3292604 : citation_key_(i:i)) /= 0) THEN
189 35379764 : ires = ires + 1
190 35379764 : tmp = citation_key_(i:i)
191 35379764 : citation_key_(ires:ires) = tmp
192 : END IF
193 : END DO
194 3292604 : citation_key_(ires + 1:) = ""
195 3292604 : 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 3334548 : mylen = LEN_TRIM(citation_key_)
200 3334548 : key_a = citation_key_(1:mylen)
201 3334548 : CALL uppercase(key_a)
202 3334548 : match = 0
203 531860406 : DO I = 1, nbib - 1
204 528525858 : key_b = thebib(I)%ref%citation_key(1:mylen)
205 528525858 : CALL uppercase(key_b)
206 531860406 : IF (key_a == key_b) match = match + 1
207 : END DO
208 3334548 : IF (match > 0) THEN
209 136318 : IF (PRESENT(citation_key)) THEN
210 0 : CPABORT("explicit citation key already exists")
211 : ELSE
212 136318 : citation_key_ = citation_key_(1:mylen)//CHAR(ICHAR('a') + match)
213 : END IF
214 : END IF
215 :
216 : ! finally store it
217 3334548 : thebib(key)%ref%citation_key = citation_key_
218 :
219 3334548 : END SUBROUTINE add_reference
220 :
221 : ! **************************************************************************************************
222 : !> \brief deallocate the bibliography
223 : !> \par History
224 : !> 08.2007 Joost VandeVondele [ ]
225 : ! **************************************************************************************************
226 10486 : SUBROUTINE remove_all_references()
227 : INTEGER :: i
228 :
229 3345034 : DO i = 1, nbib
230 3345034 : DEALLOCATE (thebib(i)%ref)
231 : END DO
232 10486 : 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 5641 : SUBROUTINE print_cited_references(unit)
242 : INTEGER, INTENT(IN) :: unit
243 :
244 : INTEGER :: i
245 5641 : INTEGER, ALLOCATABLE, DIMENSION(:) :: irank, ival
246 :
247 22564 : 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 1799479 : DO i = 1, nbib
252 1793838 : irank(i) = i
253 1799479 : ival(i) = -thebib(i)%ref%year
254 : END DO
255 5641 : CALL sort(ival, nbib, irank)
256 :
257 1799479 : DO i = 1, nbib
258 1799479 : IF (thebib(irank(i))%ref%is_cited) THEN
259 75527 : CALL print_reference_journal(key=irank(i), unit=unit)
260 75527 : WRITE (unit, '(A)') ""
261 : END IF
262 : END DO
263 :
264 5641 : 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 75527 : SUBROUTINE print_reference_journal(key, unit)
275 : INTEGER, INTENT(IN) :: key, unit
276 :
277 75527 : CHARACTER(LEN=:), ALLOCATABLE :: text
278 : CHARACTER(LEN=default_string_length) :: year_str
279 : INTEGER :: iauthor
280 :
281 : ! Authors
282 75527 : text = thebib(key)%ref%authors(1)
283 612774 : DO iauthor = 2, SIZE(thebib(key)%ref%authors)
284 612774 : text = TRIM(text)//", "//thebib(key)%ref%authors(iauthor)
285 : END DO
286 75527 : CALL write_long_text(TRIM(text)//".", unit)
287 :
288 : ! Journal, volume, pages (year).
289 75527 : text = thebib(key)%ref%source
290 75527 : IF (ALLOCATED(thebib(key)%ref%volume)) THEN
291 63765 : text = text//" "//thebib(key)%ref%volume
292 : END IF
293 75527 : IF (ALLOCATED(thebib(key)%ref%pages)) THEN
294 75521 : text = TRIM(text)//", "//thebib(key)%ref%pages
295 : END IF
296 75527 : IF (thebib(key)%ref%year > 0) THEN
297 75527 : CALL integer_to_string(thebib(key)%ref%year, year_str)
298 75527 : text = TRIM(text)//" ("//TRIM(year_str)//")"
299 : END IF
300 75527 : CALL write_long_text(TRIM(text)//".", unit)
301 :
302 : ! Title
303 75527 : CALL write_long_text(thebib(key)%ref%title//".", unit)
304 :
305 : ! DOI
306 75527 : IF (ALLOCATED(thebib(key)%ref%doi)) THEN
307 75049 : WRITE (unit, '(T2,A)') "https://doi.org/"//TRIM(thebib(key)%ref%doi)
308 : END IF
309 :
310 75527 : 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)) THEN
336 0 : WRITE (unit, '(T3,A)') '<DOI>'//TRIM(substitute_special_xml_tokens(thebib(i)%ref%doi))//'</DOI>'
337 : END IF
338 0 : IF (ALLOCATED(thebib(i)%ref%volume)) THEN
339 0 : WRITE (unit, '(T3,A)') '<VOLUME>'//thebib(i)%ref%volume//'</VOLUME>'
340 : END IF
341 0 : IF (ALLOCATED(thebib(i)%ref%pages)) THEN
342 0 : WRITE (unit, '(T3,A)') '<PAGES>'//thebib(i)%ref%pages//'</PAGES>'
343 : END IF
344 0 : IF (thebib(i)%ref%year > 0) THEN
345 0 : WRITE (unit, '(T3,A,I4.4,A)') '<YEAR>', thebib(i)%ref%year, '</YEAR>'
346 : END IF
347 0 : WRITE (unit, '(T2,A)') '</REFERENCE>'
348 : END DO
349 :
350 0 : END SUBROUTINE export_references_as_xml
351 :
352 : ! **************************************************************************************************
353 : !> \brief ...
354 : !> \param key ...
355 : !> \return ...
356 : ! **************************************************************************************************
357 0 : PURE FUNCTION get_citation_key(key) RESULT(res)
358 : INTEGER, INTENT(IN) :: key
359 : CHARACTER(LEN=default_string_length) :: res
360 :
361 0 : res = thebib(key)%ref%citation_key
362 0 : END FUNCTION get_citation_key
363 :
364 : ! **************************************************************************************************
365 : !> \brief Helper routine for print_reference_journal()
366 : !> \param text ...
367 : !> \param unit ...
368 : !> \return ...
369 : !> \author Ole Schuett
370 : ! **************************************************************************************************
371 226581 : SUBROUTINE write_long_text(text, unit)
372 : CHARACTER(LEN=*), INTENT(IN) :: text
373 : INTEGER, INTENT(IN) :: unit
374 :
375 : INTEGER :: a, b
376 :
377 226581 : a = 1; b = -1
378 559628 : DO WHILE (b < LEN(text))
379 333047 : b = next_linebreak(text, pos=a, rowlen=78)
380 333047 : WRITE (unit, '(T2,A)') text(a:b)
381 333047 : a = b + 1
382 : END DO
383 226581 : END SUBROUTINE write_long_text
384 :
385 : ! **************************************************************************************************
386 : !> \brief Helper routine for write_long_text()
387 : !> \param text ...
388 : !> \param pos ...
389 : !> \param rowlen ...
390 : !> \return ...
391 : !> \author Ole Schuett
392 : ! **************************************************************************************************
393 333047 : FUNCTION next_linebreak(text, pos, rowlen) RESULT(ibreak)
394 : CHARACTER(LEN=*), INTENT(IN) :: text
395 : INTEGER, INTENT(IN) :: pos, rowlen
396 : INTEGER :: ibreak
397 :
398 : INTEGER :: i, n
399 :
400 333047 : n = LEN_TRIM(text)
401 333047 : IF (n - pos <= rowlen) THEN
402 : ibreak = n ! remaining text shorter than line
403 : ELSE
404 106466 : i = INDEX(text(pos + 1:pos + 1 + rowlen), " ", BACK=.TRUE.)
405 106466 : IF (i == 0) THEN
406 0 : ibreak = pos + rowlen - 1 ! no space found, break mid-word
407 : ELSE
408 106466 : ibreak = pos + i ! break at space closest to rowlen
409 : END IF
410 : END IF
411 333047 : END FUNCTION next_linebreak
412 :
413 752060 : END MODULE reference_manager
|