Skip to content

Commit 7d87dd3

Browse files
committed
Implement subroutines to read 1-d and 2-d real32 datasets from HDF5
1 parent e98cb11 commit 7d87dd3

File tree

4 files changed

+108
-32
lines changed

4 files changed

+108
-32
lines changed

src/nf/io/nf_io_hdf5.f90

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,49 @@ module nf_io_hdf5
22

33
!! This module provides convenience functions to read HDF5 files.
44

5+
use iso_fortran_env, only: real32
6+
57
implicit none
68

79
private
8-
public :: get_hdf5_attribute_string
10+
public :: get_hdf5_dataset, hdf5_attribute_string
911

1012
interface
11-
12-
module function get_hdf5_attribute_string( &
13+
module function hdf5_attribute_string( &
1314
filename, object_name, attribute_name) result(res)
15+
!! Read and return an HDF5 variable-length UTF-8 string attribute.
1416
character(*), intent(in) :: filename
1517
!! HDF5 file name
1618
character(*), intent(in) :: object_name
1719
!! Object (group, dataset) name
1820
character(*), intent(in) :: attribute_name
1921
!! Name of the attribute to read
2022
character(:), allocatable :: res
21-
end function get_hdf5_attribute_string
22-
23+
end function hdf5_attribute_string
2324
end interface
2425

26+
interface get_hdf5_dataset
27+
28+
module subroutine get_hdf5_dataset_real32_1d(filename, object_name, values)
29+
!! Read a 1-d real32 array from an HDF5 dataset.
30+
character(*), intent(in) :: filename
31+
!! HDF5 file name
32+
character(*), intent(in) :: object_name
33+
!! Object (dataset) name
34+
real(real32), allocatable, intent(in out) :: values(:)
35+
!! Array to store the dataset values into
36+
end subroutine get_hdf5_dataset_real32_1d
37+
38+
module subroutine get_hdf5_dataset_real32_2d(filename, object_name, values)
39+
!! Read a 2-d real32 array from an HDF5 dataset.
40+
character(*), intent(in) :: filename
41+
!! HDF5 file name
42+
character(*), intent(in) :: object_name
43+
!! Object (dataset) name
44+
real(real32), allocatable, intent(in out) :: values(:,:)
45+
!! Array to store the dataset values into
46+
end subroutine get_hdf5_dataset_real32_2d
47+
48+
end interface get_hdf5_dataset
49+
2550
end module nf_io_hdf5

src/nf/io/nf_io_hdf5_submodule.f90

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
submodule(nf_io_hdf5) nf_io_hdf5_submodule
22

3+
use iso_fortran_env, only: int64, real32, stderr => error_unit
4+
use h5fortran, only: hdf5_file
35
use hdf5, only: H5F_ACC_RDONLY_F, HID_T, &
46
h5aget_type_f, h5aopen_by_name_f, h5aread_f, &
57
h5fclose_f, h5fopen_f
@@ -9,7 +11,7 @@
911

1012
contains
1113

12-
module function get_hdf5_attribute_string( &
14+
module function hdf5_attribute_string( &
1315
filename, object_name, attribute_name) result(res)
1416

1517
character(*), intent(in) :: filename
@@ -41,6 +43,62 @@ module function get_hdf5_attribute_string( &
4143

4244
res = string(:index(string, c_null_char) - 1)
4345

44-
end function get_hdf5_attribute_string
46+
end function hdf5_attribute_string
47+
48+
49+
module subroutine get_hdf5_dataset_real32_1d(filename, object_name, values)
50+
51+
character(*), intent(in) :: filename
52+
character(*), intent(in) :: object_name
53+
real(real32), allocatable, intent(in out) :: values(:)
54+
55+
type(hdf5_file) :: f
56+
integer(int64), allocatable :: dims(:)
57+
58+
call f % open(filename, 'r')
59+
call f % shape(object_name, dims)
60+
61+
! If values is already allocated, re-allocate only if incorrect shape
62+
if (allocated(values)) then
63+
if (.not. all(shape(values) == dims)) then
64+
deallocate(values)
65+
allocate(values(dims(1)))
66+
end if
67+
else
68+
allocate(values(dims(1)))
69+
end if
70+
71+
call f % read(object_name, values)
72+
call f % close()
73+
74+
end subroutine get_hdf5_dataset_real32_1d
75+
76+
77+
module subroutine get_hdf5_dataset_real32_2d(filename, object_name, values)
78+
79+
character(*), intent(in) :: filename
80+
character(*), intent(in) :: object_name
81+
real(real32), allocatable, intent(in out) :: values(:,:)
82+
83+
type(hdf5_file) :: f
84+
integer(int64), allocatable :: dims(:)
85+
86+
call f % open(filename, 'r')
87+
call f % shape(object_name, dims)
88+
89+
! If values is already allocated, re-allocate only if incorrect shape
90+
if (allocated(values)) then
91+
if (.not. all(shape(values) == dims)) then
92+
deallocate(values)
93+
allocate(values(dims(1), dims(2)))
94+
end if
95+
else
96+
allocate(values(dims(1), dims(2)))
97+
end if
98+
99+
call f % read(object_name, values)
100+
call f % close()
101+
102+
end subroutine get_hdf5_dataset_real32_2d
45103

46104
end submodule nf_io_hdf5_submodule

src/nf/nf_keras_submodule.f90

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
submodule(nf_keras) nf_keras_submodule
22

33
use json_module, only: json_core, json_value
4-
use nf_io_hdf5, only: get_hdf5_attribute_string
4+
use nf_io_hdf5, only: hdf5_attribute_string
55

66
implicit none
77

@@ -21,9 +21,7 @@ module function get_keras_h5_layers(filename) result(res)
2121
integer :: n, num_layers, num_elements
2222
logical :: found
2323

24-
model_config_string = get_hdf5_attribute_string( &
25-
filename, '.', 'model_config' &
26-
)
24+
model_config_string = hdf5_attribute_string(filename, '.', 'model_config')
2725

2826
call json % parse(model_config_json, model_config_string)
2927
call json % get(model_config_json, 'config.layers', layers_json)

test/test_io_hdf5.f90

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,52 @@
11
program test_io_hdf5
22

3-
use iso_fortran_env, only: int64, stderr => error_unit
3+
use iso_fortran_env, only: stderr => error_unit
44
use nf_datasets, only: download_and_unpack, keras_model_dense_mnist_url
5-
use nf_io_hdf5, only: get_hdf5_attribute_string
6-
use h5fortran, only: hdf5_file
5+
use nf_io_hdf5, only: hdf5_attribute_string, get_hdf5_dataset
76

87
implicit none
98

109
character(:), allocatable :: attr
1110
character(*), parameter :: test_data_path = 'keras_dense_mnist.h5'
12-
type(hdf5_file) :: f
1311
real, allocatable :: bias(:), weights(:,:)
14-
integer(int64), allocatable :: dims(:)
1512

1613
logical :: file_exists
1714
logical :: ok = .true.
1815

1916
inquire(file=test_data_path, exist=file_exists)
2017
if (.not. file_exists) call download_and_unpack(keras_model_dense_mnist_url)
2118

22-
attr = get_hdf5_attribute_string(test_data_path, '.', 'backend')
19+
attr = hdf5_attribute_string(test_data_path, '.', 'backend')
2320

2421
if (.not. attr == 'tensorflow') then
2522
ok = .false.
2623
write(stderr, '(a)') &
2724
'HDF5 variable length string attribute was read correctly.. failed'
2825
end if
2926

30-
call f % open(test_data_path, 'r')
27+
! Read 1-d real32 dataset
28+
call get_hdf5_dataset( &
29+
test_data_path, &
30+
'/model_weights/dense/dense/bias:0', &
31+
bias &
32+
)
3133

32-
call f % shape('/model_weights/dense/dense/bias:0', dims)
33-
allocate(bias(dims(1)))
34-
bias = 0
35-
call f % read('/model_weights/dense/dense/bias:0', bias)
34+
! Read 2-d real32 dataset
35+
call get_hdf5_dataset(test_data_path, &
36+
'/model_weights/dense/dense/kernel:0', &
37+
weights &
38+
)
3639

37-
if (.not. all(dims == [30])) then
40+
if (.not. all(shape(bias) == [30])) then
3841
ok = .false.
3942
write(stderr, '(a)') 'HDF5 1-d dataset dims inquiry is correct.. failed'
4043
end if
4144

42-
call f % shape('/model_weights/dense/dense/kernel:0', dims)
43-
allocate(weights(dims(1), dims(2)))
44-
weights = 0
45-
call f % read('/model_weights/dense/dense/kernel:0', weights)
46-
47-
if (.not. all(dims == [30, 784])) then
45+
if (.not. all(shape(weights) == [30, 784])) then
4846
ok = .false.
49-
print *, dims
5047
write(stderr, '(a)') 'HDF5 2-d dataset dims inquiry is correct.. failed'
5148
end if
5249

53-
call f % close()
54-
5550
if (ok) then
5651
print '(a)', 'test_io_hdf5: All tests passed.'
5752
else

0 commit comments

Comments
 (0)