diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c8d81b4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,167 @@ +name: HDF5 Fortran API CI + +on: + push: + pull_request: + +jobs: + build_serial_HDF5_with_gfortran: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + gfortran \ + cmake \ + libhdf5-dev \ + libhdf5-fortran-102 + + - name: Verify compilers and HDF5 + run: | + gfortran --version + cmake --version + + - name: Configure test runner with CMake and run + run: | + cmake -S ./test/. -B ./build-tests \ + -DCMAKE_Fortran_COMPILER=gfortran + cmake --build ./build-tests + ./build-tests/h5testrunner + + + build_parallel_HDF5_with_gfortran: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + gfortran \ + cmake \ + libopenmpi-dev \ + libhdf5-openmpi-dev + + - name: Verify compilers and HDF5 + run: | + gfortran --version + cmake --version + mpif90 --version + + - name: Configure test runner with CMake and run + run: | + cmake -S ./test/. -B ./build-tests \ + -DCMAKE_Fortran_COMPILER=mpif90 -DMPI_VERSION=ON + cmake --build build-tests + ./build-tests/h5testrunner + ./build-tests/h5testrunner_mpi + mpirun --allow-run-as-root --oversubscribe \ + -np 2 ./build-tests/h5testrunner_mpi + + + build_serial_HDF5_with_ifx: + runs-on: ubuntu-latest + container: + image: intel/fortran-essentials:latest + + steps: + - uses: actions/checkout@v4 + + - name: Install system dependencies + run: | + apt-get update + apt-get install -y \ + cmake make gcc g++ \ + libhdf5-dev \ + wget \ + gpg + + - name: Install serial HDF5 1.14.6 with IFX + shell: bash + run: | + wget -O hdf5-1.14.6.tar.gz \ + https://sourceforge.net/projects/hdf5.mirror/files/hdf5_1.14.6/hdf5-1.14.6.tar.gz/download + tar -xzf hdf5-1.14.6.tar.gz + mkdir -p build-hdf5-ifx + + # configure with ifx + cmake -S ./hdf5-1.14.6 -B build-hdf5-ifx \ + -DCMAKE_Fortran_COMPILER=ifx \ + -DHDF5_BUILD_FORTRAN=ON \ + -DCMAKE_INSTALL_PREFIX=$HOME/hdf5-ifx \ + -DCMAKE_Fortran_FLAGS="-fPIE" \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + + cmake --build build-hdf5-ifx -j + cmake --install build-hdf5-ifx + + - name: Build and run tests with ifx + shell: bash + run: | + ifx --version + + cmake -S ./test/. -B build-tests-ifx \ + -DCMAKE_PREFIX_PATH=$HOME/hdf5-ifx \ + -DCMAKE_Fortran_COMPILER=ifx + cmake --build build-tests-ifx + ./build-tests-ifx/h5testrunner + + # hpckit requires more disk space than available on GitHub runner + # build_parallel_HDF5_with_ifx: + # runs-on: ubuntu-latest + # container: + # image: intel/fortran-hpckit:latest + + # steps: + # - uses: actions/checkout@v4 + + # - name: Install system dependencies + # run: | + # sudo apt-get update + # sudo apt-get install -y wget cmake make gcc g++ gpg + + # - name: Install parallel HDF5 1.14.6 with IFX + # shell: bash + # run: | + # wget -O hdf5-1.14.6.tar.gz \ + # https://sourceforge.net/projects/hdf5.mirror/files/hdf5_1.14.6/hdf5-1.14.6.tar.gz/download + # tar -xzf hdf5-1.14.6.tar.gz + # mkdir -p build-hdf5-mpiifx + + # cmake -S hdf5-1.14.6 -B build-hdf5-mpiifx \ + # -DCMAKE_Fortran_COMPILER=mpiifx \ + # -DCMAKE_C_COMPILER=mpiicx \ + # -DBUILD_SHARED_LIBS=OFF \ + # -DHDF5_BUILD_FORTRAN=ON \ + # -DHDF5_ENABLE_PARALLEL=ON \ + # -DHDF5_ENABLE_Z_LIB_SUPPORT=OFF \ + # -DHDF5_ENABLE_SZIP_SUPPORT=OFF \ + # -DCMAKE_INSTALL_PREFIX=$HOME/hdf5-mpiifx \ + + # cmake --build build-hdf5-mpiifx -j + # cmake --install build-hdf5-mpiifx + + # - name: Build and run MPI tests with mpiifx + # shell: bash + # run: | + # mpiifx --version + + # cmake -S ./test/. -B build-tests-mpiifx \ + # -DCMAKE_Fortran_COMPILER=mpiifx \ + # -DCMAKE_PREFIX_PATH=$HOME/hdf5-mpiifx \ + # -DMPI_VERSION=ON + # cmake --build build-tests-mpiifx + + # ./build-tests-mpiifx/h5testrunner + # ./build-tests-mpiifx/h5testrunner_mpi + # mpirun --allow-run-as-root --oversubscribe \ + # -np 2 ./build-tests-mpiifx/h5testrunner_mpi + \ No newline at end of file diff --git a/README.md b/README.md index 7b71736..bf4a91f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,50 @@ # HDF5_Fortran_API -A Fortran library which handles the interface to HDF5 library to give simpler I/O proceedures usable within Fortran coding projects + +A Fortran library which handles the interface to HDF5 library to give simpler I/O proceedures usable within Fortran coding projects. + +The modhdf5.f90 module contains subroutines which interface with the HDF5 library so that you do not need to use it directly in your code. You only need to call upon this module to perform the I/O to the .h5 file. + +You can look at examples of how this module is used within the `./tests/testio.f90` code. + +__Author:__ Alyn D. N. James + +__Version:__ 0.1.0 + +## How to use + +To add this to your Fortran code, add this module .f90 file to the compilation (cmake, make and so on) and load the public subroutines module in your Fortran code via + +```Fortran +use modhdf5 +``` + +The .inc files will needed to be in the same directory as the .f90 file, but these will automatically be included during the compilation. (These .inc files are used to reduce the amount of repetative code.) + +This module uses macro preprocessors to be compatible with both the serial and parallel HDF5 library. However, to enable the (MPI) parallel HDF5 capabilities in modhdf5, you need to add the -DMPI_modhdf5 flag to the `make` compilation step. This is done within cmake as seen in the `Tests` section below. + +## Dependencies + +Here is the list of libraries required for the modhdf5 module (with the version it was tested with): +- HDF5 library (1.14.6) +- Fortran 2018+ (gcc 15.1.0 and ifx 2025.3.0) +- MPI (for parallel enabled version) (Openmpi 5.0.8) + +For the repository, the following additional packages (with the version it was tested with) are needed: +- cmake (3.22.1) + +## Documentation + +Within modhdf5, the public subroutines to be used in your code are given at the top of the file. All the global variables are commented, and the inputs and outputs of all of the subroutines are labelled. The subroutines are named to be self-explanatory (some have short doc-strings for clarity). ## Tests -Compile the test runner (within gnu) with +This repository uses the [naturalFRUIT v0.7.4](https://cibinjoseph.github.io/naturalFRUIT/index.html) .f90 code for constructing the test runner for testing the functionality of the code base. Credit goes to the developers for `naturalfruit.f90`. + +Please look at the `.github/workflows/ci.yml` to see how modhdf5 has been compiled in different setups. + +### Serial Tests + +Compile the test runner (within GCC+Openmpi) with ``` cmake ../ -DCMAKE_Fortran_COMPILER=mpif90 @@ -20,3 +61,36 @@ run the test suite via ``` ./h5testrunner ``` + + +### (MPI) Parallel Tests + +Compile the test runner (within GCC+Openmpi) with + +``` +cmake ../ -DCMAKE_Fortran_COMPILER=mpif90 -DMPI_VERSION=ON +``` + +For conda environment, use + +``` +cmake ../ -DCMAKE_Fortran_COMPILER=mpif90 -DMPI_VERSION=ON -DCMAKE_PREFIX_PATH=$CONDA_PREFIX +``` + +run the test suite via + +``` +mpirun -np 2 ./h5testrunner_mpi +``` + +## Developer notes + +This modhdf5 has been written for HDF5 version 1.x.y. The additional functionality of HDF5 2.x.y has not been included. + +modhdf5 uses polymorphic, assumed-rank variable inputs to keep the interface generic. The variable rank and type are resolved within the read/write subroutines to interface with the HDF5 library Fortran APIs. modhdf5 currently sticks to the standard H5 native types available within the HDF5 library. These are: +- H5T_NATIVE_INTEGER +- H5T_NATIVE_REAL +- H5T_NATIVE_DOUBLE +- H5T_FORTRAN_S1 (modhdf5 extends this to a custom n-sized character type to handle aribitary sized character variable.) + +It is suggested for users to resolve their data types to conform with these native types and add the Fortran data type it was originally to the `fortran_type` attribute (via `fdtype_name` subroutine input/output) so that other users know its origin. \ No newline at end of file diff --git a/src/modhdf5.f90 b/src/modhdf5.f90 index 6aec629..dadd037 100644 --- a/src/modhdf5.f90 +++ b/src/modhdf5.f90 @@ -8,79 +8,107 @@ module modhdf5 implicit none -!----------------------------! -! HDF5 variables ! -!----------------------------! - +! modHDF5 proceedures ! +public :: open_hdf5_file, close_hdf5_file, create_hdf5_group, open_hdf5_group, close_hdf5_group, & + init_hyperspliced_array, open_hyperspliced_array, close_entire_hyperspliced_dataset, & + write_attributeHDF5, read_attributeHDF5, writeHDF5, readHDF5, writeHDF5_hypersplice, & + readHDF5_hypersplice +private :: mpi_open_hdf5_file, set_dxpl_id, get_var_dims, get_file_or_group_id, get_file_or_group_hyp, & + get_array_dset_type, get_dset_type, create_char_dset_type, config_hyper_dims + +! modHDF5 variables ! +!HDF5 extension +character(256), public :: h5filext='.h5' !HDF5 extension -character(256) :: h5filext='.h5' +character(256), public :: modhdf5_version='0.1.0' !user input to use the hdf5 files -logical :: use_hdf5=.false. -!array of HDF5 file handles so up to 10 files can be used simulatenously -integer(HID_T), private :: file_id(10) -!array of HDF5 file handles so up to 10 property lists can be used simulatenously -integer(HID_T), private :: hyp_plist_id(10) -!array of HDF5 file handles so up to 10 dataset lists can be used simulatenously -integer(HID_T), private :: hyp_dset_id(10) -!array of HDF5 file handles so up to 10 dataspaces lists can be used simulatenously -integer(HID_T), private :: hyp_dataspace_id(10) -!array of HDF5 file handles so up to 100 h5 groups can be used simulatenously -integer(HID_T), private :: group_id(100) -!dimensions of the hyperspliced array -integer(HSIZE_T), private :: hyp_dims(7,10) +logical, public :: use_hdf5=.false. !h5 compression level integer, private :: compression_level = 4 -! mpi hdf5 variables - mpi hdf5 is enabled when compiling with -DMPI_modhdf5 -!h5 mpicheck - set to false by default -logical, private :: mpi_h5 = .false. -!h5 mpicomm copy -integer, private :: mpicomm_h5 = -1 -!h5 collective read/write if mpi hdf5 is enabled -integer(HID_T), private :: mpi_dxpl_id(10) + +type h5_ids_t + !hyperslab/hypersplice property list ids + integer(HID_T) :: plist = -1 + !hyperslab/hypersplice dataset ids + integer(HID_T) :: dset = -1 + !hyperslab/hypersplice dataspace ids + integer(HID_T) :: dspace = -1 + !dimensions of the hyperspliced array + integer(HSIZE_T) :: dims(7) + !used for h5 collective mpi read/write (when mpi_h5=.true.) + integer(HID_T) :: dxpl = -1 + !h5 compatible data type for variable to/from h5 file + integer(HID_T) :: dset_type = -1 +end type h5_ids_t + +! derived type containing the h5 file ids for each file in use +type h5_file_ids_t + !HDF5 file handle + integer(HID_T) :: fid + !h5 mpicheck - set to false by default + logical :: mpi_h5 = .false. + !h5 mpicomm copy + integer :: mpicomm_h5 = -1 + !hyperslab/hypersplice ids + type(h5_ids_t) :: hyp(10) +end type h5_file_ids_t +type(h5_file_ids_t), private, target :: h5_fids(10) + +! derived type containing the h5 file ids for each file in use +type h5_group_ids_t + !HDF5 file index for h5_file_ids_t + integer :: file_idx + !h5 group id + integer(HID_T) :: gid + !hyperslab/hypersplice ids + type(h5_ids_t) :: hyp(10) +end type h5_group_ids_t +type(h5_group_ids_t), private, target :: h5_gids(10) contains -subroutine open_hdf5_file(id_entry,filename,writing,mpi_file,mpicom) +subroutine open_hdf5_file(fid_idx,filename,writing,mpi_file,mpicom) -character(len=*), intent(in) :: filename -integer, intent(in) :: id_entry -logical, intent(in) :: writing -logical, optional, intent(in) :: mpi_file -integer, optional, intent(in) :: mpicom +character(len=*), intent(in) :: filename !prefix of .h5 file +integer, intent(in) :: fid_idx !file index to use this file +logical, intent(in) :: writing !writing (.true.) or reading (.false.) file +logical, optional, intent(in) :: mpi_file !.true. if open file in mpi mode +integer, optional, intent(in) :: mpicom !mpi communicator integer hdferr ! open file with mpi if optional inputs are present if (present(mpi_file) .and. present(mpicom)) then if (mpi_file) then - call mpi_open_hdf5_file(id_entry,filename,writing,mpicom) + call mpi_open_hdf5_file(fid_idx,filename,writing,mpicom) return end if end if call h5open_f(hdferr) if (writing) then !write to file - call h5fcreate_f(trim(filename)//trim(h5filext), H5F_ACC_TRUNC_F, file_id(id_entry), hdferr) + call h5fcreate_f(trim(filename)//trim(h5filext), H5F_ACC_TRUNC_F, h5_fids(fid_idx)%fid, hdferr) else !read file - call h5fopen_f(trim(filename)//trim(h5filext), H5F_ACC_RDONLY_F, file_id(id_entry), hdferr) + call h5fopen_f(trim(filename)//trim(h5filext), H5F_ACC_RDONLY_F, h5_fids(fid_idx)%fid, hdferr) end if end subroutine open_hdf5_file -subroutine mpi_open_hdf5_file(id_entry,filename,writing,mpicom) +subroutine mpi_open_hdf5_file(fid_idx,filename,writing,mpicom) + ! use the mpi library within mpi HDF5 enabled #ifdef MPI_modhdf5 - use mpi + use mpi, only: MPI_COMM_NULL, MPI_INFO_NULL #endif -character(len=*), intent(in) :: filename -integer, intent(in) :: id_entry -logical, intent(in) :: writing -integer, intent(in) :: mpicom +character(len=*), intent(in) :: filename !prefix of .h5 file +integer, intent(in) :: fid_idx !file index to use this file +logical, intent(in) :: writing !writing (.true.) or reading (.false.) file +integer, intent(in) :: mpicom !mpi communicator integer hdferr -integer(HID_T) :: plist_id +type(h5_ids_t) :: h5_id !preprocessor macro to enable mpi HDF5 if compiled with -DMPI_modhdf5 #ifdef MPI_modhdf5 @@ -88,22 +116,22 @@ subroutine mpi_open_hdf5_file(id_entry,filename,writing,mpicom) write(*,*) "Error mpi_open_hdf5_file: MPI Communicate has not been initiated." stop "stopping" end if - mpi_h5 = .true. - mpicomm_h5 = mpicom + h5_fids(fid_idx)%mpi_h5 = .true. + h5_fids(fid_idx)%mpicomm_h5 = mpicom call h5open_f(hdferr) ! file access property for MPIO - call h5pcreate_f(H5P_FILE_ACCESS_F, plist_id, hdferr) - call h5pset_fapl_mpio_f(plist_id, mpicom, MPI_INFO_NULL, hdferr) + call h5pcreate_f(H5P_FILE_ACCESS_F, h5_id%plist, hdferr) + call h5pset_fapl_mpio_f(h5_id%plist, mpicom, MPI_INFO_NULL, hdferr) if (writing) then !write to file - call h5fcreate_f(trim(filename)//trim(h5filext), H5F_ACC_TRUNC_F, file_id(id_entry), hdferr, access_prp = plist_id) + call h5fcreate_f(trim(filename)//trim(h5filext), H5F_ACC_TRUNC_F, h5_fids(fid_idx)%fid, hdferr, access_prp = h5_id%plist) else !read file - call h5fopen_f(trim(filename)//trim(h5filext), H5F_ACC_RDONLY_F, file_id(id_entry), hdferr, access_prp = plist_id) + call h5fopen_f(trim(filename)//trim(h5filext), H5F_ACC_RDONLY_F, h5_fids(fid_idx)%fid, hdferr, access_prp = h5_id%plist) end if - call h5pclose_f(plist_id, hdferr) + call h5pclose_f(h5_id%plist, hdferr) return #endif write(*,*) "Error mpi_open_hdf5_file: MPI_modhdf5 has not been enabled. Cannot open file." @@ -112,71 +140,64 @@ subroutine mpi_open_hdf5_file(id_entry,filename,writing,mpicom) end subroutine mpi_open_hdf5_file -subroutine create_hdf5_group(id_entry,groupname,dxpl_index) +subroutine create_hdf5_group(fid_idx,gid_idx,groupname) -character(len=*), intent(in) :: groupname -integer, intent(in) :: id_entry -integer, optional, intent(in) :: dxpl_index +character(len=*), intent(in) :: groupname !h5 group name +integer, intent(in) :: fid_idx, gid_idx !file and group indices (used to associate group to file) integer hdferr -call h5gcreate_f(file_id(id_entry), trim(groupname), group_id(id_entry), hdferr) - -if (present(dxpl_index)) call mpi_set_dxpl_id(dxpl_index) +call h5gcreate_f(h5_fids(fid_idx)%fid, trim(groupname), h5_gids(gid_idx)%gid, hdferr) +h5_gids(gid_idx)%file_idx = fid_idx end subroutine create_hdf5_group -subroutine open_hdf5_group(id_entry,groupname,dxpl_index) +subroutine open_hdf5_group(fid_idx,gid_idx,groupname) -character(len=*), intent(in) :: groupname -integer, intent(in) :: id_entry -integer, optional, intent(in) :: dxpl_index +character(len=*), intent(in) :: groupname !h5 group name +integer, intent(in) :: fid_idx, gid_idx !file and group indices (used to associate group to file) integer hdferr -call h5gopen_f(file_id(id_entry), trim(groupname), group_id(id_entry), hdferr) - -if (present(dxpl_index)) call mpi_set_dxpl_id(dxpl_index) +call h5gopen_f(h5_fids(fid_idx)%fid, trim(groupname), h5_gids(gid_idx)%gid, hdferr) +h5_gids(gid_idx)%file_idx = fid_idx end subroutine open_hdf5_group -subroutine close_hdf5_group(id_entry,dxpl_index) +subroutine close_hdf5_group(gid_idx) -integer, intent(in) :: id_entry -integer, optional, intent(in) :: dxpl_index +integer, intent(in) :: gid_idx !group index integer hdferr -call h5gclose_f(group_id(id_entry), hdferr) - -if (present(dxpl_index)) call close_mpi_set_dxpl_id(dxpl_index) +call h5gclose_f(h5_gids(gid_idx)%gid, hdferr) end subroutine close_hdf5_group -subroutine close_hdf5_file(id_entry,dxpl_index) +subroutine close_hdf5_file(fid_idx) -integer, intent(in) :: id_entry -integer, optional, intent(in) :: dxpl_index +integer, intent(in) :: fid_idx !file index integer hdferr -if (present(dxpl_index)) call h5pclose_f(mpi_dxpl_id(dxpl_index), hdferr) -call h5fclose_f(file_id(id_entry), hdferr) +call h5fclose_f(h5_fids(fid_idx)%fid, hdferr) call h5close_f(hdferr) -if (mpi_h5) then - mpi_h5 = .false. - mpicomm_h5 = -1 +if (h5_fids(fid_idx)%mpi_h5) then + h5_fids(fid_idx)%mpi_h5 = .false. + h5_fids(fid_idx)%mpicomm_h5 = -1 end if end subroutine close_hdf5_file -subroutine set_dxpl_id(dxpl_id) +subroutine set_dxpl_id(dxpl_id,using_mpi,mpi_independent) +! Set dxpl id for mpi handling (independent or collective modes) within the .h5 file integer(HID_T), intent(inout) :: dxpl_id +logical, intent(in) :: using_mpi, mpi_independent integer hdferr @@ -184,42 +205,23 @@ subroutine set_dxpl_id(dxpl_id) call h5pcreate_f(H5P_DATASET_XFER_F, dxpl_id, hdferr) ! set collective read if mpi hdf5 is enabled #ifdef MPI_modhdf5 - if (mpi_h5) then - call h5pset_dxpl_mpio_f(dxpl_id, H5FD_MPIO_COLLECTIVE_F, hdferr) - !call h5pset_dxpl_mpio_f(dxpl_id, H5FD_MPIO_INDEPENDENT_F, hdferr) - end if + if (using_mpi) then + if (mpi_independent) then + call h5pset_dxpl_mpio_f(dxpl_id, H5FD_MPIO_INDEPENDENT_F, hdferr) + else + call h5pset_dxpl_mpio_f(dxpl_id, H5FD_MPIO_COLLECTIVE_F, hdferr) + end if + end if #endif end subroutine set_dxpl_id -subroutine mpi_set_dxpl_id(dxpl_index) - -integer, intent(in) :: dxpl_index - -call set_dxpl_id(mpi_dxpl_id(dxpl_index)) - -end subroutine mpi_set_dxpl_id - - - -subroutine close_mpi_set_dxpl_id(dxpl_index) - -integer, intent(in) :: dxpl_index - -integer hdferr - -call h5pclose_f(mpi_dxpl_id(dxpl_index), hdferr) - -end subroutine close_mpi_set_dxpl_id - - - subroutine get_var_dims(variable,rnk,dims) -class(*), intent(in), dimension(..) :: variable -integer, intent(inout) :: rnk -integer(HSIZE_T), intent(inout) :: dims(7) +class(*), intent(in), dimension(..) :: variable !variable of interest +integer, intent(inout) :: rnk !rank of variable +integer(HSIZE_T), intent(inout) :: dims(7) !dimension of each rank integer i @@ -235,25 +237,43 @@ subroutine get_var_dims(variable,rnk,dims) end subroutine get_var_dims -subroutine get_file_or_group_id(to_group,id_entry,file_or_group_id) +subroutine get_file_or_group_id(to_group,id_idx,file_or_group_id,using_mpi) -logical, intent(in) :: to_group !logical for distinguishing whether to write to group or not -integer, intent(in) :: id_entry !index for file_id/group_id -integer(HID_T), intent(out) :: file_or_group_id +logical, intent(in) :: to_group !logical for distinguishing whether to write to group or file +integer, intent(in) :: id_idx !index for file/group +integer(HID_T), intent(out) :: file_or_group_id !h5 file/group id +logical, intent(out) :: using_mpi !.true. if file is using mpi mode if (to_group) then - file_or_group_id = group_id(id_entry) + file_or_group_id = h5_gids(id_idx)%gid + using_mpi = h5_fids(h5_gids(id_idx)%file_idx)%mpi_h5 else - file_or_group_id = file_id(id_entry) + file_or_group_id = h5_fids(id_idx)%fid + using_mpi = h5_fids(id_idx)%mpi_h5 end if end subroutine get_file_or_group_id +subroutine get_file_or_group_hyp(to_group,id_idx,hyp_id_idx,hyp) + +logical, intent(in) :: to_group !logical for whether to read/write to group or not +integer, intent(in) :: id_idx, hyp_id_idx !group/file index and hypersplice/hyperslab index +type(h5_ids_t), intent(inout), pointer :: hyp !hypersplice/hyperslab datatype + +if (to_group) then + hyp => h5_gids(id_idx)%hyp(hyp_id_idx) +else + hyp => h5_fids(id_idx)%hyp(hyp_id_idx) +end if + +end subroutine get_file_or_group_hyp + + subroutine get_array_dset_type(variable,dset_type,custom_type) -class(*), intent(in), dimension(..) :: variable -integer(HID_T), intent(out) :: dset_type -logical, intent(out) :: custom_type +class(*), intent(in), dimension(..) :: variable !variable of interest +integer(HID_T), intent(out) :: dset_type !corresponding h5 dataset type for I/O +logical, intent(out) :: custom_type !.true. if dset_type is a custom type select rank(variable) rank(0); call get_dset_type(variable,dset_type,custom_type) @@ -270,9 +290,10 @@ end subroutine get_array_dset_type subroutine get_dset_type(variable,dset_type,custom_type) -class(*), intent(in) :: variable -integer(HID_T), intent(out) :: dset_type -logical, intent(out) :: custom_type + +class(*), intent(in) :: variable !variable of interest +integer(HID_T), intent(out) :: dset_type !corresponding h5 dataset type for I/O +logical, intent(out) :: custom_type !.true. if dset_type is a custom type custom_type=.false. @@ -287,9 +308,10 @@ end subroutine get_dset_type subroutine create_char_dset_type(variable,char_type,custom_type) -character(*), intent(in) :: variable -integer(HID_T), intent(out) :: char_type -logical, intent(out) :: custom_type + +character(*), intent(in) :: variable !variable of interest +integer(HID_T), intent(out) :: char_type !corresponding custom h5 character type for I/O +logical, intent(out) :: custom_type !.true. if dset_type is a custom type integer :: hdferr @@ -300,101 +322,153 @@ subroutine create_char_dset_type(variable,char_type,custom_type) end subroutine -subroutine init_hyperspliced_array(id_entry,dataset_name,variable,write_to_group,hyp_id_entry,n_hyp_dim,dxpl_index,fdtype_name) +subroutine init_hyperspliced_array(id_idx,dataset_name,variable,write_to_group,hyp_id_idx,n_hyp_dim,fdtype_name) +! Initiate the hypersplice/hyperslab array for writing -integer, intent(in) :: id_entry -character(len=*), intent(in) :: dataset_name -class(*), intent(in), dimension(..) :: variable -logical, intent(in) :: write_to_group -integer, intent(in) :: hyp_id_entry,n_hyp_dim -integer, optional, intent(in) :: dxpl_index -character(len=*), optional, intent(in) :: fdtype_name +integer, intent(in) :: id_idx !index for file/group +character(len=*), intent(in) :: dataset_name !name of h5 dataset +class(*), intent(in), dimension(..) :: variable !variable slice to write +logical, intent(in) :: write_to_group !.true. to write to group, otherwise to file +integer, intent(in) :: hyp_id_idx,n_hyp_dim !hypersplice/slab index and hyper dimension size (for last rank not in slice) +character(len=*), optional, intent(in) :: fdtype_name !variable datatype name within dataset attribute integer(HID_T) :: dspace_id, dset_id, file_or_group_id, dset_type -integer :: hdferr, i, r +integer :: hdferr, i, rnk integer(HSIZE_T) :: dims(7), chunk_dims(7) -logical custom_type +logical custom_type, using_mpi +type(h5_ids_t), pointer :: hyp -r = rank(variable) +rnk = rank(variable) -if ((r < 0) .or. (r > 6)) then +if ((rnk < 0) .or. (rnk > 6)) then write(*,*) "(init_hyperspliced_array): input variable has invalid rank. Only a rank between 0 and 6 can be used." stop "Stopping." end if -call get_array_dset_type(variable,dset_type,custom_type) -call get_var_dims(variable,r,hyp_dims(:,hyp_id_entry)) -call get_file_or_group_id(write_to_group,id_entry,file_or_group_id) +call get_file_or_group_id(write_to_group,id_idx,file_or_group_id,using_mpi) +call get_file_or_group_hyp(write_to_group,id_idx,hyp_id_idx,hyp) +call get_var_dims(variable,rnk,hyp%dims) +call get_array_dset_type(variable,hyp%dset_type,custom_type) +call set_dxpl_id(hyp%dxpl,using_mpi,mpi_independent=.false.) -chunk_dims = hyp_dims(:,hyp_id_entry) -r = r + 1 -hyp_dims(r,hyp_id_entry) = n_hyp_dim -chunk_dims(r) = 1 +chunk_dims = hyp%dims +rnk = rnk + 1 +hyp%dims(rnk) = n_hyp_dim +chunk_dims(rnk) = 1 ! Create dataspace for whole dataset -call h5screate_simple_f(r, hyp_dims(:,hyp_id_entry), hyp_dataspace_id(hyp_id_entry), hdferr) +call h5screate_simple_f(rnk, hyp%dims(1:rnk), hyp%dspace, hdferr) -! Create dataset creation property list -call h5pcreate_f(H5P_DATASET_CREATE_F, hyp_plist_id(hyp_id_entry), hdferr) +! Create property list for chunking, and then compression +call h5pcreate_f(H5P_DATASET_CREATE_F, hyp%plist, hdferr) +call h5pset_chunk_f(hyp%plist, rnk, chunk_dims, hdferr) ! Set chunking +call h5pset_deflate_f(hyp%plist, compression_level, hdferr) ! Enable compression (GZIP level 4) +call h5pset_shuffle_f(hyp%plist, hdferr) ! Enable shuffle filter (best compression) -! Set chunking -call h5pset_chunk_f(hyp_plist_id(hyp_id_entry), r, chunk_dims, hdferr) +! Create dataset with chunking + compression +call h5dcreate_f(file_or_group_id, trim(dataset_name), hyp%dset_type, hyp%dspace, hyp%dset, hdferr, hyp%plist) -! Enable compression (GZIP level 4) -call h5pset_deflate_f(hyp_plist_id(hyp_id_entry), compression_level, hdferr) +if (present(fdtype_name)) call write_attributeHDF5(id_idx,dataset_name,"fortran_type",fdtype_name,write_to_group,hyp%dset) -! Enable shuffle filter (best compression) -call h5pset_shuffle_f(hyp_plist_id(hyp_id_entry), hdferr) +end subroutine init_hyperspliced_array -! Create dataset with chunking + compression -call h5dcreate_f(file_or_group_id, trim(dataset_name), dset_type, & - hyp_dataspace_id(hyp_id_entry), hyp_dset_id(hyp_id_entry), & - hdferr, hyp_plist_id(hyp_id_entry)) -if (present(fdtype_name)) call write_attributeHDF5(id_entry,dataset_name,"fortran_type",fdtype_name,write_to_group,hyp_dset_id(hyp_id_entry)) +subroutine open_hyperspliced_array(id_idx,dataset_name,variable,read_group,hyp_id_idx,fdtype_name) +! Open the hypersplice/hyperslab array for reading -if (present(dxpl_index)) call mpi_set_dxpl_id(dxpl_index) +integer, intent(in) :: id_idx !index for file/group +character(len=*), intent(in) :: dataset_name !name of h5 dataset +class(*), intent(in), dimension(..) :: variable !variable slice/slab to read +logical, intent(in) :: read_group !.true. to read from group, otherwise from file +integer, intent(in) :: hyp_id_idx !hypersplice/slab index +character(len=*), optional, intent(inout) :: fdtype_name !datatype name for corresponding attribute -end subroutine init_hyperspliced_array +integer(HID_T) :: dspace_id, dset_id, file_or_group_id, dset_type + +integer :: hdferr, i, rnk +integer(HSIZE_T) :: dims(7), chunk_dims(7) +logical custom_type, using_mpi +type(h5_ids_t), pointer :: hyp + +rnk = rank(variable) + +if ((rnk < 0) .or. (rnk > 6)) then + write(*,*) "(init_hyperspliced_array): input variable has invalid rank. Only a rank between 0 and 6 can be used." + stop "Stopping." +end if + +call get_file_or_group_id(read_group,id_idx,file_or_group_id,using_mpi) +call get_file_or_group_hyp(read_group,id_idx,hyp_id_idx,hyp) +call get_var_dims(variable,rnk,hyp%dims) +call get_array_dset_type(variable,hyp%dset_type,custom_type) +call set_dxpl_id(hyp%dxpl,using_mpi,mpi_independent=.true.) + +call h5dopen_f(file_or_group_id, trim(dataset_name), hyp%dset, hdferr) +call h5dget_space_f(hyp%dset, hyp%dspace, hdferr) +if (present(fdtype_name)) call read_attributeHDF5(id_idx,dataset_name,"fortran_type",fdtype_name,read_group,hyp%dset) -subroutine close_entire_hyperspliced_dataset(hyp_id_entry,dxpl_index) +end subroutine open_hyperspliced_array -integer, intent(in) :: hyp_id_entry -integer, optional, intent(in) :: dxpl_index + +subroutine close_entire_hyperspliced_dataset(id_idx,hyp_id_idx,in_group) + +integer, intent(in) :: id_idx, hyp_id_idx !index for file/group and hypersplice/slab index +logical, intent(in) :: in_group !.true. if variable is in group integer :: hdferr +type(h5_ids_t) :: reset_hyp +type(h5_ids_t), pointer :: hyp -if (present(dxpl_index)) call h5pclose_f(mpi_dxpl_id(dxpl_index), hdferr) -call h5pclose_f(hyp_plist_id(hyp_id_entry), hdferr) -call h5dclose_f(hyp_dset_id(hyp_id_entry), hdferr) -call h5sclose_f(hyp_dataspace_id(hyp_id_entry), hdferr) +call get_file_or_group_hyp(in_group,id_idx,hyp_id_idx,hyp) +if (hyp%dxpl /= -1) call h5pclose_f(hyp%dxpl, hdferr) +if (hyp%plist /= -1) call h5pclose_f(hyp%plist, hdferr) +if (hyp%dset /= -1) call h5dclose_f(hyp%dset, hdferr) +if (hyp%dspace /= -1) call h5sclose_f(hyp%dspace, hdferr) +hyp = reset_hyp end subroutine close_entire_hyperspliced_dataset -!-----------------------------! -! Writing Subroutines ! -!-----------------------------! +subroutine config_hyper_dims(rnk,nslice,hyp_idx,dims,offset) +integer, intent(inout) :: rnk !variable rank +integer, intent(in) :: nslice, hyp_idx !number of hyper slices and hypersplice/slab index +integer(HSIZE_T), intent(inout) :: dims(7), offset(7) !variable dimensions and offset position for I/O + +offset(:) = 0 +if (nslice == 1) then !hypersplice + rnk = rnk + 1 + dims(rnk) = nslice + offset(rnk) = hyp_idx - 1 +else if (nslice >= 1) then !hyperslab + dims(rnk) = nslice + offset(rnk) = hyp_idx - 1 +else !dummy write + dims(:) = 0 +end if +end subroutine config_hyper_dims -subroutine write_attributeHDF5(id_entry,dataset_name,attr_name,attr,write_group,dset_id) -integer, intent(in) :: id_entry -character(len=*), intent(in) :: dataset_name,attr_name -class(*), intent(in), dimension(..) :: attr -logical, intent(in) :: write_group -integer(HID_T), optional, intent(in) :: dset_id +subroutine write_attributeHDF5(id_idx,dataset_name,attr_name,attr,write_group,dset_id) +! Write the attribute to h5 dataset + +integer, intent(in) :: id_idx !index for file/group +character(len=*), intent(in) :: dataset_name,attr_name !h5 dataset and attribute name +class(*), intent(in), dimension(..) :: attr !attribute of interest +logical, intent(in) :: write_group !.true. to write to group, otherwise to file +integer(HID_T), optional, intent(in) :: dset_id !corresponding h5 dataset id integer(HID_T) :: dset_id_, space_id, file_or_group_id, type_id, attr_id integer(HSIZE_T) :: dims(7) integer :: hdferr, rnk -logical :: custom_type +logical :: custom_type, using_mpi rnk = rank(attr) call get_var_dims(attr,rnk,dims) -call get_file_or_group_id(write_group,id_entry,file_or_group_id) +call get_file_or_group_id(write_group,id_idx,file_or_group_id,using_mpi) +call get_array_dset_type(attr,type_id,custom_type) if(.not. present(dset_id)) then call h5dopen_f(file_or_group_id, trim(dataset_name), dset_id_, hdferr) @@ -403,7 +477,6 @@ subroutine write_attributeHDF5(id_entry,dataset_name,attr_name,attr,write_group, end if call h5screate_simple_f(rnk, dims, space_id, hdferr) -call get_array_dset_type(attr,type_id,custom_type) call h5acreate_f(dset_id_, attr_name, type_id, space_id, attr_id, hdferr) select rank(attr) @@ -432,344 +505,294 @@ subroutine write_attributeHDF5(id_entry,dataset_name,attr_name,attr,write_group, end subroutine -subroutine read_attributeHDF5(id_entry,dataset_name,attr_name,attr,read_group,dset_id) +subroutine writeHDF5(id_idx,dataset_name,variable_to_write,write_to_group,fdtype_name) +! Write the variable_to_write to h5 dataset -integer, intent(in) :: id_entry -character(len=*), intent(in) :: dataset_name,attr_name -class(*), intent(inout), dimension(..) :: attr -logical, intent(in) :: read_group -integer(HID_T), optional, intent(in) :: dset_id +integer, intent(in) :: id_idx !index for file/group +character(len=*), intent(in) :: dataset_name !h5 dataset name +class(*), intent(in), dimension(..) :: variable_to_write !variable of interest +logical, intent(in) :: write_to_group !.true. to write to group, otherwise to file +character(len=*), optional, intent(in) :: fdtype_name !variable datatype name within dataset attribute -integer(HID_T) :: dset_id_, file_or_group_id, type_id, attr_id -integer(HSIZE_T) :: dims(7) +integer(HID_T) :: file_or_group_id integer :: hdferr, rnk -logical :: custom_type +logical custom_type,using_mpi +type(h5_ids_t) :: h5_ids -rnk = rank(attr) -call get_var_dims(attr,rnk,dims) -call get_file_or_group_id(read_group,id_entry,file_or_group_id) +rnk = rank(variable_to_write) -if(.not. present(dset_id)) then - call h5dopen_f(file_or_group_id, trim(dataset_name), dset_id_, hdferr) -else - dset_id_ = dset_id +if ((rnk < 0) .or. (rnk > 7)) then + write(*,*) "(writeHDF5): input variable to write has invalid rank. Only a rank between 0 and 7 can be used." + stop "Stopping." end if -! Get attribute datatype and then read it -call get_array_dset_type(attr,type_id,custom_type) -call h5aopen_f(dset_id_, trim(attr_name), attr_id, hdferr) -select rank(attr) +call get_var_dims(variable_to_write,rnk,h5_ids%dims) +call get_array_dset_type(variable_to_write,h5_ids%dset_type,custom_type) +call get_file_or_group_id(write_to_group,id_idx,file_or_group_id,using_mpi) +call set_dxpl_id(h5_ids%dxpl,using_mpi,mpi_independent=.true.) + +! Create dataspace (within group or not) for integer and save within that space +call h5screate_simple_f(rnk, h5_ids%dims(1:rnk), h5_ids%dspace, hdferr) +call h5dcreate_f(file_or_group_id, trim(dataset_name), h5_ids%dset_type, h5_ids%dspace, h5_ids%dset, hdferr) + +select rank(variable_to_write) rank(0) -#include "modhdf5_attribute_read.inc" +#include "modhdf5_write.inc" rank(1) -#include "modhdf5_attribute_read.inc" +#include "modhdf5_write.inc" rank(2) -#include "modhdf5_attribute_read.inc" +#include "modhdf5_write.inc" rank(3) -#include "modhdf5_attribute_read.inc" +#include "modhdf5_write.inc" rank(4) -#include "modhdf5_attribute_read.inc" +#include "modhdf5_write.inc" rank(5) -#include "modhdf5_attribute_read.inc" +#include "modhdf5_write.inc" rank(6) -#include "modhdf5_attribute_read.inc" +#include "modhdf5_write.inc" rank(7) -#include "modhdf5_attribute_read.inc" +#include "modhdf5_write.inc" end select -call h5aclose_f(attr_id, hdferr) -if (custom_type) call h5tclose_f(type_id, hdferr) !close custom dset_type -if(.not. present(dset_id)) call h5dclose_f(dset_id_, hdferr) - -end subroutine read_attributeHDF5 - - -subroutine writeHDF5(id_entry,dataset_name,variable_to_write,write_to_group,dxpl_index,fdtype_name) - -integer, intent(in) :: id_entry -character(len=*), intent(in) :: dataset_name -class(*), intent(in), dimension(..) :: variable_to_write -logical, intent(in) :: write_to_group -integer, optional, intent(in) :: dxpl_index -character(len=*), optional, intent(in) :: fdtype_name - -integer(HID_T) :: dspace_id, dset_id, file_or_group_id, dset_type, dxpl_id -integer :: hdferr, i, r, id_type -integer(HSIZE_T) :: dims(7) -logical custom_type - -r = rank(variable_to_write) - -if ((r < 0) .or. (r > 7)) then - write(*,*) "(writeHDF5): input variable to write has invalid rank. Only a rank between 0 and 7 can be used." - stop "Stopping." -end if - -call get_array_dset_type(variable_to_write,dset_type,custom_type) -call get_var_dims(variable_to_write,r,dims) -call get_file_or_group_id(write_to_group,id_entry,file_or_group_id) - -if (present(dxpl_index)) then - dxpl_id = mpi_dxpl_id(dxpl_index) -else - call set_dxpl_id(dxpl_id) -end if - -! Create dataspace (within group or not) for integer and save within that space -call h5screate_simple_f(r, dims(1:r), dspace_id, hdferr) -call h5dcreate_f(file_or_group_id, trim(dataset_name), dset_type, dspace_id, dset_id, hdferr) -call h5dataset_write(variable_to_write, dims, dset_type, dset_id, dxpl_id) - -if (present(fdtype_name)) call write_attributeHDF5(id_entry,dataset_name,"fortran_type",fdtype_name,write_to_group,dset_id) +if (present(fdtype_name)) call write_attributeHDF5(id_idx,dataset_name,"fortran_type",fdtype_name,write_to_group,h5_ids%dset) -if (custom_type) call h5tclose_f(dset_type, hdferr) !close custom dset_type -if (.not. present(dxpl_index)) call h5pclose_f(dxpl_id, hdferr) -call h5dclose_f(dset_id, hdferr) -call h5sclose_f(dspace_id, hdferr) +if (custom_type) call h5tclose_f(h5_ids%dset_type, hdferr) !close custom dset_type +call h5pclose_f(h5_ids%dxpl, hdferr) +call h5dclose_f(h5_ids%dset, hdferr) +call h5sclose_f(h5_ids%dspace, hdferr) end subroutine writeHDF5 -subroutine writeHDF5_hypersplice(id_entry,dataset_name,variable_to_write,write_to_group,hyp_id_entry,hyp_index,nslice,dxpl_index) +subroutine writeHDF5_hypersplice(id_idx,dataset_name,variable_to_write,write_to_group,hyp_id_idx,hyp_idx,nslice) +! Write the hypersplice/hyperslab array to h5 dataset -integer, intent(in) :: id_entry !index for file_id/group_id -character(len=*), intent(in) :: dataset_name !name of dataset in h5 file -class(*), intent(in), dimension(..) :: variable_to_write !value to be written -logical, intent(in) :: write_to_group !logical for distinguishing whether to write to group or not -integer, intent(in) :: hyp_id_entry -integer, intent(in) :: hyp_index -integer, intent(in) :: nslice !>= 1 to write slice/slab, else do not write data -integer, optional, intent(in) :: dxpl_index +integer, intent(in) :: id_idx !index for file/group +character(len=*), intent(in) :: dataset_name !name of h5 dataset +class(*), intent(in), dimension(..) :: variable_to_write !variable of interest +logical, intent(in) :: write_to_group !.true. to write to group, otherwise to file +integer, intent(in) :: hyp_id_idx, hyp_idx !hypersplice/slab index and hyper array index (for last rank not in slice) +integer, intent(in) :: nslice !number of hyper slices (> 1 to write slab, = 1 for slice, else do not write data) -integer(HID_T) :: dspace_id, dset_id, file_or_group_id, dset_type, memspace_id, dxpl_id -integer :: hdferr, i, r, id_type +integer(HID_T) :: file_or_group_id, dset_type, memspace_id +integer :: hdferr, rnk integer(HSIZE_T) :: dims(7), offset(7) -logical :: dset_exists, custom_type +logical :: dset_exists, custom_type,using_mpi +type(h5_ids_t), pointer :: hyp -r = rank(variable_to_write) +rnk = rank(variable_to_write) -if ((r < 0) .or. (r > 6)) then +if ((rnk < 0) .or. (rnk > 6)) then write(*,*) "(writeHDF5_hypersplice): input variable to write has invalid rank. Only a rank between 0 and 6 can be used." stop "Stopping." end if +call get_file_or_group_id(write_to_group,id_idx,file_or_group_id,using_mpi) +call get_file_or_group_hyp(write_to_group,id_idx,hyp_id_idx,hyp) +call get_var_dims(variable_to_write,rnk,dims) call get_array_dset_type(variable_to_write,dset_type,custom_type) -call get_var_dims(variable_to_write,r,dims) -call get_file_or_group_id(write_to_group,id_entry,file_or_group_id) - -dset_id = hyp_dset_id(hyp_id_entry) -dspace_id = hyp_dataspace_id(hyp_id_entry) -if (present(dxpl_index)) then - dxpl_id = mpi_dxpl_id(dxpl_index) -else - call set_dxpl_id(dxpl_id) -end if - -offset(:) = 0 -if (nslice == 1) then !hypersplice - r = r + 1 - dims(r) = nslice - offset(r) = hyp_index - 1 -else if (nslice >= 1) then !hyperslab - dims(r) = nslice - offset(r) = hyp_index - 1 -else !dummy write - dims(:) = 0 -end if +call config_hyper_dims(rnk,nslice,hyp_idx,dims,offset) ! Check if dataset already exists call h5lexists_f(file_or_group_id, trim(dataset_name), dset_exists, hdferr) - if (.not. dset_exists) then write(*,*) "(writeHDF5_hypersplice): dataset ",trim(dataset_name)," has not been initiated for hypersplicing." stop "Stopping." end if -call h5sselect_hyperslab_f(dspace_id, H5S_SELECT_SET_F, offset(1:r), dims(1:r), hdferr) -call h5screate_simple_f(r, dims(1:r), memspace_id, hdferr) -call h5dataset_write(variable_to_write, dims, dset_type, dset_id, dxpl_id, memspace_id, dspace_id) +call h5sselect_hyperslab_f(hyp%dspace, H5S_SELECT_SET_F, offset(1:rnk), dims(1:rnk), hdferr) +call h5screate_simple_f(rnk, dims(1:rnk), memspace_id, hdferr) + +select rank(variable_to_write) +rank(0) +#include "modhdf5_write_hyper.inc" +rank(1) +#include "modhdf5_write_hyper.inc" +rank(2) +#include "modhdf5_write_hyper.inc" +rank(3) +#include "modhdf5_write_hyper.inc" +rank(4) +#include "modhdf5_write_hyper.inc" +rank(5) +#include "modhdf5_write_hyper.inc" +rank(6) +#include "modhdf5_write_hyper.inc" +rank(7) +#include "modhdf5_write_hyper.inc" +end select if (custom_type) call h5tclose_f(dset_type, hdferr) !close custom dset_type -if (.not. present(dxpl_index)) call h5pclose_f(dxpl_id, hdferr) call h5sclose_f(memspace_id, hdferr) end subroutine writeHDF5_hypersplice -!-----------------------------! -! Reading Subroutines ! -!-----------------------------! - - -subroutine readHDF5(id_entry,dataset_name,variable_to_read,read_group,dxpl_index,fdtype_name) +subroutine read_attributeHDF5(id_idx,dataset_name,attr_name,attr,read_group,dset_id) +! Read the attribute from h5 dataset -integer, intent(in) :: id_entry -character(len=*), intent(in) :: dataset_name -class(*), intent(inout), dimension(..) :: variable_to_read -logical, intent(in) :: read_group -integer, optional, intent(in) :: dxpl_index -character(len=*), optional, intent(inout) :: fdtype_name +integer, intent(in) :: id_idx !index for file/group +character(len=*), intent(in) :: dataset_name,attr_name !h5 dataset and attribute name +class(*), intent(inout), dimension(..) :: attr !attribute of interest +logical, intent(in) :: read_group !.true. to read from group, otherwise from file +integer(HID_T), optional, intent(in) :: dset_id !corresponding h5 dataset id -integer(HID_T) :: dset_id, file_or_group_id, dset_type, dxpl_id -integer :: hdferr, i, r, id_type +integer(HID_T) :: dset_id_, file_or_group_id, type_id, attr_id integer(HSIZE_T) :: dims(7) -logical custom_type - -r = rank(variable_to_read) - -if ((r < 0) .or. (r > 7)) then - write(*,*) "(readHDF5): input variable to read has invalid rank. Only a rank between 0 and 7 can be used." - stop "Stopping." -end if +integer :: hdferr, rnk +logical :: custom_type,using_mpi -call get_array_dset_type(variable_to_read,dset_type,custom_type) -call get_var_dims(variable_to_read,r,dims) -call get_file_or_group_id(read_group,id_entry,file_or_group_id) +rnk = rank(attr) +call get_var_dims(attr,rnk,dims) +call get_file_or_group_id(read_group,id_idx,file_or_group_id,using_mpi) +call get_array_dset_type(attr,type_id,custom_type) -if (present(dxpl_index)) then - dxpl_id = mpi_dxpl_id(dxpl_index) -else - call set_dxpl_id(dxpl_id) +if(.not. present(dset_id)) then + call h5dopen_f(file_or_group_id, trim(dataset_name), dset_id_, hdferr) +else + dset_id_ = dset_id end if +! Get read attribute +call h5aopen_f(dset_id_, trim(attr_name), attr_id, hdferr) -call h5dopen_f(file_or_group_id, trim(dataset_name), dset_id, hdferr) -call h5dataset_read(variable_to_read, dims, dset_type, dset_id, dxpl_id) - -if (present(fdtype_name)) call read_attributeHDF5(id_entry,dataset_name,"fortran_type",fdtype_name,read_group,dset_id) +select rank(attr) +rank(0) +#include "modhdf5_attribute_read.inc" +rank(1) +#include "modhdf5_attribute_read.inc" +rank(2) +#include "modhdf5_attribute_read.inc" +rank(3) +#include "modhdf5_attribute_read.inc" +rank(4) +#include "modhdf5_attribute_read.inc" +rank(5) +#include "modhdf5_attribute_read.inc" +rank(6) +#include "modhdf5_attribute_read.inc" +rank(7) +#include "modhdf5_attribute_read.inc" +end select -if (custom_type) call h5tclose_f(dset_type, hdferr) !close custom dset_type -if (.not. present(dxpl_index)) call h5pclose_f(dxpl_id, hdferr) -call h5dclose_f(dset_id, hdferr) +call h5aclose_f(attr_id, hdferr) +if (custom_type) call h5tclose_f(type_id, hdferr) !close custom dset_type +if(.not. present(dset_id)) call h5dclose_f(dset_id_, hdferr) -end subroutine readHDF5 +end subroutine read_attributeHDF5 -subroutine readHDF5_hypersplice(id_entry,dataset_name,variable_to_read,read_group,hyp_id_entry,hyp_index,nslice,dxpl_index) +subroutine readHDF5(id_idx,dataset_name,variable_to_read,read_group,fdtype_name) +! Read the variable_to_read from h5 dataset -integer, intent(in) :: id_entry -character(len=*), intent(in) :: dataset_name -class(*), intent(inout), dimension(..) :: variable_to_read -logical, intent(in) :: read_group -integer, intent(in) :: hyp_id_entry -integer, intent(in) :: hyp_index -integer, intent(in) :: nslice !>= 1 to write slice/slab, else do not write data -integer, optional, intent(in) :: dxpl_index +integer, intent(in) :: id_idx !index for file/group +character(len=*), intent(in) :: dataset_name !h5 dataset name +class(*), intent(inout), dimension(..) :: variable_to_read !variable of interest +logical, intent(in) :: read_group !.true. to read from group, otherwise from file +character(len=*), optional, intent(inout) :: fdtype_name !variable datatype name within dataset attribute -integer(HID_T) :: dset_id, file_or_group_id, dset_type, memspace_id, filespace_id, dxpl_id -integer :: hdferr, i, r, id_type -integer(HSIZE_T) :: dims(7), chunk_dims(7), offset(7) -logical custom_type +integer(HID_T) :: file_or_group_id +integer :: hdferr, rnk +logical custom_type,using_mpi +type(h5_ids_t) :: h5_ids -r = rank(variable_to_read) +rnk = rank(variable_to_read) -if ((r < 0) .and. (r > 6)) then - write(*,*) "(readHDF5_hypersplice): input variable to read has invalid rank. Only a rank between 0 and 6 can be used." +if ((rnk < 0) .or. (rnk > 7)) then + write(*,*) "(readHDF5): input variable to read has invalid rank. Only a rank between 0 and 7 can be used." stop "Stopping." end if -call get_array_dset_type(variable_to_read,dset_type,custom_type) -call get_var_dims(variable_to_read,r,dims) -call get_file_or_group_id(read_group,id_entry,file_or_group_id) +call get_file_or_group_id(read_group,id_idx,file_or_group_id,using_mpi) +call get_var_dims(variable_to_read,rnk,h5_ids%dims) +call get_array_dset_type(variable_to_read,h5_ids%dset_type,custom_type) +call set_dxpl_id(h5_ids%dxpl,using_mpi,mpi_independent=.true.) -if (present(dxpl_index)) then - dxpl_id = mpi_dxpl_id(dxpl_index) -else - call set_dxpl_id(dxpl_id) -end if +call h5dopen_f(file_or_group_id, trim(dataset_name), h5_ids%dset, hdferr) -call h5dopen_f(file_or_group_id, trim(dataset_name), dset_id, hdferr) - -offset(:) = 0 -if (nslice == 1) then !hypersplice - r = r + 1 - dims(r) = nslice - offset(r) = hyp_index - 1 -else if (nslice >= 1) then !hyperslab - dims(r) = nslice - offset(r) = hyp_index - 1 -else !dummy read - dims(:) = 0 -end if - -call h5dget_space_f(dset_id, filespace_id, hdferr) -call h5sselect_hyperslab_f(filespace_id, H5S_SELECT_SET_F, offset(1:r), dims(1:r), hdferr) - -if (nslice == 1) r = r - 1 !hypersplice -call h5screate_simple_f(r, dims(1:r), memspace_id, hdferr) -call h5dataset_read(variable_to_read, dims, dset_type, dset_id, dxpl_id, memspace_id, filespace_id) - -if (custom_type) call h5tclose_f(dset_type, hdferr) !close custom dset_type -if (.not. present(dxpl_index)) call h5pclose_f(dxpl_id, hdferr) -call h5sclose_f(memspace_id, hdferr) -call h5sclose_f(filespace_id, hdferr) -call h5dclose_f(dset_id, hdferr) - -end subroutine readHDF5_hypersplice - - -!----------------------------------! -! IO Interface Subroutines ! -!----------------------------------! - - -subroutine h5dataset_write(variable, dims, dset_type, dset_id, dxpl_id, memspace_id, filespace_id) -integer(HID_T), intent(in) :: dset_id, dset_type, dxpl_id -class(*), intent(in), dimension(..) :: variable !value to be written -integer(HSIZE_T), intent(in) :: dims(7) -integer(HID_T), intent(in), optional :: memspace_id, filespace_id - -integer :: hdferr, rnk - -select rank(variable) +select rank(variable_to_read) rank(0) -#include "modhdf5_write.inc" +#include "modhdf5_read.inc" rank(1) -#include "modhdf5_write.inc" +#include "modhdf5_read.inc" rank(2) -#include "modhdf5_write.inc" +#include "modhdf5_read.inc" rank(3) -#include "modhdf5_write.inc" +#include "modhdf5_read.inc" rank(4) -#include "modhdf5_write.inc" +#include "modhdf5_read.inc" rank(5) -#include "modhdf5_write.inc" +#include "modhdf5_read.inc" rank(6) -#include "modhdf5_write.inc" +#include "modhdf5_read.inc" rank(7) -#include "modhdf5_write.inc" +#include "modhdf5_read.inc" end select -end subroutine h5dataset_write +if (present(fdtype_name)) call read_attributeHDF5(id_idx,dataset_name,"fortran_type",fdtype_name,read_group,h5_ids%dset) + +if (custom_type) call h5tclose_f(h5_ids%dset_type, hdferr) !close custom dset_type +call h5pclose_f(h5_ids%dxpl, hdferr) +call h5dclose_f(h5_ids%dset, hdferr) + +end subroutine readHDF5 + +subroutine readHDF5_hypersplice(id_idx,dataset_name,variable_to_read,read_group,hyp_id_idx,hyp_idx,nslice) +! Read the hypersplice/hyperslab array from h5 dataset -subroutine h5dataset_read(variable, dims, dset_type, dset_id, dxpl_id, memspace_id, filespace_id) -integer(HID_T), intent(in) :: dset_id, dset_type, dxpl_id -class(*), intent(inout), dimension(..) :: variable !value to be written -integer(HSIZE_T), intent(in) :: dims(7) -integer(HID_T), intent(in), optional :: memspace_id, filespace_id +integer, intent(in) :: id_idx !index for file/group +character(len=*), intent(in) :: dataset_name !name of h5 dataset +class(*), intent(inout), dimension(..) :: variable_to_read !variable of interest +logical, intent(in) :: read_group !.true. to read from group, otherwise from file +integer, intent(in) :: hyp_id_idx, hyp_idx !hypersplice/slab index and hyper array index (for last rank not in slice) +integer, intent(in) :: nslice !number of hyper slices (> 1 to read slab, = 1 for slice, else do not read data) +integer(HID_T) :: file_or_group_id, dset_type, memspace_id, filespace_id integer :: hdferr, rnk +integer(HSIZE_T) :: dims(7), offset(7) +logical custom_type,using_mpi +type(h5_ids_t), pointer :: hyp -select rank(variable) +rnk = rank(variable_to_read) + +if ((rnk < 0) .and. (rnk > 6)) then + write(*,*) "(readHDF5_hypersplice): input variable to read has invalid rank. Only a rank between 0 and 6 can be used." + stop "Stopping." +end if + +call get_file_or_group_id(read_group,id_idx,file_or_group_id,using_mpi) +call get_file_or_group_hyp(read_group,id_idx,hyp_id_idx,hyp) +call get_var_dims(variable_to_read,rnk,dims) +call get_array_dset_type(variable_to_read,dset_type,custom_type) +call config_hyper_dims(rnk,nslice,hyp_idx,dims,offset) + +call h5sselect_hyperslab_f(hyp%dspace, H5S_SELECT_SET_F, offset(1:rnk), dims(1:rnk), hdferr) +if (nslice == 1) rnk = rnk - 1 !hypersplice +call h5screate_simple_f(rnk, dims(1:rnk), memspace_id, hdferr) + +select rank(variable_to_read) rank(0) -#include "modhdf5_read.inc" +#include "modhdf5_read_hyper.inc" rank(1) -#include "modhdf5_read.inc" +#include "modhdf5_read_hyper.inc" rank(2) -#include "modhdf5_read.inc" +#include "modhdf5_read_hyper.inc" rank(3) -#include "modhdf5_read.inc" +#include "modhdf5_read_hyper.inc" rank(4) -#include "modhdf5_read.inc" +#include "modhdf5_read_hyper.inc" rank(5) -#include "modhdf5_read.inc" +#include "modhdf5_read_hyper.inc" rank(6) -#include "modhdf5_read.inc" +#include "modhdf5_read_hyper.inc" rank(7) -#include "modhdf5_read.inc" +#include "modhdf5_read_hyper.inc" end select -end subroutine h5dataset_read +if (custom_type) call h5tclose_f(dset_type, hdferr) !close custom dset_type +call h5sclose_f(memspace_id, hdferr) + +end subroutine readHDF5_hypersplice end module diff --git a/src/modhdf5_attribute_read.inc b/src/modhdf5_attribute_read.inc index 0f8e3ec..f03e666 100644 --- a/src/modhdf5_attribute_read.inc +++ b/src/modhdf5_attribute_read.inc @@ -3,7 +3,6 @@ ! See the file COPYING for license details. ! can only be used in read_attributeHDF5() -rnk = rank(attr) select type(attr) type is (integer) call h5aread_f(attr_id, type_id, attr, dims(1:rnk), hdferr) diff --git a/src/modhdf5_attribute_write.inc b/src/modhdf5_attribute_write.inc index c88b0e3..3d84c16 100644 --- a/src/modhdf5_attribute_write.inc +++ b/src/modhdf5_attribute_write.inc @@ -3,7 +3,6 @@ ! See the file COPYING for license details. ! can only be used in write_attributeHDF5() -rnk = rank(attr) select type(attr) type is (integer) call h5awrite_f(attr_id, type_id, attr, dims(1:rnk), hdferr) diff --git a/src/modhdf5_read.inc b/src/modhdf5_read.inc index 86c1cc7..c31d863 100644 --- a/src/modhdf5_read.inc +++ b/src/modhdf5_read.inc @@ -2,31 +2,14 @@ ! This file is distributed under the terms of the GNU General Public License. ! See the file COPYING for license details. -! can only be used in h5dataset_read() -rnk = rank(variable) -select type(variable) +! can only be used in readHDF5() +select type(variable_to_read) type is (integer) - if (present(memspace_id) .and. present(filespace_id)) then - call h5dread_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, memspace_id, filespace_id, xfer_prp=dxpl_id) - else - call h5dread_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, xfer_prp=dxpl_id) - end if + call h5dread_f(h5_ids%dset, h5_ids%dset_type, variable_to_read, h5_ids%dims(1:rnk), hdferr, xfer_prp=h5_ids%dxpl) type is (real) - if (present(memspace_id) .and. present(filespace_id)) then - call h5dread_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, memspace_id, filespace_id, xfer_prp=dxpl_id) - else - call h5dread_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, xfer_prp=dxpl_id) - end if + call h5dread_f(h5_ids%dset, h5_ids%dset_type, variable_to_read, h5_ids%dims(1:rnk), hdferr, xfer_prp=h5_ids%dxpl) type is (real(8)) - if (present(memspace_id) .and. present(filespace_id)) then - call h5dread_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, memspace_id, filespace_id, xfer_prp=dxpl_id) - else - call h5dread_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, xfer_prp=dxpl_id) - end if + call h5dread_f(h5_ids%dset, h5_ids%dset_type, variable_to_read, h5_ids%dims(1:rnk), hdferr, xfer_prp=h5_ids%dxpl) type is (character(*)) - if (present(memspace_id) .and. present(filespace_id)) then - call h5dread_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, memspace_id, filespace_id, xfer_prp=dxpl_id) - else - call h5dread_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, xfer_prp=dxpl_id) - end if + call h5dread_f(h5_ids%dset, h5_ids%dset_type, variable_to_read, h5_ids%dims(1:rnk), hdferr, xfer_prp=h5_ids%dxpl) end select \ No newline at end of file diff --git a/src/modhdf5_read_hyper.inc b/src/modhdf5_read_hyper.inc new file mode 100644 index 0000000..55efc63 --- /dev/null +++ b/src/modhdf5_read_hyper.inc @@ -0,0 +1,15 @@ +! Copyright (C) 2025 A. D. N. James. +! This file is distributed under the terms of the GNU General Public License. +! See the file COPYING for license details. + +! can only be used in readHDF5_hypersplice() +select type(variable_to_read) +type is (integer) + call h5dread_f(hyp%dset, dset_type, variable_to_read, dims(1:rnk), hdferr, memspace_id, hyp%dspace, xfer_prp=hyp%dxpl) +type is (real) + call h5dread_f(hyp%dset, dset_type, variable_to_read, dims(1:rnk), hdferr, memspace_id, hyp%dspace, xfer_prp=hyp%dxpl) +type is (real(8)) + call h5dread_f(hyp%dset, dset_type, variable_to_read, dims(1:rnk), hdferr, memspace_id, hyp%dspace, xfer_prp=hyp%dxpl) +type is (character(*)) + call h5dread_f(hyp%dset, dset_type, variable_to_read, dims(1:rnk), hdferr, memspace_id, hyp%dspace, xfer_prp=hyp%dxpl) +end select \ No newline at end of file diff --git a/src/modhdf5_write.inc b/src/modhdf5_write.inc index 5348e99..7d6ff74 100644 --- a/src/modhdf5_write.inc +++ b/src/modhdf5_write.inc @@ -2,31 +2,14 @@ ! This file is distributed under the terms of the GNU General Public License. ! See the file COPYING for license details. -! can only be used in h5dataset_write() -rnk = rank(variable) -select type(variable) +! can only be used in writeHDF5() +select type(variable_to_write) type is (integer) - if (present(memspace_id) .and. present(filespace_id)) then - call h5dwrite_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, memspace_id, filespace_id, xfer_prp=dxpl_id) - else - call h5dwrite_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, xfer_prp=dxpl_id) - end if + call h5dwrite_f(h5_ids%dset, h5_ids%dset_type, variable_to_write, h5_ids%dims(1:rnk), hdferr, xfer_prp=h5_ids%dxpl) type is (real) - if (present(memspace_id) .and. present(filespace_id)) then - call h5dwrite_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, memspace_id, filespace_id, xfer_prp=dxpl_id) - else - call h5dwrite_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, xfer_prp=dxpl_id) - end if + call h5dwrite_f(h5_ids%dset, h5_ids%dset_type, variable_to_write, h5_ids%dims(1:rnk), hdferr, xfer_prp=h5_ids%dxpl) type is (real(8)) - if (present(memspace_id) .and. present(filespace_id)) then - call h5dwrite_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, memspace_id, filespace_id, xfer_prp=dxpl_id) - else - call h5dwrite_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, xfer_prp=dxpl_id) - end if + call h5dwrite_f(h5_ids%dset, h5_ids%dset_type, variable_to_write, h5_ids%dims(1:rnk), hdferr, xfer_prp=h5_ids%dxpl) type is (character(*)) - if (present(memspace_id) .and. present(filespace_id)) then - call h5dwrite_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, memspace_id, filespace_id, xfer_prp=dxpl_id) - else - call h5dwrite_f(dset_id, dset_type, variable, dims(1:rnk), hdferr, xfer_prp=dxpl_id) - end if + call h5dwrite_f(h5_ids%dset, h5_ids%dset_type, variable_to_write, h5_ids%dims(1:rnk), hdferr, xfer_prp=h5_ids%dxpl) end select \ No newline at end of file diff --git a/src/modhdf5_write_hyper.inc b/src/modhdf5_write_hyper.inc new file mode 100644 index 0000000..c786380 --- /dev/null +++ b/src/modhdf5_write_hyper.inc @@ -0,0 +1,15 @@ +! Copyright (C) 2025 A. D. N. James. +! This file is distributed under the terms of the GNU General Public License. +! See the file COPYING for license details. + +! can only be used in writeHDF5_hypersplice() +select type(variable_to_write) +type is (integer) + call h5dwrite_f(hyp%dset, dset_type, variable_to_write, dims(1:rnk), hdferr, memspace_id, hyp%dspace, xfer_prp=hyp%dxpl) +type is (real) + call h5dwrite_f(hyp%dset, dset_type, variable_to_write, dims(1:rnk), hdferr, memspace_id, hyp%dspace, xfer_prp=hyp%dxpl) +type is (real(8)) + call h5dwrite_f(hyp%dset, dset_type, variable_to_write, dims(1:rnk), hdferr, memspace_id, hyp%dspace, xfer_prp=hyp%dxpl) +type is (character(*)) + call h5dwrite_f(hyp%dset, dset_type, variable_to_write, dims(1:rnk), hdferr, memspace_id, hyp%dspace, xfer_prp=hyp%dxpl) +end select \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0b52d60..c80b048 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -19,6 +19,7 @@ message(STATUS "HDF5 Fortran libraries: ${HDF5_Fortran_LIBRARIES}") # CMake options # Debug flag option - enabled by default option(USE_DEBUG_FLAGS "Enable user-settable flags" OFF) +option(MPI_VERSION "Compile mpi test runner" OFF) # source files set(SOURCE_FILES @@ -31,8 +32,29 @@ set(SOURCE_FILES # Create the executable target first (common to both compilers) add_executable(${Executable} ${SOURCE_FILES}) -# Handle Intel Fortran -if(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel") +# Handle Intel Fortran (ifx) +if(CMAKE_Fortran_COMPILER_ID STREQUAL "IntelLLVM") + # Check minimum version + if(CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 2022.0) + message(FATAL_ERROR "Intel Fortran LLVM (ifx) version 2022.0 or later is required.") + endif() + + # Set compilation flags + if(USE_DEBUG_FLAGS) + message(STATUS "IntelLLVM Fortran debug flags are enabled.") + set(COMPILER_FLAGS "-fpp -O3 -fopenmp -g -check all -traceback -Wall -fstack-protector -O0") + else() + set(COMPILER_FLAGS "-fpp -O3 -fopenmp") + endif() + + # Apply flags to target + set_property(TARGET ${Executable} PROPERTY COMPILE_OPTIONS ${COMPILER_FLAGS}) + + # Link OpenMP library explicitly + set(OPENMP_LIB "-liomp5") + +# Handle older Intel Fortran (ifort) +elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel") # Check minimum version if(CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 18.0) message(FATAL_ERROR "Intel Fortran Compiler version 18.0 or later is required.") @@ -41,9 +63,9 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel") # Set compilation flags if(USE_DEBUG_FLAGS) message(STATUS "Intel Fortran debug flags are enabled.") - set(COMPILER_FLAGS "-qopenmp -cpp -g -assume realloc_lhs -check all -traceback -warn all -fstack-protector -assume protect_parens -O0") + set(COMPILER_FLAGS "-qopenmp -fpp -g -assume realloc_lhs -check all -traceback -warn all -fstack-protector -assume protect_parens -O0") else() - set(COMPILER_FLAGS "-qopenmp -O3 -cpp") + set(COMPILER_FLAGS "-qopenmp -O3 -fpp") endif() # Apply flags to target @@ -64,7 +86,7 @@ elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") message(STATUS "gfortran debug flags are enabled.") set(COMPILER_FLAGS -fopenmp -g -fbounds-check -fbacktrace -O0 -cpp) else() - set(COMPILER_FLAGS -fopenmp -O3 -cpp) + set(COMPILER_FLAGS -fopenmp -O3 -cpp -ffree-line-length-none) endif() # Apply flags to target @@ -87,4 +109,18 @@ else() target_link_libraries(${Executable} PRIVATE ${OPENMP_LIB} ${HDF5_Fortran_LIBRARIES}) endif() - +if(MPI_VERSION) + set(Executable_mpi "h5testrunner_mpi") + add_executable(${Executable_mpi} ${SOURCE_FILES}) + set(COMPILER_FLAGS ${COMPILER_FLAGS} -DMPI_modhdf5) + set_property(TARGET ${Executable_mpi} PROPERTY COMPILE_OPTIONS ${COMPILER_FLAGS}) + # Link HDF5 + if(TARGET HDF5::HDF5_Fortran) + target_link_libraries(${Executable_mpi} PRIVATE ${OPENMP_LIB} HDF5::HDF5_Fortran) + elseif(TARGET HDF5::Fortran) + target_link_libraries(${Executable_mpi} PRIVATE ${OPENMP_LIB} HDF5::Fortran) + else() + target_include_directories(${Executable_mpi} PRIVATE ${HDF5_INCLUDE_DIRS}) + target_link_libraries(${Executable_mpi} PRIVATE ${OPENMP_LIB} ${HDF5_Fortran_LIBRARIES}) + endif() +endif() diff --git a/test/h5testrunner.f90 b/test/h5testrunner.f90 index 035198d..b7af217 100644 --- a/test/h5testrunner.f90 +++ b/test/h5testrunner.f90 @@ -10,45 +10,111 @@ program h5testrunner ! Call the natural fruit unit testing framework use naturalfruit use modhdf5 +#ifdef MPI_modhdf5 +use mpi +use testio, only: mpi_initiate, mpi_terminate, lp_mpi, & + test_mpi, test_mpi_rank0, test_mpi_hypersplice +#else use testio, only: test_integers, test_reals, test_doubles, test_complex_doubles, test_characters, & - test_hypersplice, test_hyperslab + test_hypersplice, test_hypersplice_omp, test_hyperslab +#endif implicit none integer exitCode +exitCode = 0 -! Initialise the testsuite enviornment -call testsuite_initialize() - -write(*,*) -write(*,*) "Start of testing HDF5_Fortran_API capabilities:" - -write(*,*) "test Integer type I/O:" -call test_integers() -write(*,*) -write(*,*) "test Real type I/O:" -call test_reals() -write(*,*) -write(*,*) "test Double type I/O:" -call test_doubles() -write(*,*) -write(*,*) "test Complex Double type I/O:" -call test_complex_doubles() -write(*,*) -write(*,*) "test Characters type I/O:" -call test_characters() -write(*,*) -write(*,*) "test hypersplice I/O:" -call test_hypersplice() -write(*,*) -write(*,*) "test hyperslab I/O:" -call test_hyperslab() -write(*,*) - -write(*,*) "End tests" -! Summarise tests environment -call testsuite_summary() - -! Wrap-up the testsuite environment -call testsuite_finalize(exitCode) +#ifdef MPI_modhdf5 + call mpi_initiate() + ! Initialise the testsuite enviornment on rank 0 + if (lp_mpi==0) then + call testsuite_initialize() + write(*,*) + write(*,*) "Start of testing HDF5_Fortran_API capabilities:" + write(*,*) "test MPI HDF5:" + write(*,*) "test MPI I/O on rank 0:" + end if + + call test_mpi_rank0() + + if (lp_mpi==0) then + write(*,*) + write(*,*) "test MPI I/O:" + end if + + call test_mpi() + + if (lp_mpi==0) then + write(*,*) + write(*,*) "test MPI hypersplice I/O:" + end if + + call test_mpi_hypersplice() + + if (lp_mpi==0) then + write(*,*) + write(*,*) "End tests" + end if + + if (lp_mpi==0) then + ! Summarise tests environment + call testsuite_summary() + ! Wrap-up the testsuite environment + call testsuite_finalize(exitCode) + end if + + call mpi_terminate() +#else + ! Initialise the testsuite enviornment + call testsuite_initialize() + + write(*,*) + write(*,*) "Start of testing HDF5_Fortran_API capabilities:" + write(*,*) "test Integer type I/O:" + + call test_integers() + + write(*,*) + write(*,*) "test Real type I/O:" + + call test_reals() + + write(*,*) + write(*,*) "test Double type I/O:" + + call test_doubles() + + write(*,*) + write(*,*) "test Complex Double type I/O:" + + call test_complex_doubles() + + write(*,*) + write(*,*) "test Characters type I/O:" + + call test_characters() + + write(*,*) + write(*,*) "test hypersplice I/O:" + + call test_hypersplice() + + write(*,*) + write(*,*) "test hypersplice OMP I/O:" + + call test_hypersplice_omp() + + write(*,*) + write(*,*) "test hyperslab I/O:" + + call test_hyperslab() + + write(*,*) + write(*,*) "End tests" + + ! Summarise tests environment + call testsuite_summary() + ! Wrap-up the testsuite environment + call testsuite_finalize(exitCode) +#endif ! Exit the program with an exit code call exit(exitCode) diff --git a/test/testio.f90 b/test/testio.f90 index 79027d0..69ad921 100644 --- a/test/testio.f90 +++ b/test/testio.f90 @@ -14,6 +14,9 @@ module testio real, parameter :: tol_sp = 1e-8 real(dp), parameter :: tol = 1e-8 +!mpi variables if enabled at compilation +integer :: mpicom, np_mpi, lp_mpi + contains subroutine test_integers @@ -65,7 +68,7 @@ subroutine test_integers !write integer to group group_id = 1 dataset_within_group = .true. - call create_hdf5_group(group_id,'Test_Group_Integer') + call create_hdf5_group(File_id,group_id,'Test_Group_Integer') call writeHDF5(group_id,'integer_2d',test_int2d,dataset_within_group) call close_hdf5_group(group_id) @@ -100,7 +103,7 @@ subroutine test_integers test_int_read = 0.d0 group_id = 1 dataset_within_group = .true. - call open_hdf5_group(group_id,'Test_Group_Integer') + call open_hdf5_group(File_id,group_id,'Test_Group_Integer') call readHDF5(group_id,'integer_2d',test_int2d_read,dataset_within_group) call assert_equal(reshape(test_int2d,[size(test_int2d)]), reshape(test_int2d_read,[size(test_int2d_read)]), message='rank 2 integer from Test_Group_Integer differs') @@ -155,7 +158,7 @@ subroutine test_reals !write integer to group group_id = 1 dataset_within_group = .true. - call create_hdf5_group(group_id,'Test_Group_real') + call create_hdf5_group(File_id,group_id,'Test_Group_real') call writeHDF5(group_id,'real_2d',test_real2d,dataset_within_group) call close_hdf5_group(group_id) @@ -190,7 +193,7 @@ subroutine test_reals test_real2d_read = 0.d0 group_id = 1 dataset_within_group = .true. - call open_hdf5_group(group_id,'Test_Group_real') + call open_hdf5_group(File_id,group_id,'Test_Group_real') call readHDF5(group_id,'real_2d',test_real2d_read,dataset_within_group) call assert_equal(reshape(test_real2d,[size(test_real2d)]), reshape(test_real2d_read,[size(test_real2d_read)]), delta = tol_sp, message='rank 2 real from Test_Group_real differs') @@ -245,7 +248,7 @@ subroutine test_doubles !write integer to group group_id = 1 dataset_within_group = .true. - call create_hdf5_group(group_id,'Test_Group_Double') + call create_hdf5_group(File_id,group_id,'Test_Group_Double') call writeHDF5(group_id,'double_2d',test_double2d,dataset_within_group) call close_hdf5_group(group_id) @@ -280,7 +283,7 @@ subroutine test_doubles test_double2d_read = 0.d0 group_id = 1 dataset_within_group = .true. - call open_hdf5_group(group_id,'Test_Group_Double') + call open_hdf5_group(File_id,group_id,'Test_Group_Double') call readHDF5(group_id,'double_2d',test_double2d_read,dataset_within_group) call assert_equal(reshape(test_double2d,[size(test_double2d)]), reshape(test_double2d_read,[size(test_double2d_read)]), delta = tol, message='rank 2 double from Test_Group_Double differs') @@ -482,7 +485,7 @@ subroutine test_hypersplice ! open and write hyperspliced integers call open_hdf5_file(File_id,"test_hypersplice",writing) - call create_hdf5_group(group_id,'Test_Group_Integer') + call create_hdf5_group(File_id,group_id,'Test_Group_Integer') call init_hyperspliced_array(File_id,"hyper_splice_3d_test",test_int2d,not_within_group,hyper_id,dims_int3d(3)) call init_hyperspliced_array(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2,dims_int3d(3)) @@ -497,6 +500,9 @@ subroutine test_hypersplice call writeHDF5_hypersplice(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2,i,nslices) end do deallocate(rand) + call close_entire_hyperspliced_dataset(File_id,hyper_id,not_within_group) + call close_entire_hyperspliced_dataset(group_id,hyper_id2,within_group) + call close_hdf5_group(group_id) call close_hdf5_file(File_id) @@ -505,18 +511,22 @@ subroutine test_hypersplice ! open in read-only and read h5 file to check hyperspliced arrays call open_hdf5_file(File_id,"test_hypersplice",writing) + call open_hyperspliced_array(File_id,"hyper_splice_3d_test",test_int2d,not_within_group,hyper_id) do i=1,dims_int3d(3) call readHDF5_hypersplice(File_id,"hyper_splice_3d_test",test_int3d_read,not_within_group,hyper_id,i,nslices) end do call assert_equal(reshape(test_int3d,[size(test_int3d)]), reshape(test_int3d_read,[size(test_int3d_read)]), message='Hyperspliced rank 3 integer differs') + call close_entire_hyperspliced_dataset(File_id,hyper_id,not_within_group) test_int3d_read = 0 - call open_hdf5_group(group_id,'Test_Group_Integer') + call open_hdf5_group(File_id,group_id,'Test_Group_Integer') + call open_hyperspliced_array(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2) do i=1,dims_int3d(3) call readHDF5_hypersplice(group_id,"hyper_splice_3d_test",test_int3d_read,within_group,hyper_id2,i,nslices) end do call assert_equal(reshape(test_int3d,[size(test_int3d)]), reshape(test_int3d_read,[size(test_int3d_read)]), message='Hyperspliced rank 3 integer from Test_Group_Integer differs') + call close_entire_hyperspliced_dataset(group_id,hyper_id2,within_group) call close_hdf5_group(group_id) call close_hdf5_file(File_id) @@ -524,6 +534,91 @@ subroutine test_hypersplice end subroutine test_hypersplice + +subroutine test_hypersplice_omp + use omp_lib + + integer :: test_int2d(2,2) + integer :: test_int3d(2,2,32), test_int3d_read(2,2,32), test_int3d_read2(2,2,32) + + integer :: File_id, hyper_id, hyper_id2, group_id + integer :: i, nslices + integer :: dims_int3d(3) + integer :: maxthd + logical :: not_within_group, within_group, writing + real(8), allocatable :: rand(:) + + File_id = 1 + group_id = 1 + hyper_id = 1 + hyper_id2 = 2 + nslices = 1 + dims_int3d(:) = shape(test_int3d_read) + not_within_group = .false. + within_group = .true. + writing = .true. + + maxthd=max(omp_get_max_threads(),4) + + ! open and write hyperspliced integers + call open_hdf5_file(File_id,"test_hypersplice",writing) + call create_hdf5_group(File_id,group_id,'Test_Group_Integer') + + call init_hyperspliced_array(File_id,"hyper_splice_3d_test",test_int2d,not_within_group,hyper_id,dims_int3d(3)) + call init_hyperspliced_array(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2,dims_int3d(3)) + + + !$OMP PARALLEL DEFAULT(SHARED) & + !$OMP PRIVATE(rand,test_int2d) & + !$OMP NUM_THREADS(maxthd) + allocate(rand(size(test_int2d))) + !$OMP DO + do i=1,dims_int3d(3) + ! initiate with random integers + call random_number(rand) + test_int2d = int(reshape(rand,shape(test_int2d))*100) + test_int3d(:,:,i) = test_int2d + + !$OMP CRITICAL(test_hypersplice_omp_) + !write(*,'("Info(test_hypersplice_omp): writing ",I6," of ",I6," points")') i,dims_int3d(3) + call writeHDF5_hypersplice(File_id,"hyper_splice_3d_test",test_int2d,not_within_group,hyper_id,i,nslices) + call writeHDF5_hypersplice(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2,i,nslices) + !$OMP END CRITICAL(test_hypersplice_omp_) + end do + !$OMP END DO + deallocate(rand) + !$OMP END PARALLEL + call close_entire_hyperspliced_dataset(File_id,hyper_id,not_within_group) + call close_entire_hyperspliced_dataset(group_id,hyper_id2,within_group) + + call close_hdf5_group(group_id) + call close_hdf5_file(File_id) + + + writing = .false. + + ! open in read-only and read h5 file to check hyperspliced arrays + call open_hdf5_file(File_id,"test_hypersplice",writing) + call open_hdf5_group(File_id,group_id,'Test_Group_Integer') + call open_hyperspliced_array(File_id,"hyper_splice_3d_test",test_int2d,not_within_group,hyper_id) + call open_hyperspliced_array(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2) + + do i=1,dims_int3d(3) + call readHDF5_hypersplice(File_id,"hyper_splice_3d_test",test_int3d_read,not_within_group,hyper_id,i,nslices) + call readHDF5_hypersplice(group_id,"hyper_splice_3d_test",test_int3d_read,within_group,hyper_id2,i,nslices) + end do + + call close_entire_hyperspliced_dataset(File_id,hyper_id,not_within_group) + call close_entire_hyperspliced_dataset(group_id,hyper_id2,within_group) + call assert_equal(reshape(test_int3d,[size(test_int3d)]), reshape(test_int3d_read,[size(test_int3d_read)]), message='Hyperspliced rank 3 integer differs') + call assert_equal(reshape(test_int3d,[size(test_int3d)]), reshape(test_int3d_read,[size(test_int3d_read)]), message='Hyperspliced rank 3 integer from Test_Group_Integer differs') + + call close_hdf5_group(group_id) + call close_hdf5_file(File_id) + +end subroutine test_hypersplice_omp + + subroutine test_hyperslab integer :: test_int3d(2,2,10), test_int3d_read(2,2,10) @@ -556,6 +651,7 @@ subroutine test_hyperslab ! write two slabs call writeHDF5_hypersplice(File_id,"hyper_slab_3d_test",test_int3d(:,:,1:nslab1),not_within_group,hyper_id,1,nslab1) call writeHDF5_hypersplice(File_id,"hyper_slab_3d_test",test_int3d(:,:,nslab1:nslab2),not_within_group,hyper_id,nslab1,nslab2) + call close_entire_hyperspliced_dataset(File_id,hyper_id,not_within_group) call close_hdf5_file(File_id) writing = .false. @@ -563,8 +659,11 @@ subroutine test_hyperslab ! open in read-only and read h5 file to check hyperspliced arrays call open_hdf5_file(File_id,"test_hyperslab",writing) + call open_hyperspliced_array(File_id,"hyper_slab_3d_test",test_int3d_read,not_within_group,hyper_id) call readHDF5_hypersplice(File_id,"hyper_slab_3d_test",test_int3d_read(:,:,1:nslab1),not_within_group,hyper_id,1,nslab1) call readHDF5_hypersplice(File_id,"hyper_slab_3d_test",test_int3d_read(:,:,nslab1:nslab2),not_within_group,hyper_id,nslab1,nslab2) + call close_entire_hyperspliced_dataset(File_id,hyper_id,not_within_group) + call close_hdf5_file(File_id) call assert_equal(reshape(test_int3d,[size(test_int3d)]), reshape(test_int3d_read,[size(test_int3d_read)]), message='Hyperslab rank 3 integer differs') @@ -572,4 +671,299 @@ subroutine test_hyperslab end subroutine test_hyperslab +! MPI tests +#ifdef MPI_modhdf5 + +subroutine mpi_initiate + use mpi + integer ierr + + call mpi_init(ierr) + ! duplicate mpi_comm_world + call mpi_comm_dup(mpi_comm_world,mpicom,ierr) + ! determine the number of MPI processes + call mpi_comm_size(mpicom,np_mpi,ierr) + ! determine the local MPI process number + call mpi_comm_rank(mpicom,lp_mpi,ierr) +end subroutine mpi_initiate + + +subroutine mpi_terminate + use mpi + integer ierr + ! terminate MPI execution environment + call mpi_finalize(ierr) +end subroutine mpi_terminate + + +subroutine test_mpi + use mpi + integer :: test_int1d(2), test_int1d_read(2) + real :: test_real1d(2), test_real1d_read(2) + real(8) :: test_double1d(2), test_double1d_read(2) + + integer :: File_id, group_id, ierr + logical :: dataset_within_group, writing, using_mpi + real(8), allocatable :: rand(:) + + File_id = 1 + group_id = 1 + dataset_within_group = .false. + writing = .true. + + using_mpi = .true. + + ! initiate with random doubles + call random_seed() ! initialize RNG + allocate(rand(size(test_int1d))) + call random_number(rand) + test_int1d = int(rand(1:size(test_int1d))*100) + deallocate(rand) + call random_number(test_real1d) + call random_number(test_double1d) + if (np_mpi > 1) then + call mpi_allreduce(mpi_in_place,test_int1d,size(test_int1d),mpi_integer,mpi_sum,mpicom,ierr) + call mpi_allreduce(mpi_in_place,test_real1d,size(test_real1d),mpi_real,mpi_sum,mpicom,ierr) + call mpi_allreduce(mpi_in_place,test_double1d,size(test_double1d),mpi_double,mpi_sum,mpicom,ierr) + end if + + ! open and write + call open_hdf5_file(File_id,"test_mpi",writing,using_mpi,mpicom) + call create_hdf5_group(File_id,group_id,'Test_Group_Double') + + call writeHDF5(File_id,'integer_1d',test_int1d,dataset_within_group) + call writeHDF5(File_id,'real_1d',test_real1d,dataset_within_group) + call writeHDF5(File_id,'double_1d',test_double1d,dataset_within_group) + !write to group + dataset_within_group = .true. + call writeHDF5(group_id,'double_1d',test_double1d,dataset_within_group) + + call close_hdf5_group(group_id) + call close_hdf5_file(File_id) + call mpi_barrier(mpicom,ierr) + + + dataset_within_group = .false. + writing = .false. + + ! open in read-only and read h5 file + call open_hdf5_file(File_id,"test_mpi",writing,using_mpi,mpicom) + + call readHDF5(File_id,'integer_1d',test_int1d_read,dataset_within_group) + call readHDF5(File_id,'real_1d',test_real1d_read,dataset_within_group) + call readHDF5(File_id,'double_1d',test_double1d_read,dataset_within_group) + + call mpi_barrier(mpicom,ierr) + + ! check if equivalent + if (lp_mpi==0) then + call assert_equal(test_int1d, test_int1d_read, message='rank 1 integer differs') + call assert_equal(test_real1d, test_real1d_read, delta = tol_sp, message='rank 1 real differs') + call assert_equal(test_double1d, test_double1d_read, delta = tol, message='rank 1 double differs') + end if + + !test_double1d_read = 0.d0 + group_id = 1 + dataset_within_group = .true. + call open_hdf5_group(File_id,group_id,'Test_Group_Double') + call readHDF5(group_id,'double_1d',test_double1d_read,dataset_within_group) + + if (lp_mpi==0) call assert_equal(test_real1d, test_real1d_read, delta = tol_sp, message='rank 1 double from Test_Group_Double differs') + + call close_hdf5_group(group_id) + call close_hdf5_file(File_id) + + call mpi_barrier(mpicom,ierr) + +end subroutine test_mpi + + +subroutine test_mpi_rank0 + use mpi + integer :: test_int1d(2), test_int1d_read(2) + real :: test_real1d(2), test_real1d_read(2) + real(8) :: test_double1d(2), test_double1d_read(2), test_double1d_read2(2) + + integer :: File_id, group_id, ierr + logical :: dataset_within_group, writing, using_mpi + real(8), allocatable :: rand(:) + + File_id = 1 + group_id = 1 + dataset_within_group = .false. + writing = .true. + using_mpi = .true. + + ! initiate with random doubles + call random_seed() ! initialize RNG + allocate(rand(size(test_int1d))) + call random_number(rand) + test_int1d = int(rand(1:size(test_int1d))*100) + deallocate(rand) + call random_number(test_real1d) + call random_number(test_double1d) + if (np_mpi > 1) then + call mpi_allreduce(mpi_in_place,test_int1d,size(test_int1d),mpi_integer,mpi_sum,mpicom,ierr) + call mpi_allreduce(mpi_in_place,test_real1d,size(test_real1d),mpi_real,mpi_sum,mpicom,ierr) + call mpi_allreduce(mpi_in_place,test_double1d,size(test_double1d),mpi_double,mpi_sum,mpicom,ierr) + end if + + if (lp_mpi == 0) then + ! open and write + call open_hdf5_file(File_id,"test_mpi_rank0",writing) + call create_hdf5_group(File_id,group_id,'Test_Group_Double') + + call writeHDF5(File_id,'integer_1d',test_int1d,dataset_within_group) + call writeHDF5(File_id,'real_1d',test_real1d,dataset_within_group) + call writeHDF5(File_id,'double_1d',test_double1d,dataset_within_group) + + !write integer to group + dataset_within_group = .true. + call writeHDF5(group_id,'double_1d',test_double1d,dataset_within_group) + + call close_hdf5_group(group_id) + call close_hdf5_file(File_id) + end if + + call mpi_barrier(mpicom,ierr) + + dataset_within_group = .false. + writing = .false. + + test_int1d_read = 0 + test_real1d_read = 0.0 + test_double1d_read = 0.d0 + test_double1d_read2 = 0.d0 + if (lp_mpi == 0) then + ! open in read-only and read h5 file + call open_hdf5_file(File_id,"test_mpi_rank0",writing) + call open_hdf5_group(File_id,group_id,'Test_Group_Double') + + call readHDF5(File_id,'real_1d',test_real1d_read,dataset_within_group) + call readHDF5(File_id,'integer_1d',test_int1d_read,dataset_within_group) + call readHDF5(File_id,'double_1d',test_double1d_read,dataset_within_group) + + dataset_within_group = .true. + call readHDF5(group_id,'double_1d',test_double1d_read2,dataset_within_group) + + call close_hdf5_group(group_id) + call close_hdf5_file(File_id) + end if + + if (np_mpi > 1) then + call mpi_allreduce(mpi_in_place,test_int1d_read,size(test_int1d_read),mpi_integer,mpi_sum,mpicom,ierr) + call mpi_allreduce(mpi_in_place,test_real1d_read,size(test_real1d_read),mpi_real,mpi_sum,mpicom,ierr) + call mpi_allreduce(mpi_in_place,test_double1d_read,size(test_double1d_read),mpi_double,mpi_sum,mpicom,ierr) + call mpi_allreduce(mpi_in_place,test_double1d_read2,size(test_double1d_read2),mpi_double,mpi_sum,mpicom,ierr) + end if + + ! check if equivalent + if (lp_mpi==0) then + call assert_equal(test_int1d, test_int1d_read, message='rank 1 integer differs') + call assert_equal(test_real1d, test_real1d_read, delta = tol_sp, message='rank 1 real differs') + call assert_equal(test_double1d, test_double1d_read, delta = tol, message='rank 1 double differs') + call assert_equal(test_double1d, test_double1d_read2, delta = tol, message='rank 1 double from Test_Group_Double differs') + end if + + call mpi_barrier(mpicom,ierr) + +end subroutine test_mpi_rank0 + +subroutine test_mpi_hypersplice + use mpi + integer :: test_int2d(2,2) + integer :: test_int3d(2,2,10), test_int3d_read(2,2,10) + + integer :: File_id, hyper_id, hyper_id2, group_id + integer :: i, nslices, ierr + integer :: dims_int3d(3) + logical :: not_within_group, within_group, writing, using_mpi + real(8), allocatable :: rand(:) + + File_id = 1 + group_id = 1 + hyper_id = 1 + hyper_id2 = 2 + nslices = 1 + dims_int3d(:) = shape(test_int3d_read) + not_within_group = .false. + within_group = .true. + writing = .true. + + using_mpi = .true. + + ! open and write hyperspliced integers + call open_hdf5_file(File_id,"test_hypersplice_mpi",writing,using_mpi,mpicom) + call create_hdf5_group(File_id,group_id,'Test_Group_Integer') + + call init_hyperspliced_array(File_id,"hyper_splice_3d_test",test_int2d,not_within_group,hyper_id,dims_int3d(3)) + call init_hyperspliced_array(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2,dims_int3d(3)) + + allocate(rand(size(test_int3d)),source=0.d0) + ! initiate with random integers + if (lp_mpi == 0) call random_number(rand) + call mpi_barrier(mpicom,ierr) + !synchronise rand + if (np_mpi > 1) call mpi_allreduce(mpi_in_place,rand,size(rand),mpi_double,mpi_sum,mpicom,ierr) + test_int3d = int(reshape(rand,shape(test_int3d))*100) + deallocate(rand) + + do i=1,dims_int3d(3) + if (mod(i-1,np_mpi).ne.lp_mpi) then + call writeHDF5_hypersplice(File_id,"hyper_splice_3d_test",test_int2d,not_within_group,hyper_id,i,0) + call writeHDF5_hypersplice(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2,i,0) + else + test_int2d = test_int3d(:,:,i) + call writeHDF5_hypersplice(File_id,"hyper_splice_3d_test",test_int2d,not_within_group,hyper_id,i,nslices) + call writeHDF5_hypersplice(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2,i,nslices) + end if + end do + call close_entire_hyperspliced_dataset(File_id,hyper_id,not_within_group) + call close_entire_hyperspliced_dataset(group_id,hyper_id2,within_group) + + call close_hdf5_group(group_id) + call close_hdf5_file(File_id) + + call mpi_barrier(mpicom,ierr) + + + writing = .false. + + ! open in read-only and read h5 file to check hyperspliced arrays + call open_hdf5_file(File_id,"test_hypersplice_mpi",writing,using_mpi,mpicom) + call open_hdf5_group(File_id,group_id,'Test_Group_Integer') + call open_hyperspliced_array(File_id,"hyper_splice_3d_test",test_int2d,not_within_group,hyper_id) + call open_hyperspliced_array(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2) + + do i=1,dims_int3d(3) + if (mod(i-1,np_mpi).ne.lp_mpi) then + call readHDF5_hypersplice(File_id,"hyper_splice_3d_test",test_int3d_read,not_within_group,hyper_id,i,0) + else + call readHDF5_hypersplice(File_id,"hyper_splice_3d_test",test_int3d_read,not_within_group,hyper_id,i,nslices) + end if + end do + if (lp_mpi==0) call assert_equal(reshape(test_int3d,[size(test_int3d)]), reshape(test_int3d_read,[size(test_int3d_read)]), message='Hyperspliced rank 3 integer differs') + + test_int3d_read = 0 + do i=1,dims_int3d(3) + if (mod(i-1,np_mpi).ne.lp_mpi) then + call readHDF5_hypersplice(group_id,"hyper_splice_3d_test",test_int3d_read,not_within_group,hyper_id,i,0) + else + call readHDF5_hypersplice(group_id,"hyper_splice_3d_test",test_int3d_read,within_group,hyper_id2,i,nslices) + end if + end do + if (lp_mpi==0) call assert_equal(reshape(test_int3d,[size(test_int3d)]), reshape(test_int3d_read,[size(test_int3d_read)]), message='Hyperspliced rank 3 integer from Test_Group_Integer differs') + call mpi_barrier(mpicom,ierr) + + call close_entire_hyperspliced_dataset(File_id,hyper_id,not_within_group) + call close_entire_hyperspliced_dataset(group_id,hyper_id2,within_group) + call close_hdf5_group(group_id) + call close_hdf5_file(File_id) + + call mpi_barrier(mpicom,ierr) + +end subroutine test_mpi_hypersplice + +#endif + end module