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