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 :: issue
59 : CHARACTER(LEN=:), ALLOCATABLE :: pages
60 : INTEGER :: year = 0
61 : INTEGER :: month = 0
62 : INTEGER :: day = 0
63 : CHARACTER(LEN=:), ALLOCATABLE :: doi
64 : ! has this reference been cited in the program run
65 : LOGICAL :: is_cited = .FALSE.
66 : ! this is a citation key for output in the reference lists
67 : CHARACTER(LEN=default_string_length) :: citation_key = ""
68 : END TYPE reference_type
69 :
70 : ! useful to build arrays
71 : TYPE reference_p_type
72 : TYPE(reference_type), POINTER :: ref => NULL()
73 : END TYPE
74 :
75 : ! the bibliography
76 : INTEGER, SAVE :: nbib = 0
77 : TYPE(reference_p_type), DIMENSION(max_reference) :: thebib
78 :
79 : CONTAINS
80 :
81 : ! **************************************************************************************************
82 : !> \brief marks a given reference as cited.
83 : !> \param key citation key as returned from add_reference
84 : !> \par History
85 : !> XX.2007 created [ ]
86 : ! **************************************************************************************************
87 535761 : SUBROUTINE cite_reference(key)
88 : INTEGER, INTENT(IN) :: key
89 :
90 535761 : IF (key < 1 .OR. key > max_reference) CPABORT("citation key out of range")
91 :
92 : ! set as cited
93 535761 : thebib(key)%ref%is_cited = .TRUE.
94 :
95 535761 : END SUBROUTINE
96 :
97 : ! **************************************************************************************************
98 : !> \brief Checks for each reference if any mpi-rank has marked it for citation.
99 : !> \param para_env ...
100 : !> \par History
101 : !> 12.2013 created [Ole Schuett]
102 : ! **************************************************************************************************
103 9009 : SUBROUTINE collect_citations_from_ranks(para_env)
104 : TYPE(mp_para_env_type), INTENT(IN) :: para_env
105 :
106 : INTEGER :: i, t
107 :
108 2450448 : DO i = 1, nbib
109 2441439 : t = 0
110 2441439 : IF (thebib(i)%ref%is_cited) t = 1
111 2441439 : CALL para_env%max(t)
112 2450448 : thebib(i)%ref%is_cited = (t == 1)
113 : END DO
114 :
115 9009 : END SUBROUTINE collect_citations_from_ranks
116 :
117 : ! **************************************************************************************************
118 : !> \brief add a reference to the bibliography
119 : !> \param key output, this handle is needed to cite this reference later
120 : !> \param authors ...
121 : !> \param title ...
122 : !> \param source ...
123 : !> \param volume ...
124 : !> \param issue ...
125 : !> \param pages ...
126 : !> \param year ...
127 : !> \param month ...
128 : !> \param day ...
129 : !> \param doi ...
130 : !> \par History
131 : !> 08.2007 created [Joost VandeVondele]
132 : !> 07.2024 complete rewrite [Ole Schuett]
133 : !> \note
134 : !> - see bibliography.F for it use.
135 : ! **************************************************************************************************
136 2279652 : SUBROUTINE add_reference(key, authors, title, source, volume, issue, pages, year, month, day, doi)
137 : INTEGER, INTENT(OUT) :: key
138 : CHARACTER(LEN=*), DIMENSION(:), INTENT(IN) :: authors
139 : CHARACTER(LEN=*), INTENT(IN) :: title, source
140 : CHARACTER(LEN=*), INTENT(IN), OPTIONAL :: volume, issue, pages
141 : INTEGER, INTENT(IN) :: year
142 : INTEGER, INTENT(IN), OPTIONAL :: month, day
143 : CHARACTER(LEN=*), INTENT(IN), OPTIONAL :: doi
144 :
145 : CHARACTER :: tmp
146 : CHARACTER(LEN=default_string_length) :: author, citation_key, key_a, key_b
147 : INTEGER :: commaloc, i, ires, match, mylen
148 :
149 2279652 : IF (nbib + 1 > max_reference) CPABORT("increase max_reference")
150 2279652 : nbib = nbib + 1
151 2279652 : key = nbib
152 :
153 2279652 : ALLOCATE (thebib(key)%ref)
154 :
155 : ! Copy authors.
156 6838956 : ALLOCATE (thebib(key)%ref%authors(SIZE(authors)))
157 10161696 : DO i = 1, SIZE(authors)
158 7882044 : CPASSERT(LEN_TRIM(authors(i)) <= default_string_length)
159 10161696 : thebib(key)%ref%authors(i) = authors(i)
160 : END DO
161 :
162 : ! Copy mandatory attributes.
163 2279652 : thebib(key)%ref%title = TRIM(title)
164 2279652 : thebib(key)%ref%source = TRIM(source)
165 2279652 : thebib(key)%ref%year = year
166 :
167 : ! Copy optional attributes.
168 2279652 : IF (PRESENT(volume)) THEN
169 2195532 : thebib(key)%ref%volume = TRIM(volume)
170 : END IF
171 2279652 : IF (PRESENT(issue)) THEN
172 1732872 : thebib(key)%ref%issue = TRIM(issue)
173 : END IF
174 2279652 : IF (PRESENT(pages)) THEN
175 1926348 : thebib(key)%ref%pages = TRIM(pages)
176 : END IF
177 2279652 : IF (PRESENT(month)) THEN
178 1261800 : thebib(key)%ref%month = month
179 : END IF
180 2279652 : IF (PRESENT(day)) THEN
181 849612 : thebib(key)%ref%day = day
182 : END IF
183 2279652 : IF (PRESENT(doi)) THEN
184 2246004 : thebib(key)%ref%doi = TRIM(doi)
185 : END IF
186 :
187 : ! construct a citation_key
188 2279652 : author = authors(1)
189 2279652 : commaloc = INDEX(author, ',')
190 2279652 : IF (commaloc > 0) author = author(1:commaloc - 1)
191 2279652 : CPASSERT(LEN_TRIM(author) > 0)
192 2279652 : WRITE (citation_key, '(A,I4)') TRIM(author), year
193 :
194 : ! avoid special characters in names, just remove them
195 2279652 : mylen = LEN_TRIM(citation_key)
196 2279652 : ires = 0
197 26691276 : DO I = 1, mylen
198 26691276 : IF (INDEX("0123456789thequickbrownfoxjumpsoverthelazydogTHEQUICKBROWNFOXJUMPSOVERTHELAZYDOG", citation_key(i:i)) .NE. 0) THEN
199 24285444 : ires = ires + 1
200 24285444 : tmp = citation_key(i:i)
201 24285444 : citation_key(ires:ires) = tmp
202 : END IF
203 : END DO
204 2279652 : citation_key(ires + 1:) = ""
205 2279652 : CPASSERT(LEN_TRIM(citation_key) > 4) ! At least one character of the author should be left.
206 :
207 : ! avoid duplicates, search through the list for matches (case-insensitive)
208 2279652 : mylen = LEN_TRIM(citation_key)
209 2279652 : key_a = citation_key(1:mylen)
210 2279652 : CALL uppercase(key_a)
211 2279652 : match = 0
212 310032672 : DO I = 1, nbib - 1
213 307753020 : key_b = thebib(I)%ref%citation_key(1:mylen)
214 307753020 : CALL uppercase(key_b)
215 310032672 : IF (key_a == key_b) match = match + 1
216 : END DO
217 2279652 : IF (match > 0) citation_key = citation_key(1:mylen)//CHAR(ICHAR('a') + match)
218 :
219 : ! finally store it
220 2279652 : thebib(key)%ref%citation_key = citation_key
221 :
222 2279652 : END SUBROUTINE add_reference
223 :
224 : ! **************************************************************************************************
225 : !> \brief deallocate the bibliography
226 : !> \par History
227 : !> 08.2007 Joost VandeVondele [ ]
228 : ! **************************************************************************************************
229 8412 : SUBROUTINE remove_all_references()
230 : INTEGER :: i
231 :
232 2288064 : DO i = 1, nbib
233 2288064 : DEALLOCATE (thebib(i)%ref)
234 : END DO
235 8412 : END SUBROUTINE remove_all_references
236 :
237 : ! **************************************************************************************************
238 : !> \brief printout of all cited references in the journal format sorted by publication year
239 : !> \param unit ...
240 : !> \par History
241 : !> 08.2007 Joost VandeVondele
242 : !> 07.2024 Ole Schuett
243 : ! **************************************************************************************************
244 4600 : SUBROUTINE print_cited_references(unit)
245 : INTEGER, INTENT(IN) :: unit
246 :
247 : INTEGER :: i
248 4600 : INTEGER, ALLOCATABLE, DIMENSION(:) :: irank, ival
249 :
250 18400 : ALLOCATE (ival(nbib), irank(nbib))
251 :
252 : ! we'll sort the references wrt to the publication year
253 : ! the most recent first, publications without a year get last
254 1251200 : DO i = 1, nbib
255 1246600 : irank(i) = i
256 1251200 : ival(i) = -get_epoch(thebib(i)%ref)
257 : END DO
258 4600 : CALL sort(ival, nbib, irank)
259 :
260 1251200 : DO i = 1, nbib
261 1251200 : IF (thebib(irank(i))%ref%is_cited) THEN
262 57307 : CALL print_reference_journal(key=irank(i), unit=unit)
263 57307 : WRITE (unit, '(A)') ""
264 : END IF
265 : END DO
266 :
267 4600 : END SUBROUTINE print_cited_references
268 :
269 : ! **************************************************************************************************
270 : !> \brief prints a reference in a journal style citation format,
271 : !> adding also a DOI link, which is convenient
272 : !> \param key ...
273 : !> \param unit ...
274 : !> \par History
275 : !> 08.2007 created [Joost VandeVondele]
276 : ! **************************************************************************************************
277 57307 : SUBROUTINE print_reference_journal(key, unit)
278 : INTEGER, INTENT(IN) :: key, unit
279 :
280 : CHARACTER(LEN=4*default_string_length) :: journal
281 : CHARACTER(LEN=default_string_length) :: author, year_str
282 : INTEGER :: iauthor, ipos_line, ititle, jtitle
283 :
284 : ! write the author list
285 57307 : WRITE (unit, '(T2,A)', ADVANCE="NO") ""
286 57307 : ipos_line = 2
287 381882 : DO iauthor = 1, SIZE(thebib(key)%ref%authors)
288 324575 : author = thebib(key)%ref%authors(iauthor)
289 324575 : IF (ipos_line + LEN_TRIM(author) > 71) THEN
290 38103 : WRITE (unit, '(A)') ";"
291 38103 : WRITE (unit, '(T2,A)', ADVANCE="NO") ""
292 38103 : ipos_line = 2
293 : ELSE
294 286472 : IF (iauthor .NE. 1) WRITE (unit, '(A)', ADVANCE="NO") "; "
295 286472 : ipos_line = ipos_line + 2
296 : END IF
297 324575 : WRITE (unit, '(A)', ADVANCE="NO") TRIM(author)
298 381882 : ipos_line = ipos_line + LEN_TRIM(author)
299 : END DO
300 57307 : IF (iauthor > 0) THEN
301 57307 : WRITE (unit, '(A)', ADVANCE="NO") ". "
302 57307 : ipos_line = ipos_line + 2
303 : END IF
304 :
305 : ! Journal, volume (issue), pages (year).
306 57307 : journal = thebib(key)%ref%source
307 57307 : IF (ALLOCATED(thebib(key)%ref%volume)) THEN
308 47737 : journal = TRIM(journal)//", "//thebib(key)%ref%volume
309 47737 : IF (ALLOCATED(thebib(key)%ref%issue)) THEN
310 46637 : journal = TRIM(journal)//" ("//thebib(key)%ref%issue//")"
311 : END IF
312 : END IF
313 57307 : IF (ALLOCATED(thebib(key)%ref%pages)) THEN
314 48419 : journal = TRIM(journal)//", "//thebib(key)%ref%pages
315 : END IF
316 57307 : IF (thebib(key)%ref%year > 0) THEN
317 57307 : CALL integer_to_string(thebib(key)%ref%year, year_str)
318 57307 : journal = TRIM(journal)//" ("//TRIM(year_str)//")."
319 : END IF
320 57307 : IF (ipos_line + LEN_TRIM(journal) > 71) THEN
321 52997 : WRITE (unit, '(A)') ""
322 52997 : WRITE (unit, '(T2,A)', ADVANCE="NO") ""
323 52997 : ipos_line = 2
324 : END IF
325 57307 : IF (ipos_line + LEN_TRIM(journal) > 71) THEN
326 4618 : WRITE (unit, '(A)') TRIM(journal(1:69))
327 4618 : WRITE (unit, '(T2,A)', ADVANCE="NO") TRIM(journal(70:))
328 : ELSE
329 52689 : WRITE (unit, '(A)', ADVANCE="NO") TRIM(journal)
330 : END IF
331 57307 : WRITE (unit, '(T2,A)') ""
332 :
333 : ! Title
334 154498 : DO ititle = 1, LEN(thebib(key)%ref%title), 70
335 97191 : IF (ititle .NE. 1) WRITE (unit, '(A)') ""
336 97191 : jtitle = MIN(ititle + 69, LEN(thebib(key)%ref%title))
337 154498 : WRITE (unit, '(T2,A)', ADVANCE="NO") thebib(key)%ref%title(ititle:jtitle)
338 : END DO
339 57307 : IF (ititle > 0) WRITE (unit, '(A)') "."
340 :
341 : ! DOI
342 57307 : IF (ALLOCATED(thebib(key)%ref%doi)) THEN
343 56940 : WRITE (unit, '(T2,A)') "https://doi.org/"//TRIM(thebib(key)%ref%doi)
344 : END IF
345 :
346 57307 : END SUBROUTINE print_reference_journal
347 :
348 : ! **************************************************************************************************
349 : !> \brief Exports all references as XML.
350 : !> \param unit ...
351 : !> \author Ole Schuett
352 : ! **************************************************************************************************
353 0 : SUBROUTINE export_references_as_xml(unit)
354 : INTEGER, INTENT(IN) :: unit
355 :
356 : INTEGER :: i, j
357 :
358 0 : DO i = 1, nbib
359 0 : WRITE (unit, '(T2,A)') '<REFERENCE key="'//TRIM(thebib(i)%ref%citation_key)//'">'
360 :
361 : ! Authors
362 0 : DO j = 1, SIZE(thebib(i)%ref%authors)
363 0 : WRITE (unit, '(T3,A)') '<AUTHOR>'//TRIM(thebib(i)%ref%authors(j))//'</AUTHOR>'
364 : END DO
365 :
366 : ! Title and source.
367 0 : WRITE (unit, '(T3,A)') '<TITLE>'//thebib(i)%ref%title//'</TITLE>'
368 0 : WRITE (unit, '(T3,A)') '<SOURCE>'//thebib(i)%ref%source//'</SOURCE>'
369 :
370 : ! DOI, volume, issue, pages, year, month.
371 0 : IF (ALLOCATED(thebib(i)%ref%doi)) &
372 0 : WRITE (unit, '(T3,A)') '<DOI>'//TRIM(substitute_special_xml_tokens(thebib(i)%ref%doi))//'</DOI>'
373 0 : IF (ALLOCATED(thebib(i)%ref%volume)) &
374 0 : WRITE (unit, '(T3,A)') '<VOLUME>'//thebib(i)%ref%volume//'</VOLUME>'
375 0 : IF (ALLOCATED(thebib(i)%ref%issue)) &
376 0 : WRITE (unit, '(T3,A)') '<ISSUE>'//thebib(i)%ref%issue//'</ISSUE>'
377 0 : IF (ALLOCATED(thebib(i)%ref%pages)) &
378 0 : WRITE (unit, '(T3,A)') '<PAGES>'//thebib(i)%ref%pages//'</PAGES>'
379 0 : IF (thebib(i)%ref%year > 0) &
380 0 : WRITE (unit, '(T3,A,I4.4,A)') '<YEAR>', thebib(i)%ref%year, '</YEAR>'
381 0 : IF (thebib(i)%ref%month > 0) &
382 0 : WRITE (unit, '(T3,A,I2.2,A)') '<MONTH>', thebib(i)%ref%month, '</MONTH>'
383 0 : IF (thebib(i)%ref%day > 0) &
384 0 : WRITE (unit, '(T3,A,I2.2,A)') '<DAY>', thebib(i)%ref%day, '</DAY>'
385 :
386 0 : WRITE (unit, '(T2,A)') '</REFERENCE>'
387 : END DO
388 :
389 0 : END SUBROUTINE export_references_as_xml
390 :
391 : ! **************************************************************************************************
392 : !> \brief ...
393 : !> \param key ...
394 : !> \return ...
395 : ! **************************************************************************************************
396 0 : PURE FUNCTION get_citation_key(key) RESULT(res)
397 : INTEGER, INTENT(IN) :: key
398 : CHARACTER(LEN=default_string_length) :: res
399 :
400 0 : res = thebib(key)%ref%citation_key
401 0 : END FUNCTION get_citation_key
402 :
403 : ! **************************************************************************************************
404 : !> \brief This returns something epoch like, but can only be used to order the records
405 : !> missing years, months, days are implied zero(1900)
406 : !> \param ref ...
407 : !> \return ...
408 : ! **************************************************************************************************
409 1246600 : PURE FUNCTION get_epoch(ref) RESULT(epoch)
410 : TYPE(reference_type), INTENT(IN) :: ref
411 : INTEGER :: epoch
412 :
413 1246600 : epoch = ref%day + 31*ref%month + 12*31*(ref%year - 1900)
414 :
415 1246600 : END FUNCTION get_epoch
416 :
417 0 : END MODULE reference_manager
|