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.
geoMeshBase.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 *******************************************************************************/
29 #if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES)
30 # define _USE_MATH_DEFINES
31 #endif
32 
33 #include "Mesh/geoMeshBase.H"
34 
35 #include <iostream>
36 #include <set>
37 #include <utility>
38 
39 #ifdef HAVE_GMSH
40 #include <gmsh.h>
41 #endif
42 
43 #include <vtkAppendFilter.h>
44 
45 namespace NEM {
46 namespace MSH {
47 
49 #ifdef HAVE_GMSH
50  gmsh::initialize();
51  gmsh::option::setNumber("General.Terminal", 1.0); // Gmsh errors to stderr
52  gmsh::option::setNumber("Mesh.SaveAll", 1);
53  std::cout << "Gmsh initialized" << std::endl;
54 #endif
55 }
56 
58 #ifdef HAVE_GMSH
59  gmsh::finalize();
60  std::cout << "Gmsh finalized" << std::endl;
61 #endif
62 }
63 
64 std::shared_ptr<GmshInterface> GmshInterface::instance = nullptr;
65 int GmshInterface::count = 0;
66 
68  ++count;
69  if (!instance) instance = std::shared_ptr<GmshInterface>(new GmshInterface());
70 }
71 
73  --count;
74  if (count == 0) instance.reset();
75 }
76 
79 
81  : _geoMesh(std::move(inGeoMesh)), _angleThreshold(30.0 * M_PI / 180.0) {
82  InitializeObjectBase();
83 #ifdef HAVE_GMSH
85 #endif
86  std::cout << "geoMeshBase constructed" << std::endl;
87 }
88 
90  this->ReferenceCount--;
91 #ifdef HAVE_GMSH
93 #endif
94  std::cout << "geoMeshBase destructed" << std::endl;
95 }
96 
97 void geoMeshBase::report(std::ostream &out) const {
98  out << this->GetClassName() << ":\n";
99  auto mesh = getGeoMesh().mesh;
100  out << "Nodes:\t" << mesh->GetNumberOfPoints() << '\n';
101  out << "Elements:\t" << mesh->GetNumberOfCells() << '\n';
102 }
103 
105  _geoMesh = std::move(otherGeoMesh->_geoMesh);
106  otherGeoMesh->_geoMesh = {
108  otherGeoMesh->resetNative();
109  resetNative();
110 }
111 
112 // Only merges vtkDataSets. Need to add merge for sideSets, and geometry
114  vtkSmartPointer<vtkAppendFilter> appendFilter =
116  appendFilter->AddInputData(_geoMesh.mesh);
117  appendFilter->AddInputData(otherGeoMesh->_geoMesh.mesh);
118  appendFilter->MergePointsOn();
119  appendFilter->Update();
120  _geoMesh.mesh = appendFilter->GetOutput();
121  otherGeoMesh->_geoMesh = {
123  otherGeoMesh->resetNative();
124 }
125 
126 // Helper functions for geoMeshBase::get{Point, Cell, Field}DataArrayCopy
127 namespace {
128 
129 vtkSmartPointer<vtkAbstractArray> copyOrCreateCopy(
130  vtkAbstractArray *orig, vtkAbstractArray *copy = nullptr) {
131  if (copy) {
132  copy->DeepCopy(orig);
133  return nullptr;
134  } else if (orig) {
135  auto newCopy = vtkSmartPointer<vtkAbstractArray>::Take(orig->NewInstance());
136  newCopy->DeepCopy(orig);
137  return newCopy;
138  } else {
139  return nullptr;
140  }
141 }
142 
143 vtkSmartPointer<vtkAbstractArray> getArrCopy(
144  vtkFieldData *field, const std::string &arrayName, int *arrayIdx,
145  vtkAbstractArray *array = nullptr) {
146  auto orig = arrayIdx ? field->GetAbstractArray(arrayName.c_str(), *arrayIdx)
147  : field->GetAbstractArray(arrayName.c_str());
148  return copyOrCreateCopy(orig, array);
149 }
150 
151 vtkSmartPointer<vtkAbstractArray> getArrCopy(
152  vtkFieldData *field, int arrayIdx, vtkAbstractArray *array = nullptr) {
153  auto orig = field->GetAbstractArray(arrayIdx);
154  return copyOrCreateCopy(orig, array);
155 }
156 
157 } // namespace
158 
159 void geoMeshBase::getPointDataArrayCopy(const std::string &arrayName,
160  vtkAbstractArray *array,
161  int *arrayIdx) const {
162  getArrCopy(_geoMesh.mesh->GetPointData(), arrayName, arrayIdx, array);
163 }
164 
165 vtkSmartPointer<vtkAbstractArray> geoMeshBase::getPointDataArrayCopy(
166  const std::string &arrayName, int *arrayIdx) const {
167  return getArrCopy(_geoMesh.mesh->GetPointData(), arrayName, arrayIdx);
168 }
169 
171  vtkAbstractArray *array) const {
172  getArrCopy(_geoMesh.mesh->GetPointData(), arrayIdx, array);
173 }
174 
175 vtkSmartPointer<vtkAbstractArray> geoMeshBase::getPointDataArrayCopy(
176  int arrayIdx) const {
177  return getArrCopy(_geoMesh.mesh->GetPointData(), arrayIdx);
178 }
179 
180 void geoMeshBase::getCellDataArrayCopy(const std::string &arrayName,
181  vtkAbstractArray *array,
182  int *arrayIdx) const {
183  getArrCopy(_geoMesh.mesh->GetCellData(), arrayName, arrayIdx, array);
184 }
185 
186 vtkSmartPointer<vtkAbstractArray> geoMeshBase::getCellDataArrayCopy(
187  const std::string &arrayName, int *arrayIdx) const {
188  return getArrCopy(_geoMesh.mesh->GetCellData(), arrayName, arrayIdx);
189 }
190 
192  vtkAbstractArray *array) const {
193  getArrCopy(_geoMesh.mesh->GetCellData(), arrayIdx, array);
194 }
195 
196 vtkSmartPointer<vtkAbstractArray> geoMeshBase::getCellDataArrayCopy(
197  int arrayIdx) const {
198  return getArrCopy(_geoMesh.mesh->GetCellData(), arrayIdx);
199 }
200 
201 void geoMeshBase::getFieldDataArrayCopy(const std::string &arrayName,
202  vtkAbstractArray *array,
203  int *arrayIdx) const {
204  getArrCopy(_geoMesh.mesh->GetFieldData(), arrayName, arrayIdx, array);
205 }
206 
207 vtkSmartPointer<vtkAbstractArray> geoMeshBase::getFieldDataArrayCopy(
208  const std::string &arrayName, int *arrayIdx) const {
209  return getArrCopy(_geoMesh.mesh->GetFieldData(), arrayName, arrayIdx);
210 }
211 
213  vtkAbstractArray *array) const {
214  getArrCopy(_geoMesh.mesh->GetFieldData(), arrayIdx, array);
215 }
216 
217 vtkSmartPointer<vtkAbstractArray> geoMeshBase::getFieldDataArrayCopy(
218  int arrayIdx) const {
219  return getArrCopy(_geoMesh.mesh->GetFieldData(), arrayIdx);
220 }
221 
222 // void geoMeshBase::update() {
223 // /*
224 // if (_stale.GEO_MESH_LINK && !_stale.GEOMETRY && !_stale.MESH) {
225 // // Need to loop through all entities in the geometry and all points and
226 // // cells in the mesh and set the entity ID.
227 // std::vector<GEntity *> entities;
228 // _GModel.getEntities(entities, 1);
229 // // entities[0]->containsPoint()
230 //
231 // for (vtkIdType ptId = 0; ptId != _mesh->GetNumberOfPoints(); ++ptId) {
232 // std::array<double, 3> pt{};
233 // _mesh->GetPoint(ptId, pt.data());
234 // }
235 // }
236 // */
237 //}
238 
239 geoMeshBase::SideSet::SideSet(vtkPolyData *sideSet, vtkIntArray *geoEnt,
240  vtkIdTypeArray *origCell, vtkIntArray *cellFace,
241  vtkStringArray *setNames)
242  : sides(sideSet) {
243  assert(geoEnt || sideSet->GetCellData()->HasArray(GEO_ENT_NAME));
244  if (geoEnt) { setGeoEntArr(geoEnt); }
245  if (origCell) { setOrigCellArr(origCell); }
246  if (cellFace) { setCellFaceArr(cellFace); }
247  if (setNames) { setSideSetNamesArr(setNames); }
248 }
249 
250 geoMeshBase::SideSet::SideSet(vtkPolyData *sides) : SideSet(sides, nullptr) {}
251 
252 vtkSmartPointer<vtkIntArray> geoMeshBase::SideSet::getGeoEntArr() const {
253  return sides ? vtkIntArray::FastDownCast(
254  sides->GetCellData()->GetArray(GEO_ENT_NAME))
255  : nullptr;
256 }
257 
258 void geoMeshBase::SideSet::setGeoEntArr(vtkIntArray *arr) {
259  assert(arr);
260  arr->SetName(GEO_ENT_NAME);
261  sides->GetCellData()->AddArray(arr);
262 }
263 
264 vtkSmartPointer<vtkIdTypeArray> geoMeshBase::SideSet::getOrigCellArr() const {
265  return sides ? vtkIdTypeArray::FastDownCast(
266  sides->GetCellData()->GetAbstractArray(ORIG_CELL_NAME))
267  : nullptr;
268 }
269 
270 void geoMeshBase::SideSet::setOrigCellArr(vtkIdTypeArray *arr) {
271  if (arr) {
272  assert(arr->GetNumberOfTuples() == sides->GetNumberOfCells() &&
273  arr->GetNumberOfComponents() == 2);
274  arr->SetName(ORIG_CELL_NAME);
275  sides->GetCellData()->AddArray(arr);
276  } else {
277  sides->GetCellData()->RemoveArray(ORIG_CELL_NAME);
278  }
279 }
280 
281 vtkSmartPointer<vtkIntArray> geoMeshBase::SideSet::getCellFaceArr() const {
282  return sides ? vtkIntArray::FastDownCast(
283  sides->GetCellData()->GetAbstractArray(CELL_FACE_NAME))
284  : nullptr;
285 }
286 
287 void geoMeshBase::SideSet::setCellFaceArr(vtkIntArray *arr) {
288  if (arr) {
289  assert(arr->GetNumberOfTuples() == sides->GetNumberOfCells() &&
290  arr->GetNumberOfComponents() == 2);
291  arr->SetName(CELL_FACE_NAME);
292  sides->GetCellData()->AddArray(arr);
293  } else {
294  sides->GetCellData()->RemoveArray(CELL_FACE_NAME);
295  }
296 }
297 
298 vtkSmartPointer<vtkStringArray> geoMeshBase::SideSet::getSideSetNames() const {
299  return sides ? vtkStringArray::SafeDownCast(
300  sides->GetFieldData()->GetAbstractArray(NAME_ARR_NAME))
301  : nullptr;
302 }
303 
304 void geoMeshBase::SideSet::setSideSetNamesArr(vtkStringArray *arr) {
305  if (arr) {
306  assert(arr->GetNumberOfComponents() == 1);
307  arr->SetName(NAME_ARR_NAME);
308  sides->GetFieldData()->AddArray(arr);
309  } else {
310  sides->GetFieldData()->RemoveArray(NAME_ARR_NAME);
311  }
312 }
313 
315  if (sideSet.sides && !sideSet.getOrigCellArr() && !sideSet.getCellFaceArr()) {
316  vtkIdType numSides = sideSet.sides->GetNumberOfCells();
317  auto entArr = sideSet.getGeoEntArr();
318  vtkNew<vtkIdTypeArray> origCellArr;
319  origCellArr->SetNumberOfComponents(2);
320  origCellArr->SetNumberOfTuples(numSides);
321  origCellArr->FillTypedComponent(0, -1);
322  origCellArr->FillTypedComponent(1, -1);
323  vtkNew<vtkIntArray> cellFaceArr;
324  cellFaceArr->SetNumberOfComponents(2);
325  cellFaceArr->SetNumberOfTuples(numSides);
326  cellFaceArr->FillTypedComponent(0, -1);
327  cellFaceArr->FillTypedComponent(1, -1);
328  // Using points->cell, intersect the set of cells that have the same points
329  // as the side cell
330  mesh->BuildLinks();
331  auto pt2Cell = mesh->GetCellLinks();
332  for (vtkIdType i = 0; i < sideSet.sides->GetNumberOfCells(); ++i) {
333  auto sidePoints = sideSet.sides->GetCell(i)->GetPointIds();
334  auto cellList = pt2Cell->GetLink(sidePoints->GetId(0));
335  std::vector<vtkIdType> candidates{cellList.cells,
336  cellList.cells + cellList.ncells};
337  std::sort(candidates.begin(), candidates.end());
338  for (auto j = 1; j < sidePoints->GetNumberOfIds(); ++j) {
339  std::vector<vtkIdType> newCandidates{};
340  cellList = pt2Cell->GetLink(sidePoints->GetId(j));
341  for (auto iterCell = cellList.cells;
342  iterCell < cellList.cells + cellList.ncells; ++iterCell) {
343  auto findIter =
344  std::lower_bound(candidates.begin(), candidates.end(), *iterCell);
345  if (findIter != candidates.end() && *iterCell == *findIter) {
346  newCandidates.emplace_back(*iterCell);
347  }
348  }
349  candidates = std::move(newCandidates);
350  std::sort(candidates.begin(), candidates.end());
351  }
352  // For each candidate cell, check each edge/face for a match
353  bool foundMatch = false;
354  if (candidates.size() == 1) {
355  // If there is only one candidate (side is on boundary, not material
356  // interface), check by sorting points - note no check for orientation
357  std::vector<vtkIdType> sortedSidePoints(sidePoints->GetNumberOfIds());
358  auto pointsBegin = sidePoints->GetPointer(0);
359  std::partial_sort_copy(
360  pointsBegin, pointsBegin + sidePoints->GetNumberOfIds(),
361  sortedSidePoints.begin(), sortedSidePoints.end());
362  auto candOrigCell = mesh->GetCell(candidates.at(0));
363  auto dim = candOrigCell->GetCellDimension();
364  for (int j = 0; j < (dim == 3 ? candOrigCell->GetNumberOfFaces()
365  : candOrigCell->GetNumberOfEdges());
366  ++j) {
367  auto candSide =
368  dim == 3 ? candOrigCell->GetFace(j) : candOrigCell->GetEdge(j);
369  std::vector<vtkIdType> sortedCandPoints(sortedSidePoints.size());
370  auto candPointsBegin = candSide->GetPointIds()->GetPointer(0);
371  // Note use of sidePoints->GetNumberOfIds() in case, eg, candSide is
372  // a vtkQuadraticTriangle, but side is a vtkTriangle
373  auto candPointsEnd = candPointsBegin + sidePoints->GetNumberOfIds();
374  std::partial_sort_copy(candPointsBegin, candPointsEnd,
375  sortedCandPoints.begin(),
376  sortedCandPoints.end());
377  if (sortedSidePoints == sortedCandPoints) {
378  foundMatch = true;
379  origCellArr->SetTypedComponent(i, 0, candidates.at(0));
380  cellFaceArr->SetTypedComponent(i, 0, j);
381  break;
382  }
383  }
384  } else {
385  // If there are two candidates, match the orientation of the side
386  assert(candidates.size() == 2);
387  for (const auto &candIdx : candidates) {
388  auto candOrigCell = mesh->GetCell(candIdx);
389  auto dim = candOrigCell->GetCellDimension();
390  bool foundSide = false;
391  for (int j = 0; j < (dim == 3 ? candOrigCell->GetNumberOfFaces()
392  : candOrigCell->GetNumberOfEdges()) &&
393  !foundSide;
394  ++j) {
395  auto candSide =
396  dim == 3 ? candOrigCell->GetFace(j) : candOrigCell->GetEdge(j);
397  auto candPointsBegin = candSide->GetPointIds()->GetPointer(0);
398  // Note use of sidePoints->GetNumberOfIds() in case, eg, candSide is
399  // a vtkQuadraticTriangle, but side is a vtkTriangle
400  auto candPointsEnd = candPointsBegin + sidePoints->GetNumberOfIds();
401  auto findFirstPoint =
402  std::find(candPointsBegin, candPointsEnd, sidePoints->GetId(0));
403  if (findFirstPoint != candPointsEnd) {
404  std::vector<vtkIdType> rotatedCandPoints(
405  sidePoints->GetNumberOfIds());
406  std::rotate_copy(candPointsBegin, findFirstPoint, candPointsEnd,
407  rotatedCandPoints.begin());
408  if (std::equal(rotatedCandPoints.begin(), rotatedCandPoints.end(),
409  sidePoints->GetPointer(0))) {
410  foundMatch = true;
411  foundSide = true;
412  origCellArr->SetTypedComponent(i, 0, candIdx);
413  cellFaceArr->SetTypedComponent(i, 0, j);
414  } else if (std::equal(rotatedCandPoints.rbegin(),
415  rotatedCandPoints.rend(),
416  sidePoints->GetPointer(0))) {
417  foundSide = true;
418  origCellArr->SetTypedComponent(i, 1, candIdx);
419  cellFaceArr->SetTypedComponent(i, 1, j);
420  }
421  }
422  }
423  if (foundMatch) { break; }
424  }
425  }
426  if (!foundMatch) {
427  std::cerr << "No cell in mesh found to correspond to side set cell "
428  << i << ".\n";
429  }
430  }
431  sideSet.setOrigCellArr(origCellArr);
432  sideSet.setCellFaceArr(cellFaceArr);
433  }
434 }
435 
437  if (this->mesh->GetNumberOfCells() > 0) {
438  return this->mesh->GetCell(0)->GetCellDimension();
439  } else {
440  return -1;
441  }
442 }
443 
444 } // namespace MSH
445 } // namespace NEM
virtual void report(std::ostream &out) const =0
Print a report about the mesh.
Definition: geoMeshBase.C:97
void getPointDataArrayCopy(const std::string &arrayName, vtkAbstractArray *array, int *arrayIdx=nullptr) const
Get copy of point data array by name.
Definition: geoMeshBase.C:159
static constexpr auto CELL_FACE_NAME
Definition: geoMeshBase.H:414
vtkSmartPointer< vtkUnstructuredGrid > mesh
Definition: geoMeshBase.H:419
vtkSmartPointer< vtkIntArray > getCellFaceArr() const
Definition: geoMeshBase.C:281
geoMeshBase * New(MeshType meshType)
Create a new mesh object.
static void Finalize()
Finalize Gmsh.
Definition: geoMeshBase.C:72
STL namespace.
vtkSmartPointer< vtkIdTypeArray > getOrigCellArr() const
Definition: geoMeshBase.C:264
void findSide2OrigCell()
For each edge/polygon in sideSet, find the corresponding mesh element in mesh.
Definition: geoMeshBase.C:314
static void Initialize()
Initialize Gmsh.
Definition: geoMeshBase.C:67
void setCellFaceArr(vtkIntArray *arr)
Definition: geoMeshBase.C:287
static constexpr auto NAME_ARR_NAME
Definition: geoMeshBase.H:415
void setOrigCellArr(vtkIdTypeArray *arr)
Definition: geoMeshBase.C:270
void setGeoEntArr(vtkIntArray *arr)
Definition: geoMeshBase.C:258
virtual void mergeGeoMesh(geoMeshBase *otherGeoMesh)
Merge mesh data from a new geoMesh to an existing geoMesh.
Definition: geoMeshBase.C:113
vtkSmartPointer< vtkPolyData > sides
Cells represent edges/faces of some GeoMesh.
Definition: geoMeshBase.H:399
std::shared_ptr< meshBase > mesh
void getFieldDataArrayCopy(const std::string &arrayName, vtkAbstractArray *array, int *arrayIdx=nullptr) const
Get copy of field data array by name.
Definition: geoMeshBase.C:201
std::array< int, 2 > sides
vtkSmartPointer< vtkIntArray > getGeoEntArr() const
Definition: geoMeshBase.C:252
~geoMeshBase() override
Definition: geoMeshBase.C:89
double _angleThreshold
Dihedral angle threshold (in radians) to classify surfaces (Default: 30 degrees)
Definition: geoMeshBase.H:554
void setSideSetNamesArr(vtkStringArray *arr)
Definition: geoMeshBase.C:304
geoMeshBase()
Query for conversion from vtk to gmsh.
Definition: geoMeshBase.C:77
static constexpr auto GEO_ENT_NAME
Definition: geoMeshBase.H:411
static std::shared_ptr< GmshInterface > instance
Definition: geoMeshBase.H:91
virtual void resetNative()=0
void getCellDataArrayCopy(const std::string &arrayName, vtkAbstractArray *array, int *arrayIdx=nullptr) const
Get copy of cell data array by name.
Definition: geoMeshBase.C:180
abstract class to specify geometry and mesh data
Definition: geoMeshBase.H:102
vtkSmartPointer< vtkStringArray > getSideSetNames() const
Definition: geoMeshBase.C:298
int getDimension() const
Get dimension of mesh.
Definition: geoMeshBase.C:436
const GeoMesh & getGeoMesh() const
Get the underlying geometry object.
Definition: geoMeshBase.H:533
static constexpr auto ORIG_CELL_NAME
Definition: geoMeshBase.H:413
virtual void takeGeoMesh(geoMeshBase *otherGeoMesh)
Take the GeoMesh of another geoMeshBase.
Definition: geoMeshBase.C:104