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