Skip to content

Commit 0bbcae8

Browse files
committed
introspect layout (contig,chunk)
doc
1 parent 79f7dc3 commit 0bbcae8

File tree

4 files changed

+116
-33
lines changed

4 files changed

+116
-33
lines changed

README.md

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,6 @@ Tested on systems with HDF5 1.8 and 1.10 including:
2929

3030
Currently, Cygwin does not have *Fortran* HDF5 libraries.
3131

32-
## Not yet handled
33-
34-
It's possible to do these things, if there is user need.
35-
36-
* arrays of rank > 7: this has been stubbed in reader_nd.f90, writer_nd.f90. Only the latest compilers support Fortran 2008 arrays up to rank 15.
37-
* complex64/complex128: this is not natively handled in HDF5. Popular approaches to complex numbers in HDF5 include h5py's using an HDF5 compound datatype.
38-
* non-default character kind
39-
4032
## Build
4133

4234
Requirements:
@@ -133,7 +125,7 @@ type(hdf5_file) :: h5f
133125
* gzip compression may be applied for rank ≥ 2 arrays by setting `comp_lvl` to a value betwen 1 and 9.
134126
Shuffle filter is automatically applied for better compression
135127
* string attributes may be applied to any variable at time of writing or later.
136-
* `chunk_size` option may be set for better compression
128+
* `chunk_size` and `comp_lvl` options must be set to **enable compression**
137129

