NEMoSys  0.63.0
A modular, extensible resource with robust automated mesh generation, mesh quality analysis, adaptive mesh refinement, and data transfer between arbitrary meshes.
dataSetRegionBoundaryFilter.C
Go to the documentation of this file.
1 /*******************************************************************************
2 * Promesh *
3 * Copyright (C) 2022, IllinoisRocstar LLC. All rights reserved. *
4 * *
5 * Promesh is the property of IllinoisRocstar LLC. *
6 * *
7 * IllinoisRocstar LLC *
8 * Champaign, IL *
9 * www.illinoisrocstar.com *
10 * promesh@illinoisrocstar.com *
11 *******************************************************************************/
12 /*******************************************************************************
13 * This file is part of Promesh *
14 * *
15 * This version of Promesh is free software: you can redistribute it and/or *
16 * modify it under the terms of the GNU Lesser General Public License as *
17 * published by the Free Software Foundation, either version 3 of the License, *
18 * or (at your option) any later version. *
19 * *
20 * Promesh is distributed in the hope that it will be useful, but WITHOUT ANY *
21 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more *
23 * details. *
24 * *
25 * You should have received a copy of the GNU Lesser General Public License *
26 * along with this program. If not, see <https://www.gnu.org/licenses/>. *
27 * *
28 *******************************************************************************/
30 
31 #include <algorithm>
32 #include <array>
33 #include <iterator>
34 #include <map>
35 #include <tuple>
36 #include <type_traits>
37 #include <utility>
38 #include <vector>
39 
40 #include <vtkCellData.h>
41 #include <vtkDataSet.h>
42 #include <vtkIdTypeArray.h>
43 #include <vtkInformation.h>
44 #include <vtkInformationVector.h>
45 #include <vtkIntArray.h>
46 #include <vtkNew.h>
47 #include <vtkObjectFactory.h>
48 #include <vtkPointData.h>
49 #include <vtkPolyData.h>
50 #include <vtkRectilinearGrid.h>
51 
52 namespace NEM {
53 namespace MSH {
54 
55 namespace {
56 
57 struct BoundaryCell {
58  BoundaryCell(vtkIdType source, int side, int region)
59  : sources{source, -1}, sides{side, -1}, regions{region, -1} {}
60 
61  void setTwin(vtkIdType otherSource, int otherSide, int otherRegion) {
62  this->sources[1] = otherSource;
63  this->sides[1] = otherSide;
64  this->regions[1] = otherRegion;
65  if (otherRegion < this->regions[0]) {
66  std::swap(this->sources[0], this->sources[1]);
67  std::swap(this->sides[0], this->sides[1]);
68  std::swap(this->regions[0], this->regions[1]);
69  }
70  }
71 
72  std::array<vtkIdType, 2> sources;
73  std::array<int, 2> sides;
74  std::array<int, 2> regions;
75 };
76 
77 class BoundaryCellSet {
78  public:
79  using data_type = std::vector<
80  std::vector<std::pair<std::array<vtkIdType, 2>, BoundaryCell>>>;
81  explicit BoundaryCellSet(data_type::size_type size)
82  : data(size), total_size(0) {}
83 
84  class const_iterator
85  : public std::iterator<std::forward_iterator_tag, const BoundaryCell> {
86  private:
87  const_iterator(data_type::const_iterator outerIter,
88  data_type::const_iterator endOuterIter,
89  data_type::value_type::const_iterator innerIter)
90  : outerIter(outerIter),
91  endOuterIter(endOuterIter),
92  innerIter(innerIter){};
93 
94  public:
95  reference operator*() { return innerIter->second; }
96  pointer operator->() { return &innerIter->second; }
97  const_iterator &operator++() {
98  ++innerIter;
99  if (innerIter == outerIter->end()) {
100  do {
101  ++outerIter;
102  if (outerIter == endOuterIter) {
103  return *this;
104  }
105  } while (outerIter->empty());
106  innerIter = outerIter->begin();
107  }
108  return *this;
109  }
110  const_iterator operator++(int) {
111  auto temp = *this;
112  ++(*this);
113  return temp;
114  }
115  friend bool operator==(const const_iterator &l, const const_iterator &r) {
116  return (l.outerIter == r.outerIter && l.innerIter == r.innerIter) ||
117  (l.outerIter == l.endOuterIter && r.outerIter == r.endOuterIter);
118  }
119  friend bool operator!=(const const_iterator &l, const const_iterator &r) {
120  return !(l == r);
121  }
123 
124  private:
125  data_type::const_iterator outerIter;
126  const data_type::const_iterator endOuterIter;
127  data_type::value_type::const_iterator innerIter;
128  };
129 
130  const_iterator begin() const {
131  auto beginOuter = data.begin();
132  const auto endOuter = data.end();
133  while (beginOuter->empty()) {
134  ++beginOuter;
135  if (beginOuter == endOuter) {
136  return {beginOuter, endOuter, {}};
137  }
138  }
139  return {beginOuter, endOuter, beginOuter->begin()};
140  }
141 
142  const_iterator end() const {
143  auto endIter = data.end();
144  return {endIter, endIter, {}};
145  }
146 
147  void insert(vtkIdType source, int side, vtkIdList *points, int numPoints,
148  int region) {
149  auto pointsBegin = points->GetPointer(0);
150  auto numPointsKey = std::min(numPoints, 3);
151  auto pointsEnd = pointsBegin + numPoints;
152  auto pointsMiddle = pointsBegin + numPointsKey;
153  // We only need the least 2/3 point ids.
154  std::partial_sort(pointsBegin, pointsMiddle, pointsEnd);
155  insertImpl(data[pointsBegin[1]],
156  {pointsBegin[0], numPointsKey < 3 ? -1 : pointsBegin[2]}, source,
157  side, region);
158  }
159 
160  void insertTri(vtkIdType source, int face, vtkIdType point0, vtkIdType point1,
161  vtkIdType point2, int region) {
162  if (point0 > point1) std::swap(point0, point1);
163  if (point0 > point2) std::swap(point0, point2);
164  if (point1 > point2) std::swap(point1, point2);
165  insertImpl(this->data[point1], {point0, point2}, source, face, region);
166  }
167 
168  void insertQuad(vtkIdType source, int face, vtkIdType point0,
169  vtkIdType point1, vtkIdType point2, vtkIdType point3,
170  int region) {
171  if (point0 > point1) std::swap(point0, point1);
172  if (point2 > point3) std::swap(point2, point3);
173  if (point0 > point2) std::swap(point0, point2);
174  if (point1 > point3) std::swap(point1, point3);
175  if (point1 > point2) std::swap(point1, point2);
176  insertImpl(this->data[point1], {point0, point2}, source, face, region);
177  }
178 
179  // These methods are faster than repeated calls to cell->GetFace() then
180  // this->insert.
181  void insertTetFaces(vtkIdType source, const vtkIdType *points, int region) {
182  insertTri(source, 0, points[0], points[1], points[3], region);
183  insertTri(source, 1, points[1], points[2], points[3], region);
184  insertTri(source, 2, points[2], points[0], points[3], region);
185  insertTri(source, 3, points[0], points[2], points[1], region);
186  }
187 
188  void insertWedgeFaces(vtkIdType source, const vtkIdType *points, int region) {
189  insertTri(source, 0, points[0], points[1], points[2], region);
190  insertTri(source, 1, points[3], points[5], points[4], region);
191  insertQuad(source, 2, points[0], points[3], points[4], points[1], region);
192  insertQuad(source, 3, points[1], points[4], points[5], points[2], region);
193  insertQuad(source, 4, points[2], points[5], points[3], points[0], region);
194  }
195 
196  void insertHexFaces(vtkIdType source, const vtkIdType *points, int region) {
197  insertQuad(source, 0, points[0], points[4], points[7], points[3], region);
198  insertQuad(source, 1, points[1], points[2], points[6], points[5], region);
199  insertQuad(source, 2, points[0], points[1], points[5], points[4], region);
200  insertQuad(source, 3, points[3], points[7], points[6], points[2], region);
201  insertQuad(source, 4, points[0], points[3], points[2], points[1], region);
202  insertQuad(source, 5, points[4], points[5], points[6], points[7], region);
203  }
204 
205  data_type::value_type::size_type size() const { return total_size; }
206 
207  private:
208  /**
209  * Edge/face with sorted point ids (a, b, c, ...) is located at some index i
210  * in data[b], with data[b][i].first == [a, c] (for edges, third point id
211  * treated as -1). Using the second-least point as hash results in fewer
212  * collisions (using sum is even better as a hash, but slower to compute).
213  */
214  data_type data;
215  data_type::value_type::size_type total_size;
216 
217  void insertImpl(std::decay<decltype(data)>::type::value_type &bucket,
218  std::array<vtkIdType, 2> key, int source, int side,
219  int region) {
220  auto matchFind = std::find_if(
221  bucket.begin(), bucket.end(),
222  [&key](const std::decay<decltype(bucket)>::type::value_type &x) {
223  return x.first == key;
224  });
225  if (matchFind != bucket.end()) {
226  if (matchFind->second.regions[0] == region) {
227  // Remove from bucket
228  *matchFind = bucket.back();
229  bucket.pop_back();
230  total_size -= 1;
231  } else {
232  // Set twin information
233  matchFind->second.setTwin(source, side, region);
234  }
235  } else {
236  // Add to bucket
237  bucket.emplace_back(std::piecewise_construct, std::forward_as_tuple(key),
238  std::forward_as_tuple(source, side, region));
239  total_size += 1;
240  }
241  }
242 };
243 
244 } // namespace
245 
247 
249  int port, vtkInformation *info) {
250  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
251  return 1;
252 }
253 
255  vtkInformation *request, vtkInformationVector **inputVector,
256  vtkInformationVector *outputVector) {
257  // get the info objects
258  vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
259  vtkInformation *outInfo = outputVector->GetInformationObject(0);
260 
261  // get the input and output
262  auto input =
263  vtkDataSet::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
264  auto output =
265  vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
266 
267  if (input->CheckAttributes()) {
268  return 0;
269  }
270 
271  const vtkIdType numCells = input->GetNumberOfCells();
272  if (numCells == 0) {
273  vtkDebugMacro(<< "Number of cells is zero, no data to process.");
274  return 1;
275  }
276  vtkNew<vtkCellArray> newCells{};
277  // Approximate
278  newCells->Allocate(numCells);
279 
280  BoundaryCellSet boundaryCellSet(input->GetNumberOfPoints());
281  auto regionArr = vtkArrayDownCast<vtkIntArray>(
282  input->GetCellData()->GetAbstractArray(this->MaterialArrayName.c_str()));
283  for (vtkIdType i = 0; i < numCells; ++i) {
284  auto cell = input->GetCell(i);
285  auto region = regionArr->GetTypedComponent(i, 0);
286 
287  if (cell->GetCellDimension() != this->GetDimension()) {
288  continue;
289  }
290 
291  if (this->GetDimension() == 2) {
292  for (int j = 0; j < cell->GetNumberOfEdges(); ++j) {
293  auto edge = cell->GetEdge(j);
294  boundaryCellSet.insert(i, j, edge->GetPointIds(), 2, region);
295  }
296  } else { // this->GetDimension() == 3
297  switch (static_cast<VTKCellType>(cell->GetCellType())) {
298  case VTK_TETRA:
299  case VTK_QUADRATIC_TETRA:
300  case VTK_HIGHER_ORDER_TETRAHEDRON:
301  case VTK_LAGRANGE_TETRAHEDRON: {
302  auto points = cell->GetPointIds()->GetPointer(0);
303  boundaryCellSet.insertTetFaces(i, points, region);
304  break;
305  }
306  case VTK_HEXAHEDRON:
307  case VTK_QUADRATIC_HEXAHEDRON:
308  case VTK_TRIQUADRATIC_HEXAHEDRON:
309  case VTK_BIQUADRATIC_QUADRATIC_HEXAHEDRON:
310  case VTK_HIGHER_ORDER_HEXAHEDRON:
311  case VTK_LAGRANGE_HEXAHEDRON: {
312  auto points = cell->GetPointIds()->GetPointer(0);
313  boundaryCellSet.insertHexFaces(i, points, region);
314  break;
315  }
316  case VTK_WEDGE:
317  case VTK_QUADRATIC_WEDGE:
318  case VTK_QUADRATIC_LINEAR_WEDGE:
319  case VTK_BIQUADRATIC_QUADRATIC_WEDGE:
320  case VTK_HIGHER_ORDER_WEDGE:
321  case VTK_LAGRANGE_WEDGE: {
322  auto points = cell->GetPointIds()->GetPointer(0);
323  boundaryCellSet.insertWedgeFaces(i, points, region);
324  break;
325  }
326  default: {
327  for (int j = 0; j < cell->GetNumberOfFaces(); ++j) {
328  auto face = cell->GetFace(j);
329  // Treat non-linear faces as linear ones by only looking at
330  // beginning of point id array. Number of edges should be equal to
331  // number of vertices even for non-linear faces.
332  boundaryCellSet.insert(i, j, face->GetPointIds(),
333  face->GetNumberOfEdges(), region);
334  }
335  break;
336  }
337  }
338  }
339  }
340 
341  vtkSmartPointer<vtkIdTypeArray> origCellId{};
342  vtkSmartPointer<vtkIntArray> cellSideId{}, surfRegionId{};
343  if (!this->GetOrigCellArrayName().empty()) {
345  origCellId->SetName(this->GetOrigCellArrayName().c_str());
346  origCellId->SetNumberOfComponents(2);
347  origCellId->SetNumberOfTuples(boundaryCellSet.size());
348  }
349  if (!this->GetCellSideArrayName().empty()) {
350  cellSideId = vtkSmartPointer<vtkIntArray>::New();
351  cellSideId->SetName(this->GetCellSideArrayName().c_str());
352  cellSideId->SetNumberOfComponents(2);
353  cellSideId->SetNumberOfTuples(boundaryCellSet.size());
354  }
355  if (!this->GetBoundaryRegionArrayName().empty()) {
356  surfRegionId = vtkSmartPointer<vtkIntArray>::New();
357  surfRegionId->SetName(this->GetBoundaryRegionArrayName().c_str());
358  surfRegionId->SetNumberOfTuples(boundaryCellSet.size());
359  }
360 
361  // Map from regionArr to surfRegionId.
362  std::map<std::array<int, 2>, int> oldRegions2SurfRegions{};
363  for (const auto &boundaryCell : boundaryCellSet) {
364  auto origCellToInsert = input->GetCell(boundaryCell.sources[0]);
365  auto sideToInsert = this->GetDimension() == 2
366  ? origCellToInsert->GetEdge(boundaryCell.sides[0])
367  : origCellToInsert->GetFace(boundaryCell.sides[0]);
368  auto sideIdx = newCells->InsertNextCell(
369  this->GetDimension() == 2 ? 2 : sideToInsert->GetNumberOfEdges(),
370  sideToInsert->GetPointIds()->GetPointer(0));
371  if (origCellId) {
372  origCellId->SetTypedTuple(sideIdx, boundaryCell.sources.data());
373  }
374  if (cellSideId) {
375  cellSideId->SetTypedTuple(sideIdx, boundaryCell.sides.data());
376  }
377  auto surfRegionIter = oldRegions2SurfRegions.emplace(
378  boundaryCell.regions, oldRegions2SurfRegions.size());
379  if (surfRegionId) {
380  surfRegionId->SetTypedComponent(sideIdx, 0, surfRegionIter.first->second);
381  }
382  }
383  newCells->Squeeze();
384 
385  if (!this->GetRegionToMaterialArrayName().empty()) {
386  vtkNew<vtkIntArray> region2MaterialId;
387  region2MaterialId->SetName(this->GetRegionToMaterialArrayName().c_str());
388  region2MaterialId->SetNumberOfComponents(2);
389  region2MaterialId->SetNumberOfTuples(oldRegions2SurfRegions.size());
390  for (const auto &old2newRegion : oldRegions2SurfRegions) {
391  auto newRegion = old2newRegion.second;
392  region2MaterialId->SetComponent(newRegion, 0, old2newRegion.first[0]);
393  region2MaterialId->SetComponent(newRegion, 1, old2newRegion.first[1]);
394  }
395  output->GetFieldData()->AddArray(region2MaterialId);
396  }
397 
398  if (auto pointSet = vtkPointSet::SafeDownCast(input)) {
399  output->SetPoints(pointSet->GetPoints());
400  } else if (auto rectGrid = vtkRectilinearGrid::SafeDownCast(input)) {
401  rectGrid->GetPoints(output->GetPoints());
402  } else {
403  auto outPoints = output->GetPoints();
404  outPoints->Initialize();
405  outPoints->SetNumberOfPoints(input->GetNumberOfPoints());
406  for (vtkIdType i = 0; i < input->GetNumberOfPoints(); ++i) {
407  outPoints->SetPoint(i, input->GetPoint(i));
408  }
409  }
410  output->GetPointData()->ShallowCopy(input->GetPointData());
411 
412  if (this->GetDimension() == 2) {
413  output->SetLines(newCells);
414  } else { // this->GetDimension() == 3
415  output->SetPolys(newCells);
416  }
417 
418  vtkCellData *outputCD = output->GetCellData();
419  outputCD->AddArray(origCellId);
420  outputCD->AddArray(cellSideId);
421  outputCD->AddArray(surfRegionId);
422 
423  if (output->CheckAttributes()) {
424  return 0;
425  } else {
426  return 1;
427  }
428 }
429 
430 } // namespace MSH
431 } // namespace NEM
data_type data
Edge/face with sorted point ids (a, b, c, ...) is located at some index i in data[b], with data[b][i].first == [a, c] (for edges, third point id treated as -1).
geoMeshBase * New(MeshType meshType)
Create a new mesh object.
int FillInputPortInformation(int port, vtkInformation *info) override
int RequestData(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) override
data_type::value_type::const_iterator innerIter
Like vtkDataSetRegionSurfaceFilter, this filter extracts boundaries of materials, including both inte...
std::array< int, 2 > regions
std::vector< T > operator*(T a, const std::vector< T > &x)
friend BoundaryCellSet
std::vector< vtkIdType > points
points given by id in .inp file
Definition: inpGeoMesh.C:133
std::array< int, 2 > sides
data_type::value_type::size_type total_size
vtkStandardNewMacro(exoGeoMesh)
Definition: exoGeoMesh.C:247
data_type::const_iterator outerIter
const data_type::const_iterator endOuterIter
std::array< vtkIdType, 2 > sources