Loading...
Searching...
No Matches
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-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27Description
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
44const 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
54void 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
644void 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();
660
661 return *cutFacesPtr_;
662}
663
664
666{
667 if (!cutFaceMasterPtr_)
668 {
669 calcCutFaces();
671
672 return *cutFaceMasterPtr_;
673}
674
675
677{
678 if (!cutFaceSlavePtr_)
679 {
680 calcCutFaces();
681 }
682
683 return *cutFaceSlavePtr_;
684}
685
686
687// ************************************************************************* //
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
const faceList & localFaces() const
Return local faces.
const pointField & localPoints() const
Return local points.
const Map< labelList > & masterPointFaces() const
Master point face addressing.
const labelList & cutFaceMaster() const
Return cut face master list.
const faceList & enrichedFaces() const
Return enriched faces.
const labelList & meshPoints() const
Return mesh points.
const labelListList & pointPoints() const
Return point-point addressing.
const labelList & cutFaceSlave() const
Return cut face slave list.
const faceList & cutFaces() const
Return list of cut faces.
static const unsigned edgesPerPoint_
Estimated number of edges per point.
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
const dimensionedScalar mp
Proton mass.
List< labelList > labelListList
List of labelList.
Definition labelList.H:38
HashSet< edge, Hash< edge > > edgeHashSet
A HashSet with edge for its key. Hashing (and ==) on an edge is symmetric.
Definition edgeHashes.H:48
List< label > labelList
A List of labels.
Definition List.H:62
quaternion normalised(const quaternion &q)
Return the normalised (unit) quaternion of the given quaternion.
List< face > faceList
List of faces.
Definition faceListFwd.H:41
dimensionedScalar atan2(const dimensionedScalar &x, const dimensionedScalar &y)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
errorManip< error > abort(error &err)
Definition errorManip.H:139
vector point
Point is a vector.
Definition point.H:37
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
vectorField pointField
pointField is a vectorField.
Vector< scalar > vector
Definition vector.H:57
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299