138130
`integer, intent(out) :: ierr` is a mandatory parameter. It will be non-zero if error detected.
139131
This value should be checked, particularly for write operations to avoid missing error conditions.
@@ -198,6 +190,16 @@ call h5f%read('/foo', A)
198190
call h5f%finalize(ierr)
199191
```
200192

193+
### is dataset contiguous or chunked?
194+
195+
Assumed file handle h5f was already initialized, the logical status is inspected:
196+
197+
```fortran
198+
is_contig = h5f%is_contig('/foo', ierr)
199+
200+
is_chunked = h5f%is_chunked('/foo', ierr)
201+
```
202+
201203
### Create group "scope"
202204

203205
```fortran
@@ -225,3 +227,12 @@ Note the trailing `/` on `/scope/`, that tells the API you are creating a group
225227
* Using compilers like PGI or Flang may require first compiling the HDF5 library yourself.
226228
* Intel compiler HDF5 [compile notes](https://www.hdfgroup.org/downloads/hdf5/source-code/)
227229
* Polymorphic array rank is implemented by explicit code internally. We could have used pointers, but the code is simple enough to avoid the risk associated with explicit array pointers. Also, `select rank` support requires Gfortran-10 or Intel Fortran 2020, so we didn't want to make too-new compiler restriction.
230+
231+
### Missing datatypes
232+
233+
* arrays of rank > 7: this has been stubbed in reader_nd.f90, writer_nd.f90. Only the latest compilers support Fortran 2008 arrays up to rank 15.
234+
235+
The datatypes below are more complex to handle and may see little use due to their downsides.
236+
237+
* complex64/complex128: this is not natively handled in HDF5. There are performance impacts for compound datatypes, thus many choose to just write two datasets, one each for real and imaginary like foo_r and foo_i
238+
* non-default character kind

src/hdf5_interface.f90

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ module h5fortran
2828
contains
2929
!> initialize HDF5 file
3030
procedure, public :: initialize => hdf_initialize, finalize => hdf_finalize, writeattr, &
31-
open => hdf_open_group, close => hdf_close_group, shape => hdf_get_shape
31+
open => hdf_open_group, close => hdf_close_group, shape => hdf_get_shape, layout => hdf_get_layout, &
32+
exist => hdf_check_exist, is_contig => hdf_is_contig, is_chunked => hdf_is_chunked
3233

3334
!> write group or dataset integer/real
3435
generic, public :: write => hdf_write_scalar, hdf_write_1d, hdf_write_2d, hdf_write_3d, &
@@ -143,6 +144,31 @@ module subroutine hdf_get_shape(self, dname, dims, ierr)
143144
integer, intent(out) :: ierr
144145
end subroutine hdf_get_shape
145146

147+
module integer function hdf_get_layout(self, dname, ierr) result(layout)
148+
!! H5D_CONTIGUOUS_F, H5D_CHUNKED_F, H5D_VIRTUAL_F, H5D_COMPACT_F
149+
class(hdf5_file), intent(in) :: self
150+
character(*), intent(in) :: dname
151+
integer, intent(out) :: ierr
152+
end function hdf_get_layout
153+
154+
module logical function hdf_check_exist(self, dname, ierr) result(exists)
155+
class(hdf5_file), intent(in) :: self
156+
character(*), intent(in) :: dname
157+
integer, intent(out) :: ierr
158+
end function hdf_check_exist
159+
160+
module logical function hdf_is_contig(self, dname, ierr)
161+
class(hdf5_file), intent(in) :: self
162+
character(*), intent(in) :: dname
163+
integer, intent(out) :: ierr
164+
end function hdf_is_contig
165+
166+
module logical function hdf_is_chunked(self, dname, ierr)
167+
class(hdf5_file), intent(in) :: self
168+
character(*), intent(in) :: dname
169+
integer, intent(out) :: ierr
170+
end function hdf_is_chunked
171+
146172

147173
module subroutine hdf_read_scalar(self, dname, value, ierr)
148174
class(hdf5_file), intent(in) :: self

src/read.f90

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
!! This submodule is for reading HDF5, via child submodules
22
submodule (h5fortran) read
33

4-
use hdf5, only : h5dopen_f, h5dread_f, h5dclose_f
4+
use hdf5, only : h5dopen_f, h5dread_f, h5dclose_f, h5dget_create_plist_f, &
5+
h5pget_layout_f, H5D_CONTIGUOUS_F, H5D_CHUNKED_F
56
use H5LT, only : h5ltget_dataset_info_f, h5ltread_dataset_f, h5ltget_dataset_ndims_f, &
67
h5ltread_dataset_string_f, h5ltpath_valid_f
78

@@ -11,25 +12,18 @@
1112

1213

1314
module procedure hdf_setup_read
14-
1515
! module subroutine hdf_setup_read(self, dname, dims, ierr)
1616
! class(hdf5_file), intent(in) :: self
1717
! character(*), intent(in) :: dname
1818
! integer(HSIZE_T), intent(out) :: dims(:)
1919
! integer, intent(out) :: ierr
2020
integer(SIZE_T) :: dsize
2121
integer :: dtype
22-
logical :: exists
2322

24-
call h5ltpath_valid_f(self%lid, dname, .true., exists, ierr)
25-
if (.not.exists .or. ierr /= 0) then
26-
write(stderr,*) 'ERROR: ' // dname // ' does not exist in ' // self%filename
27-
ierr = -1
28-
return
29-
endif
23+
if (.not.self%exist(dname, ierr)) return
3024

3125
call h5ltget_dataset_info_f(self%lid, dname, dims, dtype, dsize, ierr)
32-
if (check(ierr, 'ERROR: open ' // dname // ' read ' // self%filename)) return
26+
if (check(ierr, 'ERROR: get_dataset_info ' // dname // ' read ' // self%filename)) return
3327

3428
end procedure hdf_setup_read
3529

@@ -38,14 +32,8 @@
3832
!! must get dims before info, as "dims" must be allocated or segfault occurs.
3933
integer(SIZE_T) :: dsize
4034
integer :: dtype, drank
41-
logical :: exists
4235

43-
call h5ltpath_valid_f(self%lid, dname, .true., exists, ierr)
44-
if (.not.exists .or. ierr /= 0) then
45-
write(stderr,*) 'ERROR: ' // dname // ' does not exist in ' // self%filename
46-
ierr = -1
47-
return
48-
endif
36+
if (.not.self%exist(dname, ierr)) return
4937

5038
call h5ltget_dataset_ndims_f(self%lid, dname, drank, ierr)
5139
if (check(ierr, 'ERROR: '// dname // ' rank ' // self%filename)) return
@@ -57,4 +45,43 @@
5745
end procedure hdf_get_shape
5846

5947

48+
module procedure hdf_get_layout
49+
50+
integer(HID_T) :: pid, did
51+
52+
layout = -1
53+
54+
if (.not.self%exist(dname, ierr)) return
55+
56+
call h5dopen_f(self%lid, dname, did, ierr)
57+
if (check(ierr, 'ERROR: open dataset ' // dname // ' read layout ' // self%filename)) return
58+
call h5dget_create_plist_f(did, pid, ierr)
59+
if (check(ierr, 'ERROR: get property list ID ' // dname // ' read layout ' // self%filename)) return
60+
call h5pget_layout_f(pid, layout, ierr)
61+
if (check(ierr, 'ERROR: get_layout ' // dname // ' read layout ' // self%filename)) return
62+
call h5dclose_f(did, ierr)
63+
if (check(ierr, 'ERROR: close dataset: ' // self%filename)) return
64+
65+
end procedure hdf_get_layout
66+
67+
68+
module procedure hdf_is_contig
69+
hdf_is_contig = self%layout(dname, ierr) == H5D_CONTIGUOUS_F
70+
end procedure hdf_is_contig
71+
72+
module procedure hdf_is_chunked
73+
hdf_is_chunked = self%layout(dname, ierr) == H5D_CHUNKED_F
74+
end procedure hdf_is_chunked
75+
76+
77+
module procedure hdf_check_exist
78+
79+
call h5ltpath_valid_f(self%lid, dname, .true., exists, ierr)
80+
if (.not.exists .or. ierr /= 0) then
81+
write(stderr,*) 'ERROR: ' // dname // ' does not exist in ' // self%filename
82+
ierr = -1
83+
endif
84+
85+
end procedure hdf_check_exist
86+
6087
end submodule read

src/tests/test_deflate.f90

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
use, intrinsic:: iso_fortran_env, only: int64, int32, real32, real64, stderr=>error_unit
33
use, intrinsic:: iso_c_binding, only: c_null_char
44
use h5fortran, only: hdf5_file, toLower, strip_trailing_null, truncate_string_null
5+
use hdf5, only: H5D_CHUNKED_F, H5D_CONTIGUOUS_F
56

67
implicit none
78

@@ -29,7 +30,9 @@ subroutine test_hdf5_deflate(path)
2930
character(*), intent(in) :: path
3031
integer(int64), parameter :: N=1000
3132
integer(int64) :: crat, chunk_size(7)
32-
integer :: fsize, ibig2(N,N) = 0, ibig3(N,N,4) = 0
33+
integer :: fsize, layout
34+
35+
integer :: ibig2(N,N) = 0, ibig3(N,N,4) = 0
3336
real(real32) :: big2(N,N) = 0., big3(N,N,4) = 0.
3437
character(:), allocatable :: fn
3538

@@ -38,19 +41,35 @@ subroutine test_hdf5_deflate(path)
3841

3942
fn = path // '/deflate1.h5'
4043
call h5f%initialize(fn, ierr, status='new', action='rw', comp_lvl=1, chunk_size=chunk_size)
41-
if(ierr/=0) error stop '#1 init'
44+
if(ierr/=0) error stop '#1 write init'
4245
call h5f%write('/big2', big2, ierr, chunk_size=[100,100])
43-
if(ierr/=0) error stop '#1 write'
46+
if(ierr/=0) error stop '#1 write chunked'
47+
call h5f%write('/small_contig', big2(:5,:5), ierr, chunk_size=[-1,-1])
48+
if(ierr/=0) error stop '#1 write contig override'
4449
call h5f%finalize(ierr)
45-
if (ierr /= 0) error stop '#1 finalize'
50+
if (ierr /= 0) error stop '#1 write finalize'
4651

4752
inquire(file=fn, size=fsize)
4853
crat = (N*N*storage_size(big2)/8) / fsize
49-
5054
print '(A,F6.2,A,I6)','filesize (Mbytes): ',fsize/1e6, ' 2D compression ratio:',crat
51-
5255
if (h5f%comp_lvl > 0 .and. crat < 10) error stop '2D low compression'
5356

57+
call h5f%initialize(fn, ierr, status='old', action='r')
58+
if(ierr/=0) error stop '#1 read init'
59+
60+
layout = h5f%layout('/big2', ierr)
61+
if(ierr/=0) error stop '#1 get layout chunk'
62+
if(layout /= H5D_CHUNKED_F) error stop '#1 not chunked layout'
63+
if(.not.h5f%is_chunked('/big2', ierr)) error stop '#1 not chunked layout'
64+
65+
layout = h5f%layout('/small_contig', ierr)
66+
if(ierr/=0) error stop '#1 get layout contig'
67+
if(layout /= H5D_CONTIGUOUS_F) error stop '#1 not contiguous layout'
68+
if(.not.h5f%is_contig('/small_contig', ierr)) error stop '#1 not contig layout'
69+
70+
call h5f%finalize(ierr)
71+
if (ierr /= 0) error stop '#1 read finalize'
72+
5473
!======================================
5574
fn = path // '/deflate2.h5'
5675
call h5f%initialize(fn, ierr, status='new',action='rw',comp_lvl=1)

0 commit comments

Comments
 (0)