Skip to content

Commit 4f204b1

Browse files
committed
datatype option for write
this would avoid implied temporary array casts in user code, if it works test:lt: more robust % write(): add datatype= optional parameter test:cast: check other arrays, print class test: print cast details
1 parent 9bf78a0 commit 4f204b1

File tree

11 files changed

+162
-80
lines changed

11 files changed

+162
-80
lines changed

.github/workflows/oneapi-linux.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ on:
2424
- "!memcheck.cmake"
2525
- "!coverage.cmake"
2626
- "!example/*"
27+
workflow_dispatch:
28+
29+
# avoid wasted runs
30+
concurrency:
31+
group: ${{ github.workflow }}-${{ github.ref }}
32+
cancel-in-progress: true
2733

2834

2935
jobs:
@@ -35,7 +41,7 @@ jobs:
3541
steps:
3642
- uses: actions/checkout@v4
3743

38-
- uses: actions/setup-python@v4
44+
- uses: actions/setup-python@v5
3945
with:
4046
python-version: '3.12'
4147

API.md

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ type(hdf5_file) :: h1, h2, h3
6363
```
6464

6565
```fortran
66-
call h%open(filename, action, comp_lvl)
66+
call h % open(filename, action, comp_lvl)
6767
!! Opens hdf5 file
6868
6969
character(*), intent(in) :: filename
@@ -215,15 +215,50 @@ character(*), intent(in) :: target, & !< target path to link dataset
215215
## file write operations
216216

217217
```fortran
218-
call h%write(dname, A, chunk_size, istart, iend, stride, compact)
218+
call h % write(dname, A, chunk_size, istart, iend, stride, compact, datatype)
219219
!! write 0d..7d dataset
220220
character(*), intent(in) :: dname
221221
class(*), intent(in) :: A(:) !< array to write
222222
integer, intent(in), optional :: chunk_size(rank(A))
223223
integer, intent(in), optional, dimension(:) :: istart, iend, stride !< array slicing
224224
logical, intent(in), optional :: compact !< faster I/O for sub-64 kB datasets
225+
integer, intent(in), optional :: datatype !< HDF5 datatype
225226
```
226227

