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-2023 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 nShapes,
97  labelList& foamToElem,
98  Map<label>& elemToFoam
99 ) const
100 {
101  const label sz = foamToElem.size();
102  foamToElem.resize(sz+nShapes);
103  if (doRead)
104  {
105  elemToFoam.reserve(elemToFoam.size()+nShapes);
106  for (label shapei = 0; shapei < nShapes; shapei++)
107  {
108  label elemi;
109  is.read(elemi);
110  foamToElem[sz+shapei] = elemi;
111  elemToFoam.insert(elemi, sz+shapei);
112  }
113  }
114  else
115  {
116  for (label shapei = 0; shapei < nShapes; shapei++)
117  {
118  foamToElem[sz+shapei] = sz+shapei;
119  }
120  }
121 }
122 
123 
125 (
126  const cellModel& model,
127  DynamicList<label>& verts,
128  const pointField& points
129 ) const
130 {
131 // // From plot3dToFoam/hexBlock.C
132 // const vector x(points[verts[1]]-points[verts[0]]);
133 // const scalar xMag(mag(x));
134 //
135 // const vector y(points[verts[3]]-points[verts[0]]);
136 // const scalar yMag(mag(y));
137 //
138 // const vector z(points[verts[4]]-points[verts[0]]);
139 // const scalar zMag(mag(z));
140 //
141 // if (xMag > SMALL && yMag > SMALL && zMag > SMALL)
142 // {
143 // if (((x ^ y) & z) < 0)
144 // {
145 // // Flipped hex
146 // std::swap(verts[0], verts[4]);
147 // std::swap(verts[1], verts[5]);
148 // std::swap(verts[2], verts[6]);
149 // std::swap(verts[3], verts[7]);
150 // }
151 // }
152 
153  if (model.mag(verts, points) < 0)
154  {
155  if (verts.size() == 8)
156  {
157  // Flipped hex
158  std::swap(verts[0], verts[4]);
159  std::swap(verts[1], verts[5]);
160  std::swap(verts[2], verts[6]);
161  std::swap(verts[3], verts[7]);
162  }
163  else if (verts.size() == 4)
164  {
165  // Flipped tet. Change orientation of base
166  std::swap(verts[0], verts[1]);
167  }
168  else if (verts.size() == 5)
169  {
170  // Flipped pyr. Change orientation of base
171  std::swap(verts[1], verts[3]);
172  }
173  else if (verts.size() == 6)
174  {
175  // Flipped prism.
176  std::swap(verts[0], verts[3]);
177  std::swap(verts[1], verts[4]);
178  std::swap(verts[2], verts[5]);
179  }
180  }
181 }
182 
183 
185 (
186  ensightReadFile& is,
187  const bool read_node_ids,
188  const bool read_elem_ids,
189 
191  labelList& pointToNodeIds,
192  Map<label>& nodeIdToPoints,
193 
194  // 3D-elems : cells (cell-to-faces)
196  labelList& cellToElemIds,
197  Map<label>& elemIdToCells,
198 
199  // 2D-elems : faces
200  faceList& faces,
201  labelList& faceToElemIDs,
202  Map<label>& elemIdToFaces
203 ) const
204 {
205  //- Read a single part. Return true if end-of-file reached. Return false
206  // if reaching next 'part'.
207 
208 
209  // Work
210  DynamicList<label> verts;
211 
212  string line;
213  while (is.good())
214  {
215  do
216  {
217  is.readKeyword(line);
218  }
219  while (line.empty() && is.good());
220 
221  const auto split = stringOps::splitSpace(line);
222 
223  if (split.size() == 0)
224  {
225  continue;
226  }
227 
228  if (split[0] == "part")
229  {
230  return false;
231  }
232  else if (split[0] == "node_ids")
233  {
234  const label nPoints = points.size();
235  // Ignore for now
236  for (label i = 0; i < nPoints; i++)
237  {
238  label index;
239  is.read(index);
240  }
241  }
242  else if (split[0] == "coordinates")
243  {
244  label nPoints;
245  is.read(nPoints);
246 
247  Pout<< indent << "coordinates " << nPoints
248  << " starting at line " << is.lineNumber()
249  << " position " << is.stdStream().tellg() << endl;
250 
251  readIDs
252  (
253  is,
254  read_node_ids,
255  nPoints,
256  pointToNodeIds,
257  nodeIdToPoints
258  );
259 
260  points.setSize(nPoints);
261 
262  for (label pointi = 0; pointi < nPoints; pointi++)
263  {
264  is.read(points[pointi].x());
265  }
266  for (label pointi = 0; pointi < nPoints; pointi++)
267  {
268  is.read(points[pointi].y());
269  }
270  for (label pointi = 0; pointi < nPoints; pointi++)
271  {
272  is.read(points[pointi].z());
273  }
274  }
275  else if (split[0] == "tetra4")
276  {
277  label nShapes;
278  is.read(nShapes);
279 
280  Pout<< indent<< "tetra4 " << nShapes
281  << " starting at line " << is.lineNumber()
282  << " position " << is.stdStream().tellg() << endl;
283 
284  const label celli = cells.size();
285  cells.resize(celli+nShapes);
286 
287  readIDs
288  (
289  is,
290  read_elem_ids,
291  nShapes,
292  cellToElemIds,
293  elemIdToCells
294  );
295 
296  const auto& model = cellModel::ref(cellModel::TET);
297  for (label shapei = 0; shapei < nShapes; shapei++)
298  {
299  readVerts(is, 4, nodeIdToPoints, verts);
300  if (setHandedness_)
301  {
302  setHandedness(model, verts, points);
303  }
304  const cellShape cellVerts(model, verts);
305  cells[celli+shapei] = cellVerts.faces();
306  }
307  }
308  else if (split[0] == "pyramid5")
309  {
310  label nShapes;
311  is.read(nShapes);
312 
313  Pout<< indent<< "pyramid5 " << nShapes
314  << " starting at line " << is.lineNumber()
315  << " position " << is.stdStream().tellg() << endl;
316 
317  const label celli = cells.size();
318  cells.resize(celli+nShapes);
319 
320  readIDs
321  (
322  is,
323  read_elem_ids,
324  nShapes,
325  cellToElemIds,
326  elemIdToCells
327  );
328 
329  const auto& model = cellModel::ref(cellModel::PYR);
330  for (label shapei = 0; shapei < nShapes; shapei++)
331  {
332  readVerts(is, 5, nodeIdToPoints, verts);
333  if (setHandedness_)
334  {
335  setHandedness(model, verts, points);
336  }
337  const cellShape cellVerts(model, verts);
338  cells[celli+shapei] = cellVerts.faces();
339  }
340  }
341  else if (split[0] == "penta6")
342  {
343  label nShapes;
344  is.read(nShapes);
345 
346  Pout<< indent<< "penta6 " << nShapes
347  << " starting at line " << is.lineNumber()
348  << " position " << is.stdStream().tellg() << endl;
349 
350  const label celli = cells.size();
351  cells.resize(celli+nShapes);
352 
353  readIDs
354  (
355  is,
356  read_elem_ids,
357  nShapes,
358  cellToElemIds,
359  elemIdToCells
360  );
361 
362  const auto& model = cellModel::ref(cellModel::PRISM);
363  for (label shapei = 0; shapei < nShapes; shapei++)
364  {
365  readVerts(is, 6, nodeIdToPoints, verts);
366  if (setHandedness_)
367  {
368  setHandedness(model, verts, points);
369  }
370  const cellShape cellVerts(model, verts);
371  cells[celli+shapei] = cellVerts.faces();
372  }
373  }
374  else if (split[0] == "hexa8")
375  {
376  label nShapes;
377  is.read(nShapes);
378 
379  Pout<< indent<< "hexa8 " << nShapes
380  << " starting at line " << is.lineNumber()
381  << " position " << is.stdStream().tellg() << endl;
382 
383  const label celli = cells.size();
384  cells.resize(celli+nShapes);
385 
386  readIDs
387  (
388  is,
389  read_elem_ids,
390  nShapes,
391  cellToElemIds,
392  elemIdToCells
393  );
394 
395  const auto& model = cellModel::ref(cellModel::HEX);
396  for (label shapei = 0; shapei < nShapes; shapei++)
397  {
398  readVerts(is, 8, nodeIdToPoints, verts);
399  if (setHandedness_)
400  {
401  setHandedness(model, verts, points);
402  }
403  const cellShape cellVerts(model, verts);
404  cells[celli+shapei] = cellVerts.faces();
405  }
406  }
407  else if (split[0] == "nfaced")
408  {
409  label nShapes;
410  is.read(nShapes);
411 
412  Pout<< indent<< "nfaced " << nShapes
413  << " starting at line " << is.lineNumber()
414  << " position " << is.stdStream().tellg() << endl;
415 
416  const label celli = cells.size();
417  cells.resize(celli+nShapes);
418 
419  readIDs
420  (
421  is,
422  read_elem_ids,
423  nShapes,
424  cellToElemIds,
425  elemIdToCells
426  );
427 
428  for (label shapei = 0; shapei < nShapes; shapei++)
429  {
430  label nFaces;
431  is.read(nFaces);
432  faceList& cellFaces = cells[celli+shapei];
433  cellFaces.setSize(nFaces);
434  }
435 
436  for (label shapei = 0; shapei < nShapes; shapei++)
437  {
438  faceList& cellFaces = cells[celli+shapei];
439  forAll(cellFaces, cellFacei)
440  {
441  label nVerts;
442  is.read(nVerts);
443  cellFaces[cellFacei].setSize(nVerts);
444  }
445  }
446 
447  for (label shapei = 0; shapei < nShapes; shapei++)
448  {
449  faceList& cellFaces = cells[celli+shapei];
450  forAll(cellFaces, cellFacei)
451  {
452  face& f = cellFaces[cellFacei];
453  readVerts(is, f.size(), nodeIdToPoints, verts);
454  f.labelList::operator=(verts);
455 
456  forAll(f, fp)
457  {
458  if (f[fp] < 0 || f[fp] >= points.size())
459  {
460  FatalErrorInFunction<< "Face:" << shapei
461  << " verts:" << f
462  << " indexes outside points:" << points.size()
463  << exit(FatalError);
464  }
465  }
466  }
467  }
468  }
469  else if (split[0] == "tria3")
470  {
471  label nShapes;
472  is.read(nShapes);
473 
474  Pout<< indent << "tria3 " << nShapes
475  << " starting at line " << is.lineNumber()
476  << " position " << is.stdStream().tellg() << endl;
477 
478  const label facei = faces.size();
479 
480  readIDs
481  (
482  is,
483  read_elem_ids,
484  nShapes,
485  faceToElemIDs,
486  elemIdToFaces
487  );
488 
489  faces.setSize(facei+nShapes);
490 
491  for (label shapei = 0; shapei < nShapes; shapei++)
492  {
493  auto& f = faces[facei+shapei];
494  f.setSize(3);
495  readVerts(is, f.size(), nodeIdToPoints, verts);
496  f.labelList::operator=(verts);
497  }
498  }
499  else if (split[0] == "quad4")
500  {
501  label nShapes;
502  is.read(nShapes);
503 
504  Pout<< indent << "quad4 " << nShapes
505  << " starting at line " << is.lineNumber()
506  << " position " << is.stdStream().tellg() << endl;
507 
508  const label facei = faces.size();
509 
510  readIDs
511  (
512  is,
513  read_elem_ids,
514  nShapes,
515  faceToElemIDs,
516  elemIdToFaces
517  );
518 
519  faces.setSize(facei+nShapes);
520 
521  for (label shapei = 0; shapei < nShapes; shapei++)
522  {
523  auto& f = faces[facei+shapei];
524  f.setSize(4);
525  readVerts(is, f.size(), nodeIdToPoints, verts);
526  f.labelList::operator=(verts);
527  }
528  }
529  else if (split[0] == "nsided")
530  {
531  label nShapes;
532  is.read(nShapes);
533 
534  Pout<< indent << "nsided " << nShapes
535  << " starting at line " << is.lineNumber()
536  << " position " << is.stdStream().tellg() << endl;
537 
538  const label facei = faces.size();
539 
540  readIDs
541  (
542  is,
543  read_elem_ids,
544  nShapes,
545  faceToElemIDs,
546  elemIdToFaces
547  );
548 
549  faces.setSize(facei+nShapes);
550 
551  for (label shapei = 0; shapei < nShapes; shapei++)
552  {
553  auto& f = faces[facei+shapei];
554  label nVerts;
555  is.read(nVerts);
556  f.setSize(nVerts);
557  }
558 
559  for (label shapei = 0; shapei < nShapes; shapei++)
560  {
561  auto& f = faces[facei+shapei];
562  readVerts(is, f.size(), nodeIdToPoints, verts);
563  f.labelList::operator=(verts);
564  }
565  }
566  else
567  {
568  WarningInFunction << "Unhandled key " << string(split[0])
569  << " from line " << line
570  << " starting at line " << is.lineNumber()
571  << " position " << is.stdStream().tellg() << endl;
572  }
573  }
574 
575  // EOF
576  return true;
577 }
578 
579 
580 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
581 
583 (
584  const scalar scaleFactor
585 )
586 {
587  ensightReadFile is(geometryFile_);
588 
589  // Skip 'binary' tag
590  is.readBinaryHeader();
591 
592  string header;
593  is.read(header);
594  Info<< "Ensight : " << header << endl;
595  is.read(header);
596  Info<< "Ensight : " << header << endl;
597 
598 
599  bool read_node_ids = false;
600  bool read_elem_ids = false;
601 
602  // Storage for all the parts
603  // ~~~~~~~~~~~~~~~~~~~~~~~~~
604 
605  List<string> partNames;
606 
607  DynamicList<label> partIDs; // per part the original Ensight part no
608  PtrList<pointField> partPoints;
609  PtrList<labelList> partNodeIDs;
610  PtrList<Map<label>> partPointIDs;
611 
612  // Cells (cell-to-faces)
613  // Note: only one internal mesh supported.
614  PtrList<faceListList> partCells;
615  // Element ID for cells
616  PtrList<labelList> partCellIDs;
617  PtrList<Map<label>> partCellElemIDs;
618  // Boundary faces
619  PtrList<faceList> partFaces;
620  // Element IDs for faces
621  PtrList<labelList> partFaceIDs;
622  PtrList<Map<label>> partFaceElemIDs;
623 
624 
625  // Parse all
626  string line;
627  while (is.good())
628  {
629  do
630  {
631  is.readKeyword(line);
632  }
633  while (line.empty() && is.good());
634  const auto split = stringOps::splitSpace(line);
635 
636  if (split[0] == "extents")
637  {
638  point min;
639  point max;
640  is.read(min.x());
641  is.read(max.x());
642  is.read(min.y());
643  is.read(max.y());
644  is.read(min.z());
645  is.read(max.z());
646  Pout<< indent
647  << "Read extents " << boundBox(min, max)
648  << endl;
649  }
650  else if (split[0] == "node")
651  {
652  word id(split[1]);
653  word op(split[2]);
654  if (op == "given" || op == "ignore")
655  {
656  Pout<< indent << "Reading node ids" << endl;
657  read_node_ids = true;
658  }
659  }
660  else if (split[0] == "element")
661  {
662  word id(split[1]);
663  word op(split[2]);
664  if (op == "given" || op == "ignore")
665  {
666  Pout<< indent << "Reading element ids" << endl;
667  read_elem_ids = true;
668  }
669  }
670  else if (split[0] == "part")
671  {
672  bool finished = false;
673  do
674  {
675  // Make space
676  partIDs.emplace_back();
677  is.read(partIDs.back());
678 
679  partNames.emplace_back();
680  is.read(partNames.back());
681 
682  Pout<< indent
683  << "Reading part " << partIDs.back()
684  << " name " << partNames.back()
685  << " starting at line " << is.lineNumber()
686  << " position " << is.stdStream().tellg() << endl;
687 
688  Pout<< incrIndent;
689  finished = readGoldPart
690  (
691  is,
692  read_node_ids,
693  read_elem_ids,
694 
695  partPoints.emplace_back(),
696  partNodeIDs.emplace_back(),
697  partPointIDs.emplace_back(),
698 
699  // Cells (cell-to-faces)
700  partCells.emplace_back(),
701  partCellIDs.emplace_back(),
702  partCellElemIDs.emplace_back(),
703 
704  // Faces
705  partFaces.emplace_back(),
706  partFaceIDs.emplace_back(),
707  partFaceElemIDs.emplace_back()
708  );
709 
710  partPoints.back() *= scaleFactor;
711 
712  Pout<< indent
713  << "For part " << partIDs.back()
714  << " read cells " << partCells.back().size()
715  << " faces " << partFaces.back().size()
716  << endl;
717 
718  Pout<< decrIndent;
719  }
720  while (!finished);
721 
722  break;
723  }
724  }
725 
726 
727  // Merge all coordinates
728  labelListList pointToMerged(partPoints.size());
729 
730  //- Use point merging - also merges points inside a part which might upset
731  //- e.g. ami
732  //{
733  // label nPoints = 0;
734  // forAll(partPoints, parti)
735  // {
736  // nPoints += partPoints[parti].size();
737  // }
738  //
739  // points_.setSize(nPoints);
740  // nodeIds_.setSize(nPoints, -1);
741  // nPoints = 0;
742  // forAll(partPoints, parti)
743  // {
744  // const auto& pts = partPoints[parti];
745  //
746  // SubList<point>(points_, pts.size(), nPoints) = pts;
747  // SubList<label>(nodeIds_, pts.size(), nPoints) =
748  // partNodeIDs[parti];
749  //
750  // auto& map = pointToMerged[parti];
751  // map = nPoints + identity(pts.size());
752  //
753  // nPoints += pts.size();
754  // }
755  //
756  // if (mergeTol_ > 0)
757  // {
758  // const scalar mergeDist = mergeTol_*boundBox(points_, true).mag();
759  //
760  // labelList sharedToMerged;
761  // const label nMerged = inplaceMergePoints
762  // (
763  // points_,
764  // mergeDist,
765  // false,
766  // sharedToMerged
767  // );
768  // Pout<< "Merged " << nMerged << " points out of " << nPoints
769  // << " using merge tolerance " << mergeTol_
770  // << ", distance " << mergeDist
771  // << endl;
772  //
773  // forAll(partNodeIDs, parti)
774  // {
775  // inplaceRenumber(sharedToMerged, pointToMerged[parti]);
776  //
777  // // Now pointToMerged contains the numbering from original,
778  // // partwise to global points
779  // UIndirectList<label>(nodeIds_, pointToMerged[parti]) =
780  // partNodeIDs[parti];
781  // }
782  // }
783  //}
784 
785  // Merge all coordinates
786  {
787  boundBox extents;
788  label nPoints = 0;
789  forAll(partPoints, parti)
790  {
791  extents.add(partPoints[parti]);
792  nPoints += partPoints[parti].size();
793  }
794  const scalar mergeDist = mergeTol_*extents.mag();
795 
796  Pout<< "Merging points closer than " << mergeDist
797  << " calculated from bounding box " << extents
798  << " and mergeTol " << mergeTol_
799  << endl;
800 
801  forAll(partPoints, parti)
802  {
803  const auto& pPoints = partPoints[parti];
804  auto& pPointMap = pointToMerged[parti];
805  pPointMap.setSize(pPoints.size(), -1);
806 
807  Pout<< "Matching part " << parti
808  << " name " << partNames[parti]
809  << " points " << pPoints.size()
810  << " to current merged points " << points_.size()
811  << endl;
812 
813  if (points_.empty())
814  {
815  points_ = pPoints;
816  pPointMap = identity(pPoints.size());
817  }
818  else
819  {
820  // Match to existing
821  labelList from0To1;
823  (
824  pPoints,
825  points_,
826  scalarField(pPoints.size(), mergeDist),
827  false,
828  from0To1
829  );
830  label nAdded = 0;
831 
832  forAll(from0To1, partPointi)
833  {
834  const label pointi = from0To1[partPointi];
835  if (pointi != -1)
836  {
837  pPointMap[partPointi] = pointi;
838  }
839  else
840  {
841  nAdded++;
842  }
843  }
844 
845  const label nOldPoints = points_.size();
846  points_.setSize(nOldPoints+nAdded);
847  nAdded = 0;
848  forAll(from0To1, partPointi)
849  {
850  if (from0To1[partPointi] == -1)
851  {
852  const label newPointi = nOldPoints+nAdded++;
853  points_[newPointi] = pPoints[partPointi];
854  pPointMap[partPointi] = newPointi;
855  }
856  }
857  }
858  }
859 
860  // Now we have complete points. Take over element IDs.
861  nodeIds_.setSize(points_.size());
862  forAll(partNodeIDs, parti)
863  {
864  const auto& pPointMap = pointToMerged[parti];
865  UIndirectList<label>(nodeIds_, pPointMap) = partNodeIDs[parti];
866  }
867 
868  Pout<< "Merged from " << nPoints << " down to " << points_.size()
869  << " points" << endl;
870  }
871 
872 
873  // Merge all cells
874  labelList cellOffsets(partCells.size()+1);
875  cellOffsets[0] = 0;
876  {
877  label nCells = 0;
878  forAll(partCells, parti)
879  {
880  nCells += partCells[parti].size();
881  cellOffsets[parti+1] = nCells;
882  }
883 
884  cellFaces_.setSize(nCells);
885  cellTableId_.setSize(nCells);
886  elementIds_.setSize(nCells, -1);
887 
888  forAll(partCells, parti)
889  {
890  // Copy cells into position
891  const auto& cells = partCells[parti];
892 
893  SubList<faceList> cellSlice
894  (
895  cellFaces_,
896  cells.size(),
897  cellOffsets[parti]
898  );
899  cellSlice = cells;
900 
901  SubList<label>
902  (
903  cellTableId_,
904  cells.size(),
905  cellOffsets[parti]
906  ) = parti;
907 
908  SubList<label>
909  (
910  elementIds_,
911  cells.size(),
912  cellOffsets[parti]
913  ) = partCellIDs[parti];
914 
915  // Renumber faces
916  const auto& pointMap = pointToMerged[parti];
917 
918  for (auto& cellFaces : cellSlice)
919  {
920  for (auto& f : cellFaces)
921  {
922  inplaceRenumber(pointMap, f);
923  }
924  }
925  }
926  }
927 
928 
929  // Add cells to separate zones
930  forAll(partCells, parti)
931  {
932  cellTable_.setMaterial(parti, "fluid");
933  cellTable_.setName(parti, "part" + Foam::name(partIDs[parti]));
934  }
935 
936 
937  // Build map from face to cell and index. Note: use exact match
938  // - no circular equivalence
939  // - but instead pass in ordered faces (lowest numbered vertex first)
940  HashTable<cellFaceIdentifier, face, face::symmHasher> vertsToCell
941  (
942  2*cellOffsets.back()
943  );
944 
945  // Insert cell's faces into hashtable
946  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
947 
948  face rotatedFace;
949  forAll(cellFaces_, celli)
950  {
951  const auto& cFaces = cellFaces_[celli];
952  forAll(cFaces, cFacei)
953  {
954  const face& f = rotateFace(cFaces[cFacei], rotatedFace);
955 
956  const auto fFnd = vertsToCell.find(f);
957  if (fFnd)
958  {
959  // Already inserted. Internal face.
960  vertsToCell.erase(fFnd);
961  }
962  else
963  {
964  vertsToCell.insert(f, cellFaceIdentifier(celli, cFacei));
965  }
966  }
967  }
968 
969 
970  labelList patchToPart(partNames.size());
971  label nPatches = 0;
972  forAll(partFaces, parti)
973  {
974  if (partFaces[parti].size())
975  {
976  Pout<< "Using part " << parti
977  << " name " << partNames[parti]
978  << " as patch " << nPatches
979  << endl;
980 
981  patchToPart[nPatches++] = parti;
982  }
983  }
984  patchToPart.setSize(nPatches);
985  boundaryIds_.setSize(nPatches);
986  patchTypes_.setSize(nPatches, "wall");
987  patchNames_.setSize(nPatches);
988  forAll(patchNames_, patchi)
989  {
990  const label parti = patchToPart[patchi];
991 
992  Pout<< "Matching part " << parti
993  << " name " << partNames[parti]
994  << " faces " << partFaces[parti].size()
995  //<< " points " << partPoints[parti].size()
996  << " to merged faces " << vertsToCell.size()
997  << ", merged points " << points_.size()
998  << endl;
999 
1000  patchNames_[patchi] = word::validate(partNames[parti]);
1001 
1002  const auto& pointMap = pointToMerged[parti];
1003  const auto& faces = partFaces[parti];
1004 
1005  auto& partCellAndFace = boundaryIds_[patchi];
1006  partCellAndFace.setSize(faces.size());
1007 
1008  label patchFacei = 0;
1009  forAll(faces, facei)
1010  {
1011  if (faces[facei].empty())
1012  {
1013  Pout<< "Ignoring empty face:" << facei << endl;
1014  continue;
1015  }
1016 
1017  // Rewrite into mesh-point ordering
1018  const face newF(pointMap, faces[facei]);
1019  // Lookup cell and face on cell using the vertices
1020  const auto cAndF = vertsToCell.find
1021  (
1022  rotateFace
1023  (
1024  newF,
1025  rotatedFace
1026  )
1027  );
1028 
1029  if (!cAndF)
1030  {
1031  //WarningInFunction
1032  // << "Did not find face " << facei
1033  // << " verts:" << faces[facei]
1034  // << " renumbered:" << newF
1035  // << " rotated:" << rotatedFace
1036  // << " in part " << parti
1037  // << endl;
1038  }
1039  else
1040  {
1041  partCellAndFace[patchFacei++] = cAndF();
1042  vertsToCell.erase(cAndF);
1043  }
1044  }
1045  partCellAndFace.setSize(patchFacei);
1046  }
1047  patchPhysicalTypes_.setSize(nPatches, "wall");
1048  patchStarts_.setSize(nPatches, 0);
1049  patchSizes_.setSize(nPatches, 0);
1050 
1051  if (vertsToCell.size())
1052  {
1053  // Unused internal or boundary faces
1054  boundaryIds_.emplace_back(vertsToCell.size());
1055  {
1056  auto& cellAndFaces = boundaryIds_.back();
1057  label i = 0;
1058  forAllConstIters(vertsToCell, iter)
1059  {
1060  cellAndFaces[i++] = iter.val();
1061  }
1062  }
1063 
1064  patchTypes_.push_back("empty");
1065  patchNames_.push_back("defaultFaces");
1066  patchPhysicalTypes_.push_back("empty");
1067  patchStarts_.push_back(0);
1068  patchSizes_.push_back(0);
1069 
1070  Pout<< "Introducing default patch " << patchNames_.size()-1
1071  << " name " << patchNames_.back() << endl;
1072  }
1073 
1074  return true;
1075 }
1076 
1077 
1078 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1079 
1081 (
1082  const fileName& geomFile,
1083  const objectRegistry& registry,
1084  const scalar mergeTol,
1085  const scalar scaleFactor,
1086  const bool setHandedness
1087 )
1088 :
1089  meshReader(geomFile, scaleFactor),
1090  mergeTol_(mergeTol),
1091  setHandedness_(setHandedness)
1092 {}
1093 
1094 
1095 // ************************************************************************* //
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:598
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
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.
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...
scalar y
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)
Foam::SubStrings< StringType > splitSpace(const StringType &str)
Split string into sub-strings at whitespace (TAB, NL, VT, FF, CR, SPC)
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 (not the indices) of 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)
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