/home/runner/work/DiFfRG_current/DiFfRG_current/DiFfRG/include/DiFfRG/discretization/data/hdf5_output.hh Source File#

DiFfRG: /home/runner/work/DiFfRG_current/DiFfRG_current/DiFfRG/include/DiFfRG/discretization/data/hdf5_output.hh Source File
DiFfRG
Discretization Framework for functional Renormalization Group flows
hdf5_output.hh
Go to the documentation of this file.
1#pragma once
2
3// DiFfRG
12
13#include <filesystem>
14#include <list>
15#include <set>
16#include <typeindex>
17#include <utility>
18#include <vector>
19
20namespace DiFfRG
21{
33 void write_config_tree(DiFfRG::hdf5::Group &group, const json::value &value);
34
42 {
43 public:
51 HDF5Output(const std::string top_folder, const std::string output_name,
52 const std::string &configuration_json = "{}");
53
54 [[deprecated("Use HDF5Output(top_folder, output_name, Config::OutputSettings(config).configuration_json) instead")]]
55 HDF5Output(const std::string top_folder, const std::string output_name, const ConfigTree &config)
56 : HDF5Output(std::move(top_folder), std::move(output_name), Config::OutputSettings(config).configuration_json)
57 {
58 }
59
61
63 static void write_series_record(DiFfRG::hdf5::Group &group, const int series_number)
64 {
65 const std::string value = std::to_string(series_number);
66
67 if (!group.has_dataset("series_numbers")) {
68 // chunked layout so we can append later
71 auto data_set = group.create_chunked_dataset("series_numbers", type, space, {128});
72 data_set.write_at(0, value);
73 } else {
74 auto data_set = group.open_dataset("series_numbers");
75 const std::size_t cur_size = static_cast<std::size_t>(data_set.dataspace().size());
76 data_set.resize({cur_size + 1});
77 data_set.write_at(static_cast<hsize_t>(cur_size), value);
78 }
79 }
80
91 template <typename T> void scalar(const std::string &name, const T value)
92 {
93 using value_type = std::decay_t<T>;
94
95 if (initial_scalars.size() > 0 &&
96 std::find(initial_scalars.begin(), initial_scalars.end(), name) == initial_scalars.end())
97 throw std::runtime_error("HDF5Output::scalar: The scalar '" + name + "' has not been registered before!");
98
99 // Every check happens before any bookkeeping is touched: a rejected write must leave the
100 // frame exactly as it was, so that a later, valid write of the same name still succeeds.
101 auto info = scalar_infos.find(name);
102 if (info != scalar_infos.end()) {
103 // std::type_index rather than H5Tequal, so that no HDF5 call is needed to validate.
104 // It is strictly finer than H5Tequal: two distinct C++ types mapping onto the same HDF5
105 // type would now be rejected. No such pair exists among the traits in hdf5.hh.
106 if (info->second.type != std::type_index(typeid(value_type)))
107 throw std::runtime_error(
108 "HDF5Output::scalar: The type of the value does not match the type of the dataset '" + name +
109 "' in the file '" + output_name + "'.");
110
111 if (std::find(written_scalars.begin(), written_scalars.end(), name) != written_scalars.end())
112 throw std::runtime_error("HDF5Output::scalar: The scalar '" + name +
113 "' has already been written to the file '" + output_name + "'.");
114 } else {
115 info = scalar_infos.emplace(name, ScalarInfo{std::type_index(typeid(value_type)), 0}).first;
116 }
117
118 written_scalars.push_back(name);
119 const bool create = info->second.count == 0;
120 const hsize_t offset = static_cast<hsize_t>(info->second.count++);
121
122 staged.actions.emplace_back([name, v = value_type(value), create, offset](HDF5FrameContext &context) {
123 auto &group = context.group("scalars");
125 if (create) {
127 auto data_set = group.create_chunked_dataset(name, type, space, {128});
128 data_set.write_at(0, v);
129 } else {
130 auto data_set = group.open_dataset(name);
131 data_set.resize({offset + 1});
132 data_set.write_at(offset, v);
133 }
134 });
135 }
136 void scalar(const std::string &name, const char *value) { scalar<std::string>(name, std::string(value)); }
137
138 template <typename COORD>
139 requires is_coordinates<COORD>
140 void map(const std::string &name, const COORD &coordinates)
141 {
142 // In-memory, because staging must not touch the file at all. It also fixes the original:
143 // this asked has_dataset() on a group handle that was closed between frames, which
144 // silently answered "no" and turned the diagnostic into a raw H5Dcreate2 failure.
145 if (coord_identifiers.find(name) != coord_identifiers.end()) {
146 throw std::runtime_error("HDF5Output::map: The coordinates '" + name +
147 "' have already been written to the file '" + output_name + "'.");
148 }
149
150 // Materialised here because it reads `coordinates`, which the caller owns; the write of
151 // the resulting buffer is staged.
152 auto grid_data = make_grid(coordinates);
153 using coord_type = typename decltype(grid_data)::value_type;
154
156 for (const auto &dim : coordinates.sizes())
157 dims.push_back(dim);
158
159 coord_identifiers[name] = coordinates.to_string();
160
161 staged.actions.emplace_back([name, dims, grid = std::move(grid_data)](HDF5FrameContext &context) {
162 auto space = DiFfRG::hdf5::Dataspace::simple(dims);
163 auto dataset = context.group("coordinates").create_dataset(name, DiFfRG::hdf5::type_of<coord_type>(), space);
164 dataset.write(grid);
165 });
166 }
167
168 template <typename COORD, typename T>
169 requires is_coordinates<COORD>
170 void map(const std::string &name, const COORD &coordinates, T *data)
171 {
172 // Note the asymmetry with the interpolator overload below: that one takes the coordinate
173 // dataset's name from the caller, this one derives it from the coordinates themselves.
174 // Both spellings therefore appear under /coordinates. Preserved deliberately -- the
175 // golden layout test pins it.
176 const std::string coord_name = coordinates.to_string();
177 if (coord_identifiers.find(coord_name) == coord_identifiers.end()) map(coord_name, coordinates);
178
179 using value_type = std::decay_t<T>;
180
182 for (const auto &dim : coordinates.sizes())
183 dims.push_back(dim);
184
185 // `data` points into live simulation state, so the copy is mandatory, not defensive.
186 std::vector<value_type> payload(data, data + coordinates.size());
187 stage_map(name, coord_name, dims, std::move(payload));
188 }
189
190 template <typename INTERP>
192 void map(const std::string &name, const INTERP &_interpolator)
193 {
194 map(name, _interpolator.get_coordinates().to_string(), _interpolator);
195 }
196
197 template <typename INTERP>
199 void map(const std::string &name, const std::string &coord_name, const INTERP &_interpolator)
200 {
201 using value_type = typename std::decay_t<INTERP>::value_type;
202
203 const auto &interpolator = _interpolator;
204
205 check_coordinates(coord_name, interpolator.get_coordinates());
206
208 const auto coordinates = interpolator.get_coordinates();
209 for (const auto &dim : coordinates.sizes())
210 dims.push_back(dim);
211
212 // Sampling stays here rather than moving into the staged action; only the resulting dense
213 // buffer is deferred.
214 std::vector<value_type> payload(coordinates.size());
215 for (size_t i = 0; i < payload.size(); ++i) {
216 const auto lcoord = coordinates.forward(coordinates.from_linear_index(i));
217 payload[i] = device::apply([&](const auto &...x) { return interpolator(x...); }, lcoord);
218 }
219
220 stage_map(name, coord_name, dims, std::move(payload));
221 }
222
229 void stage_fe_frame(unsigned int series_number, double time, const std::string &group_name,
230 const std::string &fe_output_name, unsigned int dim, std::size_t n_nodes,
231 std::vector<double> node_data,
232 std::vector<std::pair<std::string, std::vector<double>>> data_sets);
233
242
243 void flush(const double time);
244
252 void run_staged_frame(double time);
253
261 void set_writer(HDF5FrameWriter *writer) { this->writer = writer; }
262
264 void drain();
265
275 void mark_finished(bool crashed);
276
277 void open_file();
279
282 {
283 const FrameTimings taken = frame_timings;
285 return taken;
286 }
287
288 private:
295 template <typename T>
296 void stage_map(const std::string &name, const std::string &coord_name, const DiFfRG::hdf5::Dims &dims,
297 std::vector<T> payload)
298 {
299 const std::size_t series = map_series_numbers[name]++;
300 // Whether this frame has to create the per-name parent group is decided here rather than
301 // asked of the file, so that staging stays HDF5-free.
302 const bool create_parent = created_map_groups.insert(name).second;
303
304 written_map_series.emplace_back(name, series);
305 staged.actions.emplace_back(
306 [name, coord_name, dims, series, create_parent, data = std::move(payload)](HDF5FrameContext &context) {
307 auto &maps_group = context.group("maps");
308 auto parent = create_parent ? maps_group.create_group(name) : maps_group.open_group(name);
309 auto group = parent.create_group(std::to_string(series));
310 write_series_record(group, static_cast<int>(series));
311
312 auto space = DiFfRG::hdf5::Dataspace::simple(dims);
313 auto dataset = group.create_dataset("data", DiFfRG::hdf5::type_of<T>(), space);
314 dataset.write(data);
315
316 group.create_soft_link("coordinates", "/coordinates/" + coord_name);
317 });
318 }
319
320 const std::string top_folder;
321 const std::string output_name;
322
323 bool opened;
324
327 struct ScalarInfo {
328 std::type_index type;
329 std::size_t count = 0;
330 };
331 std::map<std::string, ScalarInfo> scalar_infos;
333 std::set<std::string> created_map_groups;
334
339
340 std::list<std::string> written_scalars;
345 std::vector<std::pair<std::string, size_t>> written_map_series;
346 std::map<std::string, size_t> map_series_numbers;
347 std::map<std::string, std::string> coord_identifiers;
348
349 std::list<std::string> initial_scalars;
350
351 template <typename COORD> void check_coordinates(const std::string &coord_name, const COORD &coordinates)
352 {
353 // In-memory bookkeeping rather than a file query, for the reason given in map().
354 if (coord_identifiers.find(coord_name) == coord_identifiers.end()) {
355 map(coord_name, coordinates);
356 return;
357 }
358
359 if (coordinates.to_string() != coord_identifiers[coord_name])
360 throw std::runtime_error("HDF5Output::map: The coordinates '" + coord_name +
361 "' do not match the interpolator's coordinates.");
362 }
363
366
367 std::filesystem::path path;
368
370 };
371} // namespace DiFfRG
A hierarchical configuration tree, readable from JSON and TOML files.
Definition config_tree.hh:32
The one place HDF5 identifiers live while a frame is being written.
Definition hdf5_frame.hh:33
DiFfRG::hdf5::Group & group(const std::string &name)
Writes staged HDF5 frames on a single background thread.
Definition hdf5_writer.hh:31
A class to output data to a CSV file.
Definition hdf5_output.hh:42
void mark_finished(bool crashed)
Record how the run ended: sets the root finished attribute to 1, and crashed to whether it ended by t...
void check_coordinates(const std::string &coord_name, const COORD &coordinates)
Definition hdf5_output.hh:351
void map(const std::string &name, const std::string &coord_name, const INTERP &_interpolator)
Definition hdf5_output.hh:199
void set_writer(HDF5FrameWriter *writer)
Route staged frames through a writer thread instead of writing them inline.
Definition hdf5_output.hh:261
FrameTimings frame_timings
Definition hdf5_output.hh:369
DiFfRG::hdf5::File & get_file()
Open the file for setup work performed before any frame is written.
std::map< std::string, size_t > map_series_numbers
Definition hdf5_output.hh:346
void map(const std::string &name, const INTERP &_interpolator)
Definition hdf5_output.hh:192
std::map< std::string, ScalarInfo > scalar_infos
Definition hdf5_output.hh:331
std::set< std::string > created_map_groups
Definition hdf5_output.hh:333
void stage_map(const std::string &name, const std::string &coord_name, const DiFfRG::hdf5::Dims &dims, std::vector< T > payload)
Stage one map series: the group, its series record, its data and its coordinate link.
Definition hdf5_output.hh:296
bool opened
Definition hdf5_output.hh:323
void stage_fe_frame(unsigned int series_number, double time, const std::string &group_name, const std::string &fe_output_name, unsigned int dim, std::size_t n_nodes, std::vector< double > node_data, std::vector< std::pair< std::string, std::vector< double > > > data_sets)
Stage the node coordinates and nodal values of one finite-element frame.
void map(const std::string &name, const COORD &coordinates, T *data)
Definition hdf5_output.hh:170
void flush(const double time)
std::list< std::string > written_scalars
Definition hdf5_output.hh:340
FrameTimings take_frame_timings()
Definition hdf5_output.hh:281
void run_staged_frame(double time)
Execute and clear whatever is staged, without the per-frame scalar bookkeeping.
const std::string top_folder
Definition hdf5_output.hh:320
void scalar(const std::string &name, const T value)
Add a value to the output.
Definition hdf5_output.hh:91
void map(const std::string &name, const COORD &coordinates)
Definition hdf5_output.hh:140
HDF5Output(const std::string top_folder, const std::string output_name, const ConfigTree &config)
Definition hdf5_output.hh:55
static void write_series_record(DiFfRG::hdf5::Group &group, const int series_number)
Definition hdf5_output.hh:63
HDF5FrameWriter * writer
Definition hdf5_output.hh:338
std::list< std::string > initial_scalars
Definition hdf5_output.hh:349
std::vector< std::pair< std::string, size_t > > written_map_series
Definition hdf5_output.hh:345
HDF5Frame staged
Definition hdf5_output.hh:336
void scalar(const std::string &name, const char *value)
Definition hdf5_output.hh:136
HDF5Output(const std::string top_folder, const std::string output_name, const std::string &configuration_json="{}")
Construct a new Csv Output object.
const std::string output_name
Definition hdf5_output.hh:321
std::map< std::string, std::string > coord_identifiers
Definition hdf5_output.hh:347
DiFfRG::hdf5::File h5_file
Definition hdf5_output.hh:365
std::filesystem::path path
Definition hdf5_output.hh:367
void resize(const Dims &dims)
Definition dataset.hh:38
void write(const T &value)
Definition dataset.hh:49
void write_at(hsize_t offset, const T &value)
Definition dataset.hh:61
static Dataspace simple_unlimited(const Dims &dims)
Definition dataspace.hh:65
static Dataspace simple(const Dims &dims)
Definition dataspace.hh:56
Definition file.hh:13
Definition group.hh:16
Dataset open_dataset(const std::string &name)
Definition group.hh:71
bool has_dataset(const std::string &name) const
Definition group.hh:42
Group create_group(const std::string &name)
Definition group.hh:27
Dataset create_chunked_dataset(const std::string &name, const Datatype &type, const Dataspace &space, const Dims &chunk)
Definition group.hh:55
Dataset create_dataset(const std::string &name, const Datatype &type, const Dataspace &space)
Definition group.hh:46
Group open_group(const std::string &name)
Definition group.hh:34
Definition coordinates.hh:22
A concept for what is an interpolator class.
Definition interpolation.hh:40
std::vector< hsize_t > Dims
Definition dataspace.hh:9
Datatype type_of()
Convenience factory — type_of<T>() returns the HDF5 datatype for T.
Definition datatype.hh:56
Definition complex_math.hh:10
@ config
/discretization/threads.
auto make_grid(const Coordinates &coordinates)
Definition coordinates.hh:731
void write_config_tree(DiFfRG::hdf5::Group &group, const json::value &value)
Mirror a JSON configuration value into group as a browsable tree: one subgroup per object,...
Definition output_settings.hh:15
Wall-clock cost of producing one output frame, split by where the time went.
Definition output_timings.hh:33
Everything one output frame will write to one file, and nothing else.
Definition hdf5_frame.hh:66
std::vector< HDF5Action > actions
Definition hdf5_frame.hh:69
Definition hdf5_output.hh:327
std::type_index type
Definition hdf5_output.hh:328
std::size_t count
Definition hdf5_output.hh:329