| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de> | ||
| 2 | // SPDX-License-Identifier: MIT | ||
| 3 | |||
| 4 | /*! | ||
| 5 | * \file | ||
| 6 | * \ingroup VTK | ||
| 7 | * \brief Common functionality for writing VTK HDF files. | ||
| 8 | */ | ||
| 9 | #ifndef GRIDFORMAT_VTK_HDF_COMMON_HPP_ | ||
| 10 | #define GRIDFORMAT_VTK_HDF_COMMON_HPP_ | ||
| 11 | |||
| 12 | #include <vector> | ||
| 13 | #include <cstddef> | ||
| 14 | #include <numeric> | ||
| 15 | #include <string> | ||
| 16 | #include <optional> | ||
| 17 | #include <ranges> | ||
| 18 | #include <array> | ||
| 19 | |||
| 20 | #include <gridformat/common/hdf5.hpp> | ||
| 21 | #include <gridformat/common/field.hpp> | ||
| 22 | #include <gridformat/common/precision.hpp> | ||
| 23 | #include <gridformat/common/lazy_field.hpp> | ||
| 24 | #include <gridformat/common/range_field.hpp> | ||
| 25 | #include <gridformat/common/field_transformations.hpp> | ||
| 26 | #include <gridformat/common/multi_string.hpp> | ||
| 27 | #include <gridformat/common/string_conversion.hpp> | ||
| 28 | #include <gridformat/common/exceptions.hpp> | ||
| 29 | #include <gridformat/common/logging.hpp> | ||
| 30 | #include <gridformat/grid/concepts.hpp> | ||
| 31 | #include <gridformat/grid/grid.hpp> | ||
| 32 | #include <gridformat/parallel/communication.hpp> | ||
| 33 | #include <gridformat/parallel/helpers.hpp> | ||
| 34 | |||
| 35 | namespace GridFormat { | ||
| 36 | |||
| 37 | namespace VTK { | ||
| 38 | |||
| 39 | //! Options for transient vtk-hdf file formats | ||
| 40 | struct HDFTransientOptions { | ||
| 41 | bool static_grid = false; //!< Set to true the grid is the same for all time steps (will only be written once) | ||
| 42 | bool static_meta_data = true; //!< Set to true if the metadata is same for all time steps (will only be written once) | ||
| 43 | }; | ||
| 44 | |||
| 45 | } // namespace VTK | ||
| 46 | |||
| 47 | |||
| 48 | namespace VTKHDF { | ||
| 49 | |||
| 50 | //! Helper class to store processor offsets in parallel writes | ||
| 51 | struct IOContext { | ||
| 52 | const int my_rank; | ||
| 53 | const int num_ranks; | ||
| 54 | const bool is_parallel; | ||
| 55 | const std::vector<std::size_t> rank_cells; | ||
| 56 | const std::vector<std::size_t> rank_points; | ||
| 57 | const std::size_t num_cells_total; | ||
| 58 | const std::size_t num_points_total; | ||
| 59 | const std::size_t my_cell_offset; | ||
| 60 | const std::size_t my_point_offset; | ||
| 61 | |||
| 62 | 216 | IOContext(int _my_rank, | |
| 63 | int _num_ranks, | ||
| 64 | std::vector<std::size_t> _rank_cells, | ||
| 65 | std::vector<std::size_t> _rank_points) | ||
| 66 | 216 | : my_rank{_my_rank} | |
| 67 | 216 | , num_ranks{_num_ranks} | |
| 68 | 216 | , is_parallel{_num_ranks > 1} | |
| 69 | 216 | , rank_cells{std::move(_rank_cells)} | |
| 70 | 216 | , rank_points{std::move(_rank_points)} | |
| 71 |
1/2✓ Branch 1 taken 216 times.
✗ Branch 2 not taken.
|
216 | , num_cells_total{_accumulate(rank_cells)} |
| 72 |
1/2✓ Branch 1 taken 216 times.
✗ Branch 2 not taken.
|
216 | , num_points_total{_accumulate(rank_points)} |
| 73 |
2/2✓ Branch 1 taken 214 times.
✓ Branch 2 taken 2 times.
|
216 | , my_cell_offset{_accumulate_rank_offset(rank_cells)} |
| 74 |
1/2✓ Branch 1 taken 214 times.
✗ Branch 2 not taken.
|
214 | , my_point_offset{_accumulate_rank_offset(rank_points)} { |
| 75 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 214 times.
|
214 | if (my_rank >= num_ranks) |
| 76 | ✗ | throw ValueError(as_error("Given rank is not within communicator size")); | |
| 77 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 212 times.
|
214 | if (num_ranks != static_cast<int>(rank_cells.size())) |
| 78 |
2/4✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
|
2 | throw ValueError(as_error("Cells vector does not match communicator size")); |
| 79 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 211 times.
|
212 | if (num_ranks != static_cast<int>(rank_points.size())) |
| 80 |
2/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
1 | throw ValueError(as_error("Points vector does not match communicator size")); |
| 81 | 221 | } | |
| 82 | |||
| 83 | template<Concepts::Grid Grid, Concepts::Communicator Communicator> | ||
| 84 | 261 | static IOContext from(const Grid& grid, const Communicator& comm, int root_rank = 0) { | |
| 85 |
1/2✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
|
261 | const int size = Parallel::size(comm); |
| 86 |
1/2✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
|
261 | const int rank = Parallel::rank(comm); |
| 87 |
1/2✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
|
261 | const std::size_t num_points = number_of_points(grid); |
| 88 |
1/2✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
|
261 | const std::size_t num_cells = number_of_cells(grid); |
| 89 |
2/2✓ Branch 0 taken 95 times.
✓ Branch 1 taken 114 times.
|
261 | if (size == 1) |
| 90 |
3/6✓ Branch 1 taken 95 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 95 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 95 times.
✗ Branch 8 not taken.
|
555 | return IOContext{rank, size, std::vector{num_cells}, std::vector{num_points}}; |
| 91 | |||
| 92 |
1/2✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
|
150 | const auto all_num_points = Parallel::gather(comm, num_points, root_rank); |
| 93 |
1/2✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
|
150 | const auto all_num_cells = Parallel::gather(comm, num_cells, root_rank); |
| 94 |
1/2✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
|
150 | const auto my_all_num_points = Parallel::broadcast(comm, all_num_points, root_rank); |
| 95 |
1/2✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
|
150 | const auto my_all_num_cells = Parallel::broadcast(comm, all_num_cells, root_rank); |
| 96 |
3/6✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 114 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 114 times.
✗ Branch 8 not taken.
|
150 | return IOContext{rank, size, my_all_num_cells, my_all_num_points}; |
| 97 | 150 | } | |
| 98 | |||
| 99 | private: | ||
| 100 | 432 | std::size_t _accumulate(const std::vector<std::size_t>& in) const { | |
| 101 | 432 | return std::accumulate(in.begin(), in.end(), std::size_t{0}); | |
| 102 | } | ||
| 103 | |||
| 104 | 430 | std::size_t _accumulate_rank_offset(const std::vector<std::size_t>& in) const { | |
| 105 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 428 times.
|
430 | if (in.size() <= static_cast<std::size_t>(my_rank)) |
| 106 |
1/2✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
2 | throw ValueError("Rank-vector length must be equal to number of ranks"); |
| 107 | 856 | return std::accumulate(in.begin(), std::next(in.begin(), my_rank), std::size_t{0}); | |
| 108 | } | ||
| 109 | }; | ||
| 110 | |||
| 111 | //! Throw if the given name cannot be used as a vtk-hdf array name | ||
| 112 | 4205 | inline void check_array_name(const std::string& name) { | |
| 113 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4205 times.
|
4205 | if (name.empty()) |
| 114 | ✗ | throw ValueError("VTKHDF array names must not be empty"); | |
| 115 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4201 times.
|
4205 | if (name.find_first_of("/.") != std::string::npos) |
| 116 |
3/6✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 4 times.
✗ Branch 10 not taken.
|
4 | throw ValueError("VTKHDF array names must not contain '/' or '.' (received '" + name + "')"); |
| 117 | 4201 | } | |
| 118 | |||
| 119 | //! Return true if the given field holds strings (i.e. is a flat array of characters, see MultiString) | ||
| 120 | 581 | inline bool is_string_field(const Field& field) { | |
| 121 |
9/16✓ Branch 1 taken 581 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 581 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 431 times.
✓ Branch 7 taken 150 times.
✓ Branch 9 taken 431 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 431 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 431 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 431 times.
✓ Branch 17 taken 150 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
|
581 | return field.precision().template is<char>() && field.layout().dimension() == 1; |
| 122 | } | ||
| 123 | |||
| 124 | //! Return the strings stored in the given character field (see MultiString) | ||
| 125 | 431 | inline std::vector<std::string> extract_strings(const Field& field) { | |
| 126 |
1/2✓ Branch 1 taken 431 times.
✗ Branch 2 not taken.
|
431 | const auto strings = field.template export_to<MultiString>(); |
| 127 |
1/2✓ Branch 1 taken 431 times.
✗ Branch 2 not taken.
|
431 | const auto slices = strings.slices(); |
| 128 |
1/2✓ Branch 3 taken 431 times.
✗ Branch 4 not taken.
|
1293 | return {slices.begin(), slices.end()}; |
| 129 | 431 | } | |
| 130 | |||
| 131 | #if GRIDFORMAT_HAVE_HIGH_FIVE | ||
| 132 | |||
| 133 | /*! | ||
| 134 | * \ingroup VTK | ||
| 135 | * \brief Field implementation that draws values from an open HDF5 file upon request. | ||
| 136 | */ | ||
| 137 | template<typename C> | ||
| 138 | class DataSetField : public LazyField<const HDF5::File<C>&> { | ||
| 139 | using ParentType = LazyField<const HDF5::File<C>&>; | ||
| 140 | |||
| 141 | public: | ||
| 142 | using ParentType::ParentType; | ||
| 143 | |||
| 144 | 15 | explicit DataSetField(const HDF5::File<C>& file, std::string path) | |
| 145 | : ParentType{ | ||
| 146 | file, | ||
| 147 | 15 | MDLayout{file.get_dimensions(path).value()}, | |
| 148 |
2/4✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
|
15 | DynamicPrecision{file.get_precision(path).value()}, |
| 149 | 32 | [_p=std::move(path)] (const HDF5::File<C>& file) { | |
| 150 | 34 | return file.visit_dataset(_p, [&] <typename F> (F&& field) { | |
| 151 | 9 | return field.serialized(); | |
| 152 |
1/4✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
18 | }); |
| 153 | } | ||
| 154 |
4/8✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 11 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 11 times.
✗ Branch 12 not taken.
|
75 | } {} |
| 155 | }; | ||
| 156 | |||
| 157 | template<typename C> | ||
| 158 | DataSetField(const HDF5::File<C>&, std::string) -> DataSetField<C>; | ||
| 159 | |||
| 160 | template<typename C, typename CB> | ||
| 161 | DataSetField(const HDF5::File<C>&, MDLayout, DynamicPrecision, CB&&) -> DataSetField<C>; | ||
| 162 | |||
| 163 | //! Read the vtk-hdf file type from an hdf5 file | ||
| 164 | template<typename C> | ||
| 165 | 222 | std::string get_file_type(const HDF5::File<C>& file) { | |
| 166 |
3/6✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 150 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 150 times.
|
444 | if (!file.exists("/VTKHDF")) |
| 167 | ✗ | throw IOError("Given file is not a VTK-HDF file"); | |
| 168 |
3/6✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 150 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 150 times.
|
444 | if (!file.has_attribute_at("/VTKHDF/Type")) |
| 169 | ✗ | throw IOError("VTKHDF-Type attribute missing"); | |
| 170 |
2/4✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 150 times.
✗ Branch 5 not taken.
|
444 | return file.template read_attribute_to<std::string>("/VTKHDF/Type"); |
| 171 | } | ||
| 172 | |||
| 173 | //! Check that the version stated in the file is supported | ||
| 174 | //! \note Only the major version is checked, since the specification guarantees that files can be | ||
| 175 | //! read correctly by implementations for a different minor version of the same major. | ||
| 176 | template<typename C> | ||
| 177 | 142 | void check_version_compatibility(const HDF5::File<C>& file, const std::array<std::size_t, 2>& supported) { | |
| 178 |
3/6✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 96 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 96 times.
✗ Branch 8 not taken.
|
284 | if (file.has_attribute_at("/VTKHDF/Version")) |
| 179 |
2/4✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 96 times.
✗ Branch 5 not taken.
|
572 | file.visit_attribute("/VTKHDF/Version", [&] (auto&& field) { |
| 180 |
2/48✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 16 taken 78 times.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✓ Branch 52 taken 18 times.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
|
96 | const auto version = field.template export_to<std::vector<std::size_t>>(); |
| 181 |
8/192✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✓ Branch 56 taken 78 times.
✗ Branch 57 not taken.
✓ Branch 59 taken 78 times.
✗ Branch 60 not taken.
✗ Branch 62 not taken.
✓ Branch 63 taken 78 times.
✗ Branch 64 not taken.
✓ Branch 65 taken 78 times.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✗ Branch 81 not taken.
✗ Branch 82 not taken.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 87 not taken.
✗ Branch 89 not taken.
✗ Branch 90 not taken.
✗ Branch 92 not taken.
✗ Branch 93 not taken.
✗ Branch 95 not taken.
✗ Branch 96 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 108 not taken.
✗ Branch 109 not taken.
✗ Branch 111 not taken.
✗ Branch 112 not taken.
✗ Branch 114 not taken.
✗ Branch 115 not taken.
✗ Branch 117 not taken.
✗ Branch 118 not taken.
✗ Branch 119 not taken.
✗ Branch 120 not taken.
✗ Branch 122 not taken.
✗ Branch 123 not taken.
✗ Branch 125 not taken.
✗ Branch 126 not taken.
✗ Branch 128 not taken.
✗ Branch 129 not taken.
✗ Branch 130 not taken.
✗ Branch 131 not taken.
✗ Branch 133 not taken.
✗ Branch 134 not taken.
✗ Branch 136 not taken.
✗ Branch 137 not taken.
✗ Branch 139 not taken.
✗ Branch 140 not taken.
✗ Branch 141 not taken.
✗ Branch 142 not taken.
✗ Branch 144 not taken.
✗ Branch 145 not taken.
✗ Branch 147 not taken.
✗ Branch 148 not taken.
✗ Branch 150 not taken.
✗ Branch 151 not taken.
✗ Branch 152 not taken.
✗ Branch 153 not taken.
✗ Branch 155 not taken.
✗ Branch 156 not taken.
✗ Branch 158 not taken.
✗ Branch 159 not taken.
✗ Branch 161 not taken.
✗ Branch 162 not taken.
✗ Branch 163 not taken.
✗ Branch 164 not taken.
✗ Branch 166 not taken.
✗ Branch 167 not taken.
✗ Branch 169 not taken.
✗ Branch 170 not taken.
✗ Branch 172 not taken.
✗ Branch 173 not taken.
✗ Branch 174 not taken.
✗ Branch 175 not taken.
✗ Branch 177 not taken.
✗ Branch 178 not taken.
✗ Branch 180 not taken.
✗ Branch 181 not taken.
✗ Branch 183 not taken.
✗ Branch 184 not taken.
✗ Branch 185 not taken.
✗ Branch 186 not taken.
✓ Branch 188 taken 18 times.
✗ Branch 189 not taken.
✓ Branch 191 taken 18 times.
✗ Branch 192 not taken.
✗ Branch 194 not taken.
✓ Branch 195 taken 18 times.
✗ Branch 196 not taken.
✓ Branch 197 taken 18 times.
✗ Branch 199 not taken.
✗ Branch 200 not taken.
✗ Branch 202 not taken.
✗ Branch 203 not taken.
✗ Branch 205 not taken.
✗ Branch 206 not taken.
✗ Branch 207 not taken.
✗ Branch 208 not taken.
✗ Branch 210 not taken.
✗ Branch 211 not taken.
✗ Branch 213 not taken.
✗ Branch 214 not taken.
✗ Branch 216 not taken.
✗ Branch 217 not taken.
✗ Branch 218 not taken.
✗ Branch 219 not taken.
✗ Branch 221 not taken.
✗ Branch 222 not taken.
✗ Branch 224 not taken.
✗ Branch 225 not taken.
✗ Branch 227 not taken.
✗ Branch 228 not taken.
✗ Branch 229 not taken.
✗ Branch 230 not taken.
✗ Branch 232 not taken.
✗ Branch 233 not taken.
✗ Branch 235 not taken.
✗ Branch 236 not taken.
✗ Branch 238 not taken.
✗ Branch 239 not taken.
✗ Branch 240 not taken.
✗ Branch 241 not taken.
✗ Branch 243 not taken.
✗ Branch 244 not taken.
✗ Branch 246 not taken.
✗ Branch 247 not taken.
✗ Branch 249 not taken.
✗ Branch 250 not taken.
✗ Branch 251 not taken.
✗ Branch 252 not taken.
✗ Branch 254 not taken.
✗ Branch 255 not taken.
✗ Branch 257 not taken.
✗ Branch 258 not taken.
✗ Branch 260 not taken.
✗ Branch 261 not taken.
✗ Branch 262 not taken.
✗ Branch 263 not taken.
|
96 | if (version.size() > 0 && version.at(0) > supported[0]) |
| 182 | ✗ | throw ValueError( | |
| 183 | ✗ | "File version is higher than supported by the reader (" + as_string(supported, ".") + ")" | |
| 184 | ); | ||
| 185 | 96 | }); | |
| 186 | 142 | } | |
| 187 | |||
| 188 | //! Return the given field data in the shape of tuples, i.e. (tuples) or (tuples, components) | ||
| 189 | 150 | inline FieldPtr as_field_data_tuples(FieldPtr field) { | |
| 190 |
1/2✓ Branch 2 taken 150 times.
✗ Branch 3 not taken.
|
150 | const auto layout = field->layout(); |
| 191 |
2/4✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 150 times.
|
150 | if (layout.dimension() == 0) |
| 192 | ✗ | return make_field_ptr(ReshapedField{field, MDLayout{{std::size_t{1}}}}); | |
| 193 |
2/4✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 150 times.
|
150 | if (layout.dimension() > 2) |
| 194 | ✗ | return make_field_ptr(ReshapedField{field, MDLayout{{layout.extent(0), layout.number_of_entries(1)}}}); | |
| 195 | 150 | return field; | |
| 196 | 150 | } | |
| 197 | |||
| 198 | //! Write a field data array into a file that is not transient | ||
| 199 | 240 | inline void write_field_data(HDF5::File<>& file, const std::string& name, FieldPtr field) { | |
| 200 |
1/2✓ Branch 1 taken 240 times.
✗ Branch 2 not taken.
|
240 | const std::string path = "/VTKHDF/FieldData/" + name; |
| 201 |
3/4✓ Branch 2 taken 240 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 180 times.
✓ Branch 5 taken 60 times.
|
240 | if (is_string_field(*field)) |
| 202 |
2/4✓ Branch 2 taken 180 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 180 times.
✗ Branch 6 not taken.
|
180 | file.write_strings(extract_strings(*field), path); |
| 203 | else | ||
| 204 |
2/4✓ Branch 3 taken 60 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 60 times.
✗ Branch 8 not taken.
|
60 | file.write(*as_field_data_tuples(field), path); |
| 205 | 240 | } | |
| 206 | |||
| 207 | /*! | ||
| 208 | * \brief Append a time step to a field data array of a transient file. | ||
| 209 | * \details Uses the layout written by VTK: the tuples of all steps are concatenated, and the offset (in tuples) | ||
| 210 | * and the sizes (number of components and tuples) of each step are stored in the "Steps" group. | ||
| 211 | * Strings are stored as one tuple per string. | ||
| 212 | */ | ||
| 213 | 341 | inline void write_field_data_step(HDF5::File<>& file, const std::string& name, FieldPtr field) { | |
| 214 |
1/2✓ Branch 1 taken 341 times.
✗ Branch 2 not taken.
|
341 | const std::string path = "/VTKHDF/FieldData/" + name; |
| 215 |
3/6✓ Branch 1 taken 341 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 341 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 341 times.
✗ Branch 9 not taken.
|
1023 | const auto offset = file.get_dimensions(path).value_or(std::vector<std::size_t>{0}).at(0); |
| 216 | std::array<std::size_t, 2> sizes; | ||
| 217 |
3/4✓ Branch 2 taken 341 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 251 times.
✓ Branch 5 taken 90 times.
|
341 | if (is_string_field(*field)) { |
| 218 |
1/2✓ Branch 2 taken 251 times.
✗ Branch 3 not taken.
|
251 | const auto strings = extract_strings(*field); |
| 219 | 251 | sizes = {1, strings.size()}; | |
| 220 |
1/2✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
|
251 | file.write_strings(strings, path); |
| 221 | 251 | } else { | |
| 222 |
1/2✓ Branch 2 taken 90 times.
✗ Branch 3 not taken.
|
90 | const auto tuples = as_field_data_tuples(field); |
| 223 |
1/2✓ Branch 2 taken 90 times.
✗ Branch 3 not taken.
|
90 | const auto layout = tuples->layout(); |
| 224 |
5/8✓ Branch 1 taken 90 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 86 times.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 90 times.
✗ Branch 10 not taken.
|
90 | sizes = {layout.dimension() > 1 ? layout.extent(1) : 1, layout.extent(0)}; |
| 225 |
1/2✓ Branch 3 taken 90 times.
✗ Branch 4 not taken.
|
90 | file.write(*tuples, path); |
| 226 | 90 | } | |
| 227 |
2/4✓ Branch 2 taken 341 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 341 times.
✗ Branch 6 not taken.
|
341 | file.write(std::array{offset}, "/VTKHDF/Steps/FieldDataOffsets/" + name); |
| 228 |
3/6✓ Branch 2 taken 341 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 341 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 341 times.
✗ Branch 9 not taken.
|
1023 | file.write(std::vector{sizes}, "/VTKHDF/Steps/FieldDataSizes/" + name); |
| 229 | 341 | } | |
| 230 | |||
| 231 | //! Append a time step to a field data array of a transient file, referring to the data of an earlier step | ||
| 232 | 274 | inline void write_field_data_step_referencing(std::size_t step_index, HDF5::File<>& file, const std::string& name) { | |
| 233 |
1/2✓ Branch 1 taken 274 times.
✗ Branch 2 not taken.
|
274 | const std::string offsets_path = "/VTKHDF/Steps/FieldDataOffsets/" + name; |
| 234 |
1/2✓ Branch 1 taken 274 times.
✗ Branch 2 not taken.
|
274 | const std::string sizes_path = "/VTKHDF/Steps/FieldDataSizes/" + name; |
| 235 |
1/2✓ Branch 1 taken 274 times.
✗ Branch 2 not taken.
|
274 | const auto offset = file.read_dataset_to<std::size_t>( |
| 236 |
2/8✓ Branch 1 taken 274 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 274 times.
✗ Branch 5 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
|
1370 | offsets_path, HDF5::Slice{.offset = {step_index}, .count = {1}} |
| 237 | ); | ||
| 238 | 274 | const auto sizes = file.read_dataset_to<std::vector<std::size_t>>( | |
| 239 |
2/8✓ Branch 1 taken 274 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 274 times.
✗ Branch 5 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
|
1370 | sizes_path, HDF5::Slice{.offset = {step_index, 0}, .count = {1, 2}} |
| 240 |
1/2✓ Branch 1 taken 274 times.
✗ Branch 2 not taken.
|
274 | ); |
| 241 |
1/2✓ Branch 2 taken 274 times.
✗ Branch 3 not taken.
|
274 | file.write(std::array{offset}, offsets_path); |
| 242 |
4/8✓ Branch 2 taken 274 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 274 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 274 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 274 times.
✗ Branch 12 not taken.
|
822 | file.write(std::vector{std::array{sizes.at(0), sizes.at(1)}}, sizes_path); |
| 243 | 274 | } | |
| 244 | |||
| 245 | //! Read the field data array with the given name, at the given step for transient files | ||
| 246 | template<typename C> | ||
| 247 | 595 | FieldPtr read_field_data(const HDF5::File<C>& file, const std::string& name, std::optional<std::size_t> step) { | |
| 248 |
1/2✓ Branch 1 taken 415 times.
✗ Branch 2 not taken.
|
595 | const std::string path = "VTKHDF/FieldData/" + name; |
| 249 |
2/4✓ Branch 1 taken 415 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 415 times.
✗ Branch 5 not taken.
|
595 | const auto precision = file.get_precision(path).value(); |
| 250 |
1/2✓ Branch 1 taken 415 times.
✗ Branch 2 not taken.
|
595 | const bool is_string = precision.template is<char>(); |
| 251 |
2/2✓ Branch 1 taken 66 times.
✓ Branch 2 taken 349 times.
|
595 | if (!step) |
| 252 |
5/12✓ Branch 2 taken 55 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 55 times.
✓ Branch 7 taken 11 times.
✓ Branch 9 taken 55 times.
✓ Branch 10 taken 11 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
173 | return is_string ? make_field_ptr(RangeField{file.template read_dataset_to<MultiString>(path)}) |
| 253 |
12/20✓ Branch 0 taken 55 times.
✓ Branch 1 taken 11 times.
✓ Branch 3 taken 55 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 11 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 11 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 11 times.
✓ Branch 15 taken 55 times.
✓ Branch 17 taken 11 times.
✓ Branch 18 taken 55 times.
✓ Branch 20 taken 55 times.
✓ Branch 21 taken 11 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
|
267 | : make_field_ptr(DataSetField{file, path}); |
| 254 | |||
| 255 | // Without sizes (written by GridFormat <= 0.5), each step is a single row of the dataset | ||
| 256 |
1/2✓ Branch 1 taken 349 times.
✗ Branch 2 not taken.
|
1002 | const auto offset = file.template read_dataset_to<std::size_t>( |
| 257 |
1/2✓ Branch 2 taken 349 times.
✗ Branch 3 not taken.
|
1503 | "VTKHDF/Steps/FieldDataOffsets/" + name, HDF5::Slice{.offset = {*step}, .count = {1}} |
| 258 | ); | ||
| 259 | 501 | const std::string sizes_path = "VTKHDF/Steps/FieldDataSizes/" + name; | |
| 260 |
1/2✓ Branch 1 taken 349 times.
✗ Branch 2 not taken.
|
1002 | const auto sizes = file.has_dataset_at(sizes_path) // (components, tuples) |
| 261 |
13/24✓ Branch 0 taken 346 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 346 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 346 times.
✓ Branch 6 taken 3 times.
✓ Branch 8 taken 346 times.
✓ Branch 9 taken 3 times.
✓ Branch 11 taken 346 times.
✓ Branch 12 taken 3 times.
✓ Branch 14 taken 346 times.
✓ Branch 15 taken 3 times.
✓ Branch 16 taken 346 times.
✓ Branch 17 taken 3 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
|
1998 | ? std::optional{file.template read_dataset_to<std::vector<std::size_t>>( |
| 262 | 498 | sizes_path, HDF5::Slice{.offset = {*step, 0}, .count = {1, 2}} | |
| 263 | )} | ||
| 264 | : std::nullopt; | ||
| 265 | 501 | if (is_string) { | |
| 266 |
3/6✓ Branch 1 taken 288 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 288 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 288 times.
✗ Branch 10 not taken.
|
420 | const std::size_t count = sizes ? sizes->at(0)*sizes->at(1) : 1; |
| 267 |
1/2✓ Branch 1 taken 288 times.
✗ Branch 2 not taken.
|
840 | return make_field_ptr(RangeField{file.template read_dataset_to<MultiString>( |
| 268 | path, HDF5::Slice{.offset = {offset}, .count = {count}} | ||
| 269 |
1/2✓ Branch 1 taken 288 times.
✗ Branch 2 not taken.
|
420 | )}); |
| 270 | } | ||
| 271 | |||
| 272 |
2/4✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 61 times.
✗ Branch 5 not taken.
|
81 | const auto dims = file.get_dimensions(path).value(); |
| 273 |
1/2✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
81 | std::vector<std::size_t> slice_offset(dims.size(), 0); |
| 274 |
1/2✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
|
81 | slice_offset.at(0) = offset; |
| 275 |
1/2✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
|
81 | auto count = dims; |
| 276 |
4/6✓ Branch 1 taken 58 times.
✓ Branch 2 taken 3 times.
✓ Branch 5 taken 58 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 61 times.
✗ Branch 9 not taken.
|
81 | count.at(0) = sizes ? sizes->at(1) : 1; |
| 277 |
10/18✓ Branch 1 taken 58 times.
✓ Branch 2 taken 3 times.
✓ Branch 4 taken 58 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 58 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 58 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 58 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 58 times.
✓ Branch 19 taken 58 times.
✓ Branch 20 taken 3 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 61 times.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
|
81 | if (sizes && MDLayout{count}.number_of_entries() != sizes->at(0)*sizes->at(1)) |
| 278 | ✗ | throw SizeError("Field data '" + name + "' does not match its stated number of components"); | |
| 279 |
6/10✓ Branch 1 taken 58 times.
✓ Branch 2 taken 3 times.
✓ Branch 4 taken 58 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
|
81 | MDLayout layout = sizes ? MDLayout{count} : MDLayout{count | std::views::drop(1)}; |
| 280 |
1/2✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
|
162 | return make_field_ptr(DataSetField{ |
| 281 | file, | ||
| 282 | 81 | std::move(layout), | |
| 283 | precision, | ||
| 284 |
2/12✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 61 times.
✗ Branch 9 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
|
195 | [p=path, o=std::move(slice_offset), c=std::move(count)] (const HDF5::File<C>& f) { |
| 285 | 130 | return f.visit_dataset(p, [&] <typename F> (F&& field) { | |
| 286 |
4/96✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 66 not taken.
✗ Branch 67 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✗ Branch 86 not taken.
✗ Branch 87 not taken.
✗ Branch 90 not taken.
✗ Branch 91 not taken.
✓ Branch 98 taken 9 times.
✗ Branch 99 not taken.
✓ Branch 102 taken 9 times.
✗ Branch 103 not taken.
✗ Branch 110 not taken.
✗ Branch 111 not taken.
✗ Branch 114 not taken.
✗ Branch 115 not taken.
✗ Branch 122 not taken.
✗ Branch 123 not taken.
✗ Branch 126 not taken.
✗ Branch 127 not taken.
✓ Branch 134 taken 24 times.
✗ Branch 135 not taken.
✓ Branch 138 taken 24 times.
✗ Branch 139 not taken.
✗ Branch 146 not taken.
✗ Branch 147 not taken.
✗ Branch 150 not taken.
✗ Branch 151 not taken.
✗ Branch 158 not taken.
✗ Branch 159 not taken.
✗ Branch 162 not taken.
✗ Branch 163 not taken.
✗ Branch 170 not taken.
✗ Branch 171 not taken.
✗ Branch 174 not taken.
✗ Branch 175 not taken.
✗ Branch 182 not taken.
✗ Branch 183 not taken.
✗ Branch 186 not taken.
✗ Branch 187 not taken.
✗ Branch 194 not taken.
✗ Branch 195 not taken.
✗ Branch 198 not taken.
✗ Branch 199 not taken.
✗ Branch 206 not taken.
✗ Branch 207 not taken.
✗ Branch 210 not taken.
✗ Branch 211 not taken.
✗ Branch 218 not taken.
✗ Branch 219 not taken.
✗ Branch 222 not taken.
✗ Branch 223 not taken.
✗ Branch 230 not taken.
✗ Branch 231 not taken.
✗ Branch 234 not taken.
✗ Branch 235 not taken.
✗ Branch 242 not taken.
✗ Branch 243 not taken.
✗ Branch 246 not taken.
✗ Branch 247 not taken.
✗ Branch 254 not taken.
✗ Branch 255 not taken.
✗ Branch 258 not taken.
✗ Branch 259 not taken.
✗ Branch 266 not taken.
✗ Branch 267 not taken.
✗ Branch 270 not taken.
✗ Branch 271 not taken.
✗ Branch 278 not taken.
✗ Branch 279 not taken.
✗ Branch 282 not taken.
✗ Branch 283 not taken.
|
33 | return FlattenedField{make_field_ptr(std::move(field))}.serialized(); |
| 287 |
3/12✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 33 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 33 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
|
99 | }, HDF5::Slice{.offset = o, .count = c}); |
| 288 | } | ||
| 289 |
1/2✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
|
81 | }); |
| 290 |
9/32✓ Branch 1 taken 349 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 349 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 349 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 346 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 346 times.
✗ Branch 16 not taken.
✓ Branch 19 taken 288 times.
✓ Branch 20 taken 61 times.
✓ Branch 22 taken 288 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 288 times.
✗ Branch 26 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
|
5854 | } |
| 291 | |||
| 292 | /*! | ||
| 293 | * \brief Open the given file with the default (non-MPI) driver on the root rank and pass it to `write`. | ||
| 294 | * \details Must be called collectively once all ranks have closed the file. If `write` throws, the | ||
| 295 | * exception is rethrown on the root rank, and an IOError is thrown on all other ranks. | ||
| 296 | */ | ||
| 297 | template<Concepts::Communicator C, std::invocable<HDF5::File<>&> Write> | ||
| 298 | 794 | void write_on_root(const std::string& filename, const C& comm, int root, Write&& write) { | |
| 299 | 794 | Parallel::barrier(comm); | |
| 300 | // the collective invocation also keeps the other ranks from reopening the file before the root is done | ||
| 301 |
2/2✓ Branch 1 taken 396 times.
✓ Branch 2 taken 2 times.
|
1190 | Parallel::invoke_collectively(comm, [&] () { |
| 302 |
8/8✓ Branch 1 taken 92 times.
✓ Branch 2 taken 40 times.
✓ Branch 4 taken 117 times.
✓ Branch 5 taken 72 times.
✓ Branch 7 taken 36 times.
✓ Branch 8 taken 14 times.
✓ Branch 10 taken 18 times.
✓ Branch 11 taken 9 times.
|
398 | if (Parallel::rank(comm) == root) { |
| 303 |
4/8✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 117 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 36 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
|
263 | HDF5::File file{filename, HDF5::File<>::append}; |
| 304 |
5/8✓ Branch 1 taken 90 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 117 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 36 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
|
263 | write(file); |
| 305 | 263 | } | |
| 306 |
2/4✓ Branch 1 taken 398 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 398 times.
✗ Branch 5 not taken.
|
1596 | }, "Writing to '" + filename + "'"); |
| 307 | 790 | } | |
| 308 | |||
| 309 | #endif // GRIDFORMAT_HAVE_HIGH_FIVE | ||
| 310 | |||
| 311 | } // namespace VTKHDF | ||
| 312 | } // namespace GridFormat | ||
| 313 | |||
| 314 | #endif // GRIDFORMAT_VTK_HDF_COMMON_HPP_ | ||
| 315 |