FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
export_vtk.hpp
1// FEAT3: Finite Element Analysis Toolbox, Version 3
2// Copyright (C) 2010 by Stefan Turek & the FEAT group
3// FEAT3 is released under the GNU General Public License version 3,
4// see the file 'copyright.txt' in the top level directory for details.
5
6#pragma once
7
8// includes, FEAT
10#include <kernel/geometry/conformal_mesh.hpp>
11#include <kernel/geometry/index_set.hpp>
12#include <kernel/geometry/mesh_part.hpp>
13#include <kernel/geometry/structured_mesh.hpp>
14#include <kernel/shape.hpp>
16#include <kernel/util/dist_file_io.hpp>
17#include <kernel/util/dist.hpp>
19#include <kernel/util/pack.hpp>
20#include <kernel/util/string.hpp>
21#include <kernel/util/tiny_algebra.hpp>
22
23// includes, STL
24#include <deque>
25#include <fstream>
26#include <functional>
27#include <string_view>
28#include <vector>
29
30#ifdef FEAT_HAVE_CGAL
31#include <kernel/geometry/cgal.hpp>
32#endif
33
34
35namespace FEAT::Geometry
36{
38 namespace Intern
39 {
40 template<typename Shape_>
41 struct VTKShape;
42
43 template<>
44 struct VTKShape<Shape::Simplex<1>>
45 {
46 static constexpr std::uint32_t type = 3; // VTK_LINE
47 static inline int map(int i)
48 {
49 return i;
50 }
51 };
52
53 template<>
54 struct VTKShape<Shape::Simplex<2>>
55 {
56 static constexpr std::uint32_t type = 5; // VTK_TRIANGLE
57 static inline int map(int i)
58 {
59 return i;
60 }
61 };
62
63 template<>
64 struct VTKShape<Shape::Simplex<3>>
65 {
66 static constexpr std::uint32_t type = 10; // VTK_TETRA
67 static inline int map(int i)
68 {
69 return i;
70 }
71 };
72
73 template<>
74 struct VTKShape<Shape::Hypercube<1>>
75 {
76 static constexpr std::uint32_t type = 3; // VTK_LINE
77 static inline int map(int i)
78 {
79 return i;
80 }
81 };
82
83 template<>
84 struct VTKShape<Shape::Hypercube<2>>
85 {
86 static constexpr std::uint32_t type = 9; // VTK_QUAD
87 static inline int map(int i)
88 {
89 // bit-stunt: {0, 1, 2, 3} -> {0, 1, 3, 2}
90 return (i ^ ((i >> 1) & 1));
91 }
92 };
93
94 template<>
95 struct VTKShape<Shape::Hypercube<3>>
96 {
97 static constexpr std::uint32_t type = 12; // VTK_HEXAHEDRON
98 static inline int map(int i)
99 {
100 // bit-stunt: {0, 1, ..., 7} -> {0, 1, 3, 2, 4, 5, 7, 6}
101 return (i ^ ((i >> 1) & 1));
102 }
103 };
104
105 template<typename T_>
106 struct VTKType;
107
108 template<>
109 struct VTKType<std::uint8_t>
110 {
111 static constexpr std::string_view type = "UInt8";
112 };
113
114 template<>
115 struct VTKType<std::uint16_t>
116 {
117 static constexpr std::string_view type = "UInt16";
118 };
119
120 template<>
121 struct VTKType<std::uint32_t>
122 {
123 static constexpr std::string_view type = "UInt32";
124 };
125
126 template<>
127 struct VTKType<std::uint64_t>
128 {
129 static constexpr std::string_view type = "UInt64";
130 };
131
132 template<>
133 struct VTKType<std::int8_t>
134 {
135 static constexpr std::string_view type = "Int8";
136 };
137
138 template<>
139 struct VTKType<std::int16_t>
140 {
141 static constexpr std::string_view type = "Int16";
142 };
143
144 template<>
145 struct VTKType<std::int32_t>
146 {
147 static constexpr std::string_view type = "Int32";
148 };
149
150 template<>
151 struct VTKType<std::int64_t>
152 {
153 static constexpr std::string_view type = "Int64";
154 };
155
156 template<>
157 struct VTKType<float>
158 {
159 static constexpr std::string_view type = "Float32";
160 };
161
162 template<>
163 struct VTKType<double>
164 {
165 static constexpr std::string_view type = "Float64";
166 };
167 } // namespace Intern
169
193 template<typename Mesh_, int cell_dim_ = Mesh_::ShapeType::dimension>
195 {
196 public:
198 using MeshType = Mesh_;
200 using ShapeType = typename MeshType::ShapeType;
204 using VTKShapeType = Intern::VTKShape<CellShapeType>;
205
207 static constexpr int num_coords = 3;
208
211
212#ifdef FEAT_HAVE_ZLIB
213 static constexpr bool can_compress = true;
214#else
215 static constexpr bool can_compress = false;
216#endif
217
218 protected:
220 using VarDeque = std::deque<std::pair<String, std::vector<double>>>;
221
222 // NOTE(mmuegge): Depending on wether we are writing a mesh or a meshpart,
223 // we may need to do some additional handling of indices.
224 // The accessor functions allow us to do so,
225 // while presenting a consistent interface to the actual file writing logic.
226
246 bool _ascii_output = false;
248 bool _use_compression = can_compress;
249
250 public:
262 explicit ExportVTK(const MeshType& mesh, int var_prec = 0) :
263 _num_verts(mesh.get_num_entities(0)),
264 _num_cells(mesh.get_num_entities(cell_dim_)),
265 _var_prec(Math::max(0, var_prec))
266 {
267 const auto& vertex_set = mesh.get_vertex_set();
268 const auto& index_set = mesh.template get_index_set<cell_dim_, 0>();
269
270 _vertices = [&](Index i, auto& vert) {
271 // Actual vertex might not be 3D
272 // vert is zeroed before this is called
273 for(int j(0); j < vertex_set.num_coords; j++)
274 {
275 vert[j] = float(vertex_set[i][j]);
276 }
277 };
278 _cells = [&](Index i, auto& tuple) {
279 for(int j(0); j < verts_per_cell; j++)
280 {
281 tuple[j] = index_set(i, j);
282 }
283 };
284 }
285
301 explicit ExportVTK(const MeshType& mesh, const MeshPart<MeshType>& part, int var_prec = 0) :
302 _num_verts(part.get_num_entities(0)),
303 _num_cells(part.get_num_entities(cell_dim_)),
304 _var_prec(Math::max(0, var_prec))
305 {
306 const auto& vertex_set = mesh.get_vertex_set();
307 const auto& vertex_target_set = part.template get_target_set<0>();
308 const auto& cell_target_set = part.template get_target_set<cell_dim_>();
309
310 _vertices = [&](Index i, auto& vert) {
311 // Actual vertex might not be 3D
312 // vert is zeroed before this is called
313 for(int j(0); j < vertex_set.num_coords; j++)
314 {
315 vert[j] = float(vertex_set[i][j]);
316 }
317 };
318
319 if(part.has_topology())
320 {
321 // The meshpart has its own topology. Use that.
322 const auto& index_set = part.template get_index_set<cell_dim_, 0>();
323 _cells = [&](Index i, auto& tuple) {
324 for(int j(0); j < verts_per_cell; j++)
325 {
326 tuple[j] = index_set(i, j);
327 }
328 };
329 }
330 else
331 {
332 // The meshpart has no own topology. Use the mesh topology.
333 const auto& index_set = mesh.template get_index_set<cell_dim_, 0>();
334
335 // The mesh index-set returns indices of vertices the mesh's index-space,
336 // but the .vtu will be written in the meshparts index-space.
337 // We thus need to search for the corresponding index on the meshpart for any vertex index.
338 _cells = [&](Index i, auto& tuple)
339 {
340 for(int j(0); j < verts_per_cell; j++)
341 {
342 const Index vertex = index_set(cell_target_set[i], j);
343 for(Index k(0); k < vertex_target_set.get_num_entities(); k++)
344 {
345 if(vertex_target_set[k] == vertex)
346 {
347 tuple[j] = k;
348 }
349 }
350 }
351 };
352 }
353 }
354
355#if defined(FEAT_HAVE_CGAL) || defined(DOXYGEN)
367 template<typename DT_>
368 explicit ExportVTK(const Geometry::CGALWrapper<DT_>& cw, int var_prec = 0) :
369 _num_verts(cw.get_num_entities(0)),
370 _num_cells(cw.get_num_entities(cell_dim_)),
371 _var_prec(Math::max(0, var_prec))
372 {
373 XASSERTM(cell_dim_ == 2, "CGAL Export only possible with surface mesh");
374
375 _vertices = [&](Index i, auto& vert) {
376 auto point = cw.point(i);
377 for(int j(0); j < verts_per_cell; j++)
378 {
379 vert[j] = float(point[j]);
380 }
381 };
382 _cells = [&](Index i, auto& tuple) {
383 auto it = cw.vertices_around_face().image_begin(i);
384 auto end = cw.vertices_around_face().image_end(i);
385 Index j(0);
386 for(;it != end; ++it)
387 {
388 tuple[j++] = *it;
389 }
390 };
391 }
392#endif
393
395 virtual ~ExportVTK() = default;
396
398 ExportVTK(const ExportVTK& other) = default;
399
401 ExportVTK(ExportVTK&& other) noexcept = default;
402
404 ExportVTK& operator=(const ExportVTK& other) = default;
405
408
412 void clear()
413 {
414 _cell_scalars.clear();
415 _vertex_scalars.clear();
416 _vertex_vectors.clear();
417 _cell_vectors.clear();
418 }
419
425 void enable_ascii_output(bool flag)
426 {
427 _ascii_output = flag;
428 }
429
437 void enable_compression(bool flag)
438 {
439 _use_compression = flag;
440 }
441
459 template<typename T_>
460 void add_vertex_scalar(const String& name, const T_* data, double scaling_factor = 1.0)
461 {
462 XASSERTM(data != nullptr, "data array is nullptr");
463 std::vector<double> d(_num_verts);
464 for(Index i(0); i < _num_verts; ++i)
465 {
466 d[i] = scaling_factor * double(data[i]);
467 }
468 _vertex_scalars.emplace_back(name, std::move(d));
469 }
470
490 template<typename T_>
492 const String& name,
493 const T_* x,
494 const T_* y = nullptr,
495 const T_* z = nullptr,
496 double scaling_factor = 1.0)
497 {
498 XASSERTM(x != nullptr, "x-data array is nullptr");
499 std::vector<double> d(3 * _num_verts);
500
501 if(y != nullptr && z != nullptr)
502 {
503 for(Index i(0); i < _num_verts; ++i)
504 {
505 d[(3 * i) + 0] = scaling_factor * double(x[i]);
506 d[(3 * i) + 1] = scaling_factor * double(y[i]);
507 d[(3 * i) + 2] = scaling_factor * double(z[i]);
508 }
509 }
510 else if(y != nullptr)
511 {
512 for(Index i(0); i < _num_verts; ++i)
513 {
514 d[(3 * i) + 0] = scaling_factor * double(x[i]);
515 d[(3 * i) + 1] = scaling_factor * double(y[i]);
516 d[(3 * i) + 2] = 0.0;
517 }
518 }
519 else
520 {
521 for(Index i(0); i < _num_verts; ++i)
522 {
523 d[(3 * i) + 0] = scaling_factor * double(x[i]);
524 d[(3 * i) + 1] = 0.0;
525 d[(3 * i) + 2] = 0.0;
526 }
527 }
528 _vertex_vectors.emplace_back(name, std::move(d));
529 }
530
551 template<typename VectorType_>
552 void add_vertex_vector(const String& name, const VectorType_& v, double scaling_factor = 1.0)
553 {
554 std::vector<double> d(3 * _num_verts);
555
556 for(Index i(0); i < _num_verts; ++i)
557 {
558 for(Index j(0); j < 3; ++j)
559 {
560 d[(Index(3) * i) + j] = double(0);
561 }
562
563 for(int j(0); j < v.BlockSize; ++j)
564 {
565 d[(Index(3) * i) + Index(j)] = scaling_factor * double(v(i)[j]);
566 }
567 }
568 _vertex_vectors.emplace_back(name, std::move(d));
569 }
570
591 template<typename DT_, int dim_>
592 void add_vertex_vector(const String& name, const std::vector<Tiny::Vector<DT_, dim_>>& v, double scaling_factor = 1.0)
593 {
594 std::vector<double> d(3 * _num_verts);
595
596 for(Index i(0); i < _num_verts; ++i)
597 {
598 for(Index j(0); j < 3; ++j)
599 {
600 d[(Index(3) * i) + j] = double(0);
601 }
602
603 for(int j(0); j < dim_; ++j)
604 {
605 d[(Index(3) * i) + Index(j)] = scaling_factor * double(v[i][j]);
606 }
607 }
608 _vertex_vectors.emplace_back(name, std::move(d));
609 }
610
628 template<typename T_>
629 void add_cell_scalar(const String& name, const T_* data, double scaling_factor = 1.0)
630 {
631 XASSERTM(data != nullptr, "data array is nullptr");
632 std::vector<double> d(_num_cells);
633 for(Index i(0); i < _num_cells; ++i)
634 {
635 d[i] = scaling_factor * double(data[i]);
636 }
637 _cell_scalars.emplace_back(name, std::move(d));
638 }
639
659 template<typename T_>
661 const String& name,
662 const T_* x,
663 const T_* y = nullptr,
664 const T_* z = nullptr,
665 double scaling_factor = 1.0)
666 {
667 XASSERTM(x != nullptr, "x-data array is nullptr");
668 std::vector<double> d(3 * _num_cells);
669
670 if(y != nullptr && z != nullptr)
671 {
672 for(Index i(0); i < _num_cells; ++i)
673 {
674 d[(3 * i) + 0] = scaling_factor * double(x[i]);
675 d[(3 * i) + 1] = scaling_factor * double(y[i]);
676 d[(3 * i) + 2] = scaling_factor * double(z[i]);
677 }
678 }
679 else if(y != nullptr)
680 {
681 for(Index i(0); i < _num_cells; ++i)
682 {
683 d[(3 * i) + 0] = scaling_factor * double(x[i]);
684 d[(3 * i) + 1] = scaling_factor * double(y[i]);
685 d[(3 * i) + 2] = 0.0;
686 }
687 }
688 else
689 {
690 for(Index i(0); i < _num_cells; ++i)
691 {
692 d[(3 * i) + 0] = scaling_factor * double(x[i]);
693 d[(3 * i) + 1] = 0.0;
694 d[(3 * i) + 2] = 0.0;
695 }
696 }
697 _cell_vectors.emplace_back(name, std::move(d));
698 }
699
720 template<typename VectorType_>
721 void add_cell_vector(const String& name, const VectorType_& v, double scaling_factor = 1.0)
722 {
723 std::vector<double> d(3 * _num_cells);
724
725 for(Index i(0); i < _num_cells; ++i)
726 {
727 for(Index j(0); j < 3; ++j)
728 {
729 d[(Index(3) * i) + j] = double(0);
730 }
731
732 for(int j(0); j < v.BlockSize; ++j)
733 {
734 d[(Index(3) * i) + Index(j)] = scaling_factor * double(v(i)[j]);
735 }
736 }
737 _cell_vectors.emplace_back(name, std::move(d));
738 }
739
760 template<typename DT_, int dim_>
761 void add_cell_vector(const String& name, const std::vector<Tiny::Vector<DT_, dim_>>& v, double scaling_factor = 1.0)
762 {
763 std::vector<double> d(3 * _num_cells);
764
765 for(Index i(0); i < _num_cells; ++i)
766 {
767 for(Index j(0); j < 3; ++j)
768 {
769 d[(Index(3) * i) + j] = double(0);
770 }
771
772 for(int j(0); j < dim_; ++j)
773 {
774 d[(Index(3) * i) + Index(j)] = scaling_factor * double(v[i][j]);
775 }
776 }
777 _cell_vectors.emplace_back(name, std::move(d));
778 }
779
786 void write(const String& filename) const
787 {
788 // try to open the output file
789 String vtu_name(filename + ".vtu");
790 std::ofstream ofs(vtu_name.c_str());
791 if(!(ofs.is_open() && ofs.good()))
792 {
793 throw FileError("Failed to create '" + vtu_name + "'");
794 }
795
796 // write
797 write_vtu(ofs);
798
799 // and close
800 ofs.close();
801 }
802
822 void write(const String& filename, const int rank, const int nparts)
823 {
824 // call the standard serial write version if nparts < 1
825 if(nparts <= 1)
826 {
827 write(filename);
828 return;
829 }
830
831 // verify rank
832 XASSERTM((rank >= 0) && (rank < nparts), "Invalid rank");
833
834 // Add rank cell array since we're parallel if we come to here
835 std::vector<double> rank_array((std::size_t(_num_cells)), double(rank));
836 add_cell_scalar("rank", rank_array.data());
837
838 // compute number of non-zero digits in (nparts-1) for padding
839 const std::size_t ndigits = Math::ilog10(std::size_t(nparts - 1));
840
841 // write serial VTU file: "filename.#rank.vtu"
842 write(filename + "." + stringify(rank).pad_front(ndigits, '0'));
843
844 // we're done unless we have rank = 0
845 if(rank != 0)
846 {
847 return;
848 }
849
850 // try to open our output file
851 String pvtu_name(filename + ".pvtu");
852 std::ofstream ofs(pvtu_name.c_str());
853 if(!(ofs.is_open() && ofs.good()))
854 {
855 throw FileError("Failed to create '" + pvtu_name + "'");
856 }
857
858 // extract the file title from our filename
859 std::size_t p = filename.find_last_of("\\/");
860 String file_title = filename.substr(p == FEAT::String::npos ? 0 : ++p);
861
862 // write PVTU file
863 write_pvtu(ofs, file_title, nparts);
864
865 // and close
866 ofs.close();
867 }
868
881 void write(const String& filename, const Dist::Comm& comm)
882 {
883 // Add rank cell array since we're parallel if we come to here
884 std::vector<double> rank_array(std::size_t(_num_cells), double(comm.rank()));
885 add_cell_scalar("rank", rank_array.data());
886
887 // compute number of non-zero digits in (nparts-1) for padding
888 const auto ndigits = std::size_t(Math::max(Math::ilog10(comm.size() - 1), 1));
889
890 // write serial VTU file into a stringstream
891 std::stringstream stream;
892 write_vtu(stream);
893
894 // generate pattern for filename: "filename.#rank.vtu"
895 String pattern = filename + "." + String(ndigits, '*') + ".vtu";
896
897 // write distributed VTU files
898 DistFileIO::write_sequence(stream, pattern, comm);
899
900 // we're done unless we have rank = 0
901 if(comm.rank() != 0)
902 {
903 return;
904 }
905
906 // try to open our output file
907 String pvtu_name(filename + ".pvtu");
908 std::ofstream ofs(pvtu_name.c_str());
909 if(!(ofs.is_open() && ofs.good()))
910 {
911 throw FileError("Failed to create '" + pvtu_name + "'");
912 }
913
914 // extract the file title from our filename
915 std::size_t p = filename.find_last_of("\\/");
916 String file_title = filename.substr(p == FEAT::String::npos ? 0 : ++p);
917
918 // write PVTU file
919 write_pvtu(ofs, file_title, comm.size());
920
921 // and close
922 ofs.close();
923 }
924
931 void write_vtu(std::ostream& os) const
932 {
933 // fetch basic information
934
935 // write VTK header
936 os << R"(<VTKFile type="UnstructuredGrid" version="0.1" byte_order=")";
937 os << (_little_endian() ? "LittleEndian" : "BigEndian") << "\"";
938
939 if(!_ascii_output && _use_compression && can_compress)
940 {
941#ifndef FEAT_HAVE_ZLIB
942 XABORTM("Requested compression in ExportVTK, but ZLib is not available!");
943#endif
944 os << " compressor=\"vtkZLibDataCompressor\"";
945 }
946
947 os << ">\n";
948 os << "<!-- Generated by FEAT v" << version_major << "." << version_minor;
949 os << "." << version_patch << " -->\n";
950
951 // write mesh header
952 os << "<UnstructuredGrid>\n";
953 os << "<Piece NumberOfPoints=\"" << _num_verts << "\" NumberOfCells=\"" << _num_cells << "\">\n";
954
955 // write point data
956 if((!_vertex_scalars.empty()) || (!_vertex_vectors.empty()))
957 {
958 os << "<PointData>\n";
959 for(const auto& var : _vertex_scalars)
960 {
961 _write_data_array(os, var, 1);
962 }
963
964 for(const auto& var : _vertex_vectors)
965 {
966 _write_data_array(os, var, 3);
967 }
968 os << "</PointData>\n";
969 }
970
971 // write cell data
972 if(!_cell_scalars.empty() || !_cell_vectors.empty())
973 {
974 os << "<CellData>\n";
975
976 for(const auto& var : _cell_scalars)
977 {
978 _write_data_array(os, var, 1);
979 }
980
981 for(const auto& var : _cell_vectors)
982 {
983 _write_data_array(os, var, 3);
984 }
985
986 os << "</CellData>\n";
987 }
988
989 // write vertices
990 os << "<Points>\n";
991 if(_ascii_output)
992 {
993 // Ascii output. Write data array adhoc without copying all vertices.
994 os << R"(<DataArray type="Float32" NumberOfComponents="3" format="ascii" Name="test">)" << "\n";
996 for(Index i(0); i < _num_verts; ++i)
997 {
998 vertex.format(0);
999 _vertices(i, vertex);
1000 os << vertex[0];
1001 for(int j(1); j < num_coords; ++j)
1002 {
1003 os << " " << vertex[j];
1004 }
1005 os << "\n";
1006 }
1007 os << "</DataArray>\n";
1008 }
1009 else
1010 {
1011 // Binary output. Copy vertices to buffer for base64 encoding
1012 // This might not be needed, but ensures we are independent of the internal representation of the vertices
1013 std::pair<String, std::vector<float>> vertices("", {});
1014 vertices.second.reserve(3 * _num_cells);
1015
1017 for(Index i(0); i < _num_verts; ++i)
1018 {
1019 vertex.format(0);
1020 _vertices(i, vertex);
1021 for(int j(0); j < num_coords; ++j)
1022 {
1023 vertices.second.push_back(static_cast<float>(vertex[j]));
1024 }
1025 }
1026
1027 _write_data_array(os, vertices, num_coords);
1028 }
1029 os << "</Points>\n";
1030
1031 // write cells
1032 os << "<Cells>\n";
1033 if(_ascii_output)
1034 {
1035 os << R"(<DataArray type="UInt32" Name="connectivity">)" << "\n";
1037 for(Index i(0); i < _num_cells; ++i)
1038 {
1039 _cells(i, cell);
1040 os << cell[VTKShapeType::map(0)];
1041 for(int j(1); j < verts_per_cell; ++j)
1042 {
1043 os << " " << cell[VTKShapeType::map(j)];
1044 }
1045 os << "\n";
1046 }
1047 os << "</DataArray>\n";
1048 }
1049 else
1050 {
1051 // Same as for vertices. Copy for base64 encoding
1052 std::pair<String, std::vector<std::uint32_t>> connectivity("connectivity", {});
1053 connectivity.second.reserve(_num_cells * verts_per_cell);
1054
1056 for(Index i(0); i < _num_cells; ++i)
1057 {
1058 _cells(i, cell);
1059 for(int j(0); j < verts_per_cell; ++j)
1060 {
1061 connectivity.second.push_back(static_cast<std::uint32_t>(cell[VTKShapeType::map(j)]));
1062 }
1063 }
1064
1065 _write_data_array(os, connectivity);
1066 }
1067
1068 if(_ascii_output)
1069 {
1070 os << R"(<DataArray type="UInt32" Name="offsets">)" << "\n";
1071 for(Index i(0); i < _num_cells; ++i)
1072 {
1073 os << ((i + 1) * verts_per_cell) << "\n";
1074 }
1075 os << "</DataArray>\n";
1076 }
1077 else
1078 {
1079 // Same as for vertices. Copy for base64 encoding
1080 std::pair<String, std::vector<std::uint32_t>> offsets("offsets", {});
1081 offsets.second.reserve(_num_cells);
1082
1083 for(Index i(0); i < _num_cells; ++i)
1084 {
1085 offsets.second.push_back(static_cast<std::uint32_t>((i + 1) * verts_per_cell));
1086 }
1087
1088 _write_data_array(os, offsets);
1089 }
1090
1091 if(_ascii_output)
1092 {
1093 os << R"(<DataArray type="UInt32" Name="types">)" << "\n";
1094 for(Index i(0); i < _num_cells; ++i)
1095 {
1096 os << VTKShapeType::type << "\n";
1097 }
1098 os << "</DataArray>\n";
1099 }
1100 else
1101 {
1102 // Same as for vertices. Copy for base64 encoding
1103 std::pair<String, std::vector<std::uint32_t>> types("types", {});
1104 types.second.reserve(_num_cells);
1105
1106 for(Index i(0); i < _num_cells; ++i)
1107 {
1108 types.second.push_back(VTKShapeType::type);
1109 }
1110
1111 _write_data_array(os, types);
1112 }
1113 os << "</Cells>\n";
1114
1115 // finish
1116 os << "</Piece>\n";
1117 os << "</UnstructuredGrid>\n";
1118 os << "</VTKFile>\n";
1119 }
1120
1133 void write_pvtu(std::ostream& os, const String& file_title, const int nparts) const
1134 {
1135 // write VTK header
1136 os << R"(<VTKFile type="PUnstructuredGrid" version="0.1" byte_order=")";
1137 os << (_little_endian() ? "LittleEndian" : "BigEndian") << "\"";
1138
1139 if(!_ascii_output && _use_compression && can_compress)
1140 {
1141#ifndef FEAT_HAVE_ZLIB
1142 XABORTM("Requested compression in ExportVTK, but ZLib is not available!");
1143#endif
1144 os << " compressor=\"vtkZLibDataCompressor\"";
1145 }
1146
1147 os << ">\n";
1148 os << "<!-- Generated by FEAT v" << version_major << "." << version_minor;
1149 os << "." << version_patch << " -->\n";
1150 os << "<PUnstructuredGrid GhostLevel=\"0\">\n";
1151
1152 // write vertex data
1153 if((!_vertex_scalars.empty()) || (!_vertex_vectors.empty()))
1154 {
1155 os << "<PPointData>\n";
1156
1157 // write vertex variables
1158 for(const auto& vertex_scalar : _vertex_scalars)
1159 {
1160 os << "<PDataArray type=\"Float64\" Name=\"" << vertex_scalar.first << "\" />\n";
1161 }
1162 // write vertex fields
1163 for(const auto& vertex_vector : _vertex_vectors)
1164 {
1165 os << "<PDataArray type=\"Float64\" Name=\"" << vertex_vector.first;
1166 os << "\" NumberOfComponents=\"3\" />\n";
1167 }
1168
1169 os << "</PPointData>\n";
1170 }
1171
1172 // write cell variables
1173 if(!_cell_scalars.empty() || !_cell_vectors.empty())
1174 {
1175 os << "<PCellData>\n";
1176 // write cell scalars
1177 for(const auto& cell_scalar : _cell_scalars)
1178 {
1179 os << "<PDataArray type=\"Float64\" Name=\"" << cell_scalar.first << "\" />\n";
1180 }
1181 // write cell fields
1182 for(const auto& cell_vector : _cell_vectors)
1183 {
1184 os << "<PDataArray type=\"Float64\" Name=\"" << cell_vector.first;
1185 os << "\" NumberOfComponents=\"3\" />\n";
1186 }
1187 os << "</PCellData>\n";
1188 }
1189
1190 // write vertices
1191 os << "<PPoints>\n";
1192 os << "<PDataArray type=\"Float32\" NumberOfComponents=\"3\" />\n";
1193 os << "</PPoints>\n";
1194
1195 // compute number of non-zero digits in (nparts-1) for padding
1196 const std::size_t ndigits = Math::ilog10(std::size_t(nparts - 1));
1197
1198 // now let's write our piece data
1199 for(int i(0); i < nparts; ++i)
1200 {
1201 os << "<Piece Source=\"" << file_title << "." << stringify(i).pad_front(ndigits, '0');
1202 os << ".vtu\" />\n";
1203 }
1204
1205 // finish
1206 os << "</PUnstructuredGrid>\n";
1207 os << "</VTKFile>\n";
1208 }
1209 private:
1210
1219 {
1220 std::uint32_t num_bytes;
1221
1222 explicit UncompressedHeader(std::uint32_t number_of_bytes) : num_bytes(number_of_bytes)
1223 {
1224 }
1225 };
1226
1233 {
1234 std::uint32_t blocks = 1;
1235 std::uint32_t blocksize;
1236 std::uint32_t last_blocksize;
1237 std::uint32_t compressed_blocksizes[1];
1238
1239 explicit CompressedHeader(std::uint32_t uncompressed_bytes, std::uint32_t compressed_bytes) :
1240 blocksize(uncompressed_bytes),
1241 last_blocksize(uncompressed_bytes),
1242 compressed_blocksizes{compressed_bytes}
1243 {
1244 }
1245 };
1246
1261 template<typename T_>
1262 void _write_data_array(std::ostream& os, const std::pair<String, std::vector<T_>>& data, Index number_of_components=0) const
1263 {
1264 // Ensure there is no padding in headers.
1265 // Otherwise the reinterpret_casts below would produce garbage data
1266 static_assert(sizeof(UncompressedHeader) == sizeof(std::uint32_t));
1267 static_assert(sizeof(CompressedHeader) == 4 * sizeof(std::uint32_t));
1268
1269 std::string_view format = _ascii_output ? "ascii" : "binary";
1270 std::string_view type = Intern::VTKType<T_>::type;
1271
1272 // Write XML-tag
1273 os << "<DataArray type=\"" << type << "\" format=\"" << format << "\"";
1274 if(data.first != "")
1275 {
1276 os << " Name = \"" << data.first << "\"";
1277 }
1278 if(number_of_components != 0)
1279 {
1280 os << " NumberOfComponents=\"" << number_of_components << "\"";
1281 }
1282 os << ">\n";
1283
1284 if(_ascii_output)
1285 {
1286 Index num_blocks = data.second.size() / number_of_components;
1287 for(Index i(0); i < num_blocks; ++i)
1288 {
1289 for(Index j(0); j < number_of_components; j++)
1290 {
1291 os << stringify_fp_sci(data.second[(i * number_of_components) + j], _var_prec);
1292 os << ((j == number_of_components - 1) ? "\n" : " ");
1293 }
1294 }
1295 }
1296 else if(_use_compression && can_compress)
1297 {
1298 auto uncompressed_size = static_cast<std::uint32_t>(data.second.size() * sizeof(T_));
1299
1300 // Encode with compression
1301 Pack::Type pack_type = Pack::deduct_type<T_>() | Pack::Type::Mask_Z;
1302 std::size_t buffer_size = Pack::estimate_size(data.second.size(), pack_type);
1303
1304 std::vector<Pack::u8> buffer(buffer_size, 0);
1305 std::size_t compressed_size = Pack::encode(buffer.data(), data.second.data(), buffer_size, data.second.size(), pack_type, false);
1306
1307 // Set up header and data pointers
1308 CompressedHeader header(uncompressed_size, static_cast<std::uint32_t>(compressed_size));
1309 const auto* header_begin = reinterpret_cast<const Pack::u8*>(&header);
1310 const auto* header_end = header_begin + sizeof(CompressedHeader);
1311
1312 const auto* data_begin = reinterpret_cast<const Pack::u8*>(buffer.data());
1313 const auto* data_end = data_begin + compressed_size;
1314
1315 // For compressed data, the header and the data are encoded separately
1316 os << Pack::base64_encode(header_begin, header_end) << Pack::base64_encode(data_begin, data_end) << "\n";
1317 }
1318 else
1319 {
1320 UncompressedHeader header(static_cast<std::uint32_t>(data.second.size() * sizeof(T_)));
1321
1322 const auto* header_begin = reinterpret_cast<const Pack::u8*>(&header);
1323 const auto* header_end = header_begin + sizeof(header);
1324
1325 const auto* data_begin = reinterpret_cast<const Pack::u8*>(data.second.data());
1326 const auto* data_end = data_begin + header.num_bytes;
1327
1328 os << Pack::base64_encode(header_begin, header_end) << Pack::base64_encode(data_begin, data_end) << "\n";
1329 }
1330
1331 // Close XML-tag
1332 os << "</DataArray>\n";
1333 }
1334
1335 // Copied from binary_stream-test.cpp
1336 bool _little_endian() const
1337 {
1338 std::int32_t x = 1;
1339 void* xv = (void*)&x;
1340 auto* x16 = (int16_t*)xv;
1341 return *x16 == 1;
1342 }
1343 }; // class ExportVTK
1344} // namespace FEAT::Geometry
#define XABORTM(msg)
Abortion macro definition with custom message.
Definition: assertion.hpp:192
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Definition: assertion.hpp:263
FEAT Kernel base header.
Communicator class.
Definition: dist.hpp:1349
int size() const
Returns the size of this communicator.
Definition: dist.hpp:1506
int rank() const
Returns the rank of this process in this communicator.
Definition: dist.hpp:1494
static void write_sequence(std::stringstream &stream, const String &pattern, const Dist::Comm &comm, bool truncate=true)
Writes a rank-indexed text file sequence.
Base class for file related errors.
Definition: exception.hpp:173
Wrapper for the CGAL Library.
Definition: cgal.hpp:173
CGALVerticesAroundFaceAdjactor< DT_ > vertices_around_face() const
Returns an adjactor for vertices around faces of the mesh.
PointType point(std::uint32_t idx) const
Returns the point of the surface mesh with the given index.
VTK exporter class template.
Definition: export_vtk.hpp:195
ExportVTK(const MeshType &mesh, const MeshPart< MeshType > &part, int var_prec=0)
Constructor.
Definition: export_vtk.hpp:301
virtual ~ExportVTK()=default
destructor
VarDeque _vertex_scalars
vertex variable list
Definition: export_vtk.hpp:236
void add_vertex_vector(const String &name, const T_ *x, const T_ *y=nullptr, const T_ *z=nullptr, double scaling_factor=1.0)
Adds a vector-field vertex variable to the exporter.
Definition: export_vtk.hpp:491
Index _num_verts
number of vertices in mesh
Definition: export_vtk.hpp:232
void add_cell_vector(const String &name, const std::vector< Tiny::Vector< DT_, dim_ > > &v, double scaling_factor=1.0)
Adds a vector-field cell variable given by a std::vector of Tiny::Vector to the exporter.
Definition: export_vtk.hpp:761
std::function< void(Index, Tiny::Vector< float, num_coords > &)> _vertices
Accessor function for vertices.
Definition: export_vtk.hpp:228
bool _use_compression
compressed output flag. Only applicable if _binary_output is true
Definition: export_vtk.hpp:248
static constexpr int num_coords
Coordinates per vertex. The VTK documentation states that this is always three.
Definition: export_vtk.hpp:207
ExportVTK & operator=(ExportVTK &&other)=default
Move-assignment constructor.
int _var_prec
precision of variables
Definition: export_vtk.hpp:244
void write(const String &filename, const Dist::Comm &comm)
Writes out the data to a parallel XML-PVTU file.
Definition: export_vtk.hpp:881
void write(const String &filename, const int rank, const int nparts)
Writes out the data to a parallel XML-PVTU file.
Definition: export_vtk.hpp:822
void enable_ascii_output(bool flag)
Enable or disable binary output.
Definition: export_vtk.hpp:425
void clear()
Clears all vertex and cell variables in the exporter.
Definition: export_vtk.hpp:412
void write(const String &filename) const
Writes out the data to a serial XML-VTU file.
Definition: export_vtk.hpp:786
typename MeshType::ShapeType ShapeType
our shape type
Definition: export_vtk.hpp:200
void write_vtu(std::ostream &os) const
Writes out the mesh and variable data in serial XML-VTU format.
Definition: export_vtk.hpp:931
typename Shape::FaceTraits< ShapeType, cell_dim_ >::ShapeType CellShapeType
shape type of exported cells
Definition: export_vtk.hpp:202
std::function< void(Index, IndexTuple< verts_per_cell > &)> _cells
Accessor function for cells.
Definition: export_vtk.hpp:230
VarDeque _vertex_vectors
vertex field list
Definition: export_vtk.hpp:238
void write_pvtu(std::ostream &os, const String &file_title, const int nparts) const
Writes out the partition data in parallel XML-PVTU format.
ExportVTK(const ExportVTK &other)=default
Copy constructor.
Index _num_cells
number of cells in mesh
Definition: export_vtk.hpp:234
void add_cell_vector(const String &name, const T_ *x, const T_ *y=nullptr, const T_ *z=nullptr, double scaling_factor=1.0)
Adds a vector-field cell variable to the exporter.
Definition: export_vtk.hpp:660
void add_vertex_vector(const String &name, const std::vector< Tiny::Vector< DT_, dim_ > > &v, double scaling_factor=1.0)
Adds a vector-field vertex variable given by a std::vector of Tiny::Vector to the exporter.
Definition: export_vtk.hpp:592
ExportVTK(ExportVTK &&other) noexcept=default
Move constructor.
VarDeque _cell_scalars
cell variable list
Definition: export_vtk.hpp:240
Intern::VTKShape< CellShapeType > VTKShapeType
our VTK shape type
Definition: export_vtk.hpp:204
void add_vertex_vector(const String &name, const VectorType_ &v, double scaling_factor=1.0)
Adds a vector-field vertex variable given by a LAFEM::***VectorBlocked to the exporter.
Definition: export_vtk.hpp:552
ExportVTK & operator=(const ExportVTK &other)=default
Copy-assignment constructor.
ExportVTK(const MeshType &mesh, int var_prec=0)
Constructor.
Definition: export_vtk.hpp:262
void enable_compression(bool flag)
Enable or disable compression.
Definition: export_vtk.hpp:437
void add_cell_scalar(const String &name, const T_ *data, double scaling_factor=1.0)
Adds a scalar cell variable to the exporter.
Definition: export_vtk.hpp:629
VarDeque _cell_vectors
cell field list
Definition: export_vtk.hpp:242
bool _ascii_output
ascii output flag
Definition: export_vtk.hpp:246
void add_vertex_scalar(const String &name, const T_ *data, double scaling_factor=1.0)
Adds a scalar vertex variable to the exporter.
Definition: export_vtk.hpp:460
ExportVTK(const Geometry::CGALWrapper< DT_ > &cw, int var_prec=0)
Constructor.
Definition: export_vtk.hpp:368
void _write_data_array(std::ostream &os, const std::pair< String, std::vector< T_ > > &data, Index number_of_components=0) const
Writes an DataArray XML-node to the give output stream.
std::deque< std::pair< String, std::vector< double > > > VarDeque
our variable container
Definition: export_vtk.hpp:220
static constexpr int verts_per_cell
Vertices per cell.
Definition: export_vtk.hpp:210
void add_cell_vector(const String &name, const VectorType_ &v, double scaling_factor=1.0)
Adds a vector-field cell variable given by a LAFEM::***VectorBlocked to the exporter.
Definition: export_vtk.hpp:721
Class template for partial meshes.
Definition: mesh_part.hpp:90
bool has_topology() const
Checks if this MeshPart has a mesh topology.
Definition: mesh_part.hpp:297
String class implementation.
Definition: string.hpp:47
String pad_front(size_type len, char c=' ') const
Pads the front of the string up to a desired length.
Definition: string.hpp:393
Tiny Vector class template.
CUDA_HOST_DEVICE void format(DataType alpha=DataType(0))
Formats the vector.
Geometry namespace.
@ other
generic/other permutation strategy
T_ ilog10(T_ x)
Computes the integral base-10 logarithm of an integer, i.e. its number of non-zero decimal digits.
Definition: math.hpp:231
T_ max(T_ a, T_ b)
Returns the maximum of two values.
Definition: math.hpp:137
std::size_t encode(void *buf, const T_ *src, const std::size_t buf_size, const std::size_t count, const Pack::Type pack_type, bool swap_bytes, double tolerance)
Encodes an array into a packed buffer.
Definition: pack.cpp:580
std::size_t estimate_size(const std::size_t count, const Pack::Type type, const double tolerance)
Computes the estimated (upper bound) pack buffer size for an array.
Definition: pack.cpp:557
String base64_encode(const Pack::u8 *begin, const Pack::u8 *end)
Encodes bytes into a Base64 string.
Definition: pack.cpp:1115
Type
Type enumeration.
Definition: pack.hpp:70
std::uint8_t u8
8-bit unsigned integer type
Definition: pack.hpp:36
String stringify(const T_ &item)
Converts an item into a String.
Definition: string.hpp:993
static constexpr int version_major
FEAT major version number.
static constexpr int version_patch
FEAT patch version number (month)
static constexpr int version_minor
FEAT minor version number (year)
String stringify_fp_sci(DataType_ value, int precision=0, int width=0, bool sign=false)
Prints a floating point value to a string in scientific notation.
Definition: string.hpp:1137
std::uint64_t Index
Index data type.
Header for compressed binary data.
Header for uncompressed binary data.
Index Tuple class template.
Definition: index_set.hpp:35
Face traits tag struct template.
Definition: shape.hpp:106