isoSurfaceTopo.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-2019 OpenFOAM Foundation
9  Copyright (C) 2019-2020 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
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 "isoSurfaceTopo.H"
30 #include "polyMesh.H"
31 #include "volFields.H"
32 #include "tetCell.H"
33 #include "tetMatcher.H"
34 #include "tetPointRef.H"
35 #include "DynamicField.H"
36 #include "syncTools.H"
39 
40 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
41 
42 #include "isoSurfaceBaseMethods.H"
44 
45 
46 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
47 
48 namespace Foam
49 {
51 }
52 
53 
54 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
55 
56 Foam::scalar Foam::isoSurfaceTopo::minTetQ
57 (
58  const label facei,
59  const label faceBasePtI
60 ) const
61 {
62  const scalar ownQuality =
64  (
65  mesh_,
66  mesh_.cellCentres()[mesh_.faceOwner()[facei]],
67  facei,
68  true,
69  faceBasePtI
70  );
71 
72  if (mesh_.isInternalFace(facei))
73  {
74  const scalar neiQuality =
76  (
77  mesh_,
79  facei,
80  false,
81  faceBasePtI
82  );
83 
84  if (neiQuality < ownQuality)
85  {
86  return neiQuality;
87  }
88  }
89 
90  return ownQuality;
91 }
92 
93 
94 void Foam::isoSurfaceTopo::fixTetBasePtIs()
95 {
96  // Determine points used by two faces on the same cell
97  const cellList& cells = mesh_.cells();
98  const faceList& faces = mesh_.faces();
99  const labelList& faceOwn = mesh_.faceOwner();
100  const labelList& faceNei = mesh_.faceNeighbour();
101 
102 
103  // Get face triangulation base point
104  tetBasePtIs_ = mesh_.tetBasePtIs();
105 
106 
107  // Pre-filter: mark all cells with illegal base points
108  bitSet problemCells(cells.size());
109 
110  forAll(tetBasePtIs_, facei)
111  {
112  if (tetBasePtIs_[facei] == -1)
113  {
114  problemCells.set(faceOwn[facei]);
115 
116  if (mesh_.isInternalFace(facei))
117  {
118  problemCells.set(faceNei[facei]);
119  }
120  }
121  }
122 
123 
124  // Mark all points that are shared by just two faces within an adjacent
125  // problem cell as problematic
126  bitSet problemPoints(mesh_.points().size());
127 
128  {
129  // Number of times a point occurs in a cell.
130  // Used to detect dangling vertices (count = 2)
131  Map<label> pointCount;
132 
133  // Analyse problem cells for points shared by two faces only
134  for (const label celli : problemCells)
135  {
136  pointCount.clear();
137 
138  for (const label facei : cells[celli])
139  {
140  for (const label pointi : faces[facei])
141  {
142  ++pointCount(pointi);
143  }
144  }
145 
146  forAllConstIters(pointCount, iter)
147  {
148  if (iter.val() == 1)
149  {
151  << "point:" << iter.key()
152  << " at:" << mesh_.points()[iter.key()]
153  << " only used by one face" << nl
154  << exit(FatalError);
155  }
156  else if (iter.val() == 2)
157  {
158  problemPoints.set(iter.key());
159  }
160  }
161  }
162  }
163 
164 
165  // For all faces which form a part of a problem-cell, check if the base
166  // point is adjacent to any problem points. If it is, re-calculate the base
167  // point so that it is not.
168  label nAdapted = 0;
169  forAll(tetBasePtIs_, facei)
170  {
171  if
172  (
173  problemCells.test(faceOwn[facei])
174  || (mesh_.isInternalFace(facei) && problemCells.test(faceNei[facei]))
175  )
176  {
177  const face& f = faces[facei];
178 
179  // Check if either of the points adjacent to the base point is a
180  // problem point. If not, the existing base point can be retained.
181  const label fp0 = tetBasePtIs_[facei] < 0 ? 0 : tetBasePtIs_[facei];
182 
183  if
184  (
185  !problemPoints.test(f.rcValue(fp0))
186  && !problemPoints.test(f.fcValue(fp0))
187  )
188  {
189  continue;
190  }
191 
192  // A new base point is required. Pick the point that results in the
193  // least-worst tet and which is not adjacent to any problem points.
194  scalar maxQ = -GREAT;
195  label maxFp = -1;
196  forAll(f, fp)
197  {
198  if
199  (
200  !problemPoints.test(f.rcValue(fp))
201  && !problemPoints.test(f.fcValue(fp))
202  )
203  {
204  const scalar q = minTetQ(facei, fp);
205  if (q > maxQ)
206  {
207  maxQ = q;
208  maxFp = fp;
209  }
210  }
211  }
212 
213  if (maxFp != -1)
214  {
215  // Success! Set the new base point
216  tetBasePtIs_[facei] = maxFp;
217  }
218  else
219  {
220  // No point was found on face that would not result in some
221  // duplicate triangle. Do what? Continue and hope? Spit an
222  // error? Silently or noisily reduce the filtering level?
223 
224  tetBasePtIs_[facei] = 0;
225  }
226 
227  ++nAdapted;
228  }
229  }
230 
231 
232  if (debug)
233  {
234  Pout<< "isoSurface : adapted starting point of triangulation on "
235  << nAdapted << " faces." << endl;
236  }
237 
238  syncTools::syncFaceList(mesh_, tetBasePtIs_, maxEqOp<label>());
239 }
240 
241 
242 Foam::label Foam::isoSurfaceTopo::generatePoint
243 (
244  const label facei,
245  const bool edgeIsDiag,
246  const edge& vertices,
247 
248  DynamicList<edge>& pointToVerts,
249  DynamicList<label>& pointToFace,
250  DynamicList<bool>& pointFromDiag,
251  EdgeMap<label>& vertsToPoint
252 ) const
253 {
254  // Generate new point, unless it already exists for edge
255 
256  label pointi = vertsToPoint.lookup(vertices, -1);
257  if (pointi == -1)
258  {
259  pointi = pointToVerts.size();
260 
261  pointToVerts.append(vertices);
262  pointToFace.append(facei);
263  pointFromDiag.append(edgeIsDiag);
264  vertsToPoint.insert(vertices, pointi);
265  }
266 
267  return pointi;
268 }
269 
270 
271 void Foam::isoSurfaceTopo::generateTriPoints
272 (
273  const label facei,
274  const int tetCutIndex,
275  const tetCell& tetLabels,
276  const FixedList<bool, 6>& edgeIsDiag,// Per tet edge whether is face diag
277 
278  DynamicList<edge>& pointToVerts,
279  DynamicList<label>& pointToFace,
280  DynamicList<bool>& pointFromDiag,
281 
282  EdgeMap<label>& vertsToPoint,
283  DynamicList<label>& verts // Every three verts is new triangle
284 ) const
285 {
286  // Form the vertices of the triangles for each case
287  switch (tetCutIndex)
288  {
289  case 0x00:
290  case 0x0F:
291  break;
292 
293  case 0x01:
294  case 0x0E:
295  {
296  verts.append
297  (
298  generatePoint
299  (
300  facei,
301  edgeIsDiag[0],
302  tetLabels.edge(0),
303  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
304  )
305  );
306  verts.append
307  (
308  generatePoint
309  (
310  facei,
311  edgeIsDiag[1],
312  tetLabels.edge(1),
313  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
314  )
315  );
316  verts.append
317  (
318  generatePoint
319  (
320  facei,
321  edgeIsDiag[2],
322  tetLabels.edge(2),
323  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
324  )
325  );
326 
327  if (tetCutIndex == 0x0E)
328  {
329  // Flip normals
330  const label sz = verts.size();
331  Swap(verts[sz-2], verts[sz-1]);
332  }
333  }
334  break;
335 
336  case 0x02:
337  case 0x0D:
338  {
339  verts.append
340  (
341  generatePoint
342  (
343  facei,
344  edgeIsDiag[0],
345  tetLabels.reverseEdge(0),
346  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
347  )
348  );
349  verts.append
350  (
351  generatePoint
352  (
353  facei,
354  edgeIsDiag[3],
355  tetLabels.reverseEdge(3),
356  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
357  )
358  );
359  verts.append
360  (
361  generatePoint
362  (
363  facei,
364  edgeIsDiag[4],
365  tetLabels.edge(4),
366  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
367  )
368  );
369 
370  if (tetCutIndex == 0x0D)
371  {
372  // Flip normals
373  const label sz = verts.size();
374  Swap(verts[sz-2], verts[sz-1]);
375  }
376  }
377  break;
378 
379  case 0x03:
380  case 0x0C:
381  {
382  const label p0p2
383  (
384  generatePoint
385  (
386  facei,
387  edgeIsDiag[1],
388  tetLabels.edge(1),
389  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
390  )
391  );
392  const label p1p3
393  (
394  generatePoint
395  (
396  facei,
397  edgeIsDiag[3],
398  tetLabels.reverseEdge(3),
399  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
400  )
401  );
402 
403  verts.append
404  (
405  generatePoint
406  (
407  facei,
408  edgeIsDiag[2],
409  tetLabels.edge(2),
410  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
411  )
412  );
413  verts.append(p1p3);
414  verts.append(p0p2);
415  verts.append(p1p3);
416  verts.append
417  (
418  generatePoint
419  (
420  facei,
421  edgeIsDiag[4],
422  tetLabels.edge(4),
423  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
424  )
425  );
426  verts.append(p0p2);
427 
428  if (tetCutIndex == 0x0C)
429  {
430  // Flip normals
431  const label sz = verts.size();
432  Swap(verts[sz-5], verts[sz-4]);
433  Swap(verts[sz-2], verts[sz-1]);
434  }
435  }
436  break;
437 
438  case 0x04:
439  case 0x0B:
440  {
441  verts.append
442  (
443  generatePoint
444  (
445  facei,
446  edgeIsDiag[1],
447  tetLabels.reverseEdge(1),
448  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
449  )
450  );
451  verts.append
452  (
453  generatePoint
454  (
455  facei,
456  edgeIsDiag[4],
457  tetLabels.reverseEdge(4),
458  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
459  )
460  );
461  verts.append
462  (
463  generatePoint
464  (
465  facei,
466  edgeIsDiag[5],
467  tetLabels.reverseEdge(5),
468  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
469  )
470  );
471 
472  if (tetCutIndex == 0x0B)
473  {
474  // Flip normals
475  const label sz = verts.size();
476  Swap(verts[sz-2], verts[sz-1]);
477  }
478  }
479  break;
480 
481  case 0x05:
482  case 0x0A:
483  {
484  const label p0p1
485  (
486  generatePoint
487  (
488  facei,
489  edgeIsDiag[0],
490  tetLabels.edge(0),
491  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
492  )
493  );
494  const label p2p3
495  (
496  generatePoint
497  (
498  facei,
499  edgeIsDiag[5],
500  tetLabels.reverseEdge(5),
501  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
502  )
503  );
504 
505  verts.append(p0p1);
506  verts.append(p2p3);
507  verts.append
508  (
509  generatePoint
510  (
511  facei,
512  edgeIsDiag[2],
513  tetLabels.edge(2),
514  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
515  )
516  );
517  verts.append(p0p1);
518  verts.append
519  (
520  generatePoint
521  (
522  facei,
523  edgeIsDiag[4],
524  tetLabels.edge(4),
525  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
526  )
527  );
528  verts.append(p2p3);
529 
530  if (tetCutIndex == 0x0A)
531  {
532  // Flip normals
533  const label sz = verts.size();
534  Swap(verts[sz-5], verts[sz-4]);
535  Swap(verts[sz-2], verts[sz-1]);
536  }
537  }
538  break;
539 
540  case 0x06:
541  case 0x09:
542  {
543  const label p0p1
544  (
545  generatePoint
546  (
547  facei,
548  edgeIsDiag[0],
549  tetLabels.edge(0),
550  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
551  )
552  );
553  const label p2p3
554  (
555  generatePoint
556  (
557  facei,
558  edgeIsDiag[5],
559  tetLabels.reverseEdge(5),
560  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
561  )
562  );
563 
564  verts.append(p0p1);
565  verts.append
566  (
567  generatePoint
568  (
569  facei,
570  edgeIsDiag[3],
571  tetLabels.reverseEdge(3),
572  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
573  )
574  );
575  verts.append(p2p3);
576  verts.append(p0p1);
577  verts.append(p2p3);
578  verts.append
579  (
580  generatePoint
581  (
582  facei,
583  edgeIsDiag[1],
584  tetLabels.edge(1),
585  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
586  )
587  );
588 
589  if (tetCutIndex == 0x09)
590  {
591  // Flip normals
592  const label sz = verts.size();
593  Swap(verts[sz-5], verts[sz-4]);
594  Swap(verts[sz-2], verts[sz-1]);
595  }
596  }
597  break;
598 
599  case 0x08:
600  case 0x07:
601  {
602  verts.append
603  (
604  generatePoint
605  (
606  facei,
607  edgeIsDiag[2],
608  tetLabels.reverseEdge(2),
609  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
610  )
611  );
612  verts.append
613  (
614  generatePoint
615  (
616  facei,
617  edgeIsDiag[5],
618  tetLabels.reverseEdge(5),
619  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
620  )
621  );
622  verts.append
623  (
624  generatePoint
625  (
626  facei,
627  edgeIsDiag[3],
628  tetLabels.edge(3),
629  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
630  )
631  );
632  if (tetCutIndex == 0x07)
633  {
634  // Flip normals
635  const label sz = verts.size();
636  Swap(verts[sz-2], verts[sz-1]);
637  }
638  }
639  break;
640  }
641 }
642 
643 
644 void Foam::isoSurfaceTopo::generateTriPoints
645 (
646  const label celli,
647  const bool isTet,
648 
649  DynamicList<edge>& pointToVerts,
650  DynamicList<label>& pointToFace,
651  DynamicList<bool>& pointFromDiag,
652 
653  EdgeMap<label>& vertsToPoint,
654  DynamicList<label>& verts,
655  DynamicList<label>& faceLabels
656 ) const
657 {
658  const faceList& faces = mesh_.faces();
659  const labelList& faceOwner = mesh_.faceOwner();
660  const cell& cFaces = mesh_.cells()[celli];
661 
662  if (isTet)
663  {
664  // For tets don't do cell-centre decomposition, just use the
665  // tet points and values
666 
667  const label startTrii = verts.size();
668 
669  const label facei = cFaces[0];
670  const face& f0 = faces[facei];
671 
672  // Get the other point from f1. Tbd: check if not duplicate face
673  // (ACMI / ignoreBoundaryFaces_).
674  const face& f1 = faces[cFaces[1]];
675  label oppositeI = -1;
676  forAll(f1, fp)
677  {
678  oppositeI = f1[fp];
679  if (!f0.found(oppositeI))
680  {
681  break;
682  }
683  }
684 
685  label p0 = f0[0];
686  label p1 = f0[1];
687  label p2 = f0[2];
688 
689  if (faceOwner[facei] == celli)
690  {
691  Swap(p1, p2);
692  }
693 
694  generateTriPoints
695  (
696  facei,
697  getTetCutIndex
698  (
699  pVals_[p0],
700  pVals_[p1],
701  pVals_[p2],
702  pVals_[oppositeI],
703  iso_
704  ),
705  tetCell(p0, p1, p2, oppositeI),
706  FixedList<bool, 6>(false), // edgeIsDiag = false
707 
708  pointToVerts,
709  pointToFace,
710  pointFromDiag,
711  vertsToPoint,
712  verts // Every three verts is new triangle
713  );
714 
715  for (label nTris = (verts.size()-startTrii)/3; nTris; --nTris)
716  {
717  faceLabels.append(facei);
718  }
719  }
720  else
721  {
722  for (const label facei : cFaces)
723  {
724  if
725  (
726  !mesh_.isInternalFace(facei)
727  && ignoreBoundaryFaces_.test(facei-mesh_.nInternalFaces())
728  )
729  {
730  continue;
731  }
732 
733  const label startTrii = verts.size();
734 
735  const face& f = faces[facei];
736 
737  label fp0 = tetBasePtIs_[facei];
738 
739  // Fallback
740  if (fp0 < 0)
741  {
742  fp0 = 0;
743  }
744 
745  label fp = f.fcIndex(fp0);
746  for (label i = 2; i < f.size(); ++i)
747  {
748  const label nextFp = f.fcIndex(fp);
749 
750  FixedList<bool, 6> edgeIsDiag(false);
751 
752  label p0 = f[fp0];
753  label p1 = f[fp];
754  label p2 = f[nextFp];
755  if (faceOwner[facei] == celli)
756  {
757  Swap(p1, p2);
758  if (i != 2) edgeIsDiag[1] = true;
759  if (i != f.size()-1) edgeIsDiag[0] = true;
760  }
761  else
762  {
763  if (i != 2) edgeIsDiag[0] = true;
764  if (i != f.size()-1) edgeIsDiag[1] = true;
765  }
766 
767  generateTriPoints
768  (
769  facei,
770  getTetCutIndex
771  (
772  pVals_[p0],
773  pVals_[p1],
774  pVals_[p2],
775  cVals_[celli],
776  iso_
777  ),
778  tetCell(p0, p1, p2, mesh_.nPoints()+celli),
779  edgeIsDiag,
780 
781  pointToVerts,
782  pointToFace,
783  pointFromDiag,
784  vertsToPoint,
785  verts // Every three verts is new triangle
786  );
787 
788  fp = nextFp;
789  }
790 
791  for (label nTris = (verts.size()-startTrii)/3; nTris; --nTris)
792  {
793  faceLabels.append(facei);
794  }
795  }
796  }
797 }
798 
799 
800 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
801 
802 void Foam::isoSurfaceTopo::triangulateOutside
803 (
804  const bool filterDiag,
805  const primitivePatch& pp,
806  const boolUList& pointFromDiag,
807  const labelUList& pointToFace,
808  const label cellID,
809 
810  // outputs
811  DynamicList<face>& compactFaces,
812  DynamicList<label>& compactCellIDs
813 )
814 {
815  // We can form pockets:
816  // - 1. triangle on face
817  // - 2. multiple triangles on interior (from diag edges)
818  // - the edge loop will be pocket since it is only the diag
819  // edges that give it volume?
820 
821  // Retriangulate the exterior loops
822  const labelListList& edgeLoops = pp.edgeLoops();
823  const labelList& mp = pp.meshPoints();
824 
825  for (const labelList& loop : edgeLoops)
826  {
827  if (loop.size() > 2)
828  {
829  compactFaces.append(face(loop.size()));
830  face& f = compactFaces.last();
831 
832  label fpi = 0;
833  forAll(f, i)
834  {
835  const label pointi = mp[loop[i]];
836  if (filterDiag && pointFromDiag[pointi])
837  {
838  const label prevPointi = mp[loop[loop.fcIndex(i)]];
839  if
840  (
841  pointFromDiag[prevPointi]
842  && (pointToFace[pointi] != pointToFace[prevPointi])
843  )
844  {
845  f[fpi++] = pointi;
846  }
847  else
848  {
849  // Filter out diagonal point
850  }
851  }
852  else
853  {
854  f[fpi++] = pointi;
855  }
856  }
857 
858  if (fpi > 2)
859  {
860  f.resize(fpi);
861  }
862  else
863  {
864  // Keep original face
865  forAll(f, i)
866  {
867  const label pointi = mp[loop[i]];
868  f[i] = pointi;
869  }
870  }
871  compactCellIDs.append(cellID);
872  }
873  }
874 }
875 
876 
877 void Foam::isoSurfaceTopo::removeInsidePoints
878 (
879  Mesh& s,
880  const bool filterDiag,
881 
882  // inputs
883  const boolUList& pointFromDiag,
884  const labelUList& pointToFace,
885  const labelUList& start, // Per cell the starting triangle
886 
887  // outputs
888  DynamicList<label>& pointCompactMap, // Per returned point the original
889  DynamicList<label>& compactCellIDs // Per returned tri the cellID
890 )
891 {
892  pointCompactMap.clear();
893  compactCellIDs.clear();
894 
895  const pointField& points = s.points();
896 
897  DynamicList<face> compactFaces(s.size()/8);
898 
899  for (label celli = 0; celli < start.size()-1; ++celli)
900  {
901  // All triangles for the current cell
902 
903  const label nTris = start[celli+1]-start[celli];
904 
905  if (nTris)
906  {
907  const primitivePatch pp
908  (
909  SubList<face>(s, nTris, start[celli]),
910  points
911  );
912 
913  triangulateOutside
914  (
915  filterDiag,
916  pp,
917  pointFromDiag,
918  pointToFace,
919  //protectedFace,
920  celli,
921 
922  compactFaces,
923  compactCellIDs
924  );
925  }
926  }
927 
928 
929  // Compact out unused points
930  labelList oldToCompact(points.size(), -1);
931  pointCompactMap.clear(); // Extra safety (paranoid)
932 
933  for (face& f : compactFaces)
934  {
935  forAll(f, fp)
936  {
937  label pointi = f[fp];
938  label compacti = oldToCompact[pointi];
939  if (compacti == -1)
940  {
941  compacti = pointCompactMap.size();
942  oldToCompact[pointi] = compacti;
943  pointCompactMap.append(pointi);
944  }
945  f[fp] = compacti;
946  }
947  }
948 
949  pointField compactPoints
950  (
951  UIndirectList<point>(s.points(), pointCompactMap)
952  );
953 
954  surfZoneList newZones(s.surfZones());
955 
956  s.clear();
957  Mesh updated
958  (
959  std::move(compactPoints),
960  std::move(compactFaces),
961  s.surfZones()
962  );
963 
964  s.transfer(updated);
965 }
966 
967 
968 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
969 
971 (
972  const polyMesh& mesh,
973  const scalarField& cellValues,
974  const scalarField& pointValues,
975  const scalar iso,
976  const isoSurfaceParams& params,
977  const bitSet& ignoreCells
978 )
979 :
980  isoSurfaceBase(mesh, cellValues, pointValues, iso, params),
981  cellCutType_(mesh.nCells(), cutType::UNVISITED)
982 {
983  if (debug)
984  {
985  Pout<< "isoSurfaceTopo:" << nl
986  << " cell min/max : " << minMax(cVals_) << nl
987  << " point min/max : " << minMax(pVals_) << nl
988  << " isoValue : " << iso << nl
989  << " filter : "
990  << isoSurfaceParams::filterNames[params.filter()] << nl
991  << " mesh span : " << mesh.bounds().mag() << nl
992  << " ignoreCells : " << ignoreCells.count()
993  << " / " << cVals_.size() << nl
994  << endl;
995  }
996 
997  this->ignoreCyclics();
998 
999  label nBlockedCells = 0;
1000 
1001  // Mark ignoreCells as blocked
1002  nBlockedCells += blockCells(cellCutType_, ignoreCells);
1003 
1004  // Mark cells outside bounding box as blocked
1005  nBlockedCells +=
1006  blockCells(cellCutType_, params.getClipBounds(), volumeType::OUTSIDE);
1007 
1008 
1009  fixTetBasePtIs();
1010 
1011  // Determine cell cuts
1012  const label nCutCells = calcCellCuts(cellCutType_);
1013 
1014  if (debug)
1015  {
1016  Pout<< "isoSurfaceTopo : candidate cells cut "
1017  << nCutCells
1018  << " blocked " << nBlockedCells
1019  << " total " << mesh_.nCells() << endl;
1020  }
1021 
1022  if (debug && isA<fvMesh>(mesh))
1023  {
1024  const fvMesh& fvmesh = dynamicCast<const fvMesh&>(mesh);
1025 
1026  volScalarField debugField
1027  (
1028  IOobject
1029  (
1030  "isoSurfaceTopo.cutType",
1031  fvmesh.time().timeName(),
1032  fvmesh.time(),
1035  false
1036  ),
1037  fvmesh,
1039  );
1040 
1041  auto& debugFld = debugField.primitiveFieldRef();
1042 
1043  forAll(cellCutType_, celli)
1044  {
1045  debugFld[celli] = cellCutType_[celli];
1046  }
1047 
1048  Pout<< "Writing cut types:"
1049  << debugField.objectPath() << endl;
1050 
1051  debugField.write();
1052  }
1053 
1054 
1055  // Per cell: 5 pyramids cut, each generating 2 triangles
1056  // - pointToVerts : from generated iso point to originating mesh verts
1057  DynamicList<edge> pointToVerts(10*nCutCells);
1058  // - pointToFace : from generated iso point to originating mesh face
1059  DynamicList<label> pointToFace(10*nCutCells);
1060  // - pointFromDiag : if generated iso point is on face diagonal
1061  DynamicList<bool> pointFromDiag(10*nCutCells);
1062 
1063  // Per cell: number of intersected edges:
1064  // - four faces cut so 4 mesh edges + 4 face-diagonal edges
1065  // - 4 of the pyramid edges
1066  EdgeMap<label> vertsToPoint(12*nCutCells);
1067  DynamicList<label> verts(12*nCutCells);
1068  // Per cell: 5 pyramids cut (since only one pyramid not cut)
1069  DynamicList<label> faceLabels(5*nCutCells);
1070  DynamicList<label> cellLabels(5*nCutCells);
1071 
1072 
1073  labelList startTri(mesh_.nCells()+1, Zero);
1074 
1075  for (label celli = 0; celli < mesh_.nCells(); ++celli)
1076  {
1077  startTri[celli] = faceLabels.size();
1078  if ((cellCutType_[celli] & cutType::ANYCUT) != 0)
1079  {
1080  generateTriPoints
1081  (
1082  celli,
1083 
1084  // Same as tetMatcher::test(mesh_, celli),
1085  bool(cellCutType_[celli] & cutType::TETCUT), // isTet
1086 
1087  pointToVerts,
1088  pointToFace,
1089  pointFromDiag,
1090 
1091  vertsToPoint,
1092  verts,
1093  faceLabels
1094  );
1095 
1096  for (label i = startTri[celli]; i < faceLabels.size(); ++i)
1097  {
1098  cellLabels.append(celli);
1099  }
1100  }
1101  }
1102  startTri[mesh_.nCells()] = faceLabels.size();
1103 
1104 
1105  pointToVerts_.transfer(pointToVerts);
1106  meshCells_.transfer(cellLabels);
1107  pointToFace_.transfer(pointToFace);
1108 
1110  (
1111  this->interpolateTemplate
1112  (
1113  mesh_.cellCentres(),
1114  mesh_.points()
1115  )
1116  );
1117 
1118 
1119  // Assign to MeshedSurface
1120  faceList allTris(faceLabels.size());
1121  label verti = 0;
1122  for (face& allTri : allTris)
1123  {
1124  allTri.resize(3);
1125  allTri[0] = verts[verti++];
1126  allTri[1] = verts[verti++];
1127  allTri[2] = verts[verti++];
1128  }
1129 
1130 
1131  surfZoneList allZones(one{}, surfZone("allFaces", allTris.size()));
1132 
1133  Mesh::clear();
1134  Mesh updated
1135  (
1136  std::move(allPoints),
1137  std::move(allTris),
1138  std::move(allZones)
1139  );
1140  Mesh::transfer(updated);
1141 
1142  // Now:
1143  // - generated faces and points are assigned to *this
1144  // - per point we know:
1145  // - pointOnDiag: whether it is on a face-diagonal edge
1146  // - pointToFace_: from what pyramid (cell+face) it was produced
1147  // (note that the pyramid faces are shared between multiple mesh faces)
1148  // - pointToVerts_ : originating mesh vertex or cell centre
1149 
1150 
1151  if (debug)
1152  {
1153  Pout<< "isoSurfaceTopo : generated "
1154  << Mesh::size() << " faces "
1155  << Mesh::points().size() << " points" << endl;
1156  }
1157 
1158 
1159  if (params.filter() != filterType::NONE)
1160  {
1161  // Triangulate outside (filter edges to cell centres and optionally
1162  // face diagonals)
1163  DynamicList<label> pointCompactMap(size()); // Back to original point
1164  DynamicList<label> compactCellIDs(size()); // Per tri the cell
1165 
1166  removeInsidePoints
1167  (
1168  *this,
1169  (params.filter() == filterType::DIAGCELL ? true : false),
1170 
1171  pointFromDiag,
1172  pointToFace_,
1173  startTri,
1174  pointCompactMap,
1175  compactCellIDs
1176  );
1177 
1178  pointToVerts_ = UIndirectList<edge>(pointToVerts_, pointCompactMap)();
1179  pointToFace_ = UIndirectList<label>(pointToFace_, pointCompactMap)();
1180  meshCells_.transfer(compactCellIDs);
1181 
1182  pointCompactMap.clearStorage();
1183  compactCellIDs.clearStorage();
1184 
1185  if (debug)
1186  {
1187  Pout<< "isoSurfaceTopo :"
1188  " after removing cell centre and face-diag triangles "
1189  << Mesh::size() << " faces "
1190  << Mesh::points().size() << " points"
1191  << endl;
1192  }
1193  }
1194 
1195  // Not required after this stage
1196  pointFromDiag.clearStorage();
1197 
1198 
1199  if (params.filter() == filterType::DIAGCELL)
1200  {
1201  // We remove verts on face diagonals. This is in fact just
1202  // straightening the edges of the face through the cell. This can
1203  // close off 'pockets' of triangles and create open or
1204  // multiply-connected triangles
1205 
1206  // Solved by eroding open-edges
1207  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1208 
1209  // Mark points on mesh outside.
1210  bitSet isBoundaryPoint(mesh.nPoints());
1211  for
1212  (
1213  label facei = mesh.nInternalFaces();
1214  facei < mesh.nFaces();
1215  ++facei
1216  )
1217  {
1218  isBoundaryPoint.set(mesh.faces()[facei]);
1219  }
1220 
1221 
1222  // Include faces that would be exposed from mesh subset
1223  if (nBlockedCells)
1224  {
1225  const labelList& faceOwn = mesh_.faceOwner();
1226  const labelList& faceNei = mesh_.faceNeighbour();
1227 
1228  for (label facei = 0; facei < mesh.nInternalFaces(); ++facei)
1229  {
1230  // If only one cell is blocked, the face corresponds
1231  // to an exposed subMesh face
1232  if
1233  (
1234  (cellCutType_[faceOwn[facei]] == cutType::BLOCKED)
1235  != (cellCutType_[faceNei[facei]] == cutType::BLOCKED)
1236  )
1237  {
1238  isBoundaryPoint.set(mesh.faces()[facei]);
1239  }
1240  }
1241  }
1242 
1243 
1244  // The list of surface faces that should be retained after erosion
1245  Mesh& surf = *this;
1246  labelList faceAddr(identity(surf.size()));
1247 
1249 
1250  while (true)
1251  {
1252  // Shadow the surface for the purposes of erosion
1253  uindirectPrimitivePatch erosion
1254  (
1255  UIndirectList<face>(surf, faceAddr),
1256  surf.points()
1257  );
1258 
1259  faceSelection.clear();
1260  faceSelection.resize(erosion.size());
1261 
1262  const labelList& mp = erosion.meshPoints();
1263  const edgeList& surfEdges = erosion.edges();
1264  const labelListList& edgeFaces = erosion.edgeFaces();
1265 
1266  label nEdgeRemove = 0;
1267 
1268  forAll(edgeFaces, edgei)
1269  {
1270  const labelList& eFaces = edgeFaces[edgei];
1271  if (eFaces.size() == 1)
1272  {
1273  // Open edge. Check that vertices do not originate
1274  // from a boundary face
1275  const edge& e = surfEdges[edgei];
1276 
1277  const edge& verts0 = pointToVerts_[mp[e.first()]];
1278  const edge& verts1 = pointToVerts_[mp[e.second()]];
1279 
1280  if
1281  (
1282  isBoundaryPoint.test(verts0.first())
1283  && isBoundaryPoint.test(verts0.second())
1284  && isBoundaryPoint.test(verts1.first())
1285  && isBoundaryPoint.test(verts1.second())
1286  )
1287  {
1288  // Open edge on boundary face. Keep
1289  }
1290  else
1291  {
1292  // Open edge. Mark for erosion
1293  faceSelection.set(eFaces[0]);
1294  ++nEdgeRemove;
1295  }
1296  }
1297  }
1298 
1299  if (debug)
1300  {
1301  Pout<< "isoSurfaceTopo :"
1302  << " removing " << faceSelection.count()
1303  << " / " << faceSelection.size()
1304  << " faces on " << nEdgeRemove << " open edges" << endl;
1305  }
1306 
1307  if (returnReduce(faceSelection.none(), andOp<bool>()))
1308  {
1309  break;
1310  }
1311 
1312  // Remove the faces from the addressing
1313  inplaceSubset(faceSelection, faceAddr, true); // True = remove
1314  }
1315 
1316 
1317  // Finished erosion (if any)
1318  // - retain the faces listed in the updated addressing
1319 
1320  if (surf.size() != faceAddr.size())
1321  {
1322  faceSelection.clear();
1323  faceSelection.resize(surf.size());
1324  faceSelection.set(faceAddr);
1325 
1326  inplaceSubsetMesh(faceSelection);
1327  }
1328  }
1329 }
1330 
1331 
1332 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1333 
1335 {
1336  labelList pointMap;
1338  Mesh filtered
1339  (
1340  Mesh::subsetMesh(include, pointMap, faceMap)
1341  );
1342  Mesh::transfer(filtered);
1343 
1344  meshCells_ = UIndirectList<label>(meshCells_, faceMap)();
1345 
1346  pointToVerts_ = UIndirectList<edge>(pointToVerts_, pointMap)();
1347  pointToFace_ = UIndirectList<label>(pointToFace_, pointMap)();
1348 }
1349 
1350 
1351 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
Foam::Pair::second
const T & second() const noexcept
Return second element, which is also the last element.
Definition: PairI.H:122
Foam::IOobject::NO_WRITE
Definition: IOobject.H:130
Foam::labelList
List< label > labelList
A List of labels.
Definition: List.H:71
volFields.H
Foam::pointField
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:44
Foam::IOobject
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:104
Foam::constant::atomic::mp
const dimensionedScalar mp
Proton mass.
Foam::boundBox::mag
scalar mag() const
The magnitude of the bounding box span.
Definition: boundBoxI.H:133
Foam::dimless
const dimensionSet dimless(0, 0, 0, 0, 0, 0, 0)
Dimensionless.
Definition: dimensionSets.H:50
Foam::faceMap
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
Definition: blockMeshMergeTopological.C:94
Foam::isoSurfaceBase
Low-level components common to various iso-surface algorithms.
Definition: isoSurfaceBase.H:66
isoSurfaceTopo.H
Foam::returnReduce
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
Definition: PstreamReduceOps.H:94
s
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))
Definition: gmvOutputSpray.H:25
Foam::bitSet
A bitSet stores bits (elements with only two states) in packed internal format and supports a variety...
Definition: bitSet.H:63
uindirectPrimitivePatch.H
Foam::Zero
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
Foam::DynamicList
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:55
Foam::primitiveMesh::nInternalFaces
label nInternalFaces() const
Number of internal faces.
Definition: primitiveMeshI.H:78
Foam::primitiveMesh::nFaces
label nFaces() const
Number of mesh faces.
Definition: primitiveMeshI.H:90
Foam::isoSurfaceTopo::inplaceSubsetMesh
void inplaceSubsetMesh(const bitSet &include)
Subset the surface using the selected faces.
Definition: isoSurfaceTopo.C:1334
Foam::Time::timeName
static word timeName(const scalar t, const int precision=precision_)
Definition: Time.C:780
Foam::edge
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:63
Foam::bitSet::set
void set(const bitSet &bitset)
Set specified bits from another bitset.
Definition: bitSetI.H:574
Foam::one
A class representing the concept of 1 (one) that can be used to avoid manipulating objects known to b...
Definition: one.H:61
tetPointRef.H
Foam::List::append
void append(const T &val)
Append an element at the end of the list.
Definition: ListI.H:182
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:350
defineIsoSurfaceInterpolateMethods
#define defineIsoSurfaceInterpolateMethods(ThisClass)
Definition: isoSurfaceBaseMethods.H:54
Foam::Pout
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
polyMesh.H
Foam::Swap
void Swap(DynamicList< T, SizeMin1 > &a, DynamicList< T, SizeMin2 > &b)
Definition: DynamicListI.H:913
syncTools.H
Foam::bitSet::count
unsigned int count(const bool on=true) const
Count number of bits set.
Definition: bitSetI.H:499
Foam::polyMesh
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:77
polyMeshTetDecomposition.H
Foam::faceSelection
Face selection method for createBaffles.
Definition: faceSelection.H:58
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
isoSurfaceBaseMethods.H
Convenience macros for instantiating iso-surface interpolate methods.
Foam::isoSurfaceParams::filter
filterType filter() const noexcept
Get current filter type.
Definition: isoSurfaceParams.H:202
Foam::syncTools::syncFaceList
static void syncFaceList(const polyMesh &mesh, UList< T > &faceValues, const CombineOp &cop)
Synchronize values on all mesh faces.
Definition: syncTools.H:390
Foam::isoSurfaceBase::mesh_
const polyMesh & mesh_
Reference to mesh.
Definition: isoSurfaceBase.H:102
Foam::primitiveMesh::nCells
label nCells() const
Number of mesh cells.
Definition: primitiveMeshI.H:96
Foam::Field< scalar >
Foam::isoSurfaceParams::getClipBounds
const boundBox & getClipBounds() const noexcept
Get optional clipping bounding box.
Definition: isoSurfaceParams.H:226
Foam::DynamicList::append
DynamicList< T, SizeMin > & append(const T &val)
Append an element to the end of this list.
Definition: DynamicListI.H:474
Foam::polyMesh::faceOwner
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1107
Foam::cellList
List< cell > cellList
A List of cells.
Definition: cellListFwd.H:47
Foam::andOp
Definition: ops.H:233
Foam::dimensionedScalar
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
Definition: dimensionedScalarFwd.H:43
Foam::List::resize
void resize(const label newSize)
Adjust allocated size of list.
Definition: ListI.H:139
Foam::FatalError
error FatalError
Foam::isoSurfaceParams
Preferences for controlling iso-surface algorithms.
Definition: isoSurfaceParams.H:91
Foam::vertices
pointField vertices(const blockVertexList &bvl)
Definition: blockVertexList.H:49
mesh
dynamicFvMesh & mesh
Definition: createDynamicFvMesh.H:6
Foam::Ostream::write
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
Foam::fvMesh
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:83
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::inplaceSubset
void inplaceSubset(const BoolListType &select, ListType &input, const bool invert=false)
Inplace extract elements of the input list when select is true.
Definition: ListOpsTemplates.C:590
BLOCKED
static constexpr Foam::label BLOCKED
Definition: regionSplit.C:44
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Foam::GeometricField::primitiveFieldRef
Internal::FieldType & primitiveFieldRef(const bool updateAccessTime=true)
Return a reference to the internal field.
Definition: GeometricField.C:766
Foam::polyMesh::bounds
const boundBox & bounds() const
Return mesh bounding box.
Definition: polyMesh.H:450
Foam::labelListList
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:56
Foam::EdgeMap< label >
Foam::primitiveMesh::cellCentres
const vectorField & cellCentres() const
Definition: primitiveMeshCellCentresAndVols.C:84
Foam::boolUList
UList< bool > boolUList
A UList of bools.
Definition: UList.H:78
Foam::polyMesh::faces
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1094
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:381
DynamicField.H
clear
patchWriters clear()
Foam::nl
constexpr char nl
Definition: Ostream.H:385
forAllConstIters
forAllConstIters(mixture.phases(), phase)
Definition: pEqn.H:28
f
labelList f(nPoints)
Foam::faceList
List< face > faceList
A List of faces.
Definition: faceListFwd.H:47
Foam::List< label >
Foam::surfZone
A surface zone on a MeshedSurface.
Definition: surfZone.H:56
Foam::primitiveMesh::isInternalFace
bool isInternalFace(const label faceIndex) const
Return true if given face label is internal to the mesh.
Definition: primitiveMeshI.H:102
Foam::DynamicList::transfer
void transfer(List< T > &lst)
Transfer contents of the argument List into this.
Definition: DynamicListI.H:427
points
const pointField & points
Definition: gmvOutputHeader.H:1
Foam::constant::electromagnetic::e
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
Foam::primitiveMesh::nPoints
label nPoints() const
Number of mesh points.
Definition: primitiveMeshI.H:37
Foam::identity
labelList identity(const label len, label start=0)
Create identity map of the given length with (map[i] == i)
Definition: labelList.C:38
Foam::List::clear
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:115
Foam::DynamicList::clearStorage
void clearStorage()
Clear the list and delete storage.
Definition: DynamicListI.H:355
tetMatcher.H
Foam::surfZoneList
List< surfZone > surfZoneList
Definition: surfZoneList.H:47
Foam::List::set
std::enable_if< std::is_same< bool, TypeT >::value, bool >::type set(const label i, bool val=true)
A bitSet::set() method for a list of bool.
Definition: List.H:325
Foam::isoSurfaceTopo::isoSurfaceTopo
isoSurfaceTopo(const polyMesh &mesh, const scalarField &cellValues, const scalarField &pointValues, const scalar iso, const isoSurfaceParams &params=isoSurfaceParams(), const bitSet &ignoreCells=bitSet())
Construct from cell and point values.
Definition: isoSurfaceTopo.C:971
Foam::fvMesh::time
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:275
Foam::UIndirectList
A List with indirect addressing.
Definition: faMatrix.H:60
Foam::primitivePatch
PrimitivePatch< SubList< face >, const pointField & > primitivePatch
A PrimitivePatch with a SubList addressing for the faces, const reference for the point field.
Definition: primitivePatch.H:51
Foam::isoSurfaceParams::filterNames
static const Enum< filterType > filterNames
Names for the filtering types.
Definition: isoSurfaceParams.H:142
Foam::face
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:72
Foam::DelaunayMeshTools::allPoints
tmp< pointField > allPoints(const Triangulation &t)
Extract all points in vertex-index order.
cells
const cellShapeList & cells
Definition: gmvOutputHeader.H:3
Foam::minMax
MinMax< label > minMax(const labelHashSet &set)
Find the min/max values of labelHashSet.
Definition: hashSets.C:61
p0
const volScalarField & p0
Definition: EEqn.H:36
Foam::isoSurfaceTopo
Marching tet iso surface algorithm with optional filtering to keep only points originating from mesh ...
Definition: isoSurfaceTopo.H:58
Foam::labelUList
UList< label > labelUList
A UList of labels.
Definition: UList.H:80
Foam::GeometricField< scalar, fvPatchField, volMesh >
Foam::MeshedSurface< face >
Foam::IOobject::NO_READ
Definition: IOobject.H:123
Foam::MeshedSurface::size
label size() const
The surface size is the number of faces.
Definition: MeshedSurface.H:405
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(combustionModel, 0)
Foam::polyMeshTetDecomposition::minQuality
static scalar minQuality(const polyMesh &mesh, const point &cC, const label fI, const bool isOwner, const label faceBasePtI)
Given a face and cc and starting index for triangulation determine.
Definition: polyMeshTetDecomposition.C:42
Foam::polyMesh::faceNeighbour
virtual const labelList & faceNeighbour() const
Return face neighbour.
Definition: polyMesh.C:1113
Foam::volumeType::OUTSIDE
A location outside the volume.
Definition: volumeType.H:69
Foam::PrimitivePatch
A list of faces which address into the list of points.
Definition: PrimitivePatch.H:85
tetCell.H