8#ifndef GRIDFORMAT_VTK_COMMON_HPP_
9#define GRIDFORMAT_VTK_COMMON_HPP_
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>
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>
40namespace GridFormat::Encoding {
struct Ascii;
struct Base64;
struct RawBinary; }
43namespace GridFormat::VTK {
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 {};
68template<
typename Encoder>
69inline constexpr bool produces_valid_xml(
const Encoder&) {
71 is_complete<Traits::ProducesValidXML<Encoder>>,
72 "Traits::ProducesValidXML was not specialized for the given encoder"
74 return Traits::ProducesValidXML<Encoder>::value;
77inline constexpr std::uint8_t cell_type_number(CellType 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;
96 throw NotImplemented(
"VTK cell type number for the given cell type");
99inline constexpr CellType cell_type(std::uint8_t 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;
118 throw NotImplemented(
"Cell type for the given VTK cell type number: " + std::to_string(vtk_id));
122 const auto layout = field->layout();
123 if (layout.dimension() < 2)
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; }
130 return transform(field, FieldTransformation::extend_all_to(3));
134template<std::derived_from<Field> F>
135 requires(!std::is_lvalue_reference_v<F>)
140template<
typename ctype, Gr
idDetail::ExposesPo
intRange Gr
id>
141auto make_coordinates_field(
const Grid& grid,
bool structured_grid_ordering) {
142 return make_vtk_field(PointField{
144 [&] (
const auto& point) {
return coordinates(grid, point); },
145 structured_grid_ordering,
150template<
typename HeaderType = std::
size_t, Concepts::UnstructuredGr
id Gr
id,
typename Po
intMap>
151 requires(std::is_lvalue_reference_v<PointMap>)
152auto make_connectivity_field(
const Grid& grid, PointMap&& map) {
153 class ConnectivityField :
public Field {
155 explicit ConnectivityField(
const Grid& g, PointMap&& map)
157 , _point_map{std::forward<PointMap>(map)} {
159 std::ranges::for_each(cells(g), [&] (
const auto& cell) {
160 _num_values += number_of_points(_grid, cell);
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();
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));
178 return serialization;
182 LVReferenceOrValue<PointMap> _point_map;
183 HeaderType _num_values;
184 } _field{grid, std::forward<PointMap>(map)};
186 return make_vtk_field(std::move(_field));
189template<
typename HeaderType = std::
size_t, Concepts::UnstructuredGr
id Gr
id>
190auto make_offsets_field(
const Grid& grid) {
191 class OffsetField :
public Field {
193 explicit OffsetField(
const Grid& g)
195 , _num_cells{static_cast<HeaderType>(Ranges::size(cells(g)))}
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();
206 HeaderType offset = 0;
207 std::ranges::for_each(cells(_grid), [&] (
const auto& cell) {
208 offset += number_of_points(_grid, cell);
212 return serialization;
216 HeaderType _num_cells;
219 return make_vtk_field(std::move(_field));
222template<Concepts::UnstructuredGr
id Gr
id>
223auto make_cell_types_field(
const Grid& grid) {
224 return make_vtk_field(CellField{
226 [&] (
const auto& cell) {
227 return VTK::cell_type_number(type(grid, cell));
233inline auto active_array_attribute_for_rank(
unsigned int rank) {
235 throw ValueError(
"Rank must be <= 2");
236 static constexpr std::array attributes{
"Scalars",
"Vectors",
"Tensors"};
237 return attributes[rank];
240namespace CommonDetail {
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)
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";
253 template<Concepts::StaticallySizedMDRange<2> R>
254 std::string direction_string(
const R& basis) {
258 std::string result =
"";
259 std::ranges::for_each(m, [&] (
const auto& row) {
262 result += number_string_3d(row);
264 for (
int i = static_size<R>; i < 3; ++i)
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);
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;
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);
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));
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);
306 template<Concepts::StructuredEntitySet Gr
id>
307 requires(!Concepts::StaticallySizedRange<Grid>)
308 std::string extents_string(
const Grid& grid) {
309 return extents_string(extents(grid));
312 template<Concepts::StaticallySizedRange Spacing>
313 auto structured_grid_axis_orientation(
const Spacing& spacing) {
314 std::array<bool, static_size<Spacing>> result;
316 spacing | std::views::transform([&] <
typename CT> (
const CT dx) {
return dx <= CT{0}; }),
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});
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; });
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;
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]
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(
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])
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]
370 const FlatIndexMapper mapper{layout};
371 const auto piece_origin = compute_piece_origin(
372 origin, spacing, {extents[0], extents[2], extents[4]}, direction
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());
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]
387 span_out.data() + offset
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{
400 (is_axis_aligned ? CellType::pixel : CellType::quadrilateral),
401 (is_axis_aligned ? CellType::voxel : CellType::hexahedron)
404 std::array<std::size_t, 3> counts{
405 extents[1] - extents[0],
406 extents[3] - extents[2],
407 extents[5] - extents[4]
410 const std::size_t grid_dim = structured_grid_dimension(counts);
412 throw ValueError(
"Grid must be at least 1d");
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};
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);
423 const MDIndexRange index_range{MDLayout{counts}};
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);
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);
437 std::ranges::for_each(index_range, [&] (
const auto& md_index) {
438 const auto p0 = point_mapper.map(md_index);
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
443 visitor(grid_dim_to_cell_type[grid_dim], corners);
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
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