Loading...
Searching...
No Matches
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-2025 OpenCFD Ltd.
9-------------------------------------------------------------------------------
10License
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
38namespace Foam
39{
40namespace fileFormats
41{
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 = model.faces(verts);
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 = model.faces(verts);
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 = model.faces(verts);
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 = model.faces(verts);
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 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 }
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
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
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 lineNumber() const noexcept
Const access to the current stream line number.
Definition IOstream.H:409
SubList< faceList > subList
Definition List.H:129
static const Form max
static const Form min
static const cellModel & ref(const modelType model)
Look up reference to cellModel by enumeration. Fatal on failure.
Definition cellModels.C:150
A face is a list of labels corresponding to mesh vertices.
Definition face.H:71
void readVerts(ensightReadFile &is, const label nVerts, const Map< label > &nodeIdToPoints, DynamicList< label > &verts) const
Read set of vertices. Optional mapping.
virtual bool readGeometry(const scalar scaleFactor=1.0)
Read the mesh from the file(s).
void readIDs(ensightReadFile &is, const bool doRead, const label nShapes, labelList &foamToElem, Map< label > &elemToFoam) const
Read set of element/node IDs.
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.
void setHandedness(const cellModel &model, DynamicList< label > &verts, const pointField &points) const
Swap handedness of hex if needed.
const face & rotateFace(const face &f, face &rotatedFace) const
Rotate face so lowest vertex is first.
ensightMeshReader(const fileName &geomFile, const objectRegistry &registry, const scalar mergeTol=SMALL, const scalar scaleFactor=1.0, const bool setHandedness=true)
Construct from case name.
static word validate(const std::string &s, const bool prefix=false)
Construct validated word (no invalid characters).
Definition word.C:39
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition className.H:142
static bool split(const std::string &line, std::string &key, std::string &val)
Definition cpuInfo.C:32
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
const pointField & points
label nPoints
const cellShapeList & cells
Determine correspondence between points. See below.
Geometric merging of points. See below.
#define WarningInFunction
Report a warning using Foam::Warning.
Namespace to isolate specifics for file formats, and some common utilities.
Foam::SubStrings splitSpace(const std::string &str, std::string::size_type pos=0)
Split string into sub-strings at whitespace (TAB, NL, VT, FF, CR, SPC).
Namespace for OpenFOAM.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:40
void inplaceRenumber(const labelUList &oldToNew, IntListType &input)
Inplace renumber the values within a list.
List< labelList > labelListList
List of labelList.
Definition labelList.H:38
List< label > labelList
A List of labels.
Definition List.H:62
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...
messageStream Info
Information stream (stdout output on master, null elsewhere).
List< face > faceList
List of faces.
Definition faceListFwd.H:41
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition Ostream.H:490
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
Ostream & indent(Ostream &os)
Indent stream.
Definition Ostream.H:481
List< faceList > faceListList
List of faceList.
Definition faceListFwd.H:44
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:26
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...
vector point
Point is a vector.
Definition point.H:37
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
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition exprTraits.C:127
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
vectorField pointField
pointField is a vectorField.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition errorManip.H:125
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition Ostream.H:499
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
label newPointi
labelList f(nPoints)
label nPatches
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299
#define forAllConstIters(container, iter)
Iterate across all elements of the container object with const access.
Definition stdFoam.H:235