ViennaLS
Loading...
Searching...
No Matches
lsVTKWriter.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <fstream>
4#include <string>
5#include <unordered_map>
6#include <utility>
7
8#include <lsFileFormats.hpp>
9#include <lsMesh.hpp>
10
11#include <vcLogger.hpp>
12#include <vcSmartPointer.hpp>
13
14#ifdef VIENNALS_USE_VTK
15#include <vtkCellArray.h>
16#include <vtkCellData.h>
17#include <vtkFloatArray.h>
18#include <vtkPointData.h>
19#include <vtkPoints.h>
20#include <vtkPolyData.h>
21#include <vtkSmartPointer.h>
22#include <vtkXMLPolyDataWriter.h>
23
24#include <vtkUnstructuredGrid.h>
25#include <vtkXMLUnstructuredGridWriter.h>
26#endif // VIENNALS_USE_VTK
27
28namespace viennals {
29
30using namespace viennacore;
31
33template <class T> class VTKWriter {
34 using MetaDataType = std::unordered_map<std::string, std::vector<double>>;
35
36 SmartPointer<Mesh<T>> mesh = nullptr;
38 std::string fileName;
39 MetaDataType metaData;
40
41#ifdef VIENNALS_USE_VTK
42 // Cached VTK objects for memory access
43 vtkSmartPointer<vtkPolyData> cachedPolyData = nullptr;
44 vtkSmartPointer<vtkUnstructuredGrid> cachedUnstructuredGrid = nullptr;
45
46 template <class In, class Out>
47 void addDataFromMesh(const In &inData, Out outData) const {
48 // now add pointData
49 for (unsigned i = 0; i < inData.getScalarDataSize(); ++i) {
50 vtkSmartPointer<vtkFloatArray> pointData =
51 vtkSmartPointer<vtkFloatArray>::New();
52 pointData->SetNumberOfComponents(1);
53 pointData->SetName(inData.getScalarDataLabel(i).c_str());
54 auto scalars = *(inData.getScalarData(i));
55 for (unsigned j = 0; j < inData.getScalarData(i)->size(); ++j) {
56 pointData->InsertNextValue(scalars[j]);
57 }
58 outData->AddArray(pointData);
59 }
60
61 // now add vector data
62 for (unsigned i = 0; i < inData.getVectorDataSize(); ++i) {
63 vtkSmartPointer<vtkFloatArray> vectorData =
64 vtkSmartPointer<vtkFloatArray>::New();
65 vectorData->SetNumberOfComponents(3);
66 vectorData->SetName(inData.getVectorDataLabel(i).c_str());
67 auto vectors = *(inData.getVectorData(i));
68 for (unsigned j = 0; j < inData.getVectorData(i)->size(); ++j) {
69 vectorData->InsertNextTuple3(vectors[j][0], vectors[j][1],
70 vectors[j][2]);
71 }
72 outData->AddArray(vectorData);
73 }
74 }
75
76 void addMetaDataToVTK(vtkDataSet *data) const {
77 if (metaData.empty()) {
78 return;
79 }
80
81 // add metadata to field data
82 vtkSmartPointer<vtkFieldData> fieldData = data->GetFieldData();
83 for (const auto &meta : metaData) {
84 if (meta.second.empty())
85 continue; // skip empty metadata
86
87 vtkSmartPointer<vtkFloatArray> metaDataArray =
88 vtkSmartPointer<vtkFloatArray>::New();
89 metaDataArray->SetName(meta.first.c_str());
90 metaDataArray->SetNumberOfValues(meta.second.size());
91 for (size_t i = 0; i < meta.second.size(); ++i) {
92 metaDataArray->SetValue(i, meta.second[i]);
93 }
94 fieldData->AddArray(metaDataArray);
95 }
96 }
97#endif // VIENNALS_USE_VTK
98
99public:
100 VTKWriter() = default;
101
102 VTKWriter(SmartPointer<Mesh<T>> passedMesh) : mesh(passedMesh) {}
103
104 VTKWriter(SmartPointer<Mesh<T>> passedMesh, std::string passedFileName)
105 : mesh(passedMesh), fileName(std::move(passedFileName)) {}
106
107 VTKWriter(SmartPointer<Mesh<T>> passedMesh, FileFormatEnum passedFormat,
108 std::string passedFileName)
109 : mesh(passedMesh), fileFormat(passedFormat),
110 fileName(std::move(passedFileName)) {}
111
112 void setMesh(SmartPointer<Mesh<T>> passedMesh) { mesh = passedMesh; }
113
115 void setFileFormat(FileFormatEnum passedFormat) { fileFormat = passedFormat; }
116
118 void setFileName(std::string passedFileName) {
119 fileName = std::move(passedFileName);
120 }
121
122 void setMetaData(const MetaDataType &passedMetaData) {
123 metaData = passedMetaData;
124 }
125
126 void addMetaData(const std::string &key, double value) {
127 metaData[key] = std::vector<double>{value};
128 }
129
130 void addMetaData(const std::string &key, const std::vector<double> &values) {
131 metaData[key] = values;
132 }
133
134 void addMetaData(const MetaDataType &newMetaData) {
135 for (const auto &pair : newMetaData) {
136 metaData[pair.first] = pair.second;
137 }
138 }
139
140#ifdef VIENNALS_USE_VTK
141 vtkPolyData *getPolyData() const { return cachedPolyData; }
142
143 vtkUnstructuredGrid *getUnstructuredGrid() const {
144 return cachedUnstructuredGrid;
145 }
146#endif // VIENNALS_USE_VTK
147
148 void apply() {
149 // check mesh
150 if (mesh == nullptr) {
151 Logger::getInstance()
152 .addError("No mesh was passed to VTKWriter.")
153 .print();
154 return;
155 }
156 if (mesh->nodes.empty()) {
157 VIENNACORE_LOG_WARNING("Writing empty mesh.");
158 return;
159 }
160
161 if (fileFormat == FileFormatEnum::VTK_AUTO) {
162 if (!fileName.empty()) {
163 auto dotPos = fileName.rfind('.');
164 if (dotPos == std::string::npos) {
165 fileFormat = FileFormatEnum::VTP;
166 } else {
167 auto ending = fileName.substr(dotPos);
168 if (ending == ".vtk") {
169 fileFormat = FileFormatEnum::VTK_LEGACY;
170 } else if (ending == ".vtp") {
171 fileFormat = FileFormatEnum::VTP;
172 } else if (ending == ".vtu") {
173 fileFormat = FileFormatEnum::VTU;
174 } else {
175 Logger::getInstance()
176 .addError("No valid file format found based on the file ending "
177 "passed to VTKWriter.")
178 .print();
179 return;
180 }
181 }
182 } else {
183 // No filename: default to VTP memory-only mode
184 fileFormat = FileFormatEnum::VTP;
185 }
186 }
187
188 // Build VTK object in memory, then write to file if filename is set
189 switch (fileFormat) {
191 if (fileName.empty()) {
192 VIENNACORE_LOG_ERROR("VTK_LEGACY format requires a filename. Cannot "
193 "use memory-only mode.");
194 return;
195 }
196 writeVTKLegacy(fileName);
197 break;
198#ifdef VIENNALS_USE_VTK
200 buildVTP();
201 if (!fileName.empty())
202 writeVTP(fileName);
203 break;
205 buildVTU();
206 if (!fileName.empty())
207 writeVTU(fileName);
208 break;
209#else
212 VIENNACORE_LOG_WARNING(
213 "VTKWriter was built without VTK support. Falling back "
214 "to VTK_LEGACY.");
215 writeVTKLegacy(fileName);
216 break;
217#endif
218 default:
219 VIENNACORE_LOG_ERROR("No valid file format set for VTKWriter.");
220 }
221 }
222
223private:
224#ifdef VIENNALS_USE_VTK
225 void buildVTP() {
226 cachedPolyData = vtkSmartPointer<vtkPolyData>::New();
227
228 vtkSmartPointer<vtkPoints> polyPoints = vtkSmartPointer<vtkPoints>::New();
229 for (auto it = mesh->getNodes().begin(); it != mesh->getNodes().end(); ++it)
230 polyPoints->InsertNextPoint((*it)[0], (*it)[1], (*it)[2]);
231 cachedPolyData->SetPoints(polyPoints);
232
233 if (mesh->vertices.size() > 0) {
234 vtkSmartPointer<vtkCellArray> polyCells =
235 vtkSmartPointer<vtkCellArray>::New();
236 for (auto it = mesh->vertices.begin(); it != mesh->vertices.end(); ++it) {
237 polyCells->InsertNextCell(1);
238 polyCells->InsertCellPoint((*it)[0]);
239 }
240 cachedPolyData->SetVerts(polyCells);
241 }
242
243 if (mesh->lines.size() > 0) {
244 vtkSmartPointer<vtkCellArray> polyCells =
245 vtkSmartPointer<vtkCellArray>::New();
246 for (auto it = mesh->lines.begin(); it != mesh->lines.end(); ++it) {
247 polyCells->InsertNextCell(2);
248 for (unsigned i = 0; i < 2; ++i)
249 polyCells->InsertCellPoint((*it)[i]);
250 }
251 cachedPolyData->SetLines(polyCells);
252 }
253
254 if (mesh->triangles.size() > 0) {
255 vtkSmartPointer<vtkCellArray> polyCells =
256 vtkSmartPointer<vtkCellArray>::New();
257 for (auto it = mesh->triangles.begin(); it != mesh->triangles.end();
258 ++it) {
259 polyCells->InsertNextCell(3);
260 for (unsigned i = 0; i < 3; ++i)
261 polyCells->InsertCellPoint((*it)[i]);
262 }
263 cachedPolyData->SetPolys(polyCells);
264 }
265
266 addDataFromMesh(mesh->pointData, cachedPolyData->GetPointData());
267 addDataFromMesh(mesh->cellData, cachedPolyData->GetCellData());
268 addMetaDataToVTK(cachedPolyData);
269 }
270
271 void writeVTP(std::string filename) const {
272 if (cachedPolyData == nullptr)
273 return;
274 if (filename.find(".vtp") != filename.size() - 4)
275 filename += ".vtp";
276 vtkSmartPointer<vtkXMLPolyDataWriter> pwriter =
277 vtkSmartPointer<vtkXMLPolyDataWriter>::New();
278 pwriter->SetFileName(filename.c_str());
279 // Inline binary rather than VTK's default appended mode: VTK 9.1's reader
280 // cannot parse appended data once an array spans more than one 32 kB
281 // compression block, so larger meshes are written unreadably by its own
282 // reader. Inline binary round-trips at identical file size.
283 pwriter->SetDataModeToBinary();
284 pwriter->SetInputData(cachedPolyData);
285 pwriter->Write();
286 }
287
288 void buildVTU() {
289 cachedUnstructuredGrid = vtkSmartPointer<vtkUnstructuredGrid>::New();
290
291 vtkSmartPointer<vtkUnstructuredGrid> uGrid = cachedUnstructuredGrid;
292
293 // Points
294 vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
295 for (auto it = mesh->getNodes().begin(); it != mesh->getNodes().end();
296 ++it) {
297 points->InsertNextPoint((*it)[0], (*it)[1], (*it)[2]);
298 }
299 uGrid->SetPoints(points);
300
301 // Now set all cells
302 vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
303 std::vector<int> cellTypes;
304 cellTypes.reserve(mesh->vertices.size() + mesh->lines.size() +
305 mesh->triangles.size() + mesh->tetras.size() +
306 mesh->hexas.size());
307
308 // Vertices
309 if (mesh->vertices.size() > 0) {
310 for (auto it = mesh->vertices.begin(); it != mesh->vertices.end(); ++it) {
311 cells->InsertNextCell(1);
312 cells->InsertCellPoint((*it)[0]);
313 cellTypes.push_back(1); // vtk Vertex
314 }
315 }
316
317 // Lines
318 if (mesh->lines.size() > 0) {
319 for (auto it = mesh->lines.begin(); it != mesh->lines.end(); ++it) {
320 cells->InsertNextCell(2);
321 for (unsigned i = 0; i < 2; ++i) {
322 cells->InsertCellPoint((*it)[i]);
323 }
324 cellTypes.push_back(3); // vtk Line
325 }
326 }
327
328 // Triangles
329 if (mesh->triangles.size() > 0) {
330 for (auto it = mesh->triangles.begin(); it != mesh->triangles.end();
331 ++it) {
332 cells->InsertNextCell(3);
333 for (unsigned i = 0; i < 3; ++i) {
334 cells->InsertCellPoint((*it)[i]);
335 }
336 cellTypes.push_back(5); // vtk Triangle
337 }
338 }
339
340 // Tetras
341 if (mesh->tetras.size() > 0) {
342 for (auto it = mesh->tetras.begin(); it != mesh->tetras.end(); ++it) {
343 cells->InsertNextCell(4);
344 for (unsigned i = 0; i < 4; ++i) {
345 cells->InsertCellPoint((*it)[i]);
346 }
347 cellTypes.push_back(10); // vtk Tetra
348 }
349 }
350
351 // Hexas
352 if (mesh->hexas.size() > 0) {
353 for (auto it = mesh->hexas.begin(); it != mesh->hexas.end(); ++it) {
354 cells->InsertNextCell(8);
355 for (unsigned i = 0; i < 8; ++i) {
356 cells->InsertCellPoint((*it)[i]);
357 }
358 cellTypes.push_back(12); // vtk Hexahedron
359 }
360 }
361
362 // set cells
363 uGrid->SetCells(&(cellTypes[0]), cells);
364
365 addDataFromMesh(mesh->pointData, uGrid->GetPointData());
366 addDataFromMesh(mesh->cellData, uGrid->GetCellData());
367 addMetaDataToVTK(uGrid);
368
369 // // now add pointData
370 // for (unsigned i = 0; i < mesh->cellData.getScalarDataSize(); ++i) {
371 // vtkSmartPointer<vtkFloatArray> pointData =
372 // vtkSmartPointer<vtkFloatArray>::New();
373 // pointData->SetNumberOfComponents(1);
374 // pointData->SetName(mesh->cellData.getScalarDataLabel(i).c_str());
375 // auto scalars = *(mesh->cellData.getScalarData(i));
376 // for (unsigned j = 0; j < scalars.size(); ++j) {
377 // pointData->InsertNextValue(scalars[j]);
378 // }
379 // uGrid->GetCellData()->AddArray(pointData);
380 // }
381
382 // // now add vector data
383 // for (unsigned i = 0; i < mesh->cellData.getVectorDataSize(); ++i) {
384 // vtkSmartPointer<vtkFloatArray> vectorData =
385 // vtkSmartPointer<vtkFloatArray>::New();
386 // vectorData->SetNumberOfComponents(3);
387 // vectorData->SetName(mesh->cellData.getVectorDataLabel(i).c_str());
388 // auto vectors = *(mesh->cellData.getVectorData(i));
389 // for (unsigned j = 0; j < vectors.size(); ++j) {
390 // vectorData->InsertNextTuple3(vectors[j][0], vectors[j][1],
391 // vectors[j][2]);
392 // }
393 // uGrid->GetCellData()->AddArray(vectorData);
394 // }
395 }
396
397 void writeVTU(std::string filename) const {
398 if (cachedUnstructuredGrid == nullptr)
399 return;
400 if (filename.find(".vtu") != filename.size() - 4)
401 filename += ".vtu";
402 vtkSmartPointer<vtkXMLUnstructuredGridWriter> owriter =
403 vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
404 owriter->SetFileName(filename.c_str());
405 // See buildVTP's writer: VTK 9.1 cannot read back its own appended-mode
406 // output for multi-block arrays.
407 owriter->SetDataModeToBinary();
408 owriter->SetInputData(cachedUnstructuredGrid);
409 owriter->Write();
410 }
411
412#endif // VIENNALS_USE_VTK
413
414 void writeVTKLegacy(const std::string &filename) {
415 if (mesh == nullptr) {
416 Logger::getInstance()
417 .addError("No mesh was passed to VTKWriter.")
418 .print();
419 return;
420 }
421
422 std::ofstream f(filename.c_str());
423
424 f << "# vtk DataFile Version 2.0" << std::endl;
425 f << ((!mesh->lines.empty()) ? 2 : 3) << "D Surface" << std::endl;
426 f << "ASCII" << std::endl;
427 f << "DATASET UNSTRUCTURED_GRID" << std::endl;
428 f << "POINTS " << mesh->nodes.size() << " float" << std::endl;
429
430 // print nodes
431 for (unsigned int i = 0; i < mesh->nodes.size(); i++) {
432 for (int j = 0; j < 3; j++)
433 f << static_cast<float>(mesh->nodes[i][j]) << " ";
434 f << std::endl;
435 }
436
437 const unsigned numberOfCells = mesh->vertices.size() + mesh->lines.size() +
438 mesh->triangles.size() +
439 mesh->tetras.size() + mesh->hexas.size();
440 const unsigned cellDataSize =
441 2 * mesh->vertices.size() + 3 * mesh->lines.size() +
442 4 * mesh->triangles.size() + 5 * mesh->tetras.size() +
443 9 * mesh->hexas.size();
444
445 f << "CELLS " << numberOfCells << " " << cellDataSize << std::endl;
446
447 // print elements
448 for (unsigned int i = 0; i < mesh->vertices.size(); i++) {
449 f << 1 << " " << mesh->vertices[i][0] << std::endl;
450 }
451 for (unsigned int i = 0; i < mesh->lines.size(); i++) {
452 f << 2 << " ";
453 for (int j = 0; j < 2; j++)
454 f << mesh->lines[i][j] << " ";
455 f << std::endl;
456 }
457
458 for (unsigned int i = 0; i < mesh->triangles.size(); i++) {
459 f << 3 << " ";
460 for (int j = 0; j < 3; j++)
461 f << mesh->triangles[i][j] << " ";
462 f << std::endl;
463 }
464
465 for (unsigned int i = 0; i < mesh->tetras.size(); i++) {
466 f << 4 << " ";
467 for (int j = 0; j < 4; j++)
468 f << mesh->tetras[i][j] << " ";
469 f << std::endl;
470 }
471
472 for (unsigned int i = 0; i < mesh->hexas.size(); i++) {
473 f << 8 << " ";
474 for (int j = 0; j < 8; j++)
475 f << mesh->hexas[i][j] << " ";
476 f << std::endl;
477 }
478
479 f << "CELL_TYPES " << numberOfCells << std::endl;
480 for (unsigned i = 0; i < mesh->vertices.size(); ++i)
481 f << 1 << std::endl;
482
483 for (unsigned i = 0; i < mesh->lines.size(); ++i)
484 f << 3 << std::endl;
485
486 for (unsigned i = 0; i < mesh->triangles.size(); ++i)
487 f << 5 << std::endl;
488
489 for (unsigned i = 0; i < mesh->tetras.size(); ++i)
490 f << 10 << std::endl;
491
492 for (unsigned i = 0; i < mesh->hexas.size(); ++i)
493 f << 12 << std::endl;
494
495 // WRITE POINT DATA
496 if (mesh->pointData.getScalarDataSize() ||
497 mesh->pointData.getVectorDataSize()) {
498 VIENNACORE_LOG_WARNING(
499 "Point data output not supported for legacy VTK output. "
500 "Point data is ignored.");
501 }
502
503 // WRITE SCALAR DATA
504 if (mesh->cellData.getScalarDataSize()) {
505 f << "CELL_DATA " << mesh->cellData.getScalarData(0)->size() << std::endl;
506 for (unsigned i = 0; i < mesh->cellData.getScalarDataSize(); ++i) {
507 auto scalars = *(mesh->cellData.getScalarData(i));
508 f << "SCALARS " << mesh->cellData.getScalarDataLabel(i) << " float"
509 << std::endl;
510 f << "LOOKUP_TABLE default" << std::endl;
511 for (unsigned j = 0; j < scalars.size(); ++j) {
512 f << ((std::abs(scalars[j]) < 1e-6) ? 0.0 : scalars[j]) << std::endl;
513 }
514 }
515 }
516
517 // WRITE VECTOR DATA
518 if (mesh->cellData.getVectorDataSize()) {
519 if (!mesh->cellData.getScalarDataSize())
520 f << "CELL_DATA " << mesh->cellData.getVectorData(0)->size()
521 << std::endl;
522 for (unsigned i = 0; i < mesh->cellData.getVectorDataSize(); ++i) {
523 auto vectors = *(mesh->cellData.getVectorData(i));
524 f << "VECTORS " << mesh->cellData.getVectorDataLabel(i) << " float"
525 << std::endl;
526 for (unsigned j = 0; j < vectors.size(); ++j) {
527 for (unsigned k = 0; k < 3; ++k) {
528 f << vectors[j][k] << " ";
529 }
530 f << std::endl;
531 }
532 }
533 }
534
535 f.close();
536 }
537};
538
539} // namespace viennals
This class holds an explicit mesh, which is always given in 3 dimensions. If it describes a 2D mesh,...
Definition lsMesh.hpp:25
void setFileName(std::string passedFileName)
set file name for file to write
Definition lsVTKWriter.hpp:118
VTKWriter(SmartPointer< Mesh< T > > passedMesh, FileFormatEnum passedFormat, std::string passedFileName)
Definition lsVTKWriter.hpp:107
VTKWriter(SmartPointer< Mesh< T > > passedMesh, std::string passedFileName)
Definition lsVTKWriter.hpp:104
void addMetaData(const std::string &key, double value)
Definition lsVTKWriter.hpp:126
void apply()
Definition lsVTKWriter.hpp:148
void setMetaData(const MetaDataType &passedMetaData)
Definition lsVTKWriter.hpp:122
void setMesh(SmartPointer< Mesh< T > > passedMesh)
Definition lsVTKWriter.hpp:112
void setFileFormat(FileFormatEnum passedFormat)
set file format for file to write. Defaults to VTK_LEGACY.
Definition lsVTKWriter.hpp:115
void addMetaData(const std::string &key, const std::vector< double > &values)
Definition lsVTKWriter.hpp:130
void addMetaData(const MetaDataType &newMetaData)
Definition lsVTKWriter.hpp:134
VTKWriter(SmartPointer< Mesh< T > > passedMesh)
Definition lsVTKWriter.hpp:102
Definition lsAdvect.hpp:41
FileFormatEnum
Definition lsFileFormats.hpp:4
@ VTK_AUTO
Definition lsFileFormats.hpp:8
@ VTK_LEGACY
Definition lsFileFormats.hpp:5
@ VTP
Definition lsFileFormats.hpp:6
@ VTU
Definition lsFileFormats.hpp:7