ensightMeshReader.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2022-2024 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "ensightMeshReader.H"
29 #include "cellModel.H"
30 #include "ensightReadFile.H"
31 #include "matchPoints.H"
32 #include "mergePoints.H"
33 #include "ListListOps.H"
34 #include "stringOps.H"
35 
36 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
37 
38 namespace Foam
39 {
40 namespace fileFormats
41 {
42  defineTypeNameAndDebug(ensightMeshReader, 0);
43 }
44 }
45 
46 
47 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
48 
50 (
51  const face& f,
52  face& rotatedFace
53 ) const
54 {
55  label fp = findMin(f);
56 
57  rotatedFace.setSize(f.size());
58  forAll(rotatedFace, i)
59  {
60  rotatedFace[i] = f[fp];
61  fp = f.fcIndex(fp);
62  }
63  return rotatedFace;
64 }
65 
66 
68 (
69  ensightReadFile& is,
70  const label nVerts,
71  const Map<label>& nodeIdToPoints,
72  DynamicList<label>& verts
73 ) const
74 {
75  verts.clear();
76  for (label i = 0; i < nVerts; i++)
77  {
78  label verti;
79  is.read(verti);
80  //if (nodeIdToPoints.size())
81  //{
82  // verts.push_back(nodeIdToPoints[verti]);
83  //}
84  //else
85  {
86  verts.push_back(verti-1);
87  }
88  }
89 }
90 
91 
93 (
94  ensightReadFile& is,
95  const bool doRead,
96  const label elemCount,
97  labelList& foamToElem,
98  Map<label>& elemToFoam
99 ) const
100 {
101  const label begElem = foamToElem.size();
102  const label endElem = begElem + elemCount;
103 
104  foamToElem.resize(foamToElem.size()+elemCount);
105 
106  if (doRead)
107  {
108  elemToFoam.reserve(elemToFoam.size()+elemCount);
109 
110  for (label elemi = begElem; elemi < endElem; ++elemi)
111  {
112  label id;
113  is.read(id);
114  foamToElem[elemi] = id;
115  elemToFoam.insert(id, elemi);
116  }
117  }
118  else
119  {
120  // identity
121  for (label elemi = begElem; elemi < endElem; ++elemi)
122  {
123  foamToElem[elemi] = elemi;
124  }
125  }
126 }
127 
128 
130 (
131  const cellModel& model,
132  DynamicList<label>& verts,
133  const pointField& points
134 ) const
135 {
136 // // From plot3dToFoam/hexBlock.C
137 // const vector x(points[verts[1]]-points[verts[0]]);
138 // const scalar xMag(mag(x));
139 //
140 // const vector y(points[verts[3]]-points[verts[0]]);
141 // const scalar yMag(mag(y));
142 //
143 // const vector z(points[verts[4]]-points[verts[0]]);
144 // const scalar zMag(mag(z));
145 //
146 // if (xMag > SMALL && yMag > SMALL && zMag > SMALL)
147 // {
148 // if (((x ^ y) & z) < 0)
149 // {
150 // // Flipped hex
151 // std::swap(verts[0], verts[4]);
152 // std::swap(verts[1], verts[5]);
153 // std::swap(verts[2], verts[6]);
154 // std::swap(verts[3], verts[7]);
155 // }
156 // }
157 
158  if (model.mag(verts, points) < 0)
159  {
160  if (verts.size() == 8)
161  {
162  // Flipped hex
163  std::swap(verts[0], verts[4]);
164  std::swap(verts[1], verts[5]);
165  std::swap(verts[2], verts[6]);
166  std::swap(verts[3], verts[7]);
167  }
168  else if (verts.size() == 4)
169  {
170  // Flipped tet. Change orientation of base
171  std::swap(verts[0], verts[1]);
172  }
173  else if (verts.size() == 5)
174  {
175  // Flipped pyr. Change orientation of base
176  std::swap(verts[1], verts[3]);
177  }
178  else if (verts.size() == 6)
179  {
180  // Flipped prism.
181  std::swap(verts[0], verts[3]);
182  std::swap(verts[1], verts[4]);
183  std::swap(verts[2], verts[5]);
184  }
185  }
186 }
187 
188 
190 (
191  ensightReadFile& is,
192  const bool read_node_ids,
193  const bool read_elem_ids,
194 
196  labelList& pointToNodeIds,
197  Map<label>& nodeIdToPoints,
198 
199  // 3D-elems : cells (cell-to-faces)
201  labelList& cellToElemIds,
202  Map<label>& elemIdToCells,
203 
204  // 2D-elems : faces
205  faceList& faces,
206  labelList& faceToElemIDs,
207  Map<label>& elemIdToFaces
208 ) const
209 {
210  //- Read a single part. Return true if end-of-file reached. Return false
211  // if reaching next 'part'.
212 
213 
214  // Work
215  DynamicList<label> verts;
216 
217  string buffer;
218  while (is.good())
219  {
220  do
221  {
222  // Get entire line/string
223  is.read(buffer);
224  }
225  while (buffer.empty() && is.good());
226 
227  if (!is.good())
228  {
229  break;
230  }
231  else if (buffer.contains("BEGIN TIME STEP"))
232  {
233  // Graciously handle a miscued start
234  continue;
235  }
236  else if (buffer.contains("END TIME STEP"))
237  {
238  // END TIME STEP is a valid means to terminate input
239  break;
240  }
241  const auto split = stringOps::splitSpace(buffer);
242 
243  if (split.empty())
244  {
245  continue;
246  }
247 
248  const auto keyword(split[0].str());
249 
250  if (keyword == "part")
251  {
252  return false;
253  }
254  else if (keyword == "node_ids")
255  {
256  const label nPoints = points.size();
257  // Ignore point ids
258  for (label pointi = 0; pointi < nPoints; ++pointi)
259  {
260  label id;
261  is.read(id);
262  }
263  }
264  else if (keyword == "coordinates")
265  {
266  label nPoints;
267  is.read(nPoints);
268 
269  Pout<< indent << "coordinates " << nPoints
270  << " starting at line " << is.lineNumber()
271  << " position " << is.stdStream().tellg() << endl;
272 
273  readIDs
274  (
275  is,
276  read_node_ids,
277  nPoints,
278  pointToNodeIds,
279  nodeIdToPoints
280  );
281 
282 
283  is.readPoints(nPoints, points);
284  }
285  else if (keyword == "tetra4")
286  {
287  label elemCount;
288  is.read(elemCount);
289 
290  Pout<< indent<< "tetra4 " << elemCount
291  << " starting at line " << is.lineNumber()
292  << " position " << is.stdStream().tellg() << endl;
293 
294  readIDs
295  (
296  is,
297  read_elem_ids,
298  elemCount,
299  cellToElemIds,
300  elemIdToCells
301  );
302 
303  // Extend and fill the new trailing portion
304  const label startElemi = cells.size();
305  cells.resize(startElemi+elemCount);
306  faceListList::subList myElements = cells.slice(startElemi);
307 
308  const auto& model = cellModel::ref(cellModel::TET);
309  for (auto& cellFaces : myElements)
310  {
311  readVerts(is, 4, nodeIdToPoints, verts);
312  if (setHandedness_)
313  {
314  setHandedness(model, verts, points);
315  }
316  cellFaces = cellShape(model, verts).faces();
317  }
318  }
319  else if (keyword == "pyramid5")
320  {
321  label elemCount;
322  is.read(elemCount);
323 
324  Pout<< indent<< "pyramid5 " << elemCount
325  << " starting at line " << is.lineNumber()
326  << " position " << is.stdStream().tellg() << endl;
327 
328  readIDs
329  (
330  is,
331  read_elem_ids,
332  elemCount,
333  cellToElemIds,
334  elemIdToCells
335  );
336 
337  // Extend and fill the new trailing portion
338  const label startElemi = cells.size();
339  cells.resize(startElemi+elemCount);
340  faceListList::subList myElements = cells.slice(startElemi);
341 
342  const auto& model = cellModel::ref(cellModel::PYR);
343  for (auto& cellFaces : myElements)
344  {
345  readVerts(is, 5, nodeIdToPoints, verts);
346  if (setHandedness_)
347  {
348  setHandedness(model, verts, points);
349  }
350  cellFaces = cellShape(model, verts).faces();
351  }
352  }
353  else if (keyword == "penta6")
354  {
355  label elemCount;
356  is.read(elemCount);
357 
358  Pout<< indent<< "penta6 " << elemCount
359  << " starting at line " << is.lineNumber()
360  << " position " << is.stdStream().tellg() << endl;
361 
362  readIDs
363  (
364  is,
365  read_elem_ids,
366  elemCount,
367  cellToElemIds,
368  elemIdToCells
369  );
370 
371  // Extend and fill the new trailing portion
372  const label startElemi = cells.size();
373  cells.resize(startElemi+elemCount);
374  faceListList::subList myElements = cells.slice(startElemi);
375 
376  const auto& model = cellModel::ref(cellModel::PRISM);
377  for (auto& cellFaces : myElements)
378  {
379  readVerts(is, 6, nodeIdToPoints, verts);
380  if (setHandedness_)
381  {
382  setHandedness(model, verts, points);
383  }
384  cellFaces = cellShape(model, verts).faces();
385  }
386  }
387  else if (keyword == "hexa8")
388  {
389  label elemCount;
390  is.read(elemCount);
391 
392  Pout<< indent<< "hexa8 " << elemCount
393  << " starting at line " << is.lineNumber()
394  << " position " << is.stdStream().tellg() << endl;
395 
396  readIDs
397  (
398  is,
399  read_elem_ids,
400  elemCount,
401  cellToElemIds,
402  elemIdToCells
403  );
404 
405  // Extend and fill the new trailing portion
406  const label startElemi = cells.size();
407  cells.resize(startElemi+elemCount);
408  faceListList::subList myElements = cells.slice(startElemi);
409 
410  const auto& model = cellModel::ref(cellModel::HEX);
411  for (auto& cellFaces : myElements)
412  {
413  readVerts(is, 8, nodeIdToPoints, verts);
414  if (setHandedness_)
415  {
416  setHandedness(model, verts, points);
417  }
418  cellFaces = cellShape(model, verts).faces();
419  }
420  }
421  else if (keyword == "nfaced")
422  {
423  label elemCount;
424  is.read(elemCount);
425 
426  Pout<< indent<< "nfaced " << elemCount
427  << " starting at line " << is.lineNumber()
428  << " position " << is.stdStream().tellg() << endl;
429 
430  readIDs
431  (
432  is,
433  read_elem_ids,
434  elemCount,
435  cellToElemIds,
436  elemIdToCells
437  );
438 
439  // Extend and fill the new trailing portion
440  const label startElemi = cells.size();
441  cells.resize(startElemi+elemCount);
442  faceListList::subList myElements = cells.slice(startElemi);
443 
444  for (auto& cellFaces : myElements)
445  {
446  label nFaces;
447  is.read(nFaces);
448  cellFaces.resize(nFaces);
449  }
450 
451  for (auto& cellFaces : myElements)
452  {
453  for (face& f : cellFaces)
454  {
455  label nVerts;
456  is.read(nVerts);
457  f.resize(nVerts);
458  }
459  }
460 
461  for (faceList& cellFaces : myElements)
462  {
463  for (face& f : cellFaces)
464  {
465  readVerts(is, f.size(), nodeIdToPoints, verts);
466  f.labelList::operator=(verts);
467  }
468  }
469 
470  // Full check
471  forAll(myElements, elemi)
472  {
473  for (const face& f : myElements[elemi])
474  {
475  for (label pointi : f)
476  {
477  if (pointi < 0 || pointi >= points.size())
478  {
480  << "Face:" << elemi
481  << " verts:" << f
482  << " indexes outside points:" << points.size()
483  << exit(FatalError);
484  }
485  }
486  }
487  }
488  }
489  else if (keyword == "tria3")
490  {
491  label elemCount;
492  is.read(elemCount);
493 
494  Pout<< indent << "tria3 " << elemCount
495  << " starting at line " << is.lineNumber()
496  << " position " << is.stdStream().tellg() << endl;
497 
498  readIDs
499  (
500  is,
501  read_elem_ids,
502  elemCount,
503  faceToElemIDs,
504  elemIdToFaces
505  );
506 
507  // Extend and fill the new trailing portion
508  const label startElemi = cells.size();
509  faces.resize(startElemi+elemCount, face(3)); // <- tria3
510  faceList::subList myElements = faces.slice(startElemi);
511 
512  for (face& f : myElements)
513  {
514  readVerts(is, f.size(), nodeIdToPoints, verts);
515  f.labelList::operator=(verts);
516  }
517  }
518  else if (keyword == "quad4")
519  {
520  label elemCount;
521  is.read(elemCount);
522 
523  Pout<< indent << "quad4 " << elemCount
524  << " starting at line " << is.lineNumber()
525  << " position " << is.stdStream().tellg() << endl;
526 
527  readIDs
528  (
529  is,
530  read_elem_ids,
531  elemCount,
532  faceToElemIDs,
533  elemIdToFaces
534  );
535 
536  // Extend and fill the new trailing portion
537  const label startElemi = cells.size();
538  faces.resize(startElemi+elemCount, face(4)); // <- quad4
539  faceList::subList myElements = faces.slice(startElemi);
540 
541  for (face& f : myElements)
542  {
543  readVerts(is, f.size(), nodeIdToPoints, verts);
544  f.labelList::operator=(verts);
545  }
546  }
547  else if (keyword == "nsided")
548  {
549  label elemCount;
550  is.read(elemCount);
551 
552  Pout<< indent << "nsided " << elemCount
553  << " starting at line " << is.lineNumber()
554  << " position " << is.stdStream().tellg() << endl;
555 
556  readIDs
557  (
558  is,
559  read_elem_ids,
560  elemCount,
561  faceToElemIDs,
562  elemIdToFaces
563  );
564 
565  // Extend and fill the new trailing portion
566  const label startElemi = cells.size();
567  faces.resize(startElemi+elemCount);
568  faceList::subList myElements = faces.slice(startElemi);
569 
570  for (face& f : myElements)
571  {
572  label nVerts;
573  is.read(nVerts);
574  f.resize(nVerts);
575  }
576 
577  for (face& f : myElements)
578  {
579  readVerts(is, f.size(), nodeIdToPoints, verts);
580  f.labelList::operator=(verts);
581  }
582  }
583  else
584  {
585  WarningInFunction << "Unhandled key " << keyword
586  << " from line " << buffer
587  << " starting at line " << is.lineNumber()
588  << " position " << is.stdStream().tellg() << endl;
589  }
590  }
591 
592  // EOF
593  return true;
594 }
595 
596 
597 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
598 
600 (
601  const scalar scaleFactor
602 )
603 {
604  // Auto-detect ascii/binary format,
605  // skips any initial "BEGIN TIME STEP"
606 
607  ensightReadFile is(geometryFile_);
608 
609 
610  string buffer;
611 
612  // Ensight Geometry File
613  is.read(buffer);
614  Info<< "Ensight : " << buffer << nl;
615 
616  // Description - 1
617  is.read(buffer);
618  Info<< "Ensight : " << buffer << nl;
619 
620 
621  bool read_node_ids = false;
622  bool read_elem_ids = false;
623 
624  // Storage for all the parts
625  // ~~~~~~~~~~~~~~~~~~~~~~~~~
626 
627  List<string> partNames;
628 
629  DynamicList<label> partIDs; // per part the original Ensight part no
630  PtrList<pointField> partPoints;
631  PtrList<labelList> partNodeIDs;
632  PtrList<Map<label>> partPointIDs;
633 
634  // Cells (cell-to-faces)
635  // Note: only one internal mesh supported.
636  PtrList<faceListList> partCells;
637  // Element ID for cells
638  PtrList<labelList> partCellIDs;
639  PtrList<Map<label>> partCellElemIDs;
640  // Boundary faces
641  PtrList<faceList> partFaces;
642  // Element IDs for faces
643  PtrList<labelList> partFaceIDs;
644  PtrList<Map<label>> partFaceElemIDs;
645 
646 
647  // Parse all
648  SubStrings<string> split;
649 
650  while (is.good())
651  {
652  do
653  {
654  // Get entire line/string
655  is.read(buffer);
656  }
657  while (buffer.empty() && is.good());
658  if (buffer.contains("END TIME STEP"))
659  {
660  // END TIME STEP is a valid means to terminate input
661  break;
662  }
663  split = stringOps::splitSpace(buffer);
664 
665  if (split.empty())
666  {
667  continue;
668  }
669 
670  const auto keyword(split[0].str());
671 
672  if (keyword == "extents")
673  {
674  // Optional extents (xmin, xmax, ymin, ymax, zmin, zmax)
675 
676  boundBox bb;
677  point& min = bb.min();
678  point& max = bb.max();
679 
680  is.read(min.x()); is.read(max.x());
681  is.read(min.y()); is.read(max.y());
682  is.read(min.z()); is.read(max.z());
683 
684  Pout<< indent << "Read extents " << bb << endl;
685  }
686  else if (keyword == "node")
687  {
688  // "node id (off|assign|given|ignore)"
689  std::string op(split[2]);
690  if (op == "given" || op == "ignore")
691  {
692  Pout<< indent << "Reading node ids" << endl;
693  read_node_ids = true;
694  }
695  }
696  else if (keyword == "element")
697  {
698  // "element id (off|assign|given|ignore)"
699  std::string op(split[2]);
700  if (op == "given" || op == "ignore")
701  {
702  Pout<< indent << "Reading element ids" << endl;
703  read_elem_ids = true;
704  }
705  }
706  else if (keyword == "part")
707  {
708  bool finished = false;
709  do
710  {
711  // Read part id and name
712  is.read(partIDs.emplace_back());
713  is.read(partNames.emplace_back());
714 
715  Pout<< indent
716  << "Reading part " << partIDs.back()
717  << " name " << partNames.back()
718  << " starting at line " << is.lineNumber()
719  << " position " << is.stdStream().tellg() << endl;
720 
721  Pout<< incrIndent;
722  finished = readGoldPart
723  (
724  is,
725  read_node_ids,
726  read_elem_ids,
727 
728  partPoints.emplace_back(),
729  partNodeIDs.emplace_back(),
730  partPointIDs.emplace_back(),
731 
732  // Cells (cell-to-faces)
733  partCells.emplace_back(),
734  partCellIDs.emplace_back(),
735  partCellElemIDs.emplace_back(),
736 
737  // Faces
738  partFaces.emplace_back(),
739  partFaceIDs.emplace_back(),
740  partFaceElemIDs.emplace_back()
741  );
742 
743  partPoints.back() *= scaleFactor;
744 
745  Pout<< indent
746  << "For part " << partIDs.back()
747  << " read cells " << partCells.back().size()
748  << " faces " << partFaces.back().size()
749  << endl;
750 
751  Pout<< decrIndent;
752  }
753  while (!finished);
754 
755  break;
756  }
757  }
758 
759 
760  // Merge all coordinates
761  labelListList pointToMerged(partPoints.size());
762 
763  //- Use point merging - also merges points inside a part which might upset
764  //- e.g. ami
765  //{
766  // label nPoints = 0;
767  // forAll(partPoints, parti)
768  // {
769  // nPoints += partPoints[parti].size();
770  // }
771  //
772  // points_.setSize(nPoints);
773  // nodeIds_.setSize(nPoints, -1);
774  // nPoints = 0;
775  // forAll(partPoints, parti)
776  // {
777  // const auto& pts = partPoints[parti];
778  //
779  // SubList<point>(points_, pts.size(), nPoints) = pts;
780  // SubList<label>(nodeIds_, pts.size(), nPoints) =
781  // partNodeIDs[parti];
782  //
783  // auto& map = pointToMerged[parti];
784  // map = nPoints + identity(pts.size());
785  //
786  // nPoints += pts.size();
787  // }
788  //
789  // if (mergeTol_ > 0)
790  // {
791  // const scalar mergeDist = mergeTol_*boundBox(points_, true).mag();
792  //
793  // labelList sharedToMerged;
794  // const label nMerged = inplaceMergePoints
795  // (
796  // points_,
797  // mergeDist,
798  // false,
799  // sharedToMerged
800  // );
801  // Pout<< "Merged " << nMerged << " points out of " << nPoints
802  // << " using merge tolerance " << mergeTol_
803  // << ", distance " << mergeDist
804  // << endl;
805  //
806  // forAll(partNodeIDs, parti)
807  // {
808  // inplaceRenumber(sharedToMerged, pointToMerged[parti]);
809  //
810  // // Now pointToMerged contains the numbering from original,
811  // // partwise to global points
812  // UIndirectList<label>(nodeIds_, pointToMerged[parti]) =
813  // partNodeIDs[parti];
814  // }
815  // }
816  //}
817 
818  // Merge all coordinates
819  {
820  boundBox extents;
821  label nPoints = 0;
822  forAll(partPoints, parti)
823  {
824  extents.add(partPoints[parti]);
825  nPoints += partPoints[parti].size();
826  }
827  const scalar mergeDist = mergeTol_*extents.mag();
828 
829  Pout<< "Merging points closer than " << mergeDist
830  << " calculated from bounding box " << extents
831  << " and mergeTol " << mergeTol_
832  << endl;
833 
834  forAll(partPoints, parti)
835  {
836  const auto& pPoints = partPoints[parti];
837  auto& pPointMap = pointToMerged[parti];
838  pPointMap.setSize(pPoints.size(), -1);
839 
840  Pout<< "Matching part " << parti
841  << " name " << partNames[parti]
842  << " points " << pPoints.size()
843  << " to current merged points " << points_.size()
844  << endl;
845 
846  if (points_.empty())
847  {
848  points_ = pPoints;
849  pPointMap = identity(pPoints.size());
850  }
851  else
852  {
853  // Match to existing
854  labelList from0To1;
856  (
857  pPoints,
858  points_,
859  scalarField(pPoints.size(), mergeDist),
860  false,
861  from0To1
862  );
863  label nAdded = 0;
864 
865  forAll(from0To1, partPointi)
866  {
867  const label pointi = from0To1[partPointi];
868  if (pointi != -1)
869  {
870  pPointMap[partPointi] = pointi;
871  }
872  else
873  {
874  nAdded++;
875  }
876  }
877 
878  const label nOldPoints = points_.size();
879  points_.setSize(nOldPoints+nAdded);
880  nAdded = 0;
881  forAll(from0To1, partPointi)
882  {
883  if (from0To1[partPointi] == -1)
884  {
885  const label newPointi = nOldPoints+nAdded++;
886  points_[newPointi] = pPoints[partPointi];
887  pPointMap[partPointi] = newPointi;
888  }
889  }
890  }
891  }
892 
893  // Now we have complete points. Take over element IDs.
894  nodeIds_.setSize(points_.size());
895  forAll(partNodeIDs, parti)
896  {
897  const auto& pPointMap = pointToMerged[parti];
898  UIndirectList<label>(nodeIds_, pPointMap) = partNodeIDs[parti];
899  }
900 
901  Pout<< "Merged from " << nPoints << " down to " << points_.size()
902  << " points" << endl;
903  }
904 
905 
906  // Merge all cells
907  labelList cellOffsets(partCells.size()+1);
908  cellOffsets[0] = 0;
909  {
910  label nCells = 0;
911  forAll(partCells, parti)
912  {
913  nCells += partCells[parti].size();
914  cellOffsets[parti+1] = nCells;
915  }
916 
917  cellFaces_.setSize(nCells);
918  cellTableId_.setSize(nCells);
919  elementIds_.setSize(nCells, -1);
920 
921  forAll(partCells, parti)
922  {
923  // Copy cells into position
924  const auto& cells = partCells[parti];
925 
926  SubList<faceList> cellSlice
927  (
928  cellFaces_,
929  cells.size(),
930  cellOffsets[parti]
931  );
932  cellSlice = cells;
933 
934  SubList<label>
935  (
936  cellTableId_,
937  cells.size(),
938  cellOffsets[parti]
939  ) = parti;
940 
941  SubList<label>
942  (
943  elementIds_,
944  cells.size(),
945  cellOffsets[parti]
946  ) = partCellIDs[parti];
947 
948  // Renumber faces
949  const auto& pointMap = pointToMerged[parti];
950 
951  for (auto& cellFaces : cellSlice)
952  {
953  for (auto& f : cellFaces)
954  {
955  inplaceRenumber(pointMap, f);
956  }
957  }
958  }
959  }
960 
961 
962  // Add cells to separate zones
963  forAll(partCells, parti)
964  {
965  cellTable_.setMaterial(parti, "fluid");
966  cellTable_.setName(parti, "part" + Foam::name(partIDs[parti]));
967  }
968 
969 
970  // Build map from face to cell and index. Note: use exact match
971  // - no circular equivalence
972  // - but instead pass in ordered faces (lowest numbered vertex first)
973  HashTable<cellFaceIdentifier, face, face::symmHasher> vertsToCell
974  (
975  2*cellOffsets.back()
976  );
977 
978  // Insert cell's faces into hashtable
979  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
980 
981  face rotatedFace;
982  forAll(cellFaces_, celli)
983  {
984  const auto& cFaces = cellFaces_[celli];
985  forAll(cFaces, cFacei)
986  {
987  const face& f = rotateFace(cFaces[cFacei], rotatedFace);
988 
989  const auto fFnd = vertsToCell.find(f);
990  if (fFnd.good())
991  {
992  // Already inserted. Internal face.
993  vertsToCell.erase(fFnd);
994  }
995  else
996  {
997  vertsToCell.insert(f, cellFaceIdentifier(celli, cFacei));
998  }
999  }
1000  }
1001 
1002 
1003  labelList patchToPart(partNames.size());
1004  label nPatches = 0;
1005  forAll(partFaces, parti)
1006  {
1007  if (partFaces[parti].size())
1008  {
1009  Pout<< "Using part " << parti
1010  << " name " << partNames[parti]
1011  << " as patch " << nPatches
1012  << endl;
1013 
1014  patchToPart[nPatches++] = parti;
1015  }
1016  }
1017  patchToPart.setSize(nPatches);
1018  boundaryIds_.setSize(nPatches);
1019  patchTypes_.setSize(nPatches, "wall");
1020  patchNames_.setSize(nPatches);
1021  forAll(patchNames_, patchi)
1022  {
1023  const label parti = patchToPart[patchi];
1024 
1025  Pout<< "Matching part " << parti
1026  << " name " << partNames[parti]
1027  << " faces " << partFaces[parti].size()
1028  //<< " points " << partPoints[parti].size()
1029  << " to merged faces " << vertsToCell.size()
1030  << ", merged points " << points_.size()
1031  << endl;
1032 
1033  patchNames_[patchi] = word::validate(partNames[parti]);
1034 
1035  const auto& pointMap = pointToMerged[parti];
1036  const auto& faces = partFaces[parti];
1037 
1038  auto& partCellAndFace = boundaryIds_[patchi];
1039  partCellAndFace.setSize(faces.size());
1040 
1041  label patchFacei = 0;
1042  forAll(faces, facei)
1043  {
1044  if (faces[facei].empty())
1045  {
1046  Pout<< "Ignoring empty face:" << facei << endl;
1047  continue;
1048  }
1049 
1050  // Rewrite into mesh-point ordering
1051  const face newF(pointMap, faces[facei]);
1052  // Lookup cell and face on cell using the vertices
1053  const auto cAndF = vertsToCell.find
1054  (
1055  rotateFace
1056  (
1057  newF,
1058  rotatedFace
1059  )
1060  );
1061 
1062  if (cAndF.good())
1063  {
1064  partCellAndFace[patchFacei++] = cAndF.val();
1065  vertsToCell.erase(cAndF);
1066  }
1067  else
1068  {
1069  //WarningInFunction
1070  // << "Did not find face " << facei
1071  // << " verts:" << faces[facei]
1072  // << " renumbered:" << newF
1073  // << " rotated:" << rotatedFace
1074  // << " in part " << parti
1075  // << endl;
1076  }
1077  }
1078  partCellAndFace.setSize(patchFacei);
1079  }
1080  patchPhysicalTypes_.setSize(nPatches, "wall");
1081  patchStarts_.setSize(nPatches, 0);
1082  patchSizes_.setSize(nPatches, 0);
1083 
1084  if (vertsToCell.size())
1085  {
1086  // Unused internal or boundary faces
1087  boundaryIds_.emplace_back(vertsToCell.size());
1088  {
1089  auto& cellAndFaces = boundaryIds_.back();
1090  label i = 0;
1091  forAllConstIters(vertsToCell, iter)
1092  {
1093  cellAndFaces[i++] = iter.val();
1094  }
1095  }
1096 
1097  patchTypes_.push_back("empty");
1098  patchNames_.push_back("defaultFaces");
1099  patchPhysicalTypes_.push_back("empty");
1100  patchStarts_.push_back(0);
1101  patchSizes_.push_back(0);
1102 
1103  Pout<< "Introducing default patch " << patchNames_.size()-1
1104  << " name " << patchNames_.back() << endl;
1105  }
1106 
1107  return true;
1108 }
1109 
1110 
1111 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1112 
1114 (
1115  const fileName& geomFile,
1116  const objectRegistry& registry,
1117  const scalar mergeTol,
1118  const scalar scaleFactor,
1119  const bool setHandedness
1120 )
1121 :
1122  meshReader(geomFile, scaleFactor),
1123  mergeTol_(mergeTol),
1124  setHandedness_(setHandedness)
1125 {}
1126 
1127 
1128 // ************************************************************************* //
label nPatches
Definition: readKivaGrid.H:396
static word validate(const std::string &s, const bool prefix=false)
Construct validated word (no invalid characters).
Definition: word.C:39
List< faceList > faceListList
List of faceList.
Definition: faceListFwd.H:44
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:493
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:68
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:608
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
void readIDs(ensightReadFile &is, const bool doRead, const label nShapes, labelList &foamToElem, Map< label > &elemToFoam) const
Read set of element/node IDs.
SubList< faceList > subList
Declare type of subList.
Definition: List.H:144
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
Determine correspondence between points. See below.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
const face & rotateFace(const face &f, face &rotatedFace) const
Rotate face so lowest vertex is first.
static const cellModel & ref(const modelType model)
Look up reference to cellModel by enumeration. Fatal on failure.
Definition: cellModels.C:150
List< face > faceList
List of faces.
Definition: faceListFwd.H:39
void readVerts(ensightReadFile &is, const label nVerts, const Map< label > &nodeIdToPoints, DynamicList< label > &verts) const
Read set of vertices. Optional mapping.
label findMin(const ListType &input, label start=0)
Linear search for the index of the min element, similar to std::min_element but for lists and returns...
bool matchPoints(const UList< point > &pts0, const UList< point > &pts1, const UList< scalar > &matchDistance, const bool verbose, labelList &from0To1, const point &origin=point::zero)
Determine correspondence between pointFields. Gets passed.
Definition: matchPoints.C:29
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
const cellShapeList & cells
const pointField & points
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i), works like std::iota() but returning a...
Definition: labelLists.C:44
label nPoints
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
defineTypeNameAndDebug(combustionModel, 0)
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:511
Geometric merging of points. See below.
labelList f(nPoints)
label lineNumber() const noexcept
Const access to the current stream line number.
Definition: IOstream.H:390
static bool split(const std::string &line, std::string &key, std::string &val)
Definition: cpuInfo.C:32
void inplaceRenumber(const labelUList &oldToNew, IntListType &input)
Inplace renumber the values within a list.
vector point
Point is a vector.
Definition: point.H:37
ensightMeshReader(const fileName &geomFile, const objectRegistry &registry, const scalar mergeTol=SMALL, const scalar scaleFactor=1.0, const bool setHandedness=true)
Construct from case name.
#define WarningInFunction
Report a warning using Foam::Warning.
label newPointi
Definition: readKivaGrid.H:496
void setHandedness(const cellModel &model, DynamicList< label > &verts, const pointField &points) const
Swap handedness of hex if needed.
bool readGoldPart(ensightReadFile &is, const bool read_node_ids, const bool read_elem_ids, pointField &points, labelList &pointToNodeIds, Map< label > &nodeIdToPoints, faceListList &cellFaces, labelList &cellToElemIds, Map< label > &elemIdToCells, faceList &faces, labelList &faceToElemIDs, Map< label > &elemIdToFaces) const
Read a single part until eof (return true) or until start of next.
messageStream Info
Information stream (stdout output on master, null elsewhere)
virtual bool readGeometry(const scalar scaleFactor=1.0)
Read the mesh from the file(s)
Foam::SubStrings< StringType > splitSpace(const StringType &str, std::string::size_type pos=0)
Split string into sub-strings at whitespace (TAB, NL, VT, FF, CR, SPC)
List< label > labelList
A List of labels.
Definition: List.H:62
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:502
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Namespace for OpenFOAM.
forAllConstIters(mixture.phases(), phase)
Definition: pEqn.H:28