GridFormat 0.5.0
I/O-Library for grid-like data structures
Loading...
Searching...
No Matches
common.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_VTK_COMMON_HPP_
9#define GRIDFORMAT_VTK_COMMON_HPP_
10
11#include <ranges>
12#include <cassert>
13#include <utility>
14#include <type_traits>
15#include <algorithm>
16#include <array>
17#include <cmath>
18
20#include <gridformat/common/concepts.hpp>
21#include <gridformat/common/exceptions.hpp>
22#include <gridformat/common/precision.hpp>
23#include <gridformat/common/serialization.hpp>
24#include <gridformat/common/md_layout.hpp>
25#include <gridformat/common/ranges.hpp>
26#include <gridformat/common/matrix.hpp>
27#include <gridformat/common/type_traits.hpp>
28#include <gridformat/common/string_conversion.hpp>
29#include <gridformat/common/flat_index_mapper.hpp>
30#include <gridformat/common/field_transformations.hpp>
32
33#include <gridformat/grid/entity_fields.hpp>
34#include <gridformat/grid/cell_type.hpp>
36#include <gridformat/grid/_detail.hpp>
37#include <gridformat/grid/grid.hpp>
38
39#ifndef DOXYGEN
40namespace GridFormat::Encoding { struct Ascii; struct Base64; struct RawBinary; }
41#endif // DOXYGEN
42
43namespace GridFormat::VTK {
44
47
48namespace DataFormat {
49
50struct Inlined {};
51struct Appended {};
52
53inline constexpr Inlined inlined;
54inline constexpr Appended appended;
55
56} // namespace DataFormat
57
58#ifndef DOXYGEN
59namespace Traits {
60
61template<typename T> struct ProducesValidXML;
62template<> struct ProducesValidXML<Encoding::Ascii> : public std::true_type {};
63template<> struct ProducesValidXML<Encoding::Base64> : public std::true_type {};
64template<> struct ProducesValidXML<Encoding::RawBinary> : public std::false_type {};
65
66} // namespace Traits
67
68template<typename Encoder>
69inline constexpr bool produces_valid_xml(const Encoder&) {
70 static_assert(
71 is_complete<Traits::ProducesValidXML<Encoder>>,
72 "Traits::ProducesValidXML was not specialized for the given encoder"
73 );
74 return Traits::ProducesValidXML<Encoder>::value;
75}
76
77inline constexpr std::uint8_t cell_type_number(CellType t) {
78 switch (t) {
79 case (CellType::vertex): return 1;
80 case (CellType::segment): return 3;
81 case (CellType::polyline): return 4;
82 case (CellType::triangle): return 5;
83 case (CellType::pixel): return 8;
84 case (CellType::quadrilateral): return 9;
85 case (CellType::polygon): return 7;
86 case (CellType::tetrahedron): return 10;
87 case (CellType::hexahedron): return 12;
88 case (CellType::voxel): return 11;
89 case (CellType::lagrange_segment): return 68;
90 case (CellType::lagrange_triangle): return 69;
91 case (CellType::lagrange_quadrilateral): return 70;
92 case (CellType::lagrange_tetrahedron): return 71;
93 case (CellType::lagrange_hexahedron): return 72;
94 }
95
96 throw NotImplemented("VTK cell type number for the given cell type");
97}
98
99inline constexpr CellType cell_type(std::uint8_t vtk_id) {
100 switch (vtk_id) {
101 case 1: return CellType::vertex;
102 case 3: return CellType::segment;
103 case 4: return CellType::polyline;
104 case 5: return CellType::triangle;
105 case 8: return CellType::pixel;
106 case 9: return CellType::quadrilateral;
107 case 7: return CellType::polygon;
108 case 10: return CellType::tetrahedron;
109 case 12: return CellType::hexahedron;
110 case 11: return CellType::voxel;
111 case 68: return CellType::lagrange_segment;
112 case 69: return CellType::lagrange_triangle;
113 case 70: return CellType::lagrange_quadrilateral;
114 case 71: return CellType::lagrange_tetrahedron;
115 case 72: return CellType::lagrange_hexahedron;
116 }
117
118 throw NotImplemented("Cell type for the given VTK cell type number: " + std::to_string(vtk_id));
119}
120
121FieldPtr make_vtk_field(FieldPtr field) {
122 const auto layout = field->layout();
123 if (layout.dimension() < 2)
124 return field;
125 // (maybe) make vector/tensor fields 3d
126 if (std::ranges::all_of(
127 std::views::iota(std::size_t{1}, layout.dimension()),
128 [&] (const std::size_t codim) { return layout.extent(codim) < 3; }
129 ))
130 return transform(field, FieldTransformation::extend_all_to(3));
131 return field;
132}
133
134template<std::derived_from<Field> F>
135 requires(!std::is_lvalue_reference_v<F>)
136FieldPtr make_vtk_field(F&& field) {
137 return make_vtk_field(make_field_ptr(std::forward<F>(field)));
138}
139
140template<typename ctype, GridDetail::ExposesPointRange Grid>
141auto make_coordinates_field(const Grid& grid, bool structured_grid_ordering) {
142 return make_vtk_field(PointField{
143 grid,
144 [&] (const auto& point) { return coordinates(grid, point); },
145 structured_grid_ordering,
146 Precision<ctype>{}
147 });
148}
149
150template<typename HeaderType = std::size_t, Concepts::UnstructuredGrid Grid, typename PointMap>
151 requires(std::is_lvalue_reference_v<PointMap>)
152auto make_connectivity_field(const Grid& grid, PointMap&& map) {
153 class ConnectivityField : public Field {
154 public:
155 explicit ConnectivityField(const Grid& g, PointMap&& map)
156 : _grid(g)
157 , _point_map{std::forward<PointMap>(map)} {
158 _num_values = 0;
159 std::ranges::for_each(cells(g), [&] (const auto& cell) {
160 _num_values += number_of_points(_grid, cell);
161 });
162 }
163
164 private:
165 MDLayout _layout() const override { return MDLayout{{_num_values}}; }
166 DynamicPrecision _precision() const override { return Precision<HeaderType>{}; }
167 Serialization _serialized() const override {
168 Serialization serialization(sizeof(HeaderType)*_num_values);
169 HeaderType* data = serialization.as_span_of<HeaderType>().data();
170
171 std::size_t i = 0;
172 std::ranges::for_each(cells(_grid), [&] (const auto& cell) {
173 std::ranges::for_each(points(_grid, cell), [&] (const auto& point) {
174 data[i] = _point_map.at(id(_grid, point));
175 i++;
176 });
177 });
178 return serialization;
179 }
180
181 const Grid& _grid;
182 LVReferenceOrValue<PointMap> _point_map;
183 HeaderType _num_values;
184 } _field{grid, std::forward<PointMap>(map)};
185
186 return make_vtk_field(std::move(_field));
187}
188
189template<typename HeaderType = std::size_t, Concepts::UnstructuredGrid Grid>
190auto make_offsets_field(const Grid& grid) {
191 class OffsetField : public Field {
192 public:
193 explicit OffsetField(const Grid& g)
194 : _grid(g)
195 , _num_cells{static_cast<HeaderType>(Ranges::size(cells(g)))}
196 {}
197
198 private:
199 MDLayout _layout() const override { return MDLayout{{_num_cells}}; }
200 DynamicPrecision _precision() const override { return Precision<HeaderType>{}; }
201 Serialization _serialized() const override {
202 Serialization serialization(sizeof(HeaderType)*_num_cells);
203 HeaderType* data = serialization.as_span_of<HeaderType>().data();
204
205 std::size_t i = 0;
206 HeaderType offset = 0;
207 std::ranges::for_each(cells(_grid), [&] (const auto& cell) {
208 offset += number_of_points(_grid, cell);
209 data[i] = offset;
210 i++;
211 });
212 return serialization;
213 }
214
215 const Grid& _grid;
216 HeaderType _num_cells;
217 } _field{grid};
218
219 return make_vtk_field(std::move(_field));
220}
221
222template<Concepts::UnstructuredGrid Grid>
223auto make_cell_types_field(const Grid& grid) {
224 return make_vtk_field(CellField{
225 grid,
226 [&] (const auto& cell) {
227 return VTK::cell_type_number(type(grid, cell));
228 },
229 false
230 });
231}
232
233inline auto active_array_attribute_for_rank(unsigned int rank) {
234 if (rank > 2)
235 throw ValueError("Rank must be <= 2");
236 static constexpr std::array attributes{"Scalars", "Vectors", "Tensors"};
237 return attributes[rank];
238}
239
240namespace CommonDetail {
241
242 template<Concepts::StaticallySizedRange R>
243 std::string number_string_3d(const R& r) {
244 static_assert(static_size<R> >= 1 || static_size<R> <= 3);
245 if constexpr (static_size<R> == 3)
246 return as_string(r);
247 if constexpr (static_size<R> == 2)
248 return as_string(r) + " 0";
249 if constexpr (static_size<R> == 1)
250 return as_string(r) + " 0 0";
251 }
252
253 template<Concepts::StaticallySizedMDRange<2> R>
254 std::string direction_string(const R& basis) {
255 Matrix m{basis};
256 m.transpose();
257
258 std::string result = "";
259 std::ranges::for_each(m, [&] (const auto& row) {
260 if (result != "")
261 result += " ";
262 result += number_string_3d(row);
263 });
264 for (int i = static_size<R>; i < 3; ++i)
265 result += " 0 0 0";
266 return result;
267 }
268
269 template<Concepts::StaticallySizedRange R1,
270 Concepts::StaticallySizedRange R2>
271 requires(std::integral<std::ranges::range_value_t<R1>> and
272 std::integral<std::ranges::range_value_t<R2>>)
273 std::array<std::size_t, 6> get_extents(const R1& from, const R2& to) {
274 static_assert(static_size<R1> == static_size<R2>);
275 static_assert(static_size<R1> <= 3);
276
277 int i = 0;
278 auto result = Ranges::filled_array<6>(std::size_t{0});
279 auto it1 = std::ranges::begin(from);
280 auto it2 = std::ranges::begin(to);
281 for (; it1 != std::ranges::end(from); ++it1, ++it2, ++i) {
282 result[i*2 + 0] = *it1;
283 result[i*2 + 1] = *it2;
284 }
285 return result;
286 }
287
288 template<Concepts::StaticallySizedRange R>
289 std::array<std::size_t, 6> get_extents(const R& to) {
290 using T = std::ranges::range_value_t<R>;
291 return get_extents(Ranges::filled_array<static_size<R>>(T{0}), to);
292 }
293
294 template<Concepts::StaticallySizedRange R1,
295 Concepts::StaticallySizedRange R2>
296 std::string extents_string(const R1& from, const R2& to) {
297 return as_string(get_extents(from, to));
298 }
299
300 template<Concepts::StaticallySizedRange R>
301 std::string extents_string(const R& r) {
302 using T = std::ranges::range_value_t<R>;
303 return extents_string(Ranges::filled_array<static_size<R>>(T{0}), r);
304 }
305
306 template<Concepts::StructuredEntitySet Grid>
307 requires(!Concepts::StaticallySizedRange<Grid>)
308 std::string extents_string(const Grid& grid) {
309 return extents_string(extents(grid));
310 }
311
312 template<Concepts::StaticallySizedRange Spacing>
313 auto structured_grid_axis_orientation(const Spacing& spacing) {
314 std::array<bool, static_size<Spacing>> result;
315 std::ranges::copy(
316 spacing | std::views::transform([&] <typename CT> (const CT dx) { return dx <= CT{0}; }),
317 result.begin()
318 );
319 return result;
320 }
321
322 std::size_t number_of_entities(const std::array<std::size_t, 6>& extents) {
323 return std::max(extents[1] - extents[0], std::size_t{1})
324 *std::max(extents[3] - extents[2], std::size_t{1})
325 *std::max(extents[5] - extents[4], std::size_t{1});
326 }
327
328 unsigned int structured_grid_dimension(const std::array<std::size_t, 3>& cells_per_direction) {
329 return std::ranges::count_if(cells_per_direction, [] (const std::size_t e) { return e > 0; });
330 }
331
332 template<typename T>
333 std::array<T, 3> compute_location(const std::array<T, 3>& origin,
334 const std::array<T, 3>& coordinate,
335 const std::array<T, 9>& direction) {
336 const auto& [x, y, z] = coordinate;
337 return {
338 origin[0] + x*direction[0] + y*direction[1] + z*direction[2],
339 origin[1] + x*direction[3] + y*direction[4] + z*direction[5],
340 origin[2] + x*direction[6] + y*direction[7] + z*direction[8]
341 };
342 }
343
344 template<typename T>
345 std::array<T, 3> compute_piece_origin(const std::array<T, 3>& global_origin,
346 const std::array<T, 3>& spacing,
347 const std::array<std::size_t, 3>& extents_begin,
348 const std::array<T, 9>& direction) {
349 return compute_location(
350 global_origin,
351 {
352 spacing[0]*static_cast<T>(extents_begin[0]),
353 spacing[1]*static_cast<T>(extents_begin[1]),
354 spacing[2]*static_cast<T>(extents_begin[2])
355 },
356 direction
357 );
358 }
359
360 template<typename T>
361 Serialization serialize_structured_points(const std::array<std::size_t, 6>& extents,
362 const std::array<T, 3>& origin,
363 const std::array<T, 3>& spacing,
364 const std::array<T, 9>& direction) {
365 const MDLayout layout{{
366 extents[1] - extents[0],
367 extents[3] - extents[2],
368 extents[5] - extents[4]
369 }};
370 const FlatIndexMapper mapper{layout};
371 const auto piece_origin = compute_piece_origin(
372 origin, spacing, {extents[0], extents[2], extents[4]}, direction
373 );
374
375 static constexpr unsigned int vtk_space_dim = 3;
376 Serialization result(layout.number_of_entries()*sizeof(T)*vtk_space_dim);
377 auto span_out = result.as_span_of(Precision<T>{});
378 for (const auto& md_index : MDIndexRange{layout}) {
379 const auto offset = mapper.map(md_index)*vtk_space_dim;
380 assert(offset + 2 < span_out.size());
381 std::ranges::copy(
382 compute_location(piece_origin, {
383 static_cast<T>(md_index.get(0))*spacing[0],
384 static_cast<T>(md_index.get(1))*spacing[1],
385 static_cast<T>(md_index.get(2))*spacing[2]
386 }, direction),
387 span_out.data() + offset
388 );
389 }
390 return result;
391 }
392
393 template<typename Visitor>
394 void visit_structured_cells(const Visitor& visitor,
395 const std::array<std::size_t, 6>& extents,
396 const bool is_axis_aligned = true) {
397 std::array<CellType, 4> grid_dim_to_cell_type{
398 CellType::vertex,
399 CellType::segment,
400 (is_axis_aligned ? CellType::pixel : CellType::quadrilateral),
401 (is_axis_aligned ? CellType::voxel : CellType::hexahedron)
402 };
403
404 std::array<std::size_t, 3> counts{
405 extents[1] - extents[0],
406 extents[3] - extents[2],
407 extents[5] - extents[4]
408 };
409
410 const std::size_t grid_dim = structured_grid_dimension(counts);
411 if (grid_dim == 0)
412 throw ValueError("Grid must be at least 1d");
413
414 const MDLayout point_layout{Ranges::incremented(counts, 1)};
415 const FlatIndexMapper point_mapper{point_layout};
416 const auto x_offset = grid_dim > 1 ? point_layout.extent(0) : std::size_t{0};
417 const auto y_offset = grid_dim > 2 ? point_layout.extent(0)*point_layout.extent(1) : std::size_t{0};
418
419 // avoid zero counts s.t. the index range does not degenerate
420 std::ranges::for_each(counts, [] (std::size_t& count) { count = std::max(count, std::size_t{1}); });
421 std::vector<std::size_t> corners(std::pow(2, grid_dim), 0);
422
423 const MDIndexRange index_range{MDLayout{counts}};
424 if (grid_dim == 1) {
425 std::ranges::for_each(index_range, [&] (const auto& md_index) {
426 const auto p0 = point_mapper.map(md_index);
427 corners = {p0, p0 + 1};
428 visitor(grid_dim_to_cell_type[grid_dim], corners);
429 });
430 } else if (grid_dim == 2) {
431 std::ranges::for_each(index_range, [&] (const auto& md_index) {
432 const auto p0 = point_mapper.map(md_index);
433 corners = {p0, p0 + 1, p0 + x_offset, p0 + 1 + x_offset};
434 visitor(grid_dim_to_cell_type[grid_dim], corners);
435 });
436 } else {
437 std::ranges::for_each(index_range, [&] (const auto& md_index) {
438 const auto p0 = point_mapper.map(md_index);
439 corners = {
440 p0, p0 + 1, p0 + x_offset, p0 + 1 + x_offset,
441 p0 + y_offset, p0 + y_offset + 1, p0 + y_offset + x_offset, p0 + 1 + y_offset + x_offset
442 };
443 visitor(grid_dim_to_cell_type[grid_dim], corners);
444 });
445 }
446 }
447
448} // namespace CommonDetail
449#endif // DOXYGEN
450
452} // namespace GridFormat::VTK
453
454
455#endif // GRIDFORMAT_VTK_COMMON_HPP_
Grid concepts.
std::shared_ptr< const Field > FieldPtr
Pointer type used by writers/readers for fields.
Definition: field.hpp:186
FieldPtr make_field_ptr(F &&f)
Factory function for field pointers.
Definition: field.hpp:192
Appended data format (all data is appended at the end of the xml file)
Definition: common.hpp:51
Inline data format (inside xml elements)
Definition: common.hpp:50
constexpr Inlined inlined
Instance of the inline data format.
Definition: common.hpp:53
constexpr Appended appended
Instance of the appended data format.
Definition: common.hpp:54