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.
patran.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 <algorithm>
30 
31 #include "Mesh/patran.H"
32 
33 #include <vtkCell.h>
34 #include <vtkCellData.h>
35 #include <vtkCellTypes.h>
36 #include <vtkThreshold.h>
37 #include <vtkGeometryFilter.h>
38 #include <vtkGenericCell.h>
39 #include <vtkPoints.h>
40 #include <vtkUnstructuredGrid.h>
41 
42 #include "AuxiliaryFunctions.H"
43 
44 using nemAux::operator-; // for vector subtraction.
45 
46 //////////////////////////////////
47 // patran class
48 //////////////////////////////////
49 
50 
51 // Write title card (packet 25)
52 void PATRAN::patran::write25(std::ofstream &outputStream) const
53 {
54  std::cout << "writing packet 25..." << std::endl;
55 
56  // write header card
57  std::string defaultHeader = "25 0 0 1 0 0 0 0 0";
58  outputStream << defaultHeader;
59  outputStream << std::endl;
60 
61  // write title card
62  outputStream << outFnameNeu;
63  outputStream << std::endl;
64 }
65 
66 // Write summary data (packet 26)
67 void PATRAN::patran::write26(std::ofstream &outputStream) const
68 {
69  std::cout << "writing packet 26..." << std::endl;
70 
71  // write header card
72  outputStream << "26";
73  for (int i = 0; i < 8; i++)
74  {
75  switch (i)
76  {
77  case 2:
78  outputStream << std::setw(8) << std::right << "1";
79  break;
80  case 3:
81  outputStream << std::setw(8) << std::right
82  << volMeshBase->getDataSet()->GetNumberOfPoints();
83  break;
84  case 4:
85  outputStream << std::setw(8) << std::right
86  << volMeshBase->getDataSet()->GetNumberOfCells();
87  break;
88  default:
89  outputStream << std::setw(8) << std::right << "0";
90  break;
91  }
92  }
93  outputStream << std::endl;
94 
95  // Write time stamp info card
96  std::time_t t = std::time(nullptr);
97  std::tm *now = std::localtime(&t);
98  outputStream << std::setw(12) << std::left << (now->tm_year + 1900) << '-'
99  << (now->tm_mon + 1) << '-' << now->tm_mday;
100  outputStream << std::endl;
101 }
102 
103 // Write node data (packet 1)
104 void PATRAN::patran::write1(std::ofstream &outputStream) const
105 {
106  std::cout << "writing packet 1..." << std::endl;
107 
108  for (int iNode = 0;
109  iNode < volMeshBase->getDataSet()->GetNumberOfPoints(); iNode++)
110  {
111  // Write header card
112  // packet no
113  outputStream << " 1";
114  // node id (1-indexed)
115  outputStream << std::setw(8) << std::right << iNode + 1;
116  // IV, default to 0
117  outputStream << std::setw(8) << std::right << "0";
118  // KC, default to 2
119  outputStream << std::setw(8) << std::right << "2";
120  // fill in zeros for remaining
121  for (int i = 0; i < 5; i++)
122  {
123  outputStream << std::setw(8) << std::right << "0";
124  }
125  outputStream << std::endl;
126 
127  // Write data card 1
128  // Get node coordinates
129  double coords[3];
130  volMeshBase->getDataSet()->GetPoint(iNode, coords);
131  for (double coord : coords)
132  {
133  char buffer[17];
134  snprintf(buffer, 17, "%16.9E", coord);
135  outputStream << buffer;
136  }
137  outputStream << std::endl;
138 
139  // Write data card 2
140  // currently defaults to ICF=1, GTYPE=G for first two
141  outputStream << "1G";
142  // degrees of freedom
143  outputStream << std::setw(8) << std::right << "6";
144  // node configuration
145  outputStream << std::setw(8) << std::right << "0";
146  // CID (coordinate frame for analysis results)
147  outputStream << std::setw(8) << std::right << "0";
148  // Write two white spaces
149  outputStream << std::setw(2) << std::right << "";
150  // PSPC, permanent single point constraining flags
151  outputStream << std::setw(6) << std::right << "000000";
152  outputStream << std::endl;
153  }
154 }
155 
156 // Write element data (packet 2)
157 void PATRAN::patran::write2(std::ofstream &outputStream) const
158 {
159  std::cout << "writing packet 2..." << std::endl;
160 
161  for (int iCell = 0;
162  iCell < volMeshBase->getDataSet()->GetNumberOfCells(); iCell++)
163  {
164  // Note: only tetrahedral elements are written
165 
166  // Get element type
167  double elemType[1];
168  volMeshBase->getDataSet()->GetCellData()->GetArray("elemType")
169  ->GetTuple(iCell, elemType);
170  if (elemType[0] == VTK_TETRA)
171  {
172  // Write header card
173  // packet no
174  std::string defaultHeader = " 2";
175  outputStream << defaultHeader;
176  // cell id
177  outputStream << std::setw(8) << std::right << iCell + 1;
178  // IV, default to 5 (tetrahedral)
179  outputStream << std::setw(8) << std::right << "5";
180  // KC, default to 2
181  outputStream << std::setw(8) << std::right << "2";
182  // fill in zeros (defaulting N1 and N2 to zero)
183  for (int i = 0; i < 5; i++)
184  {
185  outputStream << std::setw(8) << std::right << "0";
186  }
187  outputStream << std::endl;
188 
189  // Write data card 1
190  // Default to 4 nodes per tetrahedral
191  outputStream << std::setw(8) << std::right << "4";
192  // CONFIG, element type (unused)
193  outputStream << std::setw(8) << std::right << "0";
194  // PID, Property ID (unused)
195  outputStream << std::setw(8) << std::right << "0";
196  // CEID, Congruent Element ID (unused)
197  outputStream << std::setw(8) << std::right << "0";
198  // Material Orientation Angles (unused)
199  for (int i = 0; i < 3; i++)
200  {
201  char buffer[17];
202  snprintf(buffer, 17, "%16.9E", 0.0);
203  outputStream << buffer;
204  }
205  outputStream << std::endl;
206 
207  // Write data Card 2
208  // Get tetrahedral cell points
209  // Pts beyond 4th can be omitted (don't need to write buffer zeros)
210  vtkSmartPointer<vtkIdList> cellPtIds = vtkSmartPointer<vtkIdList>::New();
211  volMeshBase->getDataSet()->GetCellPoints(iCell, cellPtIds);
212  for (int iPt = 0; iPt < cellPtIds->GetNumberOfIds(); iPt++)
213  {
214  outputStream << std::setw(8) << std::right << cellPtIds->GetId(iPt) + 1;
215  }
216  outputStream << std::endl;
217 
218  // Data Card 3 is ignored
219  }
220  }
221 }
222 
223 // Write distributed loads (packet 6)
224 void PATRAN::patran::write6(std::ofstream &outputStream)
225 {
226  std::cout << "writing packet 6..." << std::endl;
227 
228  vtkSmartPointer<vtkIdList> facePtIds;
229  vtkSmartPointer<vtkIdList> sharedCellPtIds = vtkSmartPointer<vtkIdList>::New();
230  vtkSmartPointer<vtkGenericCell> genCell1 = vtkSmartPointer<vtkGenericCell>::New();
231  vtkSmartPointer<vtkGenericCell> genCell2 = vtkSmartPointer<vtkGenericCell>::New();
232 
233  // building cell locator for looking up patch number in remeshed surface mesh
234  vtkSmartPointer<vtkStaticCellLocator> surfCellLocator = surfMeshBase->buildStaticCellLocator();
235  // maximum number of vertices per face (to be found in proceeding loop)
236  vtkIdType nVerticesPerFaceMax = 0;
237  // maximum number of faces per cell (to be found in proceeding loop)
238  int nFacesPerCellMax = 0;
239 
240  for (nemId_t i = 0; i < volMeshBase->getNumberOfCells(); ++i)
241  {
242  // get cell i
243  volMeshBase->getDataSet()->GetCell(i, genCell1);
244  // get faces, find cells sharing it. if no cell shares it,
245  // use the locator of the surfMeshBase to find the patch number
246  int numFaces = genCell1->GetNumberOfFaces();
247  nFacesPerCellMax = (nFacesPerCellMax < numFaces
248  ? numFaces
249  : nFacesPerCellMax);
250  auto surfPatchNoArr =
251  surfMeshBase->getDataSet()->GetCellData()->GetArray("patchNo");
252  if (!surfPatchNoArr) {
253  surfPatchNoArr =
254  surfMeshBase->getDataSet()->GetCellData()->GetArray("PhysGrpId");
255  }
256  for (int j = 0; j < numFaces; ++j)
257  {
258  vtkCell *faceObj = genCell1->GetFace(j);
259  // bool shared = false;
260  vtkIdType numVerts = faceObj->GetNumberOfPoints();
261  nVerticesPerFaceMax = (nVerticesPerFaceMax < numVerts
262  ? numVerts
263  : nVerticesPerFaceMax);
264  facePtIds = faceObj->GetPointIds();
265 
266  volMeshBase->getDataSet()->GetCellNeighbors(i, facePtIds,
267  sharedCellPtIds);
268  std::vector<nemId_t> facePntIds(numVerts);
269  for (vtkIdType k = 0; k < numVerts; ++k)
270  {
271  facePntIds[k] = faceObj->GetPointId(k) + 1;
272  }
273  if (sharedCellPtIds->GetNumberOfIds() == 0)
274  {
275  double bounds[6];
276  faceObj->GetBounds(bounds);
277  auto cellIds = vtkSmartPointer<vtkIdList>::New();
278  surfCellLocator->FindCellsWithinBounds(bounds, cellIds);
279  vtkIdType closestCellId;
280  if (cellIds->GetNumberOfIds() == 0) {
281  std::cerr << "No surface cell found." << std::endl;
282  continue;
283  } else {
284  closestCellId = cellIds->GetId(0);
285  if (cellIds->GetNumberOfIds() > 1) {
286  double p1[3], p2[3], p3[3];
287  faceObj->GetPoints()->GetPoint(0, p1);
288  faceObj->GetPoints()->GetPoint(1, p2);
289  faceObj->GetPoints()->GetPoint(2, p3);
290  std::vector<double> faceCenter(3);
291  for (vtkIdType k = 0; k < numVerts; ++k) {
292  faceCenter[k] = (p1[k] + p2[k] + p3[k]) / 3.0;
293  }
294  double minDist = nemAux::l2_Norm(
295  faceCenter - surfMeshBase->getCellCenter(cellIds->GetId(0)));
296  for (vtkIdType k = 1; k < cellIds->GetNumberOfIds(); ++k) {
297  auto candidateCellCenter =
298  surfMeshBase->getCellCenter(cellIds->GetId(k));
299  auto dist = nemAux::l2_Norm(faceCenter - candidateCellCenter);
300  if (dist < minDist) {
301  minDist = dist;
302  closestCellId = cellIds->GetId(k);
303  }
304  }
305  }
306  }
307  double patchNo = surfPatchNoArr->GetComponent(closestCellId, 0);
308 
309  // Assign patch numbers to all nodes in the face
310  for (vtkIdType iFaceNode = 0;
311  iFaceNode < facePtIds->GetNumberOfIds(); iFaceNode++)
312  {
313  boundaryNodeId2PatchNo[facePtIds->GetId(iFaceNode)].push_back(
314  static_cast<int>(patchNo));
315  }
316 
317  // Write header card
318  // packet number
319  outputStream << std::setw(2) << std::right << "6";
320  // element number, 1-indexed
321  outputStream << std::setw(8) << std::right << i + 1;
322  // default load set = 1
323  outputStream << std::setw(8) << std::right << "1";
324  // default KC = 2
325  outputStream << std::setw(8) << std::right << "2";
326  // Fill in remaining zeros
327  for (int i = 0; i < 5; i++)
328  {
329  outputStream << std::setw(8) << std::right << "0";
330  }
331  outputStream << std::endl;
332 
333  // Write data card 1
334  // default face value
335  outputStream << std::setw(3) << std::right
336  << "110"; // unused by Rocfrac
337 
338  // load direction
339  outputStream << std::setw(6) << std::right
340  << "000000"; // ICOMP, unused by Rocfrac
341 
342  // face nodes
343  outputStream << std::setw(8) << std::right << face2nodes[j];
344 
345  // face number
346  outputStream << std::setw(2) << std::right
347  << "1"; // NFE, unused by Rocfrac
348  outputStream << std::endl;
349 
350  // Write data card 2
351  // card number
352  char buffer[17];
353  snprintf(buffer, 17, "%16.9E", (double) faceTypeMap[static_cast<int>(patchNo)]);
354  outputStream << buffer;
355 
356  outputStream << std::endl;
357  }
358  }
359  }
360 }
361 
362 // compare patches in nppVec to determine preference
364 {
365  auto posi = find(nppVec.begin(), nppVec.end(), i);
366  auto posj = find(nppVec.begin(), nppVec.end(), j);
367  return posi - nppVec.begin() < posj - nppVec.begin();
368 }
369 
370 // Write node BCs: structural, mesh motion, and thermal (packet 8)
371 void PATRAN::patran::write8(std::ofstream &outputStream)
372 {
373  std::cout << "writing packet 8..." << std::endl;
374 
375  for (const auto &nodeItr : boundaryNodeId2PatchNo)
376  {
377  nemId_t iNode = nodeItr.first;
378 
379  vtkSmartPointer<vtkIdList> pointCellIds = vtkSmartPointer<vtkIdList>::New();
380  volMeshBase->getDataSet()->GetPointCells(iNode, pointCellIds);
381 
382  // Write header card
383  // packet number
384  outputStream << std::setw(2) << std::right << "8";
385  // node number, 1-indexed
386  outputStream << std::setw(8) << std::right << iNode + 1;
387  // default load set = 1
388  outputStream << std::setw(8) << std::right << "1";
389  // default KC = 2
390  outputStream << std::setw(8) << std::right << "2";
391  // Fill in remaining zeros
392  for (int i = 0; i < 5; i++)
393  {
394  outputStream << std::setw(8) << std::right << "0";
395  }
396  outputStream << std::endl;
397 
398  // Get patch no
399  std::vector<int> patchNoVec;
400  for (int &patchItr : boundaryNodeId2PatchNo[iNode])
401  patchNoVec.push_back(patchItr);
402 
403  // sort patches
404  // int outFlag = 0;
405  int patchVal = -1;
406  for (auto patchItr = patchNoVec.begin();
407  patchItr != patchNoVec.end() - 1; ++patchItr)
408  {
409  // int firstVal = *patchItr;
410  if (patchVal == -1)
411  patchVal = *patchItr;
412  else
413  if (comparePatch(*patchItr, patchVal))
414  patchVal = *patchItr;
415  }
416  int patchNo[1] = {patchVal};
417 
418  // Write data card 1
419  // coordinate frame id, set default to 0
420  outputStream << std::setw(8) << std::right
421  << "0"; // default CID, unused by Rocfrac
422  std::string structuralFlag, thermalFlag, meshMotionFlag;
423  int nFlags = 0;
424  if (nodeStructuralMap[patchNo[0]])
425  {
426  structuralFlag = "1";
427  nFlags++;
428  }
429  else
430  {
431  structuralFlag = "0";
432  }
433  if (nodeThermalMap[patchNo[0]])
434  {
435  thermalFlag = "1";
436  nFlags++;
437  }
438  else
439  {
440  thermalFlag = "0";
441  }
442  if (nodeMeshMotionMap[patchNo[0]])
443  {
444  meshMotionFlag = "1";
445  nFlags++;
446  }
447  else
448  {
449  meshMotionFlag = "0";
450  }
451 
452  outputStream << std::setw(6) << std::right
453  << structuralFlag + thermalFlag + meshMotionFlag + "000";
454  outputStream << std::endl;
455 
456  // Write data card 2
457  // card number
458  for (int i = 0; i < nFlags; i++)
459  {
460  char buffer[17];
461  snprintf(buffer, 17, "%16.9E", (double) nodeTypeMap[patchNo[0]]);
462  outputStream << buffer;
463  }
464  outputStream << std::endl;
465  }
466 }
467 
468 // Write "end of file" line (packet 99)
469 void PATRAN::patran::write99(std::ofstream &outputStream) const
470 {
471  outputStream
472  << "99 0 0 1 0 0 0 0 0";
473  outputStream << std::endl;
474 }
475 
476 // constructor from meshBase object
477 PATRAN::patran::patran(const std::shared_ptr<meshBase> _fullMesh,
478  const std::string &_inFnameVtk,
479  const std::string &_outFnameNeu,
480  const std::map<int, int> &_faceTypeMap,
481  const std::map<int, int> &_nodeTypeMap,
482  const std::map<int, bool> &_nodeStructuralMap,
483  const std::map<int, bool> &_nodeMeshMotionMap,
484  const std::map<int, bool> &_nodeThermalMap,
485  const std::vector<int> &_nppVec)
486 {
487  std::cout << "Writing Patran file in Rocfrac input format..." << std::endl;
488 
489  fullMesh = _fullMesh;
490  inFnameVtk = _inFnameVtk;
491  outFnameNeu = _outFnameNeu;
492  faceTypeMap = _faceTypeMap;
493  nodeTypeMap = _nodeTypeMap;
494  nodeStructuralMap = _nodeStructuralMap;
495  nodeMeshMotionMap = _nodeMeshMotionMap;
496  nodeThermalMap = _nodeThermalMap;
497  nppVec = _nppVec;
498 
499  // Face/Node ordering convention: face2Nodes[faceNo] = nodes used; 1 = used, 0 = unused
500  face2nodes[0] = "11010000";
501  face2nodes[1] = "01110000";
502  face2nodes[2] = "10110000";
503  face2nodes[3] = "11100000";
504 
505  //if (inFnameVtk.find(".vt") != -1)
506  //{
507  // // get trimmed filename
508  // size_t lastindex = inFnameVtk.find_last_of(".");
509  //}
510  //else
511  //{
512  // std::cout << "File must be in VTK format." << std::endl;
513  // exit(1);
514  //}
515 
516  // instantiate output stream for writing
517  std::ofstream outputStream(outFnameNeu);
518  if (!outputStream.good())
519  {
520  std::cout << "Cannot open file " << outFnameNeu << std::endl;
521  exit(1);
522  }
523 
524  // declare array to store element type
525  vtkSmartPointer<vtkDataArray> elementTypeArray = vtkSmartPointer<vtkIdTypeArray>::New();
526  elementTypeArray->SetNumberOfComponents(1);
527  elementTypeArray->SetName("elemType");
528 
529  // get element types
530  double tmp[1]; // storing inserted tuples
531  for (vtkIdType iCell = 0;
532  iCell < fullMesh->getDataSet()->GetNumberOfCells(); iCell++)
533  {
534  if (fullMesh->getDataSet()->GetCellType(iCell) != VTK_TETRA
535  && fullMesh->getDataSet()->GetCellType(iCell) != VTK_TRIANGLE)
536  {
537  std::cout << "Error: only triangle and tetrahedral elements supported."
538  << std::endl;
539  exit(1);
540  }
541  tmp[0] = fullMesh->getDataSet()->GetCellType(iCell);
542  elementTypeArray->InsertNextTuple(tmp);
543  }
544  fullMesh->getDataSet()->GetCellData()->AddArray(elementTypeArray);
545 
546  // threshold to extract only volume cells
547  vtkSmartPointer<vtkThreshold> volThreshold = vtkSmartPointer<vtkThreshold>::New();
548  volThreshold->SetInputData(fullMesh->getDataSet());
549  volThreshold->SetInputArrayToProcess(0, 0, 0,
550  vtkDataObject::FIELD_ASSOCIATION_CELLS,
551  "elemType");
552  volThreshold->ThresholdBetween(VTK_TETRA, VTK_TETRA);
553  volThreshold->Update();
554  // store output in unstructured grid
555  vtkSmartPointer<vtkUnstructuredGrid> volUG = volThreshold->GetOutput();
556  // create meshbase object
557  volMeshBase = meshBase::CreateShared(volUG, "extractedVolume.vtu");
558  //volMeshBase->write(); // write to file
559 
560  // threshold to extract only surface cells
561  vtkSmartPointer<vtkThreshold> surfThreshold = vtkSmartPointer<vtkThreshold>::New();
562  surfThreshold->SetInputData(fullMesh->getDataSet());
563  surfThreshold->SetInputArrayToProcess(0, 0, 0,
564  vtkDataObject::FIELD_ASSOCIATION_CELLS,
565  "elemType");
566  surfThreshold->ThresholdBetween(VTK_TRIANGLE, VTK_TRIANGLE);
567  surfThreshold->Update();
568  // store output in unstructured grid
569  vtkSmartPointer<vtkUnstructuredGrid> surfUG = surfThreshold->GetOutput();
570  // geometry filter to polydata object
571  vtkSmartPointer<vtkGeometryFilter> geomFilter = vtkGeometryFilter::New();
572  geomFilter->SetInputData(surfUG);
573  geomFilter->Update();
574  vtkSmartPointer<vtkPolyData> surfPD = geomFilter->GetOutput();
575  // create meshbase object
576  surfMeshBase = meshBase::CreateShared(surfPD, "extractedSurface.vtp");
577  //surfMeshBase->write(); // write to file
578 
579  // Write Patran file packets
580  std::cout << "Writing Patran file..." << std::endl;
581 
582  // write title card
583  write25(outputStream);
584 
585  // write summary data
586  write26(outputStream);
587 
588  // write node data
589  write1(outputStream);
590 
591  // write element data
592  write2(outputStream);
593 
594  // write distributed load BCs
595  write6(outputStream);
596 
597  // write all node BCs
598  write8(outputStream);
599 
600  // write end of file flag
601  write99(outputStream);
602 }
std::string outFnameNeu
Definition: patran.H:63
std::shared_ptr< meshBase > surfMeshBase
Definition: patran.H:65
std::map< int, std::string > face2nodes
Definition: patran.H:72
void write2(std::ofstream &outputStream) const
Definition: patran.C:157
std::size_t nemId_t
Definition: meshBase.H:51
std::vector< int > nppVec
Definition: patran.H:71
geoMeshBase * New(MeshType meshType)
Create a new mesh object.
std::map< int, int > faceTypeMap
Definition: patran.H:66
void write8(std::ofstream &outputStream)
Definition: patran.C:371
std::string inFnameVtk
Definition: patran.H:62
void write25(std::ofstream &outputStream) const
Definition: patran.C:52
std::map< int, bool > nodeStructuralMap
Definition: patran.H:68
patran(std::shared_ptr< meshBase > fullMesh, const std::string &inFnameVtk, const std::string &outFnameNeu, const std::map< int, int > &faceTypeMap, const std::map< int, int > &nodeTypeMap, const std::map< int, bool > &nodeStructuralMap, const std::map< int, bool > &nodeMeshMotionMap, const std::map< int, bool > &nodeThermalMap, const std::vector< int > &nppItr)
Definition: patran.C:477
std::map< int, bool > nodeMeshMotionMap
Definition: patran.H:69
static std::shared_ptr< meshBase > CreateShared(const std::string &fname)
Create shared ptr from fname.
Definition: meshBase.C:171
bool comparePatch(int i, int j)
Definition: patran.C:363
void write1(std::ofstream &outputStream) const
Definition: patran.C:104
std::map< int, int > nodeTypeMap
Definition: patran.H:67
void write26(std::ofstream &outputStream) const
Definition: patran.C:67
void write99(std::ofstream &outputStream) const
Definition: patran.C:469
std::map< nemId_t, std::vector< int > > boundaryNodeId2PatchNo
Definition: patran.H:73
void write6(std::ofstream &outputStream)
Definition: patran.C:224
std::map< int, bool > nodeThermalMap
Definition: patran.H:70
std::shared_ptr< meshBase > volMeshBase
Definition: patran.H:64
std::shared_ptr< meshBase > fullMesh
Definition: patran.H:61
T l2_Norm(const std::vector< T > &x)