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.
gmshGeoMesh.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/gmshGeoMesh.H"
34 
35 #include <array>
36 #include <map>
37 #include <set>
38 
39 #include <gmsh.h>
40 
41 #include <vtkCellIterator.h>
42 #include <vtkCellType.h>
43 #include <vtkIntArray.h>
44 
45 #include "AuxiliaryFunctions.H"
46 
47 namespace {
48 template <typename T>
49 void addGmshEntitiesToDataSet(
50  const gmsh::vectorpair &entityTags,
51  const std::map<std::size_t, vtkIdType> &gmshNodeNum2vtkId,
52  vtkSmartPointer<T> dataSet, vtkIntArray *vtkEntities = nullptr) {
53  // Parse by entity
54  for (const auto &dimTag : entityTags) {
55  // Get GMSH elements
56  std::vector<int> elementTypes;
57  std::vector<std::vector<std::size_t>> elementTags;
58  std::vector<std::vector<std::size_t>> nodeTags;
59  gmsh::model::mesh::getElements(elementTypes, elementTags, nodeTags,
60  dimTag.first, dimTag.second);
61 
62  // Convert GMSH elements to VTK cells
63  for (std::size_t iety = 0; iety < elementTypes.size(); ++iety) {
64  // Get element type information from GMSH
65  std::string elementName;
66  int dim, order, numNodes, numPrimaryNodes;
67  std::vector<double> nodeCoord;
68  gmsh::model::mesh::getElementProperties(elementTypes[iety], elementName,
69  dim, order, numNodes, nodeCoord,
70  numPrimaryNodes);
71 
72  // Convert GMSH element type to VTK cell type
73  VTKCellType ct =
75  for (std::size_t ietg = 0; ietg < elementTags[iety].size(); ++ietg) {
76  // Create list of point ids for the cell.
77  vtkNew<vtkIdList> ptIds{};
78  ptIds->Allocate(numNodes);
79  for (int nn = 0; nn < numNodes; ++nn) {
80  ptIds->InsertNextId(
81  gmshNodeNum2vtkId.at(nodeTags[iety][ietg * numNodes + nn]));
82  }
83 
84  // Add cell to mesh/sideSet
85  dataSet->InsertNextCell(ct, ptIds);
86 
87  // geoEnt: Add to geometric entity array
88  if (vtkEntities) vtkEntities->InsertNextValue(dimTag.second);
89  }
90  }
91  }
92 }
93 } // namespace
94 
95 namespace NEM {
96 namespace MSH {
97 
99 
100 gmshGeoMesh *gmshGeoMesh::Read(const std::string &fileName) {
102 
103  gmsh::open(fileName);
104 
105  std::string gmshMesh;
106  gmsh::model::getCurrent(gmshMesh);
107 
108  return new gmshGeoMesh(gmshMesh);
109 }
110 
112 
114  : geoMeshBase(gmsh2GM(gmshMesh)), _gmshMesh(gmshMesh) {
115  std::cout << "gmshGeoMesh constructed" << std::endl;
116 }
117 
120  std::cout << "gmshGeoMesh destructed" << std::endl;
121 }
122 
123 void gmshGeoMesh::write(const std::string &fileName) {
124  _gmshMesh = GM2gmsh(this->getGeoMesh());
125  gmsh::write(fileName);
126  gmsh::model::mesh::clear();
127 }
128 
129 void gmshGeoMesh::report(std::ostream &out) const { geoMeshBase::report(out); }
130 
131 VTKCellType gmshGeoMesh::getVTKTypeFromGmshType(const std::string &gmshType) {
132  std::string name = nemAux::findToStr(gmshType, " ");
133  int numV;
134  if (name == "Point") {
135  numV = 1;
136  } else {
137  numV = std::stoi(nemAux::findFromStr(gmshType, " "));
138  }
139 
140  if (name == "Point") {
141  return VTK_VERTEX;
142  } else if (name == "Line") {
143  switch (numV) {
144  case 3: return VTK_QUADRATIC_EDGE;
145  default: return VTK_LINE;
146  }
147  } else if (name == "Triangle") {
148  switch (numV) {
149  case 6: return VTK_QUADRATIC_TRIANGLE;
150  default: return VTK_TRIANGLE;
151  }
152  } else if (name == "Quadrilateral") {
153  switch (numV) {
154  case 8: return VTK_QUADRATIC_QUAD;
155  case 9: return VTK_BIQUADRATIC_QUAD;
156  default: return VTK_QUAD;
157  }
158  } else if (name == "Polygon") {
159  return VTK_POLYGON;
160  } else if (name == "Tetrahedron") {
161  switch (numV) {
162  case 10: return VTK_QUADRATIC_TETRA;
163  default: return VTK_TETRA;
164  }
165  } else if (name == "Hexahedron") {
166  switch (numV) {
167  case 20: return VTK_QUADRATIC_HEXAHEDRON;
168  case 27: return VTK_TRIQUADRATIC_HEXAHEDRON;
169  default: return VTK_HEXAHEDRON;
170  }
171  } else if (name == "Prism") {
172  return VTK_WEDGE;
173  } else if (name == "Pyramid") {
174  return VTK_PYRAMID;
175  } else if (name == "Trihedron" || // Flat Quad + 2 Tris.
176  name == "Polyhedron") // Occurs in MElementCuts
177  {
178  std::cerr << "ERROR in Gmsh to VTK element conversion: Gmsh element \""
179  << name << "\" is not supported by VTK." << std::endl;
180  } else {
181  std::cerr << "ERROR in Gmsh to VTK element conversion: Gmsh element \""
182  << name << "\" is not recognized." << std::endl;
183  }
184  throw;
185 }
186 
189 
190  if (gmshMesh.empty()) return {vtkMesh, gmshMesh, "", {}};
191 
192  std::string geoEntArrayName = GEO_ENT_DEFAULT_NAME;
193 
194  gmsh::model::setCurrent(gmshMesh);
195 
196  // Track GMSH node ids and VTK point ids.
197  std::map<std::size_t, vtkIdType> gmshNodeNum2vtkId;
198 
199  { // Add points
200  vtkSmartPointer<vtkPoints> points =
201  vtkSmartPointer<vtkPoints>::Take(vtkPoints::New(VTK_DOUBLE));
202 
203  // Get GMSH nodes
204  std::vector<std::size_t> nodeTags;
205  std::vector<double> coord;
206  std::vector<double> parametricCoord;
207  gmsh::model::mesh::getNodes(nodeTags, coord, parametricCoord);
208 
209  // Allocate
210  points->Allocate(nodeTags.size());
211 
212  // Convert GMSH nodes to VTK points
213  for (std::size_t i = 0; i < nodeTags.size(); ++i) {
214  gmshNodeNum2vtkId[nodeTags[i]] = points->GetNumberOfPoints();
215  points->InsertNextPoint(coord[i * 3 + 0], coord[i * 3 + 1],
216  coord[i * 3 + 2]);
217  }
218 
219  vtkMesh->SetPoints(points);
220  }
221 
222  { // Add elements and geometric entities
223  // Note: Cannot pre-allocate cells since GMSH does not provide an API to get
224  // total number of elements
225 
226  gmsh::vectorpair dimTags;
227  gmsh::model::getEntities(dimTags, gmsh::model::getDimension());
228 
229  // geoEnt: Track entities to place as cell data
230  vtkSmartPointer<vtkIntArray> vtkEntities =
232  vtkEntities->Initialize(); // Cannot allocate
233  vtkEntities->SetName(geoEntArrayName.c_str());
234 
235  addGmshEntitiesToDataSet(dimTags, gmshNodeNum2vtkId, vtkMesh, vtkEntities);
236 
237  // geoEnt: Add geometric entity data to VTK
238  vtkMesh->GetCellData()->AddArray(vtkEntities);
239 
240  // Since could not allocate elements/cells, we have to squeeze
241  vtkMesh->Squeeze();
242  }
243 
244  SideSet sideSet{};
245  { // sideSet
246  gmsh::vectorpair dimTags;
247  gmsh::model::getEntities(dimTags, gmsh::model::getDimension() - 1);
248  if (!dimTags.empty()) {
249  auto sideSetPD = vtkSmartPointer<vtkPolyData>::New();
250  sideSetPD->SetPoints(vtkMesh->GetPoints());
251  sideSetPD->Allocate();
252  vtkNew<vtkIntArray> vtkEntities{};
253 
254  addGmshEntitiesToDataSet(dimTags, gmshNodeNum2vtkId, sideSetPD,
255  vtkEntities);
256  if (sideSetPD->GetNumberOfCells() > 0) {
257  sideSetPD->Squeeze();
258  sideSet = SideSet(sideSetPD, vtkEntities);
259  }
260  }
261  }
262 
263  // TODO: Add point, cell data
264 
265  // Only keep the geometry in the gmshMesh
266  gmsh::model::mesh::clear();
267 
268  return {vtkMesh, gmshMesh, geoEntArrayName, sideSet};
269 }
270 
271 std::string gmshGeoMesh::GM2gmsh(const GeoMesh &geoMesh) {
272  gmsh::model::setCurrent(geoMesh.geo);
273 
274  if (geoMesh.mesh->GetNumberOfPoints() > 0) { // Add points
275  std::vector<std::size_t> nodeTags;
276  std::vector<double> coord;
277 
278  vtkSmartPointer<vtkPoints> dataSetPoints = geoMesh.mesh->GetPoints();
279  coord.resize(3 * dataSetPoints->GetNumberOfPoints());
280  nodeTags.resize(dataSetPoints->GetNumberOfPoints());
281  for (vtkIdType ptId = 0; ptId < dataSetPoints->GetNumberOfPoints();
282  ++ptId) {
283  nodeTags[ptId] = ptId + 1;
284  dataSetPoints->GetPoint(ptId, &coord[3 * ptId]);
285  }
286 
287  gmsh::vectorpair dimTags;
288  gmsh::model::getEntities(dimTags);
289  gmsh::model::mesh::addNodes(dimTags[0].first, dimTags[0].second, nodeTags,
290  coord);
291  }
292 
293  if (geoMesh.mesh->GetNumberOfCells() > 0) { // Add elements
294  // Sort all cells by entity and element type
295  auto it =
296  vtkSmartPointer<vtkCellIterator>::Take(geoMesh.mesh->NewCellIterator());
297  vtkSmartPointer<vtkIntArray> geoEntArray = vtkIntArray::FastDownCast(
298  geoMesh.mesh->GetCellData()->GetArray(geoMesh.link.c_str()));
299  for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextCell()) {
300  int geoEnt = geoEntArray->GetTypedComponent(it->GetCellId(), 0);
301 
302  vtkSmartPointer<vtkIdList> ptIds = it->GetPointIds();
303  std::vector<std::size_t> nodeTag;
304  for (vtkIdType pt = 0; pt < it->GetNumberOfPoints(); ++pt) {
305  nodeTag.emplace_back(ptIds->GetId(pt) + 1);
306  }
307 
308  gmsh::model::mesh::addElementsByType(
309  geoEnt, getGmshTypeFromVTKType(it->GetCellType()),
310  {static_cast<std::size_t>(it->GetCellId() + 1)}, nodeTag);
311  }
312  }
313 
314  if (geoMesh.sideSet.sides && geoMesh.sideSet.sides->GetNumberOfCells() > 0) {
315  auto it = vtkSmartPointer<vtkCellIterator>::Take(
316  geoMesh.sideSet.sides->NewCellIterator());
317  auto geoEntArray = geoMesh.sideSet.getGeoEntArr();
318  for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextCell()) {
319  int geoEnt = geoEntArray->GetTypedComponent(it->GetCellId(), 0);
320 
321  vtkSmartPointer<vtkIdList> ptIds = it->GetPointIds();
322  std::vector<std::size_t> nodeTag;
323  for (vtkIdType pt = 0; pt < it->GetNumberOfPoints(); ++pt) {
324  nodeTag.emplace_back(ptIds->GetId(pt) + 1);
325  }
326 
327  gmsh::model::mesh::addElementsByType(
328  geoEnt, getGmshTypeFromVTKType(it->GetCellType()),
329  {static_cast<std::size_t>(it->GetCellId() + 1)}, nodeTag);
330  }
331  }
332 
333  gmsh::model::mesh::reclassifyNodes();
334 
335  return geoMesh.geo;
336 }
337 
339  auto gm = getGeoMesh();
340  if (gm.geo.empty()) {
341  gm.geo = "geoMesh_" + nemAux::getRandomString(6);
342  gmsh::model::add(gm.geo);
343  }
344  if (gm.link.empty()) {
345  gm.link = GEO_ENT_DEFAULT_NAME;
346  auto linkArr = vtkSmartPointer<vtkIntArray>::New();
347  linkArr->SetName(gm.link.c_str());
348  linkArr->SetNumberOfTuples(getGeoMesh().mesh->GetNumberOfCells());
349  linkArr->FillComponent(0, 1);
350  getGeoMesh().mesh->GetCellData()->AddArray(linkArr);
351  }
352  setGeoMesh(gm);
353  _gmshMesh = gm.geo;
354 }
355 
356 } // namespace MSH
357 } // namespace NEM
virtual void report(std::ostream &out) const =0
Print a report about the mesh.
Definition: geoMeshBase.C:97
vtkSmartPointer< vtkUnstructuredGrid > mesh
Definition: geoMeshBase.H:419
geoMeshBase * Read(const std::string &fileName)
Read a mesh from file.
void report(std::ostream &out) const override
Print a report about the mesh.
Definition: gmshGeoMesh.C:129
static vtkTypeMacro(gmshGeoMesh, geoMeshBase) public VTKCellType getVTKTypeFromGmshType(const std::string &gmshType)
Create a gmshGeoMesh from a file.
Definition: gmshGeoMesh.C:131
static constexpr auto GEO_ENT_DEFAULT_NAME
Definition: geoMeshBase.H:446
gmshGeoMesh()
Construct a gmshGeoMesh with an empty mesh.
Definition: gmshGeoMesh.C:111
geoMeshBase * New(MeshType meshType)
Create a new mesh object.
static void Finalize()
Finalize Gmsh.
Definition: geoMeshBase.C:72
static GeoMesh gmsh2GM(const std::string &gmshMesh)
Convert gmshMesh (name of a gmsh model) to GeoMesh.
Definition: gmshGeoMesh.C:187
STL namespace.
void setGeoMesh(const geoMeshBase::GeoMesh &geoMesh)
Set the underlying geometry object.
Definition: geoMeshBase.H:538
static void Initialize()
Initialize Gmsh.
Definition: geoMeshBase.C:67
std::string findToStr(const std::string &str, const std::string &ptrn)
std::string findFromStr(const std::string &str, const std::string &ptrn)
A concrete implementation of geoMeshBase representing a Gmsh mesh.
Definition: gmshGeoMesh.H:41
std::string _gmshMesh
Definition: gmshGeoMesh.H:92
std::string getRandomString(int length)
vtkSmartPointer< vtkPolyData > sides
Cells represent edges/faces of some GeoMesh.
Definition: geoMeshBase.H:399
std::shared_ptr< meshBase > mesh
void resetNative() override
Definition: gmshGeoMesh.C:338
std::vector< vtkIdType > points
points given by id in .inp file
Definition: inpGeoMesh.C:133
vtkSmartPointer< vtkIntArray > getGeoEntArr() const
Definition: geoMeshBase.C:252
vtkStandardNewMacro(exoGeoMesh)
Definition: exoGeoMesh.C:247
~gmshGeoMesh() override
Definition: gmshGeoMesh.C:118
static std::string GM2gmsh(const GeoMesh &geoMesh)
Add the mesh from geoMesh to the gmsh model in geoMesh.
Definition: gmshGeoMesh.C:271
abstract class to specify geometry and mesh data
Definition: geoMeshBase.H:102
void write(const std::string &fileName) override
Write mesh to file.
Definition: gmshGeoMesh.C:123
const GeoMesh & getGeoMesh() const
Get the underlying geometry object.
Definition: geoMeshBase.H:533