SpECTRE Documentation Coverage Report
Current view: top level - IO/H5 - VolumeData.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 59.5 % 42 25
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            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              :   /*!
     110              :    * \brief Write volume data at an observation id and observation value.
     111              :    *
     112              :    * \param observation_id The integral observation id at which the data is
     113              :    * written.
     114              :    * \param observation_value The floating point observation value (e.g. time)
     115              :    * at which the data is written.
     116              :    * \param elements The volume data to write, passed as a vector of
     117              :    * ElementVolumeData structs.
     118              :    * \param serialized_domain An optional serialized domain. It will only be
     119              :    * written if there is not already a domain stored in the subfile.
     120              :    * \param serialized_observation_functions_of_time An optional serialized
     121              :    * observation-specific functions of time. It should be valid at the given
     122              :    * observation value, but not contain the entire history to save space.
     123              :    * \param serialized_global_functions_of_time An optional serialized global
     124              :    * functions of time oject. It should be valid for the entire simulation. It
     125              :    * will be used to overwrite the existing global functions of time iff the old
     126              :    * object was written at a smaller observation_value (i.e. an earlier time).
     127              :    */
     128            1 :   void write_volume_data(
     129              :       size_t observation_id, double observation_value,
     130              :       const std::vector<ElementVolumeData>& elements,
     131              :       const std::optional<std::vector<char>>& serialized_domain = std::nullopt,
     132              :       const std::optional<std::vector<char>>&
     133              :           serialized_observation_functions_of_time = std::nullopt,
     134              :       const std::optional<std::vector<char>>&
     135              :           serialized_global_functions_of_time = std::nullopt);
     136              : 
     137              :   /// \returns true if a serialized domain has been written to the subfile.
     138            1 :   bool has_domain() const;
     139              : 
     140              :   /// \returns true if serialized functions of time have been written to the
     141              :   /// subfile.
     142            1 :   bool has_global_functions_of_time() const;
     143              : 
     144              :   /// Overwrites the current connectivity dataset with a new one. This new
     145              :   /// connectivity dataset builds connectivity within each block in the domain
     146              :   /// for each observation id in a list of observation id's
     147              :   template <size_t SpatialDim>
     148            1 :   void extend_connectivity_data(const std::vector<size_t>& observation_ids);
     149              : 
     150            0 :   void write_tensor_component(const size_t observation_id,
     151              :                               const std::string& component_name,
     152              :                               const DataVector& contiguous_tensor_data,
     153              :                               bool overwrite_existing = false);
     154              : 
     155            0 :   void write_tensor_component(const size_t observation_id,
     156              :                               const std::string& component_name,
     157              :                               const std::vector<float>& contiguous_tensor_data,
     158              :                               bool overwrite_existing = false);
     159              : 
     160              :   /// List all the integral observation ids in the subfile
     161              :   ///
     162              :   /// The list of observation IDs is sorted by their observation value, as
     163              :   /// returned by get_observation_value(size_t).
     164            1 :   std::vector<size_t> list_observation_ids() const;
     165              : 
     166              :   /// Get the observation value at the the integral observation id in the
     167              :   /// subfile
     168            1 :   double get_observation_value(size_t observation_id) const;
     169              : 
     170              :   /// Find the observation ID that matches the `observation_value`
     171              :   ///
     172              :   /// \details An epsilon can be specified and the observation id that matches
     173              :   /// within the epsilon of `observation_value` will be returned. If there is
     174              :   /// more than one id that is within the epsilon, an error will occur. If no
     175              :   /// epsilon is specified, this function will do exact comparison.
     176            1 :   size_t find_observation_id(
     177              :       double observation_value,
     178              :       const std::optional<double>& observation_value_epsilon =
     179              :           std::nullopt) const;
     180              : 
     181              :   /// List all the tensor components at observation id `observation_id`
     182            1 :   std::vector<std::string> list_tensor_components(size_t observation_id) const;
     183              : 
     184              :   /// List the names of all the grids at observation id `observation_id`
     185            1 :   std::vector<std::string> get_grid_names(size_t observation_id) const;
     186              : 
     187              :   /// Read a tensor component with name `tensor_component` at observation id
     188              :   /// `observation_id` from all grids in the file
     189            1 :   TensorComponent get_tensor_component(
     190              :       size_t observation_id, const std::string& tensor_component) const;
     191              : 
     192              :   /*!\brief Read a contiguous subset of a rank-1 tensor component dataset.
     193              :    *
     194              :    * This overload avoids loading data for every grid when only one grid is
     195              :    * needed. Use `offset_and_length_for_grid()` to obtain `offset` and
     196              :    * `length` from the grid names and extents at the observation.
     197              :    */
     198            1 :   TensorComponent get_tensor_component(size_t observation_id,
     199              :                                        const std::string& tensor_component,
     200              :                                        size_t offset, size_t length) const;
     201              : 
     202              :   /// Read the extents of all the grids stored in the file at the observation id
     203              :   /// `observation_id`
     204            1 :   std::vector<std::vector<size_t>> get_extents(size_t observation_id) const;
     205              : 
     206              :   /// Retrieve volume data for IDs in
     207              :   /// `[start_observation_value, end_observation_value]`.
     208              :   ///
     209              :   /// Returns a `std::vector` over times, sorted in ascending order of the
     210              :   /// observation value (the time in evolutions). The vector holds a
     211              :   /// `std::tuple` that holds
     212              :   /// 1. the integral observation ID
     213              :   /// 2. the observation value (time in evolutions)
     214              :   /// 3. a vector of `ElementVolumeData` sorted by the elements' name string.
     215              :   ///
     216              :   /// - If `start_observation_value` is `std::nullopt` then return
     217              :   ///   everything from the beginning of the data to `end_observation_value`. If
     218              :   ///   `end_observation_value` is `std::nullopt` then return everything from
     219              :   ///   `start_observation_value` to the end of the data.
     220              :   /// - If `components_to_retrieve` is `std::nullopt` then return all
     221              :   ///   components.
     222            1 :   auto get_data_by_element(std::optional<double> start_observation_value,
     223              :                            std::optional<double> end_observation_value,
     224              :                            const std::optional<std::vector<std::string>>&
     225              :                                components_to_retrieve = std::nullopt) const
     226              :       -> std::vector<
     227              :           std::tuple<size_t, double, std::vector<ElementVolumeData>>>;
     228              : 
     229              :   /// Read the dimensionality of the grids.  Note : This is the dimension of
     230              :   /// the grids as manifolds, not the dimension of the embedding space.  For
     231              :   /// example, the volume data of a sphere is 2-dimensional, even though
     232              :   /// each point has an x, y, and z coordinate.
     233            1 :   size_t get_dimension() const;
     234              : 
     235              :   /// Return the character used as a separator between grids in the subfile.
     236            1 :   static char separator() { return ':'; }
     237              : 
     238              :   /// Return the basis being used for each element along each axis
     239            1 :   std::vector<std::vector<Spectral::Basis>> get_bases(
     240              :       size_t observation_id) const;
     241              : 
     242              :   /// Return the quadrature being used for each element along each axis
     243            1 :   std::vector<std::vector<Spectral::Quadrature>> get_quadratures(
     244              :       size_t observation_id) const;
     245              : 
     246              :   /*!
     247              :    * \brief Get the serialized domain if it was written.
     248              :    */
     249            1 :   std::optional<std::vector<char>> get_domain() const;
     250              : 
     251              :   /*!
     252              :    * \brief Get the observation-specific serialized functions of time at an \p
     253              :    * observation_id if they were written.
     254              :    */
     255            1 :   std::optional<std::vector<char>> get_functions_of_time(
     256              :       size_t observation_id) const;
     257              : 
     258              :   /*!
     259              :    * \brief Get the serialized global functions of time in the subfile if they
     260              :    * were written.
     261              :    */
     262            1 :   std::optional<std::vector<char>> get_global_functions_of_time() const;
     263              : 
     264            1 :   const std::string& subfile_path() const override { return path_; }
     265              : 
     266              :  private:
     267            0 :   detail::OpenGroup group_{};
     268            0 :   std::string name_{};
     269            0 :   std::string path_{};
     270            0 :   uint32_t version_{};
     271            0 :   detail::OpenGroup volume_data_group_{};
     272            0 :   std::string header_{};
     273              : };
     274              : 
     275              : /*!
     276              :  * \brief Find the interval within the contiguous dataset stored in
     277              :  * `h5::VolumeData` that holds data for a particular `grid_name`.
     278              :  *
     279              :  * `h5::VolumeData` stores data for all grids that compose the volume
     280              :  * contiguously. This function helps with reconstructing which part of that
     281              :  * contiguous dataset belongs to a particular grid. See the `h5::VolumeData`
     282              :  * documentation for more information on how it stores data.
     283              :  *
     284              :  * To use this function, call `h5::VolumeData::get_grid_names` and
     285              :  * `h5::VolumeData::get_extents` and pass the results for the `all_grid_names`
     286              :  * and `all_extents` arguments, respectively. This means you can retrieve this
     287              :  * information from an `h5::VolumeData` once and use it to call
     288              :  * `offset_and_length_for_grid` multiple times with different `grid_name`s.
     289              :  *
     290              :  * Here is an example for using this function:
     291              :  *
     292              :  * \snippet Test_VolumeData.cpp find_offset
     293              :  *
     294              :  * \see `h5::VolumeData`
     295              :  */
     296            1 : std::pair<size_t, size_t> offset_and_length_for_grid(
     297              :     const std::string& grid_name,
     298              :     const std::vector<std::string>& all_grid_names,
     299              :     const std::vector<std::vector<size_t>>& all_extents);
     300              : 
     301              : template <size_t Dim>
     302            0 : Mesh<Dim> mesh_for_grid(
     303              :     const std::string& grid_name,
     304              :     const std::vector<std::string>& all_grid_names,
     305              :     const std::vector<std::vector<size_t>>& all_extents,
     306              :     const std::vector<std::vector<Spectral::Basis>>& all_bases,
     307              :     const std::vector<std::vector<Spectral::Quadrature>>& all_quadratures);
     308              : 
     309              : }  // namespace h5
        

Generated by: LCOV version 2.0-1