DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
HDF5Interface.h
1// Copyright (C) 2012 Chris N. Richardson and Garth N. Wells
2//
3// This file is part of DOLFINx (https://www.fenicsproject.org)
4//
5// SPDX-License-Identifier: LGPL-3.0-or-later
6
7#pragma once
8
9#include <array>
10#include <cassert>
11#include <chrono>
12#include <cstdint>
13#include <dolfinx/common/log.h>
14#include <filesystem>
15#include <hdf5.h>
16#include <mpi.h>
17#include <numeric>
18#include <string>
19#include <string_view>
20#include <vector>
21
22namespace dolfinx::io::hdf5
23{
25template <typename T>
26hid_t hdf5_type()
27{
28 if constexpr (std::is_same_v<T, float>)
29 return H5T_NATIVE_FLOAT;
30 else if constexpr (std::is_same_v<T, double>)
31 return H5T_NATIVE_DOUBLE;
32 else if constexpr (std::is_same_v<T, std::int32_t>)
33 return H5T_NATIVE_INT32;
34 else if constexpr (std::is_same_v<T, std::uint32_t>)
35 return H5T_NATIVE_UINT32;
36 else if constexpr (std::is_same_v<T, std::int64_t>)
37 return H5T_NATIVE_INT64;
38 else if constexpr (std::is_same_v<T, std::uint64_t>)
39 return H5T_NATIVE_UINT64;
40 else if constexpr (std::is_same_v<T, std::uint8_t>)
41 return H5T_NATIVE_UINT8;
42 else if constexpr (std::is_same_v<T, std::size_t>)
43 {
44 throw std::runtime_error(
45 "Cannot determine size of std::size_t. std::size_t is not the same "
46 "size as long or int.");
47 }
48 else
49 {
50 throw std::runtime_error("Cannot get HDF5 primitive data type. No "
51 "specialised function for this data type.");
52 }
53}
54
60hid_t open_file(MPI_Comm comm, const std::filesystem::path& filename,
61 std::string_view mode, bool use_mpi_io);
62
65void close_file(hid_t handle);
66
69void flush_file(hid_t handle);
70
74std::filesystem::path get_filename(hid_t handle);
75
80bool has_dataset(hid_t handle, std::string_view dataset_path);
81
87void set_attribute(hid_t handle, std::string_view attr_name,
88 std::string_view value);
89void set_attribute(hid_t handle, std::string_view attr_name,
90 const std::vector<std::int32_t>& value);
91void set_attribute(hid_t handle, std::string_view attr_name,
92 std::int32_t value);
93
98hid_t open_dataset(hid_t handle, std::string_view path);
99
104std::vector<std::int64_t> get_dataset_shape(hid_t handle,
105 std::string_view dataset_path);
106
116void set_mpi_atomicity(hid_t handle, bool atomic);
117
122bool get_mpi_atomicity(hid_t handle);
123
127void add_group(hid_t handle, std::string_view dataset_path);
128
143template <typename T>
144void write_dataset(hid_t file_handle, std::string_view dataset_path,
145 const T* data, std::array<std::int64_t, 2> range,
146 const std::vector<int64_t>& global_size, bool use_mpi_io,
147 bool use_chunking)
148{
149 // Check that group exists and recursively create if required
150 const std::string group_name(dataset_path, 0, dataset_path.rfind('/'));
151 add_group(file_handle, group_name);
152
153 // Null-terminated copy for C API calls
154 const std::string path(dataset_path);
155
156 // Data rank
157 const int rank = global_size.size();
158 assert(rank != 0);
159 if (rank > 2)
160 {
161 throw std::runtime_error("Cannot write dataset to HDF5 file"
162 "Only rank 1 and rank 2 dataset are supported");
163 }
164
165 // Get HDF5 data type
166 const hid_t h5type = hdf5::hdf5_type<T>();
167
168 // Hyperslab selection parameters
169 std::vector<hsize_t> count(global_size.begin(), global_size.end());
170 count[0] = range[1] - range[0];
171
172 // Data offsets
173 std::vector<hsize_t> offset(rank, 0);
174 offset[0] = range[0];
175
176 // Dataset dimensions
177 const std::vector<hsize_t> dimsf(global_size.begin(), global_size.end());
178
179 // Writing into an existing dataset
180 if (has_dataset(file_handle, dataset_path))
181 {
182 // Resize to new global size
183 hid_t dset_id = hdf5::open_dataset(file_handle, dataset_path);
184 H5Dset_extent(dset_id, dimsf.data());
185 H5Dclose(dset_id);
186 }
187 else
188 {
189 std::vector<hsize_t> maxdims(dimsf.begin(), dimsf.end());
190
191 // Set chunking parameters
192 hid_t chunking_properties;
193 if (use_chunking)
194 {
195 // Make array extensible, if chunking is set
196 std::fill(maxdims.begin(), maxdims.end(), H5S_UNLIMITED);
197
198 // Set chunk size and limit to 1kB min/1MB max
199 hsize_t chunk_size = dimsf[0] / 2;
200 if (chunk_size > 1048576)
201 chunk_size = 1048576;
202 else if (chunk_size < 1024)
203 chunk_size = 1024;
204
205 std::array chunk_dims{chunk_size, rank == 2 ? dimsf[1] : hsize_t(0)};
206 chunking_properties = H5Pcreate(H5P_DATASET_CREATE);
207 H5Pset_chunk(chunking_properties, rank, chunk_dims.data());
208 }
209 else
210 chunking_properties = H5P_DEFAULT;
211
212 // Create a global data space
213 const hid_t filespace0
214 = H5Screate_simple(rank, dimsf.data(), maxdims.data());
215 if (filespace0 == H5I_INVALID_HID)
216 throw std::runtime_error("Failed to create HDF5 data space");
217
218 // Create global dataset (using dataset_path)
219 const hid_t dset_id
220 = H5Dcreate2(file_handle, path.c_str(), h5type, filespace0, H5P_DEFAULT,
221 chunking_properties, H5P_DEFAULT);
222 if (dset_id == H5I_INVALID_HID)
223 throw std::runtime_error("Failed to create HDF5 global dataset.");
224
225 // Close global data space
226 if (H5Sclose(filespace0) < 0)
227 throw std::runtime_error("Failed to close HDF5 global data space.");
228
229 if (use_chunking)
230 {
231 // Close chunking properties
232 if (H5Pclose(chunking_properties) < 0)
233 throw std::runtime_error("Failed to close HDF5 chunking properties.");
234 }
235
236 // Close dataset collectively
237 if (H5Dclose(dset_id) < 0)
238 throw std::runtime_error("Failed to close HDF5 dataset.");
239 }
240
241 // Reopen dataset and write data
242 hid_t dset_id = hdf5::open_dataset(file_handle, dataset_path);
243 assert(dset_id != H5I_INVALID_HID);
244
245 hid_t dataspace = H5Dget_space(dset_id);
246 if (dataspace == H5I_INVALID_HID)
247 throw std::runtime_error("Failed to open HDF5 data space.");
248
249 herr_t status = H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, offset.data(),
250 nullptr, count.data(), nullptr);
251 if (status < 0)
252 throw std::runtime_error("Failed to create HDF5 dataspace.");
253
254 // Set parallel access
255 const hid_t plist_id = H5Pcreate(H5P_DATASET_XFER);
256 if (use_mpi_io)
257 {
258 if (herr_t status = H5Pset_dxpl_mpio(plist_id, H5FD_MPIO_COLLECTIVE);
259 status < 0)
260 {
261 throw std::runtime_error(
262 "Failed to set HDF5 data transfer property list.");
263 }
264 }
265
266 // Create a local data space
267 const hid_t memspace = H5Screate_simple(rank, count.data(), nullptr);
268 if (memspace == H5I_INVALID_HID)
269 throw std::runtime_error("Failed to create HDF5 local data space.");
270
271 // Write local dataset
272 if (H5Dwrite(dset_id, h5type, memspace, dataspace, plist_id, data) < 0)
273 {
274 throw std::runtime_error("Failed to write HDF5 local dataset.");
275 }
276
277 H5Sclose(memspace);
278 H5Sclose(dataspace);
279
280 H5Dclose(dset_id);
281 // Release file-access template
282 if (H5Pclose(plist_id) < 0)
283 throw std::runtime_error("Failed to release HDF5 file-access template.");
284}
285
295template <typename T>
296std::vector<T> read_dataset(hid_t dset_id, std::array<std::int64_t, 2> range,
297 bool allow_cast)
298{
299 auto timer_start = std::chrono::system_clock::now();
300
301 if (!allow_cast)
302 {
303 // Check that HDF5 dataset type and the type T are the same
304
305 hid_t dtype = H5Dget_type(dset_id);
306 if (dtype == H5I_INVALID_HID)
307 throw std::runtime_error("Failed to get HDF5 data type.");
308 if (htri_t eq = H5Tequal(dtype, hdf5::hdf5_type<T>()); eq < 0)
309 throw std::runtime_error("HDF5 datatype equality test failed.");
310 else if (!eq)
311 {
312 H5Tclose(dtype);
313 throw std::runtime_error("Wrong type for reading from HDF5. Use \"h5ls "
314 "-v\" to inspect the types in the HDF5 file.");
315 }
316 }
317
318 // Open dataspace
319 hid_t dataspace = H5Dget_space(dset_id);
320 if (dataspace == H5I_INVALID_HID)
321 throw std::runtime_error("Failed to open HDF5 data space.");
322
323 // Get rank of data set
324 int rank = H5Sget_simple_extent_ndims(dataspace);
325 if (rank < 1)
326 throw std::runtime_error("Failed to get rank of data space.");
327 else if (rank > 2)
328 spdlog::warn("io::hdf5::read_dataset untested for rank > 2.");
329
330 // Allocate data for shape
331 std::vector<hsize_t> shape(rank);
332
333 // Get size in each dimension
334 if (int ndims = H5Sget_simple_extent_dims(dataspace, shape.data(), nullptr);
335 ndims != rank)
336 {
337 throw std::runtime_error("Failed to get dimensionality of dataspace.");
338 }
339
340 // Hyperslab selection
341 std::vector<hsize_t> offset(rank, 0);
342 std::vector<hsize_t> count = shape;
343 if (range[0] != -1 and range[1] != -1)
344 {
345 offset[0] = range[0];
346 count[0] = range[1] - range[0];
347 }
348 else
349 offset[0] = 0;
350
351 // Select a block in the dataset beginning at offset[], with
352 // size=count[]
353 if (herr_t status
354 = H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, offset.data(), nullptr,
355 count.data(), nullptr);
356 status < 0)
357 {
358 throw std::runtime_error("Failed to select HDF5 hyperslab.");
359 }
360
361 // Create a memory dataspace
362 hid_t memspace = H5Screate_simple(rank, count.data(), nullptr);
363 if (memspace == H5I_INVALID_HID)
364 throw std::runtime_error("Failed to create HDF5 dataspace.");
365
366 // Create local data to read into
367 std::vector<T> data(
368 std::reduce(count.begin(), count.end(), 1, std::multiplies{}));
369
370 // Read data on each process
371 hid_t h5type = hdf5::hdf5_type<T>();
372 if (herr_t status
373 = H5Dread(dset_id, h5type, memspace, dataspace, H5P_DEFAULT, data.data());
374 status < 0)
375 {
376 throw std::runtime_error("Failed to read HDF5 data.");
377 }
378
379 // Close dataspace
380 if (herr_t status = H5Sclose(dataspace); status < 0)
381 throw std::runtime_error("Failed to close HDF5 dataspace.");
382
383 // Close memspace
384 if (herr_t status = H5Sclose(memspace); status < 0)
385 throw std::runtime_error("Failed to close HDF5 memory space.");
386
387 auto timer_end = std::chrono::system_clock::now();
388 std::chrono::duration<double> dt = (timer_end - timer_start);
389 double data_rate = data.size() * sizeof(T) / (1e6 * dt.count());
390 spdlog::info("HDF5 Read data rate: {} MB/s", data_rate);
391
392 return data;
393}
394} // namespace dolfinx::io::hdf5
int rank(MPI_Comm comm)
Return process rank for the communicator.
Definition MPI.cpp:64