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