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
25#include <lsDomain.hpp>
26#include <lsMaterialMap.hpp>
28
29// #define LS_TO_VISUALIZATION_DEBUG
30#ifdef LS_TO_VISUALIZATION_DEBUG
31#include <vtkXMLRectilinearGridWriter.h>
32#endif
33
34namespace viennals {
35
36using namespace viennacore;
37
44template <class T, int D> class WriteVisualizationMesh {
45 typedef typename Domain<T, D>::DomainType hrleDomainType;
46 using LevelSetsType = std::vector<SmartPointer<Domain<T, D>>>;
47 LevelSetsType levelSets;
48 SmartPointer<MaterialMap> materialMap = nullptr;
49 std::string fileName;
50 bool extractVolumeMesh = true;
51 bool extractHullMesh = false;
52 bool bottomRemoved = false;
53 double LSEpsilon = 1e-2;
54
58 void removeDuplicatePoints(vtkSmartPointer<vtkPolyData> &polyData,
59 const double tolerance) {
60
61 vtkSmartPointer<vtkPolyData> newPolyData =
62 vtkSmartPointer<vtkPolyData>::New();
63 vtkSmartPointer<vtkIncrementalOctreePointLocator> ptInserter =
64 vtkSmartPointer<vtkIncrementalOctreePointLocator>::New();
65 ptInserter->SetTolerance(tolerance);
66
67 vtkSmartPointer<vtkPoints> newPoints = vtkSmartPointer<vtkPoints>::New();
68
69 // get bounds
70 double gridBounds[6];
71 polyData->GetBounds(gridBounds);
72
73 // start point insertion from original points
74 std::vector<vtkIdType> newPointIds;
75 newPointIds.reserve(polyData->GetNumberOfPoints());
76 ptInserter->InitPointInsertion(newPoints, gridBounds);
77
78 // make new point list
79 for (vtkIdType pointId = 0; pointId < polyData->GetNumberOfPoints();
80 ++pointId) {
81 vtkIdType globalPtId = 0;
82 ptInserter->InsertUniquePoint(polyData->GetPoint(pointId), globalPtId);
83 newPointIds.push_back(globalPtId);
84 }
85
86 // now set the new points to the unstructured grid
87 newPolyData->SetPoints(newPoints);
88
89 // go through all cells and change point ids to match the new ids
90 vtkSmartPointer<vtkCellArray> oldCells = polyData->GetPolys();
91 vtkSmartPointer<vtkCellArray> newCells =
92 vtkSmartPointer<vtkCellArray>::New();
93
94 vtkSmartPointer<vtkIdList> cellPoints = vtkIdList::New();
95 oldCells->InitTraversal();
96 while (oldCells->GetNextCell(cellPoints)) {
97 for (vtkIdType pointId = 0; pointId < cellPoints->GetNumberOfIds();
98 ++pointId) {
99 cellPoints->SetId(pointId, newPointIds[cellPoints->GetId(pointId)]);
100 }
101 // insert same cell with new points
102 newCells->InsertNextCell(cellPoints);
103 }
104
105 newPolyData->SetPolys(newCells);
106
107 // conserve all cell data
108 // TODO transfer point data as well (do with "InsertTuples (vtkIdList
109 // *dstIds, vtkIdList *srcIds, vtkAbstractArray *source) override)" of
110 // vtkDataArray class)
111 newPolyData->GetCellData()->ShallowCopy(polyData->GetCellData());
112
113 // set ugrid to the newly created grid
114 polyData = newPolyData;
115 }
116
119 void removeDuplicatePoints(vtkSmartPointer<vtkUnstructuredGrid> &ugrid,
120 const double tolerance) {
121
122 vtkSmartPointer<vtkUnstructuredGrid> newGrid =
123 vtkSmartPointer<vtkUnstructuredGrid>::New();
124 vtkSmartPointer<vtkIncrementalOctreePointLocator> ptInserter =
125 vtkSmartPointer<vtkIncrementalOctreePointLocator>::New();
126 ptInserter->SetTolerance(tolerance);
127
128 vtkSmartPointer<vtkPoints> newPoints = vtkSmartPointer<vtkPoints>::New();
129
130 // get bounds
131 double gridBounds[6];
132 ugrid->GetBounds(gridBounds);
133
134 // start point insertion from original points
135 std::vector<vtkIdType> newPointIds;
136 newPointIds.reserve(ugrid->GetNumberOfPoints());
137 ptInserter->InitPointInsertion(newPoints, gridBounds);
138
139 // make new point list
140 for (vtkIdType pointId = 0; pointId < ugrid->GetNumberOfPoints();
141 ++pointId) {
142 vtkIdType globalPtId = 0;
143 ptInserter->InsertUniquePoint(ugrid->GetPoint(pointId), globalPtId);
144 newPointIds.push_back(globalPtId);
145 }
146
147 // now set the new points to the unstructured grid
148 newGrid->SetPoints(newPoints);
149
150 // go through all cells and change point ids to match the new ids
151 for (vtkIdType cellId = 0; cellId < ugrid->GetNumberOfCells(); ++cellId) {
152 vtkSmartPointer<vtkIdList> cellPoints = vtkSmartPointer<vtkIdList>::New();
153 ugrid->GetCellPoints(cellId, cellPoints);
154 for (vtkIdType pointId = 0; pointId < cellPoints->GetNumberOfIds();
155 ++pointId) {
156 cellPoints->SetId(pointId, newPointIds[cellPoints->GetId(pointId)]);
157 }
158 // insert same cell with new points
159 newGrid->InsertNextCell(ugrid->GetCell(cellId)->GetCellType(),
160 cellPoints);
161 }
162
163 // conserve all cell data
164 // TODO transfer point data as well (do with "InsertTuples (vtkIdList
165 // *dstIds, vtkIdList *srcIds, vtkAbstractArray *source) override)" of
166 // vtkDataArray class)
167 newGrid->GetCellData()->ShallowCopy(ugrid->GetCellData());
168
169 // set ugrid to the newly created grid
170 ugrid = newGrid;
171 }
172
174 void removeDegenerateTetras(vtkSmartPointer<vtkUnstructuredGrid> &ugrid) {
175 vtkSmartPointer<vtkUnstructuredGrid> newGrid =
176 vtkSmartPointer<vtkUnstructuredGrid>::New();
177
178 // need to copy material numbers
179 vtkSmartPointer<vtkIntArray> materialNumberArray =
180 vtkSmartPointer<vtkIntArray>::New();
181 materialNumberArray->SetNumberOfComponents(1);
182 materialNumberArray->SetName("Material");
183
184 // see if material is defined
185 int arrayIndex;
186 vtkDataArray *matArray =
187 ugrid->GetCellData()->GetArray("Material", arrayIndex);
188 const int &materialArrayIndex = arrayIndex;
189
190 // go through all cells and delete those with duplicate entries
191 for (vtkIdType cellId = 0; cellId < ugrid->GetNumberOfCells(); ++cellId) {
192 vtkSmartPointer<vtkIdList> cellPoints = vtkSmartPointer<vtkIdList>::New();
193 ugrid->GetCellPoints(cellId, cellPoints);
194 bool isDuplicate = false;
195 for (vtkIdType pointId = 0; pointId < cellPoints->GetNumberOfIds();
196 ++pointId) {
197 for (vtkIdType nextId = pointId + 1;
198 nextId < cellPoints->GetNumberOfIds(); ++nextId) {
199 // if they are the same, remove the cell
200 if (cellPoints->GetId(pointId) == cellPoints->GetId(nextId))
201 isDuplicate = true;
202 }
203 }
204 if (!isDuplicate) {
205 // insert same cell if no degenerate points
206 newGrid->InsertNextCell(ugrid->GetCell(cellId)->GetCellType(),
207 cellPoints);
208 // if material was defined before, use it now
209 if (materialArrayIndex >= 0)
210 materialNumberArray->InsertNextValue(matArray->GetTuple1(cellId));
211 }
212 }
213
214 // just take the old points and point data
215 newGrid->SetPoints(ugrid->GetPoints());
216 newGrid->GetPointData()->ShallowCopy(ugrid->GetPointData());
217 // set material cell data
218 newGrid->GetCellData()->SetScalars(materialNumberArray);
219
220 ugrid = newGrid;
221 }
222
223 // This function takes a levelset and converts it to a vtkRectilinearGrid
224 // The full domain contains values, which are capped at numLayers * gridDelta
225 // gridExtraPoints layers of grid points are added to the domain according to
226 // boundary conditions
227 template <int gridExtraPoints = 0>
228 vtkSmartPointer<vtkRectilinearGrid>
229 LS2RectiLinearGrid(SmartPointer<Domain<T, D>> levelSet, const double LSOffset,
230 int infiniteMinimum = std::numeric_limits<int>::max(),
231 int infiniteMaximum = -std::numeric_limits<int>::max()) {
232
233 auto &grid = levelSet->getGrid();
234 auto &domain = levelSet->getDomain();
235 double gridDelta = grid.getGridDelta();
236 int numLayers = levelSet->getLevelSetWidth();
237
238 vtkSmartPointer<vtkFloatArray>
239 coords[3]; // needs to be 3 because vtk only knows 3D
240 int gridMin = 0, gridMax = 0;
241 // int openJumpDirection = -1; // direction above the open boundary
242 // direction
243
244 // fill grid with offset depending on orientation
245 for (unsigned i = 0; i < D; ++i) {
246 coords[i] = vtkSmartPointer<vtkFloatArray>::New();
247
248 if (grid.getBoundaryConditions(i) ==
249 Domain<T, D>::BoundaryType::INFINITE_BOUNDARY) {
250 // add one to gridMin and gridMax for numerical stability
251 gridMin = std::min(domain.getMinRunBreak(i), infiniteMinimum) -
252 1; // choose the smaller number so that for first levelset the
253 // overall minimum can be chosen
254 gridMax = std::max(domain.getMaxRunBreak(i), infiniteMaximum) + 1;
255
256 // openJumpDirection = i + 1;
257 } else {
258 gridMin = grid.getMinGridPoint(i) - gridExtraPoints;
259 gridMax = grid.getMaxGridPoint(i) + gridExtraPoints;
260 }
261
262 for (int x = gridMin; x <= gridMax; ++x) {
263 coords[i]->InsertNextValue(x * gridDelta);
264 }
265 }
266
267 // if we work in 2D, just add 1 grid point at origin
268 if (D == 2) {
269 coords[2] = vtkSmartPointer<vtkFloatArray>::New();
270 coords[2]->InsertNextValue(0);
271 }
272
273 vtkSmartPointer<vtkRectilinearGrid> rgrid =
274 vtkSmartPointer<vtkRectilinearGrid>::New();
275
276 rgrid->SetDimensions(coords[0]->GetNumberOfTuples(),
277 coords[1]->GetNumberOfTuples(),
278 coords[2]->GetNumberOfTuples());
279 rgrid->SetXCoordinates(coords[0]);
280 rgrid->SetYCoordinates(coords[1]);
281 rgrid->SetZCoordinates(coords[2]);
282
283 // bool fixBorderPoints = (gridExtraPoints != 0);
284
285 // need to save the current position one dimension above open boundary
286 // direction, so we can register a jump in the open boundary direction when
287 // it occurs, so we can fix the LS value as follows: if remove_bottom==true,
288 // we need to flip the sign otherwise the sign stays the same
289 // int currentOpenIndex =
290 // it.getIndices()[(openJumpDirection < D) ? openJumpDirection : 0];
291
292 // Make array to store signed distance function
293 auto const numGridPoints = rgrid->GetNumberOfPoints();
294 vtkSmartPointer<vtkFloatArray> signedDistances =
295 vtkSmartPointer<vtkFloatArray>::New();
296 signedDistances->SetNumberOfComponents(1);
297 signedDistances->SetNumberOfTuples(numGridPoints);
298 signedDistances->SetName("SignedDistances");
299
300#pragma omp parallel
301 {
302 // use dense iterator to got to every index location
303 hrleConstDenseIterator<typename Domain<T, D>::DomainType> it(
304 levelSet->getDomain());
305
306#pragma omp for
307 for (vtkIdType pointId = 0; pointId < numGridPoints; ++pointId) {
308 // iterate until all grid points have a signed distance value
309
310 double p[3];
311 rgrid->GetPoint(pointId, p);
312 // create index vector
313 hrleVectorType<hrleIndexType, D> indices(
314 grid.globalCoordinates2GlobalIndices(p));
315
316 // write the corresponding LSValue
317 T value;
318
319 // if indices are outside of domain map to local indices
320 if (grid.isOutsideOfDomain(indices)) {
321 indices = grid.globalIndices2LocalIndices(indices);
322 // fixBorderPoints = true;
323 // signedDistances->InsertNextValue(
324 // signedDistances->GetDataTypeValueMax());
325 }
326
327 it.goToIndices(indices);
328 if (it.getValue() == Domain<T, D>::POS_VALUE) {
329 value = numLayers;
330 } else if (it.getValue() == Domain<T, D>::NEG_VALUE) {
331 value = -numLayers;
332 } else {
333 value = it.getValue() + LSOffset;
334 }
335
336 // if (removeBottom) {
337 // // if we jump from one end of the domain to the other and are not
338 // // already in the new run, we need to fix the sign of the run
339 // if (currentOpenIndex != indices[openJumpDirection]) {
340 // currentOpenIndex = indices[openJumpDirection];
341 // if (indices >= it.getIndices()) {
342 // value = -value;
343 // }
344 // }
345 // }
346
347 signedDistances->SetValue(pointId, value * gridDelta);
348 // signedDistances->InsertNextValue(value * gridDelta);
349 }
350
351 // // advance iterator until it is at correct point
352 // while (compare(it_l.end_indices(), indices) < 0) {
353 // it_l.next();
354 // if (it_l.is_finished())
355 // break;
356 // }
357 // // now move iterator with pointId to get to next point
358 // switch (compare(it_l.end_indices(), indices)) {
359 // case 0:
360 // it_l.next();
361 // default:
362 // ++pointId;
363 // }
364 // }
365 }
366
367 // now need to go through again to fix border points, this is done by
368 // mapping existing points onto the points outside of the domain according
369 // to the correct boundary conditions
370 // if (fixBorderPoints) {
371 // vtkIdType pointId = 0;
372 // while ((pointId < rgrid->GetNumberOfPoints())) {
373 // if (signedDistances->GetValue(pointId) ==
374 // signedDistances->GetDataTypeValueMax()) {
375 // double p[3];
376 // rgrid->GetPoint(pointId, p);
377
378 // // create index vector
379 // hrleVectorType<hrleIndexType, D> indices(
380 // grid.globalCoordinates2GlobalIndices(p));
381
382 // // vector for mapped point inside domain
383 // hrleVectorType<hrleIndexType, D> localIndices =
384 // grid.globalIndices2LocalIndices(indices);
385
386 // // now find Id of point we need to take value from
387 // int originalPointId = 0;
388 // for (int i = D - 1; i >= 0; --i) {
389 // originalPointId *=
390 // coords[i]->GetNumberOfTuples(); // extent in direction
391 // originalPointId += localIndices[i] - indices[i];
392 // }
393 // originalPointId += pointId;
394
395 // // now put value of mapped point in global point
396 // signedDistances->SetValue(pointId,
397 // signedDistances->GetValue(originalPointId));
398 // }
399 // ++pointId;
400 // }
401 // }
402
403 // Add the SignedDistances to the grid
404 rgrid->GetPointData()->SetScalars(signedDistances);
405
406 return rgrid;
407 }
408
409public:
410 WriteVisualizationMesh() {}
411
412 WriteVisualizationMesh(SmartPointer<Domain<T, D>> levelSet) {
413 levelSets.push_back(levelSet);
414 }
415
417 void insertNextLevelSet(SmartPointer<Domain<T, D>> levelSet) {
418 levelSets.push_back(levelSet);
419 }
420
423 void setFileName(std::string passedFileName) { fileName = passedFileName; }
424
426 void setExtractHullMesh(bool passedExtractHullMesh) {
427 extractHullMesh = passedExtractHullMesh;
428 }
429
431 void setExtractVolumeMesh(bool passedExtractVolumeMesh) {
432 extractVolumeMesh = passedExtractVolumeMesh;
433 }
434
435 void setMaterialMap(SmartPointer<MaterialMap> passedMaterialMap) {
436 materialMap = passedMaterialMap;
437 }
438
439 void setWrappingLayerEpsilon(double epsilon) { LSEpsilon = epsilon; }
440
441 void apply() {
442 // check if level sets have enough layers
443 for (unsigned i = 0; i < levelSets.size(); ++i) {
444 if (levelSets[i]->getLevelSetWidth() < 2) {
445 Logger::getInstance()
446 .addWarning(
447 "WriteVisualizationMesh: Level Set " + std::to_string(i) +
448 " should have a width greater than 1! Conversion might fail!")
449 .print();
450 }
451 }
452
453 const double gridDelta = levelSets[0]->getGrid().getGridDelta();
454
455 // store volume for each material
456 std::vector<vtkSmartPointer<vtkUnstructuredGrid>> materialMeshes;
457 std::vector<unsigned> materialIds;
458
459 int totalMinimum = std::numeric_limits<int>::max();
460 int totalMaximum = -std::numeric_limits<int>::max();
461 for (auto &it : levelSets) {
462 if (it->getNumberOfPoints() == 0) {
463 continue;
464 }
465 auto &grid = it->getGrid();
466 auto &domain = it->getDomain();
467 for (unsigned i = 0; i < D; ++i) {
468 if (grid.getBoundaryConditions(i) ==
469 Domain<T, D>::BoundaryType::INFINITE_BOUNDARY) {
470 totalMinimum = std::min(totalMinimum, domain.getMinRunBreak(i));
471 totalMaximum = std::max(totalMaximum, domain.getMaxRunBreak(i));
472 }
473 }
474 }
475
476 // create volume mesh for largest LS
477 // Use vtkClipDataSet to slice the grid
478 vtkSmartPointer<vtkTableBasedClipDataSet> clipper =
479 vtkSmartPointer<vtkTableBasedClipDataSet>::New();
480 auto topGrid = vtkSmartPointer<vtkRectilinearGrid>::New();
481 // if (bottomRemoved) {
482 topGrid =
483 LS2RectiLinearGrid(levelSets.back(), 0, totalMinimum, totalMaximum);
484 // } else {
485 // topGrid = LS2RectiLinearGrid<false>(levelSets.back(), 0,
486 // totalMinimum,
487 // totalMaximum);
488 // }
489#ifdef LS_TO_VISUALIZATION_DEBUG
490 {
491 auto gwriter = vtkSmartPointer<vtkXMLRectilinearGridWriter>::New();
492 gwriter->SetFileName("./grid_0.vtr");
493 gwriter->SetInputData(topGrid);
494 gwriter->Write();
495 std::cout << "Wrote grid 0" << std::endl;
496 }
497#endif
498 clipper->SetInputData(topGrid);
499 clipper->InsideOutOn();
500 clipper->SetValue(0.0);
501 clipper->GenerateClippedOutputOn(); // TODO remove
502 clipper->Update();
503
504#ifdef LS_TO_VISUALIZATION_DEBUG
505 {
506 auto gwriter = vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
507 gwriter->SetFileName("./clipped.vtu");
508 gwriter->SetInputData(clipper->GetClippedOutput());
509 gwriter->Write();
510 std::cout << "Wrote clipped" << std::endl;
511 }
512#endif
513
514 const bool useMaterialMap = materialMap != nullptr;
515 materialMeshes.push_back(clipper->GetOutput());
516 materialIds.push_back(useMaterialMap ? materialMap->getMaterialId(0) : 0);
517
518#ifdef LS_TO_VISUALIZATION_DEBUG
519 {
520 auto gwriter = vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
521 gwriter->SetFileName("./probed_0.vtu");
522 gwriter->SetInputData(materialMeshes.front());
523 gwriter->Write();
524 }
525#endif
526
527 unsigned counter = 1;
528
529 // now cut large volume mesh with all the smaller ones
530 for (typename LevelSetsType::const_reverse_iterator it =
531 ++levelSets.rbegin();
532 it != levelSets.rend(); ++it) {
533 if (it->get()->getNumberOfPoints() == 0)
534 continue; // ignore empty levelSets
535
536 // create grid of next LS with slight offset and project into current
537 // mesh
538 vtkSmartPointer<vtkRectilinearGrid> rgrid =
539 vtkSmartPointer<vtkRectilinearGrid>::New();
540 // if (bottomRemoved) {
541 rgrid = LS2RectiLinearGrid<1>(*it, -LSEpsilon * counter, totalMinimum,
542 totalMaximum);
543 // } else {
544 // rgrid = LS2RectiLinearGrid<false, 1>(*it, -LSEpsilon * counter,
545 // totalMinimum, totalMaximum);
546 // }
547
548#ifdef LS_TO_VISUALIZATION_DEBUG
549 {
550 vtkSmartPointer<vtkXMLRectilinearGridWriter> gwriter =
551 vtkSmartPointer<vtkXMLRectilinearGridWriter>::New();
552 gwriter->SetFileName(
553 ("./grid_" + std::to_string(counter) + ".vtr").c_str());
554 gwriter->SetInputData(rgrid);
555 gwriter->Write();
556 std::cout << "Wrote grid " << counter << std::endl;
557 }
558#endif
559
560 // now transfer implicit values to mesh points
561 vtkSmartPointer<vtkProbeFilter> probeFilter =
562 vtkSmartPointer<vtkProbeFilter>::New();
563 probeFilter->SetInputData(materialMeshes.back()); // last element
564 probeFilter->SetSourceData(rgrid);
565 probeFilter->Update();
566
567#ifdef LS_TO_VISUALIZATION_DEBUG
568 {
569 vtkSmartPointer<vtkXMLUnstructuredGridWriter> gwriter =
570 vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
571 gwriter->SetFileName(
572 ("./probed_" + std::to_string(counter) + ".vtu").c_str());
573 gwriter->SetInputData(probeFilter->GetOutput());
574 gwriter->Write();
575 std::cout << "Wrote unstructured grid " << counter << std::endl;
576 }
577#endif
578
579 // now clip the mesh and save the clipped as the 1st layer and use the
580 // inverse for the next layer clipping Use vtkTabelBasedClipDataSet to
581 // slice the grid
582 vtkSmartPointer<vtkTableBasedClipDataSet> insideClipper =
583 vtkSmartPointer<vtkTableBasedClipDataSet>::New();
584 insideClipper->SetInputConnection(probeFilter->GetOutputPort());
585 insideClipper->GenerateClippedOutputOn();
586 insideClipper->Update();
587
588 materialMeshes.back() = insideClipper->GetOutput();
589 materialMeshes.push_back(insideClipper->GetClippedOutput());
590 int material = counter;
591 if (useMaterialMap)
592 material = materialMap->getMaterialId(counter);
593 materialIds.push_back(material);
594
595 ++counter;
596 }
597
598 vtkSmartPointer<vtkAppendFilter> appendFilter =
599 vtkSmartPointer<vtkAppendFilter>::New();
600
601 vtkSmartPointer<vtkAppendPolyData> hullAppendFilter =
602 vtkSmartPointer<vtkAppendPolyData>::New();
603
604 for (unsigned i = 0; i < materialMeshes.size(); ++i) {
605
606 // write material number in mesh
607 vtkSmartPointer<vtkIntArray> materialNumberArray =
608 vtkSmartPointer<vtkIntArray>::New();
609 materialNumberArray->SetNumberOfComponents(1);
610 materialNumberArray->SetName("Material");
611 for (unsigned j = 0;
612 j <
613 materialMeshes[materialMeshes.size() - 1 - i]->GetNumberOfCells();
614 ++j) {
615 materialNumberArray->InsertNextValue(materialIds[i]);
616 }
617 materialMeshes[materialMeshes.size() - 1 - i]->GetCellData()->SetScalars(
618 materialNumberArray);
619
620 // delete all point data, so it is not in ouput
621 // TODO this includes signed distance information which could be
622 // conserved for debugging also includes wheter a cell was vaild for
623 // cutting by the grid
624 vtkSmartPointer<vtkPointData> pointData =
625 materialMeshes[materialMeshes.size() - 1 - i]->GetPointData();
626 const int numberOfArrays = pointData->GetNumberOfArrays();
627 for (int j = 0; j < numberOfArrays; ++j) {
628 pointData->RemoveArray(0); // remove first array until none are left
629 }
630
631 // if hull mesh should be exported, create hull for each layer and put
632 // them together
633 if (extractHullMesh) {
634 vtkSmartPointer<vtkGeometryFilter> geoFilter =
635 vtkSmartPointer<vtkGeometryFilter>::New();
636 geoFilter->SetInputData(materialMeshes[materialMeshes.size() - 1 - i]);
637 geoFilter->Update();
638 hullAppendFilter->AddInputData(geoFilter->GetOutput());
639 }
640
641 appendFilter->AddInputData(materialMeshes[materialMeshes.size() - 1 - i]);
642 }
643
644 // do not need tetrahedral volume mesh if we do not print volume
645 auto volumeVTK = vtkSmartPointer<vtkUnstructuredGrid>::New();
646 auto hullVTK = vtkSmartPointer<vtkPolyData>::New();
647 if (extractVolumeMesh) {
648 appendFilter->Update();
649
650 // remove degenerate points and remove cells which collapse to zero
651 // volume then
652 volumeVTK = appendFilter->GetOutput();
653#ifdef LS_TO_VISUALIZATION_DEBUG
654 {
655 std::cout << "Before duplicate removal: " << std::endl;
656 std::cout << "Points: " << volumeVTK->GetNumberOfPoints() << std::endl;
657 std::cout << "Cells: " << volumeVTK->GetNumberOfCells() << std::endl;
658 vtkSmartPointer<vtkXMLUnstructuredGridWriter> gwriter =
659 vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
660 gwriter->SetFileName("before_removal.vtu");
661 gwriter->SetInputData(appendFilter->GetOutput());
662 gwriter->Update();
663 }
664#endif
665
666 // use 1/1000th of grid spacing for contraction of two similar points,
667 // so that tetrahedralisation works correctly
668 removeDuplicatePoints(volumeVTK, 1e-3 * gridDelta);
669
670#ifdef LS_TO_VISUALIZATION_DEBUG
671 {
672 std::cout << "After duplicate removal: " << std::endl;
673 std::cout << "Points: " << volumeVTK->GetNumberOfPoints() << std::endl;
674 std::cout << "Cells: " << volumeVTK->GetNumberOfCells() << std::endl;
675 vtkSmartPointer<vtkXMLUnstructuredGridWriter> gwriter =
676 vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
677 gwriter->SetFileName("after_removal.vtu");
678 gwriter->SetInputData(volumeVTK);
679 gwriter->Update();
680 }
681#endif
682
683 // change all 3D cells into tetras and all 2D cells to triangles
684 vtkSmartPointer<vtkDataSetTriangleFilter> triangleFilter =
685 vtkSmartPointer<vtkDataSetTriangleFilter>::New();
686 triangleFilter->SetInputData(volumeVTK);
687 triangleFilter->Update();
688 volumeVTK = triangleFilter->GetOutput();
689
690 // now that only tetras are left, remove the ones with degenerate points
691 removeDegenerateTetras(volumeVTK);
692
693 auto writer = vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New();
694 writer->SetFileName((fileName + "_volume.vtu").c_str());
695 writer->SetInputData(volumeVTK);
696 writer->Write();
697 }
698
699 // Now make hull mesh if necessary
700 if (extractHullMesh) {
701 hullAppendFilter->Update();
702 hullVTK = hullAppendFilter->GetOutput();
703 // use 1/1000th of grid spacing for contraction of two similar points
704 removeDuplicatePoints(hullVTK, 1e-3 * gridDelta);
705
706 vtkSmartPointer<vtkTriangleFilter> hullTriangleFilter =
707 vtkSmartPointer<vtkTriangleFilter>::New();
708 hullTriangleFilter->SetInputData(hullVTK);
709 hullTriangleFilter->Update();
710
711 hullVTK = hullTriangleFilter->GetOutput();
712
713 auto writer = vtkSmartPointer<vtkXMLPolyDataWriter>::New();
714 writer->SetFileName((fileName + "_hull.vtp").c_str());
715 writer->SetInputData(hullVTK);
716 writer->Write();
717 }
718 }
719}; // namespace viennals
720
721// add all template specialisations for this class
722PRECOMPILE_PRECISION_DIMENSION(WriteVisualizationMesh)
723
724} // namespace viennals
725
726#endif // VIENNALS_USE_VTK
#define PRECOMPILE_PRECISION_DIMENSION(className)
Definition lsPreCompileMacros.hpp:24
float gridDelta
Definition AirGapDeposition.py:21
int counter
Definition Deposition.py:71
Definition lsAdvect.hpp:37
constexpr int D
Definition pyWrap.cpp:66
double T
Definition pyWrap.cpp:64