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.
exoMesh.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 #include "Mesh/exoMesh.H"
30 
31 #include <algorithm>
32 #include <cmath>
33 #include <iomanip>
34 #include <iostream>
35 #include <limits>
36 #include <map>
37 #include <numeric>
38 #include <set>
39 #include <utility>
40 
41 #include <ANN/ANN.h>
42 #include <exodusII.h>
43 
44 #include "AuxiliaryFunctions.H"
45 
46 namespace NEM {
47 namespace MSH {
48 namespace EXOMesh {
49 
50 VTKCellType e2vEMap(elementType et) {
51  std::map<elementType, VTKCellType> eMap = {
52  {elementType::TRIANGLE, VTK_TRIANGLE},
53  {elementType::QUAD, VTK_QUAD},
54  {elementType::TETRA, VTK_TETRA},
55  {elementType::HEX, VTK_HEXAHEDRON},
56  {elementType::WEDGE, VTK_WEDGE},
57  {elementType::OTHER, VTK_EMPTY_CELL}};
58  return eMap[et];
59 }
60 
61 elementType v2eEMap(VTKCellType vt) {
62  std::map<VTKCellType, elementType> eMap = {
63  {VTK_TRIANGLE, elementType::TRIANGLE},
64  {VTK_QUAD, elementType::QUAD},
65  {VTK_TETRA, elementType::TETRA},
66  {VTK_HEXAHEDRON, elementType::HEX},
67  {VTK_WEDGE, elementType::WEDGE},
68  {VTK_QUADRATIC_TRIANGLE, elementType::TRIANGLE},
69  {VTK_QUADRATIC_QUAD, elementType::QUAD},
70  {VTK_QUADRATIC_TETRA, elementType::TETRA},
71  {VTK_QUADRATIC_HEXAHEDRON, elementType::HEX},
72  {VTK_EMPTY_CELL, elementType::OTHER}};
73  return eMap[vt];
74 }
75 
76 surfaceBCTag bcTagNum(std::string &tag) {
77  nemAux::toLower(tag);
78  if (tag == "fixed") return surfaceBCTag::FIXED;
79  if (tag == "symmx") return surfaceBCTag::SYMMX;
80  if (tag == "symmy") return surfaceBCTag::SYMMY;
81  if (tag == "symmz") return surfaceBCTag::SYMMZ;
82  std::cerr << "Unknown surface tag " << tag << std::endl;
83  throw;
84 }
85 
86 std::string bcTagStr(surfaceBCTag tag) {
87  switch (tag) {
88  case surfaceBCTag::FIXED: return "FIXED";
89  case surfaceBCTag::SYMMX: return "SYMMX";
90  case surfaceBCTag::SYMMY: return "SYMMY";
91  case surfaceBCTag::SYMMZ: return "SYMMZ";
92  }
93  std::cerr << "Unknown surface tag." << std::endl;
94  throw;
95 }
96 
97 elementType elmTypeNum(std::string tag) {
98  nemAux::toLower(tag);
99  if (tag == "triangle") return elementType::TRIANGLE;
100  if (tag == "tri") return elementType::TRIANGLE;
101  if (tag == "tri3") return elementType::TRIANGLE;
102  if (tag == "trishell3") return elementType::TRIANGLE;
103 
104  if (tag == "quadrilateral") return elementType::QUAD;
105  if (tag == "quad") return elementType::QUAD;
106  if (tag == "quad4") return elementType::QUAD;
107  if (tag == "shell4") return elementType::QUAD;
108 
109  if (tag == "tetrahedron") return elementType::TETRA;
110  if (tag == "tetrahedral") return elementType::TETRA;
111  if (tag == "tetra") return elementType::TETRA;
112  if (tag == "tetra4") return elementType::TETRA;
113 
114  if (tag == "hexahedron") return elementType::HEX;
115  if (tag == "hexahedral") return elementType::HEX;
116  if (tag == "brick") return elementType::HEX;
117  if (tag == "hex") return elementType::HEX;
118  if (tag == "hex8") return elementType::HEX;
119 
120  if (tag == "wedge") return elementType::WEDGE;
121  if (tag == "prismatic") return elementType::WEDGE;
122  if (tag == "prism") return elementType::WEDGE;
123 
124  std::cout << "Warning : Element type " << tag << " may be unsupported.\n";
125  return elementType::OTHER;
126 }
127 
128 std::string elmTypeStr(elementType et) {
129  if (et == elementType::QUAD) return "QUAD";
130  if (et == elementType::TRIANGLE) return "TRIANGLE";
131  if (et == elementType::QUAD) return "QUAD";
132  if (et == elementType::TETRA) return "TETRA";
133  if (et == elementType::HEX) return "HEX";
134  if (et == elementType::WEDGE) return "WEDGE";
135  return "OTHER";
136 }
137 
138 int elmNumNde(elementType tag, int order) {
139  if (tag == elementType::TRIANGLE && order == 1) return 3;
140  if (tag == elementType::TRIANGLE && order == 2) return 6;
141  if (tag == elementType::QUAD && order == 1) return 4;
142  if (tag == elementType::QUAD && order == 2) return 9;
143  if (tag == elementType::TETRA && order == 1) return 4;
144  if (tag == elementType::TETRA && order == 2) return 10;
145  if (tag == elementType::HEX && order == 1) return 8;
146  if (tag == elementType::HEX && order == 2) return 21;
147  if (tag == elementType::WEDGE && order == 1) return 6;
148  if (tag == elementType::WEDGE && order == 2) return 16;
149  std::cerr << "Unknown element/order combination " << elmTypeStr(tag) << " " << order
150  << "\n";
151  throw;
152 }
153 
155  switch (tag) {
156  case elementType::TRIANGLE: return 3;
157  case elementType::QUAD: return 4;
158  case elementType::TETRA: return 4;
159  case elementType::HEX: return 8;
160  case elementType::WEDGE: return 5;
161  case elementType::OTHER: break;
162  }
163  std::cerr << "Unknown element type " << elmTypeStr(tag) << "\n";
164  throw;
165 }
166 
167 //////////////////////////////////
168 // exoMesh class
169 //////////////////////////////////
170 
172  : _numDim(2),
173  _numNdes(0),
174  _numElms(0),
175  _fid(-1),
176  _api_v(0.0),
177  _dbs_v(0.0),
178  _exErr(0),
179  _isSupported(true),
180  _isPopulated(false),
181  _isOpen(false),
182  _isVerbose(false) {}
183 
184 exoMesh::exoMesh(std::string ifname)
185  : _numDim(2),
186  _numNdes(0),
187  _numElms(0),
188  _fid(-1),
189  _api_v(0.0),
190  _dbs_v(0.0),
191  _exErr(0),
192  _ifname(std::move(ifname)),
193  _isSupported(true),
194  _isPopulated(false),
195  _isOpen(false),
196  _isVerbose(false) {}
197 
199  if (_isOpen) ex_close(_fid);
200 }
201 
202 void wrnErrMsg(int errCode, const std::string &msg) {
203  if (errCode < 0) {
204  std::cerr << "Error: " << msg << " with error code " << errCode
205  << std::endl;
206  throw;
207  } else if (errCode > 0) {
208  std::cerr << "Warning: " << msg << " with warning code " << errCode
209  << std::endl;
210  }
211 }
212 
214  // preparing database
215  // regardless we update it
216  exoPopulate(true);
217 
218  // writing to file
219  int comp_ws = 8;
220  int io_ws = 8;
221  _fid = ex_create(_ifname.c_str(), EX_CLOBBER, &comp_ws, &io_ws);
222  _isOpen = true;
223 
224  // initializing exodus database
225  _exErr =
226  ex_put_init(_fid, "NEMoSys ExodusII database", _numDim, _numNdes,
227  _numElms, _elmBlks.size(), _ndeSets.size(), _sdeSets.size());
228  wrnErrMsg(_exErr, "Problem initializing EXODUS II database");
229 
230  // writing node coordinates
231  _exErr = ex_put_coord(_fid, _xCrds.data(), _yCrds.data(), _zCrds.data());
232  wrnErrMsg(_exErr, "Problem writing node coordinates.");
233 
234  // writing element blocks
235  for (const auto &ieb : _elmBlks) {
236  _exErr = ex_put_elem_block(_fid, ieb.id, elmTypeStr(ieb.eTpe).c_str(),
237  ieb.nElm, ieb.ndePerElm, 0);
238  wrnErrMsg(_exErr, "Problem writing element block parameters.");
239  /*
240  int elem_blk_id, num_elem_this_blk, num_nodes_per_elem, num_attr;
241  std::string elem_type;
242  elem_type.resize(MAX_STR_LENGTH, '\0');
243  elem_blk_id = ieb + 1;
244  ex_get_elem_block(_fid, elem_blk_id, &elem_type[0], &num_elem_this_blk,
245  &num_nodes_per_elem, &num_attr);
246  std::cout << "elem_blk_id : " << elem_blk_id
247  << "\nelem_type : " << std::string(elem_type)
248  << "\nnum_elem_this_blk : " << num_elem_this_blk
249  << "\nnum_nodes_per_elem : " << num_nodes_per_elem
250  << "\nnum_attr : " << num_attr
251  << std::endl;
252  */
253  _exErr = ex_put_elem_conn(_fid, ieb.id, ieb.conn.data());
254  wrnErrMsg(_exErr, "Problem writing element block connectivities.");
255  // write element attribute
256  /*
257  std::vector<double> elmAttrib;
258  elmAttrib.resize(_elmBlock[ieb].nElm, 1);
259  _exErr = ex_put_elem_attr(_fid, 1, elmAttrib.data());
260  wrnErrMsg(_exErr, "Problem during writing elementBlock attributes.");
261  */
262  }
263 
264  // writing node sets
265  for (const auto &ins : _ndeSets) {
266  _exErr = ex_put_node_set_param(_fid, ins.id, ins.nNde, 0);
267  wrnErrMsg(_exErr, "Problem writing node set parameters.");
268  _exErr = ex_put_node_set(_fid, ins.id, ins.ndeIds.data());
269  wrnErrMsg(_exErr, "Problem writing node set node ids.");
270  }
271 
272  // writing side sets
273  for (const auto &iss : _sdeSets) {
274  _exErr = ex_put_side_set_param(_fid, iss.id, iss.nSde, 0);
275  wrnErrMsg(_exErr, "Problem writing side set parameters.");
276  _exErr =
277  ex_put_side_set(_fid, iss.id, iss.elmIds.data(), iss.sdeIds.data());
278  wrnErrMsg(_exErr, "Problem writing side set element and side ids.");
279  }
280 
281  // writing names
282  std::vector<char *> names;
283  for (auto &&_elmBlkName : _elmBlkNames) names.push_back(&_elmBlkName[0]);
284  if (!names.empty()) {
285  _exErr = ex_put_names(_fid, EX_ELEM_BLOCK, names.data());
286  wrnErrMsg(_exErr, "Problem writing element block names.");
287  }
288 
289  names.clear();
290  for (auto &&_ndeSetName : _ndeSetNames) names.push_back(&_ndeSetName[0]);
291  if (!names.empty()) {
292  _exErr = ex_put_names(_fid, EX_NODE_SET, names.data());
293  wrnErrMsg(_exErr, "Problem writing node set names.");
294  }
295 
296  names.clear();
297  for (auto &&_sdeSetName : _sdeSetNames) names.push_back(&_sdeSetName[0]);
298  if (!names.empty()) {
299  _exErr = ex_put_names(_fid, EX_SIDE_SET, names.data());
300  wrnErrMsg(_exErr, "Problem writing side set names.");
301  }
302 
303  // closing the file
304  _exErr = ex_close(_fid);
305  wrnErrMsg(_exErr, "Problem closing the EXODUS II database.");
306  _isOpen = false;
307 }
308 
309 void exoMesh::exoPopulate(bool updElmLst) {
310  _numElms = 0;
311  _elmBlkNames.clear();
312  if (updElmLst) {
313  for (auto &&ieb : _elmBlks) ieb.elmIds.clear();
314  glbConn.clear();
315  }
316  _ndeSetNames.clear();
317  _sdeSetNames.clear();
318 
319  // removing empty element blocks
320  std::vector<elmBlkType> nebs;
321  for (auto &&ieb : _elmBlks)
322  if (ieb.nElm > 0) nebs.emplace_back(std::move(ieb));
323  _elmBlks = nebs;
324 
325  // removing empty node sets
326  std::vector<ndeSetType> nnss;
327  for (auto &&ins : _ndeSets)
328  if (ins.nNde > 0) nnss.emplace_back(std::move(ins));
329  _ndeSets = nnss;
330 
331  // removing empty side sets
332  std::vector<sdeSetType> nsss;
333  for (auto &&iss : _sdeSets)
334  if (iss.nSde > 0) nsss.emplace_back(std::move(iss));
335  _sdeSets = nsss;
336 
337  // updating element blocks
338  int blkId = 0; // Reindex the blocks.
339  for (auto &&ieb : _elmBlks) {
340  if (_reindexBlks) ieb.id = ++blkId;
341  _elmBlkNames.emplace_back(ieb.name);
342 
343  // offsetting element connectivities
344  if (ieb.ndeIdOffset != 0) {
345  for (auto &&conn : ieb.conn) conn += ieb.ndeIdOffset;
346  ieb.ndeIdOffset = 0;
347  }
348 
349  // updating global connectivity
350  if (updElmLst) {
351  ieb.elmIds.clear();
352  int nn = ieb.ndePerElm;
353  for (int elmIdx = 0; elmIdx < ieb.nElm; ++elmIdx) {
354  std::vector<int> elmConn;
355  for (int idx = elmIdx * nn; idx < (elmIdx + 1) * nn; ++idx)
356  elmConn.emplace_back(ieb.conn[idx]);
357  ieb.elmIds.emplace_back(_numElms + elmIdx + 1);
358  glbConn.emplace_back(elmConn);
359  }
360 
361  // sanity check
362  if (ieb.nElm != ieb.elmIds.size())
363  std::cerr << "WARNING: Malformed element block " << ieb.name << ".\n";
364  }
365 
366  // sanity check
367  if (ieb.nElm * ieb.ndePerElm != ieb.conn.size())
368  std::cerr << "WARNING: Malformed element block " << ieb.name << ".\n";
369 
370  _numElms += ieb.nElm;
371  }
372 
373  // updating node sets
374  int nsetId = 0; // Reindex the node sets.
375  for (auto &&ins : _ndeSets) {
376  ins.id = ++nsetId;
377  _ndeSetNames.emplace_back(ins.name);
378 
379  // offsetting node ids
380  if (ins.ndeIdOffset != 0) {
381  for (auto &&it2 : ins.ndeIds) it2 += ins.ndeIdOffset;
382  ins.ndeIdOffset = 0;
383  }
384 
385  // sanity check
386  if (ins.nNde != ins.ndeIds.size())
387  std::cerr << "WARNING: Malformed node set " << ins.name << ".\n";
388  }
389 
390  // updating side sets
391  int ssetId = 0; // Reindex the side sets.
392  for (auto &&iss : _sdeSets) {
393  iss.id = ++ssetId;
394  _sdeSetNames.emplace_back(iss.name);
395 
396  // offsetting element ids
397  if (iss.elmIdOffset != 0) {
398  for (auto &&elm : iss.elmIds) elm += iss.elmIdOffset;
399  iss.elmIdOffset = 0;
400  }
401 
402  // sanity check
403  if (iss.nSde != iss.sdeIds.size() || iss.nSde != iss.elmIds.size())
404  std::cerr << "WARNING: Malformed side set " << iss.name << ".\n";
405  }
406 
407  if (_isVerbose) report();
408 
409  _isPopulated = true;
410 }
411 
412 void exoMesh::report() const {
413  std::cout << " ----- Exodus II Database Report ----- \n";
414  std::cout << "Database: " << _ifname << "\n";
415  std::cout << "Dimension: " << _numDim << "\n";
416  std::cout << "Nodes: " << _numNdes << "\n";
417  std::cout << "Elements: " << _numElms << "\n";
418  std::cout << "Element blocks: " << getNumberOfElementBlocks() << "\n";
419  std::cout << "Node sets: " << getNumberOfNodeSets() << "\n";
420  std::cout << "Side sets: " << getNumberOfSideSets() << "\n";
421  if (getNumberOfElementBlocks() > 0) {
422  std::cout << " Blk nElm eType Name\n";
423  std::cout << " --- ---- ----- ------\n";
424  for (const auto &ieb : _elmBlks)
425  std::cout << std::setw(5) << ieb.id << std::setw(10) << ieb.nElm
426  << std::setw(10) << elmTypeStr(ieb.eTpe) << std::setw(20)
427  << ieb.name << "\n";
428  }
429  if (getNumberOfNodeSets() > 0) {
430  std::cout << " Nde nNde Name\n";
431  std::cout << " --- ---- ------\n";
432  for (const auto &ins : _ndeSets)
433  std::cout << std::setw(5) << ins.id << std::setw(10) << ins.nNde
434  << std::setw(20) << ins.name << "\n";
435  }
436  if (getNumberOfSideSets() > 0) {
437  std::cout << " Sde nSde Name\n";
438  std::cout << " --- ---- ------\n";
439  for (const auto &iss : _sdeSets)
440  std::cout << std::setw(5) << iss.id << std::setw(10) << iss.nSde
441  << std::setw(20) << iss.name << "\n";
442  }
443  std::cout << std::flush;
444 }
445 
446 //***************** Combine Element Blocks ***************//
447 //********************************************************//
448 void exoMesh::combineElmBlks(const std::vector<int> &blkIdLst,
449  const std::string &newName) {
450  nemAux::Timer T;
451  T.start();
452  // Check for duplicate entries in blkIdLst
453  if (findDuplicates(blkIdLst).size() != 0) {
454  std::cerr << "Error: Duplicate block IDs defined." << std::endl;
455  throw;
456  }
457  // Check there is more than one id in list
458  if (blkIdLst.size() < 2) {
459  std::cerr << "Error: More than one block ID is needed." << std::endl;
460  throw;
461  }
462  // Check that block ID exists
463  elementType elmtype;
464  std::vector<elementType> eType_vec;
465  for (auto &id : blkIdLst) {
466  auto it = std::find(_elmBlkIds.begin(), _elmBlkIds.end(), id);
467  if (it == _elmBlkIds.end()) {
468  std::cerr << "Error: Block ID " << id << " does not exist." << std::endl;
469  throw;
470  }
471  // Check element types for each ID
472  elmtype = getElmBlkTypeById(id);
473  eType_vec.push_back(elmtype);
474  }
475  // Check that element type in each block is the same
476  for (int i = 0; i < eType_vec.size(); ++i) {
477  elementType e;
478  if (i == 0)
479  e = eType_vec[i];
480  else if (eType_vec[i] != e) {
481  std::cerr << "Error: Block element types are not the same. Cannot "
482  "combine blocks."
483  << std::endl;
484  throw;
485  }
486  }
487 
488  // Loop through registered blocks and make vector list
489  std::vector<int> blkIds;
490  for (auto &blk : _elmBlks) {
491  blkIds.push_back(blk.id);
492  }
493 
494  std::vector<int> arrange_list;
495  // Get the first id in combine block list
496  int first = blkIdLst[0];
497  // Loop through blkIds and insert into new vector
498  arrange_list.push_back(blkIds[0]);
499  for (int &id : blkIds) {
500  if (id != first) {
501  // Check if id is already in arrange_list,
502  // if so, don't insert
503  auto it = std::find(arrange_list.begin(), arrange_list.end(), id);
504  if (it == arrange_list.end()) arrange_list.push_back(id);
505  } else {
506  // Check if each combine list item is in arrange_list
507  // if so, remove from arrange_list
508  for (const int &comId : blkIdLst) {
509  for (int i = 0; i < arrange_list.size(); ++i) {
510  if (comId == arrange_list[i]) {
511  arrange_list.erase(arrange_list.begin() + i);
512  }
513  }
514  }
515  // Insert entire combine list into arrange_list
516  for (const int &j : blkIdLst) {
517  arrange_list.push_back(j);
518  }
519  }
520  }
521 
522  // Populate old to new element ids map
523  std::map<int, int> old2NewElmIds;
524  int i = 0;
525  int numElms = getNumberOfElements();
526  for (int &id : arrange_list) {
527  for (auto &elmBlk : _elmBlks) {
528  if (id == elmBlk.id) {
529  for (auto &eid : elmBlk.elmIds) {
530  i++;
531  old2NewElmIds.insert(std::pair<int, int>(eid, i));
532  }
533  }
534  }
535  }
536 
537  //**************************************************************//
538  // Any part of a side set that is between the blocks being
539  // combined should be deleted. Therefore, delete these parts
540  // before combining the blocks by:
541  //
542  // Find all node IDs common between combining blocks (blkIdLst).
543  // Build combining blocks point kdTree.
544  // Go block by block and insert points into tree.
545  // Find elements that have these common node IDs.
546  // Find those elements in the side sets and compare common nodes
547  // to the face nodes.
548  //**************************************************************//
549  std::cout << "\nModifying sidesets..." << std::flush;
550  // Get the number nodes in block to be combined
551  int numNodes = 0;
552  for (auto &id : blkIdLst) {
553  int idx = getElmBlkIndex(id);
554  numNodes += _elmBlks[idx].ndeCoords.size();
555  }
556 
557  // Build kdtree from node coords in each block
558  // This will create duplicate nodes in the kdtree
559  // which is desired to find common nodes b/w blocks
560  int nDim = 3;
561  std::map<int, int> tree_ndeId_map;
562  ANNpointArray pntCrd;
563  pntCrd = annAllocPts(numNodes, nDim);
564  int ipts = 0;
565  for (auto &id : blkIdLst) {
566  int idx = getElmBlkIndex(id);
567  auto it = _elmBlks[idx].ndeCoords.begin();
568  while (it != _elmBlks[idx].ndeCoords.end()) {
569  pntCrd[ipts][0] = it->second[0];
570  pntCrd[ipts][1] = it->second[1];
571  pntCrd[ipts][2] = it->second[2];
572  tree_ndeId_map[ipts] = it->first;
573  ipts++;
574  it++;
575  }
576  }
577  ANNkd_tree *kdTree = new ANNkd_tree(pntCrd, numNodes, nDim);
578 
579  // Finding duplicate node ids
580  std::set<int> commonNodes;
581  int nNib = 2;
582  ANNpoint qryPnt;
583  ANNidxArray nnIdx = new ANNidx[nNib];
584  ANNdistArray dists = new ANNdist[nNib];
585  qryPnt = annAllocPt(nDim);
586 
587  for (int iNde = 0; iNde < _numNdes; iNde++) {
588  qryPnt[0] = _xCrds[iNde];
589  qryPnt[1] = _yCrds[iNde];
590  qryPnt[2] = _zCrds[iNde];
591  kdTree->annkSearch(qryPnt, nNib, nnIdx, dists);
592 
593  if (dists[1] <= 1e-12) {
594  int id1 = tree_ndeId_map[nnIdx[0]];
595  int id2 = tree_ndeId_map[nnIdx[1]];
596  commonNodes.insert(id1);
597  commonNodes.insert(id2);
598  }
599  }
600  delete kdTree;
601 
602  // Find all elements that have commonNodes
603  std::set<int> commonElms;
604  int idx, num_nodes_per_elem, ndeID, c;
605  numElms = 0;
606  for (auto &id : blkIdLst) {
607  idx = getElmBlkIndex(id);
608  numElms = _elmBlks[idx].nElm;
609  num_nodes_per_elem = _elmBlks[idx].ndePerElm;
610  c = 0;
611  for (int el = 0; el < numElms; ++el) {
612  for (int n = 0; n < num_nodes_per_elem; ++n) {
613  c = el * num_nodes_per_elem + n;
614  ndeID = _elmBlks[idx].conn[c];
615  auto pos = commonNodes.find(ndeID);
616  if (pos != commonNodes.end())
617  commonElms.insert(_elmBlks[idx].elmIds[el]);
618  }
619  }
620  }
621 
622  // Face to local element connectivity maps
623  std::map<int, std::vector<int>> tri_face_conn_map = {
624  {1, {1, 2}}, {2, {2, 3}}, {3, {3, 1}}};
625  std::map<int, std::vector<int>> quad_face_conn_map = {
626  {1, {1, 2}}, {2, {2, 3}}, {3, {3, 4}}, {4, {4, 1}}};
627  std::map<int, std::vector<int>> tet_face_conn_map = {
628  {1, {1, 2, 4}}, {2, {2, 3, 4}}, {3, {1, 4, 3}}, {4, {1, 3, 2}}};
629  std::map<int, std::vector<int>> hex_face_conn_map = {
630  {1, {1, 2, 6, 5}}, {2, {2, 3, 7, 6}}, {3, {3, 4, 8, 7}},
631  {4, {1, 5, 8, 4}}, {5, {1, 4, 3, 2}}, {6, {5, 6, 7, 8}}};
632  std::map<int, std::vector<int>> wedge_face_conn_map = {{1, {1, 2, 5, 4}},
633  {2, {2, 3, 6, 5}},
634  {3, {3, 1, 4, 6}},
635  {4, {1, 2, 3}},
636  {5, {4, 5, 6}}};
637 
638  int numCommon = 0;
639  // Find commonElms in sides sets
640  for (auto &sdeSet : _sdeSets) {
641  // Iterators for face id vector
642  auto itf = sdeSet.sdeIds.begin();
643  int loc = 0;
644  int elmID = 0;
645  for (auto ite = sdeSet.elmIds.begin(); ite != sdeSet.elmIds.end();) {
646  loc = ite - sdeSet.elmIds.begin();
647  elmID = sdeSet.elmIds[loc];
648  auto pos = commonElms.find(elmID);
649  if (pos != commonElms.end()) {
650  int faceID = sdeSet.sdeIds[loc];
651 
652  // Use the elmID to get the connectivity
653  std::vector<int> elmConn = glbConn[elmID - 1];
654  std::vector<int> faceConn;
655  // With the faceID get the local connectivity (map)
656  if (elmtype == elementType::TETRA)
657  faceConn = tet_face_conn_map[faceID];
658  else if (elmtype == elementType::HEX)
659  faceConn = hex_face_conn_map[faceID];
660  else if (elmtype == elementType::WEDGE)
661  faceConn = wedge_face_conn_map[faceID];
662  else if (elmtype == elementType::TRIANGLE)
663  faceConn = tri_face_conn_map[faceID];
664  else if (elmtype == elementType::QUAD)
665  faceConn = quad_face_conn_map[faceID];
666  else {
667  std::cerr << "Error: Mesh element type not supported." << std::endl;
668  throw;
669  }
670 
671  // Compare the local(face) nodes with commonNodes
672  bool allCommon = false;
673  for (int i = 0; i < faceConn.size(); ++i) {
674  // This is the node ID on the face
675  int ndeID = elmConn[faceConn[i] - 1];
676  auto pos = commonNodes.find(ndeID);
677  if (pos == commonNodes.end()) {
678  // If a commonNode is not found, break
679  allCommon = false;
680  break;
681  } else
682  allCommon = true;
683  }
684  if (allCommon) {
685  ite = sdeSet.elmIds.erase(ite);
686  itf = sdeSet.sdeIds.erase(itf);
687  sdeSet.nSde--;
688  numCommon++;
689  } else {
690  ++ite;
691  ++itf;
692  }
693  } else {
694  ++ite;
695  ++itf;
696  }
697  }
698  }
699  std::cout << "done." << std::endl;
700 
701  //**************************************************************//
702  // Combine the blocks
703  //**************************************************************//
704  std::cout << "Combining blocks..." << std::flush;
705  int firstBlk = getElmBlkIndex(blkIdLst[0]);
706  // Combine the blocks
707  for (int i = 0; i < blkIdLst.size(); ++i) {
708  if (i > 0) {
709  int idx = getElmBlkIndex(blkIdLst[i]);
710  int ei = -1;
711  for (const auto &eid : _elmBlks[idx].elmIds) {
712  ei++;
713  _elmBlks[firstBlk].elmIds.emplace_back(eid);
714  _elmBlks[firstBlk].nElm++;
715 
716  for (int ni = ei * _elmBlks[idx].ndePerElm;
717  ni < (ei + 1) * _elmBlks[idx].ndePerElm; ni++) {
718  _elmBlks[firstBlk].conn.emplace_back(_elmBlks[idx].conn[ni]);
719  }
720  }
721  // Set new block name, if given
722  if (newName != "") _elmBlks[firstBlk].name = newName;
723  }
724  }
725  std::cout << "done." << std::endl;
726  std::cout << "Updating sideset element IDs..." << std::flush;
727  // Update sides sets
728  updateSidesets(old2NewElmIds);
729  std::cout << "done." << std::endl;
730 
731  // Remove blocks, don't reindex blocks
732  _reindexBlks = false;
733  for (int i = 0; i < blkIdLst.size(); ++i) {
734  if (i > 0) removeElmBlkById(blkIdLst[i]);
735  }
736  T.stop();
737 
738  std::cout << "Combined blocks and updated sidesets in "
739  << T.elapsed() / 1000.0 << " seconds.\n"
740  << std::endl;
741 }
742 
743 //******************** Update Sidesets *******************//
744 //********************************************************//
745 void exoMesh::updateSidesets(const std::map<int, int> &old2NewElmIds) {
746  // Loop through side sets, update elem ids with map
747  for (auto &sSet : _sdeSets) {
748  for (auto &elmId : sSet.elmIds) {
749  auto it = old2NewElmIds.find(elmId);
750  if (it != old2NewElmIds.end())
751  elmId = it->second;
752  else
753  std::cerr << "Error: element not found in sideset" << std::endl;
754  }
755  }
756 }
757 
758 void exoMesh::removeByElmIdLst(int blkIdx, const std::vector<int> &idLst) {
759  // TODO: Does NOT update side sets properly!
760  // range check
761  if (blkIdx > getNumberOfElementBlocks() || blkIdx < 0)
762  wrnErrMsg(-1, "Block Id is out of the range.");
763 
764  // the idLst should not eliminate the old block
765  // at least one element should remain
766  // if (idLst.size() >= _elmBlock[blkIdx].elmIds.size())
767  //{
768  // std::cout << idLst.size() << " " << _elmBlock[blkIdx].elmIds.size() <<
769  // std::endl; wrnErrMsg(-1, "Can not remove the entire block!.");
770  //}
771 
772  // now updating the old block
773  _isPopulated = false;
774 
775  elmBlkType oeb = _elmBlks[blkIdx];
776  elmBlkType neb;
777  neb.id = oeb.id;
778  neb.name = oeb.name;
779  neb.eTpe = oeb.eTpe;
780  neb.ndePerElm = oeb.ndePerElm;
781  neb.ndeIdOffset = 0;
782  neb.nElm = 0;
783 
784  // elmIds and connectivities
785  int oei = -1;
786  for (const auto &eid : oeb.elmIds) {
787  oei++;
788 
789  bool stays = true;
790  for (const auto &id : idLst)
791  if (id == eid) {
792  stays = false;
793  break;
794  }
795  if (!stays) continue;
796 
797  neb.nElm++;
798  neb.elmIds.emplace_back(eid);
799  for (int ni = oei * oeb.ndePerElm; ni < (oei + 1) * oeb.ndePerElm; ni++)
800  neb.conn.emplace_back(oeb.conn[ni]);
801  }
802  std::cout << "Removed " << oeb.elmIds.size() - neb.elmIds.size()
803  << " elements from original" << std::endl;
804  _elmBlks[blkIdx] = neb;
805 }
806 
807 void exoMesh::addElmBlkByElmIdLst(const std::string &name,
808  std::vector<int> &lst) {
809  if (lst.empty()) {
810  std::cerr << "WARNING: Attempting to add element block " << name
811  << " with no elements." << std::endl;
812  return;
813  }
814 
815  // preparing database
816  if (!_isPopulated) exoPopulate(false);
817 
818  _isPopulated = false;
819  // create element block
820  // Assumption here: All elements in the provided list are in the same element
821  // block
822  elmBlkType neb;
823  neb.id = _elmBlks.size() + 1;
824  neb.name = name;
825  neb.ndeIdOffset = 0;
826 
827  // int blkIdx = findElmBlkIdxByElmId(lst[0]); // faster but less accurate
828  int blkIdx = findElmBlkIdxByElmIdLst(lst); // slower, more accurate
829  if (blkIdx == -1) wrnErrMsg(-1, "Elements ids are not registered.");
830  // std::cout << "Block Indx = " << blkIdx << std::endl;
831 
832  // now adjust the list and remove elements that are not owned by this block
833  std::vector<int> owndLst;
834  bool allIn = false;
835  owndLst = lstElmInBlk(blkIdx, lst, allIn);
836  if (!allIn) {
837  std::cout << owndLst.size() << " Elements are in the block.\n";
838  std::cout << "Some of the elements in the list are outside the block.\n";
839  lst = owndLst;
840  }
841 
842  neb.elmIds = lst;
843  neb.nElm = lst.size();
844  neb.eTpe = getElmBlkType(blkIdx);
845  neb.ndePerElm = _elmBlks[blkIdx].ndePerElm;
846  for (const auto &eid : lst)
847  for (int idx = 0; idx < neb.ndePerElm; ++idx)
848  neb.conn.emplace_back(glbConn[eid][idx]);
849 
850  // updating the old element block and adding a new one
851  removeByElmIdLst(blkIdx, lst);
852  addElmBlk(neb);
853 
854  exoPopulate(false);
855 }
856 
857 void exoMesh::addNdeSetByNdeIdLst(const std::string &name,
858  const std::vector<int> &idLst) {
859  // preparing database
860  if (!_isPopulated) exoPopulate(false);
861 
862  _isPopulated = false;
863  // create and add new node set
864  ndeSetType nns;
865  nns.id = _ndeSets.size() + 1; // 1-indexed
866  nns.nNde = idLst.size();
867  nns.name = name;
868  nns.ndeIdOffset = 0;
869  nns.ndeIds = idLst;
870  addNdeSet(nns);
871 
872  // update exodus information
873  exoPopulate(false);
874 }
875 
876 void exoMesh::snapNdeCrdsZero(double tol) {
877  int nPnt = 0;
878 
879  // loop through nodes
880  bool chk;
881  for (auto i = 0; i < _numNdes; ++i) {
882  chk = false;
883  if (std::abs(_xCrds[i]) <= tol) {
884  _xCrds[i] = 0.0;
885  chk = true;
886  }
887  if (std::abs(_yCrds[i]) <= tol) {
888  _yCrds[i] = 0.0;
889  chk = true;
890  }
891  if (std::abs(_zCrds[i]) <= tol) {
892  _zCrds[i] = 0.0;
893  chk = true;
894  }
895  if (chk) nPnt++;
896  }
897 
898  std::cout << "Number of points snapped : " << nPnt << std::endl;
899 }
900 
901 void exoMesh::removeElmBlkByName(const std::string &blkName) {
902  if (blkName.empty()) return;
903 
904  std::vector<elmBlkType> nebs;
905  for (const auto &ieb : _elmBlks)
906  if (ieb.name != blkName)
907  nebs.emplace_back(ieb);
908  else
909  _isPopulated = false;
910  _elmBlks = nebs;
911 
912  exoPopulate(true);
913 }
914 
916  std::vector<elmBlkType> nebs;
917  for (const auto &ieb : _elmBlks)
918  if (ieb.id != id)
919  nebs.emplace_back(ieb);
920  else
921  _isPopulated = false;
922  _elmBlks = nebs;
923 
924  exoPopulate(true);
925 }
926 
927 int exoMesh::findElmBlkIdxByElmId(int elmId) const {
928  int blkIdx = 0;
929  for (const auto &ieb : _elmBlks) {
930  for (const auto &eid : ieb.elmIds)
931  if (eid == elmId) return blkIdx;
932 
933  blkIdx++;
934  }
935  return -1;
936 }
937 
938 int exoMesh::findElmBlkIdxByElmIdLst(const std::vector<int> &elmIds) const {
939  if (elmIds.empty()) return -1;
940 
941  int blkIdx = -1;
942  int intfCnt = 0;
943 
944  bool allIn = false;
945  for (int ieb = 0; ieb < _elmBlks.size(); ++ieb) {
946  std::vector<int> intfLst;
947  intfLst = lstElmInBlk(ieb, elmIds, allIn);
948 
949  if (allIn) {
950  return ieb;
951  } else if (intfLst.size() > intfCnt) {
952  intfCnt = intfLst.size();
953  blkIdx = ieb;
954  }
955  }
956 
957  if (!allIn)
958  std::cerr << "WARNING: Elements in list are not all in one element block. "
959  "Only block containing largest amount of elements is "
960  "processed. This might indicate selected elements overlap or "
961  "are different element types (e.g., HEX and TETRA)."
962  << std::endl;
963 
964  return blkIdx;
965 }
966 
967 std::vector<int> exoMesh::lstElmInBlk(int blkIdx,
968  const std::vector<int> &elmIds,
969  bool &allIn) const {
970  std::vector<int> out;
971  allIn = true;
972 
973  // compare elmIds with list of the elements within block
974  std::set<int> current;
975  for (const auto &elmId : _elmBlks[blkIdx].elmIds) current.insert(elmId);
976  for (const auto &elmId : elmIds)
977  if (!current.insert(elmId).second)
978  out.emplace_back(elmId);
979  else
980  allIn = false;
981 
982  return out;
983 }
984 
986  _isPopulated = false;
987  _ndeSets.clear();
988  _elmBlks.clear();
989  _sdeSets.clear();
990  _fid = 0;
991  _numDim = 2;
992  _numNdes = 0;
993  _numElms = 0;
994  _xCrds.clear();
995  _yCrds.clear();
996  _zCrds.clear();
997  glbConn.clear();
998  _ndeSetNames.clear();
999  _elmBlkNames.clear();
1000  _sdeSetNames.clear();
1001 }
1002 
1003 void exoMesh::read(const std::string &ifname) {
1004  if (!ifname.empty()) _ifname = ifname;
1005 
1006  // before reading all internal data base will be reset
1007  reset();
1008 
1009  int CPU_word_size = sizeof(double);
1010  int IO_word_size = 0;
1011  float version;
1012  _exErr = 0;
1013 
1014  /* open EXODUS II files */
1015  _fid = ex_open(_ifname.c_str(), EX_READ, &CPU_word_size, &IO_word_size,
1016  &version);
1017  _isOpen = true;
1018  wrnErrMsg(_fid > 0 ? 0 : -1, "Problem opening file " + _ifname + "\n");
1019 
1020  int numElmBlks;
1021  int numNdeSets;
1022  int numSdeSets;
1023 
1024  // parameter inquiry from Exodus file
1025  int idum;
1026  float fdum;
1027  char cdum;
1028 
1029  _exErr = ex_inquire(_fid, EX_INQ_API_VERS, &idum, &fdum, &cdum);
1030  wrnErrMsg(_exErr, "Problem reading file contents.\n");
1031  std::cout << "Exodus II API version is " << fdum << std::endl;
1032  _api_v = fdum;
1033 
1034  _exErr = ex_inquire(_fid, EX_INQ_DB_VERS, &idum, &fdum, &cdum);
1035  wrnErrMsg(_exErr, "Problem reading file contents.\n");
1036  std::cout << "Exodus II Database version is " << fdum << std::endl;
1037  _dbs_v = fdum;
1038 
1039  _exErr = ex_inquire(_fid, EX_INQ_DIM, &idum, &fdum, &cdum);
1040  wrnErrMsg(_exErr, "Problem reading file contents.\n");
1041  _numDim = idum;
1042  std::cout << "Number of coordinate dimensions is " << idum << std::endl;
1043 
1044  _exErr = ex_inquire(_fid, EX_INQ_NODES, &idum, &fdum, &cdum);
1045  wrnErrMsg(_exErr, "Problem reading file contents.\n");
1046  std::cout << "Number of points " << idum << std::endl;
1047  _numNdes = idum;
1048 
1049  _exErr = ex_inquire(_fid, EX_INQ_ELEM, &idum, &fdum, &cdum);
1050  wrnErrMsg(_exErr, "Problem reading file contents.\n");
1051  std::cout << "Number of elements " << idum << std::endl;
1052  _numElms = idum;
1053 
1054  _exErr = ex_inquire(_fid, EX_INQ_ELEM_BLK, &idum, &fdum, &cdum);
1055  wrnErrMsg(_exErr, "Problem reading file contents.\n");
1056  std::cout << "Number of element blocks " << idum << std::endl;
1057  numElmBlks = idum;
1058 
1059  _exErr = ex_inquire(_fid, EX_INQ_NODE_SETS, &idum, &fdum, &cdum);
1060  wrnErrMsg(_exErr, "Problem reading file contents.\n");
1061  std::cout << "Number of node sets " << idum << std::endl;
1062  numNdeSets = idum;
1063 
1064  _exErr = ex_inquire(_fid, EX_INQ_SIDE_SETS, &idum, &fdum, &cdum);
1065  wrnErrMsg(_exErr, "Problem reading file contents.\n");
1066  std::cout << "Number of side sets " << idum << std::endl;
1067  numSdeSets = idum;
1068 
1069  // nodal coordinates
1070  _xCrds.resize(_numNdes, 0.0);
1071  _yCrds.resize(_numNdes, 0.0);
1072  _zCrds.resize(_numNdes, 0.0);
1073  _exErr = ex_get_coord(_fid, _xCrds.data(), _yCrds.data(), _zCrds.data());
1074  wrnErrMsg(_exErr, "Problem reading nodal coordinates.\n");
1075 
1076  // ids
1077  std::vector<int> elmBlkIds(numElmBlks, 0);
1078  if (numElmBlks > 0) {
1079  _exErr = ex_get_elem_blk_ids(_fid, elmBlkIds.data());
1080  wrnErrMsg(_exErr, "Problem reading element block ids.\n");
1081 
1082  for (int i = 0; i < numElmBlks; ++i) {
1083  _elmBlkIds.push_back(elmBlkIds[i]);
1084  }
1085  }
1086 
1087  std::vector<int> ndeSetIds(numNdeSets, 0);
1088  if (numNdeSets > 0) {
1089  _exErr = ex_get_node_set_ids(_fid, ndeSetIds.data());
1090  wrnErrMsg(_exErr, "Problem reading node set ids.\n");
1091  }
1092 
1093  std::vector<int> sdeSetIds(numSdeSets, 0);
1094  if (numSdeSets > 0) {
1095  _exErr = ex_get_side_set_ids(_fid, sdeSetIds.data());
1096  wrnErrMsg(_exErr, "Problem reading side set ids.\n");
1097  }
1098 
1099  // element block names
1100  std::string blk_name;
1101  for (int iEB = 0; iEB < numElmBlks; ++iEB) {
1102  blk_name.clear();
1103  blk_name.resize(MAX_STR_LENGTH);
1104  int blk_id = elmBlkIds[iEB];
1105  _exErr = ex_get_name(_fid, EX_ELEM_BLOCK, blk_id, &blk_name[0]);
1106  wrnErrMsg(_exErr, "Problem reading element block names.\n");
1107  blk_name.erase(std::remove(blk_name.begin(), blk_name.end(), '\0'),
1108  blk_name.end());
1109  // std::cout << "debugging" << std::endl;
1110  // std::cout << blk_id << " " << blk_name << std::endl;
1111  _elmBlkNames.push_back(blk_name);
1112  }
1113 
1114  // node set names
1115  for (int iNS = 0; iNS < numNdeSets; ++iNS) {
1116  blk_name.clear();
1117  blk_name.resize(MAX_STR_LENGTH);
1118  int blk_id = ndeSetIds[iNS];
1119  _exErr = ex_get_name(_fid, EX_NODE_SET, blk_id, &blk_name[0]);
1120  wrnErrMsg(_exErr, "Problem reading node set names.\n");
1121  blk_name.erase(std::remove(blk_name.begin(), blk_name.end(), '\0'),
1122  blk_name.end());
1123  // std::cout << blk_id << " " << blk_name << std::endl;
1124  _ndeSetNames.push_back(blk_name);
1125  }
1126 
1127  // side set names
1128  for (int iSS = 0; iSS < numSdeSets; ++iSS) {
1129  blk_name.clear();
1130  blk_name.resize(MAX_STR_LENGTH);
1131  int blk_id = sdeSetIds[iSS];
1132  _exErr = ex_get_name(_fid, EX_SIDE_SET, blk_id, &blk_name[0]);
1133  wrnErrMsg(_exErr, "Problem reading side set names.\n");
1134  blk_name.erase(std::remove(blk_name.begin(), blk_name.end(), '\0'),
1135  blk_name.end());
1136  // std::cout << blk_id << " " << blk_name << std::endl;
1137  _sdeSetNames.push_back(blk_name);
1138  }
1139 
1140  // element blocks
1141  std::string elem_type;
1142  int elmId = 1;
1143  for (int iEB = 0; iEB < numElmBlks; ++iEB) {
1144  elem_type.clear();
1145  elem_type.resize(MAX_STR_LENGTH);
1146  elmBlkType eb;
1147  eb.id = elmBlkIds[iEB];
1148  int num_el_in_blk, num_nod_per_el, num_attr;
1149  _exErr = ex_get_elem_block(_fid, elmBlkIds[iEB], &elem_type[0],
1150  &num_el_in_blk, &num_nod_per_el, &num_attr);
1151  wrnErrMsg(_exErr, "Problem reading element block parameters.\n");
1152  elem_type.erase(std::remove(elem_type.begin(), elem_type.end(), '\0'),
1153  elem_type.end());
1154  eb.nElm = num_el_in_blk;
1155  eb.name = _elmBlkNames[iEB];
1156  eb.ndePerElm = num_nod_per_el;
1157  eb.eTpe = elmTypeNum(elem_type);
1158 
1159  eb.ndeIdOffset = 0;
1160 
1161  // read element connectivity
1162  eb.conn.clear();
1163  eb.conn.resize(num_el_in_blk * num_nod_per_el, 0);
1164  _exErr = ex_get_elem_conn(_fid, elmBlkIds[iEB], eb.conn.data());
1165  wrnErrMsg(_exErr, "Problem reading element block connectivities.\n");
1166 
1167  double x, y, z;
1168  // get node coords for each block
1169  for (int &ndeId : eb.conn) {
1170  x = _xCrds[ndeId];
1171  y = _yCrds[ndeId];
1172  z = _zCrds[ndeId];
1173  eb.ndeCoords.insert(
1174  std::pair<int, std::vector<double>>(ndeId, {x, y, z}));
1175  }
1176 
1177  // fill element ids
1178  eb.elmIds.clear();
1179  eb.elmIds.resize(num_el_in_blk);
1180  std::iota(eb.elmIds.begin(), eb.elmIds.end(), elmId);
1181  elmId += num_el_in_blk;
1182 
1183  // global connectivity
1184  int nn = eb.ndePerElm;
1185  for (int elmIdx = 0; elmIdx < eb.nElm; ++elmIdx) {
1186  std::vector<int> elmConn;
1187  for (int idx = elmIdx * nn; idx < (elmIdx + 1) * nn; ++idx)
1188  elmConn.emplace_back(eb.conn[idx]);
1189  glbConn.emplace_back(elmConn);
1190  }
1191 
1192  _elmBlks.push_back(eb);
1193  }
1194 
1195  // node sets
1196  for (int iNS = 0; iNS < numNdeSets; ++iNS) {
1197  ndeSetType ns;
1198  ns.id = ndeSetIds[iNS];
1199  _exErr = ex_get_node_set_param(_fid, ns.id, &ns.nNde, &idum);
1200  wrnErrMsg(_exErr, "Problem reading node set parameters.\n");
1201  ns.name = _ndeSetNames[iNS];
1202 
1203  ns.ndeIdOffset = 0;
1204  ns.ndeIds.resize(ns.nNde, 0);
1205  _exErr = ex_get_node_set(_fid, ns.id, ns.ndeIds.data());
1206  wrnErrMsg(_exErr, "Problem reading node set node ids.\n");
1207  _ndeSets.push_back(ns);
1208  }
1209 
1210  // side sets
1211  for (int iSS = 0; iSS < numSdeSets; ++iSS) {
1212  sdeSetType ss;
1213  ss.id = sdeSetIds[iSS];
1214  _exErr = ex_get_side_set_param(_fid, ss.id, &ss.nSde, &idum);
1215  wrnErrMsg(_exErr, "Problem reading side set parameters.\n");
1216  ss.name = _sdeSetNames[iSS];
1217 
1218  ss.elmIdOffset = 0;
1219  ss.sdeIds.resize(ss.nSde, 0);
1220  ss.elmIds.resize(ss.nSde, 0);
1221  _exErr = ex_get_side_set(_fid, ss.id, ss.elmIds.data(), ss.sdeIds.data());
1222  wrnErrMsg(_exErr, "Problem reading side set element and side ids.\n");
1223  _sdeSets.push_back(ss);
1224  }
1225 
1226  report();
1227 
1228  // since all data structures are manually populated from the file
1229  _isPopulated = true;
1230 }
1231 
1232 void exoMesh::mergeNodes(double tol) {
1233  // build all point kdTree
1234  int nDim = 3;
1235  ANNpointArray pntCrd;
1236  pntCrd = annAllocPts(_numNdes, nDim);
1237  for (int iPnt = 0; iPnt < _numNdes; iPnt++) {
1238  pntCrd[iPnt][0] = _xCrds[iPnt];
1239  pntCrd[iPnt][1] = _yCrds[iPnt];
1240  pntCrd[iPnt][2] = _zCrds[iPnt];
1241  }
1242  ANNkd_tree *kdTree = new ANNkd_tree(pntCrd, _numNdes, nDim);
1243 
1244  // finding duplicate node ids
1245  std::map<int, int> dupNdeMap; // exoMesh is one-indexed
1246  int nNib = 2;
1247  ANNpoint qryPnt;
1248  ANNidxArray nnIdx = new ANNidx[nNib];
1249  ANNdistArray dists = new ANNdist[nNib];
1250  qryPnt = annAllocPt(nDim);
1251  for (int iNde = 0; iNde < _numNdes; iNde++) {
1252  qryPnt[0] = _xCrds[iNde];
1253  qryPnt[1] = _yCrds[iNde];
1254  qryPnt[2] = _zCrds[iNde];
1255  kdTree->annkSearch(qryPnt, nNib, nnIdx, dists);
1256  if (dists[1] <= tol) dupNdeMap[nnIdx[0] + 1] = nnIdx[1] + 1;
1257  }
1258  std::cout << "Found " << dupNdeMap.size() << " duplicate nodes.\n";
1259 
1260  // removing duplicated nodal coordinates
1261  std::vector<double> xn, yn, zn;
1262  std::map<int, int> old2NewNde;
1263  for (int iNde = 0; iNde < _numNdes; iNde++) {
1264  if (dupNdeMap.find(iNde + 1) == dupNdeMap.end()) {
1265  xn.push_back(_xCrds[iNde]);
1266  yn.push_back(_yCrds[iNde]);
1267  zn.push_back(_zCrds[iNde]);
1268  }
1269  }
1270 
1271  delete kdTree;
1272 
1273  // mapping
1274  for (int iPnt = 0; iPnt < xn.size(); iPnt++) {
1275  pntCrd[iPnt][0] = xn[iPnt];
1276  pntCrd[iPnt][1] = yn[iPnt];
1277  pntCrd[iPnt][2] = zn[iPnt];
1278  }
1279  kdTree = new ANNkd_tree(pntCrd, xn.size(), nDim);
1280 
1281  // creating map from old ids to new ids
1282  for (int iNde = 0; iNde < _numNdes; iNde++) {
1283  qryPnt[0] = _xCrds[iNde];
1284  qryPnt[1] = _yCrds[iNde];
1285  qryPnt[2] = _zCrds[iNde];
1286  kdTree->annkSearch(qryPnt, 1, nnIdx, dists);
1287  if (dists[0] <= tol) {
1288  old2NewNde[iNde + 1] = nnIdx[0] + 1;
1289  } else {
1290  std::cerr << "ERROR: Found a node in database not properly copied.\n";
1291  exit(1);
1292  }
1293  }
1294 
1295  // sanity check
1296  int max = 0;
1297  int min = std::numeric_limits<int>::max();
1298  for (const auto &im : old2NewNde) {
1299  max = std::max(max, im.second);
1300  min = std::min(min, im.second);
1301  }
1302  std::cout << "Max new node id = " << max << "\n";
1303  std::cout << "Min new node id = " << min << "\n";
1304 
1305  // update node coordinates
1306  _xCrds.clear();
1307  _yCrds.clear();
1308  _zCrds.clear();
1309  _xCrds.assign(xn.begin(), xn.end());
1310  _yCrds.assign(yn.begin(), yn.end());
1311  _zCrds.assign(zn.begin(), zn.end());
1312  std::cout << "Number of nodes changed from " << _numNdes << " to "
1313  << xn.size() << "\n";
1314  _numNdes = xn.size();
1315 
1316  // update element block connectivities
1317  max = 0;
1318  min = std::numeric_limits<int>::max();
1319  for (auto &&ieb : _elmBlks) {
1320  for (auto &&icn : ieb.conn) {
1321  int nid = icn;
1322  icn = old2NewNde[nid];
1323  max = std::max(max, icn);
1324  min = std::min(min, icn);
1325  }
1326  }
1327  std::cout << "Max conn = " << max << "\n";
1328  std::cout << "Min conn = " << min << "\n";
1329 
1330  // update node set ids
1331  for (auto &&ins : _ndeSets) {
1332  // updating node indices, removing duplicates
1333  std::set<int> newNSNdeIds; // set must have unique entries
1334 
1335  for (const auto &nid : ins.ndeIds) newNSNdeIds.insert(old2NewNde[nid]);
1336 
1337  ins.ndeIds.clear();
1338  ins.ndeIds.assign(newNSNdeIds.begin(), newNSNdeIds.end());
1339  std::cout << "Original node set with " << ins.nNde
1340  << " nodes after cleaning becomes " << ins.ndeIds.size() << "\n";
1341 
1342  // updating the nodeset data structure
1343  ins.nNde = ins.ndeIds.size();
1344  }
1345 
1346  // side sets do not need to be updated since they contain only element ids
1347 
1348  // deleting KDTree
1349  delete kdTree;
1350  delete[] nnIdx;
1351  delete[] dists;
1352 
1353  // Need to update the element list.
1354  exoPopulate(true);
1355 }
1356 
1357 void exoMesh::scaleNodes(double sc) {
1358  using nemAux::operator*; // for vector multiplication.
1359  _xCrds = sc * _xCrds;
1360  _yCrds = sc * _yCrds;
1361  _zCrds = sc * _zCrds;
1362 }
1363 
1364 void exoMesh::stitch(const exoMesh &otherMesh) {
1365  // Append nodes.
1366  _xCrds.insert(_xCrds.end(), otherMesh._xCrds.begin(), otherMesh._xCrds.end());
1367  _yCrds.insert(_yCrds.end(), otherMesh._yCrds.begin(), otherMesh._yCrds.end());
1368  _zCrds.insert(_zCrds.end(), otherMesh._zCrds.begin(), otherMesh._zCrds.end());
1369 
1370  // Append all element blocks with extra offset.
1371  for (const auto &elmBlk : otherMesh._elmBlks) {
1372  // offset by number of nodes in current mesh.
1373  auto offElmBlk = elmBlk;
1374  offElmBlk.ndeIdOffset += _numNdes;
1375 
1376  addElmBlk(offElmBlk);
1377  }
1378 
1379  // Append all node sets.
1380  for (const auto &ndeSet : otherMesh._ndeSets) {
1381  auto offNdeSet = ndeSet;
1382  offNdeSet.ndeIdOffset += _numNdes;
1383 
1384  addNdeSet(offNdeSet);
1385  }
1386 
1387  // Append all side sets.
1388  for (const auto &sdeSet : otherMesh._sdeSets) {
1389  auto offSdeSet = sdeSet;
1390  offSdeSet.elmIdOffset += _numElms;
1391 
1392  addSdeSet(offSdeSet);
1393  }
1394 
1395  // Increase node count. This is delayed as the old value is needed for adding
1396  // offset to blocks and sets.
1397  _numNdes += otherMesh._numNdes;
1398 
1399  // Full update to the database. Also updates element count _numElms
1400  exoPopulate(true);
1401 }
1402 
1404  auto eb = _elmBlks.begin();
1405  for (; eb != _elmBlks.end(); eb++)
1406  if (eb->id == id) return (eb->eTpe);
1407 
1408  // warning if the element block id is not registered
1409  if (eb == _elmBlks.end())
1410  std::cerr << "Warning: There is no element block with id " << id
1411  << std::endl;
1412 
1413  return (NEM::MSH::EXOMesh::elementType::OTHER);
1414 }
1415 
1416 elementType exoMesh::getElmBlkType(const std::string &ebName) const {
1417  auto eb = _elmBlks.begin();
1418  for (; eb != _elmBlks.end(); eb++)
1419  if (eb->name == ebName) return (eb->eTpe);
1420 
1421  // warning if the element block name is not registered
1422  if (eb == _elmBlks.end())
1423  std::cerr << "Warning: There is no element block with name " << ebName
1424  << std::endl;
1425 
1426  return (NEM::MSH::EXOMesh::elementType::OTHER);
1427 }
1428 
1429 int exoMesh::getNumElmsInBlk(const std::string &ebName) const {
1430  auto eb = _elmBlks.begin();
1431  for (; eb != _elmBlks.end(); eb++)
1432  if (eb->name == ebName) return (eb->nElm);
1433 
1434  // warning if the element block name is not registered
1435  if (eb == _elmBlks.end())
1436  std::cerr << "Warning: There is no element block with name " << ebName
1437  << std::endl;
1438 
1439  return -1;
1440 }
1441 
1442 int exoMesh::getNumElmsInBlkById(int id) const {
1443  auto eb = _elmBlks.begin();
1444  for (; eb != _elmBlks.end(); eb++)
1445  if (eb->id == id) return (eb->nElm);
1446 
1447  // warning if the element block name is not registered
1448  if (eb == _elmBlks.end())
1449  std::cerr << "Warning: There is no element block with ID " << id
1450  << std::endl;
1451 
1452  return -1;
1453 }
1454 
1455 const std::string exoMesh::getElmBlkNameById(int id) const {
1456  auto eb = _elmBlks.begin();
1457  for (; eb != _elmBlks.end(); eb++)
1458  if (eb->id == id) return (eb->name);
1459 
1460  // warning if the element block name is not registered
1461  if (eb == _elmBlks.end())
1462  std::cerr << "Warning: There is no element block with ID " << id
1463  << std::endl;
1464 
1465  return "";
1466 }
1467 
1468 int exoMesh::getElmBlkIndex(int id) const {
1469  int numElmBlks = getNumberOfElementBlocks();
1470  for (int i = 0; i < numElmBlks; ++i) {
1471  if (_elmBlks[i].id == id) return i;
1472  }
1473  return -1;
1474 }
1475 
1476 int exoMesh::getElmBlkIndex(std::string name) const {
1477  int numElmBlks = getNumberOfElementBlocks();
1478  for (int i = 0; i < numElmBlks; ++i) {
1479  if (_elmBlks[i].name == name) return i;
1480  }
1481  return -1;
1482 }
1483 
1484 int exoMesh::getElmBlkId(std::string ebName) const {
1485  auto eb = _elmBlks.begin();
1486  for (; eb != _elmBlks.end(); eb++)
1487  if (eb->name == ebName) return (eb->id);
1488 
1489  // warning if the element block name is not registered
1490  if (eb == _elmBlks.end())
1491  std::cerr << "Warning: There is no element block with name " << ebName
1492  << std::endl;
1493 
1494  return -1;
1495 }
1496 
1497 std::string exoMesh::getNdeSetNameById(int id) const {
1498  auto ns = _ndeSets.begin();
1499  for (; ns != _ndeSets.end(); ns++)
1500  if (ns->id == id) return (ns->name);
1501 
1502  // warning if the element block name is not registered
1503  if (ns == _ndeSets.end())
1504  std::cerr << "Warning: There is no nodeset with ID " << id << std::endl;
1505 
1506  return "";
1507 }
1508 
1509 int exoMesh::getNumNdesInNdeSet(const std::string &nsName) const {
1510  auto ns = _ndeSets.begin();
1511  for (; ns != _ndeSets.end(); ns++)
1512  if (ns->name == nsName) return (ns->nNde);
1513 
1514  // warning if the element block name is not registered
1515  if (ns == _ndeSets.end())
1516  std::cerr << "Warning: There is no nodeset with name " << nsName
1517  << std::endl;
1518 
1519  return -1;
1520 }
1521 
1523  auto ns = _ndeSets.begin();
1524  for (; ns != _ndeSets.end(); ns++)
1525  if (ns->id == id) return (ns->nNde);
1526 
1527  // warning if the element block name is not registered
1528  if (ns == _ndeSets.end())
1529  std::cerr << "Warning: There is no nodeset with ID " << id << std::endl;
1530 
1531  return -1;
1532 }
1533 
1534 int exoMesh::getNdeSetId(const std::string &nsName) const {
1535  auto ns = _ndeSets.begin();
1536  for (; ns != _ndeSets.end(); ns++)
1537  if (ns->name == nsName) return (ns->id);
1538 
1539  // warning if the element block name is not registered
1540  if (ns == _ndeSets.end())
1541  std::cerr << "Warning: There is no nodeset with name " << nsName
1542  << std::endl;
1543 
1544  return -1;
1545 }
1546 
1547 int exoMesh::getNdeSetIndex(const std::string &nsName) const {
1548  auto ns = _ndeSets.begin();
1549  for (; ns != _ndeSets.end(); ns++)
1550  if (ns->name == nsName) return ns - _ndeSets.begin();
1551 
1552  // warning if the element block name is not registered
1553  if (ns == _ndeSets.end())
1554  std::cerr << "Warning: There is no nodeset with name " << nsName
1555  << std::endl;
1556 
1557  return -1;
1558 }
1559 
1560 int exoMesh::getSdeSetId(const std::string &ssName) const {
1561  auto ss = _sdeSets.begin();
1562  for (; ss != _sdeSets.end(); ss++)
1563  if (ss->name == ssName) return (ss->id);
1564 
1565  // warning if the sideset name is not registered
1566  if (ss == _sdeSets.end())
1567  std::cerr << "Warning: There is no sideset with name " << ssName
1568  << std::endl;
1569 
1570  return -1;
1571 }
1572 
1573 int exoMesh::getSdeSetIndex(const std::string &ssName) const {
1574  auto ss = _sdeSets.begin();
1575  for (; ss != _sdeSets.end(); ss++)
1576  if (ss->name == ssName) return ss - _sdeSets.begin();
1577 
1578  // warning if the sideset name is not registered
1579  if (ss == _sdeSets.end())
1580  std::cerr << "Warning: There is no sideset with name " << ssName
1581  << std::endl;
1582 
1583  return -1;
1584 }
1585 
1586 int exoMesh::getNumSdesInSdeSet(const std::string &ssName) const {
1587  auto ss = _sdeSets.begin();
1588  for (; ss != _sdeSets.end(); ss++)
1589  if (ss->name == ssName) return (ss->nSde);
1590 
1591  // warning if the element block name is not registered
1592  if (ss == _sdeSets.end())
1593  std::cerr << "Warning: There is no sideset with name " << ssName
1594  << std::endl;
1595 
1596  return -1;
1597 }
1598 
1600  auto ss = _sdeSets.begin();
1601  for (; ss != _sdeSets.end(); ss++)
1602  if (ss->id == id) return (ss->nSde);
1603 
1604  // warning if the element block name is not registered
1605  if (ss == _sdeSets.end())
1606  std::cerr << "Warning: There is no sideset with ID " << id << std::endl;
1607 
1608  return -1;
1609 }
1610 
1611 } // namespace EXOMesh
1612 } // namespace MSH
1613 } // namespace NEM
int getElmBlkId(int idx) const
Returns the id for the element block for given index.
Definition: exoMesh.H:267
int getNumSdesInSdeSet(int idx) const
Returns the number of sides for the sideset.
Definition: exoMesh.H:369
void updateSidesets(const std::map< int, int > &old2NewElmIds)
Updates sidesets from combining blocks.
Definition: exoMesh.C:745
const std::string getElmBlkNameById(int id) const
Returns the name the element block for given element block ID.
Definition: exoMesh.C:1455
std::vector< int > _elmBlkIds
Definition: exoMesh.H:713
void addElmBlkByElmIdLst(const std::string &name, std::vector< int > &idLst)
Creates a new element block and augments previous owners.
Definition: exoMesh.C:807
elementType v2eEMap(VTKCellType vt)
Convert VTK cell type to EXODUS element type.
Definition: exoMesh.C:61
int getNumSdesInSdeSetById(int id) const
Returns the number of sides for the sideset.
Definition: exoMesh.C:1599
surfaceBCTag
Definition: pntMesh.H:62
void combineElmBlks(const std::vector< int > &blkIdLst, const std::string &newName)
Combines element blocks into one block.
Definition: exoMesh.C:448
elementType getElmBlkType(int idx) const
Returns the element type for the block.
Definition: exoMesh.H:315
std::vector< std::string > _ndeSetNames
Definition: exoMesh.H:717
std::vector< double > _xCrds
Definition: exoMesh.H:705
int findElmBlkIdxByElmIdLst(const std::vector< int > &elmIds) const
Finds index of the block containing most or all of the list (slower)
Definition: exoMesh.C:938
A complete I/O class for EXODUS II file format.
Definition: exoMesh.H:172
std::vector< int > sdeIds
Definition: exoMesh.H:102
int getNumElmsInBlkById(int id) const
Returns the number of elements for the block.
Definition: exoMesh.C:1442
int getNumberOfNodeSets() const
Returns total number of node sets.
Definition: exoMesh.H:212
int getNdeSetId(int idx) const
Returns the id for the nodeset for given index.
Definition: exoMesh.H:279
std::string elmTypeStr(elementType et)
Convert EXODUS element type to string tab.
Definition: exoMesh.C:128
STL namespace.
int elmNumSrf(elementType tag)
Get number of surfaces given EXODUS element type.
Definition: exoMesh.C:154
std::vector< int > elmIds
Definition: exoMesh.H:101
elementType elmTypeNum(std::string tag)
Convert string to EXODUS element type.
Definition: exoMesh.C:97
std::map< T, int > findDuplicates(const std::vector< T > &vecOfElements)
Definition: exoMesh.H:680
WEDGE
Definition: exoMesh.H:55
std::string getNdeSetNameById(int id) const
Returns the name the nodeset for given nodeset id.
Definition: exoMesh.C:1497
QUAD
Definition: exoMesh.H:55
HEX
Definition: exoMesh.H:55
int getNumberOfSideSets() const
Returns total number of side sets.
Definition: exoMesh.H:217
void write()
Write to file name specified at construction or using setFileName method.
Definition: exoMesh.C:213
int getNumElmsInBlk(int idx) const
Returns the number of elements for the block.
Definition: exoMesh.H:333
void snapNdeCrdsZero(double tol=1e-5)
Filter nodal coordinates and snap to zero.
Definition: exoMesh.C:876
void removeByElmIdLst(int blkIdx, const std::vector< int > &idLst)
Removes a list of elements from an element block.
Definition: exoMesh.C:758
Stores side set information.
Definition: exoMesh.H:97
TETRA
Definition: exoMesh.H:55
std::vector< std::string > _elmBlkNames
Definition: exoMesh.H:712
surfaceBCTag bcTagNum(std::string &tag)
Definition: exoMesh.C:76
SYMMY
Definition: exoMesh.H:66
void toLower(std::string &str)
vtkIdType id
id in .inp file
Definition: inpGeoMesh.C:128
std::string bcTagStr(surfaceBCTag tag)
Definition: exoMesh.C:86
int getNdeSetIndex(const std::string &nsName) const
Returns the index for the nodeset for nodeset name.
Definition: exoMesh.C:1547
int getSdeSetIndex(const std::string &ssName) const
Returns the index of the sideset for sideset name.
Definition: exoMesh.C:1573
int getNumNdesInNdeSet(int idx) const
Returns the number of nodes for the nodeset.
Definition: exoMesh.H:351
void addNdeSetByNdeIdLst(const std::string &name, const std::vector< int > &idLst)
Creates a new node set and augments previous ones if needed.
Definition: exoMesh.C:857
Stores element block information.
Definition: exoMesh.H:82
SYMMX
Definition: exoMesh.H:66
FIXED
Definition: exoMesh.H:66
void exoPopulate(bool updElmLst=false)
Definition: exoMesh.C:309
std::vector< int > lstElmInBlk(int blkIdx, const std::vector< int > &elmIds, bool &allIn) const
Finds all elements that are within the block and generates a list of them.
Definition: exoMesh.C:967
int findElmBlkIdxByElmId(int elmId) const
Finds index of the first block containing element.
Definition: exoMesh.C:927
void addSdeSet(const sdeSetType &ss)
Add side set to the database.
Definition: exoMesh.H:520
std::vector< std::vector< int > > glbConn
Definition: exoMesh.H:737
std::vector< double > _zCrds
Definition: exoMesh.H:707
void stitch(const exoMesh &otherMesh)
Stitch another mesh into the current.
Definition: exoMesh.C:1364
std::vector< std::string > _sdeSetNames
Definition: exoMesh.H:721
void addElmBlk(const elmBlkType &eb)
Add element block to the database.
Definition: exoMesh.H:504
void addNdeSet(const ndeSetType &ns)
Add node set to the database.
Definition: exoMesh.H:512
elementType
Definition: pntMesh.H:47
void reset()
Resets the EXODUS database.
Definition: exoMesh.C:985
TRIANGLE
Definition: exoMesh.H:55
Stores node set information.
Definition: exoMesh.H:71
int getElmBlkIndex(int id) const
Returns the index for the element block for given id.
Definition: exoMesh.C:1468
void scaleNodes(double sc=1.0)
scales the nodal coordinates
Definition: exoMesh.C:1357
std::vector< int > ndeIds
Definition: exoMesh.H:75
elementType getElmBlkTypeById(int id) const
Returns the element type for the block.
Definition: exoMesh.C:1403
void removeElmBlkById(int id)
Remove an element block by ID.
Definition: exoMesh.C:915
void report() const
Print out a report of the current EXODUS database.
Definition: exoMesh.C:412
int getNumberOfElements() const
Returns total number of elements.
Definition: exoMesh.H:202
VTKCellType e2vEMap(elementType et)
Convert EXODUS element type to VTK cell type.
Definition: exoMesh.C:50
void read(const std::string &ifname=std::string())
Resets the class and reads from file name provided.
Definition: exoMesh.C:1003
void mergeNodes(double tol=1e-15)
Merges duplicated and nodes within given proximity.
Definition: exoMesh.C:1232
void removeElmBlkByName(const std::string &blkName)
Remove an element block by name.
Definition: exoMesh.C:901
int elmNumNde(elementType tag, int order)
Get number of nodes given EXODUS element type and order.
Definition: exoMesh.C:138
int getNumberOfElementBlocks() const
Returns total number of element blocks.
Definition: exoMesh.H:207
void wrnErrMsg(int errCode, const std::string &msg)
Logging method.
Definition: exoMesh.C:202
std::vector< double > _yCrds
Definition: exoMesh.H:706
std::map< int, std::vector< double > > ndeCoords
Definition: exoMesh.H:91
int getSdeSetId(int idx) const
Returns the id for the sideset for given index.
Definition: exoMesh.H:297
std::vector< int > conn
Definition: exoMesh.H:88
int getNumNdesInNdeSetById(int id) const
Returns the number of nodes for the nodeset.
Definition: exoMesh.C:1522
std::vector< int > elmIds
Definition: exoMesh.H:90