SpECTRE Documentation Coverage Report
Current view: top level - IO/Exporter - ModalTimeSeriesReader.hpp Hit Total Coverage
Commit: 107e15b340886ae54549b1baa4bfc92e676f667e Lines: 12 23 52.2 %
Date: 2026-09-17 16:38:56
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 <memory>
       8             : #include <optional>
       9             : #include <string>
      10             : #include <unordered_map>
      11             : #include <utility>
      12             : #include <variant>
      13             : #include <vector>
      14             : 
      15             : #include "Domain/Structure/ElementId.hpp"
      16             : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      17             : 
      18             : namespace spectre::Exporter {
      19             : 
      20             : /*!
      21             :  * \brief Reads time series of modal coefficients from volume data files.
      22             :  *
      23             :  * This class reads tensor components written by multiple observations of a
      24             :  * volume data subfile, transforms the nodal data of each element to modal
      25             :  * coefficients, and returns the time series of every modal coefficient one
      26             :  * element at a time. Reading element-by-element keeps memory usage low: only
      27             :  * a single element's time series is held in memory at once, so the volume
      28             :  * data never has to be loaded into memory as a whole.
      29             :  *
      30             :  * Request the time series with `modal_time_series()`, e.g.:
      31             :  *
      32             :  * \snippet Test_ModalTimeSeriesReader.cpp modal_time_series_reader_example
      33             :  *
      34             :  * The observation times must be uniformly spaced. Multiple files can be used
      35             :  * when the elements of each observation are distributed across files, such as
      36             :  * files written by different nodes. All files must contain the same
      37             :  * observations in the requested time interval, and each element must reside
      38             :  * in the same file with the same mesh for all observations. Files from
      39             :  * different simulation segments must be joined first, with overlapping
      40             :  * observations removed. Adaptive mesh refinement and element migration
      41             :  * between files (e.g. by load balancing) are not supported yet and raise
      42             :  * errors.
      43             :  *
      44             :  * Only the Legendre basis is supported for now.
      45             :  *
      46             :  * \note When observing fields on a smaller mesh, use the `ProjectToMesh`
      47             :  * option of the ObserveFields event to ensure the data is truncated cleanly
      48             :  * in modal space. Do not use `InterpolateToMesh` as this creates a
      49             :  * catastrophic aliasing error.
      50             :  */
      51             : template <size_t Dim>
      52           1 : class ModalTimeSeriesReader {
      53             :  public:
      54             :   /// Modal coefficient time series of a single element, indexed as
      55             :   /// `series[component][mode][observation]`. Modes are ordered by the
      56             :   /// collapsed index of the element's mesh.
      57           1 :   using Series = std::vector<std::vector<std::vector<double>>>;
      58             : 
      59             :   /*!
      60             :    * \brief Construct from one or more volume files.
      61             :    *
      62             :    * Reads and validates metadata from all files. The volume data itself is
      63             :    * only read when calling `modal_time_series()`.
      64             :    *
      65             :    * \param volume_files_or_glob A list of volume H5 files, or a glob string
      66             :    *     that resolves to volume files. The files can distribute elements of
      67             :    *     the same observations across nodes, but files from different
      68             :    *     simulation segments must be joined first.
      69             :    * \param subfile_name The name of the volume data subfile in the H5 files.
      70             :    * \param tensor_components Tensor component names to read. Each component
      71             :    *     must exist in every file at every observation.
      72             :    * \param start_time Optional lower bound to restrict observations.
      73             :    * \param end_time Optional upper bound to restrict observations.
      74             :    */
      75           1 :   ModalTimeSeriesReader(const std::variant<std::vector<std::string>,
      76             :                                            std::string>& volume_files_or_glob,
      77             :                         std::string subfile_name,
      78             :                         std::vector<std::string> tensor_components,
      79             :                         std::optional<double> start_time = std::nullopt,
      80             :                         std::optional<double> end_time = std::nullopt);
      81             : 
      82           0 :   ModalTimeSeriesReader(ModalTimeSeriesReader&&);
      83           0 :   ModalTimeSeriesReader& operator=(ModalTimeSeriesReader&&);
      84           0 :   ModalTimeSeriesReader(const ModalTimeSeriesReader&) = delete;
      85           0 :   ModalTimeSeriesReader& operator=(const ModalTimeSeriesReader&) = delete;
      86           0 :   ~ModalTimeSeriesReader();
      87             : 
      88             :   /// Time of the first observation (after filtering by `start_time` and
      89             :   /// `end_time`)
      90           1 :   double start_time() const { return obs_ids_and_times_.front().second; }
      91             : 
      92             :   /// Uniform spacing between observation times
      93           1 :   double time_step() const { return time_step_; }
      94             : 
      95             :   /// Number of observations (after filtering by `start_time` and `end_time`)
      96           1 :   size_t num_observations() const { return obs_ids_and_times_.size(); }
      97             : 
      98             :   /// The tensor components that will be read
      99           1 :   const std::vector<std::string>& tensor_components() const {
     100             :     return tensor_components_;
     101             :   }
     102             : 
     103             :   /*!
     104             :    * \brief All elements in the volume files and their meshes.
     105             :    *
     106             :    * The elements are grouped by the volume file they reside in. Requesting
     107             :    * the `modal_time_series()` in this order avoids re-reading per-file
     108             :    * metadata.
     109             :    */
     110           1 :   const std::vector<std::pair<ElementId<Dim>, Mesh<Dim>>>& elements() const {
     111             :     return elements_;
     112             :   }
     113             : 
     114             :   /*!
     115             :    * \brief The time series of all modal coefficients of all tensor
     116             :    * components of the given element.
     117             :    *
     118             :    * Metadata of the file in which the element resides is cached between
     119             :    * calls, so requesting elements in the order of `elements()` is most
     120             :    * efficient (this is also why this function is not const).
     121             :    *
     122             :    * Reading the volume data is currently not optimized: every observation of
     123             :    * a tensor component is read from disk once per element residing in the
     124             :    * file. Reading only the element's subset of the data would avoid this
     125             :    * amplification without changing this interface.
     126             :    */
     127           1 :   Series modal_time_series(const ElementId<Dim>& element_id);
     128             : 
     129             :  private:
     130             :   struct FileCache;
     131             : 
     132           0 :   std::vector<std::string> filenames_;
     133           0 :   std::string subfile_name_;
     134           0 :   std::vector<std::string> tensor_components_;
     135           0 :   std::vector<std::pair<size_t, double>> obs_ids_and_times_;
     136           0 :   double time_step_{};
     137             :   /// Elements grouped by file, in the order they appear in the files
     138           1 :   std::vector<std::pair<ElementId<Dim>, Mesh<Dim>>> elements_;
     139             :   /// Mesh and file index of each element for fast lookup
     140             :   std::unordered_map<ElementId<Dim>, std::pair<Mesh<Dim>, size_t>>
     141           1 :       element_info_;
     142             :   /// Metadata of the most recently accessed file
     143           1 :   std::unique_ptr<FileCache> file_cache_;
     144             : };
     145             : 
     146             : }  // namespace spectre::Exporter

Generated by: LCOV version 1.14