BELFEM 0.9.0
Berkeley Lab Finite Element Framework
Loading...
Searching...
No Matches
cl_HDF5_Dataset.hpp
Go to the documentation of this file.
1/*
2 * BELFEM -- The Berkeley Lab Finite Element Framework
3 * Copyright (c) 2026, The Regents of the University of California, through
4 * Lawrence Berkeley National Laboratory (subject to receipt of any required
5 * approvals from the U.S. Dept. of Energy). All rights reserved.
6 *
7 * Developers: Christian Messe, Gregory Giard
8 *
9 * See the top-level LICENSE file for the complete license and disclaimer.
10 */
11
12#ifndef BELFEM_CL_HDF5_DATASET_HPP
13#define BELFEM_CL_HDF5_DATASET_HPP
14
15#include "assert.hpp"
16#include "hdf5_tools.hpp"
17
18namespace belfem
19{
20 namespace hdf5
21 {
22
23#ifdef BELFEM_HDF5
24 template< typename T >
25 class Dataset
26 {
27 const hid_t mType ;
28 const bool mWriteMode ;
29 const string mLabel ;
30 hsize_t mSize;
31
32 hid_t mSpace ;
33 hid_t mDataset ;
34 hvl_t * mData = nullptr;
35 public:
36
37 // aForceWrite selects the write branch even when aSize == 0, so an
38 // empty collection ( zero faces/facets ) can still be written. It is
39 // additive: the default ( false ) preserves the size-based inference,
40 // so Dataset( file, label ) still reads and Dataset( file, label, N )
41 // still writes exactly as before.
42 Dataset( hid_t aFile, const string & aLabel, const hsize_t aSize=0,
43 const bool aForceWrite=false ) :
44
45 mType( H5Tvlen_create( hdf5::datatype<T>() ) ),
46 mWriteMode( aForceWrite || aSize > 0 ),
47 mLabel( aLabel ),
48 mSize( aSize )
49 {
50 if ( ! mWriteMode )
51 {
52 // read mode: open the existing dataset and read it.
53 // Every error path below closes the handles already opened
54 // before raising: a throwing constructor does not run
55 // ~Dataset(), so partially-built state must be torn down by
56 // hand (debug-only — a release build aborts instead of throwing, see assert.hpp).
57 if ( ! hdf5::dataset_exists( aFile, aLabel ) )
58 {
59 H5Tclose( mType );
60 BELFEM_ERROR( false, "Dataset %s does not exist", aLabel.c_str() );
61 }
62
63 mDataset = H5Dopen1( aFile, aLabel.c_str() );
64
65 // guard: the on-disk type must be a vlen of base type T. This
66 // is an I/O-integrity check ( a wrong/corrupt on-disk type
67 // would otherwise be read as silent garbage ), so it stays
68 // active in release as a BELFEM_ERROR. The cost is O(1) type
69 // metadata per open, negligible against the H5Dread itself.
70 //
71 // HDF5 stores standardized file types ( e.g. STD_I32LE ), so an
72 // H5Tequal against the native type would yield false negatives
73 // on every valid read. Compare the vlen base by class + size
74 // ( + sign for integers ) instead, which catches the real
75 // hazards ( 4- vs 8-byte, int-vs-float, signed-vs-unsigned )
76 // without tripping on representation.
77 {
78 hid_t tFileType = H5Dget_type( mDataset );
79 const H5T_class_t tOuterClass = H5Tget_class( tFileType );
80
81 hid_t tWantBase = hdf5::datatype< T >();
82 const H5T_class_t tWantClass = H5Tget_class( tWantBase );
83 const size_t tWantBytes = H5Tget_size( tWantBase );
84 const H5T_sign_t tWantSign = ( tWantClass == H5T_INTEGER )
85 ? H5Tget_sign( tWantBase ) : H5T_SGN_NONE ;
86
87 // base comparands are only meaningful for a vlen type; if the
88 // on-disk type is not vlen, leave them so the outer check ( which
89 // runs first ) is what reports the mismatch
90 H5T_class_t tBaseClass = H5T_NO_CLASS ;
91 size_t tBaseBytes = 0 ;
92 H5T_sign_t tBaseSign = H5T_SGN_NONE ;
93
94 if ( tOuterClass == H5T_VLEN )
95 {
96 hid_t tFileBase = H5Tget_super( tFileType ); // vlen element type
97 tBaseClass = H5Tget_class( tFileBase );
98 tBaseBytes = H5Tget_size( tFileBase );
99 tBaseSign = ( tWantClass == H5T_INTEGER )
100 ? H5Tget_sign( tFileBase ) : H5T_SGN_NONE ;
101 H5Tclose( tFileBase );
102 }
103
104 // close before checking so the throw path ( debug ) leaks nothing
105 H5Tclose( tFileType );
106
107 // on a type mismatch, close the dataset + vlen type before raising
108 auto tFail = [ & ] () { H5Dclose( mDataset ); H5Tclose( mType ); };
109
110 if ( tOuterClass != H5T_VLEN )
111 {
112 tFail();
113 BELFEM_ERROR( false,
114 "Dataset %s is not a variable-length array on disk", aLabel.c_str() );
115 }
116 if ( tBaseClass != tWantClass )
117 {
118 tFail();
119 BELFEM_ERROR( false,
120 "On-disk element type of dataset %s does not match the requested type", aLabel.c_str() );
121 }
122 if ( tBaseBytes != tWantBytes )
123 {
124 tFail();
125 BELFEM_ERROR( false,
126 "On-disk element size (%lu) of dataset %s does not match requested size (%lu)",
127 ( long unsigned int ) tBaseBytes, aLabel.c_str(), ( long unsigned int ) tWantBytes );
128 }
129 if ( tBaseSign != tWantSign )
130 {
131 tFail();
132 BELFEM_ERROR( false,
133 "On-disk element signedness of dataset %s does not match the requested type", aLabel.c_str() );
134 }
135 }
136
137 mSpace = H5Dget_space( mDataset );
138
139 // number of rows = number of points in the dataspace
140 hssize_t tNumPoints = H5Sget_simple_extent_npoints( mSpace );
141 if ( tNumPoints < 0 )
142 {
143 H5Sclose( mSpace );
144 H5Dclose( mDataset );
145 H5Tclose( mType );
146 BELFEM_ERROR( false, "Failed to read extent of dataset %s", aLabel.c_str() );
147 }
148 mSize = static_cast< hsize_t >( tNumPoints );
149
150 // value-initialize so each row starts as { len=0, p=null }
151 mData = new hvl_t[ mSize ]() ;
152
153 herr_t tStatus = H5Dread( mDataset, mType, H5S_ALL, H5S_ALL, H5P_DEFAULT, mData );
154 if ( tStatus != 0 )
155 {
156 // failed read: drop the row array. A partial read leaves
157 // indeterminate per-row pointers, so do not vlen-reclaim.
158 delete[] mData;
159 mData = nullptr ;
160 H5Sclose( mSpace );
161 H5Dclose( mDataset );
162 H5Tclose( mType );
163 BELFEM_ERROR( false, "Failed to read dataset %s", aLabel.c_str() );
164 }
165 }
166 else
167 {
168 // write mode: create a new dataset with aSize rows
169 mSpace = H5Screate_simple( 1, &aSize, nullptr );
170 mDataset = H5Dcreate2(
171 aFile,
172 aLabel.c_str(),
173 mType, mSpace,
174 H5P_DEFAULT,
175 H5P_DEFAULT,
176 H5P_DEFAULT );
177
178 // value-initialize so unallocated rows are { len=0, p=null }
179 mData = new hvl_t[ aSize ]() ;
180 }
181 }
182
183 // owns raw buffers + HDF5 handles — non-copyable / non-movable
184 Dataset( const Dataset & ) = delete ;
185 Dataset & operator=( const Dataset & ) = delete ;
186 Dataset( Dataset && ) = delete ;
187 Dataset & operator=( Dataset && ) = delete ;
188
189 T *
190 set_size( const hsize_t aIndex, const hsize_t aMemory )
191 {
192 BELFEM_ASSERT( mWriteMode, "set_size() called on read-mode dataset %s", mLabel.c_str() );
193
194 BELFEM_ASSERT( aIndex < mSize , "Index %lu out of range (must be < %lu)",
195 ( long unsigned int ) aIndex,
196 ( long unsigned int ) mSize );
197
198 BELFEM_ASSERT( mData[ aIndex ].len == 0 , "Index %lu of dataset %s already allocated",
199 ( long unsigned int ) aIndex, mLabel.c_str() );
200
201 mData[ aIndex ].p = new T[ aMemory ];
202 mData[ aIndex ].len = aMemory;
203 return static_cast< T * >( mData[ aIndex ].p );
204 }
205 T *
206 operator[]( const hsize_t aIndex )
207 {
208 BELFEM_ASSERT( aIndex < mSize , "Index %lu out of range (must be < %lu)",
209 ( long unsigned int ) aIndex,
210 ( long unsigned int ) mSize );
211 return static_cast< T * >( mData[ aIndex ].p );
212 }
213
214 const T *
215 operator[]( const hsize_t aIndex ) const
216 {
217 BELFEM_ASSERT( aIndex < mSize , "Index %lu out of range (must be < %lu)",
218 ( long unsigned int ) aIndex,
219 ( long unsigned int ) mSize );
220 return static_cast< const T * >( mData[ aIndex ].p );
221 }
222
223
224 // number of T entries stored in row aIndex
225 hsize_t
226 length( const hsize_t aIndex ) const
227 {
228 BELFEM_ASSERT( aIndex < mSize , "Index %lu out of range (must be < %lu)",
229 ( long unsigned int ) aIndex,
230 ( long unsigned int ) mSize );
231 return mData[ aIndex ].len ;
232 }
233
234 void
235 close()
236 {
237 if ( mData == nullptr ) return ;
238
239 if ( mWriteMode )
240 {
241 // we own the per-row buffers ( new T[] in set_size() )
242 for ( hsize_t k=0; k<mSize; ++k )
243 {
244 delete[] static_cast< T * >( mData[ k ].p );
245 }
246 }
247 else
248 {
249 // per-row buffers were allocated by HDF5 during H5Dread
250 H5Dvlen_reclaim( mType, mSpace, H5P_DEFAULT, mData );
251 }
252
253 delete[] mData;
254 mData = nullptr ;
255 mSize = 0 ;
256
257 H5Dclose( mDataset );
258 H5Tclose( mType );
259 H5Sclose( mSpace );
260 }
261 hsize_t
262 size() const
263 {
264 return mSize;
265 }
266
267 ~Dataset()
268 {
269 this->close();
270 }
271
272 void
273 save()
274 {
275 BELFEM_ASSERT( mWriteMode, "dataset %s not in write mode", mLabel.c_str() );
276
277 herr_t tStatus = H5Dwrite( mDataset, mType, H5S_ALL, H5S_ALL, H5P_DEFAULT, mData );
278
279 BELFEM_ERROR( tStatus == 0 , "Failed to write dataset %s", mLabel.c_str() );
280
281 this->close();
282 }
283 };
284#endif // BELFEM_HDF5
285 }
286}
287#endif //BELFEM_CL_HDF5_DATASET_HPP
#define BELFEM_ERROR(aCheck,...)
Definition assert.hpp:264
#define BELFEM_ASSERT(aCheck,...)
Definition assert.hpp:244
void hvl_t
Definition cl_Mesh_BfmFile.hpp:18
Definition cl_HDF5_Dataset.hpp:21
hid_t datatype()
Definition hdf5_types.hpp:36
bool dataset_exists(hid_t aLoc, const std::string &aLabel)
test if a dataset exists ( tests for a link of that name; the link type is not checked )
Definition hdf5_tools.hpp:37
USER GUIDES:
Definition cl_Capacitor.cpp:16
int hsize_t
Definition hdf5_types.hpp:22
int hid_t
Definition hdf5_types.hpp:20
int herr_t
Definition hdf5_types.hpp:21