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