LCOV - code coverage report
Current view: top level - src/input - cp_output_handling_openpmd.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:1155b05) Lines: 38.9 % 18 7
Test Date: 2026-03-21 06:31:29 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)                 :: outName, outPath, root
     318              :       INTEGER                                            :: my_ind1, my_ind2
     319              :       LOGICAL                                            :: has_root
     320              : 
     321              :       CALL section_vals_val_get(print_key, "FILENAME", c_val=outPath)
     322              :       IF (outPath(1:1) == '=') THEN
     323              :          CPASSERT(LEN(outPath) - 1 <= LEN(filename))
     324              :          filename = outPath(2:)
     325              :          RETURN
     326              :       END IF
     327              :       IF (outPath == "__STD_OUT__") outPath = ""
     328              :       outName = TRIM(outPath)
     329              :       has_root = .FALSE.
     330              :       my_ind1 = INDEX(outPath, "/")
     331              :       my_ind2 = LEN_TRIM(outPath)
     332              :       IF (my_ind1 /= 0) THEN
     333              :          has_root = .TRUE.
     334              :          DO WHILE (INDEX(outPath(my_ind1 + 1:my_ind2), "/") /= 0)
     335              :             my_ind1 = INDEX(outPath(my_ind1 + 1:my_ind2), "/") + my_ind1
     336              :          END DO
     337              :          IF (my_ind1 == my_ind2) THEN
     338              :             outName = ""
     339              :          ELSE
     340              :             outName = outPath(my_ind1 + 1:my_ind2)
     341              :          END IF
     342              :       END IF
     343              : 
     344              :       IF (.NOT. has_root) THEN
     345              :          root = TRIM(logger%iter_info%project_name)
     346              :       ELSE IF (outName == "") THEN
     347              :          root = outPath(1:my_ind1)//TRIM(logger%iter_info%project_name)
     348              :       ELSE
     349              :          root = outPath(1:my_ind1)
     350              :       END IF
     351              : 
     352              :       filename = ADJUSTL(TRIM(root)//"_"//TRIM(openpmd_basename)//TRIM(extension))
     353              : 
     354              :    END FUNCTION cp_print_key_generate_openpmd_filename
     355              : 
     356              : ! **************************************************************************************************
     357              : !> \brief CP2K Iteration numbers are n-dimensional while openPMD Iteration numbers are scalars.
     358              : !         This checks if the Iteration number has changed from the previous call (stored in
     359              : !         openpmd_file%iteration_counter) and updates it if needed.
     360              : !> \param logger ...
     361              : !> \return ...
     362              : ! **************************************************************************************************
     363              :    FUNCTION cp_advance_iteration_number(logger, openpmd_file) RESULT(did_advance_iteration)
     364              :       TYPE(cp_logger_type), POINTER                      :: logger
     365              :       TYPE(cp_openpmd_per_callsite_value_type)           :: openpmd_file
     366              :       LOGICAL                                            :: did_advance_iteration
     367              : 
     368              :       INTEGER                                            :: len
     369              : 
     370              :       #:set ic = 'openpmd_file%iteration_counter'
     371              : 
     372              :       did_advance_iteration = .FALSE.
     373              :       len = SIZE(logger%iter_info%iteration)
     374              :       IF (len /= ${ic}$%complex_iteration_depth) THEN
     375              :          did_advance_iteration = .TRUE.
     376              :          ${ic}$%complex_iteration_depth = len
     377              :          ALLOCATE (${ic}$%complex_iteration(len))
     378              :       ELSE
     379              :          did_advance_iteration &
     380              :             = ANY(${ic}$%complex_iteration(1:len) &
     381              :                   /= logger%iter_info%iteration(1:len))
     382              :       END IF
     383              : 
     384              :       IF (.NOT. did_advance_iteration) RETURN
     385              : 
     386              :       ${ic}$%flat_iteration = ${ic}$%flat_iteration + 1
     387              :       ${ic}$%complex_iteration(1:len) &
     388              :          = logger%iter_info%iteration(1:len)
     389              : 
     390              :    END FUNCTION cp_advance_iteration_number
     391              : 
     392              : ! **************************************************************************************************
     393              : !> \brief CP2K deals with output handles in terms of unit numbers.
     394              : !         The openPMD output logic does not change this association.
     395              : !         For this, we need to emulate unit numbers as (1) they are not native to openPMD and
     396              : !         (2) a single openPMD Series might contain multiple datasets treated logically by CP2K
     397              : !         as distinct outputs. As a result, a single unit number is resolved by the openPMD logic
     398              : !         to the values represented by the cp_openpmd_per_call_value_type struct,
     399              : !         containing the output Series and the referred datasets therein (Iteration number,
     400              : !         name prefix for meshes and particles, referred output Series ...).
     401              : !> \param series ...
     402              : !> \param middle_name ...
     403              : !> \param logger ...
     404              : !> \return ...
     405              : ! **************************************************************************************************
     406              :    FUNCTION cp_openpmd_create_unit_nr_entry(openpmd_file_index, middle_name, logger) RESULT(res)
     407              :       INTEGER                                            :: openpmd_file_index
     408              :       CHARACTER(len=*), INTENT(IN)                       :: middle_name
     409              :       TYPE(cp_logger_type), POINTER                      :: logger
     410              :       TYPE(cp_openpmd_per_call_value_type)               :: res
     411              : 
     412              :       LOGICAL, SAVE                                      :: opened_new_iteration = .FALSE.
     413              :       TYPE(openpmd_attributable_type)                    :: attr
     414              :       TYPE(cp_openpmd_per_callsite_value_type), POINTER  :: opmd
     415              : 
     416              :       opmd => cp_openpmd_per_callsite(openpmd_file_index)%value
     417              : 
     418              :       res%series = opmd%output_series
     419              : 
     420              :       opened_new_iteration = cp_advance_iteration_number(logger, opmd)
     421              : 
     422              :       res%iteration = opmd%output_series%write_iteration(opmd%iteration_counter%flat_iteration)
     423              :       res%name_prefix = TRIM(middle_name)
     424              : 
     425              :       IF (opened_new_iteration) THEN
     426              :          attr = res%iteration%as_attributable()
     427              :          CALL attr%set_attribute_vec_int( &
     428              :             "ndim_iteration_index", &
     429              :             opmd%iteration_counter%complex_iteration)
     430              :       END IF
     431              :    END FUNCTION cp_openpmd_create_unit_nr_entry
     432              : 
     433              : ! **************************************************************************************************
     434              : !> \brief Check if there is already an output Series created for the callsite identified
     435              : !         by openpmd_basename. If so, then return it (by index), otherwise open the Series now
     436              : !         and return the index then.
     437              :    FUNCTION cp_openpmd_get_openpmd_file_entry(openpmd_basename, filename, openpmd_config, logger, use_mpi) RESULT(file_index)
     438              :       CHARACTER(len=*), INTENT(IN)             :: openpmd_basename, filename, openpmd_config
     439              :       TYPE(cp_logger_type), POINTER            :: logger
     440              :       LOGICAL                                  :: use_mpi
     441              :       INTEGER                                  :: file_index
     442              :       CHARACTER(:), ALLOCATABLE                :: merged_config
     443              : 
     444              :       CHARACTER(len=default_string_length), SAVE :: basename_copied = ' '
     445              :       TYPE(cp_openpmd_per_callsite_value_type) :: emplace_new
     446              : 
     447              :       INTEGER                                  :: handle
     448              :       TYPE(cp_openpmd_per_callsite_value_type) :: series_data
     449              :       TYPE(openpmd_iteration_type)             :: iteration
     450              :       INTEGER                                  :: i
     451              : 
     452              :       basename_copied(1:LEN_TRIM(openpmd_basename)) = TRIM(openpmd_basename)
     453              : 
     454              :       file_index = cp_openpmd_get_index_filedata(basename_copied)
     455              : 
     456              :       CALL timeset('openpmd_close_iterations', handle)
     457              :       DO i = 1, cp_num_openpmd_per_callsite
     458              :          IF (i /= file_index) THEN
     459              :             series_data = cp_openpmd_per_callsite(i)%value
     460              :             iteration = series_data%output_series%get_iteration( &
     461              :                         series_data%iteration_counter%flat_iteration)
     462              :             IF (.NOT. iteration%closed()) THEN
     463              :                CALL iteration%close()
     464              :             END IF
     465              :          END IF
     466              :       END DO
     467              :       CALL timestop(handle)
     468              : 
     469              :       IF (file_index /= -1) RETURN
     470              : 
     471              : #ifndef _WIN32
     472              :       merged_config = openpmd_json_merge(cp_default_backend_config, cp_default_backend_config_non_windows)
     473              : #else
     474              :       merged_config = cp_default_backend_config
     475              : #endif
     476              :       IF (use_mpi) THEN
     477              :          merged_config = openpmd_json_merge(merged_config, openpmd_config, logger%para_env)
     478              :          emplace_new%output_series = openpmd_series_create( &
     479              :                                      filename, openpmd_access_create, logger%para_env, merged_config)
     480              :       ELSE
     481              :          merged_config = openpmd_json_merge(merged_config, openpmd_config)
     482              :          emplace_new%output_series = openpmd_series_create( &
     483              :                                      filename, openpmd_access_create, config=merged_config)
     484              :       END IF
     485              :       DEALLOCATE (merged_config)
     486              :       file_index = cp_openpmd_add_filedata(basename_copied, emplace_new)
     487              :    END FUNCTION cp_openpmd_get_openpmd_file_entry
     488              : 
     489              : #else ! defined(__OPENPMD)
     490              : 
     491              :    FUNCTION cp_openpmd_get_value_unit_nr(key) RESULT(value)
     492              :       INTEGER, INTENT(in)                           :: key
     493              :       TYPE(cp_openpmd_per_call_value_type)                               :: value
     494              : 
     495              :       MARK_USED(key)
     496              :       MARK_USED(value)
     497              :       CPABORT("CP2K compiled without the openPMD-api")
     498              : 
     499              :    END FUNCTION cp_openpmd_get_value_unit_nr
     500              : 
     501              : #endif
     502              : 
     503              : ! **************************************************************************************************
     504              : !> \brief Close all outputs.
     505              : ! **************************************************************************************************
     506              :    SUBROUTINE cp_openpmd_output_finalize()
     507              : #ifdef __OPENPMD
     508              :       INTEGER :: i
     509              :       DO i = 1, cp_num_openpmd_per_callsite
     510              :          DEALLOCATE (cp_openpmd_per_callsite(i)%value%iteration_counter%complex_iteration)
     511              :          CALL cp_openpmd_per_callsite(i)%value%output_series%close()
     512              :       END DO
     513              :       IF (ALLOCATED(cp_openpmd_per_callsite)) THEN
     514              :          DEALLOCATE (cp_openpmd_per_callsite)
     515              :       END IF
     516              :       cp_num_openpmd_per_callsite = 0
     517              : #endif
     518              :    END SUBROUTINE cp_openpmd_output_finalize
     519              : 
     520              : ! **************************************************************************************************
     521              : !> \brief ...
     522              : !> \param logger ...
     523              : !> \param basis_section ...
     524              : !> \param print_key_path ...
     525              : !> \param extension ...
     526              : !> \param middle_name ...
     527              : !> \param local ...
     528              : !> \param log_filename ...
     529              : !> \param ignore_should_output ...
     530              : !> \param do_backup ...
     531              : !> \param is_new_file true if this rank created a new (or rewound) file, false otherwise
     532              : !> \param mpi_io True if the file should be opened in parallel on all processors belonging to
     533              : !>               the communicator group. Automatically disabled if the file form or access mode
     534              : !>               is unsuitable for MPI IO. Return value indicates whether MPI was actually used
     535              : !>               and therefore the flag must also be passed to the file closing directive.
     536              : !> \param fout   Name of the actual file where the output will be written. Needed mainly for MPI IO
     537              : !>               because inquiring the filename from the MPI filehandle does not work across
     538              : !>               all MPI libraries.
     539              : !> \param openpmd_basename Used to associate an identifier to each callsite of this module
     540              : !> \param use_openpmd ...
     541              : !> \return ...
     542              : ! **************************************************************************************************
     543              :    FUNCTION cp_openpmd_print_key_unit_nr(logger, basis_section, print_key_path, &
     544              :                                          middle_name, ignore_should_output, &
     545              :                                          mpi_io, &
     546              :                                          fout, openpmd_basename) RESULT(res)
     547              :       TYPE(cp_logger_type), POINTER                      :: logger
     548              :       TYPE(section_vals_type), INTENT(IN)                :: basis_section
     549              :       CHARACTER(len=*), INTENT(IN), OPTIONAL             :: print_key_path
     550              :       CHARACTER(len=*), INTENT(IN), OPTIONAL             :: middle_name
     551              :       LOGICAL, INTENT(IN), OPTIONAL                      :: ignore_should_output
     552              :       LOGICAL, INTENT(INOUT), OPTIONAL                   :: mpi_io
     553              :       CHARACTER(len=default_path_length), INTENT(OUT), &
     554              :          OPTIONAL                                        :: fout
     555              :       CHARACTER(len=*), INTENT(IN), OPTIONAL             :: openpmd_basename
     556              :       INTEGER                                            :: res
     557              : 
     558              : #ifdef __OPENPMD
     559              : 
     560              :       CHARACTER(len=default_path_length)                 :: filename
     561              : 
     562              :       CHARACTER(len=default_string_length)               :: openpmd_config, outPath, file_extension
     563              :       LOGICAL                                            :: found, &
     564              :                                                             my_mpi_io, &
     565              :                                                             my_should_output, &
     566              :                                                             replace
     567              :       INTEGER                                            :: openpmd_file_index, openpmd_call_index
     568              :       TYPE(section_vals_type), POINTER                   :: print_key
     569              : 
     570              :       my_mpi_io = .FALSE.
     571              :       replace = .FALSE.
     572              :       found = .FALSE.
     573              :       res = -1
     574              :       IF (PRESENT(mpi_io)) THEN
     575              : #if defined(__parallel)
     576              :          IF (logger%para_env%num_pe > 1 .AND. mpi_io) THEN
     577              :             my_mpi_io = .TRUE.
     578              :          ELSE
     579              :             my_mpi_io = .FALSE.
     580              :          END IF
     581              : #else
     582              :          my_mpi_io = .FALSE.
     583              : #endif
     584              :          ! Set return value
     585              :          mpi_io = my_mpi_io
     586              :       END IF
     587              :       NULLIFY (print_key)
     588              :       CPASSERT(ASSOCIATED(logger))
     589              :       CPASSERT(basis_section%ref_count > 0)
     590              :       CPASSERT(logger%ref_count > 0)
     591              :       my_should_output = BTEST(cp_print_key_should_output(logger%iter_info, &
     592              :                                                           basis_section, print_key_path, used_print_key=print_key), cp_p_file)
     593              :       IF (PRESENT(ignore_should_output)) my_should_output = my_should_output .OR. ignore_should_output
     594              :       IF (.NOT. my_should_output) RETURN
     595              :       IF (logger%para_env%is_source() .OR. my_mpi_io) THEN
     596              : 
     597            0 :          CALL section_vals_val_get(print_key, "FILENAME", c_val=outPath)
     598              :          CALL section_vals_val_get(print_key, "OPENPMD_EXTENSION", c_val=file_extension)
     599              :          CALL section_vals_val_get(print_key, "OPENPMD_CFG_FILE", c_val=openpmd_config)
     600              :          IF (LEN_TRIM(openpmd_config) == 0) THEN
     601              :             CALL section_vals_val_get(print_key, "OPENPMD_CFG", c_val=openpmd_config)
     602              :          ELSE
     603            0 :             openpmd_config = "@"//TRIM(openpmd_config)
     604              :          END IF
     605            0 :          filename = cp_print_key_generate_openpmd_filename(logger, print_key, openpmd_basename, file_extension)
     606              : 
     607              :          IF (PRESENT(fout)) THEN
     608              :             fout = filename
     609              :          END IF
     610              : 
     611              :          openpmd_file_index = cp_openpmd_get_openpmd_file_entry( &
     612         9544 :                               openpmd_basename, filename, openpmd_config, logger, my_mpi_io)
     613              : 
     614              :          OPEN (newunit=res, status='scratch', action='write')
     615              :          openpmd_call_index = cp_openpmd_add_unit_nr( &
     616              :                               res, &
     617              :                               cp_openpmd_create_unit_nr_entry( &
     618              :                               openpmd_file_index, middle_name, logger))
     619              : 
     620              :       ELSE
     621              :          res = -1
     622              :       END IF
     623              : #else
     624         9544 :       MARK_USED(logger)
     625              :       MARK_USED(basis_section)
     626              :       MARK_USED(print_key_path)
     627              :       MARK_USED(middle_name)
     628              :       MARK_USED(ignore_should_output)
     629              :       MARK_USED(mpi_io)
     630              :       MARK_USED(fout)
     631              :       MARK_USED(openpmd_basename)
     632              :       res = 0
     633              :       CPABORT("CP2K compiled without the openPMD-api")
     634              : #endif
     635              :    END FUNCTION cp_openpmd_print_key_unit_nr
     636              : 
     637              : ! **************************************************************************************************
     638              : !> \brief should be called after you finish working with a unit obtained with
     639              : !>      cp_openpmd_print_key_unit_nr, so that the file that might have been opened
     640              : !>      can be closed.
     641              : !>
     642              : !>      the inputs should be exactly the same of the corresponding
     643              : !>      cp_openpmd_print_key_unit_nr
     644              : !> \param unit_nr ...
     645              : !> \param logger ...
     646              : !> \param basis_section ...
     647              : !> \param print_key_path ...
     648              : !> \param local ...
     649            0 : !> \param ignore_should_output ...
     650              : !> \param mpi_io True if file was opened in parallel with MPI
     651              : !> \param use_openpmd ...
     652              : !> \note
     653              : !>      closes if the corresponding filename of the printkey is
     654              : !>      not __STD_OUT__
     655              : ! **************************************************************************************************
     656              :    SUBROUTINE cp_openpmd_print_key_finished_output(unit_nr, logger, basis_section, &
     657              :                                                    print_key_path, local, ignore_should_output, &
     658              :                                                    mpi_io)
     659              :       INTEGER, INTENT(INOUT)                             :: unit_nr
     660              :       TYPE(cp_logger_type), POINTER                      :: logger
     661              :       TYPE(section_vals_type), INTENT(IN)                :: basis_section
     662              :       CHARACTER(len=*), INTENT(IN), OPTIONAL             :: print_key_path
     663              :       LOGICAL, INTENT(IN), OPTIONAL                      :: local, ignore_should_output, &
     664              :                                                             mpi_io
     665              : 
     666              : #ifdef __OPENPMD
     667              : 
     668              :       CHARACTER(len=default_string_length)               :: outPath
     669              :       LOGICAL                                            :: my_local, my_mpi_io, &
     670              :                                                             my_should_output
     671              :       TYPE(section_vals_type), POINTER                   :: print_key
     672              : 
     673              :       my_local = .FALSE.
     674              :       my_mpi_io = .FALSE.
     675              :       NULLIFY (print_key)
     676              :       IF (PRESENT(local)) my_local = local
     677              :       IF (PRESENT(mpi_io)) my_mpi_io = mpi_io
     678              :       CPASSERT(ASSOCIATED(logger))
     679              :       CPASSERT(basis_section%ref_count > 0)
     680              :       CPASSERT(logger%ref_count > 0)
     681              :       my_should_output = BTEST(cp_print_key_should_output(logger%iter_info, basis_section, &
     682              :                                                           print_key_path, used_print_key=print_key), cp_p_file)
     683              :       IF (PRESENT(ignore_should_output)) my_should_output = my_should_output .OR. ignore_should_output
     684              :       IF (my_should_output .AND. (my_local .OR. &
     685              :                                   logger%para_env%is_source() .OR. &
     686              :                                   my_mpi_io)) THEN
     687              :          CALL section_vals_val_get(print_key, "FILENAME", c_val=outPath)
     688              :          IF (cp_openpmd_remove_unit_nr(unit_nr)) THEN
     689              :             CLOSE (unit_nr)
     690              :          END IF
     691              : 
     692              :          unit_nr = -1
     693              :       END IF
     694              :       CPASSERT(unit_nr == -1)
     695              :       unit_nr = -1
     696              : #else
     697              :       MARK_USED(unit_nr)
     698              :       MARK_USED(logger)
     699              :       MARK_USED(basis_section)
     700              :       MARK_USED(print_key_path)
     701              :       MARK_USED(local)
     702              :       MARK_USED(ignore_should_output)
     703              :       MARK_USED(mpi_io)
     704              :       CPABORT("CP2K compiled without the openPMD-api")
     705              : #endif
     706              :    END SUBROUTINE cp_openpmd_print_key_finished_output
     707              : 
     708              :    SUBROUTINE cp_openpmd_close_iterations()
     709              : #ifdef __OPENPMD
     710              :       INTEGER                                  :: handle
     711              :       TYPE(cp_openpmd_per_callsite_value_type) :: series_data
     712              :       TYPE(openpmd_iteration_type)             :: iteration
     713              :       INTEGER                                  :: i
     714              : 
     715              :       CALL timeset('openpmd_close_iterations', handle)
     716              :       DO i = 1, cp_num_openpmd_per_callsite
     717              :          series_data = cp_openpmd_per_callsite(i)%value
     718              :          iteration = series_data%output_series%get_iteration( &
     719              :                      series_data%iteration_counter%flat_iteration)
     720              :          IF (.NOT. iteration%closed()) THEN
     721              :             CALL iteration%close()
     722              :          END IF
     723              :       END DO
     724              :       CALL timestop(handle)
     725              : #endif
     726              :    END SUBROUTINE cp_openpmd_close_iterations
     727              : 
     728              :    FUNCTION cp_openpmd_get_default_extension() RESULT(extension)
     729              :       CHARACTER(len=default_string_length)               :: extension
     730              : 
     731              : #ifdef __OPENPMD
     732              :       extension = openpmd_get_default_extension()
     733              : #else
     734              :       extension = ".bp5"
     735              : #endif
     736              : 
     737              :    END FUNCTION cp_openpmd_get_default_extension
     738            0 : 
     739            0 : END MODULE cp_output_handling_openpmd
        

Generated by: LCOV version 2.0-1