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