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 :: 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 :
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 557450 : SUBROUTINE cite_reference(key)
85 : INTEGER, INTENT(IN) :: key
86 :
87 557450 : IF (key < 1 .OR. key > max_reference) CPABORT("citation key out of range")
88 :
89 : ! set as cited
90 557450 : thebib(key)%ref%is_cited = .TRUE.
91 :
92 557450 : 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 9771 : SUBROUTINE collect_citations_from_ranks(para_env)
101 : TYPE(mp_para_env_type), INTENT(IN) :: para_env
102 :
103 : INTEGER :: i, t
104 :
105 2667483 : DO i = 1, nbib
106 2657712 : t = 0
107 2657712 : IF (thebib(i)%ref%is_cited) t = 1
108 2657712 : CALL para_env%max(t)
109 2667483 : thebib(i)%ref%is_cited = (t == 1)
110 : END DO
111 :
112 9771 : 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 2495328 : 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 2495328 : IF (nbib + 1 > max_reference) CPABORT("increase max_reference")
143 2495328 : nbib = nbib + 1
144 2495328 : key = nbib
145 :
146 2495328 : ALLOCATE (thebib(key)%ref)
147 :
148 : ! Copy authors.
149 7485984 : ALLOCATE (thebib(key)%ref%authors(SIZE(authors)))
150 11183106 : DO i = 1, SIZE(authors)
151 8687778 : CPASSERT(LEN_TRIM(authors(i)) <= default_string_length)
152 11183106 : thebib(key)%ref%authors(i) = authors(i)
153 : END DO
154 :
155 : ! Copy mandatory attributes.
156 2495328 : thebib(key)%ref%title = TRIM(title)
157 2495328 : thebib(key)%ref%source = TRIM(source)
158 2495328 : thebib(key)%ref%year = year
159 :
160 : ! Copy optional attributes.
161 2495328 : IF (PRESENT(volume)) THEN
162 2412762 : thebib(key)%ref%volume = TRIM(volume)
163 : END IF
164 2495328 : IF (PRESENT(pages)) THEN
165 2449458 : thebib(key)%ref%pages = TRIM(pages)
166 : END IF
167 2495328 : IF (PRESENT(doi)) THEN
168 2458632 : thebib(key)%ref%doi = TRIM(doi)
169 : END IF
170 :
171 : ! construct a citation_key
172 2495328 : author = authors(1)
173 2495328 : periodloc = INDEX(author, '.', back=.TRUE.)
174 2495328 : IF (periodloc > 0) author = author(periodloc + 1:)
175 2495328 : CPASSERT(LEN_TRIM(author) > 0)
176 2495328 : WRITE (citation_key, '(A,I4)') TRIM(author), year
177 :
178 : ! avoid special characters in names, just remove them
179 2495328 : mylen = LEN_TRIM(citation_key)
180 2495328 : ires = 0
181 31631952 : DO I = 1, mylen
182 31631952 : IF (INDEX("0123456789thequickbrownfoxjumpsoverthelazydogTHEQUICKBROWNFOXJUMPSOVERTHELAZYDOG", citation_key(i:i)) .NE. 0) THEN
183 26503686 : ires = ires + 1
184 26503686 : tmp = citation_key(i:i)
185 26503686 : citation_key(ires:ires) = tmp
186 : END IF
187 : END DO
188 2495328 : citation_key(ires + 1:) = ""
189 2495328 : 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 2495328 : mylen = LEN_TRIM(citation_key)
193 2495328 : key_a = citation_key(1:mylen)
194 2495328 : CALL uppercase(key_a)
195 2495328 : match = 0
196 340612272 : DO I = 1, nbib - 1
197 338116944 : key_b = thebib(I)%ref%citation_key(1:mylen)
198 338116944 : CALL uppercase(key_b)
199 340612272 : IF (key_a == key_b) match = match + 1
200 : END DO
201 2495328 : IF (match > 0) citation_key = citation_key(1:mylen)//CHAR(ICHAR('a') + match)
202 :
203 : ! finally store it
204 2495328 : thebib(key)%ref%citation_key = citation_key
205 :
206 2495328 : END SUBROUTINE add_reference
207 :
208 : ! **************************************************************************************************
209 : !> \brief deallocate the bibliography
210 : !> \par History
211 : !> 08.2007 Joost VandeVondele [ ]
212 : ! **************************************************************************************************
213 9174 : SUBROUTINE remove_all_references()
214 : INTEGER :: i
215 :
216 2504502 : DO i = 1, nbib
217 2504502 : DEALLOCATE (thebib(i)%ref)
218 : END DO
219 9174 : 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 4981 : SUBROUTINE print_cited_references(unit)
229 : INTEGER, INTENT(IN) :: unit
230 :
231 : INTEGER :: i
232 4981 : INTEGER, ALLOCATABLE, DIMENSION(:) :: irank, ival
233 :
234 19924 : 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 1359813 : DO i = 1, nbib
239 1354832 : irank(i) = i
240 1359813 : ival(i) = -thebib(i)%ref%year
241 : END DO
242 4981 : CALL sort(ival, nbib, irank)
243 :
244 1359813 : DO i = 1, nbib
245 1359813 : IF (thebib(irank(i))%ref%is_cited) THEN
246 62402 : CALL print_reference_journal(key=irank(i), unit=unit)
247 62402 : WRITE (unit, '(A)') ""
248 : END IF
249 : END DO
250 :
251 4981 : 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 62402 : SUBROUTINE print_reference_journal(key, unit)
262 : INTEGER, INTENT(IN) :: key, unit
263 :
264 62402 : CHARACTER(LEN=:), ALLOCATABLE :: text
265 : CHARACTER(LEN=default_string_length) :: year_str
266 : INTEGER :: iauthor
267 :
268 : ! Authors
269 62402 : text = thebib(key)%ref%authors(1)
270 359287 : DO iauthor = 2, SIZE(thebib(key)%ref%authors)
271 359287 : text = TRIM(text)//", "//thebib(key)%ref%authors(iauthor)
272 : END DO
273 62402 : CALL write_long_text(TRIM(text)//".", unit)
274 :
275 : ! Journal, volume, pages (year).
276 62402 : text = thebib(key)%ref%source
277 62402 : IF (ALLOCATED(thebib(key)%ref%volume)) THEN
278 52066 : text = text//" "//thebib(key)%ref%volume
279 : END IF
280 62402 : IF (ALLOCATED(thebib(key)%ref%pages)) THEN
281 62395 : text = TRIM(text)//", "//thebib(key)%ref%pages
282 : END IF
283 62402 : IF (thebib(key)%ref%year > 0) THEN
284 62402 : CALL integer_to_string(thebib(key)%ref%year, year_str)
285 62402 : text = TRIM(text)//" ("//TRIM(year_str)//")"
286 : END IF
287 62402 : CALL write_long_text(TRIM(text)//".", unit)
288 :
289 : ! Title
290 62402 : CALL write_long_text(thebib(key)%ref%title//".", unit)
291 :
292 : ! DOI
293 62402 : IF (ALLOCATED(thebib(key)%ref%doi)) THEN
294 62030 : WRITE (unit, '(T2,A)') "https://doi.org/"//TRIM(thebib(key)%ref%doi)
295 : END IF
296 :
297 62402 : END SUBROUTINE print_reference_journal
298 :
299 : ! **************************************************************************************************
300 : !> \brief Exports all references as XML.
301 : !> \param unit ...
302 : !> \author Ole Schuett
303 : ! **************************************************************************************************
304 0 : SUBROUTINE export_references_as_xml(unit)
305 : INTEGER, INTENT(IN) :: unit
306 :
307 : INTEGER :: i, j
308 :
309 0 : DO i = 1, nbib
310 0 : WRITE (unit, '(T2,A)') '<REFERENCE key="'//TRIM(thebib(i)%ref%citation_key)//'">'
311 :
312 : ! Authors
313 0 : DO j = 1, SIZE(thebib(i)%ref%authors)
314 0 : WRITE (unit, '(T3,A)') '<AUTHOR>'//TRIM(thebib(i)%ref%authors(j))//'</AUTHOR>'
315 : END DO
316 :
317 : ! Title and source.
318 0 : WRITE (unit, '(T3,A)') '<TITLE>'//thebib(i)%ref%title//'</TITLE>'
319 0 : WRITE (unit, '(T3,A)') '<SOURCE>'//thebib(i)%ref%source//'</SOURCE>'
320 :
321 : ! DOI, volume, pages, year, month.
322 0 : IF (ALLOCATED(thebib(i)%ref%doi)) &
323 0 : WRITE (unit, '(T3,A)') '<DOI>'//TRIM(substitute_special_xml_tokens(thebib(i)%ref%doi))//'</DOI>'
324 0 : IF (ALLOCATED(thebib(i)%ref%volume)) &
325 0 : WRITE (unit, '(T3,A)') '<VOLUME>'//thebib(i)%ref%volume//'</VOLUME>'
326 0 : IF (ALLOCATED(thebib(i)%ref%pages)) &
327 0 : WRITE (unit, '(T3,A)') '<PAGES>'//thebib(i)%ref%pages//'</PAGES>'
328 0 : IF (thebib(i)%ref%year > 0) &
329 0 : WRITE (unit, '(T3,A,I4.4,A)') '<YEAR>', thebib(i)%ref%year, '</YEAR>'
330 0 : WRITE (unit, '(T2,A)') '</REFERENCE>'
331 : END DO
332 :
333 0 : END SUBROUTINE export_references_as_xml
334 :
335 : ! **************************************************************************************************
336 : !> \brief ...
337 : !> \param key ...
338 : !> \return ...
339 : ! **************************************************************************************************
340 0 : PURE FUNCTION get_citation_key(key) RESULT(res)
341 : INTEGER, INTENT(IN) :: key
342 : CHARACTER(LEN=default_string_length) :: res
343 :
344 0 : res = thebib(key)%ref%citation_key
345 0 : 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 187206 : SUBROUTINE write_long_text(text, unit)
355 : CHARACTER(LEN=*), INTENT(IN) :: text
356 : INTEGER, INTENT(IN) :: unit
357 :
358 : INTEGER :: a, b
359 :
360 187206 : a = 1; b = -1
361 435971 : DO WHILE (b < LEN(text))
362 248765 : b = next_linebreak(text, pos=a, rowlen=78)
363 248765 : WRITE (unit, '(T2,A)') text(a:b)
364 248765 : a = b + 1
365 : END DO
366 187206 : 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 248765 : 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 248765 : n = LEN_TRIM(text)
384 248765 : IF (n - pos <= rowlen) THEN
385 : ibreak = n ! remaining text shorter than line
386 : ELSE
387 61559 : i = INDEX(text(pos + 1:pos + 1 + rowlen), " ", BACK=.TRUE.)
388 61559 : IF (i == 0) THEN
389 0 : ibreak = pos + rowlen - 1 ! no space found, break mid-word
390 : ELSE
391 61559 : ibreak = pos + i ! break at space closest to rowlen
392 : END IF
393 : END IF
394 248765 : END FUNCTION next_linebreak
395 :
396 473748 : END MODULE reference_manager
|