GridFormat 0.2.1
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 <string>
12#include <utility>
13#include <ranges>
14#include <fstream>
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
28#include <gridformat/grid/grid.hpp>
29#include <gridformat/grid/_detail.hpp>
30#include <gridformat/grid/entity_fields.hpp>
31
32namespace GridFormat {
33namespace Traits {
34
36template<typename Writer> struct WritesConnectivity : public std::true_type {};
37
39template<typename Writer>
41 static constexpr auto get(const Writer&) { return NullCommunicator{}; }
42};
43
45template<typename Writer>
46 requires( requires(const Writer& w) { { w.communicator() }; } )
48 static constexpr Concepts::Communicator decltype(auto) get(const Writer& w) {
49 return w.communicator();
50 }
51};
52
53} // namespace Traits
54
57
62
63 bool operator==(const WriterOptions& other) const {
66 }
67};
68
70template<typename G>
72 public:
73 using Grid = G;
74 using Field = typename FieldStorage::Field;
75 using FieldPtr = typename FieldStorage::FieldPtr;
76
77 explicit GridWriterBase(const Grid& grid, std::optional<WriterOptions> opts)
78 : _grid(grid)
79 , _opts{std::move(opts)}
80 {}
81
82 template<std::ranges::range R>
83 void set_meta_data(const std::string& name, R&& range) {
84 _meta_data.set(name, RangeField{std::forward<R>(range)});
85 }
86
87 void set_meta_data(const std::string& name, std::string text) {
88 if (_opts && _opts.value().append_null_terminator_to_strings)
89 text.push_back('\0');
90 _meta_data.set(name, RangeField{std::move(text)});
91 }
92
93 template<Concepts::Scalar T>
94 void set_meta_data(const std::string& name, T value) {
95 _meta_data.set(name, ScalarField{value});
96 }
97
98 template<std::derived_from<Field> F>
99 void set_meta_data(const std::string& name, F&& field) {
100 static_assert(!std::is_lvalue_reference_v<F>, "Cannot take metadata fields by reference, please move");
101 set_meta_data(name, make_field_ptr(std::move(field)));
102 }
103
104 void set_meta_data(const std::string& name, FieldPtr ptr) {
105 _meta_data.set(name, ptr);
106 }
107
108 FieldPtr remove_meta_data(const std::string& name) {
109 return _meta_data.pop(name);
110 }
111
112 template<Concepts::PointFunction<Grid> F, Concepts::Scalar T = GridDetail::PointFunctionScalarType<Grid, F>>
113 void set_point_field(const std::string& name, F&& point_function, const Precision<T>& prec = {}) {
114 static_assert(!std::is_lvalue_reference_v<F>, "Cannot take functions by reference, please move");
115 set_point_field(name, _make_point_field(std::move(point_function), prec));
116 }
117
118 template<std::derived_from<Field> F>
119 void set_point_field(const std::string& name, F&& field) {
120 static_assert(!std::is_lvalue_reference_v<F>, "Cannot take fields by reference, please move");
121 set_point_field(name, make_field_ptr(std::forward<F>(field)));
122 }
123
124 void set_point_field(const std::string& name, FieldPtr field_ptr) {
125 _point_fields.set(name, std::move(field_ptr));
126 }
127
128 FieldPtr remove_point_field(const std::string& name) {
129 return _point_fields.pop(name);
130 }
131
132 template<Concepts::CellFunction<Grid> F, Concepts::Scalar T = GridDetail::CellFunctionScalarType<Grid, F>>
133 void set_cell_field(const std::string& name, F&& cell_function, const Precision<T>& prec = {}) {
134 static_assert(!std::is_lvalue_reference_v<F>, "Cannot take functions by reference, please move");
135 set_cell_field(name, _make_cell_field(std::move(cell_function), prec));
136 }
137
138 template<std::derived_from<Field> F>
139 void set_cell_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_cell_field(name, make_field_ptr(std::forward<F>(field)));
142 }
143
144 void set_cell_field(const std::string& name, FieldPtr field_ptr) {
145 _cell_fields.set(name, field_ptr);
146 }
147
148 FieldPtr remove_cell_field(const std::string& name) {
149 return _cell_fields.pop(name);
150 }
151
152 void clear() {
153 _meta_data.clear();
154 _point_fields.clear();
155 _cell_fields.clear();
156 }
157
158 void set_ignore_warnings(bool value) {
159 _ignore_warnings = value;
160 }
161
162 const Grid& grid() const {
163 return _grid;
164 }
165
166 const std::optional<WriterOptions>& writer_options() const {
167 return _opts;
168 }
169
170 template<typename Writer>
171 void copy_fields(Writer& w) const {
172 if (_opts.has_value() != w.writer_options().has_value())
173 throw TypeError("Cannot copy fields into writers with different options");
174 if (_opts.has_value() && writer_options().value() != w.writer_options().value())
175 throw TypeError("Cannot copy fields into writers with different options");
176
177 for (const auto& [name, field_ptr] : meta_data_fields(*this))
178 w.set_meta_data(name, field_ptr);
179 for (const auto& [name, field_ptr] : point_fields(*this))
180 w.set_point_field(name, field_ptr);
181 for (const auto& [name, field_ptr] : cell_fields(*this))
182 w.set_cell_field(name, field_ptr);
183 }
184
186 friend Concepts::RangeOf<std::pair<std::string, FieldPtr>> auto
187 point_fields_of_rank(unsigned int rank, const GridWriterBase& writer) {
188 return writer._point_field_names()
189 | std::views::filter([&, r=rank] (const std::string& n) {
190 return writer._get_point_field(n).layout().dimension() - 1 == r;
191 })
192 | std::views::transform([&] (std::string n) {
193 auto field_ptr = writer._get_point_field_ptr(n);
194 return std::make_pair(std::move(n), std::move(field_ptr));
195 });
196 }
197
199 friend Concepts::RangeOf<std::pair<std::string, FieldPtr>> auto
200 cell_fields_of_rank(unsigned int rank, const GridWriterBase& writer) {
201 return writer._cell_field_names()
202 | std::views::filter([&, r=rank] (const std::string& n) {
203 return writer._get_cell_field(n).layout().dimension() - 1 == r;
204 })
205 | std::views::transform([&] (std::string n) {
206 auto field_ptr = writer._get_cell_field_ptr(n);
207 return std::make_pair(std::move(n), std::move(field_ptr));
208 });
209 }
210
211 friend Concepts::RangeOf<std::pair<std::string, FieldPtr>> auto point_fields(const GridWriterBase& writer) {
212 return writer._point_field_names() | 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
218 friend Concepts::RangeOf<std::pair<std::string, FieldPtr>> auto cell_fields(const GridWriterBase& writer) {
219 return writer._cell_field_names() | std::views::transform([&] (std::string n) {
220 auto field_ptr = writer._get_cell_field_ptr(n);
221 return std::make_pair(std::move(n), std::move(field_ptr));
222 });
223 }
224
225 friend Concepts::RangeOf<std::pair<std::string, FieldPtr>> auto meta_data_fields(const GridWriterBase& writer) {
226 return writer._meta_data_field_names() | std::views::transform([&] (std::string n) {
227 auto field_ptr = writer._get_meta_data_field_ptr(n);
228 return std::make_pair(std::move(n), std::move(field_ptr));
229 });
230 }
231
232 protected:
233 void _log_warning(std::string_view warning) const {
234 if (!_ignore_warnings)
235 log_warning(
236 std::string{warning}
237 + (warning.ends_with("\n") ? "" : "\n")
238 + "To deactivate this warning, call set_ignore_warnings(true);"
239 );
240 }
241
242 template<typename EntityFunction, Concepts::Scalar T>
243 auto _make_point_field(EntityFunction&& f, const Precision<T>& prec) const {
244 if (_opts.has_value())
245 return PointField{_grid, std::move(f), _opts.value().use_structured_grid_ordering, prec};
246 return PointField{_grid, std::move(f), false, prec};
247 }
248
249 template<typename EntityFunction, Concepts::Scalar T>
250 auto _make_cell_field(EntityFunction&& f, const Precision<T>& prec) const {
251 if (_opts.has_value())
252 return CellField{_grid, std::move(f), _opts.value().use_structured_grid_ordering, prec};
253 return CellField{_grid, std::move(f), false, prec};
254 }
255
256 std::ranges::range auto _point_field_names() const {
257 return _point_fields.field_names();
258 }
259
260 std::ranges::range auto _cell_field_names() const {
261 return _cell_fields.field_names();
262 }
263
264 const Field& _get_point_field(const std::string& name) const {
265 return _point_fields.get(name);
266 }
267
268 FieldPtr _get_point_field_ptr(const std::string& name) const {
269 return _point_fields.get_ptr(name);
270 }
271
272 const Field& _get_cell_field(const std::string& name) const {
273 return _cell_fields.get(name);
274 }
275
276 FieldPtr _get_cell_field_ptr(const std::string& name) const {
277 return _cell_fields.get_ptr(name);
278 }
279
280 std::ranges::range auto _meta_data_field_names() const {
281 return _meta_data.field_names();
282 }
283
284 const Field& _get_meta_data_field(const std::string& name) const {
285 return _meta_data.get(name);
286 }
287
288 FieldPtr _get_meta_data_field_ptr(const std::string& name) const {
289 return _meta_data.get_ptr(name);
290 }
291
292 private:
293 const Grid& _grid;
294 FieldStorage _point_fields;
295 FieldStorage _cell_fields;
296 FieldStorage _meta_data;
297 std::optional<WriterOptions> _opts;
298 bool _ignore_warnings = false;
299};
300
302template<typename Grid>
303class GridWriter : public GridWriterBase<Grid> {
304 public:
305 virtual ~GridWriter() = default;
306
307 GridWriter(GridWriter&&) = default;
308 GridWriter(const GridWriter&) = delete;
309 GridWriter& operator=(GridWriter&&) = default;
310 GridWriter& operator=(const GridWriter&) = delete;
311
312 explicit GridWriter(const Grid& grid, std::string extension, std::optional<WriterOptions> opts)
313 : GridWriterBase<Grid>(grid, std::move(opts))
314 , _extension(std::move(extension))
315 {}
316
317 std::string write(const std::string& filename) const {
318 std::string filename_with_ext = filename + _extension;
319 _write(filename_with_ext);
320 return filename_with_ext;
321 }
322
323 void write(std::ostream& s) const {
324 _write(s);
325 }
326
327 const std::string& extension() const {
328 return _extension;
329 }
330
331 private:
332 std::string _extension;
333
334 virtual void _write(const std::string& filename_with_ext) const {
335 std::ofstream result_file(filename_with_ext, std::ios::out);
336 _write(result_file);
337 }
338
339 virtual void _write(std::ostream&) const = 0;
340};
341
343template<typename Grid>
345 public:
346 virtual ~TimeSeriesGridWriter() = default;
347
350 TimeSeriesGridWriter& operator=(TimeSeriesGridWriter&&) = default;
351 TimeSeriesGridWriter& operator=(const TimeSeriesGridWriter&) = delete;
352
353 explicit TimeSeriesGridWriter(const Grid& grid, std::optional<WriterOptions> opts)
354 : GridWriterBase<Grid>(grid, std::move(opts))
355 {}
356
357 std::string write(double t) {
358 std::string filename = _write(t);
359 _step_count++;
360 return filename;
361 }
362
363 protected:
364 unsigned _step_count = 0;
365
366 private:
367 virtual std::string _write(double) = 0;
368};
369
370#ifndef DOXYGEN
371namespace GridDetail {
372
373 template<bool is_transient, typename G>
374 struct WriterBase;
375 template<typename G>
376 struct WriterBase<true, G> : std::type_identity<TimeSeriesGridWriter<G>> {};
377 template<typename G>
378 struct WriterBase<false, G> : std::type_identity<GridWriter<G>> {};
379
380} // namespace GridDetail
381#endif // DOXYGEN
382
384
385} // namespace GridFormat
386
387#endif // GRIDFORMAT_GRID_WRITER_HPP_
Base class for all writer implementations.
Definition: writer.hpp:71
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:200
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:187
Abstract base class for grid file writers.
Definition: writer.hpp:303
Abstract base class for time series file writers.
Definition: writer.hpp:344
Interface to the writers for all supported file formats. Depending on the chosen format,...
Definition: writer.hpp:90
void set_point_field(const std::string &name, Field &&field)
Set a point data field to be added to the output.
Definition: writer.hpp:210
const std::optional< WriterOptions > & writer_options() const
Return the basic options used by this writer.
Definition: writer.hpp:317
void set_cell_field(const std::string &name, Field &&field)
Set a cell data field to be added to the output.
Definition: writer.hpp:239
void set_meta_data(const std::string &name, Field &&field)
Set a meta data field to be added to the output.
Definition: writer.hpp:194
FieldPtr make_field_ptr(F &&f)
Factory function for field pointers.
Definition: field.hpp:192
Can be specialized by parallel writers to expose their underlying communicator.
Definition: writer.hpp:40
Can be specialized by writers in case the file format does not contain connectivity information.
Definition: writer.hpp:36
Options that writer implementations can pass to the base class.
Definition: writer.hpp:59
bool append_null_terminator_to_strings
If true, '\0' is appended to string meta data.
Definition: writer.hpp:61
bool use_structured_grid_ordering
Use row-major structured grid ordering.
Definition: writer.hpp:60