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 routines to handle the output, The idea is to remove the
10 : !> decision of wheter to output and what to output from the code
11 : !> that does the output, and centralize it here.
12 : !> \note
13 : !> These were originally together with the log handling routines,
14 : !> but have been spawned off. Some dependencies are still there,
15 : !> and some of the comments about log handling also applies to output
16 : !> handling: @see cp_log_handling
17 : ! **************************************************************************************************
18 : MODULE cp_output_handling_openpmd
19 :
20 : USE cp_output_handling, only: cp_print_key_should_output, cp_p_file
21 : USE cp_files, ONLY: close_file, &
22 : open_file
23 : USE cp_iter_types, ONLY: cp_iteration_info_release, &
24 : cp_iteration_info_retain, &
25 : cp_iteration_info_type, &
26 : each_desc_labels, &
27 : each_possible_labels
28 : USE cp_log_handling, ONLY: cp_logger_generate_filename, &
29 : cp_logger_get_default_unit_nr, &
30 : cp_logger_get_unit_nr, &
31 : cp_logger_type, &
32 : cp_to_string
33 : USE input_keyword_types, ONLY: keyword_create, &
34 : keyword_release, &
35 : keyword_type
36 : USE input_section_types, ONLY: section_add_keyword, &
37 : section_add_subsection, &
38 : section_create, &
39 : section_release, &
40 : section_type, &
41 : section_vals_get_subs_vals, &
42 : section_vals_type, &
43 : section_vals_val_get
44 : USE kinds, ONLY: default_path_length, &
45 : default_string_length, &
46 : dp
47 : USE machine, ONLY: m_mov
48 : USE memory_utilities, ONLY: reallocate
49 : USE message_passing, ONLY: mp_file_delete, &
50 : mp_file_get_amode, &
51 : mp_file_type
52 : #ifdef __OPENPMD
53 : USE physcon, ONLY: seconds
54 : USE cp2k_info, ONLY: cp2k_version
55 : USE openpmd_api, ONLY: &
56 : openpmd_access_create, &
57 : openpmd_attributable_type, openpmd_iteration_type, openpmd_mesh_type, &
58 : openpmd_particle_species_type, &
59 : openpmd_record_type, &
60 : openpmd_series_create, openpmd_series_type, &
61 : openpmd_type_int, openpmd_json_merge, openpmd_get_default_extension
62 : #endif
63 : USE string_utilities, ONLY: compress, &
64 : s2a
65 : #include "../base/base_uses.f90"
66 :
67 : IMPLICIT NONE
68 : PRIVATE
69 :
70 : LOGICAL, PRIVATE, PARAMETER :: debug_this_module = .TRUE.
71 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_output_handling_openpmd'
72 : PUBLIC :: cp_openpmd_print_key_unit_nr, cp_openpmd_print_key_finished_output
73 : PUBLIC :: cp_openpmd_get_value_unit_nr, cp_openpmd_per_call_value_type
74 : PUBLIC :: cp_openpmd_output_finalize
75 : PUBLIC :: cp_openpmd_get_default_extension
76 : PUBLIC :: cp_openpmd_close_iterations
77 :
78 : #ifdef __OPENPMD
79 : TYPE :: cp_openpmd_per_call_value_type
80 : TYPE(openpmd_series_type) :: series = openpmd_series_type()
81 : TYPE(openpmd_iteration_type) :: iteration = openpmd_iteration_type()
82 : ! TYPE(openpmd_mesh_type) :: mesh = openpmd_mesh_type()
83 : ! TYPE(openpmd_particle_species_type) :: particle_species = openpmd_particle_species_type()
84 : CHARACTER(len=default_string_length) :: name_prefix = "" ! e.g. 'WFN_00008_1'
85 : REAL(kind=dp), DIMENSION(7) :: unit_dimension = [0, 0, 0, 0, 0, 0, 0]
86 : REAL(kind=dp) :: unit_si = 1
87 : END TYPE cp_openpmd_per_call_value_type
88 :
89 : TYPE :: cp_openpmd_per_call_type
90 : INTEGER :: key = -1 ! unit_nr
91 : TYPE(cp_openpmd_per_call_value_type) :: value = cp_openpmd_per_call_value_type()
92 : END TYPE cp_openpmd_per_call_type
93 :
94 : TYPE :: cp_current_iteration_counter_type
95 : INTEGER :: flat_iteration = 0
96 : INTEGER, ALLOCATABLE :: complex_iteration(:)
97 : INTEGER :: complex_iteration_depth = 0
98 : END TYPE cp_current_iteration_counter_type
99 :
100 : TYPE :: cp_openpmd_per_callsite_value_type
101 : ! openPMD output Series.
102 : TYPE(openpmd_series_type) :: output_series = openpmd_series_type()
103 : ! Information on the last Iteration that was written to, including
104 : ! CP2Ks complex Iteration number and its associated contiguous scalar
105 : ! openPMD Iteration number.
106 : TYPE(cp_current_iteration_counter_type) :: iteration_counter = cp_current_iteration_counter_type()
107 : END TYPE cp_openpmd_per_callsite_value_type
108 :
109 : TYPE :: cp_openpmd_per_callsite_type
110 : CHARACTER(len=default_string_length) :: key = "" ! openpmd_basename
111 : TYPE(cp_openpmd_per_callsite_value_type) :: value = cp_openpmd_per_callsite_value_type()
112 : END TYPE cp_openpmd_per_callsite_type
113 :
114 : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
115 : ! Begin data members for openPMD output. !
116 : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
117 :
118 : ! Map that associates opened unit numbers with their associated openPMD content.
119 : ! Since CP2K logically opens a new file for every single dataset, multiple
120 : ! unit numbers may point to the same openPMD Series.
121 : TYPE(cp_openpmd_per_call_type), ALLOCATABLE :: cp_openpmd_per_call(:)
122 : INTEGER :: cp_num_openpmd_per_call = 0
123 : INTEGER :: cp_capacity_openpmd_per_call = 0
124 :
125 : ! Map that associates callsites from which functions of this module may be invoked
126 : ! to their associated openPMD content.
127 : ! This stores the actual output Series (which stays open across calls from the
128 : ! same callsite) and the Iteration counter (which associates complex CP2k
129 : ! Iterations with flattened scalar Iteration indexes in the openPMD output).
130 : TYPE(cp_openpmd_per_callsite_type), ALLOCATABLE, TARGET :: cp_openpmd_per_callsite(:)
131 : INTEGER :: cp_num_openpmd_per_callsite = 0
132 : INTEGER :: cp_capacity_openpmd_per_callsite = 0
133 :
134 : ! This is currently hardcoded, reallocation in case of greater needed map sizes
135 : ! is not (yet) supported. However, the maps should normally not grow to large
136 : ! sizes:
137 : !
138 : ! * cp_openpmd_per_call will normally contain one single element, since a
139 : ! (virtual) file is opened, written and then closed.
140 : ! The output routines normally do not contain interleaved open-write-close
141 : ! logic.
142 : ! * cp_openpmd_per_callsite will normally contain a handful of elements,
143 : ! equal to the number of output modules activated in the input file
144 : ! (and in openPMD: equal to the number of output Series).
145 : ! There are not 100 of them.
146 : INTEGER, PARAMETER :: cp_allocation_size = 100
147 : ! Some default settings. May be overwritten / extended by specifying a JSON/TOML
148 : ! config in the input file.
149 : CHARACTER(len=*), PARAMETER :: cp_default_backend_config = &
150 : "[hdf5]"//new_line('a')// &
151 : "# will be overridden by particle flushes"//new_line('a')// &
152 : "independent_stores = false"//new_line('a')// &
153 : "dont_warn_unused_keys = ['independent_stores']"//new_line('a')// &
154 : ""//new_line('a')// &
155 : "[adios2]"//new_line('a')// &
156 : "# discard any attributes written on ranks other than 0"//new_line('a')// &
157 : "attribute_writing_ranks = 0"//new_line('a')// &
158 : "[adios2.engine]"//new_line('a')// &
159 : "# CP2K generally has many small IO operations, "//new_line('a')// &
160 : "# so stage IO memory to the buffer first and then "//new_line('a')// &
161 : "# run it all at once, instead of writing to disk directly."//new_line('a')// &
162 : "# Save memory by specifying 'disk' here instead."//new_line('a')// &
163 : "# TODO: In future, maybe implement some input variable"//new_line('a')// &
164 : "# to specify intervals at which to flush to disk."//new_line('a')// &
165 : "preferred_flush_target = 'buffer'"//new_line('a')
166 : #ifndef _WIN32
167 : CHARACTER(len=*), PARAMETER :: cp_default_backend_config_non_windows = &
168 : "# Raise the BufferChunkSize to the maximum (2GB), since large operations"//new_line('a')// &
169 : "# improve IO performance and the allocation overhead only cuts into"//new_line('a')// &
170 : "# virtual memory (except on Windows, hence do not do that there)"//new_line('a')// &
171 : "[adios2.engine.parameters]"//new_line('a')// &
172 : "BufferChunkSize = 2147381248"//new_line('a')
173 : #endif
174 :
175 : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
176 : ! End data members for openPMD output. !
177 : !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
178 :
179 : #else ! defined(__OPENPMD)
180 :
181 : TYPE :: cp_openpmd_per_call_value_type
182 : ! nothing there
183 : END TYPE cp_openpmd_per_call_value_type
184 :
185 : #endif
186 :
187 : CONTAINS
188 :
189 : #ifdef __OPENPMD
190 : ! Helper functions for interacting with the two maps declared above.
191 : #:set name_suffixes = ['unit_nr', 'filedata']
192 : #:set key_types = ['INTEGER', 'CHARACTER(len=default_string_length)']
193 : #:set value_types = ['cp_openpmd_per_call_value_type', 'cp_openpmd_per_callsite_value_type']
194 : #:set map_storages = ['cp_openpmd_per_call', 'cp_openpmd_per_callsite']
195 : #:set map_counters = ['cp_num_openpmd_per_call', 'cp_num_openpmd_per_callsite']
196 : #:set map_capacities = ['cp_capacity_openpmd_per_call', 'cp_capacity_openpmd_per_callsite']
197 :
198 : #:for name_suffix, key_type, value_type, map_storage, map_counter, map_capacity in zip(name_suffixes, key_types, value_types, map_storages, map_counters, map_capacities)
199 :
200 : ! TODO No reallocation support for now, change cp_allocation_size if larger sizes needed.
201 :
202 : ! **************************************************************************************************
203 : !> \brief ...
204 : !> \param key ...
205 : !> \param value ...
206 : ! **************************************************************************************************
207 : FUNCTION cp_openpmd_add_${name_suffix}$ (key, value) RESULT(index)
208 : ${key_type}$, INTENT(in) :: key
209 : TYPE(${value_type}$), INTENT(in) :: value
210 : INTEGER :: index
211 :
212 : LOGICAL :: check_capacity
213 : INTEGER :: i
214 :
215 : ! Check if the key already exists
216 : DO i = 1, ${map_counter}$
217 : IF (${map_storage}$ (i)%key == key) THEN
218 : ${map_storage}$ (i)%value = value
219 : index = i
220 : RETURN
221 : END IF
222 : END DO
223 :
224 : IF (${map_capacity}$ == 0) THEN
225 : ALLOCATE (${map_storage}$ (cp_allocation_size))
226 : ${map_capacity}$ = cp_allocation_size
227 : END IF
228 :
229 : ! No idea how to do reallocations, so for now just assert that they're not needed
230 : check_capacity = ${map_counter}$ < ${map_capacity}$
231 : CPASSERT(check_capacity)
232 :
233 : ! Add a new entry
234 : ${map_counter}$ = ${map_counter}$+1
235 : ${map_storage}$ (${map_counter}$)%key = key
236 : ${map_storage}$ (${map_counter}$)%value = value
237 : index = ${map_counter}$
238 : END FUNCTION cp_openpmd_add_${name_suffix}$
239 :
240 : ! **************************************************************************************************
241 : !> \brief ...
242 : !> \param key ...
243 : !> \return ...
244 : ! **************************************************************************************************
245 : FUNCTION cp_openpmd_get_index_${name_suffix}$ (key) RESULT(index)
246 : ${key_type}$, INTENT(in) :: key
247 : INTEGER :: index
248 :
249 : INTEGER :: i
250 :
251 : index = -1
252 :
253 : DO i = 1, ${map_counter}$
254 : IF (${map_storage}$ (i)%key == key) THEN
255 : index = i
256 : RETURN
257 : END IF
258 : END DO
259 : END FUNCTION cp_openpmd_get_index_${name_suffix}$
260 :
261 : FUNCTION cp_openpmd_get_value_${name_suffix}$ (key) RESULT(value)
262 : ${key_type}$, INTENT(in) :: key
263 : TYPE(${value_type}$) :: value
264 :
265 : INTEGER :: i
266 :
267 : i = cp_openpmd_get_index_${name_suffix}$ (key)
268 : IF (i == -1) RETURN
269 :
270 : value = ${map_storage}$ (i)%value
271 : END FUNCTION cp_openpmd_get_value_${name_suffix}$
272 :
273 : ! **************************************************************************************************
274 : !> \brief ...
275 : !> \param key ...
276 : !> \return ...
277 : ! **************************************************************************************************
278 : FUNCTION cp_openpmd_remove_${name_suffix}$ (key) RESULT(was_found)
279 : ${key_type}$, INTENT(in) :: key
280 : LOGICAL :: was_found
281 :
282 : INTEGER :: i
283 :
284 : was_found = .FALSE.
285 :
286 : DO i = 1, ${map_counter}$
287 : IF (${map_storage}$ (i)%key == key) THEN
288 : was_found = .TRUE.
289 : IF (i /= ${map_counter}$) THEN
290 : ! Swap last element to now freed place
291 : ${map_storage}$ (i) = ${map_storage}$ (${map_counter}$)
292 : END IF
293 :
294 : ${map_counter}$ = ${map_counter}$-1
295 : IF (${map_counter}$ == 0) THEN
296 : DEALLOCATE (${map_storage}$)
297 : ${map_capacity}$ = 0
298 : END IF
299 : RETURN
300 : END IF
301 : END DO
302 : END FUNCTION cp_openpmd_remove_${name_suffix}$
303 :
304 : #:endfor
305 :
306 : ! **************************************************************************************************
307 : !> \brief Simplified version of cp_print_key_generate_filename. Since an openPMD Series encompasses
308 : ! multiple datasets that would be separate outputs in e.g. .cube files, this needs not
309 : ! consider dataset names for creation of a filename.
310 : !> \param logger ...
311 : !> \param print_key ...
312 : !> \param openpmd_basename ...
313 : !> \param extension ...
314 : !> \param my_local ...
315 : !> \return ...
316 : ! **************************************************************************************************
317 : FUNCTION cp_print_key_generate_openpmd_filename(logger, print_key, openpmd_basename, extension) RESULT(filename)
318 : TYPE(cp_logger_type), POINTER :: logger
319 : TYPE(section_vals_type), POINTER :: print_key
320 : CHARACTER(len=*), INTENT(IN) :: openpmd_basename, extension
321 : CHARACTER(len=default_path_length) :: filename
322 :
323 : CHARACTER(len=default_path_length) :: outName, outPath, root
324 : INTEGER :: my_ind1, my_ind2
325 : LOGICAL :: has_root
326 :
327 : CALL section_vals_val_get(print_key, "FILENAME", c_val=outPath)
328 : IF (outPath(1:1) == '=') THEN
329 : CPASSERT(LEN(outPath) - 1 <= LEN(filename))
330 : filename = outPath(2:)
331 : RETURN
332 : END IF
333 : IF (outPath == "__STD_OUT__") outPath = ""
334 : outName = TRIM(outPath)
335 : has_root = .FALSE.
336 : my_ind1 = INDEX(outPath, "/")
337 : my_ind2 = LEN_TRIM(outPath)
338 : IF (my_ind1 /= 0) THEN
339 : has_root = .TRUE.
340 : DO WHILE (INDEX(outPath(my_ind1 + 1:my_ind2), "/") /= 0)
341 : my_ind1 = INDEX(outPath(my_ind1 + 1:my_ind2), "/") + my_ind1
342 : END DO
343 : IF (my_ind1 == my_ind2) THEN
344 : outName = ""
345 : ELSE
346 : outName = outPath(my_ind1 + 1:my_ind2)
347 : END IF
348 : END IF
349 :
350 : IF (.NOT. has_root) THEN
351 : root = TRIM(logger%iter_info%project_name)
352 : ELSE IF (outName == "") THEN
353 : root = outPath(1:my_ind1)//TRIM(logger%iter_info%project_name)
354 : ELSE
355 : root = outPath(1:my_ind1)
356 : END IF
357 :
358 : filename = ADJUSTL(TRIM(root)//"_"//TRIM(openpmd_basename)//TRIM(extension))
359 :
360 : END FUNCTION cp_print_key_generate_openpmd_filename
361 :
362 : ! **************************************************************************************************
363 : !> \brief CP2K Iteration numbers are n-dimensional while openPMD Iteration numbers are scalars.
364 : ! This checks if the Iteration number has changed from the previous call (stored in
365 : ! openpmd_file%iteration_counter) and updates it if needed.
366 : !> \param logger ...
367 : !> \return ...
368 : ! **************************************************************************************************
369 : FUNCTION cp_advance_iteration_number(logger, openpmd_file) RESULT(did_advance_iteration)
370 : TYPE(cp_logger_type), POINTER :: logger
371 : TYPE(cp_openpmd_per_callsite_value_type) :: openpmd_file
372 : LOGICAL :: did_advance_iteration
373 :
374 : INTEGER :: len
375 :
376 : #:set ic = 'openpmd_file%iteration_counter'
377 :
378 : did_advance_iteration = .FALSE.
379 : len = SIZE(logger%iter_info%iteration)
380 : IF (len /= ${ic}$%complex_iteration_depth) THEN
381 : did_advance_iteration = .TRUE.
382 : ${ic}$%complex_iteration_depth = len
383 : ALLOCATE (${ic}$%complex_iteration(len))
384 : ELSE
385 : did_advance_iteration &
386 : = ANY(${ic}$%complex_iteration(1:len) &
387 : /= logger%iter_info%iteration(1:len))
388 : END IF
389 :
390 : IF (.NOT. did_advance_iteration) RETURN
391 :
392 : ${ic}$%flat_iteration = ${ic}$%flat_iteration + 1
393 : ${ic}$%complex_iteration(1:len) &
394 : = logger%iter_info%iteration(1:len)
395 :
396 : END FUNCTION cp_advance_iteration_number
397 :
398 : ! **************************************************************************************************
399 : !> \brief CP2K deals with output handles in terms of unit numbers.
400 : ! The openPMD output logic does not change this association.
401 : ! For this, we need to emulate unit numbers as (1) they are not native to openPMD and
402 : ! (2) a single openPMD Series might contain multiple datasets treated logically by CP2K
403 : ! as distinct outputs. As a result, a single unit number is resolved by the openPMD logic
404 : ! to the values represented by the cp_openpmd_per_call_value_type struct,
405 : ! containing the output Series and the referred datasets therein (Iteration number,
406 : ! name prefix for meshes and particles, referred output Series ...).
407 : !> \param series ...
408 : !> \param middle_name ...
409 : !> \param logger ...
410 : !> \param sim_time Current simulation time in atomic units (used for timeOffset)
411 : !> \return ...
412 : ! **************************************************************************************************
413 : FUNCTION cp_openpmd_create_unit_nr_entry( &
414 : openpmd_file_index, &
415 : middle_name, &
416 : logger, &
417 : openpmd_unit_dimension, &
418 : openpmd_unit_si, &
419 : sim_time) RESULT(res)
420 : INTEGER :: openpmd_file_index
421 : CHARACTER(len=*), INTENT(IN) :: middle_name
422 : TYPE(cp_logger_type), POINTER :: logger
423 : REAL(kind=dp), DIMENSION(7), OPTIONAL, INTENT(IN) :: openpmd_unit_dimension
424 : REAL(kind=dp), OPTIONAL, INTENT(IN) :: openpmd_unit_si
425 : REAL(kind=dp), OPTIONAL, INTENT(IN) :: sim_time
426 : TYPE(cp_openpmd_per_call_value_type) :: res
427 :
428 : LOGICAL :: opened_new_iteration
429 : TYPE(openpmd_attributable_type) :: attr
430 : TYPE(cp_openpmd_per_callsite_value_type), POINTER :: opmd
431 :
432 : opened_new_iteration = .FALSE.
433 :
434 : opmd => cp_openpmd_per_callsite(openpmd_file_index)%value
435 :
436 : res%series = opmd%output_series
437 :
438 : opened_new_iteration = cp_advance_iteration_number(logger, opmd)
439 :
440 : res%iteration = opmd%output_series%write_iteration(opmd%iteration_counter%flat_iteration)
441 : CALL res%iteration%set_time_unit_SI(seconds)
442 : res%name_prefix = TRIM(middle_name)
443 :
444 : IF (opened_new_iteration) THEN
445 :
446 : IF (PRESENT(sim_time)) THEN
447 : CALL res%iteration%set_time(sim_time)
448 : END IF
449 :
450 : IF (PRESENT(openpmd_unit_dimension)) THEN
451 : res%unit_dimension = openpmd_unit_dimension
452 : END IF
453 :
454 : IF (PRESENT(openpmd_unit_si)) THEN
455 : res%unit_si = openpmd_unit_si
456 : END IF
457 :
458 : attr = res%iteration%as_attributable()
459 : CALL attr%set_attribute_vec_int( &
460 : "ndim_iteration_index", &
461 : opmd%iteration_counter%complex_iteration)
462 : END IF
463 : END FUNCTION cp_openpmd_create_unit_nr_entry
464 :
465 : ! **************************************************************************************************
466 : !> \brief Check if there is already an output Series created for the callsite identified
467 : ! by openpmd_basename. If so, then return it (by index), otherwise open the Series now
468 : ! and return the index then.
469 : FUNCTION cp_openpmd_get_openpmd_file_entry(openpmd_basename, filename, openpmd_config, logger, use_mpi) RESULT(file_index)
470 : CHARACTER(len=*), INTENT(IN) :: openpmd_basename, filename, openpmd_config
471 : TYPE(cp_logger_type), POINTER :: logger
472 : LOGICAL :: use_mpi
473 : INTEGER :: file_index
474 : CHARACTER(:), ALLOCATABLE :: merged_config
475 :
476 : CHARACTER(len=default_string_length) :: basename_copied
477 : TYPE(cp_openpmd_per_callsite_value_type) :: emplace_new
478 :
479 : INTEGER :: handle
480 : TYPE(cp_openpmd_per_callsite_value_type) :: series_data
481 : TYPE(openpmd_iteration_type) :: iteration
482 : INTEGER :: i
483 :
484 : basename_copied = ' '
485 : basename_copied(1:LEN_TRIM(openpmd_basename)) = TRIM(openpmd_basename)
486 :
487 : file_index = cp_openpmd_get_index_filedata(basename_copied)
488 :
489 : CALL timeset('openpmd_close_iterations', handle)
490 : DO i = 1, cp_num_openpmd_per_callsite
491 : IF (i /= file_index) THEN
492 : series_data = cp_openpmd_per_callsite(i)%value
493 : iteration = series_data%output_series%get_iteration( &
494 : series_data%iteration_counter%flat_iteration)
495 : IF (.NOT. iteration%closed()) THEN
496 : CALL iteration%close()
497 : END IF
498 : END IF
499 : END DO
500 : CALL timestop(handle)
501 :
502 : IF (file_index /= -1) RETURN
503 :
504 : #ifndef _WIN32
505 : merged_config = openpmd_json_merge(cp_default_backend_config, cp_default_backend_config_non_windows)
506 : #else
507 : merged_config = cp_default_backend_config
508 : #endif
509 : IF (use_mpi) THEN
510 : merged_config = openpmd_json_merge(merged_config, openpmd_config, logger%para_env)
511 : emplace_new%output_series = openpmd_series_create( &
512 : filename, openpmd_access_create, logger%para_env, merged_config)
513 : ELSE
514 : merged_config = openpmd_json_merge(merged_config, openpmd_config)
515 : emplace_new%output_series = openpmd_series_create( &
516 : filename, openpmd_access_create, config=merged_config)
517 : END IF
518 :
519 : CALL emplace_new%output_series%set_software("CP2K", cp2k_version)
520 :
521 : DEALLOCATE (merged_config)
522 : file_index = cp_openpmd_add_filedata(basename_copied, emplace_new)
523 : END FUNCTION cp_openpmd_get_openpmd_file_entry
524 :
525 : #else ! defined(__OPENPMD)
526 :
527 : FUNCTION cp_openpmd_get_value_unit_nr(key) RESULT(value)
528 : INTEGER, INTENT(in) :: key
529 : TYPE(cp_openpmd_per_call_value_type) :: value
530 :
531 : MARK_USED(key)
532 : MARK_USED(value)
533 : CPABORT("CP2K compiled without the openPMD-api")
534 :
535 : END FUNCTION cp_openpmd_get_value_unit_nr
536 :
537 : #endif
538 :
539 : ! **************************************************************************************************
540 : !> \brief Close all outputs.
541 : ! **************************************************************************************************
542 : SUBROUTINE cp_openpmd_output_finalize()
543 : #ifdef __OPENPMD
544 : INTEGER :: i
545 : DO i = 1, cp_num_openpmd_per_callsite
546 : DEALLOCATE (cp_openpmd_per_callsite(i)%value%iteration_counter%complex_iteration)
547 : CALL cp_openpmd_per_callsite(i)%value%output_series%close()
548 : END DO
549 : IF (ALLOCATED(cp_openpmd_per_callsite)) THEN
550 : DEALLOCATE (cp_openpmd_per_callsite)
551 : END IF
552 : cp_num_openpmd_per_callsite = 0
553 : #endif
554 : END SUBROUTINE cp_openpmd_output_finalize
555 :
556 : ! **************************************************************************************************
557 : !> \brief ...
558 : !> \param logger ...
559 : !> \param basis_section ...
560 : !> \param print_key_path ...
561 : !> \param extension ...
562 : !> \param middle_name ...
563 : !> \param local ...
564 : !> \param log_filename ...
565 : !> \param ignore_should_output ...
566 : !> \param do_backup ...
567 : !> \param is_new_file true if this rank created a new (or rewound) file, false otherwise
568 : !> \param mpi_io True if the file should be opened in parallel on all processors belonging to
569 : !> the communicator group. Automatically disabled if the file form or access mode
570 : !> is unsuitable for MPI IO. Return value indicates whether MPI was actually used
571 : !> and therefore the flag must also be passed to the file closing directive.
572 : !> \param fout Name of the actual file where the output will be written. Needed mainly for MPI IO
573 : !> because inquiring the filename from the MPI filehandle does not work across
574 : !> all MPI libraries.
575 : !> \param openpmd_basename Used to associate an identifier to each callsite of this module
576 : !> \param use_openpmd ...
577 : !> \param sim_time Current simulation time in atomic units (used for timeOffset)
578 : !> \return ...
579 : ! **************************************************************************************************
580 : FUNCTION cp_openpmd_print_key_unit_nr( &
581 : logger, &
582 : basis_section, &
583 : print_key_path, &
584 : middle_name, &
585 : ignore_should_output, &
586 : mpi_io, &
587 : fout, &
588 : openpmd_basename, &
589 : openpmd_unit_dimension, &
590 : openpmd_unit_si, &
591 : sim_time) RESULT(res)
592 :
593 : TYPE(cp_logger_type), POINTER :: logger
594 : TYPE(section_vals_type), INTENT(IN) :: basis_section
595 : CHARACTER(len=*), INTENT(IN), OPTIONAL :: print_key_path
596 : CHARACTER(len=*), INTENT(IN), OPTIONAL :: middle_name
597 : LOGICAL, INTENT(IN), OPTIONAL :: ignore_should_output
598 : LOGICAL, INTENT(INOUT), OPTIONAL :: mpi_io
599 : CHARACTER(len=default_path_length), INTENT(OUT), &
600 : OPTIONAL :: fout
601 : CHARACTER(len=*), INTENT(IN), OPTIONAL :: openpmd_basename
602 : REAL(kind=dp), DIMENSION(7), OPTIONAL, INTENT(IN) :: openpmd_unit_dimension
603 : REAL(kind=dp), OPTIONAL, INTENT(IN) :: openpmd_unit_si
604 : REAL(kind=dp), OPTIONAL, INTENT(IN) :: sim_time
605 : INTEGER :: res
606 :
607 : #ifdef __OPENPMD
608 :
609 : CHARACTER(len=default_path_length) :: filename
610 :
611 : CHARACTER(len=default_string_length) :: openpmd_config, outPath, file_extension
612 : LOGICAL :: found, &
613 : my_mpi_io, &
614 : my_should_output, &
615 : replace
616 : INTEGER :: openpmd_file_index, openpmd_call_index
617 : TYPE(section_vals_type), POINTER :: print_key
618 :
619 : my_mpi_io = .FALSE.
620 : replace = .FALSE.
621 : found = .FALSE.
622 : res = -1
623 : IF (PRESENT(mpi_io)) THEN
624 : #if defined(__parallel)
625 : IF (logger%para_env%num_pe > 1 .AND. mpi_io) THEN
626 : my_mpi_io = .TRUE.
627 : ELSE
628 : my_mpi_io = .FALSE.
629 : END IF
630 : #else
631 : my_mpi_io = .FALSE.
632 : #endif
633 0 : ! Set return value
634 : mpi_io = my_mpi_io
635 : END IF
636 : NULLIFY (print_key)
637 : CPASSERT(ASSOCIATED(logger))
638 : CPASSERT(basis_section%ref_count > 0)
639 0 : CPASSERT(logger%ref_count > 0)
640 : my_should_output = BTEST(cp_print_key_should_output(logger%iter_info, &
641 0 : basis_section, print_key_path, used_print_key=print_key), cp_p_file)
642 : IF (PRESENT(ignore_should_output)) my_should_output = my_should_output .OR. ignore_should_output
643 : IF (.NOT. my_should_output) RETURN
644 : IF (logger%para_env%is_source() .OR. my_mpi_io) THEN
645 :
646 : CALL section_vals_val_get(print_key, "FILENAME", c_val=outPath)
647 : CALL section_vals_val_get(print_key, "OPENPMD_EXTENSION", c_val=file_extension)
648 10850 : CALL section_vals_val_get(print_key, "OPENPMD_CFG_FILE", c_val=openpmd_config)
649 : IF (LEN_TRIM(openpmd_config) == 0) THEN
650 : CALL section_vals_val_get(print_key, "OPENPMD_CFG", c_val=openpmd_config)
651 : ELSE
652 : openpmd_config = "@"//TRIM(openpmd_config)
653 : END IF
654 : filename = cp_print_key_generate_openpmd_filename(logger, print_key, openpmd_basename, file_extension)
655 :
656 : IF (PRESENT(fout)) THEN
657 : fout = filename
658 : END IF
659 :
660 10850 : openpmd_file_index = cp_openpmd_get_openpmd_file_entry( &
661 : openpmd_basename, filename, openpmd_config, logger, my_mpi_io)
662 :
663 : OPEN (newunit=res, status='scratch', action='write')
664 : BLOCK
665 : TYPE(cp_openpmd_per_call_value_type) :: output_info
666 :
667 : output_info = cp_openpmd_create_unit_nr_entry( &
668 : openpmd_file_index, &
669 : middle_name, &
670 : logger, &
671 : openpmd_unit_dimension, &
672 : openpmd_unit_si, &
673 : sim_time)
674 : openpmd_call_index = cp_openpmd_add_unit_nr( &
675 : res, output_info)
676 : END BLOCK
677 :
678 : ELSE
679 : res = -1
680 : END IF
681 : #else
682 : MARK_USED(logger)
683 : MARK_USED(basis_section)
684 : MARK_USED(print_key_path)
685 : MARK_USED(middle_name)
686 0 : MARK_USED(ignore_should_output)
687 : MARK_USED(mpi_io)
688 : MARK_USED(fout)
689 : MARK_USED(openpmd_basename)
690 : MARK_USED(openpmd_basename)
691 : MARK_USED(openpmd_unit_dimension)
692 : MARK_USED(openpmd_unit_si)
693 : MARK_USED(sim_time)
694 : res = 0
695 : CPABORT("CP2K compiled without the openPMD-api")
696 : #endif
697 : END FUNCTION cp_openpmd_print_key_unit_nr
698 :
699 : ! **************************************************************************************************
700 : !> \brief should be called after you finish working with a unit obtained with
701 : !> cp_openpmd_print_key_unit_nr, so that the file that might have been opened
702 : !> can be closed.
703 : !>
704 : !> the inputs should be exactly the same of the corresponding
705 : !> cp_openpmd_print_key_unit_nr
706 : !> \param unit_nr ...
707 : !> \param logger ...
708 : !> \param basis_section ...
709 : !> \param print_key_path ...
710 : !> \param local ...
711 : !> \param ignore_should_output ...
712 : !> \param mpi_io True if file was opened in parallel with MPI
713 : !> \param use_openpmd ...
714 : !> \note
715 : !> closes if the corresponding filename of the printkey is
716 : !> not __STD_OUT__
717 : ! **************************************************************************************************
718 : SUBROUTINE cp_openpmd_print_key_finished_output(unit_nr, logger, basis_section, &
719 : print_key_path, local, ignore_should_output, &
720 : mpi_io)
721 : INTEGER, INTENT(INOUT) :: unit_nr
722 : TYPE(cp_logger_type), POINTER :: logger
723 : TYPE(section_vals_type), INTENT(IN) :: basis_section
724 : CHARACTER(len=*), INTENT(IN), OPTIONAL :: print_key_path
725 : LOGICAL, INTENT(IN), OPTIONAL :: local, ignore_should_output, &
726 : mpi_io
727 :
728 : #ifdef __OPENPMD
729 :
730 : CHARACTER(len=default_string_length) :: outPath
731 : LOGICAL :: my_local, my_mpi_io, &
732 : my_should_output
733 : TYPE(section_vals_type), POINTER :: print_key
734 :
735 : my_local = .FALSE.
736 : my_mpi_io = .FALSE.
737 : NULLIFY (print_key)
738 : IF (PRESENT(local)) my_local = local
739 : IF (PRESENT(mpi_io)) my_mpi_io = mpi_io
740 : CPASSERT(ASSOCIATED(logger))
741 : CPASSERT(basis_section%ref_count > 0)
742 : CPASSERT(logger%ref_count > 0)
743 : my_should_output = BTEST(cp_print_key_should_output(logger%iter_info, basis_section, &
744 : print_key_path, used_print_key=print_key), cp_p_file)
745 : IF (PRESENT(ignore_should_output)) my_should_output = my_should_output .OR. ignore_should_output
746 : IF (my_should_output .AND. (my_local .OR. &
747 : logger%para_env%is_source() .OR. &
748 : my_mpi_io)) THEN
749 : CALL section_vals_val_get(print_key, "FILENAME", c_val=outPath)
750 : IF (cp_openpmd_remove_unit_nr(unit_nr)) THEN
751 : CLOSE (unit_nr)
752 : END IF
753 :
754 : unit_nr = -1
755 : END IF
756 : CPASSERT(unit_nr == -1)
757 : unit_nr = -1
758 : #else
759 : MARK_USED(unit_nr)
760 : MARK_USED(logger)
761 : MARK_USED(basis_section)
762 : MARK_USED(print_key_path)
763 : MARK_USED(local)
764 : MARK_USED(ignore_should_output)
765 : MARK_USED(mpi_io)
766 : CPABORT("CP2K compiled without the openPMD-api")
767 : #endif
768 : END SUBROUTINE cp_openpmd_print_key_finished_output
769 :
770 : SUBROUTINE cp_openpmd_close_iterations()
771 : #ifdef __OPENPMD
772 : INTEGER :: handle
773 : TYPE(cp_openpmd_per_callsite_value_type) :: series_data
774 : TYPE(openpmd_iteration_type) :: iteration
775 : INTEGER :: i
776 :
777 : CALL timeset('openpmd_close_iterations', handle)
778 : DO i = 1, cp_num_openpmd_per_callsite
779 : series_data = cp_openpmd_per_callsite(i)%value
780 : iteration = series_data%output_series%get_iteration( &
781 : series_data%iteration_counter%flat_iteration)
782 : IF (.NOT. iteration%closed()) THEN
783 : CALL iteration%close()
784 : END IF
785 : END DO
786 : CALL timestop(handle)
787 : #endif
788 : END SUBROUTINE cp_openpmd_close_iterations
789 :
790 : FUNCTION cp_openpmd_get_default_extension() RESULT(extension)
791 : CHARACTER(len=default_string_length) :: extension
792 :
793 : #ifdef __OPENPMD
794 : extension = openpmd_get_default_extension()
795 : #else
796 : extension = ".bp5"
797 : #endif
798 :
799 : END FUNCTION cp_openpmd_get_default_extension
800 0 :
801 0 : END MODULE cp_output_handling_openpmd
802 : /* /opt/cp2k/src/input/cp_output_handling_openpmd.F not long enough */
803 0 : /* END: function "__cp_output_handling_openpmd_MOD_cp_openpmd_print_key_unit_nr" */
804 : /* ... */
805 : /* ... */
806 : /* ... */
807 : /* ... */
808 : /* ... */
809 : /* ... */
810 : /* ... */
811 : /* ... */
812 : /* ... */
813 : /* ... */
814 : /* ... */
815 : /* ... */
816 : /* ... */
817 : /* ... */
818 : /* ... */
819 : /* ... */
820 : /* ... */
821 : /* ... */
822 : /* /opt/cp2k/src/input/cp_output_handling_openpmd.F not long enough */
823 : /* (content generated from coverage data) */
824 0 : /* BEGIN: function "__cp_output_handling_openpmd_MOD_cp_openpmd_print_key_finished_output" */
825 : /* ... */
826 : /* ... */
827 : /* ... */
828 : /* ... */
829 : /* ... */
830 : /* ... */
831 : /* ... */
832 : /* ... */
833 : /* ... */
834 : /* ... */
835 : /* ... */
836 : /* ... */
837 : /* ... */
838 : /* ... */
839 : /* ... */
840 : /* ... */
841 : /* ... */
842 : /* /opt/cp2k/src/input/cp_output_handling_openpmd.F not long enough */
843 : /* (content generated from coverage data) */
844 : /* ... */
845 : /* ... */
846 : /* ... */
847 : /* ... */
848 : /* ... */
849 : /* ... */
850 : /* ... */
851 : /* ... */
852 : /* ... */
853 : /* ... */
854 : /* ... */
855 : /* ... */
856 : /* ... */
857 : /* ... */
858 : /* ... */
859 : /* ... */
860 : /* ... */
861 : /* ... */
862 : /* /opt/cp2k/src/input/cp_output_handling_openpmd.F not long enough */
863 : /* (content generated from coverage data) */
864 : /* ... */
865 : /* ... */
866 : /* ... */
867 : /* ... */
868 : /* ... */
869 : /* ... */
870 : /* ... */
871 : /* ... */
872 0 : /* ... */
873 : /* ... */
874 0 : /* END: function "__cp_output_handling_openpmd_MOD_cp_openpmd_print_key_finished_output" */
875 : /* ... */
876 12035 : /* BEGIN: function "__cp_output_handling_openpmd_MOD_cp_openpmd_close_iterations" */
877 : /* ... */
878 : /* ... */
879 : /* ... */
880 : /* ... */
881 : /* ... */
882 : /* /opt/cp2k/src/input/cp_output_handling_openpmd.F not long enough */
883 : /* (content generated from coverage data) */
884 : /* ... */
885 : /* ... */
886 : /* ... */
887 : /* ... */
888 : /* ... */
889 : /* ... */
890 : /* ... */
891 : /* ... */
892 : /* ... */
893 : /* ... */
894 12035 : /* END: function "__cp_output_handling_openpmd_MOD_cp_openpmd_close_iterations" */
895 : /* ... */
896 32607 : /* BEGIN: function "__cp_output_handling_openpmd_MOD_cp_openpmd_get_default_extension" */
897 : /* ... */
898 : /* ... */
899 : /* ... */
900 : /* ... */
901 : /* ... */
902 32607 : /* /opt/cp2k/src/input/cp_output_handling_openpmd.F not long enough */
903 : /* (content generated from coverage data) */
904 : /* ... */
905 32607 : /* END: function "__cp_output_handling_openpmd_MOD_cp_openpmd_get_default_extension" */
906 : /* ... */
907 0 : /* END: function "__cp_output_handling_openpmd_MOD___copy_93A4A3" */
|