SpECTRE Documentation Coverage Report
Current view: top level - IO/H5 - VolumeData.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 24 41 58.5 %
Date: 2025-12-05 05:03:31
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             :   /// Read the extents of all the grids stored in the file at the observation id
     193             :   /// `observation_id`
     194           1 :   std::vector<std::vector<size_t>> get_extents(size_t observation_id) const;
     195             : 
     196             :   /// Retrieve volume data for IDs in
     197             :   /// `[start_observation_value, end_observation_value]`.
     198             :   ///
     199             :   /// Returns a `std::vector` over times, sorted in ascending order of the
     200             :   /// observation value (the time in evolutions). The vector holds a
     201             :   /// `std::tuple` that holds
     202             :   /// 1. the integral observation ID
     203             :   /// 2. the observation value (time in evolutions)
     204             :   /// 3. a vector of `ElementVolumeData` sorted by the elements' name string.
     205             :   ///
     206             :   /// - If `start_observation_value` is `std::nullopt` then return
     207             :   ///   everything from the beginning of the data to `end_observation_value`. If
     208             :   ///   `end_observation_value` is `std::nullopt` then return everything from
     209             :   ///   `start_observation_value` to the end of the data.
     210             :   /// - If `components_to_retrieve` is `std::nullopt` then return all
     211             :   ///   components.
     212           1 :   auto get_data_by_element(std::optional<double> start_observation_value,
     213             :                            std::optional<double> end_observation_value,
     214             :                            const std::optional<std::vector<std::string>>&
     215             :                                components_to_retrieve = std::nullopt) const
     216             :       -> std::vector<
     217             :           std::tuple<size_t, double, std::vector<ElementVolumeData>>>;
     218             : 
     219             :   /// Read the dimensionality of the grids.  Note : This is the dimension of
     220             :   /// the grids as manifolds, not the dimension of the embedding space.  For
     221             :   /// example, the volume data of a sphere is 2-dimensional, even though
     222             :   /// each point has an x, y, and z coordinate.
     223           1 :   size_t get_dimension() const;
     224             : 
     225             :   /// Return the character used as a separator between grids in the subfile.
     226           1 :   static char separator() { return ':'; }
     227             : 
     228             :   /// Return the basis being used for each element along each axis
     229           1 :   std::vector<std::vector<Spectral::Basis>> get_bases(
     230             :       size_t observation_id) const;
     231             : 
     232             :   /// Return the quadrature being used for each element along each axis
     233           1 :   std::vector<std::vector<Spectral::Quadrature>> get_quadratures(
     234             :       size_t observation_id) const;
     235             : 
     236             :   /*!
     237             :    * \brief Get the serialized domain if it was written.
     238             :    */
     239           1 :   std::optional<std::vector<char>> get_domain() const;
     240             : 
     241             :   /*!
     242             :    * \brief Get the observation-specific serialized functions of time at an \p
     243             :    * observation_id if they were written.
     244             :    */
     245           1 :   std::optional<std::vector<char>> get_functions_of_time(
     246             :       size_t observation_id) const;
     247             : 
     248             :   /*!
     249             :    * \brief Get the serialized global functions of time in the subfile if they
     250             :    * were written.
     251             :    */
     252           1 :   std::optional<std::vector<char>> get_global_functions_of_time() const;
     253             : 
     254           1 :   const std::string& subfile_path() const override { return path_; }
     255             : 
     256             :  private:
     257           0 :   detail::OpenGroup group_{};
     258           0 :   std::string name_{};
     259           0 :   std::string path_{};
     260           0 :   uint32_t version_{};
     261           0 :   detail::OpenGroup volume_data_group_{};
     262           0 :   std::string header_{};
     263             : };
     264             : 
     265             : /*!
     266             :  * \brief Find the interval within the contiguous dataset stored in
     267             :  * `h5::VolumeData` that holds data for a particular `grid_name`.
     268             :  *
     269             :  * `h5::VolumeData` stores data for all grids that compose the volume
     270             :  * contiguously. This function helps with reconstructing which part of that
     271             :  * contiguous dataset belongs to a particular grid. See the `h5::VolumeData`
     272             :  * documentation for more information on how it stores data.
     273             :  *
     274             :  * To use this function, call `h5::VolumeData::get_grid_names` and
     275             :  * `h5::VolumeData::get_extents` and pass the results for the `all_grid_names`
     276             :  * and `all_extents` arguments, respectively. This means you can retrieve this
     277             :  * information from an `h5::VolumeData` once and use it to call
     278             :  * `offset_and_length_for_grid` multiple times with different `grid_name`s.
     279             :  *
     280             :  * Here is an example for using this function:
     281             :  *
     282             :  * \snippet Test_VolumeData.cpp find_offset
     283             :  *
     284             :  * \see `h5::VolumeData`
     285             :  */
     286           1 : std::pair<size_t, size_t> offset_and_length_for_grid(
     287             :     const std::string& grid_name,
     288             :     const std::vector<std::string>& all_grid_names,
     289             :     const std::vector<std::vector<size_t>>& all_extents);
     290             : 
     291             : template <size_t Dim>
     292           0 : Mesh<Dim> mesh_for_grid(
     293             :     const std::string& grid_name,
     294             :     const std::vector<std::string>& all_grid_names,
     295             :     const std::vector<std::vector<size_t>>& all_extents,
     296             :     const std::vector<std::vector<Spectral::Basis>>& all_bases,
     297             :     const std::vector<std::vector<Spectral::Quadrature>>& all_quadratures);
     298             : 
     299             : }  // namespace h5

Generated by: LCOV version 1.14