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.
exoGeoMesh.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/exoGeoMesh.H"
34 
35 #include <exodusII.h>
36 #include <vtkCellIterator.h>
37 #include <vtkCompositeDataIterator.h>
38 #include <vtkEmptyCell.h>
39 #include <vtkExodusIIReader.h>
40 #include <vtkInformation.h>
41 #include <vtkMultiBlockDataSet.h>
42 #include <vtkTransform.h>
43 #include <vtkTransformFilter.h>
44 #include <numeric>
45 #include <unordered_set>
46 
47 #include "AuxiliaryFunctions.H"
49 
50 #ifdef HAVE_GMSH
51 #include <gmsh.h>
52 #endif
53 
54 namespace NEM {
55 namespace MSH {
56 
57 namespace {
58 
59 /**
60  * Checks and prints errors, if any, from calls to the Exodus API. Note if
61  * Exodus has a fatal error, will cause exit with non-zero code.
62  * @param errNum return value from a call to Exodus API functions
63  * @param createOrOpen whether call was from ex_create or ex_open
64  */
65 void checkExodusErr(int errNum, bool createOrOpen = false) {
66  if (errNum < 0 || (!createOrOpen && errNum > 0)) {
67  const char *msg;
68  const char *func;
69  ex_get_err(&msg, &func, &errNum);
70  ex_opts(EX_VERBOSE | EX_ABORT);
71  ex_err(func, msg, errNum);
72  }
73 }
74 
75 const char *vtkCellType2exoType(VTKCellType vtkCellType) {
76  switch (vtkCellType) {
77  case VTK_TRIANGLE: return "TRIANGLE";
78  case VTK_QUAD: return "QUAD";
79  case VTK_TETRA: return "TETRA";
80  case VTK_HEXAHEDRON: return "HEX";
81  case VTK_WEDGE: return "WEDGE";
82  default: return "OTHER";
83  }
84 }
85 
86 int exoSide2vtkSide(int exoSide, VTKCellType vtkCellType) {
87  switch (vtkCellType) {
88  case VTK_HEXAHEDRON:
89  switch (exoSide) {
90  case 1: return 2;
91  case 2: return 1;
92  case 3: return 3;
93  case 4: return 0;
94  case 5: return 4;
95  case 6: return 5;
96  default: return -1;
97  }
98  case VTK_WEDGE:
99  switch (exoSide) {
100  case 1: return 2;
101  case 2: return 3;
102  case 3: return 4;
103  case 4: return 0;
104  case 5: return 1;
105  default: return -1;
106  }
107  default: return exoSide - 1;
108  }
109 }
110 
111 int vtkSide2exoSide(int vtkSide, int vtkCellType) {
112  switch (vtkCellType) {
113  case VTK_HEXAHEDRON:
114  switch (vtkSide) {
115  case 0: return 4;
116  case 1: return 2;
117  case 2: return 1;
118  case 3: return 3;
119  case 4: return 5;
120  case 5: return 6;
121  default: return -1;
122  }
123  case VTK_WEDGE:
124  switch (vtkSide) {
125  case 0: return 4;
126  case 1: return 5;
127  case 2: return 1;
128  case 3: return 2;
129  case 4: return 3;
130  default: return -1;
131  }
132  default: return vtkSide + 1;
133  }
134 }
135 
136 std::string getArrComponentName(vtkDataArray *arr, int component) {
137  auto componentName = arr->GetComponentName(component);
138  if (componentName) {
139  return std::string{componentName};
140  }
141  if (!arr->GetName()) {
142  return std::string{};
143  }
144  std::string varName = arr->GetName();
145  if (arr->GetNumberOfComponents() == 2) {
146  if (component == 0) {
147  varName += 'r';
148  } else { // component == 1
149  varName += 'z';
150  }
151  } else if (arr->GetNumberOfComponents() == 3) {
152  if (component == 0) {
153  varName += 'x';
154  } else if (component == 1) {
155  varName += 'y';
156  } else { // component == 2
157  varName += 'z';
158  }
159  } else if (arr->GetNumberOfComponents() == 6) {
160  if (component == 0) {
161  varName += "xx";
162  } else if (component == 1) {
163  varName += "yy";
164  } else if (component == 2) {
165  varName += "zz";
166  } else if (component == 3) {
167  varName += "xy";
168  } else if (component == 4) {
169  varName += "xz";
170  } else { // component == 5
171  varName += "yz";
172  }
173  } else if (arr->GetNumberOfComponents() > 1) {
174  varName += std::to_string(component);
175  }
176  return varName;
177 }
178 
179 /**
180  * Combine @p mesh1 and @p mesh2 into @p outMesh. PointData and CellData are
181  * added to @p outMesh only if present in both @p mesh1 and @p mesh2.
182  * @param mesh1 First mesh to combine. Note that cell global IDs and cell
183  * pedigree IDs are removed, if present.
184  * @param mesh2 Second mesh to combine.
185  * @param outMesh Resulting vtkUnstructuredGrid. Should have no points, cells,
186  * vtkPointData, or vtkCellData.
187  * @param tol tolerance for merging points
188  * @return maps from mesh1 points to outMesh points and mesh2 points to outMesh
189  * points (all using 0-based indices; point global IDs are not used)
190  */
191 std::pair<vtkSmartPointer<vtkIdTypeArray>, vtkSmartPointer<vtkIdTypeArray>>
192 mergeMeshes(vtkUnstructuredGrid *mesh1, vtkUnstructuredGrid *mesh2,
193  vtkUnstructuredGrid *outMesh, float tol) {
194  if (mesh1->GetCellData()->GetGlobalIds()) {
195  mesh1->GetCellData()->RemoveArray(
196  mesh1->GetCellData()->GetGlobalIds()->GetName());
197  }
198  if (mesh1->GetCellData()->GetPedigreeIds()) {
199  mesh1->GetCellData()->RemoveArray(
200  mesh1->GetCellData()->GetPedigreeIds()->GetName());
201  }
202  // Note that mesh1's Points will be overwritten in first call to MergeDataSet
203  auto merge = vtkSmartPointer<mergeCells>::New();
204  merge->SetUnstructuredGrid(outMesh);
205  merge->SetTotalNumberOfDataSets(2);
206  merge->SetTotalNumberOfPoints(mesh1->GetNumberOfPoints() +
207  mesh2->GetNumberOfPoints());
208  merge->SetTotalNumberOfCells(mesh1->GetNumberOfCells() +
209  mesh2->GetNumberOfCells());
210  merge->SetPointMergeTolerance(tol);
211  auto nodeMap1 = merge->MergeDataSet(mesh1);
212  auto nodeMap2 = merge->MergeDataSet(mesh2);
213  merge->Finish();
214  for (int i = outMesh->GetCellData()->GetNumberOfArrays() - 1; i >= 0; --i) {
215  if (outMesh->GetCellData()->GetArray(i)->GetNumberOfTuples() !=
216  outMesh->GetNumberOfCells()) {
217  outMesh->GetCellData()->RemoveArray(i);
218  }
219  }
220  for (int i = outMesh->GetPointData()->GetNumberOfArrays() - 1; i >= 0; --i) {
221  if (outMesh->GetPointData()->GetArray(i)->GetNumberOfTuples() !=
222  outMesh->GetNumberOfPoints()) {
223  outMesh->GetPointData()->RemoveArray(i);
224  }
225  }
226  return {nodeMap1, nodeMap2};
227 }
228 
229 void resetSideSetPoints(vtkUnstructuredGrid *mesh, vtkPolyData *sideSet,
230  vtkIdTypeArray *nodeMap) {
231  if (sideSet) {
232  sideSet->SetPoints(mesh->GetPoints());
233  for (vtkIdType i = 0; i < sideSet->GetNumberOfCells(); ++i) {
234  auto oldCell = sideSet->GetCell(i);
235  auto numPoints = oldCell->GetNumberOfPoints();
236  auto oldPoints = oldCell->GetPointIds();
237  for (int j = 0; j < numPoints; ++j) {
238  oldPoints->SetId(j, nodeMap->GetValue(oldPoints->GetId(j)));
239  }
240  sideSet->ReplaceCell(i, numPoints, oldPoints->GetPointer(0));
241  }
242  }
243 }
244 
245 } // namespace
246 
248 
249 exoGeoMesh *exoGeoMesh::Read(const std::string &fileName, int timeStep) {
250  return new exoGeoMesh(fileName, timeStep);
251 }
252 
254  return vtkExodusIIReader::GetObjectIdArrayName();
255 }
256 
258 
259 exoGeoMesh::exoGeoMesh(const vtkSmartPointer<vtkExodusIIReader> &reader)
260  : geoMeshBase(exoReader2GM(reader)) {
261  if (reader && reader->GetOutput() &&
262  reader->GetOutput()->GetNumberOfBlocks() == 8) {
263  auto mbds = reader->GetOutput();
264  _title = reader->GetTitle();
265  auto elemBlocks = vtkMultiBlockDataSet::SafeDownCast(mbds->GetBlock(0));
266  for (unsigned int i = 0; i < elemBlocks->GetNumberOfBlocks(); ++i) {
267  std::string elemBlockName =
268  elemBlocks->GetMetaData(i)->Get(vtkMultiBlockDataSet::NAME());
269  auto elemBlock =
270  vtkUnstructuredGrid::SafeDownCast(elemBlocks->GetBlock(i));
271  int elemBlockId;
272  VTKCellType cellType;
273  if (elemBlock->GetNumberOfCells() > 0) {
274  elemBlockId =
275  vtkIntArray::FastDownCast(
276  elemBlock->GetCellData()->GetAbstractArray(getExoIdArrName()))
277  ->GetValue(0);
278  cellType = static_cast<VTKCellType>(elemBlock->GetCellType(0));
279  } else {
280  elemBlockId =
281  vtkIntArray::FastDownCast(
282  elemBlock->GetFieldData()->GetAbstractArray("ElementBlockIds"))
283  ->GetValue(0);
284  cellType = VTK_EMPTY_CELL;
285  }
286  _elemBlocks[elemBlockId] = {elemBlockName, cellType, {}};
287  }
288  auto sideSets = vtkMultiBlockDataSet::SafeDownCast(mbds->GetBlock(4));
289  if (sideSets->GetNumberOfBlocks() >= 1) {
290  for (int i = 0; i < sideSets->GetNumberOfBlocks(); ++i) {
291  auto sideSetName = reader->GetSideSetArrayName(i);
292  auto sideSetId = reader->GetObjectId(vtkExodusIIReader::SIDE_SET, i);
293  _sideSetNames[sideSetId] = sideSetName ? sideSetName : "";
294  }
295  }
296 
297  auto mesh = getGeoMesh().mesh;
298  std::map<vtkIdType, vtkIdType> globalNodeId2meshIdx;
299  auto globalNodeIdArr =
300  vtkIdTypeArray::FastDownCast(mesh->GetPointData()->GetAbstractArray(
301  reader->GetGlobalNodeIdArrayName()));
302  for (vtkIdType i = 0; i < mesh->GetNumberOfPoints(); ++i) {
303  globalNodeId2meshIdx[globalNodeIdArr->GetValue(i)] = i;
304  }
305  mesh->GetPointData()->RemoveArray(reader->GetGlobalNodeIdArrayName());
306  mesh->GetPointData()->RemoveArray(reader->GetPedigreeNodeIdArrayName());
307  auto nodeSets = vtkMultiBlockDataSet::SafeDownCast(mbds->GetBlock(7));
308  for (unsigned int i = 0; i < nodeSets->GetNumberOfBlocks(); ++i) {
309  std::string nodeSetName =
310  nodeSets->GetMetaData(i)->Get(vtkMultiBlockDataSet::NAME());
311  auto nodeSet = vtkUnstructuredGrid::SafeDownCast(nodeSets->GetBlock(i));
312  auto nodeSetId =
313  vtkIntArray::FastDownCast(
314  nodeSet->GetCellData()->GetAbstractArray(getExoIdArrName()))
315  ->GetValue(0);
316  std::vector<vtkIdType> nodes;
317  nodes.reserve(nodeSet->GetNumberOfCells());
318  auto nodeSetGlobalNodeIdArr = vtkIdTypeArray::FastDownCast(
319  nodeSet->GetPointData()->GetAbstractArray(
320  reader->GetGlobalNodeIdArrayName()));
321  for (vtkIdType j = 0; j < nodeSet->GetNumberOfCells(); ++j) {
322  auto nodeGlobalId = nodeSetGlobalNodeIdArr->GetValue(
323  nodeSet->GetCell(j)->GetPointId(0));
324  auto find = globalNodeId2meshIdx.find(nodeGlobalId);
325  if (find != globalNodeId2meshIdx.end()) {
326  nodes.emplace_back(find->second);
327  }
328  }
329  _nodeSets[nodeSetId] = {nodeSetName, nodes};
330  }
331  // Unfortunately the vtkExodusIIReader does not seem to support exodus
332  // properties at the moment.
333  int comp_ws = sizeof(double);
334  int io_ws = 0;
335  float version;
336  auto exoid =
337  ex_open(reader->GetFileName(), EX_READ, &comp_ws, &io_ws, &version);
338  checkExodusErr(exoid, true);
339  // We get the properties in order, so, just in case, we get the element
340  // block ids in order
341  std::vector<int> elemBlockIds;
342  elemBlockIds.resize(_elemBlocks.size());
343  auto err = ex_get_ids(exoid, EX_ELEM_BLOCK, elemBlockIds.data());
344  checkExodusErr(err);
345  int numProps = ex_inquire_int(exoid, EX_INQ_EB_PROP);
346  std::vector<std::string> propNames;
347  propNames.resize(numProps);
348  std::vector<const char *> propNamesPtr;
349  propNamesPtr.reserve(numProps);
350  for (int i = 0; i < numProps; ++i) {
351  propNames[i].resize(MAX_NAME_LENGTH);
352  propNamesPtr.emplace_back(propNames[i].c_str());
353  }
354  err = ex_get_prop_names(exoid, EX_ELEM_BLOCK,
355  const_cast<char **>(propNamesPtr.data()));
356  checkExodusErr(err);
357  for (int i = 0; i < numProps; ++i) {
358  if (strcmp(propNamesPtr[i], "ID") != 0) {
359  std::vector<int> propVals;
360  propVals.resize(_elemBlocks.size());
361  err = ex_get_prop_array(exoid, EX_ELEM_BLOCK, propNamesPtr[i],
362  propVals.data());
363  checkExodusErr(err);
364  _elemBlockPropNames.insert(propNamesPtr[i]);
365  for (std::size_t j = 0; j < _elemBlocks.size(); ++j) {
366  _elemBlocks[elemBlockIds[j]].properties[propNamesPtr[i]] =
367  propVals[j];
368  }
369  }
370  }
371  err = ex_close(exoid);
372  checkExodusErr(err);
373  }
374  std::cout << "exoGeoMesh constructed" << std::endl;
375 }
376 
377 exoGeoMesh::exoGeoMesh(const std::string &fileName, int timeStep)
378  : exoGeoMesh(getExoReader(fileName, timeStep)) {}
379 
380 exoGeoMesh::~exoGeoMesh() { std::cout << "exoGeoMesh destructed" << std::endl; }
381 
382 void exoGeoMesh::write(const std::string &fileName) {
383  std::string fileExt = nemAux::find_ext(fileName);
384  if (fileExt == ".exo" || fileExt == ".e" || fileExt == ".gen" ||
385  fileExt == ".g") {
386  int comp_ws = sizeof(double);
387  int io_ws = sizeof(double);
388  int exoid = ex_create(fileName.c_str(), EX_CLOBBER, &comp_ws, &io_ws);
389  checkExodusErr(exoid, true);
390 
391  auto geoMesh = this->getGeoMesh();
392  auto mesh = geoMesh.mesh;
393  auto sideSet = geoMesh.sideSet;
394  if (!mesh || mesh->GetNumberOfCells() == 0) {
395  std::cerr << "No mesh information to write." << std::endl;
396  return;
397  }
398 
399  // Make sure no empty element blocks
400  std::map<int, std::vector<vtkIdType>> elemBlocks;
401  {
402  auto meshEntities = vtkIntArray::FastDownCast(
403  mesh->GetCellData()->GetAbstractArray(getExoIdArrName()));
404  for (const auto &elemBlock : _elemBlocks) {
405  elemBlocks[elemBlock.first];
406  }
407  for (vtkIdType i = 0; i < mesh->GetNumberOfCells(); ++i) {
408  auto entity = meshEntities->GetValue(i);
409  elemBlocks.at(entity).emplace_back(i);
410  }
411  for (auto it = elemBlocks.begin(); it != elemBlocks.end();) {
412  if (it->second.empty()) {
413  it = elemBlocks.erase(it);
414  } else {
415  ++it;
416  }
417  }
418  }
419 
420  // Get number of element block variables, nodal variables, and global
421  // variables
422  int numElemVars = 0;
423  int numNodeVars = 0;
424  int numGlobalVars = 0;
425  std::vector<bool> cellDataWriteFlag;
426  std::vector<bool> pointDataWriteFlag;
427  std::vector<bool> fieldDataWriteFlag;
428  int timeStep = 1;
429  {
430  // Cell Data
431  auto globalIds = mesh->GetCellData()->GetGlobalIds();
432  const char *globalIdName;
433  if (globalIds) {
434  globalIdName = globalIds->GetName();
435  } else {
436  globalIdName = "GlobalElementId";
437  }
438  auto pedigreeIds = mesh->GetCellData()->GetPedigreeIds();
439  const char *pedgreeIdName;
440  if (pedigreeIds) {
441  pedgreeIdName = pedigreeIds->GetName();
442  } else {
443  pedgreeIdName = "PedigreeElementId";
444  }
445  auto numCells = mesh->GetNumberOfCells();
446  cellDataWriteFlag.resize(mesh->GetCellData()->GetNumberOfArrays(), false);
447  for (int i = 0; i < mesh->GetCellData()->GetNumberOfArrays(); ++i) {
448  // Use GetArray instead of GetAbstractArray to filter for vtkDataArray
449  auto arr = mesh->GetCellData()->GetArray(i);
450  if (arr) {
451  auto arrName = arr->GetName();
452  if (strcmp(arrName, globalIdName) != 0 &&
453  strcmp(arrName, pedgreeIdName) != 0 &&
454  strcmp(arrName, getExoIdArrName()) != 0 &&
455  arr->GetNumberOfTuples() == numCells) {
456  auto numComponents = arr->GetNumberOfComponents();
457  cellDataWriteFlag[i] = (numComponents > 0);
458  numElemVars += numComponents;
459  }
460  }
461  }
462  // Point Data
463  globalIds = mesh->GetPointData()->GetGlobalIds();
464  if (globalIds) {
465  globalIdName = globalIds->GetName();
466  } else {
467  globalIdName = "GlobalNodeId";
468  }
469  pedigreeIds = mesh->GetPointData()->GetPedigreeIds();
470  if (pedigreeIds) {
471  pedgreeIdName = pedigreeIds->GetName();
472  } else {
473  pedgreeIdName = "PedigreeNodeId";
474  }
475  auto numNodes = mesh->GetNumberOfPoints();
476  pointDataWriteFlag.resize(mesh->GetPointData()->GetNumberOfArrays(),
477  false);
478  for (int i = 0; i < mesh->GetPointData()->GetNumberOfArrays(); ++i) {
479  // Use GetArray instead of GetAbstractArray to filter for vtkDataArray
480  auto arr = mesh->GetPointData()->GetArray(i);
481  if (arr) {
482  auto arrName = arr->GetName();
483  if (strcmp(arrName, globalIdName) != 0 &&
484  strcmp(arrName, pedgreeIdName) != 0 &&
485  arr->GetNumberOfTuples() == numNodes) {
486  auto numComponents = arr->GetNumberOfComponents();
487  pointDataWriteFlag[i] = (numComponents > 0);
488  numNodeVars += numComponents;
489  }
490  }
491  }
492  // Field Data
493  fieldDataWriteFlag.resize(mesh->GetFieldData()->GetNumberOfArrays(),
494  false);
495  for (int i = 0; i < mesh->GetFieldData()->GetNumberOfArrays(); ++i) {
496  // Use GetArray instead of GetAbstractArray to filter for vtkDataArray
497  auto arr = mesh->GetFieldData()->GetArray(i);
498  if (arr && arr->GetNumberOfTuples() == 1) {
499  auto numComponents = arr->GetNumberOfComponents();
500  fieldDataWriteFlag[i] = (numComponents > 0);
501  numGlobalVars += numComponents;
502  }
503  }
504  }
505 
506  // Have to make sure no empty side sets; might as well get the
507  // indices now.
508  std::map<int, std::vector<vtkIdType>> exoSideSets;
509  if (sideSet.sides) {
510  auto sideSetExoId = vtkIntArray::FastDownCast(
511  sideSet.sides->GetCellData()->GetAbstractArray(getExoIdArrName()));
512  for (vtkIdType i = 0; i < sideSet.sides->GetNumberOfCells(); ++i) {
513  auto entity = sideSetExoId->GetValue(i);
514  exoSideSets[entity].emplace_back(i);
515  }
516  }
517 
518  // Make sure no empty node sets and no duplicate nodes
520  int numNodeSets = std::accumulate(
521  _nodeSets.begin(), _nodeSets.end(), 0,
522  [](const int &a, const std::pair<const int, nodeSet> &b) {
523  return a + (!b.second.nodes.empty());
524  });
525 
526  auto err = ex_put_init(exoid, _title.c_str(), geoMesh.getDimension(),
527  mesh->GetNumberOfPoints(), mesh->GetNumberOfCells(),
528  elemBlocks.size(), numNodeSets, exoSideSets.size());
529  checkExodusErr(err);
530  if (numElemVars > 0) {
531  err = ex_put_variable_param(exoid, EX_ELEM_BLOCK, numElemVars);
532  checkExodusErr(err);
533  }
534  if (numNodeVars > 0) {
535  err = ex_put_variable_param(exoid, EX_NODAL, numNodeVars);
536  checkExodusErr(err);
537  }
538  if (numGlobalVars > 0) {
539  err = ex_put_variable_param(exoid, EX_GLOBAL, numGlobalVars);
540  checkExodusErr(err);
541  }
542  if (numElemVars > 0 || numNodeVars > 0 || numGlobalVars > 0) {
543  double time = 0;
544  err = ex_put_time(exoid, timeStep, &time);
545  checkExodusErr(err);
546  }
547 
548  std::vector<double> x_coord;
549  std::vector<double> y_coord;
550  std::vector<double> z_coord;
551  x_coord.resize(mesh->GetNumberOfPoints(), 0);
552  y_coord.resize(mesh->GetNumberOfPoints(), 0);
553  z_coord.resize(mesh->GetNumberOfPoints(), 0);
554  auto coord = mesh->GetPoints()->GetData();
555  for (vtkIdType i = 0; i < mesh->GetNumberOfPoints(); ++i) {
556  x_coord[i] = coord->GetComponent(i, 0);
557  y_coord[i] = coord->GetComponent(i, 1);
558  z_coord[i] = coord->GetComponent(i, 2);
559  }
560  err = ex_put_coord(exoid, x_coord.data(), y_coord.data(), z_coord.data());
561  checkExodusErr(err);
562 
563  // map from index in mesh to implicit index in exodus output
564  std::vector<vtkIdType> vtkIdMap(mesh->GetNumberOfCells(), -1);
565  int lastElemIdx = 0;
566  for (const auto &elemBlock : elemBlocks) {
567  auto firstCell = mesh->GetCell(elemBlock.second[0]);
568  auto cellType = static_cast<VTKCellType>(firstCell->GetCellType());
569  err = ex_put_block(exoid, EX_ELEM_BLOCK, elemBlock.first,
570  vtkCellType2exoType(cellType), elemBlock.second.size(),
571  firstCell->GetNumberOfPoints(), 0, 0, 0);
572  checkExodusErr(err);
573  std::vector<int> conn;
574  conn.reserve(elemBlock.second.size() * firstCell->GetNumberOfPoints());
575  for (auto elem : elemBlock.second) {
576  auto cell = mesh->GetCell(elem);
577  vtkIdMap[elem] = ++lastElemIdx;
578  for (int i = 0; i < cell->GetNumberOfPoints(); ++i) {
579  // Exodus starts indexing from 1
580  conn.emplace_back(cell->GetPointId(i) + 1);
581  }
582  }
583  err = ex_put_conn(exoid, EX_ELEM_BLOCK, elemBlock.first, conn.data(),
584  nullptr, nullptr);
585  checkExodusErr(err);
586  if (!_elemBlocks.at(elemBlock.first).name.empty()) {
587  err = ex_put_name(exoid, EX_ELEM_BLOCK, elemBlock.first,
588  _elemBlocks.at(elemBlock.first).name.c_str());
589  checkExodusErr(err);
590  }
591  }
592 
593  if (sideSet.sides) {
594  auto sideSetOrigCellId = geoMesh.sideSet.getOrigCellArr();
595  auto sideSetCellFaceId = geoMesh.sideSet.getCellFaceArr();
596  for (const auto &exoSideSet : exoSideSets) {
597  err = ex_put_set_param(exoid, EX_SIDE_SET, exoSideSet.first,
598  exoSideSet.second.size(), 0);
599  checkExodusErr(err);
600  std::vector<int> sideSetElem, sideSetSide;
601  sideSetElem.reserve(exoSideSet.second.size());
602  sideSetSide.reserve(exoSideSet.second.size());
603  for (auto sideIdx : exoSideSet.second) {
604  auto parentCellIdx = sideSetOrigCellId->GetTypedComponent(sideIdx, 0);
605  sideSetElem.emplace_back(vtkIdMap[parentCellIdx]);
606  sideSetSide.emplace_back(
607  vtkSide2exoSide(sideSetCellFaceId->GetTypedComponent(sideIdx, 0),
608  mesh->GetCell(parentCellIdx)->GetCellType()));
609  }
610  err = ex_put_set(exoid, EX_SIDE_SET, exoSideSet.first,
611  sideSetElem.data(), sideSetSide.data());
612  checkExodusErr(err);
613  auto foundName = _sideSetNames.find(exoSideSet.first);
614  if (foundName != _sideSetNames.end()) {
615  err = ex_put_name(exoid, EX_SIDE_SET, foundName->first,
616  foundName->second.c_str());
617  checkExodusErr(err);
618  }
619  }
620  }
621 
622  for (const auto &nodeSet : _nodeSets) {
623  if (!nodeSet.second.nodes.empty()) {
624  err = ex_put_set_param(exoid, EX_NODE_SET, nodeSet.first,
625  nodeSet.second.nodes.size(), 0);
626  checkExodusErr(err);
627  std::vector<int> nodes; // Have to add 1
628  nodes.reserve(nodeSet.second.nodes.size());
629  std::transform(nodeSet.second.nodes.begin(), nodeSet.second.nodes.end(),
630  std::back_inserter(nodes), [](int x) { return x + 1; });
631  err = ex_put_set(exoid, EX_NODE_SET, nodeSet.first, nodes.data(),
632  nullptr);
633  checkExodusErr(err);
634  if (!nodeSet.second.name.empty()) {
635  err = ex_put_name(exoid, EX_NODE_SET, nodeSet.first,
636  nodeSet.second.name.c_str());
637  checkExodusErr(err);
638  }
639  }
640  }
641 
642  if (numElemVars > 0) {
643  int var_index = 1;
644  std::vector<double> vals(mesh->GetNumberOfCells());
645  for (int i = 0; i < mesh->GetCellData()->GetNumberOfArrays(); ++i) {
646  if (cellDataWriteFlag[i]) {
647  auto arr = mesh->GetCellData()->GetArray(i);
648  for (int j = 0; j < arr->GetNumberOfComponents(); ++j) {
649  for (vtkIdType k = 0; k < mesh->GetNumberOfCells(); ++k) {
650  vals[vtkIdMap[k] - 1] = arr->GetComponent(k, j);
651  }
652  auto name = getArrComponentName(arr, j);
653  if (name.empty()) {
654  name = "ElemBlockVar" + std::to_string(var_index);
655  }
656  err = ex_put_variable_name(exoid, EX_ELEM_BLOCK, var_index,
657  name.c_str());
658  checkExodusErr(err);
659  std::size_t varOffset = 0;
660  for (const auto &elemBlock : elemBlocks) {
661  err = ex_put_var(exoid, timeStep, EX_ELEM_BLOCK, var_index,
662  elemBlock.first, elemBlock.second.size(),
663  vals.data() + varOffset);
664  checkExodusErr(err);
665  varOffset += elemBlock.second.size();
666  }
667  ++var_index;
668  }
669  }
670  }
671  }
672 
673  if (numNodeVars > 0) {
674  int var_index = 1;
675  std::vector<double> vals(mesh->GetNumberOfPoints());
676  for (int i = 0; i < mesh->GetPointData()->GetNumberOfArrays(); ++i) {
677  if (pointDataWriteFlag[i]) {
678  auto arr = mesh->GetPointData()->GetArray(i);
679  for (int j = 0; j < arr->GetNumberOfComponents(); ++j) {
680  auto name = getArrComponentName(arr, j);
681  if (name.empty()) {
682  name = "NodalVar" + std::to_string(var_index);
683  }
684  err =
685  ex_put_variable_name(exoid, EX_NODAL, var_index, name.c_str());
686  checkExodusErr(err);
687  for (vtkIdType k = 0; k < mesh->GetNumberOfPoints(); ++k) {
688  vals[k] = arr->GetComponent(k, j);
689  }
690  err = ex_put_var(exoid, timeStep, EX_NODAL, var_index, 1,
691  mesh->GetNumberOfPoints(), vals.data());
692  checkExodusErr(err);
693  ++var_index;
694  }
695  }
696  }
697  }
698  if (numGlobalVars > 0) {
699  int var_index = 1;
700  std::vector<double> vals;
701  for (int i = 0; i < mesh->GetFieldData()->GetNumberOfArrays(); ++i) {
702  if (fieldDataWriteFlag[i]) {
703  auto arr = mesh->GetFieldData()->GetArray(i);
704  for (int j = 0; j < arr->GetNumberOfComponents(); ++j) {
705  vals.emplace_back(arr->GetComponent(0, j));
706  auto name = getArrComponentName(arr, j);
707  if (!name.empty()) {
708  err = ex_put_variable_name(exoid, EX_GLOBAL, var_index,
709  name.c_str());
710  checkExodusErr(err);
711  }
712  ++var_index;
713  }
714  }
715  }
716  err = ex_put_var(exoid, timeStep, EX_GLOBAL, 1, 0, numGlobalVars,
717  vals.data());
718  checkExodusErr(err);
719  }
720 
721  if (!_elemBlockPropNames.empty()) {
722  // Element block properties
723  std::vector<const char *> propNames;
724  propNames.reserve(_elemBlockPropNames.size());
725  std::vector<std::vector<int>> propVals;
726  propVals.resize(_elemBlockPropNames.size());
727  for (const auto &elemBlockProp : _elemBlockPropNames) {
728  propNames.emplace_back(elemBlockProp.c_str());
729  }
730  err = ex_put_prop_names(exoid, EX_ELEM_BLOCK, _elemBlockPropNames.size(),
731  const_cast<char **>(propNames.data()));
732  checkExodusErr(err);
733  // Use elemBlocks to verify non-empty
734  auto nonEmptyIt = elemBlocks.begin();
735  auto it = _elemBlocks.begin();
736  while (nonEmptyIt != elemBlocks.end()) {
737  if (it->first == nonEmptyIt->first) {
738  std::size_t propValsIdx = 0;
739  auto propNamesIt = propNames.begin();
740  auto propValsIt = it->second.properties.begin();
741  while (propNamesIt != propNames.end()) {
742  if (propValsIt != it->second.properties.end() &&
743  *propNamesIt == propValsIt->first) {
744  propVals[propValsIdx].emplace_back(propValsIt->second);
745  ++propValsIt;
746  } else {
747  propVals[propValsIdx].emplace_back(0);
748  }
749  ++propNamesIt;
750  ++propValsIdx;
751  }
752  ++nonEmptyIt;
753  }
754  ++it;
755  }
756  for (std::size_t i = 0; i < propNames.size(); ++i) {
757  err = ex_put_prop_array(exoid, EX_ELEM_BLOCK, propNames[i],
758  propVals[i].data());
759  checkExodusErr(err);
760  }
761  }
762  err = ex_close(exoid);
763  checkExodusErr(err);
764  } else {
765  std::cerr << "For exoGeoMesh, " << fileExt
766  << " format not currently supported." << std::endl;
767  }
768 }
769 
770 void exoGeoMesh::report(std::ostream &out) const {
771  geoMeshBase::report(out);
772  out << "Title:\t" << _title << '\n';
773  out << "Element Blocks:\t" << _elemBlocks.size() << '\n';
774  out << "Side Sets:\t" << _sideSetNames.size() << '\n';
775  out << "Node Sets:\t" << _nodeSets.size() << '\n';
776 }
777 
779  auto otherExoGM = dynamic_cast<exoGeoMesh *>(otherGeoMesh);
780  if (otherExoGM) {
781  setGeoMesh(otherExoGM->getGeoMesh());
782  otherExoGM->setGeoMesh(
784  _title = std::move(otherExoGM->_title);
785  _elemBlocks = std::move(otherExoGM->_elemBlocks);
786  _sideSetNames = std::move(otherExoGM->_sideSetNames);
787  _nodeSets = std::move(otherExoGM->_nodeSets);
788  _elemBlockPropNames = std::move(otherExoGM->_elemBlockPropNames);
789  _physGrpName = std::move(otherExoGM->_physGrpName);
790  otherExoGM->resetNative();
791  } else {
792  geoMeshBase::takeGeoMesh(otherGeoMesh);
793  }
794 }
795 
797  std::string link =
799  auto mesh = getGeoMesh().mesh;
800  vtkSmartPointer<vtkDataArray> linkArr =
801  mesh->GetCellData()->GetArray(link.c_str());
802  if (!linkArr) {
804  linkArr->SetName(link.c_str());
805  linkArr->SetNumberOfTuples(mesh->GetNumberOfCells());
806  mesh->GetCellData()->AddArray(linkArr);
807  }
808  auto elemBlockIdArr = vtkIntArray::FastDownCast(
809  mesh->GetCellData()->GetAbstractArray(getExoIdArrName()));
810  for (vtkIdType i = 0; i < mesh->GetNumberOfCells(); ++i) {
811  auto physGroup = elemBlockIdArr->GetValue(i);
812  if (!_physGrpName.empty()) {
813  physGroup = getElemBlockProperty(_physGrpName, physGroup);
814  }
815  linkArr->SetComponent(i, 0, physGroup);
816  }
819  // Should now always have side set
820  setSideSetObjId();
821 }
822 
823 const std::string &exoGeoMesh::getTitle() const { return _title; }
824 
825 void exoGeoMesh::setTitle(const std::string &title) { _title = title; }
826 
827 int exoGeoMesh::getNumElemBlocks() const { return _elemBlocks.size(); }
828 
829 std::map<int, std::string> exoGeoMesh::getElemBlockNames() const {
830  std::map<int, std::string> names;
831  std::transform(_elemBlocks.begin(), _elemBlocks.end(),
832  std::inserter(names, names.begin()),
833  [](const decltype(_elemBlocks)::value_type &block)
834  -> decltype(names)::value_type {
835  return {block.first, block.second.name};
836  });
837  return names;
838 }
839 
840 const std::string &exoGeoMesh::getElemBlockName(int id) const {
841  auto it = _elemBlocks.find(id);
842  if (it == _elemBlocks.end()) {
843  std::cerr << "No block found with id: " << id << std::endl;
844  exit(1);
845  } else {
846  return it->second.name;
847  }
848 }
849 
850 void exoGeoMesh::setElemBlockName(int id, const std::string &name) {
851  auto it = _elemBlocks.find(id);
852  if (it == _elemBlocks.end()) {
853  std::cerr << "No block found with id: " << id << std::endl;
854  exit(1);
855  } else {
856  it->second.name = name;
857  }
858 }
859 
860 std::vector<int> exoGeoMesh::getElemBlockIds() const {
861  std::vector<int> ids;
862  ids.reserve(_elemBlocks.size());
863  for (const auto &elemBlock : _elemBlocks) {
864  ids.emplace_back(elemBlock.first);
865  }
866  return ids;
867 }
868 
869 std::vector<vtkIdType> exoGeoMesh::getElemBlock(int blockId) const {
870  auto blockIt = _elemBlocks.find(blockId);
871  if (blockIt == _elemBlocks.end()) {
872  std::cerr << "No block found with id: " << blockId << std::endl;
873  exit(1);
874  }
875  std::vector<vtkIdType> cellIdxList;
876  auto elemBlockIdArr = vtkIntArray::FastDownCast(
877  getGeoMesh().mesh->GetCellData()->GetAbstractArray(getExoIdArrName()));
878  for (vtkIdType i = 0; i < getGeoMesh().mesh->GetNumberOfCells(); ++i) {
879  if (elemBlockIdArr->GetValue(i) == blockId) {
880  cellIdxList.emplace_back(i);
881  }
882  }
883  return cellIdxList;
884 }
885 
886 int exoGeoMesh::getElemBlockId(vtkIdType cellIdx) const {
887  auto mesh = getGeoMesh().mesh;
888  if (!mesh || mesh->GetNumberOfCells() == 0) {
889  std::cerr << "Mesh is empty. No cells to find" << std::endl;
890  return -1;
891  }
892  if (cellIdx < 0 || cellIdx >= mesh->GetNumberOfCells()) {
893  std::cerr << "No cell found with index: " << cellIdx << std::endl;
894  return -1;
895  }
896  auto elemBlockIdArr = vtkIntArray::FastDownCast(
897  mesh->GetCellData()->GetAbstractArray(getExoIdArrName()));
898  return elemBlockIdArr->GetValue(cellIdx);
899 }
900 
901 int exoGeoMesh::addElemBlock(vtkUnstructuredGrid *elements,
902  const std::string &name, float tol) {
903  if (elements && elements->GetNumberOfCells() > 0) {
904  auto types = vtkSmartPointer<vtkCellTypes>::New();
905  elements->GetCellTypes(types);
906  if (types->GetNumberOfTypes() > 1) {
907  std::cerr << "Single element block cannot have multiple cell types"
908  << std::endl;
909  return -1;
910  }
911  auto mesh = getGeoMesh().mesh;
912  if (mesh->GetNumberOfCells() == 0) {
913  mesh->DeepCopy(elements);
914 #ifdef HAVE_GMSH
915  if (!getGeoMesh().geo.empty()) {
916  gmsh::model::setCurrent(getGeoMesh().geo);
917  gmsh::model::remove();
918  }
919 #endif
920  setGeoMesh({mesh, "", "", {}});
921  resetNative();
922  // Assumption that there is exactly one element block now.
923  return _elemBlocks.begin()->first;
924  } else {
925  int newElemBlockId = nemAux::leastUnusedKey(_elemBlocks);
926  auto &elemBlock = _elemBlocks[newElemBlockId];
927  elemBlock.cellType = static_cast<VTKCellType>(types->GetCellType(0));
928  elemBlock.name = name;
929  addCellsToBlock(elements, newElemBlockId, tol);
930  return newElemBlockId;
931  }
932  } else {
933  std::cerr << "No elements to add." << std::endl;
934  return -1;
935  }
936 }
937 
938 void exoGeoMesh::addCellsToBlock(vtkUnstructuredGrid *elements, int blockId,
939  float tol) {
940  if (elements && elements->GetNumberOfCells() > 0) {
941  auto types = vtkSmartPointer<vtkCellTypes>::New();
942  elements->GetCellTypes(types);
943  if (types->GetNumberOfTypes() > 1) {
944  std::cerr << "Single element block cannot have multiple cell types"
945  << std::endl;
946  exit(1);
947  }
948  auto elemBlock = _elemBlocks.find(blockId);
949  if (getGeoMesh().mesh->GetNumberOfCells() == 0) {
950  _elemBlocks.clear();
951  std::cerr << "No existing element blocks." << std::endl;
952  exit(1);
953  }
954  if (elemBlock == _elemBlocks.end()) {
955  std::cerr << "No existing element block with id: " << blockId
956  << std::endl;
957  exit(1);
958  }
959  if (types->GetCellType(0) != elemBlock->second.cellType) {
960  std::cerr
961  << "Cell type does not match cell type of existing element block."
962  << std::endl;
963  exit(1);
964  }
965  vtkSmartPointer<vtkAbstractArray> oldBlockIdArr =
966  elements->GetCellData()->GetAbstractArray(getExoIdArrName());
967  if (oldBlockIdArr) {
968  elements->GetCellData()->RemoveArray(getExoIdArrName());
969  }
970  auto mesh = getGeoMesh().mesh;
971  auto newBlockIdArr = vtkSmartPointer<vtkIntArray>::Take(
972  vtkIntArray::FastDownCast(mesh->GetCellData()
973  ->GetAbstractArray(getExoIdArrName())
974  ->NewInstance()));
975  newBlockIdArr->SetName(getExoIdArrName());
976  newBlockIdArr->SetNumberOfValues(elements->GetNumberOfCells());
977  newBlockIdArr->FillComponent(0, blockId);
978  elements->GetCellData()->AddArray(newBlockIdArr);
979 #ifdef HAVE_GMSH
980  if (!getGeoMesh().geo.empty()) {
981  gmsh::model::setCurrent(getGeoMesh().geo);
982  gmsh::model::remove();
983  setGeoMesh({mesh, "", "", getGeoMesh().sideSet});
984  }
985 #endif
987  auto nodeMaps = mergeMeshes(mesh, elements, newMesh, tol);
988  setGeoMesh(
989  {newMesh, getGeoMesh().geo, getGeoMesh().link, getGeoMesh().sideSet});
990  resetNodeSetPoints(nodeMaps.first);
991  resetSideSetPoints(newMesh, getGeoMesh().sideSet.sides, nodeMaps.first);
992  elements->GetCellData()->RemoveArray(getExoIdArrName());
993  if (oldBlockIdArr) {
994  elements->GetCellData()->AddArray(oldBlockIdArr);
995  }
996  } else {
997  std::cerr << "No elements to add." << std::endl;
998  }
999 }
1000 
1001 int exoGeoMesh::reassignCells(const std::vector<vtkIdType> &cells,
1002  int blockId, const std::string &elemBlockName) {
1003  if (!cells.empty()) {
1004  auto mesh = getGeoMesh().mesh;
1005  auto cellType =
1006  static_cast<VTKCellType>(mesh->GetCellType(cells.front()));
1007  for (const auto &cell : cells) {
1008  if (mesh->GetCellType(cell) != cellType) {
1009  std::cerr << "Single element block cannot have multiple cell types"
1010  << std::endl;
1011  exit(1);
1012  }
1013  }
1014  auto elemBlockIt = _elemBlocks.find(blockId);
1015  if (elemBlockIt == _elemBlocks.end()) {
1016  if (blockId <= 0) {
1018  }
1019  _elemBlocks[blockId] = {elemBlockName, cellType, {}};
1020  }
1021  auto elemBlockIdArr = vtkIntArray::FastDownCast(
1022  mesh->GetCellData()->GetAbstractArray(getExoIdArrName()));
1023  for (const auto &cell : cells) {
1024  elemBlockIdArr->SetValue(cell, blockId);
1025  }
1026 #ifdef HAVE_GMSH
1027  if (!getGeoMesh().geo.empty()) {
1028  gmsh::model::setCurrent(getGeoMesh().geo);
1029  gmsh::model::remove();
1030  setGeoMesh({mesh, "", "", getGeoMesh().sideSet});
1031  }
1032 #endif
1033  return blockId;
1034  } else {
1035  std::cerr << "No cells to add." << std::endl;
1036  return -1;
1037  }
1038 }
1039 
1040 bool exoGeoMesh::addElemBlockProperty(const std::string &propName) {
1041  if (getGeoMesh().mesh->GetNumberOfCells() == 0) {
1042  return false;
1043  }
1044  return _elemBlockPropNames.insert(propName).second;
1045 }
1046 
1047 std::vector<std::string> exoGeoMesh::getElemBlockPropertyNames() const {
1048  std::vector<std::string> names;
1049  names.assign(_elemBlockPropNames.begin(), _elemBlockPropNames.end());
1050  return names;
1051 }
1052 
1053 int exoGeoMesh::getElemBlockProperty(const std::string &propName,
1054  int blockId) const {
1055  if (_elemBlockPropNames.find(propName) == _elemBlockPropNames.end()) {
1056  std::cerr << "No property found with name: " << propName << std::endl;
1057  exit(1);
1058  }
1059  auto blockIt = _elemBlocks.find(blockId);
1060  if (blockIt == _elemBlocks.end()) {
1061  std::cerr << "No block found with id: " << blockId << std::endl;
1062  exit(1);
1063  } else {
1064  auto it = blockIt->second.properties.find(propName);
1065  if (it == blockIt->second.properties.end()) {
1066  return 0;
1067  } else {
1068  return it->second;
1069  }
1070  }
1071 }
1072 
1073 void exoGeoMesh::setElemBlockProperty(const std::string &propName, int blockId,
1074  int value) {
1075  if (_elemBlockPropNames.find(propName) == _elemBlockPropNames.end()) {
1076  std::cerr << "No property found with name: " << propName << std::endl;
1077  exit(1);
1078  }
1079  auto blockIt = _elemBlocks.find(blockId);
1080  if (blockIt == _elemBlocks.end()) {
1081  std::cerr << "No block found with id: " << blockId << std::endl;
1082  exit(1);
1083  } else {
1084  blockIt->second.properties[propName] = value;
1085  }
1086 }
1087 
1088 int exoGeoMesh::getNumSideSets() const { return _sideSetNames.size(); }
1089 
1090 const std::map<int, std::string> & exoGeoMesh::getSideSetNames() const {
1091  return _sideSetNames;
1092 }
1093 
1094 const std::string &exoGeoMesh::getSideSetName(int id) const {
1095  auto it = _sideSetNames.find(id);
1096  if (it == _sideSetNames.end()) {
1097  std::cerr << "No side set found with id: " << id << std::endl;
1098  exit(1);
1099  } else {
1100  return it->second;
1101  }
1102 }
1103 
1104 void exoGeoMesh::setSideSetName(int id, const std::string &name) {
1105  auto it = _sideSetNames.find(id);
1106  if (it == _sideSetNames.end()) {
1107  std::cerr << "No side set found with id: " << id << std::endl;
1108  exit(1);
1109  } else {
1110  it->second = name;
1111  }
1112 }
1113 
1114 std::vector<int> exoGeoMesh::getSideSetIds() const {
1115  std::vector<int> ids;
1116  ids.reserve(_sideSetNames.size());
1117  for (const auto &sideSet : _sideSetNames) {
1118  ids.emplace_back(sideSet.first);
1119  }
1120  return ids;
1121 }
1122 
1123 std::pair<std::vector<vtkIdType>, std::vector<int>> exoGeoMesh::getSideSet(
1124  int sideSetId) const {
1125  std::vector<vtkIdType> cellIdVec;
1126  std::vector<int> sideVec;
1127  if (_sideSetNames.find(sideSetId) == _sideSetNames.end()) {
1128  std::cerr << "No side set found with id: " << sideSetId << std::endl;
1129  }
1130  auto geoMesh = getGeoMesh();
1131  auto sideSet = geoMesh.sideSet;
1132  auto sideSetExoId = vtkIntArray::FastDownCast(
1133  sideSet.sides->GetCellData()->GetAbstractArray(getExoIdArrName()));
1134  auto sideSetOrigCellId = geoMesh.sideSet.getOrigCellArr();
1135  auto sideSetCellFaceId = geoMesh.sideSet.getCellFaceArr();
1136  for (vtkIdType i = 0; i < sideSet.sides->GetNumberOfCells(); ++i) {
1137  if (sideSetExoId->GetValue(i) == sideSetId) {
1138  cellIdVec.emplace_back(sideSetOrigCellId->GetTypedComponent(i, 0));
1139  sideVec.emplace_back(sideSetCellFaceId->GetTypedComponent(i, 0));
1140  }
1141  }
1142  return {cellIdVec, sideVec};
1143 }
1144 
1145 int exoGeoMesh::addSideSet(const std::vector<vtkIdType> &elements,
1146  const std::vector<int> &sides,
1147  const std::string &name) {
1148  if (!elements.empty() && elements.size() == sides.size()) {
1149  auto newId = nemAux::leastUnusedKey(_sideSetNames);
1150  auto gm = getGeoMesh();
1151  if (!gm.sideSet.sides) {
1152  auto sideSetPD = vtkSmartPointer<vtkPolyData>::New();
1153  sideSetPD->Allocate();
1154  sideSetPD->SetPoints(getGeoMesh().mesh->GetPoints());
1155  auto sideSetExoId = vtkSmartPointer<vtkIntArray>::New();
1156  sideSetExoId->SetName(getExoIdArrName());
1157  sideSetPD->GetCellData()->AddArray(sideSetExoId);
1158  auto sideSetEntities = vtkSmartPointer<vtkIntArray>::New();
1159  auto sideSetOrigCellId = vtkSmartPointer<vtkIdTypeArray>::New();
1160  sideSetOrigCellId->SetNumberOfComponents(2);
1161  auto sideSetCellFaceId = vtkSmartPointer<vtkIntArray>::New();
1162  sideSetCellFaceId->SetNumberOfComponents(2);
1163  setGeoMesh(
1164  {getGeoMesh().mesh,
1165  getGeoMesh().geo,
1166  getGeoMesh().link,
1167  {sideSetPD, sideSetEntities, sideSetOrigCellId, sideSetCellFaceId}});
1168  }
1169  auto sideSet = getGeoMesh().sideSet;
1170  auto sideSetExoId = vtkIntArray::FastDownCast(
1171  sideSet.sides->GetCellData()->GetAbstractArray(getExoIdArrName()));
1172  sideSetExoId->Resize(sideSet.sides->GetNumberOfCells() + elements.size());
1173  auto sideSetEntities = sideSet.getGeoEntArr();
1174  sideSetEntities->Resize(sideSet.sides->GetNumberOfCells() +
1175  elements.size());
1176  auto sideSetOrigCellId = sideSet.getOrigCellArr();
1177  sideSetOrigCellId->Resize(sideSet.sides->GetNumberOfCells() +
1178  elements.size());
1179  auto sideSetCellFaceId = sideSet.getCellFaceArr();
1180  sideSetCellFaceId->Resize(sideSet.sides->GetNumberOfCells() +
1181  elements.size());
1182 
1183  auto mesh = getGeoMesh().mesh;
1184  auto elemIt = elements.begin();
1185  auto sidesIt = sides.begin();
1186  for (; elemIt != elements.end(); ++sidesIt, ++elemIt) {
1187  auto parent = mesh->GetCell(*elemIt);
1188  if (!parent) {
1189  std::cerr << "No cell found with index " << *elemIt << std::endl;
1190  continue;
1191  }
1192  auto side = parent->GetCellDimension() == 2 ? parent->GetEdge(*sidesIt)
1193  : parent->GetFace(*sidesIt);
1194  sideSet.sides->InsertNextCell(side->GetCellType(), side->GetPointIds());
1195  sideSetExoId->InsertNextValue(newId);
1196  sideSetEntities->InsertNextValue(newId);
1197  sideSetOrigCellId->InsertNextTuple2(*elemIt, -1);
1198  sideSetCellFaceId->InsertNextTuple2(*sidesIt, -1);
1199  }
1200 
1201  _sideSetNames[newId] = name;
1202  return newId;
1203  }
1204  return -1;
1205 }
1206 
1207 int exoGeoMesh::getNumNodeSets() const { return _nodeSets.size(); }
1208 
1209 std::map<int, std::string> exoGeoMesh::getNodeSetNames() const {
1210  std::map<int, std::string> names;
1211  std::transform(_nodeSets.begin(), _nodeSets.end(),
1212  std::inserter(names, names.begin()),
1213  [](const std::pair<int, nodeSet> &set) {
1214  return std::make_pair(set.first, set.second.name);
1215  });
1216  return names;
1217 }
1218 
1219 const std::string &exoGeoMesh::getNodeSetName(int id) const {
1220  auto it = _nodeSets.find(id);
1221  if (it == _nodeSets.end()) {
1222  std::cerr << "No side set found with id: " << id << std::endl;
1223  exit(1);
1224  } else {
1225  return it->second.name;
1226  }
1227 }
1228 
1229 void exoGeoMesh::setNodeSetName(int id, const std::string &name) {
1230  auto it = _nodeSets.find(id);
1231  if (it == _nodeSets.end()) {
1232  std::cerr << "No side set found with id: " << id << std::endl;
1233  exit(1);
1234  } else {
1235  it->second.name = name;
1236  }
1237 }
1238 
1239 std::vector<int> exoGeoMesh::getNodeSetIds() const {
1240  std::vector<int> ids;
1241  ids.reserve(_nodeSets.size());
1242  for (const auto &nodeSet : _nodeSets) {
1243  ids.emplace_back(nodeSet.first);
1244  }
1245  return ids;
1246 }
1247 
1248 const std::vector<vtkIdType> &exoGeoMesh::getNodeSet(int nodeSetId) const {
1249  auto it = _nodeSets.find(nodeSetId);
1250  if (it == _nodeSets.end()) {
1251  std::cerr << "No node set found with id: " << nodeSetId << std::endl;
1252  exit(1);
1253  }
1254  return it->second.nodes;
1255 }
1256 
1257 int exoGeoMesh::addNodeSet(const std::vector<vtkIdType> &nodes,
1258  const std::string &name) {
1259  if (!nodes.empty()) {
1260  auto minmax = std::minmax_element(nodes.begin(), nodes.end());
1261  if (*minmax.first < 0) {
1262  std::cerr << "Unknown node " << *minmax.first << std::endl;
1263  return -1;
1264  } else if (*minmax.second >= getGeoMesh().mesh->GetNumberOfPoints()) {
1265  std::cerr << "Unknown node " << *minmax.second << std::endl;
1266  return -1;
1267  }
1268  auto newId = nemAux::leastUnusedKey(_nodeSets);
1269  _nodeSets[newId] = {name, nodes};
1270  return newId;
1271  }
1272  return -1;
1273 }
1274 
1275 const std::string &exoGeoMesh::getPhysGrpPropertyName() const {
1276  return _physGrpName;
1277 }
1278 
1279 void exoGeoMesh::setPhysGrpPropertyName(const std::string &physGrpName) {
1280  if (_elemBlockPropNames.find(physGrpName) == _elemBlockPropNames.end()) {
1281  std::cerr << "No property found with name " << physGrpName << std::endl;
1282  return;
1283  }
1284  _physGrpName = physGrpName;
1285  auto link = getGeoMesh().link;
1286  if (!link.empty()) {
1287  auto mesh = getGeoMesh().mesh;
1288  auto elemBlockIdArr = vtkIntArray::FastDownCast(
1289  mesh->GetCellData()->GetAbstractArray(getExoIdArrName()));
1290  auto linkArr = mesh->GetCellData()->GetArray(link.c_str());
1291  if (linkArr) {
1292  for (vtkIdType i = 0; i < mesh->GetNumberOfCells(); ++i) {
1293  linkArr->SetComponent(
1294  i, 0,
1295  getElemBlockProperty(_physGrpName, elemBlockIdArr->GetValue(i)));
1296  }
1297  }
1298  }
1299 }
1300 
1301 void exoGeoMesh::stitch(const exoGeoMesh &otherGM, float tol) {
1302  auto mesh = this->getGeoMesh().mesh;
1303  if (mesh->GetNumberOfCells() == 0) {
1304  std::cerr << "Current mesh is empty. Cannot stitch onto empty mesh."
1305  << std::endl;
1306  exit(1);
1307  }
1308  if (this->getGeoMesh().getDimension() !=
1309  otherGM.getGeoMesh().getDimension()) {
1310  std::cerr << "Dimension of meshes does not match." << std::endl;
1311  exit(1);
1312  }
1313  auto numOrigCells = mesh->GetNumberOfCells();
1314  auto otherMesh = otherGM.getGeoMesh().mesh;
1315  std::map<int, int> old2newElemBlock;
1316 
1317  std::set<std::string> propsToCopy;
1318  std::set_intersection(_elemBlockPropNames.begin(), _elemBlockPropNames.end(),
1319  otherGM._elemBlockPropNames.begin(),
1320  otherGM._elemBlockPropNames.end(),
1321  std::inserter(propsToCopy, propsToCopy.begin()));
1322  auto newBlockId = nemAux::leastUnusedKey(this->_elemBlocks);
1323  for (const auto &otherElemBlock : otherGM._elemBlocks) {
1324  old2newElemBlock[otherElemBlock.first] = newBlockId;
1325  auto &newElemBlock = this->_elemBlocks[newBlockId];
1326  newElemBlock.name = otherElemBlock.second.name;
1327  newElemBlock.cellType = otherElemBlock.second.cellType;
1328  for (const auto &property : propsToCopy) {
1329  auto it = otherElemBlock.second.properties.find(property);
1330  if (it != otherElemBlock.second.properties.end()) {
1331  newElemBlock.properties[property] = it->second;
1332  }
1333  }
1334  newBlockId = nemAux::leastUnusedKey(this->_elemBlocks, newBlockId);
1335  }
1336  auto oldOtherBlockIdArr =
1337  vtkSmartPointer<vtkIntArray>(vtkIntArray::FastDownCast(
1338  otherMesh->GetCellData()->GetAbstractArray(getExoIdArrName())));
1339  otherMesh->GetCellData()->RemoveArray(getExoIdArrName());
1340  auto newOtherBlockIdArr = vtkSmartPointer<vtkIntArray>::Take(
1341  vtkIntArray::FastDownCast(mesh->GetCellData()
1342  ->GetAbstractArray(getExoIdArrName())
1343  ->NewInstance()));
1344  newOtherBlockIdArr->SetName(getExoIdArrName());
1345  newOtherBlockIdArr->SetNumberOfValues(otherMesh->GetNumberOfCells());
1346  for (vtkIdType i = 0; i < otherMesh->GetNumberOfCells(); ++i) {
1347  newOtherBlockIdArr->SetComponent(
1348  i, 0, old2newElemBlock.at(oldOtherBlockIdArr->GetValue(i)));
1349  }
1350  otherMesh->GetCellData()->AddArray(newOtherBlockIdArr);
1351 
1352 #ifdef HAVE_GMSH
1353  if (!this->getGeoMesh().geo.empty()) {
1354  gmsh::model::setCurrent(this->getGeoMesh().geo);
1355  gmsh::model::remove();
1356  mesh->GetCellData()->RemoveArray(this->getGeoMesh().link.c_str());
1357  this->setGeoMesh({mesh, "", "", this->getGeoMesh().sideSet});
1358  }
1359 #endif
1361  auto nodeMaps = mergeMeshes(mesh, otherMesh, newMesh, tol);
1362  resetNodeSetPoints(nodeMaps.first);
1363  if (!otherGM._nodeSets.empty()) {
1364  auto nextNodeSetId = nemAux::leastUnusedKey(_nodeSets);
1365  for (const auto &otherNodeSet : otherGM._nodeSets) {
1366  auto &nodeSet = _nodeSets[nextNodeSetId];
1367  nodeSet.name = otherNodeSet.second.name;
1368  nodeSet.nodes.reserve(otherNodeSet.second.nodes.size());
1369  std::transform(
1370  otherNodeSet.second.nodes.begin(), otherNodeSet.second.nodes.end(),
1371  std::back_inserter(nodeSet.nodes),
1372  [&nodeMaps](vtkIdType x) { return nodeMaps.second->GetValue(x); });
1373  nextNodeSetId = nemAux::leastUnusedKey(_nodeSets, nextNodeSetId);
1374  }
1375  }
1376  resetSideSetPoints(newMesh, getGeoMesh().sideSet.sides, nodeMaps.first);
1377  setGeoMesh(
1378  {newMesh, getGeoMesh().geo, getGeoMesh().link, getGeoMesh().sideSet});
1379  auto otherGeoMesh = otherGM.getGeoMesh();
1380  if (otherGeoMesh.sideSet.sides) {
1381  auto otherSideSet = otherGeoMesh.sideSet;
1382  auto otherExoIdArr = vtkIntArray::FastDownCast(
1383  otherSideSet.sides->GetCellData()->GetAbstractArray(getExoIdArrName()));
1384  auto otherEntitiesArr = otherGeoMesh.sideSet.getGeoEntArr();
1385  auto otherOrigCellIdArr = otherGeoMesh.sideSet.getOrigCellArr();
1386  auto otherCellFaceIdArr = otherGeoMesh.sideSet.getCellFaceArr();
1387  auto geoMesh = this->getGeoMesh();
1388  auto sideSet = geoMesh.sideSet;
1389  vtkSmartPointer<vtkIntArray> sideSetExoId;
1390  vtkSmartPointer<vtkIntArray> sideSetEntities;
1391  vtkSmartPointer<vtkIdTypeArray> sideSetOrigCellId;
1392  vtkSmartPointer<vtkIntArray> sideSetCellFaceId;
1393  if (sideSet.sides) {
1394  sideSetExoId = vtkIntArray::FastDownCast(
1395  sideSet.sides->GetCellData()->GetAbstractArray(getExoIdArrName()));
1396  sideSetEntities = geoMesh.sideSet.getGeoEntArr();
1397  sideSetOrigCellId = geoMesh.sideSet.getOrigCellArr();
1398  sideSetCellFaceId = geoMesh.sideSet.getCellFaceArr();
1399  } else {
1400  auto sideSetPD = vtkSmartPointer<vtkPolyData>::New();
1401  sideSetPD->Allocate(otherSideSet.sides);
1402  sideSetExoId = vtkSmartPointer<vtkIntArray>::New();
1403  sideSetExoId->SetName(getExoIdArrName());
1404  sideSetExoId->Allocate(otherSideSet.sides->GetNumberOfCells());
1405  sideSet.sides->GetCellData()->AddArray(sideSetExoId);
1406  sideSetEntities = vtkSmartPointer<vtkIntArray>::New();
1407  sideSetEntities->Allocate(otherSideSet.sides->GetNumberOfCells());
1408  sideSetOrigCellId = vtkSmartPointer<vtkIdTypeArray>::New();
1409  sideSetOrigCellId->SetNumberOfComponents(2);
1410  sideSetOrigCellId->Allocate(otherSideSet.sides->GetNumberOfCells());
1411  sideSetCellFaceId = vtkSmartPointer<vtkIntArray>::New();
1412  sideSetCellFaceId->SetNumberOfComponents(2);
1413  sideSetCellFaceId->Allocate(otherSideSet.sides->GetNumberOfCells());
1414  sideSet = {sideSetPD, sideSetEntities, sideSetOrigCellId,
1415  sideSetCellFaceId};
1416  this->setGeoMesh({newMesh, this->getGeoMesh().geo,
1417  this->getGeoMesh().link, sideSet});
1418  }
1419  std::map<int, int> old2newSideSet;
1420  auto newSideSetId = nemAux::leastUnusedKey(this->_sideSetNames);
1421  for (const auto &otherSet : otherGM._sideSetNames) {
1422  old2newSideSet[otherSet.first] = newSideSetId;
1423  this->_sideSetNames[newSideSetId] = otherSet.second;
1424  newSideSetId = nemAux::leastUnusedKey(this->_sideSetNames, newSideSetId);
1425  }
1426  std::map<int, int> old2newGeoEnt;
1427  for (vtkIdType i = 0; i < sideSet.sides->GetNumberOfCells(); ++i) {
1428  old2newGeoEnt[sideSetEntities->GetValue(i)] = -1;
1429  }
1430  int nextSideSetEntId = nemAux::leastUnusedKey(old2newGeoEnt);
1431  for (vtkIdType i = 0; i < otherSideSet.sides->GetNumberOfCells(); ++i) {
1432  auto origCellId = otherOrigCellIdArr->GetTypedComponent(i, 0) + numOrigCells;
1433  auto cellFaceId = otherCellFaceIdArr->GetTypedComponent(i, 0);
1434  auto parent = newMesh->GetCell(origCellId);
1435  auto side = parent->GetCellDimension() == 2 ? parent->GetEdge(cellFaceId)
1436  : parent->GetFace(cellFaceId);
1437  sideSet.sides->InsertNextCell(side->GetCellType(), side->GetPointIds());
1438  int newEntId = otherEntitiesArr->GetValue(i);
1439  auto insertIter = old2newGeoEnt.insert({newEntId, nextSideSetEntId});
1440  if (insertIter.second || insertIter.first->second == -1) {
1441  if (insertIter.first->second == -1) {
1442  insertIter.first->second = nextSideSetEntId;
1443  }
1444  nextSideSetEntId =
1445  nemAux::leastUnusedKey(old2newGeoEnt, nextSideSetEntId);
1446  }
1447  sideSetExoId->InsertNextValue(old2newSideSet[otherExoIdArr->GetValue(i)]);
1448  sideSetEntities->InsertNextValue(insertIter.first->second);
1449  sideSetOrigCellId->InsertNextTuple2(origCellId, -1);
1450  sideSetCellFaceId->InsertNextTuple2(cellFaceId, -1);
1451  }
1452  }
1453  otherMesh->GetCellData()->RemoveArray(getExoIdArrName());
1454  otherMesh->GetCellData()->AddArray(oldOtherBlockIdArr);
1455 }
1456 
1457 void exoGeoMesh::scaleNodes(double scale) {
1458  auto transform = vtkSmartPointer<vtkTransform>::New();
1459  transform->Scale(scale, scale, scale);
1460  auto transformFilter = vtkSmartPointer<vtkTransformFilter>::New();
1461  transformFilter->SetTransform(transform);
1462  transformFilter->SetInputData(getGeoMesh().mesh);
1463  transformFilter->Update();
1464  auto mesh = vtkUnstructuredGrid::SafeDownCast(transformFilter->GetOutput());
1465  auto sideSet = getGeoMesh().sideSet;
1466  if (sideSet.sides) {
1467  sideSet.sides->SetPoints(mesh->GetPoints());
1468  }
1470 }
1471 
1473  vtkSmartPointer<vtkExodusIIReader> reader) {
1475  SideSet sideSet{};
1476  if (!reader) {
1477  return {mesh, "", "", {}};
1478  }
1479 
1480  auto mbds = reader->GetOutput();
1481  if (mbds->GetNumberOfBlocks() != 8) {
1482  std::cerr << "Malformed output of vtkExodusIIReader. Check that file "
1483  "exists and is valid Exodus II file."
1484  << std::endl;
1485  exit(1);
1486  }
1487  // Map from "GlobalElementId", set by vtkExodusIIReader, to the index in
1488  // mesh, which becomes GeoMesh.mesh
1489  std::map<vtkIdType, vtkIdType> elemId2MeshIdx;
1490  if (reader->GetNumberOfElementsInFile() > 0) {
1491  auto exoElemBlocks = vtkMultiBlockDataSet::SafeDownCast(mbds->GetBlock(0));
1492  auto merge = vtkSmartPointer<mergeCells>::New();
1493  merge->SetUnstructuredGrid(mesh);
1494  merge->SetTotalNumberOfDataSets(exoElemBlocks->GetNumberOfBlocks());
1495  merge->SetTotalNumberOfPoints(reader->GetNumberOfNodes());
1496  merge->SetTotalNumberOfCells(reader->GetTotalNumberOfElements());
1497  merge->SetUseGlobalIds(1);
1498  merge->SetUseGlobalCellIds(1);
1499  auto it = vtkSmartPointer<vtkCompositeDataIterator>::Take(
1500  exoElemBlocks->NewIterator());
1501  for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextItem()) {
1502  merge->MergeDataSet(vtkDataSet::SafeDownCast(it->GetCurrentDataObject()));
1503  }
1504  merge->Finish();
1505  for (int i = mesh->GetCellData()->GetNumberOfArrays() - 1; i >= 0; --i) {
1506  if (mesh->GetCellData()->GetArray(i)->GetNumberOfTuples() !=
1507  mesh->GetNumberOfCells()) {
1508  mesh->GetCellData()->RemoveArray(i);
1509  }
1510  }
1511  for (int i = mesh->GetPointData()->GetNumberOfArrays() - 1; i >= 0; --i) {
1512  if (mesh->GetPointData()->GetArray(i)->GetNumberOfTuples() !=
1513  mesh->GetNumberOfPoints()) {
1514  mesh->GetPointData()->RemoveArray(i);
1515  }
1516  }
1517  auto globalElemIdArr = vtkIdTypeArray::FastDownCast(
1518  mesh->GetCellData()->GetArray(reader->GetGlobalElementIdArrayName()));
1519  for (vtkIdType i = 0; i < mesh->GetNumberOfCells(); ++i) {
1520  elemId2MeshIdx[globalElemIdArr->GetValue(i)] = i;
1521  }
1522  mesh->GetCellData()->RemoveArray(reader->GetPedigreeElementIdArrayName());
1523  mesh->GetCellData()->RemoveArray(reader->GetGlobalElementIdArrayName());
1524  }
1525  if (reader->GetNumberOfSideSetArrays() > 0 &&
1526  reader->GetDimensionality() >= 2) {
1527  auto sideSetPD = vtkSmartPointer<vtkPolyData>::New();
1528  sideSetPD->SetPoints(mesh->GetPoints());
1529  sideSetPD->Allocate();
1530  auto sideSetExoId = vtkSmartPointer<vtkIntArray>::New();
1531  sideSetExoId->SetName(getExoIdArrName());
1532  auto sideSetEntities = vtkSmartPointer<vtkIntArray>::New();
1533  auto sideSetOrigCellId = vtkSmartPointer<vtkIdTypeArray>::New();
1534  sideSetOrigCellId->SetNumberOfComponents(2);
1535  auto sideSetCellFaceId = vtkSmartPointer<vtkIntArray>::New();
1536  sideSetCellFaceId->SetNumberOfComponents(2);
1537 
1538  int comp_ws = sizeof(double);
1539  int io_ws = 0;
1540  float version;
1541  auto exoid =
1542  ex_open(reader->GetFileName(), EX_READ, &comp_ws, &io_ws, &version);
1543  checkExodusErr(exoid, true);
1544 
1545  auto exoSideSets = vtkMultiBlockDataSet::SafeDownCast(mbds->GetBlock(4));
1546  for (unsigned int i = 0; i < exoSideSets->GetNumberOfBlocks(); ++i) {
1547  auto exoSideSet =
1548  vtkUnstructuredGrid::SafeDownCast(exoSideSets->GetBlock(i));
1549  // vtkExodusIIReader sets the element id off by 1 compared to
1550  // GlobalElementId (see vtkExodusIIReader.cxx::832 in v7.1.0)
1551  auto sourceElemArr = vtkIdTypeArray::FastDownCast(
1552  exoSideSet->GetCellData()->GetAbstractArray(
1553  reader->GetSideSetSourceElementIdArrayName()));
1554  auto objectIdArr = exoSideSet->GetCellData()->GetArray(getExoIdArrName());
1555  // Because the element ids are off by 1 compared to GlobalElementId,
1556  // the side number is off because the wrong parent cell type is used to
1557  // convert Exodus side index to VTK side index. Thus we do not use
1558  // exoSideSet->GetCellData()->GetAbstractArray(
1559  // reader->GetSideSetSourceElementSideArrayName());
1560  // and instead read from the file.
1561  // Here we rely on the assumption that vtkExodusIIReader does not change
1562  // the order of entries in the side set.
1563  std::vector<int> sourceSideVec;
1564  sourceSideVec.resize(exoSideSet->GetNumberOfCells());
1565  auto err = ex_get_set(
1566  exoid, EX_SIDE_SET,
1567  vtkIntArray::FastDownCast(
1568  exoSideSet->GetCellData()->GetAbstractArray(getExoIdArrName()))
1569  ->GetValue(0),
1570  nullptr, sourceSideVec.data());
1571  checkExodusErr(err);
1572 
1573  for (vtkIdType j = 0; j < exoSideSet->GetNumberOfCells(); ++j) {
1574  auto origCellId = elemId2MeshIdx.at(sourceElemArr->GetValue(j) + 1);
1575  auto parent = mesh->GetCell(origCellId);
1576  auto cellFaceId = exoSide2vtkSide(
1577  sourceSideVec[j], static_cast<VTKCellType>(parent->GetCellType()));
1578  if (reader->GetDimensionality() == parent->GetCellDimension()) {
1579  if (reader->GetDimensionality() == 2) {
1580  auto edge = parent->GetEdge(cellFaceId);
1581  sideSetPD->InsertNextCell(edge->GetCellType(), edge->GetPointIds());
1582  } else if (reader->GetDimensionality() == 3) {
1583  auto face = parent->GetFace(cellFaceId);
1584  sideSetPD->InsertNextCell(face->GetCellType(), face->GetPointIds());
1585  }
1586  sideSetExoId->InsertNextValue(
1587  static_cast<int>(objectIdArr->GetComponent(j, 0)));
1588  sideSetEntities->InsertNextValue(
1589  static_cast<int>(objectIdArr->GetComponent(j, 0)));
1590  sideSetOrigCellId->InsertNextTuple2(origCellId, -1);
1591  sideSetCellFaceId->InsertNextTuple2(cellFaceId, -1);
1592  }
1593  }
1594  }
1595  sideSetPD->GetCellData()->AddArray(sideSetExoId);
1596  sideSetPD->Squeeze();
1597  sideSet = {sideSetPD, sideSetEntities, sideSetOrigCellId,
1598  sideSetCellFaceId};
1599  auto err = ex_close(exoid);
1600  checkExodusErr(err);
1601  }
1602  return {mesh, "", "", sideSet};
1603 }
1604 
1605 vtkSmartPointer<vtkExodusIIReader> exoGeoMesh::getExoReader(
1606  const std::string &fileName, int timeStep) {
1607  static constexpr std::array<vtkExodusIIReader::ObjectType, 8> objectTypes = {
1608  vtkExodusIIReader::ObjectType::ELEM_BLOCK_ELEM_CONN,
1609  vtkExodusIIReader::ObjectType::NODE_SET_CONN,
1610  vtkExodusIIReader::ObjectType::SIDE_SET_CONN,
1611  vtkExodusIIReader::ObjectType::NODAL,
1612  vtkExodusIIReader::ObjectType::GLOBAL,
1613  vtkExodusIIReader::ObjectType::ELEM_BLOCK,
1614  vtkExodusIIReader::ObjectType::NODE_SET,
1615  vtkExodusIIReader::ObjectType::SIDE_SET};
1616  if (fileName.empty()) {
1617  return nullptr;
1618  }
1620  reader->SetFileName(fileName.c_str());
1621  // time steps are 1-indexed in Exodus
1622  reader->SetTimeStep(timeStep - 1);
1623  reader->UpdateInformation();
1624  for (const auto &objectType : objectTypes) {
1625  reader->SetAllArrayStatus(objectType, 1);
1626  }
1627  for (int i = 0; i < reader->GetNumberOfNodeMapArrays(); ++i) {
1628  reader->SetNodeMapArrayStatus(reader->GetNodeMapArrayName(i), 0);
1629  }
1630  for (int i = 0; i < reader->GetNumberOfElementMapArrays(); ++i) {
1631  reader->SetElementMapArrayStatus(reader->GetElementMapArrayName(i), 0);
1632  }
1633  reader->GenerateGlobalElementIdArrayOn();
1634  reader->GenerateGlobalNodeIdArrayOn();
1635  reader->Update();
1636  return reader;
1637 }
1638 
1640  _nodeSets.clear();
1641  auto mesh = this->getGeoMesh().mesh;
1642  auto link = this->getGeoMesh().link;
1643  _physGrpName = link;
1644  if (!mesh->GetCellData()->HasArray(getExoIdArrName())) {
1645  auto elemBlockIdArr = vtkSmartPointer<vtkIntArray>::New();
1646  elemBlockIdArr->SetName(getExoIdArrName());
1647  auto linkArr = mesh->GetCellData()->GetArray(link.c_str());
1648  if (!link.empty() && linkArr) {
1649  elemBlockIdArr->Allocate(mesh->GetNumberOfCells());
1650  for (vtkIdType i = 0; i < mesh->GetNumberOfCells(); ++i) {
1651  elemBlockIdArr->InsertNextValue(
1652  static_cast<int>(linkArr->GetComponent(i, 0)));
1653  }
1654  } else {
1655  elemBlockIdArr->SetNumberOfValues(mesh->GetNumberOfCells());
1656  elemBlockIdArr->FillComponent(0, 1);
1657  }
1658  mesh->GetCellData()->AddArray(elemBlockIdArr);
1659  }
1660  resetElemBlocks();
1661  auto gm = getGeoMesh();
1662  gm.findSide2OrigCell();
1663  setGeoMesh(gm);
1664  setSideSetObjId();
1665 }
1666 
1668  _elemBlocks.clear();
1669  _elemBlockPropNames.clear();
1670  if (!_physGrpName.empty()) {
1672  }
1673  std::map<std::pair<int, VTKCellType>, int> old2newElemBlock;
1674  auto mesh = getGeoMesh().mesh;
1675  auto elemBlockIds = vtkIntArray::SafeDownCast(
1676  mesh->GetCellData()->GetAbstractArray(getExoIdArrName()));
1677  vtkIdType i = 0;
1678  auto nextElemBlockId = nemAux::leastUnusedKey(_elemBlocks);
1679  for (auto it =
1680  vtkSmartPointer<vtkCellIterator>::Take(mesh->NewCellIterator());
1681  !it->IsDoneWithTraversal(); it->GoToNextCell()) {
1682  auto oldElemBlockId = elemBlockIds->GetValue(i);
1683  auto cellType = static_cast<VTKCellType>(it->GetCellType());
1684  auto inserted =
1685  old2newElemBlock.insert({{oldElemBlockId, cellType}, nextElemBlockId});
1686  if (inserted.second) {
1687  auto &elemBlock = _elemBlocks[nextElemBlockId];
1689  if (!_physGrpName.empty()) {
1690  elemBlock.properties[_physGrpName] = oldElemBlockId;
1691  }
1692  nextElemBlockId = nemAux::leastUnusedKey(_elemBlocks);
1693  }
1694  elemBlockIds->SetValue(i, inserted.first->second);
1695  ++i;
1696  }
1697 }
1698 
1699 void exoGeoMesh::resetNodeSetPoints(vtkIdTypeArray *nodeMap) {
1700  for (auto &nodeSet : _nodeSets) {
1701  std::unordered_set<vtkIdType> set;
1702  for (const auto &node : nodeSet.second.nodes) {
1703  set.insert(nodeMap ? nodeMap->GetValue(node) : node);
1704  }
1705  nodeSet.second.nodes.assign(set.begin(), set.end());
1706  }
1707 }
1708 
1710  _sideSetNames.clear();
1711  auto gm = getGeoMesh();
1712  auto mesh = gm.mesh;
1713  auto link = gm.link;
1714  auto sideSet = gm.sideSet;
1715  if (sideSet.sides) {
1716  auto sideSetObjId = vtkIntArray::FastDownCast(
1717  sideSet.sides->GetCellData()->GetAbstractArray(getExoIdArrName()));
1718  if (sideSetObjId) {
1719  for (vtkIdType i = 0; i < sideSet.sides->GetNumberOfCells(); ++i) {
1720  _sideSetNames[sideSetObjId->GetValue(i)];
1721  }
1722  } else {
1723  sideSetObjId = vtkIntArray::New();
1724  sideSetObjId->SetName(getExoIdArrName());
1725  sideSetObjId->Allocate(sideSet.sides->GetNumberOfCells());
1726  sideSet.sides->GetCellData()->AddArray(sideSetObjId);
1727  auto sideSetEntities = gm.sideSet.getGeoEntArr();
1728  auto nameArr = sideSet.getSideSetNames();
1729  // Exodus II requires IDs to be positive, which is not always the case for
1730  // other mesh types, hence use of extra array
1731  std::map<int, int> geoEnt2ExoId;
1732  int nextSideSetId = 1;
1733  for (vtkIdType i = 0; i < sideSet.sides->GetNumberOfCells(); ++i) {
1734  int geoEnt = sideSetEntities->GetValue(i);
1735  // If geoEnt is not found in geoEnt2ExoID, call to operator[] to sets
1736  // geoEnt2ExoId[geoEnt] to 0 (which is used as sentinel value)
1737  auto &exoID = geoEnt2ExoId[geoEnt];
1738  if (exoID == 0) {
1739  exoID = nextSideSetId;
1740  auto emplaceIter = _sideSetNames.emplace(exoID, std::string{});
1741  if (emplaceIter.second && nameArr && geoEnt >= 0 &&
1742  geoEnt < nameArr->GetNumberOfValues()) {
1743  emplaceIter.first->second = nameArr->GetValue(geoEnt);
1744  }
1745  ++nextSideSetId;
1746  }
1747  sideSetObjId->InsertNextValue(exoID);
1748  }
1749  sideSetObjId->Delete();
1750  }
1751  }
1752 }
1753 
1754 } // namespace MSH
1755 } // namespace NEM
std::string _title
Definition: exoGeoMesh.H:387
virtual void report(std::ostream &out) const =0
Print a report about the mesh.
Definition: geoMeshBase.C:97
std::set< std::string > _elemBlockPropNames
Definition: exoGeoMesh.H:391
void setTitle(const std::string &title)
Set the title of the exoGeoMesh (used for writing out to EXODUS II file)
Definition: exoGeoMesh.C:825
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).
std::pair< std::vector< vtkIdType >, std::vector< int > > getSideSet(int sideSetId) const
Get sides in a side set.
Definition: exoGeoMesh.C:1123
int addSideSet(const std::vector< vtkIdType > &elements, const std::vector< int > &sides, const std::string &name="")
Add a new side set.
Definition: exoGeoMesh.C:1145
std::vector< std::pair< vtkIdType, std::vector< double > > > nodes
Each entry stores (node ID in .inp file, coordinates)
Definition: inpGeoMesh.C:139
vtkSmartPointer< vtkUnstructuredGrid > mesh
Definition: geoMeshBase.H:419
int getNumSideSets() const
Get number of side sets.
Definition: exoGeoMesh.C:1088
geoMeshBase * Read(const std::string &fileName)
Read a mesh from file.
A leastUnusedKey(const std::map< A, B > &map, A min=1)
virtual void reconstructGeo()
Construct the geometry from the mesh alone.
int getElemBlockProperty(const std::string &propName, int blockId) const
Get the value of an element block property on a block.
Definition: exoGeoMesh.C:1053
static constexpr auto GEO_ENT_DEFAULT_NAME
Definition: geoMeshBase.H:446
std::map< int, std::string > _sideSetNames
Definition: exoGeoMesh.H:389
std::map< std::string, std::vector< vtkIdType > > nodeSets
Map from NSET name to node ids (ids given by .inp file)
Definition: inpGeoMesh.C:152
std::vector< int > getNodeSetIds() const
Get the IDs of all node sets.
Definition: exoGeoMesh.C:1239
geoMeshBase * New(MeshType meshType)
Create a new mesh object.
void scaleNodes(double scale)
Scale point locations.
Definition: exoGeoMesh.C:1457
void resetElemBlocks()
Clears element block names, element block properties.
Definition: exoGeoMesh.C:1667
void setSideSetName(int id, const std::string &name)
Set the name of a side set (used for writing out to EXODUS II file)
Definition: exoGeoMesh.C:1104
void resetNative() override
Definition: exoGeoMesh.C:1639
int getElemBlockId(vtkIdType cellIdx) const
Get the element block ID of a cell.
Definition: exoGeoMesh.C:886
void write(const std::string &fileName) override
Write exoGeoMesh out to a file.
Definition: exoGeoMesh.C:382
const std::string & getSideSetName(int id) const
Get the name of a side set (used for writing out to EXODUS II file)
Definition: exoGeoMesh.C:1094
const std::string & getPhysGrpPropertyName() const
Get the name of the element block property that maps element blocks to physical groups (if empty...
Definition: exoGeoMesh.C:1275
void setGeoMesh(const geoMeshBase::GeoMesh &geoMesh)
Set the underlying geometry object.
Definition: geoMeshBase.H:538
std::vector< std::string > getElemBlockPropertyNames() const
Get the names of all block properties.
Definition: exoGeoMesh.C:1047
const std::vector< vtkIdType > & getNodeSet(int nodeSetId) const
Get the nodes of a node set.
Definition: exoGeoMesh.C:1248
std::string find_ext(const std::string &fname)
std::map< int, elemBlock > _elemBlocks
Definition: exoGeoMesh.H:388
const std::string & getElemBlockName(int id) const
Get the name of an element block (used for writing out to EXODUS II file)
Definition: exoGeoMesh.C:840
int addElemBlock(vtkUnstructuredGrid *elements, const std::string &name="", float tol=1e-15f)
Add a new element block to the mesh.
Definition: exoGeoMesh.C:901
std::map< int, nodeSet > _nodeSets
Definition: exoGeoMesh.H:390
void setElemBlockProperty(const std::string &propName, int blockId, int value)
Set the value of an element block property on a block.
Definition: exoGeoMesh.C:1073
void addCellsToBlock(vtkUnstructuredGrid *elements, int blockId, float tol=1e-15f)
Add cells to an existing element block.
Definition: exoGeoMesh.C:938
void setElemBlockName(int id, const std::string &name)
Set the name of an element block (used for writing out to EXODUS II file)
Definition: exoGeoMesh.C:850
std::map< int, std::string > getElemBlockNames() const
Get all element block names.
Definition: exoGeoMesh.C:829
bool addElemBlockProperty(const std::string &propName)
Add a property to element blocks (with default value 0 on all element blocks)
Definition: exoGeoMesh.C:1040
std::vector< int > getSideSetIds() const
Get all side set IDs.
Definition: exoGeoMesh.C:1114
void setSideSetObjId()
Sets the (Exodus) side set IDs of each cell in the GeoMesh.sideSet by creating a side set for each pa...
Definition: exoGeoMesh.C:1709
std::map< int, std::string > getNodeSetNames() const
Get all node set names.
Definition: exoGeoMesh.C:1209
const std::string & getTitle() const
Get the title of the exoGeoMesh (used for writing out to EXODUS II file)
Definition: exoGeoMesh.C:823
VTKCellType cellType
Definition: inpGeoMesh.C:129
vtkSmartPointer< vtkPolyData > sides
Cells represent edges/faces of some GeoMesh.
Definition: geoMeshBase.H:399
std::shared_ptr< meshBase > mesh
void setNodeSetName(int id, const std::string &name)
Set the name of a node set (used for writing out to EXODUS II file)
Definition: exoGeoMesh.C:1229
static GeoMesh exoReader2GM(vtkSmartPointer< vtkExodusIIReader > reader)
Definition: exoGeoMesh.C:1472
void setPhysGrpPropertyName(const std::string &physGrpName)
Sets the name of the element block property that maps element blocks to physical groups (if empty...
Definition: exoGeoMesh.C:1279
std::array< int, 2 > sides
vtkSmartPointer< vtkIntArray > getGeoEntArr() const
Definition: geoMeshBase.C:252
std::vector< vtkIdType > getElemBlock(int blockId) const
Get (0-based) indices of the cells that are in a specific element block.
Definition: exoGeoMesh.C:869
int reassignCells(const std::vector< vtkIdType > &cells, int blockId=-1, const std::string &elemBlockName={})
Reassign cells to a different element block.
Definition: exoGeoMesh.C:1001
A concrete implementation of geoMeshBase representing an Exodus II mesh.
Definition: exoGeoMesh.H:56
vtkStandardNewMacro(exoGeoMesh)
Definition: exoGeoMesh.C:247
const std::map< int, std::string > & getSideSetNames() const
Get all side set names.
Definition: exoGeoMesh.C:1090
void stitch(const exoGeoMesh &otherGM, float tol=1e-15f)
Stitch another exoGeoMesh object onto this one.
Definition: exoGeoMesh.C:1301
std::vector< vtkIdType > nodes
Definition: exoGeoMesh.H:383
int addNodeSet(const std::vector< vtkIdType > &nodes, const std::string &name="")
Add a new node set.
Definition: exoGeoMesh.C:1257
~exoGeoMesh() override
Definition: exoGeoMesh.C:380
exoGeoMesh()
Reads an EXODUS II file into an exoGeoMesh object.
Definition: exoGeoMesh.C:257
std::vector< int > getElemBlockIds() const
Get all element block IDs.
Definition: exoGeoMesh.C:860
std::string _physGrpName
Definition: exoGeoMesh.H:392
int getNumNodeSets() const
Get number of node sets.
Definition: exoGeoMesh.C:1207
static vtkSmartPointer< vtkExodusIIReader > getExoReader(const std::string &fileName, int timeStep=1)
Definition: exoGeoMesh.C:1605
void reconstructGeo() override
Construct the geometry from the mesh alone.
Definition: exoGeoMesh.C:796
int getNumElemBlocks() const
Get number of element blocks.
Definition: exoGeoMesh.C:827
std::map< std::string, int > properties
Definition: exoGeoMesh.H:379
void resetNodeSetPoints(vtkIdTypeArray *nodeMap=nullptr)
Renumbers the nodes in all node sets based on nodeMap if provided.
Definition: exoGeoMesh.C:1699
void takeGeoMesh(geoMeshBase *otherGeoMesh) override
Take the GeoMesh of another geoMeshBase.
Definition: exoGeoMesh.C:778
abstract class to specify geometry and mesh data
Definition: geoMeshBase.H:102
static const char * getExoIdArrName()
Definition: exoGeoMesh.C:253
int getDimension() const
Get dimension of mesh.
Definition: geoMeshBase.C:436
const std::string & getNodeSetName(int id) const
Get the name of a node set (used for writing out to EXODUS II file)
Definition: exoGeoMesh.C:1219
const GeoMesh & getGeoMesh() const
Get the underlying geometry object.
Definition: geoMeshBase.H:533
void report(std::ostream &out) const override
Write a summary of this exoGeoMesh to out.
Definition: exoGeoMesh.C:770
virtual void takeGeoMesh(geoMeshBase *otherGeoMesh)
Take the GeoMesh of another geoMeshBase.
Definition: geoMeshBase.C:104