GridFormat 0.6.0
I/O-Library for grid-like data structures
Loading...
Searching...
No Matches
writer.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de>
2// SPDX-License-Identifier: MIT
8#ifndef GRIDFORMAT_GRID_WRITER_HPP_
9#define GRIDFORMAT_GRID_WRITER_HPP_
10
11#include <array>
12#include <string>
13#include <utility>
14#include <ranges>
15#include <ostream>
16#include <concepts>
17#include <type_traits>
18
19#include <gridformat/parallel/communication.hpp>
20#include <gridformat/common/type_traits.hpp>
21#include <gridformat/common/precision.hpp>
22#include <gridformat/common/concepts.hpp>
23#include <gridformat/common/field_storage.hpp>
24#include <gridformat/common/range_field.hpp>
25#include <gridformat/common/scalar_field.hpp>
26#include <gridformat/common/logging.hpp>
27#include <gridformat/common/output_file.hpp>
28
29#include <gridformat/grid/grid.hpp>
30#include <gridformat/grid/_detail.hpp>
31#include <gridformat/grid/entity_fields.hpp>
32
33namespace GridFormat {
34namespace Traits {
35
37template<typename Writer> struct WritesConnectivity : public std::true_type {};
38
40template<typename Writer>
42 static constexpr auto get(const Writer&) { return NullCommunicator{}; }
43};
44
46template<typename Writer>
47 requires( requires(const Writer& w) { { w.communicator() }; } )
49 static constexpr Concepts::Communicator decltype(auto) get(const Writer& w) {
50 return w.communicator();
51 }
52};
53
54} // namespace Traits
55
58
69
71template<typename G>
72class GridWriterBase {
73 public:
74 using Grid = G;
75 using Field = typename FieldStorage::Field;
76 using FieldPtr = typename FieldStorage::FieldPtr;
77
78 explicit GridWriterBase(const Grid& grid, std::optional<WriterOptions> opts)
79 : _grid(grid)
80 , _opts{std::move(opts)}
81 {}
82
88 template<std::ranges::range R>
89 void set_meta_data(const std::string& name, R&& range) {
90 static_assert(
91 !std::is_array_v<std::remove_cvref_t<R>>,
92 "Built-in arrays are not supported, please use std::array (e.g. via std::to_array)"
93 );
94 static_assert(
95 !std::is_lvalue_reference_v<R>,
96 "Cannot take ranges by reference, please move or pass a view (e.g. std::views::all) "
97 "to data that outlives the writer"
98 );
99 _meta_data.set(name, RangeField{std::move(range)});
100 }
101
102 template<std::size_t N>
103 void set_meta_data(const std::string& name, const char (&text)[N]) {
104 _meta_data.set(name, RangeField{std::to_array(text)});
105 }
106
107 void set_meta_data(const std::string& name, std::string text) {
108 if (_opts && _opts.value().append_null_terminator_to_strings)
109 text.push_back('\0');
110 _meta_data.set(name, RangeField{std::move(text)});
111 }
112
113 template<Concepts::Scalar T>
114 void set_meta_data(const std::string& name, T value) {
115 _meta_data.set(name, ScalarField{value});
116 }
117
118 template<std::derived_from<Field> F>
119 void set_meta_data(const std::string& name, F&& field) {
120 static_assert(!std::is_lvalue_reference_v<F>, "Cannot take metadata fields by reference, please move");
121 set_meta_data(name, make_field_ptr(std::move(field)));
122 }
123
124 void set_meta_data(const std::string& name, FieldPtr ptr) {
125 _meta_data.set(name, ptr);
126 }
127
128 FieldPtr remove_meta_data(const std::string& name) {
129 return _meta_data.pop(name);
130 }
131
132 template<Concepts::PointFunction<Grid> F, Concepts::Scalar T = GridDetail::PointFunctionScalarType<Grid, F>>
133 void set_point_field(const std::string& name, F&& point_function, const Precision<T>& prec = {}) {
134 static_assert(!std::is_lvalue_reference_v<F>, "Cannot take functions by reference, please move");
135 set_point_field(name, _make_point_field(std::move(point_function), prec));
136 }
137
138 template<std::derived_from<Field> F>
139 void set_point_field(const std::string& name, F&& field) {
140 static_assert(!std::is_lvalue_reference_v<F>, "Cannot take fields by reference, please move");
141 set_point_field(name, make_field_ptr(std::forward<F>(field)));
142 }
143
144 void set_point_field(const std::string& name, FieldPtr field_ptr) {
145 _point_fields.set(name, std::move(field_ptr));
146 }
147
148 FieldPtr remove_point_field(const std::string& name) {
149 return _point_fields.pop(name);
150 }
151
152 template<Concepts::CellFunction<Grid> F, Concepts::Scalar T = GridDetail::CellFunctionScalarType<Grid, F>>
153 void set_cell_field(const std::string& name, F&& cell_function, const Precision<T>& prec = {}) {
154 static_assert(!std::is_lvalue_reference_v<F>, "Cannot take functions by reference, please move");
155 set_cell_field(name, _make_cell_field(std::move(cell_function), prec));
156 }
157
158 template<std::derived_from<Field> F>
159 void set_cell_field(const std::string& name, F&& field) {
160 static_assert(!std::is_lvalue_reference_v<F>, "Cannot take fields by reference, please move");
161 set_cell_field(name, make_field_ptr(std::forward<F>(field)));
162 }
163
164 void set_cell_field(const std::string& name, FieldPtr field_ptr) {
165 _cell_fields.set(name, field_ptr);
166 }
167
168 FieldPtr remove_cell_field(const std::string& name) {
169 return _cell_fields.pop(name);
170 }
171
172 void clear() {
173 _meta_data.clear();
174 _point_fields.clear();
175 _cell_fields.clear();
176 }
177
178 void set_ignore_warnings(bool value) {
179 _ignore_warnings = value;
180 }
181
182 const Grid& grid() const {
183 return _grid;
184 }
185
186 const std::optional<WriterOptions>& writer_options() const {
187 return _opts;
188 }
189
190 template<typename Writer>
191 void copy_fields(Writer& w) const {
192 if (_opts.has_value() != w.writer_options().has_value())
193 throw TypeError("Cannot copy fields into writers with different options");
194 if (_opts.has_value() && writer_options().value() != w.writer_options().value())
195 throw TypeError("Cannot copy fields into writers with different options");
196
197 for (const auto& [name, field_ptr] : meta_data_fields(*this))
198 w.set_meta_data(name, field_ptr);
199 for (const auto& [name, field_ptr] : point_fields(*this))
200 w.set_point_field(name, field_ptr);
201 for (const auto& [name, field_ptr] : cell_fields(*this))
202 w.set_cell_field(name, field_ptr);
203 }
204
206 friend Concepts::RangeOf<std::pair<std::string, FieldPtr>> auto
207 point_fields_of_rank(unsigned int rank, const GridWriterBase& writer) {
208 return writer._point_field_names()
209 | std::views::filter([&, r=rank] (const std::string& n) {
210 return writer._get_point_field(n).layout().dimension() - 1 == r;
211 })
212 | std::views::transform([&] (std::string n) {
213 auto field_ptr = writer._get_point_field_ptr(n);
214 return std::make_pair(std::move(n), std::move(field_ptr));
215 });
216 }
217
219 friend Concepts::RangeOf<std::pair<std::string, FieldPtr>> auto
220 cell_fields_of_rank(unsigned int rank, const GridWriterBase& writer) {
221 return writer._cell_field_names()
222 | std::views::filter([&, r=rank] (const std::string& n) {
223 return writer._get_cell_field(n).layout().dimension() - 1 == r;
224 })
225 | std::views::transform([&] (std::string n) {
226 auto field_ptr = writer._get_cell_field_ptr(n);
227 return std::make_pair(std::move(n), std::move(field_ptr));
228 });
229 }
230
231 friend Concepts::RangeOf<std::pair<std::string, FieldPtr>> auto point_fields(const GridWriterBase& writer) {
232 return writer._point_field_names() | std::views::transform([&] (std::string n) {
233 auto field_ptr = writer._get_point_field_ptr(n);
234 return std::make_pair(std::move(n), std::move(field_ptr));
235 });
236 }
237
238 friend Concepts::RangeOf<std::pair<std::string, FieldPtr>> auto cell_fields(const GridWriterBase& writer) {
239 return writer._cell_field_names() | std::views::transform([&] (std::string n) {
240 auto field_ptr = writer._get_cell_field_ptr(n);
241 return std::make_pair(std::move(n), std::move(field_ptr));
242 });
243 }
244
245 friend Concepts::RangeOf<std::pair<std::string, FieldPtr>> auto meta_data_fields(const GridWriterBase& writer) {
246 return writer._meta_data_field_names() | std::views::transform([&] (std::string n) {
247 auto field_ptr = writer._get_meta_data_field_ptr(n);
248 return std::make_pair(std::move(n), std::move(field_ptr));
249 });
250 }
251
252 protected:
253 void _log_warning(std::string_view warning) const {
254 if (!_ignore_warnings)
255 log_warning(
256 std::string{warning}
257 + (warning.ends_with("\n") ? "" : "\n")
258 + "To deactivate this warning, call set_ignore_warnings(true);"
259 );
260 }
261
262 template<typename EntityFunction, Concepts::Scalar T>
263 auto _make_point_field(EntityFunction&& f, const Precision<T>& prec) const {
264 if (_opts.has_value())
265 return PointField{_grid, std::move(f), _opts.value().use_structured_grid_ordering, prec};
266 return PointField{_grid, std::move(f), false, prec};
267 }
268
269 template<typename EntityFunction, Concepts::Scalar T>
270 auto _make_cell_field(EntityFunction&& f, const Precision<T>& prec) const {
271 if (_opts.has_value())
272 return CellField{_grid, std::move(f), _opts.value().use_structured_grid_ordering, prec};
273 return CellField{_grid, std::move(f), false, prec};
274 }
275
276 std::ranges::range auto _point_field_names() const {
277 return _point_fields.field_names();
278 }
279
280 std::ranges::range auto _cell_field_names() const {
281 return _cell_fields.field_names();
282 }
283
284 const Field& _get_point_field(const std::string& name) const {
285 return _point_fields.get(name);
286 }
287
288 FieldPtr _get_point_field_ptr(const std::string& name) const {
289 return _point_fields.get_ptr(name);
290 }
291
292 const Field& _get_cell_field(const std::string& name) const {
293 return _cell_fields.get(name);
294 }
295
296 FieldPtr _get_cell_field_ptr(const std::string& name) const {
297 return _cell_fields.get_ptr(name);
298 }
299
300 std::ranges::range auto _meta_data_field_names() const {
301 return _meta_data.field_names();
302 }
303
304 const Field& _get_meta_data_field(const std::string& name) const {
305 return _meta_data.get(name);
306 }
307
308 FieldPtr _get_meta_data_field_ptr(const std::string& name) const {
309 return _meta_data.get_ptr(name);
310 }
311
312 private:
313 const Grid& _grid;
314 FieldStorage _point_fields;
315 FieldStorage _cell_fields;
316 FieldStorage _meta_data;
317 std::optional<WriterOptions> _opts;
318 bool _ignore_warnings = false;
319};
320
322template<typename Grid>
323class GridWriter : public GridWriterBase<Grid> {
324 public:
325 virtual ~GridWriter() = default;
326
327 GridWriter(GridWriter&&) = default;
328 GridWriter(const GridWriter&) = delete;
329 GridWriter& operator=(GridWriter&&) = default;
330 GridWriter& operator=(const GridWriter&) = delete;
331
332 explicit GridWriter(const Grid& grid, std::string extension, std::optional<WriterOptions> opts)
333 : GridWriterBase<Grid>(grid, std::move(opts))
334 , _extension(std::move(extension))
335 {}
336
337 std::string write(const std::string& filename) const {
338 std::string filename_with_ext = filename + _extension;
339 _write(filename_with_ext);
340 return filename_with_ext;
341 }
342
343 void write(std::ostream& s) const {
344 _write(s);
345 }
346
347 const std::string& extension() const {
348 return _extension;
349 }
350
351 private:
352 std::string _extension;
353
354 virtual void _write(const std::string& filename_with_ext) const {
355 write_to(filename_with_ext, [&] (std::ostream& s) { _write(s); });
356 }
357
358 virtual void _write(std::ostream&) const = 0;
359};
360
362template<typename Grid>
363class TimeSeriesGridWriter : public GridWriterBase<Grid> {
364 public:
365 virtual ~TimeSeriesGridWriter() = default;
366
367 TimeSeriesGridWriter(TimeSeriesGridWriter&&) = default;
368 TimeSeriesGridWriter(const TimeSeriesGridWriter&) = delete;
369 TimeSeriesGridWriter& operator=(TimeSeriesGridWriter&&) = default;
370 TimeSeriesGridWriter& operator=(const TimeSeriesGridWriter&) = delete;
371
372 explicit TimeSeriesGridWriter(const Grid& grid, std::optional<WriterOptions> opts)
373 : GridWriterBase<Grid>(grid, std::move(opts))
374 {}
375
376 std::string write(double t) {
377 std::string filename = _write(t);
378 _step_count++;
379 return filename;
380 }
381
382 protected:
383 unsigned _step_count = 0;
384
385 private:
386 virtual std::string _write(double) = 0;
387};
388
389#ifndef DOXYGEN
390namespace GridDetail {
391
392 template<bool is_transient, typename G>
393 struct WriterBase;
394 template<typename G>
395 struct WriterBase<true, G> : std::type_identity<TimeSeriesGridWriter<G>> {};
396 template<typename G>
397 struct WriterBase<false, G> : std::type_identity<GridWriter<G>> {};
398
399} // namespace GridDetail
400#endif // DOXYGEN
401
403
404} // namespace GridFormat
405
406#endif // GRIDFORMAT_GRID_WRITER_HPP_
Base class for all writer implementations.
Definition writer.hpp:72
void set_meta_data(const std::string &name, R &&range)
Definition writer.hpp:89
friend Concepts::RangeOf< std::pair< std::string, FieldPtr > > auto cell_fields_of_rank(unsigned int rank, const GridWriterBase &writer)
Return a range over the fields with the given rank (0=scalars, 1=vectors, 2=tensors).
Definition writer.hpp:220
friend Concepts::RangeOf< std::pair< std::string, FieldPtr > > auto point_fields_of_rank(unsigned int rank, const GridWriterBase &writer)
Return a range over the fields with the given rank (0=scalars, 1=vectors, 2=tensors).
Definition writer.hpp:207
Interface to the writers for all supported file formats. Depending on the chosen format,...
Definition writer.hpp:90
FieldPtr make_field_ptr(F &&f)
Factory function for field pointers.
Definition field.hpp:211
Can be specialized by parallel writers to expose their underlying communicator.
Definition writer.hpp:41
Can be specialized by writers in case the file format does not contain connectivity information.
Definition writer.hpp:37
Options that writer implementations can pass to the base class.
Definition writer.hpp:60
bool append_null_terminator_to_strings
If true, '\0' is appended to string meta data.
Definition writer.hpp:62
bool use_structured_grid_ordering
Use row-major structured grid ordering.
Definition writer.hpp:61