GridFormat 0.4.0
I/O-Library for grid-like data structures
Loading...
Searching...
No Matches
converter.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_CONVERTER_HPP_
9#define GRIDFORMAT_GRID_CONVERTER_HPP_
10
11#include <array>
12#include <vector>
13#include <ranges>
14#include <concepts>
15#include <utility>
16#include <numeric>
17#include <optional>
18#include <cstdint>
19
21#include <gridformat/common/exceptions.hpp>
22
23#include <gridformat/grid/cell_type.hpp>
26
27namespace GridFormat {
28
29#ifndef DOXYGEN
30namespace ConverterDetail {
31
32 struct ConverterGrid {
33 const GridReader& reader;
34 std::vector<std::array<double, 3>> points;
35 std::vector<std::pair<CellType, std::vector<std::size_t>>> cells;
36
37 explicit ConverterGrid(const GridReader& r) : reader{r} {}
38
39 void make_grid() {
40 points.clear();
41 cells.clear();
42 _make_points();
43 _make_cells();
44 }
45
46 private:
47 void _make_points() {
48 const auto in_points = reader.points();
49 const auto in_layout = in_points->layout();
50 const auto in_np = in_layout.extent(0);
51 const auto in_dim = in_layout.dimension() > 1 ? in_layout.extent(1) : 0;
52 if (in_np != reader.number_of_points())
53 throw SizeError("Mismatch between stored and defined number of points.");
54
55 points.reserve(in_np);
56 in_points->visit_field_values([&] <typename T> (std::span<const T> values) {
57 std::ranges::for_each(std::views::iota(std::size_t{0}, in_np), [&] (auto p_idx) {
58 points.push_back({});
59 std::ranges::for_each(std::views::iota(std::size_t{0}, in_dim), [&] (auto dim) {
60 points.back().at(dim) = static_cast<double>(values[p_idx*in_dim + dim]);
61 });
62 });
63 });
64 }
65
66 void _make_cells() {
67 cells.reserve(reader.number_of_cells());
68 reader.visit_cells([&] (CellType ct, std::vector<std::size_t> corners) {
69 cells.emplace_back(std::pair{std::move(ct), std::move(corners)});
70 });
71 if (cells.size() != reader.number_of_cells())
72 throw SizeError("Mismatch between stored and defined number of cells.");
73 }
74 };
75
76 template<typename T>
77 concept Writer
78 = requires { typename std::remove_cvref_t<T>::Grid; }
79 and std::derived_from<std::remove_cvref_t<T>, GridWriterBase<typename T::Grid>>;
80
81 template<typename T>
82 concept PieceWriter = Writer<T> and std::derived_from<std::remove_cvref_t<T>, GridWriter<typename T::Grid>>;
83
84 template<typename T>
85 concept TimeSeriesWriter = Writer<T> and std::derived_from<std::remove_cvref_t<T>, TimeSeriesGridWriter<typename T::Grid>>;
86
87 template<typename T>
88 concept PieceWriterFactory = requires (const T& factory, const ConverterGrid& grid) {
89 { factory(grid) } -> PieceWriter;
90 };
91
92 template<typename T>
93 concept TimeSeriesWriterFactory = requires (const T& factory, const ConverterGrid& grid, const std::string& filename) {
94 { factory(grid) } -> TimeSeriesWriter;
95 };
96
97 template<typename T>
98 concept WriterFactory = PieceWriterFactory<T> or TimeSeriesWriterFactory<T>;
99
100 template<typename Reader, Writer Writer>
101 void add_piece_fields(const Reader& reader, Writer& writer) {
102 writer.clear();
103 for (auto [name, field_ptr] : cell_fields(reader))
104 writer.set_cell_field(std::move(name), std::move(field_ptr));
105 for (auto [name, field_ptr] : point_fields(reader))
106 writer.set_point_field(std::move(name), std::move(field_ptr));
107 for (auto [name, field_ptr] : meta_data_fields(reader))
108 writer.set_meta_data(std::move(name), std::move(field_ptr));
109 }
110
111 template<typename Reader, PieceWriter Writer>
112 std::string write_piece(const Reader& reader, Writer& writer, const std::string& filename) {
113 add_piece_fields(reader, writer);
114 return writer.write(filename);
115 }
116
117 template<typename Reader, TimeSeriesWriter Writer>
118 std::string write_piece(const Reader& reader, Writer& writer, double time_step) {
119 add_piece_fields(reader, writer);
120 return writer.write(time_step);
121 }
122
123} // namespace ConverterDetail
124#endif // DOXYGEN
125
133template<std::derived_from<GridReader> Reader, ConverterDetail::WriterFactory Factory>
134std::string convert(const Reader& reader, const std::string& filename, const Factory& factory) {
135 ConverterDetail::ConverterGrid grid{reader};
136 auto writer = factory(grid);
137 if (reader.filename() == filename + writer.extension())
138 throw GridFormat::IOError("Cannot read/write from/to the same file");
139 if constexpr (Traits::WritesConnectivity<std::remove_cvref_t<decltype(writer)>>::value)
140 grid.make_grid();
141 return ConverterDetail::write_piece(reader, writer, filename);
142}
143
151template<std::derived_from<GridReader> Reader,
152 ConverterDetail::TimeSeriesWriterFactory Factory,
153 std::invocable<std::size_t, const std::string&> StepCallBack = decltype([] (std::size_t, const std::string&) {})>
154std::string convert(Reader& reader,
155 const Factory& factory,
156 const StepCallBack& call_back = {}) {
157 if (!reader.is_sequence())
158 throw ValueError("Cannot convert data from reader to a sequence as the file read is no sequence.");
159
160 ConverterDetail::ConverterGrid grid{reader};
161 auto writer = factory(grid);
162 std::string filename;
163 for (std::size_t step = 0; step < reader.number_of_steps(); ++step) {
164 reader.set_step(step);
165 if constexpr (Traits::WritesConnectivity<std::remove_cvref_t<decltype(writer)>>::value)
166 grid.make_grid();
167 filename = ConverterDetail::write_piece(reader, writer, reader.time_at_step(step));
168 call_back(step, filename);
169 }
170 return filename;
171}
172
173namespace Traits {
174
175// to distinguish points/cells we use different integer types
176
177template<>
178struct Points<ConverterDetail::ConverterGrid> {
179 static std::ranges::range auto get(const ConverterDetail::ConverterGrid& grid) {
180 return std::views::iota(std::size_t{0}, grid.reader.number_of_points());
181 }
182};
183
184template<>
185struct Cells<ConverterDetail::ConverterGrid> {
186 static std::ranges::range auto get(const ConverterDetail::ConverterGrid& grid) {
187 const auto max = static_cast<std::int64_t>(grid.reader.number_of_cells());
188 if (max < 0)
189 throw TypeError("Integer overflow. Too many grid cells.");
190 return std::views::iota(std::int64_t{0}, max);
191 }
192};
193
194template<>
195struct NumberOfPoints<ConverterDetail::ConverterGrid> {
196 static std::size_t get(const ConverterDetail::ConverterGrid& grid) {
197 return grid.reader.number_of_points();
198 }
199};
200
201template<>
202struct NumberOfCells<ConverterDetail::ConverterGrid> {
203 static std::size_t get(const ConverterDetail::ConverterGrid& grid) {
204 return grid.reader.number_of_cells();
205 }
206};
207
208template<>
209struct CellPoints<ConverterDetail::ConverterGrid, std::int64_t> {
210 static std::ranges::range auto get(const ConverterDetail::ConverterGrid& grid, const std::int64_t i) {
211 return grid.cells.at(i).second | std::views::all;
212 }
213};
214
215template<>
216struct CellType<ConverterDetail::ConverterGrid, std::int64_t> {
217 static GridFormat::CellType get(const ConverterDetail::ConverterGrid& grid, const std::int64_t i) {
218 return grid.cells.at(i).first;
219 }
220};
221
222template<>
223struct PointCoordinates<ConverterDetail::ConverterGrid, std::size_t> {
224 static std::array<double, 3> get(const ConverterDetail::ConverterGrid& grid, const std::size_t i) {
225 return grid.points.at(i);
226 }
227};
228
229template<>
230struct PointId<ConverterDetail::ConverterGrid, std::size_t> {
231 static std::size_t get(const ConverterDetail::ConverterGrid&, const std::size_t i) {
232 return i;
233 }
234};
235
236template<>
237struct NumberOfCellPoints<ConverterDetail::ConverterGrid, std::int64_t> {
238 static std::size_t get(const ConverterDetail::ConverterGrid& grid, const std::int64_t i) {
239 return grid.cells.at(i).second.size();
240 }
241};
242
243template<>
244struct Origin<ConverterDetail::ConverterGrid> {
245 static std::array<double, 3> get(const ConverterDetail::ConverterGrid& grid) {
246 return grid.reader.origin();
247 }
248};
249
250template<>
251struct Spacing<ConverterDetail::ConverterGrid> {
252 static std::array<double, 3> get(const ConverterDetail::ConverterGrid& grid) {
253 return grid.reader.spacing();
254 }
255};
256
257template<>
258struct Basis<ConverterDetail::ConverterGrid> {
259 static std::array<std::array<double, 3>, 3> get(const ConverterDetail::ConverterGrid& grid) {
260 std::array<std::array<double, 3>, 3> result;
261 for (unsigned int i = 0; i < 3; ++i)
262 result[i] = grid.reader.basis_vector(i);
263 return result;
264 }
265};
266
267template<>
268struct Extents<ConverterDetail::ConverterGrid> {
269 static std::array<std::size_t, 3> get(const ConverterDetail::ConverterGrid& grid) {
270 return grid.reader.extents();
271 }
272};
273
274template<>
275struct Ordinates<ConverterDetail::ConverterGrid> {
276 static std::vector<double> get(const ConverterDetail::ConverterGrid& grid, unsigned int i) {
277 return grid.reader.ordinates(i);
278 }
279};
280
281template<typename Entity>
282struct Location<ConverterDetail::ConverterGrid, Entity> {
283 static std::array<std::size_t, 3> get(const ConverterDetail::ConverterGrid& grid, const std::size_t point) {
284 return _get(Ranges::incremented(Extents<ConverterDetail::ConverterGrid>::get(grid), 1), point);
285 }
286
287 static std::array<std::size_t, 3> get(const ConverterDetail::ConverterGrid& grid, const std::int64_t cell) {
288 return _get(Extents<ConverterDetail::ConverterGrid>::get(grid), cell);
289 }
290
291 private:
292 static std::array<std::size_t, 3> _get(std::ranges::range auto extents, std::integral auto index) {
293 // avoid zero extents
294 std::ranges::for_each(extents, [] <std::integral T> (T& e) { e = std::max(e, T{1}); });
295 const auto accumulate_until = [&] (int dim) {
296 auto range = extents | std::views::take(dim);
297 return std::accumulate(
298 std::ranges::begin(range),
299 std::ranges::end(range),
300 std::size_t{1},
301 std::multiplies{}
302 );
303 };
304
305 const auto divisor1 = accumulate_until(1);
306 const auto divisor2 = accumulate_until(2);
307 return {
308 index%divisor2%divisor1,
309 index%divisor2/divisor1,
310 index/divisor2
311 };
312 }
313};
314
315} // namespace Traits
316} // namespace GridFormat
317
318#endif // GRIDFORMAT_GRID_CONVERTER_HPP_
bool is_sequence() const
Return true if the read file is a sequence.
Definition: reader.hpp:152
const std::string & filename() const
Return the name of the opened grid file (empty string until open() is called)
Definition: reader.hpp:92
void set_step(std::size_t step_idx)
Set the step from which to read data (only available for sequence formats)
Definition: reader.hpp:167
double time_at_step(std::size_t step_idx) const
Return the time at the current step (only available for sequence formats)
Definition: reader.hpp:162
std::size_t number_of_steps() const
Return the number of available steps (only available for sequence formats)
Definition: reader.hpp:157
Interface to the readers for all supported file formats.
Definition: reader.hpp:94
Base class for grid data readers.
Base classes for grid data writers.
std::string convert(const std::string &in, const std::string &out, const ConversionOptions< OutFormat, InFormat > &opts, const Communicator &communicator={})
Convert between parallel grid file formats.
Definition: gridformat.hpp:1004
Can be specialized by writers in case the file format does not contain connectivity information.
Definition: writer.hpp:36