(git:03cdb6f)
Loading...
Searching...
No Matches
cp_output_handling_openpmd.F
Go to the documentation of this file.
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! **************************************************************************************************
20 USE cp_files, ONLY: close_file, &
43 USE kinds, ONLY: default_path_length, &
45 USE machine, ONLY: m_mov
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'
73
74#ifdef __OPENPMD
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'
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
176 ! nothing there
178
179#endif
180
181CONTAINS
182
183#ifdef __OPENPMD
184 ! Helper functions for interacting with the two maps declared above.
185
186
187 ! TODO No reallocation support for now, change cp_allocation_size if larger sizes needed.
188
189 ! **************************************************************************************************
190 !> \brief ...
191 !> \param key ...
192 !> \param value ...
193 ! **************************************************************************************************
194 FUNCTION cp_openpmd_add_unit_nr (key, value) RESULT(index)
195 INTEGER, INTENT(in) :: key
196 TYPE(cp_openpmd_per_call_value_type), INTENT(in) :: value
197 INTEGER :: index
198
199 LOGICAL :: check_capacity
200 INTEGER :: i
201
202 ! Check if the key already exists
203 DO i = 1, cp_num_openpmd_per_call
204 IF (cp_openpmd_per_call(i)%key == key) THEN
205 cp_openpmd_per_call(i)%value = value
206 index = i
207 RETURN
208 END IF
209 END DO
210
211 IF (cp_capacity_openpmd_per_call == 0) THEN
212 ALLOCATE (cp_openpmd_per_call(cp_allocation_size))
213 cp_capacity_openpmd_per_call = cp_allocation_size
214 END IF
215
216 ! No idea how to do reallocations, so for now just assert that they're not needed
217 check_capacity = cp_num_openpmd_per_call < cp_capacity_openpmd_per_call
218 cpassert(check_capacity)
219
220 ! Add a new entry
221 cp_num_openpmd_per_call = cp_num_openpmd_per_call+1
222 cp_openpmd_per_call(cp_num_openpmd_per_call)%key = key
223 cp_openpmd_per_call(cp_num_openpmd_per_call)%value = value
224 index = cp_num_openpmd_per_call
225 END FUNCTION cp_openpmd_add_unit_nr
226
227 ! **************************************************************************************************
228 !> \brief ...
229 !> \param key ...
230 !> \return ...
231 ! **************************************************************************************************
232 FUNCTION cp_openpmd_get_index_unit_nr (key) RESULT(index)
233 INTEGER, INTENT(in) :: key
234 INTEGER :: index
235
236 INTEGER :: i
237
238 index = -1
239
240 DO i = 1, cp_num_openpmd_per_call
241 IF (cp_openpmd_per_call(i)%key == key) THEN
242 index = i
243 RETURN
244 END IF
245 END DO
246 END FUNCTION cp_openpmd_get_index_unit_nr
247
248 FUNCTION cp_openpmd_get_value_unit_nr (key) RESULT(value)
249 INTEGER, INTENT(in) :: key
250 TYPE(cp_openpmd_per_call_value_type) :: value
251
252 INTEGER :: i
253
254 i = cp_openpmd_get_index_unit_nr(key)
255 IF (i == -1) RETURN
256
257 value = cp_openpmd_per_call(i)%value
259
260 ! **************************************************************************************************
261 !> \brief ...
262 !> \param key ...
263 !> \return ...
264 ! **************************************************************************************************
265 FUNCTION cp_openpmd_remove_unit_nr (key) RESULT(was_found)
266 INTEGER, INTENT(in) :: key
267 LOGICAL :: was_found
268
269 INTEGER :: i
270
271 was_found = .false.
272
273 DO i = 1, cp_num_openpmd_per_call
274 IF (cp_openpmd_per_call(i)%key == key) THEN
275 was_found = .true.
276 IF (i /= cp_num_openpmd_per_call) THEN
277 ! Swap last element to now freed place
278 cp_openpmd_per_call(i) = cp_openpmd_per_call(cp_num_openpmd_per_call)
279 END IF
280
281 cp_num_openpmd_per_call = cp_num_openpmd_per_call-1
282 IF (cp_num_openpmd_per_call == 0) THEN
283 DEALLOCATE (cp_openpmd_per_call)
284 cp_capacity_openpmd_per_call = 0
285 END IF
286 RETURN
287 END IF
288 END DO
289 END FUNCTION cp_openpmd_remove_unit_nr
290
291
292 ! TODO No reallocation support for now, change cp_allocation_size if larger sizes needed.
293
294 ! **************************************************************************************************
295 !> \brief ...
296 !> \param key ...
297 !> \param value ...
298 ! **************************************************************************************************
299 FUNCTION cp_openpmd_add_filedata (key, value) RESULT(index)
300 CHARACTER(len=default_string_length), INTENT(in) :: key
301 TYPE(cp_openpmd_per_callsite_value_type), INTENT(in) :: value
302 INTEGER :: index
303
304 LOGICAL :: check_capacity
305 INTEGER :: i
306
307 ! Check if the key already exists
308 DO i = 1, cp_num_openpmd_per_callsite
309 IF (cp_openpmd_per_callsite(i)%key == key) THEN
310 cp_openpmd_per_callsite(i)%value = value
311 index = i
312 RETURN
313 END IF
314 END DO
315
316 IF (cp_capacity_openpmd_per_callsite == 0) THEN
317 ALLOCATE (cp_openpmd_per_callsite(cp_allocation_size))
318 cp_capacity_openpmd_per_callsite = cp_allocation_size
319 END IF
320
321 ! No idea how to do reallocations, so for now just assert that they're not needed
322 check_capacity = cp_num_openpmd_per_callsite < cp_capacity_openpmd_per_callsite
323 cpassert(check_capacity)
324
325 ! Add a new entry
326 cp_num_openpmd_per_callsite = cp_num_openpmd_per_callsite+1
327 cp_openpmd_per_callsite(cp_num_openpmd_per_callsite)%key = key
328 cp_openpmd_per_callsite(cp_num_openpmd_per_callsite)%value = value
329 index = cp_num_openpmd_per_callsite
330 END FUNCTION cp_openpmd_add_filedata
331
332 ! **************************************************************************************************
333 !> \brief ...
334 !> \param key ...
335 !> \return ...
336 ! **************************************************************************************************
337 FUNCTION cp_openpmd_get_index_filedata (key) RESULT(index)
338 CHARACTER(len=default_string_length), INTENT(in) :: key
339 INTEGER :: index
340
341 INTEGER :: i
342
343 index = -1
344
345 DO i = 1, cp_num_openpmd_per_callsite
346 IF (cp_openpmd_per_callsite(i)%key == key) THEN
347 index = i
348 RETURN
349 END IF
350 END DO
351 END FUNCTION cp_openpmd_get_index_filedata
352
353 FUNCTION cp_openpmd_get_value_filedata (key) RESULT(value)
354 CHARACTER(len=default_string_length), INTENT(in) :: key
355 TYPE(cp_openpmd_per_callsite_value_type) :: value
356
357 INTEGER :: i
358
359 i = cp_openpmd_get_index_filedata(key)
360 IF (i == -1) RETURN
361
362 value = cp_openpmd_per_callsite(i)%value
363 END FUNCTION cp_openpmd_get_value_filedata
364
365 ! **************************************************************************************************
366 !> \brief ...
367 !> \param key ...
368 !> \return ...
369 ! **************************************************************************************************
370 FUNCTION cp_openpmd_remove_filedata (key) RESULT(was_found)
371 CHARACTER(len=default_string_length), INTENT(in) :: key
372 LOGICAL :: was_found
373
374 INTEGER :: i
375
376 was_found = .false.
377
378 DO i = 1, cp_num_openpmd_per_callsite
379 IF (cp_openpmd_per_callsite(i)%key == key) THEN
380 was_found = .true.
381 IF (i /= cp_num_openpmd_per_callsite) THEN
382 ! Swap last element to now freed place
383 cp_openpmd_per_callsite(i) = cp_openpmd_per_callsite(cp_num_openpmd_per_callsite)
384 END IF
385
386 cp_num_openpmd_per_callsite = cp_num_openpmd_per_callsite-1
387 IF (cp_num_openpmd_per_callsite == 0) THEN
388 DEALLOCATE (cp_openpmd_per_callsite)
389 cp_capacity_openpmd_per_callsite = 0
390 END IF
391 RETURN
392 END IF
393 END DO
394 END FUNCTION cp_openpmd_remove_filedata
395
396
397! **************************************************************************************************
398!> \brief Simplified version of cp_print_key_generate_filename. Since an openPMD Series encompasses
399! multiple datasets that would be separate outputs in e.g. .cube files, this needs not
400! consider dataset names for creation of a filename.
401!> \param logger ...
402!> \param print_key ...
403!> \param openpmd_basename ...
404!> \param extension ...
405!> \param my_local ...
406!> \return ...
407! **************************************************************************************************
408 FUNCTION cp_print_key_generate_openpmd_filename(logger, print_key, openpmd_basename, extension) RESULT(filename)
409 TYPE(cp_logger_type), POINTER :: logger
410 TYPE(section_vals_type), POINTER :: print_key
411 CHARACTER(len=*), INTENT(IN) :: openpmd_basename, extension
412 CHARACTER(len=default_path_length) :: filename
413
414 CHARACTER(len=default_path_length) :: outName, outPath, root
415 INTEGER :: my_ind1, my_ind2
416 LOGICAL :: has_root
417
418 CALL section_vals_val_get(print_key, "FILENAME", c_val=outpath)
419 IF (outpath(1:1) == '=') THEN
420 cpassert(len(outpath) - 1 <= len(filename))
421 filename = outpath(2:)
422 RETURN
423 END IF
424 IF (outpath == "__STD_OUT__") outpath = ""
425 outname = trim(outpath)
426 has_root = .false.
427 my_ind1 = index(outpath, "/")
428 my_ind2 = len_trim(outpath)
429 IF (my_ind1 /= 0) THEN
430 has_root = .true.
431 DO WHILE (index(outpath(my_ind1 + 1:my_ind2), "/") /= 0)
432 my_ind1 = index(outpath(my_ind1 + 1:my_ind2), "/") + my_ind1
433 END DO
434 IF (my_ind1 == my_ind2) THEN
435 outname = ""
436 ELSE
437 outname = outpath(my_ind1 + 1:my_ind2)
438 END IF
439 END IF
440
441 IF (.NOT. has_root) THEN
442 root = trim(logger%iter_info%project_name)
443 ELSE IF (outname == "") THEN
444 root = outpath(1:my_ind1)//trim(logger%iter_info%project_name)
445 ELSE
446 root = outpath(1:my_ind1)
447 END IF
448
449 filename = adjustl(trim(root)//"_"//trim(openpmd_basename)//trim(extension))
450
451 END FUNCTION cp_print_key_generate_openpmd_filename
452
453! **************************************************************************************************
454!> \brief CP2K Iteration numbers are n-dimensional while openPMD Iteration numbers are scalars.
455! This checks if the Iteration number has changed from the previous call (stored in
456! openpmd_file%iteration_counter) and updates it if needed.
457!> \param logger ...
458!> \return ...
459! **************************************************************************************************
460 FUNCTION cp_advance_iteration_number(logger, openpmd_file) RESULT(did_advance_iteration)
461 TYPE(cp_logger_type), POINTER :: logger
462 TYPE(cp_openpmd_per_callsite_value_type) :: openpmd_file
463 LOGICAL :: did_advance_iteration
464
465 INTEGER :: len
466
467
468 did_advance_iteration = .false.
469 len = SIZE(logger%iter_info%iteration)
470 IF (len /= openpmd_file%iteration_counter%complex_iteration_depth) THEN
471 did_advance_iteration = .true.
472 openpmd_file%iteration_counter%complex_iteration_depth = len
473 ALLOCATE (openpmd_file%iteration_counter%complex_iteration(len))
474 ELSE
475 did_advance_iteration &
476 = any(openpmd_file%iteration_counter%complex_iteration(1:len) &
477 /= logger%iter_info%iteration(1:len))
478 END IF
479
480 IF (.NOT. did_advance_iteration) RETURN
481
482 openpmd_file%iteration_counter%flat_iteration = openpmd_file%iteration_counter%flat_iteration + 1
483 openpmd_file%iteration_counter%complex_iteration(1:len) &
484 = logger%iter_info%iteration(1:len)
485
486 END FUNCTION cp_advance_iteration_number
487
488! **************************************************************************************************
489!> \brief CP2K deals with output handles in terms of unit numbers.
490! The openPMD output logic does not change this association.
491! For this, we need to emulate unit numbers as (1) they are not native to openPMD and
492! (2) a single openPMD Series might contain multiple datasets treated logically by CP2K
493! as distinct outputs. As a result, a single unit number is resolved by the openPMD logic
494! to the values represented by the cp_openpmd_per_call_value_type struct,
495! containing the output Series and the referred datasets therein (Iteration number,
496! name prefix for meshes and particles, referred output Series ...).
497!> \param series ...
498!> \param middle_name ...
499!> \param logger ...
500!> \return ...
501! **************************************************************************************************
502 FUNCTION cp_openpmd_create_unit_nr_entry(openpmd_file_index, middle_name, logger) RESULT(res)
503 INTEGER :: openpmd_file_index
504 CHARACTER(len=*), INTENT(IN) :: middle_name
505 TYPE(cp_logger_type), POINTER :: logger
506 TYPE(cp_openpmd_per_call_value_type) :: res
507
508 LOGICAL, SAVE :: opened_new_iteration = .false.
509 TYPE(openpmd_attributable_type) :: attr
510 TYPE(cp_openpmd_per_callsite_value_type), POINTER :: opmd
511
512 opmd => cp_openpmd_per_callsite(openpmd_file_index)%value
513
514 res%series = opmd%output_series
515
516 opened_new_iteration = cp_advance_iteration_number(logger, opmd)
517
518 res%iteration = opmd%output_series%write_iteration(opmd%iteration_counter%flat_iteration)
519 res%name_prefix = trim(middle_name)
520
521 IF (opened_new_iteration) THEN
522 attr = res%iteration%as_attributable()
523 CALL attr%set_attribute_vec_int( &
524 "ndim_iteration_index", &
525 opmd%iteration_counter%complex_iteration)
526 END IF
527 END FUNCTION cp_openpmd_create_unit_nr_entry
528
529! **************************************************************************************************
530!> \brief Check if there is already an output Series created for the callsite identified
531! by openpmd_basename. If so, then return it (by index), otherwise open the Series now
532! and return the index then.
533 FUNCTION cp_openpmd_get_openpmd_file_entry(openpmd_basename, filename, openpmd_config, logger, use_mpi) RESULT(file_index)
534 CHARACTER(len=*), INTENT(IN) :: openpmd_basename, filename, openpmd_config
535 TYPE(cp_logger_type), POINTER :: logger
536 LOGICAL :: use_mpi
537 INTEGER :: file_index
538 CHARACTER(:), ALLOCATABLE :: merged_config
539
540 CHARACTER(len=default_string_length), SAVE :: basename_copied = ' '
541 TYPE(cp_openpmd_per_callsite_value_type) :: emplace_new
542
543 INTEGER :: handle
544 TYPE(cp_openpmd_per_callsite_value_type) :: series_data
545 TYPE(openpmd_iteration_type) :: iteration
546 INTEGER :: i
547
548 basename_copied(1:len_trim(openpmd_basename)) = trim(openpmd_basename)
549
550 file_index = cp_openpmd_get_index_filedata(basename_copied)
551
552 CALL timeset('openpmd_close_iterations', handle)
553 DO i = 1, cp_num_openpmd_per_callsite
554 IF (i /= file_index) THEN
555 series_data = cp_openpmd_per_callsite(i)%value
556 iteration = series_data%output_series%get_iteration( &
557 series_data%iteration_counter%flat_iteration)
558 IF (.NOT. iteration%closed()) THEN
559 CALL iteration%close()
560 END IF
561 END IF
562 END DO
563 CALL timestop(handle)
564
565 IF (file_index /= -1) RETURN
566
567#ifndef _WIN32
568 merged_config = openpmd_json_merge(cp_default_backend_config, cp_default_backend_config_non_windows)
569#else
570 merged_config = cp_default_backend_config
571#endif
572 IF (use_mpi) THEN
573 merged_config = openpmd_json_merge(merged_config, openpmd_config, logger%para_env)
574 emplace_new%output_series = openpmd_series_create( &
575 filename, openpmd_access_create, logger%para_env, merged_config)
576 ELSE
577 merged_config = openpmd_json_merge(merged_config, openpmd_config)
578 emplace_new%output_series = openpmd_series_create( &
579 filename, openpmd_access_create, config=merged_config)
580 END IF
581 DEALLOCATE (merged_config)
582 file_index = cp_openpmd_add_filedata(basename_copied, emplace_new)
583 END FUNCTION cp_openpmd_get_openpmd_file_entry
584
585#else ! defined(__OPENPMD)
586
587 FUNCTION cp_openpmd_get_value_unit_nr(key) RESULT(value)
588 INTEGER, INTENT(in) :: key
589 TYPE(cp_openpmd_per_call_value_type) :: value
590
591 mark_used(key)
592 mark_used(value)
593 cpabort("CP2K compiled without the openPMD-api")
594
596
597#endif
598
599! **************************************************************************************************
600!> \brief Close all outputs.
601! **************************************************************************************************
603#ifdef __OPENPMD
604 INTEGER :: i
605 DO i = 1, cp_num_openpmd_per_callsite
606 DEALLOCATE (cp_openpmd_per_callsite(i)%value%iteration_counter%complex_iteration)
607 CALL cp_openpmd_per_callsite(i)%value%output_series%close()
608 END DO
609 IF (ALLOCATED(cp_openpmd_per_callsite)) THEN
610 DEALLOCATE (cp_openpmd_per_callsite)
611 END IF
612 cp_num_openpmd_per_callsite = 0
613#endif
614 END SUBROUTINE cp_openpmd_output_finalize
615
616! **************************************************************************************************
617!> \brief ...
618!> \param logger ...
619!> \param basis_section ...
620!> \param print_key_path ...
621!> \param extension ...
622!> \param middle_name ...
623!> \param local ...
624!> \param log_filename ...
625!> \param ignore_should_output ...
626!> \param do_backup ...
627!> \param is_new_file true if this rank created a new (or rewound) file, false otherwise
628!> \param mpi_io True if the file should be opened in parallel on all processors belonging to
629!> the communicator group. Automatically disabled if the file form or access mode
630!> is unsuitable for MPI IO. Return value indicates whether MPI was actually used
631!> and therefore the flag must also be passed to the file closing directive.
632!> \param fout Name of the actual file where the output will be written. Needed mainly for MPI IO
633!> because inquiring the filename from the MPI filehandle does not work across
634!> all MPI libraries.
635!> \param openpmd_basename Used to associate an identifier to each callsite of this module
636!> \param use_openpmd ...
637!> \return ...
638! **************************************************************************************************
639 FUNCTION cp_openpmd_print_key_unit_nr(logger, basis_section, print_key_path, &
640 middle_name, ignore_should_output, &
641 mpi_io, &
642 fout, openpmd_basename) RESULT(res)
643 TYPE(cp_logger_type), POINTER :: logger
644 TYPE(section_vals_type), INTENT(IN) :: basis_section
645 CHARACTER(len=*), INTENT(IN), OPTIONAL :: print_key_path
646 CHARACTER(len=*), INTENT(IN), OPTIONAL :: middle_name
647 LOGICAL, INTENT(IN), OPTIONAL :: ignore_should_output
648 LOGICAL, INTENT(INOUT), OPTIONAL :: mpi_io
649 CHARACTER(len=default_path_length), INTENT(OUT), &
650 OPTIONAL :: fout
651 CHARACTER(len=*), INTENT(IN), OPTIONAL :: openpmd_basename
652 INTEGER :: res
653
654#ifdef __OPENPMD
655
656 CHARACTER(len=default_path_length) :: filename
657
658 CHARACTER(len=default_string_length) :: openpmd_config, outpath, file_extension
659 LOGICAL :: found, &
660 my_mpi_io, &
661 my_should_output, &
662 replace
663 INTEGER :: openpmd_file_index, openpmd_call_index
664 TYPE(section_vals_type), POINTER :: print_key
665
666 my_mpi_io = .false.
667 replace = .false.
668 found = .false.
669 res = -1
670 IF (PRESENT(mpi_io)) THEN
671#if defined(__parallel)
672 IF (logger%para_env%num_pe > 1 .AND. mpi_io) THEN
673 my_mpi_io = .true.
674 ELSE
675 my_mpi_io = .false.
676 END IF
677#else
678 my_mpi_io = .false.
679#endif
680 ! Set return value
681 mpi_io = my_mpi_io
682 END IF
683 NULLIFY (print_key)
684 cpassert(ASSOCIATED(logger))
685 cpassert(basis_section%ref_count > 0)
686 cpassert(logger%ref_count > 0)
687 my_should_output = btest(cp_print_key_should_output(logger%iter_info, &
688 basis_section, print_key_path, used_print_key=print_key), cp_p_file)
689 IF (PRESENT(ignore_should_output)) my_should_output = my_should_output .OR. ignore_should_output
690 IF (.NOT. my_should_output) RETURN
691 IF (logger%para_env%is_source() .OR. my_mpi_io) THEN
692
693 CALL section_vals_val_get(print_key, "FILENAME", c_val=outpath)
694 CALL section_vals_val_get(print_key, "OPENPMD_EXTENSION", c_val=file_extension)
695 CALL section_vals_val_get(print_key, "OPENPMD_CFG_FILE", c_val=openpmd_config)
696 IF (len_trim(openpmd_config) == 0) THEN
697 CALL section_vals_val_get(print_key, "OPENPMD_CFG", c_val=openpmd_config)
698 ELSE
699 openpmd_config = "@"//trim(openpmd_config)
700 END IF
701 filename = cp_print_key_generate_openpmd_filename(logger, print_key, openpmd_basename, file_extension)
702
703 IF (PRESENT(fout)) THEN
704 fout = filename
705 END IF
706
707 openpmd_file_index = cp_openpmd_get_openpmd_file_entry( &
708 openpmd_basename, filename, openpmd_config, logger, my_mpi_io)
709
710 OPEN (newunit=res, status='scratch', action='write')
711 openpmd_call_index = cp_openpmd_add_unit_nr( &
712 res, &
713 cp_openpmd_create_unit_nr_entry( &
714 openpmd_file_index, middle_name, logger))
715
716 ELSE
717 res = -1
718 END IF
719#else
720 mark_used(logger)
721 mark_used(basis_section)
722 mark_used(print_key_path)
723 mark_used(middle_name)
724 mark_used(ignore_should_output)
725 mark_used(mpi_io)
726 mark_used(fout)
727 mark_used(openpmd_basename)
728 res = 0
729 cpabort("CP2K compiled without the openPMD-api")
730#endif
732
733! **************************************************************************************************
734!> \brief should be called after you finish working with a unit obtained with
735!> cp_openpmd_print_key_unit_nr, so that the file that might have been opened
736!> can be closed.
737!>
738!> the inputs should be exactly the same of the corresponding
739!> cp_openpmd_print_key_unit_nr
740!> \param unit_nr ...
741!> \param logger ...
742!> \param basis_section ...
743!> \param print_key_path ...
744!> \param local ...
745!> \param ignore_should_output ...
746!> \param mpi_io True if file was opened in parallel with MPI
747!> \param use_openpmd ...
748!> \note
749!> closes if the corresponding filename of the printkey is
750!> not __STD_OUT__
751! **************************************************************************************************
752 SUBROUTINE cp_openpmd_print_key_finished_output(unit_nr, logger, basis_section, &
753 print_key_path, local, ignore_should_output, &
754 mpi_io)
755 INTEGER, INTENT(INOUT) :: unit_nr
756 TYPE(cp_logger_type), POINTER :: logger
757 TYPE(section_vals_type), INTENT(IN) :: basis_section
758 CHARACTER(len=*), INTENT(IN), OPTIONAL :: print_key_path
759 LOGICAL, INTENT(IN), OPTIONAL :: local, ignore_should_output, &
760 mpi_io
761
762#ifdef __OPENPMD
763
764 CHARACTER(len=default_string_length) :: outpath
765 LOGICAL :: my_local, my_mpi_io, &
766 my_should_output
767 TYPE(section_vals_type), POINTER :: print_key
768
769 my_local = .false.
770 my_mpi_io = .false.
771 NULLIFY (print_key)
772 IF (PRESENT(local)) my_local = local
773 IF (PRESENT(mpi_io)) my_mpi_io = mpi_io
774 cpassert(ASSOCIATED(logger))
775 cpassert(basis_section%ref_count > 0)
776 cpassert(logger%ref_count > 0)
777 my_should_output = btest(cp_print_key_should_output(logger%iter_info, basis_section, &
778 print_key_path, used_print_key=print_key), cp_p_file)
779 IF (PRESENT(ignore_should_output)) my_should_output = my_should_output .OR. ignore_should_output
780 IF (my_should_output .AND. (my_local .OR. &
781 logger%para_env%is_source() .OR. &
782 my_mpi_io)) THEN
783 CALL section_vals_val_get(print_key, "FILENAME", c_val=outpath)
784 IF (cp_openpmd_remove_unit_nr(unit_nr)) THEN
785 CLOSE (unit_nr)
786 END IF
787
788 unit_nr = -1
789 END IF
790 cpassert(unit_nr == -1)
791 unit_nr = -1
792#else
793 mark_used(unit_nr)
794 mark_used(logger)
795 mark_used(basis_section)
796 mark_used(print_key_path)
797 mark_used(local)
798 mark_used(ignore_should_output)
799 mark_used(mpi_io)
800 cpabort("CP2K compiled without the openPMD-api")
801#endif
803
805#ifdef __OPENPMD
806 INTEGER :: handle
807 TYPE(cp_openpmd_per_callsite_value_type) :: series_data
808 TYPE(openpmd_iteration_type) :: iteration
809 INTEGER :: i
810
811 CALL timeset('openpmd_close_iterations', handle)
812 DO i = 1, cp_num_openpmd_per_callsite
813 series_data = cp_openpmd_per_callsite(i)%value
814 iteration = series_data%output_series%get_iteration( &
815 series_data%iteration_counter%flat_iteration)
816 IF (.NOT. iteration%closed()) THEN
817 CALL iteration%close()
818 END IF
819 END DO
820 CALL timestop(handle)
821#endif
822 END SUBROUTINE cp_openpmd_close_iterations
823
824 FUNCTION cp_openpmd_get_default_extension() RESULT(extension)
825 CHARACTER(len=default_string_length) :: extension
826
827#ifdef __OPENPMD
828 extension = openpmd_get_default_extension()
829#else
830 extension = ".bp5"
831#endif
832
834
Utility routines to open and close files. Tracking of preconnections.
Definition cp_files.F:16
subroutine, public open_file(file_name, file_status, file_form, file_action, file_position, file_pad, unit_number, debug, skip_get_unit_number, file_access)
Opens the requested file using a free unit number.
Definition cp_files.F:311
subroutine, public close_file(unit_number, file_status, keep_preconnection)
Close an open file given by its logical unit number. Optionally, keep the file and unit preconnected.
Definition cp_files.F:122
Collection of routines to handle the iteration info.
character(len=default_path_length), dimension(19), parameter, public each_possible_labels
subroutine, public cp_iteration_info_retain(iteration_info)
retains the iteration_info (see doc/ReferenceCounting.html)
subroutine, public cp_iteration_info_release(iteration_info)
releases the iteration_info (see doc/ReferenceCounting.html)
character(len=default_path_length), dimension(19), parameter, public each_desc_labels
various routines to log and control the output. The idea is that decisions about where to log should ...
recursive integer function, public cp_logger_get_default_unit_nr(logger, local, skip_not_ionode)
asks the default unit number of the given logger. try to use cp_logger_get_unit_nr
integer function, public cp_logger_get_unit_nr(logger, local)
returns the unit nr for the requested kind of log.
subroutine, public cp_logger_generate_filename(logger, res, root, postfix, local)
generates a unique filename (ie adding eventual suffixes and process ids)
routines to handle the output, The idea is to remove the decision of wheter to output and what to out...
subroutine, public cp_openpmd_close_iterations()
character(len=default_string_length) function, public cp_openpmd_get_default_extension()
subroutine, public cp_openpmd_output_finalize()
Close all outputs.
type(cp_openpmd_per_call_value_type) function, public cp_openpmd_get_value_unit_nr(key)
integer function, public cp_openpmd_print_key_unit_nr(logger, basis_section, print_key_path, middle_name, ignore_should_output, mpi_io, fout, openpmd_basename)
...
subroutine, public cp_openpmd_print_key_finished_output(unit_nr, logger, basis_section, print_key_path, local, ignore_should_output, mpi_io)
should be called after you finish working with a unit obtained with cp_openpmd_print_key_unit_nr,...
routines to handle the output, The idea is to remove the decision of wheter to output and what to out...
integer, parameter, public cp_p_file
integer function, public cp_print_key_should_output(iteration_info, basis_section, print_key_path, used_print_key, first_time)
returns what should be done with the given property if btest(res,cp_p_store) then the property should...
represents keywords in an input
subroutine, public keyword_release(keyword)
releases the given keyword (see doc/ReferenceCounting.html)
subroutine, public keyword_create(keyword, location, name, description, usage, type_of_var, n_var, repeats, variants, default_val, default_l_val, default_r_val, default_lc_val, default_c_val, default_i_val, default_l_vals, default_r_vals, default_c_vals, default_i_vals, lone_keyword_val, lone_keyword_l_val, lone_keyword_r_val, lone_keyword_c_val, lone_keyword_i_val, lone_keyword_l_vals, lone_keyword_r_vals, lone_keyword_c_vals, lone_keyword_i_vals, enum_c_vals, enum_i_vals, enum, enum_strict, enum_desc, unit_str, citations, deprecation_notice, removed)
creates a keyword object
objects that represent the structure of input sections and the data contained in an input section
subroutine, public section_create(section, location, name, description, n_keywords, n_subsections, repeats, citations, deprecation_notice)
creates a list of keywords
subroutine, public section_add_keyword(section, keyword)
adds a keyword to the given section
subroutine, public section_add_subsection(section, subsection)
adds a subsection to the given section
recursive type(section_vals_type) function, pointer, public section_vals_get_subs_vals(section_vals, subsection_name, i_rep_section, can_return_null)
returns the values of the requested subsection
recursive subroutine, public section_release(section)
releases the given keyword list (see doc/ReferenceCounting.html)
subroutine, public section_vals_val_get(section_vals, keyword_name, i_rep_section, i_rep_val, n_rep_val, val, l_val, i_val, r_val, c_val, l_vals, i_vals, r_vals, c_vals, explicit)
returns the requested value
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public default_string_length
Definition kinds.F:57
integer, parameter, public default_path_length
Definition kinds.F:58
Machine interface based on Fortran 2003 and POSIX.
Definition machine.F:17
subroutine, public m_mov(source, target)
...
Definition machine.F:707
Utility routines for the memory handling.
Interface to the message passing library MPI.
subroutine, public mp_file_delete(filepath, info)
Deletes a file. Auxiliary routine to emulate 'replace' action for mp_file_open. Only the master proce...
subroutine, public mp_file_get_amode(mpi_io, replace, amode, form, action, status, position)
(parallel) Utility routine to determine MPI file access mode based on variables
Utilities for string manipulations.
subroutine, public compress(string, full)
Eliminate multiple space characters in a string. If full is .TRUE., then all spaces are eliminated.
contains the information about the current state of the program to be able to decide if output is nec...
type of a logger, at the moment it contains just a print level starting at which level it should be l...
represent a keyword in the input
represent a section of the input file