Loading...
Searching...
No Matches
polyMeshGeometry.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) 2011-2017 OpenFOAM Foundation
9 Copyright (C) 2015-2022 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "polyMeshGeometry.H"
31#include "pyramid.H"
32#include "tetrahedron.H"
33#include "syncTools.H"
35#include "primitiveMeshTools.H"
36
37// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
38
39namespace Foam
40{
42}
43
44// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
45
46namespace Foam
47{
48
49// Average of points
50// Note: use double precision to avoid overflows when summing
52(
53 const UList<point>& points,
55)
56{
57 doubleVector avg(Zero);
58
59 if (const auto n = pointLabels.size(); n)
60 {
61 for (const auto pointi : pointLabels)
62 {
63 avg += points[pointi];
64 }
65 avg /= n;
66 }
67
68 return avg;
69}
70
71} // End namespace Foam
72
73
74// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
75
76void Foam::polyMeshGeometry::updateFaceCentresAndAreas
77(
78 const pointField& p,
79 const labelList& changedFaces
80)
81{
82 const faceList& fcs = mesh_.faces();
83
84 for (const label facei : changedFaces)
85 {
86 const face& f = fcs[facei];
87 const label nPoints = f.size();
88
89 // If the face is a triangle, do a direct calculation for efficiency
90 // and to avoid round-off error-related problems
91 if (nPoints == 3)
92 {
93 const triPointRef tri(p, f[0], f[1], f[2]);
94 faceCentres_[facei] = tri.centre();
95 faceAreas_[facei] = tri.areaNormal();
96 }
97 else
98 {
99 solveVector sumN(Zero);
100 solveScalar sumA(0);
101 solveVector sumAc(Zero);
102
103 // Estimated centre by averaging the face points
104 const solveVector fCentre(pointsAverage(p, f));
105
106 for (label pi = 0; pi < nPoints; ++pi)
107 {
108 const solveVector thisPoint(p[f.thisLabel(pi)]);
109 const solveVector nextPoint(p[f.nextLabel(pi)]);
110
111 solveVector c = thisPoint + nextPoint + fCentre;
112 solveVector n = (nextPoint - thisPoint)^(fCentre - thisPoint);
113 solveScalar a = mag(n);
114
115 sumN += n;
116 sumA += a;
117 sumAc += a*c;
118 }
119
120 if (sumA < ROOTVSMALL)
121 {
122 faceCentres_[facei] = fCentre;
123 faceAreas_[facei] = Zero;
124 }
125 else
126 {
127 faceCentres_[facei] = (1.0/3.0)*sumAc/sumA;
128 faceAreas_[facei] = 0.5*sumN;
129 }
130 }
131 }
132}
133
134
135void Foam::polyMeshGeometry::updateCellCentresAndVols
136(
137 const labelList& changedCells,
138 const labelList& changedFaces
139)
140{
141 const labelList& own = mesh().faceOwner();
142 const cellList& cells = mesh().cells();
143
144 // Clear the fields for accumulation
145 UIndirectList<vector>(cellCentres_, changedCells) = Zero;
146 UIndirectList<scalar>(cellVolumes_, changedCells) = Zero;
147
148
149 // Re-calculate the changed cell centres and volumes
150 for (const label celli : changedCells)
151 {
152 const labelList& cFaces = cells[celli];
153
154 // Estimate the cell centre and bounding box using the face centres
155 vector cEst(Zero);
156 boundBox bb;
157
158 for (const label facei : cFaces)
159 {
160 const point& fc = faceCentres_[facei];
161 cEst += fc;
162 bb.add(fc);
163 }
164 cEst /= cFaces.size();
165
166
167 // Sum up the face-pyramid contributions
168 for (const label facei : cFaces)
169 {
170 // Calculate 3* the face-pyramid volume
171 scalar pyr3Vol = faceAreas_[facei] & (faceCentres_[facei] - cEst);
172
173 if (own[facei] != celli)
174 {
175 pyr3Vol = -pyr3Vol;
176 }
177
178 // Accumulate face-pyramid volume
179 cellVolumes_[celli] += pyr3Vol;
180
181 // Calculate the face-pyramid centre
182 const vector pCtr = (3.0/4.0)*faceCentres_[facei] + (1.0/4.0)*cEst;
183
184 // Accumulate volume-weighted face-pyramid centre
185 cellCentres_[celli] += pyr3Vol*pCtr;
186 }
187
188 // Average the accumulated quantities
189
190 if (mag(cellVolumes_[celli]) > VSMALL)
191 {
192 point cc = cellCentres_[celli] / cellVolumes_[celli];
193
194 // Do additional check for collapsed cells since some volumes
195 // (e.g. 1e-33) do not trigger above but do return completely
196 // wrong cell centre
197 if (bb.contains(cc))
198 {
199 cellCentres_[celli] = cc;
200 }
201 else
202 {
203 cellCentres_[celli] = cEst;
204 }
205 }
206 else
207 {
208 cellCentres_[celli] = cEst;
210
211 cellVolumes_[celli] *= (1.0/3.0);
212 }
213}
214
215
217(
218 const polyMesh& mesh,
219 const labelList& changedFaces
220)
221{
222 const labelList& own = mesh.faceOwner();
223 const labelList& nei = mesh.faceNeighbour();
224
225 labelHashSet affectedCells(2*changedFaces.size());
226
227 for (const label facei : changedFaces)
228 {
229 affectedCells.insert(own[facei]);
230
231 if (mesh.isInternalFace(facei))
232 {
233 affectedCells.insert(nei[facei]);
234 }
235 }
236 return affectedCells.toc();
237}
238
239
240Foam::scalar Foam::polyMeshGeometry::checkNonOrtho
241(
242 const polyMesh& mesh,
243 const bool report,
244 const scalar severeNonorthogonalityThreshold,
245 const label facei,
246 const vector& s, // face area vector
247 const vector& d, // cc-cc vector
248
249 label& severeNonOrth,
250 label& errorNonOrth,
251 labelHashSet* setPtr
252)
253{
254 scalar dDotS = (d & s)/(mag(d)*mag(s) + VSMALL);
255
256 if (dDotS < severeNonorthogonalityThreshold)
257 {
258 label nei = -1;
259
260 if (mesh.isInternalFace(facei))
261 {
262 nei = mesh.faceNeighbour()[facei];
263 }
264
265 if (dDotS > SMALL)
266 {
267 if (report)
268 {
269 // Severe non-orthogonality but mesh still OK
270 Pout<< "Severe non-orthogonality for face " << facei
271 << " between cells " << mesh.faceOwner()[facei]
272 << " and " << nei
273 << ": Angle = "
274 << radToDeg(::acos(dDotS))
275 << " deg." << endl;
276 }
277
278 ++severeNonOrth;
279 }
280 else
281 {
282 // Non-orthogonality greater than 90 deg
283 if (report)
284 {
286 << "Severe non-orthogonality detected for face "
287 << facei
288 << " between cells " << mesh.faceOwner()[facei]
289 << " and " << nei
290 << ": Angle = "
291 << radToDeg(::acos(dDotS))
292 << " deg." << endl;
293 }
294
295 ++errorNonOrth;
296 }
297
298 if (setPtr)
299 {
300 setPtr->insert(facei);
301 }
302 }
303 return dDotS;
304}
305
306
307// Create the neighbour pyramid - it will have positive volume
308bool Foam::polyMeshGeometry::checkFaceTet
309(
310 const polyMesh& mesh,
311 const bool report,
312 const scalar minTetQuality,
313 const pointField& p,
314 const label facei,
315 const point& fc, // face centre
316 const point& cc, // cell centre
317
318 labelHashSet* setPtr
319)
320{
321 const face& f = mesh.faces()[facei];
322
323 forAll(f, fp)
324 {
325 scalar tetQual = tetPointRef
326 (
327 p[f[fp]],
328 p[f.nextLabel(fp)],
329 fc,
330 cc
331 ).quality();
332
333 if (tetQual < minTetQuality)
334 {
335 if (report)
336 {
337 Pout<< "bool polyMeshGeometry::checkFaceTets("
338 << "const bool, const scalar, const pointField&"
339 << ", const pointField&"
340 << ", const labelList&, labelHashSet*) : "
341 << "face " << facei
342 << " has a triangle that points the wrong way." << nl
343 << "Tet quality: " << tetQual
344 << " Face " << facei
345 << endl;
346 }
347 if (setPtr)
348 {
349 setPtr->insert(facei);
350 }
351 return true;
352 }
353 }
355 return false;
356}
357
358
359// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
360
362:
363 mesh_(mesh)
365 correct();
366}
367
368
369// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
370
372{
373 faceAreas_ = mesh_.faceAreas();
374 faceCentres_ = mesh_.faceCentres();
375 cellCentres_ = mesh_.cellCentres();
376 cellVolumes_ = mesh_.cellVolumes();
377}
378
379
381(
382 const pointField& p,
383 const labelList& changedFaces
384)
385{
386 // Update face quantities
387 updateFaceCentresAndAreas(p, changedFaces);
388 // Update cell quantities from face quantities
389 updateCellCentresAndVols(affectedCells(mesh_, changedFaces), changedFaces);
390}
391
392
394(
395 const bool report,
396 const scalar orthWarn,
397 const polyMesh& mesh,
398 const vectorField& cellCentres,
399 const vectorField& faceAreas,
400 const labelList& checkFaces,
401 const List<labelPair>& baffles,
402 labelHashSet* setPtr
403)
404{
405 // for all internal and coupled faces check that the d dot S product
406 // is positive
407
408 const labelList& own = mesh.faceOwner();
409 const labelList& nei = mesh.faceNeighbour();
410 const polyBoundaryMesh& patches = mesh.boundaryMesh();
411
412 // Severe nonorthogonality threshold
413 const scalar severeNonorthogonalityThreshold = ::cos(degToRad(orthWarn));
414
415 // Calculate coupled cell centre
416 pointField neiCc(mesh.nBoundaryFaces());
417
418 for (label facei = mesh.nInternalFaces(); facei < mesh.nFaces(); ++facei)
419 {
420 neiCc[facei-mesh.nInternalFaces()] = cellCentres[own[facei]];
421 }
422
424
425 scalar minDDotS = GREAT;
426
427 scalar sumDDotS = 0;
428 label nDDotS = 0;
429
430 label severeNonOrth = 0;
431
432 label errorNonOrth = 0;
433
434 for (const label facei : checkFaces)
435 {
436 const point& ownCc = cellCentres[own[facei]];
437
438 if (mesh.isInternalFace(facei))
439 {
440 scalar dDotS = checkNonOrtho
441 (
442 mesh,
443 report,
444 severeNonorthogonalityThreshold,
445 facei,
446 faceAreas[facei],
447 cellCentres[nei[facei]] - ownCc,
448
449 severeNonOrth,
450 errorNonOrth,
451 setPtr
452 );
453
454 if (dDotS < minDDotS)
455 {
456 minDDotS = dDotS;
457 }
458
459 sumDDotS += dDotS;
460 ++nDDotS;
461 }
462 else
463 {
464 const label patchi = patches.whichPatch(facei);
465
466 if (patches[patchi].coupled())
467 {
468 scalar dDotS = checkNonOrtho
469 (
470 mesh,
471 report,
472 severeNonorthogonalityThreshold,
473 facei,
474 faceAreas[facei],
475 neiCc[facei-mesh.nInternalFaces()] - ownCc,
476
477 severeNonOrth,
478 errorNonOrth,
479 setPtr
480 );
481
482 if (dDotS < minDDotS)
483 {
484 minDDotS = dDotS;
485 }
486
487 sumDDotS += dDotS;
488 ++nDDotS;
489 }
490 }
491 }
492
493 for (const labelPair& baffle : baffles)
494 {
495 const label face0 = baffle.first();
496 const label face1 = baffle.second();
497
498 const point& ownCc = cellCentres[own[face0]];
499
500 scalar dDotS = checkNonOrtho
501 (
502 mesh,
503 report,
504 severeNonorthogonalityThreshold,
505 face0,
506 faceAreas[face0],
507 cellCentres[own[face1]] - ownCc,
508
509 severeNonOrth,
510 errorNonOrth,
511 setPtr
512 );
513
514 if (dDotS < minDDotS)
515 {
516 minDDotS = dDotS;
517 }
518
519 sumDDotS += dDotS;
520 ++nDDotS;
521 }
522
523 reduce(minDDotS, minOp<scalar>());
524 reduce(sumDDotS, sumOp<scalar>());
525 reduce(nDDotS, sumOp<label>());
526 reduce(severeNonOrth, sumOp<label>());
527 reduce(errorNonOrth, sumOp<label>());
528
529 // Only report if there are some internal faces
530 if (nDDotS > 0)
531 {
532 if (report && minDDotS < severeNonorthogonalityThreshold)
533 {
534 Info<< "Number of non-orthogonality errors: " << errorNonOrth
535 << ". Number of severely non-orthogonal faces: "
536 << severeNonOrth << "." << endl;
537 }
538 }
539
540 if (report)
541 {
542 if (nDDotS > 0)
543 {
544 Info<< "Mesh non-orthogonality Max: "
545 << radToDeg(::acos(minDDotS))
546 << " average: " << radToDeg(::acos(sumDDotS/nDDotS))
547 << endl;
548 }
549 }
550
551 if (errorNonOrth > 0)
552 {
553 if (report)
554 {
556 << "Error in non-orthogonality detected" << endl;
557 }
558
559 return true;
560 }
561
562 if (report)
563 {
564 Info<< "Non-orthogonality check OK.\n" << endl;
565 }
566
567 return false;
568}
569
570
572(
573 const bool report,
574 const scalar minPyrVol,
575 const polyMesh& mesh,
576 const vectorField& cellCentres,
577 const pointField& p,
578 const labelList& checkFaces,
579 const List<labelPair>& baffles,
580 labelHashSet* setPtr
581)
582{
583 // check whether face area vector points to the cell with higher label
584 const labelList& own = mesh.faceOwner();
585 const labelList& nei = mesh.faceNeighbour();
586
587 const faceList& f = mesh.faces();
588
589 label nErrorPyrs = 0;
590
591 for (const label facei : checkFaces)
592 {
593 // Create the owner pyramid - it will have negative volume
594 scalar pyrVol = pyramidPointFaceRef
595 (
596 f[facei],
597 cellCentres[own[facei]]
598 ).mag(p);
599
600 if (pyrVol > -minPyrVol)
601 {
602 ++nErrorPyrs;
603
604 if (report)
605 {
606 Pout<< "bool polyMeshGeometry::checkFacePyramids("
607 << "const bool, const scalar, const pointField&"
608 << ", const labelList&, labelHashSet*): "
609 << "face " << facei << " points the wrong way. " << endl
610 << "Pyramid volume: " << -pyrVol
611 << " Face " << f[facei] << " area: " << f[facei].mag(p)
612 << " Owner cell: " << own[facei] << endl
613 << "Owner cell vertex labels: "
614 << mesh.cells()[own[facei]].labels(f)
615 << endl;
616 }
617 if (setPtr)
618 {
619 setPtr->insert(facei);
620 }
621 }
622
623 if (mesh.isInternalFace(facei))
624 {
625 // Create the neighbour pyramid - it will have positive volume
626 scalar pyrVol =
627 pyramidPointFaceRef(f[facei], cellCentres[nei[facei]]).mag(p);
628
629 if (pyrVol < minPyrVol)
630 {
631 ++nErrorPyrs;
632
633 if (report)
634 {
635 Pout<< "bool polyMeshGeometry::checkFacePyramids("
636 << "const bool, const scalar, const pointField&"
637 << ", const labelList&, labelHashSet*): "
638 << "face " << facei << " points the wrong way. " << endl
639 << "Pyramid volume: " << -pyrVol
640 << " Face " << f[facei] << " area: " << f[facei].mag(p)
641 << " Neighbour cell: " << nei[facei] << endl
642 << "Neighbour cell vertex labels: "
643 << mesh.cells()[nei[facei]].labels(f)
644 << endl;
645 }
646 if (setPtr)
647 {
648 setPtr->insert(facei);
649 }
650 }
651 }
652 }
653
654 for (const labelPair& baffle : baffles)
655 {
656 const label face0 = baffle.first();
657 const label face1 = baffle.second();
658
659 const point& ownCc = cellCentres[own[face0]];
660
661 // Create the owner pyramid - it will have negative volume
662 scalar pyrVolOwn = pyramidPointFaceRef
663 (
664 f[face0],
665 ownCc
666 ).mag(p);
667
668 if (pyrVolOwn > -minPyrVol)
669 {
670 ++nErrorPyrs;
671
672 if (report)
673 {
674 Pout<< "bool polyMeshGeometry::checkFacePyramids("
675 << "const bool, const scalar, const pointField&"
676 << ", const labelList&, labelHashSet*): "
677 << "face " << face0 << " points the wrong way. " << endl
678 << "Pyramid volume: " << -pyrVolOwn
679 << " Face " << f[face0] << " area: " << f[face0].mag(p)
680 << " Owner cell: " << own[face0] << endl
681 << "Owner cell vertex labels: "
682 << mesh.cells()[own[face0]].labels(f)
683 << endl;
684 }
685 if (setPtr)
686 {
687 setPtr->insert(face0);
688 }
689 }
690
691 // Create the neighbour pyramid - it will have positive volume
692 scalar pyrVolNbr =
693 pyramidPointFaceRef(f[face0], cellCentres[own[face1]]).mag(p);
694
695 if (pyrVolNbr < minPyrVol)
696 {
697 ++nErrorPyrs;
698
699 if (report)
700 {
701 Pout<< "bool polyMeshGeometry::checkFacePyramids("
702 << "const bool, const scalar, const pointField&"
703 << ", const labelList&, labelHashSet*): "
704 << "face " << face0 << " points the wrong way. " << endl
705 << "Pyramid volume: " << -pyrVolNbr
706 << " Face " << f[face0] << " area: " << f[face0].mag(p)
707 << " Neighbour cell: " << own[face1] << endl
708 << "Neighbour cell vertex labels: "
709 << mesh.cells()[own[face1]].labels(f)
710 << endl;
711 }
712 if (setPtr)
713 {
714 setPtr->insert(face0);
715 }
716 }
717 }
718
719 reduce(nErrorPyrs, sumOp<label>());
720
721 if (nErrorPyrs > 0)
722 {
723 if (report)
724 {
726 << "Error in face pyramids: faces pointing the wrong way."
727 << endl;
728 }
729
730 return true;
731 }
732
733 if (report)
734 {
735 Info<< "Face pyramids OK.\n" << endl;
736 }
737
738 return false;
739}
740
741
743(
744 const bool report,
745 const scalar minTetQuality,
746 const polyMesh& mesh,
747 const vectorField& cellCentres,
748 const vectorField& faceCentres,
749 const pointField& p,
750 const labelList& checkFaces,
751 const List<labelPair>& baffles,
752 labelHashSet* setPtr
753)
754{
755 // check whether decomposing each cell into tets results in
756 // positive volume, non-flat tets
757 const labelList& own = mesh.faceOwner();
758 const labelList& nei = mesh.faceNeighbour();
759 const polyBoundaryMesh& patches = mesh.boundaryMesh();
760
761 // Calculate coupled cell centre
762 pointField neiCc(mesh.nBoundaryFaces());
763
764 for (label facei = mesh.nInternalFaces(); facei < mesh.nFaces(); ++facei)
765 {
766 neiCc[facei - mesh.nInternalFaces()] = cellCentres[own[facei]];
767 }
768
770
771 label nErrorTets = 0;
772
773 for (const label facei : checkFaces)
774 {
775 // Create the owner pyramid - note: exchange cell and face centre
776 // to get positive volume.
777 bool tetError = checkFaceTet
778 (
779 mesh,
780 report,
781 minTetQuality,
782 p,
783 facei,
784 cellCentres[own[facei]], // face centre
785 faceCentres[facei], // cell centre
786 setPtr
787 );
788
789 if (tetError)
790 {
791 ++nErrorTets;
792 }
793
794 if (mesh.isInternalFace(facei))
795 {
796 // Create the neighbour tets - they will have positive volume
797 bool tetError = checkFaceTet
798 (
799 mesh,
800 report,
801 minTetQuality,
802 p,
803 facei,
804 faceCentres[facei], // face centre
805 cellCentres[nei[facei]], // cell centre
806 setPtr
807 );
808
809 if (tetError)
810 {
811 ++nErrorTets;
812 }
813
814 if
815 (
817 (
818 mesh,
819 facei,
820 minTetQuality,
821 report
822 ) == -1
823 )
824 {
825 ++nErrorTets;
826 if (setPtr)
827 {
828 setPtr->insert(facei);
829 }
830 }
831 }
832 else
833 {
834 label patchi = patches.whichPatch(facei);
835
836 if (patches[patchi].coupled())
837 {
838 if
839 (
841 (
842 mesh,
843 facei,
844 neiCc[facei - mesh.nInternalFaces()],
845 minTetQuality,
846 report
847 ) == -1
848 )
849 {
850 ++nErrorTets;
851 if (setPtr)
852 {
853 setPtr->insert(facei);
854 }
855 }
856 }
857 else
858 {
859 if
860 (
862 (
863 mesh,
864 facei,
865 minTetQuality,
866 report
867 ) == -1
868 )
869 {
870 ++nErrorTets;
871 if (setPtr)
872 {
873 setPtr->insert(facei);
874 }
875 }
876 }
877 }
878 }
879
880 for (const labelPair& baffle : baffles)
881 {
882 const label face0 = baffle.first();
883 const label face1 = baffle.second();
884
885 bool tetError = checkFaceTet
886 (
887 mesh,
888 report,
889 minTetQuality,
890 p,
891 face0,
892 cellCentres[own[face0]], // face centre
893 faceCentres[face0], // cell centre
894 setPtr
895 );
896
897 if (tetError)
898 {
899 ++nErrorTets;
900 }
901
902 // Create the neighbour tets - they will have positive volume
903 tetError = checkFaceTet
904 (
905 mesh,
906 report,
907 minTetQuality,
908 p,
909 face0,
910 faceCentres[face0], // face centre
911 cellCentres[own[face1]], // cell centre
912 setPtr
913 );
914
915 if (tetError)
916 {
917 ++nErrorTets;
918 }
919
920 if
921 (
923 (
924 mesh,
925 face0,
926 cellCentres[own[face1]],
927 minTetQuality,
928 report
929 ) == -1
930 )
931 {
932 ++nErrorTets;
933 if (setPtr)
934 {
935 setPtr->insert(face0);
936 }
937 }
938 }
939
940 reduce(nErrorTets, sumOp<label>());
941
942 if (nErrorTets > 0)
943 {
944 if (report)
945 {
947 << "Error in face decomposition: negative tets."
948 << endl;
949 }
950
951 return true;
952 }
953
954 if (report)
955 {
956 Info<< "Face tets OK.\n" << endl;
957 }
958
959 return false;
960}
961
962
964(
965 const bool report,
966 const scalar internalSkew,
967 const scalar boundarySkew,
968 const polyMesh& mesh,
969 const pointField& points,
970 const vectorField& cellCentres,
971 const vectorField& faceCentres,
972 const vectorField& faceAreas,
973 const labelList& checkFaces,
974 const List<labelPair>& baffles,
975 labelHashSet* setPtr
976)
977{
978 // Warn if the skew correction vector is more than skew times
979 // larger than the face area vector
980
981 const labelList& own = mesh.faceOwner();
982 const labelList& nei = mesh.faceNeighbour();
983 const faceList& faces = mesh.faces();
984 const polyBoundaryMesh& patches = mesh.boundaryMesh();
985
986 // Calculate coupled cell centre
987 pointField neiCc;
988 syncTools::swapBoundaryCellPositions(mesh, cellCentres, neiCc);
989
990 scalar maxSkew = 0;
991
992 label nWarnSkew = 0;
993
994 for (const label facei : checkFaces)
995 {
996 if (mesh.isInternalFace(facei))
997 {
998 scalar skewness = primitiveMeshTools::faceSkewness
999 (
1000 faces,
1001 points,
1002 faceCentres,
1003 faceAreas,
1004
1005 facei,
1006 cellCentres[own[facei]],
1007 cellCentres[nei[facei]]
1008 );
1009
1010 // Check if the skewness vector is greater than the PN vector.
1011 // This does not cause trouble but is a good indication of a poor
1012 // mesh.
1013 if (skewness > internalSkew)
1014 {
1015 ++nWarnSkew;
1016
1017 if (report)
1018 {
1019 Pout<< "Severe skewness for face " << facei
1020 << " skewness = " << skewness << endl;
1021 }
1022 if (setPtr)
1023 {
1024 setPtr->insert(facei);
1025 }
1026 }
1027
1028 maxSkew = max(maxSkew, skewness);
1029 }
1030 else if (patches[patches.whichPatch(facei)].coupled())
1031 {
1032 scalar skewness = primitiveMeshTools::faceSkewness
1033 (
1034 faces,
1035 points,
1036 faceCentres,
1037 faceAreas,
1038
1039 facei,
1040 cellCentres[own[facei]],
1041 neiCc[facei-mesh.nInternalFaces()]
1042 );
1043
1044 // Check if the skewness vector is greater than the PN vector.
1045 // This does not cause trouble but is a good indication of a poor
1046 // mesh.
1047 if (skewness > internalSkew)
1048 {
1049 ++nWarnSkew;
1050
1051 if (report)
1052 {
1053 Pout<< "Severe skewness for coupled face " << facei
1054 << " skewness = " << skewness << endl;
1055 }
1056 if (setPtr)
1057 {
1058 setPtr->insert(facei);
1059 }
1060 }
1061
1062 maxSkew = max(maxSkew, skewness);
1063 }
1064 else
1065 {
1067 (
1068 faces,
1069 points,
1070 faceCentres,
1071 faceAreas,
1072
1073 facei,
1074 cellCentres[own[facei]]
1075 );
1076
1077
1078 // Check if the skewness vector is greater than the PN vector.
1079 // This does not cause trouble but is a good indication of a poor
1080 // mesh.
1081 if (skewness > boundarySkew)
1082 {
1083 ++nWarnSkew;
1084
1085 if (report)
1086 {
1087 Pout<< "Severe skewness for boundary face " << facei
1088 << " skewness = " << skewness << endl;
1089 }
1090 if (setPtr)
1091 {
1092 setPtr->insert(facei);
1093 }
1094 }
1095
1096 maxSkew = max(maxSkew, skewness);
1097 }
1098 }
1099
1100 for (const labelPair& baffle : baffles)
1101 {
1102 const label face0 = baffle.first();
1103 const label face1 = baffle.second();
1104
1105 const point& ownCc = cellCentres[own[face0]];
1106 const point& neiCc = cellCentres[own[face1]];
1107
1108 scalar skewness = primitiveMeshTools::faceSkewness
1109 (
1110 faces,
1111 points,
1112 faceCentres,
1113 faceAreas,
1114
1115 face0,
1116 ownCc,
1117 neiCc
1118 );
1119
1120 // Check if the skewness vector is greater than the PN vector.
1121 // This does not cause trouble but is a good indication of a poor
1122 // mesh.
1123 if (skewness > internalSkew)
1124 {
1125 ++nWarnSkew;
1126
1127 if (report)
1128 {
1129 Pout<< "Severe skewness for face " << face0
1130 << " skewness = " << skewness << endl;
1131 }
1132 if (setPtr)
1133 {
1134 setPtr->insert(face0);
1135 }
1136 }
1137
1138 maxSkew = max(maxSkew, skewness);
1139 }
1140
1141
1142 reduce(maxSkew, maxOp<scalar>());
1143 reduce(nWarnSkew, sumOp<label>());
1144
1145 if (nWarnSkew > 0)
1146 {
1147 if (report)
1148 {
1150 << 100*maxSkew
1151 << " percent.\nThis may impair the quality of the result." << nl
1152 << nWarnSkew << " highly skew faces detected."
1153 << endl;
1154 }
1155
1156 return true;
1157 }
1158
1159 if (report)
1160 {
1161 Info<< "Max skewness = " << 100*maxSkew
1162 << " percent. Face skewness OK.\n" << endl;
1163 }
1164
1165 return false;
1166}
1167
1168
1170(
1171 const bool report,
1172 const scalar warnWeight,
1173 const polyMesh& mesh,
1174 const vectorField& cellCentres,
1175 const vectorField& faceCentres,
1176 const vectorField& faceAreas,
1177 const labelList& checkFaces,
1178 const List<labelPair>& baffles,
1179 labelHashSet* setPtr
1180)
1181{
1182 // Warn if the delta factor (0..1) is too large.
1183
1184 const labelList& own = mesh.faceOwner();
1185 const labelList& nei = mesh.faceNeighbour();
1186 const polyBoundaryMesh& patches = mesh.boundaryMesh();
1187
1188 // Calculate coupled cell centre
1189 pointField neiCc(mesh.nBoundaryFaces());
1190
1191 for (label facei = mesh.nInternalFaces(); facei < mesh.nFaces(); ++facei)
1192 {
1193 neiCc[facei-mesh.nInternalFaces()] = cellCentres[own[facei]];
1194 }
1196
1197
1198 scalar minWeight = GREAT;
1199
1200 label nWarnWeight = 0;
1201
1202 for (const label facei : checkFaces)
1203 {
1204 const point& fc = faceCentres[facei];
1205 const vector& fa = faceAreas[facei];
1206
1207 scalar dOwn = mag(fa & (fc-cellCentres[own[facei]]));
1208
1209 if (mesh.isInternalFace(facei))
1210 {
1211 scalar dNei = mag(fa & (cellCentres[nei[facei]]-fc));
1212 scalar weight = min(dNei,dOwn)/(dNei+dOwn+VSMALL);
1213
1214 if (weight < warnWeight)
1215 {
1216 ++nWarnWeight;
1217
1218 if (report)
1219 {
1220 Pout<< "Small weighting factor for face " << facei
1221 << " weight = " << weight << endl;
1222 }
1223 if (setPtr)
1224 {
1225 setPtr->insert(facei);
1226 }
1227 }
1228
1229 minWeight = min(minWeight, weight);
1230 }
1231 else
1232 {
1233 label patchi = patches.whichPatch(facei);
1234
1235 if (patches[patchi].coupled())
1236 {
1237 scalar dNei = mag(fa & (neiCc[facei-mesh.nInternalFaces()]-fc));
1238 scalar weight = min(dNei,dOwn)/(dNei+dOwn+VSMALL);
1239
1240 if (weight < warnWeight)
1241 {
1242 ++nWarnWeight;
1243
1244 if (report)
1245 {
1246 Pout<< "Small weighting factor for face " << facei
1247 << " weight = " << weight << endl;
1248 }
1249 if (setPtr)
1250 {
1251 setPtr->insert(facei);
1252 }
1253 }
1254
1255 minWeight = min(minWeight, weight);
1256 }
1257 }
1258 }
1259
1260 for (const labelPair& baffle : baffles)
1261 {
1262 const label face0 = baffle.first();
1263 const label face1 = baffle.second();
1264
1265 const point& ownCc = cellCentres[own[face0]];
1266 const point& fc = faceCentres[face0];
1267 const vector& fa = faceAreas[face0];
1268
1269 scalar dOwn = mag(fa & (fc-ownCc));
1270 scalar dNei = mag(fa & (cellCentres[own[face1]]-fc));
1271 scalar weight = min(dNei,dOwn)/(dNei+dOwn+VSMALL);
1272
1273 if (weight < warnWeight)
1274 {
1275 ++nWarnWeight;
1276
1277 if (report)
1278 {
1279 Pout<< "Small weighting factor for face " << face0
1280 << " weight = " << weight << endl;
1281 }
1282 if (setPtr)
1283 {
1284 setPtr->insert(face0);
1285 }
1286 }
1287
1288 minWeight = min(minWeight, weight);
1289 }
1290
1291 reduce(minWeight, minOp<scalar>());
1292 reduce(nWarnWeight, sumOp<label>());
1293
1294 if (minWeight < warnWeight)
1295 {
1296 if (report)
1297 {
1299 << minWeight << '.' << nl
1300 << nWarnWeight << " faces with small weights detected."
1301 << endl;
1302 }
1303
1304 return true;
1305 }
1306
1307 if (report)
1308 {
1309 Info<< "Min weight = " << minWeight
1310 << ". Weights OK.\n" << endl;
1311 }
1312
1313 return false;
1314}
1315
1316
1318(
1319 const bool report,
1320 const scalar warnRatio,
1321 const polyMesh& mesh,
1322 const scalarField& cellVolumes,
1323 const labelList& checkFaces,
1324 const List<labelPair>& baffles,
1325 labelHashSet* setPtr
1326)
1327{
1328 // Warn if the volume ratio between neighbouring cells is too large
1329
1330 const labelList& own = mesh.faceOwner();
1331 const labelList& nei = mesh.faceNeighbour();
1332 const polyBoundaryMesh& patches = mesh.boundaryMesh();
1333
1334 // Calculate coupled cell vol
1335 scalarField neiVols(mesh.nBoundaryFaces());
1336
1337 for (label facei = mesh.nInternalFaces(); facei < mesh.nFaces(); ++facei)
1338 {
1339 neiVols[facei-mesh.nInternalFaces()] = cellVolumes[own[facei]];
1340 }
1342
1343
1344 scalar minRatio = GREAT;
1345
1346 label nWarnRatio = 0;
1347
1348 for (const label facei : checkFaces)
1349 {
1350 scalar ownVol = mag(cellVolumes[own[facei]]);
1351
1352 scalar neiVol = -GREAT;
1353
1354 if (mesh.isInternalFace(facei))
1355 {
1356 neiVol = mag(cellVolumes[nei[facei]]);
1357 }
1358 else
1359 {
1360 label patchi = patches.whichPatch(facei);
1361
1362 if (patches[patchi].coupled())
1363 {
1364 neiVol = mag(neiVols[facei-mesh.nInternalFaces()]);
1365 }
1366 }
1367
1368 if (neiVol >= 0)
1369 {
1370 scalar ratio = min(ownVol, neiVol) / (max(ownVol, neiVol) + VSMALL);
1371
1372 if (ratio < warnRatio)
1373 {
1374 ++nWarnRatio;
1375
1376 if (report)
1377 {
1378 Pout<< "Small ratio for face " << facei
1379 << " ratio = " << ratio << endl;
1380 }
1381 if (setPtr)
1382 {
1383 setPtr->insert(facei);
1384 }
1385 }
1386
1387 minRatio = min(minRatio, ratio);
1388 }
1389 }
1390
1391 for (const labelPair& baffle : baffles)
1392 {
1393 const label face0 = baffle.first();
1394 const label face1 = baffle.second();
1395
1396 scalar ownVol = mag(cellVolumes[own[face0]]);
1397
1398 scalar neiVol = mag(cellVolumes[own[face1]]);
1399
1400 if (neiVol >= 0)
1401 {
1402 scalar ratio = min(ownVol, neiVol) / (max(ownVol, neiVol) + VSMALL);
1403
1404 if (ratio < warnRatio)
1405 {
1406 ++nWarnRatio;
1407
1408 if (report)
1409 {
1410 Pout<< "Small ratio for face " << face0
1411 << " ratio = " << ratio << endl;
1412 }
1413 if (setPtr)
1414 {
1415 setPtr->insert(face0);
1416 }
1417 }
1418
1419 minRatio = min(minRatio, ratio);
1420 }
1421 }
1422
1423 reduce(minRatio, minOp<scalar>());
1424 reduce(nWarnRatio, sumOp<label>());
1425
1426 if (minRatio < warnRatio)
1427 {
1428 if (report)
1429 {
1431 << minRatio << '.' << nl
1432 << nWarnRatio << " faces with small ratios detected."
1433 << endl;
1434 }
1435
1436 return true;
1437 }
1438
1439 if (report)
1440 {
1441 Info<< "Min ratio = " << minRatio
1442 << ". Ratios OK.\n" << endl;
1443 }
1444
1445 return false;
1447
1448
1449// Check convexity of angles in a face. Allow a slight non-convexity.
1450// E.g. maxDeg = 10 allows for angles < 190 (or 10 degrees concavity)
1451// (if truly concave and points not visible from face centre the face-pyramid
1452// check in checkMesh will fail)
1454(
1455 const bool report,
1456 const scalar maxDeg,
1457 const polyMesh& mesh,
1458 const vectorField& faceAreas,
1459 const pointField& p,
1460 const labelList& checkFaces,
1461 labelHashSet* setPtr
1462)
1463{
1464 if (maxDeg < -SMALL || maxDeg > 180+SMALL)
1465 {
1467 << "maxDeg should be [0..180] but is now " << maxDeg
1468 << abort(FatalError);
1469 }
1470
1471 const scalar maxSin = Foam::sin(degToRad(maxDeg));
1472
1473 const faceList& fcs = mesh.faces();
1474
1475 scalar maxEdgeSin = 0.0;
1476
1477 label nConcave = 0;
1478
1479 label errorFacei = -1;
1480
1481 for (const label facei : checkFaces)
1482 {
1483 const face& f = fcs[facei];
1484
1485 const vector faceNormal = normalised(faceAreas[facei]);
1486
1487 // Normalized vector from f[size-1] to f[0];
1488 vector ePrev(p[f.first()] - p[f.last()]);
1489 scalar magEPrev = mag(ePrev);
1490 ePrev /= magEPrev + VSMALL;
1491
1492 forAll(f, fp0)
1493 {
1494 // Normalized vector between two consecutive points
1495 vector e10(p[f.nextLabel(fp0)] - p[f.thisLabel(fp0)]);
1496 scalar magE10 = mag(e10);
1497 e10 /= magE10 + VSMALL;
1498
1499 if (magEPrev > SMALL && magE10 > SMALL)
1500 {
1501 vector edgeNormal = ePrev ^ e10;
1502 scalar magEdgeNormal = mag(edgeNormal);
1503
1504 if (magEdgeNormal < maxSin)
1505 {
1506 // Edges (almost) aligned -> face is ok.
1507 }
1508 else
1509 {
1510 // Check normal
1511 edgeNormal /= magEdgeNormal;
1512
1513 if ((edgeNormal & faceNormal) < SMALL)
1514 {
1515 if (facei != errorFacei)
1516 {
1517 // Count only one error per face.
1518 errorFacei = facei;
1519 ++nConcave;
1520 }
1521
1522 maxEdgeSin = max(maxEdgeSin, magEdgeNormal);
1523
1524 if (setPtr)
1525 {
1526 setPtr->insert(facei);
1527 }
1528 }
1529 }
1530 }
1531
1532 ePrev = e10;
1533 magEPrev = magE10;
1534 }
1535 }
1536
1537 reduce(nConcave, sumOp<label>());
1538 reduce(maxEdgeSin, maxOp<scalar>());
1539
1540 if (report)
1541 {
1542 if (maxEdgeSin > SMALL)
1543 {
1544 scalar maxConcaveDegr =
1545 radToDeg(Foam::asin(Foam::min(1.0, maxEdgeSin)));
1546
1547 Info<< "There are " << nConcave
1548 << " faces with concave angles between consecutive"
1549 << " edges. Max concave angle = " << maxConcaveDegr
1550 << " degrees.\n" << endl;
1551 }
1552 else
1553 {
1554 Info<< "All angles in faces are convex or less than " << maxDeg
1555 << " degrees concave.\n" << endl;
1556 }
1557 }
1558
1559 if (nConcave > 0)
1560 {
1561 if (report)
1562 {
1564 << nConcave << " face points with severe concave angle (> "
1565 << maxDeg << " deg) found.\n"
1566 << endl;
1567 }
1568
1569 return true;
1570 }
1572 return false;
1573}
1574
1575
1576// Check twist of faces. Is calculated as the difference between normals of
1577// individual triangles and the cell-cell centre edge.
1579(
1580 const bool report,
1581 const scalar minTwist,
1582 const polyMesh& mesh,
1583 const vectorField& cellCentres,
1584 const vectorField& faceAreas,
1585 const vectorField& faceCentres,
1586 const pointField& p,
1587 const labelList& checkFaces,
1588 labelHashSet* setPtr
1589)
1590{
1591 if (minTwist < -1-SMALL || minTwist > 1+SMALL)
1592 {
1594 << "minTwist should be [-1..1] but is now " << minTwist
1595 << abort(FatalError);
1596 }
1597
1598
1599 const faceList& fcs = mesh.faces();
1600
1601 label nWarped = 0;
1602
1603 const labelList& own = mesh.faceOwner();
1604 const labelList& nei = mesh.faceNeighbour();
1605 const polyBoundaryMesh& patches = mesh.boundaryMesh();
1606
1607 // Calculate coupled cell centre
1608 pointField neiCc(mesh.nBoundaryFaces());
1609
1610 for (label facei = mesh.nInternalFaces(); facei < mesh.nFaces(); ++facei)
1611 {
1612 neiCc[facei-mesh.nInternalFaces()] = cellCentres[own[facei]];
1613 }
1615
1616 for (const label facei : checkFaces)
1617 {
1618 const face& f = fcs[facei];
1619
1620 if (f.size() > 3)
1621 {
1622 vector nf(Zero);
1623
1624 if (mesh.isInternalFace(facei))
1625 {
1626 nf =
1628 (
1629 cellCentres[nei[facei]]
1630 - cellCentres[own[facei]]
1631 );
1632 }
1633 else if (patches[patches.whichPatch(facei)].coupled())
1634 {
1635 nf =
1637 (
1638 neiCc[facei-mesh.nInternalFaces()]
1639 - cellCentres[own[facei]]
1640 );
1641 }
1642 else
1643 {
1644 nf =
1646 (
1647 faceCentres[facei]
1648 - cellCentres[own[facei]]
1649 );
1650 }
1651
1652 if (nf != vector::zero)
1653 {
1654 const point& fc = faceCentres[facei];
1655
1656 forAll(f, fpI)
1657 {
1658 vector triArea
1659 (
1661 (
1662 p[f[fpI]],
1663 p[f.nextLabel(fpI)],
1664 fc
1665 )
1666 );
1667
1668 scalar magTri = mag(triArea);
1669
1670 if (magTri > VSMALL && ((nf & triArea/magTri) < minTwist))
1671 {
1672 ++nWarped;
1673
1674 if (setPtr)
1675 {
1676 setPtr->insert(facei);
1677 }
1678
1679 break;
1680 }
1681 }
1682 }
1683 }
1684 }
1685
1686 reduce(nWarped, sumOp<label>());
1687
1688 if (report)
1689 {
1690 if (nWarped> 0)
1691 {
1692 Info<< "There are " << nWarped
1693 << " faces with cosine of the angle"
1694 << " between triangle normal and face normal less than "
1695 << minTwist << nl << endl;
1696 }
1697 else
1698 {
1699 Info<< "All faces are flat in that the cosine of the angle"
1700 << " between triangle normal and face normal less than "
1701 << minTwist << nl << endl;
1702 }
1703 }
1704
1705 if (nWarped > 0)
1706 {
1707 if (report)
1708 {
1710 << nWarped << " faces with severe warpage "
1711 << "(cosine of the angle between triangle normal and "
1712 << "face normal < " << minTwist << ") found.\n"
1713 << endl;
1714 }
1715
1716 return true;
1718
1719 return false;
1720}
1721
1722
1723// Like checkFaceTwist but compares normals of consecutive triangles.
1725(
1726 const bool report,
1727 const scalar minTwist,
1728 const polyMesh& mesh,
1729 const vectorField& faceAreas,
1730 const vectorField& faceCentres,
1731 const pointField& p,
1732 const labelList& checkFaces,
1733 labelHashSet* setPtr
1734)
1735{
1736 if (minTwist < -1-SMALL || minTwist > 1+SMALL)
1737 {
1739 << "minTwist should be [-1..1] but is now " << minTwist
1740 << abort(FatalError);
1741 }
1742
1743 const faceList& fcs = mesh.faces();
1744
1745 label nWarped = 0;
1746
1747 for (const label facei : checkFaces)
1748 {
1749 const face& f = fcs[facei];
1750
1751 if (f.size() > 3)
1752 {
1753 const point& fc = faceCentres[facei];
1754
1755 // Find starting triangle (at startFp) with non-zero area
1756 label startFp = -1;
1757 vector prevN;
1758
1759 forAll(f, fp)
1760 {
1762 (
1763 p[f[fp]],
1764 p[f.nextLabel(fp)],
1765 fc
1766 );
1767
1768 scalar magTri = mag(prevN);
1769
1770 if (magTri > VSMALL)
1771 {
1772 startFp = fp;
1773 prevN /= magTri;
1774 break;
1775 }
1776 }
1777
1778 if (startFp != -1)
1779 {
1780 label fp = startFp;
1781
1782 do
1783 {
1784 fp = f.fcIndex(fp);
1785
1786 vector triN
1787 (
1789 (
1790 p[f[fp]],
1791 p[f.nextLabel(fp)],
1792 fc
1793 )
1794 );
1795 scalar magTri = mag(triN);
1796
1797 if (magTri > VSMALL)
1798 {
1799 triN /= magTri;
1800
1801 if ((prevN & triN) < minTwist)
1802 {
1803 ++nWarped;
1804
1805 if (setPtr)
1806 {
1807 setPtr->insert(facei);
1808 }
1809
1810 break;
1811 }
1812
1813 prevN = triN;
1814 }
1815 else if (minTwist > 0)
1816 {
1817 ++nWarped;
1818
1819 if (setPtr)
1820 {
1821 setPtr->insert(facei);
1822 }
1823
1824 break;
1825 }
1826 }
1827 while (fp != startFp);
1828 }
1829 }
1830 }
1831
1832
1833 reduce(nWarped, sumOp<label>());
1834
1835 if (report)
1836 {
1837 if (nWarped> 0)
1838 {
1839 Info<< "There are " << nWarped
1840 << " faces with cosine of the angle"
1841 << " between consecutive triangle normals less than "
1842 << minTwist << nl << endl;
1843 }
1844 else
1845 {
1846 Info<< "All faces are flat in that the cosine of the angle"
1847 << " between consecutive triangle normals is less than "
1848 << minTwist << nl << endl;
1849 }
1850 }
1851
1852 if (nWarped > 0)
1853 {
1854 if (report)
1855 {
1857 << nWarped << " faces with severe warpage "
1858 << "(cosine of the angle between consecutive triangle normals"
1859 << " < " << minTwist << ") found.\n"
1860 << endl;
1861 }
1862
1863 return true;
1864 }
1865
1866 return false;
1867}
1868
1869
1871(
1872 const bool report,
1873 const scalar minFlatness,
1874 const polyMesh& mesh,
1875 const vectorField& faceAreas,
1876 const vectorField& faceCentres,
1877 const pointField& p,
1878 const labelList& checkFaces,
1879 labelHashSet* setPtr
1880)
1881{
1882 if (minFlatness < -SMALL || minFlatness > 1+SMALL)
1883 {
1885 << "minFlatness should be [0..1] but is now " << minFlatness
1886 << abort(FatalError);
1887 }
1888
1889 const faceList& fcs = mesh.faces();
1890
1891 label nWarped = 0;
1892
1893 for (const label facei : checkFaces)
1894 {
1895 const face& f = fcs[facei];
1896
1897 if (f.size() > 3)
1898 {
1899 const point& fc = faceCentres[facei];
1900
1901 // Sum triangle areas
1902 scalar sumArea = 0.0;
1903
1904 forAll(f, fp)
1905 {
1906 sumArea += triPointRef
1907 (
1908 p[f[fp]],
1909 p[f.nextLabel(fp)],
1910 fc
1911 ).mag();
1912 }
1913
1914 if (sumArea/mag(faceAreas[facei]) < minFlatness)
1915 {
1916 ++nWarped;
1917 if (setPtr)
1918 {
1919 setPtr->insert(facei);
1920 }
1921 }
1922 }
1923 }
1924
1925 reduce(nWarped, sumOp<label>());
1926
1927 if (report)
1928 {
1929 if (nWarped> 0)
1930 {
1931 Info<< "There are " << nWarped
1932 << " faces with area of individual triangles"
1933 << " compared to overall area less than "
1934 << minFlatness << nl << endl;
1935 }
1936 else
1937 {
1938 Info<< "All faces are flat in that the area of individual triangles"
1939 << " compared to overall area is less than "
1940 << minFlatness << nl << endl;
1941 }
1942 }
1943
1944 if (nWarped > 0)
1945 {
1946 if (report)
1947 {
1949 << nWarped << " non-flat faces "
1950 << "(area of individual triangles"
1951 << " compared to overall area"
1952 << " < " << minFlatness << ") found.\n"
1953 << endl;
1954 }
1955
1956 return true;
1957 }
1958
1959 return false;
1960}
1961
1962
1964(
1965 const bool report,
1966 const scalar minArea,
1967 const polyMesh& mesh,
1968 const vectorField& faceAreas,
1969 const labelList& checkFaces,
1970 labelHashSet* setPtr
1971)
1972{
1973 label nZeroArea = 0;
1974
1975 for (const label facei : checkFaces)
1976 {
1977 if (mag(faceAreas[facei]) < minArea)
1978 {
1979 ++nZeroArea;
1980 if (setPtr)
1981 {
1982 setPtr->insert(facei);
1983 }
1984 }
1985 }
1986
1987
1988 reduce(nZeroArea, sumOp<label>());
1989
1990 if (report)
1991 {
1992 if (nZeroArea > 0)
1993 {
1994 Info<< "There are " << nZeroArea
1995 << " faces with area < " << minArea << '.' << nl << endl;
1996 }
1997 else
1998 {
1999 Info<< "All faces have area > " << minArea << '.' << nl << endl;
2000 }
2001 }
2002
2003 if (nZeroArea > 0)
2004 {
2005 if (report)
2006 {
2008 << nZeroArea << " faces with area < " << minArea
2009 << " found.\n"
2010 << endl;
2011 }
2012
2013 return true;
2014 }
2015
2016 return false;
2017}
2018
2019
2021(
2022 const bool report,
2023 const scalar warnDet,
2024 const polyMesh& mesh,
2025 const vectorField& faceAreas,
2026 const labelList& checkFaces,
2027 const labelList& affectedCells,
2028 labelHashSet* setPtr
2029)
2030{
2031 const cellList& cells = mesh.cells();
2032
2033 scalar minDet = GREAT;
2034 scalar sumDet = 0.0;
2035 label nSumDet = 0;
2036 label nWarnDet = 0;
2037
2038 for (const label celli : affectedCells)
2039 {
2040 const cell& cFaces = cells[celli];
2041
2042 tensor areaSum(Zero);
2043 scalar magAreaSum = 0;
2044
2045 for (const label facei : cFaces)
2046 {
2047 scalar magArea = mag(faceAreas[facei]);
2048
2049 magAreaSum += magArea;
2050 areaSum += faceAreas[facei]*(faceAreas[facei]/(magArea+VSMALL));
2051 }
2052
2053 scalar scaledDet = det(areaSum/(magAreaSum+VSMALL))/0.037037037037037;
2054
2055 minDet = min(minDet, scaledDet);
2056 sumDet += scaledDet;
2057 ++nSumDet;
2058
2059 if (scaledDet < warnDet)
2060 {
2061 ++nWarnDet;
2062 if (setPtr)
2063 {
2064 setPtr->insert(cFaces); // Insert all faces of the cell
2065 }
2066 }
2067 }
2068
2069 reduce(minDet, minOp<scalar>());
2070 reduce(sumDet, sumOp<scalar>());
2071 reduce(nSumDet, sumOp<label>());
2072 reduce(nWarnDet, sumOp<label>());
2073
2074 if (report)
2075 {
2076 if (nSumDet > 0)
2077 {
2078 Info<< "Cell determinant (1 = uniform cube) : average = "
2079 << sumDet / nSumDet << " min = " << minDet << endl;
2080 }
2081
2082 if (nWarnDet > 0)
2083 {
2084 Info<< "There are " << nWarnDet
2085 << " cells with determinant < " << warnDet << '.' << nl
2086 << endl;
2087 }
2088 else
2089 {
2090 Info<< "All faces have determinant > " << warnDet << '.' << nl
2091 << endl;
2092 }
2093 }
2094
2095 if (nWarnDet > 0)
2096 {
2097 if (report)
2098 {
2100 << nWarnDet << " cells with determinant < " << warnDet
2101 << " found.\n"
2102 << endl;
2103 }
2104
2105 return true;
2106 }
2107
2108 return false;
2109}
2110
2111
2113(
2114 const bool report,
2115 const scalar minEdgeLength,
2116 const polyMesh& mesh,
2117 const labelList& checkFaces,
2118 labelHashSet* setPtr
2119)
2120{
2121 const scalar reportLenSqr(Foam::sqr(minEdgeLength));
2122
2123 const pointField& points = mesh.points();
2124 const faceList& faces = mesh.faces();
2125
2126 scalar minLenSqr = sqr(GREAT);
2127 scalar maxLenSqr = -sqr(GREAT);
2128
2129 label nSmall = 0;
2130
2131 for (const label facei : checkFaces)
2132 {
2133 const face& f = faces[facei];
2134
2135 forAll(f, fp)
2136 {
2137 label fp1 = f.fcIndex(fp);
2138
2139 scalar magSqrE = magSqr(points[f[fp]] - points[f[fp1]]);
2140
2141 if (setPtr && magSqrE < reportLenSqr)
2142 {
2143 if (setPtr->insert(f[fp]) || setPtr->insert(f[fp1]))
2144 {
2145 nSmall++;
2146 }
2147 }
2148
2149 minLenSqr = min(minLenSqr, magSqrE);
2150 maxLenSqr = max(maxLenSqr, magSqrE);
2151 }
2152 }
2153
2154 reduce(minLenSqr, minOp<scalar>());
2155 reduce(maxLenSqr, maxOp<scalar>());
2156
2157 reduce(nSmall, sumOp<label>());
2158
2159 if (nSmall > 0)
2160 {
2161 if (report)
2162 {
2163 Info<< " *Edges too small, min/max edge length = "
2164 << sqrt(minLenSqr) << " " << sqrt(maxLenSqr)
2165 << ", number too small: " << nSmall << endl;
2166 }
2167
2168 return true;
2169 }
2170
2171 if (report)
2172 {
2173 Info<< " Min/max edge length = "
2174 << sqrt(minLenSqr) << " " << sqrt(maxLenSqr) << " OK." << endl;
2175 }
2176
2177 return false;
2178}
2179
2180
2182(
2183 const bool report,
2184 const scalar orthWarn,
2185 const labelList& checkFaces,
2186 const List<labelPair>& baffles,
2187 labelHashSet* setPtr
2188) const
2189{
2190 return checkFaceDotProduct
2191 (
2192 report,
2193 orthWarn,
2194 mesh_,
2195 cellCentres_,
2196 faceAreas_,
2197 checkFaces,
2198 baffles,
2199 setPtr
2200 );
2201}
2202
2203
2205(
2206 const bool report,
2207 const scalar minPyrVol,
2208 const pointField& p,
2209 const labelList& checkFaces,
2210 const List<labelPair>& baffles,
2211 labelHashSet* setPtr
2212) const
2213{
2214 return checkFacePyramids
2215 (
2216 report,
2217 minPyrVol,
2218 mesh_,
2219 cellCentres_,
2220 p,
2221 checkFaces,
2222 baffles,
2223 setPtr
2224 );
2225}
2226
2227
2229(
2230 const bool report,
2231 const scalar minTetQuality,
2232 const pointField& p,
2233 const labelList& checkFaces,
2234 const List<labelPair>& baffles,
2235 labelHashSet* setPtr
2236) const
2237{
2238 return checkFaceTets
2239 (
2240 report,
2241 minTetQuality,
2242 mesh_,
2243 cellCentres_,
2244 faceCentres_,
2245 p,
2246 checkFaces,
2247 baffles,
2248 setPtr
2249 );
2250}
2251
2252
2253//bool Foam::polyMeshGeometry::checkFaceSkewness
2254//(
2255// const bool report,
2256// const scalar internalSkew,
2257// const scalar boundarySkew,
2258// const labelList& checkFaces,
2259// const List<labelPair>& baffles,
2260// labelHashSet* setPtr
2261//) const
2262//{
2263// return checkFaceSkewness
2264// (
2265// report,
2266// internalSkew,
2267// boundarySkew,
2268// mesh_,
2269// cellCentres_,
2270// faceCentres_,
2271// faceAreas_,
2272// checkFaces,
2273// baffles,
2274// setPtr
2275// );
2276//}
2277
2278
2280(
2281 const bool report,
2282 const scalar warnWeight,
2283 const labelList& checkFaces,
2284 const List<labelPair>& baffles,
2285 labelHashSet* setPtr
2286) const
2287{
2288 return checkFaceWeights
2289 (
2290 report,
2291 warnWeight,
2292 mesh_,
2293 cellCentres_,
2294 faceCentres_,
2295 faceAreas_,
2296 checkFaces,
2297 baffles,
2298 setPtr
2299 );
2300}
2301
2302
2304(
2305 const bool report,
2306 const scalar warnRatio,
2307 const labelList& checkFaces,
2308 const List<labelPair>& baffles,
2309 labelHashSet* setPtr
2310) const
2311{
2312 return checkVolRatio
2313 (
2314 report,
2315 warnRatio,
2316 mesh_,
2317 cellVolumes_,
2318 checkFaces,
2319 baffles,
2320 setPtr
2321 );
2322}
2323
2324
2326(
2327 const bool report,
2328 const scalar maxDeg,
2329 const pointField& p,
2330 const labelList& checkFaces,
2331 labelHashSet* setPtr
2332) const
2333{
2334 return checkFaceAngles
2335 (
2336 report,
2337 maxDeg,
2338 mesh_,
2339 faceAreas_,
2341 checkFaces,
2342 setPtr
2343 );
2344}
2345
2346
2348(
2349 const bool report,
2350 const scalar minTwist,
2351 const pointField& p,
2352 const labelList& checkFaces,
2353 labelHashSet* setPtr
2354) const
2355{
2356 return checkFaceTwist
2357 (
2358 report,
2359 minTwist,
2360 mesh_,
2361 cellCentres_,
2362 faceAreas_,
2363 faceCentres_,
2365 checkFaces,
2366 setPtr
2367 );
2368}
2369
2370
2372(
2373 const bool report,
2374 const scalar minTwist,
2375 const pointField& p,
2376 const labelList& checkFaces,
2377 labelHashSet* setPtr
2378) const
2379{
2380 return checkTriangleTwist
2381 (
2382 report,
2383 minTwist,
2384 mesh_,
2385 faceAreas_,
2386 faceCentres_,
2388 checkFaces,
2389 setPtr
2390 );
2391}
2392
2393
2395(
2396 const bool report,
2397 const scalar minFlatness,
2398 const pointField& p,
2399 const labelList& checkFaces,
2400 labelHashSet* setPtr
2401) const
2402{
2403 return checkFaceFlatness
2404 (
2405 report,
2406 minFlatness,
2407 mesh_,
2408 faceAreas_,
2409 faceCentres_,
2411 checkFaces,
2412 setPtr
2413 );
2414}
2415
2416
2418(
2419 const bool report,
2420 const scalar minArea,
2421 const labelList& checkFaces,
2422 labelHashSet* setPtr
2423) const
2424{
2425 return checkFaceArea
2426 (
2427 report,
2428 minArea,
2429 mesh_,
2430 faceAreas_,
2431 checkFaces,
2432 setPtr
2433 );
2434}
2435
2436
2438(
2439 const bool report,
2440 const scalar warnDet,
2441 const labelList& checkFaces,
2442 const labelList& affectedCells,
2443 labelHashSet* setPtr
2444) const
2445{
2446 return checkCellDeterminant
2447 (
2448 report,
2449 warnDet,
2450 mesh_,
2451 faceAreas_,
2452 checkFaces,
2454 setPtr
2455 );
2456}
2457
2458
2460(
2461 const bool report,
2462 const scalar minEdgeLength,
2463 const labelList& checkFaces,
2464 labelHashSet* setPtr
2465) const
2466{
2467 return checkEdgeLength
2468 (
2469 report,
2470 minEdgeLength,
2471 mesh_,
2472 checkFaces,
2473 setPtr
2474 );
2475}
2476
2477
2478// ************************************************************************* //
constexpr scalar pi(M_PI)
label n
bool insert(const Key &key)
Insert a new entry, not overwriting existing entries.
Definition HashSet.H:229
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition List.H:72
A List with indirect addressing. Like IndirectList but does not store addressing.
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition UList.H:89
void size(const label n)
Older name for setAddressableSize.
Definition UList.H:118
label fcIndex(const label i) const noexcept
The forward circular index. The next index in the list which returns to the first at the end of the l...
Definition UListI.H:99
static const Form zero
A bounding box defined in terms of min/max extrema points.
Definition boundBox.H:71
A cell is defined as a list of faces with extra functionality.
Definition cell.H:56
A face is a list of labels corresponding to mesh vertices.
Definition face.H:71
A polyBoundaryMesh is a polyPatch list with registered IO, a reference to the associated polyMesh,...
label whichPatch(const label meshFacei) const
Return patch index for a given mesh face index. Uses binary search.
Updateable mesh geometry and checking routines.
static bool checkFacePyramids(const bool report, const scalar minPyrVol, const polyMesh &, const vectorField &cellCentres, const pointField &p, const labelList &checkFaces, const List< labelPair > &baffles, labelHashSet *)
See primitiveMesh.
const scalarField & cellVolumes() const
static bool checkTriangleTwist(const bool report, const scalar minTwist, const polyMesh &, const vectorField &faceAreas, const vectorField &faceCentres, const pointField &p, const labelList &checkFaces, labelHashSet *setPtr)
Consecutive triangle (from face-centre decomposition) normals.
static bool checkFaceTwist(const bool report, const scalar minTwist, const polyMesh &, const vectorField &cellCentres, const vectorField &faceAreas, const vectorField &faceCentres, const pointField &p, const labelList &checkFaces, labelHashSet *setPtr)
Triangle (from face-centre decomposition) normal v.s.
polyMeshGeometry(const polyMesh &)
Construct from mesh.
static bool checkFaceSkewness(const bool report, const scalar internalSkew, const scalar boundarySkew, const polyMesh &mesh, const pointField &points, const vectorField &cellCentres, const vectorField &faceCentres, const vectorField &faceAreas, const labelList &checkFaces, const List< labelPair > &baffles, labelHashSet *setPtr)
See primitiveMesh.
const vectorField & faceAreas() const
const polyMesh & mesh() const
void correct()
Take over properties from mesh.
static bool checkFaceArea(const bool report, const scalar minArea, const polyMesh &, const vectorField &faceAreas, const labelList &checkFaces, labelHashSet *setPtr)
Small faces.
static bool checkFaceDotProduct(const bool report, const scalar orthWarn, const polyMesh &, const vectorField &cellCentres, const vectorField &faceAreas, const labelList &checkFaces, const List< labelPair > &baffles, labelHashSet *setPtr)
See primitiveMesh.
static bool checkFaceAngles(const bool report, const scalar maxDeg, const polyMesh &mesh, const vectorField &faceAreas, const pointField &p, const labelList &checkFaces, labelHashSet *setPtr)
See primitiveMesh.
static bool checkFaceWeights(const bool report, const scalar warnWeight, const polyMesh &mesh, const vectorField &cellCentres, const vectorField &faceCentres, const vectorField &faceAreas, const labelList &checkFaces, const List< labelPair > &baffles, labelHashSet *setPtr)
Interpolation weights (0.5 for regular mesh).
const vectorField & faceCentres() const
static bool checkVolRatio(const bool report, const scalar warnRatio, const polyMesh &mesh, const scalarField &cellVolumes, const labelList &checkFaces, const List< labelPair > &baffles, labelHashSet *setPtr)
Cell volume ratio of neighbouring cells (1 for regular mesh).
static bool checkEdgeLength(const bool report, const scalar minEdgeLength, const polyMesh &mesh, const labelList &checkFaces, labelHashSet *pointSetPtr)
Small edges. Optionally collects points of small edges.
static bool checkFaceTets(const bool report, const scalar minPyrVol, const polyMesh &, const vectorField &cellCentres, const vectorField &faceCentres, const pointField &p, const labelList &checkFaces, const List< labelPair > &baffles, labelHashSet *)
See primitiveMesh.
static bool checkFaceFlatness(const bool report, const scalar minFlatness, const polyMesh &, const vectorField &faceAreas, const vectorField &faceCentres, const pointField &p, const labelList &checkFaces, labelHashSet *setPtr)
Area of faces v.s. sum of triangle areas.
const vectorField & cellCentres() const
static bool checkCellDeterminant(const bool report, const scalar minDet, const polyMesh &, const vectorField &faceAreas, const labelList &checkFaces, const labelList &affectedCells, labelHashSet *setPtr)
Area of internal faces v.s. boundary faces.
static labelList affectedCells(const polyMesh &, const labelList &changedFaces)
Helper function: get affected cells from faces.
static label findSharedBasePoint(const polyMesh &mesh, label fI, const point &nCc, scalar tol, bool report=false)
Find the first suitable base point to use for a minimum.
static label findBasePoint(const polyMesh &mesh, label fI, scalar tol, bool report=false)
Find the base point to use for a minimum triangle.
Mesh consisting of general polyhedral cells.
Definition polyMesh.H:79
virtual const faceList & faces() const
Return raw faces.
Definition polyMesh.C:1088
virtual const labelList & faceOwner() const
Return face owner.
Definition polyMesh.C:1101
virtual const labelList & faceNeighbour() const
Return face neighbour.
Definition polyMesh.C:1107
static scalar boundaryFaceSkewness(const UList< face > &faces, const pointField &p, const vectorField &fCtrs, const vectorField &fAreas, const label facei, const point &ownCc)
Skewness of single boundary face.
static tmp< scalarField > faceSkewness(const primitiveMesh &mesh, const pointField &points, const vectorField &fCtrs, const vectorField &fAreas, const vectorField &cellCtrs)
Generate skewness field.
bool isInternalFace(const label faceIndex) const noexcept
Return true if given face label is internal to the mesh.
label nInternalFaces() const noexcept
Number of internal faces.
const cellList & cells() const
scalar mag(const UList< point > &points) const
Return scalar magnitude - returns volume of pyramid.
Definition pyramidI.H:69
static void swapBoundaryFacePositions(const polyMesh &mesh, UList< point > &positions, const bool parRun=UPstream::parRun())
Swap coupled positions. Uses eqOp.
Definition syncTools.H:545
static void swapBoundaryCellPositions(const polyMesh &mesh, const UList< point > &cellData, List< point > &neighbourCellData, const bool parRun=UPstream::parRun())
Extract and swap to obtain neighbour cell positions for all boundary faces.
Definition syncTools.C:27
static void swapBoundaryFaceList(const polyMesh &mesh, UList< T > &faceValues, const bool parRun=UPstream::parRun())
Swap coupled boundary face values. Uses eqOp.
Definition syncTools.H:524
Tensor of scalars, i.e. Tensor<scalar>.
scalar quality() const
Return quality: Ratio of tetrahedron and circum-sphere volume, scaled so that a regular tetrahedron h...
static vector areaNormal(const point &p0, const point &p1, const point &p2)
scalar mag() const
The magnitude of the triangle area.
Definition triangleI.H:309
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition className.H:142
volScalarField & p
thermo correct()
const polyBoundaryMesh & patches
bool coupled
dynamicFvMesh & mesh
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
const pointField & points
label nPoints
const cellShapeList & cells
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
#define WarningInFunction
Report a warning using Foam::Warning.
#define SeriousErrorInFunction
Report an error message using Foam::SeriousError.
const dimensionedScalar c
Speed of light in a vacuum.
Namespace for finite-area.
Definition limitHeight.C:30
Namespace for OpenFOAM.
dimensionedScalar det(const dimensionedSphericalTensor &dt)
Pair< label > labelPair
A pair of labels.
Definition Pair.H:54
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:40
dimensionedScalar asin(const dimensionedScalar &ds)
List< label > labelList
A List of labels.
Definition List.H:62
dimensionedSymmTensor sqr(const dimensionedVector &dv)
quaternion normalised(const quaternion &q)
Return the normalised (unit) quaternion of the given quaternion.
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
Definition HashSet.H:85
static Foam::doubleVector pointsAverage(const UList< point > &points, const labelUList &pointLabels)
dimensionedScalar sin(const dimensionedScalar &ds)
messageStream Info
Information stream (stdout output on master, null elsewhere).
List< face > faceList
List of faces.
Definition faceListFwd.H:41
tetrahedron< point, const point & > tetPointRef
A tetrahedron using referred points.
Definition tetrahedron.H:72
Tensor< scalar > tensor
Definition symmTensor.H:57
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
constexpr scalar degToRad() noexcept
Multiplication factor for degrees to radians conversion.
dimensionedScalar sqrt(const dimensionedScalar &ds)
List< cell > cellList
List of cell.
Definition cellListFwd.H:41
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
constexpr scalar radToDeg() noexcept
Multiplication factor for radians to degrees conversion.
Vector< double > doubleVector
Definition vector.H:54
void reduce(T &value, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce).
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:26
pyramid< point, const point &, const face & > pyramidPointFaceRef
A pyramid using referred point and face.
Definition pyramid.H:70
errorManip< error > abort(error &err)
Definition errorManip.H:139
Field< vector > vectorField
Specialisation of Field<T> for vector.
vector point
Point is a vector.
Definition point.H:37
static constexpr const zero Zero
Global zero (0).
Definition zero.H:127
triangle< point, const point & > triPointRef
A triangle using referred points.
Definition triangleFwd.H:46
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
vectorField pointField
pointField is a vectorField.
Vector< scalar > vector
Definition vector.H:57
UList< label > labelUList
A UList of labels.
Definition UList.H:75
Vector< solveScalar > solveVector
Definition vector.H:60
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
dimensionedScalar cos(const dimensionedScalar &ds)
dimensionedScalar acos(const dimensionedScalar &ds)
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
labelList f(nPoints)
labelList pointLabels(nPoints, -1)
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299
Unit conversion functions.