ViennaLS
Loading...
Searching...
No Matches
lsWriteVisualizationMesh.hpp
Go to the documentation of this file.
1#pragma once
2
3#ifdef VIENNALS_USE_VTK // this class needs vtk support
4
5#include <vtkAppendFilter.h>
6#include <vtkAppendPolyData.h>
7#include <vtkCellData.h>
8#include <vtkDataSetTriangleFilter.h>
9#include <vtkFloatArray.h>
10#include <vtkGeometryFilter.h>
11#include <vtkIncrementalOctreePointLocator.h>
12#include <vtkIntArray.h>
13#include <vtkPointData.h>
14#include <vtkProbeFilter.h>
15#include <vtkRectilinearGrid.h>
16#include <vtkSmartPointer.h>
17#include <vtkTableBasedClipDataSet.h>
18#include <vtkTriangleFilter.h>
19#include <vtkUnstructuredGrid.h>
20#include <vtkXMLPolyDataWriter.h>
21#include <vtkXMLUnstructuredGridWriter.h>
22
23#include <hrleDenseIterator.hpp>
24
26#include <lsDomain.hpp>
27#include <lsMaterialMap.hpp>
29#include <unordered_map>
30#include <utility>
31
32// #define LS_TO_VISUALIZATION_DEBUG
33#ifdef LS_TO_VISUALIZATION_DEBUG
34#include <vtkXMLRectilinearGridWriter.h>
35#endif
36
37namespace viennals {
38
39using namespace viennacore;
40
47template <class T, int D> class WriteVisualizationMesh {
48 typedef typename Domain<T, D>::DomainType hrleDomainType;
49 using LevelSetsType = std::vector<SmartPointer<Domain<T, D>>>;
50 LevelSetsType levelSets;
51 SmartPointer<MaterialMap> materialMap = nullptr;
52 std::string fileName;
53 bool extractVolumeMesh = true;
54 bool extractHullMesh = false;
55 bool bottomRemoved = false;
56 bool writeToFile = true;
57 double LSEpsilon = 1e-2;
58 std::unordered_map<std::string, std::vector<double>> metaData;
59
60 vtkSmartPointer<vtkUnstructuredGrid> volumeVTK;
61 vtkSmartPointer<vtkPolyData> hullVTK;
62
66 static void removeDuplicatePoints(vtkSmartPointer<vtkPolyData> &polyData,
67 const double tolerance) {
68
69 vtkSmartPointer<vtkPolyData> newPolyData =
70 vtkSmartPointer<vtkPolyData>::New();
71 vtkSmartPointer<vtkIncrementalOctreePointLocator> ptInserter =
72 vtkSmartPointer<vtkIncrementalOctreePointLocator>::New();
73 ptInserter->SetTolerance(tolerance);
74
75 vtkSmartPointer<vtkPoints> newPoints = vtkSmartPointer<vtkPoints>::New();
76
77 // get bounds
78 double gridBounds[6];
79 polyData->GetBounds(gridBounds);
80
81 // start point insertion from original points
82 std::vector<vtkIdType> newPointIds;
83 newPointIds.reserve(polyData->GetNumberOfPoints());
84 ptInserter->InitPointInsertion(newPoints, gridBounds);
85
86 // make new point list
87 for (vtkIdType pointId = 0; pointId < polyData->GetNumberOfPoints();
88 ++pointId) {
89 vtkIdType globalPtId = 0;
90 ptInserter->InsertUniquePoint(polyData->GetPoint(pointId), globalPtId);
91 newPointIds.push_back(globalPtId);
92 }
93
94 // now set the new points to the unstructured grid
95 newPolyData->SetPoints(newPoints);
96
97 // go through all cells and change point ids to match the new ids
98 vtkSmartPointer<vtkCellArray> oldCells = polyData->GetPolys();
99 vtkSmartPointer<vtkCellArray> newCells =
100 vtkSmartPointer<vtkCellArray>::New();
101
102 vtkSmartPointer<vtkIdList> cellPoints = vtkSmartPointer<vtkIdList>::New();
103 oldCells->InitTraversal();
104 while (oldCells->GetNextCell(cellPoints)) {
105 for (vtkIdType pointId = 0; pointId < cellPoints->GetNumberOfIds();
106 ++pointId) {
107 cellPoints->SetId(pointId, newPointIds[cellPoints->GetId(pointId)]);
108 }
109 // insert same cell with new points
110 newCells->InsertNextCell(cellPoints);
111 }
112
113 newPolyData->SetPolys(newCells);
114
115 // conserve all cell data
116 // TODO transfer point data as well (do with "InsertTuples (vtkIdList
117 // *dstIds, vtkIdList *srcIds, vtkAbstractArray *source) override)" of
118 // vtkDataArray class)
119 newPolyData->GetCellData()->ShallowCopy(polyData->GetCellData());
120
121 // set ugrid to the newly created grid
122 polyData = newPolyData;
123 }
124
127 static void removeDuplicatePoints(vtkSmartPointer<vtkUnstructuredGrid> &ugrid,
128 const double tolerance) {
129
130 vtkSmartPointer<vtkUnstructuredGrid> newGrid =
131 vtkSmartPointer<vtkUnstructuredGrid>::New();
132 vtkSmartPointer<vtkIncrementalOctreePointLocator> ptInserter =
133 vtkSmartPointer<vtkIncrementalOctreePointLocator>::New();
134 ptInserter->SetTolerance(tolerance);
135
136 vtkSmartPointer<vtkPoints> newPoints = vtkSmartPointer<vtkPoints>::New();
137
138 // get bounds
139 double gridBounds[6];
140 ugrid->GetBounds(gridBounds);
141
142 // start point insertion from original points
143 std::vector<vtkIdType> newPointIds;
144 newPointIds.reserve(ugrid->GetNumberOfPoints());
145 ptInserter->InitPointInsertion(newPoints, gridBounds);
146
147 // make new point list
148 for (vtkIdType pointId = 0; pointId < ugrid->GetNumberOfPoints();
149 ++pointId) {
150 vtkIdType globalPtId = 0;
151 ptInserter->InsertUniquePoint(ugrid->GetPoint(pointId), globalPtId);
152 newPointIds.push_back(globalPtId);
153 }
154
155 // now set the new points to the unstructured grid
156 newGrid->SetPoints(newPoints);
157
158 // go through all cells and change point ids to match the new ids
159 for (vtkIdType cellId = 0; cellId < ugrid->GetNumberOfCells(); ++cellId) {
160 vtkSmartPointer<vtkIdList> cellPoints = vtkSmartPointer<vtkIdList>::New();
161 ugrid->GetCellPoints(cellId, cellPoints);
162 for (vtkIdType pointId = 0; pointId < cellPoints->GetNumberOfIds();
163 ++pointId) {
164 cellPoints->SetId(pointId, newPointIds[cellPoints->GetId(pointId)]);
165 }
166 // insert same cell with new points
167 newGrid->InsertNextCell(ugrid->GetCell(cellId)->GetCellType(),
168 cellPoints);
169 }
170
171 // conserve all cell data
172 // TODO transfer point data as well (do with "InsertTuples (vtkIdList
173 // *dstIds, vtkIdList *srcIds, vtkAbstractArray *source) override)" of
174 // vtkDataArray class)
175 newGrid->GetCellData()->ShallowCopy(ugrid->GetCellData());
176
177 // set ugrid to the newly created grid
178 ugrid = newGrid;
179 }
180
182 static void
183 removeDegenerateTetras(vtkSmartPointer<vtkUnstructuredGrid> &ugrid) {
184 vtkSmartPointer<vtkUnstructuredGrid> newGrid =
185 vtkSmartPointer<vtkUnstructuredGrid>::New();
186
187 // need to copy material numbers
188 vtkSmartPointer<vtkIntArray> materialNumberArray =
189 vtkSmartPointer<vtkIntArray>::New();
190 materialNumberArray->SetNumberOfComponents(1);
191 materialNumberArray->SetName("Material");
192
193 // see if material is defined
194 int arrayIndex;
195 vtkDataArray *matArray =
196 ugrid->GetCellData()->GetArray("Material", arrayIndex);
197 const int &materialArrayIndex = arrayIndex;
198
199 // go through all cells and delete those with duplicate entries
200 for (vtkIdType cellId = 0; cellId < ugrid->GetNumberOfCells(); ++cellId) {
201 vtkSmartPointer<vtkIdList> cellPoints = vtkSmartPointer<vtkIdList>::New();
202 ugrid->GetCellPoints(cellId, cellPoints);
203 bool isDuplicate = false;
204 for (vtkIdType pointId = 0; pointId < cellPoints->GetNumberOfIds();
205 ++pointId) {
206 for (vtkIdType nextId = pointId + 1;
207 nextId < cellPoints->GetNumberOfIds(); ++nextId) {
208 // if they are the same, remove the cell
209 if (cellPoints->GetId(pointId) == cellPoints->GetId(nextId))
210 isDuplicate = true;
211 }
212 }
213 if (!isDuplicate) {
214 // insert same cell if no degenerate points
215 newGrid->InsertNextCell(ugrid->GetCell(cellId)->GetCellType(),
216 cellPoints);
217 // if material was defined before, use it now
218 if (materialArrayIndex >= 0)
219 materialNumberArray->InsertNextValue(matArray->GetTuple1(cellId));
220 }
221 }
222
223 // just take the old points and point data
224 newGrid->SetPoints(ugrid->GetPoints());
225 newGrid->GetPointData()->ShallowCopy(ugrid->GetPointData());
226 // set material cell data
227 newGrid->GetCellData()->SetScalars(materialNumberArray);
228
229 ugrid = newGrid;
230 }
231
232 // This function takes a levelset and converts it to a vtkRectilinearGrid
233 // The full domain contains values, which are capped at numLayers * gridDelta
234 // gridExtraPoints layers of grid points are added to the domain according to
235 // boundary conditions
236 template <int gridExtraPoints = 0>
237 vtkSmartPointer<vtkRectilinearGrid>
238 LS2RectiLinearGrid(SmartPointer<Domain<T, D>> levelSet, const double LSOffset,
239 int infiniteMinimum = std::numeric_limits<int>::max(),
240 int infiniteMaximum = -std::numeric_limits<int>::max()) {
241
242 auto &grid = levelSet->getGrid();
243 auto &domain = levelSet->getDomain();
244 double gridDelta = grid.getGridDelta();
245 int numLayers = levelSet->getLevelSetWidth();
246
247 vtkSmartPointer<vtkFloatArray>
248 coords[3]; // needs to be 3 because vtk only knows 3D
249 int gridMin = 0, gridMax = 0;
250 // int openJumpDirection = -1; // direction above the open boundary
251 // direction
252
253 // fill grid with offset depending on orientation
254 for (unsigned i = 0; i < D; ++i) {
255 coords[i] = vtkSmartPointer<vtkFloatArray>::New();
256
257 if (grid.getBoundaryConditions(i) ==
258 Domain<T, D>::BoundaryType::INFINITE_BOUNDARY) {
259 // add one to gridMin and gridMax for numerical stability
260 gridMin = std::min(domain.getMinRunBreak(i), infiniteMinimum) -
261 1; // choose the smaller number so that for first levelset the
262 // overall minimum can be chosen
263 gridMax = std::max(domain.getMaxRunBreak(i), infiniteMaximum) + 1;
264
265 // openJumpDirection = i + 1;
266 } else {
267 gridMin = grid.getMinGridPoint(i) - gridExtraPoints;
268 gridMax = grid.getMaxGridPoint(i) + gridExtraPoints;
269 }
270
271 for (int x = gridMin; x <= gridMax; ++x) {
272 coords[i]->InsertNextValue(x * gridDelta);
273 }
274 }
275
276 // if we work in 2D, just add 1 grid point at origin
277 if (D == 2) {
278 coords[2] = vtkSmartPointer<vtkFloatArray>::New();
279 coords[2]->InsertNextValue(0);
280 }
281
282 vtkSmartPointer<vtkRectilinearGrid> rgrid =
283 vtkSmartPointer<vtkRectilinearGrid>::New();
284
285 rgrid->SetDimensions(coords[0]->GetNumberOfTuples(),
286 coords[1]->GetNumberOfTuples(),
287 coords[2]->GetNumberOfTuples());
288 rgrid->SetXCoordinates(coords[0]);
289 rgrid->SetYCoordinates(coords[1]);
290 rgrid->SetZCoordinates(coords[2]);
291
292 // bool fixBorderPoints = (gridExtraPoints != 0);
293
294 // need to save the current position one dimension above open boundary
295 // direction, so we can register a jump in the open boundary direction when
296 // it occurs, so we can fix the LS value as follows: if remove_bottom==true,
297 // we need to flip the sign otherwise the sign stays the same
298 // int currentOpenIndex =
299 // it.getIndices()[(openJumpDirection < D) ? openJumpDirection : 0];
300
301 // Make array to store signed distance function
302 auto const numGridPoints = rgrid->GetNumberOfPoints();
303 vtkSmartPointer<vtkFloatArray> signedDistances =
304 vtkSmartPointer<vtkFloatArray>::New();
305 signedDistances->SetNumberOfComponents(1);
306 signedDistances->SetNumberOfTuples(numGridPoints);
307 signedDistances->SetName("SignedDistances");
308
309#pragma omp parallel
310 {
311 // use dense iterator to got to every index location
312 viennahrle::ConstDenseIterator<typename Domain<T, D>::DomainType> it(
313 levelSet->getDomain());
314
315#pragma omp for
316 for (vtkIdType pointId = 0; pointId < numGridPoints; ++pointId) {
317 // iterate until all grid points have a signed distance value
318
319 double p[3];
320 rgrid->GetPoint(pointId, p);
321 // create index vector
322 viennahrle::Index<D> indices(grid.globalCoordinates2GlobalIndices(p));
323
324 // write the corresponding LSValue
325 T value;
326
327 // if indices are outside of domain map to local indices
328 if (grid.isOutsideOfDomain(indices)) {
329 indices = grid.globalIndices2LocalIndices(indices);
330 // fixBorderPoints = true;
331 // signedDistances->InsertNextValue(
332 // signedDistances->GetDataTypeValueMax());
333 }
334
335 it.goToIndices(indices);
336 if (it.getValue() == Domain<T, D>::POS_VALUE) {
337 value = numLayers;
338 } else if (it.getValue() == Domain<T, D>::NEG_VALUE) {
339 value = -numLayers;
340 } else {
341 value = it.getValue() + LSOffset;
342 }
343
344 // if (removeBottom) {
345 // // if we jump from one end of the domain to the other and are not
346 // // already in the new run, we need to fix the sign of the run
347 // if (currentOpenIndex != indices[openJumpDirection]) {
348 // currentOpenIndex = indices[openJumpDirection];
349 // if (indices >= it.getIndices()) {
350 // value = -value;
351 // }
352 // }
353 // }
354
355 signedDistances->SetValue(pointId, value * gridDelta);
356 // signedDistances->InsertNextValue(value * gridDelta);
357 }
358
359 // // advance iterator until it is at correct point
360 // while (compare(it_l.end_indices(), indices) < 0) {
361 // it_l.next();
362 // if (it_l.is_finished())
363 // break;
364 // }
365 // // now move iterator with pointId to get to next point
366 // switch (compare(it_l.end_indices(), indices)) {
367 // case 0:
368 // it_l.next();
369 // default:
370 // ++pointId;
371 // }
372 // }
373 }
374
375 // now need to go through again to fix border points, this is done by
376 // mapping existing points onto the points outside the domain according
377 // to the correct boundary conditions
378 // if (fixBorderPoints) {
379 // vtkIdType pointId = 0;
380 // while ((pointId < rgrid->GetNumberOfPoints())) {
381 // if (signedDistances->GetValue(pointId) ==
382 // signedDistances->GetDataTypeValueMax()) {
383 // double p[3];
384 // rgrid->GetPoint(pointId, p);
385
386 // // create index vector
387 // viennahrle::Index<D> indices(
388 // grid.globalCoordinates2GlobalIndices(p));
389
390 // // vector for mapped point inside domain
391 // viennahrle::Index<D> localIndices =
392 // grid.globalIndices2LocalIndices(indices);
393
394 // // now find ID of point we need to take value from
395 // int originalPointId = 0;
396 // for (int i = D - 1; i >= 0; --i) {
397 // originalPointId *=
398 // coords[i]->GetNumberOfTuples(); // extent in direction
399 // originalPointId += localIndices[i] - indices[i];
400 // }
401 // originalPointId += pointId;
402
403 // // now put value of mapped point in global point
404 // signedDistances->SetValue(pointId,
405 // signedDistances->GetValue(originalPointId));
406 // }
407 // ++pointId;
408 // }
409 // }
410
411 // Add the SignedDistances to the grid
412 rgrid->GetPointData()->SetScalars(signedDistances);
413
414 return rgrid;
415 }
416
417 void addMetaDataToVTK(vtkDataSet *data) const {
418 if (metaData.empty()) {
419 return;
420 }
421
422 // add metadata to field data
423 vtkSmartPointer<vtkFieldData> fieldData = data->GetFieldData();
424 for (const auto &meta : metaData) {
425 if (meta.second.empty())
426 continue; // skip empty metadata
427
428 vtkSmartPointer<vtkFloatArray> metaDataArray =
429 vtkSmartPointer<vtkFloatArray>::New();
430 metaDataArray->SetName(meta.first.c_str());
431 metaDataArray->SetNumberOfValues(meta.second.size());
432 for (size_t i = 0; i < meta.second.size(); ++i) {
433 metaDataArray->SetValue(i, meta.second[i]);
434 }
435 fieldData->AddArray(metaDataArray);
436 }
437 }
438
439public:
440 WriteVisualizationMesh() = default;
441
442 WriteVisualizationMesh(SmartPointer<Domain<T, D>> levelSet) {
443 levelSets.push_back(levelSet);
444 }
445
447 void insertNextLevelSet(SmartPointer<Domain<T, D>> levelSet) {
448 levelSets.push_back(levelSet);
449 }
450
451 void clearLevelSets() { levelSets.clear(); }
452
455 void setFileName(std::string passedFileName) {
456 fileName = std::move(passedFileName);
457 }
458
460 void setExtractHullMesh(bool passedExtractHullMesh) {
461 extractHullMesh = passedExtractHullMesh;
462 }
463
465 void setExtractVolumeMesh(bool passedExtractVolumeMesh) {
466 extractVolumeMesh = passedExtractVolumeMesh;
467 }
468
469 void setWriteToFile(bool passedWriteToFile) {
470 writeToFile = passedWriteToFile;
471 }
472
473 void setMaterialMap(SmartPointer<MaterialMap> passedMaterialMap) {
474 materialMap = passedMaterialMap;
475 }
476
477 void setWrappingLayerEpsilon(double epsilon) { LSEpsilon = epsilon; }
478
479 void setMetaData(const std::unordered_map<std::string, std::vector<double>>
480 &passedMetaData) {
481 metaData = passedMetaData;
482 }
483
484 void addMetaData(const std::string &key, double value) {
485 metaData[key] = std::vector<double>{value};
486 }
487
488 void addMetaData(const std::string &key, const std::vector<double> &values) {
489 metaData[key] = values;
490 }
491
492 void addMetaData(
493 const std::unordered_map<std::string, std::vector<double>> &newMetaData) {
494 for (const auto &pair : newMetaData) {
495 metaData[pair.first] = pair.second;
496 }
497 }
498
499 auto getVolumeMesh() const { return volumeVTK; }
500
501 auto getHullMesh() const { return hullVTK; }
502
503 void apply() {
504 // check if level sets have enough layers
505 for (unsigned i = 0; i < levelSets.size(); ++i) {
506 if (levelSets[i]->getLevelSetWidth() < 2) {
507 VIENNACORE_LOG_WARNING(
508 "WriteVisualizationMesh: Level Set " + std::to_string(i) +
509 " should have a width greater than 1! Conversion might fail!");
510 }
511 }
512
513 // This algorithm peels material regions inward: it starts from the region
514 // inside levelSets.back() and repeatedly subdivides that with the earlier
515 // level sets. It therefore requires a NESTED stack, where the last level
516 // set encloses every earlier one.
517 //
518 // A domain may legitimately hold a top level set that does not wrap the
519 // others — a LOCOS mask inserted with wrapLowerLevelSet=false, which has to
520 // stay a separate body for the mask mechanics. Passing that in unchanged
521 // clips the entire mesh to the mask's own extent, so the substrate and most
522 // of the oxide silently vanish from the output.
523 //
524 // Repair it here with cumulative unions. This is a no-op on an already
525 // nested stack (the union of a set with a subset is that set), so it only
526 // affects inputs that would otherwise be rendered wrongly. The unions are
527 // built on deep copies, leaving the caller's level sets untouched.
528 if (levelSets.size() > 1) {
529 LevelSetsType nested;
530 nested.reserve(levelSets.size());
531 for (auto &ls : levelSets) {
532 auto merged = SmartPointer<Domain<T, D>>::New(ls);
533 if (!nested.empty())
534 BooleanOperation<T, D>(merged, nested.back(),
535 BooleanOperationEnum::UNION)
536 .apply();
537 nested.push_back(merged);
538 }
539 levelSets = std::move(nested);
540 }
541
542 const double gridDelta = levelSets[0]->getGrid().getGridDelta();
543
544 // store volume for each material
545 std::vector<vtkSmartPointer<vtkUnstructuredGrid>> materialMeshes;
546 std::vector<unsigned> materialIds;
547
548 int totalMinimum = std::numeric_limits<int>::max();
549 int totalMaximum = -std::numeric_limits<int>::max();
550 for (auto &it : levelSets) {
551 if (it->getNumberOfPoints() == 0) {
552 continue;
553 }
554 auto &grid = it->getGrid();
555 auto &domain = it->getDomain();
556 for (unsigned i = 0; i < D; ++i) {
557 if (grid.getBoundaryConditions(i) ==
558 Domain<T, D>::BoundaryType::INFINITE_BOUNDARY) {
559 totalMinimum = std::min(totalMinimum, domain.getMinRunBreak(i));
560 totalMaximum = std::max(totalMaximum, domain.getMaxRunBreak(i));
561 }
562 }
563 }
564
565 // create volume mesh for largest LS
566 // Use vtkClipDataSet to slice the grid
567 vtkSmartPointer<vtkTableBasedClipDataSet> clipper =
568 vtkSmartPointer<vtkTableBasedClipDataSet>::New();
569 auto topGrid = vtkSmartPointer<vtkRectilinearGrid>::New();
570 // if (bottomRemoved) {
571 topGrid =
572 LS2RectiLinearGrid(levelSets.back(), 0, totalMinimum, totalMaximum);
573 // } else {
574 // topGrid = LS2RectiLinearGrid<false>(levelSets.back(), 0,
575 // totalMinimum,
576 // totalMaximum);
577 // }
578#ifdef LS_TO_VISUALIZATION_DEBUG
579 {
580 auto gwriter = vtkSmartPointer<vtkXMLRectilinearGridWriter>::New();
581 gwriter->SetFileName("./grid_0.vtr");
582 gwriter->SetInputData(topGrid);
583 gwriter->Write();
584 std::cout << "Wrote grid 0" << std::endl;
585 }
586#endif
587 clipper->SetInputData(topGrid);
588 clipper->InsideOutOn();
589 clipper->SetValue(0.0);
590 clipper->GenerateClippedOutputOn(); // TODO remove
591 clipper->Update();
592
593#ifdef LS_TO_VISUALIZATION_DEBUG
594 {
595 auto gwriter = vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
596 gwriter->SetFileName("./clipped.vtu");
597 gwriter->SetInputData(clipper->GetClippedOutput());
598 gwriter->Write();
599 std::cout << "Wrote clipped" << std::endl;
600 }
601#endif
602
603 const bool useMaterialMap = materialMap != nullptr;
604 materialMeshes.emplace_back(clipper->GetOutput());
605 materialIds.push_back(useMaterialMap ? materialMap->getMaterialId(0) : 0);
606
607#ifdef LS_TO_VISUALIZATION_DEBUG
608 {
609 auto gwriter = vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
610 gwriter->SetFileName("./probed_0.vtu");
611 gwriter->SetInputData(materialMeshes.front());
612 gwriter->Write();
613 }
614#endif
615
616 unsigned counter = 1;
617
618 // now cut large volume mesh with all the smaller ones
619 for (typename LevelSetsType::const_reverse_iterator it =
620 ++levelSets.rbegin();
621 it != levelSets.rend(); ++it) {
622 if (it->get()->getNumberOfPoints() == 0)
623 continue; // ignore empty levelSets
624
625 // create grid of next LS with slight offset and project into current
626 // mesh
627 vtkSmartPointer<vtkRectilinearGrid> rgrid =
628 vtkSmartPointer<vtkRectilinearGrid>::New();
629 // if (bottomRemoved) {
630 rgrid = LS2RectiLinearGrid<1>(*it, -LSEpsilon * counter, totalMinimum,
631 totalMaximum);
632 // } else {
633 // rgrid = LS2RectiLinearGrid<false, 1>(*it, -LSEpsilon * counter,
634 // totalMinimum, totalMaximum);
635 // }
636
637#ifdef LS_TO_VISUALIZATION_DEBUG
638 {
639 vtkSmartPointer<vtkXMLRectilinearGridWriter> gwriter =
640 vtkSmartPointer<vtkXMLRectilinearGridWriter>::New();
641 gwriter->SetFileName(
642 ("./grid_" + std::to_string(counter) + ".vtr").c_str());
643 gwriter->SetInputData(rgrid);
644 gwriter->Write();
645 std::cout << "Wrote grid " << counter << std::endl;
646 }
647#endif
648
649 // now transfer implicit values to mesh points
650 vtkSmartPointer<vtkProbeFilter> probeFilter =
651 vtkSmartPointer<vtkProbeFilter>::New();
652 probeFilter->SetInputData(materialMeshes.back()); // last element
653 probeFilter->SetSourceData(rgrid);
654 probeFilter->Update();
655
656#ifdef LS_TO_VISUALIZATION_DEBUG
657 {
658 vtkSmartPointer<vtkXMLUnstructuredGridWriter> gwriter =
659 vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
660 gwriter->SetFileName(
661 ("./probed_" + std::to_string(counter) + ".vtu").c_str());
662 gwriter->SetInputData(probeFilter->GetOutput());
663 gwriter->Write();
664 std::cout << "Wrote unstructured grid " << counter << std::endl;
665 }
666#endif
667
668 // now clip the mesh and save the clipped as the 1st layer and use the
669 // inverse for the next layer clipping Use vtkTabelBasedClipDataSet to
670 // slice the grid
671 vtkSmartPointer<vtkTableBasedClipDataSet> insideClipper =
672 vtkSmartPointer<vtkTableBasedClipDataSet>::New();
673 insideClipper->SetInputConnection(probeFilter->GetOutputPort());
674 insideClipper->GenerateClippedOutputOn();
675 insideClipper->Update();
676
677 materialMeshes.back() = insideClipper->GetOutput();
678 materialMeshes.emplace_back(insideClipper->GetClippedOutput());
679 auto material = counter;
680 if (useMaterialMap)
681 material = materialMap->getMaterialId(counter);
682 materialIds.push_back(material);
683
684 ++counter;
685 }
686
687 vtkSmartPointer<vtkAppendFilter> appendFilter =
688 vtkSmartPointer<vtkAppendFilter>::New();
689
690 vtkSmartPointer<vtkAppendPolyData> hullAppendFilter =
691 vtkSmartPointer<vtkAppendPolyData>::New();
692
693 for (unsigned i = 0; i < materialMeshes.size(); ++i) {
694
695 // write material number in mesh
696 vtkSmartPointer<vtkIntArray> materialNumberArray =
697 vtkSmartPointer<vtkIntArray>::New();
698 materialNumberArray->SetNumberOfComponents(1);
699 materialNumberArray->SetName("Material");
700 for (unsigned j = 0;
701 j <
702 materialMeshes[materialMeshes.size() - 1 - i]->GetNumberOfCells();
703 ++j) {
704 materialNumberArray->InsertNextValue(materialIds[i]);
705 }
706 materialMeshes[materialMeshes.size() - 1 - i]->GetCellData()->SetScalars(
707 materialNumberArray);
708
709 // delete all point data, so it is not in ouput
710 // TODO this includes signed distance information which could be
711 // conserved for debugging also includes wheter a cell was vaild for
712 // cutting by the grid
713 vtkSmartPointer<vtkPointData> pointData =
714 materialMeshes[materialMeshes.size() - 1 - i]->GetPointData();
715 const int numberOfArrays = pointData->GetNumberOfArrays();
716 for (int j = 0; j < numberOfArrays; ++j) {
717 pointData->RemoveArray(0); // remove first array until none are left
718 }
719
720 // if hull mesh should be exported, create hull for each layer and put
721 // them together
722 if (extractHullMesh) {
723 vtkSmartPointer<vtkGeometryFilter> geoFilter =
724 vtkSmartPointer<vtkGeometryFilter>::New();
725 geoFilter->SetInputData(materialMeshes[materialMeshes.size() - 1 - i]);
726 geoFilter->Update();
727 hullAppendFilter->AddInputData(geoFilter->GetOutput());
728 }
729
730 appendFilter->AddInputData(materialMeshes[materialMeshes.size() - 1 - i]);
731 }
732
733 // do not need tetrahedral volume mesh if we do not print volume
734 volumeVTK = vtkSmartPointer<vtkUnstructuredGrid>::New();
735 hullVTK = vtkSmartPointer<vtkPolyData>::New();
736 if (extractVolumeMesh) {
737 appendFilter->Update();
738
739 // remove degenerate points and remove cells which collapse to zero
740 // volume then
741 volumeVTK = appendFilter->GetOutput();
742#ifdef LS_TO_VISUALIZATION_DEBUG
743 {
744 std::cout << "Before duplicate removal: " << std::endl;
745 std::cout << "Points: " << volumeVTK->GetNumberOfPoints() << std::endl;
746 std::cout << "Cells: " << volumeVTK->GetNumberOfCells() << std::endl;
747 vtkSmartPointer<vtkXMLUnstructuredGridWriter> gwriter =
748 vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
749 gwriter->SetFileName("before_removal.vtu");
750 gwriter->SetInputData(appendFilter->GetOutput());
751 gwriter->Update();
752 }
753#endif
754
755 // use 1/1000th of grid spacing for contraction of two similar points,
756 // so that tetrahedralisation works correctly
757 removeDuplicatePoints(volumeVTK, 1e-3 * gridDelta);
758
759#ifdef LS_TO_VISUALIZATION_DEBUG
760 {
761 std::cout << "After duplicate removal: " << std::endl;
762 std::cout << "Points: " << volumeVTK->GetNumberOfPoints() << std::endl;
763 std::cout << "Cells: " << volumeVTK->GetNumberOfCells() << std::endl;
764 vtkSmartPointer<vtkXMLUnstructuredGridWriter> gwriter =
765 vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
766 gwriter->SetFileName("after_removal.vtu");
767 gwriter->SetInputData(volumeVTK);
768 gwriter->Update();
769 }
770#endif
771
772 // change all 3D cells into tetras and all 2D cells to triangles
773 vtkSmartPointer<vtkDataSetTriangleFilter> triangleFilter =
774 vtkSmartPointer<vtkDataSetTriangleFilter>::New();
775 triangleFilter->SetInputData(volumeVTK);
776 triangleFilter->Update();
777 volumeVTK = triangleFilter->GetOutput();
778
779 // now that only tetras are left, remove the ones with degenerate points
780 removeDegenerateTetras(volumeVTK);
781
782 // add meta data to volume mesh
783 addMetaDataToVTK(volumeVTK);
784
785 if (writeToFile) {
786 auto writer = vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
787 writer->SetFileName((fileName + "_volume.vtu").c_str());
788 // Inline binary rather than VTK's default appended mode: VTK 9.1's
789 // reader cannot parse appended data once an array spans more than one
790 // 32 kB compression block, so meshes above a few thousand cells are
791 // written unreadably by its own reader (and by anything else linking
792 // 9.1). Verified across every appended variant — base64 or raw,
793 // compressed or not — all fail to round-trip, while inline binary
794 // succeeds at identical file size.
795 writer->SetDataModeToBinary();
796 writer->SetInputData(volumeVTK);
797 writer->Write();
798 }
799 }
800
801 // Now make hull mesh if necessary
802 if (extractHullMesh) {
803 hullAppendFilter->Update();
804 hullVTK = hullAppendFilter->GetOutput();
805 // use 1/1000th of grid spacing for contraction of two similar points
806 removeDuplicatePoints(hullVTK, 1e-3 * gridDelta);
807
808 vtkSmartPointer<vtkTriangleFilter> hullTriangleFilter =
809 vtkSmartPointer<vtkTriangleFilter>::New();
810 hullTriangleFilter->SetInputData(hullVTK);
811 hullTriangleFilter->Update();
812
813 hullVTK = hullTriangleFilter->GetOutput();
814
815 // add meta data to hull mesh
816 addMetaDataToVTK(hullVTK);
817
818 if (writeToFile) {
819 auto writer = vtkSmartPointer<vtkXMLPolyDataWriter>::New();
820 writer->SetFileName((fileName + "_hull.vtp").c_str());
821 // See the volume writer above: VTK 9.1 cannot read back its own
822 // appended-mode output for multi-block arrays.
823 writer->SetDataModeToBinary();
824 writer->SetInputData(hullVTK);
825 writer->Write();
826 } else {
827 hullTriangleFilter->Update();
828 }
829 }
830 }
831};
832
833// add all template specialisations for this class
834PRECOMPILE_PRECISION_DIMENSION(WriteVisualizationMesh)
835
836} // namespace viennals
837
838#endif // VIENNALS_USE_VTK
constexpr int D
Definition Epitaxy.cpp:12
double T
Definition Epitaxy.cpp:13
constexpr T gridDelta
Definition OxidationNitride.cpp:35
#define PRECOMPILE_PRECISION_DIMENSION(className)
Definition lsPreCompileMacros.hpp:24
writer
Definition AirGapDeposition.py:173
int counter
Definition Deposition.py:75
Definition lsAdvect.hpp:41