Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <cstddef>
7 : #include <cstdint>
8 : #include <optional>
9 : #include <string>
10 : #include <tuple>
11 : #include <utility>
12 : #include <vector>
13 :
14 : #include "IO/H5/Object.hpp"
15 : #include "IO/H5/OpenGroup.hpp"
16 :
17 : /// \cond
18 : class DataVector;
19 : struct ElementVolumeData;
20 : template <size_t>
21 : class Mesh;
22 : namespace Spectral {
23 : enum class Basis : uint8_t;
24 : enum class Quadrature : uint8_t;
25 : } // namespace Spectral
26 : struct TensorComponent;
27 : /// \endcond
28 :
29 : namespace h5 {
30 : /*!
31 : * \ingroup HDF5Group
32 : * \brief A volume data subfile written inside an H5 file.
33 : *
34 : * The volume data inside the subfile can be of any dimensionality greater than
35 : * zero. This means that in a 3D simulation, data on 2-dimensional surfaces are
36 : * written as a VolumeData subfile. Data can be written using the
37 : * `write_volume_data()` method. An integral observation id is used to keep
38 : * track of the observation instance at which the data is written, and
39 : * associated with it is a floating point observation value, such as the
40 : * simulation time at which the data was written. The observation id will
41 : * generally be the result of hashing the temporal identifier used for the
42 : * simulation.
43 : *
44 : * \par Grid names
45 : * The data stored in the subfile are the tensor components passed to the
46 : * `write_volume_data()` method as a `std::vector<ElementVolumeData>`. Typically
47 : * the `GRID_NAME` should be the output of the stream operator of the spatial ID
48 : * of the parallel component element sending the data to be observed. For
49 : * example, in the case of a dG evolution where the spatial IDs are
50 : * `ElementId`s, the grid names would be of the form `[B0,(L2I3,L2I3,L2I3)]`.
51 : *
52 : * \par Data layout in the H5 file
53 : * The data are written contiguously inside of the H5 subfile, in that each
54 : * tensor component has a single dataset which holds all of the data from all
55 : * elements, e.g. a tensor component `T_xx` which is found on all grids appears
56 : * in the path `H5_FILE_NAME/SUBFILE_NAME.vol/OBSERVATION_ID/GRID_NAME/T_xx` and
57 : * that is where all of the `T_xx` data from all of the grids resides. Note that
58 : * coordinates must be written as tensors in order to visualize the data in
59 : * ParaView, Visit, etc. In order to reconstruct which data came from which
60 : * grid, the `get_grid_names()`, and `get_extents()` methods list the grids
61 : * and their extents in the order which they and the data were written.
62 : * For example, if the first grid has name `GRID_NAME` with extents
63 : * `{2, 2, 2}`, it was responsible for contributing the first 2*2*2 = 8 grid
64 : * points worth of data in each tensor dataset. Use the
65 : * `h5::offset_and_length_for_grid` function to compute the offset into the
66 : * contiguous dataset that corresponds to a particular grid.
67 : *
68 : * \par Domain and FunctionsOfTime
69 : * A serialized representation of the domain and the functions of time can be
70 : * written into the subfile alongside the tensor data. Reconstructing the domain
71 : * (specifically the block maps) is needed for accurate interpolation of the
72 : * tensor data to another set of points. The domain is currently stored as a
73 : * `std::vector<char>`, and it is up to the calling code to serialize and
74 : * deserialize the data, taking into account that files may be written and read
75 : * with different versions of the code.
76 : *
77 : * \warning Currently the topology of the grids is assumed to be tensor products
78 : * of lines, i.e. lines, quadrilaterals, and hexahedrons. However, this can be
79 : * extended in the future. If support for more topologies is required, please
80 : * file an issue.
81 : */
82 1 : class VolumeData : public h5::Object {
83 : public:
84 0 : static std::string extension() { return ".vol"; }
85 :
86 0 : VolumeData(bool subfile_exists, detail::OpenGroup&& group, hid_t location,
87 : const std::string& name, uint32_t version = 1);
88 :
89 0 : VolumeData(const VolumeData& /*rhs*/) = delete;
90 0 : VolumeData& operator=(const VolumeData& /*rhs*/) = delete;
91 0 : VolumeData(VolumeData&& /*rhs*/) = delete; // NOLINT
92 0 : VolumeData& operator=(VolumeData&& /*rhs*/) = delete; // NOLINT
93 :
94 0 : ~VolumeData() override = default;
95 :
96 : /*!
97 : * \returns the header of the VolumeData file
98 : */
99 1 : const std::string& get_header() const { return header_; }
100 :
101 : /*!
102 : * \returns the user-specified version number of the VolumeData file
103 : *
104 : * \note h5::Version returns a uint32_t, so we return one here too for the
105 : * version
106 : */
107 1 : uint32_t get_version() const { return version_; }
108 :
109 : /// Insert tensor components at `observation_id` with floating point value
110 : /// `observation_value`. Optionally write a serialized representation of the
111 : /// domain and the functions of time into the subfile as well.
112 : ///
113 : /// All `elements` must contain the same tensor components in the same order.
114 1 : void write_volume_data(
115 : size_t observation_id, double observation_value,
116 : const std::vector<ElementVolumeData>& elements,
117 : const std::optional<std::vector<char>>& serialized_domain = std::nullopt,
118 : const std::optional<std::vector<char>>& serialized_functions_of_time =
119 : std::nullopt);
120 :
121 : /// Overwrites the current connectivity dataset with a new one. This new
122 : /// connectivity dataset builds connectivity within each block in the domain
123 : /// for each observation id in a list of observation id's
124 : template <size_t SpatialDim>
125 1 : void extend_connectivity_data(const std::vector<size_t>& observation_ids);
126 :
127 0 : void write_tensor_component(const size_t observation_id,
128 : const std::string& component_name,
129 : const DataVector& contiguous_tensor_data,
130 : bool overwrite_existing = false);
131 :
132 0 : void write_tensor_component(const size_t observation_id,
133 : const std::string& component_name,
134 : const std::vector<float>& contiguous_tensor_data,
135 : bool overwrite_existing = false);
136 :
137 : /// List all the integral observation ids in the subfile
138 : ///
139 : /// The list of observation IDs is sorted by their observation value, as
140 : /// returned by get_observation_value(size_t).
141 1 : std::vector<size_t> list_observation_ids() const;
142 :
143 : /// Get the observation value at the the integral observation id in the
144 : /// subfile
145 1 : double get_observation_value(size_t observation_id) const;
146 :
147 : /// Find the observation ID that matches the `observation_value`
148 : ///
149 : /// \details An epsilon can be specified and the observation id that matches
150 : /// within the epsilon of `observation_value` will be returned. If there is
151 : /// more than one id that is within the epsilon, an error will occur. If no
152 : /// epsilon is specified, this function will do exact comparison.
153 1 : size_t find_observation_id(
154 : double observation_value,
155 : const std::optional<double>& observation_value_epsilon =
156 : std::nullopt) const;
157 :
158 : /// List all the tensor components at observation id `observation_id`
159 1 : std::vector<std::string> list_tensor_components(size_t observation_id) const;
160 :
161 : /// List the names of all the grids at observation id `observation_id`
162 1 : std::vector<std::string> get_grid_names(size_t observation_id) const;
163 :
164 : /// Read a tensor component with name `tensor_component` at observation id
165 : /// `observation_id` from all grids in the file
166 1 : TensorComponent get_tensor_component(
167 : size_t observation_id, const std::string& tensor_component) const;
168 :
169 : /// Read the extents of all the grids stored in the file at the observation id
170 : /// `observation_id`
171 1 : std::vector<std::vector<size_t>> get_extents(size_t observation_id) const;
172 :
173 : /// Retrieve volume data for IDs in
174 : /// `[start_observation_value, end_observation_value]`.
175 : ///
176 : /// Returns a `std::vector` over times, sorted in ascending order of the
177 : /// observation value (the time in evolutions). The vector holds a
178 : /// `std::tuple` that holds
179 : /// 1. the integral observation ID
180 : /// 2. the observation value (time in evolutions)
181 : /// 3. a vector of `ElementVolumeData` sorted by the elements' name string.
182 : ///
183 : /// - If `start_observation_value` is `std::nullopt` then return
184 : /// everything from the beginning of the data to `end_observation_value`. If
185 : /// `end_observation_value` is `std::nullopt` then return everything from
186 : /// `start_observation_value` to the end of the data.
187 : /// - If `components_to_retrieve` is `std::nullopt` then return all
188 : /// components.
189 1 : auto get_data_by_element(std::optional<double> start_observation_value,
190 : std::optional<double> end_observation_value,
191 : const std::optional<std::vector<std::string>>&
192 : components_to_retrieve = std::nullopt) const
193 : -> std::vector<
194 : std::tuple<size_t, double, std::vector<ElementVolumeData>>>;
195 :
196 : /// Read the dimensionality of the grids. Note : This is the dimension of
197 : /// the grids as manifolds, not the dimension of the embedding space. For
198 : /// example, the volume data of a sphere is 2-dimensional, even though
199 : /// each point has an x, y, and z coordinate.
200 1 : size_t get_dimension() const;
201 :
202 : /// Return the character used as a separator between grids in the subfile.
203 1 : static char separator() { return ':'; }
204 :
205 : /// Return the basis being used for each element along each axis
206 1 : std::vector<std::vector<Spectral::Basis>> get_bases(
207 : size_t observation_id) const;
208 :
209 : /// Return the quadrature being used for each element along each axis
210 1 : std::vector<std::vector<Spectral::Quadrature>> get_quadratures(
211 : size_t observation_id) const;
212 :
213 : /// Get the serialized domain in the subfile at this observation ID, or
214 : /// `std::nullopt` if no domain was written.
215 1 : std::optional<std::vector<char>> get_domain(size_t observation_id) const;
216 :
217 : /// Get the serialized functions of time in the subfile at this observation
218 : /// ID, or `std::nullopt` if none were written.
219 1 : std::optional<std::vector<char>> get_functions_of_time(
220 : size_t observation_id) const;
221 :
222 1 : const std::string& subfile_path() const override { return path_; }
223 :
224 : private:
225 0 : detail::OpenGroup group_{};
226 0 : std::string name_{};
227 0 : std::string path_{};
228 0 : uint32_t version_{};
229 0 : detail::OpenGroup volume_data_group_{};
230 0 : std::string header_{};
231 : };
232 :
233 : /*!
234 : * \brief Find the interval within the contiguous dataset stored in
235 : * `h5::VolumeData` that holds data for a particular `grid_name`.
236 : *
237 : * `h5::VolumeData` stores data for all grids that compose the volume
238 : * contiguously. This function helps with reconstructing which part of that
239 : * contiguous dataset belongs to a particular grid. See the `h5::VolumeData`
240 : * documentation for more information on how it stores data.
241 : *
242 : * To use this function, call `h5::VolumeData::get_grid_names` and
243 : * `h5::VolumeData::get_extents` and pass the results for the `all_grid_names`
244 : * and `all_extents` arguments, respectively. This means you can retrieve this
245 : * information from an `h5::VolumeData` once and use it to call
246 : * `offset_and_length_for_grid` multiple times with different `grid_name`s.
247 : *
248 : * Here is an example for using this function:
249 : *
250 : * \snippet Test_VolumeData.cpp find_offset
251 : *
252 : * \see `h5::VolumeData`
253 : */
254 1 : std::pair<size_t, size_t> offset_and_length_for_grid(
255 : const std::string& grid_name,
256 : const std::vector<std::string>& all_grid_names,
257 : const std::vector<std::vector<size_t>>& all_extents);
258 :
259 : template <size_t Dim>
260 0 : Mesh<Dim> mesh_for_grid(
261 : const std::string& grid_name,
262 : const std::vector<std::string>& all_grid_names,
263 : const std::vector<std::vector<size_t>>& all_extents,
264 : const std::vector<std::vector<Spectral::Basis>>& all_bases,
265 : const std::vector<std::vector<Spectral::Quadrature>>& all_quadratures);
266 :
267 : } // namespace h5
|