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 688849 : SUBROUTINE cite_reference(key)
85 : INTEGER, INTENT(IN) :: key
86 :
87 688849 : IF (key < 1 .OR. key > max_reference) CPABORT("citation key out of range")
88 :
89 : ! set as cited
90 688849 : thebib(key)%ref%is_cited = .TRUE.
91 :
92 688849 : 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 10419 : SUBROUTINE collect_citations_from_ranks(para_env)
101 : TYPE(mp_para_env_type), INTENT(IN) :: para_env
102 :
103 : INTEGER :: i, t
104 :
105 3115281 : DO i = 1, nbib
106 3104862 : t = 0
107 3104862 : IF (thebib(i)%ref%is_cited) t = 1
108 3104862 : CALL para_env%max(t)
109 3115281 : thebib(i)%ref%is_cited = (t == 1)
110 : END DO
111 :
112 10419 : 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 5854508 : 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 2927254 : IF (nbib + 1 > max_reference) CPABORT("increase max_reference")
144 2927254 : nbib = nbib + 1
145 2927254 : key = nbib
146 :
147 2927254 : ALLOCATE (thebib(key)%ref)
148 :
149 : ! Copy authors.
150 8781762 : ALLOCATE (thebib(key)%ref%authors(SIZE(authors)))
151 13496802 : DO i = 1, SIZE(authors)
152 10569548 : CPASSERT(LEN_TRIM(authors(i)) <= default_string_length)
153 13496802 : thebib(key)%ref%authors(i) = authors(i)
154 : END DO
155 :
156 : ! Copy mandatory attributes.
157 2927254 : thebib(key)%ref%title = TRIM(title)
158 2927254 : thebib(key)%ref%source = TRIM(source)
159 2927254 : thebib(key)%ref%year = year
160 :
161 : ! Copy optional attributes.
162 2927254 : IF (PRESENT(volume)) THEN
163 2838847 : thebib(key)%ref%volume = TRIM(volume)
164 : END IF
165 2927254 : IF (PRESENT(pages)) THEN
166 2887962 : thebib(key)%ref%pages = TRIM(pages)
167 : END IF
168 2927254 : IF (PRESENT(doi)) THEN
169 2897785 : thebib(key)%ref%doi = TRIM(doi)
170 : END IF
171 :
172 2927254 : IF (PRESENT(citation_key)) THEN
173 19646 : citation_key_ = citation_key
174 19646 : CPASSERT(LEN_TRIM(citation_key_) > 4)
175 : ELSE
176 : ! construct a citation_key
177 2907608 : author = authors(1)
178 2907608 : periodloc = INDEX(author, '.', back=.TRUE.)
179 2907608 : IF (periodloc > 0) author = author(periodloc + 1:)
180 2907608 : CPASSERT(LEN_TRIM(author) > 0)
181 2907608 : WRITE (citation_key_, '(A,I4)') TRIM(author), year
182 :
183 : ! avoid special characters in names, just remove them
184 2907608 : mylen = LEN_TRIM(citation_key_)
185 2907608 : ires = 0
186 37199701 : DO I = 1, mylen
187 34292093 : IF (INDEX("0123456789thequickbrownfoxjumpsoverthelazydogTHEQUICKBROWNFOXJUMPSOVERTHELAZYDOG", &
188 2907608 : citation_key_(i:i)) /= 0) THEN
189 31197848 : ires = ires + 1
190 31197848 : tmp = citation_key_(i:i)
191 31197848 : citation_key_(ires:ires) = tmp
192 : END IF
193 : END DO
194 2907608 : citation_key_(ires + 1:) = ""
195 2907608 : 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 2927254 : mylen = LEN_TRIM(citation_key_)
200 2927254 : key_a = citation_key_(1:mylen)
201 2927254 : CALL uppercase(key_a)
202 2927254 : match = 0
203 437624473 : DO I = 1, nbib - 1
204 434697219 : key_b = thebib(I)%ref%citation_key(1:mylen)
205 434697219 : CALL uppercase(key_b)
206 437624473 : IF (key_a == key_b) match = match + 1
207 : END DO
208 2927254 : IF (match > 0) THEN
209 117876 : IF (PRESENT(citation_key)) THEN
210 0 : CPABORT("explicit citation key already exists")
211 : ELSE
212 117876 : citation_key_ = citation_key_(1:mylen)//CHAR(ICHAR('a') + match)
213 : END IF
214 : END IF
215 :
216 : ! finally store it
217 2927254 : thebib(key)%ref%citation_key = citation_key_
218 :
219 2927254 : END SUBROUTINE add_reference
220 :
221 : ! **************************************************************************************************
222 : !> \brief deallocate the bibliography
223 : !> \par History
224 : !> 08.2007 Joost VandeVondele [ ]
225 : ! **************************************************************************************************
226 9822 : SUBROUTINE remove_all_references()
227 : INTEGER :: i
228 :
229 2936778 : DO i = 1, nbib
230 2936778 : DEALLOCATE (thebib(i)%ref)
231 : END DO
232 9822 : 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 5305 : SUBROUTINE print_cited_references(unit)
242 : INTEGER, INTENT(IN) :: unit
243 :
244 : INTEGER :: i
245 5305 : INTEGER, ALLOCATABLE, DIMENSION(:) :: irank, ival
246 :
247 21220 : 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 1586195 : DO i = 1, nbib
252 1580890 : irank(i) = i
253 1586195 : ival(i) = -thebib(i)%ref%year
254 : END DO
255 5305 : CALL sort(ival, nbib, irank)
256 :
257 1586195 : DO i = 1, nbib
258 1586195 : IF (thebib(irank(i))%ref%is_cited) THEN
259 69955 : CALL print_reference_journal(key=irank(i), unit=unit)
260 69955 : WRITE (unit, '(A)') ""
261 : END IF
262 : END DO
263 :
264 5305 : 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 69955 : SUBROUTINE print_reference_journal(key, unit)
275 : INTEGER, INTENT(IN) :: key, unit
276 :
277 69955 : CHARACTER(LEN=:), ALLOCATABLE :: text
278 : CHARACTER(LEN=default_string_length) :: year_str
279 : INTEGER :: iauthor
280 :
281 : ! Authors
282 69955 : text = thebib(key)%ref%authors(1)
283 571526 : DO iauthor = 2, SIZE(thebib(key)%ref%authors)
284 571526 : text = TRIM(text)//", "//thebib(key)%ref%authors(iauthor)
285 : END DO
286 69955 : CALL write_long_text(TRIM(text)//".", unit)
287 :
288 : ! Journal, volume, pages (year).
289 69955 : text = thebib(key)%ref%source
290 69955 : IF (ALLOCATED(thebib(key)%ref%volume)) THEN
291 58886 : text = text//" "//thebib(key)%ref%volume
292 : END IF
293 69955 : IF (ALLOCATED(thebib(key)%ref%pages)) THEN
294 69949 : text = TRIM(text)//", "//thebib(key)%ref%pages
295 : END IF
296 69955 : IF (thebib(key)%ref%year > 0) THEN
297 69955 : CALL integer_to_string(thebib(key)%ref%year, year_str)
298 69955 : text = TRIM(text)//" ("//TRIM(year_str)//")"
299 : END IF
300 69955 : CALL write_long_text(TRIM(text)//".", unit)
301 :
302 : ! Title
303 69955 : CALL write_long_text(thebib(key)%ref%title//".", unit)
304 :
305 : ! DOI
306 69955 : IF (ALLOCATED(thebib(key)%ref%doi)) THEN
307 69498 : WRITE (unit, '(T2,A)') "https://doi.org/"//TRIM(thebib(key)%ref%doi)
308 : END IF
309 :
310 69955 : 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 209865 : SUBROUTINE write_long_text(text, unit)
368 : CHARACTER(LEN=*), INTENT(IN) :: text
369 : INTEGER, INTENT(IN) :: unit
370 :
371 : INTEGER :: a, b
372 :
373 209865 : a = 1; b = -1
374 518869 : DO WHILE (b < LEN(text))
375 309004 : b = next_linebreak(text, pos=a, rowlen=78)
376 309004 : WRITE (unit, '(T2,A)') text(a:b)
377 309004 : a = b + 1
378 : END DO
379 209865 : 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 309004 : 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 309004 : n = LEN_TRIM(text)
397 309004 : IF (n - pos <= rowlen) THEN
398 : ibreak = n ! remaining text shorter than line
399 : ELSE
400 99139 : i = INDEX(text(pos + 1:pos + 1 + rowlen), " ", BACK=.TRUE.)
401 99139 : IF (i == 0) THEN
402 0 : ibreak = pos + rowlen - 1 ! no space found, break mid-word
403 : ELSE
404 99139 : ibreak = pos + i ! break at space closest to rowlen
405 : END IF
406 : END IF
407 309004 : END FUNCTION next_linebreak
408 :
409 700361 : END MODULE reference_manager
|