isoSurface.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2019 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "isoSurface.H"
27 #include "polyMesh.H"
28 #include "tetMatcher.H"
29 #include "tetPointRef.H"
30 #include "DynamicField.H"
31 #include "syncTools.H"
33 
34 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
35 
36 namespace Foam
37 {
38  defineTypeNameAndDebug(isoSurface, 0);
39 }
40 
41 
42 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
43 
44 bool Foam::isoSurface::isTriCut
45 (
46  const triFace& tri,
47  const scalarField& pointValues
48 ) const
49 {
50  bool aLower = (pointValues[tri[0]] < iso_);
51  bool bLower = (pointValues[tri[1]] < iso_);
52  bool cLower = (pointValues[tri[2]] < iso_);
53 
54  return !(aLower == bLower && aLower == cLower);
55 }
56 
57 
58 Foam::isoSurface::cellCutType Foam::isoSurface::calcCutType
59 (
60  const bool isTet,
61  const label celli
62 ) const
63 {
64  const cell& cFaces = mesh_.cells()[celli];
65 
66  if (isTet)
67  {
68  forAll(cFaces, cFacei)
69  {
70  const face& f = mesh_.faces()[cFaces[cFacei]];
71 
72  for (label fp = 1; fp < f.size() - 1; fp++)
73  {
74  triFace tri(f[0], f[fp], f[f.fcIndex(fp)]);
75 
76  if (isTriCut(tri, pVals_))
77  {
78  return CUT;
79  }
80  }
81  }
82  return NOTCUT;
83  }
84  else
85  {
86  bool cellLower = (cVals_[celli] < iso_);
87 
88  // First check if there is any cut in cell
89  bool edgeCut = false;
90 
91  forAll(cFaces, cFacei)
92  {
93  label facei = cFaces[cFacei];
94  const face& f = mesh_.faces()[facei];
95 
96  // Check pyramids cut
97  forAll(f, fp)
98  {
99  if ((pVals_[f[fp]] < iso_) != cellLower)
100  {
101  edgeCut = true;
102  break;
103  }
104  }
105 
106  if (edgeCut)
107  {
108  break;
109  }
110 
111  const label fp0 = tetBasePtIs_[facei] < 0 ? 0 : tetBasePtIs_[facei];
112 
113  label fp = f.fcIndex(fp0);
114  for (label i = 2; i < f.size(); i++)
115  {
116  label nextFp = f.fcIndex(fp);
117 
118  if (isTriCut(triFace(f[fp0], f[fp], f[nextFp]), pVals_))
119  {
120  edgeCut = true;
121  break;
122  }
123 
124  fp = nextFp;
125  }
126 
127  if (edgeCut)
128  {
129  break;
130  }
131  }
132 
133  if (edgeCut)
134  {
135  // Count actual cuts (expensive since addressing needed)
136  // Note: not needed if you don't want to preserve maxima/minima
137  // centred around cellcentre. In that case just always return CUT
138 
139  const labelList& cPoints = mesh_.cellPoints(celli);
140 
141  label nPyrCuts = 0;
142 
143  forAll(cPoints, i)
144  {
145  if ((pVals_[cPoints[i]] < iso_) != cellLower)
146  {
147  nPyrCuts++;
148  }
149  }
150 
151  if (nPyrCuts == cPoints.size())
152  {
153  return SPHERE;
154  }
155  else
156  {
157  return CUT;
158  }
159  }
160  else
161  {
162  return NOTCUT;
163  }
164  }
165 }
166 
167 
168 Foam::label Foam::isoSurface::calcCutTypes
169 (
170  tetMatcher& tet,
171  List<cellCutType>& cellCutTypes
172 )
173 {
174  const cellList& cells = mesh_.cells();
175 
176  cellCutTypes.setSize(cells.size());
177  label nCutCells = 0;
178  forAll(cells, celli)
179  {
180  cellCutTypes[celli] = calcCutType(tet.isA(mesh_, celli), celli);
181 
182  if (cellCutTypes[celli] == CUT)
183  {
184  nCutCells++;
185  }
186  }
187 
188  if (debug)
189  {
190  Pout<< "isoSurface : detected " << nCutCells
191  << " candidate cut cells." << endl;
192  }
193  return nCutCells;
194 }
195 
196 
197 Foam::scalar Foam::isoSurface::minTetQ
198 (
199  const label facei,
200  const label faceBasePtI
201 ) const
202 {
204  (
205  mesh_,
206  mesh_.cellCentres()[mesh_.faceOwner()[facei]],
207  facei,
208  true,
209  faceBasePtI
210  );
211 
212  if (mesh_.isInternalFace(facei))
213  {
214  q = min
215  (
216  q,
218  (
219  mesh_,
220  mesh_.cellCentres()[mesh_.faceNeighbour()[facei]],
221  facei,
222  false,
223  faceBasePtI
224  )
225  );
226  }
227  return q;
228 }
229 
230 
231 void Foam::isoSurface::fixTetBasePtIs()
232 {
233  // Determine points used by two faces on the same cell
234  const cellList& cells = mesh_.cells();
235  const faceList& faces = mesh_.faces();
236  const labelList& faceOwner = mesh_.faceOwner();
237  const labelList& faceNeighbour = mesh_.faceNeighbour();
238 
239 
240  // Get face triangulation base point
241  tetBasePtIs_ = mesh_.tetBasePtIs();
242 
243 
244  // Mark all cells with illegal base points as potentially problematic
245  PackedBoolList problemCells(cells.size(), false);
246  forAll(tetBasePtIs_, facei)
247  {
248  if (tetBasePtIs_[facei] == -1)
249  {
250  problemCells[faceOwner[facei]] = true;
251  if (mesh_.isInternalFace(facei))
252  {
253  problemCells[faceNeighbour[facei]] = true;
254  }
255  }
256  }
257 
258 
259  // Mark all points which are shared by just two faces within an adjacent
260  // problem cell as problematic
261  PackedBoolList problemPoints(mesh_.points().size(), false);
262  forAll(cells, celli)
263  {
264  if (problemCells[celli])
265  {
266  const cell& cFaces = cells[celli];
267 
268  Map<label> pointCount;
269  forAll(cFaces, i)
270  {
271  const label facei = cFaces[i];
272  const face& f = faces[facei];
273  forAll(f, fp)
274  {
275  const label pointi = f[fp];
276 
277  Map<label>::iterator pointFnd = pointCount.find(pointi);
278  if (pointFnd == pointCount.end())
279  {
280  pointCount.insert(pointi, 1);
281  }
282  else
283  {
284  ++ pointFnd();
285  }
286  }
287  }
288 
289  forAllConstIter(Map<label>, pointCount, iter)
290  {
291  if (iter() == 1)
292  {
293  FatalErrorInFunction << "point:" << iter.key()
294  << " at:" << mesh_.points()[iter.key()]
295  << " only used by one face" << exit(FatalError);
296  }
297  if (iter() == 2)
298  {
299  problemPoints[iter.key()] = true;
300  }
301  }
302  }
303  }
304 
305 
306  // For all faces which form a part of a problem-cell, check if the base
307  // point is adjacent to any problem points. If it is, re-calculate the base
308  // point so that it is not.
309  label nAdapted = 0;
310  forAll(tetBasePtIs_, facei)
311  {
312  if
313  (
314  problemCells[faceOwner[facei]]
315  || (mesh_.isInternalFace(facei) && problemCells[faceNeighbour[facei]])
316  )
317  {
318  const face& f = faces[facei];
319 
320  // Check if either of the points adjacent to the base point is a
321  // problem point. If not, the existing base point can be retained.
322  const label fp0 = tetBasePtIs_[facei] < 0 ? 0 : tetBasePtIs_[facei];
323 
324  const bool prevPointIsProblem = problemPoints[f[f.rcIndex(fp0)]];
325  const bool nextPointIsProblem = problemPoints[f[f.fcIndex(fp0)]];
326 
327  if (!prevPointIsProblem && !nextPointIsProblem)
328  {
329  continue;
330  }
331 
332  // A new base point is required. Pick the point that results in the
333  // least-worst-worst tet, and which is not adjacent to any problem
334  // points.
335  scalar maxQ = -GREAT;
336  label maxFp = -1;
337  forAll(f, fp)
338  {
339  const bool prevPointIsProblem = problemPoints[f[f.rcIndex(fp)]];
340  const bool nextPointIsProblem = problemPoints[f[f.fcIndex(fp)]];
341 
342  if (!prevPointIsProblem && !nextPointIsProblem)
343  {
344  const scalar q = minTetQ(facei, fp);
345  if (q > maxQ)
346  {
347  maxQ = q;
348  maxFp = fp;
349  }
350  }
351  }
352 
353  if (maxFp != -1)
354  {
355  // Success! Set the new base point
356  tetBasePtIs_[facei] = maxFp;
357  }
358  else
359  {
360  // No point was found on face that would not result in some
361  // duplicate triangle. Do what? Continue and hope? Spit an
362  // error? Silently or noisily reduce the filtering level?
363  }
364 
365  ++ nAdapted;
366  }
367  }
368 
369  if (debug)
370  {
371  Pout<< "isoSurface : adapted starting point of triangulation on "
372  << nAdapted << " faces." << endl;
373  }
374 }
375 
376 
377 Foam::label Foam::isoSurface::generatePoint
378 (
379  const label facei,
380  const bool edgeIsDiag,
381  const edge& vertices,
382 
383  DynamicList<edge>& pointToVerts,
384  DynamicList<label>& pointToFace,
385  DynamicList<bool>& pointFromDiag,
386  EdgeMap<label>& vertsToPoint
387 ) const
388 {
389  EdgeMap<label>::const_iterator edgeFnd = vertsToPoint.find(vertices);
390  if (edgeFnd != vertsToPoint.end())
391  {
392  return edgeFnd();
393  }
394  else
395  {
396  // Generate new point
397  label pointi = pointToVerts.size();
398 
399  pointToVerts.append(vertices);
400  pointToFace.append(facei);
401  pointFromDiag.append(edgeIsDiag);
402  vertsToPoint.insert(vertices, pointi);
403  return pointi;
404  }
405 }
406 
407 
408 void Foam::isoSurface::generateTriPoints
409 (
410  const label facei,
411  const FixedList<scalar, 4>& s,
412  const FixedList<point, 4>& p,
413  const FixedList<label, 4>& pIndex,
414  const FixedList<bool, 6>& edgeIsDiag,// Per tet edge whether is face diag
415 
416  DynamicList<edge>& pointToVerts,
417  DynamicList<label>& pointToFace,
418  DynamicList<bool>& pointFromDiag,
419 
420  EdgeMap<label>& vertsToPoint,
421  DynamicList<label>& verts // Every three verts is new triangle
422 ) const
423 {
424  int triIndex = 0;
425  if (s[0] < iso_)
426  {
427  triIndex |= 1;
428  }
429  if (s[1] < iso_)
430  {
431  triIndex |= 2;
432  }
433  if (s[2] < iso_)
434  {
435  triIndex |= 4;
436  }
437  if (s[3] < iso_)
438  {
439  triIndex |= 8;
440  }
441 
442  // Form the vertices of the triangles for each case
443  switch (triIndex)
444  {
445  case 0x00:
446  case 0x0F:
447  break;
448 
449  case 0x01:
450  case 0x0E:
451  {
452  verts.append
453  (
454  generatePoint
455  (
456  facei,
457  edgeIsDiag[0],
458  edge(pIndex[0], pIndex[1]),
459  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
460  )
461  );
462  verts.append
463  (
464  generatePoint
465  (
466  facei,
467  edgeIsDiag[1],
468  edge(pIndex[0], pIndex[2]),
469  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
470  )
471  );
472  verts.append
473  (
474  generatePoint
475  (
476  facei,
477  edgeIsDiag[2],
478  edge(pIndex[0], pIndex[3]),
479  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
480  )
481  );
482 
483  if (triIndex == 0x0E)
484  {
485  // Flip normals
486  label sz = verts.size();
487  Swap(verts[sz-2], verts[sz-1]);
488  }
489  }
490  break;
491 
492  case 0x02:
493  case 0x0D:
494  {
495  verts.append
496  (
497  generatePoint
498  (
499  facei,
500  edgeIsDiag[0],
501  edge(pIndex[1], pIndex[0]),
502  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
503  )
504  );
505  verts.append
506  (
507  generatePoint
508  (
509  facei,
510  edgeIsDiag[3],
511  edge(pIndex[1], pIndex[3]),
512  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
513  )
514  );
515  verts.append
516  (
517  generatePoint
518  (
519  facei,
520  edgeIsDiag[4],
521  edge(pIndex[1], pIndex[2]),
522  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
523  )
524  );
525 
526  if (triIndex == 0x0D)
527  {
528  // Flip normals
529  label sz = verts.size();
530  Swap(verts[sz-2], verts[sz-1]);
531  }
532  }
533  break;
534 
535  case 0x03:
536  case 0x0C:
537  {
538  label p0p2
539  (
540  generatePoint
541  (
542  facei,
543  edgeIsDiag[1],
544  edge(pIndex[0], pIndex[2]),
545  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
546  )
547  );
548  label p1p3
549  (
550  generatePoint
551  (
552  facei,
553  edgeIsDiag[3],
554  edge(pIndex[1], pIndex[3]),
555  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
556  )
557  );
558 
559  verts.append
560  (
561  generatePoint
562  (
563  facei,
564  edgeIsDiag[2],
565  edge(pIndex[0], pIndex[3]),
566  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
567  )
568  );
569  verts.append(p1p3);
570  verts.append(p0p2);
571  verts.append(p1p3);
572  verts.append
573  (
574  generatePoint
575  (
576  facei,
577  edgeIsDiag[4],
578  edge(pIndex[1], pIndex[2]),
579  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
580  )
581  );
582  verts.append(p0p2);
583 
584  if (triIndex == 0x0C)
585  {
586  // Flip normals
587  label sz = verts.size();
588  Swap(verts[sz-5], verts[sz-4]);
589  Swap(verts[sz-2], verts[sz-1]);
590  }
591  }
592  break;
593 
594  case 0x04:
595  case 0x0B:
596  {
597  verts.append
598  (
599  generatePoint
600  (
601  facei,
602  edgeIsDiag[1],
603  edge(pIndex[2], pIndex[0]),
604  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
605  )
606  );
607  verts.append
608  (
609  generatePoint
610  (
611  facei,
612  edgeIsDiag[4],
613  edge(pIndex[2], pIndex[1]),
614  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
615  )
616  );
617  verts.append
618  (
619  generatePoint
620  (
621  facei,
622  edgeIsDiag[5],
623  edge(pIndex[2], pIndex[3]),
624  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
625  )
626  );
627 
628  if (triIndex == 0x0B)
629  {
630  // Flip normals
631  label sz = verts.size();
632  Swap(verts[sz-2], verts[sz-1]);
633  }
634  }
635  break;
636 
637  case 0x05:
638  case 0x0A:
639  {
640  label p0p1
641  (
642  generatePoint
643  (
644  facei,
645  edgeIsDiag[0],
646  edge(pIndex[0], pIndex[1]),
647  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
648  )
649  );
650  label p2p3
651  (
652  generatePoint
653  (
654  facei,
655  edgeIsDiag[5],
656  edge(pIndex[2], pIndex[3]),
657  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
658  )
659  );
660 
661  verts.append(p0p1);
662  verts.append(p2p3);
663  verts.append
664  (
665  generatePoint
666  (
667  facei,
668  edgeIsDiag[2],
669  edge(pIndex[0], pIndex[3]),
670  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
671  )
672  );
673  verts.append(p0p1);
674  verts.append
675  (
676  generatePoint
677  (
678  facei,
679  edgeIsDiag[4],
680  edge(pIndex[1], pIndex[2]),
681  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
682  )
683  );
684  verts.append(p2p3);
685 
686  if (triIndex == 0x0A)
687  {
688  // Flip normals
689  label sz = verts.size();
690  Swap(verts[sz-5], verts[sz-4]);
691  Swap(verts[sz-2], verts[sz-1]);
692  }
693  }
694  break;
695 
696  case 0x06:
697  case 0x09:
698  {
699  label p0p1
700  (
701  generatePoint
702  (
703  facei,
704  edgeIsDiag[0],
705  edge(pIndex[0], pIndex[1]),
706  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
707  )
708  );
709  label p2p3
710  (
711  generatePoint
712  (
713  facei,
714  edgeIsDiag[5],
715  edge(pIndex[2], pIndex[3]),
716  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
717  )
718  );
719 
720  verts.append(p0p1);
721  verts.append
722  (
723  generatePoint
724  (
725  facei,
726  edgeIsDiag[3],
727  edge(pIndex[1], pIndex[3]),
728  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
729  )
730  );
731  verts.append(p2p3);
732  verts.append(p0p1);
733  verts.append(p2p3);
734  verts.append
735  (
736  generatePoint
737  (
738  facei,
739  edgeIsDiag[1],
740  edge(pIndex[0], pIndex[2]),
741  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
742  )
743  );
744 
745  if (triIndex == 0x09)
746  {
747  // Flip normals
748  label sz = verts.size();
749  Swap(verts[sz-5], verts[sz-4]);
750  Swap(verts[sz-2], verts[sz-1]);
751  }
752  }
753  break;
754 
755  case 0x08:
756  case 0x07:
757  {
758  verts.append
759  (
760  generatePoint
761  (
762  facei,
763  edgeIsDiag[2],
764  edge(pIndex[3], pIndex[0]),
765  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
766  )
767  );
768  verts.append
769  (
770  generatePoint
771  (
772  facei,
773  edgeIsDiag[5],
774  edge(pIndex[3], pIndex[2]),
775  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
776  )
777  );
778  verts.append
779  (
780  generatePoint
781  (
782  facei,
783  edgeIsDiag[3],
784  edge(pIndex[3], pIndex[1]),
785  pointToVerts, pointToFace, pointFromDiag, vertsToPoint
786  )
787  );
788  if (triIndex == 0x07)
789  {
790  // Flip normals
791  label sz = verts.size();
792  Swap(verts[sz-2], verts[sz-1]);
793  }
794  }
795  break;
796  }
797 }
798 
799 
800 void Foam::isoSurface::generateTriPoints
801 (
802  const label celli,
803  const bool isTet,
804 
805  DynamicList<edge>& pointToVerts,
806  DynamicList<label>& pointToFace,
807  DynamicList<bool>& pointFromDiag,
808 
809  EdgeMap<label>& vertsToPoint,
810  DynamicList<label>& verts,
811  DynamicList<label>& faceLabels
812 ) const
813 {
814  const faceList& faces = mesh_.faces();
815  const labelList& faceOwner = mesh_.faceOwner();
816  const pointField& points = mesh_.points();
817  const cell& cFaces = mesh_.cells()[celli];
818 
819  if (isTet)
820  {
821  // For tets don't do cell-centre decomposition, just use the
822  // tet points and values
823 
824  label facei = cFaces[0];
825  const face& f0 = faces[facei];
826 
827  // Get the other point
828  const face& f1 = faces[cFaces[1]];
829  label oppositeI = -1;
830  forAll(f1, fp)
831  {
832  oppositeI = f1[fp];
833  if (findIndex(f0, oppositeI) == -1)
834  {
835  break;
836  }
837  }
838 
839 
840  label p0 = f0[0];
841  label p1 = f0[1];
842  label p2 = f0[2];
843  FixedList<bool, 6> edgeIsDiag(false);
844 
845  if (faceOwner[facei] == celli)
846  {
847  Swap(p1, p2);
848  }
849 
850  tetPointRef tet
851  (
852  points[p0],
853  points[p1],
854  points[p2],
855  points[oppositeI]
856  );
857 
858  label startTrii = verts.size();
859  generateTriPoints
860  (
861  facei,
862  FixedList<scalar, 4>
863  ({
864  pVals_[p0],
865  pVals_[p1],
866  pVals_[p2],
867  pVals_[oppositeI]
868  }),
869  FixedList<point, 4>
870  ({
871  points[p0],
872  points[p1],
873  points[p2],
874  points[oppositeI]
875  }),
876  FixedList<label, 4>
877  ({
878  p0,
879  p1,
880  p2,
881  oppositeI
882  }),
883  edgeIsDiag,
884 
885  pointToVerts,
886  pointToFace,
887  pointFromDiag,
888  vertsToPoint,
889  verts // Every three verts is new triangle
890  );
891 
892  label nTris = (verts.size()-startTrii)/3;
893  for (label i = 0; i < nTris; i++)
894  {
895  faceLabels.append(facei);
896  }
897  }
898  else
899  {
900  const pointField& cellCentres = mesh_.cellCentres();
901 
902  forAll(cFaces, cFacei)
903  {
904  label facei = cFaces[cFacei];
905  const face& f = faces[facei];
906 
907  const label fp0 = tetBasePtIs_[facei] < 0 ? 0 : tetBasePtIs_[facei];
908 
909  label startTrii = verts.size();
910 
911  label fp = f.fcIndex(fp0);
912  for (label i = 2; i < f.size(); i++)
913  {
914  label nextFp = f.fcIndex(fp);
915 
916  FixedList<bool, 6> edgeIsDiag(false);
917 
918  label p0 = f[fp0];
919  label p1 = f[fp];
920  label p2 = f[nextFp];
921  if (faceOwner[facei] == celli)
922  {
923  Swap(p1, p2);
924  if (i != 2) edgeIsDiag[1] = true;
925  if (i != f.size()-1) edgeIsDiag[0] = true;
926  }
927  else
928  {
929  if (i != 2) edgeIsDiag[0] = true;
930  if (i != f.size()-1) edgeIsDiag[1] = true;
931  }
932 
933  tetPointRef tet
934  (
935  points[p0],
936  points[p1],
937  points[p2],
938  cellCentres[celli]
939  );
940 
941  generateTriPoints
942  (
943  facei,
944  FixedList<scalar, 4>
945  ({
946  pVals_[p0],
947  pVals_[p1],
948  pVals_[p2],
949  cVals_[celli]
950  }),
951  FixedList<point, 4>
952  ({
953  points[p0],
954  points[p1],
955  points[p2],
956  cellCentres[celli]
957  }),
958  FixedList<label, 4>
959  ({
960  p0,
961  p1,
962  p2,
963  mesh_.nPoints()+celli
964  }),
965  edgeIsDiag,
966 
967  pointToVerts,
968  pointToFace,
969  pointFromDiag,
970  vertsToPoint,
971  verts // Every three verts is new triangle
972  );
973 
974  fp = nextFp;
975  }
976 
977  label nTris = (verts.size()-startTrii)/3;
978  for (label i = 0; i < nTris; i++)
979  {
980  faceLabels.append(facei);
981  }
982  }
983  }
984 }
985 
986 
987 void Foam::isoSurface::triangulateOutside
988 (
989  const bool filterDiag,
990  const primitivePatch& pp,
991  const boolList& pointFromDiag,
992  const labelList& pointToFace,
993  const label cellID,
994 
995  DynamicList<face>& compactFaces,
996  DynamicList<label>& compactCellIDs
997 ) const
998 {
999  // We can form pockets:
1000  // - 1. triangle on face
1001  // - 2. multiple triangles on interior (from diag edges)
1002  // - the edge loop will be pocket since it is only the diag
1003  // edges that give it volume?
1004 
1005  // Retriangulate the exterior loops
1006  const labelListList& edgeLoops = pp.edgeLoops();
1007  const labelList& mp = pp.meshPoints();
1008 
1009  forAll(edgeLoops, loopi)
1010  {
1011  const labelList& loop = edgeLoops[loopi];
1012 
1013  if (loop.size() > 2)
1014  {
1015  compactFaces.append(face(0));
1016  face& f = compactFaces.last();
1017 
1018  f.setSize(loop.size());
1019  label fpi = 0;
1020  forAll(f, i)
1021  {
1022  label pointi = mp[loop[i]];
1023  if (filterDiag && pointFromDiag[pointi])
1024  {
1025  label prevPointi = mp[loop[loop.fcIndex(i)]];
1026  if
1027  (
1028  pointFromDiag[prevPointi]
1029  && (pointToFace[pointi] != pointToFace[prevPointi])
1030  )
1031  {
1032  f[fpi++] = pointi;
1033  }
1034  else
1035  {
1036  // Filter out diagonal point
1037  }
1038  }
1039  else
1040  {
1041  f[fpi++] = pointi;
1042  }
1043  }
1044 
1045  if (fpi > 2)
1046  {
1047  f.setSize(fpi);
1048  }
1049  else
1050  {
1051  // Keep original face
1052  forAll(f, i)
1053  {
1054  label pointi = mp[loop[i]];
1055  f[i] = pointi;
1056  }
1057  }
1058  compactCellIDs.append(cellID);
1059  }
1060  }
1061 }
1062 
1063 
1064 Foam::MeshedSurface<Foam::face> Foam::isoSurface::removeInsidePoints
1065 (
1066  const bool filterDiag,
1067  const MeshedSurface<face>& s,
1068  const boolList& pointFromDiag,
1069  const labelList& pointToFace,
1070  const labelList& start, // Per cell the starting triangle
1071  DynamicList<label>& pointCompactMap, // Per returned point the original
1072  DynamicList<label>& compactCellIDs // Per returned tri the cellID
1073 ) const
1074 {
1075  const pointField& points = s.points();
1076 
1077  pointCompactMap.clear();
1078  compactCellIDs.clear();
1079 
1080  DynamicList<face> compactFaces(s.size()/8);
1081 
1082  for (label celli = 0; celli < start.size()-1; celli++)
1083  {
1084  // All triangles of the current cell
1085 
1086  label nTris = start[celli+1]-start[celli];
1087 
1088  if (nTris)
1089  {
1090  const SubList<face> cellFaces(s, nTris, start[celli]);
1091  const primitivePatch pp(cellFaces, points);
1092 
1093  triangulateOutside
1094  (
1095  filterDiag,
1096  pp,
1097  pointFromDiag,
1098  pointToFace,
1099  //protectedFace,
1100  celli,
1101 
1102  compactFaces,
1103  compactCellIDs
1104  );
1105  }
1106  }
1107 
1108 
1109  // Compact out unused points
1110  // Pick up the used vertices
1111  labelList oldToCompact(points.size(), -1);
1112  DynamicField<point> compactPoints(points.size());
1113  pointCompactMap.clear();
1114 
1115  forAll(compactFaces, i)
1116  {
1117  face& f = compactFaces[i];
1118  forAll(f, fp)
1119  {
1120  label pointi = f[fp];
1121  label compacti = oldToCompact[pointi];
1122  if (compacti == -1)
1123  {
1124  compacti = compactPoints.size();
1125  oldToCompact[pointi] = compacti;
1126  compactPoints.append(points[pointi]);
1127  pointCompactMap.append(pointi);
1128  }
1129  f[fp] = compacti;
1130  }
1131  }
1132 
1133 
1134  MeshedSurface<face> cpSurface;
1135  cpSurface.reset
1136  (
1137  move(compactPoints),
1138  move(compactFaces),
1139  surfZoneList(s.surfZones())
1140  );
1141 
1142  return cpSurface;
1143 }
1144 
1145 
1146 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1147 
1150  const polyMesh& mesh,
1151  const scalarField& cVals,
1152  const scalarField& pVals,
1153  const scalar iso,
1154  const filterType filter
1155 )
1156 :
1157  mesh_(mesh),
1158  cVals_(cVals),
1159  pVals_(pVals),
1160  iso_(iso)
1161 {
1162  if (debug)
1163  {
1164  Pout<< "isoSurface : iso:" << iso_ << " filter:" << filter << endl;
1165  }
1166 
1167  fixTetBasePtIs();
1168 
1169  tetMatcher tet;
1170 
1171  // Determine if any cut through cell
1172  List<cellCutType> cellCutTypes;
1173  const label nCutCells = calcCutTypes(tet, cellCutTypes);
1174 
1175  // Per cell: 5 pyramids cut, each generating 2 triangles
1176  // - pointToVerts : from generated iso point to originating mesh verts
1177  DynamicList<edge> pointToVerts(10*nCutCells);
1178  // - pointToFace : from generated iso point to originating mesh face
1179  DynamicList<label> pointToFace(10*nCutCells);
1180  // - pointToFace : from generated iso point whether is on face diagonal
1181  DynamicList<bool> pointFromDiag(10*nCutCells);
1182 
1183  // Per cell: number of intersected edges:
1184  // - four faces cut so 4 mesh edges + 4 face-diagonal edges
1185  // - 4 of the pyramid edges
1186  EdgeMap<label> vertsToPoint(12*nCutCells);
1187  DynamicList<label> verts(12*nCutCells);
1188  // Per cell: 5 pyramids cut (since only one pyramid not cut)
1189  DynamicList<label> faceLabels(5*nCutCells);
1190  DynamicList<label> cellLabels(5*nCutCells);
1191 
1192 
1193  labelList startTri(mesh_.nCells()+1, 0);
1194 
1195  for (label celli = 0; celli < mesh_.nCells(); celli++)
1196  {
1197  startTri[celli] = faceLabels.size();
1198  if (cellCutTypes[celli] != NOTCUT)
1199  {
1200  generateTriPoints
1201  (
1202  celli,
1203  tet.isA(mesh_, celli),
1204 
1205  pointToVerts,
1206  pointToFace,
1207  pointFromDiag,
1208 
1209  vertsToPoint,
1210  verts,
1211  faceLabels
1212  );
1213 
1214  for (label i = startTri[celli]; i < faceLabels.size(); i++)
1215  {
1216  cellLabels.append(celli);
1217  }
1218  }
1219  }
1220  startTri[mesh_.nCells()] = faceLabels.size();
1221 
1222 
1223  pointToVerts_.transfer(pointToVerts);
1224  meshCells_.transfer(cellLabels);
1225  pointToFace_.transfer(pointToFace);
1226 
1228  (
1229  interpolate
1230  (
1231  mesh_.cellCentres(),
1232  mesh_.points()
1233  )
1234  );
1235 
1236 
1237  // Assign to MeshedSurface
1238  faceList allTris(faceLabels.size());
1239  label verti = 0;
1240  forAll(allTris, i)
1241  {
1242  allTris[i].setSize(3);
1243  allTris[i][0] = verts[verti++];
1244  allTris[i][1] = verts[verti++];
1245  allTris[i][2] = verts[verti++];
1246  }
1247 
1248 
1249  surfZoneList allZones(1);
1250  allZones[0] = surfZone
1251  (
1252  "allFaces",
1253  allTris.size(), // Size
1254  0, // Start
1255  0 // Index
1256  );
1257 
1259  (
1260  move(allPoints.ref()),
1261  move(allTris),
1262  move(allZones)
1263  );
1264 
1265 
1266  // Now:
1267  // - generated faces and points are assigned to *this
1268  // - per point we know:
1269  // - pointOnDiag: whether it is on a face-diagonal edge
1270  // - pointToFace_: from what pyramid (cell+face) it was produced
1271  // (note that the pyramid faces are shared between multiple mesh faces)
1272  // - pointToVerts_ : originating mesh vertex or cell centre
1273 
1274 
1275  if (debug)
1276  {
1277  Pout<< "isoSurface : generated " << size() << " faces." << endl;
1278  }
1279 
1280 
1281  if (filter != NONE)
1282  {
1283  // Triangulate outside (filter edges to cell centres and optionally
1284  // face diagonals)
1285  DynamicList<label> pointCompactMap; // Back to original point
1286  DynamicList<label> compactCellIDs; // Per returned tri the cellID
1288  (
1289  removeInsidePoints
1290  (
1291  (filter == DIAGCELL ? true : false),
1292  *this,
1293  pointFromDiag,
1294  pointToFace_,
1295  startTri,
1296  pointCompactMap,
1297  compactCellIDs
1298  )
1299  );
1300 
1301  pointToVerts_ = UIndirectList<edge>(pointToVerts_, pointCompactMap)();
1302  pointToFace_ = UIndirectList<label>(pointToFace_, pointCompactMap)();
1303  pointFromDiag = UIndirectList<bool>(pointFromDiag, pointCompactMap)();
1304  meshCells_.transfer(compactCellIDs);
1305 
1306  if (debug)
1307  {
1308  Pout<< "isoSurface :"
1309  << " after removing cell centre and face-diag triangles : "
1310  << size() << endl;
1311  }
1312 
1313 
1314  if (filter == DIAGCELL)
1315  {
1316  // We remove verts on face diagonals. This is in fact just
1317  // straightening the edges of the face through the cell. This can
1318  // close off 'pockets' of triangles and create open or
1319  // multiply-connected triangles
1320 
1321  // Solved by eroding open-edges
1322  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1323 
1324 
1325  // Mark points on mesh outside. Note that we extend with nCells
1326  // so we can easily index with pointToVerts_.
1327  PackedBoolList isBoundaryPoint(mesh.nPoints() + mesh.nCells());
1328  for
1329  (
1330  label facei = mesh.nInternalFaces();
1331  facei < mesh.nFaces();
1332  facei++
1333  )
1334  {
1335  isBoundaryPoint.set(mesh.faces()[facei]);
1336  }
1337 
1338 
1339  while (true)
1340  {
1341  const labelList& mp = meshPoints();
1342 
1343  PackedBoolList removeFace(this->size());
1344  label nFaces = 0;
1345  {
1346  const labelListList& edgeFaces =
1348  forAll(edgeFaces, edgei)
1349  {
1350  const labelList& eFaces = edgeFaces[edgei];
1351  if (eFaces.size() == 1)
1352  {
1353  // Open edge. Check that vertices do not originate
1354  // from a boundary face
1355  const edge& e = edges()[edgei];
1356  const edge& verts0 = pointToVerts_[mp[e[0]]];
1357  const edge& verts1 = pointToVerts_[mp[e[1]]];
1358  if
1359  (
1360  isBoundaryPoint[verts0[0]]
1361  && isBoundaryPoint[verts0[1]]
1362  && isBoundaryPoint[verts1[0]]
1363  && isBoundaryPoint[verts1[1]]
1364  )
1365  {
1366  // Open edge on boundary face. Keep
1367  }
1368  else
1369  {
1370  // Open edge. Mark for erosion
1371  if (removeFace.set(eFaces[0]))
1372  {
1373  nFaces++;
1374  }
1375  }
1376  }
1377  }
1378  }
1379 
1380  if (debug)
1381  {
1382  Pout<< "isoSurface :"
1383  << " removing " << nFaces
1384  << " faces since on open edges" << endl;
1385  }
1386 
1387  if (returnReduce(nFaces, sumOp<label>()) == 0)
1388  {
1389  break;
1390  }
1391 
1392  // Remove the faces
1393  labelHashSet keepFaces(2*size());
1394  forAll(removeFace, facei)
1395  {
1396  if (!removeFace[facei])
1397  {
1398  keepFaces.insert(facei);
1399  }
1400  }
1401 
1402  labelList pointMap;
1404  MeshedSurface<face> filteredSurf
1405  (
1407  (
1408  keepFaces,
1409  pointMap,
1410  faceMap
1411  )
1412  );
1413  MeshedSurface<face>::transfer(filteredSurf);
1414 
1415  pointToVerts_ = UIndirectList<edge>(pointToVerts_, pointMap)();
1416  pointToFace_ = UIndirectList<label>(pointToFace_, pointMap)();
1417  pointFromDiag = UIndirectList<bool>(pointFromDiag, pointMap)();
1418  meshCells_ = UIndirectList<label>(meshCells_, faceMap)();
1419  }
1420  }
1421  }
1422 }
1423 
1424 
1425 // ************************************************************************* //
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:57
static const scalar GREAT
Definition: scalar.H:108
tetrahedron< point, const point & > tetPointRef
Definition: tetPointRef.H:44
A cellMatcher for tet cells.
Definition: tetMatcher.H:51
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
error FatalError
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:319
label nInternalFaces() const
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: HashTable.H:59
HashTable< label, label, Hash< label > >::iterator iterator
Definition: Map.H:56
bool interpolate(const vector &p1, const vector &p2, const vector &o, vector &n, scalar l)
Definition: curveTools.C:75
label nFaces() const
T & ref() const
Return non-const reference or generate a fatal error.
Definition: tmpI.H:174
A surface zone on a MeshedSurface.
Definition: surfZone.H:62
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:163
List< face > faceList
Definition: faceListFwd.H:43
label nCells() const
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.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:256
void transfer(MeshedSurface< Face > &)
Transfer the contents of the argument and annul the argument.
bool insert(const Key &key)
Insert a new entry.
Definition: HashSet.H:108
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
isoSurface(const polyMesh &mesh, const scalarField &cellValues, const scalarField &pointValues, const scalar iso, const filterType filter=DIAGCELL)
Construct from dictionary.
Definition: isoSurface.C:1149
List< bool > boolList
Bool container classes.
Definition: boolList.H:50
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
void Swap(T &a, T &b)
Definition: Swap.H:43
void set(const PackedList< 1 > &)
Set specified bits.
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects...
Definition: DynamicList.H:56
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:58
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:124
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
friend class const_iterator
Declare friendship with the const_iterator.
Definition: HashTable.H:197
face triFace(3)
void append(const T &)
Append an element at the end of the list.
Definition: ListI.H:177
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:296
const labelListList & edgeFaces() const
Return edge-face addressing.
List< label > labelList
A List of labels.
Definition: labelList.H:56
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1156
forAllConstIter(PtrDictionary< phaseModel >, mixture.phases(), phase)
Definition: pEqn.H:29
virtual void reset(pointField &&points, List< Face > &&faces, surfZoneList &&zones)
Reset primitive data (points, faces and zones)
defineTypeNameAndDebug(combustionModel, 0)
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurrence of given element and return index,.
dimensioned< Type > min(const dimensioned< Type > &, const dimensioned< Type > &)
PrimitivePatch< SubList< face >, const pointField & > primitivePatch
Addressing for a faceList slice.
List< surfZone > surfZoneList
Definition: surfZoneList.H:45
virtual bool isA(const primitiveMesh &mesh, const label celli)
Exact match. Uses faceSizeMatch.
Definition: tetMatcher.C:218
void setSize(const label)
Reset size of List.
Definition: List.C:281
A bit-packed bool list.
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
A List with indirect addressing.
Definition: fvMatrix.H:106
tmp< pointField > allPoints(const Triangulation &t)
Extract all points in vertex-index order.
label nPoints() const
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
const doubleScalar e
Elementary charge.
Definition: doubleScalar.H:98
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:74
A class for managing temporary objects.
Definition: PtrList.H:53
List< cell > cellList
list of cells
Definition: cellList.H:42
Namespace for OpenFOAM.