From b37caff17e6469886ebbf41224ecf75253a651f3 Mon Sep 17 00:00:00 2001 From: Alyn James Date: Tue, 23 Dec 2025 16:59:28 +0000 Subject: [PATCH 1/7] Refactor module globals into data types. Also used these new data types as local variables within the subroutines. Made the group ids correspond to their file (index). --- src/modhdf5.f90 | 250 +++++++++++++++++++++++++++++------------------- test/testio.f90 | 20 ++-- 2 files changed, 162 insertions(+), 108 deletions(-) diff --git a/src/modhdf5.f90 b/src/modhdf5.f90 index 6aec629..52297a6 100644 --- a/src/modhdf5.f90 +++ b/src/modhdf5.f90 @@ -16,34 +16,61 @@ module modhdf5 character(256) :: h5filext='.h5' !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) !h5 compression level integer, private :: compression_level = 4 ! mpi hdf5 variables - mpi hdf5 is enabled when compiling with -DMPI_modhdf5 +!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 +!hyperslab/hypersplice dataset ids +integer(HID_T) :: dset +!hyperslab/hypersplice dataspace ids +integer(HID_T) :: dspace +!dimensions of the hyperspliced array +integer(HSIZE_T) :: dims(7) +!used for h5 collective mpi read/write +integer(HID_T) :: dxpl +!h5 compatible data type for variable to/from h5 file +integer(HID_T) :: dset_type +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) :: file_id !h5 mpicheck - set to false by default -logical, private :: mpi_h5 = .false. +logical :: mpi_h5 = .false. !h5 mpicomm copy -integer, private :: mpicomm_h5 = -1 +integer :: mpicomm_h5 = -1 !h5 collective read/write if mpi hdf5 is enabled -integer(HID_T), private :: mpi_dxpl_id(10) +integer(HID_T) :: mpi_dxpl_id(10) +!hyperslab/hypersplice ids +type(h5_ids_t) :: hyp(10) +end type h5_file_ids_t +type(h5_file_ids_t), private, target :: h5_file_ids(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_index +!h5 group ids +integer(HID_T) :: group_id +!h5 collective read/write if mpi hdf5 is enabled +integer(HID_T) :: mpi_dxpl_id(10) +!hyperslab/hypersplice ids +type(h5_ids_t) :: hyp(10) +end type h5_group_ids_t +type(h5_group_ids_t), private, target :: h5_group_ids(10) contains -subroutine open_hdf5_file(id_entry,filename,writing,mpi_file,mpicom) +subroutine open_hdf5_file(fid_entry,filename,writing,mpi_file,mpicom) character(len=*), intent(in) :: filename -integer, intent(in) :: id_entry +integer, intent(in) :: fid_entry logical, intent(in) :: writing logical, optional, intent(in) :: mpi_file integer, optional, intent(in) :: mpicom @@ -53,34 +80,34 @@ subroutine open_hdf5_file(id_entry,filename,writing,mpi_file,mpicom) ! 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_entry,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_file_ids(fid_entry)%file_id, 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_file_ids(fid_entry)%file_id, hdferr) end if end subroutine open_hdf5_file -subroutine mpi_open_hdf5_file(id_entry,filename,writing,mpicom) +subroutine mpi_open_hdf5_file(fid_entry,filename,writing,mpicom) ! use the mpi library within mpi HDF5 enabled #ifdef MPI_modhdf5 use mpi #endif character(len=*), intent(in) :: filename -integer, intent(in) :: id_entry +integer, intent(in) :: fid_entry logical, intent(in) :: writing integer, intent(in) :: mpicom 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 +115,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_file_ids(fid_entry)%mpi_h5 = .true. + h5_file_ids(fid_entry)%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_file_ids(fid_entry)%file_id, 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_file_ids(fid_entry)%file_id, 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,63 +139,65 @@ 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_entry,gid_entry,groupname,dxpl_index) character(len=*), intent(in) :: groupname -integer, intent(in) :: id_entry +integer, intent(in) :: fid_entry, gid_entry integer, optional, intent(in) :: dxpl_index integer hdferr -call h5gcreate_f(file_id(id_entry), trim(groupname), group_id(id_entry), hdferr) +call h5gcreate_f(h5_file_ids(fid_entry)%file_id, trim(groupname), h5_group_ids(gid_entry)%group_id, hdferr) +h5_group_ids(gid_entry)%file_index = fid_entry if (present(dxpl_index)) call mpi_set_dxpl_id(dxpl_index) end subroutine create_hdf5_group -subroutine open_hdf5_group(id_entry,groupname,dxpl_index) +subroutine open_hdf5_group(fid_entry,gid_entry,groupname,dxpl_index) character(len=*), intent(in) :: groupname -integer, intent(in) :: id_entry +integer, intent(in) :: fid_entry, gid_entry integer, optional, intent(in) :: dxpl_index integer hdferr -call h5gopen_f(file_id(id_entry), trim(groupname), group_id(id_entry), hdferr) +call h5gopen_f(h5_file_ids(fid_entry)%file_id, trim(groupname), h5_group_ids(gid_entry)%group_id, hdferr) +h5_group_ids(gid_entry)%file_index = fid_entry if (present(dxpl_index)) call mpi_set_dxpl_id(dxpl_index) end subroutine open_hdf5_group -subroutine close_hdf5_group(id_entry,dxpl_index) +subroutine close_hdf5_group(gid_entry,dxpl_index) -integer, intent(in) :: id_entry +integer, intent(in) :: gid_entry integer, optional, intent(in) :: dxpl_index integer hdferr -call h5gclose_f(group_id(id_entry), hdferr) +call h5gclose_f(h5_group_ids(gid_entry)%group_id, hdferr) if (present(dxpl_index)) call close_mpi_set_dxpl_id(dxpl_index) end subroutine close_hdf5_group -subroutine close_hdf5_file(id_entry,dxpl_index) +subroutine close_hdf5_file(fid_entry,dxpl_index) -integer, intent(in) :: id_entry +integer, intent(in) :: fid_entry integer, optional, intent(in) :: dxpl_index integer hdferr -if (present(dxpl_index)) call h5pclose_f(mpi_dxpl_id(dxpl_index), hdferr) -call h5fclose_f(file_id(id_entry), hdferr) +if (present(dxpl_index)) call h5pclose_f(h5_file_ids(fid_entry)%mpi_dxpl_id(dxpl_index), hdferr) +call h5fclose_f(h5_file_ids(fid_entry)%file_id, hdferr) call h5close_f(hdferr) -if (mpi_h5) then - mpi_h5 = .false. - mpicomm_h5 = -1 +if (h5_file_ids(fid_entry)%mpi_h5) then + h5_file_ids(fid_entry)%mpi_h5 = .false. + h5_file_ids(fid_entry)%mpicomm_h5 = -1 end if end subroutine close_hdf5_file @@ -242,13 +271,27 @@ subroutine get_file_or_group_id(to_group,id_entry,file_or_group_id) integer(HID_T), intent(out) :: file_or_group_id if (to_group) then - file_or_group_id = group_id(id_entry) + file_or_group_id = h5_group_ids(id_entry)%group_id else - file_or_group_id = file_id(id_entry) + file_or_group_id = h5_file_ids(id_entry)%file_id end if end subroutine get_file_or_group_id +subroutine get_file_or_group_hyp(to_group,id_entry,hyp_id_entry,hyp) + +logical, intent(in) :: to_group !logical for distinguishing whether to write to group or not +integer, intent(in) :: id_entry, hyp_id_entry +type(h5_ids_t), intent(inout), pointer :: hyp !hypersplice/hyperslab datatype + +if (to_group) then + hyp => h5_group_ids(id_entry)%hyp(hyp_id_entry) +else + hyp => h5_file_ids(id_entry)%hyp(hyp_id_entry) +end if + +end subroutine get_file_or_group_hyp + subroutine get_array_dset_type(variable,dset_type,custom_type) class(*), intent(in), dimension(..) :: variable @@ -315,6 +358,7 @@ subroutine init_hyperspliced_array(id_entry,dataset_name,variable,write_to_group integer :: hdferr, i, r integer(HSIZE_T) :: dims(7), chunk_dims(7) logical custom_type +type(h5_ids_t), pointer :: hyp r = rank(variable) @@ -323,53 +367,56 @@ subroutine init_hyperspliced_array(id_entry,dataset_name,variable,write_to_group 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_hyp(write_to_group,id_entry,hyp_id_entry,hyp) +call get_array_dset_type(variable,hyp%dset_type,custom_type) +call get_var_dims(variable,r,hyp%dims) call get_file_or_group_id(write_to_group,id_entry,file_or_group_id) -chunk_dims = hyp_dims(:,hyp_id_entry) +chunk_dims = hyp%dims r = r + 1 -hyp_dims(r,hyp_id_entry) = n_hyp_dim +hyp%dims(r) = n_hyp_dim chunk_dims(r) = 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(r, hyp%dims(1:r), hyp%dspace, hdferr) ! Create dataset creation property list -call h5pcreate_f(H5P_DATASET_CREATE_F, hyp_plist_id(hyp_id_entry), hdferr) +call h5pcreate_f(H5P_DATASET_CREATE_F, hyp%plist, hdferr) ! Set chunking -call h5pset_chunk_f(hyp_plist_id(hyp_id_entry), r, chunk_dims, hdferr) +call h5pset_chunk_f(hyp%plist, r, chunk_dims, hdferr) ! Enable compression (GZIP level 4) -call h5pset_deflate_f(hyp_plist_id(hyp_id_entry), compression_level, hdferr) +call h5pset_deflate_f(hyp%plist, compression_level, hdferr) ! Enable shuffle filter (best compression) -call h5pset_shuffle_f(hyp_plist_id(hyp_id_entry), hdferr) +call h5pset_shuffle_f(hyp%plist, hdferr) ! 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)) +call h5dcreate_f(file_or_group_id, trim(dataset_name), hyp%dset_type, & + hyp%dspace, hyp%dset, hdferr, hyp%plist) -if (present(fdtype_name)) call write_attributeHDF5(id_entry,dataset_name,"fortran_type",fdtype_name,write_to_group,hyp_dset_id(hyp_id_entry)) +if (present(fdtype_name)) call write_attributeHDF5(id_entry,dataset_name,"fortran_type",fdtype_name,write_to_group,hyp%dset) if (present(dxpl_index)) call mpi_set_dxpl_id(dxpl_index) end subroutine init_hyperspliced_array -subroutine close_entire_hyperspliced_dataset(hyp_id_entry,dxpl_index) +subroutine close_entire_hyperspliced_dataset(id_entry,hyp_id_entry,in_group,dxpl_index) -integer, intent(in) :: hyp_id_entry +integer, intent(in) :: id_entry, hyp_id_entry +logical, intent(in) :: in_group integer, optional, intent(in) :: dxpl_index integer :: hdferr +type(h5_ids_t), pointer :: hyp +call get_file_or_group_hyp(in_group,id_entry,hyp_id_entry,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 h5pclose_f(hyp%plist, hdferr) +call h5dclose_f(hyp%dset, hdferr) +call h5sclose_f(hyp%dspace, hdferr) end subroutine close_entire_hyperspliced_dataset @@ -493,10 +540,11 @@ subroutine writeHDF5(id_entry,dataset_name,variable_to_write,write_to_group,dxpl 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(HID_T) :: file_or_group_id +integer :: hdferr, i, r integer(HSIZE_T) :: dims(7) logical custom_type +type(h5_ids_t) :: h5_ids r = rank(variable_to_write) @@ -505,27 +553,27 @@ subroutine writeHDF5(id_entry,dataset_name,variable_to_write,write_to_group,dxpl stop "Stopping." end if -call get_array_dset_type(variable_to_write,dset_type,custom_type) +call get_array_dset_type(variable_to_write,h5_ids%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) + h5_ids%dxpl = mpi_dxpl_id(dxpl_index) else - call set_dxpl_id(dxpl_id) + call set_dxpl_id(h5_ids%dxpl) 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) +call h5screate_simple_f(r, dims(1:r), 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) +call h5dataset_write(variable_to_write, dims, h5_ids%dset_type, h5_ids%dset, h5_ids%dxpl) -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_entry,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 +if (.not. present(dxpl_index)) call h5pclose_f(h5_ids%dxpl, hdferr) +call h5dclose_f(h5_ids%dset, hdferr) +call h5sclose_f(h5_ids%dspace, hdferr) end subroutine writeHDF5 @@ -541,10 +589,11 @@ subroutine writeHDF5_hypersplice(id_entry,dataset_name,variable_to_write,write_t integer, intent(in) :: nslice !>= 1 to write slice/slab, else do not write data integer, optional, intent(in) :: dxpl_index -integer(HID_T) :: dspace_id, dset_id, file_or_group_id, dset_type, memspace_id, dxpl_id +integer(HID_T) :: file_or_group_id, dset_type, memspace_id, dxpl_id integer :: hdferr, i, r, id_type integer(HSIZE_T) :: dims(7), offset(7) logical :: dset_exists, custom_type +type(h5_ids_t), pointer :: hyp r = rank(variable_to_write) @@ -553,12 +602,11 @@ subroutine writeHDF5_hypersplice(id_entry,dataset_name,variable_to_write,write_t stop "Stopping." end if +call get_file_or_group_hyp(write_to_group,id_entry,hyp_id_entry,hyp) 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 @@ -585,9 +633,9 @@ subroutine writeHDF5_hypersplice(id_entry,dataset_name,variable_to_write,write_t stop "Stopping." end if -call h5sselect_hyperslab_f(dspace_id, H5S_SELECT_SET_F, offset(1:r), dims(1:r), hdferr) +call h5sselect_hyperslab_f(hyp%dspace, 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 h5dataset_write(variable_to_write, dims, dset_type, hyp%dset, dxpl_id, memspace_id, hyp%dspace) if (custom_type) call h5tclose_f(dset_type, hdferr) !close custom dset_type if (.not. present(dxpl_index)) call h5pclose_f(dxpl_id, hdferr) @@ -610,10 +658,11 @@ subroutine readHDF5(id_entry,dataset_name,variable_to_read,read_group,dxpl_index integer, optional, intent(in) :: dxpl_index character(len=*), optional, intent(inout) :: fdtype_name -integer(HID_T) :: dset_id, file_or_group_id, dset_type, dxpl_id -integer :: hdferr, i, r, id_type +integer(HID_T) :: file_or_group_id +integer :: hdferr, i, r integer(HSIZE_T) :: dims(7) logical custom_type +type(h5_ids_t) :: h5_ids r = rank(variable_to_read) @@ -622,24 +671,24 @@ subroutine readHDF5(id_entry,dataset_name,variable_to_read,read_group,dxpl_index stop "Stopping." end if -call get_array_dset_type(variable_to_read,dset_type,custom_type) +call get_array_dset_type(variable_to_read,h5_ids%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) if (present(dxpl_index)) then - dxpl_id = mpi_dxpl_id(dxpl_index) + h5_ids%dxpl = mpi_dxpl_id(dxpl_index) else - call set_dxpl_id(dxpl_id) + call set_dxpl_id(h5_ids%dxpl) end if -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) +call h5dopen_f(file_or_group_id, trim(dataset_name), h5_ids%dset, hdferr) +call h5dataset_read(variable_to_read, dims, h5_ids%dset_type, h5_ids%dset, h5_ids%dxpl) -if (present(fdtype_name)) call read_attributeHDF5(id_entry,dataset_name,"fortran_type",fdtype_name,read_group,dset_id) +if (present(fdtype_name)) call read_attributeHDF5(id_entry,dataset_name,"fortran_type",fdtype_name,read_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) +if (custom_type) call h5tclose_f(h5_ids%dset_type, hdferr) !close custom dset_type +if (.not. present(dxpl_index)) call h5pclose_f(h5_ids%dxpl, hdferr) +call h5dclose_f(h5_ids%dset, hdferr) end subroutine readHDF5 @@ -655,10 +704,11 @@ subroutine readHDF5_hypersplice(id_entry,dataset_name,variable_to_read,read_grou integer, intent(in) :: nslice !>= 1 to write slice/slab, else do not write data integer, optional, intent(in) :: dxpl_index -integer(HID_T) :: dset_id, file_or_group_id, dset_type, memspace_id, filespace_id, dxpl_id +integer(HID_T) :: 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 +type(h5_ids_t) :: hyp r = rank(variable_to_read) @@ -677,7 +727,7 @@ subroutine readHDF5_hypersplice(id_entry,dataset_name,variable_to_read,read_grou call set_dxpl_id(dxpl_id) end if -call h5dopen_f(file_or_group_id, trim(dataset_name), dset_id, hdferr) +call h5dopen_f(file_or_group_id, trim(dataset_name), hyp%dset, hdferr) offset(:) = 0 if (nslice == 1) then !hypersplice @@ -691,18 +741,18 @@ subroutine readHDF5_hypersplice(id_entry,dataset_name,variable_to_read,read_grou dims(:) = 0 end if -call h5dget_space_f(dset_id, filespace_id, hdferr) +call h5dget_space_f(hyp%dset, 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) +call h5dataset_read(variable_to_read, dims, dset_type, hyp%dset, 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) +call h5dclose_f(hyp%dset, hdferr) end subroutine readHDF5_hypersplice diff --git a/test/testio.f90 b/test/testio.f90 index 79027d0..502434c 100644 --- a/test/testio.f90 +++ b/test/testio.f90 @@ -65,7 +65,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 +100,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 +155,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 +190,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 +245,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 +280,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 +482,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 +497,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) @@ -512,7 +515,7 @@ subroutine test_hypersplice 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 - call open_hdf5_group(group_id,'Test_Group_Integer') + call open_hdf5_group(File_id,group_id,'Test_Group_Integer') 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 @@ -556,6 +559,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. From 4dc3dc18d0a9342d687bb78aef8ed0250a9dc4ff Mon Sep 17 00:00:00 2001 From: Alyn James Date: Tue, 23 Dec 2025 21:40:55 +0000 Subject: [PATCH 2/7] Rename variables to be more concise. removed global mpi_h5 use and more specific variables on the variables from the mpi module. Added more explicit free-form in gfotran compiler flags. --- src/modhdf5.f90 | 234 ++++++++++++++++++++++---------------------- test/CMakeLists.txt | 2 +- 2 files changed, 118 insertions(+), 118 deletions(-) diff --git a/src/modhdf5.f90 b/src/modhdf5.f90 index 52297a6..b75eece 100644 --- a/src/modhdf5.f90 +++ b/src/modhdf5.f90 @@ -40,7 +40,7 @@ module modhdf5 ! derived type containing the h5 file ids for each file in use type h5_file_ids_t !HDF5 file handle -integer(HID_T) :: file_id +integer(HID_T) :: fid !h5 mpicheck - set to false by default logical :: mpi_h5 = .false. !h5 mpicomm copy @@ -50,27 +50,27 @@ module modhdf5 !hyperslab/hypersplice ids type(h5_ids_t) :: hyp(10) end type h5_file_ids_t -type(h5_file_ids_t), private, target :: h5_file_ids(10) +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_index -!h5 group ids -integer(HID_T) :: group_id +integer :: file_idx +!h5 group id +integer(HID_T) :: gid !h5 collective read/write if mpi hdf5 is enabled integer(HID_T) :: mpi_dxpl_id(10) !hyperslab/hypersplice ids type(h5_ids_t) :: hyp(10) end type h5_group_ids_t -type(h5_group_ids_t), private, target :: h5_group_ids(10) +type(h5_group_ids_t), private, target :: h5_gids(10) contains -subroutine open_hdf5_file(fid_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) :: fid_entry +integer, intent(in) :: fid_idx logical, intent(in) :: writing logical, optional, intent(in) :: mpi_file integer, optional, intent(in) :: mpicom @@ -80,29 +80,29 @@ subroutine open_hdf5_file(fid_entry,filename,writing,mpi_file,mpicom) ! 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(fid_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, h5_file_ids(fid_entry)%file_id, 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, h5_file_ids(fid_entry)%file_id, 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(fid_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) :: fid_entry +integer, intent(in) :: fid_idx logical, intent(in) :: writing integer, intent(in) :: mpicom @@ -115,8 +115,8 @@ subroutine mpi_open_hdf5_file(fid_entry,filename,writing,mpicom) write(*,*) "Error mpi_open_hdf5_file: MPI Communicate has not been initiated." stop "stopping" end if - h5_file_ids(fid_entry)%mpi_h5 = .true. - h5_file_ids(fid_entry)%mpicomm_h5 = mpicom + h5_fids(fid_idx)%mpi_h5 = .true. + h5_fids(fid_idx)%mpicomm_h5 = mpicom call h5open_f(hdferr) @@ -125,9 +125,9 @@ subroutine mpi_open_hdf5_file(fid_entry,filename,writing,mpicom) 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, h5_file_ids(fid_entry)%file_id, hdferr, access_prp = h5_id%plist) + 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, h5_file_ids(fid_entry)%file_id, hdferr, access_prp = h5_id%plist) + 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(h5_id%plist, hdferr) @@ -139,65 +139,65 @@ subroutine mpi_open_hdf5_file(fid_entry,filename,writing,mpicom) end subroutine mpi_open_hdf5_file -subroutine create_hdf5_group(fid_entry,gid_entry,groupname,dxpl_index) +subroutine create_hdf5_group(fid_idx,gid_idx,groupname,dxpl_idx) character(len=*), intent(in) :: groupname -integer, intent(in) :: fid_entry, gid_entry -integer, optional, intent(in) :: dxpl_index +integer, intent(in) :: fid_idx, gid_idx +integer, optional, intent(in) :: dxpl_idx integer hdferr -call h5gcreate_f(h5_file_ids(fid_entry)%file_id, trim(groupname), h5_group_ids(gid_entry)%group_id, hdferr) -h5_group_ids(gid_entry)%file_index = fid_entry +call h5gcreate_f(h5_fids(fid_idx)%fid, trim(groupname), h5_gids(gid_idx)%gid, hdferr) +h5_gids(gid_idx)%file_idx = fid_idx -if (present(dxpl_index)) call mpi_set_dxpl_id(dxpl_index) +if (present(dxpl_idx)) call mpi_set_dxpl_id(dxpl_idx) end subroutine create_hdf5_group -subroutine open_hdf5_group(fid_entry,gid_entry,groupname,dxpl_index) +subroutine open_hdf5_group(fid_idx,gid_idx,groupname,dxpl_idx) character(len=*), intent(in) :: groupname -integer, intent(in) :: fid_entry, gid_entry -integer, optional, intent(in) :: dxpl_index +integer, intent(in) :: fid_idx, gid_idx +integer, optional, intent(in) :: dxpl_idx integer hdferr -call h5gopen_f(h5_file_ids(fid_entry)%file_id, trim(groupname), h5_group_ids(gid_entry)%group_id, hdferr) -h5_group_ids(gid_entry)%file_index = fid_entry +call h5gopen_f(h5_fids(fid_idx)%fid, trim(groupname), h5_gids(gid_idx)%gid, hdferr) +h5_gids(gid_idx)%file_idx = fid_idx -if (present(dxpl_index)) call mpi_set_dxpl_id(dxpl_index) +if (present(dxpl_idx)) call mpi_set_dxpl_id(dxpl_idx) end subroutine open_hdf5_group -subroutine close_hdf5_group(gid_entry,dxpl_index) +subroutine close_hdf5_group(gid_idx,dxpl_idx) -integer, intent(in) :: gid_entry -integer, optional, intent(in) :: dxpl_index +integer, intent(in) :: gid_idx +integer, optional, intent(in) :: dxpl_idx integer hdferr -call h5gclose_f(h5_group_ids(gid_entry)%group_id, hdferr) +call h5gclose_f(h5_gids(gid_idx)%gid, hdferr) -if (present(dxpl_index)) call close_mpi_set_dxpl_id(dxpl_index) +if (present(dxpl_idx)) call close_mpi_set_dxpl_id(dxpl_idx) end subroutine close_hdf5_group -subroutine close_hdf5_file(fid_entry,dxpl_index) +subroutine close_hdf5_file(fid_idx,dxpl_idx) -integer, intent(in) :: fid_entry -integer, optional, intent(in) :: dxpl_index +integer, intent(in) :: fid_idx +integer, optional, intent(in) :: dxpl_idx integer hdferr -if (present(dxpl_index)) call h5pclose_f(h5_file_ids(fid_entry)%mpi_dxpl_id(dxpl_index), hdferr) -call h5fclose_f(h5_file_ids(fid_entry)%file_id, hdferr) +if (present(dxpl_idx)) call h5pclose_f(h5_fids(fid_idx)%mpi_dxpl_id(dxpl_idx), hdferr) +call h5fclose_f(h5_fids(fid_idx)%fid, hdferr) call h5close_f(hdferr) -if (h5_file_ids(fid_entry)%mpi_h5) then - h5_file_ids(fid_entry)%mpi_h5 = .false. - h5_file_ids(fid_entry)%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 @@ -213,32 +213,32 @@ 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 + !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 + !end if #endif end subroutine set_dxpl_id -subroutine mpi_set_dxpl_id(dxpl_index) +subroutine mpi_set_dxpl_id(dxpl_idx) -integer, intent(in) :: dxpl_index +integer, intent(in) :: dxpl_idx -call set_dxpl_id(mpi_dxpl_id(dxpl_index)) +call set_dxpl_id(mpi_dxpl_id(dxpl_idx)) end subroutine mpi_set_dxpl_id -subroutine close_mpi_set_dxpl_id(dxpl_index) +subroutine close_mpi_set_dxpl_id(dxpl_idx) -integer, intent(in) :: dxpl_index +integer, intent(in) :: dxpl_idx integer hdferr -call h5pclose_f(mpi_dxpl_id(dxpl_index), hdferr) +call h5pclose_f(mpi_dxpl_id(dxpl_idx), hdferr) end subroutine close_mpi_set_dxpl_id @@ -264,30 +264,30 @@ 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) 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, intent(in) :: id_idx !index for file_id/group_id integer(HID_T), intent(out) :: file_or_group_id if (to_group) then - file_or_group_id = h5_group_ids(id_entry)%group_id + file_or_group_id = h5_gids(id_idx)%gid else - file_or_group_id = h5_file_ids(id_entry)%file_id + file_or_group_id = h5_fids(id_idx)%fid end if end subroutine get_file_or_group_id -subroutine get_file_or_group_hyp(to_group,id_entry,hyp_id_entry,hyp) +subroutine get_file_or_group_hyp(to_group,id_idx,hyp_id_idx,hyp) logical, intent(in) :: to_group !logical for distinguishing whether to write to group or not -integer, intent(in) :: id_entry, hyp_id_entry +integer, intent(in) :: id_idx, hyp_id_idx type(h5_ids_t), intent(inout), pointer :: hyp !hypersplice/hyperslab datatype if (to_group) then - hyp => h5_group_ids(id_entry)%hyp(hyp_id_entry) + hyp => h5_gids(id_idx)%hyp(hyp_id_idx) else - hyp => h5_file_ids(id_entry)%hyp(hyp_id_entry) + hyp => h5_fids(id_idx)%hyp(hyp_id_idx) end if end subroutine get_file_or_group_hyp @@ -343,14 +343,14 @@ 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,dxpl_idx,fdtype_name) -integer, intent(in) :: id_entry +integer, intent(in) :: id_idx 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 +integer, intent(in) :: hyp_id_idx,n_hyp_dim +integer, optional, intent(in) :: dxpl_idx character(len=*), optional, intent(in) :: fdtype_name integer(HID_T) :: dspace_id, dset_id, file_or_group_id, dset_type @@ -367,10 +367,10 @@ subroutine init_hyperspliced_array(id_entry,dataset_name,variable,write_to_group stop "Stopping." end if -call get_file_or_group_hyp(write_to_group,id_entry,hyp_id_entry,hyp) +call get_file_or_group_hyp(write_to_group,id_idx,hyp_id_idx,hyp) call get_array_dset_type(variable,hyp%dset_type,custom_type) call get_var_dims(variable,r,hyp%dims) -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) chunk_dims = hyp%dims r = r + 1 @@ -396,24 +396,24 @@ subroutine init_hyperspliced_array(id_entry,dataset_name,variable,write_to_group call h5dcreate_f(file_or_group_id, trim(dataset_name), hyp%dset_type, & hyp%dspace, hyp%dset, hdferr, hyp%plist) -if (present(fdtype_name)) call write_attributeHDF5(id_entry,dataset_name,"fortran_type",fdtype_name,write_to_group,hyp%dset) +if (present(fdtype_name)) call write_attributeHDF5(id_idx,dataset_name,"fortran_type",fdtype_name,write_to_group,hyp%dset) -if (present(dxpl_index)) call mpi_set_dxpl_id(dxpl_index) +if (present(dxpl_idx)) call mpi_set_dxpl_id(dxpl_idx) end subroutine init_hyperspliced_array -subroutine close_entire_hyperspliced_dataset(id_entry,hyp_id_entry,in_group,dxpl_index) +subroutine close_entire_hyperspliced_dataset(id_idx,hyp_id_idx,in_group,dxpl_idx) -integer, intent(in) :: id_entry, hyp_id_entry +integer, intent(in) :: id_idx, hyp_id_idx logical, intent(in) :: in_group -integer, optional, intent(in) :: dxpl_index +integer, optional, intent(in) :: dxpl_idx integer :: hdferr type(h5_ids_t), pointer :: hyp -call get_file_or_group_hyp(in_group,id_entry,hyp_id_entry,hyp) -if (present(dxpl_index)) call h5pclose_f(mpi_dxpl_id(dxpl_index), hdferr) +call get_file_or_group_hyp(in_group,id_idx,hyp_id_idx,hyp) +if (present(dxpl_idx)) call h5pclose_f(mpi_dxpl_id(dxpl_idx), hdferr) call h5pclose_f(hyp%plist, hdferr) call h5dclose_f(hyp%dset, hdferr) call h5sclose_f(hyp%dspace, hdferr) @@ -426,9 +426,9 @@ end subroutine close_entire_hyperspliced_dataset !-----------------------------! -subroutine write_attributeHDF5(id_entry,dataset_name,attr_name,attr,write_group,dset_id) +subroutine write_attributeHDF5(id_idx,dataset_name,attr_name,attr,write_group,dset_id) -integer, intent(in) :: id_entry +integer, intent(in) :: id_idx character(len=*), intent(in) :: dataset_name,attr_name class(*), intent(in), dimension(..) :: attr logical, intent(in) :: write_group @@ -441,7 +441,7 @@ subroutine write_attributeHDF5(id_entry,dataset_name,attr_name,attr,write_group, 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) if(.not. present(dset_id)) then call h5dopen_f(file_or_group_id, trim(dataset_name), dset_id_, hdferr) @@ -479,9 +479,9 @@ 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 read_attributeHDF5(id_idx,dataset_name,attr_name,attr,read_group,dset_id) -integer, intent(in) :: id_entry +integer, intent(in) :: id_idx character(len=*), intent(in) :: dataset_name,attr_name class(*), intent(inout), dimension(..) :: attr logical, intent(in) :: read_group @@ -494,7 +494,7 @@ subroutine read_attributeHDF5(id_entry,dataset_name,attr_name,attr,read_group,ds rnk = rank(attr) call get_var_dims(attr,rnk,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) if(.not. present(dset_id)) then call h5dopen_f(file_or_group_id, trim(dataset_name), dset_id_, hdferr) @@ -531,13 +531,13 @@ subroutine read_attributeHDF5(id_entry,dataset_name,attr_name,attr,read_group,ds end subroutine read_attributeHDF5 -subroutine writeHDF5(id_entry,dataset_name,variable_to_write,write_to_group,dxpl_index,fdtype_name) +subroutine writeHDF5(id_idx,dataset_name,variable_to_write,write_to_group,dxpl_idx,fdtype_name) -integer, intent(in) :: id_entry +integer, intent(in) :: id_idx 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 +integer, optional, intent(in) :: dxpl_idx character(len=*), optional, intent(in) :: fdtype_name integer(HID_T) :: file_or_group_id @@ -555,10 +555,10 @@ subroutine writeHDF5(id_entry,dataset_name,variable_to_write,write_to_group,dxpl call get_array_dset_type(variable_to_write,h5_ids%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) +call get_file_or_group_id(write_to_group,id_idx,file_or_group_id) -if (present(dxpl_index)) then - h5_ids%dxpl = mpi_dxpl_id(dxpl_index) +if (present(dxpl_idx)) then + h5_ids%dxpl = mpi_dxpl_id(dxpl_idx) else call set_dxpl_id(h5_ids%dxpl) end if @@ -568,26 +568,26 @@ subroutine writeHDF5(id_entry,dataset_name,variable_to_write,write_to_group,dxpl call h5dcreate_f(file_or_group_id, trim(dataset_name), h5_ids%dset_type, h5_ids%dspace, h5_ids%dset, hdferr) call h5dataset_write(variable_to_write, dims, h5_ids%dset_type, h5_ids%dset, h5_ids%dxpl) -if (present(fdtype_name)) call write_attributeHDF5(id_entry,dataset_name,"fortran_type",fdtype_name,write_to_group,h5_ids%dset) +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(h5_ids%dset_type, hdferr) !close custom dset_type -if (.not. present(dxpl_index)) call h5pclose_f(h5_ids%dxpl, hdferr) +if (.not. present(dxpl_idx)) 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,dxpl_idx) -integer, intent(in) :: id_entry !index for file_id/group_id +integer, intent(in) :: id_idx !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) :: hyp_id_idx +integer, intent(in) :: hyp_idx integer, intent(in) :: nslice !>= 1 to write slice/slab, else do not write data -integer, optional, intent(in) :: dxpl_index +integer, optional, intent(in) :: dxpl_idx integer(HID_T) :: file_or_group_id, dset_type, memspace_id, dxpl_id integer :: hdferr, i, r, id_type @@ -602,13 +602,13 @@ subroutine writeHDF5_hypersplice(id_entry,dataset_name,variable_to_write,write_t stop "Stopping." end if -call get_file_or_group_hyp(write_to_group,id_entry,hyp_id_entry,hyp) +call get_file_or_group_hyp(write_to_group,id_idx,hyp_id_idx,hyp) 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) +call get_file_or_group_id(write_to_group,id_idx,file_or_group_id) -if (present(dxpl_index)) then - dxpl_id = mpi_dxpl_id(dxpl_index) +if (present(dxpl_idx)) then + dxpl_id = mpi_dxpl_id(dxpl_idx) else call set_dxpl_id(dxpl_id) end if @@ -617,10 +617,10 @@ subroutine writeHDF5_hypersplice(id_entry,dataset_name,variable_to_write,write_t if (nslice == 1) then !hypersplice r = r + 1 dims(r) = nslice - offset(r) = hyp_index - 1 + offset(r) = hyp_idx - 1 else if (nslice >= 1) then !hyperslab dims(r) = nslice - offset(r) = hyp_index - 1 + offset(r) = hyp_idx - 1 else !dummy write dims(:) = 0 end if @@ -638,7 +638,7 @@ subroutine writeHDF5_hypersplice(id_entry,dataset_name,variable_to_write,write_t call h5dataset_write(variable_to_write, dims, dset_type, hyp%dset, dxpl_id, memspace_id, hyp%dspace) if (custom_type) call h5tclose_f(dset_type, hdferr) !close custom dset_type -if (.not. present(dxpl_index)) call h5pclose_f(dxpl_id, hdferr) +if (.not. present(dxpl_idx)) call h5pclose_f(dxpl_id, hdferr) call h5sclose_f(memspace_id, hdferr) end subroutine writeHDF5_hypersplice @@ -649,13 +649,13 @@ end subroutine writeHDF5_hypersplice !-----------------------------! -subroutine readHDF5(id_entry,dataset_name,variable_to_read,read_group,dxpl_index,fdtype_name) +subroutine readHDF5(id_idx,dataset_name,variable_to_read,read_group,dxpl_idx,fdtype_name) -integer, intent(in) :: id_entry +integer, intent(in) :: id_idx character(len=*), intent(in) :: dataset_name class(*), intent(inout), dimension(..) :: variable_to_read logical, intent(in) :: read_group -integer, optional, intent(in) :: dxpl_index +integer, optional, intent(in) :: dxpl_idx character(len=*), optional, intent(inout) :: fdtype_name integer(HID_T) :: file_or_group_id @@ -673,10 +673,10 @@ subroutine readHDF5(id_entry,dataset_name,variable_to_read,read_group,dxpl_index call get_array_dset_type(variable_to_read,h5_ids%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) -if (present(dxpl_index)) then - h5_ids%dxpl = mpi_dxpl_id(dxpl_index) +if (present(dxpl_idx)) then + h5_ids%dxpl = mpi_dxpl_id(dxpl_idx) else call set_dxpl_id(h5_ids%dxpl) end if @@ -684,25 +684,25 @@ subroutine readHDF5(id_entry,dataset_name,variable_to_read,read_group,dxpl_index call h5dopen_f(file_or_group_id, trim(dataset_name), h5_ids%dset, hdferr) call h5dataset_read(variable_to_read, dims, h5_ids%dset_type, h5_ids%dset, h5_ids%dxpl) -if (present(fdtype_name)) call read_attributeHDF5(id_entry,dataset_name,"fortran_type",fdtype_name,read_group,h5_ids%dset) +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 -if (.not. present(dxpl_index)) call h5pclose_f(h5_ids%dxpl, hdferr) +if (.not. present(dxpl_idx)) call h5pclose_f(h5_ids%dxpl, hdferr) call h5dclose_f(h5_ids%dset, hdferr) end subroutine readHDF5 -subroutine readHDF5_hypersplice(id_entry,dataset_name,variable_to_read,read_group,hyp_id_entry,hyp_index,nslice,dxpl_index) +subroutine readHDF5_hypersplice(id_idx,dataset_name,variable_to_read,read_group,hyp_id_idx,hyp_idx,nslice,dxpl_idx) -integer, intent(in) :: id_entry +integer, intent(in) :: id_idx 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) :: hyp_id_idx +integer, intent(in) :: hyp_idx integer, intent(in) :: nslice !>= 1 to write slice/slab, else do not write data -integer, optional, intent(in) :: dxpl_index +integer, optional, intent(in) :: dxpl_idx integer(HID_T) :: file_or_group_id, dset_type, memspace_id, filespace_id, dxpl_id integer :: hdferr, i, r, id_type @@ -719,10 +719,10 @@ subroutine readHDF5_hypersplice(id_entry,dataset_name,variable_to_read,read_grou 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) -if (present(dxpl_index)) then - dxpl_id = mpi_dxpl_id(dxpl_index) +if (present(dxpl_idx)) then + dxpl_id = mpi_dxpl_id(dxpl_idx) else call set_dxpl_id(dxpl_id) end if @@ -733,10 +733,10 @@ subroutine readHDF5_hypersplice(id_entry,dataset_name,variable_to_read,read_grou if (nslice == 1) then !hypersplice r = r + 1 dims(r) = nslice - offset(r) = hyp_index - 1 + offset(r) = hyp_idx - 1 else if (nslice >= 1) then !hyperslab dims(r) = nslice - offset(r) = hyp_index - 1 + offset(r) = hyp_idx - 1 else !dummy read dims(:) = 0 end if @@ -749,7 +749,7 @@ subroutine readHDF5_hypersplice(id_entry,dataset_name,variable_to_read,read_grou call h5dataset_read(variable_to_read, dims, dset_type, hyp%dset, 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) +if (.not. present(dxpl_idx)) call h5pclose_f(dxpl_id, hdferr) call h5sclose_f(memspace_id, hdferr) call h5sclose_f(filespace_id, hdferr) call h5dclose_f(hyp%dset, hdferr) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0b52d60..9648bc6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -64,7 +64,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 From 5a8980b080c987e790d61e44667a5671c47a8de6 Mon Sep 17 00:00:00 2001 From: Alyn James Date: Wed, 24 Dec 2025 22:25:27 +0000 Subject: [PATCH 3/7] Added mpi tests to testio.f90 and h5testrunner.f90. These additions are within the mpi preprocessor macro which is enabled through the compilation stage. Therefore another test runner (h5testrunner_mpi) is compiled through the CMakeLists.txt to test mpi functionality. This allows the testing for serial and parallel (mpi) hdf5 APIs. Updated the modhdf5 module to enable independent IO when mpi macros are enabled. --- src/modhdf5.f90 | 22 ++-- test/CMakeLists.txt | 17 ++- test/h5testrunner.f90 | 63 ++++++--- test/testio.f90 | 294 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 363 insertions(+), 33 deletions(-) diff --git a/src/modhdf5.f90 b/src/modhdf5.f90 index b75eece..2e355fc 100644 --- a/src/modhdf5.f90 +++ b/src/modhdf5.f90 @@ -203,9 +203,10 @@ subroutine close_hdf5_file(fid_idx,dxpl_idx) end subroutine close_hdf5_file -subroutine set_dxpl_id(dxpl_id) +subroutine set_dxpl_id(dxpl_id,mpi_independent) integer(HID_T), intent(inout) :: dxpl_id +logical, intent(in) :: mpi_independent integer hdferr @@ -213,10 +214,11 @@ 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 + 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) - !call h5pset_dxpl_mpio_f(dxpl_id, H5FD_MPIO_INDEPENDENT_F, hdferr) - !end if + end if #endif end subroutine set_dxpl_id @@ -226,12 +228,11 @@ subroutine mpi_set_dxpl_id(dxpl_idx) integer, intent(in) :: dxpl_idx -call set_dxpl_id(mpi_dxpl_id(dxpl_idx)) +call set_dxpl_id(mpi_dxpl_id(dxpl_idx),mpi_independent=.false.) end subroutine mpi_set_dxpl_id - subroutine close_mpi_set_dxpl_id(dxpl_idx) integer, intent(in) :: dxpl_idx @@ -243,7 +244,6 @@ subroutine close_mpi_set_dxpl_id(dxpl_idx) end subroutine close_mpi_set_dxpl_id - subroutine get_var_dims(variable,rnk,dims) class(*), intent(in), dimension(..) :: variable @@ -560,7 +560,7 @@ subroutine writeHDF5(id_idx,dataset_name,variable_to_write,write_to_group,dxpl_i if (present(dxpl_idx)) then h5_ids%dxpl = mpi_dxpl_id(dxpl_idx) else - call set_dxpl_id(h5_ids%dxpl) + call set_dxpl_id(h5_ids%dxpl,mpi_independent=.true.) end if ! Create dataspace (within group or not) for integer and save within that space @@ -610,7 +610,7 @@ subroutine writeHDF5_hypersplice(id_idx,dataset_name,variable_to_write,write_to_ if (present(dxpl_idx)) then dxpl_id = mpi_dxpl_id(dxpl_idx) else - call set_dxpl_id(dxpl_id) + call set_dxpl_id(dxpl_id,mpi_independent=.true.) end if offset(:) = 0 @@ -678,7 +678,7 @@ subroutine readHDF5(id_idx,dataset_name,variable_to_read,read_group,dxpl_idx,fdt if (present(dxpl_idx)) then h5_ids%dxpl = mpi_dxpl_id(dxpl_idx) else - call set_dxpl_id(h5_ids%dxpl) + call set_dxpl_id(h5_ids%dxpl,mpi_independent=.true.) end if call h5dopen_f(file_or_group_id, trim(dataset_name), h5_ids%dset, hdferr) @@ -724,7 +724,7 @@ subroutine readHDF5_hypersplice(id_idx,dataset_name,variable_to_read,read_group, if (present(dxpl_idx)) then dxpl_id = mpi_dxpl_id(dxpl_idx) else - call set_dxpl_id(dxpl_id) + call set_dxpl_id(dxpl_id,mpi_independent=.true.) end if call h5dopen_f(file_or_group_id, trim(dataset_name), hyp%dset, hdferr) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9648bc6..773b45d 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 @@ -87,4 +88,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..bceb70b 100644 --- a/test/h5testrunner.f90 +++ b/test/h5testrunner.f90 @@ -10,8 +10,14 @@ 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, & + 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 +#endif implicit none integer exitCode @@ -21,27 +27,42 @@ program h5testrunner 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(*,*) +#ifdef MPI_modhdf5 + call mpi_initiate() + write(*,*) "test MPI HDF5:" + write(*,*) "test MPI I/O on rank 0:" + call test_mpi_rank0() + write(*,*) + write(*,*) "test MPI I/O:" + call test_mpi() + write(*,*) + write(*,*) "test MPI hypersplice I/O:" + call test_mpi_hypersplice() + write(*,*) + call mpi_terminate() +#else + 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(*,*) +#endif write(*,*) "End tests" ! Summarise tests environment diff --git a/test/testio.f90 b/test/testio.f90 index 502434c..9b41a35 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 @@ -576,4 +579,295 @@ 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 + 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') + + !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) + + 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 + 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 + call mpi_barrier(mpicom,ierr) + + ! check if equivalent + 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') + + 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, dxpl_idx, dxpl_idx2 + 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. + dxpl_idx = 1 + dxpl_idx2 = 2 + + ! 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),dxpl_idx) + call init_hyperspliced_array(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2,dims_int3d(3),dxpl_idx2) + + 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,dxpl_idx) + call writeHDF5_hypersplice(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2,i,0,dxpl_idx2) + else + test_int2d = test_int3d(:,:,i) + call writeHDF5_hypersplice(File_id,"hyper_splice_3d_test",test_int2d,not_within_group,hyper_id,i,nslices,dxpl_idx) + call writeHDF5_hypersplice(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2,i,nslices,dxpl_idx2) + end if + end do + call close_entire_hyperspliced_dataset(File_id,hyper_id,not_within_group,dxpl_idx) + call close_entire_hyperspliced_dataset(group_id,hyper_id2,within_group,dxpl_idx2) + + 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 mpi_set_dxpl_id(dxpl_idx) + + 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,dxpl_idx) + else + call readHDF5_hypersplice(File_id,"hyper_splice_3d_test",test_int3d_read,not_within_group,hyper_id,i,nslices,dxpl_idx) + end if + end do + call mpi_barrier(mpicom,ierr) + 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 + call open_hdf5_group(File_id,group_id,'Test_Group_Integer',dxpl_idx2) + 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,dxpl_idx2) + else + call readHDF5_hypersplice(group_id,"hyper_splice_3d_test",test_int3d_read,within_group,hyper_id2,i,nslices,dxpl_idx2) + end if + end do + call mpi_barrier(mpicom,ierr) + 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,dxpl_idx2) + call close_hdf5_file(File_id,dxpl_idx) + + call mpi_barrier(mpicom,ierr) + +end subroutine test_mpi_hypersplice + +#endif + end module From 25a50b2e11d0c96859b2704aba48f1c1ab2dfeb1 Mon Sep 17 00:00:00 2001 From: Alyn James Date: Fri, 26 Dec 2025 17:09:40 +0000 Subject: [PATCH 4/7] Refactor the modhdf5 ro remove the global mpi_dxpl_id array and instead the dxpl within the h5_ids_t type. This bundles the hypersplice/hyperslab mpi dxpl_ids to the corresponding hyper id index (reducing complexity). The non-hyper datasets will use the mpi independent dxpl_id generated within the readHDF5/writeHDF5 arrays. This removes the need of the dxpl_index. The h5_ids_t variables are initialised to -1 to make it easier to close and reset the hypersplice id entries. Also, extracted whether the file is using mpi mode which is then used when setting the the dxpl id. Added a test using openmp parallelisation for the hypersplices. Updated the hypersplice/hyperslab subroutines to open the hypersplice dataset for reading instead of opening the dataset each time the hypersplice array is read. Also moved the configuration of the hypersplice/hyperslab dims and offset into a separate subroutine. Tidied the init/open/read/write subroutines so that all the variable configuration is done and then the h5 subroutines are called. Updated the tests with respect to these modifications. Lastly, the writing to terminal calls and test assertions are now done only on the rank 0 mpi processor (for clarity - mpi tests still use multiple processors when specified). --- src/modhdf5.f90 | 317 ++++++++++++++++++------------------------ test/h5testrunner.f90 | 89 +++++++++--- test/testio.f90 | 158 ++++++++++++++++----- 3 files changed, 332 insertions(+), 232 deletions(-) diff --git a/src/modhdf5.f90 b/src/modhdf5.f90 index 2e355fc..fe81975 100644 --- a/src/modhdf5.f90 +++ b/src/modhdf5.f90 @@ -18,23 +18,20 @@ module modhdf5 logical :: use_hdf5=.false. !h5 compression level integer, private :: compression_level = 4 -! mpi hdf5 variables - mpi hdf5 is enabled when compiling with -DMPI_modhdf5 -!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 +integer(HID_T) :: plist = -1 !hyperslab/hypersplice dataset ids -integer(HID_T) :: dset +integer(HID_T) :: dset = -1 !hyperslab/hypersplice dataspace ids -integer(HID_T) :: dspace +integer(HID_T) :: dspace = -1 !dimensions of the hyperspliced array integer(HSIZE_T) :: dims(7) -!used for h5 collective mpi read/write -integer(HID_T) :: dxpl +!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 +integer(HID_T) :: dset_type = -1 end type h5_ids_t ! derived type containing the h5 file ids for each file in use @@ -45,8 +42,6 @@ module modhdf5 logical :: mpi_h5 = .false. !h5 mpicomm copy integer :: mpicomm_h5 = -1 -!h5 collective read/write if mpi hdf5 is enabled -integer(HID_T) :: mpi_dxpl_id(10) !hyperslab/hypersplice ids type(h5_ids_t) :: hyp(10) end type h5_file_ids_t @@ -58,8 +53,6 @@ module modhdf5 integer :: file_idx !h5 group id integer(HID_T) :: gid -!h5 collective read/write if mpi hdf5 is enabled -integer(HID_T) :: mpi_dxpl_id(10) !hyperslab/hypersplice ids type(h5_ids_t) :: hyp(10) end type h5_group_ids_t @@ -139,60 +132,49 @@ subroutine mpi_open_hdf5_file(fid_idx,filename,writing,mpicom) end subroutine mpi_open_hdf5_file -subroutine create_hdf5_group(fid_idx,gid_idx,groupname,dxpl_idx) +subroutine create_hdf5_group(fid_idx,gid_idx,groupname) character(len=*), intent(in) :: groupname integer, intent(in) :: fid_idx, gid_idx -integer, optional, intent(in) :: dxpl_idx integer hdferr call h5gcreate_f(h5_fids(fid_idx)%fid, trim(groupname), h5_gids(gid_idx)%gid, hdferr) h5_gids(gid_idx)%file_idx = fid_idx -if (present(dxpl_idx)) call mpi_set_dxpl_id(dxpl_idx) - end subroutine create_hdf5_group -subroutine open_hdf5_group(fid_idx,gid_idx,groupname,dxpl_idx) +subroutine open_hdf5_group(fid_idx,gid_idx,groupname) character(len=*), intent(in) :: groupname integer, intent(in) :: fid_idx, gid_idx -integer, optional, intent(in) :: dxpl_idx integer hdferr call h5gopen_f(h5_fids(fid_idx)%fid, trim(groupname), h5_gids(gid_idx)%gid, hdferr) h5_gids(gid_idx)%file_idx = fid_idx -if (present(dxpl_idx)) call mpi_set_dxpl_id(dxpl_idx) - end subroutine open_hdf5_group -subroutine close_hdf5_group(gid_idx,dxpl_idx) +subroutine close_hdf5_group(gid_idx) integer, intent(in) :: gid_idx -integer, optional, intent(in) :: dxpl_idx integer hdferr call h5gclose_f(h5_gids(gid_idx)%gid, hdferr) -if (present(dxpl_idx)) call close_mpi_set_dxpl_id(dxpl_idx) - end subroutine close_hdf5_group -subroutine close_hdf5_file(fid_idx,dxpl_idx) +subroutine close_hdf5_file(fid_idx) integer, intent(in) :: fid_idx -integer, optional, intent(in) :: dxpl_idx integer hdferr -if (present(dxpl_idx)) call h5pclose_f(h5_fids(fid_idx)%mpi_dxpl_id(dxpl_idx), hdferr) call h5fclose_f(h5_fids(fid_idx)%fid, hdferr) call h5close_f(hdferr) if (h5_fids(fid_idx)%mpi_h5) then @@ -203,10 +185,10 @@ subroutine close_hdf5_file(fid_idx,dxpl_idx) end subroutine close_hdf5_file -subroutine set_dxpl_id(dxpl_id,mpi_independent) +subroutine set_dxpl_id(dxpl_id,using_mpi,mpi_independent) integer(HID_T), intent(inout) :: dxpl_id -logical, intent(in) :: mpi_independent +logical, intent(in) :: using_mpi, mpi_independent integer hdferr @@ -214,36 +196,18 @@ subroutine set_dxpl_id(dxpl_id,mpi_independent) call h5pcreate_f(H5P_DATASET_XFER_F, dxpl_id, hdferr) ! set collective read if mpi hdf5 is enabled #ifdef MPI_modhdf5 - 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 + 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_idx) - -integer, intent(in) :: dxpl_idx - -call set_dxpl_id(mpi_dxpl_id(dxpl_idx),mpi_independent=.false.) - -end subroutine mpi_set_dxpl_id - - -subroutine close_mpi_set_dxpl_id(dxpl_idx) - -integer, intent(in) :: dxpl_idx - -integer hdferr - -call h5pclose_f(mpi_dxpl_id(dxpl_idx), hdferr) - -end subroutine close_mpi_set_dxpl_id - - subroutine get_var_dims(variable,rnk,dims) class(*), intent(in), dimension(..) :: variable @@ -264,20 +228,24 @@ subroutine get_var_dims(variable,rnk,dims) end subroutine get_var_dims -subroutine get_file_or_group_id(to_group,id_idx,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_idx !index for file_id/group_id integer(HID_T), intent(out) :: file_or_group_id +logical, intent(out) :: using_mpi if (to_group) then 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 = 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 distinguishing whether to write to group or not @@ -343,21 +311,20 @@ subroutine create_char_dset_type(variable,char_type,custom_type) end subroutine -subroutine init_hyperspliced_array(id_idx,dataset_name,variable,write_to_group,hyp_id_idx,n_hyp_dim,dxpl_idx,fdtype_name) +subroutine init_hyperspliced_array(id_idx,dataset_name,variable,write_to_group,hyp_id_idx,n_hyp_dim,fdtype_name) integer, intent(in) :: id_idx character(len=*), intent(in) :: dataset_name class(*), intent(in), dimension(..) :: variable logical, intent(in) :: write_to_group integer, intent(in) :: hyp_id_idx,n_hyp_dim -integer, optional, intent(in) :: dxpl_idx character(len=*), optional, intent(in) :: fdtype_name integer(HID_T) :: dspace_id, dset_id, file_or_group_id, dset_type integer :: hdferr, i, r 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) @@ -367,10 +334,11 @@ subroutine init_hyperspliced_array(id_idx,dataset_name,variable,write_to_group,h 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_array_dset_type(variable,hyp%dset_type,custom_type) call get_var_dims(variable,r,hyp%dims) -call get_file_or_group_id(write_to_group,id_idx,file_or_group_id) +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 r = r + 1 @@ -380,17 +348,11 @@ subroutine init_hyperspliced_array(id_idx,dataset_name,variable,write_to_group,h ! Create dataspace for whole dataset call h5screate_simple_f(r, hyp%dims(1:r), hyp%dspace, hdferr) -! Create dataset creation property list +! Create property list for chunking, and then compression call h5pcreate_f(H5P_DATASET_CREATE_F, hyp%plist, hdferr) - -! Set chunking -call h5pset_chunk_f(hyp%plist, r, chunk_dims, hdferr) - -! Enable compression (GZIP level 4) -call h5pset_deflate_f(hyp%plist, compression_level, hdferr) - -! Enable shuffle filter (best compression) -call h5pset_shuffle_f(hyp%plist, hdferr) +call h5pset_chunk_f(hyp%plist, r, 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) ! Create dataset with chunking + compression call h5dcreate_f(file_or_group_id, trim(dataset_name), hyp%dset_type, & @@ -398,29 +360,84 @@ subroutine init_hyperspliced_array(id_idx,dataset_name,variable,write_to_group,h if (present(fdtype_name)) call write_attributeHDF5(id_idx,dataset_name,"fortran_type",fdtype_name,write_to_group,hyp%dset) -if (present(dxpl_idx)) call mpi_set_dxpl_id(dxpl_idx) - end subroutine init_hyperspliced_array -subroutine close_entire_hyperspliced_dataset(id_idx,hyp_id_idx,in_group,dxpl_idx) +subroutine open_hyperspliced_array(id_idx,dataset_name,variable,read_group,hyp_id_idx,fdtype_name) + +integer, intent(in) :: id_idx +character(len=*), intent(in) :: dataset_name +class(*), intent(in), dimension(..) :: variable +logical, intent(in) :: read_group +integer, intent(in) :: hyp_id_idx +character(len=*), optional, intent(inout) :: fdtype_name + +integer(HID_T) :: dspace_id, dset_id, file_or_group_id, dset_type + +integer :: hdferr, i, r +integer(HSIZE_T) :: dims(7), chunk_dims(7) +logical custom_type, using_mpi +type(h5_ids_t), pointer :: hyp + +r = rank(variable) + +if ((r < 0) .or. (r > 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,r,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) + +end subroutine open_hyperspliced_array + + +subroutine close_entire_hyperspliced_dataset(id_idx,hyp_id_idx,in_group) integer, intent(in) :: id_idx, hyp_id_idx logical, intent(in) :: in_group -integer, optional, intent(in) :: dxpl_idx integer :: hdferr +type(h5_ids_t) :: reset_hyp type(h5_ids_t), pointer :: hyp call get_file_or_group_hyp(in_group,id_idx,hyp_id_idx,hyp) -if (present(dxpl_idx)) call h5pclose_f(mpi_dxpl_id(dxpl_idx), hdferr) -call h5pclose_f(hyp%plist, hdferr) -call h5dclose_f(hyp%dset, hdferr) -call h5sclose_f(hyp%dspace, hdferr) +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 +subroutine config_hyper_dims(rnk,nslice,hyp_idx,dims,offset) +integer, intent(inout) :: rnk +integer, intent(in) :: nslice, hyp_idx +integer(HSIZE_T), intent(inout) :: dims(7), offset(7) + +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 + + !-----------------------------! ! Writing Subroutines ! !-----------------------------! @@ -437,11 +454,12 @@ subroutine write_attributeHDF5(id_idx,dataset_name,attr_name,attr,write_group,ds 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_idx,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) @@ -450,7 +468,6 @@ subroutine write_attributeHDF5(id_idx,dataset_name,attr_name,attr,write_group,ds 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) @@ -490,19 +507,19 @@ subroutine read_attributeHDF5(id_idx,dataset_name,attr_name,attr,read_group,dset integer(HID_T) :: dset_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(read_group,id_idx,file_or_group_id) +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(.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 attribute datatype and then read it -call get_array_dset_type(attr,type_id,custom_type) +! Get read attribute call h5aopen_f(dset_id_, trim(attr_name), attr_id, hdferr) select rank(attr) @@ -531,19 +548,17 @@ subroutine read_attributeHDF5(id_idx,dataset_name,attr_name,attr,read_group,dset end subroutine read_attributeHDF5 -subroutine writeHDF5(id_idx,dataset_name,variable_to_write,write_to_group,dxpl_idx,fdtype_name) +subroutine writeHDF5(id_idx,dataset_name,variable_to_write,write_to_group,fdtype_name) integer, intent(in) :: id_idx character(len=*), intent(in) :: dataset_name class(*), intent(in), dimension(..) :: variable_to_write logical, intent(in) :: write_to_group -integer, optional, intent(in) :: dxpl_idx character(len=*), optional, intent(in) :: fdtype_name integer(HID_T) :: file_or_group_id integer :: hdferr, i, r -integer(HSIZE_T) :: dims(7) -logical custom_type +logical custom_type,using_mpi type(h5_ids_t) :: h5_ids r = rank(variable_to_write) @@ -553,32 +568,27 @@ subroutine writeHDF5(id_idx,dataset_name,variable_to_write,write_to_group,dxpl_i stop "Stopping." end if +call get_var_dims(variable_to_write,r,h5_ids%dims) call get_array_dset_type(variable_to_write,h5_ids%dset_type,custom_type) -call get_var_dims(variable_to_write,r,dims) -call get_file_or_group_id(write_to_group,id_idx,file_or_group_id) - -if (present(dxpl_idx)) then - h5_ids%dxpl = mpi_dxpl_id(dxpl_idx) -else - call set_dxpl_id(h5_ids%dxpl,mpi_independent=.true.) -end if +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(r, dims(1:r), h5_ids%dspace, hdferr) +call h5screate_simple_f(r, h5_ids%dims(1:r), 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) -call h5dataset_write(variable_to_write, dims, h5_ids%dset_type, h5_ids%dset, h5_ids%dxpl) +call h5dataset_write(variable_to_write, h5_ids%dims, h5_ids%dset_type, h5_ids%dset, h5_ids%dxpl) 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(h5_ids%dset_type, hdferr) !close custom dset_type -if (.not. present(dxpl_idx)) call h5pclose_f(h5_ids%dxpl, hdferr) +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_idx,dataset_name,variable_to_write,write_to_group,hyp_id_idx,hyp_idx,nslice,dxpl_idx) +subroutine writeHDF5_hypersplice(id_idx,dataset_name,variable_to_write,write_to_group,hyp_id_idx,hyp_idx,nslice) integer, intent(in) :: id_idx !index for file_id/group_id character(len=*), intent(in) :: dataset_name !name of dataset in h5 file @@ -587,12 +597,11 @@ subroutine writeHDF5_hypersplice(id_idx,dataset_name,variable_to_write,write_to_ integer, intent(in) :: hyp_id_idx integer, intent(in) :: hyp_idx integer, intent(in) :: nslice !>= 1 to write slice/slab, else do not write data -integer, optional, intent(in) :: dxpl_idx -integer(HID_T) :: 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, i, r 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) @@ -602,32 +611,14 @@ subroutine writeHDF5_hypersplice(id_idx,dataset_name,variable_to_write,write_to_ 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_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_idx,file_or_group_id) - -if (present(dxpl_idx)) then - dxpl_id = mpi_dxpl_id(dxpl_idx) -else - call set_dxpl_id(dxpl_id,mpi_independent=.true.) -end if - -offset(:) = 0 -if (nslice == 1) then !hypersplice - r = r + 1 - dims(r) = nslice - offset(r) = hyp_idx - 1 -else if (nslice >= 1) then !hyperslab - dims(r) = nslice - offset(r) = hyp_idx - 1 -else !dummy write - dims(:) = 0 -end if +call get_array_dset_type(variable_to_write,dset_type,custom_type) +call config_hyper_dims(r,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." @@ -635,10 +626,9 @@ subroutine writeHDF5_hypersplice(id_idx,dataset_name,variable_to_write,write_to_ call h5sselect_hyperslab_f(hyp%dspace, 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, hyp%dset, dxpl_id, memspace_id, hyp%dspace) +call h5dataset_write(variable_to_write, dims, dset_type, hyp%dset, hyp%dxpl, memspace_id, hyp%dspace) if (custom_type) call h5tclose_f(dset_type, hdferr) !close custom dset_type -if (.not. present(dxpl_idx)) call h5pclose_f(dxpl_id, hdferr) call h5sclose_f(memspace_id, hdferr) end subroutine writeHDF5_hypersplice @@ -649,19 +639,17 @@ end subroutine writeHDF5_hypersplice !-----------------------------! -subroutine readHDF5(id_idx,dataset_name,variable_to_read,read_group,dxpl_idx,fdtype_name) +subroutine readHDF5(id_idx,dataset_name,variable_to_read,read_group,fdtype_name) integer, intent(in) :: id_idx character(len=*), intent(in) :: dataset_name class(*), intent(inout), dimension(..) :: variable_to_read logical, intent(in) :: read_group -integer, optional, intent(in) :: dxpl_idx character(len=*), optional, intent(inout) :: fdtype_name integer(HID_T) :: file_or_group_id integer :: hdferr, i, r -integer(HSIZE_T) :: dims(7) -logical custom_type +logical custom_type,using_mpi type(h5_ids_t) :: h5_ids r = rank(variable_to_read) @@ -671,29 +659,24 @@ subroutine readHDF5(id_idx,dataset_name,variable_to_read,read_group,dxpl_idx,fdt stop "Stopping." end if +call get_file_or_group_id(read_group,id_idx,file_or_group_id,using_mpi) +call get_var_dims(variable_to_read,r,h5_ids%dims) call get_array_dset_type(variable_to_read,h5_ids%dset_type,custom_type) -call get_var_dims(variable_to_read,r,dims) -call get_file_or_group_id(read_group,id_idx,file_or_group_id) - -if (present(dxpl_idx)) then - h5_ids%dxpl = mpi_dxpl_id(dxpl_idx) -else - call set_dxpl_id(h5_ids%dxpl,mpi_independent=.true.) -end if +call set_dxpl_id(h5_ids%dxpl,using_mpi,mpi_independent=.true.) call h5dopen_f(file_or_group_id, trim(dataset_name), h5_ids%dset, hdferr) -call h5dataset_read(variable_to_read, dims, h5_ids%dset_type, h5_ids%dset, h5_ids%dxpl) +call h5dataset_read(variable_to_read, h5_ids%dims, h5_ids%dset_type, h5_ids%dset, h5_ids%dxpl) 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 -if (.not. present(dxpl_idx)) call h5pclose_f(h5_ids%dxpl, hdferr) +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,dxpl_idx) +subroutine readHDF5_hypersplice(id_idx,dataset_name,variable_to_read,read_group,hyp_id_idx,hyp_idx,nslice) integer, intent(in) :: id_idx character(len=*), intent(in) :: dataset_name @@ -702,13 +685,12 @@ subroutine readHDF5_hypersplice(id_idx,dataset_name,variable_to_read,read_group, integer, intent(in) :: hyp_id_idx integer, intent(in) :: hyp_idx integer, intent(in) :: nslice !>= 1 to write slice/slab, else do not write data -integer, optional, intent(in) :: dxpl_idx -integer(HID_T) :: 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 -type(h5_ids_t) :: hyp +integer(HID_T) :: file_or_group_id, dset_type, memspace_id, filespace_id +integer :: hdferr, i, r +integer(HSIZE_T) :: dims(7), offset(7) +logical custom_type,using_mpi +type(h5_ids_t), pointer :: hyp r = rank(variable_to_read) @@ -717,42 +699,19 @@ subroutine readHDF5_hypersplice(id_idx,dataset_name,variable_to_read,read_group, stop "Stopping." end if -call get_array_dset_type(variable_to_read,dset_type,custom_type) +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,r,dims) -call get_file_or_group_id(read_group,id_idx,file_or_group_id) - -if (present(dxpl_idx)) then - dxpl_id = mpi_dxpl_id(dxpl_idx) -else - call set_dxpl_id(dxpl_id,mpi_independent=.true.) -end if - -call h5dopen_f(file_or_group_id, trim(dataset_name), hyp%dset, hdferr) - -offset(:) = 0 -if (nslice == 1) then !hypersplice - r = r + 1 - dims(r) = nslice - offset(r) = hyp_idx - 1 -else if (nslice >= 1) then !hyperslab - dims(r) = nslice - offset(r) = hyp_idx - 1 -else !dummy read - dims(:) = 0 -end if - -call h5dget_space_f(hyp%dset, filespace_id, hdferr) -call h5sselect_hyperslab_f(filespace_id, H5S_SELECT_SET_F, offset(1:r), dims(1:r), hdferr) +call get_array_dset_type(variable_to_read,dset_type,custom_type) +call config_hyper_dims(r,nslice,hyp_idx,dims,offset) +call h5sselect_hyperslab_f(hyp%dspace, 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, hyp%dset, dxpl_id, memspace_id, filespace_id) +call h5dataset_read(variable_to_read, dims, dset_type, hyp%dset, hyp%dxpl, memspace_id, hyp%dspace) if (custom_type) call h5tclose_f(dset_type, hdferr) !close custom dset_type -if (.not. present(dxpl_idx)) call h5pclose_f(dxpl_id, hdferr) call h5sclose_f(memspace_id, hdferr) -call h5sclose_f(filespace_id, hdferr) -call h5dclose_f(hyp%dset, hdferr) end subroutine readHDF5_hypersplice diff --git a/test/h5testrunner.f90 b/test/h5testrunner.f90 index bceb70b..b7af217 100644 --- a/test/h5testrunner.f90 +++ b/test/h5testrunner.f90 @@ -12,64 +12,109 @@ program h5testrunner use modhdf5 #ifdef MPI_modhdf5 use mpi -use testio, only: mpi_initiate, mpi_terminate, & +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 - -! Initialise the testsuite enviornment -call testsuite_initialize() - -write(*,*) -write(*,*) "Start of testing HDF5_Fortran_API capabilities:" +exitCode = 0 #ifdef MPI_modhdf5 call mpi_initiate() - write(*,*) "test MPI HDF5:" - write(*,*) "test MPI I/O on rank 0:" + ! 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() - write(*,*) - write(*,*) "test MPI I/O:" + + if (lp_mpi==0) then + write(*,*) + write(*,*) "test MPI I/O:" + end if + call test_mpi() - write(*,*) - write(*,*) "test MPI hypersplice I/O:" + + if (lp_mpi==0) then + write(*,*) + write(*,*) "test MPI hypersplice I/O:" + end if + call test_mpi_hypersplice() - write(*,*) + + 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(*,*) -#endif + write(*,*) "End tests" -write(*,*) "End tests" -! Summarise tests environment -call testsuite_summary() - -! Wrap-up the testsuite environment -call testsuite_finalize(exitCode) + ! 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 9b41a35..69ad921 100644 --- a/test/testio.f90 +++ b/test/testio.f90 @@ -511,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(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) @@ -530,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) @@ -570,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') @@ -664,9 +756,11 @@ subroutine test_mpi call mpi_barrier(mpicom,ierr) ! check if equivalent - 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') + 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 @@ -674,7 +768,7 @@ subroutine test_mpi call open_hdf5_group(File_id,group_id,'Test_Group_Double') call readHDF5(group_id,'double_1d',test_double1d_read,dataset_within_group) - call assert_equal(test_real1d, test_real1d_read, delta = tol_sp, message='rank 1 double from Test_Group_Double differs') + 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) @@ -739,6 +833,7 @@ subroutine test_mpi_rank0 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) @@ -761,13 +856,14 @@ subroutine test_mpi_rank0 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 - call mpi_barrier(mpicom,ierr) ! check if equivalent - 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') + 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) @@ -778,7 +874,7 @@ subroutine test_mpi_hypersplice 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, dxpl_idx, dxpl_idx2 + 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 @@ -795,15 +891,13 @@ subroutine test_mpi_hypersplice writing = .true. using_mpi = .true. - dxpl_idx = 1 - dxpl_idx2 = 2 ! 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),dxpl_idx) - call init_hyperspliced_array(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2,dims_int3d(3),dxpl_idx2) + 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 @@ -816,16 +910,16 @@ subroutine test_mpi_hypersplice 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,dxpl_idx) - call writeHDF5_hypersplice(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2,i,0,dxpl_idx2) + 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,dxpl_idx) - call writeHDF5_hypersplice(group_id,"hyper_splice_3d_test",test_int2d,within_group,hyper_id2,i,nslices,dxpl_idx2) + 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,dxpl_idx) - call close_entire_hyperspliced_dataset(group_id,hyper_id2,within_group,dxpl_idx2) + 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) @@ -837,32 +931,34 @@ subroutine test_mpi_hypersplice ! 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 mpi_set_dxpl_id(dxpl_idx) + 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,dxpl_idx) + 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,dxpl_idx) + call readHDF5_hypersplice(File_id,"hyper_splice_3d_test",test_int3d_read,not_within_group,hyper_id,i,nslices) end if end do - call mpi_barrier(mpicom,ierr) - call assert_equal(reshape(test_int3d,[size(test_int3d)]), reshape(test_int3d_read,[size(test_int3d_read)]), message='Hyperspliced rank 3 integer differs') + 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 - call open_hdf5_group(File_id,group_id,'Test_Group_Integer',dxpl_idx2) 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,dxpl_idx2) + 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,dxpl_idx2) + 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 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,dxpl_idx2) - call close_hdf5_file(File_id,dxpl_idx) + 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) From bcc61b7a0834a11f45d92b87a5271b9f7a1dee7d Mon Sep 17 00:00:00 2001 From: Alyn James Date: Sun, 28 Dec 2025 15:36:41 +0000 Subject: [PATCH 5/7] declare the module public and private variables and subroutines. --- src/modhdf5.f90 | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/modhdf5.f90 b/src/modhdf5.f90 index fe81975..61ea857 100644 --- a/src/modhdf5.f90 +++ b/src/modhdf5.f90 @@ -8,14 +8,20 @@ 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, h5dataset_write, & + h5dataset_read + +! modHDF5 variables ! !HDF5 extension -character(256) :: h5filext='.h5' +character(256), public :: h5filext='.h5' !user input to use the hdf5 files -logical :: use_hdf5=.false. +logical, public :: use_hdf5=.false. !h5 compression level integer, private :: compression_level = 4 From df36af616149ab0e097614beed841d1d8ee7f5b2 Mon Sep 17 00:00:00 2001 From: AlynJ <34216043+AlynJ@users.noreply.github.com> Date: Sun, 28 Dec 2025 18:06:13 +0000 Subject: [PATCH 6/7] Add CI * Initial CI jobs * Initial CI jobs * Initial CI jobs * Initial CI jobs --- .github/workflows/ci.yml | 67 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7a7979c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,67 @@ +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 From 62d3f2e7961ba4c2bae0b730b1e7a6bcd117ba90 Mon Sep 17 00:00:00 2001 From: AlynJ <34216043+AlynJ@users.noreply.github.com> Date: Wed, 31 Dec 2025 14:54:53 +0000 Subject: [PATCH 7/7] Update modhdf5 and docs (#2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ifx intel compilers to CI (and update CMakeLists.txt), make read/write subroutines in modhdf5 compatible with the intel compilers to avoid IntelLLVM’s restriction on polymorphic assumed-rank dummies + optional arguments. Also adjusted the .inc files and added new ones to be used directly in the readHDF5*/writeHDF5* subroutines. Add further documentation to README.md and comments on the subroutines' dummy inputs and outputs (and some minor docstrings). Tidy variables used in subroutines. Reorder subroutines in modhdf5. Added version global variable to module --- .github/workflows/ci.yml | 100 ++++++ README.md | 78 ++++- src/modhdf5.f90 | 546 ++++++++++++++++---------------- src/modhdf5_attribute_read.inc | 1 - src/modhdf5_attribute_write.inc | 1 - src/modhdf5_read.inc | 29 +- src/modhdf5_read_hyper.inc | 15 + src/modhdf5_write.inc | 29 +- src/modhdf5_write_hyper.inc | 15 + test/CMakeLists.txt | 29 +- 10 files changed, 520 insertions(+), 323 deletions(-) create mode 100644 src/modhdf5_read_hyper.inc create mode 100644 src/modhdf5_write_hyper.inc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a7979c..c8d81b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,3 +65,103 @@ jobs: ./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 61ea857..dadd037 100644 --- a/src/modhdf5.f90 +++ b/src/modhdf5.f90 @@ -14,53 +14,54 @@ module modhdf5 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, h5dataset_write, & - h5dataset_read + 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), public :: modhdf5_version='0.1.0' !user input to use the hdf5 files logical, public :: use_hdf5=.false. !h5 compression level integer, private :: compression_level = 4 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 + !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) + !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) + !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) @@ -68,11 +69,11 @@ module modhdf5 subroutine open_hdf5_file(fid_idx,filename,writing,mpi_file,mpicom) -character(len=*), intent(in) :: filename -integer, intent(in) :: fid_idx -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 @@ -95,15 +96,16 @@ end subroutine open_hdf5_file subroutine mpi_open_hdf5_file(fid_idx,filename,writing,mpicom) + ! use the mpi library within mpi HDF5 enabled #ifdef MPI_modhdf5 use mpi, only: MPI_COMM_NULL, MPI_INFO_NULL #endif -character(len=*), intent(in) :: filename -integer, intent(in) :: fid_idx -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 type(h5_ids_t) :: h5_id @@ -140,8 +142,8 @@ end subroutine mpi_open_hdf5_file subroutine create_hdf5_group(fid_idx,gid_idx,groupname) -character(len=*), intent(in) :: groupname -integer, intent(in) :: fid_idx, gid_idx +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 @@ -153,8 +155,8 @@ end subroutine create_hdf5_group subroutine open_hdf5_group(fid_idx,gid_idx,groupname) -character(len=*), intent(in) :: groupname -integer, intent(in) :: fid_idx, gid_idx +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 @@ -166,7 +168,7 @@ end subroutine open_hdf5_group subroutine close_hdf5_group(gid_idx) -integer, intent(in) :: gid_idx +integer, intent(in) :: gid_idx !group index integer hdferr @@ -177,7 +179,7 @@ end subroutine close_hdf5_group subroutine close_hdf5_file(fid_idx) -integer, intent(in) :: fid_idx +integer, intent(in) :: fid_idx !file index integer hdferr @@ -192,6 +194,7 @@ end subroutine close_hdf5_file 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 @@ -216,9 +219,9 @@ end subroutine 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 @@ -236,10 +239,10 @@ end subroutine get_var_dims 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_idx !index for file_id/group_id -integer(HID_T), intent(out) :: file_or_group_id -logical, intent(out) :: using_mpi +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 = h5_gids(id_idx)%gid @@ -254,8 +257,8 @@ 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 distinguishing whether to write to group or not -integer, intent(in) :: id_idx, hyp_id_idx +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 @@ -268,9 +271,9 @@ 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) @@ -287,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. @@ -304,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 @@ -318,51 +323,51 @@ subroutine create_char_dset_type(variable,char_type,custom_type) 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_idx -character(len=*), intent(in) :: dataset_name -class(*), intent(in), dimension(..) :: variable -logical, intent(in) :: write_to_group -integer, intent(in) :: hyp_id_idx,n_hyp_dim -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, 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_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,r,hyp%dims) +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 -r = r + 1 -hyp%dims(r) = n_hyp_dim -chunk_dims(r) = 1 +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(1:r), hyp%dspace, hdferr) +call h5screate_simple_f(rnk, hyp%dims(1:rnk), hyp%dspace, 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, r, chunk_dims, hdferr) ! Set chunking +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) ! 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) +call h5dcreate_f(file_or_group_id, trim(dataset_name), hyp%dset_type, hyp%dspace, hyp%dset, hdferr, hyp%plist) if (present(fdtype_name)) call write_attributeHDF5(id_idx,dataset_name,"fortran_type",fdtype_name,write_to_group,hyp%dset) @@ -370,31 +375,32 @@ end subroutine init_hyperspliced_array subroutine open_hyperspliced_array(id_idx,dataset_name,variable,read_group,hyp_id_idx,fdtype_name) +! Open the hypersplice/hyperslab array for reading -integer, intent(in) :: id_idx -character(len=*), intent(in) :: dataset_name -class(*), intent(in), dimension(..) :: variable -logical, intent(in) :: read_group -integer, intent(in) :: hyp_id_idx -character(len=*), optional, intent(inout) :: 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/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 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, 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_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,r,hyp%dims) +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.) @@ -408,8 +414,8 @@ end subroutine open_hyperspliced_array subroutine close_entire_hyperspliced_dataset(id_idx,hyp_id_idx,in_group) -integer, intent(in) :: id_idx, hyp_id_idx -logical, intent(in) :: 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 @@ -426,9 +432,10 @@ end subroutine close_entire_hyperspliced_dataset subroutine config_hyper_dims(rnk,nslice,hyp_idx,dims,offset) -integer, intent(inout) :: rnk -integer, intent(in) :: nslice, hyp_idx -integer(HSIZE_T), intent(inout) :: dims(7), offset(7) + +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 @@ -438,24 +445,20 @@ subroutine config_hyper_dims(rnk,nslice,hyp_idx,dims,offset) else if (nslice >= 1) then !hyperslab dims(rnk) = nslice offset(rnk) = hyp_idx - 1 -else !dummy write +else !dummy write dims(:) = 0 end if end subroutine config_hyper_dims -!-----------------------------! -! Writing Subroutines ! -!-----------------------------! - - 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 -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 +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) @@ -502,87 +505,54 @@ subroutine write_attributeHDF5(id_idx,dataset_name,attr_name,attr,write_group,ds end subroutine -subroutine read_attributeHDF5(id_idx,dataset_name,attr_name,attr,read_group,dset_id) - -integer, intent(in) :: id_idx -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(HID_T) :: dset_id_, file_or_group_id, type_id, attr_id -integer(HSIZE_T) :: dims(7) -integer :: hdferr, rnk -logical :: custom_type,using_mpi - -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(.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) - -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 - -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_idx,dataset_name,variable_to_write,write_to_group,fdtype_name) +! Write the variable_to_write to h5 dataset -integer, intent(in) :: id_idx -character(len=*), intent(in) :: dataset_name -class(*), intent(in), dimension(..) :: variable_to_write -logical, intent(in) :: write_to_group -character(len=*), optional, intent(in) :: fdtype_name +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) :: file_or_group_id -integer :: hdferr, i, r +integer :: hdferr, rnk logical custom_type,using_mpi type(h5_ids_t) :: h5_ids -r = rank(variable_to_write) +rnk = rank(variable_to_write) -if ((r < 0) .or. (r > 7)) then +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 -call get_var_dims(variable_to_write,r,h5_ids%dims) +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(r, h5_ids%dims(1:r), h5_ids%dspace, hdferr) +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) -call h5dataset_write(variable_to_write, h5_ids%dims, h5_ids%dset_type, h5_ids%dset, h5_ids%dxpl) + +select rank(variable_to_write) +rank(0) +#include "modhdf5_write.inc" +rank(1) +#include "modhdf5_write.inc" +rank(2) +#include "modhdf5_write.inc" +rank(3) +#include "modhdf5_write.inc" +rank(4) +#include "modhdf5_write.inc" +rank(5) +#include "modhdf5_write.inc" +rank(6) +#include "modhdf5_write.inc" +rank(7) +#include "modhdf5_write.inc" +end select if (present(fdtype_name)) call write_attributeHDF5(id_idx,dataset_name,"fortran_type",fdtype_name,write_to_group,h5_ids%dset) @@ -595,33 +565,33 @@ end subroutine writeHDF5 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_idx !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_idx -integer, intent(in) :: hyp_idx -integer, intent(in) :: nslice !>= 1 to write slice/slab, else do not write data +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) :: file_or_group_id, dset_type, memspace_id -integer :: hdferr, i, r +integer :: hdferr, rnk integer(HSIZE_T) :: dims(7), offset(7) 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,r,dims) +call get_var_dims(variable_to_write,rnk,dims) call get_array_dset_type(variable_to_write,dset_type,custom_type) -call config_hyper_dims(r,nslice,hyp_idx,dims,offset) +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) @@ -630,9 +600,27 @@ subroutine writeHDF5_hypersplice(id_idx,dataset_name,variable_to_write,write_to_ stop "Stopping." end if -call h5sselect_hyperslab_f(hyp%dspace, 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, hyp%dset, hyp%dxpl, memspace_id, hyp%dspace) +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 call h5sclose_f(memspace_id, hdferr) @@ -640,38 +628,105 @@ subroutine writeHDF5_hypersplice(id_idx,dataset_name,variable_to_write,write_to_ end subroutine writeHDF5_hypersplice -!-----------------------------! -! Reading Subroutines ! -!-----------------------------! +subroutine read_attributeHDF5(id_idx,dataset_name,attr_name,attr,read_group,dset_id) +! Read the attribute from 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(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, type_id, attr_id +integer(HSIZE_T) :: dims(7) +integer :: hdferr, rnk +logical :: custom_type,using_mpi + +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(.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) + +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 + +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 readHDF5(id_idx,dataset_name,variable_to_read,read_group,fdtype_name) +! Read the variable_to_read from h5 dataset -integer, intent(in) :: id_idx -character(len=*), intent(in) :: dataset_name -class(*), intent(inout), dimension(..) :: variable_to_read -logical, intent(in) :: read_group -character(len=*), optional, intent(inout) :: fdtype_name +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) :: file_or_group_id -integer :: hdferr, i, r +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) .or. (r > 7)) then +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_file_or_group_id(read_group,id_idx,file_or_group_id,using_mpi) -call get_var_dims(variable_to_read,r,h5_ids%dims) +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.) call h5dopen_f(file_or_group_id, trim(dataset_name), h5_ids%dset, hdferr) -call h5dataset_read(variable_to_read, h5_ids%dims, h5_ids%dset_type, h5_ids%dset, h5_ids%dxpl) + +select rank(variable_to_read) +rank(0) +#include "modhdf5_read.inc" +rank(1) +#include "modhdf5_read.inc" +rank(2) +#include "modhdf5_read.inc" +rank(3) +#include "modhdf5_read.inc" +rank(4) +#include "modhdf5_read.inc" +rank(5) +#include "modhdf5_read.inc" +rank(6) +#include "modhdf5_read.inc" +rank(7) +#include "modhdf5_read.inc" +end select if (present(fdtype_name)) call read_attributeHDF5(id_idx,dataset_name,"fortran_type",fdtype_name,read_group,h5_ids%dset) @@ -683,108 +738,61 @@ 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 -integer, intent(in) :: id_idx -character(len=*), intent(in) :: dataset_name -class(*), intent(inout), dimension(..) :: variable_to_read -logical, intent(in) :: read_group -integer, intent(in) :: hyp_id_idx -integer, intent(in) :: hyp_idx -integer, intent(in) :: nslice !>= 1 to write slice/slab, else do not write data +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, i, r +integer :: hdferr, rnk integer(HSIZE_T) :: dims(7), offset(7) logical custom_type,using_mpi type(h5_ids_t), pointer :: hyp -r = rank(variable_to_read) +rnk = rank(variable_to_read) -if ((r < 0) .and. (r > 6)) then +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,r,dims) +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(r,nslice,hyp_idx,dims,offset) +call config_hyper_dims(rnk,nslice,hyp_idx,dims,offset) -call h5sselect_hyperslab_f(hyp%dspace, 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, hyp%dset, hyp%dxpl, memspace_id, hyp%dspace) +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) -if (custom_type) call h5tclose_f(dset_type, hdferr) !close custom dset_type -call h5sclose_f(memspace_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_hyper.inc" rank(1) -#include "modhdf5_write.inc" +#include "modhdf5_read_hyper.inc" rank(2) -#include "modhdf5_write.inc" +#include "modhdf5_read_hyper.inc" rank(3) -#include "modhdf5_write.inc" +#include "modhdf5_read_hyper.inc" rank(4) -#include "modhdf5_write.inc" +#include "modhdf5_read_hyper.inc" rank(5) -#include "modhdf5_write.inc" +#include "modhdf5_read_hyper.inc" rank(6) -#include "modhdf5_write.inc" +#include "modhdf5_read_hyper.inc" rank(7) -#include "modhdf5_write.inc" +#include "modhdf5_read_hyper.inc" end select -end subroutine h5dataset_write - - -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 :: hdferr, rnk - -select rank(variable) -rank(0) -#include "modhdf5_read.inc" -rank(1) -#include "modhdf5_read.inc" -rank(2) -#include "modhdf5_read.inc" -rank(3) -#include "modhdf5_read.inc" -rank(4) -#include "modhdf5_read.inc" -rank(5) -#include "modhdf5_read.inc" -rank(6) -#include "modhdf5_read.inc" -rank(7) -#include "modhdf5_read.inc" -end select +if (custom_type) call h5tclose_f(dset_type, hdferr) !close custom dset_type +call h5sclose_f(memspace_id, hdferr) -end subroutine h5dataset_read +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 773b45d..c80b048 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -32,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.") @@ -42,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