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

Generated by: LCOV version 2.0-1