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

Generated by: LCOV version 2.0-1