enrichedPatchCutFaces.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-2016 OpenFOAM Foundation
9  Copyright (C) 2017-2022 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 Description
28  Calculating cut faces of the enriched patch, together with the addressing
29  into master and slave patch.
30 
31 \*---------------------------------------------------------------------------*/
32 
33 #include "enrichedPatch.H"
34 #include "boolList.H"
35 #include "CircularBuffer.H"
36 #include "DynamicList.H"
37 #include "labelPair.H"
38 #include "primitiveMesh.H"
39 #include "edgeHashes.H"
40 
41 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
42 
43 // If the cut face gets more than this number of points, it will be checked
44 const Foam::label Foam::enrichedPatch::maxFaceSizeDebug_ = 100;
45 
46 
47 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
48 
49 // Index of debug signs:
50 // x - skip a point
51 // l - left turn
52 // r - right turn
53 
54 void Foam::enrichedPatch::calcCutFaces() const
55 {
56  if (cutFacesPtr_ || cutFaceMasterPtr_ || cutFaceSlavePtr_)
57  {
59  << "Cut faces addressing already calculated."
60  << abort(FatalError);
61  }
62 
63  const faceList& enFaces = enrichedFaces();
64  const labelList& mp = meshPoints();
65  const faceList& lf = localFaces();
66  const pointField& lp = localPoints();
67  const labelListList& pp = pointPoints();
68  // Pout<< "enFaces: " << enFaces << endl;
69  // Pout<< "lf: " << lf << endl;
70  // Pout<< "lp: " << lp << endl;
71  // Pout<< "pp: " << pp << endl;
72  const Map<labelList>& masterPointFaceAddr = masterPointFaces();
73 
74  // Prepare the storage
75  DynamicList<face> cf(2*lf.size());
76  DynamicList<label> cfMaster(2*lf.size());
77  DynamicList<label> cfSlave(2*lf.size());
78 
79  // Algorithm
80  // Go through all the faces
81  // 1) For each face, start at point zero and grab the edge zero.
82  // Both points are added into the cut face. Mark the first edge
83  // as used and position the current point as the end of the first
84  // edge and previous point as point zero.
85  // 2) Grab all possible points from the current point. Excluding
86  // the previous point find the point giving the biggest right
87  // turn. Add the point to the face and mark the edges as used.
88  // Continue doing this until the face is complete, i.e. the next point
89  // to add is the first point of the face.
90  // 3) Once the facet is completed, register its owner from the face
91  // it has been created from (remember that the first lot of faces
92  // inserted into the enriched faces list are the slaves, to allow
93  // matching of the other side).
94  // 4) If the facet is created from an original slave face, go
95  // through the master patch and try to identify the master face
96  // this facet belongs to. This is recognised by the fact that the
97  // faces consists exclusively of the points of the master face and
98  // the points projected onto the face.
99 
100  // Create a set of edge usage parameters
101  edgeHashSet edgesUsedOnce(pp.size());
102  edgeHashSet edgesUsedTwice(pp.size()*primitiveMesh::edgesPerPoint_);
103 
104 
105  DynamicList<bool> usedFaceEdges(256);
106  CircularBuffer<edge> edgeSeeds(256);
107 
108  forAll(lf, facei)
109  {
110  const face& curLocalFace = lf[facei];
111  const face& curGlobalFace = enFaces[facei];
112 
113  // Pout<< "Doing face " << facei
114  // << " local: " << curLocalFace
115  // << " or " << curGlobalFace
116  // << endl;
117 
118  // if (facei < slavePatch_.size())
119  // {
120  // Pout<< "original slave: " << slavePatch_[facei]
121  // << " local: " << slavePatch_.localFaces()[facei] << endl;
122  // }
123  // else
124  // {
125  // Pout<< "original master: "
126  // << masterPatch_[facei - slavePatch_.size()] << " "
127  // << masterPatch_.localFaces()[facei - slavePatch_.size()]
128  // << endl;
129  // }
130  // {
131  // pointField facePoints = curLocalFace.points(lp);
132  // forAll(curLocalFace, pointi)
133  // {
134  // Pout<< "v " << facePoints[pointi].x() << " "
135  // << facePoints[pointi].y() << " "
136  // << facePoints[pointi].z() << endl;
137  // }
138  // }
139 
140  // Track the usage of face edges. When all edges are used, the
141  // face decomposition is complete.
142  // The seed edges include all the edges of the original face + all edges
143  // of other faces that have been used in the construction of the
144  // facet. Edges from other faces can be considered as
145  // internal to the current face if used only once.
146 
147  // Track the edge usage to avoid duplicate faces and reset it to unused
148  usedFaceEdges.resize_nocopy(curLocalFace.size());
149  usedFaceEdges = false;
150 
151  // Add edges of current face into the seed list.
152  edgeSeeds.clear();
153  edgeSeeds.push_back(curLocalFace.edges());
154 
155  // Grab face normal
156  const vector normal = curLocalFace.unitNormal(lp);
157 
158  while (!edgeSeeds.empty())
159  {
160  // Pout<< "edgeSeeds.size(): " << edgeSeeds.size() << endl;
161 
162  const edge curEdge = edgeSeeds.front();
163  edgeSeeds.pop_front();
164 
165  // Locate the edge in current face
166  const label curEdgeWhich = curLocalFace.which(curEdge.start());
167 
168  // Check if the edge is in current face and if it has been
169  // used already. If so, skip it
170  if
171  (
172  curEdgeWhich > -1
173  && curLocalFace.nextLabel(curEdgeWhich) == curEdge.end()
174  )
175  {
176  // Edge is in the starting face.
177  // If unused, mark it as used; if used, skip it
178  if (usedFaceEdges[curEdgeWhich]) continue;
179 
180  usedFaceEdges[curEdgeWhich] = true;
181  }
182 
183  // If the edge has already been used twice, skip it
184  if (edgesUsedTwice.found(curEdge)) continue;
185 
186  // Pout<< "Trying new edge (" << mp[curEdge.start()]
187  // << ", " << mp[curEdge.end()]
188  // << ") seed: " << curEdge
189  // << " used: " << edgesUsedTwice.found(curEdge)
190  // << endl;
191 
192  // Estimate the size of cut face as twice the size of original face
193  DynamicList<label> cutFaceGlobalPoints(2*curLocalFace.size());
194  DynamicList<label> cutFaceLocalPoints(2*curLocalFace.size());
195 
196  // Found unused edge.
197  label prevPointLabel = curEdge.start();
198  cutFaceGlobalPoints.append(mp[prevPointLabel]);
199  cutFaceLocalPoints.append(prevPointLabel);
200  // Pout<< "prevPointLabel: " << mp[prevPointLabel] << endl;
201 
202  // Grab current point and append it to the list
203  label curPointLabel = curEdge.end();
204  point curPoint = lp[curPointLabel];
205  cutFaceGlobalPoints.append(mp[curPointLabel]);
206  cutFaceLocalPoints.append(curPointLabel);
207 
208  bool completedCutFace = false;
209 
210  label faceSizeDebug = maxFaceSizeDebug_;
211 
212  do
213  {
214  // Grab the next point options
215 
216  // Pout<< "curPointLabel: " << mp[curPointLabel] << endl;
217 
218  const labelList& nextPoints = pp[curPointLabel];
219 
220  // Pout<< "nextPoints: "
221  // << labelUIndList(mp, nextPoints)
222  // << endl;
223 
224  // Get the vector along the edge and the right vector
225  vector ahead = curPoint - lp[prevPointLabel];
226  ahead.removeCollinear(normal);
227  ahead.normalise();
228 
229  const vector right = normalised(normal ^ ahead);
230 
231  // Pout<< "normal: " << normal
232  // << " ahead: " << ahead
233  // << " right: " << right
234  // << endl;
235 
236  scalar atanTurn = -GREAT;
237  label bestAtanPoint = -1;
238 
239  forAll(nextPoints, nextI)
240  {
241  // Exclude the point we are coming from; there will always
242  // be more than one edge, so this is safe
243  if (nextPoints[nextI] != prevPointLabel)
244  {
245  // Pout<< "cur point: " << curPoint
246  // << " trying for point: "
247  // << mp[nextPoints[nextI]]
248  // << " " << lp[nextPoints[nextI]];
249  vector newDir = lp[nextPoints[nextI]] - curPoint;
250  // Pout<< " newDir: " << newDir
251  // << " mag: " << mag(newDir) << flush;
252  newDir.removeCollinear(normal);
253  scalar magNewDir = mag(newDir);
254  // Pout<< " corrected: " << newDir
255  // << " mag: " << mag(newDir) << flush;
256 
257  if (magNewDir < SMALL)
258  {
260  << "projection error: slave patch probably "
261  << "does not project onto master. "
262  << "Please switch on "
263  << "enriched patch debug for more info"
264  << abort(FatalError);
265  }
266 
267  newDir /= magNewDir;
268 
269  const scalar curAtanTurn =
270  atan2(newDir & right, newDir & ahead);
271 
272  // Pout<< " atan: " << curAtanTurn << endl;
273 
274  if (curAtanTurn > atanTurn)
275  {
276  bestAtanPoint = nextPoints[nextI];
277  atanTurn = curAtanTurn;
278  }
279  } // end of prev point skip
280  } // end of next point selection
281 
282  // Pout<< " bestAtanPoint: " << bestAtanPoint << " or "
283  // << mp[bestAtanPoint]
284  // << endl;
285 
286  // Selected next best point.
287 
288  // Pout<< "cutFaceGlobalPoints: "
289  // << cutFaceGlobalPoints
290  // << endl;
291 
292  // Check if the edge about to be added has been used
293  // in the current face or twice in other faces. If
294  // so, the face is bad.
295  const label whichNextPoint = curLocalFace.which(curPointLabel);
296 
297  if
298  (
299  whichNextPoint > -1
300  && curLocalFace.nextLabel(whichNextPoint) == bestAtanPoint
301  && usedFaceEdges[whichNextPoint]
302  )
303  {
304  // This edge is already used in current face
305  // face cannot be good; start on a new one
306 
307  // Pout<< "Double usage in current face, cannot be good"
308  // << endl;
309 
310  completedCutFace = true;
311  }
312 
313 
314  if (edgesUsedTwice.found(edge(curPointLabel, bestAtanPoint)))
315  {
316  // This edge is already used -
317  // face cannot be good; start on a new one
318  completedCutFace = true;
319 
320  // Pout<< "Double usage elsewhere, cannot be good" << endl;
321  }
322 
323  if (completedCutFace) continue;
324 
325  // Insert the next best point into the list
326  if (mp[bestAtanPoint] == cutFaceGlobalPoints[0])
327  {
328  // Face is completed. Save it and move on to the next
329  // available edge
330  completedCutFace = true;
331 
332  if (debug)
333  {
334  Pout<< " local: " << cutFaceLocalPoints
335  << " one side: " << facei;
336  }
337 
338  // Append the face
339  face cutFaceGlobal;
340  cutFaceGlobal.transfer(cutFaceGlobalPoints);
341 
342  cf.append(cutFaceGlobal);
343 
344  face cutFaceLocal;
345  cutFaceLocal.transfer(cutFaceLocalPoints);
346 
347  // Pout<< "\ncutFaceLocal: "
348  // << cutFaceLocal.points(lp)
349  // << endl;
350 
351  // Go through all edges of the cut faces.
352  // If the edge corresponds to a starting face edge,
353  // mark the starting face edge as true
354 
355  forAll(cutFaceLocal, cuti)
356  {
357  const edge curCutFaceEdge
358  (
359  cutFaceLocal[cuti],
360  cutFaceLocal.nextLabel(cuti)
361  );
362 
363  // Increment the usage count using two hash sets
364  auto euoIter = edgesUsedOnce.find(curCutFaceEdge);
365 
366  if (!euoIter.good())
367  {
368  // Pout<< "Found edge not used before: "
369  // << curCutFaceEdge
370  // << endl;
371  edgesUsedOnce.insert(curCutFaceEdge);
372  }
373  else
374  {
375  // Pout<< "Found edge used once: "
376  // << curCutFaceEdge
377  // << endl;
378  edgesUsedOnce.erase(euoIter);
379  edgesUsedTwice.insert(curCutFaceEdge);
380  }
381 
382  const label curCutFaceEdgeWhich = curLocalFace.which
383  (
384  curCutFaceEdge.start()
385  );
386 
387  if
388  (
389  curCutFaceEdgeWhich > -1
390  && curLocalFace.nextLabel(curCutFaceEdgeWhich)
391  == curCutFaceEdge.end()
392  )
393  {
394  // Found edge in original face
395 
396  // Pout<< "Found edge in orig face: "
397  // << curCutFaceEdge << ": "
398  // << curCutFaceEdgeWhich
399  // << endl;
400 
401  usedFaceEdges[curCutFaceEdgeWhich] = true;
402  }
403  else
404  {
405  // Edge not in original face. Add it to seeds
406 
407  // Pout<< "Found new edge seed: "
408  // << curCutFaceEdge
409  // << endl;
410 
411  edgeSeeds.push_back(curCutFaceEdge.reverseEdge());
412  }
413  }
414 
415  // Find out what the other side is
416 
417  // Algorithm
418 
419  // If the face is in the slave half of the
420  // enrichedFaces lists, it may be matched against
421  // the master face. It can be recognised by the
422  // fact that all its points belong to one master
423  // face. For this purpose:
424  // 1) Grab the master faces around the point zero
425  // of the cut face and collect all master faces
426  // around it.
427  // 2) Loop through the rest of cut face points and
428  // if the point refers to one of the faces marked
429  // by point zero, increment its count.
430  // 3) When completed, the face whose count is
431  // equal to the number of points in the cut face
432  // is the other side. If this is not the case, there is no
433  // face on the other side.
434 
435  if (facei < slavePatch_.size())
436  {
437  const auto mpfAddrIter =
438  masterPointFaceAddr.cfind(cutFaceGlobal[0]);
439 
440  bool otherSideFound = false;
441 
442  if (mpfAddrIter.good())
443  {
444  bool miss = false;
445 
446  // Create the label count using point zero
447  const labelList& masterFacesOfPZero = mpfAddrIter();
448 
449  labelList hits(masterFacesOfPZero.size(), 1);
450 
451  for
452  (
453  label pointi = 1;
454  pointi < cutFaceGlobal.size();
455  pointi++
456  )
457  {
458  const auto mpfAddrPointIter =
459  masterPointFaceAddr.cfind
460  (
461  cutFaceGlobal[pointi]
462  );
463 
464  if (!mpfAddrPointIter.good())
465  {
466  // Point is off the master patch. Skip
467  miss = true;
468  break;
469  }
470 
471  const labelList& curMasterFaces =
472  mpfAddrPointIter();
473 
474  // For every current face, try to find it in the
475  // zero-list
476  for (const label facei : curMasterFaces)
477  {
478  forAll(masterFacesOfPZero, j)
479  {
480  if (facei == masterFacesOfPZero[j])
481  {
482  hits[j]++;
483  break;
484  }
485  }
486  }
487  }
488 
489  // If all point are found attempt matching
490  if (!miss)
491  {
492  forAll(hits, pointi)
493  {
494  if (hits[pointi] == cutFaceGlobal.size())
495  {
496  // Found other side.
497  otherSideFound = true;
498 
499  cfMaster.append
500  (
501  masterFacesOfPZero[pointi]
502  );
503 
504  cfSlave.append(facei);
505 
506  // Reverse the face such that it
507  // points out of the master patch
508  cf.last().flip();
509 
510  if (debug)
511  {
512  Pout<< " other side: "
513  << masterFacesOfPZero[pointi]
514  << endl;
515  }
516  } // end of hits
517  } // end of for all hits
518 
519  } // end of not miss
520 
521  if (!otherSideFound || miss)
522  {
523  if (debug)
524  {
525  Pout<< " solo slave A" << endl;
526  }
527 
528  cfMaster.append(-1);
529  cfSlave.append(facei);
530  }
531  }
532  else
533  {
534  // First point not in master patch
535  if (debug)
536  {
537  Pout<< " solo slave B" << endl;
538  }
539 
540  cfMaster.append(-1);
541  cfSlave.append(facei);
542  }
543  }
544  else
545  {
546  if (debug)
547  {
548  Pout<< " master side" << endl;
549  }
550 
551  cfMaster.append(facei - slavePatch_.size());
552  cfSlave.append(-1);
553  }
554  }
555  else
556  {
557  // Face not completed. Prepare for the next point search
558  prevPointLabel = curPointLabel;
559  curPointLabel = bestAtanPoint;
560  curPoint = lp[curPointLabel];
561  cutFaceGlobalPoints.append(mp[curPointLabel]);
562  cutFaceLocalPoints.append(curPointLabel);
563 
564  if (debug || cutFaceGlobalPoints.size() > faceSizeDebug)
565  {
566  faceSizeDebug *= 2;
567 
568  // Check for duplicate points in the face
569  forAll(cutFaceGlobalPoints, checkI)
570  {
571  for
572  (
573  label checkJ = checkI + 1;
574  checkJ < cutFaceGlobalPoints.size();
575  checkJ++
576  )
577  {
578  if
579  (
580  cutFaceGlobalPoints[checkI]
581  == cutFaceGlobalPoints[checkJ]
582  )
583  {
584  // Shrink local points for debugging
585  cutFaceLocalPoints.shrink();
586 
587  face origFace;
588  face origFaceLocal;
589  if (facei < slavePatch_.size())
590  {
591  origFace = slavePatch_[facei];
592  origFaceLocal =
593  slavePatch_.localFaces()[facei];
594  }
595  else
596  {
597  origFace =
598  masterPatch_
599  [facei - slavePatch_.size()];
600 
601  origFaceLocal =
602  masterPatch_.localFaces()
603  [facei - slavePatch_.size()];
604  }
605 
607  << "Duplicate point found in cut face. "
608  << "Error in the face cutting "
609  << "algorithm for global face "
610  << origFace << " local face "
611  << origFaceLocal << nl
612  << "Slave size: " << slavePatch_.size()
613  << " Master size: "
614  << masterPatch_.size()
615  << " index: " << facei << ".\n"
616  << "Face: " << curGlobalFace << nl
617  << "Cut face: " << cutFaceGlobalPoints
618  << " local: " << cutFaceLocalPoints
619  << nl << "Points: "
620  << face(cutFaceLocalPoints).points(lp)
621  << abort(FatalError);
622  }
623  }
624  }
625  } // end of debug
626  }
627  } while (!completedCutFace);
628  } // end of used edges
629 
630  if (debug)
631  {
632  Pout<< " Finished face " << facei << endl;
633  }
634 
635  } // end of local faces
636 
637  // Re-pack lists into compact storage
638  cutFacesPtr_.reset(new faceList(std::move(cf)));
639  cutFaceMasterPtr_.reset(new labelList(std::move(cfMaster)));
640  cutFaceSlavePtr_.reset(new labelList(std::move(cfSlave)));
641 }
642 
643 
644 void Foam::enrichedPatch::clearCutFaces()
645 {
646  cutFacesPtr_.reset(nullptr);
647  cutFaceMasterPtr_.reset(nullptr);
648  cutFaceSlavePtr_.reset(nullptr);
649 }
650 
651 
652 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
653 
655 {
656  if (!cutFacesPtr_)
657  {
658  calcCutFaces();
659  }
660 
661  return *cutFacesPtr_;
662 }
663 
664 
666 {
667  if (!cutFaceMasterPtr_)
668  {
669  calcCutFaces();
670  }
671 
672  return *cutFaceMasterPtr_;
673 }
674 
675 
677 {
678  if (!cutFaceSlavePtr_)
679  {
680  calcCutFaces();
681  }
682 
683  return *cutFaceSlavePtr_;
684 }
685 
686 
687 // ************************************************************************* //
const List< face_type > & localFaces() const
Return patch faces addressing into local point list.
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
const labelList & cutFaceMaster() const
Return cut face master list.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
HashSet< edge, Hash< edge > > edgeHashSet
A HashSet with edge for its key. Hashing (and ==) on an edge is symmetric.
Definition: edgeHashes.H:48
iterator end() noexcept
Return an iterator to end of VectorSpace.
Definition: VectorSpaceI.H:199
const faceList & cutFaces() const
Return list of cut faces.
Vector< Cmpt > & normalise(const scalar tol=ROOTVSMALL)
Inplace normalise the vector by its magnitude.
Definition: VectorI.H:114
static const unsigned edgesPerPoint_
Estimated number of edges per point.
quaternion normalised(const quaternion &q)
Return the normalised (unit) quaternion of the given quaternion.
Definition: quaternionI.H:674
const Map< labelList > & masterPointFaces() const
Master point face addressing.
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
const faceList & enrichedFaces() const
Return enriched faces.
List< face > faceList
List of faces.
Definition: faceListFwd.H:39
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
const labelList & cutFaceSlave() const
Return cut face slave list.
Vector< scalar > vector
Definition: vector.H:57
errorManip< error > abort(error &err)
Definition: errorManip.H:139
const labelListList & pointPoints() const
Return point-point addressing.
dimensionedScalar atan2(const dimensionedScalar &x, const dimensionedScalar &y)
int debug
Static debugging option.
const pointField & localPoints() const
Return local points.
vector point
Point is a vector.
Definition: point.H:37
const faceList & localFaces() const
Return local faces.
List< label > labelList
A List of labels.
Definition: List.H:62
const labelList & meshPoints() const
Return mesh points.
Vector< Cmpt > & removeCollinear(const Vector< Cmpt > &unitVec)
Inplace removal of components that are collinear to the given unit vector.
Definition: VectorI.H:136
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
const dimensionedScalar mp
Proton mass.
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())