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.
inpGeoMesh.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/inpGeoMesh.H"
34 
35 #include <algorithm>
36 #include <cassert>
37 #include <fstream>
38 #include <map>
39 #include <set>
40 #include <sstream>
41 #include <string>
42 #include <utility>
43 
44 #include <jsoncons/json.hpp>
45 #include <jsoncons_ext/csv/csv.hpp>
46 #ifdef HAVE_GMSH
47 # include <gmsh.h>
48 #endif
49 
50 #include "AuxiliaryFunctions.H"
51 
52 namespace NEM {
53 namespace MSH {
54 
55 namespace {
56 
57 constexpr std::array<std::tuple<VTKCellType, const char *, int>, 5> vtk2inp{
58  {{VTK_TRIANGLE, "CPS3", 2},
59  {VTK_QUAD, "CPS4", 2},
60  {VTK_TETRA, "C3D4", 3},
61  {VTK_HEXAHEDRON, "C3D8", 3},
62  {VTK_WEDGE, "C3D6", 3}}};
63 
64 int inpSide2vtkSide(VTKCellType cellType, int inpSide) {
65  switch (cellType) {
66  case VTK_TETRA:
67  switch (inpSide) {
68  case 1: return 3;
69  case 2: return 0;
70  case 3: return 1;
71  case 4: return 2;
72  default: return -1;
73  }
74  case VTK_HEXAHEDRON:
75  switch (inpSide) {
76  case 1: return 4;
77  case 2: return 5;
78  case 3: return 2;
79  case 4: return 1;
80  case 5: return 3;
81  case 6: return 0;
82  default: return -1;
83  }
84  default: return inpSide - 1;
85  }
86 }
87 
88 int vtkSide2inpSide(VTKCellType cellType, int vtkSide) {
89  switch (cellType) {
90  case VTK_TETRA:
91  switch (vtkSide) {
92  case 0: return 2;
93  case 1: return 3;
94  case 2: return 4;
95  case 3: return 1;
96  default: return -1;
97  }
98  case VTK_HEXAHEDRON:
99  switch (vtkSide) {
100  case 0: return 6;
101  case 1: return 4;
102  case 2: return 3;
103  case 3: return 5;
104  case 4: return 1;
105  case 5: return 2;
106  default: return -1;
107  }
108  default: return vtkSide + 1;
109  }
110 }
111 
112 class InpParser {
113  public:
114  InpParser() = default;
115 
116  /**
117  * @brief Class to store the result of parser a .inp file
118  */
119  struct Mesh {
120  struct Elem {
121  Elem(vtkIdType id, VTKCellType cellType, std::vector<vtkIdType> points)
122  : id(id), cellType(cellType), points(std::move(points)) {}
123  Elem(vtkIdType id, VTKCellType cellType)
124  : id(id), cellType(cellType), points() {}
125  /**
126  * @brief id in .inp file
127  */
128  vtkIdType id;
129  VTKCellType cellType;
130  /**
131  * @brief points given by id in .inp file
132  */
133  std::vector<vtkIdType> points;
134  };
135  int maxDim{0};
136  /**
137  * @brief Each entry stores (node ID in .inp file, coordinates)
138  */
139  std::vector<std::pair<vtkIdType, std::vector<double>>> nodes{};
140  /**
141  * @brief If the ELEMENT keyword line contains ELSET=name, then the element
142  * is stored in elems[name], otherwise, stored in elems[std::string{}].
143  */
144  std::map<std::string, std::vector<Elem>> elems{};
145  /**
146  * @brief Map from ELSET name to element ids (ids given by .inp file)
147  */
148  std::map<std::string, std::vector<vtkIdType>> elSets{};
149  /**
150  * @brief Map from NSET name to node ids (ids given by .inp file)
151  */
152  std::map<std::string, std::vector<vtkIdType>> nodeSets{};
153  /**
154  * @brief Map from SURFACE name to (element id, side) (id and side both use
155  * .inp IDs)
156  */
157  std::map<std::string, std::vector<std::pair<vtkIdType, int>>> surfaces{};
158  };
159 
160  /**
161  * @brief Parse line (ended by newline in file)
162  * @param line line of file
163  */
164  void parseLine(const std::string &line) {
165  if (line.at(0) == '*') {
166  if (!this->setState(parseKwLine(line))) {
167  return; // Do nothing if comment line
168  };
169  } else {
170  if (this->currContinue == Continue::KEYWORD) {
171  bool continueNextLine{false};
172  auto parsed = parseCSV(line);
173  for (const auto &field : parsed[0].array_range()) {
174  if (field.is_null()) {
175  continueNextLine = true;
176  break;
177  } else if (field.is_array()) {
178  this->currKwParams.emplace(field[0].as_string(),
179  field[1].as_string());
180  } else {
181  this->currKwParams.emplace(field.as_string(), std::string{});
182  }
183  }
184  this->currContinue =
185  continueNextLine ? Continue::KEYWORD : Continue::NONE;
186  } else {
187  // Pretend to parse sections w/ unknown keywords
188  if (this->currKw == Keyword::UNKNOWN) { return; }
189  jsoncons::json parsed = parseCSV(line);
190  auto parsedRange = parsed.array_range();
191  auto iter = parsedRange.begin();
192  bool continueNextLine = (parsedRange.end() - 1)->is_null();
193  if (this->currKw == Keyword::NODE) {
194  if (this->currContinue != Continue::DATA) {
195  this->currId = (iter++)->as<vtkIdType>();
196  if (!this->currSet.empty()) {
197  this->mesh_.nodeSets[currSet].emplace_back(this->currId);
198  }
199  this->mesh_.nodes.emplace_back();
200  auto &back = this->mesh_.nodes.back();
201  back.first = this->currId;
202  back.second.reserve(3);
203  }
204  auto &back = this->mesh_.nodes.back();
205  for (; iter != parsedRange.end(); ++iter) {
206  if (!iter->is_null()) {
207  back.second.emplace_back(iter->as_double());
208  }
209  }
210  } else if (this->currKw == Keyword::ELEMENT) {
211  if (this->currCellType != VTK_EMPTY_CELL) {
212  auto &elSet = this->mesh_.elems[this->currSet];
213  if (this->currContinue != Continue::DATA) {
214  this->currId = (iter++)->as<vtkIdType>();
215  elSet.emplace_back(this->currId, this->currCellType);
216  }
217  auto &back = elSet.back();
218  for (; iter != parsedRange.end(); ++iter) {
219  if (!iter->is_null()) {
220  back.points.emplace_back(iter->as<vtkIdType>());
221  }
222  }
223  }
224  } else if (this->currKw == Keyword::SURFACE) {
225  auto iterType = this->currKwParams.find("TYPE");
226  if (iterType == this->currKwParams.end() ||
227  iterType->second != "ELEMENT") {
228  auto &surf = this->mesh_.surfaces[this->currSet];
229  auto iterElSet = this->mesh_.elSets.find(iter->as_string());
230  if (iterElSet != this->mesh_.elSets.end()) {
231  ++iter;
232  auto side = std::stoi(iter->as_string().substr(1));
233  for (const auto &elem : iterElSet->second) {
234  surf.emplace_back(elem, side);
235  }
236  } else {
237  auto elem = (iter++)->as<vtkIdType>();
238  auto side = std::stoi(iter->as_string().substr(1));
239  surf.emplace_back(elem, side);
240  }
241  }
242  } else if (this->currKw == Keyword::NSET ||
243  this->currKw == Keyword::ELSET) {
244  auto iterGenerate = this->currKwParams.find("GENERATE");
245  auto &set = this->currKw == Keyword::NSET
246  ? this->mesh_.nodeSets[this->currSet]
247  : this->mesh_.elSets[this->currSet];
248  if (iterGenerate != this->currKwParams.end()) {
249  auto lower = (iter++)->as<vtkIdType>();
250  auto upper = (iter++)->as<vtkIdType>();
251  auto stride = iter == parsedRange.end() ? 1 : (iter++)->as<int>();
252  for (auto i = lower; i <= upper; i += stride) {
253  set.emplace_back(i);
254  }
255  } else {
256  for (; iter != parsedRange.end(); ++iter) {
257  if (!iter->is_null()) {
258  auto iterOtherSet =
259  this->currKw == Keyword::NSET
260  ? this->mesh_.nodeSets.find(iter->as_string())
261  : this->mesh_.elSets.find(iter->as_string());
262  if (iterOtherSet != (this->currKw == Keyword::NSET
263  ? this->mesh_.nodeSets.end()
264  : this->mesh_.elSets.end())) {
265  set.insert(set.end(), iterOtherSet->second.begin(),
266  iterOtherSet->second.end());
267  } else {
268  set.emplace_back(iter->as<vtkIdType>());
269  }
270  }
271  }
272  }
273  }
274  this->currContinue = continueNextLine ? Continue::DATA : Continue::NONE;
275  }
276  }
277  };
278 
279  private:
280  Mesh mesh_{};
281 
282  public:
283  Mesh &getMesh() { return mesh_; }
284 
285  private:
286  enum class Keyword {
287  COMMENT,
288  NODE,
289  ELEMENT,
290  ELSET,
291  NSET,
292  SURFACE,
293  UNKNOWN,
294  };
295 
296  // Current data, that has not been registered in the above data members
297  // Includes data from the keyword line
298  /**
299  * @brief current keyword
300  */
301  Keyword currKw{Keyword::UNKNOWN};
302  /**
303  * @brief Params in current keyword line
304  */
305  std::map<std::string, std::string> currKwParams{};
306  /**
307  * @brief TYPE, if inside data lines following ELEMENT keyword
308  */
309  VTKCellType currCellType{VTK_EMPTY_CELL};
310  enum class Continue { NONE, KEYWORD, DATA };
311  /**
312  * @brief If previous line was keyword line that is continued, KEYWORD. If
313  * previous line was data line that is continued, DATA. Otherwose, NONE
314  */
316  /**
317  * @brief If previous line was data line that is continued and current keyword
318  * is NODE or ELEMEMT, the ID of the node/cell being processed. Otherwise,
319  * undefined.
320  */
321  vtkIdType currId{-1};
322  /**
323  * @brief Name of ELSET/NSET/Surface (empty string for ELEMENT keyword that
324  * does not specify ELSET)
325  */
326  std::string currSet{};
327 
328  /**
329  * @brief Parse keyword line
330  * @param line Line of file
331  * @return (Keyword, All other parameters in keyword line, whether or not line
332  * is continued on next line)
333  */
334  static std::tuple<Keyword, std::map<std::string, std::string>, bool>
335  parseKwLine(const std::string &line) {
336  static constexpr std::array<std::pair<Keyword, const char *>, 5> matchArr{
337  {{Keyword::NODE, "*NODE"},
338  {Keyword::ELEMENT, "*ELEMENT"},
339  {Keyword::ELSET, "*ELSET"},
340  {Keyword::NSET, "*NSET"},
341  {Keyword::SURFACE, "*SURFACE"}}};
342  if (line.rfind("**", 0) == 0) {
343  return {Keyword::COMMENT, {}, false};
344  } else {
345  Keyword kw{Keyword::UNKNOWN};
346  std::map<std::string, std::string> params{};
347  auto parsed = parseCSV(line);
348  auto range = parsed.array_range();
349  auto iter = range.begin();
350  auto keywordStr = iter->as_string();
351  nemAux::toUpper(keywordStr);
352  for (const auto &match : matchArr) {
353  if (keywordStr == match.second) {
354  kw = match.first;
355  break;
356  }
357  }
358  ++iter;
359  bool continueNextLine = (range.end() - 1)->is_null();
360  for (; iter != range.end(); ++iter) {
361  if (!iter->is_null()) {
362  if (iter->is_array()) {
363  auto key = iter->at(0).as_string();
364  nemAux::toUpper(key);
365  params.emplace(std::move(key), iter->at(1).as_string());
366  } else {
367  auto key = iter->as_string();
368  nemAux::toUpper(key);
369  params[std::move(key)];
370  }
371  }
372  }
373  return {kw, std::move(params), continueNextLine};
374  }
375  };
376 
377  /**
378  * @brief Sets the parser's state based on output of @c parseKwLine
379  * @param kwLine output of @c parseKwLine
380  * @return whether or not line is continued on next line
381  */
382  bool setState(
383  std::tuple<Keyword, std::map<std::string, std::string>, bool> kwLine) {
384  if (std::get<0>(kwLine) != Keyword::COMMENT) {
385  this->currKw = std::get<0>(kwLine);
386  this->currKwParams = std::move(std::get<1>(kwLine));
387  this->currSet.clear();
388  this->currCellType = VTK_EMPTY_CELL;
389  if (this->currKw == Keyword::ELEMENT) {
390  auto &cellTypeStr = this->currKwParams.at("TYPE");
391  auto cellTypeIter = std::find_if(
392  vtk2inp.begin(), vtk2inp.end(),
393  [&cellTypeStr](const decltype(vtk2inp)::value_type &x) {
394  auto cellType = std::get<1>(x);
395  return std::equal(
396  cellType, cellType + std::char_traits<char>::length(cellType),
397  cellTypeStr.begin());
398  });
399  if (cellTypeIter != vtk2inp.end()) {
400  auto dim = std::get<2>(*cellTypeIter);
401  this->currCellType = std::get<0>(*cellTypeIter);
402  if (this->mesh_.maxDim < dim) {
403  this->mesh_.maxDim = dim;
404  this->mesh_.elems.clear();
405  } else if (this->mesh_.maxDim > dim) {
406  // Ignore these elements
407  this->currCellType = VTK_EMPTY_CELL;
408  }
409  } else {
410  std::cerr << "Unsupported cell type " << cellTypeStr << '\n';
411  }
412  auto elSetParamIter = this->currKwParams.find("ELSET");
413  if (elSetParamIter != this->currKwParams.end()) {
414  this->currSet = elSetParamIter->second;
415  }
416  }
417  if (this->currKw == Keyword::ELSET) {
418  this->currSet = this->currKwParams.at("ELSET");
419  }
420  if (this->currKw == Keyword::NODE) {
421  auto nsetIter = this->currKwParams.find("NSET");
422  if (nsetIter != this->currKwParams.end()) {
423  this->currSet = nsetIter->second;
424  }
425  }
426  if (this->currKw == Keyword::NSET) {
427  this->currSet = this->currKwParams.at("NSET");
428  }
429  if (this->currKw == Keyword::SURFACE) {
430  this->currSet = this->currKwParams.at("NAME");
431  }
432  this->currContinue =
433  std::get<2>(kwLine) ? Continue::KEYWORD : Continue::NONE;
434  this->currId = -1;
435  return true;
436  } else {
437  return false;
438  }
439  }
440 
441  /**
442  * @brief Extract comma-separated-values
443  * @param line data line
444  * @return JSON array - if line in file is continued, last entry will be null.
445  * Keyword parameters split as arrays
446  */
447  static jsoncons::json parseCSV(const std::string &line) {
448  return jsoncons::csv::decode_csv<jsoncons::json>(
449  line, jsoncons::csv::basic_csv_options<char>{}
450  .assume_header(false)
451  .subfield_delimiter('=')
452  .unquoted_empty_value_is_null(true)
453  .trim(true))[0];
454  }
455 };
456 
457 template <typename T>
458 void writeCheckWidth(std::ostream &outStream, const T &data,
459  const std::string &delimiter = ", ",
460  std::size_t maxWidth = 80, std::size_t maxEntries = 16) {
461  std::size_t width = 0;
462  std::size_t entries = 0;
463  auto lenDelimiter = delimiter.size();
464  for (auto iter = data.begin(); iter != data.end(); ++iter) {
465  bool last = iter == data.end() - 1;
466  width += iter->size() + (last ? 0 : lenDelimiter);
467  entries += 1;
468  if (width > maxWidth || entries > maxEntries) {
469  outStream << "\n";
470  width = iter->size() + (last ? 0 : lenDelimiter);
471  entries = 1;
472  }
473  outStream << *iter;
474  if (last) {
475  outStream << '\n';
476  } else {
477  outStream << ", ";
478  }
479  }
480 }
481 
482 void writeCells(std::ostream &outStream, vtkDataSet *data,
483  const std::string &geo, const std::string &groupArrName) {
484  // ELEMENT
485  std::map<std::pair<VTKCellType, int>, std::vector<vtkIdType>> elemBlocks{};
486  auto entArr = data->GetCellData()->GetArray(groupArrName.c_str());
487  for (vtkIdType i = 0; i < data->GetNumberOfCells(); ++i) {
488  elemBlocks[{static_cast<VTKCellType>(data->GetCellType(i)),
489  entArr ? static_cast<int>(entArr->GetComponent(i, 0)) : 0}]
490  .emplace_back(i);
491  }
492 #ifdef HAVE_GMSH
493  if (!geo.empty()) {
494  gmsh::model::setCurrent(geo);
495  }
496 #endif
497  for (const auto &elems : elemBlocks) {
498  auto cellType = elems.first.first;
499  auto inpType =
500  std::find_if(vtk2inp.begin(), vtk2inp.end(),
501  [cellType](const decltype(vtk2inp)::value_type &x) {
502  return std::get<0>(x) == cellType;
503  });
504  if (inpType == vtk2inp.end()) {
505  std::cerr << "Unsupported element type" << cellType << '\n';
506  continue;
507  }
508  std::string elSet{};
509 #ifdef HAVE_GMSH
510  if (!geo.empty()) {
511  gmsh::model::getEntityName(gmsh::model::getDimension(),
512  elems.first.second, elSet);
513  }
514 #endif
515  outStream << "*ELEMENT, TYPE=" << std::get<1>(*inpType);
516  if (!elSet.empty()) { outStream << " ELSET=" << elSet; }
517  outStream << '\n';
518  for (const auto &cellIdx : elems.second) {
519  auto cell = data->GetCell(cellIdx);
520  std::vector<std::string> outStr{};
521  outStr.reserve(cell->GetNumberOfPoints() + 1);
522  outStr.emplace_back(std::to_string(cellIdx + 1));
523  if (cellType == VTK_WEDGE) {
524  outStr.emplace_back(std::to_string(cell->GetPointId(0) + 1));
525  outStr.emplace_back(std::to_string(cell->GetPointId(2) + 1));
526  outStr.emplace_back(std::to_string(cell->GetPointId(1) + 1));
527  outStr.emplace_back(std::to_string(cell->GetPointId(3) + 1));
528  outStr.emplace_back(std::to_string(cell->GetPointId(5) + 1));
529  outStr.emplace_back(std::to_string(cell->GetPointId(4) + 1));
530  } else {
531  for (vtkIdType k = 0; k < cell->GetNumberOfPoints(); ++k) {
532  outStr.emplace_back(std::to_string(cell->GetPointId(k) + 1));
533  }
534  }
535  writeCheckWidth(outStream, outStr);
536  }
537  }
538 }
539 
540 #ifdef HAVE_GMSH
541 /**
542  * @brief Run @p func for each appearance of a cell in @p dataSet in each
543  * physical group of the current gmsh model
544  * @tparam Func Callable type like with signature void (const std::string &,
545  * vtkIdType)
546  * @param dataSet dataSet with cells
547  * @param entArr array mapping cells in @p dataSet to gmsh entity
548  * @param dim dimension of gmsh physical group
549  * @param func Callable object signature like void (const std::string &,
550  * vtkIdType)
551  */
552 template <typename Func>
553 void eachGmshPhyGroup(vtkDataSet *dataSet, vtkDataArray *entArr, int dim,
554  Func &&func) {
555  std::map<int, std::vector<std::string>> ent2PhysGroup;
556  {
557  gmsh::vectorpair dimTags;
558  gmsh::model::getEntities(dimTags, dim);
559  for (const auto &dimTag : dimTags) {
560  std::vector<int> phyGrpIds{};
561  gmsh::model::getPhysicalGroupsForEntity(dimTag.first, dimTag.second,
562  phyGrpIds);
563  for (const auto &phyGrp : phyGrpIds) {
564  auto &phyGrpNameVec = ent2PhysGroup[dimTag.second];
565  phyGrpNameVec.emplace_back();
566  auto &phyGrpName = phyGrpNameVec.back();
567  gmsh::model::getPhysicalName(dimTag.first, phyGrp, phyGrpName);
568  if (phyGrpName.empty()) {
569  if (dim == 3) {
570  phyGrpName = "PhysicalVolume";
571  } else if (dim == 2) {
572  phyGrpName = "PhysicalSurface";
573  } else {
574  phyGrpName = "PhysicalGroup";
575  }
576  phyGrpName += std::to_string(phyGrp);
577  gmsh::model::setPhysicalName(dimTag.first, phyGrp, phyGrpName);
578  }
579  }
580  }
581  }
582  for (vtkIdType i = 0; i < dataSet->GetNumberOfCells(); ++i) {
583  for (const auto &phyGrp :
584  ent2PhysGroup[static_cast<int>(entArr->GetComponent(i, 0))]) {
585  func(phyGrp, i);
586  }
587  }
588 }
589 #endif
590 
591 } // namespace
592 
594 
595 inpGeoMesh *inpGeoMesh::Read(const std::string &fileName) {
596  return new inpGeoMesh(fileName);
597 }
598 
599 inpGeoMesh::inpGeoMesh(const std::string &fileName)
600  : inpGeoMesh(inp2GM(fileName)) {}
601 
602 inpGeoMesh::inpGeoMesh(std::pair<GeoMesh, InpSets> mesh)
603  : geoMeshBase(std::move(mesh.first)), inpSets_(std::move(mesh.second)) {}
604 
606 
607 void inpGeoMesh::write(const std::string &fileName) {
608  const auto &gm = getGeoMesh();
609  auto mesh = gm.mesh;
610  std::ofstream outFile(fileName);
611  { // NODE
612  outFile << "*NODE\n";
613  auto points = mesh->GetPoints();
614  for (vtkIdType i = 0; i < points->GetNumberOfPoints(); ++i) {
615  double *point = points->GetPoint(i);
616  std::array<std::string, 4> outStr{
617  std::to_string(i + 1), std::to_string(point[0]),
618  std::to_string(point[1]), std::to_string(point[2])};
619  writeCheckWidth(outFile, outStr);
620  }
621  }
622  // mesh (ELEMENT)
623  writeCells(outFile, mesh, gm.geo, gm.link);
624  // ELSET
625  for (const auto &elSet : this->inpSets_.elSets) {
626  if (!elSet.second.empty()) {
627  outFile << "*ELSET, ELSET=" << elSet.first << '\n';
628  std::vector<std::string> outStr;
629  outStr.reserve(elSet.second.size());
630  std::transform(elSet.second.begin(), elSet.second.end(),
631  std::back_inserter(outStr),
632  [](int x) { return std::to_string(x + 1); });
633  writeCheckWidth(outFile, outStr);
634  }
635  }
636  // NSET
637  for (const auto &nodeSet : this->inpSets_.nodeSets) {
638  if (!nodeSet.second.empty()) {
639  outFile << "*NSET, NSET=" << nodeSet.first << '\n';
640  std::vector<std::string> outStr;
641  outStr.reserve(nodeSet.second.size());
642  std::transform(nodeSet.second.begin(), nodeSet.second.end(),
643  std::back_inserter(outStr),
644  [](int x) { return std::to_string(x + 1); });
645  writeCheckWidth(outFile, outStr);
646  }
647  }
648  // SURFACE
649  if (gm.sideSet.sides) {
650  auto origCellArr = gm.sideSet.getOrigCellArr();
651  auto cellFaceArr = gm.sideSet.getCellFaceArr();
652  for (const auto &surface : this->inpSets_.surfaces) {
653  if (!surface.second.empty()) {
654  outFile << "*SURFACE, NAME=" << surface.first << ", TYPE=ELEMENT\n";
655  for (const auto &id : surface.second) {
656  auto origCellIdx = origCellArr->GetTypedComponent(id, 0);
657  auto origCellType =
658  static_cast<VTKCellType>(mesh->GetCellType(origCellIdx));
659  outFile << origCellIdx + 1 << ", S"
660  << vtkSide2inpSide(origCellType,
661  cellFaceArr->GetTypedComponent(id, 0))
662  << '\n';
663  }
664  }
665  }
666  }
667 }
668 
669 void inpGeoMesh::report(std::ostream &out) const {
670  this->geoMeshBase::report(out);
671 }
672 
675  this->resetNative();
676 }
677 
679  this->inpSets_.nodeSets.clear();
680  this->inpSets_.elSets.clear();
681  this->inpSets_.surfaces.clear();
682  // Default behavior:
683  // Convert mesh entities to element sets and mesh physical groups to element
684  // sets and node sets
685  // Convert sideSet physical groups to surfaces and node sets
686  auto gm = getGeoMesh();
687 #ifdef HAVE_GMSH
688  if (!gm.geo.empty()) {
689  gmsh::model::setCurrent(gm.geo);
690  if (!gm.link.empty() && gm.mesh->GetNumberOfCells() > 0) {
691  eachGmshPhyGroup(
692  gm.mesh, gm.mesh->GetCellData()->GetArray(gm.link.c_str()),
693  gm.mesh->GetCell(0)->GetCellDimension(),
694  [this](const std::string &phyGroup, vtkIdType cellIdx) {
695  this->inpSets_.elSets[phyGroup].emplace(cellIdx);
696  auto cell = this->getGeoMesh().mesh->GetCell(cellIdx);
697  for (vtkIdType i = 0; i < cell->GetNumberOfPoints(); ++i) {
698  this->inpSets_.nodeSets[phyGroup].emplace(cell->GetPointId(i));
699  }
700  });
701  }
702  if (gm.sideSet.sides && gm.sideSet.sides->GetNumberOfCells() > 0) {
703  gm.findSide2OrigCell();
704  eachGmshPhyGroup(
705  gm.sideSet.sides, gm.sideSet.getGeoEntArr(),
706  gm.sideSet.sides->GetCell(0)->GetCellDimension(),
707  [this](const std::string &phyGroup, vtkIdType cellIdx) {
708  this->inpSets_.surfaces[phyGroup].emplace(cellIdx);
709  auto cell = this->getGeoMesh().sideSet.sides->GetCell(cellIdx);
710  for (vtkIdType i = 0; i < cell->GetNumberOfPoints(); ++i) {
711  this->inpSets_.nodeSets[phyGroup].emplace(cell->GetPointId(i));
712  }
713  });
714  }
715  }
716 #endif
717 }
718 
719 std::pair<geoMeshBase::GeoMesh, inpGeoMesh::InpSets> inpGeoMesh::inp2GM(
720  const std::string &fileName) {
722  std::map<std::string, std::set<vtkIdType>> outNSets, outElSets, outSurfaces;
723  SideSet sideSet{};
724  std::string geoName{};
725  std::string linkName{};
726  if (fileName.empty()) { return {{mesh, {}, {}, {}}, {}}; }
727  std::ifstream inFile(fileName);
728  std::string line;
729  InpParser parser{};
730  while (std::getline(inFile, line)) { parser.parseLine(line); }
731  auto parserMesh = parser.getMesh();
732 
733  std::map<vtkIdType, vtkIdType> nodeInpId2GMIdx;
734  if (!parserMesh.nodes.empty()) {
735  vtkNew<vtkPoints> points;
736  points->Allocate(parserMesh.nodes.size());
737  for (const auto &parserNode : parserMesh.nodes) {
738  nodeInpId2GMIdx[parserNode.first] =
739  points->InsertNextPoint(parserNode.second.data());
740  }
741  mesh->SetPoints(points);
742  }
743  auto nodeMapper = [&nodeInpId2GMIdx](vtkIdType x) {
744  return nodeInpId2GMIdx.at(x);
745  };
746  for (const auto &nSet : parserMesh.nodeSets) {
747  auto &outNSet = outNSets[nSet.first];
748  std::transform(nSet.second.begin(), nSet.second.end(),
749  std::inserter(outNSet, outNSet.end()), nodeMapper);
750  }
751  std::map<vtkIdType, vtkIdType> inpId2GMIdx;
752  if (!parserMesh.elems.empty()) {
753  mesh->Allocate(parserMesh.elems.size());
754 #ifdef HAVE_GMSH
756  geoName = "geoMesh_" + nemAux::getRandomString(6);
757  gmsh::model::add(geoName);
758  gmsh::model::setCurrent(geoName);
759 #endif
760  linkName = GEO_ENT_DEFAULT_NAME;
761  vtkNew<vtkIntArray> geoEntArr;
762  geoEntArr->SetName(linkName.c_str());
763 #ifndef HAVE_GMSH
764  int entTag = 1;
765 #endif
766  for (const auto &elemSet : parserMesh.elems) {
767 #ifdef HAVE_GMSH
768  auto entTag = gmsh::model::addDiscreteEntity(parserMesh.maxDim);
769  gmsh::model::setEntityName(parserMesh.maxDim, entTag, elemSet.first);
770 #endif
771  for (const auto &elem : elemSet.second) {
772  auto points = elem.points;
773  std::transform(points.begin(), points.end(), points.begin(),
774  nodeMapper);
775  if (elem.cellType == VTK_WEDGE) {
776  std::swap(points.at(1), points.at(2));
777  std::swap(points.at(4), points.at(5));
778  }
779  inpId2GMIdx[elem.id] =
780  mesh->InsertNextCell(elem.cellType, points.size(), points.data());
781  geoEntArr->InsertNextValue(entTag);
782  }
783 #ifndef HAVE_GMSH
784  ++entTag;
785 #endif
786  }
787  mesh->GetCellData()->AddArray(geoEntArr);
788  }
789  for (const auto &elSet : parserMesh.elSets) {
790  auto &outElSet = outElSets[elSet.first];
791  for (const auto &elem : elSet.second) {
792  auto iterCellIdxMap = inpId2GMIdx.find(elem);
793  if (iterCellIdxMap != inpId2GMIdx.end()) {
794  outElSet.emplace(iterCellIdxMap->second);
795  }
796  }
797  }
798  if (!parserMesh.surfaces.empty()) {
799  auto sideSetPD = vtkSmartPointer<vtkPolyData>::New();
800  sideSetPD->SetPoints(mesh->GetPoints());
801  sideSetPD->Allocate();
802  vtkNew<vtkIntArray> entArr;
803  vtkNew<vtkIdTypeArray> origCellArr;
804  origCellArr->SetNumberOfComponents(2);
805  vtkNew<vtkIntArray> cellFaceArr;
806  cellFaceArr->SetNumberOfComponents(2);
807 #ifndef HAVE_GMSH
808  int entTag = 1;
809 #endif
810  for (const auto &surf : parserMesh.surfaces) {
811  auto &outSurface = outSurfaces[surf.first];
812 #ifdef HAVE_GMSH
813  auto entTag = gmsh::model::addDiscreteEntity(parserMesh.maxDim - 1);
814  auto phyGroupTag =
815  gmsh::model::addPhysicalGroup(parserMesh.maxDim - 1, {entTag});
816  gmsh::model::setPhysicalName(parserMesh.maxDim - 1, phyGroupTag,
817  surf.first);
818 #endif
819  for (const auto &side : surf.second) {
820  auto iterCellIdxMap = inpId2GMIdx.find(side.first);
821  if (iterCellIdxMap != inpId2GMIdx.end()) {
822  auto origCell = mesh->GetCell(iterCellIdxMap->second);
823  auto vtkSide = inpSide2vtkSide(
824  static_cast<VTKCellType>(origCell->GetCellType()), side.second);
825  auto sideCell = origCell->GetCellDimension() == 2
826  ? origCell->GetEdge(vtkSide)
827  : origCell->GetFace(vtkSide);
828  auto sideIdx = sideSetPD->InsertNextCell(sideCell->GetCellType(),
829  sideCell->GetPointIds());
830  entArr->InsertTypedComponent(sideIdx, 0, entTag);
831  origCellArr->InsertTuple2(sideIdx, iterCellIdxMap->second, -1);
832  cellFaceArr->InsertTuple2(sideIdx, vtkSide, -1);
833  outSurface.emplace(sideIdx);
834  }
835  }
836 #ifndef HAVE_GMSH
837  ++entTag;
838 #endif
839  }
840  sideSet = {sideSetPD, entArr, origCellArr, cellFaceArr};
841  }
842  return {{mesh, std::move(geoName), std::move(linkName), sideSet},
843  {std::move(outElSets), std::move(outNSets), std::move(outSurfaces)}};
844 }
845 
846 } // namespace MSH
847 } // namespace NEM
std::map< std::string, std::vector< vtkIdType > > elSets
Map from ELSET name to element ids (ids given by .inp file)
Definition: inpGeoMesh.C:148
std::map< std::string, std::set< vtkIdType > > surfaces
SURFACE keyword; indexing matches GeoMesh::sideSet.
Definition: inpGeoMesh.H:63
std::map< std::string, std::set< vtkIdType > > nodeSets
NSET keyword; indexing matches GeoMesh::mesh.
Definition: inpGeoMesh.H:55
Class representing meshes in CalculiX input deck (similar to ABAQUS)
Definition: inpGeoMesh.H:49
virtual void report(std::ostream &out) const =0
Print a report about the mesh.
Definition: geoMeshBase.C:97
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).
static std::pair< GeoMesh, InpSets > inp2GM(const std::string &fileName)
Definition: inpGeoMesh.C:719
int maxDim
Definition: inpGeoMesh.C:135
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
virtual void reconstructGeo()
Construct the geometry from the mesh alone.
static constexpr auto GEO_ENT_DEFAULT_NAME
Definition: geoMeshBase.H:446
std::map< std::string, std::vector< vtkIdType > > nodeSets
Map from NSET name to node ids (ids given by .inp file)
Definition: inpGeoMesh.C:152
geoMeshBase * New(MeshType meshType)
Create a new mesh object.
STL namespace.
VTKCellType currCellType
TYPE, if inside data lines following ELEMENT keyword.
Definition: inpGeoMesh.C:309
static void Initialize()
Initialize Gmsh.
Definition: geoMeshBase.C:67
std::map< std::string, std::set< vtkIdType > > elSets
ELSET keyword; indexing matches GeoMesh::mesh.
Definition: inpGeoMesh.H:59
Keyword currKw
current keyword
Definition: inpGeoMesh.C:301
std::string currSet
Name of ELSET/NSET/Surface (empty string for ELEMENT keyword that does not specify ELSET) ...
Definition: inpGeoMesh.C:326
void toUpper(std::string &str)
std::string getRandomString(int length)
vtkIdType id
id in .inp file
Definition: inpGeoMesh.C:128
VTKCellType cellType
Definition: inpGeoMesh.C:129
void resetNative() override
Definition: inpGeoMesh.C:678
vtkSmartPointer< vtkPolyData > sides
Cells represent edges/faces of some GeoMesh.
Definition: geoMeshBase.H:399
std::shared_ptr< meshBase > mesh
static inpGeoMesh * Read(const std::string &fileName)
std::vector< vtkIdType > points
points given by id in .inp file
Definition: inpGeoMesh.C:133
std::map< std::string, std::vector< std::pair< vtkIdType, int > > > surfaces
Map from SURFACE name to (element id, side) (id and side both use .inp IDs)
Definition: inpGeoMesh.C:157
vtkStandardNewMacro(exoGeoMesh)
Definition: exoGeoMesh.C:247
mesh_(gen_->CreateMesh(false))
Definition: smeshGeoMesh.C:123
Continue currContinue
If previous line was keyword line that is continued, KEYWORD.
Definition: inpGeoMesh.C:315
std::map< std::string, std::vector< Elem > > elems
If the ELEMENT keyword line contains ELSET=name, then the element is stored in elems[name], otherwise, stored in elems[std::string{}].
Definition: inpGeoMesh.C:144
std::map< std::string, std::string > currKwParams
Params in current keyword line.
Definition: inpGeoMesh.C:305
void reconstructGeo() override
Construct the geometry from the mesh alone.
Definition: inpGeoMesh.C:673
vtkIdType currId
If previous line was data line that is continued and current keyword is NODE or ELEMEMT, the ID of the node/cell being processed.
Definition: inpGeoMesh.C:321
virtual void write(const std::string &fileName)=0
Write mesh to file.
abstract class to specify geometry and mesh data
Definition: geoMeshBase.H:102
InpSets inpSets_
Holds data specific to inp format.
Definition: inpGeoMesh.H:96
const GeoMesh & getGeoMesh() const
Get the underlying geometry object.
Definition: geoMeshBase.H:533