Line data Source code
1 : !--------------------------------------------------------------------------------------------------!
2 : ! CP2K: A general program to perform molecular dynamics simulations !
3 : ! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4 : ! !
5 : ! SPDX-License-Identifier: GPL-2.0-or-later !
6 : !--------------------------------------------------------------------------------------------------!
7 :
8 : ! **************************************************************************************************
9 : !> \brief routines to handle the output, The idea is to remove the
10 : !> decision of wheter to output and what to output from the code
11 : !> that does the output, and centralize it here.
12 : !> \note
13 : !> These were originally together with the log handling routines,
14 : !> but have been spawned off. Some dependencies are still there,
15 : !> and some of the comments about log handling also applies to output
16 : !> handling: @see cp_log_handling
17 : ! **************************************************************************************************
18 : MODULE cp_output_handling_openpmd
19 : USE cp_output_handling, only: cp_print_key_should_output, cp_p_file
20 : USE cp_files, ONLY: close_file, &
21 : open_file
22 : USE cp_iter_types, ONLY: cp_iteration_info_release, &
23 : cp_iteration_info_retain, &
24 : cp_iteration_info_type, &
25 : each_desc_labels, &
26 : each_possible_labels
27 : USE cp_log_handling, ONLY: cp_logger_generate_filename, &
28 : cp_logger_get_default_unit_nr, &
29 : cp_logger_get_unit_nr, &
30 : cp_logger_type, &
31 : cp_to_string
32 : USE input_keyword_types, ONLY: keyword_create, &
33 : keyword_release, &
34 : keyword_type
35 : USE input_section_types, ONLY: section_add_keyword, &
36 : section_add_subsection, &
37 : section_create, &
38 : section_release, &
39 : section_type, &
40 : section_vals_get_subs_vals, &
41 : section_vals_type, &
42 : section_vals_val_get
43 : USE kinds, ONLY: default_path_length, &
44 : default_string_length, &
45 : dp
46 : USE machine, ONLY: m_mov
47 : USE memory_utilities, ONLY: reallocate
48 : USE message_passing, ONLY: mp_file_delete, &
49 : mp_file_get_amode, &
50 : mp_file_type
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'
71 : PUBLIC :: cp_openpmd_print_key_unit_nr, cp_openpmd_print_key_finished_output
72 : PUBLIC :: cp_openpmd_get_value_unit_nr, cp_openpmd_per_call_value_type
73 : PUBLIC :: cp_openpmd_output_finalize
74 : PUBLIC :: cp_openpmd_get_default_extension
75 : PUBLIC :: cp_openpmd_close_iterations
76 :
77 : #ifdef __OPENPMD
78 : TYPE :: cp_openpmd_per_call_value_type
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
86 : END TYPE cp_openpmd_per_call_value_type
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 :
180 : TYPE :: cp_openpmd_per_call_value_type
181 : ! nothing there
182 : END TYPE cp_openpmd_per_call_value_type
183 :
184 : #endif
185 :
186 : CONTAINS
187 :
188 : #ifdef __OPENPMD
189 : ! Helper functions for interacting with the two maps declared above.
190 : #:set name_suffixes = ['unit_nr', 'filedata']
191 : #:set key_types = ['INTEGER', 'CHARACTER(len=default_string_length)']
192 : #:set value_types = ['cp_openpmd_per_call_value_type', 'cp_openpmd_per_callsite_value_type']
193 : #:set map_storages = ['cp_openpmd_per_call', 'cp_openpmd_per_callsite']
194 : #:set map_counters = ['cp_num_openpmd_per_call', 'cp_num_openpmd_per_callsite']
195 : #:set map_capacities = ['cp_capacity_openpmd_per_call', 'cp_capacity_openpmd_per_callsite']
196 :
197 : #:for name_suffix, key_type, value_type, map_storage, map_counter, map_capacity in zip(name_suffixes, key_types, value_types, map_storages, map_counters, map_capacities)
198 :
199 : ! TODO No reallocation support for now, change cp_allocation_size if larger sizes needed.
200 :
201 : ! **************************************************************************************************
202 : !> \brief ...
203 : !> \param key ...
204 : !> \param value ...
205 : ! **************************************************************************************************
206 : FUNCTION cp_openpmd_add_${name_suffix}$ (key, value) RESULT(index)
207 : ${key_type}$, INTENT(in) :: key
208 : TYPE(${value_type}$), INTENT(in) :: value
209 : INTEGER :: index
210 :
211 : LOGICAL :: check_capacity
212 : INTEGER :: i
213 :
214 : ! Check if the key already exists
215 : DO i = 1, ${map_counter}$
216 : IF (${map_storage}$ (i)%key == key) THEN
217 : ${map_storage}$ (i)%value = value
218 : index = i
219 : RETURN
220 : END IF
221 : END DO
222 :
223 : IF (${map_capacity}$ == 0) THEN
224 : ALLOCATE (${map_storage}$ (cp_allocation_size))
225 : ${map_capacity}$ = cp_allocation_size
226 : END IF
227 :
228 : ! No idea how to do reallocations, so for now just assert that they're not needed
229 : check_capacity = ${map_counter}$ < ${map_capacity}$
230 : CPASSERT(check_capacity)
231 :
232 : ! Add a new entry
233 : ${map_counter}$ = ${map_counter}$+1
234 : ${map_storage}$ (${map_counter}$)%key = key
235 : ${map_storage}$ (${map_counter}$)%value = value
236 : index = ${map_counter}$
237 : END FUNCTION cp_openpmd_add_${name_suffix}$
238 :
239 : ! **************************************************************************************************
240 : !> \brief ...
241 : !> \param key ...
242 : !> \return ...
243 : ! **************************************************************************************************
244 : FUNCTION cp_openpmd_get_index_${name_suffix}$ (key) RESULT(index)
245 : ${key_type}$, INTENT(in) :: key
246 : INTEGER :: index
247 :
248 : INTEGER :: i
249 :
250 : index = -1
251 :
252 : DO i = 1, ${map_counter}$
253 : IF (${map_storage}$ (i)%key == key) THEN
254 : index = i
255 : RETURN
256 : END IF
257 : END DO
258 : END FUNCTION cp_openpmd_get_index_${name_suffix}$
259 :
260 : FUNCTION cp_openpmd_get_value_${name_suffix}$ (key) RESULT(value)
261 : ${key_type}$, INTENT(in) :: key
262 : TYPE(${value_type}$) :: value
263 :
264 : INTEGER :: i
265 :
266 : i = cp_openpmd_get_index_${name_suffix}$ (key)
267 : IF (i == -1) RETURN
268 :
269 : value = ${map_storage}$ (i)%value
270 : END FUNCTION cp_openpmd_get_value_${name_suffix}$
271 :
272 : ! **************************************************************************************************
273 : !> \brief ...
274 : !> \param key ...
275 : !> \return ...
276 : ! **************************************************************************************************
277 : FUNCTION cp_openpmd_remove_${name_suffix}$ (key) RESULT(was_found)
278 : ${key_type}$, INTENT(in) :: key
279 : LOGICAL :: was_found
280 :
281 : INTEGER :: i
282 :
283 : was_found = .FALSE.
284 :
285 : DO i = 1, ${map_counter}$
286 : IF (${map_storage}$ (i)%key == key) THEN
287 : was_found = .TRUE.
288 : IF (i /= ${map_counter}$) THEN
289 : ! Swap last element to now freed place
290 : ${map_storage}$ (i) = ${map_storage}$ (${map_counter}$)
291 : END IF
292 :
293 : ${map_counter}$ = ${map_counter}$-1
294 : IF (${map_counter}$ == 0) THEN
295 : DEALLOCATE (${map_storage}$)
296 : ${map_capacity}$ = 0
297 : END IF
298 : RETURN
299 : END IF
300 : END DO
301 : END FUNCTION cp_openpmd_remove_${name_suffix}$
302 :
303 : #:endfor
304 :
305 : ! **************************************************************************************************
306 : !> \brief Simplified version of cp_print_key_generate_filename. Since an openPMD Series encompasses
307 : ! multiple datasets that would be separate outputs in e.g. .cube files, this needs not
308 : ! consider dataset names for creation of a filename.
309 : !> \param logger ...
310 : !> \param print_key ...
311 : !> \param openpmd_basename ...
312 : !> \param extension ...
313 : !> \param my_local ...
314 : !> \return ...
315 : ! **************************************************************************************************
316 : FUNCTION cp_print_key_generate_openpmd_filename(logger, print_key, openpmd_basename, extension) RESULT(filename)
317 : TYPE(cp_logger_type), POINTER :: logger
318 : TYPE(section_vals_type), POINTER :: print_key
319 : CHARACTER(len=*), INTENT(IN) :: openpmd_basename, extension
320 : CHARACTER(len=default_path_length) :: filename
321 :
322 : CHARACTER(len=default_path_length) :: outName, outPath, root
323 : INTEGER :: my_ind1, my_ind2
324 : LOGICAL :: has_root
325 :
326 : CALL section_vals_val_get(print_key, "FILENAME", c_val=outPath)
327 : IF (outPath(1:1) == '=') THEN
328 : CPASSERT(LEN(outPath) - 1 <= LEN(filename))
329 : filename = outPath(2:)
330 : RETURN
331 : END IF
332 : IF (outPath == "__STD_OUT__") outPath = ""
333 : outName = TRIM(outPath)
334 : has_root = .FALSE.
335 : my_ind1 = INDEX(outPath, "/")
336 : my_ind2 = LEN_TRIM(outPath)
337 : IF (my_ind1 /= 0) THEN
338 : has_root = .TRUE.
339 : DO WHILE (INDEX(outPath(my_ind1 + 1:my_ind2), "/") /= 0)
340 : my_ind1 = INDEX(outPath(my_ind1 + 1:my_ind2), "/") + my_ind1
341 : END DO
342 : IF (my_ind1 == my_ind2) THEN
343 : outName = ""
344 : ELSE
345 : outName = outPath(my_ind1 + 1:my_ind2)
346 : END IF
347 : END IF
348 :
349 : IF (.NOT. has_root) THEN
350 : root = TRIM(logger%iter_info%project_name)
351 : ELSE IF (outName == "") THEN
352 : root = outPath(1:my_ind1)//TRIM(logger%iter_info%project_name)
353 : ELSE
354 : root = outPath(1:my_ind1)
355 : END IF
356 :
357 : filename = ADJUSTL(TRIM(root)//"_"//TRIM(openpmd_basename)//TRIM(extension))
358 :
359 : END FUNCTION cp_print_key_generate_openpmd_filename
360 :
361 : ! **************************************************************************************************
362 : !> \brief CP2K Iteration numbers are n-dimensional while openPMD Iteration numbers are scalars.
363 : ! This checks if the Iteration number has changed from the previous call (stored in
364 : ! openpmd_file%iteration_counter) and updates it if needed.
365 : !> \param logger ...
366 : !> \return ...
367 : ! **************************************************************************************************
368 : FUNCTION cp_advance_iteration_number(logger, openpmd_file) RESULT(did_advance_iteration)
369 : TYPE(cp_logger_type), POINTER :: logger
370 : TYPE(cp_openpmd_per_callsite_value_type) :: openpmd_file
371 : LOGICAL :: did_advance_iteration
372 :
373 : INTEGER :: len
374 :
375 : #:set ic = 'openpmd_file%iteration_counter'
376 :
377 : did_advance_iteration = .FALSE.
378 : len = SIZE(logger%iter_info%iteration)
379 : IF (len /= ${ic}$%complex_iteration_depth) THEN
380 : did_advance_iteration = .TRUE.
381 : ${ic}$%complex_iteration_depth = len
382 : ALLOCATE (${ic}$%complex_iteration(len))
383 : ELSE
384 : did_advance_iteration &
385 : = ANY(${ic}$%complex_iteration(1:len) &
386 : /= logger%iter_info%iteration(1:len))
387 : END IF
388 :
389 : IF (.NOT. did_advance_iteration) RETURN
390 :
391 : ${ic}$%flat_iteration = ${ic}$%flat_iteration + 1
392 : ${ic}$%complex_iteration(1:len) &
393 : = logger%iter_info%iteration(1:len)
394 :
395 : END FUNCTION cp_advance_iteration_number
396 :
397 : ! **************************************************************************************************
398 : !> \brief CP2K deals with output handles in terms of unit numbers.
399 : ! The openPMD output logic does not change this association.
400 : ! For this, we need to emulate unit numbers as (1) they are not native to openPMD and
401 : ! (2) a single openPMD Series might contain multiple datasets treated logically by CP2K
402 : ! as distinct outputs. As a result, a single unit number is resolved by the openPMD logic
403 : ! to the values represented by the cp_openpmd_per_call_value_type struct,
404 : ! containing the output Series and the referred datasets therein (Iteration number,
405 : ! name prefix for meshes and particles, referred output Series ...).
406 : !> \param series ...
407 : !> \param middle_name ...
408 : !> \param logger ...
409 : !> \param sim_time Current simulation time in atomic units (used for timeOffset)
410 : !> \return ...
411 : ! **************************************************************************************************
412 : FUNCTION cp_openpmd_create_unit_nr_entry( &
413 : openpmd_file_index, &
414 : middle_name, &
415 : logger, &
416 : openpmd_unit_dimension, &
417 : openpmd_unit_si, &
418 : sim_time) RESULT(res)
419 : INTEGER :: openpmd_file_index
420 : CHARACTER(len=*), INTENT(IN) :: middle_name
421 : TYPE(cp_logger_type), POINTER :: logger
422 : REAL(kind=dp), DIMENSION(7), OPTIONAL, INTENT(IN) :: openpmd_unit_dimension
423 : REAL(kind=dp), OPTIONAL, INTENT(IN) :: openpmd_unit_si
424 : REAL(kind=dp), OPTIONAL, INTENT(IN) :: sim_time
425 : TYPE(cp_openpmd_per_call_value_type) :: res
426 :
427 : LOGICAL :: opened_new_iteration
428 : TYPE(openpmd_attributable_type) :: attr
429 : TYPE(cp_openpmd_per_callsite_value_type), POINTER :: opmd
430 :
431 : opened_new_iteration = .FALSE.
432 :
433 : opmd => cp_openpmd_per_callsite(openpmd_file_index)%value
434 :
435 : res%series = opmd%output_series
436 :
437 : opened_new_iteration = cp_advance_iteration_number(logger, opmd)
438 :
439 : res%iteration = opmd%output_series%write_iteration(opmd%iteration_counter%flat_iteration)
440 : CALL res%iteration%set_time_unit_SI(seconds)
441 : res%name_prefix = TRIM(middle_name)
442 :
443 : IF (opened_new_iteration) THEN
444 :
445 : IF (PRESENT(sim_time)) THEN
446 : CALL res%iteration%set_time(sim_time)
447 : END IF
448 :
449 : IF (PRESENT(openpmd_unit_dimension)) THEN
450 : res%unit_dimension = openpmd_unit_dimension
451 : END IF
452 :
453 : IF (PRESENT(openpmd_unit_si)) THEN
454 : res%unit_si = openpmd_unit_si
455 : END IF
456 :
457 : attr = res%iteration%as_attributable()
458 : CALL attr%set_attribute_vec_int( &
459 : "ndim_iteration_index", &
460 : opmd%iteration_counter%complex_iteration)
461 : END IF
462 : END FUNCTION cp_openpmd_create_unit_nr_entry
463 :
464 : ! **************************************************************************************************
465 : !> \brief Check if there is already an output Series created for the callsite identified
466 : ! by openpmd_basename. If so, then return it (by index), otherwise open the Series now
467 : ! and return the index then.
468 : FUNCTION cp_openpmd_get_openpmd_file_entry(openpmd_basename, filename, openpmd_config, logger, use_mpi) RESULT(file_index)
469 : CHARACTER(len=*), INTENT(IN) :: openpmd_basename, filename, openpmd_config
470 : TYPE(cp_logger_type), POINTER :: logger
471 : LOGICAL :: use_mpi
472 : INTEGER :: file_index
473 : CHARACTER(:), ALLOCATABLE :: merged_config
474 :
475 : CHARACTER(len=default_string_length) :: basename_copied
476 : TYPE(cp_openpmd_per_callsite_value_type) :: emplace_new
477 :
478 : INTEGER :: handle
479 : TYPE(cp_openpmd_per_callsite_value_type) :: series_data
480 : TYPE(openpmd_iteration_type) :: iteration
481 : INTEGER :: i
482 :
483 : basename_copied = ' '
484 : basename_copied(1:LEN_TRIM(openpmd_basename)) = TRIM(openpmd_basename)
485 :
486 : file_index = cp_openpmd_get_index_filedata(basename_copied)
487 :
488 : CALL timeset('openpmd_close_iterations', handle)
489 : DO i = 1, cp_num_openpmd_per_callsite
490 : IF (i /= file_index) THEN
491 : series_data = cp_openpmd_per_callsite(i)%value
492 : iteration = series_data%output_series%get_iteration( &
493 : series_data%iteration_counter%flat_iteration)
494 : IF (.NOT. iteration%closed()) THEN
495 : CALL iteration%close()
496 : END IF
497 : END IF
498 : END DO
499 : CALL timestop(handle)
500 :
501 : IF (file_index /= -1) RETURN
502 :
503 : #ifndef _WIN32
504 : merged_config = openpmd_json_merge(cp_default_backend_config, cp_default_backend_config_non_windows)
505 : #else
506 : merged_config = cp_default_backend_config
507 : #endif
508 : IF (use_mpi) THEN
509 : merged_config = openpmd_json_merge(merged_config, openpmd_config, logger%para_env)
510 : emplace_new%output_series = openpmd_series_create( &
511 : filename, openpmd_access_create, logger%para_env, merged_config)
512 : ELSE
513 : merged_config = openpmd_json_merge(merged_config, openpmd_config)
514 : emplace_new%output_series = openpmd_series_create( &
515 : filename, openpmd_access_create, config=merged_config)
516 : END IF
517 :
518 : CALL emplace_new%output_series%set_software("CP2K", cp2k_version)
519 :
520 : DEALLOCATE (merged_config)
521 : file_index = cp_openpmd_add_filedata(basename_copied, emplace_new)
522 : END FUNCTION cp_openpmd_get_openpmd_file_entry
523 :
524 : #else ! defined(__OPENPMD)
525 :
526 : FUNCTION cp_openpmd_get_value_unit_nr(key) RESULT(value)
527 : INTEGER, INTENT(in) :: key
528 : TYPE(cp_openpmd_per_call_value_type) :: value
529 :
530 : MARK_USED(key)
531 : MARK_USED(value)
532 : CPABORT("CP2K compiled without the openPMD-api")
533 :
534 : END FUNCTION cp_openpmd_get_value_unit_nr
535 :
536 : #endif
537 :
538 : ! **************************************************************************************************
539 : !> \brief Close all outputs.
540 : ! **************************************************************************************************
541 : SUBROUTINE cp_openpmd_output_finalize()
542 : #ifdef __OPENPMD
543 : INTEGER :: i
544 : DO i = 1, cp_num_openpmd_per_callsite
545 : DEALLOCATE (cp_openpmd_per_callsite(i)%value%iteration_counter%complex_iteration)
546 : CALL cp_openpmd_per_callsite(i)%value%output_series%close()
547 : END DO
548 : IF (ALLOCATED(cp_openpmd_per_callsite)) THEN
549 : DEALLOCATE (cp_openpmd_per_callsite)
550 : END IF
551 : cp_num_openpmd_per_callsite = 0
552 : #endif
553 : END SUBROUTINE cp_openpmd_output_finalize
554 :
555 : ! **************************************************************************************************
556 : !> \brief ...
557 : !> \param logger ...
558 : !> \param basis_section ...
559 : !> \param print_key_path ...
560 : !> \param extension ...
561 : !> \param middle_name ...
562 : !> \param local ...
563 : !> \param log_filename ...
564 : !> \param ignore_should_output ...
565 : !> \param do_backup ...
566 : !> \param is_new_file true if this rank created a new (or rewound) file, false otherwise
567 : !> \param mpi_io True if the file should be opened in parallel on all processors belonging to
568 : !> the communicator group. Automatically disabled if the file form or access mode
569 : !> is unsuitable for MPI IO. Return value indicates whether MPI was actually used
570 : !> and therefore the flag must also be passed to the file closing directive.
571 : !> \param fout Name of the actual file where the output will be written. Needed mainly for MPI IO
572 : !> because inquiring the filename from the MPI filehandle does not work across
573 : !> all MPI libraries.
574 : !> \param openpmd_basename Used to associate an identifier to each callsite of this module
575 : !> \param use_openpmd ...
576 : !> \param sim_time Current simulation time in atomic units (used for timeOffset)
577 : !> \return ...
578 : ! **************************************************************************************************
579 : FUNCTION cp_openpmd_print_key_unit_nr( &
580 : logger, &
581 : basis_section, &
582 : print_key_path, &
583 : middle_name, &
584 : ignore_should_output, &
585 : mpi_io, &
586 : fout, &
587 : openpmd_basename, &
588 : openpmd_unit_dimension, &
589 : openpmd_unit_si, &
590 : sim_time) RESULT(res)
591 :
592 : TYPE(cp_logger_type), POINTER :: logger
593 : TYPE(section_vals_type), INTENT(IN) :: basis_section
594 : CHARACTER(len=*), INTENT(IN), OPTIONAL :: print_key_path
595 : CHARACTER(len=*), INTENT(IN), OPTIONAL :: middle_name
596 : LOGICAL, INTENT(IN), OPTIONAL :: ignore_should_output
597 : LOGICAL, INTENT(INOUT), OPTIONAL :: mpi_io
598 : CHARACTER(len=default_path_length), INTENT(OUT), &
599 : OPTIONAL :: fout
600 : CHARACTER(len=*), INTENT(IN), OPTIONAL :: openpmd_basename
601 : REAL(kind=dp), DIMENSION(7), OPTIONAL, INTENT(IN) :: openpmd_unit_dimension
602 : REAL(kind=dp), OPTIONAL, INTENT(IN) :: openpmd_unit_si
603 : REAL(kind=dp), OPTIONAL, INTENT(IN) :: sim_time
604 : INTEGER :: res
605 :
606 : #ifdef __OPENPMD
607 :
608 : CHARACTER(len=default_path_length) :: filename
609 :
610 : CHARACTER(len=default_string_length) :: openpmd_config, outPath, file_extension
611 : LOGICAL :: found, &
612 : my_mpi_io, &
613 : my_should_output, &
614 : replace
615 : INTEGER :: openpmd_file_index, openpmd_call_index
616 : TYPE(section_vals_type), POINTER :: print_key
617 :
618 : my_mpi_io = .FALSE.
619 : replace = .FALSE.
620 : found = .FALSE.
621 : res = -1
622 : IF (PRESENT(mpi_io)) THEN
623 : #if defined(__parallel)
624 : IF (logger%para_env%num_pe > 1 .AND. mpi_io) THEN
625 : my_mpi_io = .TRUE.
626 : ELSE
627 : my_mpi_io = .FALSE.
628 : END IF
629 : #else
630 : my_mpi_io = .FALSE.
631 : #endif
632 0 : ! Set return value
633 : mpi_io = my_mpi_io
634 : END IF
635 : NULLIFY (print_key)
636 : CPASSERT(ASSOCIATED(logger))
637 : CPASSERT(basis_section%ref_count > 0)
638 0 : CPASSERT(logger%ref_count > 0)
639 : my_should_output = BTEST(cp_print_key_should_output(logger%iter_info, &
640 0 : basis_section, print_key_path, used_print_key=print_key), cp_p_file)
641 : IF (PRESENT(ignore_should_output)) my_should_output = my_should_output .OR. ignore_should_output
642 : IF (.NOT. my_should_output) RETURN
643 : IF (logger%para_env%is_source() .OR. my_mpi_io) THEN
644 :
645 : CALL section_vals_val_get(print_key, "FILENAME", c_val=outPath)
646 : CALL section_vals_val_get(print_key, "OPENPMD_EXTENSION", c_val=file_extension)
647 9822 : CALL section_vals_val_get(print_key, "OPENPMD_CFG_FILE", c_val=openpmd_config)
648 : IF (LEN_TRIM(openpmd_config) == 0) THEN
649 : CALL section_vals_val_get(print_key, "OPENPMD_CFG", c_val=openpmd_config)
650 : ELSE
651 : openpmd_config = "@"//TRIM(openpmd_config)
652 : END IF
653 : filename = cp_print_key_generate_openpmd_filename(logger, print_key, openpmd_basename, file_extension)
654 :
655 : IF (PRESENT(fout)) THEN
656 : fout = filename
657 : END IF
658 :
659 9822 : openpmd_file_index = cp_openpmd_get_openpmd_file_entry( &
660 : openpmd_basename, filename, openpmd_config, logger, my_mpi_io)
661 :
662 : OPEN (newunit=res, status='scratch', action='write')
663 : BLOCK
664 : TYPE(cp_openpmd_per_call_value_type) :: output_info
665 :
666 : output_info = cp_openpmd_create_unit_nr_entry( &
667 : openpmd_file_index, &
668 : middle_name, &
669 : logger, &
670 : openpmd_unit_dimension, &
671 : openpmd_unit_si, &
672 : sim_time)
673 : openpmd_call_index = cp_openpmd_add_unit_nr( &
674 : res, output_info)
675 : END BLOCK
676 :
677 : ELSE
678 : res = -1
679 : END IF
680 : #else
681 : MARK_USED(logger)
682 : MARK_USED(basis_section)
683 : MARK_USED(print_key_path)
684 : MARK_USED(middle_name)
685 0 : MARK_USED(ignore_should_output)
686 : MARK_USED(mpi_io)
687 : MARK_USED(fout)
688 : MARK_USED(openpmd_basename)
689 : MARK_USED(openpmd_basename)
690 : MARK_USED(openpmd_unit_dimension)
691 : MARK_USED(openpmd_unit_si)
692 : MARK_USED(sim_time)
693 : res = 0
694 : CPABORT("CP2K compiled without the openPMD-api")
695 : #endif
696 : END FUNCTION cp_openpmd_print_key_unit_nr
697 :
698 : ! **************************************************************************************************
699 : !> \brief should be called after you finish working with a unit obtained with
700 : !> cp_openpmd_print_key_unit_nr, so that the file that might have been opened
701 : !> can be closed.
702 : !>
703 : !> the inputs should be exactly the same of the corresponding
704 : !> cp_openpmd_print_key_unit_nr
705 : !> \param unit_nr ...
706 : !> \param logger ...
707 : !> \param basis_section ...
708 : !> \param print_key_path ...
709 : !> \param local ...
710 : !> \param ignore_should_output ...
711 : !> \param mpi_io True if file was opened in parallel with MPI
712 : !> \param use_openpmd ...
713 : !> \note
714 : !> closes if the corresponding filename of the printkey is
715 : !> not __STD_OUT__
716 : ! **************************************************************************************************
717 : SUBROUTINE cp_openpmd_print_key_finished_output(unit_nr, logger, basis_section, &
718 : print_key_path, local, ignore_should_output, &
719 : mpi_io)
720 : INTEGER, INTENT(INOUT) :: unit_nr
721 : TYPE(cp_logger_type), POINTER :: logger
722 : TYPE(section_vals_type), INTENT(IN) :: basis_section
723 : CHARACTER(len=*), INTENT(IN), OPTIONAL :: print_key_path
724 : LOGICAL, INTENT(IN), OPTIONAL :: local, ignore_should_output, &
725 : mpi_io
726 :
727 : #ifdef __OPENPMD
728 :
729 : CHARACTER(len=default_string_length) :: outPath
730 : LOGICAL :: my_local, my_mpi_io, &
731 : my_should_output
732 : TYPE(section_vals_type), POINTER :: print_key
733 :
734 : my_local = .FALSE.
735 : my_mpi_io = .FALSE.
736 : NULLIFY (print_key)
737 : IF (PRESENT(local)) my_local = local
738 : IF (PRESENT(mpi_io)) my_mpi_io = mpi_io
739 : CPASSERT(ASSOCIATED(logger))
740 : CPASSERT(basis_section%ref_count > 0)
741 : CPASSERT(logger%ref_count > 0)
742 : my_should_output = BTEST(cp_print_key_should_output(logger%iter_info, basis_section, &
743 : print_key_path, used_print_key=print_key), cp_p_file)
744 : IF (PRESENT(ignore_should_output)) my_should_output = my_should_output .OR. ignore_should_output
745 : IF (my_should_output .AND. (my_local .OR. &
746 : logger%para_env%is_source() .OR. &
747 : my_mpi_io)) THEN
748 : CALL section_vals_val_get(print_key, "FILENAME", c_val=outPath)
749 : IF (cp_openpmd_remove_unit_nr(unit_nr)) THEN
750 : CLOSE (unit_nr)
751 : END IF
752 :
753 : unit_nr = -1
754 : END IF
755 : CPASSERT(unit_nr == -1)
756 : unit_nr = -1
757 : #else
758 : MARK_USED(unit_nr)
759 : MARK_USED(logger)
760 : MARK_USED(basis_section)
761 : MARK_USED(print_key_path)
762 : MARK_USED(local)
763 : MARK_USED(ignore_should_output)
764 : MARK_USED(mpi_io)
765 : CPABORT("CP2K compiled without the openPMD-api")
766 : #endif
767 : END SUBROUTINE cp_openpmd_print_key_finished_output
768 :
769 : SUBROUTINE cp_openpmd_close_iterations()
770 : #ifdef __OPENPMD
771 : INTEGER :: handle
772 : TYPE(cp_openpmd_per_callsite_value_type) :: series_data
773 : TYPE(openpmd_iteration_type) :: iteration
774 : INTEGER :: i
775 :
776 : CALL timeset('openpmd_close_iterations', handle)
777 : DO i = 1, cp_num_openpmd_per_callsite
778 : series_data = cp_openpmd_per_callsite(i)%value
779 : iteration = series_data%output_series%get_iteration( &
780 : series_data%iteration_counter%flat_iteration)
781 : IF (.NOT. iteration%closed()) THEN
782 : CALL iteration%close()
783 : END IF
784 : END DO
785 : CALL timestop(handle)
786 : #endif
787 : END SUBROUTINE cp_openpmd_close_iterations
788 :
789 : FUNCTION cp_openpmd_get_default_extension() RESULT(extension)
790 : CHARACTER(len=default_string_length) :: extension
791 :
792 : #ifdef __OPENPMD
793 : extension = openpmd_get_default_extension()
794 : #else
795 : extension = ".bp5"
796 : #endif
797 :
798 : END FUNCTION cp_openpmd_get_default_extension
799 0 :
800 0 : END MODULE cp_output_handling_openpmd
|