├── .archive ├── build_hdf5.py ├── cdash.png └── find_compiler.cmake ├── .codespellrc ├── .editorconfig ├── .flake8 ├── .github └── workflows │ ├── ci.yml │ ├── ci_build.yml │ ├── ci_fpm.yml │ ├── ci_windows.yml │ ├── composite-cmake │ └── action.yml │ ├── composite-pkg │ └── action.yml │ ├── composite-unix │ └── action.yml │ ├── oneapi-linux.yml │ ├── oneapi_cache_exclude_linux.sh │ ├── oneapi_install_linux_apt.sh │ └── oneapi_setup_apt_repo_linux.sh ├── API.md ├── CITATION.cff ├── CMakeLists.txt ├── CMakePresets.json ├── Examples.md ├── Install.md ├── LICENSE ├── README.md ├── ci.cmake ├── cmake ├── CheckHDF5.cmake ├── FindHDF5.cmake ├── Modules │ └── CodeCoverage.cmake ├── check_hdf5.c ├── check_hdf5.f90 ├── check_mpi.cmake ├── compilers.cmake ├── config.cmake.in ├── h5fortran.cmake ├── hdf5.cmake ├── install.cmake ├── libraries.json ├── pkgconf.cmake ├── pkgconf.pc.in └── zlib.cmake ├── codemeta.json ├── conanfile.py ├── concepts ├── CMakeLists.txt ├── file_image.f90 ├── file_image.py ├── h5compact.f90 ├── h5ex_t_string_F03.f90 ├── int64.f90 ├── meson.build ├── reader_nd.f90 ├── virtual_dataset.f90 ├── vlen.f90 └── writer_nd.f90 ├── example ├── CMakeLists.txt ├── Readme.md ├── char_repeat_read.f90 ├── ex_fcn.c ├── ex_fcn.cpp ├── ex_fcn.f90 ├── ex_oo.f90 ├── fortran_interface.f90 ├── fortran_interface.h └── vtk_write.f90 ├── ford.md ├── fpm.toml ├── memcheck.cmake ├── options.cmake ├── paper ├── paper.bib └── paper.md ├── pyproject.toml ├── scripts ├── CMakeLists.txt └── README.md ├── src ├── CMakeLists.txt ├── attr.f90 ├── attr_read.f90 ├── attr_read.inc ├── attr_write.f90 ├── attr_write.inc ├── datatype.f90 ├── deflate.f90 ├── interface.f90 ├── read.f90 ├── read_ascii.f90 ├── read_char.inc ├── read_fixed.inc ├── read_scalar.f90 ├── read_vlen.inc ├── reader.f90 ├── reader.inc ├── reader_lt.f90 ├── reader_lt.inc ├── utils.f90 ├── write.f90 ├── write_scalar.f90 ├── writer.f90 ├── writer.inc ├── writer_lt.f90 └── writer_lt.inc ├── test ├── CMakeLists.txt ├── check_shape.py ├── generate_attributes.py ├── generate_string_data.py ├── read_slice.f90 ├── test_array.f90 ├── test_attributes.f90 ├── test_attributes_read.f90 ├── test_cast.f90 ├── test_deflate_props.f90 ├── test_deflate_read.f90 ├── test_deflate_write.f90 ├── test_destructor.f90 ├── test_exist.f90 ├── test_fail_nonexist_variable.f90 ├── test_fail_read_rank_mismatch.f90 ├── test_fail_read_size_mismatch.f90 ├── test_fill.f90 ├── test_groups.f90 ├── test_layout.f90 ├── test_lt.f90 ├── test_minimal.f90 ├── test_scalar.f90 ├── test_shape.f90 ├── test_string.f90 ├── test_string_read.f90 ├── test_version.f90 └── test_write.f90 └── valgrind.supp /.archive/cdash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geospace-code/h5fortran/4734a4da21ab6b55546c9702904b3e779d6e6794/.archive/cdash.png -------------------------------------------------------------------------------- /.archive/find_compiler.cmake: -------------------------------------------------------------------------------- 1 | # --- Help CMake find matching compilers, especially needed for MacOS 2 | 3 | set(_paths) 4 | if(APPLE) 5 | # CMAKE_SYSTEM_NAME is not set till project() 6 | set(_paths /usr/local/bin /opt/homebrew/bin) 7 | # for Homebrew that's not on PATH (can be an issue on CI) 8 | endif() 9 | 10 | 11 | function(find_fortran) 12 | 13 | if(NOT DEFINED FC AND DEFINED ENV{FC}) 14 | set(FC $ENV{FC}) 15 | endif() 16 | 17 | if(FC) 18 | get_filename_component(_dir ${FC} DIRECTORY) 19 | endif() 20 | # determine if the user is intending to use Intel oneAPI or default Gfortran 21 | # Need to check ifort because MKLROOT may be defined for 22 | # use of oneMKL with Gfortran on MacOS and Linux. 23 | if(DEFINED ENV{MKLROOT} OR FC MATCHES ".*ifort") 24 | find_program(FC 25 | NAMES ifort 26 | HINTS ${_dir}) 27 | endif() 28 | 29 | find_program(FC 30 | NAMES gfortran gfortran-12 gfortran-11 gfortran-10 gfortran-9 gfortran-8 gfortran-7 31 | NAMES_PER_DIR 32 | HINTS ${_dir} 33 | PATHS ${_paths}) 34 | 35 | if(FC) 36 | set(ENV{FC} ${FC}) 37 | # ENV{FC} is how project() picks up our hint 38 | endif() 39 | 40 | endfunction(find_fortran) 41 | 42 | 43 | function(find_c) 44 | 45 | if(NOT DEFINED CC) 46 | if(DEFINED ENV{CC}) 47 | set(CC $ENV{CC}) 48 | elseif(NOT DEFINED FC AND DEFINED ENV{FC}) 49 | set(FC $ENV{FC}) 50 | endif() 51 | endif() 52 | 53 | if(NOT DEFINED CC) 54 | # remember, Apple has "/usr/bin/gcc" which is really clang 55 | # the technique below is NECESSARY to work on Mac and not find the wrong GCC 56 | if(DEFINED FC) 57 | get_filename_component(_dir ${FC} DIRECTORY) 58 | endif() 59 | # use same compiler for C and Fortran, which CMake might not do itself 60 | if(FC MATCHES ".*ifort") 61 | if(WIN32) 62 | set(_cc icl) 63 | else() 64 | set(_cc icc) 65 | endif() 66 | elseif(FC MATCHES ".*gfortran") 67 | # get same GCC version as Gfortran 68 | execute_process(COMMAND ${FC} -dumpversion 69 | OUTPUT_VARIABLE _v 70 | RESULT_VARIABLE _err) 71 | if(NOT _err EQUAL 0) 72 | return() 73 | endif() 74 | string(REGEX MATCH "^([0-9]+)" _v ${_v}) 75 | if(_v) 76 | set(_cc gcc-${_v}) 77 | else() 78 | set(_cc gcc-12 gcc-11 gcc-10 gcc-9 gcc-8 gcc-7 gcc) # generic last to avoid AppleClang 79 | endif() 80 | endif() 81 | endif() 82 | 83 | if(NOT _cc) 84 | return() 85 | endif() 86 | 87 | find_program(CC 88 | NAMES ${_cc} 89 | NAMES_PER_DIR 90 | PATHS ${_paths} # PATHS are searched last 91 | HINTS ${_dir} 92 | NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) 93 | 94 | if(CC) 95 | set(ENV{CC} ${CC}) 96 | # ENV{CC} is how project() picks up our hint 97 | endif() 98 | 99 | endfunction(find_c) 100 | 101 | 102 | function(find_c_fortran) 103 | find_fortran() 104 | find_c() 105 | endfunction(find_c_fortran) 106 | -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | skip = */.git,*/build,*/.mypy_cache 3 | ignore-words-list = inout 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | [*.py] 13 | indent_size = 4 14 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 132 3 | ignore = E501, W503, W504 4 | exclude = .git,__pycache__,.eggs/,doc/,docs/,build/,dist/,archive/,.archive/ 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | env: 4 | HOMEBREW_NO_INSTALL_CLEANUP: 1 5 | CTEST_NO_TESTS_ACTION: error 6 | CTEST_PARALLEL_LEVEL: 0 7 | CMAKE_BUILD_PARALLEL_LEVEL: 4 8 | CMAKE_INSTALL_PREFIX: ~/libs 9 | CMAKE_PREFIX_PATH: ~/libs 10 | 11 | on: 12 | push: 13 | paths: 14 | - "**.c" 15 | - "**.cpp" 16 | - "**.f90" 17 | - "**.F90" 18 | - "**/CMakeLists.txt" 19 | - "**.cmake" 20 | - ".github/workflows/**" 21 | - "!memcheck.cmake" 22 | - "!coverage.cmake" 23 | workflow_dispatch: 24 | 25 | # avoid wasted runs 26 | concurrency: 27 | group: ${{ github.workflow }}-${{ github.ref }} 28 | cancel-in-progress: true 29 | 30 | 31 | jobs: 32 | 33 | gcc-new: 34 | timeout-minutes: 15 35 | 36 | strategy: 37 | matrix: 38 | os: [ubuntu-24.04] 39 | shared: [false] 40 | gcc: [12, 13, 14] 41 | include: 42 | - os: ubuntu-24.04 43 | shared: true 44 | gcc: 14 45 | - os: macos-latest 46 | shared: true 47 | gcc: 14 48 | - os: macos-latest 49 | shared: false 50 | gcc: 14 51 | 52 | runs-on: ${{ matrix.os }} 53 | 54 | env: 55 | FC: gfortran-${{ matrix.gcc }} 56 | 57 | steps: 58 | - uses: actions/checkout@v4 59 | 60 | - name: GCC (Linux) 61 | if: runner.os == 'Linux' 62 | run: echo "CC=gcc-${{ matrix.gcc }}" >> $GITHUB_ENV 63 | 64 | - uses: ./.github/workflows/composite-pkg 65 | - uses: ./.github/workflows/composite-unix 66 | 67 | 68 | gcc-old: 69 | timeout-minutes: 15 70 | 71 | strategy: 72 | matrix: 73 | shared: [false] 74 | gcc: [9, 10, 11] 75 | 76 | runs-on: ubuntu-22.04 77 | env: 78 | CC: gcc-${{ matrix.gcc }} 79 | FC: gfortran-${{ matrix.gcc }} 80 | 81 | steps: 82 | - uses: actions/checkout@v4 83 | - uses: ./.github/workflows/composite-pkg 84 | - uses: ./.github/workflows/composite-unix 85 | 86 | 87 | linux-flang: 88 | runs-on: ubuntu-latest 89 | timeout-minutes: 15 90 | 91 | strategy: 92 | matrix: 93 | llvm-version: [20] 94 | 95 | env: 96 | CC: clang-${{ matrix.llvm-version }} 97 | CXX: clang++-${{ matrix.llvm-version }} 98 | FC: flang-${{ matrix.llvm-version }} 99 | 100 | steps: 101 | - name: Apt LLVM 102 | run: | 103 | wget https://apt.llvm.org/llvm.sh 104 | chmod +x llvm.sh 105 | sudo ./llvm.sh ${{ matrix.llvm-version }} 106 | sudo apt-get update 107 | 108 | - name: install Flang 109 | run: sudo apt install --no-install-recommends clang-${{ matrix.llvm-version }} flang-${{ matrix.llvm-version }} 110 | 111 | - uses: actions/checkout@v4 112 | 113 | - name: CMake build & test 114 | run: cmake --workflow nofind 115 | 116 | 117 | 118 | valgrind-memory: 119 | runs-on: ubuntu-latest 120 | needs: gcc-new 121 | timeout-minutes: 10 122 | 123 | steps: 124 | 125 | - name: install valgrind 126 | run: | 127 | sudo apt update 128 | sudo apt install --no-install-recommends valgrind libhdf5-dev 129 | 130 | - uses: actions/checkout@v4 131 | 132 | - run: ctest -Dexclude_label="deflate|python" -S memcheck.cmake -VV 133 | 134 | 135 | # issue with python loading writer.inc 136 | linux-coverage: 137 | if: false 138 | needs: gcc-new 139 | runs-on: ubuntu-latest 140 | timeout-minutes: 10 141 | 142 | steps: 143 | - uses: actions/checkout@v4 144 | - uses: actions/setup-python@v5 145 | with: 146 | python-version: '3.x' 147 | 148 | - name: install hdf5 149 | run: | 150 | sudo apt update 151 | sudo apt install --no-install-recommends libhdf5-dev 152 | 153 | - name: install Gcovr 154 | run: pip install gcovr 155 | 156 | - run: cmake --preset coverage 157 | - run: cmake --build --parallel --preset coverage 158 | 159 | - name: Code coverage 160 | run: cmake --build --parallel --preset run-coverage 161 | 162 | - uses: actions/upload-artifact@v4 163 | with: 164 | name: coverage-report-html 165 | path: build-coverage/coverage/ 166 | 167 | 168 | cmake-older: 169 | timeout-minutes: 30 170 | 171 | runs-on: ubuntu-latest 172 | 173 | strategy: 174 | matrix: 175 | cmake_version: ["3.20.6"] 176 | 177 | steps: 178 | 179 | - uses: actions/checkout@v4 180 | 181 | - name: prereqs 182 | run: | 183 | sudo apt-get update 184 | sudo apt install --no-install-recommends libhdf5-dev 185 | 186 | - uses: ./.github/workflows/composite-cmake 187 | 188 | - name: configure 189 | run: $CMAKE -Bbuild -DCMAKE_INSTALL_PREFIX:PATH=$(pwd)/build/local 190 | 191 | - name: build 192 | run: $CMAKE --build build 193 | 194 | - name: test 195 | run: $CTEST --test-dir build 196 | 197 | - name: install package 198 | run: $CMAKE --install build 199 | -------------------------------------------------------------------------------- /.github/workflows/ci_build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | env: 4 | CMAKE_TLS_VERIFY: true 5 | HOMEBREW_NO_INSTALL_CLEANUP: 1 6 | CTEST_NO_TESTS_ACTION: error 7 | CTEST_PARALLEL_LEVEL: 0 8 | CMAKE_BUILD_PARALLEL_LEVEL: 4 9 | CMAKE_INSTALL_PREFIX: ~/libs 10 | CMAKE_PREFIX_PATH: ~/libs 11 | 12 | 13 | on: 14 | push: 15 | paths: 16 | - "scripts/CMakeLists.txt" 17 | - "cmake/libraries.json" 18 | - ".github/workflows/ci_build.yml" 19 | 20 | jobs: 21 | 22 | linux_mac: 23 | timeout-minutes: 20 24 | 25 | strategy: 26 | matrix: 27 | os: [ubuntu-latest] 28 | shared: [true, false] 29 | include: 30 | - os: macos-latest 31 | shared: false 32 | 33 | runs-on: ${{ matrix.os}} 34 | 35 | steps: 36 | - uses: actions/checkout@v4 37 | 38 | - name: GCC 14 (macOS) 39 | if: runner.os == 'macOS' 40 | run: echo "FC=gfortran-14" >> $GITHUB_ENV 41 | 42 | - name: Configure HDF5 library 43 | run: >- 44 | cmake 45 | -S scripts -B scripts/build 46 | -DBUILD_SHARED_LIBS:BOOL=${{ matrix.shared }} 47 | 48 | - name: Build/install HDF5 49 | run: cmake --build scripts/build 50 | 51 | - name: configure h5fortran 52 | run: >- 53 | cmake --preset default 54 | -DBUILD_SHARED_LIBS:BOOL=${{ matrix.shared }} 55 | 56 | - name: build / test h5fortran 57 | run: cmake --workflow default 58 | 59 | - name: install h5fortran 60 | run: cmake --install build 61 | 62 | - name: example workflow 63 | run: cmake -S example -B example/build 64 | 65 | - run: cmake --build example/build 66 | 67 | - run: ctest --test-dir example/build -V 68 | -------------------------------------------------------------------------------- /.github/workflows/ci_fpm.yml: -------------------------------------------------------------------------------- 1 | name: ci_fpm 2 | 3 | on: 4 | push: 5 | paths: 6 | - "fpm.toml" 7 | - ".github/workflows/ci_fpm.yml" 8 | 9 | jobs: 10 | 11 | linux: 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 15 14 | 15 | env: 16 | FPM_FFLAGS: -I/usr/include/hdf5/serial 17 | FPM_LDFLAGS: -L/usr/lib/x86_64-linux-gnu/hdf5/serial 18 | 19 | steps: 20 | 21 | - uses: fortran-lang/setup-fpm@v7 22 | with: 23 | github-token: ${{ secrets.GITHUB_TOKEN }} 24 | 25 | - name: install hdf5 26 | run: | 27 | sudo apt update 28 | sudo apt install --no-install-recommends libhdf5-dev 29 | 30 | - uses: actions/checkout@v4 31 | 32 | - run: fpm build 33 | -------------------------------------------------------------------------------- /.github/workflows/ci_windows.yml: -------------------------------------------------------------------------------- 1 | name: ci_windows 2 | 3 | env: 4 | CTEST_NO_TESTS_ACTION: error 5 | CTEST_PARALLEL_LEVEL: 0 6 | CMAKE_BUILD_PARALLEL_LEVEL: 4 7 | 8 | on: 9 | push: 10 | paths: 11 | - "**.c" 12 | - "**.cpp" 13 | - "**.f90" 14 | - "**.F90" 15 | - "**/CMakeLists.txt" 16 | - "**.cmake" 17 | - ".github/workflows/ci_windows.yml" 18 | - "!memcheck.cmake" 19 | - "!coverage.cmake" 20 | workflow_dispatch: 21 | 22 | # avoid wasted runs 23 | concurrency: 24 | group: ${{ github.workflow }}-${{ github.ref }} 25 | cancel-in-progress: true 26 | 27 | jobs: 28 | 29 | msys2: 30 | timeout-minutes: 30 31 | runs-on: windows-latest 32 | 33 | # Windows needs these defined within the job. $HOME doesn't work nor does ~. 34 | env: 35 | CMAKE_INSTALL_PREFIX: libs 36 | CMAKE_PREFIX_PATH: libs 37 | 38 | steps: 39 | - uses: msys2/setup-msys2@v2 40 | id: msys2 41 | with: 42 | update: true 43 | install: >- 44 | mingw-w64-ucrt-x86_64-gcc-fortran 45 | mingw-w64-ucrt-x86_64-hdf5 46 | 47 | - name: Put MSYS2_MinGW64 on PATH 48 | run: echo "${{ steps.msys2.outputs.msys2-location }}/ucrt64/bin/" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 49 | 50 | - uses: actions/checkout@v4 51 | 52 | - name: config 53 | run: cmake --preset default -G "MinGW Makefiles" 54 | env: 55 | HDF5_ROOT: ${{ steps.msys2.outputs.msys2-location }}/ucrt64/ 56 | 57 | - name: print config log 58 | if: ${{ failure() }} 59 | run: cat build/CMakeFiles/CMakeConfigureLog.yaml 60 | 61 | - name: Build 62 | run: cmake --build --preset default 63 | 64 | - name: test 65 | run: ctest --preset default 66 | 67 | - name: install package 68 | run: cmake --install build 69 | 70 | - name: example workflow 71 | run: cmake -S example -B example/build -G "MinGW Makefiles" 72 | env: 73 | HDF5_ROOT: ${{ steps.msys2.outputs.msys2-location }}/ucrt64/ 74 | 75 | - run: cmake --build example/build 76 | 77 | - run: ctest --test-dir example/build -V 78 | -------------------------------------------------------------------------------- /.github/workflows/composite-cmake/action.yml: -------------------------------------------------------------------------------- 1 | runs: 2 | 3 | using: "composite" 4 | 5 | steps: 6 | - name: environment 7 | shell: bash 8 | run: | 9 | echo "osarch=linux-x86_64" >> $GITHUB_ENV 10 | echo "archive=linux-x86_64.tar.gz" >> $GITHUB_ENV 11 | 12 | - name: Install CMake ${{ matrix.cmake_version }} 13 | shell: bash 14 | run: | 15 | curl -LO "https://github.com/Kitware/CMake/releases/download/v${{ matrix.cmake_version }}/cmake-${{ matrix.cmake_version }}-$archive" 16 | tar -xf cmake-${{ matrix.cmake_version }}-$archive 17 | 18 | - name: CMake path 19 | shell: bash 20 | run: | 21 | echo "CMAKE=$GITHUB_WORKSPACE/cmake-${{ matrix.cmake_version }}-$osarch/bin/cmake" >> $GITHUB_ENV 22 | echo "CTEST=$GITHUB_WORKSPACE/cmake-${{ matrix.cmake_version }}-$osarch/bin/ctest" >> $GITHUB_ENV 23 | 24 | - name: echo CMake version 25 | shell: bash 26 | run: $CMAKE --version 27 | -------------------------------------------------------------------------------- /.github/workflows/composite-pkg/action.yml: -------------------------------------------------------------------------------- 1 | name: "pkgs" 2 | 3 | runs: 4 | 5 | using: "composite" 6 | 7 | steps: 8 | 9 | - uses: actions/setup-python@v5 10 | with: 11 | python-version: '3.12' 12 | 13 | - name: Python pkgs 14 | shell: bash 15 | run: pip install numpy h5py 16 | 17 | - name: install HDF5 (Linux) 18 | shell: bash 19 | if: runner.os == 'Linux' 20 | run: | 21 | sudo apt update 22 | sudo apt install --no-install-recommends libhdf5-dev 23 | 24 | - name: Install HDF5 (MacOS) 25 | shell: bash 26 | if: runner.os == 'macOS' 27 | run: brew install hdf5 28 | -------------------------------------------------------------------------------- /.github/workflows/composite-unix/action.yml: -------------------------------------------------------------------------------- 1 | name: "build-test-install-example" 2 | 3 | runs: 4 | 5 | using: "composite" 6 | 7 | steps: 8 | 9 | - name: Configure 10 | shell: bash 11 | run: >- 12 | cmake --preset multi 13 | -DBUILD_SHARED_LIBS:BOOL=${{ matrix.shared }} 14 | 15 | - name: Build / test Debug 16 | shell: bash 17 | run: cmake --workflow debug 18 | 19 | - name: Build Release 20 | shell: bash 21 | run: cmake --workflow release 22 | 23 | - name: install package 24 | shell: bash 25 | run: cmake --install build 26 | 27 | - name: example workflow 28 | shell: bash 29 | run: cmake -S example -B example/build 30 | 31 | - name: Build examples 32 | shell: bash 33 | run: cmake --build example/build 34 | 35 | - name: Test examples 36 | shell: bash 37 | run: ctest --test-dir example/build -V 38 | -------------------------------------------------------------------------------- /.github/workflows/oneapi-linux.yml: -------------------------------------------------------------------------------- 1 | name: oneapi-linux 2 | 3 | env: 4 | CC: icx 5 | CXX: icpx 6 | FC: ifx 7 | # https://github.com/oneapi-src/oneapi-ci/blob/master/.github/workflows/build_all.yml 8 | CTEST_NO_TESTS_ACTION: error 9 | CTEST_PARALLEL_LEVEL: 4 10 | CMAKE_BUILD_PARALLEL_LEVEL: 4 11 | CMAKE_BUILD_TYPE: Release 12 | # debug triggers asan build errors, peculiar to GitHub Actions 13 | 14 | on: 15 | push: 16 | paths: 17 | - "**.c" 18 | - "**.cpp" 19 | - "**.f90" 20 | - "**.F90" 21 | - "**.cmake" 22 | - "**/CMakeLists.txt" 23 | - ".github/workflows/oneapi-linux.yml" 24 | - "!memcheck.cmake" 25 | - "!coverage.cmake" 26 | - "!example/*" 27 | workflow_dispatch: 28 | 29 | # avoid wasted runs 30 | concurrency: 31 | group: ${{ github.workflow }}-${{ github.ref }} 32 | cancel-in-progress: true 33 | 34 | 35 | jobs: 36 | 37 | linux: 38 | runs-on: ubuntu-latest 39 | timeout-minutes: 10 40 | 41 | steps: 42 | - uses: actions/checkout@v4 43 | 44 | - uses: actions/setup-python@v5 45 | with: 46 | python-version: '3.12' 47 | 48 | - name: Python pkgs 49 | run: pip install numpy h5py 50 | 51 | - name: cache install oneAPI 52 | id: cache-install 53 | uses: actions/cache@v4 54 | with: 55 | path: | 56 | /opt/intel/oneapi 57 | key: install-apt 58 | 59 | - name: non-cache install oneAPI 60 | if: steps.cache-install.outputs.cache-hit != 'true' 61 | timeout-minutes: 5 62 | run: | 63 | .github/workflows/oneapi_setup_apt_repo_linux.sh 64 | sudo apt install intel-oneapi-compiler-dpcpp-cpp intel-oneapi-compiler-fortran 65 | 66 | - name: Setup Intel oneAPI environment 67 | run: | 68 | source /opt/intel/oneapi/setvars.sh 69 | printenv >> $GITHUB_ENV 70 | 71 | - name: Configure CMake 72 | run: >- 73 | cmake --preset default --install-prefix ${{ runner.temp }} 74 | 75 | - name: print config log 76 | if: ${{ failure() }} 77 | run: cat build/CMakeFiles/CMakeConfigureLog.yaml 78 | 79 | - name: Release workflow 80 | run: cmake --workflow default 81 | 82 | - name: install package 83 | run: cmake --install build 84 | 85 | - name: example workflow 86 | run: cmake -S example -B example/build -DCMAKE_PREFIX_PATH=${{ runner.temp }} 87 | 88 | - run: cmake --build example/build 89 | 90 | - run: ctest --test-dir example/build -V 91 | 92 | # BUILD_SHARED_LIBS=false since as with any C++ / Fortran program with Intel compiler, 93 | # need to have GCC environment carefully set 94 | # so that underlying libstdc++ is compatible. 95 | 96 | - name: exclude unused files from cache 97 | if: steps.cache-install.outputs.cache-hit != 'true' 98 | run: .github/workflows/oneapi_cache_exclude_linux.sh 99 | -------------------------------------------------------------------------------- /.github/workflows/oneapi_cache_exclude_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2020 Intel Corporation 4 | # 5 | # SPDX-License-Identifier: MIT 6 | 7 | #shellcheck disable=SC2010 8 | LATEST_VERSION=$(ls -1 /opt/intel/oneapi/compiler/ | grep -v latest | sort | tail -1) 9 | 10 | sudo rm -rf /opt/intel/oneapi/compiler/"$LATEST_VERSION"/linux/compiler/lib/ia32_lin 11 | sudo rm -rf /opt/intel/oneapi/compiler/"$LATEST_VERSION"/linux/bin/ia32 12 | sudo rm -rf /opt/intel/oneapi/compiler/"$LATEST_VERSION"/linux/lib/emu 13 | sudo rm -rf /opt/intel/oneapi/compiler/"$LATEST_VERSION"/linux/lib/oclfpga 14 | -------------------------------------------------------------------------------- /.github/workflows/oneapi_install_linux_apt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2020 Intel Corporation 4 | # 5 | # SPDX-License-Identifier: MIT 6 | 7 | COMPONENTS=$(echo "$1" | sed "s/,/ /g") 8 | sudo apt-get install -y "$COMPONENTS" 9 | sudo apt-get clean 10 | -------------------------------------------------------------------------------- /.github/workflows/oneapi_setup_apt_repo_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2020 Intel Corporation 4 | # 5 | # SPDX-License-Identifier: MIT 6 | 7 | wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB 8 | sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB 9 | echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list 10 | sudo apt-get update -o Dir::Etc::sourcelist="sources.list.d/oneAPI.list" -o APT::Get::List-Cleanup="0" 11 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | authors: 3 | - family-names: Hirsch 4 | given-names: Michael 5 | orcid: https://orcid.org/0000-0002-1637-6526 6 | title: h5fortran 7 | doi: 10.5281/zenodo.3562521 8 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20...4.0) 2 | 3 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) 4 | message(FATAL_ERROR "Please do out of source build like 5 | cmake -Bbuild") 6 | endif() 7 | 8 | project(h5fortran 9 | LANGUAGES C Fortran 10 | VERSION 4.11.0 11 | ) 12 | 13 | include(CTest) 14 | 15 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 16 | 17 | if(NOT DEFINED CRAY AND DEFINED ENV{PE_ENV}) 18 | set(CRAY true) 19 | endif() 20 | 21 | if(CMAKE_VERSION VERSION_LESS 3.25 AND CMAKE_SYSTEM_NAME STREQUAL "Linux") 22 | set(LINUX true) 23 | endif() 24 | 25 | # --- avoid Anaconda libraries 26 | find_package(Python COMPONENTS Interpreter) 27 | 28 | if(DEFINED ENV{CONDA_PREFIX}) 29 | list(APPEND CMAKE_IGNORE_PREFIX_PATH $ENV{CONDA_PREFIX}) 30 | list(APPEND CMAKE_IGNORE_PATH $ENV{CONDA_PREFIX}/bin) 31 | # need CMAKE_IGNORE_PATH for CMake < 3.23 32 | # and to ensure system env var PATH doesn't interfere 33 | # despite CMAKE_IGNORE_PREFIX_PATH 34 | endif() 35 | 36 | include(options.cmake) 37 | include(cmake/compilers.cmake) 38 | include(cmake/CheckHDF5.cmake) 39 | 40 | if(NOT TARGET HDF5::HDF5 AND (h5fortran_find OR CRAY)) 41 | find_package(HDF5 COMPONENTS Fortran) 42 | endif() 43 | 44 | if(h5fortran_IS_TOP_LEVEL AND HDF5_FOUND) 45 | check_hdf5() 46 | elseif(NOT TARGET HDF5::HDF5) 47 | include(cmake/hdf5.cmake) 48 | endif() 49 | 50 | set(HDF5_VERSION ${HDF5_VERSION} CACHE STRING "HDF5 version") 51 | 52 | if(h5fortran_IS_TOP_LEVEL AND (hdf5_parallel OR HDF5_HAVE_PARALLEL)) 53 | target_link_libraries(HDF5::HDF5 INTERFACE MPI::MPI_Fortran) 54 | endif() 55 | 56 | # HDF5 bug #3663 for HDF5 1.14.2..5, ...? 57 | # https://github.com/HDFGroup/hdf5/issues/3663 58 | if(WIN32 AND CMAKE_Fortran_COMPILER_ID MATCHES "^Intel") 59 | if(HDF5_VERSION MATCHES "1.14.[2-6]") 60 | message(STATUS "HDF5: applying workaround for HDF5 bug #3663 with Intel oneAPI on Windows") 61 | target_link_libraries(HDF5::HDF5 INTERFACE shlwapi) 62 | endif() 63 | endif() 64 | 65 | # --- h5fortran library 66 | 67 | add_library(h5fortran) 68 | target_include_directories(h5fortran PUBLIC 69 | $ 70 | $ 71 | ) 72 | target_link_libraries(h5fortran PUBLIC HDF5::HDF5) 73 | set_property(TARGET h5fortran PROPERTY Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include) 74 | set_property(TARGET h5fortran PROPERTY VERSION ${PROJECT_VERSION}) 75 | 76 | # GLOBAL needed for use by parent projects 77 | add_library(h5fortran::h5fortran INTERFACE IMPORTED GLOBAL) 78 | target_link_libraries(h5fortran::h5fortran INTERFACE h5fortran) 79 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include) 80 | # avoid race condition when used by parent projects 81 | 82 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/h5fortran.mod DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 83 | 84 | install(TARGETS h5fortran EXPORT h5fortran-targets) 85 | 86 | add_subdirectory(src) 87 | 88 | get_property(_h5f_defs TARGET h5fortran PROPERTY COMPILE_DEFINITIONS) 89 | message(VERBOSE "h5fortran COMPILE_DEFINITIONS: ${_h5f_defs}") 90 | get_property(_h5f_flags TARGET h5fortran PROPERTY COMPILE_OPTIONS) 91 | message(VERBOSE "h5fortran COMPILE_OPTIONS: ${_h5f_flags}") 92 | get_property(_h5f_libs TARGET h5fortran PROPERTY LINK_LIBRARIES) 93 | message(VERBOSE "h5fortran LINK_LIBRARIES: ${_h5f_libs}") 94 | 95 | 96 | if(h5fortran_BUILD_TESTING) 97 | add_subdirectory(test) 98 | endif() 99 | 100 | # additional Find*.cmake necessary 101 | install(FILES 102 | ${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindHDF5.cmake 103 | DESTINATION cmake 104 | ) 105 | 106 | include(cmake/pkgconf.cmake) 107 | include(cmake/install.cmake) 108 | 109 | file(GENERATE OUTPUT .gitignore CONTENT "*") 110 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | 4 | "configurePresets": [ 5 | { 6 | "name": "default", 7 | "binaryDir": "${sourceDir}/build", 8 | "cacheVariables": { 9 | "CMAKE_COMPILE_WARNING_AS_ERROR": true 10 | } 11 | }, 12 | { 13 | "name": "multi", "inherits": "default", 14 | "displayName": "Ninja Multi-Config", 15 | "generator": "Ninja Multi-Config" 16 | }, 17 | { 18 | "name": "nofind", "inherits": "default", 19 | "binaryDir": "${sourceDir}/build-nofind", 20 | "displayName": "don't find HDF5 or Zlib", 21 | "cacheVariables": { 22 | "h5fortran_find": false 23 | } 24 | }, 25 | { 26 | "name": "coverage", 27 | "binaryDir": "${sourceDir}/build-coverage", 28 | "displayName": "Code Coverage", 29 | "description": "Build with code coverage enabled.", 30 | "cacheVariables": { 31 | "CMAKE_BUILD_TYPE": "Debug", 32 | "h5fortran_COVERAGE": true 33 | } 34 | } 35 | ], 36 | "buildPresets": [ 37 | { "name": "default", "configurePreset": "default" }, 38 | { "name": "nofind", "configurePreset": "nofind" }, 39 | { 40 | "name": "explain", 41 | "configurePreset": "default", 42 | "nativeToolOptions": ["-d", "explain"] 43 | }, 44 | { 45 | "name": "keep", 46 | "configurePreset": "default", 47 | "nativeToolOptions": ["-d", "keeprsp", "-d", "keepdepfile"] 48 | }, 49 | { 50 | "name": "stats", 51 | "configurePreset": "default", 52 | "nativeToolOptions": ["-d", "stats"] 53 | }, 54 | { 55 | "name": "release", 56 | "configurePreset": "multi", 57 | "configuration": "Release" 58 | }, 59 | { 60 | "name": "reldebug", 61 | "configurePreset": "multi", 62 | "configuration": "RelWithDebInfo", 63 | "displayName": "Release with Debug Info" 64 | }, 65 | { 66 | "name": "debug", 67 | "configurePreset": "multi", 68 | "configuration": "Debug" 69 | }, 70 | { 71 | "name": "coverage", 72 | "configurePreset": "coverage" 73 | }, 74 | { 75 | "name": "run-coverage", 76 | "configurePreset": "coverage", 77 | "targets": "coverage" 78 | } 79 | ], 80 | "testPresets": [ 81 | { 82 | "name": "default", 83 | "configurePreset": "default", 84 | "output": { 85 | "outputOnFailure": true, 86 | "verbosity": "verbose" 87 | }, 88 | "execution": { 89 | "noTestsAction": "error", 90 | "scheduleRandom": true, 91 | "stopOnFailure": false, 92 | "timeout": 60 93 | } 94 | }, 95 | { "name": "nofind", "configurePreset": "nofind", "inherits": "default" }, 96 | { 97 | "name": "release", "inherits": "default", 98 | "configurePreset": "multi", 99 | "configuration": "Release" 100 | }, 101 | { 102 | "name": "reldebug", "inherits": "default", 103 | "configurePreset": "multi", 104 | "configuration": "RelWithDebInfo", 105 | "displayName": "Release with Debug Info" 106 | }, 107 | { 108 | "name": "debug", "inherits": "default", 109 | "configurePreset": "multi", 110 | "configuration": "Debug" 111 | } 112 | ], 113 | "workflowPresets": [ 114 | { 115 | "name": "default", 116 | "steps": [ 117 | { "type": "configure", "name": "default" }, 118 | { "type": "build", "name": "default" }, 119 | { "type": "test", "name": "default" } 120 | ] 121 | }, 122 | { 123 | "name": "debug", 124 | "steps": [ 125 | { "type": "configure", "name": "multi" }, 126 | { "type": "build", "name": "debug" }, 127 | { "type": "test", "name": "debug" } 128 | ] 129 | }, 130 | { 131 | "name": "release", 132 | "steps": [ 133 | { "type": "configure", "name": "multi" }, 134 | { "type": "build", "name": "release" }, 135 | { "type": "test", "name": "release" } 136 | ] 137 | }, 138 | { 139 | "name": "nofind", 140 | "steps": [ 141 | { "type": "configure", "name": "nofind" }, 142 | { "type": "build", "name": "nofind" }, 143 | { "type": "test", "name": "nofind" } 144 | ] 145 | } 146 | ] 147 | } 148 | -------------------------------------------------------------------------------- /Install.md: -------------------------------------------------------------------------------- 1 | # Install h5fortran 2 | 3 | h5fortran is typically built and installed with CMake or Meson. 4 | 5 | ## Requirements 6 | 7 | * Fortran 2018 compiler (this project uses `submodule` and `error stop`). For example, Gfortran ≥ 7 or Intel oneAPI. 8 | * HDF5 Fortran library (>= 1.8.7, including 1.10.x and 1.12.x) 9 | * MacOS / Homebrew: `brew install gcc hdf5` 10 | * Linux / Windows Subsystem for Linux: `apt install gfortran libhdf5-dev` 11 | * Windows MSYS2: `pacman -S mingw-w64-x86_64-hdf5` 12 | * build from source (optional): `python scripts/build_hdf5.py` 13 | 14 | Note that some precompiled HDF5 libraries have only C / C++ without Fortran. 15 | 16 | The library `libh5fortran` is built, link it into your program as usual along with the HDF5 libraries and include files. 17 | 18 | ## CMake 19 | 20 | Build and self-test via: 21 | 22 | ```sh 23 | cmake -B build 24 | cmake --build build 25 | 26 | # optional self-test 27 | cd build 28 | ctest 29 | ``` 30 | 31 | (optional) to install to a directory like ~/h5fortran: 32 | 33 | ```sh 34 | cmake -B build -DCMAKE_INSTALL_PREFIX=~/h5fortran 35 | cmake --install build 36 | ``` 37 | 38 | ### use h5fortran from your project 39 | 40 | ```sh 41 | cmake -B build -Dh5fortran_ROOT=~/h5fortran/ 42 | ``` 43 | 44 | and in your CMakeLists.txt 45 | 46 | ```cmake 47 | cmake_minimum_required(VERSION 3.20) 48 | project(myProject LANGUAGES Fortran) 49 | 50 | find_package(h5fortran) 51 | 52 | if(NOT h5fortran_FOUND) 53 | include(FetchContent) 54 | 55 | FetchContent_Declare(H5FORTRAN 56 | GIT_REPOSITORY https://github.com/geospace-code/h5fortran.git 57 | GIT_TAG v3.6.6) 58 | FetchContent_MakeAvailable(H5FORTRAN) 59 | endif() 60 | 61 | # --- your project targets: 62 | 63 | add_executable(myProj main.f90) 64 | target_link_libraries(myProj PRIVATE h5fortran::h5fortran) 65 | ``` 66 | 67 | and where the main.f90 is like: 68 | 69 | ```fortran 70 | program main 71 | 72 | use h5fortran, only : hdf5_file 73 | implicit none 74 | 75 | type(hdf5_file) :: h5f 76 | 77 | call h5f%open('h5fortran_example2.h5', action='w') 78 | call h5f%write('/x', 123) 79 | call h5f%close() 80 | 81 | 82 | end program 83 | ``` 84 | 85 | ### [optional] create distributable archive 86 | 87 | If you wish to create a package archive that is usable on systems with compatible Fortran ABI, after building: 88 | 89 | ```sh 90 | cpack --config build/CPackConfig.cmake 91 | ``` 92 | 93 | ### [optional] specify a particular HDF5 library 94 | 95 | ```sh 96 | cmake -DHDF5_ROOT=/path/to/hdf5lib -B build 97 | ``` 98 | 99 | or set environment variable `HDF5_ROOT=/path/to/hdf5lib` 100 | 101 | ## Meson 102 | 103 | To build h5fortran as a standalone project 104 | 105 | ```sh 106 | meson build 107 | 108 | meson test -C build 109 | ``` 110 | 111 | ### h5fortran Meson subproject 112 | 113 | To include h5fortran as a Meson subproject, in the main project meson.build (that uses h5fortran) have like: 114 | 115 | ```meson 116 | hdf5_proj = subproject('h5fortran') 117 | hdf5_interface = hdf5_proj.get_variable('hdf5_interface') 118 | 119 | my_exe = executable('myexe', 'main.f90', dependencies: hdf5_interface) 120 | ``` 121 | 122 | and have a file in the main project `subprojects/h5fortran.wrap` containing: 123 | 124 | ```ini 125 | [wrap-git] 126 | directory = h5fortran 127 | url = https://github.com/geospace-code/h5fortran.git 128 | revision = v3.6.5 129 | ``` 130 | 131 | ## Standalone compiler wrapper 132 | 133 | h5fortran can be used from the 134 | [HDF5 compiler wrapper "h5fc"](https://support.hdfgroup.org/HDF5/Tutor/compile.html) like: 135 | 136 | ```sh 137 | h5fc -I~/h5fortran/include myprogram.f90 ~/h5fortran/lib/libh5fortran.a 138 | ``` 139 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018-2020, Michael Hirsch, Ph.D. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /ci.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | set(CTEST_PROJECT_NAME "h5fortran") 4 | 5 | set(opts 6 | -DCMAKE_COMPILE_WARNING_AS_ERROR:BOOL=ON 7 | ) 8 | 9 | option(submit "use CDash upload" true) 10 | 11 | # --- main script 12 | 13 | set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC") 14 | set(CTEST_SUBMIT_URL "https://my.cdash.org/submit.php?project=${CTEST_PROJECT_NAME}") 15 | 16 | if(NOT CTEST_MODEL) 17 | set(CTEST_MODEL "Experimental") 18 | endif() 19 | 20 | # --- other defaults 21 | set(CTEST_TEST_TIMEOUT 10) 22 | 23 | set(CTEST_USE_LAUNCHERS true) 24 | set(CTEST_OUTPUT_ON_FAILURE true) 25 | set(CTEST_START_WITH_EMPTY_BINARY_DIRECTORY_ONCE true) 26 | 27 | set(CTEST_SOURCE_DIRECTORY ${CTEST_SCRIPT_DIRECTORY}) 28 | if(NOT DEFINED CTEST_BINARY_DIRECTORY) 29 | set(CTEST_BINARY_DIRECTORY ${CTEST_SOURCE_DIRECTORY}/build) 30 | endif() 31 | 32 | if(NOT DEFINED CTEST_SITE AND DEFINED ENV{CTEST_SITE}) 33 | set(CTEST_SITE $ENV{CTEST_SITE}) 34 | endif() 35 | 36 | find_program(GIT_EXECUTABLE NAMES git REQUIRED) 37 | 38 | # --- CTEST_BUILD_NAME is used by ctest_submit(); must be set before ctest_start() 39 | 40 | if(NOT CTEST_BUILD_NAME) 41 | execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags 42 | WORKING_DIRECTORY ${CTEST_SOURCE_DIRECTORY} 43 | OUTPUT_VARIABLE git_rev OUTPUT_STRIP_TRAILING_WHITESPACE 44 | RESULT_VARIABLE ret 45 | ) 46 | if(ret EQUAL 0) 47 | set(CTEST_BUILD_NAME ${git_rev}) 48 | endif() 49 | endif() 50 | 51 | 52 | function(find_generator) 53 | 54 | if(NOT CTEST_CMAKE_GENERATOR AND DEFINED ENV{CMAKE_GENERATOR}) 55 | set(CTEST_CMAKE_GENERATOR $ENV{CMAKE_GENERATOR} PARENT_SCOPE) 56 | return() 57 | # return here as if(...) wouldn't detect it in parent_scope 58 | endif() 59 | if(CTEST_CMAKE_GENERATOR) 60 | return() 61 | endif() 62 | 63 | find_program(ninja NAMES ninja ninja-build samu) 64 | 65 | if(ninja) 66 | set(CTEST_CMAKE_GENERATOR "Ninja" PARENT_SCOPE) 67 | elseif(WIN32) 68 | set(CTEST_CMAKE_GENERATOR "MinGW Makefiles" PARENT_SCOPE) 69 | else() 70 | set(CTEST_CMAKE_GENERATOR "Unix Makefiles" PARENT_SCOPE) 71 | endif() 72 | 73 | endfunction(find_generator) 74 | 75 | find_generator() 76 | 77 | # --- CTest Dashboard 78 | 79 | set(CTEST_SUBMIT_RETRY_COUNT 2) 80 | # avoid auto-detect version control failures on some systems 81 | set(CTEST_UPDATE_TYPE git) 82 | set(CTEST_UPDATE_COMMAND git) 83 | 84 | ctest_start(${CTEST_MODEL}) 85 | 86 | if(CTEST_MODEL MATCHES "(Nightly|Continuous)") 87 | # this erases local code changes i.e. anything not "git push" already is lost forever! 88 | # we try to avoid that by guarding with a Git porcelain check 89 | execute_process(COMMAND ${GIT_EXECUTABLE} status --porcelain 90 | WORKING_DIRECTORY ${CTEST_SOURCE_DIRECTORY} 91 | TIMEOUT 5 92 | OUTPUT_VARIABLE ret OUTPUT_STRIP_TRAILING_WHITESPACE 93 | COMMAND_ERROR_IS_FATAL ANY 94 | ) 95 | if(ret) 96 | message(FATAL_ERROR "CTest would have erased the non-Git Push'd changes.") 97 | else() 98 | ctest_update( 99 | RETURN_VALUE ret 100 | CAPTURE_CMAKE_ERROR err 101 | ) 102 | if(ret LESS 0 OR NOT err EQUAL 0) 103 | message(FATAL_ERROR "Update failed: return ${ret} cmake return ${err}") 104 | endif() 105 | if(ret EQUAL 0 AND CTEST_MODEL STREQUAL "Continuous") 106 | message(NOTICE "No Git-updated files -> no need to test in CTest Model ${CTEST_MODEL}. CTest stopping.") 107 | return() 108 | endif() 109 | 110 | endif() 111 | endif() 112 | 113 | # --- configure 114 | 115 | ctest_configure( 116 | OPTIONS "${opts}" 117 | RETURN_VALUE ret 118 | CAPTURE_CMAKE_ERROR err 119 | ) 120 | if(NOT (ret EQUAL 0 AND err EQUAL 0)) 121 | if(submit) 122 | ctest_submit(BUILD_ID build_id) 123 | endif() 124 | message(FATAL_ERROR "Configure ${build_id} failed: return ${ret} cmake return ${err}") 125 | endif() 126 | 127 | if(DEFINED ENV{CMAKE_BUILD_PARALLEL_LEVEL}) 128 | set(Ncpu $ENV{CMAKE_BUILD_PARALLEL_LEVEL}) 129 | else() 130 | cmake_host_system_information(RESULT Ncpu QUERY NUMBER_OF_PHYSICAL_CORES) 131 | endif() 132 | 133 | ctest_build( 134 | PARALLEL_LEVEL ${Ncpu} 135 | RETURN_VALUE ret 136 | CAPTURE_CMAKE_ERROR err 137 | ) 138 | if(NOT (ret EQUAL 0 AND err EQUAL 0)) 139 | if(submit) 140 | ctest_submit(BUILD_ID build_id) 141 | endif() 142 | message(FATAL_ERROR "Build ${build_id} failed: return ${ret} cmake return ${err}") 143 | endif() 144 | 145 | if(DEFINED ENV{CTEST_PARALLEL_LEVEL}) 146 | set(Ntest $ENV{CTEST_PARALLEL_LEVEL}) 147 | else() 148 | set(Ntest ${Ncpu}) 149 | endif() 150 | 151 | ctest_test( 152 | SCHEDULE_RANDOM true 153 | PARALLEL_LEVEL ${Ntest} 154 | RETURN_VALUE ret 155 | CAPTURE_CMAKE_ERROR err 156 | ) 157 | 158 | if(submit) 159 | ctest_submit(BUILD_ID build_id) 160 | endif() 161 | if(NOT (ret EQUAL 0 AND err EQUAL 0)) 162 | message(FATAL_ERROR "Test ${build_id} failed: CTest code ${ret}, CMake code ${err}.") 163 | endif() 164 | 165 | message(STATUS "OK: CTest build ${build_id}") 166 | -------------------------------------------------------------------------------- /cmake/CheckHDF5.cmake: -------------------------------------------------------------------------------- 1 | # HDF5 1.14.0 has a bug where basic types are not correct, which causes confusing runtime errors 2 | # check for those and other problems that may arise before build. 3 | 4 | include(CheckSourceRuns) 5 | 6 | macro(windows_oneapi_hdf5_workaround) 7 | 8 | # HDF5 bug #3663 for HDF5 1.14.2, 1.14.3, ...? 9 | # https://github.com/HDFGroup/hdf5/issues/3663 10 | if(WIN32 AND CMAKE_Fortran_COMPILER_ID MATCHES "^Intel") 11 | if(HDF5_VERSION MATCHES "1.14.[2-3]") 12 | message(VERBOSE "HDF5: applying workaround for HDF5 bug #3663 with Intel oneAPI on Windows") 13 | list(APPEND CMAKE_REQUIRED_LIBRARIES shlwapi) 14 | endif() 15 | endif() 16 | 17 | endmacro() 18 | 19 | 20 | function(hdf5_run_err_diag stderr) 21 | 22 | if(DEFINED ENV{CONDA_PREFIX}) 23 | message(WARNING "suggest running 'conda deactivate' and re-running cmake as Conda may be interfering with HDF5 library.") 24 | elseif(WIN32 AND stderr MATCHES "0xc0000135") 25 | message(STATUS "test run error may be due to missing HDF5 DLLs in PATH 26 | ${stderr}") 27 | elseif(WIN32 AND stderr MATCHES "0xc0000139") 28 | message(STATUS "test run error may be due to Windows security policy 29 | ${stderr}") 30 | else() 31 | message(WARNING "HDF5 C types failed check") 32 | endif() 33 | endfunction() 34 | 35 | 36 | function(check_hdf5_c) 37 | 38 | if(DEFINED hdf5_c_types_run) 39 | return() 40 | endif() 41 | 42 | set(CMAKE_REQUIRED_LIBRARIES HDF5::HDF5) 43 | 44 | windows_oneapi_hdf5_workaround() 45 | 46 | message(CHECK_START "Checking HDF5 C types") 47 | 48 | try_run(hdf5_c_types_run hdf5_c_types_build 49 | SOURCES ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/check_hdf5.c 50 | LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} 51 | RUN_OUTPUT_STDERR_VARIABLE _stderr 52 | ) 53 | 54 | if(hdf5_c_types_build AND hdf5_c_types_run EQUAL 0) 55 | message(CHECK_PASS "passed") 56 | return() 57 | endif() 58 | 59 | message(CHECK_FAIL "failed") 60 | 61 | hdf5_run_err_diag(${_stderr}) 62 | 63 | endfunction(check_hdf5_c) 64 | 65 | 66 | function(check_hdf5_fortran) 67 | 68 | if(DEFINED hdf5_fortran_types_run) 69 | return() 70 | endif() 71 | 72 | set(CMAKE_REQUIRED_LIBRARIES HDF5::HDF5) 73 | 74 | windows_oneapi_hdf5_workaround() 75 | 76 | message(CHECK_START "Checking HDF5 Fortran types") 77 | 78 | try_run(hdf5_fortran_types_run hdf5_fortran_types_build 79 | SOURCES ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/check_hdf5.f90 80 | LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} 81 | RUN_OUTPUT_STDERR_VARIABLE _stderr 82 | ) 83 | 84 | if(hdf5_fortran_types_build AND hdf5_fortran_types_run EQUAL 0) 85 | message(CHECK_PASS "passed") 86 | return() 87 | endif() 88 | 89 | message(CHECK_FAIL "failed") 90 | 91 | hdf5_run_err_diag(${_stderr}) 92 | 93 | endfunction(check_hdf5_fortran) 94 | 95 | 96 | function(check_hdf5) 97 | 98 | if(CMAKE_VERSION VERSION_LESS 3.25) 99 | message(STATUS "HDF5: skipping check for C and Fortran types due to CMake version < 3.25") 100 | return() 101 | endif() 102 | 103 | check_hdf5_c() 104 | check_hdf5_fortran() 105 | 106 | endfunction() 107 | -------------------------------------------------------------------------------- /cmake/check_hdf5.c: -------------------------------------------------------------------------------- 1 | #include "hdf5.h" 2 | 3 | #include 4 | #include 5 | 6 | int main(void){ 7 | 8 | if(H5open() != 0){ 9 | fprintf(stderr, "H5open() failed"); 10 | return EXIT_FAILURE; 11 | } 12 | 13 | if(H5F_ACC_RDONLY == H5F_ACC_TRUNC || H5F_ACC_RDONLY == H5F_ACC_RDWR){ 14 | fprintf(stderr, "H5F_ACC_RDONLY, H5F_ACC_TRUNC, H5F_ACC_RDWR are not all distinct"); 15 | return EXIT_FAILURE; 16 | } 17 | 18 | if(H5close() != 0){ 19 | fprintf(stderr, "H5close() failed"); 20 | return EXIT_FAILURE; 21 | } 22 | printf("OK: HDF5 C type check"); 23 | return EXIT_SUCCESS; 24 | } 25 | -------------------------------------------------------------------------------- /cmake/check_hdf5.f90: -------------------------------------------------------------------------------- 1 | program main 2 | 3 | use hdf5 4 | 5 | implicit none 6 | 7 | integer :: i 8 | integer(HID_T) :: fid 9 | 10 | call H5open_f(i) 11 | if(i /= 0) error stop "H5open_f failed [0]" 12 | 13 | call H5open_f(i) 14 | if(i /= 0) error stop "H5open_f failed [1]" 15 | 16 | print '(a,i0)', "H5F_ACC_RDONLY_F = ", H5F_ACC_RDONLY_F 17 | print '(a,i0)', "H5F_ACC_TRUNC_F = ", H5F_ACC_TRUNC_F 18 | print '(a,i0)', "H5F_ACC_RDWR_F = ", H5F_ACC_RDWR_F 19 | 20 | if(H5F_ACC_RDONLY_F == H5F_ACC_TRUNC_F .or. H5F_ACC_RDONLY_F == H5F_ACC_RDWR_F) then 21 | error stop "H5F_ACC_RDONLY, H5F_ACC_TRUNC, H5F_ACC_RDWR are not all distinct" 22 | endif 23 | 24 | call H5close_f(i) 25 | if (i /= 0) error stop "H5close() failed" 26 | 27 | print '(a)', "OK: HDF5 C type check" 28 | 29 | end program 30 | -------------------------------------------------------------------------------- /cmake/check_mpi.cmake: -------------------------------------------------------------------------------- 1 | # even factory FindMPI missing this on say MacOS despite printing MPI_VERSION on find 2 | function(check_mpi_version) 3 | 4 | if(MPI_VERSION) 5 | return() 6 | endif() 7 | 8 | message(CHECK_START "Checking MPI API level") 9 | 10 | if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/find_mpi/get_mpi_version.c) 11 | file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/find_mpi/get_mpi_version.c 12 | [=[ 13 | #include 14 | #include 15 | 16 | int main(void) { 17 | int version, subversion; 18 | 19 | int ierr = MPI_Get_version(&version, &subversion); 20 | if (ierr != 0) return 1; 21 | printf("CMAKE_MPI_VERSION %d.%d\n", version, subversion); 22 | 23 | return 0; 24 | } 25 | ]=] 26 | ) 27 | endif() 28 | 29 | try_run(mpi_run_code mpi_build_code 30 | ${CMAKE_CURRENT_BINARY_DIR}/find_mpi/build 31 | ${CMAKE_CURRENT_BINARY_DIR}/find_mpi/get_mpi_version.c 32 | CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:PATH=${MPI_C_INCLUDE_DIRS}" 33 | LINK_LIBRARIES ${MPI_C_LIBRARIES} 34 | RUN_OUTPUT_VARIABLE MPI_VERSION_STRING 35 | COMPILE_OUTPUT_VARIABLE mpi_vers_build_out 36 | ) 37 | # CMAKE_FLAGS must have quotes to handles CMake list 38 | 39 | if(NOT mpi_build_code) 40 | message(CHECK_FAIL "MPI_VERSION test failed to build: 41 | ${mpi_vers_build_out}" 42 | ) 43 | return() 44 | endif() 45 | 46 | # We don't if(mpi_run_code EQUAL 0) as some platforms / MPI libs don't precisely 47 | # return 0. The regex should be enough. 48 | if("${MPI_VERSION_STRING}" MATCHES "CMAKE_MPI_VERSION ([0-9]+\\.[0-9]+)") 49 | set(MPI_VERSION ${CMAKE_MATCH_1} CACHE STRING "MPI API level") 50 | message(CHECK_PASS "${MPI_VERSION}") 51 | endif() 52 | 53 | endfunction(check_mpi_version) 54 | -------------------------------------------------------------------------------- /cmake/compilers.cmake: -------------------------------------------------------------------------------- 1 | include(CheckSourceCompiles) 2 | 3 | # --- not all platforms have ieee_arithmetic e.g. aarch64 GCC 4 | check_source_compiles(Fortran 5 | "program a 6 | use, intrinsic :: ieee_arithmetic, only : ieee_quiet_nan, ieee_value 7 | real :: NaN 8 | NaN = ieee_value(0., ieee_quiet_nan) 9 | end program" 10 | HAVE_IEEE_ARITH 11 | ) 12 | 13 | # --- C compile flags 14 | if(CMAKE_C_COMPILER_ID MATCHES "Clang|GNU|^Intel") 15 | add_compile_options( 16 | "$<$,$>:-Wextra>" 17 | "$<$,$>:-Wall>" 18 | "$<$:-Werror=implicit-function-declaration>" 19 | ) 20 | elseif(CMAKE_C_COMPILER_ID MATCHES "MSVC") 21 | add_compile_options("$<$:/W3>") 22 | endif() 23 | 24 | if(WIN32) 25 | if(CMAKE_C_COMPILER_ID MATCHES "^Intel|MSVC") 26 | add_compile_options($<$,$>:/Od>) 27 | endif() 28 | elseif(CMAKE_C_COMPILER_ID MATCHES "^Intel") 29 | add_compile_options($<$,$>:-O0>) 30 | endif() 31 | 32 | # --- Fortran compile flags 33 | if(CMAKE_Fortran_COMPILER_ID MATCHES "^Intel") 34 | 35 | add_compile_options( 36 | "$<$,$>:-warn>" 37 | "$<$,$>:-traceback;-check;-debug>" 38 | ) 39 | 40 | if(WIN32) 41 | add_compile_options($<$,$>:/Od>) 42 | else() 43 | add_compile_options($<$,$>:-O0>) 44 | endif() 45 | 46 | elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") 47 | 48 | add_compile_options( 49 | "$<$,$>:-Wall>" 50 | "$<$:-fimplicit-none;-Wno-maybe-uninitialized>" 51 | "$<$,$>:-Wextra;-fcheck=all;-Werror=array-bounds>" 52 | "$<$,$>:-fno-backtrace>" 53 | ) 54 | 55 | endif() 56 | 57 | # --- code coverage 58 | if(h5fortran_COVERAGE) 59 | include(${CMAKE_CURRENT_LIST_DIR}/Modules/CodeCoverage.cmake) 60 | append_coverage_compiler_flags() 61 | set(COVERAGE_EXCLUDES ${PROJECT_SOURCE_DIR}/test) 62 | endif() 63 | 64 | # --- clang-tidy 65 | if(tidy AND h5fortran_IS_TOP_LEVEL) 66 | find_program(CLANG_TIDY_EXE NAMES "clang-tidy" REQUIRED) 67 | set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_EXE}) 68 | set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE}) 69 | endif() 70 | -------------------------------------------------------------------------------- /cmake/config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(CMakeFindDependencyMacro) 4 | 5 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) 6 | 7 | find_dependency(HDF5 COMPONENTS Fortran) 8 | 9 | include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake) 10 | 11 | check_required_components(@PROJECT_NAME@) 12 | -------------------------------------------------------------------------------- /cmake/h5fortran.cmake: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | 3 | set(h5fortran_INCLUDE_DIRS ${CMAKE_INSTALL_FULL_INCLUDEDIR}) 4 | 5 | if(BUILD_SHARED_LIBS) 6 | if(WIN32) 7 | set(h5fortran_LIBRARIES ${CMAKE_INSTALL_FULL_BINDIR}/${CMAKE_SHARED_LIBRARY_PREFIX}h5fortran${CMAKE_SHARED_LIBRARY_SUFFIX}) 8 | else() 9 | set(h5fortran_LIBRARIES ${CMAKE_INSTALL_FULL_LIBDIR}/${CMAKE_SHARED_LIBRARY_PREFIX}h5fortran${CMAKE_SHARED_LIBRARY_SUFFIX}) 10 | endif() 11 | else() 12 | set(h5fortran_LIBRARIES ${CMAKE_INSTALL_FULL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}h5fortran${CMAKE_STATIC_LIBRARY_SUFFIX}) 13 | endif() 14 | 15 | set(h5fortran_cmake_args 16 | -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} 17 | -DCMAKE_PREFIX_PATH:PATH=${CMAKE_INSTALL_PREFIX} 18 | -DBUILD_SHARED_LIBS:BOOL=${BUILD_SHARED_LIBS} 19 | -DCMAKE_BUILD_TYPE=Release 20 | -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} 21 | -DCMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER} 22 | -Dh5fortran_BUILD_TESTING:BOOL=false 23 | ) 24 | if(HDF5_ROOT) 25 | list(APPEND h5fortran_cmake_args -DHDF5_ROOT:PATH=${HDF5_ROOT}) 26 | endif() 27 | 28 | ExternalProject_Add(H5FORTRAN 29 | SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/.. 30 | CMAKE_ARGS ${h5fortran_cmake_args} 31 | BUILD_BYPRODUCTS ${h5fortran_LIBRARIES} 32 | CONFIGURE_HANDLED_BY_BUILD ON 33 | DEPENDS HDF5::HDF5 34 | ) 35 | 36 | file(MAKE_DIRECTORY ${h5fortran_INCLUDE_DIRS}) 37 | 38 | add_library(h5fortran::h5fortran INTERFACE IMPORTED GLOBAL) 39 | target_include_directories(h5fortran::h5fortran INTERFACE ${h5fortran_INCLUDE_DIRS}) 40 | target_link_libraries(h5fortran::h5fortran INTERFACE ${h5fortran_LIBRARIES} HDF5::HDF5) 41 | 42 | # race condition for linking without this 43 | add_dependencies(h5fortran::h5fortran H5FORTRAN) 44 | -------------------------------------------------------------------------------- /cmake/hdf5.cmake: -------------------------------------------------------------------------------- 1 | # builds HDF5 library from scratch 2 | # note: the use of "lib" vs. CMAKE_*_LIBRARY_PREFIX is deliberate based on HDF5 3 | # across Intel Fortran on Windows (MSVC-like) vs. Gfortran on Windows vs. Linux. 4 | include(GNUInstallDirs) 5 | include(ExternalProject) 6 | 7 | if(NOT DEFINED hdf5_key) 8 | if(LINUX) 9 | set(hdf5_key hdf5-1.10) 10 | else() 11 | set(hdf5_key hdf5-1.14) 12 | endif() 13 | endif() 14 | 15 | if(hdf5_parallel) 16 | find_package(MPI REQUIRED COMPONENTS C) 17 | endif() 18 | 19 | # pass MPI hints to HDF5 20 | if(NOT MPI_ROOT AND DEFINED ENV{MPI_ROOT}) 21 | set(MPI_ROOT $ENV{MPI_ROOT}) 22 | endif() 23 | 24 | set(HDF5_LIBRARIES) 25 | foreach(_name IN ITEMS hdf5_hl_fortran hdf5_hl_f90cstub hdf5_fortran hdf5_f90cstub hdf5_hl hdf5) 26 | # need ${CMAKE_INSTALL_PREFIX}/lib as HDF5 doesn't use GNUInstallDirs 27 | if(BUILD_SHARED_LIBS) 28 | if(WIN32) 29 | list(APPEND HDF5_LIBRARIES ${CMAKE_INSTALL_FULL_BINDIR}/lib${_name}${CMAKE_SHARED_LIBRARY_SUFFIX}) 30 | else() 31 | list(APPEND HDF5_LIBRARIES ${CMAKE_INSTALL_PREFIX}/lib/lib${_name}${CMAKE_SHARED_LIBRARY_SUFFIX}) 32 | endif() 33 | else() 34 | list(APPEND HDF5_LIBRARIES ${CMAKE_INSTALL_PREFIX}/lib/lib${_name}${CMAKE_STATIC_LIBRARY_SUFFIX}) 35 | endif() 36 | endforeach() 37 | 38 | set(HDF5_INCLUDE_DIRS ${CMAKE_INSTALL_FULL_INCLUDEDIR}) 39 | 40 | file(READ ${CMAKE_CURRENT_LIST_DIR}/libraries.json json) 41 | 42 | # --- Zlib 43 | if(build_zlib) 44 | set(zlib_dep DEPENDS ZLIB) 45 | else() 46 | set(zlib_dep) 47 | endif() 48 | 49 | if(TARGET ZLIB::ZLIB) 50 | add_custom_target(ZLIB) 51 | elseif(h5fortran_find AND NOT build_zlib) 52 | find_package(ZLIB) 53 | endif() 54 | 55 | if(NOT TARGET ZLIB::ZLIB) 56 | include(${CMAKE_CURRENT_LIST_DIR}/zlib.cmake) 57 | endif() 58 | 59 | # --- HDF5 60 | # https://forum.hdfgroup.org/t/issues-when-using-hdf5-as-a-git-submodule-and-using-cmake-with-add-subdirectory/7189/2 61 | 62 | set(hdf5_cmake_args 63 | -DHDF5_ENABLE_Z_LIB_SUPPORT:BOOL=ON 64 | -DZLIB_USE_EXTERNAL:BOOL=OFF 65 | -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} 66 | -DCMAKE_MODULE_PATH:PATH=${CMAKE_MODULE_PATH} 67 | -DHDF5_GENERATE_HEADERS:BOOL=false 68 | -DHDF5_DISABLE_COMPILER_WARNINGS:BOOL=true 69 | -DBUILD_STATIC_LIBS:BOOL=$> 70 | -DBUILD_SHARED_LIBS:BOOL=${BUILD_SHARED_LIBS} 71 | -DCMAKE_BUILD_TYPE=Release 72 | -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} 73 | -DCMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER} 74 | -DHDF5_BUILD_FORTRAN:BOOL=true 75 | -DHDF5_BUILD_CPP_LIB:BOOL=false 76 | -DBUILD_TESTING:BOOL=false 77 | -DHDF5_BUILD_EXAMPLES:BOOL=false 78 | -DHDF5_BUILD_TOOLS:BOOL=true 79 | -DHDF5_ENABLE_PARALLEL:BOOL=$ 80 | -DHDF5_BUILD_PARALLEL_TOOLS:BOOL=$ 81 | ) 82 | 83 | #-DHDF5_USE_GNU_DIRS:BOOL=ON # not yet, new for 1.14 84 | 85 | if(MPI_ROOT) 86 | list(APPEND hdf5_cmake_args -DMPI_ROOT:PATH=${MPI_ROOT}) 87 | endif() 88 | 89 | if(NOT hdf5_url) 90 | string(JSON hdf5_url GET ${json} ${hdf5_key}) 91 | endif() 92 | 93 | # Get HDF5 version from underscore-separated version in URL 94 | 95 | if(hdf5_url MATCHES "hdf5_([0-9]+\.[0-9]+\.[0-9]+)\.") 96 | set(HDF5_VERSION "${CMAKE_MATCH_1}") 97 | elseif(hdf5_url MATCHES "hdf5-([0-9]+\_[0-9]+\_[0-9]+)") 98 | string(REPLACE "_" "." HDF5_VERSION "${CMAKE_MATCH_1}") 99 | else() 100 | message(FATAL_ERROR "Could not determine HDF5 version from URL: ${hdf5_url}") 101 | endif() 102 | 103 | message(STATUS "Building HDF5 version ${HDF5_VERSION}") 104 | 105 | ExternalProject_Add(HDF5 106 | URL ${hdf5_url} 107 | CMAKE_ARGS ${hdf5_cmake_args} 108 | BUILD_BYPRODUCTS ${HDF5_LIBRARIES} 109 | ${zlib_dep} 110 | CONFIGURE_HANDLED_BY_BUILD ON 111 | USES_TERMINAL_DOWNLOAD true 112 | USES_TERMINAL_UPDATE true 113 | USES_TERMINAL_PATCH true 114 | USES_TERMINAL_CONFIGURE true 115 | USES_TERMINAL_BUILD true 116 | USES_TERMINAL_INSTALL true 117 | USES_TERMINAL_TEST true 118 | ) 119 | 120 | # --- imported target 121 | 122 | file(MAKE_DIRECTORY ${HDF5_INCLUDE_DIRS}) 123 | # avoid race condition 124 | 125 | # this GLOBAL is required to be visible to parent projects 126 | add_library(HDF5::HDF5 INTERFACE IMPORTED GLOBAL) 127 | target_include_directories(HDF5::HDF5 INTERFACE "${HDF5_INCLUDE_DIRS}") 128 | target_link_libraries(HDF5::HDF5 INTERFACE "${HDF5_LIBRARIES}") 129 | 130 | add_dependencies(HDF5::HDF5 HDF5) 131 | 132 | # --- HDF5 parallel compression support 133 | # this could be improved by making it an ExternalProject post-build step instead of assumptions made here 134 | if(hdf5_parallel) 135 | if(MPI_VERSION VERSION_GREATER_EQUAL 3) 136 | message(STATUS "Building HDF5-MPI: MPI-3 available, assuming HDF5 parallel compression enabled") 137 | set(hdf5_parallel_compression ".true." CACHE STRING "configure variable for HDF5 parallel compression") 138 | else() 139 | message(STATUS "Building HDF5-MPI: MPI-3 NOT available => HDF5 parallel compression disabled") 140 | set(hdf5_parallel_compression ".false." CACHE STRING "configure variable for HDF5 parallel compression: MPI < 3") 141 | endif() 142 | endif(hdf5_parallel) 143 | 144 | # --- external deps 145 | find_package(Threads) 146 | 147 | target_link_libraries(HDF5::HDF5 INTERFACE 148 | ZLIB::ZLIB 149 | ${CMAKE_THREAD_LIBS_INIT} 150 | ${CMAKE_DL_LIBS} 151 | $<$:m> 152 | ) 153 | # libdl and libm are needed on some systems 154 | -------------------------------------------------------------------------------- /cmake/install.cmake: -------------------------------------------------------------------------------- 1 | # --- BOILERPLATE: install / packaging 2 | 3 | include(CMakePackageConfigHelpers) 4 | 5 | configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/config.cmake.in 6 | ${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}Config.cmake 7 | INSTALL_DESTINATION cmake 8 | ) 9 | 10 | write_basic_package_version_file( 11 | ${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}ConfigVersion.cmake 12 | COMPATIBILITY SameMajorVersion 13 | ) 14 | 15 | install(EXPORT ${PROJECT_NAME}-targets 16 | NAMESPACE ${PROJECT_NAME}:: 17 | DESTINATION cmake 18 | ) 19 | 20 | install(FILES 21 | ${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}Config.cmake 22 | ${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}ConfigVersion.cmake 23 | DESTINATION cmake 24 | ) 25 | 26 | # --- CPack 27 | 28 | set(CPACK_GENERATOR "TBZ2") 29 | set(CPACK_SOURCE_GENERATOR "TBZ2") 30 | set(CPACK_PACKAGE_VENDOR "Michael Hirsch") 31 | set(CPACK_PACKAGE_CONTACT "Michael Hirsch") 32 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libhdf5-dev (>=1.10)") 33 | set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") 34 | set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") 35 | 36 | # not .gitignore as its regex syntax is more advanced than CMake 37 | set(CPACK_SOURCE_IGNORE_FILES .git/ .github/ .vscode/ .mypy_cache/ _CPack_Packages/ 38 | ${CMAKE_BINARY_DIR}/ ${PROJECT_BINARY_DIR}/ 39 | .archive/ concepts/ paper/ 40 | ) 41 | 42 | install(FILES ${CPACK_RESOURCE_FILE_README} ${CPACK_RESOURCE_FILE_LICENSE} 43 | DESTINATION share/docs/${PROJECT_NAME} 44 | ) 45 | 46 | include(CPack) 47 | -------------------------------------------------------------------------------- /cmake/libraries.json: -------------------------------------------------------------------------------- 1 | { 2 | "zlib1": "https://zlib.net/zlib-1.3.1.tar.gz", 3 | "zlib2": "https://github.com/zlib-ng/zlib-ng/archive/refs/tags/2.2.4.tar.gz", 4 | "hdf5-1.10": "https://github.com/HDFGroup/hdf5/archive/refs/tags/hdf5-1_10_11.tar.gz", 5 | "hdf5-1.14": "https://github.com/HDFGroup/hdf5/archive/refs/tags/hdf5_1.14.6.tar.gz" 6 | } 7 | -------------------------------------------------------------------------------- /cmake/pkgconf.cmake: -------------------------------------------------------------------------------- 1 | # --- generate pkg-config .pc 2 | 3 | set(pc_requires "hdf5 >= 1.8.7") 4 | 5 | set(pc_filename h5fortran.pc) 6 | configure_file(${CMAKE_CURRENT_LIST_DIR}/pkgconf.pc.in ${pc_filename} @ONLY) 7 | 8 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${pc_filename} DESTINATION pkgconfig) 9 | -------------------------------------------------------------------------------- /cmake/pkgconf.pc.in: -------------------------------------------------------------------------------- 1 | prefix="@CMAKE_INSTALL_PREFIX@" 2 | exec_prefix="${prefix}" 3 | libdir="${prefix}/lib" 4 | includedir="${prefix}/include" 5 | 6 | Name: @PROJECT_NAME@ 7 | Description: @CMAKE_PROJECT_DESCRIPTION@ 8 | URL: @CMAKE_PROJECT_HOMEPAGE_URL@ 9 | Version: @PROJECT_VERSION@ 10 | Requires: @pc_requires@ 11 | Cflags: -I"${includedir}" 12 | Libs: -L"${libdir}" -l@PROJECT_NAME@ 13 | Libs.private: -L"${libdir}" -l@PROJECT_NAME@ 14 | -------------------------------------------------------------------------------- /cmake/zlib.cmake: -------------------------------------------------------------------------------- 1 | # build Zlib to ensure compatibility. 2 | # We use Zlib 2.x for speed and robustness. 3 | include(GNUInstallDirs) 4 | include(ExternalProject) 5 | 6 | if(NOT DEFINED zlib_key) 7 | set(zlib_key zlib2) 8 | endif() 9 | 10 | if(NOT zlib_url) 11 | string(JSON zlib_url GET ${json} ${zlib_key}) 12 | endif() 13 | 14 | set(ZLIB_INCLUDE_DIRS ${CMAKE_INSTALL_FULL_INCLUDEDIR}) 15 | 16 | if(BUILD_SHARED_LIBS) 17 | if(WIN32) 18 | set(ZLIB_LIBRARIES ${CMAKE_INSTALL_FULL_BINDIR}/${CMAKE_SHARED_LIBRARY_PREFIX}zlib1${CMAKE_SHARED_LIBRARY_SUFFIX}) 19 | else() 20 | set(ZLIB_LIBRARIES ${CMAKE_INSTALL_FULL_LIBDIR}/${CMAKE_SHARED_LIBRARY_PREFIX}z${CMAKE_SHARED_LIBRARY_SUFFIX}) 21 | endif() 22 | else() 23 | if(MSVC OR (WIN32 AND zlib_key STREQUAL "zlib1")) 24 | set(ZLIB_LIBRARIES ${CMAKE_INSTALL_FULL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}zlibstatic${CMAKE_STATIC_LIBRARY_SUFFIX}) 25 | else() 26 | set(ZLIB_LIBRARIES ${CMAKE_INSTALL_FULL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}z${CMAKE_STATIC_LIBRARY_SUFFIX}) 27 | endif() 28 | endif() 29 | 30 | 31 | set(zlib_cmake_args 32 | -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} 33 | -DBUILD_SHARED_LIBS:BOOL=${BUILD_SHARED_LIBS} 34 | -DCMAKE_BUILD_TYPE=Release 35 | -DZLIB_COMPAT:BOOL=on 36 | -DZLIB_ENABLE_TESTS:BOOL=off 37 | -DZLIBNG_ENABLE_TESTS:BOOL=off 38 | -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON 39 | -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} 40 | ) 41 | # NetCDF 4.9/4.6 needs fPIC 42 | 43 | ExternalProject_Add(ZLIB 44 | URL ${zlib_url} 45 | CMAKE_ARGS ${zlib_cmake_args} 46 | BUILD_BYPRODUCTS ${ZLIB_LIBRARIES} 47 | CONFIGURE_HANDLED_BY_BUILD ON 48 | USES_TERMINAL_DOWNLOAD true 49 | USES_TERMINAL_UPDATE true 50 | USES_TERMINAL_PATCH true 51 | USES_TERMINAL_CONFIGURE true 52 | USES_TERMINAL_BUILD true 53 | USES_TERMINAL_INSTALL true 54 | USES_TERMINAL_TEST true 55 | ) 56 | 57 | # --- imported target 58 | 59 | file(MAKE_DIRECTORY ${ZLIB_INCLUDE_DIRS}) 60 | # avoid race condition 61 | 62 | # for visibility in parent projects 63 | add_library(ZLIB::ZLIB INTERFACE IMPORTED GLOBAL) 64 | add_dependencies(ZLIB::ZLIB ZLIB) # to avoid include directory race condition 65 | target_link_libraries(ZLIB::ZLIB INTERFACE ${ZLIB_LIBRARIES}) 66 | target_include_directories(ZLIB::ZLIB INTERFACE ${ZLIB_INCLUDE_DIRS}) 67 | -------------------------------------------------------------------------------- /codemeta.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://doi.org/10.5063/schema/codemeta-2.0", 3 | "@type": "SoftwareSourceCode", 4 | "codeRepository": "https://github.com/geospace-code/h5fortran", 5 | "contIntegration": "https://github.com/geospace-code/h5fortran/actions", 6 | "dateModified": "2022-07-20", 7 | "downloadUrl": "https://github.com/geospace-code/h5fortran/releases", 8 | "issueTracker": "https://github.com/geospace-code/h5fortran/issues", 9 | "name": "h5fortran", 10 | "identifier": "10.5281/zenodo.3757269", 11 | "description": "Lightweight object-oriented HDF5 Fortran interface", 12 | "applicationCategory": "file I/O", 13 | "developmentStatus": "active", 14 | "funder": { 15 | "@type": "Organization", 16 | "name": "NASA" 17 | }, 18 | "keywords": [ 19 | "hdf5", 20 | "object-oriented" 21 | ], 22 | "programmingLanguage": [ 23 | "Fortran" 24 | ], 25 | "author": [ 26 | { 27 | "@type": "Person", 28 | "@id": "https://orcid.org/0000-0002-1637-6526", 29 | "givenName": "Michael", 30 | "familyName": "Hirsch" 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, CMake 2 | 3 | 4 | class H5fortranConan(ConanFile): 5 | name = "h5fortran" 6 | version = "3.4.1" 7 | license = "MIT" # noqa: A003 8 | url = "https://github.com/geospace-code/h5fortran" 9 | requires = "hdf5/[>=1.8.7]" 10 | build_policy = "missing" 11 | description = "Easy, thin, robust polymorphic Fortran HDF5 interface" 12 | settings = "os", "compiler", "build_type", "arch" 13 | options = {"shared": [True, False]} 14 | default_options = {"shared": False} 15 | generators = "cmake" 16 | 17 | def source(self): 18 | self.run("git clone https://github.com/geospace-code/h5fortran.git") 19 | 20 | def build(self): 21 | cmake = CMake(self) 22 | args = f'-DCMAKE_INSTALL_PREFIX="{self.package_folder}"' 23 | print(cmake.command_line) 24 | self.run(f"cmake -S{self.source_folder} {cmake.command_line} {args}") 25 | self.run("cmake --install .") 26 | -------------------------------------------------------------------------------- /concepts/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | project(HDF5concepts 4 | LANGUAGES C Fortran 5 | ) 6 | 7 | set_directory_properties(PROPERTIES LABELS concept) 8 | 9 | cmake_path(SET CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/Modules) 10 | 11 | find_package(HDF5 COMPONENTS Fortran REQUIRED) 12 | 13 | add_executable(compact_dataset h5compact.f90) 14 | target_link_libraries(compact_dataset PRIVATE HDF5::HDF5) 15 | add_test(NAME compact COMMAND compact_dataset) 16 | 17 | add_executable(int64_write_read int64.f90) 18 | target_link_libraries(int64_write_read PRIVATE HDF5::HDF5) 19 | add_test(NAME int64_write_read COMMAND int64_write_read) 20 | 21 | add_executable(proto_file_image file_image.f90) 22 | target_link_libraries(proto_file_image PRIVATE HDF5::HDF5) 23 | add_test(NAME file_image COMMAND proto_file_image) 24 | 25 | add_executable(virtual_dataset virtual_dataset.f90) 26 | target_link_libraries(virtual_dataset PRIVATE HDF5::HDF5) 27 | add_test(NAME virtual_dataset COMMAND virtual_dataset) 28 | -------------------------------------------------------------------------------- /concepts/file_image.f90: -------------------------------------------------------------------------------- 1 | module file_image 2 | !! HDF5 file images are an in-RAM fast HDF5 virtual file. 3 | !! there are many caveats to their use and they might never be included in h5fortran 4 | 5 | use, intrinsic :: iso_fortran_env, only : stderr=>error_unit, real32, real64, int32 6 | use, intrinsic :: iso_c_binding, only : c_ptr, c_loc, c_sizeof, c_associated 7 | use hdf5, only : h5pcreate_f, h5pclose_f, & 8 | h5pset_file_image_f, h5pget_file_image_f, & 9 | H5P_FILE_ACCESS_F, hid_t, size_t, & 10 | h5open_f, h5close_f 11 | 12 | ! use h5lt, only : H5LTopen_file_image_f 13 | !! h5ltopen_file_image_f does NOT exist yet as of HDF5 1.12.0. The API was specified for HDF5 1.8.9, 14 | !! but never implemented (yet). 15 | 16 | implicit none (type, external) 17 | private 18 | public :: hid_t, create_file_image, close_file_image, write_file_image, read_file_image 19 | 20 | contains 21 | 22 | subroutine create_file_image(fapl_id) 23 | 24 | integer(hid_t), intent(out) :: fapl_id 25 | 26 | integer(size_t) :: b_size 27 | integer :: ier 28 | type(C_PTR) :: f_ptr(1:1) 29 | 30 | !> open HDF5 library 31 | call h5open_f(ier) 32 | if(ier/=0) error stop 'could not open HDF5 library' 33 | 34 | !> create file property ID "fapl_id" 35 | call h5pcreate_f(H5P_FILE_ACCESS_F, fapl_id, ier) 36 | if (ier/=0) error stop 'h5pcreate_f' 37 | 38 | !> create and check empty buffer 39 | call h5pget_file_image_f(fapl_id, f_ptr, b_size, ier) 40 | if (ier/=0) error stop 'h5pget_file_image: create buffer' 41 | if (b_size /= 0) error stop 'expected empty buffer' 42 | 43 | end subroutine create_file_image 44 | 45 | 46 | subroutine write_file_image(data, fapl_id) 47 | !! polymorphic in future implementation 48 | 49 | integer(hid_t), intent(in) :: fapl_id 50 | integer, intent(in) :: data(:) 51 | 52 | integer, dimension(1:size(data)), target :: buffer 53 | type(c_ptr) :: f_ptr 54 | integer :: ier 55 | 56 | !> write data to HDF5 file image 57 | buffer = data 58 | f_ptr = c_loc(buffer(1)) 59 | call h5pset_file_image_f(fapl_id, f_ptr, c_sizeof(buffer), ier) 60 | if(ier/=0) error stop 'h5pset_file_image_f: set buffer size' 61 | 62 | end subroutine write_file_image 63 | 64 | 65 | subroutine read_file_image(buffer, fapl_id) 66 | 67 | integer(hid_t), intent(in) :: fapl_id 68 | integer, dimension(:), intent(out), target :: buffer 69 | 70 | type(c_ptr) :: f_ptr(1:size(buffer)) 71 | integer(size_t) :: b_size 72 | integer :: i, buffer_size 73 | 74 | buffer_size = size(buffer) * storage_size(int32) / 8 75 | 76 | do i = 1, size(buffer) 77 | f_ptr(i) = C_LOC(buffer(i)) 78 | enddo 79 | 80 | call h5pget_file_image_f(fapl_id, f_ptr, b_size, i) 81 | if(i/=0) error stop 'read_file_image: could not read buffer' 82 | if(b_size /= buffer_size) then 83 | write(stderr, *) "variable size: ", buffer_size, " /= file_image size: ",b_size 84 | error stop 'read_file_image: variable size /= file_image size' 85 | endif 86 | 87 | end subroutine read_file_image 88 | 89 | 90 | subroutine close_file_image(fapl_id) 91 | 92 | integer(hid_t), intent(in) :: fapl_id 93 | integer :: i 94 | 95 | !> close file image and library 96 | call h5pclose_f(fapl_id, i) 97 | if (i/=0) error stop 'problem closing file image' 98 | 99 | call h5close_f(i) 100 | if (i/=0) error stop 'problem closing HDF5 library' 101 | 102 | 103 | end subroutine close_file_image 104 | 105 | 106 | end module file_image 107 | 108 | 109 | program test_file_image 110 | 111 | use, intrinsic :: iso_fortran_env, only : stderr=>error_unit 112 | use file_image, only : create_file_image, write_file_image, read_file_image, close_file_image, hid_t 113 | 114 | implicit none (type, external) 115 | 116 | integer(hid_t) :: fapl_id 117 | 118 | integer, dimension(1:8) :: in, out 119 | !! arbitrary user data 120 | 121 | in = [1,3,7,13,129, -13, 23, 42] 122 | 123 | call create_file_image(fapl_id) 124 | 125 | call write_file_image(in, fapl_id) 126 | 127 | call read_file_image(out, fapl_id) 128 | 129 | call close_file_image(fapl_id) 130 | 131 | if (any(in /= out)) then 132 | write(stderr,*) 'in=',in 133 | write(stderr,*) 'out=',out 134 | error stop 'output data does not match input data' 135 | endif 136 | 137 | print *, 'OK: file image prototype' 138 | 139 | end program 140 | -------------------------------------------------------------------------------- /concepts/file_image.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Demonstrates low level h5py HDF5 file_image API 4 | 5 | from h5py source code. 6 | """ 7 | 8 | from binascii import a2b_base64 9 | from zlib import decompress 10 | 11 | import h5py 12 | from h5py import h5f, h5p 13 | 14 | compressed_image = "eJzr9HBx4+WS4mIAAQ4OBhYGAQZk8B8KKjhQ+TD5BCjNCKU7oPQKJpg4I1hOAiouCDUfXV1IkKsrSPV/NACzx4AFQnMwjIKRCDxcHQNAdASUD0ulJ5hQ1ZWkFpeAaFh69KDQXkYGNohZjDA+JCUzMkIEmKHqELQAWKkAByytOoBJViAPJM7ExATWyAE0B8RgZkyAJmlYDoEAIahukJoNU6+HMTA0UOgT6oBgP38XUI6G5UMFZrzKR8EoGAUjGMDKYVgxDSsuAHcfMK8=" 15 | 16 | 17 | def test_file(tmp_path): 18 | image = decompress(a2b_base64(compressed_image)) 19 | 20 | # FAPL: File access property list 21 | fapl = h5p.create(h5py.h5p.FILE_ACCESS) 22 | fapl.set_fapl_core() 23 | fapl.set_file_image(image) 24 | 25 | file = tmp_path / "disk.h5" 26 | fid = h5f.open(bytes(file), h5py.h5f.ACC_RDONLY, fapl=fapl) 27 | with h5py.File(fid) as f: 28 | assert (f["test"][:] == [1, 2, 3]).all() 29 | assert f["test"].dtype == "int64" 30 | 31 | 32 | def test_image(): 33 | image = decompress(a2b_base64(compressed_image)) 34 | 35 | fid = h5f.open_file_image(image) 36 | 37 | with h5py.File(fid) as f: 38 | assert (f["test"][:] == [1, 2, 3]).all() 39 | assert f["test"].dtype == "int64" 40 | -------------------------------------------------------------------------------- /concepts/h5ex_t_string_F03.f90: -------------------------------------------------------------------------------- 1 | !************************************************************ 2 | ! 3 | ! This example shows how to read and write fixed string size 4 | ! datatypes to a dataset. The program first writes strings to a 5 | ! dataset with a dataspace of DIM0, then closes the file. 6 | ! Next, it reopens the file, reads back the data, and 7 | ! outputs it to the screen. 8 | ! 9 | ! This file is intended for use with HDF5 Library version 1.8 10 | ! with --enable-fortran2003 11 | ! 12 | ! ************************************************************/ 13 | PROGRAM main 14 | 15 | USE hdf5 16 | USE ISO_C_BINDING 17 | 18 | IMPLICIT NONE 19 | 20 | CHARACTER(LEN=20), PARAMETER :: filename = "h5ex_t_string_F03.h5" 21 | CHARACTER(LEN=3) , PARAMETER :: dataset = "DS1" 22 | INTEGER , PARAMETER :: dim0 = 4 23 | INTEGER(SIZE_T) , PARAMETER :: sdim = 8 24 | 25 | INTEGER(HID_T) :: file, filetype, memtype, space, dset ! Handles 26 | INTEGER :: hdferr 27 | 28 | INTEGER(HSIZE_T), DIMENSION(1:1) :: dims = (/dim0/) 29 | INTEGER(HSIZE_T), DIMENSION(1:1) :: maxdims 30 | 31 | CHARACTER(LEN=sdim), DIMENSION(1:dim0), TARGET :: & 32 | wdata = (/"Parting", "is such", "sweet ", "sorrow."/) 33 | CHARACTER(LEN=sdim), DIMENSION(:), ALLOCATABLE, TARGET :: rdata 34 | INTEGER :: i, len 35 | INTEGER(SIZE_T) :: size 36 | TYPE(C_PTR) :: f_ptr 37 | ! 38 | ! Initialize FORTRAN interface. 39 | ! 40 | CALL h5open_f(hdferr) 41 | ! 42 | ! Create a new file using the default properties. 43 | ! 44 | CALL h5fcreate_f(filename, H5F_ACC_TRUNC_F, file, hdferr) 45 | ! 46 | ! Create file datatypes. For this example we will save 47 | ! the strings as FORTRAN strings 48 | ! 49 | CALL H5Tcopy_f(H5T_FORTRAN_S1, filetype, hdferr) 50 | CALL H5Tset_size_f(filetype, sdim, hdferr) 51 | ! 52 | ! Create dataspace. 53 | ! 54 | CALL h5screate_simple_f(1, dims, space, hdferr) 55 | ! 56 | ! Create the dataset and write the string data to it. 57 | ! 58 | CALL h5dcreate_f(file, dataset, filetype, space, dset, hdferr) 59 | 60 | f_ptr = C_LOC(wdata(1)(1:1)) 61 | CALL H5Dwrite_f(dset, filetype, f_ptr, hdferr); 62 | ! 63 | ! Close and release resources. 64 | ! 65 | CALL h5dclose_f(dset , hdferr) 66 | CALL h5sclose_f(space, hdferr) 67 | CALL H5Tclose_f(filetype, hdferr) 68 | CALL h5fclose_f(file , hdferr) 69 | ! 70 | ! Now we begin the read section of this example. 71 | ! 72 | ! Open file and dataset. 73 | ! 74 | CALL h5fopen_f(filename, H5F_ACC_RDONLY_F, file, hdferr) 75 | CALL h5dopen_f(file, dataset, dset, hdferr) 76 | ! 77 | ! Get the datatype and its size. 78 | ! 79 | CALL H5Dget_type_f(dset, filetype, hdferr) 80 | CALL H5Tget_size_f(filetype, size, hdferr) 81 | 82 | ! Make sure the declared length is large enough 83 | IF(size.GT.sdim)THEN 84 | PRINT*,'ERROR: Character LEN is to small' 85 | STOP 86 | ENDIF 87 | ! 88 | ! Get dataspace. 89 | ! 90 | CALL H5Dget_space_f(dset, space, hdferr) 91 | CALL H5Sget_simple_extent_dims_f(space, dims, maxdims, hdferr) 92 | 93 | ALLOCATE(rdata(1:dims(1))) 94 | ! 95 | ! Create the memory datatype. 96 | ! 97 | CALL H5Tcopy_f(H5T_FORTRAN_S1, memtype, hdferr) 98 | CALL H5Tset_size_f(memtype, sdim, hdferr) 99 | ! 100 | ! Read the data. 101 | ! 102 | f_ptr = C_LOC(rdata(1)(1:1)) 103 | CALL H5Dread_f(dset, memtype, f_ptr, hdferr, space) 104 | ! 105 | ! Output the data to the screen. 106 | ! 107 | DO i = 1, dims(1) 108 | WRITE(*,'(A,"(",I0,"): ", A)') DATASET, i, TRIM(rdata(i)) 109 | END DO 110 | ! 111 | ! Close and release resources. 112 | ! 113 | CALL H5Dclose_f(dset, hdferr) 114 | CALL H5Sclose_f(space, hdferr) 115 | CALL H5Tclose_f(memtype, hdferr) 116 | CALL H5Fclose_f(file, hdferr) 117 | 118 | DEALLOCATE(rdata) 119 | 120 | END PROGRAM main 121 | -------------------------------------------------------------------------------- /concepts/int64.f90: -------------------------------------------------------------------------------- 1 | program main_int64 2 | !! hdf5 fortran interface can also read/write int64, but it's not in the h5lt interface 3 | use hdf5 4 | use, intrinsic :: iso_fortran_env, only : int64 5 | 6 | implicit none (type, external) 7 | 8 | integer :: ierr 9 | 10 | character(:), allocatable :: filename 11 | 12 | filename = "int64.h5" 13 | 14 | !> initialize fortran interface and file 15 | call h5open_f(ierr) 16 | 17 | call write_data(filename) 18 | 19 | call verify_data(filename) 20 | 21 | call h5close_f(ierr) 22 | 23 | 24 | contains 25 | 26 | 27 | subroutine verify_data(filename) 28 | 29 | character(*), intent(in) :: filename 30 | 31 | integer(hid_t) :: fid 32 | integer :: ierr 33 | 34 | call h5fopen_f(filename, h5f_acc_rdonly_f, fid, ierr) 35 | 36 | if(read_int64(fid, "/example") /= 12345) error stop "example not match" 37 | if(read_int64(fid, "/big") /= huge(0_int64)) error stop "huge not match" 38 | 39 | call h5fclose_f(fid, ierr) 40 | 41 | end subroutine verify_data 42 | 43 | 44 | integer(int64) function read_int64(fid, name) result(i) 45 | 46 | integer(hid_t), intent(in) :: fid 47 | character(*), intent(in) :: name 48 | 49 | integer(hid_t) :: dset_id 50 | 51 | call h5dopen_f(fid, name, dset_id, ierr) 52 | if (ierr/=0) error stop "dataset not opened: " // name // " in file: " // filename 53 | 54 | CALL h5dread_f(dset_id, h5kind_to_type(int64, h5_integer_kind), i, shape(i, hsize_t), ierr) 55 | if (ierr/=0) error stop "dataset not read: " // name // " in file: " // filename 56 | 57 | call h5dclose_f(dset_id, ierr) 58 | 59 | end function read_int64 60 | 61 | 62 | 63 | subroutine write_data(filename) 64 | 65 | character(*), intent(in) :: filename 66 | 67 | integer(hid_t) :: fid 68 | integer :: ierr 69 | 70 | call h5fcreate_f(filename, h5f_acc_trunc_f, fid, ierr) 71 | 72 | call write_int64(fid, 12345_int64, "example") 73 | call write_int64(fid, huge(0_int64), "big") !< 9223372036854775807 74 | 75 | call h5fclose_f(fid, ierr) 76 | 77 | 78 | end subroutine write_data 79 | 80 | 81 | subroutine write_int64(fid, i, name) 82 | 83 | integer(hid_t) :: fid 84 | integer(int64), intent(in) :: i 85 | character(*), intent(in) :: name 86 | 87 | integer(hid_t) :: h5_kind_int64, filespace_id, dset_id 88 | 89 | h5_kind_int64 = h5kind_to_type(int64, h5_integer_kind) 90 | 91 | !> dataspace 92 | call h5screate_f(h5s_scalar_f, filespace_id, ierr) 93 | 94 | !> create the dataset. 95 | call h5dcreate_f(fid, name, h5_kind_int64, filespace_id, dset_id, ierr) 96 | 97 | !> write data 98 | call h5dwrite_f(dset_id, h5_kind_int64, i, shape(i, hsize_t), ierr) 99 | 100 | !> close handles 101 | call h5dclose_f(dset_id, ierr) 102 | call h5sclose_f(filespace_id, ierr) 103 | 104 | end subroutine write_int64 105 | 106 | end program 107 | -------------------------------------------------------------------------------- /concepts/meson.build: -------------------------------------------------------------------------------- 1 | file_image_exe = executable('test_file_image', 'file_image.f90', 2 | dependencies: hdf5) 3 | test('file_image', file_image_exe, suite:'proto', timeout: 10) 4 | -------------------------------------------------------------------------------- /concepts/reader_nd.f90: -------------------------------------------------------------------------------- 1 | !! conceptual--not tested--will use h5dread_f instead 2 | submodule (h5fortran:read) reader_ND 3 | 4 | implicit none (type, external) 5 | 6 | contains 7 | 8 | module procedure hdf_read_8d 9 | 10 | integer(HSIZE_T) :: dims(rank(A)) 11 | integer :: i(rank(A)) 12 | TYPE(C_PTR) :: f_ptr 13 | 14 | call hdf_setup_read(self, dname, dims, ierr) 15 | if (ierr /= 0) return 16 | 17 | select type (A) 18 | type is (real(real64)) 19 | i = lbound(A) 20 | f_ptr = c_loc(A(i(1),i(2),i(3),i(4),i(5),i(6),i(7),i(8))) 21 | call h5ltread_dataset_f(self%file_id, dname, h5kind_to_type(kind(A),H5_REAL_KIND), f_ptr, ierr) 22 | type is (real(real32)) 23 | i = lbound(A) 24 | f_ptr = c_loc(A(i(1),i(2),i(3),i(4),i(5),i(6),i(7),i(8))) 25 | call h5ltread_dataset_f(self%file_id, dname, h5kind_to_type(kind(A),H5_REAL_KIND), f_ptr, ierr) 26 | type is (integer(int32)) 27 | i = lbound(A) 28 | f_ptr = c_loc(A(i(1),i(2),i(3),i(4),i(5),i(6),i(7),i(8))) 29 | call h5ltread_dataset_f(self%file_id, dname, h5kind_to_type(kind(A),H5_INTEGER_KIND), f_ptr, ierr) 30 | type is (integer(int64)) 31 | i = lbound(A) 32 | f_ptr = c_loc(A(i(1),i(2),i(3),i(4),i(5),i(6),i(7),i(8))) 33 | call h5ltread_dataset_f(self%file_id, dname, h5kind_to_type(kind(A),H5_INTEGER_KIND), f_ptr, ierr) 34 | class default 35 | ierr = 6 36 | end select 37 | 38 | if (ierr /= 0) write(stderr,*) 'ERROR: ' // dname // ' read ' // self%filename 39 | 40 | end procedure hdf_read_8d 41 | 42 | end submodule reader_ND 43 | -------------------------------------------------------------------------------- /concepts/virtual_dataset.f90: -------------------------------------------------------------------------------- 1 | program vds_simple 2 | 3 | use hdf5 4 | 5 | implicit none 6 | 7 | character(*), parameter :: vfn = "demo_vds_simple.h5", src_fn="a.h5", src_name = "/A", v_name = "/V" 8 | ! character(1000) :: src_filename, dsetname 9 | 10 | integer(HSIZE_T), parameter :: dims(2) = [4, 6] !< Source dataset dimensions 11 | 12 | integer(HID_T) :: fid, space, src_space, vspace, dset_id,dcpl 13 | integer :: ier 14 | integer(HSIZE_T) :: vdsdims(2) = dims !< Virtual dataset dimension 15 | integer :: wdata(dims(1), dims(2)) !< Write buffer for source dataset 16 | integer :: rdata(dims(1), dims(2)) !< Read buffer for virtual dataset 17 | integer(SIZE_T) :: i, j 18 | integer(SIZE_T) :: L !< Length of the string; also a return value 19 | 20 | 21 | integer :: layout !< Storage layout 22 | integer :: type 23 | integer(SIZE_T) :: num_map !< Number of mappings 24 | 25 | !> Initialize data. 26 | do i = 1, dims(1) 27 | do j = 1, dims(2) 28 | wdata(i, j) = i + 1 29 | end do 30 | end do 31 | 32 | CALL h5open_f(ier) 33 | 34 | !> Create the source file and the dataset. Write data to the source dataset 35 | !> and close all resources. 36 | 37 | call H5Fcreate_f(src_fn, H5F_ACC_TRUNC_F, fid, ier) 38 | call H5Screate_simple_f(size(dims), dims, space, ier) 39 | call H5Dcreate_f(fid, src_name, H5T_NATIVE_INTEGER, space, dset_id, ier) 40 | call H5Dwrite_f(dset_id, H5T_NATIVE_INTEGER, wdata, dims, ier); 41 | call H5Sclose_f(space, ier) 42 | call H5Dclose_f(dset_id, ier) 43 | call H5Fclose_f(fid, ier) 44 | 45 | !> Create file in which virtual dataset will be stored. 46 | call H5Fcreate_f(vfn, H5F_ACC_TRUNC_F, fid, ier) 47 | 48 | !> Create VDS dataspace 49 | call H5Screate_simple_f(size(dims), vdsdims, vspace, ier) 50 | 51 | !> Set VDS creation property 52 | call H5Pcreate_f(H5P_DATASET_CREATE_F, dcpl, ier) 53 | 54 | !> Build the mappings. 55 | !> Selections in the source datasets are H5S_ALL. 56 | !> In the virtual dataset we select the first, the second and the third rows 57 | !> and map each row to the data in the corresponding source dataset. 58 | call H5Screate_simple_f(size(dims), dims, src_space, ier) 59 | if(ier/=0) error stop "H5Screate_simple_f" // src_fn 60 | call H5Pset_virtual_f(dcpl, vspace, src_fn, src_name, src_space, ier) 61 | if (ier/=0) error stop "H5Pset_virtual_f " // src_name 62 | 63 | !> Create a virtual dataset 64 | call H5Dcreate_f(fid, v_name, H5T_NATIVE_INTEGER, vspace, dset_id, ier, dcpl_id=dcpl) 65 | if(ier/=0) error stop "H5Dcreate_f " // v_name 66 | call H5Sclose_f(vspace, ier) 67 | call H5Sclose_f(src_space, ier) 68 | call H5Dclose_f(dset_id, ier) 69 | call H5Fclose_f(fid, ier) 70 | 71 | !> READ: Open the file and virtual dataset 72 | 73 | call H5Fopen_f(vfn, H5F_ACC_RDONLY_F, fid, ier) 74 | call H5Dopen_f(fid, v_name, dset_id, ier) 75 | 76 | !> Get creation property list and mapping properties. 77 | call H5Dget_create_plist_f(dset_id, dcpl, ier) 78 | 79 | !> Get storage layout. 80 | 81 | call H5Pget_layout_f(dcpl, layout, ier) 82 | if(layout == H5D_VIRTUAL_F) then 83 | print '(a)', 'Dataset has virtual layout' 84 | else 85 | print '(a)', 'Wrong layout found' 86 | endif 87 | 88 | !> Find the number of mappings. 89 | 90 | call H5Pget_virtual_count_f(dcpl, num_map, ier) 91 | print '(A,i0)', "Number of mappings: ", num_map 92 | 93 | !> Get mapping parameters for each mapping. 94 | 95 | do i = 0, num_map-1 96 | print '(a,i0)', "Mapping ", i 97 | print '(a)', 'Selection in the virtual dataset' 98 | !> Get selection in the virttual dataset 99 | call H5Pget_virtual_vspace_f(dcpl, i, vspace, ier) 100 | if(ier/=0) error stop "H5Pget_virtual_vspace_f" 101 | 102 | !> Make sure it is ALL selection and then print selection. 103 | call H5Sget_select_type_f(vspace, type, ier) 104 | if (type == H5S_SEL_ALL_F) print '(a)', "Selection is H5S_ALL" 105 | 106 | ! !> Get source file name. 107 | ! call H5Pget_virtual_filename_f(dcpl, i, src_filename, ier, L) 108 | ! if(ier/=0) error stop "H5Pget_virtual_filename_f" 109 | ! print '(a)', "Source filename " // src_filename(:L) 110 | 111 | ! !> Get source dataset name 112 | ! call H5Pget_virtual_dsetname_f(dcpl, i, dsetname, ier, L) 113 | ! print '(a)', "Source dataset name " // dsetname(:L) 114 | 115 | !> Get selection in the source dataset. 116 | print '(a)', "Selection in the source dataset" 117 | call H5Pget_virtual_srcspace_f(dcpl, i, src_space, ier) 118 | if(ier/=0) error stop "H5Pget_virtual_srcspace_f" 119 | 120 | !> Make sure it is ALL selection and then print selection 121 | call H5Sget_select_type_f(src_space, type, ier) 122 | if (type == H5S_SEL_ALL_F) print '(a)', "Selection is H5S_ALL" 123 | 124 | call H5Sclose_f(vspace, ier) 125 | call H5Sclose_f(src_space, ier) 126 | 127 | end do 128 | 129 | !> Read the data using the default properties. 130 | 131 | call H5Dread_f(dset_id, H5T_NATIVE_INTEGER, rdata, dims, ier) 132 | 133 | print '(a)', "VDS Data:" 134 | do i = 1, dims(1) 135 | print '(10i0)', rdata(i,:) 136 | end do 137 | 138 | if (any(rdata /= wdata)) error stop "data in VDS read didn't match known original data" 139 | 140 | call H5Pclose_f(dcpl, ier) 141 | call H5Dclose_f(dset_id, ier) 142 | call H5Fclose_f(fid, ier) 143 | 144 | CALL h5close_f(ier) 145 | 146 | end program 147 | -------------------------------------------------------------------------------- /concepts/vlen.f90: -------------------------------------------------------------------------------- 1 | !************************************************************ 2 | ! 3 | ! This example shows how to read and write variable-length 4 | ! string datatypes to a dataset. The program first writes 5 | ! variable-length strings to a dataset with a dataspace of 6 | ! DIM0, then closes the file. Next, it reopens the file, 7 | ! reads back the data, and outputs it to the screen. 8 | ! 9 | ! This file is intended for use with HDF5 Library version 1.8 10 | ! 11 | ! 12 | !************************************************************ 13 | 14 | PROGRAM main 15 | 16 | USE HDF5 17 | 18 | IMPLICIT NONE 19 | 20 | CHARACTER(LEN=18), PARAMETER :: filename = "h5ex_t_vlstring.h5" 21 | CHARACTER(LEN=3) , PARAMETER :: dataset = "DS1" 22 | 23 | INTEGER(HSIZE_T), PARAMETER :: dim0 = 1 24 | INTEGER(HSIZE_T), PARAMETER :: sdim = 7 25 | INTEGER(HID_T) :: file, filetype, space, dset ! Handles 26 | INTEGER :: hdferr 27 | INTEGER(HSIZE_T), DIMENSION(1:1) :: dims = (/dim0/) 28 | INTEGER(HSIZE_T), DIMENSION(1:2) :: maxdims 29 | 30 | CHARACTER(LEN=sdim), DIMENSION(1:dim0), TARGET :: wdata = "Parting" ! Write buffer 31 | CHARACTER(LEN=sdim) :: rdata(1) ! Read buffer 32 | INTEGER(HSIZE_T), DIMENSION(2) :: data_dims = (/sdim,dim0/) 33 | INTEGER(SIZE_T), DIMENSION(1) :: str_len = [7] 34 | INTEGER :: i 35 | ! 36 | ! Initialize FORTRAN interface. 37 | ! 38 | CALL h5open_f(hdferr) 39 | ! 40 | ! Create a new file using the default properties. 41 | ! 42 | CALL h5fcreate_f(filename, H5F_ACC_TRUNC_F, file, hdferr) 43 | ! 44 | ! Create file and memory datatypes. For this example we will save 45 | ! the strings as C variable length strings, H5T_STRING is defined 46 | ! as a variable length string. 47 | ! 48 | CALL H5Tcopy_f(H5T_STRING, filetype, hdferr) 49 | CALL H5Tset_strpad_f(filetype, H5T_STR_NULLPAD_F, hdferr) 50 | ! 51 | ! Create dataspace. 52 | ! 53 | CALL h5screate_simple_f(1, dims, space, hdferr) 54 | ! 55 | ! Create the dataset and write the variable-length string data to 56 | ! it. 57 | ! 58 | CALL h5dcreate_f(file, dataset, filetype, space, dset, hdferr) 59 | 60 | CALL h5dwrite_vl_f(dset, filetype, wdata, data_dims, str_len, hdferr, space) 61 | 62 | ! 63 | ! Close and release resources. 64 | ! 65 | CALL h5dclose_f(dset , hdferr) 66 | CALL h5sclose_f(space, hdferr) 67 | CALL H5Tclose_f(filetype, hdferr) 68 | CALL h5fclose_f(file , hdferr) 69 | 70 | ! 71 | ! Now we begin the read section of this example. 72 | ! 73 | ! 74 | ! Open file and dataset. 75 | ! 76 | CALL h5fopen_f(filename, H5F_ACC_RDONLY_F, file, hdferr) 77 | CALL h5dopen_f(file, dataset, dset, hdferr) 78 | ! 79 | ! Get the datatype. 80 | ! 81 | CALL H5Dget_type_f(dset, filetype, hdferr) 82 | ! 83 | ! Get dataspace and allocate memory for read buffer. 84 | ! 85 | CALL H5Dget_space_f(dset, space, hdferr) 86 | CALL H5Sget_simple_extent_dims_f(space, dims, maxdims, hdferr) 87 | 88 | ! 89 | ! Read the data. 90 | ! 91 | CALL h5dread_vl_f(dset, filetype, rdata, data_dims, str_len, hdferr, space) 92 | 93 | ! 94 | ! Output the data to the screen. 95 | ! 96 | DO i = 1, dims(1) 97 | WRITE(*,'(A,"(",I0,"): ",A)') DATASET, i, rdata(i) 98 | END DO 99 | 100 | CALL h5dclose_f(dset , hdferr) 101 | CALL h5sclose_f(space, hdferr) 102 | CALL H5Tclose_f(filetype, hdferr) 103 | CALL h5fclose_f(file , hdferr) 104 | 105 | END PROGRAM main 106 | -------------------------------------------------------------------------------- /concepts/writer_nd.f90: -------------------------------------------------------------------------------- 1 | !! conceptual--not tested 2 | submodule (h5fortran:write) writer_ND 3 | 4 | implicit none (type, external) 5 | 6 | contains 7 | 8 | module procedure h5write_8d 9 | 10 | integer(HID_T) :: dtype, filespace_id, dset_id 11 | integer(HSIZE_T) :: dims(rank(A)) 12 | integer :: i(rank(A)) 13 | TYPE(C_PTR) :: f_ptr 14 | 15 | ierr = 0 16 | 17 | select type (A) 18 | type is (real(real64)) 19 | dims = shape(A, HSIZE_T) 20 | dtype = h5kind_to_type(kind(A),H5_REAL_KIND) 21 | call hdf_create(self,dname,dtype,dims,filespace_id,dset_id) 22 | 23 | i = lbound(A) 24 | f_ptr = c_loc(A(i(1),i(2),i(3),i(4),i(5),i(6),i(7),i(8))) 25 | call h5dwrite_f(dset_id, dtype, f_ptr, ierr) 26 | type is (real(real32)) 27 | dtype = h5kind_to_type(kind(A),H5_REAL_KIND) 28 | dims = shape(A, HSIZE_T) 29 | call hdf_create(self,dname,dtype,dims,filespace_id,dset_id) 30 | 31 | i = lbound(A) 32 | f_ptr = c_loc(A(i(1),i(2),i(3),i(4),i(5),i(6),i(7),i(8))) 33 | call h5dwrite_f(dset_id, dtype, f_ptr, ierr) 34 | type is (integer(int32)) 35 | dtype = h5kind_to_type(kind(A),H5_INTEGER_KIND) 36 | dims = shape(A, HSIZE_T) 37 | call hdf_create(self,dname,dtype,dims,filespace_id,dset_id) 38 | 39 | i = lbound(A) 40 | f_ptr = c_loc(A(i(1),i(2),i(3),i(4),i(5),i(6),i(7),i(8))) 41 | call h5dwrite_f(dset_id, dtype, f_ptr, ierr) 42 | type is (integer(int64)) 43 | dtype = h5kind_to_type(kind(A),H5_INTEGER_KIND) 44 | dims = shape(A, HSIZE_T) 45 | call hdf_create(self,dname,dtype,dims,filespace_id,dset_id) 46 | 47 | i = lbound(A) 48 | f_ptr = c_loc(A(i(1),i(2),i(3),i(4),i(5),i(6),i(7),i(8))) 49 | call h5dwrite_f(dset_id, dtype, f_ptr, ierr) 50 | class default 51 | ierr = 6 52 | end select 53 | 54 | call hdf_wrapup(dset_id, filespace_id) 55 | if (check(ierr, 'ERROR:h5fortran: ' // dname // ' write ' // self%filename)) return 56 | 57 | end procedure h5write_8d 58 | 59 | end submodule writer_ND 60 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22...3.27) 2 | 3 | project(h5fortranExample 4 | LANGUAGES C CXX Fortran) 5 | 6 | enable_testing() 7 | 8 | find_package(h5fortran CONFIG REQUIRED) 9 | 10 | # --- Fortran interface for examples 11 | add_library(fortran_interface fortran_interface.f90) 12 | target_link_libraries(fortran_interface PRIVATE h5fortran::h5fortran) 13 | 14 | add_executable(ex_fcn ex_fcn.f90) 15 | target_link_libraries(ex_fcn PRIVATE h5fortran::h5fortran) 16 | add_test(NAME Fortran_fcn COMMAND ex_fcn) 17 | 18 | add_executable(ex_oo ex_oo.f90) 19 | target_link_libraries(ex_oo PRIVATE h5fortran::h5fortran) 20 | add_test(NAME Fortran_oo COMMAND ex_oo) 21 | 22 | add_executable(repeat_char_read char_repeat_read.f90) 23 | target_link_libraries(repeat_char_read PRIVATE h5fortran::h5fortran) 24 | 25 | ## VTK HDF5 write example 26 | add_executable(vtk_write vtk_write.f90) 27 | target_link_libraries(vtk_write PRIVATE h5fortran::h5fortran) 28 | add_test(NAME VTK COMMAND vtk_write ${CMAKE_CURRENT_BINARY_DIR}/vtk.hdf) 29 | 30 | ## C, C++ examples 31 | 32 | add_executable(c_fcn ex_fcn.c) 33 | target_link_libraries(c_fcn PRIVATE fortran_interface) 34 | target_compile_features(c_fcn PRIVATE c_std_99) 35 | # https://en.cppreference.com/w/c/types/integer 36 | add_test(NAME C_fcn COMMAND c_fcn) 37 | 38 | add_executable(cpp_fcn ex_fcn.cpp) 39 | target_link_libraries(cpp_fcn PRIVATE fortran_interface) 40 | target_compile_features(cpp_fcn PRIVATE cxx_std_11) 41 | # https://en.cppreference.com/w/cpp/types/integer 42 | add_test(NAME CPP_fcn COMMAND cpp_fcn) 43 | 44 | # properties 45 | get_property(test_names DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY TESTS) 46 | set_property(TEST ${test_names} PROPERTY WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 47 | 48 | if(WIN32) 49 | set_property(TEST ${test_names} PROPERTY 50 | ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:${ZLIB_INCLUDE_DIRS}/../bin;PATH=path_list_prepend:${h5fortran_DIR}/../bin" 51 | ) 52 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") 53 | set_property(TEST ${test_names} PROPERTY 54 | ENVIRONMENT_MODIFICATION "LD_LIBRARY_PATH=path_list_prepend:${ZLIB_INCLUDE_DIRS}/../lib;LD_LIBRARY_PATH=path_list_prepend:${h5fortran_DIR}/../lib" 55 | ) 56 | endif() 57 | -------------------------------------------------------------------------------- /example/Readme.md: -------------------------------------------------------------------------------- 1 | # h5fortran Examples 2 | 3 | From the h5fortran/ directory, specify the h5fortran install like: 4 | 5 | ```sh 6 | cmake -B build -DCMAKE_INSTALL_PREFIX=~/h5fortran 7 | cmake --build build 8 | cmake --install build 9 | 10 | cmake -B example/build -S Examples -Dh5fortran_ROOT=~/h5fortran 11 | ``` 12 | 13 | ## Example 1 14 | 15 | Example 1 shows the functional one-step interface of h5fortran 16 | 17 | ## Example 2 18 | 19 | Example 2 shows the object-oriented interface of h5fortran, which may offer faster performance if more than one variable is being read or written. 20 | 21 | ## Example 3 22 | 23 | Example 3 is of a C main program calling a Fortran interface to h5fortran 24 | 25 | ## Example 4 26 | 27 | Example 4 is of a C++ main program calling a Fortran interface to h5fortran 28 | 29 | For a C++ header-only object-oriented HDF5 library, consider [HighFive](https://github.com/BlueBrain/HighFive) 30 | 31 | 32 | ## Notes 33 | 34 | ### Non CMake build 35 | 36 | CMake makes building much easier. 37 | If for whatever you don't wish to use CMake, the HDF5 compiler wrapper (if available on your system) may work. 38 | Since the HDF5 compiler wrapper is not always working or available, we strongly recommended CMake as above for any HDF5-based application. 39 | 40 | On Ubuntu it looks like: 41 | 42 | ```sh 43 | $ h5fc -show 44 | 45 | gfortran -I/usr/include/hdf5/serial -L/usr/lib/x86_64-linux-gnu/hdf5/serial /usr/lib/x86_64-linux-gnu/hdf5/serial/libhdf5hl_fortran.a /usr/lib/x86_64-linux-gnu/hdf5/serial/libhdf5_hl.a /usr/lib/x86_64-linux-gnu/hdf5/serial/libhdf5_fortran.a /usr/lib/x86_64-linux-gnu/hdf5/serial/libhdf5.a -lpthread -lsz -lz -ldl -lm -Wl,-rpath -Wl,/usr/lib/x86_64-linux-gnu/hdf5/serial 46 | ``` 47 | -------------------------------------------------------------------------------- /example/char_repeat_read.f90: -------------------------------------------------------------------------------- 1 | program repeat_read 2 | 3 | use h5fortran, only : hdf5_file 4 | implicit none 5 | 6 | type(hdf5_file) :: h 7 | 8 | integer :: i 9 | 10 | character(:), allocatable :: filename 11 | character(2) :: ct, c1 12 | character(4) :: ds 13 | 14 | !filename = '../h5fortran_example.h5' 15 | filename = 'char.h5' 16 | ct = "MA" 17 | 18 | call h%open(filename, 'rw') 19 | 20 | do i = 1,6 21 | write(ds,'(a1,i1,a2)') '/', i, '/a' 22 | call h%write(ds, ct) 23 | enddo 24 | 25 | call h%close() 26 | 27 | 28 | call h%open(filename, 'r') 29 | do i = 1,6 30 | write(ds,'(a1,i1,a2)') '/', i, '/a' 31 | call h%read(ds, c1) 32 | if (c1 /= ct) error stop "failed on read of dataset " // ds 33 | enddo 34 | call h%close() 35 | 36 | print *, 'OK: repeat read' 37 | 38 | end program 39 | -------------------------------------------------------------------------------- /example/ex_fcn.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "fortran_interface.h" 5 | 6 | 7 | 8 | int main(void) { 9 | 10 | int32_t x = 321, y; 11 | 12 | char filename[256] = "h5fortran_example3.h5"; 13 | char varname[64] = "/x_c"; 14 | 15 | write_int32(filename, varname, &x); 16 | 17 | read_int32(filename, varname, &y); 18 | 19 | if (x != y) { 20 | fprintf(stderr, "ERROR: read/write mismatch value. Expected %d but got %d", x, y); 21 | return 1; 22 | } 23 | 24 | printf("OK: example 3"); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /example/ex_fcn.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "fortran_interface.h" 5 | 6 | 7 | int main(void) { 8 | 9 | int32_t x = 321, y; 10 | 11 | char filename[256] = "h5fortran_example4.h5"; 12 | char varname[64] = "/x_cpp"; 13 | 14 | write_int32(filename, varname, &x); 15 | 16 | read_int32(filename, varname, &y); 17 | 18 | if (x != y) { 19 | std::cerr << "ERROR: read/write mismatch value. Expected " << x << " but got " << y << std::endl; 20 | return EXIT_FAILURE; 21 | } 22 | 23 | std::cout << "OK: example 4" << std::endl; 24 | 25 | return EXIT_SUCCESS; 26 | } 27 | -------------------------------------------------------------------------------- /example/ex_fcn.f90: -------------------------------------------------------------------------------- 1 | program example1 2 | 3 | use h5fortran, only : h5write, h5read 4 | implicit none 5 | 6 | character(:), allocatable :: filename 7 | integer :: i32 8 | 9 | filename = 'h5fortran_example1.h5' 10 | 11 | call h5write(filename, '/x', 123) 12 | 13 | call h5read(filename, '/x', i32) 14 | if (i32 /= 123) error stop 'incorrect value read' 15 | 16 | print *, 'OK: example 1' 17 | 18 | end program 19 | -------------------------------------------------------------------------------- /example/ex_oo.f90: -------------------------------------------------------------------------------- 1 | program example2 2 | 3 | use h5fortran, only : hdf5_file 4 | implicit none 5 | 6 | character(:), allocatable :: filename 7 | integer :: i32 8 | 9 | type(hdf5_file) :: h5f 10 | 11 | filename = 'h5fortran_example2.h5' 12 | 13 | call h5f%open(filename, action='w') 14 | call h5f%write('/x', 123) 15 | call h5f%close() 16 | 17 | call h5f%open(filename, action='r') 18 | call h5f%read('/x', i32) 19 | if (i32 /= 123) error stop 'incorrect value read' 20 | 21 | print *, 'OK: example 2' 22 | 23 | end program 24 | -------------------------------------------------------------------------------- /example/fortran_interface.f90: -------------------------------------------------------------------------------- 1 | module fortran_interface 2 | !! filename(256) and var_name(64) are arbitrary sizes. 3 | !! ensure calling program buffers are equally sized 4 | use, intrinsic :: iso_c_binding, only : C_INT32_T, C_CHAR, C_NULL_CHAR 5 | use h5fortran, only : h5write, h5read 6 | 7 | implicit none 8 | 9 | contains 10 | 11 | 12 | subroutine write_int32(filename, var_name, i32) bind(C) 13 | character(kind=C_CHAR) :: filename(256) 14 | character(kind=C_CHAR) :: var_name(64) 15 | integer(C_INT32_T), intent(in) :: i32 16 | 17 | call h5write(cstr2fstr(filename), cstr2fstr(var_name), i32) 18 | 19 | end subroutine write_int32 20 | 21 | 22 | subroutine read_int32(filename, var_name, i32) bind(C) 23 | character(kind=C_CHAR) :: filename(256) 24 | character(kind=C_CHAR) :: var_name(64) 25 | integer(C_INT32_T), intent(out) :: i32 26 | 27 | call h5read(cstr2fstr(filename), cstr2fstr(var_name), i32) 28 | 29 | end subroutine read_int32 30 | 31 | 32 | function cstr2fstr(c_str) result(f_str) 33 | 34 | character(kind=C_CHAR), intent(in) :: c_str(:) 35 | character(len=size(c_str)) :: buf 36 | character(:), allocatable :: f_str 37 | integer :: i 38 | 39 | buf = "" 40 | !! clean variable, will get extra garbled text otherwise, maybe blank but non-trimmable character 41 | 42 | do i = 1, size(c_str) 43 | if (c_str(i) == C_NULL_CHAR) exit 44 | buf(i:i) = c_str(i) 45 | enddo 46 | 47 | f_str = trim(buf) 48 | 49 | end function cstr2fstr 50 | 51 | end module fortran_interface 52 | -------------------------------------------------------------------------------- /example/fortran_interface.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif 4 | 5 | extern void write_int32(char*, char*, int_least32_t*); 6 | 7 | extern void read_int32(char*, char*, int_least32_t*); 8 | 9 | #ifdef __cplusplus 10 | } 11 | #endif 12 | -------------------------------------------------------------------------------- /example/vtk_write.f90: -------------------------------------------------------------------------------- 1 | !! example of VTK HDF5 file format 2 | !! this is a de facto HDF5 file template using specific group names and dataset hierarchy. 3 | !! ParaView can also read these VTK HDF5 files. 4 | !! 5 | !! References: 6 | !! https://kitware.github.io/vtk-examples/site/VTKFileFormats/#hdf-file-formats 7 | !! https://www.kitware.com/vtk-hdf-reader/ 8 | !! https://gitlab.kitware.com/danlipsa/vtkxml-to-vtkhdf 9 | !! https://gitlab.kitware.com/vtk/vtk/-/blob/master/IO/HDF/Testing/Cxx/TestHDFReader.cxx 10 | 11 | program vtk_write 12 | 13 | use h5fortran, only : hdf5_file 14 | 15 | implicit none 16 | 17 | character(1024) :: filename 18 | integer :: ierr 19 | 20 | !> these are just to avoid typos, you can type out full name each time 21 | character(7), parameter :: vtkhdf = "/VTKHDF" 22 | character(17), parameter :: pointdata = vtkhdf // "/PointData" 23 | 24 | type(hdf5_file) :: h 25 | 26 | real, allocatable :: Iterations(:,:,:), IterationsGradient(:,:,:,:) 27 | 28 | call get_command_argument(1, filename, status=ierr) 29 | if(ierr /= 0) error stop "please give filename to write" 30 | 31 | call h % open(filename, 'w') 32 | 33 | call h % create_group(vtkhdf) 34 | call h % writeattr(vtkhdf ,"Version", [1, 0]) 35 | 36 | !> ImageData example 37 | call h % writeattr(vtkhdf, "Direction", [1, 0, 0, 0, 1, 0, 0, 0, 1]) 38 | call h % writeattr(vtkhdf, "Origin", [-1.75, -1.25, 0.]) 39 | call h % writeattr(vtkhdf, "Spacing", [0.131579, 0.125, 0.0952381]) 40 | call h % writeattr(vtkhdf, "WholeExtent", [0, 19, 0, 20, 0, 21]) 41 | call h % writeattr(vtkhdf, "Type", "ImageData") 42 | call h % create_group(pointdata) 43 | call h % writeattr(pointdata, "Scalars", "IterationsGradient") 44 | 45 | allocate(Iterations(20, 21, 22)) 46 | call random_number(Iterations) 47 | call h % write(pointdata // "/Iterations", Iterations) 48 | deallocate(Iterations) 49 | 50 | ! allocate(IterationsGradient(3, 20, 21, 22)) 51 | ! call h % write(pointdata // "/IterationsGradient", IterationsGradient) 52 | ! deallocate(IterationsGradient) 53 | 54 | call h % close() 55 | 56 | 57 | end program 58 | -------------------------------------------------------------------------------- /fpm.toml: -------------------------------------------------------------------------------- 1 | name = "h5fortran" 2 | description = "Lightweight object-oriented HDF5 interface" 3 | categories = "io" 4 | version = "4.11.0" 5 | 6 | [build] 7 | auto-tests = false 8 | auto-examples = false 9 | external-modules = ["hdf5", "h5lt"] 10 | 11 | [dependencies] 12 | hdf5 = "*" 13 | 14 | [install] 15 | library = true 16 | 17 | [[test]] 18 | name = "minimal" 19 | main = "test_minimal.f90" 20 | 21 | [[test]] 22 | name = "array" 23 | main = "test_array.f90" 24 | 25 | [[test]] 26 | name = "attributes" 27 | main = "test_attributes.f90" 28 | 29 | [[test]] 30 | name = "cast" 31 | main = "test_cast.f90" 32 | 33 | [[test]] 34 | name = "write" 35 | main = "test_write.f90" 36 | 37 | [[test]] 38 | name = "deflate_write" 39 | main = "test_deflate_write.f90" 40 | 41 | [[test]] 42 | name = "deflate_read" 43 | main = "test_deflate_read.f90" 44 | 45 | [[test]] 46 | name = "deflate_props" 47 | main = "test_deflate_props.f90" 48 | 49 | [[test]] 50 | name = "destructor" 51 | main = "test_destructor.f90" 52 | 53 | [[test]] 54 | name = "exist" 55 | main = "test_exist.f90" 56 | 57 | [[test]] 58 | name = "layout" 59 | main = "test_layout.f90" 60 | 61 | [[test]] 62 | name = "lt" 63 | main = "test_lt.f90" 64 | 65 | [[test]] 66 | name = "scalar" 67 | main = "test_scalar.f90" 68 | 69 | [[test]] 70 | name = "shape" 71 | main = "test_shape.f90" 72 | 73 | [[test]] 74 | name = "string" 75 | main = "test_string.f90" 76 | 77 | [[test]] 78 | name = "version" 79 | main = "test_version.f90" 80 | -------------------------------------------------------------------------------- /memcheck.cmake: -------------------------------------------------------------------------------- 1 | # run like: 2 | # 3 | # ctest -S memcheck.cmake 4 | # 5 | # optionally, tell path to memory checker like: 6 | # 7 | # ctest -DMEMCHECK_ROOT=/path/to/bin/valgrind -S memcheck.cmake 8 | 9 | cmake_minimum_required(VERSION 3.19) 10 | 11 | list(APPEND opts -DCMAKE_BUILD_TYPE=Debug) 12 | 13 | if(NOT DEFINED CTEST_MEMORYCHECK_TYPE) 14 | set(CTEST_MEMORYCHECK_TYPE "Valgrind") 15 | endif() 16 | 17 | if(CTEST_MEMORYCHECK_TYPE STREQUAL "Valgrind") 18 | # https://www.cprogramming.com/debugging/valgrind.html 19 | find_program(CTEST_MEMORYCHECK_COMMAND NAMES valgrind HINTS ${MEMCHECK_ROOT} REQUIRED) 20 | set(CTEST_MEMORYCHECK_COMMAND_OPTIONS --leak-check=full) 21 | set(supp ${CMAKE_CURRENT_LIST_DIR}/valgrind.supp) 22 | if(EXISTS ${supp}) 23 | list(APPEND CTEST_MEMORYCHECK_COMMAND_OPTIONS --suppressions=${supp}) 24 | endif() 25 | elseif(CTEST_MEMORYCHECK_TYPE STREQUAL "DrMemory") 26 | find_program(CTEST_MEMORYCHECK_COMMAND NAMES drmemory HINTS ${MEMCHECK_ROOT} REQUIRED) 27 | set(CTEST_MEMORYCHECK_COMMAND_OPTIONS -light -count_leaks) 28 | elseif(CTEST_MEMORYCHECK_TYPE STREQUAL "AddressSanitizer") 29 | set(check_flags -fsanitize=address) 30 | elseif(CTEST_MEMORYCHECK_TYPE STREQUAL "LeakSanitizer") 31 | set(check_flags -fsanitize=leak) 32 | elseif(CTEST_MEMORYCHECK_TYPE STREQUAL "MemorySanitizer") 33 | set(check_flags -fsanitize=memory) 34 | elseif(CTEST_MEMORYCHECK_TYPE STREQUAL "ThreadSanitizer") 35 | set(check_flags -fsanitize=thread) 36 | elseif(CTEST_MEMORYCHECK_TYPE STREQUAL "UndefinedBehaviorSanitizer") 37 | set(check_flags -fsanitize=undefined) 38 | else() 39 | message(FATAL_ERROR "Unknown memory checker type: ${CTEST_MEMORYCHECK_TYPE}") 40 | endif() 41 | 42 | if(check_flags) 43 | list(APPEND opts 44 | -DCMAKE_C_FLAGS_DEBUG=${check_flags} 45 | -DCMAKE_CXX_FLAGS_DEBUG=${check_flags} 46 | -DCMAKE_EXE_LINKER_FLAGS_INIT=${check_flags} 47 | ) 48 | endif() 49 | 50 | set(CTEST_SOURCE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) 51 | set(CTEST_BINARY_DIRECTORY ${CTEST_SOURCE_DIRECTORY}/build-${CTEST_MEMORYCHECK_TYPE}) 52 | set(CTEST_BUILD_CONFIGURATION Debug) 53 | 54 | if(DEFINED ENV{CMAKE_GENERATOR}) 55 | set(CTEST_CMAKE_GENERATOR $ENV{CMAKE_GENERATOR}) 56 | elseif(WIN32) 57 | set(CTEST_CMAKE_GENERATOR "MinGW Makefiles") 58 | else() 59 | set(CTEST_CMAKE_GENERATOR "Unix Makefiles") 60 | endif() 61 | 62 | message(STATUS "Checker ${CTEST_MEMORYCHECK_TYPE}: ${CTEST_MEMORYCHECK_COMMAND}") 63 | 64 | string(REPLACE ";" " " CTEST_MEMORYCHECK_COMMAND_OPTIONS "${CTEST_MEMORYCHECK_COMMAND_OPTIONS}") 65 | 66 | ctest_start(Experimental) 67 | 68 | ctest_configure( 69 | OPTIONS "${opts}" 70 | RETURN_VALUE ret 71 | CAPTURE_CMAKE_ERROR err 72 | ) 73 | if(NOT (ret EQUAL 0 AND err EQUAL 0)) 74 | message(FATAL_ERROR "CMake configure failed: ${ret} ${err}") 75 | endif() 76 | 77 | ctest_build( 78 | RETURN_VALUE ret 79 | CAPTURE_CMAKE_ERROR err 80 | ) 81 | if(NOT (ret EQUAL 0 AND err EQUAL 0)) 82 | message(FATAL_ERROR "CMake build failed: ${ret} ${err}") 83 | endif() 84 | 85 | ctest_memcheck( 86 | INCLUDE ${include} 87 | INCLUDE_LABEL ${include_label} 88 | EXCLUDE "${exclude}" 89 | EXCLUDE_LABEL "${exclude_label}" 90 | RETURN_VALUE ret 91 | CAPTURE_CMAKE_ERROR err 92 | DEFECT_COUNT count 93 | ) 94 | 95 | if(NOT (ret EQUAL 0 AND err EQUAL 0)) 96 | message(FATAL_ERROR "Memory check failed: ${ret} ${err}") 97 | endif() 98 | 99 | if(NOT count EQUAL 0) 100 | message(FATAL_ERROR "Memory check found ${count} defects") 101 | endif() 102 | -------------------------------------------------------------------------------- /options.cmake: -------------------------------------------------------------------------------- 1 | message(STATUS "${PROJECT_NAME} ${PROJECT_VERSION} CMake ${CMAKE_VERSION} Toolchain ${CMAKE_TOOLCHAIN_FILE}") 2 | 3 | include(GNUInstallDirs) 4 | 5 | option(h5fortran_find "try to find libraries" on) 6 | 7 | option(h5fortran_COVERAGE "Code coverage tests") 8 | option(tidy "Run clang-tidy on the code") 9 | 10 | option(matlab "check HDF5 file writes with Matlab") 11 | option(concepts "conceptual testing, for devs only" off) 12 | 13 | option(h5fortran_BUILD_TESTING "build tests" ${h5fortran_IS_TOP_LEVEL}) 14 | 15 | set_property(DIRECTORY PROPERTY EP_UPDATE_DISCONNECTED true) 16 | 17 | # Necessary for shared library with Visual Studio / Windows oneAPI 18 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS true) 19 | 20 | if(h5fortran_IS_TOP_LEVEL AND CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 21 | set_property(CACHE CMAKE_INSTALL_PREFIX PROPERTY VALUE "${PROJECT_BINARY_DIR}/local") 22 | endif() 23 | -------------------------------------------------------------------------------- /paper/paper.bib: -------------------------------------------------------------------------------- 1 | @online{h5fortran, 2 | author = {Michael Hirsch}, 3 | title = {h5fortran: object-oriented Fortran HDF5 interface}, 4 | year = 2018, 5 | month = 12, 6 | url = {https://github.com/geospace-code/h5fortran}, 7 | doi = {10.5281/zenodo.3733569}, 8 | } 9 | 10 | @online{hdf5_utils, 11 | author = {Justin Irwin}, 12 | title = {HDF5 Utils}, 13 | year = 2017, 14 | url = {https://github.com/tiasus/HDF5_utils}, 15 | } 16 | 17 | @ONLINE{hdf5, 18 | author = {{The HDF Group}}, 19 | title = "{Hierarchical Data Format, version 5}", 20 | year = {1997}, 21 | url = {http://www.hdfgroup.org/HDF5/} 22 | } 23 | 24 | @online{h5py, 25 | author = {Andrew Collette}, 26 | title = {h5py: HDF5 Python interface}, 27 | year = {2012}, 28 | url = {https://github.com/h5py/h5py} 29 | } 30 | 31 | @book{h5pybook, 32 | title={Python and HDF5: Unlocking Scientific Data}, 33 | author={Collette, Andrew}, 34 | year={2013}, 35 | isbn = {9781449367831}, 36 | publisher={O'Reilly Media, Inc.} 37 | } 38 | 39 | @online{nc4fortran, 40 | author = {Michael Hirsch}, 41 | title = {nc4fortran: object-oriented Fortran NetCDF4 interface}, 42 | year = {2019}, 43 | url = {https://github.com/geospace-code/nc4fortran/}, 44 | doi = {10.5281/zenodo.3718418} 45 | } 46 | 47 | @online{gemini3d, 48 | author = {Zettergren, Matthew and Grubbs, Guy and Hirsch, Michael}, 49 | title = {GEMINI3D: time-dependent ionospheric model}, 50 | year = {2017}, 51 | url = {https://github.com/gemini3d/gemini3d}, 52 | doi = {10.5281/zenodo.3731716} 53 | } 54 | 55 | @online{h5pp, 56 | author = {David Aceituno}, 57 | title = {h5pp: a C++17 wrapper for HDF5}, 58 | year = 2019, 59 | url = {https://github.com/DavidAce/h5pp}, 60 | } 61 | 62 | @online{h5xx, 63 | author= {Felix Höfling}, 64 | title = {h5xx — a template-based C++ wrapper for the HDF5 library}, 65 | year = 2020, 66 | url = {https://github.com/fhoefling/h5xx}, 67 | } 68 | 69 | @article{zettergren, 70 | author = {Zettergren, M. D. and Snively, J. B.}, 71 | title = {Latitude and Longitude Dependence of Ionospheric TEC and Magnetic Perturbations From Infrasonic-Acoustic Waves Generated by Strong Seismic Events}, 72 | journal = {Geophysical Research Letters}, 73 | volume = {46}, 74 | number = {3}, 75 | pages = {1132-1140}, 76 | keywords = {ionosphere, atmosphere, TEC, acoustic waves, infrasound, earthquakes}, 77 | doi = {10.1029/2018GL081569}, 78 | url = {https://agupubs.onlinelibrary.wiley.com/doi/abs/10.1029/2018GL081569}, 79 | eprint = {https://agupubs.onlinelibrary.wiley.com/doi/pdf/10.1029/2018GL081569}, 80 | year = {2019} 81 | } 82 | 83 | @inproceedings{2011hdf5, 84 | author = {Folk, Mike and Heber, Gerd and Koziol, Quincey and Pourmal, Elena and Robinson, Dana}, 85 | title = {An Overview of the HDF5 Technology Suite and Its Applications}, 86 | year = {2011}, 87 | isbn = {9781450306140}, 88 | publisher = {Association for Computing Machinery}, 89 | address = {New York, NY, USA}, 90 | url = {https://doi.org/10.1145/1966895.1966900}, 91 | doi = {10.1145/1966895.1966900}, 92 | booktitle = {Proceedings of the EDBT/ICDT 2011 Workshop on Array Databases}, 93 | pages = {36–47}, 94 | numpages = {12}, 95 | keywords = {data management, HDF5, databases, data models}, 96 | location = {Uppsala, Sweden}, 97 | series = {AD ’11} 98 | } 99 | -------------------------------------------------------------------------------- /paper/paper.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'h5fortran: object-oriented polymorphic Fortran interface for HDF5 file IO' 3 | tags: 4 | authors: 5 | - name: Michael Hirsch 6 | orcid: 0000-0002-1637-6526 7 | affiliation: "1" 8 | affiliations: 9 | - name: Boston University 10 | index: 1 11 | date: 6 Nov 2020 12 | bibliography: paper.bib 13 | --- 14 | 15 | # Summary 16 | 17 | h5fortran [@h5fortran] is a Fortran interface to HDF5 that abstracts away most details of a frequently-used subset of HDF5 file read/write operations. 18 | h5fortran has object-oriented and functional interfaces that makes HDF5 use from Fortran as easy as in high-level languages. 19 | For example, using the official HDF5 Fortran interface to write an array to disk involves over twenty subroutine calls, plus the associated program logic to call those procedures with efficient parameters. 20 | The same array write operation with h5fortran is accomplished with a single subroutine call. 21 | A similar reduction in user-facing complexity is achieved by h5fortran for HDF5 array reads. 22 | h5fortran adheres to Fortran 2008 standard, working on Gfortran and Intel compilers for Linux, MacOS, Windows on Intel / AMD, ARM and IBM POWER systems. 23 | CMake is used to build h5fortran, detecting if the HDF5 library is present and working and building HDF5 from source if necessary. 24 | CPack can be used to generate distributable binary archives. 25 | Conan package manager may also be used to automatically install the HDF5 library and then install h5fortran. 26 | Meson build system is also supported by h5fortran. 27 | 28 | h5fortran has general applicability to projects needing to do any of: 29 | 30 | * writing variables to HDF5: scalar to 7-D, of type real32, real64 or integer 31 | * reading variables from HDF5: scalar to 7-D, of type real32, real64 or integer 32 | * read / write character variables to / from HDF5 file 33 | * read / write variable attributes to / from HDF5 file 34 | * get the shape of a disk variable to allocate a memory variable for reading that data 35 | 36 | HDF5 does not have native support for complex numbers or booleans (`logical` Fortran datatype). 37 | h5fortran does not yet support complex numbers or booleans. 38 | High-level HDF5 interfaces in other code languages such as h5py have implemented these types using HDF5 struct and HDF5 enum respectively. 39 | If the HDF5 community continues to coalesce around these *de facto* data type implementations, we may consider implementing them in h5fortran in the future. 40 | h5fortran currently supports the serial HDF5 interface, and does not yet support the parallel MPI-based HDF5 interface. 41 | h5fortran does not yet support extensible datasets, although we would be open to investigating adding this support upon community request. 42 | 43 | In addition to the object-oriented interface, h5fortran provides single-command read / write procedures. 44 | Array slicing allows reading or writing a portion of a large disk variable to/from RAM. 45 | If the user has HDF5 with SZIP or ZLIB compression enabled, h5fortran is capable of reading and writing compressed variables, which can save over 50% disk space depending on the data lossless compressibility. 46 | Data shuffling and Fletcher32 checksums provide better compression and a check of file integrity respectively. 47 | h5fortran was designed for use by individual users on their laptops or embedded devices, as well as for use in HPC applications where parallel tasks need read only part of a milestone or shared HDF5 variable. 48 | 49 | h5fortran was originally developed for the GEMINI [@gemini3d; @zettergren] ionospheric model, funded in part by NASA ROSES \#80NSSC20K0176 and DARPA Cooperative Agreement HR00112120003. 50 | This work is approved for public release; distribution is unlimited. 51 | The information does not necessarily reflect the position or the policy of the Government. 52 | 53 | # Statement of need 54 | 55 | Fortran has only raw file input-output (IO) built in to the language. 56 | To support reproducibility of work done in any programming language and long-term usefulness of the data generated or processed, it is beneficial to use self-describing data file formats like HDF5 [@2011hdf5]. 57 | Many popular languages and libraries used for simulation and data science use the HDF5 [@hdf5] file IO library. 58 | Most programs and libraries intended for use by practitioners such as modelers and data scientists themselves use an object-oriented HDF5 interface like h5py [@h5py; @h5pybook]. 59 | 60 | # Other programs 61 | 62 | While other HDF5 interfaces exist, h5fortran presents a broad set of commonly used features, comprehensive test coverage and robustness across compilers and computing systems. 63 | We have written a companion library for NetCDF4 called nc4fortran [@nc4fortran], which by design has a nearly identical user-facing API. 64 | Other Fortran HDF5 interfaces such as HDF5_Utils [@hdf5_utils] use a functional interface mimicking the HDF5 LT functions, which require the end user to keep track of extra variables versus the single object used by h5fortran. 65 | A package for C++ with similar priorities of using modern language features and simple commands is h5pp [@h5pp]. 66 | A template-based C++ implementation is provided in h5xx [@h5xx]. 67 | 68 | # References 69 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=61.0.0", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.black] 6 | line-length = 90 7 | 8 | [tool.mypy] 9 | files = ["test"] 10 | ignore_missing_imports = true 11 | -------------------------------------------------------------------------------- /scripts/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20...3.29) 2 | # use max version to avoid deprecation warnings 3 | 4 | project(HDF5_build 5 | LANGUAGES C Fortran 6 | ) 7 | 8 | option(hdf5_parallel "build HDF5 parallel MPI") 9 | option(build_zlib "build zlib") 10 | 11 | # --- system checks 12 | message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") 13 | file(MAKE_DIRECTORY ${CMAKE_INSTALL_PREFIX}) 14 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.29) 15 | if(NOT IS_WRITABLE ${CMAKE_INSTALL_PREFIX}) 16 | message(FATAL_ERROR "CMAKE_INSTALL_PREFIX is not writable: ${CMAKE_INSTALL_PREFIX}") 17 | endif() 18 | else() 19 | file(TOUCH ${CMAKE_INSTALL_PREFIX}/.cmake_writable "") 20 | endif() 21 | 22 | if(hdf5_parallel) 23 | 24 | if(NOT MPI_ROOT AND DEFINED ENV{MPI_ROOT}) 25 | set(MPI_ROOT $ENV{MPI_ROOT}) 26 | endif() 27 | 28 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND MPI_ROOT) 29 | set(ld_path $ENV{LD_LIBRARY_PATH}) 30 | cmake_path(CONVERT "${ld_path}" TO_CMAKE_PATH_LIST ld_path NORMALIZE) 31 | cmake_path(CONVERT "${MPI_ROOT}" TO_CMAKE_PATH_LIST MPI_ROOT NORMALIZE) 32 | 33 | if(NOT "${ld_path}" MATCHES "${MPI_ROOT}/lib") 34 | message(WARNING "${MPI_ROOT}/lib not found in LD_LIBRARY_PATH: $ENV{LD_LIBRARY_PATH} 35 | HDF5 build may fail due to bugs in HDF5 package CMake scripts. 36 | Fix this by adding to ~/.bashrc or similar: 37 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${MPI_ROOT}/lib") 38 | endif() 39 | endif() 40 | 41 | endif() 42 | 43 | # HDF5 install fails to work (link) if prior HDF5 library is installed there 44 | find_library(_hdf5_libprior NAMES hdf5 PATHS ${CMAKE_INSTALL_PREFIX} PATH_SUFFIXES lib NO_DEFAULT_PATH NO_CACHE) 45 | find_path(_hdf5_incprior NAMES hdf5.h PATHS ${CMAKE_INSTALL_PREFIX} PATH_SUFFIXES include NO_DEFAULT_PATH NO_CACHE) 46 | find_program(_hdf5_binprior NAMES h5cc PATHS ${CMAKE_INSTALL_PREFIX} PATH_SUFFIXES bin NO_DEFAULT_PATH NO_CACHE) 47 | if(_hdf5_binprior) 48 | cmake_path(GET _hdf5_binprior PARENT_PATH _hdf5_binprior) 49 | else() 50 | set(_hdf5_binprior "") 51 | endif() 52 | if(_hdf5_libprior) 53 | cmake_path(GET _hdf5_libprior PARENT_PATH _hdf5_libprior) 54 | endif() 55 | if(_hdf5_libprior OR _hdf5_incprior OR _hdf5_binprior) 56 | message(FATAL_ERROR "HDF5 library already installed: 57 | ${_hdf5_libprior} 58 | ${_hdf5_incprior} 59 | ${_hdf5_binprior} 60 | Please pick a new install location or completely remove the old HDF5 install directory. 61 | Otherwise, HDF5 will fail to link correctly with prior version and this version mixed.") 62 | endif() 63 | 64 | # --- commence HDF5 build/install 65 | set_property(DIRECTORY PROPERTY EP_UPDATE_DISCONNECTED true) 66 | 67 | if(hdf5_parallel) 68 | find_package(MPI COMPONENTS C REQUIRED) 69 | include(${PROJECT_SOURCE_DIR}/../cmake/check_mpi.cmake) 70 | check_mpi_version() 71 | endif() 72 | 73 | include(${PROJECT_SOURCE_DIR}/../cmake/hdf5.cmake) 74 | 75 | message(STATUS "Build / install HDF5 ${hdf5_tag} to ${CMAKE_INSTALL_PREFIX}") 76 | 77 | # --- features 78 | include(FeatureSummary) 79 | 80 | add_feature_info(HDF5parallel hdf5_parallel "HDF5 MPI layer") 81 | 82 | feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES) 83 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # Build HDF5 scripts 2 | 3 | This folder can build HDF5 and ZLIB. 4 | Pick CMAKE_INSTALL_PREFIX to be the directory you wish to install HDF5 under. 5 | 6 | ```sh 7 | cmake -B build -DCMAKE_INSTALL_PREFIX=~/local 8 | cmake --build build 9 | ``` 10 | 11 | Optionally, build the MPI layer (parallel HDF5) 12 | 13 | ```sh 14 | cmake -B build -DCMAKE_INSTALL_PREFIX=~/local -Dhdf5_parallel=on 15 | cmake --build build 16 | ``` 17 | 18 | Optionally, request a specific 19 | [HDF5 release URL source archive](https://github.com/HDFGroup/hdf5/releases) 20 | and/or ZLIB URL source archive URL: 21 | 22 | ```sh 23 | cmake -B build \ 24 | -Dhdf5_url=https://github.com/HDFGroup/hdf5/archive/refs/tags/hdf5_1.14.4.3.tar.gz \ 25 | -Dzlib_url=https://github.com/zlib-ng/zlib-ng/archive/refs/tags/2.2.1.tar.gz 26 | ``` 27 | 28 | ## compiler wrappers 29 | 30 | The HDF5 compiler wrappers will be installed under the install_prefix/bin for macOS and Linux. 31 | 32 | * non-MPI (default): `h5cc` (C), `h5fc` (Fortran) 33 | * MPI: `h5pcc` (C), `h5pfc` (Fortran) 34 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(s ${CMAKE_CURRENT_SOURCE_DIR}) 2 | 3 | target_sources(h5fortran PRIVATE 4 | ${s}/utils.f90 ${s}/datatype.f90 ${s}/deflate.f90 5 | ${s}/read.f90 ${s}/read_scalar.f90 ${s}/read_ascii.f90 ${s}/reader.f90 6 | ${s}/write.f90 ${s}/write_scalar.f90 ${s}/writer.f90 7 | ${s}/reader_lt.f90 ${s}/writer_lt.f90 8 | ${s}/interface.f90 9 | ${s}/attr.f90 10 | ${s}/attr_read.f90 11 | ${s}/attr_write.f90 12 | ) 13 | -------------------------------------------------------------------------------- /src/attr.f90: -------------------------------------------------------------------------------- 1 | submodule (h5fortran) attr_smod 2 | 3 | use hdf5, only : H5S_SCALAR_F, & 4 | H5Aexists_by_name_f, H5Aopen_by_name_f, H5Aclose_f, H5Acreate_by_name_f, H5Adelete_f, H5Aget_space_f, & 5 | H5Screate_f, H5Screate_simple_f, H5Sclose_f, & 6 | H5Sget_simple_extent_dims_f, H5Sget_simple_extent_ndims_f, & 7 | H5Tcopy_f, H5Tset_size_f, H5Tclose_f, & 8 | H5Dopen_f, H5Dclose_f 9 | 10 | implicit none 11 | 12 | contains 13 | 14 | 15 | subroutine attr_create(self, obj_name, attr_name, dtype, attr_dims, attr_id, dtype_id, charlen) 16 | 17 | class(hdf5_file), intent(in) :: self 18 | character(*), intent(in) :: obj_name, attr_name 19 | integer(HID_T), intent(in) :: dtype 20 | integer(HSIZE_T), dimension(:), intent(in) :: attr_dims 21 | integer(HID_T), intent(out) :: attr_id, dtype_id 22 | integer, intent(in), optional :: charlen !< length of character scalar 23 | 24 | integer :: ier 25 | integer(HID_T) :: space_id 26 | 27 | call H5Tcopy_f(dtype, dtype_id, ier) 28 | call estop(ier, "attr_create:H5Tcopy", self%filename, obj_name, attr_name) 29 | 30 | 31 | if(dtype == H5T_NATIVE_CHARACTER) then 32 | if(.not. present(charlen)) error stop "ERROR:h5fortran:attr_create: character type must specify charlen" 33 | if (charlen < 1) error stop "ERROR:h5fortran:attr_create: character type must specify charlen > 0" 34 | 35 | call H5Tset_size_f(dtype_id, int(charlen, SIZE_T), ier) 36 | call estop(ier, "attr_create:H5Aset_size", self%filename, obj_name, attr_name) 37 | endif 38 | 39 | if(self % exist_attr(obj_name, attr_name)) then 40 | !! unlike datasets, H5Awrite_f doesn't seem to handle overwrites. Errors result like "H5Oattribute.c line 918 in H5O__attr_write(): can't locate open attribute?" 41 | !! since attribute writes are whole dataset, so we workaround by deleting attribute and creating a new attribute of the same name. 42 | 43 | !! FIXME: assumes object is a dataset. How to detect this automatically? 44 | !! For now, user can manually delete attribute first if not a dataset. 45 | 46 | call attr_delete(self, obj_name, attr_name) 47 | endif 48 | 49 | !> create attribute dataspace 50 | if(size(attr_dims) == 0) then 51 | call H5Screate_f(H5S_SCALAR_F, space_id, ier) 52 | else 53 | call H5Screate_simple_f(size(attr_dims), attr_dims, space_id, ier) 54 | endif 55 | call estop(ier, "attr_create:H5Screate", self%filename, obj_name, attr_name) 56 | 57 | call H5Acreate_by_name_f(self%file_id, obj_name, attr_name, dtype_id, space_id, attr_id, ier) 58 | call estop(ier, "attr_create:H5Acreate_by_name", self%filename, obj_name, attr_name) 59 | 60 | call H5Sclose_f(space_id, ier) 61 | call estop(ier, "attr_create:H5Aclose", self%filename, obj_name, attr_name) 62 | 63 | end subroutine attr_create 64 | 65 | 66 | module procedure attr_delete 67 | !! assumes object is a dataset 68 | 69 | integer(HID_T) :: dset_id 70 | integer :: ier 71 | 72 | call H5Dopen_f(self%file_id, obj_name, dset_id, ier) 73 | call estop(ier, "attr_delete:H5Dopen", self%filename, obj_name, attr_name) 74 | 75 | call H5Adelete_f(dset_id, attr_name, ier) 76 | call estop(ier, "attr_delete:H5Adelete", self%filename, obj_name, attr_name) 77 | 78 | call H5Dclose_f(dset_id, ier) 79 | call estop(ier, "attr_delete:H5Dclose", self%filename, obj_name, attr_name) 80 | 81 | end procedure attr_delete 82 | 83 | 84 | module procedure attr_exist 85 | 86 | integer :: ier 87 | 88 | attr_exist = self%exist(obj_name) 89 | if(.not. attr_exist) return 90 | 91 | call H5Aexists_by_name_f(self%file_id, obj_name, attr_name, attr_exist, ier) 92 | call estop(ier, "attr_exist:H5Aexists_by_name", self%filename, obj_name, attr_name) 93 | 94 | end procedure attr_exist 95 | 96 | 97 | end submodule attr_smod 98 | -------------------------------------------------------------------------------- /src/attr_read.f90: -------------------------------------------------------------------------------- 1 | submodule (h5fortran:attr_smod) attr_read 2 | 3 | use hdf5, only : H5Aread_f 4 | 5 | implicit none 6 | 7 | contains 8 | 9 | module procedure readattr_scalar 10 | include 'attr_read.inc' 11 | end procedure 12 | 13 | module procedure readattr_1d 14 | include 'attr_read.inc' 15 | end procedure 16 | 17 | module procedure readattr_2d 18 | include 'attr_read.inc' 19 | end procedure 20 | 21 | module procedure readattr_3d 22 | include 'attr_read.inc' 23 | end procedure 24 | 25 | module procedure readattr_4d 26 | include 'attr_read.inc' 27 | end procedure 28 | 29 | module procedure readattr_5d 30 | include 'attr_read.inc' 31 | end procedure 32 | 33 | module procedure readattr_6d 34 | include 'attr_read.inc' 35 | end procedure 36 | 37 | module procedure readattr_7d 38 | include 'attr_read.inc' 39 | end procedure 40 | 41 | module procedure lt0readattr 42 | 43 | type(hdf5_file) :: h 44 | 45 | call h%open(filename, action='r') 46 | call h%readattr(obj_name, attr, A) 47 | call h%close() 48 | 49 | end procedure lt0readattr 50 | 51 | 52 | module procedure lt1readattr 53 | 54 | type(hdf5_file) :: h 55 | 56 | call h%open(filename, action='r') 57 | call h%readattr(obj_name, attr, A) 58 | call h%close() 59 | 60 | end procedure lt1readattr 61 | 62 | 63 | end submodule attr_read 64 | -------------------------------------------------------------------------------- /src/attr_read.inc: -------------------------------------------------------------------------------- 1 | integer :: ier, attr_class 2 | integer, parameter :: rA = rank(A) 3 | integer(HID_T) :: attr_id, space_id 4 | integer(HSIZE_T) :: attr_dims(rA) 5 | logical :: is_scalar 6 | 7 | attr_dims = shape(A, HSIZE_T) 8 | 9 | call H5Aopen_by_name_f(self%file_id, obj_name, attr_name, attr_id, ier) 10 | call estop(ier, "attr_read:H5Aopen_by_name", self%filename, obj_name, attr_name) 11 | 12 | call H5Aget_space_f(attr_id, space_id, ier) 13 | call estop(ier, "attr_read:H5Aget_space", self%filename, obj_name, attr_name) 14 | 15 | if(rA == 0) then 16 | call hdf_rank_check(self, obj_name // ":" // attr_name, space_id, rA, is_scalar) 17 | else 18 | call hdf_shape_check(self, obj_name // ":" // attr_name, space_id, attr_dims) 19 | endif 20 | 21 | call get_obj_class(self, obj_name // ":" // attr_name, attr_id, attr_class) 22 | 23 | !> cast the dataset read from disk to the variable type presented by user h5f%read("/my_dataset", x, "y") 24 | !! select case doesn't allow H5T_* 25 | if(attr_class == H5T_FLOAT_F .OR. attr_class == H5T_INTEGER_F) then 26 | select type(A) 27 | type is (real(real64)) 28 | call H5Aread_f(attr_id, H5T_NATIVE_DOUBLE, A, attr_dims, ier) 29 | type is (real(real32)) 30 | call H5Aread_f(attr_id, H5T_NATIVE_REAL, A, attr_dims, ier) 31 | type is (integer(int32)) 32 | call H5Aread_f(attr_id, H5T_NATIVE_INTEGER, A, attr_dims, ier) 33 | type is (integer(int64)) 34 | call H5Aread_f(attr_id, H5T_STD_I64LE, A, attr_dims, ier) 35 | class default 36 | error stop 'ERROR:h5fortran:readattr: numeric dataset ' // obj_name // ':' // attr_name // ' needs real or integer variable' 37 | end select 38 | call estop(ier, "attr_read:H5Aread", self%filename, obj_name, attr_name) 39 | elseif(attr_class == H5T_STRING_F) then 40 | select type(A) 41 | type is (character(*)) !< kind=c_char too 42 | call read_char(self, obj_name//":"//attr_name, A, attr_id, 0_HID_T, space_id) 43 | !! the "0_HID_T" is unused by H5Aread_F. GCC 7 doesn't like H5S_ALL_F, and it's unused anyway 44 | !! making this argument optional in read_char() caused problems with all compilers recognizing the TKR 45 | class default 46 | error stop 'ERROR:h5fortran:readattr: string dataset ' // obj_name // ':' // attr_name // ' needs character memory variable' 47 | end select 48 | else 49 | error stop "ERROR:h5fortran:readattr: unknown attribute type for " // obj_name // ':' // attr_name 50 | endif 51 | 52 | 53 | call H5Aclose_f(attr_id, ier) 54 | call estop(ier, "attr_read:H5Aclose", self%filename, obj_name, attr_name) 55 | 56 | call H5Sclose_f(space_id, ier) 57 | call estop(ier, "attr_read:H5Sclose", self%filename, obj_name, attr_name) 58 | -------------------------------------------------------------------------------- /src/attr_write.f90: -------------------------------------------------------------------------------- 1 | submodule (h5fortran:attr_smod) attr_write 2 | 3 | use hdf5, only: H5Awrite_f 4 | 5 | implicit none 6 | 7 | contains 8 | 9 | module procedure writeattr_scalar 10 | include 'attr_write.inc' 11 | end procedure 12 | 13 | module procedure writeattr_1d 14 | include 'attr_write.inc' 15 | end procedure 16 | 17 | module procedure writeattr_2d 18 | include 'attr_write.inc' 19 | end procedure 20 | 21 | module procedure writeattr_3d 22 | include 'attr_write.inc' 23 | end procedure 24 | 25 | module procedure writeattr_4d 26 | include 'attr_write.inc' 27 | end procedure 28 | 29 | module procedure writeattr_5d 30 | include 'attr_write.inc' 31 | end procedure 32 | 33 | module procedure writeattr_6d 34 | include 'attr_write.inc' 35 | end procedure 36 | 37 | module procedure writeattr_7d 38 | include 'attr_write.inc' 39 | end procedure 40 | 41 | 42 | module procedure lt0writeattr 43 | 44 | type(hdf5_file) :: h 45 | 46 | call h%open(filename, action='r+') 47 | call h%writeattr(obj_name, attr, A) 48 | call h%close() 49 | 50 | end procedure lt0writeattr 51 | 52 | 53 | module procedure lt1writeattr 54 | 55 | type(hdf5_file) :: h 56 | 57 | call h%open(filename, action='r+') 58 | call h%writeattr(obj_name, attr, A) 59 | call h%close() 60 | 61 | end procedure lt1writeattr 62 | 63 | 64 | end submodule attr_write 65 | -------------------------------------------------------------------------------- /src/attr_write.inc: -------------------------------------------------------------------------------- 1 | integer :: ier, charlen 2 | integer(HID_T) :: dtype, attr_id, dtype_id 3 | integer(HSIZE_T) :: attr_dims(rank(A)) 4 | 5 | if(.not.self%is_open()) error stop 'ERROR:h5fortran:writeattr: file handle is not open' 6 | 7 | attr_dims = shape(A, HSIZE_T) 8 | 9 | charlen = 0 10 | 11 | select type(A) 12 | type is (real(real32)) 13 | dtype = H5T_NATIVE_REAL 14 | type is (real(real64)) 15 | dtype = H5T_NATIVE_DOUBLE 16 | type is (integer(int32)) 17 | dtype = H5T_NATIVE_INTEGER 18 | type is (integer(int64)) 19 | dtype = H5T_STD_I64LE 20 | type is(character(*)) 21 | dtype = H5T_NATIVE_CHARACTER 22 | charlen = len(A) !< workaround for GCC 8.3.0 bug 23 | if(charlen == 0) charlen = 1 !< empty string is OK but charlen is strictly positive. 24 | class default 25 | error stop "ERROR:h5fortran:writeattr: unknown dataset type for " // obj_name // ":" // attr // " in " // self%filename 26 | end select 27 | 28 | call attr_create(self, obj_name, attr, dtype, attr_dims, attr_id, dtype_id, charlen=charlen) 29 | 30 | select type(A) 31 | type is (real(real32)) 32 | call H5Awrite_f(attr_id, dtype_id, A, attr_dims, ier) 33 | type is (real(real64)) 34 | call H5Awrite_f(attr_id, dtype_id, A, attr_dims, ier) 35 | type is (integer(int32)) 36 | call H5Awrite_f(attr_id, dtype_id, A, attr_dims, ier) 37 | type is (integer(int64)) 38 | call H5Awrite_f(attr_id, dtype_id, A, attr_dims, ier) 39 | type is(character(*)) 40 | call H5Awrite_f(attr_id, dtype_id, A, attr_dims, ier) 41 | class default 42 | error stop "ERROR:h5fortran:writeattr: unknown dataset type for " // obj_name // ":" // attr // " in " // self%filename 43 | end select 44 | call estop(ier, "attr_write:H5Awrite", self%filename, obj_name, attr) 45 | 46 | call H5Tclose_f(dtype_id, ier) 47 | call estop(ier, "attr_write:H5Tclose", self%filename, obj_name, attr) 48 | 49 | call H5Aclose_f(attr_id, ier) 50 | call estop(ier, "attr_write:H5Aclose", self%filename, obj_name, attr) 51 | -------------------------------------------------------------------------------- /src/datatype.f90: -------------------------------------------------------------------------------- 1 | submodule (h5fortran:hdf5_read) h5f_datatype 2 | 3 | implicit none 4 | 5 | contains 6 | 7 | 8 | module procedure get_class 9 | 10 | integer(HID_T) :: dset_id 11 | integer :: ier 12 | 13 | call H5Dopen_f(self%file_id, dname, dset_id, ier) 14 | call estop(ier, "get_class:H5Dopen", self%filename, dname) 15 | 16 | call get_obj_class(self, dname, dset_id, get_class) 17 | 18 | call H5Dclose_f(dset_id, ier) 19 | call estop(ier, "get_class:H5Dclose", self%filename, dname) 20 | 21 | end procedure get_class 22 | 23 | 24 | module procedure get_obj_class 25 | 26 | integer :: ier, obj_type 27 | integer(HID_T) :: obj_dtype, native_dtype 28 | 29 | call H5Iget_type_f(obj_id, obj_type, ier) 30 | call estop(ier, "get_obj_class:H5Iget_type", self%filename, obj_name) 31 | 32 | if(obj_type == H5I_DATASET_F) then 33 | call H5Dget_type_f(obj_id, obj_dtype, ier) 34 | elseif(obj_type == H5I_ATTR_F) then 35 | call H5Aget_type_f(obj_id, obj_dtype, ier) 36 | else 37 | error stop "ERROR:h5fortran:get_obj_class: only datasets and attributes have datatype " // obj_name // " " // self%filename 38 | endif 39 | call estop(ier, "get_obj_class:H5[A,D]get_type", self%filename, obj_name) 40 | 41 | call H5Tget_native_type_f(obj_dtype, H5T_DIR_ASCEND_F, native_dtype, ier) 42 | call estop(ier, "get_obj_class:H5Tget_native_type", self%filename, obj_name) 43 | 44 | !> compose datatype inferred 45 | call H5Tget_class_f(native_dtype, class, ier) 46 | call estop(ier, "get_obj_class:H5Tget_class", self%filename, obj_name) 47 | 48 | if(present(size_bytes)) then 49 | call H5Tget_size_f(native_dtype, size_bytes, ier) 50 | call estop(ier, "get_obj_class:H5Tget_size", self%filename, obj_name) 51 | endif 52 | 53 | call H5Tclose_f(native_dtype, ier) 54 | call estop(ier, "get_obj_class:H5Tclose", self%filename, obj_name) 55 | 56 | if(present(pad_type)) then 57 | if(class /= H5T_STRING_F) error stop "ERROR:h5fortran:get_class: pad_type only for string" 58 | 59 | call H5Tget_strpad_f(obj_dtype, pad_type, ier) 60 | call estop(ier, "get_obj_class:H5Tget_strpad", self%filename, obj_name) 61 | endif 62 | 63 | call H5Tclose_f(obj_dtype, ier) 64 | call estop(ier, "get_obj_class:H5Tclose", self%filename, obj_name) 65 | 66 | end procedure get_obj_class 67 | 68 | 69 | module procedure get_native_dtype 70 | !! get the dataset variable type: 71 | !! {H5T_NATIVE_REAL, H5T_NATIVE_DOUBLE, H5T_NATIVE_INTEGER, H5T_NATIVE_CHARACTER, H5T_STD_I64LE} 72 | 73 | integer :: class, ier 74 | ! integer :: order, machine_order 75 | integer(size_t) :: size_bytes 76 | integer(HID_T) :: o_id 77 | 78 | if(present(obj_id)) then 79 | o_id = obj_id 80 | else 81 | !! assume dataset 82 | call H5Dopen_f(self%file_id, dname, o_id, ier) 83 | call estop(ier, "get_native_dtype:H5Dopen", self%filename, dname) 84 | endif 85 | 86 | call get_obj_class(self, dname, o_id, class, size_bytes=size_bytes) 87 | 88 | if(.not.present(obj_id)) then 89 | call H5Dclose_f(o_id, ier) 90 | call estop(ier, "get_native_dtype:H5Dclose", self%filename, dname) 91 | endif 92 | 93 | !> endianness and within type casting is handled by HDF5 94 | ! call h5tget_order_f(native_dtype, order, ier) 95 | ! if(ier/=0) error stop 'ERROR:h5fortran:reader: get endianness ' // dname // ' from ' // self%filename 96 | ! !> check dataset endianness matches machine (in future, could swap endianness if needed) 97 | ! call h5tget_order_f(H5T_NATIVE_INTEGER, machine_order, ier) 98 | ! if(order /= machine_order) error stop 'ERROR:h5fortran:read: endianness /= machine native: ' & 99 | ! // dname // ' from ' // self%filename 100 | 101 | if(class == H5T_INTEGER_F) then 102 | if(size_bytes == 4) then 103 | get_native_dtype = H5T_NATIVE_INTEGER 104 | elseif(size_bytes == 8) then 105 | get_native_dtype = H5T_STD_I64LE 106 | else 107 | error stop "ERROR:h5fortran:get_native_dtype: expected 32-bit or 64-bit integer:" // dname // ' from ' // self%filename 108 | endif 109 | elseif(class == H5T_FLOAT_F) then 110 | if(size_bytes == 4) then 111 | get_native_dtype = H5T_NATIVE_REAL 112 | elseif(size_bytes == 8) then 113 | get_native_dtype = H5T_NATIVE_DOUBLE 114 | else 115 | error stop "ERROR:h5fortran:get_native_dtype: expected 32-bit or 64-bit real:" // dname // ' from ' // self%filename 116 | endif 117 | elseif(class == H5T_STRING_F) then 118 | get_native_dtype = H5T_NATIVE_CHARACTER 119 | else 120 | error stop "ERROR:h5fortran:get_native_dtype: non-handled datatype: " // dname // " from " // self%filename 121 | endif 122 | 123 | end procedure get_native_dtype 124 | 125 | end submodule h5f_datatype 126 | -------------------------------------------------------------------------------- /src/deflate.f90: -------------------------------------------------------------------------------- 1 | submodule (h5fortran:hdf5_read) h5f_deflate 2 | 3 | use hdf5, only : H5Z_FILTER_DEFLATE_F 4 | 5 | implicit none 6 | 7 | contains 8 | 9 | module procedure hdf_get_chunk 10 | 11 | integer :: ier, drank 12 | integer(HID_T) :: dapl, dset_id, space_id 13 | integer(HSIZE_T) :: cs(size(chunk_size)) 14 | 15 | cs = -1 16 | 17 | if (.not.self%exist(dname)) error stop 'ERROR:h5fortran:get_chunk: ' // dname // ' does not exist in ' // self%filename 18 | 19 | if(self%is_chunked(dname)) then 20 | call H5Dopen_f(self%file_id, dname, dset_id, ier) 21 | call estop(ier, "get_chunk:H5Dopen", self%filename, dname) 22 | 23 | call H5Dget_space_f(dset_id, space_id, ier) 24 | call estop(ier, "get_chunk:H5Dget_space", self%filename, dname) 25 | call H5Sget_simple_extent_ndims_f(space_id, drank, ier) 26 | call estop(ier, "get_chunk:H5Sget_simple_extent_ndims", self%filename, dname) 27 | call H5Sclose_f(space_id, ier) 28 | call estop(ier, "get_chunk:H5Sclose", self%filename, dname) 29 | 30 | call h5dget_create_plist_f(dset_id, dapl, ier) 31 | call estop(ier, "get_chunk:H5Dget_create_plist", self%filename, dname) 32 | 33 | call h5dclose_f(dset_id, ier) 34 | call estop(ier, "get_chunk:H5Dclose", self%filename, dname) 35 | 36 | call h5pget_chunk_f(dapl, drank, cs, ier) 37 | if (ier /= drank) error stop 'ERROR:h5fortran:get_chunk:h5pget_chunk ' // dname // ' ' // self%filename 38 | !! yes ier == drank is success for this call 39 | 40 | call h5pclose_f(dapl, ier) 41 | call estop(ier, "get_chunk:H5Pclose", self%filename, dname) 42 | endif 43 | 44 | select type (chunk_size) 45 | type is (integer(HSIZE_T)) 46 | chunk_size = cs 47 | type is (integer(int32)) 48 | chunk_size = int(cs) 49 | class default 50 | error stop 'ERROR:h5fortran:get_chunk: unknown type for chunk_size' 51 | end select 52 | 53 | end procedure hdf_get_chunk 54 | 55 | 56 | module procedure get_deflate 57 | !! h5pget_filter_f doesn't work collectively, will crash on h5fclose_f 58 | !! if(mpi_id==0) with mpi_bcast does not work, same crash. 59 | !! better to use H5Pall_filters_avail_f when mpi=.true. 60 | 61 | integer :: i, j, ier 62 | integer :: flags !< bit pattern 63 | integer(HID_T) :: dcpl, dset_id 64 | integer(SIZE_T) :: Naux 65 | integer :: Aux(8) !< arbitrary length 66 | integer :: Nf, filter_id 67 | character(32) :: filter_name 68 | 69 | logical :: debug = .false. 70 | 71 | 72 | get_deflate = .false. 73 | 74 | Naux = size(Aux, kind=SIZE_T) 75 | 76 | if(.not.self%exist(dname)) error stop "ERROR:h5fortran:get_deflate: " // dname // " does not exist: " // self%filename 77 | call H5Dopen_f(self%file_id, dname, dset_id, ier) 78 | call estop(ier, "get_deflate:H5Dopen", self%filename, dname) 79 | 80 | call h5dget_create_plist_f(dset_id, dcpl, ier) 81 | call estop(ier, "get_deflate:H5Dget_create_plist", self%filename, dname) 82 | 83 | call H5Dclose_f(dset_id, ier) 84 | call estop(ier, "get_deflate:H5Dclose", self%filename, dname) 85 | 86 | call h5pget_nfilters_f(dcpl, Nf, ier) 87 | call estop(ier, "get_deflate:H5Pget_nfilters", self%filename, dname) 88 | 89 | filters: do i = 1, Nf 90 | filter_name = "" 91 | 92 | call h5pget_filter_f(dcpl, i, & 93 | flags, & 94 | Naux, Aux, & 95 | len(filter_name, SIZE_T), filter_name, & 96 | filter_id, ier) 97 | call estop(ier, "get_deflate:H5Pget_filter", self%filename, dname) 98 | if(filter_id < 0) write(stderr,'(a,i0)') "ERROR:h5fortran:get_deflate:h5pget_filter: index error " // dname, i 99 | 100 | if (debug) then 101 | j = index(filter_name, c_null_char) 102 | if(j>0) print *, "TRACE:get_filter: filter name: ", filter_name(:j-1) 103 | endif 104 | 105 | get_deflate = filter_id == H5Z_FILTER_DEFLATE_F 106 | if(get_deflate) exit filters 107 | 108 | end do filters 109 | 110 | call h5pclose_f(dcpl, ier) 111 | 112 | end procedure get_deflate 113 | 114 | 115 | end submodule h5f_deflate 116 | -------------------------------------------------------------------------------- /src/read.f90: -------------------------------------------------------------------------------- 1 | submodule (h5fortran) hdf5_read 2 | 3 | use, intrinsic:: iso_c_binding, only : c_null_char 4 | 5 | use hdf5, only : & 6 | H5Aget_space_f, H5Aget_type_f, H5Aopen_by_name_f, H5Aclose_f, H5Aget_storage_size_f, & 7 | h5pget_layout_f, h5pget_chunk_f, h5pclose_f, h5pget_nfilters_f, h5pget_filter_f, & 8 | H5Dget_create_plist_f, h5dget_type_f, h5dopen_f, h5dclose_f, H5Dget_space_f, H5Dget_storage_size_f, & 9 | H5Iget_type_f, & 10 | h5lexists_f, & 11 | H5Sget_simple_extent_ndims_f, H5Sget_simple_extent_dims_f, H5Sget_simple_extent_npoints_f, H5Sclose_f, & 12 | h5tclose_f, h5tget_native_type_f, h5tget_class_f, H5Tget_order_f, h5tget_size_f, h5tget_strpad_f, H5Tis_variable_str_f, & 13 | H5T_DIR_ASCEND_F, & 14 | H5I_ATTR_F, H5I_DATASET_F 15 | 16 | use H5LT, only : h5ltpath_valid_f, h5ltget_dataset_ndims_f 17 | 18 | implicit none 19 | 20 | contains 21 | 22 | 23 | module procedure get_strpad 24 | !! H5T_STR_NULLTERM Null terminate (as C does). 25 | !! H5T_STR_NULLPAD Pad with zeros. 26 | !! H5T_STR_SPACEPAD Pad with spaces (as FORTRAN does). 27 | 28 | integer :: class 29 | integer(HID_T) :: dset_id 30 | integer :: ier 31 | 32 | call H5Dopen_f(self%file_id, dset_name, dset_id, ier) 33 | call estop(ier, "get_strpad:H5Dopen", self%filename, dset_name) 34 | 35 | call get_obj_class(self, dset_name, dset_id, class, pad_type=get_strpad) 36 | 37 | call H5Dclose_f(dset_id, ier) 38 | call estop(ier, "get_strpad:H5Dclose", self%filename, dset_name) 39 | 40 | end procedure get_strpad 41 | 42 | 43 | module procedure hdf_get_ndim 44 | !! get rank or "ndims" 45 | integer :: ier 46 | 47 | if (.not. self%exist(dname)) error stop 'ERROR:h5fortran:get_ndim: ' // dname // ' does not exist in ' // self%filename 48 | 49 | call H5LTget_dataset_ndims_f(self%file_id, dname, drank, ier) 50 | call estop(ier, "get_ndim:H5LTget_dataset_ndims", self%filename, dname) 51 | 52 | end procedure hdf_get_ndim 53 | 54 | 55 | module procedure hdf_get_shape 56 | 57 | integer :: drank, ier 58 | integer(HID_T) :: obj_id, space_id 59 | integer(HSIZE_T), allocatable :: maxdims(:) 60 | 61 | 62 | if(.not. self%exist(obj_name)) error stop 'ERROR:h5fortran:get_shape: ' // obj_name // ' does not exist in ' // self%filename 63 | 64 | if(present(attr_name)) then 65 | call H5Aopen_by_name_f(self%file_id, obj_name, attr_name, obj_id, ier) 66 | call estop(ier, "get_shape:H5Aopen_by_name", self%filename, obj_name) 67 | call H5Aget_space_f(obj_id, space_id, ier) 68 | call estop(ier, "get_shape:H5Aget_space", self%filename, obj_name) 69 | else 70 | call H5Dopen_f(self%file_id, obj_name, obj_id, ier) 71 | call estop(ier, "get_shape:H5Dopen", self%filename, obj_name) 72 | call H5Dget_space_f(obj_id, space_id, ier) 73 | call estop(ier, "get_shape:H5Dget_space", self%filename, obj_name) 74 | endif 75 | 76 | 77 | 78 | call H5Sget_simple_extent_ndims_f(space_id, drank, ier) 79 | call estop(ier, "get_shape:H5Sget_simple_extent_ndims", self%filename, obj_name) 80 | 81 | allocate(dims(drank), maxdims(drank)) 82 | call H5Sget_simple_extent_dims_f(space_id, dims, maxdims, ier) 83 | if (ier /= drank) error stop 'ERROR:h5fortran:get_shape:H5Sget_simple_extent_dims: ' // obj_name // ' in ' // self%filename 84 | 85 | call H5Sclose_f(space_id, ier) 86 | call estop(ier, "get_shape:H5Sclose", self%filename, obj_name) 87 | 88 | if(present(attr_name)) then 89 | call H5Aclose_f(obj_id, ier) 90 | call estop(ier, "get_shape:H5Aclose", self%filename, obj_name) 91 | else 92 | call H5Dclose_f(obj_id, ier) 93 | call estop(ier, "get_shape:H5Dclose", self%filename, obj_name) 94 | endif 95 | 96 | end procedure hdf_get_shape 97 | 98 | 99 | module procedure hdf_get_layout 100 | 101 | integer(HID_T) :: dapl, dset_id 102 | integer :: ier 103 | 104 | layout = -1 105 | 106 | if (.not. self%exist(dname)) error stop 'ERROR:h5fortran:get_layout: ' // dname // ' does not exist in ' // self%filename 107 | 108 | call h5dopen_f(self%file_id, dname, dset_id, ier) 109 | call estop(ier, "get_layout:H5Dopen", self%filename, dname) 110 | 111 | call h5dget_create_plist_f(dset_id, dapl, ier) 112 | call estop(ier, "get_layout:H5Dget_create_plist", self%filename, dname) 113 | 114 | call h5dclose_f(dset_id, ier) 115 | call estop(ier, "get_layout:H5Dclose", self%filename, dname) 116 | 117 | call h5pget_layout_f(dapl, layout, ier) 118 | call estop(ier, "get_layout:H5Pget_layout", self%filename, dname) 119 | 120 | call h5pclose_f(dapl, ier) 121 | call estop(ier, "get_layout:H5Pclose", self%filename, dname) 122 | 123 | end procedure hdf_get_layout 124 | 125 | 126 | module procedure hdf_check_exist 127 | 128 | integer :: ier 129 | 130 | if(.not. self%is_open()) error stop 'ERROR:h5fortran:exist: file handle is not open: ' // self%filename 131 | 132 | if(len_trim(obj_name) == 0) error stop "ERROR:h5fortran:check_exist: object name must not be empty" 133 | 134 | call h5ltpath_valid_f(self%file_id, obj_name, .true., hdf_check_exist, ier) 135 | !! h5lexists_f can false error with groups--just use h5ltpath_valid 136 | 137 | call estop(ier, "check_exist:H5LTpath_valid", self%filename, obj_name) 138 | 139 | end procedure hdf_check_exist 140 | 141 | 142 | end submodule hdf5_read 143 | -------------------------------------------------------------------------------- /src/read_char.inc: -------------------------------------------------------------------------------- 1 | integer(HID_T) :: type_id 2 | integer :: ier, charlen, obj_type 3 | logical :: is_vlen 4 | 5 | !> variable length string 6 | integer(HSIZE_T) :: dims(rank(A)), Npts, dsize 7 | 8 | charlen = len(A) 9 | 10 | call open_char(self, obj_name, obj_id, file_space_id, charlen, obj_type, type_id, dims, Npts, dsize, is_vlen) 11 | 12 | if(is_vlen) then 13 | call read_vlen(self, obj_name, obj_type, obj_id, type_id, dsize, A, mem_space_id, file_space_id) 14 | else 15 | call read_fixed(self, obj_name, obj_type, obj_id, type_id, dsize, A, mem_space_id, file_space_id) 16 | endif 17 | 18 | call H5Tclose_f(type_id, ier) 19 | call estop(ier, "read_char:H5Tclose", self%filename, obj_name) 20 | -------------------------------------------------------------------------------- /src/read_fixed.inc: -------------------------------------------------------------------------------- 1 | class(hdf5_file), intent(in) :: self 2 | character(*), intent(in) :: obj_name 3 | integer, intent(in) :: obj_type 4 | integer(HID_T), intent(in) :: obj_id, type_id, mem_space_id, file_space_id 5 | 6 | TYPE(C_PTR) :: f_ptr 7 | 8 | integer :: ier 9 | 10 | allocate(buf, mold=A) 11 | f_ptr = C_LOC(buf) 12 | 13 | if(obj_type == H5I_DATASET_F) then 14 | call H5Dread_f(obj_id, type_id, f_ptr, ier, mem_space_id, file_space_id) 15 | elseif(obj_type == H5I_ATTR_F) then 16 | call H5Aread_f(obj_id, type_id, f_ptr, ier) 17 | endif 18 | call estop(ier, "read_fixed:H5[D,A]read", self%filename, obj_name) 19 | 20 | A = pad_trim(buf) 21 | -------------------------------------------------------------------------------- /src/read_scalar.f90: -------------------------------------------------------------------------------- 1 | submodule (h5fortran:hdf5_read) read_scalar 2 | 3 | use hdf5, only : H5Dread_f 4 | 5 | implicit none 6 | 7 | 8 | contains 9 | 10 | 11 | module procedure h5read_scalar 12 | 13 | integer(HSIZE_T) :: dims(0) 14 | integer(HID_T) :: dset_id, xfer_id, file_space_id, mem_space_id 15 | integer :: dclass, ier 16 | 17 | logical :: is_scalar 18 | 19 | 20 | call H5Dopen_f(self%file_id, dname, dset_id, ier) 21 | call estop(ier, "read_scalar:H5Dopen", self%filename, dname) 22 | call H5Dget_space_f(dset_id, file_space_id, ier) 23 | call estop(ier, "read_scalar:H5Dget_space", self%filename, dname) 24 | 25 | call hdf_rank_check(self, dname, file_space_id, 0, is_scalar) 26 | 27 | if (is_scalar) then 28 | call hdf_get_slice(dims, dset_id, file_space_id, mem_space_id, [1], [1]) 29 | else 30 | call H5Dget_space_f(dset_id, mem_space_id, ier) 31 | call estop(ier, "read_scalar:H5Dget_space", self%filename, dname) 32 | endif 33 | 34 | call get_obj_class(self, dname, dset_id, dclass) 35 | 36 | xfer_id = H5P_DEFAULT_F 37 | 38 | !> cast the dataset read from disk to the variable type presented by user h5f%read("/my_dataset", x) 39 | !> We only cast when needed to save memory. 40 | !! select case doesn't allow H5T_* 41 | !! https://support.hdfgroup.org/HDF5/doc/UG/HDF5_Users_Guide-Responsive%20HTML5/index.html#t=HDF5_Users_Guide%2FDatatypes%2FHDF5_Datatypes.htm%23TOC_6_10_Data_Transferbc-26&rhtocid=6.5_2 42 | if(dclass == H5T_FLOAT_F .OR. dclass == H5T_INTEGER_F) then 43 | select type(A) 44 | type is (real(real64)) 45 | call H5Dread_f(dset_id, H5T_NATIVE_DOUBLE, A, dims, ier, mem_space_id, file_space_id) 46 | type is (real(real32)) 47 | call H5Dread_f(dset_id, H5T_NATIVE_REAL, A, dims, ier, mem_space_id, file_space_id) 48 | type is (integer(int32)) 49 | call H5Dread_f(dset_id, H5T_NATIVE_INTEGER, A, dims, ier, mem_space_id, file_space_id) 50 | type is (integer(int64)) 51 | call H5Dread_f(dset_id, H5T_STD_I64LE, A, dims, ier, mem_space_id, file_space_id) 52 | class default 53 | error stop 'ERROR:h5fortran:read: numeric dataset ' // dname // ' needs numeric memory variable' 54 | end select 55 | call estop(ier, "read_scalar:H5Dread", self%filename, dname) 56 | elseif(dclass == H5T_STRING_F) then 57 | select type(A) 58 | type is (character(*)) !< kind=c_char too 59 | call read_char0(self, dname, A, dset_id, mem_space_id, file_space_id) 60 | class default 61 | error stop 'ERROR:h5fortran:read: string dataset ' // dname // ' needs character memory variable' 62 | end select 63 | else 64 | error stop 'ERROR:h5fortran:reader: non-handled datatype--please reach out to developers.' 65 | end if 66 | 67 | call H5Dclose_f(dset_id, ier) 68 | call estop(ier, "read_scalar:H5Dclose", self%filename, dname) 69 | 70 | call H5Sclose_f(mem_space_id, ier) 71 | call estop(ier, "read_scalar:H5Sclose", self%filename, dname) 72 | 73 | call H5Sclose_f(file_space_id, ier) 74 | call estop(ier, "read_scalar:H5Sclose", self%filename, dname) 75 | 76 | end procedure h5read_scalar 77 | 78 | end submodule read_scalar 79 | -------------------------------------------------------------------------------- /src/read_vlen.inc: -------------------------------------------------------------------------------- 1 | class(hdf5_file), intent(in) :: self 2 | character(*), intent(in) :: obj_name 3 | integer, intent(in) :: obj_type 4 | integer(HID_T), intent(in) :: obj_id, type_id, mem_space_id, file_space_id 5 | integer(HSIZE_T), intent(in) :: dsize 6 | 7 | TYPE(C_PTR), DIMENSION(:), ALLOCATABLE, TARGET :: cbuf 8 | character(dsize), dimension(:), allocatable :: buf 9 | CHARACTER(dsize, kind=C_CHAR), POINTER :: cstr 10 | TYPE(C_PTR) :: f_ptr 11 | 12 | integer :: ier, dims(rank(A)) 13 | integer(HSIZE_T) :: i 14 | 15 | dims = shape(A) 16 | 17 | allocate(cbuf(product(dims)), buf(product(dims))) 18 | f_ptr = C_LOC(cbuf) 19 | 20 | if(obj_type == H5I_DATASET_F) then 21 | call H5Dread_f(obj_id, type_id, f_ptr, ier, mem_space_id, file_space_id) 22 | elseif(obj_type == H5I_ATTR_F) then 23 | call H5Aread_f(obj_id, type_id, f_ptr, ier) 24 | endif 25 | call estop(ier, "read_vlen:H5[D,A]read", self%filename, obj_name) 26 | 27 | do i = 1, size(cbuf) 28 | call C_F_POINTER(cbuf(i), cstr) 29 | buf(i) = pad_trim(cstr) 30 | enddo 31 | 32 | A = reshape(buf, dims) 33 | -------------------------------------------------------------------------------- /src/reader.f90: -------------------------------------------------------------------------------- 1 | submodule (h5fortran:hdf5_read) hdf5_reader 2 | 3 | use hdf5, only : H5Dread_f 4 | 5 | implicit none 6 | 7 | contains 8 | 9 | 10 | module procedure h5read_1d 11 | include "reader.inc" 12 | end procedure 13 | 14 | module procedure h5read_2d 15 | include "reader.inc" 16 | end procedure 17 | 18 | module procedure h5read_3d 19 | include "reader.inc" 20 | end procedure 21 | 22 | module procedure h5read_4d 23 | include "reader.inc" 24 | end procedure 25 | 26 | module procedure h5read_5d 27 | include "reader.inc" 28 | end procedure 29 | 30 | module procedure h5read_6d 31 | include "reader.inc" 32 | end procedure 33 | 34 | module procedure h5read_7d 35 | include "reader.inc" 36 | end procedure 37 | 38 | end submodule hdf5_reader 39 | -------------------------------------------------------------------------------- /src/reader.inc: -------------------------------------------------------------------------------- 1 | integer(HSIZE_T), dimension(rank(A)) :: dims 2 | integer(HID_T) :: dset_id, file_space_id, mem_space_id, xfer_id 3 | integer :: dclass, ier 4 | 5 | 6 | dims = shape(A, HSIZE_T) 7 | 8 | call h5dopen_f(self%file_id, dname, dset_id, ier) 9 | if(ier/=0) error stop 'ERROR:h5fortran:reader:H5Dopen ' // dname // ' from ' // self%filename 10 | call H5Dget_space_f(dset_id, file_space_id, ier) 11 | if(ier/=0) error stop 'ERROR:h5fortran:reader:H5Dget_space ' // dname // ' from ' // self%filename 12 | 13 | if(present(istart) .and. present(iend)) then 14 | call hdf_get_slice(dims, dset_id, file_space_id, mem_space_id, istart=istart, iend=iend, stride=stride) 15 | else 16 | call hdf_shape_check(self, dname, file_space_id, dims) 17 | 18 | call H5Dget_space_f(dset_id, mem_space_id, ier) 19 | if(ier/=0) error stop "ERROR:h5fortran:reader:H5Dget_space " // dname 20 | endif 21 | 22 | xfer_id = H5P_DEFAULT_F 23 | 24 | call get_obj_class(self, dname, dset_id, dclass) 25 | 26 | !> casting is handled by HDF5 library internally 27 | !! select case doesn't allow H5T_* 28 | if(dclass == H5T_FLOAT_F .OR. dclass == H5T_INTEGER_F) then 29 | select type(A) 30 | type is (real(real64)) 31 | call h5dread_f(dset_id, H5T_NATIVE_DOUBLE, A, dims, ier, mem_space_id, file_space_id, xfer_id) 32 | type is (real(real32)) 33 | call h5dread_f(dset_id, H5T_NATIVE_REAL, A, dims, ier, mem_space_id, file_space_id, xfer_id) 34 | type is (integer(int32)) 35 | call h5dread_f(dset_id, H5T_NATIVE_INTEGER, A, dims, ier, mem_space_id, file_space_id, xfer_id) 36 | type is (integer(int64)) 37 | call h5dread_f(dset_id, H5T_STD_I64LE, A, dims, ier, mem_space_id, file_space_id, xfer_id) 38 | class default 39 | error stop 'ERROR:h5fortran:read: integer disk dataset ' // dname // ' needs integer memory variable' 40 | end select 41 | call estop(ier, "reader:H5Dread", self%filename, dname) 42 | elseif(dclass == H5T_STRING_F) then 43 | select type(A) 44 | type is (character(*)) !< kind=c_char too 45 | call read_char(self, dname, A, dset_id, mem_space_id, file_space_id) 46 | class default 47 | error stop 'ERROR:h5fortran:read: string dataset ' // dname // ' needs character memory variable' 48 | end select 49 | else 50 | error stop 'ERROR:h5fortran:reader: non-handled datatype--please reach out to developers.' 51 | end if 52 | 53 | call H5Dclose_f(dset_id, ier) 54 | call estop(ier, "reader:H5Dclose", self%filename, dname) 55 | 56 | call H5Sclose_f(mem_space_id, ier) 57 | call estop(ier, "reader:H5Sclose", self%filename, dname) 58 | 59 | call H5Sclose_f(file_space_id, ier) 60 | call estop(ier, "reader:H5Sclose", self%filename, dname) 61 | -------------------------------------------------------------------------------- /src/reader_lt.f90: -------------------------------------------------------------------------------- 1 | submodule (h5fortran:hdf5_read) reader_lt 2 | 3 | implicit none 4 | 5 | contains 6 | 7 | module procedure h5exist 8 | 9 | type(hdf5_file) :: h 10 | 11 | logical :: d 12 | 13 | d = .false. 14 | if(present(debug)) d = debug 15 | 16 | call h%open(filename, action='r', debug=d) 17 | h5exist = h%exist(dname) 18 | call h%close() 19 | 20 | end procedure h5exist 21 | 22 | 23 | module procedure lt0read 24 | include "reader_lt.inc" 25 | end procedure 26 | 27 | module procedure lt1read 28 | include "reader_lt.inc" 29 | end procedure 30 | 31 | module procedure lt2read 32 | include "reader_lt.inc" 33 | end procedure 34 | 35 | module procedure lt3read 36 | include "reader_lt.inc" 37 | end procedure 38 | 39 | module procedure lt4read 40 | include "reader_lt.inc" 41 | end procedure 42 | 43 | module procedure lt5read 44 | include "reader_lt.inc" 45 | end procedure 46 | 47 | module procedure lt6read 48 | include "reader_lt.inc" 49 | end procedure 50 | 51 | module procedure lt7read 52 | include "reader_lt.inc" 53 | end procedure 54 | 55 | end submodule reader_lt 56 | -------------------------------------------------------------------------------- /src/reader_lt.inc: -------------------------------------------------------------------------------- 1 | type(hdf5_file) :: h 2 | 3 | call h%open(filename, action='r') 4 | call h%read(dname, A) 5 | call h%close() 6 | -------------------------------------------------------------------------------- /src/write_scalar.f90: -------------------------------------------------------------------------------- 1 | submodule (h5fortran:write) write_scalar 2 | 3 | use hdf5, only: H5Dwrite_f 4 | 5 | implicit none 6 | 7 | contains 8 | 9 | 10 | module procedure h5write_scalar 11 | 12 | integer(HSIZE_T) :: dset_dims(0), mem_dims(0) 13 | integer(HID_T) :: file_space_id, dset_id, dtype_id, xfer_id, dtypew 14 | integer :: ier, charlen 15 | 16 | charlen = 0 17 | 18 | if (present(datatype)) then 19 | dtypew = datatype 20 | else 21 | select type (A) 22 | type is (real(real32)) 23 | dtypew = H5T_NATIVE_REAL 24 | type is (real(real64)) 25 | dtypew = H5T_NATIVE_DOUBLE 26 | type is (integer(int32)) 27 | dtypew = H5T_NATIVE_INTEGER 28 | type is (integer(int64)) 29 | dtypew = H5T_STD_I64LE 30 | type is (character(*)) 31 | dtypew = H5T_NATIVE_CHARACTER 32 | class default 33 | error stop "ERROR:h5fortran:write: unknown variable type for " // dname 34 | end select 35 | endif 36 | 37 | select type (A) 38 | type is (character(*)) 39 | charlen = len_trim(A) !< workaround for GCC 8.3.0 bug 40 | if(charlen == 0) charlen = 1 !< empty string is OK but charlen is strictly positive. 41 | end select 42 | 43 | call hdf_create(self, dname, dtypew, mem_dims=mem_dims, dset_dims=dset_dims, & 44 | filespace_id=file_space_id, dset_id=dset_id, compact=compact, dtype_id=dtype_id, & 45 | charlen=charlen) 46 | 47 | xfer_id = H5P_DEFAULT_F 48 | 49 | select type (A) 50 | type is (real(real32)) 51 | call H5Dwrite_f(dset_id, dtypew, A, dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id) 52 | type is (real(real64)) 53 | call H5Dwrite_f(dset_id, dtypew, A, dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id) 54 | type is (integer(int32)) 55 | call H5Dwrite_f(dset_id, dtypew, A, dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id) 56 | type is (integer(int64)) 57 | call H5Dwrite_f(dset_id, dtypew, A, dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id) 58 | type is (character(*)) 59 | call H5Dwrite_f(dset_id, dtype_id, trim(A), dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id) 60 | class default 61 | error stop "ERROR:h5fortran:write: unsupported type for " // dname 62 | end select 63 | call estop(ier, "write_scalar:H5Dwrite", self%filename, dname) 64 | 65 | call H5Tclose_f(dtype_id, ier) 66 | call estop(ier, "write_scalar:H5Tclose", self%filename, dname) 67 | 68 | call H5Dclose_f(dset_id, ier) 69 | call estop(ier, "write_scalar:H5Dclose", self%filename, dname) 70 | 71 | call H5Sclose_f(file_space_id, ier) 72 | call estop(ier, "write_scalar:H5Sclose", self%filename, dname) 73 | 74 | 75 | end procedure h5write_scalar 76 | 77 | end submodule write_scalar 78 | -------------------------------------------------------------------------------- /src/writer.f90: -------------------------------------------------------------------------------- 1 | submodule (h5fortran:write) writer 2 | 3 | use hdf5, only: H5Dwrite_f 4 | 5 | implicit none 6 | 7 | contains 8 | 9 | module procedure h5write_1d 10 | include "writer.inc" 11 | end procedure 12 | 13 | module procedure h5write_2d 14 | include "writer.inc" 15 | end procedure 16 | 17 | module procedure h5write_3d 18 | include "writer.inc" 19 | end procedure 20 | 21 | module procedure h5write_4d 22 | include "writer.inc" 23 | end procedure 24 | 25 | module procedure h5write_5d 26 | include "writer.inc" 27 | end procedure 28 | 29 | module procedure h5write_6d 30 | include "writer.inc" 31 | end procedure 32 | 33 | module procedure h5write_7d 34 | include "writer.inc" 35 | end procedure 36 | 37 | end submodule writer 38 | -------------------------------------------------------------------------------- /src/writer.inc: -------------------------------------------------------------------------------- 1 | integer(HID_T) :: file_space_id, mem_space_id, dset_id, xfer_id, dtypew, dtype_id 2 | integer(HSIZE_T), dimension(rank(A)) :: mem_dims, dims_dset 3 | integer :: ier, charlen 4 | 5 | mem_dims = shape(A, HSIZE_T) 6 | 7 | if(present(dset_dims)) then 8 | dims_dset = dset_dims 9 | else 10 | dims_dset = mem_dims 11 | endif 12 | 13 | charlen = 0 14 | 15 | if (present(datatype)) then 16 | dtypew = datatype 17 | else 18 | select type (A) 19 | type is (real(real32)) 20 | dtypew = H5T_NATIVE_REAL 21 | type is (real(real64)) 22 | dtypew = H5T_NATIVE_DOUBLE 23 | type is (integer(int32)) 24 | dtypew = H5T_NATIVE_INTEGER 25 | type is (integer(int64)) 26 | dtypew = H5T_STD_I64LE 27 | type is (character(*)) 28 | dtypew = H5T_NATIVE_CHARACTER 29 | class default 30 | error stop "ERROR:h5fortran:writer:unknown variable type for " // dname 31 | end select 32 | endif 33 | !! https://docs.hdfgroup.org/archive/support/HDF5/doc/RM/PredefDTypes.html#F90 34 | 35 | select type (A) 36 | type is (character(*)) 37 | charlen = len(A) !< workaround for GCC 8.3.0 bug 38 | if(charlen == 0) charlen = 1 !< empty string is OK but charlen is strictly positive. 39 | end select 40 | 41 | call hdf_create(self, dname, dtypew, mem_dims, dims_dset, file_space_id, dset_id, & 42 | chunk_size=chunk_size, istart=istart, iend=iend, stride=stride, compact=compact, & 43 | dtype_id=dtype_id, charlen=charlen) 44 | 45 | if(present(istart) .and. present(iend)) then 46 | call hdf_get_slice(mem_dims, dset_id, file_space_id, mem_space_id, istart, iend, stride=stride, dset_dims=dims_dset) 47 | else 48 | call H5Dget_space_f(dset_id, mem_space_id, ier) 49 | call estop(ier, "writer:H5Dget_space", self%filename, dname) 50 | endif 51 | 52 | xfer_id = H5P_DEFAULT_F 53 | 54 | select type (A) 55 | type is (real(real32)) 56 | call h5dwrite_f(dset_id, dtypew, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id) 57 | type is (real(real64)) 58 | call h5dwrite_f(dset_id, dtypew, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id) 59 | type is (integer(int32)) 60 | call h5dwrite_f(dset_id, dtypew, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id) 61 | type is (integer(int64)) 62 | call h5dwrite_f(dset_id, dtypew, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id) 63 | type is (character(*)) 64 | call h5dwrite_f(dset_id, dtype_id, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id) 65 | class default 66 | error stop "ERROR:h5fortran:writer: unknown variable type for " // dname 67 | end select 68 | call estop(ier, "writer:H5Dwrite", self%filename, dname) 69 | 70 | call H5Tclose_f(dtype_id, ier) 71 | call estop(ier, "writer:H5Tclose", self%filename, dname) 72 | 73 | call H5Dclose_f(dset_id, ier) 74 | call estop(ier, "writer:H5Dclose", self%filename, dname) 75 | 76 | call H5Sclose_f(mem_space_id, ier) 77 | call estop(ier, "writer:H5Sclose", self%filename, dname) 78 | 79 | call H5Sclose_f(file_space_id, ier) 80 | call estop(ier, "writer:H5Sclose", self%filename, dname) 81 | -------------------------------------------------------------------------------- /src/writer_lt.f90: -------------------------------------------------------------------------------- 1 | submodule (h5fortran:write) writer_lt 2 | 3 | implicit none 4 | 5 | contains 6 | 7 | 8 | module procedure lt0write 9 | include "writer_lt.inc" 10 | end procedure 11 | 12 | module procedure lt1write 13 | include "writer_lt.inc" 14 | end procedure 15 | 16 | module procedure lt2write 17 | include "writer_lt.inc" 18 | end procedure 19 | 20 | module procedure lt3write 21 | include "writer_lt.inc" 22 | end procedure 23 | 24 | module procedure lt4write 25 | include "writer_lt.inc" 26 | end procedure 27 | 28 | module procedure lt5write 29 | include "writer_lt.inc" 30 | end procedure 31 | 32 | module procedure lt6write 33 | include "writer_lt.inc" 34 | end procedure 35 | 36 | module procedure lt7write 37 | include "writer_lt.inc" 38 | end procedure 39 | 40 | end submodule writer_lt 41 | -------------------------------------------------------------------------------- /src/writer_lt.inc: -------------------------------------------------------------------------------- 1 | type(hdf5_file) :: h 2 | 3 | call h%open(filename, action='rw') 4 | call h%write(dname, A) 5 | call h%close() 6 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # AssignProcessToJobObject: (87) The parameter is incorrect. 2 | # Not sure which test this is coming from. On Windows, avoiding parallel run works around this error. 3 | 4 | set_property(DIRECTORY PROPERTY LABELS h5fortran) 5 | 6 | # workaround flags for tests 7 | 8 | if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") 9 | add_compile_options("$<$:-Wno-compare-reals;-Wno-maybe-uninitialized>") 10 | endif() 11 | 12 | # test files 13 | 14 | set(attr_file ${CMAKE_CURRENT_BINARY_DIR}/test_attr_py.h5) 15 | set(shape_file ${CMAKE_CURRENT_BINARY_DIR}/test_shape.h5) 16 | set(string_file ${CMAKE_CURRENT_BINARY_DIR}/test_string_py.h5) 17 | 18 | # --- fundamental HDF5 library check 19 | 20 | add_executable(test_minimal test_minimal.f90) 21 | # even though we're not using h5fortran, we're testing that HDF5 was linked 22 | # as part of h5fortran 23 | target_link_libraries(test_minimal PRIVATE h5fortran::h5fortran) 24 | add_test(NAME minimal COMMAND test_minimal) 25 | set_property(TEST minimal PROPERTY FIXTURES_SETUP minimal) 26 | 27 | # --- h5fortran unit tests 28 | 29 | function(setup_test names) 30 | 31 | set(CI $ENV{CI}) 32 | 33 | foreach(name IN LISTS names) 34 | 35 | add_executable(test_${name} test_${name}.f90) 36 | target_link_libraries(test_${name} PRIVATE h5fortran::h5fortran) 37 | 38 | if(${name} STREQUAL "string_read") 39 | add_test(NAME ${name} COMMAND test_${name} ${string_file}) 40 | elseif(${name} STREQUAL "attributes_read") 41 | add_test(NAME ${name} COMMAND test_${name} ${attr_file}) 42 | else() 43 | add_test(NAME ${name} COMMAND test_${name}) 44 | endif() 45 | 46 | if(${name} MATCHES ".*fail.*") 47 | set_tests_properties(${name} PROPERTIES 48 | WILL_FAIL true 49 | LABELS shaky 50 | DISABLED $,$>> 51 | ) 52 | endif() 53 | 54 | endforeach() 55 | 56 | endfunction(setup_test) 57 | 58 | # --- setup unit tests 59 | 60 | set(test_names array attributes attributes_read 61 | cast deflate_write deflate_read deflate_props destructor exist 62 | groups layout lt scalar shape string string_read version write 63 | fail_read_size_mismatch fail_read_rank_mismatch fail_nonexist_variable) 64 | if(HAVE_IEEE_ARITH) 65 | list(APPEND test_names fill) 66 | endif() 67 | 68 | setup_test("${test_names}") 69 | 70 | set_property(TEST write PROPERTY FIXTURES_SETUP test_files) 71 | 72 | set_property(TEST shape PROPERTY FIXTURES_SETUP h5shape) 73 | 74 | set_property(TEST layout PROPERTY FIXTURES_REQUIRED test_files) 75 | set_property(TEST layout PROPERTY REQUIRED_FILES ${CMAKE_CURRENT_BINARY_DIR}/test_write.h5) 76 | 77 | set_tests_properties(deflate_write PROPERTIES 78 | FIXTURES_SETUP deflate_files 79 | LABELS deflate 80 | ) 81 | 82 | set_tests_properties(deflate_props deflate_read PROPERTIES 83 | FIXTURES_REQUIRED deflate_files 84 | REQUIRED_FILES ${CMAKE_CURRENT_BINARY_DIR}/deflate1.h5 85 | LABELS deflate 86 | ) 87 | 88 | if(h5fortran_COVERAGE) 89 | setup_target_for_coverage_gcovr_html( 90 | NAME coverage 91 | EXECUTABLE ${CMAKE_CTEST_COMMAND} 92 | ) 93 | endif() 94 | 95 | # --- Windows shared DLLs 96 | if(WIN32 AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.22) 97 | set_property(TEST ${test_names} PROPERTY 98 | ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:${ZLIB_INCLUDE_DIRS}/../bin;PATH=path_list_prepend:${ZLIB_INCLUDE_DIR}/../bin" 99 | ) 100 | endif() 101 | 102 | # --- Python h5py 103 | if(NOT DEFINED h5py_ok) 104 | execute_process(COMMAND ${Python_EXECUTABLE} -c "import h5py" 105 | RESULT_VARIABLE ret 106 | ERROR_VARIABLE err 107 | ) 108 | message(VERBOSE "${ret} ${err}") 109 | if(ret EQUAL 0) 110 | set(h5py_ok true CACHE BOOL "h5py OK") 111 | else() 112 | set(h5py_ok false CACHE BOOL "h5py not OK") 113 | endif() 114 | endif() 115 | 116 | set_property(TEST string_read PROPERTY FIXTURES_REQUIRED h5str) 117 | set_property(TEST string_read PROPERTY REQUIRED_FILES ${string_file}) 118 | 119 | # --- attributes 120 | 121 | add_test(NAME PythonAttributes 122 | COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generate_attributes.py ${attr_file} 123 | ) 124 | 125 | set_property(TEST PythonAttributes PROPERTY FIXTURES_SETUP h5attr) 126 | 127 | set_property(TEST attributes_read PROPERTY FIXTURES_REQUIRED h5attr) 128 | set_property(TEST attributes_read PROPERTY REQUIRED_FILES ${attr_file}) 129 | 130 | # --- shape 131 | 132 | add_test(NAME PythonShape 133 | COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/check_shape.py ${shape_file} 134 | ) 135 | 136 | set_property(TEST PythonShape PROPERTY REQUIRED_FILES ${shape_file}) 137 | set_property(TEST PythonShape PROPERTY FIXTURES_REQUIRED h5shape) 138 | 139 | # --- String 140 | add_test(NAME PythonString 141 | COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generate_string_data.py ${string_file} 142 | ) 143 | 144 | set_property(TEST PythonString PROPERTY FIXTURES_SETUP h5str) 145 | 146 | set_property(TEST string_read PythonAttributes PythonShape PythonString attributes_read PROPERTY DISABLED $>) 147 | 148 | set_property(TEST PythonAttributes PythonShape PythonString PROPERTY LABELS python) 149 | 150 | # --- Matlab HDF5 151 | 152 | if(matlab) 153 | find_package(Matlab COMPONENTS MAIN_PROGRAM REQUIRED) 154 | 155 | set(matlab_cmd "i=h5info('${shape_file}', '/d7').Dataspace.Size; assert(all(i == [2, 1, 3, 4, 7, 6, 5]))") 156 | 157 | add_test(NAME MatlabShape COMMAND ${Matlab_MAIN_PROGRAM} -batch ${matlab_cmd}) 158 | 159 | set_tests_properties(MatlabShape PROPERTIES 160 | LABELS "shaky;matlab" 161 | DEPENDS shape 162 | REQUIRED_FILES ${shape_file} 163 | FIXTURES_REQUIRED h5shape 164 | ) 165 | 166 | endif(matlab) 167 | 168 | # --- h5ls 169 | 170 | find_program(h5ls NAMES h5ls) 171 | add_test(NAME h5ls COMMAND ${h5ls} ${shape_file}/d7) 172 | 173 | set_tests_properties(h5ls PROPERTIES 174 | REQUIRED_FILES ${shape_file} 175 | FIXTURES_REQUIRED h5shape 176 | DEPENDS shape 177 | PASS_REGULAR_EXPRESSION "{5, 6, 7, 4, 3, 1, 2}" 178 | DISABLED $> 179 | ) 180 | 181 | 182 | get_property(test_names DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY TESTS) 183 | 184 | set_property(TEST ${test_names} PROPERTY WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 185 | 186 | foreach(n IN LISTS test_names) 187 | if(n STREQUAL "minimal") 188 | continue() 189 | endif() 190 | 191 | get_property(f TEST ${n} PROPERTY FIXTURES_REQUIRED) 192 | set_property(TEST ${n} PROPERTY FIXTURES_REQUIRED "minimal;${f}") 193 | endforeach() 194 | -------------------------------------------------------------------------------- /test/check_shape.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | read output of test_shape.f90 and see if it's the right shape 4 | in a few coding languages 5 | """ 6 | 7 | import argparse 8 | from pathlib import Path 9 | 10 | import h5py 11 | 12 | p = argparse.ArgumentParser(description="analyze output of test_shape.f90") 13 | p.add_argument("file", help="hDF5 file to analyze") 14 | P = p.parse_args() 15 | 16 | fn = Path(P.file).expanduser() 17 | 18 | var = "/d7" 19 | f_order = (2, 1, 3, 4, 7, 6, 5) 20 | c_order = f_order[::-1] 21 | 22 | 23 | if not fn.is_file(): 24 | raise FileNotFoundError(fn) 25 | 26 | with h5py.File(fn, "r") as f: 27 | if f[var].shape != c_order: 28 | raise ValueError(f"h5py: expected {c_order} but got {f[var].shape}") 29 | 30 | print("OK: Python h5py") 31 | -------------------------------------------------------------------------------- /test/generate_attributes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | write attributes to ensure h5fortran can read 4 | """ 5 | 6 | import argparse 7 | from pathlib import Path 8 | 9 | import numpy as np 10 | import h5py 11 | 12 | p = argparse.ArgumentParser(description="write test attributes in HDF5") 13 | p.add_argument("file", help="HDF5 file to write") 14 | P = p.parse_args() 15 | 16 | fn = Path(P.file).expanduser() 17 | 18 | with h5py.File(fn, "w") as f: 19 | v = f.create_dataset("/empty", dtype=float) 20 | 21 | v.attrs["real32-scalar"] = np.float32(3.0) 22 | v.attrs["real64-scalar"] = np.float64(3.0) 23 | v.attrs["real16-scalar"] = np.float16(3.0) 24 | 25 | v.attrs["variable_str"] = "Hi there" 26 | v.attrs["variable_str1"] = ["eight", "nine"] 27 | v.attrs["variable_str2"] = [ 28 | ["eight", "nine", "ten"], 29 | ["eleven", "twelve", "thirteen"], 30 | ] 31 | 32 | v.attrs.create("nullpad", dtype=h5py.string_dtype("utf-8", 40), data="Hello World!") 33 | v.attrs.create( 34 | "nullpad1", dtype=h5py.string_dtype("utf-8", 20), data=["two", "three", "four"] 35 | ) 36 | v.attrs.create( 37 | "nullpad2", 38 | dtype=h5py.string_dtype("utf-8", 20), 39 | data=[["eight", "nine", "ten"], ["eleven", "twelve", "thirteen"]], 40 | ) 41 | 42 | v.attrs["smiley"] = "😀" 43 | v.attrs["wink"] = "😉" 44 | -------------------------------------------------------------------------------- /test/generate_string_data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | write strings to ensure h5fortran can read 4 | """ 5 | 6 | import argparse 7 | from pathlib import Path 8 | 9 | import h5py 10 | 11 | p = argparse.ArgumentParser(description="write test strings in HDF5") 12 | p.add_argument("file", help="hDF5 file to write") 13 | P = p.parse_args() 14 | 15 | fn = Path(P.file).expanduser() 16 | 17 | with h5py.File(fn, "w") as f: 18 | 19 | """H5T_STR_NULLTERM""" 20 | v = f.create_dataset("/variable", data="Hello World!") 21 | v.attrs["smiley"] = "😀" 22 | 23 | n = f.create_dataset( 24 | "/nullpad", dtype=h5py.string_dtype("utf-8", 40), data="Hello World!" 25 | ) 26 | n.attrs["wink"] = "😉" 27 | 28 | f["/smiley"] = "😀" 29 | 30 | f.create_dataset("/wink", dtype=h5py.string_dtype("utf-8"), data="😉") 31 | 32 | f["/1d"] = ["Hi", "Bye"] 33 | -------------------------------------------------------------------------------- /test/read_slice.f90: -------------------------------------------------------------------------------- 1 | program read_slice 2 | !! example of Fortran reading smaller array into slice of larger array via subroutine 3 | 4 | implicit none 5 | 6 | type :: T 7 | integer :: i44(4,4) 8 | end type T 9 | 10 | integer :: i, bigA(4,4) 11 | type(T) :: B 12 | 13 | bigA = -1 14 | 15 | call getter(bigA(2:3,3:4)) 16 | 17 | do i = 1,size(bigA,1) 18 | print '(4I3)', bigA(i,:) 19 | enddo 20 | 21 | ! ---- 22 | 23 | B%i44 = -1 24 | 25 | call getter(B%i44(2:3,3:4)) 26 | 27 | print *,'' 28 | do i = 1,size(B%i44,1) 29 | print '(4I3)', B%i44(i,:) 30 | enddo 31 | 32 | !! should print 33 | !! -1 -1 -1 -1 34 | !! -1 -1 1 2 35 | !! -1 -1 3 4 36 | !! -1 -1 -1 -1 37 | 38 | contains 39 | 40 | subroutine getter(A) 41 | integer, intent(out) :: A(2,2) 42 | 43 | A = reshape([1,2,3,4], shape(A), order=[2,1]) 44 | 45 | end subroutine getter 46 | 47 | end program 48 | -------------------------------------------------------------------------------- /test/test_attributes.f90: -------------------------------------------------------------------------------- 1 | program test_attributes 2 | 3 | !! GCC 7 segfaults if -O3 is on. Fine with -O0 4 | 5 | use, intrinsic:: iso_fortran_env, only: int32, real32, real64, stderr=>error_unit 6 | use, intrinsic:: iso_c_binding, only: C_NULL_CHAR 7 | 8 | use h5fortran, only: hdf5_file, h5write_attr, h5read_attr, HSIZE_T 9 | 10 | implicit none 11 | 12 | character(*), parameter :: filename = 'test_attr.h5' 13 | character(8) :: s32 !< arbitrary length 14 | integer :: i32(1) 15 | 16 | call test_write_attributes(filename) 17 | 18 | call h5write_attr(filename, '/x', 'str29', '29') 19 | call h5write_attr(filename, '/x', 'int29', [29]) 20 | print *,'PASSED: HDF5 write attributes' 21 | 22 | call test_read_attributes(filename) 23 | 24 | call h5read_attr(filename, '/x', 'str29', s32) 25 | if (s32 /= '29') error stop 'readattr_lt string' 26 | 27 | call h5read_attr(filename, '/x', 'int29', i32) 28 | if (i32(1) /= 29) error stop 'readattr_lt integer' 29 | 30 | print *, 'PASSED: HDF5 read attributes' 31 | 32 | contains 33 | 34 | subroutine test_write_attributes(path) 35 | 36 | type(hdf5_file) :: h 37 | character(*), intent(in) :: path 38 | 39 | character(123) :: buf 40 | 41 | integer :: i2(1,1), i3(1,1,1), i4(1,1,1,1), i5(1,1,1,1,1), i6(1,1,1,1,1,1), i7(1,1,1,1,1,1,1) 42 | 43 | call h%open(path, action='w', debug=.true.) 44 | 45 | call h%write('/x', 1) 46 | 47 | call h%writeattr('/x', 'int32-scalar', 42) 48 | call h%writeattr('/x', 'real32-scalar', 42.0) 49 | call h%writeattr('/x', 'char','this is just a little number') 50 | call h%writeattr('/x', 'hello', 'hi') 51 | call h%writeattr('/x', 'real32_1d', [real(real32) :: 42, 84]) 52 | call h%writeattr('/x', 'real64_1d0', [42._real64]) 53 | 54 | call h%writeattr('/x', 'i2', i2) 55 | call h%writeattr('/x', 'i3', i3) 56 | call h%writeattr('/x', 'i4', i4) 57 | call h%writeattr('/x', 'i5', i5) 58 | call h%writeattr('/x', 'i6', i6) 59 | call h%writeattr('/x', 'i7', i7) 60 | 61 | buf = "" 62 | !! This auxiliary buffer fixes GCC 11.3.0 and oneAPI. 63 | !! It wasn't necessary on GCC 11.4.1 and newer. 64 | !! with h5dump, what's seen on disk file is "\001" value instead of "\000" or space. 65 | call h%writeattr("/x", "empty_char", buf) 66 | 67 | call h%writeattr("/x", "1d_empty", [character(1) :: "", ""]) 68 | call h%writeattr("/x", "c1d", [character(5) :: 'one', 'two', 'three']) 69 | call h%writeattr("/x", "c2d", reshape([character(5) :: 'one', 'two', 'three', 'four', 'five', 'six'], [2,3])) 70 | 71 | 72 | call h%close() 73 | 74 | !> test overwrite of attributes 75 | call h%open(path, action='a') 76 | call h%writeattr('/x', 'int32-scalar', 142) 77 | call h%writeattr('/x', 'real32_1d', [real(real32) :: 142, 84]) 78 | call h%writeattr('/x', 'char', 'overwrite attrs') 79 | call h%delete_attr('/x', 'hello') 80 | call h%close() 81 | 82 | print '(a)', 'PASSED: HDF5 write attributes: file handle' 83 | 84 | end subroutine test_write_attributes 85 | 86 | 87 | subroutine test_read_attributes(path) 88 | 89 | type(hdf5_file) :: h 90 | character(*), intent(in) :: path 91 | character(1024) :: attr_str 92 | integer :: int32_0 93 | real(real32) :: attr32(2) 94 | real(real64) :: attr64 95 | integer(HSIZE_T), allocatable :: dims(:) 96 | 97 | character(5), allocatable :: c1d(:) 98 | 99 | integer :: x 100 | 101 | integer :: i2(1,1), i3(1,1,1), i4(1,1,1,1), i5(1,1,1,1,1), i6(1,1,1,1,1,1), i7(1,1,1,1,1,1,1) 102 | 103 | call h%open(path, action='r') 104 | 105 | call h%read('/x', x) 106 | if (x/=1) error stop 'readattr: unexpected value' 107 | 108 | !> character scalar 109 | call h%readattr('/x', 'char', attr_str) 110 | if (attr_str /= 'overwrite attrs') error stop 'overwrite attrs failed: ' // attr_str 111 | 112 | call h%readattr("/x", "empty_char", attr_str) 113 | if (len_trim(attr_str) /= 0) then 114 | write(stderr, '(a,L1)') 'hanging null char: ', trim(attr_str) == c_null_char 115 | write(stderr, '(a,i0)') "empty char attribute: expected 0 length, got length: ", len_trim(attr_str) 116 | error stop "empty char attribute failed" 117 | endif 118 | if (trim(attr_str) /= "") error stop "empty char attribute failed, got: " // trim(attr_str) 119 | 120 | !> scalar numbers 121 | call h%readattr('/x', 'int32-scalar', int32_0) 122 | if (int32_0 /= 142) error stop 'readattr: int32-scalar' 123 | 124 | call h%readattr('/x', 'real32_1d', attr32) 125 | if (any(attr32 /= [real(real32) :: 142, 84])) error stop 'readattr: real32' 126 | 127 | call h%readattr('/x', 'real64_1d0', attr64) 128 | if (attr64 /= 42._real64) error stop 'readattr: real64' 129 | 130 | !> casting 131 | call h%readattr('/x', 'real32-scalar', int32_0) 132 | if(int32_0 /= 42) error stop "readattr cast real to int" 133 | 134 | call h%readattr('/x', 'int32-scalar', attr64) 135 | if(attr64 /= 142) error stop "readattr cast int to real" 136 | 137 | if (h%exist_attr('/x', 'hello')) error stop "delete attr failed" 138 | 139 | call h%readattr('/x', 'i2', i2) 140 | call h%readattr('/x', 'i3', i3) 141 | call h%readattr('/x', 'i4', i4) 142 | call h%readattr('/x', 'i5', i5) 143 | call h%readattr('/x', 'i6', i6) 144 | call h%readattr('/x', 'i7', i7) 145 | 146 | !> character array 147 | 148 | call h%shape("/x", dims, "c1d") 149 | if(dims(1) /= 3) error stop "attr % shape: c1d" 150 | 151 | allocate(c1d(dims(1))) 152 | call h%readattr('/x', 'c1d', c1d) 153 | if(c1d(1) /= 'one') error stop "attr:char:1d: index=1 " // c1d(1) 154 | if(c1d(2) /= 'two') error stop "attr:char:1d: index=2 " // c1d(2) 155 | if(c1d(3) /= 'three') error stop "attr:char:1d: index=3 " // c1d(3) 156 | 157 | call h%close() 158 | 159 | print '(a)', 'PASSED: HDF5 read attributes: file handle' 160 | 161 | end subroutine test_read_attributes 162 | 163 | end program 164 | -------------------------------------------------------------------------------- /test/test_attributes_read.f90: -------------------------------------------------------------------------------- 1 | program main 2 | 3 | use, intrinsic :: iso_fortran_env, only : stderr=>error_unit 4 | 5 | use h5fortran, only: hdf5_file, HSIZE_T 6 | 7 | implicit none 8 | 9 | character(1000) :: pyp, vstr, fstr 10 | character(4), parameter :: smiley = "😀", wink = "😉" 11 | character(4) :: u1 12 | 13 | character(:), allocatable :: dn 14 | character(20), allocatable :: c1d(:), c2d(:,:) 15 | 16 | integer(HSIZE_T), allocatable :: dims(:) 17 | 18 | integer :: i 19 | 20 | type(hdf5_file) :: h 21 | 22 | dn = "/empty" 23 | 24 | call get_command_argument(1, pyp, status=i) 25 | if (i/=0) error stop "specify file to read" 26 | 27 | call h%open(pyp, "r") 28 | 29 | call h%readattr(dn, "variable_str", vstr) 30 | if(vstr /= "Hi there") error stop "h5py read_attr variable length failed: " // trim(vstr) 31 | 32 | call h%shape(dn, dims, "variable_str1") 33 | allocate(c1d(dims(1))) 34 | call h%readattr(dn, "variable_str1", c1d) 35 | if(c1d(1) /= "eight") then 36 | write(stderr,*) "ERROR: read_attr var. length index 1: " // trim(c1d(1)) // " len: ", len(c1d(1)), len_trim(c1d(1)), dims(1) 37 | error stop 38 | endif 39 | if(c1d(2) /= "nine") error stop "h5py read_attr variable length failed index 2: " // trim(c1d(2)) 40 | 41 | call h%shape(dn, dims, "variable_str2") 42 | allocate(c2d(dims(1), dims(2))) 43 | call h%readattr(dn, "variable_str2", c2d) 44 | if(c2d(1, 1) /= "eight") then 45 | write(stderr,*) "ERROR: read_attr var. length index 1,1: " // trim(c2d(1, 1)) // " len: ", len(c2d(1,1)), len_trim(c2d(1,1)) 46 | error stop 47 | endif 48 | if(c2d(3, 2) /= "thirteen") error stop "h5py read_attr variable length failed index 3,2: " // trim(c2d(3,2)) 49 | 50 | call h%readattr(dn, "nullpad", fstr) 51 | if(fstr /= "Hello World!") error stop "h5py read null pad failed: " // trim(fstr) 52 | 53 | !> 1-D array of character fixed string 54 | call h%shape(dn, dims, "nullpad1") 55 | deallocate(c1d) 56 | allocate(c1d(dims(1))) 57 | call h%readattr(dn, "nullpad1", c1d) 58 | if(c1d(1) /= "two") error stop "h5py read null pad failed index 1: " // trim(c1d(1)) 59 | if(c1d(2) /= "three") error stop "h5py read null pad failed index 2: " // trim(c1d(2)) 60 | if(c1d(3) /= "four") error stop "h5py read null pad failed index 3: " // trim(c1d(3)) 61 | 62 | !> 2-D array of character fixed string 63 | call h%shape(dn, dims, "nullpad2") 64 | deallocate(c2d) 65 | allocate(c2d(dims(1), dims(2))) 66 | call h%readattr(dn, "nullpad2", c2d) 67 | if(c2d(1,1) /= "eight") error stop "h5py read null pad failed index 1,1: " // trim(c2d(1,1)) 68 | if(c2d(3,2) /= "thirteen") error stop "h5py read null pad failed index 3,2: " // trim(c2d(3,2)) 69 | 70 | call h%readattr(dn, "smiley", u1) 71 | print '(a)', "smiley: " // u1 72 | if(u1 /= smiley) error stop "test_utf8: smiley failed" 73 | 74 | call h%readattr(dn, "wink", u1) 75 | print '(a)', "wink: " // u1 76 | if(u1 /= wink) error stop "test_utf8: wink failed" 77 | 78 | 79 | call h%close() 80 | 81 | print *, "OK: variable/nullpad length string read" 82 | 83 | end program 84 | -------------------------------------------------------------------------------- /test/test_cast.f90: -------------------------------------------------------------------------------- 1 | program test_cast 2 | !! test HDF5 built-in casting 3 | 4 | use h5fortran, only : hdf5_file 5 | use hdf5 6 | use, intrinsic :: iso_fortran_env, only : real32, real64, int32, int64, stderr=>error_unit 7 | 8 | implicit none 9 | 10 | character(*), parameter :: fn = 'test_cast.h5' 11 | 12 | call test_cast_write(fn) 13 | print "(A)", "OK: cast write" 14 | 15 | call test_cast_read(fn) 16 | print "(A)", "OK: cast read" 17 | 18 | 19 | contains 20 | 21 | 22 | subroutine test_cast_write(fn) 23 | 24 | character(*), intent(in) :: fn 25 | 26 | type(hdf5_file) :: h 27 | real(real64) :: darr(2) = 12._real64 28 | 29 | call h%open(fn, action='w') 30 | 31 | !> test values 32 | call h%write('/scalar_int32', 42_int32) 33 | call h%write('/scalar_int64', 42_int64) 34 | call h%write('/scalar_real32', 42._real32) 35 | call h%write('/scalar_real64', 42._real64) 36 | call h%write('/1d_real32', [1._real32, 32._real32]) 37 | call h%write('/1d_int32', [2_int32, 4_int32]) 38 | call h%write('/char', "hello") 39 | call h%write('/cast/r64tor32', darr, datatype=H5T_NATIVE_REAL) 40 | call h%write('/cast/i64toi32', 42_int64, datatype=H5T_NATIVE_INTEGER) 41 | 42 | call h%close() 43 | 44 | end subroutine test_cast_write 45 | 46 | 47 | subroutine test_cast_read(fn) 48 | 49 | character(*), intent(in) :: fn 50 | 51 | type(hdf5_file) :: h 52 | 53 | real(real64) :: r64, r1_64(2) 54 | real(real32) :: r32 55 | integer(int32) :: i32 56 | integer(int64) :: i64, i1_64(2) 57 | 58 | call h%open(fn, action='r') 59 | 60 | !> %class method 61 | print '(a)', 'HDF5 H5T class and dtype values:' 62 | print '(a, i0)', 'H5T_INTEGER_F = ', H5T_INTEGER_F 63 | print '(a, i0)', 'H5T_FLOAT_F = ', H5T_FLOAT_F 64 | print '(a, i0)', 'H5T_STRING_F = ', H5T_STRING_F 65 | print '(a, i0)', 'H5T_NATIVE_REAL = ', H5T_NATIVE_REAL 66 | print '(a, i0)', 'H5T_NATIVE_DOUBLE = ', H5T_NATIVE_DOUBLE 67 | print '(a, i0)', 'H5T_NATIVE_INTEGER = ', H5T_NATIVE_INTEGER 68 | print '(a, i0)', 'H5T_NATIVE_CHARACTER = ', H5T_NATIVE_CHARACTER 69 | print '(a, i0)', 'H5T_STD_I64LE = ', H5T_STD_I64LE 70 | if (h%class("/scalar_int32") /= H5T_INTEGER_F) error stop "int32 not integer" 71 | if (h%class("/scalar_int64") /= H5T_INTEGER_F) error stop "int64 not integer" 72 | if (h%class("/1d_real32") /= H5T_FLOAT_F) error stop "1d_real32 not float" 73 | if (h%class("/scalar_real32") /= H5T_FLOAT_F) error stop "real32 not float" 74 | if (h%class("/scalar_real64") /= H5T_FLOAT_F) error stop "real64 not float" 75 | if (h%class("/char") /= H5T_STRING_F) error stop "char not string" 76 | if (h%class("/cast/i64toi32") /= H5T_INTEGER_F) then 77 | write(stderr,*) "expected cast i64toi32 ", H5T_INTEGER_F, " but got ", h%class('/cast/i64toi32') 78 | error stop "cast not integer" 79 | endif 80 | if (h%class('/cast/r64tor32') /= H5T_FLOAT_F) then 81 | write(stderr,*) "expected cast r64tor32 ", H5T_FLOAT_F, " but got ", h%class('/cast/r64tor32') 82 | error stop "cast not float" 83 | endif 84 | print *, "OK: class method" 85 | 86 | !> %dtype method 87 | if (h%dtype('/scalar_int32') /= H5T_NATIVE_INTEGER) error stop "int32 type" 88 | if (h%dtype("/scalar_int64") /= H5T_STD_I64LE) error stop "int64 type" 89 | if (h%dtype("/scalar_real32") /= H5T_NATIVE_REAL) error stop "real32 type" 90 | if (h%dtype("/scalar_real64") /= H5T_NATIVE_DOUBLE) error stop "real64 type" 91 | if (h%dtype("/char") /= H5T_NATIVE_CHARACTER) error stop "char type" 92 | if (h%dtype('/cast/i64toi32') /= H5T_NATIVE_INTEGER) error stop "cast type" 93 | if (h%dtype('/cast/r64tor32') /= H5T_NATIVE_REAL) error stop "cast type" 94 | print *, "OK: dtype method" 95 | 96 | !> read casting -- real32 to real64 and int32 to int64 97 | call h%read('/scalar_real32', r64) 98 | if(r64 /= 42) error stop 'scalar cast real32 => real64' 99 | call h%read('/scalar_real64', r32) 100 | if(r32 /= 42) error stop 'scalar cast real64 => real32' 101 | call h%read('/scalar_int32', i64) 102 | if(i64 /= 42) error stop 'scalar cast int32 => int64' 103 | call h%read('/scalar_int64', i32) 104 | if(i32 /= 42) error stop 'scalar cast int64 => int32' 105 | print *, 'PASSED: scalar cast on read' 106 | 107 | !> 1D vector read casting -- real to int and int to real 108 | call h%read('/1d_real32', r1_64) 109 | if (.not.all([1., 32.] == r1_64)) error stop '1D cast real32 => real64' 110 | call h%read('/1d_int32', i1_64) 111 | if (.not.all([2, 4] == i1_64)) error stop '1D cast int32 => int64' 112 | 113 | call h%close() 114 | 115 | print "(A)", "OK: cast" 116 | 117 | end subroutine test_cast_read 118 | 119 | end program 120 | -------------------------------------------------------------------------------- /test/test_deflate_props.f90: -------------------------------------------------------------------------------- 1 | program test_deflate_props 2 | 3 | use, intrinsic :: iso_fortran_env, only : int64, stderr=>output_unit 4 | 5 | use hdf5, only : H5D_CHUNKED_F, H5D_CONTIGUOUS_F 6 | 7 | use h5fortran, only: hdf5_file, HSIZE_T 8 | 9 | implicit none 10 | 11 | character(*), parameter :: fn1='deflate1.h5' 12 | integer, parameter :: N(2) = [50, 1000], & 13 | MIN_COMP = 2 !< lots of CPUs, smaller arrays => poorer compression 14 | 15 | call test_read_deflate_props(fn1, N) 16 | print *,'OK: HDF5 read deflate properties' 17 | 18 | call test_get_deflate(fn1) 19 | print *, 'OK: HDF5 get deflate' 20 | 21 | contains 22 | 23 | 24 | subroutine test_read_deflate_props(fn, N) 25 | 26 | character(*), intent(in) :: fn 27 | integer, intent(in) :: N(2) 28 | 29 | type(hdf5_file) :: h5f 30 | 31 | integer :: layout 32 | real :: fsize, crat 33 | integer(HSIZE_T) :: chunks(2) 34 | 35 | call h5f%open(fn, action='r') 36 | 37 | fsize = real(h5f%filesize()) 38 | 39 | crat = (N(1) * N(2) * 32 / 8) / fsize 40 | print '(A,F6.2,A,f7.1)','#1 filesize (Mbytes): ',fsize/1e6, ' compression ratio:',crat 41 | if(crat < MIN_COMP) error stop '2D low compression' 42 | 43 | layout = h5f%layout('/A') 44 | if(layout /= H5D_CHUNKED_F) error stop '#1 not chunked layout: ' // fn 45 | if(.not.h5f%is_chunked('/A')) error stop '#1 not chunked layout: ' // fn 46 | call h5f%chunks('/A', chunks) 47 | if(chunks(1) /= 5) then 48 | write(stderr, '(a,2I5)') "expected chunks(1) = 5 but got chunks ", chunks 49 | error stop '#1 get_chunk mismatch' 50 | endif 51 | layout = h5f%layout('/small_contig') 52 | if(layout /= H5D_CONTIGUOUS_F) error stop '#1 not contiguous layout' 53 | if(.not.h5f%is_contig('/small_contig')) error stop '#1 not contig layout' 54 | call h5f%chunks('/small_contig', chunks) 55 | if(any(chunks(:2) /= -1)) error stop '#1 get_chunk mismatch' 56 | 57 | call h5f%close() 58 | 59 | end subroutine test_read_deflate_props 60 | 61 | 62 | subroutine test_get_deflate(fn) 63 | 64 | character(*), intent(in) :: fn 65 | 66 | type(hdf5_file) :: h5f 67 | 68 | call h5f%open(fn, action='r') 69 | 70 | if (.not. h5f%deflate("/A")) error stop "test_get_deflate: expected deflate" 71 | 72 | call h5f%close() 73 | 74 | end subroutine test_get_deflate 75 | 76 | end program 77 | -------------------------------------------------------------------------------- /test/test_deflate_read.f90: -------------------------------------------------------------------------------- 1 | program test_deflate_read 2 | 3 | use, intrinsic:: iso_fortran_env, only: int32, int64, real32, real64, stderr=>error_unit 4 | 5 | use h5fortran, only: hdf5_file 6 | 7 | implicit none 8 | 9 | character(*), parameter :: fn1='deflate1.h5' 10 | integer, parameter :: N(2) = [50, 1000] 11 | 12 | 13 | call test_read_deflate(fn1, N) 14 | print *,'OK: HDF5 read deflate' 15 | 16 | contains 17 | 18 | subroutine test_read_deflate(fn, N) 19 | 20 | character(*), intent(in) :: fn 21 | integer, intent(in) :: N(2) 22 | 23 | type(hdf5_file) :: h5f 24 | real(real32), allocatable :: A(:,:) 25 | 26 | allocate(A(N(1), N(2))) 27 | 28 | call h5f%open(fn, action='r') 29 | call h5f%read('/A', A) 30 | call h5f%read('/noMPI', A) 31 | call h5f%close() 32 | 33 | end subroutine test_read_deflate 34 | 35 | end program 36 | -------------------------------------------------------------------------------- /test/test_deflate_write.f90: -------------------------------------------------------------------------------- 1 | program test_deflate 2 | !! unit tests and registration tests of HDF5 deflate compression write 3 | !! these tests are adapted from non-MPI h5fortran. 4 | !! several of them don't actually need MPI, but demonstrate that properties 5 | !! can be read by each MPI worker when the file is opened wiht h5%open(..., mpi=.true.) 6 | 7 | use, intrinsic:: iso_fortran_env, only: int32, int64, real32, real64, stderr=>error_unit 8 | 9 | use h5fortran, only: hdf5_file 10 | 11 | implicit none 12 | 13 | character(*), parameter :: fn1='deflate1.h5', fn2='deflate2.h5', fn3='deflate3.h5' 14 | integer, parameter :: N(2) = [50, 1000], & 15 | MIN_COMP = 2 !< lots of CPUs, smaller arrays => poorer compression 16 | 17 | 18 | call test_write_deflate(fn1, N) 19 | print *,'OK: HDF5 write deflate' 20 | 21 | call test_deflate_whole(fn2, N) 22 | print *,'OK: HDF5 compress whole' 23 | 24 | call test_deflate_slice(fn3, N) 25 | print *,'OK: HDF5 compress slice' 26 | 27 | contains 28 | 29 | subroutine test_write_deflate(fn, N) 30 | 31 | character(*), intent(in) :: fn 32 | integer, intent(in) :: N(2) 33 | 34 | type(hdf5_file) :: h5f 35 | real(real32), allocatable :: A(:,:) 36 | 37 | allocate(A(N(1), N(2))) 38 | 39 | A = 0 !< simplest data 40 | 41 | call h5f%open(fn, action='w', comp_lvl=1) 42 | call h5f%write('/A', A, dset_dims=N, chunk_size=[5, 50]) 43 | call h5f%close() 44 | 45 | deallocate(A) 46 | 47 | allocate(A(N(1), N(2))) 48 | A = 1 !< simplest data 49 | 50 | !! write with compression 51 | call h5f%open(fn, action='a', comp_lvl=1) 52 | 53 | call h5f%write('/small_contig', A(:4,:4)) 54 | !! not compressed because too small 55 | 56 | call h5f%write('/noMPI', A) 57 | !! write without MPI, with compression 58 | 59 | call h5f%close() 60 | 61 | end subroutine test_write_deflate 62 | 63 | 64 | subroutine test_deflate_whole(fn, N) 65 | 66 | character(*), intent(in) :: fn 67 | integer, intent(in) :: N(2) 68 | 69 | type(hdf5_file) :: h5f 70 | real, allocatable :: A(:,:,:) 71 | integer :: chunks(3) 72 | real :: fsize, crat 73 | 74 | allocate(A(N(1), N(2), 4)) 75 | 76 | call h5f%open(fn, action='w', comp_lvl=3) 77 | 78 | call h5f%write('/A', A, dset_dims=[N(1), N(2), 4], chunk_size=[4, 20, 1]) 79 | call h5f%chunks('/A', chunks) 80 | if(chunks(1) /= 4 .or. chunks(3) /= 1) then 81 | write(stderr, '(a,3I5)') "expected chunks: 4,*,1 but got chunks ", chunks 82 | error stop '#2 manual chunk unexpected chunk size' 83 | endif 84 | 85 | call h5f%write('/A_autochunk', A, [N(1), N(2), 4]) 86 | call h5f%chunks('/A_autochunk', chunks) 87 | if(any(chunks < 1)) error stop '#2 auto chunk unexpected chunk size' 88 | 89 | fsize = real(h5f%filesize()) 90 | crat = (2 * N(1) * N(2) * 4 * storage_size(A) / 8) / fsize 91 | !! 2* since two datasets same size 92 | 93 | print '(A,F6.2,A,f7.1)','#2 filesize (Mbytes): ', fsize / 1e6, ' compression ratio:', crat 94 | 95 | if(crat < MIN_COMP) error stop fn // ' low compression' 96 | 97 | call h5f%close() 98 | 99 | end subroutine test_deflate_whole 100 | 101 | 102 | subroutine test_deflate_slice(fn, N) 103 | 104 | character(*), intent(in) :: fn 105 | integer, intent(in) :: N(2) 106 | 107 | type(hdf5_file) :: h5f 108 | integer, allocatable :: A(:,:,:) 109 | integer :: chunks(3) 110 | real :: fsize, crat 111 | integer :: M(3) 112 | 113 | M = [N(1), N(2) - 20, 4] 114 | 115 | allocate(A(N(1)+1, M(2), 4)) !< dim 1 is deliberately not the "right" size, we will index at %write() 116 | 117 | A = 0 118 | 119 | call h5f%open(fn, action='w', comp_lvl=1) 120 | print *, "#3 file opened" 121 | call h5f%write('/A', A(:M(1), :, :), dset_dims=M) 122 | print *, "#3 written slice of A" 123 | call h5f%chunks('/A', chunks) 124 | if(any(chunks < 1)) error stop '#3 auto chunk unexpected chunk size' 125 | 126 | fsize = real(h5f%filesize()) 127 | crat = (N(1) * N(2) * storage_size(A) / 8) / fsize 128 | 129 | print '(A,F6.2,A,f7.1)','#3 filesize (Mbytes): ',fsize / 1e6, ' compression ratio:', crat 130 | 131 | if(crat < MIN_COMP) error stop fn // ' low compression' 132 | 133 | 134 | call h5f%close() 135 | 136 | end subroutine test_deflate_slice 137 | 138 | 139 | end program 140 | -------------------------------------------------------------------------------- /test/test_destructor.f90: -------------------------------------------------------------------------------- 1 | program test_destruct 2 | !! test hdf5_file destructor, that should auto-flush and close file 3 | !! if user forgets to %close() file 4 | 5 | use, intrinsic :: iso_fortran_env, only : stderr=>error_unit 6 | use h5fortran, only: hdf5_file 7 | 8 | implicit none 9 | 10 | character(*), parameter :: fn = "test_destruct.h5" 11 | 12 | call test_destructor_write(fn) 13 | print *, 'OK: destructor write' 14 | 15 | call test_destructor_read(fn) 16 | print *, 'OK: destructor read' 17 | 18 | contains 19 | 20 | 21 | subroutine test_destructor_write(fn) 22 | 23 | character(*), intent(in) :: fn 24 | 25 | type(hdf5_file) :: h 26 | 27 | call h%open(fn, action="w") 28 | 29 | call h%write('/x', 42) 30 | 31 | !! deliberately omitted %close() to test destructor 32 | 33 | end subroutine test_destructor_write 34 | 35 | 36 | subroutine test_destructor_read(fn) 37 | 38 | character(*), intent(in) :: fn 39 | 40 | integer :: i 41 | type(hdf5_file) :: h 42 | 43 | call h%open(fn, action="r") 44 | 45 | call h%read("/x", i) 46 | if(i/=42) error stop "destructor did not flush " // fn 47 | 48 | !! deliberately omitted %close() to test destructor 49 | 50 | end subroutine test_destructor_read 51 | 52 | end program 53 | -------------------------------------------------------------------------------- /test/test_exist.f90: -------------------------------------------------------------------------------- 1 | program exist_tests 2 | !! test "exist" variable 3 | use, intrinsic :: iso_fortran_env, only : stderr=>error_unit 4 | use h5fortran, only: hdf5_file, h5exist, is_hdf5, hdf5_close, H5T_NATIVE_INTEGER, hdf5_is_initialized 5 | 6 | implicit none 7 | 8 | call test_is_hdf5() 9 | print *, 'OK: is_hdf5' 10 | 11 | call test_exist('exist.h5') 12 | print *, 'OK: exist' 13 | 14 | call test_softlink('soft.h5') 15 | print *, "OK: softlink" 16 | 17 | call test_multifiles() 18 | print *, 'OK: multiple files open at once' 19 | 20 | contains 21 | 22 | 23 | subroutine test_is_hdf5() 24 | integer :: i 25 | 26 | if(is_hdf5('apidfjpj-8j9ejfpq984jfp89q39SHf.h5')) error stop 'test_exist: non-existent file declared hdf5' 27 | 28 | open(newunit=i, file='not_hdf5.h5', action='write') 29 | write(i,*) 'I am not an HDF5 file.' 30 | close(i) 31 | 32 | if(is_hdf5('not_hdf5.h5')) error stop 'text files are not hdf5' 33 | 34 | end subroutine test_is_hdf5 35 | 36 | 37 | subroutine test_exist(fn) 38 | 39 | type(hdf5_file) :: h 40 | 41 | character(*), intent(in) :: fn 42 | 43 | print '(a)', 'test_exist: creating file ' // fn 44 | call h%open(fn, action='w', debug=.true.) 45 | 46 | print '(a)', "creating test dataset /x" 47 | call h%create('/x', H5T_NATIVE_INTEGER, [0]) 48 | 49 | print '(a)', 'test_exist: closing file ' // fn 50 | call h%close() 51 | if(.not.is_hdf5(fn)) error stop 'file does not exist' 52 | 53 | print *, "test_exist: attemping to open file: ", fn 54 | call h%open(fn, action='r', debug=.true.) 55 | if (.not. h%exist('/x')) error stop 'x exists' 56 | 57 | if (h%exist('/A')) error stop 'variable /A should not exist in ' // h%filename 58 | 59 | call h%close() 60 | 61 | print '(a,L1)', "test_exist: library still initialized: ", hdf5_is_initialized() 62 | 63 | if(h%is_open()) error stop 'file is closed' 64 | 65 | print '(a)', "test_exist: check h5exist() on file: " // fn 66 | if (.not. h5exist(fn, '/x', debug=.true.)) error stop 'x exists' 67 | if (h5exist(fn, '/A', debug=.true.)) error stop 'A not exist' 68 | 69 | end subroutine test_exist 70 | 71 | 72 | subroutine test_softlink(fn) 73 | 74 | type(hdf5_file) :: h 75 | character(*), intent(in) :: fn 76 | 77 | integer :: y 78 | 79 | call h%open(fn, action='w') 80 | 81 | call h%write("/actual", 142) 82 | call h%softlink("/actual", "/additional") 83 | call h%read("/additional", y) 84 | 85 | if (.not.h%exist("/additional")) error stop "softlink not present" 86 | 87 | if (y /= 142) error stop "did not read softlink correctly" 88 | 89 | !> test dangling link 90 | 91 | call h%softlink("/not_here", "/not_yet") 92 | if (h%exist("/not_yet")) error stop "dangling softlink" 93 | 94 | call h%write("/not_here", 36) 95 | call h%read("/not_yet", y) 96 | if (y /= 36) error stop "finalizing dangling link failed" 97 | 98 | call h%close() 99 | 100 | end subroutine test_softlink 101 | 102 | 103 | subroutine test_multifiles() 104 | 105 | type(hdf5_file) :: f,g,h 106 | 107 | call f%open(filename='A.h5', action='w') 108 | call g%open(filename='B.h5', action='w') 109 | if (h%is_open()) error stop 'is_open() not isolated at constructor' 110 | call h%open(filename='C.h5', action='w') 111 | 112 | call f%flush() 113 | 114 | call f%close() 115 | if (.not.g%is_open() .or. .not. h%is_open()) error stop 'is_open() not isolated at destructor' 116 | call g%close() 117 | call h%close() 118 | 119 | call hdf5_close() 120 | 121 | end subroutine test_multifiles 122 | 123 | end program 124 | -------------------------------------------------------------------------------- /test/test_fail_nonexist_variable.f90: -------------------------------------------------------------------------------- 1 | program nonexistvar 2 | 3 | use h5fortran, only : hdf5_file 4 | implicit none 5 | 6 | integer :: u 7 | type(hdf5_file) :: h 8 | character(*), parameter :: filename = 'bad.h5' 9 | 10 | call h%open(filename, action='r') 11 | call h%read('/not-exist', u) 12 | call h%close() 13 | 14 | end program 15 | -------------------------------------------------------------------------------- /test/test_fail_read_rank_mismatch.f90: -------------------------------------------------------------------------------- 1 | program fail_slice_mismatch 2 | !> test writing wrong size 3 | use h5fortran, only : hdf5_file 4 | implicit none 5 | type(hdf5_file) :: h 6 | 7 | call h%open('mismatch.h5', action='w') 8 | call h%write('/int32-1d', [-1,1]) 9 | call h%write('/int32-1d', reshape([-1], shape=[1,1])) 10 | call h%close() 11 | 12 | end program 13 | -------------------------------------------------------------------------------- /test/test_fail_read_size_mismatch.f90: -------------------------------------------------------------------------------- 1 | program fail_slice_mismatch 2 | !> test writing wrong size 3 | use h5fortran, only : hdf5_file 4 | implicit none 5 | type(hdf5_file) :: h 6 | 7 | call h%open('mismatch.h5', action='w') 8 | call h%write('/int32-1d', [-1,1]) 9 | call h%write('/int32-1d', [-1]) 10 | call h%close() 11 | 12 | end program 13 | -------------------------------------------------------------------------------- /test/test_fill.f90: -------------------------------------------------------------------------------- 1 | program fill 2 | 3 | use, intrinsic:: ieee_arithmetic, only : ieee_value, ieee_quiet_nan, ieee_is_finite 4 | 5 | use h5fortran, only : hdf5_file 6 | use hdf5, only : H5T_NATIVE_REAL, H5T_NATIVE_DOUBLE, H5T_NATIVE_INTEGER, H5T_NATIVE_CHARACTER 7 | 8 | implicit none 9 | 10 | type(hdf5_file) :: h5 11 | 12 | character(*), parameter :: fn = "test_fill.h5" 13 | 14 | real :: NaN, r(3) 15 | integer :: i(2) 16 | character(10) :: c, fill_value 17 | 18 | NaN = ieee_value(0., ieee_quiet_nan) 19 | 20 | call h5%open(fn, "w") 21 | 22 | call h5%create("/r32", H5T_NATIVE_REAL, dset_dims=[3], fill_value=NaN) 23 | call h5%create("/r64", H5T_NATIVE_DOUBLE, dset_dims=[3], fill_value=NaN) 24 | call h5%create("/i32", H5T_NATIVE_INTEGER, dset_dims=[2], fill_value=-1) 25 | 26 | !> Note that character fill value must have same length as dataset, hence we use a character(10) 27 | fill_value = "NaN" 28 | call h5%create("/char", H5T_NATIVE_CHARACTER, dset_dims=[1], fill_value=fill_value, charlen=10) 29 | fill_value = "" 30 | call h5%create("/char_blank", H5T_NATIVE_CHARACTER, dset_dims=[1], fill_value=fill_value, charlen=10) 31 | fill_value = " " 32 | call h5%create("/char_space", H5T_NATIVE_CHARACTER, dset_dims=[1], fill_value=fill_value, charlen=10) 33 | 34 | call h5%close() 35 | 36 | call h5%open(fn, "r") 37 | 38 | call h5%read("/r32", r) 39 | if(any(ieee_is_finite(r))) error stop "real32: expected all NaN" 40 | 41 | call h5%read("/r64", r) 42 | if(any(ieee_is_finite(r))) error stop "real64: expected all NaN" 43 | 44 | call h5%read("/i32", i) 45 | if(any(i /= -1)) error stop "int32: expected all -1" 46 | 47 | call h5%read("/char", c) 48 | if(c /= "NaN") error stop "char: expected 'NaN', got: " // c 49 | 50 | call h5%read("/char_blank", c) 51 | if(c /= "") error stop "char: expected '', got: " // c 52 | 53 | call h5%read("/char_space", c) 54 | if(c /= " ") error stop "char: expected ' ', got: " // c 55 | 56 | call h5%close() 57 | 58 | print *, "OK: fill value" 59 | 60 | end program 61 | -------------------------------------------------------------------------------- /test/test_groups.f90: -------------------------------------------------------------------------------- 1 | program test_hdf5 2 | !! unit tests and registration tests of HDF5 OO interface 3 | use, intrinsic:: iso_fortran_env, only: int32, real32, real64, stderr=>error_unit 4 | 5 | use h5fortran, only: hdf5_file, h5write, h5read 6 | 7 | implicit none 8 | 9 | call test_group('test_groups.h5') 10 | print *,'OK: HDF5 group' 11 | 12 | call test_write_existing('overwrite.h5') 13 | print *,'OK: write existing variable' 14 | 15 | contains 16 | 17 | subroutine test_group(fn) 18 | 19 | character(*), intent(in) :: fn 20 | 21 | type(hdf5_file) :: h 22 | 23 | call h % open(fn, action='w', debug=.true.) 24 | 25 | call h % create_group('/test') 26 | call h % create_group('test_trailing_slash/') 27 | 28 | call h % write('/test/group3/scalar', 1_int32) 29 | 30 | call h % write('/test/group3/scalar_real', 1._real32) 31 | 32 | if(.not. h % exist('/test/group3/scalar')) error stop "/test/group3/scalar does not exist: create gorup failed" 33 | 34 | call h % close() 35 | 36 | end subroutine test_group 37 | 38 | 39 | subroutine test_write_existing(fn) 40 | type(hdf5_file) :: h 41 | character(*), intent(in) :: fn 42 | 43 | call h % open(fn, action='w') 44 | call h % write('/scalar_int', 42_int32) 45 | call h % write('/int1d', [42_int32, 1_int32]) 46 | call h % close() 47 | 48 | call h % open(fn, action='r+') 49 | call h % write('/scalar_int', 100_int32) 50 | call h % write('/int1d', [100_int32, 10_int32]) 51 | call h % close() 52 | 53 | end subroutine test_write_existing 54 | 55 | end program 56 | -------------------------------------------------------------------------------- /test/test_layout.f90: -------------------------------------------------------------------------------- 1 | program test_layout 2 | 3 | use, intrinsic :: iso_fortran_env, only : real32, real64, int32 4 | use h5fortran, only : hdf5_file 5 | use hdf5, only : H5D_COMPACT_F 6 | 7 | implicit none 8 | 9 | character(*), parameter :: fn = 'test_layout.h5' 10 | 11 | call test_layout_read(fn) 12 | 13 | call test_layout_props(fn) 14 | 15 | print *, "OK: test layout" 16 | 17 | contains 18 | 19 | 20 | subroutine test_layout_read(fn) 21 | 22 | character(*), intent(in) :: fn 23 | 24 | type(hdf5_file) :: h 25 | 26 | real(real32) :: r32 27 | real(real64) :: r64 28 | integer(int32) :: ri32 29 | 30 | real(real32), dimension(1,1,1,1,1,1,1) :: r7_32 31 | real(real64), dimension(1,1,1,1,1,1,1) :: r7_64 32 | 33 | !> read casting 34 | call h%open(fn, action="r") 35 | 36 | call h%read("/compact_r32", r64) 37 | if(r64 /= 142) error stop "read real32 => real64" 38 | call h%read("/compact_r64", r32) 39 | if(r32 /= 142) error stop "read real64 => real32" 40 | call h%read("/compact_i32", ri32) 41 | if(r32 /= 142) error stop "read int32 => int32" 42 | 43 | 44 | call h%read("/compact7d_32", r7_64) 45 | if (any(r7_64 /= 42)) error stop "read real32 => real64" 46 | 47 | call h%read("/compact7d_64", r7_32) 48 | if (any(r7_32 /= 42)) error stop "read real64 => real32" 49 | 50 | call h%close() 51 | 52 | end subroutine test_layout_read 53 | 54 | 55 | subroutine test_layout_props(fn) 56 | 57 | character(*), intent(in) :: fn 58 | 59 | type(hdf5_file) :: h 60 | 61 | call h%open(fn, action="r") 62 | 63 | if (h%layout("/compact1d") /= H5D_COMPACT_F) error stop "expected compact" 64 | if (.not. h%is_compact("/compact1d")) error stop "1d is_compact fail" 65 | 66 | if (.not. h%is_compact("/compact7d_32")) error stop "7d is_compact fail" 67 | if (.not. h%is_compact("/compact0d")) error stop "0d is_compact fail" 68 | 69 | call h%close() 70 | 71 | end subroutine test_layout_props 72 | 73 | end program 74 | -------------------------------------------------------------------------------- /test/test_lt.f90: -------------------------------------------------------------------------------- 1 | program test_lt 2 | 3 | use h5fortran, only : h5write, h5read, is_hdf5 4 | 5 | implicit none 6 | 7 | integer :: L, L1(8), L2(2,1), L3(1,1,1), L4(1,1,1,1), L5(1,1,1,1,1), L6(1,1,1,1,1,1), L7(1,1,1,1,1,1,1) 8 | 9 | character(*), parameter :: f1 = "LT_test_file.h5" 10 | logical :: fexists 11 | 12 | L = 121242 13 | L2 = L; L3=L; L4=L; L5=L; L6=L; L7=L 14 | 15 | inquire(file=f1, exist=fexists) 16 | 17 | !! in case of botched prior test with invalid HDF5 file, don't falsely fail 18 | if (fexists) then 19 | print '(a)', f1 // ' exists, deleting in case it is a bad file that is_hdf5 does not detect.' 20 | call std_unlink(f1) 21 | end if 22 | 23 | call h5write(f1, '/int', 42) 24 | call h5write(f1, '/int32_0d', 121242) 25 | 26 | call h5read(f1, '/int32_0d', L) 27 | if (L /= 121242) error stop 'incorrect read value' 28 | 29 | ! --- 1d 30 | 31 | call h5write(f1,'/int32_1d', [1,2,3,4,5,6]) 32 | L1 = 0 33 | call h5read(f1, '/int32_1d', L1(2:7)) 34 | if(.not. all(L1(2:7) == [1,2,3,4,5,6])) error stop '1d slice read error' 35 | 36 | ! --- 2d 37 | 38 | call h5write(f1,'/int32_2d', L2) 39 | call h5read(f1, '/int32_2d', L2) 40 | 41 | ! --- 3d 42 | 43 | call h5write(f1,'/int32_3d', L3) 44 | call h5read(f1, '/int32_3d', L3) 45 | 46 | call h5write(f1,'/int32_4d', L4) 47 | call h5read(f1, '/int32_4d', L4) 48 | 49 | call h5write(f1,'/int32_5d', L5) 50 | call h5read(f1, '/int32_5d', L5) 51 | 52 | call h5write(f1,'/int32_6d', L6) 53 | call h5read(f1, '/int32_6d', L6) 54 | 55 | call h5write(f1,'/int32_7d', L7) 56 | call h5read(f1, '/int32_7d', L7) 57 | 58 | contains 59 | 60 | subroutine std_unlink(file) 61 | character(*), intent(in) :: file 62 | integer :: u 63 | open(newunit=u, file=file) 64 | close(u, status="delete") 65 | end subroutine std_unlink 66 | 67 | end program 68 | -------------------------------------------------------------------------------- /test/test_minimal.f90: -------------------------------------------------------------------------------- 1 | program test_minimal 2 | 3 | use hdf5, only : HID_T, HSIZE_T, H5_INTEGER_KIND, h5kind_to_type, h5open_f, h5close_f, h5fclose_f, h5fcreate_f, H5F_ACC_TRUNC_F 4 | use h5lt, only : h5ltmake_dataset_f 5 | 6 | implicit none 7 | 8 | integer :: i, p 9 | integer(HID_T) :: lid 10 | character(*), parameter :: filename='test_minimal.h5' 11 | 12 | p = 42 13 | 14 | !! check that repeated calls to h5open_f() do not cause problems as per docs 15 | !! not calling h5open_f() at all makes failures as library isn't initialized 16 | !! unlike C HDF5, Fortran HDF5 does not auto-initialize. 17 | call h5open_f(i) 18 | if(i /= 0) error stop "test_minimal: h5open_f failed [0]" 19 | 20 | call h5open_f(i) 21 | if(i /= 0) error stop "test_minimal: h5open_f failed [1]" 22 | 23 | call h5fcreate_f(filename, H5F_ACC_TRUNC_F, lid, i) 24 | if (i/=0) error stop 'minimal: could not create file' 25 | print *, 'minimal: created '//filename 26 | 27 | call h5ltmake_dataset_f(lid, "A", 0, shape(p, kind=HSIZE_T), h5kind_to_type(kind(p),H5_INTEGER_KIND), p, i) 28 | if (i/=0) error stop 'minimal: could not create dataset A' 29 | print *, 'minimal: created variable' 30 | 31 | call h5fclose_f(lid, i) 32 | if (i/=0) error stop 'minimal: could not close file' 33 | print *, 'minimal: closed '//filename 34 | 35 | call H5close_f(i) 36 | if (i /= 0) error stop 'could not close hdf5 library [0]' 37 | 38 | ! this is a Fortran-standard way to delete files 39 | open(newunit=i, file=filename) 40 | close(i, status='delete') 41 | 42 | end program 43 | -------------------------------------------------------------------------------- /test/test_scalar.f90: -------------------------------------------------------------------------------- 1 | program test_scalar 2 | 3 | use, intrinsic :: iso_fortran_env, only: real32, real64, int32, int64, stderr=>error_unit 4 | 5 | use hdf5, only: HSIZE_T, H5T_NATIVE_INTEGER, H5T_STD_I64LE 6 | use h5fortran, only: hdf5_file 7 | 8 | implicit none 9 | 10 | character(*), parameter :: fn = 'test_scalar.h5' 11 | 12 | call test_scalar_write(fn) 13 | print *, 'OK: scalar and vector: write and rewrite' 14 | 15 | call test_scalar_read(fN) 16 | print *, 'OK: scalar and vector: read' 17 | 18 | contains 19 | 20 | subroutine test_scalar_write(fn) 21 | 22 | character(*), intent(in) :: fn 23 | 24 | type(hdf5_file) :: h 25 | 26 | real(real32) :: r1(4) 27 | integer(int32) :: i1(4) 28 | integer(int64) :: i1_64(4) 29 | 30 | integer :: i 31 | 32 | do i = 1,size(i1) 33 | i1(i) = i 34 | enddo 35 | 36 | r1 = i1 37 | i1_64 = i1 38 | 39 | !> write 40 | call h%open(fn, action='w') 41 | !> scalar tests 42 | call h%write('/scalar_int32', 42_int32) 43 | call h%write('/scalar_int64', 42_int64) 44 | call h%write('/scalar_real32', -1._real32) 45 | call h%write('/scalar_real64', -1._real64) 46 | 47 | !> vector 48 | call h%write('/1d_real', r1) 49 | call h%write('/vector_scalar_real', [37.]) 50 | 51 | !> create then write 52 | call h%create('/1d_int32', H5T_NATIVE_INTEGER, dset_dims=shape(i1)) 53 | call h%write('/1d_int32', i1) 54 | 55 | call h%create('/1d_int64', H5T_STD_I64LE, dset_dims=shape(i1_64)) 56 | call h%write('/1d_int64', i1_64) 57 | 58 | !> test rewrite 59 | call h%write('/scalar_real32', 42._real32) 60 | call h%write('/scalar_real64', 42._real64) 61 | call h%write('/scalar_int32', 42_int32) 62 | call h%write('/scalar_int64', 42_int64) 63 | call h%close() 64 | 65 | end subroutine test_scalar_write 66 | 67 | 68 | subroutine test_scalar_read(fn) 69 | 70 | character(*), intent(in) :: fn 71 | 72 | type(hdf5_file) :: h 73 | 74 | real(real32) :: rt, r1(4) 75 | integer(int32) :: i, it, i1(4) 76 | integer(int32), allocatable :: i1t(:) 77 | integer(int64) :: it_64, i1_64(4) 78 | integer(int64), allocatable :: i1t_64(:) 79 | real(real32), allocatable :: rr1_32(:) 80 | integer(HSIZE_T), allocatable :: dims(:) 81 | 82 | !> test data 83 | do i = 1,size(i1) 84 | i1(i) = i 85 | enddo 86 | 87 | r1 = i1 88 | i1_64 = i1 89 | 90 | !> read 91 | 92 | call h%open(fn, action='r') 93 | 94 | call h%read('/scalar_int32', it) 95 | call h%read('/scalar_int64', it_64) 96 | 97 | call h%read('/scalar_real32', rt) 98 | if (.not.(rt==it .and. it==42)) then 99 | write(stderr,*) it,'/=',rt 100 | error stop 'scalar real / int: not equal 42' 101 | endif 102 | 103 | !> read vector length 1 as scalar 104 | call h%shape('/vector_scalar_real', dims) 105 | if (any(dims /= [1])) error stop "vector_scalar: expected vector length 1" 106 | 107 | call h%read('/vector_scalar_real', rt) 108 | if(rt /= 37) error stop 'vector_scalar: 1d length 1 => scalar' 109 | 110 | !> 1D vector read 111 | call h%shape('/1d_real', dims) 112 | allocate(rr1_32(dims(1))) 113 | 114 | call h%read('/1d_real', rr1_32) 115 | if (.not.all(r1 == rr1_32)) error stop 'real32 1-D: read does not match write' 116 | 117 | call h%shape('/1d_int32',dims) 118 | allocate(i1t(dims(1))) 119 | call h%read('/1d_int32', i1t) 120 | if (.not.all(i1==i1t)) error stop 'int32 1-D: read does not match write' 121 | 122 | allocate(i1t_64(dims(1))) 123 | call h%read('/1d_int64', i1t_64) 124 | if (.not.all(i1_64==i1t_64)) error stop 'int64 1-D: read does not match write' 125 | 126 | !> check filename property 127 | if (.not. h%filename == fn) error stop h%filename // ' mismatch filename' 128 | 129 | call h%close() 130 | 131 | end subroutine test_scalar_read 132 | 133 | end program 134 | -------------------------------------------------------------------------------- /test/test_shape.f90: -------------------------------------------------------------------------------- 1 | program test_shape 2 | !! This program shows how HDF5 dimension orders are distinct in different languages 3 | use h5fortran, only: hdf5_file,hsize_t, is_hdf5 4 | use, intrinsic:: iso_fortran_env, only: real64, stdout=>output_unit, stderr=>error_unit 5 | 6 | implicit none 7 | 8 | character(*), parameter :: fn = 'test_shape.h5' 9 | 10 | 11 | call test_shape_write(fn) 12 | 13 | call test_shape_read(fn) 14 | print *, "OK: test_shape" 15 | 16 | 17 | contains 18 | 19 | 20 | subroutine test_shape_write(fn) 21 | 22 | character(*), intent(in) :: fn 23 | 24 | type(hdf5_file) :: h 25 | integer :: d2(3,4), d7(2,1,3,4,7,6,5) 26 | 27 | call h%open(fn, action='w') 28 | call h%write('/d2', d2) 29 | call h%write('/d7', d7) 30 | call h%close() 31 | 32 | end subroutine test_shape_write 33 | 34 | 35 | subroutine test_shape_read(fn) 36 | 37 | character(*), intent(in) :: fn 38 | 39 | type(hdf5_file) :: h 40 | integer(HSIZE_T), allocatable :: dims(:) 41 | integer :: d2(3,4), d7(2,1,3,4,7,6,5) 42 | 43 | call h%open(fn, action='r') 44 | call h%shape('/d2', dims) 45 | if (h%ndim('/d2') /= size(dims)) error stop 'rank /= size(dims)' 46 | if (any(dims /= shape(d2))) error stop '2-D: file shape not match variable shape' 47 | 48 | call h%shape('/d7', dims) 49 | if (h%ndim('/d7') /= size(dims)) error stop 'rank /= size(dims)' 50 | if (any(dims /= shape(d7))) error stop '7-D: file shape not match variable shape' 51 | 52 | call h%close() 53 | 54 | end subroutine test_shape_read 55 | 56 | end program 57 | -------------------------------------------------------------------------------- /test/test_string.f90: -------------------------------------------------------------------------------- 1 | program test_string 2 | 3 | use, intrinsic:: iso_fortran_env, only: stderr=>error_unit 4 | use, intrinsic:: iso_c_binding, only : C_NULL_CHAR 5 | 6 | use hdf5, only: H5T_STR_SPACEPAD_F, HSIZE_T 7 | use h5fortran, only: hdf5_file 8 | 9 | implicit none 10 | 11 | character(*), parameter :: fn='test_string.h5' 12 | 13 | call test_write(fn) 14 | print *, "OK: HDF5 string write" 15 | 16 | call test_read(fn) 17 | print *,'OK: HDF5 string read' 18 | 19 | call test_overwrite(fn) 20 | print *, "OK: string overwrite" 21 | 22 | print *,'PASSED: HDF5 string write/read' 23 | 24 | contains 25 | 26 | 27 | subroutine test_write(fn) 28 | 29 | character(*), intent(in) :: fn 30 | 31 | type(hdf5_file) :: h 32 | 33 | character(123) :: aux 34 | 35 | call h%open(fn, action='w', debug=.true.) 36 | 37 | aux = "" 38 | !! compillers like oneAPI need an auxiliary variable not a raw constant 39 | call h%write("/empty", aux) 40 | 41 | call h%write('/little', '42') 42 | call h%write('/MySentence', 'this is a little sentence.') 43 | call h%write('/vector_scalar', ['vector scalar']) 44 | 45 | call h%write("/1d_empty", [character(1) :: ""]) 46 | call h%write("/1d", [character(3) :: "hi", "bye"]) 47 | call h%write("/2d", reshape([character(5) :: "one", "two", "three", "four", "five", "six"], [2,3])) 48 | 49 | call h%close() 50 | 51 | end subroutine test_write 52 | 53 | 54 | subroutine test_read(fn) 55 | 56 | character(*), intent(in) :: fn 57 | 58 | type(hdf5_file) :: h 59 | character(2) :: value 60 | character(1024) :: val1k 61 | character(13) :: vs 62 | integer :: L 63 | 64 | integer(HSIZE_T), allocatable :: dims(:) 65 | 66 | call h%open(fn, action='r') 67 | 68 | value = "" 69 | call h%read('/empty', value) 70 | L = len_trim(value) 71 | print '(L1,1x,i0,1x,a)', trim(value) == C_NULL_CHAR, L, trim(value) 72 | select case (L) 73 | case (1) 74 | write(stderr, '(a)') 'WARNING: test_string: empty string failure: len_trim should be zero but is 1' 75 | case (0) 76 | if (value /= '') error stop 'test_string: empty string failure: value = ' // trim(value) 77 | case default 78 | write(stderr, '(a,i0)') 'test_string: empty string failure: len_trim should be zero but is ', L 79 | error stop 80 | end select 81 | 82 | call h%read('/little', value) 83 | 84 | if(len_trim(value) /= 2) then 85 | write(stderr,'(a,i0,a)') "test_string: read length ", len_trim(value), " /= 2" 86 | error stop 87 | endif 88 | if (value /= '42') error stop 'test_string: read/write verification failure. Value: '// value 89 | 90 | !> check padding 91 | if (h%get_strpad("/little") /= H5T_STR_SPACEPAD_F) error stop "SPACEPAD expected for /little" 92 | 93 | !> longer character than data 94 | call h%read('/little', val1k) 95 | 96 | if (len_trim(val1k) /= 2) then 97 | write(stderr, '(a,i0,/,a)') 'expected character len_trim 2 but got len_trim() = ', len_trim(val1k), val1k 98 | error stop 99 | endif 100 | 101 | !> vector scalar (length 1 vector) 102 | call h%read('/vector_scalar', vs) 103 | if(vs /= "vector scalar") then 104 | write(stderr, *) "test_string: vector_scalar: expected 'vector_scalar' but got ", vs 105 | error stop "test_string: vector_scalar" 106 | endif 107 | 108 | !> vector 109 | call h%shape("/1d", dims) 110 | if(dims(1) /= 2) error stop "test_string: 1d shape" 111 | 112 | !> 2d 113 | call h%shape("/2d", dims) 114 | if(any(dims /= [2, 3])) error stop "test_string: 2d shape" 115 | 116 | call h%close() 117 | 118 | end subroutine test_read 119 | 120 | 121 | subroutine test_overwrite(fn) 122 | 123 | character(*), intent(in) :: fn 124 | 125 | type(hdf5_file) :: h 126 | character(2) :: v 127 | 128 | call h%open(fn, action='rw') 129 | call h%write('/little', '73') 130 | call h%close() 131 | 132 | call h%open(fn, action='r') 133 | call h%read('/little', v) 134 | call h%close() 135 | 136 | if (v /= '73') error stop 'test_string: overwrite string failure. Value: '// v // " /= 73" 137 | 138 | end subroutine test_overwrite 139 | 140 | end program 141 | -------------------------------------------------------------------------------- /test/test_string_read.f90: -------------------------------------------------------------------------------- 1 | program main 2 | 3 | use hdf5, only: H5T_STR_NULLPAD_F, H5T_STR_NULLTERM_F 4 | use h5fortran, only: hdf5_file 5 | 6 | implicit none 7 | 8 | character(1000) :: pyp, vstr, fstr 9 | character(4), parameter :: smiley = "😀", wink = "😉" 10 | character(4) :: u1 11 | 12 | 13 | integer :: i 14 | 15 | type(hdf5_file) :: h 16 | 17 | call get_command_argument(1, pyp, status=i) 18 | if (i/=0) error stop "specify file to read" 19 | 20 | call h%open(pyp, "r") 21 | 22 | call h%read("/variable", vstr) 23 | if(vstr /= "Hello World!") error stop "h5py read variable length failed: " // trim(vstr) 24 | if (h%get_strpad("/variable") /= H5T_STR_NULLTERM_F) error stop "NULLTERM expected for /variable" 25 | 26 | call h%read("/nullpad", fstr) 27 | if(fstr /= "Hello World!") error stop "h5py read null pad failed: " // trim(fstr) 28 | if (h%get_strpad("/nullpad") /= H5T_STR_NULLPAD_F) error stop "NULLPAD expected for /nullpad" 29 | 30 | call h%read("/smiley", u1) 31 | print '(a)', "smiley: " // u1 32 | if(u1 /= smiley) error stop "test_utf8: smiley failed" 33 | 34 | call h%read("/wink", u1) 35 | print '(a)', "wink: " // u1 36 | if(u1 /= wink) error stop "test_utf8: wink failed" 37 | 38 | call h%readattr("/variable", "smiley", u1) 39 | print '(a)', "attribute: smiley: " // u1 40 | if(u1 /= smiley) error stop "test_utf8:attr: smiley failed" 41 | 42 | call h%readattr("/nullpad", "wink", u1) 43 | print '(a)', "attribute: wink: " // u1 44 | if(u1 /= wink) error stop "test_utf8:attr: wink failed" 45 | 46 | 47 | call h%close() 48 | 49 | print *, "OK: variable/nullpad length string read" 50 | 51 | end program 52 | -------------------------------------------------------------------------------- /test/test_version.f90: -------------------------------------------------------------------------------- 1 | program test_version 2 | !! tests that HDF5 library version is available 3 | 4 | use h5fortran, only : hdf5version 5 | 6 | implicit none 7 | 8 | character(24) :: vstr 9 | integer :: v(3), i, j, k, c 10 | 11 | v = hdf5version() 12 | 13 | print '(i0,a1,i0,a1,i0)', v(1), '.', v(2), '.', v(3) 14 | 15 | if(command_argument_count() < 1) stop 16 | 17 | call get_command_argument(1, vstr, status=i) 18 | if (i/=0) error stop "input version string to compare" 19 | 20 | k = index(vstr, '.') 21 | read(vstr(:k-1), '(I3)') c 22 | if(c /= v(1)) error stop "Major version mismatch: " // vstr(:k-1) 23 | j = index(vstr(k+1:), '.') 24 | read(vstr(k+1:k+j-1), '(I3)') c 25 | if(c /= v(2)) error stop "Minor version mismatch: " // vstr(k+1:k+j-1) 26 | i = scan(vstr(k+j+1:), '.-_') 27 | if (i == 0) then 28 | read(vstr(k+j+1:), '(I3)') c 29 | else 30 | read(vstr(k+j+1:k+j+i-1), '(I3)') c 31 | end if 32 | if(c /= v(3)) error stop "Release version mismatch: " // vstr(k+j+1:) 33 | 34 | end program 35 | -------------------------------------------------------------------------------- /test/test_write.f90: -------------------------------------------------------------------------------- 1 | program test_scalar 2 | 3 | use, intrinsic :: iso_fortran_env, only : real32, real64, int32 4 | 5 | use h5fortran, only : hdf5_file 6 | 7 | implicit none 8 | 9 | call test_simple_write('test_write.h5') 10 | print *, "OK: test simple write" 11 | 12 | call test_layout_write('test_layout.h5') 13 | print *, "OK: test layout write" 14 | 15 | 16 | contains 17 | 18 | 19 | subroutine test_simple_write(fn) 20 | character(*), intent(in) :: fn 21 | 22 | type(hdf5_file) :: h5 23 | integer(int32) :: d0, d1(1), d2(1,2), d3(1,2,3), d4(1,2,3,4), d5(1,2,3,4,5), d6(1,2,3,4,5,6), d7(1,2,3,4,5,6,7) 24 | 25 | call h5%open(fn, action="w") 26 | 27 | call h5%write("/d0", d0) 28 | call h5%write("/d1", d1) 29 | call h5%write("/d2", d2) 30 | call h5%write("/d3", d3) 31 | call h5%write("/d4", d4) 32 | call h5%write("/d5", d5) 33 | call h5%write("/d6", d6) 34 | call h5%write("/d7", d7) 35 | 36 | call h5%close() 37 | 38 | end subroutine test_simple_write 39 | 40 | 41 | subroutine test_layout_write(fn) 42 | 43 | character(*), intent(in) :: fn 44 | 45 | type(hdf5_file) :: h 46 | 47 | real(real32), dimension(1,1,1,1,1,1,1) :: d7_32 48 | real(real64), dimension(1,1,1,1,1,1,1) :: d7_64 49 | 50 | d7_32 = 42 51 | d7_64 = 42 52 | 53 | 54 | call h%open(fn, action="w") 55 | 56 | call h%write("/compact1d", [1,2,3], compact=.true.) 57 | call h%write("/contig1d", [1,2,3], compact=.false.) 58 | 59 | call h%write("/compact0d", 42_int32, compact=.true.) 60 | call h%write("/compact7d_32", d7_32, compact=.true.) 61 | call h%write("/compact7d_64", d7_64, compact=.true.) 62 | 63 | call h%write('/compact_r32', 142._real32, compact=.true.) 64 | call h%write('/compact_r64', 142._real64, compact=.true.) 65 | call h%write('/compact_i32', 142_int32, compact=.true.) 66 | 67 | call h%close() 68 | 69 | end subroutine test_layout_write 70 | 71 | end program 72 | -------------------------------------------------------------------------------- /valgrind.supp: -------------------------------------------------------------------------------- 1 | { 2 | HDF5malloc 3 | Memcheck:Leak 4 | match-leak-kinds: reachable 5 | fun:malloc 6 | ... 7 | obj:*/libhdf5* 8 | } 9 | { 10 | HDF5calloc 11 | Memcheck:Leak 12 | match-leak-kinds: reachable 13 | fun:calloc 14 | ... 15 | obj:*/libhdf5* 16 | } 17 | { 18 | HDF5param 19 | Memcheck:Param 20 | pwrite64(buf) 21 | fun:pwrite 22 | ... 23 | obj:*/libhdf5* 24 | } 25 | { 26 | HDF5memmove 27 | Memcheck:Cond 28 | fun:memmove 29 | ... 30 | obj:*/libhdf5* 31 | } 32 | { 33 | HDF5memmovevalue 34 | Memcheck:Value8 35 | fun:memmove 36 | ... 37 | obj:*/libhdf5* 38 | } 39 | --------------------------------------------------------------------------------