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