meshCutAndRemove.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 | Copyright (C) 2011-2015 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 "meshCutAndRemove.H"
27 #include "polyMesh.H"
28 #include "polyTopoChange.H"
29 #include "polyAddFace.H"
30 #include "polyAddPoint.H"
31 #include "polyRemovePoint.H"
32 #include "polyRemoveFace.H"
33 #include "polyModifyFace.H"
34 #include "cellCuts.H"
35 #include "mapPolyMesh.H"
36 #include "meshTools.H"
37 
38 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
39 
40 namespace Foam
41 {
42 defineTypeNameAndDebug(meshCutAndRemove, 0);
43 }
44 
45 
46 // * * * * * * * * * * * * * Private Static Functions * * * * * * * * * * * //
47 
48 // Returns -1 or index in elems1 of first shared element.
50 (
51  const labelList& elems1,
52  const labelList& elems2
53 )
54 {
55  forAll(elems1, elemI)
56  {
57  label index1 = findIndex(elems2, elems1[elemI]);
58 
59  if (index1 != -1)
60  {
61  return index1;
62  }
63  }
64  return -1;
65 }
66 
67 
68 // Check if twoCuts at two consecutive position in cuts.
70 (
71  const edge& twoCuts,
72  const labelList& cuts
73 )
74 {
75  label index = findIndex(cuts, twoCuts[0]);
76 
77  if (index == -1)
78  {
79  return false;
80  }
81 
82  return
83  (
84  cuts[cuts.fcIndex(index)] == twoCuts[1]
85  || cuts[cuts.rcIndex(index)] == twoCuts[1]
86  );
87 }
88 
89 
90 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
91 
92 // Returns the cell in cellLabels that is cut. Or -1.
94 (
95  const cellCuts& cuts,
96  const labelList& cellLabels
97 ) const
98 {
99  forAll(cellLabels, labelI)
100  {
101  label cellI = cellLabels[labelI];
102 
103  if (cuts.cellLoops()[cellI].size())
104  {
105  return cellI;
106  }
107  }
108  return -1;
109 }
110 
111 
112 //- Returns first pointI in pointLabels that uses an internal
113 // face. Used to find point to inflate cell/face from (has to be
114 // connected to internal face). Returns -1 (so inflate from nothing) if
115 // none found.
117 (
118  const labelList& pointLabels
119 ) const
120 {
122  {
123  label pointI = pointLabels[labelI];
124 
125  const labelList& pFaces = mesh().pointFaces()[pointI];
126 
127  forAll(pFaces, pFaceI)
128  {
129  label faceI = pFaces[pFaceI];
130 
131  if (mesh().isInternalFace(faceI))
132  {
133  return pointI;
134  }
135  }
136  }
137 
138  if (pointLabels.empty())
139  {
141  << "Empty pointLabels" << abort(FatalError);
142  }
143 
144  return -1;
145 }
146 
147 
148 // Find point on face that is part of original mesh and that is point connected
149 // to the patch
151 (
152  const face& f,
153  const label exposedPatchI
154 ) const
155 {
156  const labelListList& pointFaces = mesh().pointFaces();
157  const polyBoundaryMesh& patches = mesh().boundaryMesh();
158 
159  forAll(f, fp)
160  {
161  label pointI = f[fp];
162 
163  if (pointI < mesh().nPoints())
164  {
165  const labelList& pFaces = pointFaces[pointI];
166 
167  forAll(pFaces, i)
168  {
169  if (patches.whichPatch(pFaces[i]) == exposedPatchI)
170  {
171  return pointI;
172  }
173  }
174  }
175  }
176  return -1;
177 }
178 
179 
180 // Get new owner and neighbour of face. Checks anchor points to see if
181 // cells have been removed.
183 (
184  const cellCuts& cuts,
185  const label exposedPatchI,
186  const label faceI,
187  label& own,
188  label& nei,
189  label& patchID
190 ) const
191 {
192  const labelListList& anchorPts = cuts.cellAnchorPoints();
193  const labelListList& cellLoops = cuts.cellLoops();
194 
195  const face& f = mesh().faces()[faceI];
196 
197  own = mesh().faceOwner()[faceI];
198 
199  if (cellLoops[own].size() && firstCommon(f, anchorPts[own]) == -1)
200  {
201  // owner has been split and this is the removed part.
202  own = -1;
203  }
204 
205  nei = -1;
206 
207  if (mesh().isInternalFace(faceI))
208  {
209  nei = mesh().faceNeighbour()[faceI];
210 
211  if (cellLoops[nei].size() && firstCommon(f, anchorPts[nei]) == -1)
212  {
213  nei = -1;
214  }
215  }
216 
217  patchID = mesh().boundaryMesh().whichPatch(faceI);
218 
219  if (patchID == -1 && (own == -1 || nei == -1))
220  {
221  // Face was internal but becomes external
222  patchID = exposedPatchI;
223  }
224 }
225 
226 
228 (
229  const label faceI,
230  label& zoneID,
231  bool& zoneFlip
232 ) const
233 {
234  zoneID = mesh().faceZones().whichZone(faceI);
235 
236  zoneFlip = false;
237 
238  if (zoneID >= 0)
239  {
240  const faceZone& fZone = mesh().faceZones()[zoneID];
241 
242  zoneFlip = fZone.flipMap()[fZone.whichFace(faceI)];
243  }
244 }
245 
246 
247 // Adds a face from point.
249 (
250  polyTopoChange& meshMod,
251  const label faceI,
252  const label masterPointI,
253  const face& newFace,
254  const label own,
255  const label nei,
256  const label patchID
257 )
258 {
259  label zoneID;
260  bool zoneFlip;
261 
262  getZoneInfo(faceI, zoneID, zoneFlip);
263 
264  if ((nei == -1) || (own != -1 && own < nei))
265  {
266  // Ordering ok.
267  if (debug & 2)
268  {
269  Pout<< "Adding face " << newFace
270  << " with new owner:" << own
271  << " with new neighbour:" << nei
272  << " patchID:" << patchID
273  << " anchor:" << masterPointI
274  << " zoneID:" << zoneID
275  << " zoneFlip:" << zoneFlip
276  << endl;
277  }
278 
279  meshMod.setAction
280  (
282  (
283  newFace, // face
284  own, // owner
285  nei, // neighbour
286  masterPointI, // master point
287  -1, // master edge
288  -1, // master face for addition
289  false, // flux flip
290  patchID, // patch for face
291  zoneID, // zone for face
292  zoneFlip // face zone flip
293  )
294  );
295  }
296  else
297  {
298  // Reverse owner/neighbour
299  if (debug & 2)
300  {
301  Pout<< "Adding (reversed) face " << newFace.reverseFace()
302  << " with new owner:" << nei
303  << " with new neighbour:" << own
304  << " patchID:" << patchID
305  << " anchor:" << masterPointI
306  << " zoneID:" << zoneID
307  << " zoneFlip:" << zoneFlip
308  << endl;
309  }
310 
311  meshMod.setAction
312  (
314  (
315  newFace.reverseFace(), // face
316  nei, // owner
317  own, // neighbour
318  masterPointI, // master point
319  -1, // master edge
320  -1, // master face for addition
321  false, // flux flip
322  patchID, // patch for face
323  zoneID, // zone for face
324  zoneFlip // face zone flip
325  )
326  );
327  }
328 }
329 
330 
331 // Modifies existing faceI for either new owner/neighbour or new face points.
333 (
334  polyTopoChange& meshMod,
335  const label faceI,
336  const face& newFace,
337  const label own,
338  const label nei,
339  const label patchID
340 )
341 {
342  label zoneID;
343  bool zoneFlip;
344 
345  getZoneInfo(faceI, zoneID, zoneFlip);
346 
347  if
348  (
349  (own != mesh().faceOwner()[faceI])
350  || (
351  mesh().isInternalFace(faceI)
352  && (nei != mesh().faceNeighbour()[faceI])
353  )
354  || (newFace != mesh().faces()[faceI])
355  )
356  {
357  if (debug & 2)
358  {
359  Pout<< "Modifying face " << faceI
360  << " old vertices:" << mesh().faces()[faceI]
361  << " new vertices:" << newFace
362  << " new owner:" << own
363  << " new neighbour:" << nei
364  << " new patch:" << patchID
365  << " new zoneID:" << zoneID
366  << " new zoneFlip:" << zoneFlip
367  << endl;
368  }
369 
370  if ((nei == -1) || (own != -1 && own < nei))
371  {
372  meshMod.setAction
373  (
375  (
376  newFace, // modified face
377  faceI, // label of face being modified
378  own, // owner
379  nei, // neighbour
380  false, // face flip
381  patchID, // patch for face
382  false, // remove from zone
383  zoneID, // zone for face
384  zoneFlip // face flip in zone
385  )
386  );
387  }
388  else
389  {
390  meshMod.setAction
391  (
393  (
394  newFace.reverseFace(), // modified face
395  faceI, // label of face being modified
396  nei, // owner
397  own, // neighbour
398  false, // face flip
399  patchID, // patch for face
400  false, // remove from zone
401  zoneID, // zone for face
402  zoneFlip // face flip in zone
403  )
404  );
405  }
406  }
407 }
408 
409 
410 // Copies face starting from startFp up to and including endFp.
412 (
413  const face& f,
414  const label startFp,
415  const label endFp,
416  face& newFace
417 ) const
418 {
419  label fp = startFp;
420 
421  label newFp = 0;
422 
423  while (fp != endFp)
424  {
425  newFace[newFp++] = f[fp];
426 
427  fp = (fp + 1) % f.size();
428  }
429  newFace[newFp] = f[fp];
430 }
431 
432 
433 // Actually split face in two along splitEdge v0, v1 (the two vertices in new
434 // vertex numbering). Generates faces in same ordering
435 // as original face. Replaces cutEdges by the points introduced on them
436 // (addedPoints_).
438 (
439  const face& f,
440  const label v0,
441  const label v1,
442 
443  face& f0,
444  face& f1
445 ) const
446 {
447  // Check if we find any new vertex which is part of the splitEdge.
448  label startFp = findIndex(f, v0);
449 
450  if (startFp == -1)
451  {
453  << "Cannot find vertex (new numbering) " << v0
454  << " on face " << f
455  << abort(FatalError);
456  }
457 
458  label endFp = findIndex(f, v1);
459 
460  if (endFp == -1)
461  {
463  << "Cannot find vertex (new numbering) " << v1
464  << " on face " << f
465  << abort(FatalError);
466  }
467 
468 
469  f0.setSize((endFp + 1 + f.size() - startFp) % f.size());
470  f1.setSize(f.size() - f0.size() + 2);
471 
472  copyFace(f, startFp, endFp, f0);
473  copyFace(f, endFp, startFp, f1);
474 }
475 
476 
477 // Adds additional vertices (from edge cutting) to face. Used for faces which
478 // are not split but still might use edge that has been cut.
480 {
481  const face& f = mesh().faces()[faceI];
482 
483  face newFace(2 * f.size());
484 
485  label newFp = 0;
486 
487  forAll(f, fp)
488  {
489  // Duplicate face vertex.
490  newFace[newFp++] = f[fp];
491 
492  // Check if edge has been cut.
493  label fp1 = f.fcIndex(fp);
494 
495  HashTable<label, edge, Hash<edge> >::const_iterator fnd =
496  addedPoints_.find(edge(f[fp], f[fp1]));
497 
498  if (fnd != addedPoints_.end())
499  {
500  // edge has been cut. Introduce new vertex.
501  newFace[newFp++] = fnd();
502  }
503  }
504 
505  newFace.setSize(newFp);
506 
507  return newFace;
508 }
509 
510 
511 // Walk loop (loop of cuts) across circumference of cellI. Returns face in
512 // new vertices.
513 // Note: tricky bit is that it can use existing edges which have been split.
515 (
516  const label cellI,
517  const labelList& loop
518 ) const
519 {
520  face newFace(2*loop.size());
521 
522  label newFaceI = 0;
523 
524  forAll(loop, fp)
525  {
526  label cut = loop[fp];
527 
528  if (isEdge(cut))
529  {
530  label edgeI = getEdge(cut);
531 
532  const edge& e = mesh().edges()[edgeI];
533 
534  label vertI = addedPoints_[e];
535 
536  newFace[newFaceI++] = vertI;
537  }
538  else
539  {
540  // cut is vertex.
541  label vertI = getVertex(cut);
542 
543  newFace[newFaceI++] = vertI;
544 
545  label nextCut = loop[loop.fcIndex(fp)];
546 
547  if (!isEdge(nextCut))
548  {
549  // From vertex to vertex -> cross cut only if no existing edge.
550  label nextVertI = getVertex(nextCut);
551 
552  label edgeI = meshTools::findEdge(mesh(), vertI, nextVertI);
553 
554  if (edgeI != -1)
555  {
556  // Existing edge. Insert split-edge point if any.
557  HashTable<label, edge, Hash<edge> >::const_iterator fnd =
558  addedPoints_.find(mesh().edges()[edgeI]);
559 
560  if (fnd != addedPoints_.end())
561  {
562  newFace[newFaceI++] = fnd();
563  }
564  }
565  }
566  }
567  }
568  newFace.setSize(newFaceI);
569 
570  return newFace;
571 }
572 
573 
574 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
575 
576 // Construct from components
578 :
579  edgeVertex(mesh),
580  addedFaces_(),
581  addedPoints_()
582 {}
583 
584 
585 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
586 
588 (
589  const label exposedPatchI,
590  const cellCuts& cuts,
591  const labelList& cutPatch,
592  polyTopoChange& meshMod
593 )
594 {
595  // Clear and size maps here since mesh size will change.
596  addedFaces_.clear();
597  addedFaces_.resize(cuts.nLoops());
598 
599  addedPoints_.clear();
600  addedPoints_.resize(cuts.nLoops());
601 
602  if (cuts.nLoops() == 0)
603  {
604  return;
605  }
606 
607  const labelListList& anchorPts = cuts.cellAnchorPoints();
608  const labelListList& cellLoops = cuts.cellLoops();
610 
611  if (exposedPatchI < 0 || exposedPatchI >= patches.size())
612  {
614  << "Illegal exposed patch " << exposedPatchI
615  << abort(FatalError);
616  }
617 
618 
619  //
620  // Add new points along cut edges.
621  //
622 
623  forAll(cuts.edgeIsCut(), edgeI)
624  {
625  if (cuts.edgeIsCut()[edgeI])
626  {
627  const edge& e = mesh().edges()[edgeI];
628 
629  // Check if there is any cell using this edge.
630  if (debug && findCutCell(cuts, mesh().edgeCells()[edgeI]) == -1)
631  {
633  << "Problem: cut edge but none of the cells using it is\n"
634  << "edge:" << edgeI << " verts:" << e
635  << abort(FatalError);
636  }
637 
638  // One of the edge end points should be master point of nbCellI.
639  label masterPointI = e.start();
640 
641  const point& v0 = mesh().points()[e.start()];
642  const point& v1 = mesh().points()[e.end()];
643 
644  scalar weight = cuts.edgeWeight()[edgeI];
645 
646  point newPt = weight*v1 + (1.0-weight)*v0;
647 
648  label addedPointI =
649  meshMod.setAction
650  (
652  (
653  newPt, // point
654  masterPointI, // master point
655  -1, // zone for point
656  true // supports a cell
657  )
658  );
659 
660  // Store on (hash of) edge.
661  addedPoints_.insert(e, addedPointI);
662 
663  if (debug & 2)
664  {
665  Pout<< "Added point " << addedPointI
666  << " to vertex "
667  << masterPointI << " of edge " << edgeI
668  << " vertices " << e << endl;
669  }
670  }
671  }
672 
673 
674  //
675  // Remove all points that will not be used anymore
676  //
677  {
678  boolList usedPoint(mesh().nPoints(), false);
679 
680  forAll(cellLoops, cellI)
681  {
682  const labelList& loop = cellLoops[cellI];
683 
684  if (loop.size())
685  {
686  // Cell is cut. Uses only anchor points and loop itself.
687  forAll(loop, fp)
688  {
689  label cut = loop[fp];
690 
691  if (!isEdge(cut))
692  {
693  usedPoint[getVertex(cut)] = true;
694  }
695  }
696 
697  const labelList& anchors = anchorPts[cellI];
698 
699  forAll(anchors, i)
700  {
701  usedPoint[anchors[i]] = true;
702  }
703  }
704  else
705  {
706  // Cell is not cut so use all its points
707  const labelList& cPoints = mesh().cellPoints()[cellI];
708 
709  forAll(cPoints, i)
710  {
711  usedPoint[cPoints[i]] = true;
712  }
713  }
714  }
715 
716 
717  // Check
718  const Map<edge>& faceSplitCut = cuts.faceSplitCut();
719 
720  forAllConstIter(Map<edge>, faceSplitCut, iter)
721  {
722  const edge& fCut = iter();
723 
724  forAll(fCut, i)
725  {
726  label cut = fCut[i];
727 
728  if (!isEdge(cut))
729  {
730  label pointI = getVertex(cut);
731 
732  if (!usedPoint[pointI])
733  {
735  << "Problem: faceSplitCut not used by any loop"
736  << " or cell anchor point"
737  << "face:" << iter.key() << " point:" << pointI
738  << " coord:" << mesh().points()[pointI]
739  << abort(FatalError);
740  }
741  }
742  }
743  }
744 
745  forAll(cuts.pointIsCut(), pointI)
746  {
747  if (cuts.pointIsCut()[pointI])
748  {
749  if (!usedPoint[pointI])
750  {
752  << "Problem: point is marked as cut but"
753  << " not used by any loop"
754  << " or cell anchor point"
755  << "point:" << pointI
756  << " coord:" << mesh().points()[pointI]
757  << abort(FatalError);
758  }
759  }
760  }
761 
762 
763  // Remove unused points.
764  forAll(usedPoint, pointI)
765  {
766  if (!usedPoint[pointI])
767  {
768  meshMod.setAction(polyRemovePoint(pointI));
769 
770  if (debug & 2)
771  {
772  Pout<< "Removing unused point " << pointI << endl;
773  }
774  }
775  }
776  }
777 
778 
779  //
780  // For all cut cells add an internal or external face
781  //
782 
783  forAll(cellLoops, cellI)
784  {
785  const labelList& loop = cellLoops[cellI];
786 
787  if (loop.size())
788  {
789  if (cutPatch[cellI] < 0 || cutPatch[cellI] >= patches.size())
790  {
792  << "Illegal patch " << cutPatch[cellI]
793  << " provided for cut cell " << cellI
794  << abort(FatalError);
795  }
796 
797  //
798  // Convert loop (=list of cuts) into proper face.
799  // cellCuts sets orientation is towards anchor side so reverse.
800  //
801  face newFace(loopToFace(cellI, loop));
802 
803  reverse(newFace);
804 
805  // Pick any anchor point on cell
806  label masterPointI = findPatchFacePoint(newFace, exposedPatchI);
807 
808  label addedFaceI =
809  meshMod.setAction
810  (
812  (
813  newFace, // face
814  cellI, // owner
815  -1, // neighbour
816  masterPointI, // master point
817  -1, // master edge
818  -1, // master face for addition
819  false, // flux flip
820  cutPatch[cellI], // patch for face
821  -1, // zone for face
822  false // face zone flip
823  )
824  );
825 
826  addedFaces_.insert(cellI, addedFaceI);
827 
828  if (debug & 2)
829  {
830  Pout<< "Added splitting face " << newFace << " index:"
831  << addedFaceI << " from masterPoint:" << masterPointI
832  << " to owner " << cellI << " with anchors:"
833  << anchorPts[cellI]
834  << " from Loop:";
835 
836  // Gets edgeweights of loop
837  scalarField weights(loop.size());
838  forAll(loop, i)
839  {
840  label cut = loop[i];
841 
842  weights[i] =
843  (
844  isEdge(cut)
845  ? cuts.edgeWeight()[getEdge(cut)]
846  : -GREAT
847  );
848  }
849  writeCuts(Pout, loop, weights);
850  Pout<< endl;
851  }
852  }
853  }
854 
855 
856  //
857  // Modify faces to use only anchorpoints and loop points
858  // (so throw away part without anchorpoints)
859  //
860 
861 
862  // Maintain whether face has been updated (for -split edges
863  // -new owner/neighbour)
864  boolList faceUptodate(mesh().nFaces(), false);
865 
866  const Map<edge>& faceSplitCuts = cuts.faceSplitCut();
867 
868  forAllConstIter(Map<edge>, faceSplitCuts, iter)
869  {
870  label faceI = iter.key();
871 
872  // Renumber face to include split edges.
873  face newFace(addEdgeCutsToFace(faceI));
874 
875  // Edge splitting the face. Convert edge to new vertex numbering.
876  const edge& splitEdge = iter();
877 
878  label cut0 = splitEdge[0];
879 
880  label v0;
881  if (isEdge(cut0))
882  {
883  label edgeI = getEdge(cut0);
884  v0 = addedPoints_[mesh().edges()[edgeI]];
885  }
886  else
887  {
888  v0 = getVertex(cut0);
889  }
890 
891  label cut1 = splitEdge[1];
892  label v1;
893  if (isEdge(cut1))
894  {
895  label edgeI = getEdge(cut1);
896  v1 = addedPoints_[mesh().edges()[edgeI]];
897  }
898  else
899  {
900  v1 = getVertex(cut1);
901  }
902 
903  // Split face along the elements of the splitEdge.
904  face f0, f1;
905  splitFace(newFace, v0, v1, f0, f1);
906 
907  label own = mesh().faceOwner()[faceI];
908 
909  label nei = -1;
910 
911  if (mesh().isInternalFace(faceI))
912  {
913  nei = mesh().faceNeighbour()[faceI];
914  }
915 
916  if (debug & 2)
917  {
918  Pout<< "Split face " << mesh().faces()[faceI]
919  << " own:" << own << " nei:" << nei
920  << " into f0:" << f0
921  << " and f1:" << f1 << endl;
922  }
923 
924 
925  // Check which cell using face uses anchorPoints (so is kept)
926  // and which one doesn't (gets removed)
927 
928  // Bit tricky. We have to know whether this faceSplit splits owner/
929  // neighbour or both. Even if cell is cut we have to make sure this is
930  // the one that cuts it (this face cut might not be the one splitting
931  // the cell)
932  // The face f gets split into two parts, f0 and f1.
933  // Each of these can have a different owner and or neighbour.
934 
935  const face& f = mesh().faces()[faceI];
936 
937  label f0Own = -1;
938  label f1Own = -1;
939 
940  if (cellLoops[own].empty())
941  {
942  // Owner side is not split so keep both halves.
943  f0Own = own;
944  f1Own = own;
945  }
946  else if (isIn(splitEdge, cellLoops[own]))
947  {
948  // Owner is cut by this splitCut. See which of f0, f1 gets
949  // preserved and becomes owner, and which gets removed.
950  if (firstCommon(f0, anchorPts[own]) != -1)
951  {
952  // f0 preserved so f1 gets deleted
953  f0Own = own;
954  f1Own = -1;
955  }
956  else
957  {
958  f0Own = -1;
959  f1Own = own;
960  }
961  }
962  else
963  {
964  // Owner not cut by this splitCut but by another.
965  // Check on original face whether
966  // use anchorPts.
967  if (firstCommon(f, anchorPts[own]) != -1)
968  {
969  // both f0 and f1 owner side preserved
970  f0Own = own;
971  f1Own = own;
972  }
973  else
974  {
975  // both f0 and f1 owner side removed
976  f0Own = -1;
977  f1Own = -1;
978  }
979  }
980 
981 
982  label f0Nei = -1;
983  label f1Nei = -1;
984 
985  if (nei != -1)
986  {
987  if (cellLoops[nei].empty())
988  {
989  f0Nei = nei;
990  f1Nei = nei;
991  }
992  else if (isIn(splitEdge, cellLoops[nei]))
993  {
994  // Neighbour is cut by this splitCut. So anchor part of it
995  // gets kept, non-anchor bit gets removed. See which of f0, f1
996  // connects to which part.
997 
998  if (firstCommon(f0, anchorPts[nei]) != -1)
999  {
1000  f0Nei = nei;
1001  f1Nei = -1;
1002  }
1003  else
1004  {
1005  f0Nei = -1;
1006  f1Nei = nei;
1007  }
1008  }
1009  else
1010  {
1011  // neighbour not cut by this splitCut. Check on original face
1012  // whether use anchorPts.
1013 
1014  if (firstCommon(f, anchorPts[nei]) != -1)
1015  {
1016  f0Nei = nei;
1017  f1Nei = nei;
1018  }
1019  else
1020  {
1021  // both f0 and f1 on neighbour side removed
1022  f0Nei = -1;
1023  f1Nei = -1;
1024  }
1025  }
1026  }
1027 
1028 
1029  if (debug & 2)
1030  {
1031  Pout<< "f0 own:" << f0Own << " nei:" << f0Nei
1032  << " f1 own:" << f1Own << " nei:" << f1Nei
1033  << endl;
1034  }
1035 
1036 
1037  // If faces were internal but now become external set a patch.
1038  // If they were external already keep the patch.
1039  label patchID = patches.whichPatch(faceI);
1040 
1041  if (patchID == -1)
1042  {
1043  patchID = exposedPatchI;
1044  }
1045 
1046 
1047  // Do as much as possible by modifying faceI. Delay any remove
1048  // face. Keep track of whether faceI has been used.
1049 
1050  bool modifiedFaceI = false;
1051 
1052  if (f0Own == -1)
1053  {
1054  if (f0Nei != -1)
1055  {
1056  // f0 becomes external face (note:modFace will reverse face)
1057  modFace(meshMod, faceI, f0, f0Own, f0Nei, patchID);
1058  modifiedFaceI = true;
1059  }
1060  }
1061  else
1062  {
1063  if (f0Nei == -1)
1064  {
1065  // f0 becomes external face
1066  modFace(meshMod, faceI, f0, f0Own, f0Nei, patchID);
1067  modifiedFaceI = true;
1068  }
1069  else
1070  {
1071  // f0 stays internal face.
1072  modFace(meshMod, faceI, f0, f0Own, f0Nei, -1);
1073  modifiedFaceI = true;
1074  }
1075  }
1076 
1077 
1078  // f1 is added face (if at all)
1079 
1080  if (f1Own == -1)
1081  {
1082  if (f1Nei == -1)
1083  {
1084  // f1 not needed.
1085  }
1086  else
1087  {
1088  // f1 becomes external face (note:modFace will reverse face)
1089  if (!modifiedFaceI)
1090  {
1091  modFace(meshMod, faceI, f1, f1Own, f1Nei, patchID);
1092  modifiedFaceI = true;
1093  }
1094  else
1095  {
1096  label masterPointI = findPatchFacePoint(f1, patchID);
1097 
1098  addFace
1099  (
1100  meshMod,
1101  faceI, // face for zone info
1102  masterPointI, // inflation point
1103  f1, // vertices of face
1104  f1Own,
1105  f1Nei,
1106  patchID // patch for new face
1107  );
1108  }
1109  }
1110  }
1111  else
1112  {
1113  if (f1Nei == -1)
1114  {
1115  // f1 becomes external face
1116  if (!modifiedFaceI)
1117  {
1118  modFace(meshMod, faceI, f1, f1Own, f1Nei, patchID);
1119  modifiedFaceI = true;
1120  }
1121  else
1122  {
1123  label masterPointI = findPatchFacePoint(f1, patchID);
1124 
1125  addFace
1126  (
1127  meshMod,
1128  faceI,
1129  masterPointI,
1130  f1,
1131  f1Own,
1132  f1Nei,
1133  patchID
1134  );
1135  }
1136  }
1137  else
1138  {
1139  // f1 is internal face.
1140  if (!modifiedFaceI)
1141  {
1142  modFace(meshMod, faceI, f1, f1Own, f1Nei, -1);
1143  modifiedFaceI = true;
1144  }
1145  else
1146  {
1147  label masterPointI = findPatchFacePoint(f1, -1);
1148 
1149  addFace(meshMod, faceI, masterPointI, f1, f1Own, f1Nei, -1);
1150  }
1151  }
1152  }
1153 
1154  if (f0Own == -1 && f0Nei == -1 && !modifiedFaceI)
1155  {
1156  meshMod.setAction(polyRemoveFace(faceI));
1157 
1158  if (debug & 2)
1159  {
1160  Pout<< "Removed face " << faceI << endl;
1161  }
1162  }
1163 
1164  faceUptodate[faceI] = true;
1165  }
1166 
1167 
1168  //
1169  // Faces that have not been split but just appended to. Are guaranteed
1170  // to be reachable from an edgeCut.
1171  //
1172 
1173  const boolList& edgeIsCut = cuts.edgeIsCut();
1174 
1175  forAll(edgeIsCut, edgeI)
1176  {
1177  if (edgeIsCut[edgeI])
1178  {
1179  const labelList& eFaces = mesh().edgeFaces()[edgeI];
1180 
1181  forAll(eFaces, i)
1182  {
1183  label faceI = eFaces[i];
1184 
1185  if (!faceUptodate[faceI])
1186  {
1187  // So the face has not been split itself (i.e. its owner
1188  // or neighbour have not been split) so it only
1189  // borders by edge a cell which has been split.
1190 
1191  // Get (new or original) owner and neighbour of faceI
1192  label own, nei, patchID;
1193  faceCells(cuts, exposedPatchI, faceI, own, nei, patchID);
1194 
1195 
1196  if (own == -1 && nei == -1)
1197  {
1198  meshMod.setAction(polyRemoveFace(faceI));
1199 
1200  if (debug & 2)
1201  {
1202  Pout<< "Removed face " << faceI << endl;
1203  }
1204  }
1205  else
1206  {
1207  // Renumber face to include split edges.
1208  face newFace(addEdgeCutsToFace(faceI));
1209 
1210  if (debug & 2)
1211  {
1212  Pout<< "Added edge cuts to face " << faceI
1213  << " f:" << mesh().faces()[faceI]
1214  << " newFace:" << newFace << endl;
1215  }
1216 
1217  modFace
1218  (
1219  meshMod,
1220  faceI,
1221  newFace,
1222  own,
1223  nei,
1224  patchID
1225  );
1226  }
1227 
1228  faceUptodate[faceI] = true;
1229  }
1230  }
1231  }
1232  }
1233 
1234 
1235  //
1236  // Remove any faces on the non-anchor side of a split cell.
1237  // Note: could loop through all cut cells only and check their faces but
1238  // looping over all faces is cleaner and probably faster for dense
1239  // cut patterns.
1240 
1241  const faceList& faces = mesh().faces();
1242 
1243  forAll(faces, faceI)
1244  {
1245  if (!faceUptodate[faceI])
1246  {
1247  // Get (new or original) owner and neighbour of faceI
1248  label own, nei, patchID;
1249  faceCells(cuts, exposedPatchI, faceI, own, nei, patchID);
1250 
1251  if (own == -1 && nei == -1)
1252  {
1253  meshMod.setAction(polyRemoveFace(faceI));
1254 
1255  if (debug & 2)
1256  {
1257  Pout<< "Removed face " << faceI << endl;
1258  }
1259  }
1260  else
1261  {
1262  modFace(meshMod, faceI, faces[faceI], own, nei, patchID);
1263  }
1264 
1265  faceUptodate[faceI] = true;
1266  }
1267  }
1268 
1269  if (debug)
1270  {
1271  Pout<< "meshCutAndRemove:" << nl
1272  << " cells split:" << cuts.nLoops() << nl
1273  << " faces added:" << addedFaces_.size() << nl
1274  << " points added on edges:" << addedPoints_.size() << nl
1275  << endl;
1276  }
1277 }
1278 
1279 
1281 {
1282  // Update stored labels for mesh change.
1283  {
1284  Map<label> newAddedFaces(addedFaces_.size());
1285 
1286  forAllConstIter(Map<label>, addedFaces_, iter)
1287  {
1288  label cellI = iter.key();
1289  label newCellI = map.reverseCellMap()[cellI];
1290 
1291  label addedFaceI = iter();
1292 
1293  label newAddedFaceI = map.reverseFaceMap()[addedFaceI];
1294 
1295  if ((newCellI >= 0) && (newAddedFaceI >= 0))
1296  {
1297  if
1298  (
1299  (debug & 2)
1300  && (newCellI != cellI || newAddedFaceI != addedFaceI)
1301  )
1302  {
1303  Pout<< "meshCutAndRemove::updateMesh :"
1304  << " updating addedFace for cell " << cellI
1305  << " from " << addedFaceI
1306  << " to " << newAddedFaceI
1307  << endl;
1308  }
1309  newAddedFaces.insert(newCellI, newAddedFaceI);
1310  }
1311  }
1312 
1313  // Copy
1314  addedFaces_.transfer(newAddedFaces);
1315  }
1316 
1317  {
1318  HashTable<label, edge, Hash<edge> > newAddedPoints(addedPoints_.size());
1319 
1320  for
1321  (
1322  HashTable<label, edge, Hash<edge> >::const_iterator iter =
1323  addedPoints_.begin();
1324  iter != addedPoints_.end();
1325  ++iter
1326  )
1327  {
1328  const edge& e = iter.key();
1329 
1330  label newStart = map.reversePointMap()[e.start()];
1331 
1332  label newEnd = map.reversePointMap()[e.end()];
1333 
1334  label addedPointI = iter();
1335 
1336  label newAddedPointI = map.reversePointMap()[addedPointI];
1337 
1338  if ((newStart >= 0) && (newEnd >= 0) && (newAddedPointI >= 0))
1339  {
1340  edge newE = edge(newStart, newEnd);
1341 
1342  if
1343  (
1344  (debug & 2)
1345  && (e != newE || newAddedPointI != addedPointI)
1346  )
1347  {
1348  Pout<< "meshCutAndRemove::updateMesh :"
1349  << " updating addedPoints for edge " << e
1350  << " from " << addedPointI
1351  << " to " << newAddedPointI
1352  << endl;
1353  }
1354 
1355  newAddedPoints.insert(newE, newAddedPointI);
1356  }
1357  }
1358 
1359  // Copy
1360  addedPoints_.transfer(newAddedPoints);
1361  }
1362 }
1363 
1364 
1365 // ************************************************************************* //
Foam::polyMesh::points
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:979
meshTools.H
Foam::face::reverseFace
face reverseFace() const
Return face with reverse direction.
Definition: face.C:611
Foam::cellCuts::nLoops
label nLoops() const
Number of valid cell loops.
Definition: cellCuts.H:575
Foam::cellCuts::pointIsCut
const boolList & pointIsCut() const
Is mesh point cut.
Definition: cellCuts.H:534
polyRemovePoint.H
Foam::cellCuts::faceSplitCut
const Map< edge > & faceSplitCut() const
Gives for split face the two cuts that split the face into two.
Definition: cellCuts.H:563
Foam::polyBoundaryMesh
Foam::polyBoundaryMesh.
Definition: polyBoundaryMesh.H:60
Foam::polyRemovePoint
Class containing data for point removal.
Definition: polyRemovePoint.H:46
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:406
Foam::primitiveMesh::cellPoints
const labelListList & cellPoints() const
Definition: primitiveMeshCellPoints.C:31
Foam::findIndex
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurence of given element and return index,.
polyAddFace.H
Foam::primitiveMesh::edgeFaces
const labelListList & edgeFaces() const
Definition: primitiveMeshEdgeFaces.C:31
Foam::meshCutAndRemove::findCutCell
label findCutCell(const cellCuts &, const labelList &) const
Returns -1 or the cell in cellLabels that is cut.
Definition: meshCutAndRemove.C:94
polyRemoveFace.H
mapPolyMesh.H
polyTopoChange.H
Foam::polyTopoChange
Direct mesh changes based on v1.3 polyTopoChange syntax.
Definition: polyTopoChange.H:97
Foam::meshCutAndRemove::addEdgeCutsToFace
face addEdgeCutsToFace(const label faceI) const
Add cuts of edges to face.
Definition: meshCutAndRemove.C:479
Foam::edge
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:58
Foam::edgeVertex
Combines edge or vertex in single label. Used to specify cuts across cell circumference.
Definition: edgeVertex.H:52
Foam::HashTable::insert
bool insert(const Key &, const T &newElmt)
Insert a new hashedEntry.
Definition: HashTableI.H:80
Foam::Map
A HashTable to objects of type <T> with a label key.
Definition: PrimitivePatchTemplate.H:68
Foam::polyModifyFace
Class describing modification of a face.
Definition: polyModifyFace.H:48
Foam::polyMesh::boundaryMesh
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:421
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
Foam::cellCuts::cellAnchorPoints
const labelListList & cellAnchorPoints() const
For each cut cell the points on the 'anchor' side of the cell.
Definition: cellCuts.H:581
Foam::primitiveMesh::edges
const edgeList & edges() const
Return mesh edges. Uses calcEdges.
Definition: primitiveMeshEdges.C:353
polyMesh.H
Foam::polyMesh
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:74
meshCutAndRemove.H
forAllConstIter
forAllConstIter(PtrDictionary< phaseModel >, mixture.phases(), phase)
Definition: pEqn.H:39
Foam::meshCutAndRemove::loopToFace
face loopToFace(const label cellI, const labelList &loop) const
Convert loop of cuts into face.
Definition: meshCutAndRemove.C:515
pFaces
Info<< "Finished reading KIVA file"<< endl;cellShapeList cellShapes(nPoints);labelList cellZoning(nPoints, -1);const cellModel &hex=*(cellModeller::lookup("hex"));labelList hexLabels(8);label activeCells=0;labelList pointMap(nPoints);forAll(pointMap, i){ pointMap[i]=i;}for(label i=0;i< nPoints;i++){ if(f[i] > 0.0) { hexLabels[0]=i;hexLabels[1]=i1tab[i];hexLabels[2]=i3tab[i1tab[i]];hexLabels[3]=i3tab[i];hexLabels[4]=i8tab[i];hexLabels[5]=i1tab[i8tab[i]];hexLabels[6]=i3tab[i1tab[i8tab[i]]];hexLabels[7]=i3tab[i8tab[i]];cellShapes[activeCells]=cellShape(hex, hexLabels);edgeList edges=cellShapes[activeCells].edges();forAll(edges, ei) { if(edges[ei].mag(points)< SMALL) { label start=pointMap[edges[ei].start()];while(start !=pointMap[start]) { start=pointMap[start];} label end=pointMap[edges[ei].end()];while(end !=pointMap[end]) { end=pointMap[end];} label minLabel=min(start, end);pointMap[start]=pointMap[end]=minLabel;} } cellZoning[activeCells]=idreg[i];activeCells++;}}cellShapes.setSize(activeCells);cellZoning.setSize(activeCells);forAll(cellShapes, celli){ cellShape &cs=cellShapes[celli];forAll(cs, i) { cs[i]=pointMap[cs[i]];} cs.collapse();}label bcIDs[11]={-1, 0, 2, 4, -1, 5, -1, 6, 7, 8, 9};const label nBCs=12;const word *kivaPatchTypes[nBCs]={ &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &symmetryPolyPatch::typeName, &wedgePolyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &symmetryPolyPatch::typeName, &oldCyclicPolyPatch::typeName};enum patchTypeNames{ PISTON, VALVE, LINER, CYLINDERHEAD, AXIS, WEDGE, INFLOW, OUTFLOW, PRESIN, PRESOUT, SYMMETRYPLANE, CYCLIC};const char *kivaPatchNames[nBCs]={ "piston", "valve", "liner", "cylinderHead", "axis", "wedge", "inflow", "outflow", "presin", "presout", "symmetryPlane", "cyclic"};List< SLList< face > > pFaces[nBCs]
Definition: readKivaGrid.H:235
nPoints
label nPoints
Definition: gmvOutputHeader.H:2
Foam::meshCutAndRemove::faceCells
void faceCells(const cellCuts &cuts, const label exposedPatchI, const label faceI, label &own, label &nei, label &patchID) const
Get new owner and neighbour of face. Checks anchor points to see if.
Definition: meshCutAndRemove.C:183
Foam::cellCuts::edgeWeight
const scalarField & edgeWeight() const
If edge is cut gives weight (ratio between start() and end())
Definition: cellCuts.H:546
Foam::meshCutAndRemove::addedPoints_
HashTable< label, edge, Hash< edge > > addedPoints_
Points added in last setRefinement. Per split edge label of added.
Definition: meshCutAndRemove.H:72
Foam::meshCutAndRemove::isIn
static bool isIn(const edge &, const labelList &)
Do the elements of edge appear in consecutive order in the list.
Definition: meshCutAndRemove.C:70
Foam::Hash
Hash function class for primitives. All non-primitives used to hash entries on hash tables likely nee...
Definition: Hash.H:56
Foam::label
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
Foam::meshTools::findEdge
label findEdge(const edgeList &edges, const labelList &candidates, const label v0, const label v1)
Return edge among candidates that uses the two vertices.
Definition: meshTools.C:336
Foam::Field
Pre-declare SubField and related Field type.
Definition: Field.H:57
Foam::faceZone
A subset of mesh faces organised as a primitive patch.
Definition: faceZone.H:64
Foam::meshCutAndRemove::getZoneInfo
void getZoneInfo(const label faceI, label &zoneID, bool &zoneFlip) const
Get zone information for face.
Definition: meshCutAndRemove.C:228
Foam::nl
static const char nl
Definition: Ostream.H:260
Foam::polyMesh::faceOwner
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1017
f1
scalar f1
Definition: createFields.H:28
Foam::cellCuts::cellLoops
const labelListList & cellLoops() const
For each cut cell the cut along the circumference.
Definition: cellCuts.H:569
polyAddPoint.H
Foam::meshCutAndRemove::meshCutAndRemove
meshCutAndRemove(const meshCutAndRemove &)
Disallow default bitwise copy construct.
cut
Patchify triangles based on orientation w.r.t other (triangulated or triangulatable) surfaces.
cellCuts.H
Foam::FatalError
error FatalError
mesh
dynamicFvMesh & mesh
Definition: createDynamicFvMesh.H:18
Foam
Namespace for OpenFOAM.
Definition: combustionModel.C:30
Foam::faceZone::whichFace
label whichFace(const label globalCellID) const
Helper function to re-direct to zone::localID(...)
Definition: faceZone.C:314
Foam::abort
errorManip< error > abort(error &err)
Definition: errorManip.H:131
Foam::e
const double e
Elementary charge.
Definition: doubleFloat.H:94
Foam::meshCutAndRemove::updateMesh
void updateMesh(const mapPolyMesh &)
Force recalculation of locally stored data on topological change.
Definition: meshCutAndRemove.C:1280
Foam::HashTable::find
iterator find(const Key &)
Find and return an iterator set at the hashedEntry.
polyModifyFace.H
Foam::HashTable
An STL-conforming hash table.
Definition: HashTable.H:61
Foam::mapPolyMesh::reverseFaceMap
const labelList & reverseFaceMap() const
Reverse face map.
Definition: mapPolyMesh.H:497
Foam::List::setSize
void setSize(const label)
Reset size of List.
Foam::meshCutAndRemove::findPatchFacePoint
label findPatchFacePoint(const face &f, const label patchI) const
Find point on face that is part of original mesh and that is.
Definition: meshCutAndRemove.C:151
Foam::mapPolyMesh::reverseCellMap
const labelList & reverseCellMap() const
Reverse cell map.
Definition: mapPolyMesh.H:528
Foam::polyMesh::faces
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1004
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:318
Foam::polyTopoChange::setAction
label setAction(const topoAction &action)
For compatibility with polyTopoChange: set topological action.
Definition: polyTopoChange.C:2530
Foam::meshCutAndRemove::setRefinement
void setRefinement(const label exposedPatchI, const cellCuts &cuts, const labelList &cutPatch, polyTopoChange &meshMod)
Do actual cutting with cut description. Inserts mesh changes.
Definition: meshCutAndRemove.C:588
Foam::Pout
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
f
labelList f(nPoints)
Foam::mapPolyMesh::reversePointMap
const labelList & reversePointMap() const
Reverse point map.
Definition: mapPolyMesh.H:465
Foam::Vector< scalar >
Foam::polyRemoveFace
Class containing data for face removal.
Definition: polyRemoveFace.H:46
Foam::List
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
Foam::cellCuts::edgeIsCut
const boolList & edgeIsCut() const
Is edge cut.
Definition: cellCuts.H:540
Foam::meshCutAndRemove::addFace
void addFace(polyTopoChange &meshMod, const label faceI, const label masterPointI, const face &newFace, const label owner, const label neighbour, const label patchID)
Adds a face from point. Flips face if owner>neighbour.
Definition: meshCutAndRemove.C:249
Foam::polyAddPoint
Class containing data for point addition.
Definition: polyAddPoint.H:47
Foam::mapPolyMesh
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
Definition: mapPolyMesh.H:158
Foam::polyAddFace
A face addition data class. A face can be inflated either from a point or from another face and can e...
Definition: polyAddFace.H:51
patches
patches[0]
Definition: createSingleCellMesh.H:36
Foam::face
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:75
Foam::List::size
void size(const label)
Override size to be inconsistent with allocated storage.
Foam::meshCutAndRemove::splitFace
void splitFace(const face &f, const label v0, const label v1, face &f0, face &f1) const
Split face along cut into two faces. Faces are in same point.
Definition: meshCutAndRemove.C:438
Foam::faceZone::flipMap
const boolList & flipMap() const
Return face flip map.
Definition: faceZone.H:249
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(combustionModel, 0)
Foam::meshCutAndRemove::modFace
void modFace(polyTopoChange &meshMod, const label faceI, const face &newFace, const label owner, const label neighbour, const label patchID)
Modifies existing faceI for either new owner/neighbour or.
Definition: meshCutAndRemove.C:333
pointLabels
labelList pointLabels(nPoints, -1)
Foam::polyMesh::faceNeighbour
virtual const labelList & faceNeighbour() const
Return face neighbour.
Definition: polyMesh.C:1023
Foam::meshCutAndRemove::findInternalFacePoint
label findInternalFacePoint(const labelList &pointLabels) const
Returns first pointI in pointLabels that uses an internal.
Definition: meshCutAndRemove.C:117
Foam::meshCutAndRemove::copyFace
void copyFace(const face &f, const label startFp, const label endFp, face &newFace) const
Definition: meshCutAndRemove.C:412
Foam::meshCutAndRemove::firstCommon
static label firstCommon(const labelList &lst1, const labelList &lst2)
Definition: meshCutAndRemove.C:50
Foam::cellCuts
Description of cuts across cells.
Definition: cellCuts.H:108
Foam::labelI
static const labelSphericalTensor labelI(1)
Identity labelTensor.
Foam::edgeVertex::mesh
const polyMesh & mesh() const
Definition: edgeVertex.H:98
Foam::reverse
void reverse(UList< T > &, const label n)
Definition: UListI.H:322