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). Only the subset
121 : * of each tensor component dataset belonging to this element is read.
122 : */
123 1 : Series modal_time_series(const ElementId<Dim>& element_id);
124 :
125 : private:
126 : struct FileCache;
127 :
128 0 : std::vector<std::string> filenames_;
129 0 : std::string subfile_name_;
130 0 : std::vector<std::string> tensor_components_;
131 0 : std::vector<std::pair<size_t, double>> obs_ids_and_times_;
132 0 : double time_step_{};
133 : /// Elements grouped by file, in the order they appear in the files
134 1 : std::vector<std::pair<ElementId<Dim>, Mesh<Dim>>> elements_;
135 : /// Mesh and file index of each element for fast lookup
136 : std::unordered_map<ElementId<Dim>, std::pair<Mesh<Dim>, size_t>>
137 1 : element_info_;
138 : /// Metadata of the most recently accessed file
139 1 : std::unique_ptr<FileCache> file_cache_;
140 : };
141 :
142 : } // namespace spectre::Exporter
|