228+
The "datatype" parameter is optional and defaults to the native type of the input array A.
229+
Rather than implicitly cast with array temporaries in end-user code, which can be slow and trigger memory issues or compiler bugs (like GCC 9.5.0), instead use the "datatype" parameter to specify the HDF5 datatype explicitly.
230+
For example, if the data in memory is real64, but it's desired to write real32 to disk to save disk space, do like:
231+
232+
```fortran
233+
real(real64) :: darr(3,4,5)
234+
type(h5fortran) :: h
235+
236+
call h % open("myfile.h5", action="w")
237+
238+
call h % write("/my_3d_data", darr, datatype=H5T_NATIVE_REAL)
239+
```
240+
241+
The HDF5
242+
[Fortran datatypes](https://support.hdfgroup.org/documentation/hdf5/latest/group___f_h5_t.html)
243+
include the
244+
[following](https://docs.hdfgroup.org/archive/support/HDF5/doc/RM/PredefDTypes.html#F90):
245+
246+
* H5T_NATIVE_REAL: Fortran single-precision real (typically 32-bit float unless using non-default compiler settings)
247+
* H5T_NATIVE_DOUBLE: Fortran double-precision real (typically 64-bit float unless using non-default compiler settings)
248+
* H5T_NATIVE_INTEGER: Fortran integer (typically 32-bit unless using non-default compiler settings)
249+
250+
To be explicit about the datatype, one can use the following HDF5 datatypes:
251+
252+
* H5T_STD_F32LE: real floating (32-bit, little-endian)
253+
* H5T_STD_F64LE: real floating (64-bit, little-endian)
254+
* H5T_STD_I32LE: integer (32-bit, little-endian)
255+
* H5T_STD_I64LE: integer (64-bit, little-endian)
256+
257+
Of course, part of the beauty of HDF5 is the computer reading the HDF5 will automatically coerce the data to the native type of the host machine, so the end-user does not need to worry about this in most cases.
258+
259+
---
260+
261+
227262
Write dataset attribute (e.g. units or instrument)
228263

229264
```fortran

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
cmake_minimum_required(VERSION 3.20...3.30)
1+
cmake_minimum_required(VERSION 3.20...4.0)
22

33
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
44
message(FATAL_ERROR "Please do out of source build like
@@ -7,7 +7,7 @@ endif()
77

88
project(h5fortran
99
LANGUAGES C Fortran
10-
VERSION 4.10.6
10+
VERSION 4.11.0
1111
)
1212

1313
include(CTest)

fpm.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "h5fortran"
22
description = "Lightweight object-oriented HDF5 interface"
33
categories = "io"
4-
version = "4.10.1"
4+
version = "4.11.0"
55

66
[build]
77
auto-tests = false

src/interface.f90

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -257,67 +257,75 @@ module subroutine lt7read(filename, dname, A)
257257

258258
interface !< writer.f90
259259

260-
module subroutine h5write_scalar(self, dname, A, compact)
260+
module subroutine h5write_scalar(self, dname, A, compact, datatype)
261261
class(hdf5_file), intent(in) :: self
262262
character(*), intent(in) :: dname
263263
class(*), intent(in) :: A
264264
logical, intent(in), optional :: compact
265+
integer(HID_T), intent(in), optional :: datatype
265266
end subroutine
266267

267-
module subroutine h5write_1d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims)
268+
module subroutine h5write_1d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims, datatype)
268269
class(hdf5_file), intent(in) :: self
269270
character(*), intent(in) :: dname
270271
class(*), intent(in), dimension(:) :: A
271272
integer, intent(in), dimension(1), optional :: chunk_size, istart, iend, stride, dset_dims
272273
logical, intent(in), optional :: compact
274+
integer(HID_T), intent(in), optional :: datatype
273275
end subroutine
274276

275-
module subroutine h5write_2d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims)
277+
module subroutine h5write_2d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims, datatype)
276278
class(hdf5_file), intent(in) :: self
277279
character(*), intent(in) :: dname
278280
class(*), intent(in), dimension(:,:) :: A
279281
integer, intent(in), dimension(2), optional :: chunk_size, istart, iend, stride, dset_dims
280282
logical, intent(in), optional :: compact
283+
integer(HID_T), intent(in), optional :: datatype
281284
end subroutine
282285

283-
module subroutine h5write_3d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims)
286+
module subroutine h5write_3d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims, datatype)
284287
class(hdf5_file), intent(in) :: self
285288
character(*), intent(in) :: dname
286289
class(*), intent(in), dimension(:,:,:) :: A
287290
integer, intent(in), dimension(3), optional :: chunk_size, istart, iend, stride, dset_dims
288291
logical, intent(in), optional :: compact
292+
integer(HID_T), intent(in), optional :: datatype
289293
end subroutine
290294

291-
module subroutine h5write_4d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims)
295+
module subroutine h5write_4d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims, datatype)
292296
class(hdf5_file), intent(in) :: self
293297
character(*), intent(in) :: dname
294298
class(*), intent(in), dimension(:,:,:,:) :: A
295299
integer, intent(in), dimension(4), optional :: chunk_size, istart, iend, stride, dset_dims
296300
logical, intent(in), optional :: compact
301+
integer(HID_T), intent(in), optional :: datatype
297302
end subroutine
298303

299-
module subroutine h5write_5d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims)
304+
module subroutine h5write_5d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims, datatype)
300305
class(hdf5_file), intent(in) :: self
301306
character(*), intent(in) :: dname
302307
class(*), intent(in), dimension(:,:,:,:,:) :: A
303308
integer, intent(in), dimension(5), optional :: chunk_size, istart, iend, stride, dset_dims
304309
logical, intent(in), optional :: compact
310+
integer(HID_T), intent(in), optional :: datatype
305311
end subroutine
306312

307-
module subroutine h5write_6d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims)
313+
module subroutine h5write_6d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims, datatype)
308314
class(hdf5_file), intent(in) :: self
309315
character(*), intent(in) :: dname
310316
class(*), intent(in), dimension(:,:,:,:,:,:) :: A
311317
integer, intent(in), dimension(6), optional :: chunk_size, istart, iend, stride, dset_dims
312318
logical, intent(in), optional :: compact
319+
integer(HID_T), intent(in), optional :: datatype
313320
end subroutine
314321

315-
module subroutine h5write_7d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims)
322+
module subroutine h5write_7d(self, dname, A, chunk_size, istart, iend, stride, compact, dset_dims, datatype)
316323
class(hdf5_file), intent(in) :: self
317324
character(*), intent(in) :: dname
318325
class(*), intent(in), dimension(:,:,:,:,:,:,:) :: A
319326
integer, intent(in), dimension(7), optional :: chunk_size, istart, iend, stride, dset_dims
320327
logical, intent(in), optional :: compact
328+
integer(HID_T), intent(in), optional :: datatype
321329
end subroutine
322330

323331
end interface

src/utils.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
h5eset_auto_f, &
55
h5iis_valid_f, h5iget_name_f, H5Iget_type_f, &
66
h5open_f, h5close_f, &
7-
h5fopen_f, h5fcreate_f, h5fclose_f, h5fis_hdf5_f, h5fget_filesize_f, &
7+
H5Fopen_f, h5fcreate_f, h5fclose_f, h5fis_hdf5_f, h5fget_filesize_f, &
88
h5fget_obj_count_f, h5fget_obj_ids_f, h5fget_name_f, &
99
h5sselect_hyperslab_f, h5screate_simple_f, &
1010
H5Sget_simple_extent_ndims_f, H5Sget_simple_extent_dims_f, H5Sget_simple_extent_npoints_f, &

src/write_scalar.f90

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,43 +10,51 @@
1010
module procedure h5write_scalar
1111

1212
integer(HSIZE_T) :: dset_dims(0), mem_dims(0)
13-
integer(HID_T) :: file_space_id, dset_id, dtype_id, xfer_id, dtype
13+
integer(HID_T) :: file_space_id, dset_id, dtype_id, xfer_id, dtypew
1414
integer :: ier, charlen
1515

1616
charlen = 0
1717

18+
if (present(datatype)) then
19+
dtypew = datatype
20+
else
21+
select type (A)
22+
type is (real(real32))
23+
dtypew = H5T_NATIVE_REAL
24+
type is (real(real64))
25+
dtypew = H5T_NATIVE_DOUBLE
26+
type is (integer(int32))
27+
dtypew = H5T_NATIVE_INTEGER
28+
type is (integer(int64))
29+
dtypew = H5T_STD_I64LE
30+
type is (character(*))
31+
dtypew = H5T_NATIVE_CHARACTER
32+
class default
33+
error stop "ERROR:h5fortran:write: unknown variable type for " // dname
34+
end select
35+
endif
36+
1837
select type (A)
19-
type is (real(real32))
20-
dtype = H5T_NATIVE_REAL
21-
type is (real(real64))
22-
dtype = H5T_NATIVE_DOUBLE
23-
type is (integer(int32))
24-
dtype = H5T_NATIVE_INTEGER
25-
type is (integer(int64))
26-
dtype = H5T_STD_I64LE
27-
type is (character(*))
28-
dtype = H5T_NATIVE_CHARACTER
29-
charlen = len_trim(A) !< workaround for GCC 8.3.0 bug
30-
if(charlen == 0) charlen = 1 !< empty string is OK but charlen is strictly positive.
31-
class default
32-
error stop "ERROR:h5fortran:write: unknown variable type for " // dname
38+
type is (character(*))
39+
charlen = len_trim(A) !< workaround for GCC 8.3.0 bug
40+
if(charlen == 0) charlen = 1 !< empty string is OK but charlen is strictly positive.
3341
end select
3442

35-
call hdf_create(self, dname, dtype, mem_dims=mem_dims, dset_dims=dset_dims, &
43+
call hdf_create(self, dname, dtypew, mem_dims=mem_dims, dset_dims=dset_dims, &
3644
filespace_id=file_space_id, dset_id=dset_id, compact=compact, dtype_id=dtype_id, &
3745
charlen=charlen)
3846

3947
xfer_id = H5P_DEFAULT_F
4048

4149
select type (A)
4250
type is (real(real32))
43-
call H5Dwrite_f(dset_id, dtype, A, dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id)
51+
call H5Dwrite_f(dset_id, dtypew, A, dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id)
4452
type is (real(real64))
45-
call H5Dwrite_f(dset_id, dtype, A, dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id)
53+
call H5Dwrite_f(dset_id, dtypew, A, dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id)
4654
type is (integer(int32))
47-
call H5Dwrite_f(dset_id, dtype, A, dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id)
55+
call H5Dwrite_f(dset_id, dtypew, A, dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id)
4856
type is (integer(int64))
49-
call H5Dwrite_f(dset_id, dtype, A, dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id)
57+
call H5Dwrite_f(dset_id, dtypew, A, dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id)
5058
type is (character(*))
5159
call H5Dwrite_f(dset_id, dtype_id, trim(A), dset_dims, ier, file_space_id=file_space_id, xfer_prp=xfer_id)
5260
class default

src/writer.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
submodule (h5fortran:write) writer
22

3-
use hdf5, only: h5dwrite_f
3+
use hdf5, only: H5Dwrite_f
44

55
implicit none
66

src/writer.inc

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
integer(HID_T) :: file_space_id, mem_space_id, dset_id, xfer_id, dtype, dtype_id
1+
integer(HID_T) :: file_space_id, mem_space_id, dset_id, xfer_id, dtypew, dtype_id
22
integer(HSIZE_T), dimension(rank(A)) :: mem_dims, dims_dset
33
integer :: ier, charlen
44

@@ -12,24 +12,33 @@ endif
1212

1313
charlen = 0
1414

15+
if (present(datatype)) then
16+
dtypew = datatype
17+
else
18+
select type (A)
19+
type is (real(real32))
20+
dtypew = H5T_NATIVE_REAL
21+
type is (real(real64))
22+
dtypew = H5T_NATIVE_DOUBLE
23+
type is (integer(int32))
24+
dtypew = H5T_NATIVE_INTEGER
25+
type is (integer(int64))
26+
dtypew = H5T_STD_I64LE
27+
type is (character(*))
28+
dtypew = H5T_NATIVE_CHARACTER
29+
class default
30+
error stop "ERROR:h5fortran:writer:unknown variable type for " // dname
31+
end select
32+
endif
33+
!! https://docs.hdfgroup.org/archive/support/HDF5/doc/RM/PredefDTypes.html#F90
34+
1535
select type (A)
16-
type is (real(real32))
17-
dtype = H5T_NATIVE_REAL
18-
type is (real(real64))
19-
dtype = H5T_NATIVE_DOUBLE
20-
type is (integer(int32))
21-
dtype = H5T_NATIVE_INTEGER
22-
type is (integer(int64))
23-
dtype = H5T_STD_I64LE
24-
type is (character(*))
25-
dtype = H5T_NATIVE_CHARACTER
26-
charlen = len(A) !< workaround for GCC 8.3.0 bug
27-
if(charlen == 0) charlen = 1 !< empty string is OK but charlen is strictly positive.
28-
class default
29-
error stop "ERROR:h5fortran:writer:unknown variable type for " // dname
36+
type is (character(*))
37+
charlen = len(A) !< workaround for GCC 8.3.0 bug
38+
if(charlen == 0) charlen = 1 !< empty string is OK but charlen is strictly positive.
3039
end select
3140

32-
call hdf_create(self, dname, dtype, mem_dims, dims_dset, file_space_id, dset_id, &
41+
call hdf_create(self, dname, dtypew, mem_dims, dims_dset, file_space_id, dset_id, &
3342
chunk_size=chunk_size, istart=istart, iend=iend, stride=stride, compact=compact, &
3443
dtype_id=dtype_id, charlen=charlen)
3544

@@ -44,13 +53,13 @@ xfer_id = H5P_DEFAULT_F
4453

4554
select type (A)
4655
type is (real(real32))
47-
call h5dwrite_f(dset_id, dtype, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id)
56+
call h5dwrite_f(dset_id, dtypew, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id)
4857
type is (real(real64))
49-
call h5dwrite_f(dset_id, dtype, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id)
58+
call h5dwrite_f(dset_id, dtypew, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id)
5059
type is (integer(int32))
51-
call h5dwrite_f(dset_id, dtype, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id)
60+
call h5dwrite_f(dset_id, dtypew, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id)
5261
type is (integer(int64))
53-
call h5dwrite_f(dset_id, dtype, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id)
62+
call h5dwrite_f(dset_id, dtypew, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id)
5463
type is (character(*))
5564
call h5dwrite_f(dset_id, dtype_id, A, dims_dset, ier, file_space_id=file_space_id, mem_space_id=mem_space_id, xfer_prp=xfer_id)
5665
class default

test/test_cast.f90

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ subroutine test_cast_write(fn)
2424
character(*), intent(in) :: fn
2525

2626
type(hdf5_file) :: h
27-
2827
real(real64) :: darr(2) = 12._real64
2928

3029
call h%open(fn, action='w')
@@ -37,8 +36,8 @@ subroutine test_cast_write(fn)
3736
call h%write('/1d_real32', [1._real32, 32._real32])
3837
call h%write('/1d_int32', [2_int32, 4_int32])
3938
call h%write('/char', "hello")
40-
call h%write('/cast/r64tor32', real(darr, real32))
41-
call h%write('/cast/i64toi32', int(42_int64, int32))
39+
call h%write('/cast/r64tor32', darr, datatype=H5T_NATIVE_REAL)
40+
call h%write('/cast/i64toi32', 42_int64, datatype=H5T_NATIVE_INTEGER)
4241

4342
call h%close()
4443

@@ -68,8 +67,6 @@ subroutine test_cast_read(fn)
6867
print '(a, i0)', 'H5T_NATIVE_INTEGER = ', H5T_NATIVE_INTEGER
6968
print '(a, i0)', 'H5T_NATIVE_CHARACTER = ', H5T_NATIVE_CHARACTER
7069
print '(a, i0)', 'H5T_STD_I64LE = ', H5T_STD_I64LE
71-
72-
7370
if (h%class("/scalar_int32") /= H5T_INTEGER_F) error stop "int32 not integer"
7471
if (h%class("/scalar_int64") /= H5T_INTEGER_F) error stop "int64 not integer"
7572
if (h%class("/1d_real32") /= H5T_FLOAT_F) error stop "1d_real32 not float"
@@ -84,7 +81,6 @@ subroutine test_cast_read(fn)
8481
write(stderr,*) "expected cast r64tor32 ", H5T_FLOAT_F, " but got ", h%class('/cast/r64tor32')
8582
error stop "cast not float"
8683
endif
87-
8884
print *, "OK: class method"
8985

9086
!> %dtype method

0 commit comments

Comments
 (0)