LCOV - code coverage report
Current view: top level - src/input - cp_output_handling_openpmd.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:8ebf9ad) Lines: 38.9 % 18 7
Test Date: 2026-01-22 06:43:13 Functions: 42.9 % 7 3

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

Generated by: LCOV version 2.0-1