faceCoupleInfo.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-2017 OpenFOAM Foundation
9 Copyright (C) 2019 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
27\*---------------------------------------------------------------------------*/
28
29#include "faceCoupleInfo.H"
30#include "polyMesh.H"
31#include "matchPoints.H"
33#include "meshTools.H"
34#include "treeDataFace.H"
35#include "indexedOctree.H"
36#include "OFstream.H"
37
38// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
39
40namespace Foam
41{
43 const scalar faceCoupleInfo::angleTol_ = 1e-3;
44}
45
46
47// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
48
49void Foam::faceCoupleInfo::writeOBJ
50(
51 const fileName& fName,
52 const edgeList& edges,
53 const pointField& points,
54 const bool compact
55)
56{
57 OFstream str(fName);
58
59 labelList pointMap;
60
61 if (compact)
62 {
63 pointMap.resize(points.size(), -1);
64
65 label newPointi = 0;
66
67 forAll(edges, edgeI)
68 {
69 const edge& e = edges[edgeI];
70
71 forAll(e, eI)
72 {
73 const label pointi = e[eI];
74
75 if (pointMap[pointi] == -1)
76 {
77 pointMap[pointi] = newPointi++;
78
79 meshTools::writeOBJ(str, points[pointi]);
80 }
81 }
82 }
83 }
84 else
85 {
86 pointMap = identity(points.size());
87
88 forAll(points, pointi)
89 {
90 meshTools::writeOBJ(str, points[pointi]);
91 }
92 }
93
94 forAll(edges, edgeI)
95 {
96 const edge& e = edges[edgeI];
97
98 str<< "l " << pointMap[e[0]]+1 << ' ' << pointMap[e[1]]+1 << nl;
99 }
100}
101
102
103void Foam::faceCoupleInfo::writeOBJ
104(
105 const fileName& fName,
106 const pointField& points0,
107 const pointField& points1
108)
109{
110 Pout<< "Writing connections as edges to " << fName << endl;
111
112 OFstream str(fName);
113
114 label vertI = 0;
115
116 forAll(points0, i)
117 {
119 vertI++;
120 meshTools::writeOBJ(str, points1[i]);
121 vertI++;
122 str << "l " << vertI-1 << ' ' << vertI << nl;
123 }
124}
125
126
127void Foam::faceCoupleInfo::writePointsFaces() const
128{
129 const indirectPrimitivePatch& m = masterPatch();
130 const indirectPrimitivePatch& s = slavePatch();
131 const primitiveFacePatch& c = cutFaces();
132
133 // Patches
134 {
135 OFstream str("masterPatch.obj");
136 Pout<< "Writing masterPatch to " << str.name() << endl;
137 meshTools::writeOBJ(str, m.localFaces(), m.localPoints());
138 }
139 {
140 OFstream str("slavePatch.obj");
141 Pout<< "Writing slavePatch to " << str.name() << endl;
142 meshTools::writeOBJ(str, s.localFaces(), s.localPoints());
143 }
144 {
145 OFstream str("cutFaces.obj");
146 Pout<< "Writing cutFaces to " << str.name() << endl;
147 meshTools::writeOBJ(str, c.localFaces(), c.localPoints());
148 }
149
150 // Point connectivity
151 {
152 Pout<< "Writing cutToMasterPoints to cutToMasterPoints.obj" << endl;
153
155 (
156 "cutToMasterPoints.obj",
157 m.localPoints(),
158 pointField(c.localPoints(), masterToCutPoints_));
159 }
160 {
161 Pout<< "Writing cutToSlavePoints to cutToSlavePoints.obj" << endl;
162
164 (
165 "cutToSlavePoints.obj",
166 s.localPoints(),
167 pointField(c.localPoints(), slaveToCutPoints_)
168 );
169 }
170
171 // Face connectivity
172 {
173 Pout<< "Writing cutToMasterFaces to cutToMasterFaces.obj" << endl;
174
175 pointField equivMasterFaces(c.size());
176
177 forAll(cutToMasterFaces(), cutFacei)
178 {
179 label masterFacei = cutToMasterFaces()[cutFacei];
180
181 if (masterFacei != -1)
182 {
183 equivMasterFaces[cutFacei] = m[masterFacei].centre(m.points());
184 }
185 else
186 {
188 << "No master face for cut face " << cutFacei
189 << " at position " << c[cutFacei].centre(c.points())
190 << endl;
191
192 equivMasterFaces[cutFacei] = Zero;
193 }
194 }
195
197 (
198 "cutToMasterFaces.obj",
199 calcFaceCentres<List>(c, cutPoints(), 0, c.size()),
200 equivMasterFaces
201 );
202 }
203
204 {
205 Pout<< "Writing cutToSlaveFaces to cutToSlaveFaces.obj" << endl;
206
207 pointField equivSlaveFaces(c.size());
208
209 forAll(cutToSlaveFaces(), cutFacei)
210 {
211 label slaveFacei = cutToSlaveFaces()[cutFacei];
212
213 equivSlaveFaces[cutFacei] = s[slaveFacei].centre(s.points());
214 }
215
217 (
218 "cutToSlaveFaces.obj",
219 calcFaceCentres<List>(c, cutPoints(), 0, c.size()),
220 equivSlaveFaces
221 );
222 }
223
224 Pout<< endl;
225}
226
227
228void Foam::faceCoupleInfo::writeEdges
229(
230 const labelList& cutToMasterEdges,
231 const labelList& cutToSlaveEdges
232) const
233{
234 const indirectPrimitivePatch& m = masterPatch();
235 const indirectPrimitivePatch& s = slavePatch();
236 const primitiveFacePatch& c = cutFaces();
237
238 // Edge connectivity
239 {
240 OFstream str("cutToMasterEdges.obj");
241 Pout<< "Writing cutToMasterEdges to " << str.name() << endl;
242
243 label vertI = 0;
244
245 forAll(cutToMasterEdges, cutEdgeI)
246 {
247 if (cutToMasterEdges[cutEdgeI] != -1)
248 {
249 const edge& masterEdge = m.edges()[cutToMasterEdges[cutEdgeI]];
250 const edge& cutEdge = c.edges()[cutEdgeI];
251
252 meshTools::writeOBJ(str, m.localPoints()[masterEdge[0]]);
253 vertI++;
254 meshTools::writeOBJ(str, m.localPoints()[masterEdge[1]]);
255 vertI++;
256 meshTools::writeOBJ(str, c.localPoints()[cutEdge[0]]);
257 vertI++;
258 meshTools::writeOBJ(str, c.localPoints()[cutEdge[1]]);
259 vertI++;
260 str << "l " << vertI-3 << ' ' << vertI-2 << nl;
261 str << "l " << vertI-3 << ' ' << vertI-1 << nl;
262 str << "l " << vertI-3 << ' ' << vertI << nl;
263 str << "l " << vertI-2 << ' ' << vertI-1 << nl;
264 str << "l " << vertI-2 << ' ' << vertI << nl;
265 str << "l " << vertI-1 << ' ' << vertI << nl;
266 }
267 }
268 }
269 {
270 OFstream str("cutToSlaveEdges.obj");
271 Pout<< "Writing cutToSlaveEdges to " << str.name() << endl;
272
273 label vertI = 0;
274
275 labelList slaveToCut(invert(s.nEdges(), cutToSlaveEdges));
276
277 forAll(slaveToCut, edgeI)
278 {
279 if (slaveToCut[edgeI] != -1)
280 {
281 const edge& slaveEdge = s.edges()[edgeI];
282 const edge& cutEdge = c.edges()[slaveToCut[edgeI]];
283
284 meshTools::writeOBJ(str, s.localPoints()[slaveEdge[0]]);
285 vertI++;
286 meshTools::writeOBJ(str, s.localPoints()[slaveEdge[1]]);
287 vertI++;
288 meshTools::writeOBJ(str, c.localPoints()[cutEdge[0]]);
289 vertI++;
290 meshTools::writeOBJ(str, c.localPoints()[cutEdge[1]]);
291 vertI++;
292 str << "l " << vertI-3 << ' ' << vertI-2 << nl;
293 str << "l " << vertI-3 << ' ' << vertI-1 << nl;
294 str << "l " << vertI-3 << ' ' << vertI << nl;
295 str << "l " << vertI-2 << ' ' << vertI-1 << nl;
296 str << "l " << vertI-2 << ' ' << vertI << nl;
297 str << "l " << vertI-1 << ' ' << vertI << nl;
298 }
299 }
300 }
301
302 Pout<< endl;
303}
304
305
306Foam::labelList Foam::faceCoupleInfo::findMappedEdges
307(
308 const edgeList& edges,
309 const labelList& pointMap,
310 const indirectPrimitivePatch& patch
311)
312{
313 labelList toPatchEdges(edges.size());
314
315 forAll(toPatchEdges, edgeI)
316 {
317 const edge& e = edges[edgeI];
318
319 label v0 = pointMap[e[0]];
320 label v1 = pointMap[e[1]];
321
322 toPatchEdges[edgeI] =
324 (
325 patch.edges(),
326 patch.pointEdges()[v0],
327 v0,
328 v1
329 );
330 }
331 return toPatchEdges;
332}
333
334
335bool Foam::faceCoupleInfo::regionEdge
336(
337 const polyMesh& slaveMesh,
338 const label slaveEdgeI
339) const
340{
341 const labelList& eFaces = slavePatch().edgeFaces()[slaveEdgeI];
342
343 if (eFaces.size() == 1)
344 {
345 return true;
346 }
347 else
348 {
349 // Count how many different patches connected to this edge.
350
351 label patch0 = -1;
352
353 forAll(eFaces, i)
354 {
355 label facei = eFaces[i];
356
357 label meshFacei = slavePatch().addressing()[facei];
358
359 label patchi = slaveMesh.boundaryMesh().whichPatch(meshFacei);
360
361 if (patch0 == -1)
362 {
363 patch0 = patchi;
364 }
365 else if (patchi != patch0)
366 {
367 // Found two different patches connected to this edge.
368 return true;
369 }
370 }
371 }
372
373 return false;
374}
375
376
377Foam::label Foam::faceCoupleInfo::mostAlignedCutEdge
378(
379 const bool report,
380 const polyMesh& slaveMesh,
381 const bool patchDivision,
382 const labelList& cutToMasterEdges,
383 const labelList& cutToSlaveEdges,
384 const label pointi,
385 const label edgeStart,
386 const label edgeEnd
387) const
388{
389 // Find edge using pointi that is most aligned with vector between master
390 // points. Patchdivision tells us whether or not to use patch information to
391 // match edges.
392
393 const pointField& localPoints = cutFaces().localPoints();
394
395 const labelList& pEdges = cutFaces().pointEdges()[pointi];
396
397 if (report)
398 {
399 Pout<< "mostAlignedEdge : finding nearest edge among "
400 << UIndirectList<edge>(cutFaces().edges(), pEdges)
401 << " connected to point " << pointi
402 << " coord:" << localPoints[pointi]
403 << " running between " << edgeStart << " coord:"
404 << localPoints[edgeStart]
405 << " and " << edgeEnd << " coord:"
406 << localPoints[edgeEnd]
407 << endl;
408 }
409
410 // Find the edge that gets us nearest end.
411
412 label maxEdgeI = -1;
413 scalar maxCos = -GREAT;
414
415 forAll(pEdges, i)
416 {
417 label edgeI = pEdges[i];
418
419 if
420 (
421 !(
422 patchDivision
423 && cutToMasterEdges[edgeI] == -1
424 )
425 || (
426 patchDivision
427 && regionEdge(slaveMesh, cutToSlaveEdges[edgeI])
428 )
429 )
430 {
431 const edge& e = cutFaces().edges()[edgeI];
432
433 label otherPointi = e.otherVertex(pointi);
434
435 if (otherPointi == edgeEnd)
436 {
437 // Shortcut: found edge end point.
438 if (report)
439 {
440 Pout<< " mostAlignedEdge : found end point " << edgeEnd
441 << endl;
442 }
443 return edgeI;
444 }
445
446 // Get angle between edge and edge to masterEnd
447
448 vector eVec(localPoints[otherPointi] - localPoints[pointi]);
449
450 scalar magEVec = mag(eVec);
451
452 if (magEVec < VSMALL)
453 {
455 << "Crossing zero sized edge " << edgeI
456 << " coords:" << localPoints[otherPointi]
457 << localPoints[pointi]
458 << " when walking from " << localPoints[edgeStart]
459 << " to " << localPoints[edgeEnd]
460 << endl;
461 return edgeI;
462 }
463
464 eVec /= magEVec;
465
466 const vector eToEndPoint =
468 (
469 localPoints[edgeEnd] - localPoints[otherPointi]
470 );
471
472 scalar cosAngle = eVec & eToEndPoint;
473
474 if (report)
475 {
476 Pout<< " edge:" << e << " points:" << localPoints[pointi]
477 << localPoints[otherPointi]
478 << " vec:" << eVec
479 << " vecToEnd:" << eToEndPoint
480 << " cosAngle:" << cosAngle
481 << endl;
482 }
483
484 if (cosAngle > maxCos)
485 {
486 maxCos = cosAngle;
487 maxEdgeI = edgeI;
488 }
489 }
490 }
491
492 if (maxCos > 1 - angleTol_)
493 {
494 return maxEdgeI;
495 }
496 else
497 {
498 return -1;
499 }
500}
501
502
503void Foam::faceCoupleInfo::setCutEdgeToPoints(const labelList& cutToMasterEdges)
504{
505 labelListList masterToCutEdges
506 (
508 (
509 masterPatch().nEdges(),
510 cutToMasterEdges
511 )
512 );
513
514 const edgeList& cutEdges = cutFaces().edges();
515
516 // Size extra big so searching is faster
517 cutEdgeToPoints_.resize
518 (
519 masterPatch().nEdges()
520 + slavePatch().nEdges()
521 + cutEdges.size()
522 );
523
524 forAll(masterToCutEdges, masterEdgeI)
525 {
526 const edge& masterE = masterPatch().edges()[masterEdgeI];
527
528 //Pout<< "Master:" << masterPatch().localPoints()[masterE[0]] << ' '
529 // << masterPatch().localPoints()[masterE[1]] << endl;
530
531 const labelList& stringedEdges = masterToCutEdges[masterEdgeI];
532
533 if (stringedEdges.empty())
534 {
536 << "Did not match all of master edges to cutFace edges"
537 << nl
538 << "First unmatched edge:" << masterEdgeI << " endPoints:"
539 << masterPatch().localPoints()[masterE[0]]
540 << masterPatch().localPoints()[masterE[1]]
541 << endl
542 << "This usually means that the slave patch is not a"
543 << " subdivision of the master patch"
544 << abort(FatalError);
545 }
546 else if (stringedEdges.size() > 1)
547 {
548 // String up the edges between e[0] and e[1]. Store the points
549 // inbetween e[0] and e[1] (all in cutFaces() labels)
550
551 DynamicList<label> splitPoints(stringedEdges.size()-1);
552
553 // Unsplit edge endpoints
554 const edge unsplitEdge
555 (
556 masterToCutPoints_[masterE[0]],
557 masterToCutPoints_[masterE[1]]
558 );
559
560 label startVertI = unsplitEdge[0];
561 label startEdgeI = -1;
562
563 while (startVertI != unsplitEdge[1])
564 {
565 // Loop over all string of edges. Update
566 // - startVertI : previous vertex
567 // - startEdgeI : previous edge
568 // and insert any points into splitPoints
569
570 // For checking
571 label oldStart = startVertI;
572
573 forAll(stringedEdges, i)
574 {
575 label edgeI = stringedEdges[i];
576
577 if (edgeI != startEdgeI)
578 {
579 const edge& e = cutEdges[edgeI];
580
581 //Pout<< " cut:" << e << " at:"
582 // << cutFaces().localPoints()[e[0]]
583 // << ' ' << cutFaces().localPoints()[e[1]] << endl;
584
585 if (e[0] == startVertI)
586 {
587 startEdgeI = edgeI;
588 startVertI = e[1];
589 if (e[1] != unsplitEdge[1])
590 {
591 splitPoints.append(e[1]);
592 }
593 break;
594 }
595 else if (e[1] == startVertI)
596 {
597 startEdgeI = edgeI;
598 startVertI = e[0];
599 if (e[0] != unsplitEdge[1])
600 {
601 splitPoints.append(e[0]);
602 }
603 break;
604 }
605 }
606 }
607
608 // Check
609 if (oldStart == startVertI)
610 {
612 << " unsplitEdge:" << unsplitEdge
613 << " does not correspond to split edges "
614 << UIndirectList<edge>(cutEdges, stringedEdges)
615 << abort(FatalError);
616 }
617 }
618
619 //Pout<< "For master edge:"
620 // << unsplitEdge
621 // << " Found stringed points "
622 // << UIndirectList<point>
623 // (
624 // cutFaces().localPoints(),
625 // splitPoints.shrink()
626 // )
627 // << endl;
628
629 cutEdgeToPoints_.insert(unsplitEdge, splitPoints.shrink());
630 }
631 }
632}
633
634
635Foam::label Foam::faceCoupleInfo::matchFaces
636(
637 const scalar absTol,
638 const pointField& points0,
639 const face& f0,
640 const pointField& points1,
641 const face& f1,
642 const bool sameOrientation
643)
644{
645 if (f0.size() != f1.size())
646 {
648 << "Different sizes for supposedly matching faces." << nl
649 << "f0:" << f0 << " coords:" << UIndirectList<point>(points0, f0)
650 << nl
651 << "f1:" << f1 << " coords:" << UIndirectList<point>(points1, f1)
652 << abort(FatalError);
653 }
654
655 const scalar absTolSqr = sqr(absTol);
656
657
658 label matchFp = -1;
659
660 forAll(f0, startFp)
661 {
662 // See -if starting from startFp on f0- the two faces match.
663 bool fullMatch = true;
664
665 label fp0 = startFp;
666 label fp1 = 0;
667
668 forAll(f1, i)
669 {
670 scalar distSqr = Foam::magSqr(points0[f0[fp0]] - points1[f1[fp1]]);
671
672 if (distSqr > absTolSqr)
673 {
674 fullMatch = false;
675 break;
676 }
677
678 fp0 = f0.fcIndex(fp0); // walk forward
679
680 if (sameOrientation)
681 {
682 fp1 = f1.fcIndex(fp1);
683 }
684 else
685 {
686 fp1 = f1.rcIndex(fp1);
687 }
688 }
689
690 if (fullMatch)
691 {
692 matchFp = startFp;
693 break;
694 }
695 }
696
697 if (matchFp == -1)
698 {
700 << "No unique match between two faces" << nl
701 << "Face " << f0 << " coords "
702 << UIndirectList<point>(points0, f0) << nl
703 << "Face " << f1 << " coords "
704 << UIndirectList<point>(points1, f1)
705 << "when using tolerance " << absTol
706 << " and forwardMatching:" << sameOrientation
707 << abort(FatalError);
708 }
709
710 return matchFp;
711}
712
713
714bool Foam::faceCoupleInfo::matchPointsThroughFaces
715(
716 const scalar absTol,
717 const pointField& cutPoints,
718 const faceList& cutFaces,
719 const pointField& patchPoints,
720 const faceList& patchFaces,
721 const bool sameOrientation,
722
723 labelList& patchToCutPoints, // patch to (uncompacted) cut points
724 labelList& cutToCompact, // compaction list for cut points
725 labelList& compactToCut // inverse ,,
726)
727{
728 // Find correspondence from patch points to cut points. This might detect
729 // shared points so the output is a patch-to-cut point list and a compaction
730 // list for the cut points (which will always be equal or more connected
731 // than the patch). Returns true if there are any duplicates.
732
733 // From slave to cut point
734 patchToCutPoints.setSize(patchPoints.size());
735 patchToCutPoints = -1;
736
737 // Compaction list for cut points: either -1 or index into master which
738 // gives the point to compact to.
739 labelList cutPointRegion(cutPoints.size(), -1);
740 DynamicList<label> cutPointRegionMaster;
741
742 forAll(patchFaces, patchFacei)
743 {
744 const face& patchF = patchFaces[patchFacei];
745
746 //const face& cutF = cutFaces[patchToCutFaces[patchFacei]];
747 const face& cutF = cutFaces[patchFacei];
748
749 // Do geometric matching to get position of cutF[0] in patchF
750 label patchFp = matchFaces
751 (
752 absTol,
753 patchPoints,
754 patchF,
755 cutPoints,
756 cutF,
757 sameOrientation // orientation
758 );
759
760 forAll(cutF, cutFp)
761 {
762 label cutPointi = cutF[cutFp];
763 label patchPointi = patchF[patchFp];
764
765 //const point& cutPt = cutPoints[cutPointi];
766 //const point& patchPt = patchPoints[patchPointi];
767 //if (mag(cutPt - patchPt) > SMALL)
768 //{
769 // FatalErrorInFunction
770 // << "cutP:" << cutPt
771 // << " patchP:" << patchPt
772 // << abort(FatalError);
773 //}
774
775 if (patchToCutPoints[patchPointi] == -1)
776 {
777 patchToCutPoints[patchPointi] = cutPointi;
778 }
779 else if (patchToCutPoints[patchPointi] != cutPointi)
780 {
781 // Multiple cut points connecting to same patch.
782 // Check if already have region & region master for this set
783 label otherCutPointi = patchToCutPoints[patchPointi];
784
785 //Pout<< "PatchPoint:" << patchPt
786 // << " matches to:" << cutPointi
787 // << " coord:" << cutPoints[cutPointi]
788 // << " and to:" << otherCutPointi
789 // << " coord:" << cutPoints[otherCutPointi]
790 // << endl;
791
792 if (cutPointRegion[otherCutPointi] != -1)
793 {
794 // Have region for this set. Copy.
795 label region = cutPointRegion[otherCutPointi];
796 cutPointRegion[cutPointi] = region;
797
798 // Update region master with min point label
799 cutPointRegionMaster[region] = min
800 (
801 cutPointRegionMaster[region],
802 cutPointi
803 );
804 }
805 else
806 {
807 // Create new region.
808 label region = cutPointRegionMaster.size();
809 cutPointRegionMaster.append
810 (
811 min(cutPointi, otherCutPointi)
812 );
813 cutPointRegion[cutPointi] = region;
814 cutPointRegion[otherCutPointi] = region;
815 }
816 }
817
818 if (sameOrientation)
819 {
820 patchFp = patchF.fcIndex(patchFp);
821 }
822 else
823 {
824 patchFp = patchF.rcIndex(patchFp);
825 }
826 }
827 }
828
829 // Rework region&master into compaction array
830 compactToCut.setSize(cutPointRegion.size());
831 cutToCompact.setSize(cutPointRegion.size());
832 cutToCompact = -1;
833 label compactPointi = 0;
834
835 forAll(cutPointRegion, i)
836 {
837 if (cutPointRegion[i] == -1)
838 {
839 // Unduplicated point. Allocate new compacted point.
840 cutToCompact[i] = compactPointi;
841 compactToCut[compactPointi] = i;
842 compactPointi++;
843 }
844 else
845 {
846 // Duplicate point. Get master.
847
848 label masterPointi = cutPointRegionMaster[cutPointRegion[i]];
849
850 if (cutToCompact[masterPointi] == -1)
851 {
852 cutToCompact[masterPointi] = compactPointi;
853 compactToCut[compactPointi] = masterPointi;
854 compactPointi++;
855 }
856 cutToCompact[i] = cutToCompact[masterPointi];
857 }
858 }
859 compactToCut.setSize(compactPointi);
860
861 return compactToCut.size() != cutToCompact.size();
862}
863
864
866(
867 const face& cutF,
868 const pointField& cutPoints,
869 const face& masterF,
870 const pointField& masterPoints
871)
872{
873 scalar maxDist = -GREAT;
874
875 forAll(cutF, fp)
876 {
877 const point& cutPt = cutPoints[cutF[fp]];
878
879 pointHit pHit = masterF.nearestPoint(cutPt, masterPoints);
880
881 maxDist = max(maxDist, pHit.distance());
882 }
883 return maxDist;
884}
885
886
887void Foam::faceCoupleInfo::findPerfectMatchingFaces
888(
889 const primitiveMesh& mesh0,
890 const primitiveMesh& mesh1,
891 const scalar absTol,
892
893 labelList& mesh0Faces,
894 labelList& mesh1Faces
895)
896{
897 // Quick check: skip face matching if either mesh has no faces
898 if (!mesh0.nFaces() || !mesh1.nFaces())
899 {
900 mesh0Faces.clear();
901 mesh1Faces.clear();
902
903 return;
904 }
905
906 // Face centres of external faces (without invoking
907 // mesh.faceCentres since mesh might have been clearedOut)
908
909 pointField fc0
910 (
911 calcFaceCentres<List>
912 (
913 mesh0.faces(),
914 mesh0.points(),
915 mesh0.nInternalFaces(),
916 mesh0.nBoundaryFaces()
917 )
918 );
919
920 pointField fc1
921 (
922 calcFaceCentres<List>
923 (
924 mesh1.faces(),
925 mesh1.points(),
926 mesh1.nInternalFaces(),
927 mesh1.nBoundaryFaces()
928 )
929 );
930
931
932 if (debug)
933 {
934 Pout<< "Face matching tolerance : " << absTol << endl;
935 }
936
937
938 // Match geometrically
939 labelList from1To0;
940 bool matchedAllFaces = matchPoints
941 (
942 fc1,
943 fc0,
944 scalarField(fc1.size(), absTol),
945 false,
946 from1To0
947 );
948
949 if (matchedAllFaces)
950 {
952 << "Matched ALL " << fc1.size()
953 << " boundary faces of mesh0 to boundary faces of mesh1." << endl
954 << "This is only valid if the mesh to add is fully"
955 << " enclosed by the mesh it is added to." << endl;
956 }
957
958
959 // Collect matches.
960 label nMatched = 0;
961
962 mesh0Faces.setSize(fc0.size());
963 mesh1Faces.setSize(fc1.size());
964
965 forAll(from1To0, i)
966 {
967 if (from1To0[i] != -1)
968 {
969 mesh1Faces[nMatched] = i + mesh1.nInternalFaces();
970 mesh0Faces[nMatched] = from1To0[i] + mesh0.nInternalFaces();
971
972 nMatched++;
973 }
974 }
975
976 mesh0Faces.setSize(nMatched);
977 mesh1Faces.setSize(nMatched);
978}
979
980
981void Foam::faceCoupleInfo::findSlavesCoveringMaster
982(
983 const primitiveMesh& mesh0,
984 const primitiveMesh& mesh1,
985 const scalar absTol,
986
987 labelList& mesh0Faces,
988 labelList& mesh1Faces
989)
990{
991 // Construct octree from all mesh0 boundary faces
992 labelList bndFaces
993 (
994 identity(mesh0.nBoundaryFaces(), mesh0.nInternalFaces())
995 );
996
997 treeBoundBox overallBb(mesh0.points());
998
999 Random rndGen(123456);
1000
1001 indexedOctree<treeDataFace> tree
1002 (
1003 treeDataFace // all information needed to search faces
1004 (
1005 false, // do not cache bb
1006 mesh0,
1007 bndFaces // boundary faces only
1008 ),
1009 overallBb.extend(rndGen, 1e-4), // overall search domain
1010 8, // maxLevel
1011 10, // leafsize
1012 3.0 // duplicity
1013 );
1014
1015 if (debug)
1016 {
1017 Pout<< "findSlavesCoveringMaster :"
1018 << " constructed octree for mesh0 boundary faces" << endl;
1019 }
1020
1021 // Search nearest face for every mesh1 boundary face.
1022
1023 labelHashSet mesh0Set(mesh0.nBoundaryFaces());
1024 labelHashSet mesh1Set(mesh1.nBoundaryFaces());
1025
1026 for
1027 (
1028 label mesh1Facei = mesh1.nInternalFaces();
1029 mesh1Facei < mesh1.nFaces();
1030 mesh1Facei++
1031 )
1032 {
1033 const face& f1 = mesh1.faces()[mesh1Facei];
1034
1035 // Generate face centre (prevent cellCentres() reconstruction)
1036 point fc(f1.centre(mesh1.points()));
1037
1038 pointIndexHit nearInfo = tree.findNearest(fc, Foam::sqr(absTol));
1039
1040 if (nearInfo.hit())
1041 {
1042 label mesh0Facei = tree.shapes().faceLabels()[nearInfo.index()];
1043
1044 // Check if points of f1 actually lie on top of mesh0 face
1045 // This is the bit that might fail since if f0 is severely warped
1046 // and f1's points are not present on f0 (since subdivision)
1047 // then the distance of the points to f0 might be quite large
1048 // and the test will fail. This all should in fact be some kind
1049 // of walk from known corresponding points/edges/faces.
1050 scalar dist =
1051 maxDistance
1052 (
1053 f1,
1054 mesh1.points(),
1055 mesh0.faces()[mesh0Facei],
1056 mesh0.points()
1057 );
1058
1059 if (dist < absTol)
1060 {
1061 mesh0Set.insert(mesh0Facei);
1062 mesh1Set.insert(mesh1Facei);
1063 }
1064 }
1065 }
1066
1067 if (debug)
1068 {
1069 Pout<< "findSlavesCoveringMaster :"
1070 << " matched " << mesh1Set.size() << " mesh1 faces to "
1071 << mesh0Set.size() << " mesh0 faces" << endl;
1072 }
1073
1074 mesh0Faces = mesh0Set.toc();
1075 mesh1Faces = mesh1Set.toc();
1076}
1077
1078
1079Foam::label Foam::faceCoupleInfo::growCutFaces
1080(
1081 const labelList& cutToMasterEdges,
1082 Map<labelList>& candidates
1083)
1084{
1085 if (debug)
1086 {
1087 Pout<< "growCutFaces :"
1088 << " growing cut faces to masterPatch" << endl;
1089 }
1090
1091 label nTotChanged = 0;
1092
1093 while (true)
1094 {
1095 const labelListList& cutFaceEdges = cutFaces().faceEdges();
1096 const labelListList& cutEdgeFaces = cutFaces().edgeFaces();
1097
1098 label nChanged = 0;
1099
1100 forAll(cutToMasterFaces_, cutFacei)
1101 {
1102 const label masterFacei = cutToMasterFaces_[cutFacei];
1103
1104 if (masterFacei != -1)
1105 {
1106 // We now have a cutFace for which we have already found a
1107 // master face. Grow this masterface across any internal edge
1108 // (internal: no corresponding master edge)
1109
1110 const labelList& fEdges = cutFaceEdges[cutFacei];
1111
1112 forAll(fEdges, i)
1113 {
1114 const label cutEdgeI = fEdges[i];
1115
1116 if (cutToMasterEdges[cutEdgeI] == -1)
1117 {
1118 // So this edge:
1119 // - internal to the cutPatch (since all region edges
1120 // marked before)
1121 // - on cutFacei which corresponds to masterFace.
1122 // Mark all connected faces with this masterFace.
1123
1124 const labelList& eFaces = cutEdgeFaces[cutEdgeI];
1125
1126 forAll(eFaces, j)
1127 {
1128 const label facei = eFaces[j];
1129
1130 if (cutToMasterFaces_[facei] == -1)
1131 {
1132 cutToMasterFaces_[facei] = masterFacei;
1133 candidates.erase(facei);
1134 nChanged++;
1135 }
1136 else if (cutToMasterFaces_[facei] != masterFacei)
1137 {
1138 const pointField& cutPoints =
1139 cutFaces().points();
1140 const pointField& masterPoints =
1141 masterPatch().points();
1142
1143 const edge& e = cutFaces().edges()[cutEdgeI];
1144
1145 label myMaster = cutToMasterFaces_[facei];
1146 const face& myF = masterPatch()[myMaster];
1147
1148 const face& nbrF = masterPatch()[masterFacei];
1149
1151 << "Cut face "
1152 << cutFaces()[facei].points(cutPoints)
1153 << " has master "
1154 << myMaster
1155 << " but also connects to nbr face "
1156 << cutFaces()[cutFacei].points(cutPoints)
1157 << " with master " << masterFacei
1158 << nl
1159 << "myMasterFace:"
1160 << myF.points(masterPoints)
1161 << " nbrMasterFace:"
1162 << nbrF.points(masterPoints) << nl
1163 << "Across cut edge " << cutEdgeI
1164 << " coord:"
1165 << cutFaces().localPoints()[e[0]]
1166 << cutFaces().localPoints()[e[1]]
1167 << abort(FatalError);
1168 }
1169 }
1170 }
1171 }
1172 }
1173 }
1174
1175 if (debug)
1176 {
1177 Pout<< "growCutFaces : Grown an additional " << nChanged
1178 << " cut to master face" << " correspondence" << endl;
1179 }
1180
1181 nTotChanged += nChanged;
1182
1183 if (nChanged == 0)
1184 {
1185 break;
1186 }
1187 }
1188
1189 return nTotChanged;
1190}
1191
1192
1193void Foam::faceCoupleInfo::checkMatch(const labelList& cutToMasterEdges) const
1194{
1195 const pointField& cutLocalPoints = cutFaces().localPoints();
1196
1197 const pointField& masterLocalPoints = masterPatch().localPoints();
1198 const faceList& masterLocalFaces = masterPatch().localFaces();
1199
1200 forAll(cutToMasterEdges, cutEdgeI)
1201 {
1202 const edge& e = cutFaces().edges()[cutEdgeI];
1203
1204 if (cutToMasterEdges[cutEdgeI] == -1)
1205 {
1206 // Internal edge. Check that master face is same on both sides.
1207 const labelList& cutEFaces = cutFaces().edgeFaces()[cutEdgeI];
1208
1209 label masterFacei = -1;
1210
1211 forAll(cutEFaces, i)
1212 {
1213 label cutFacei = cutEFaces[i];
1214
1215 if (cutToMasterFaces_[cutFacei] != -1)
1216 {
1217 if (masterFacei == -1)
1218 {
1219 masterFacei = cutToMasterFaces_[cutFacei];
1220 }
1221 else if (masterFacei != cutToMasterFaces_[cutFacei])
1222 {
1223 label myMaster = cutToMasterFaces_[cutFacei];
1224 const face& myF = masterLocalFaces[myMaster];
1225
1226 const face& nbrF = masterLocalFaces[masterFacei];
1227
1229 << "Internal CutEdge " << e
1230 << " coord:"
1231 << cutLocalPoints[e[0]]
1232 << cutLocalPoints[e[1]]
1233 << " connects to master " << myMaster
1234 << " and to master " << masterFacei << nl
1235 << "myMasterFace:"
1236 << myF.points(masterLocalPoints)
1237 << " nbrMasterFace:"
1238 << nbrF.points(masterLocalPoints)
1239 << abort(FatalError);
1240 }
1241 }
1242 }
1243 }
1244 }
1245}
1246
1247
1248Foam::label Foam::faceCoupleInfo::matchEdgeFaces
1249(
1250 const labelList& cutToMasterEdges,
1251 Map<labelList>& candidates
1252)
1253{
1254 // Extends matching information by elimination across cutFaces using more
1255 // than one region edge. Updates cutToMasterFaces_ and sets candidates which
1256 // is for every cutface on a region edge the possible master faces.
1257
1258 // For every unassigned cutFacei the possible list of master faces.
1259 candidates.clear();
1260 candidates.resize(cutFaces().size());
1261
1262 label nChanged = 0;
1263
1264 forAll(cutToMasterEdges, cutEdgeI)
1265 {
1266 label masterEdgeI = cutToMasterEdges[cutEdgeI];
1267
1268 if (masterEdgeI != -1)
1269 {
1270 // So cutEdgeI is matched to masterEdgeI. For all cut faces using
1271 // the cut edge store the master face as a candidate match.
1272
1273 const labelList& cutEFaces = cutFaces().edgeFaces()[cutEdgeI];
1274 const labelList& masterEFaces =
1275 masterPatch().edgeFaces()[masterEdgeI];
1276
1277 forAll(cutEFaces, i)
1278 {
1279 label cutFacei = cutEFaces[i];
1280
1281 if (cutToMasterFaces_[cutFacei] == -1)
1282 {
1283 // So this face (cutFacei) is on an edge (cutEdgeI) that
1284 // is used by some master faces (masterEFaces). Check if
1285 // the cutFacei and some of these masterEFaces share more
1286 // than one edge (which uniquely defines face)
1287
1288 // Combine master faces with current set of candidate
1289 // master faces.
1290 auto fnd = candidates.find(cutFacei);
1291
1292 if (!fnd.found())
1293 {
1294 // No info yet for cutFacei. Add all master faces as
1295 // candidates
1296 candidates.insert(cutFacei, masterEFaces);
1297 }
1298 else
1299 {
1300 // From some other cutEdgeI there are already some
1301 // candidate master faces. Check the overlap with
1302 // the current set of master faces.
1303 const labelList& masterFaces = fnd.val();
1304
1305 DynamicList<label> newCandidates(masterFaces.size());
1306
1307 forAll(masterEFaces, j)
1308 {
1309 if (masterFaces.found(masterEFaces[j]))
1310 {
1311 newCandidates.append(masterEFaces[j]);
1312 }
1313 }
1314
1315 if (newCandidates.size() == 1)
1316 {
1317 // Perfect match. Delete entry from candidates map.
1318 cutToMasterFaces_[cutFacei] = newCandidates[0];
1319 candidates.erase(cutFacei);
1320 nChanged++;
1321 }
1322 else
1323 {
1324 // Should not happen?
1325 //Pout<< "On edge:" << cutEdgeI
1326 // << " have connected masterFaces:"
1327 // << masterEFaces
1328 // << " and from previous edge we have"
1329 // << " connected masterFaces" << masterFaces
1330 // << " . Overlap:" << newCandidates << endl;
1331
1332 fnd() = newCandidates.shrink();
1333 }
1334 }
1335 }
1336
1337 }
1338 }
1339 }
1340
1341 if (debug)
1342 {
1343 Pout<< "matchEdgeFaces : Found " << nChanged
1344 << " faces where there was"
1345 << " only one remaining choice for cut-master correspondence"
1346 << endl;
1347 }
1348
1349 return nChanged;
1350}
1351
1352
1353Foam::label Foam::faceCoupleInfo::geometricMatchEdgeFaces
1354(
1355 Map<labelList>& candidates
1356)
1357{
1358 const pointField& cutPoints = cutFaces().points();
1359
1360 label nChanged = 0;
1361
1362 // Mark all master faces that have been matched so far.
1363
1364 labelListList masterToCutFaces
1365 (
1367 (
1368 masterPatch().size(),
1369 cutToMasterFaces_
1370 )
1371 );
1372
1373 forAllConstIters(candidates, iter)
1374 {
1375 label cutFacei = iter.key();
1376 const labelList& masterFaces = iter.val();
1377
1378 const face& cutF = cutFaces()[cutFacei];
1379
1380 if (cutToMasterFaces_[cutFacei] == -1)
1381 {
1382 // Find the best matching master face.
1383 scalar minDist = GREAT;
1384 label minMasterFacei = -1;
1385
1386 forAll(masterFaces, i)
1387 {
1388 label masterFacei = masterFaces[i];
1389
1390 if (masterToCutFaces[masterFaces[i]].empty())
1391 {
1392 scalar dist = maxDistance
1393 (
1394 cutF,
1395 cutPoints,
1396 masterPatch()[masterFacei],
1397 masterPatch().points()
1398 );
1399
1400 if (dist < minDist)
1401 {
1402 minDist = dist;
1403 minMasterFacei = masterFacei;
1404 }
1405 }
1406 }
1407
1408 if (minMasterFacei != -1)
1409 {
1410 cutToMasterFaces_[cutFacei] = minMasterFacei;
1411 masterToCutFaces[minMasterFacei] = cutFacei;
1412 nChanged++;
1413 }
1414 }
1415 }
1416
1417 // (inefficiently) force candidates to be uptodate.
1418 forAll(cutToMasterFaces_, cutFacei)
1419 {
1420 if (cutToMasterFaces_[cutFacei] != -1)
1421 {
1422 candidates.erase(cutFacei);
1423 }
1424 }
1425
1426
1427 if (debug)
1428 {
1429 Pout<< "geometricMatchEdgeFaces : Found " << nChanged
1430 << " faces where there was"
1431 << " only one remaining choice for cut-master correspondence"
1432 << endl;
1433 }
1434
1435 return nChanged;
1436}
1437
1438
1439void Foam::faceCoupleInfo::perfectPointMatch
1440(
1441 const scalar absTol,
1442 const bool slaveFacesOrdered
1443)
1444{
1445 // Calculate the set of cut faces inbetween master and slave patch assuming
1446 // perfect match (and optional face ordering on slave)
1447
1448 if (debug)
1449 {
1450 Pout<< "perfectPointMatch :"
1451 << " Matching master and slave to cut."
1452 << " Master and slave faces are identical" << nl;
1453
1454 if (slaveFacesOrdered)
1455 {
1456 Pout<< "and master and slave faces are ordered"
1457 << " (on coupled patches)" << endl;
1458 }
1459 else
1460 {
1461 Pout<< "and master and slave faces are not ordered"
1462 << " (on coupled patches)" << endl;
1463 }
1464 }
1465
1466 cutToMasterFaces_ = identity(masterPatch().size());
1467 cutPoints_ = masterPatch().localPoints();
1468 cutFacesPtr_.reset
1469 (
1471 (
1472 masterPatch().localFaces(),
1473 cutPoints_
1474 )
1475 );
1476 masterToCutPoints_ = identity(cutPoints_.size());
1477
1478
1479 // Cut faces to slave patch.
1480 bool matchedAllFaces = false;
1481
1482 if (slaveFacesOrdered)
1483 {
1484 cutToSlaveFaces_ = identity(cutFaces().size());
1485 matchedAllFaces = (cutFaces().size() == slavePatch().size());
1486 }
1487 else
1488 {
1489 // Faces do not have to be ordered (but all have
1490 // to match). Note: Faces will be already ordered if we enter here from
1491 // construct from meshes.
1492
1493 matchedAllFaces = matchPoints
1494 (
1495 calcFaceCentres<List>
1496 (
1497 cutFaces(),
1498 cutFaces().points(),
1499 0,
1500 cutFaces().size()
1501 ),
1502 calcFaceCentres<IndirectList>
1503 (
1504 slavePatch(),
1505 slavePatch().points(),
1506 0,
1507 slavePatch().size()
1508 ),
1509 scalarField(slavePatch().size(), absTol),
1510 false,
1511 cutToSlaveFaces_
1512 );
1513
1514 // If some of the face centres did not match, then try to match the
1515 // point averages instead. There is no division by the face area in
1516 // calculating the point average, so this is more stable when faces
1517 // collapse onto a line or point.
1518 if (!matchedAllFaces)
1519 {
1520 labelList cutToSlaveFacesTemp(cutToSlaveFaces_.size(), -1);
1521
1523 (
1524 calcFacePointAverages<List>
1525 (
1526 cutFaces(),
1527 cutFaces().points(),
1528 0,
1529 cutFaces().size()
1530 ),
1531 calcFacePointAverages<IndirectList>
1532 (
1533 slavePatch(),
1534 slavePatch().points(),
1535 0,
1536 slavePatch().size()
1537 ),
1538 scalarField(slavePatch().size(), absTol),
1539 true,
1540 cutToSlaveFacesTemp
1541 );
1542
1543 cutToSlaveFaces_ = max(cutToSlaveFaces_, cutToSlaveFacesTemp);
1544
1545 matchedAllFaces = min(cutToSlaveFaces_) != -1;
1546 }
1547 }
1548
1549
1550 if (!matchedAllFaces)
1551 {
1553 << "Did not match all of the master faces to the slave faces"
1554 << endl
1555 << "This usually means that the slave patch and master patch"
1556 << " do not align to within " << absTol << " metre."
1557 << abort(FatalError);
1558 }
1559
1560
1561 // Find correspondence from slave points to cut points. This might
1562 // detect shared points so the output is a slave-to-cut point list
1563 // and a compaction list.
1564
1565 labelList cutToCompact, compactToCut;
1566 matchPointsThroughFaces
1567 (
1568 absTol,
1569 cutFaces().localPoints(),
1570 reorder(cutToSlaveFaces_, cutFaces().localFaces()),
1571 slavePatch().localPoints(),
1572 slavePatch().localFaces(),
1573 false, // slave and cut have opposite orientation
1574
1575 slaveToCutPoints_, // slave to (uncompacted) cut points
1576 cutToCompact, // compaction map: from cut to compacted
1577 compactToCut // compaction map: from compacted to cut
1578 );
1579
1580
1581 // Use compaction lists to renumber cutPoints.
1582 cutPoints_ = UIndirectList<point>(cutPoints_, compactToCut)();
1583 {
1584 const faceList& cutLocalFaces = cutFaces().localFaces();
1585
1586 faceList compactFaces(cutLocalFaces.size());
1587 forAll(cutLocalFaces, i)
1588 {
1589 compactFaces[i] = renumber(cutToCompact, cutLocalFaces[i]);
1590 }
1591 cutFacesPtr_.reset
1592 (
1594 (
1595 compactFaces,
1596 cutPoints_
1597 )
1598 );
1599 }
1600 inplaceRenumber(cutToCompact, slaveToCutPoints_);
1601 inplaceRenumber(cutToCompact, masterToCutPoints_);
1602}
1603
1604
1605void Foam::faceCoupleInfo::subDivisionMatch
1606(
1607 const polyMesh& slaveMesh,
1608 const bool patchDivision,
1609 const scalar absTol
1610)
1611{
1612 if (debug)
1613 {
1614 Pout<< "subDivisionMatch :"
1615 << " Matching master and slave to cut."
1616 << " Slave can be subdivision of master but all master edges"
1617 << " have to be covered by slave edges." << endl;
1618 }
1619
1620 // Assume slave patch is subdivision of the master patch so cutFaces is a
1621 // copy of the slave (but reversed since orientation has to be like master).
1622 cutPoints_ = slavePatch().localPoints();
1623 {
1624 faceList cutFaces(slavePatch().size());
1625
1626 forAll(cutFaces, i)
1627 {
1628 cutFaces[i] = slavePatch().localFaces()[i].reverseFace();
1629 }
1630 cutFacesPtr_.reset(new primitiveFacePatch(cutFaces, cutPoints_));
1631 }
1632
1633 // Cut is copy of slave so addressing to slave is trivial.
1634 cutToSlaveFaces_ = identity(cutFaces().size());
1635 slaveToCutPoints_ = identity(slavePatch().nPoints());
1636
1637 // Cut edges to slave patch
1638 labelList cutToSlaveEdges
1639 (
1640 findMappedEdges
1641 (
1642 cutFaces().edges(),
1643 slaveToCutPoints_, //note:should be cutToSlavePoints but since iden
1644 slavePatch()
1645 )
1646 );
1647
1648
1649 if (debug)
1650 {
1651 OFstream str("regionEdges.obj");
1652
1653 Pout<< "subDivisionMatch :"
1654 << " addressing for slave patch fully done."
1655 << " Dumping region edges to " << str.name() << endl;
1656
1657 label vertI = 0;
1658
1659 forAll(slavePatch().edges(), slaveEdgeI)
1660 {
1661 if (regionEdge(slaveMesh, slaveEdgeI))
1662 {
1663 const edge& e = slavePatch().edges()[slaveEdgeI];
1664
1665 meshTools::writeOBJ(str, slavePatch().localPoints()[e[0]]);
1666 vertI++;
1667 meshTools::writeOBJ(str, slavePatch().localPoints()[e[1]]);
1668 vertI++;
1669 str<< "l " << vertI-1 << ' ' << vertI << nl;
1670 }
1671 }
1672 }
1673
1674
1675 // Addressing from cut to master.
1676
1677 // Cut points to master patch
1678 // - determine master points to cut points. Has to be full!
1679 // - invert to get cut to master
1680 if (debug)
1681 {
1682 Pout<< "subDivisionMatch :"
1683 << " matching master points to cut points" << endl;
1684 }
1685
1686 bool matchedAllPoints = matchPoints
1687 (
1688 masterPatch().localPoints(),
1689 cutPoints_,
1690 scalarField(masterPatch().nPoints(), absTol),
1691 true,
1692 masterToCutPoints_
1693 );
1694
1695 if (!matchedAllPoints)
1696 {
1698 << "Did not match all of the master points to the slave points"
1699 << endl
1700 << "This usually means that the slave patch is not a"
1701 << " subdivision of the master patch"
1702 << abort(FatalError);
1703 }
1704
1705
1706 // Do masterEdges to cutEdges :
1707 // - mark all edges between two masterEdge endpoints. (geometric test since
1708 // this is the only distinction)
1709 // - this gives the borders inbetween which all cutfaces come from
1710 // a single master face.
1711 if (debug)
1712 {
1713 Pout<< "subDivisionMatch :"
1714 << " matching cut edges to master edges" << endl;
1715 }
1716
1717 const edgeList& masterEdges = masterPatch().edges();
1718 const pointField& masterPoints = masterPatch().localPoints();
1719
1720 const edgeList& cutEdges = cutFaces().edges();
1721
1722 labelList cutToMasterEdges(cutFaces().nEdges(), -1);
1723
1724 forAll(masterEdges, masterEdgeI)
1725 {
1726 const edge& masterEdge = masterEdges[masterEdgeI];
1727
1728 label cutPoint0 = masterToCutPoints_[masterEdge[0]];
1729 label cutPoint1 = masterToCutPoints_[masterEdge[1]];
1730
1731 // Find edges between cutPoint0 and cutPoint1.
1732
1733 label cutPointi = cutPoint0;
1734 do
1735 {
1736 // Find edge (starting at pointi on cut), aligned with master
1737 // edge.
1738 label cutEdgeI =
1739 mostAlignedCutEdge
1740 (
1741 false,
1742 slaveMesh,
1743 patchDivision,
1744 cutToMasterEdges,
1745 cutToSlaveEdges,
1746 cutPointi,
1747 cutPoint0,
1748 cutPoint1
1749 );
1750
1751 if (cutEdgeI == -1)
1752 {
1753 // Problem. Report while matching to produce nice error message
1754 mostAlignedCutEdge
1755 (
1756 true,
1757 slaveMesh,
1758 patchDivision,
1759 cutToMasterEdges,
1760 cutToSlaveEdges,
1761 cutPointi,
1762 cutPoint0,
1763 cutPoint1
1764 );
1765
1766 Pout<< "Dumping unmatched pointEdges to errorEdges.obj"
1767 << endl;
1768
1769 writeOBJ
1770 (
1771 "errorEdges.obj",
1772 edgeList
1773 (
1774 UIndirectList<edge>
1775 (
1776 cutFaces().edges(),
1777 cutFaces().pointEdges()[cutPointi]
1778 )
1779 ),
1780 cutFaces().localPoints(),
1781 false
1782 );
1783
1785 << "Problem in finding cut edges corresponding to"
1786 << " master edge " << masterEdge
1787 << " points:" << masterPoints[masterEdge[0]]
1788 << ' ' << masterPoints[masterEdge[1]]
1789 << " corresponding cut edge: (" << cutPoint0
1790 << ") " << cutPoint1
1791 << abort(FatalError);
1792 }
1793
1794 cutToMasterEdges[cutEdgeI] = masterEdgeI;
1795
1796 cutPointi = cutEdges[cutEdgeI].otherVertex(cutPointi);
1797
1798 } while (cutPointi != cutPoint1);
1799 }
1800
1801 if (debug)
1802 {
1803 // Write all matched edges.
1804 writeEdges(cutToMasterEdges, cutToSlaveEdges);
1805 }
1806
1807 // Rework cutToMasterEdges into list of points inbetween two endpoints
1808 // (cutEdgeToPoints_)
1809 setCutEdgeToPoints(cutToMasterEdges);
1810
1811
1812 // Now that we have marked the cut edges that lie on top of master edges
1813 // we can find cut faces that have two (or more) of these edges.
1814 // Note that there can be multiple faces having two or more matched edges
1815 // since the cut faces can form a non-manifold surface(?)
1816 // So the matching is done as an elimination process where for every
1817 // cutFace on a matched edge we store the possible master faces and
1818 // eliminate more and more until we only have one possible master face
1819 // left.
1820
1821 if (debug)
1822 {
1823 Pout<< "subDivisionMatch :"
1824 << " matching (topological) cut faces to masterPatch" << endl;
1825 }
1826
1827 // For every unassigned cutFacei the possible list of master faces.
1828 Map<labelList> candidates(cutFaces().size());
1829
1830 cutToMasterFaces_.setSize(cutFaces().size());
1831 cutToMasterFaces_ = -1;
1832
1833 while (true)
1834 {
1835 // See if there are any edges left where there is only one remaining
1836 // candidate.
1837 label nChanged = matchEdgeFaces(cutToMasterEdges, candidates);
1838
1839 checkMatch(cutToMasterEdges);
1840
1841
1842 // Now we should have found a face correspondence for every cutFace
1843 // that uses two or more cutEdges. Floodfill from these across
1844 // 'internal' cutedges (i.e. edges that do not have a master
1845 // equivalent) to determine some more cutToMasterFaces
1846 nChanged += growCutFaces(cutToMasterEdges, candidates);
1847
1848 checkMatch(cutToMasterEdges);
1849
1850 if (nChanged == 0)
1851 {
1852 break;
1853 }
1854 }
1855
1856 if (debug)
1857 {
1858 Pout<< "subDivisionMatch :"
1859 << " matching (geometric) cut faces to masterPatch" << endl;
1860 }
1861
1862 // Above should have matched all cases fully. Cannot prove this yet so
1863 // do any remaining unmatched edges by a geometric test.
1864 while (true)
1865 {
1866 // See if there are any edges left where there is only one remaining
1867 // candidate.
1868 label nChanged = geometricMatchEdgeFaces(candidates);
1869
1870 checkMatch(cutToMasterEdges);
1871
1872 nChanged += growCutFaces(cutToMasterEdges, candidates);
1873
1874 checkMatch(cutToMasterEdges);
1875
1876 if (nChanged == 0)
1877 {
1878 break;
1879 }
1880 }
1881
1882
1883 // All cut faces matched?
1884 forAll(cutToMasterFaces_, cutFacei)
1885 {
1886 if (cutToMasterFaces_[cutFacei] == -1)
1887 {
1888 const face& cutF = cutFaces()[cutFacei];
1889
1891 << "Did not match all of cutFaces to a master face" << nl
1892 << "First unmatched cut face:" << cutFacei << " with points:"
1893 << UIndirectList<point>(cutFaces().points(), cutF)
1894 << nl
1895 << "This usually means that the slave patch is not a"
1896 << " subdivision of the master patch"
1897 << abort(FatalError);
1898 }
1899 }
1900
1901 if (debug)
1902 {
1903 Pout<< "subDivisionMatch :"
1904 << " finished matching master and slave to cut" << endl;
1905 }
1906
1907 if (debug)
1908 {
1909 writePointsFaces();
1910 }
1911}
1912
1913
1914// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1915
1917(
1918 const polyMesh& masterMesh,
1919 const polyMesh& slaveMesh,
1920 const scalar absTol,
1921 const bool perfectMatch
1922)
1923:
1924 masterPatchPtr_(nullptr),
1925 slavePatchPtr_(nullptr),
1926 cutPoints_(0),
1927 cutFacesPtr_(nullptr),
1928 cutToMasterFaces_(0),
1929 masterToCutPoints_(0),
1930 cutToSlaveFaces_(0),
1931 slaveToCutPoints_(0),
1932 cutEdgeToPoints_(0)
1933{
1934 // Get faces on both meshes that are aligned.
1935 // (not ordered i.e. masterToMesh[0] does
1936 // not couple to slaveToMesh[0]. This ordering is done later on)
1937 labelList masterToMesh;
1938 labelList slaveToMesh;
1939
1940 if (perfectMatch)
1941 {
1942 // Identical faces so use tight face-centre comparison.
1943 findPerfectMatchingFaces
1944 (
1945 masterMesh,
1946 slaveMesh,
1947 absTol,
1948 masterToMesh,
1949 slaveToMesh
1950 );
1951 }
1952 else
1953 {
1954 // Slave subdivision of master so use 'nearest'. Bit dodgy, especially
1955 // with using absTol (which is quite small)
1956 findSlavesCoveringMaster
1957 (
1958 masterMesh,
1959 slaveMesh,
1960 absTol,
1961 masterToMesh,
1962 slaveToMesh
1963 );
1964 }
1965
1966 // Construct addressing engines for both sides
1967 masterPatchPtr_.reset
1968 (
1970 (
1971 IndirectList<face>(masterMesh.faces(), masterToMesh),
1972 masterMesh.points()
1973 )
1974 );
1975
1976 slavePatchPtr_.reset
1977 (
1979 (
1980 IndirectList<face>(slaveMesh.faces(), slaveToMesh),
1981 slaveMesh.points()
1982 )
1983 );
1984
1985
1986 if (perfectMatch)
1987 {
1988 // Faces are perfectly aligned but probably not ordered.
1989 perfectPointMatch(absTol, false);
1990 }
1991 else
1992 {
1993 // Slave faces are subdivision of master face. Faces not ordered.
1994 subDivisionMatch(slaveMesh, false, absTol);
1995 }
1996
1997 if (debug)
1998 {
1999 writePointsFaces();
2000 }
2001}
2002
2003
2005(
2006 const polyMesh& masterMesh,
2007 const labelList& masterAddressing,
2008 const polyMesh& slaveMesh,
2009 const labelList& slaveAddressing,
2010 const scalar absTol,
2011 const bool perfectMatch,
2012 const bool orderedFaces,
2013 const bool patchDivision
2014)
2015:
2016 masterPatchPtr_
2017 (
2019 (
2020 IndirectList<face>(masterMesh.faces(), masterAddressing),
2021 masterMesh.points()
2022 )
2023 ),
2024 slavePatchPtr_
2025 (
2027 (
2028 IndirectList<face>(slaveMesh.faces(), slaveAddressing),
2029 slaveMesh.points()
2030 )
2031 ),
2032 cutPoints_(0),
2033 cutFacesPtr_(nullptr),
2034 cutToMasterFaces_(0),
2035 masterToCutPoints_(0),
2036 cutToSlaveFaces_(0),
2037 slaveToCutPoints_(0),
2038 cutEdgeToPoints_(0)
2039{
2040 if (perfectMatch && (masterAddressing.size() != slaveAddressing.size()))
2041 {
2043 << "Perfect match specified but number of master and slave faces"
2044 << " differ." << endl
2045 << "master:" << masterAddressing.size()
2046 << " slave:" << slaveAddressing.size()
2047 << abort(FatalError);
2048 }
2049
2050 if
2051 (
2052 masterAddressing.size()
2053 && min(masterAddressing) < masterMesh.nInternalFaces()
2054 )
2055 {
2057 << "Supplied internal face on master mesh to couple." << nl
2058 << "Faces to be coupled have to be boundary faces."
2059 << abort(FatalError);
2060 }
2061 if
2062 (
2063 slaveAddressing.size()
2064 && min(slaveAddressing) < slaveMesh.nInternalFaces()
2065 )
2066 {
2068 << "Supplied internal face on slave mesh to couple." << nl
2069 << "Faces to be coupled have to be boundary faces."
2070 << abort(FatalError);
2071 }
2072
2073
2074 if (perfectMatch)
2075 {
2076 perfectPointMatch(absTol, orderedFaces);
2077 }
2078 else
2079 {
2080 // Slave faces are subdivision of master face. Faces not ordered.
2081 subDivisionMatch(slaveMesh, patchDivision, absTol);
2082 }
2083
2084 if (debug)
2085 {
2086 writePointsFaces();
2087 }
2088}
2089
2090
2091// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
2092
2094{
2095 labelList faces(pp.size());
2096
2097 label facei = pp.start();
2098
2099 forAll(pp, i)
2100 {
2101 faces[i] = facei++;
2102 }
2103 return faces;
2104}
2105
2106
2108{
2109 Map<label> map(lst.size());
2110
2111 forAll(lst, i)
2112 {
2113 if (lst[i] != -1)
2114 {
2115 map.insert(i, lst[i]);
2116 }
2117 }
2118 return map;
2119}
2120
2121
2123(
2124 const labelListList& lst
2125)
2126{
2127 Map<labelList> map(lst.size());
2128
2129 forAll(lst, i)
2130 {
2131 if (lst[i].size())
2132 {
2133 map.insert(i, lst[i]);
2134 }
2135 }
2136 return map;
2137}
2138
2139
2140// ************************************************************************* //
bool insert(const Key &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition: HashTableI.H:180
A List with indirect addressing.
Definition: IndirectList.H:119
void append(const T &val)
Append an element at the end of the list.
Definition: ListI.H:175
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:139
A HashTable to objects of type <T> with a label key.
Definition: Map.H:60
virtual const fileName & name() const
Get the name of the stream.
Definition: OSstream.H:107
A list of faces which address into the list of points.
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
Container for information needed to couple to meshes. When constructed from two meshes and a geometri...
static Map< label > makeMap(const labelList &)
Create Map from List.
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:75
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:81
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1108
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1083
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:75
label start() const
Return start label of this patch in the polyMesh face list.
Definition: polyPatch.H:364
label nInternalFaces() const noexcept
Number of internal faces.
scalar maxDistance() const
Highest distance of all features.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
const labelList & faceLabels() const noexcept
Values for "faces" (XML only)
Definition: foamVtuCellsI.H:79
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition: className.H:121
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
const labelList nEdges(UPstream::listGatherValues< label >(aMesh.nEdges()))
const pointField & points
label nPoints
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
Determine correspondence between points. See below.
#define WarningInFunction
Report a warning using Foam::Warning.
const dimensionedScalar c
Speed of light in a vacuum.
const std::string patch
OpenFOAM patch number as a std::string.
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:359
void writeOBJ(Ostream &os, const point &pt)
Write obj representation of a point.
Definition: meshTools.C:203
Namespace for OpenFOAM.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i)
Definition: labelList.C:38
void inplaceRenumber(const labelUList &oldToNew, IntListType &input)
Inplace renumber the values (not the indices) of a list.
List< label > labelList
A List of labels.
Definition: List.H:66
dimensionedSymmTensor sqr(const dimensionedVector &dv)
quaternion normalised(const quaternion &q)
Return the normalised (unit) quaternion of the given quaternion.
Definition: quaternionI.H:680
PointIndexHit< point > pointIndexHit
A PointIndexHit for 3D points.
Definition: pointIndexHit.H:46
IntListType renumber(const labelUList &oldToNew, const IntListType &input)
Renumber the values (not the indices) of a list.
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:44
vector point
Point is a vector.
Definition: point.H:43
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:56
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:33
errorManip< error > abort(error &err)
Definition: errorManip.H:144
PointHit< point > pointHit
A PointIndexHit for 3D points.
Definition: pointHit.H:44
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
bool matchPoints(const UList< point > &pts0, const UList< point > &pts1, const UList< scalar > &matchDistance, const bool verbose, labelList &from0To1, const point &origin=point::zero)
Determine correspondence between pointFields. Gets passed.
Definition: matchPoints.C:36
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
Definition: HashSet.H:85
PrimitivePatch< List< face >, const pointField & > primitiveFacePatch
A PrimitivePatch with List storage for the faces, const reference for the point field.
error FatalError
List< edge > edgeList
A List of edges.
Definition: edgeList.H:63
labelList invert(const label len, const labelUList &map)
Create an inverse one-to-one mapping.
Definition: ListOps.C:36
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
labelListList invertOneToMany(const label len, const labelUList &map)
Invert one-to-many map. Unmapped elements will be size 0.
Definition: ListOps.C:114
List< face > faceList
A List of faces.
Definition: faceListFwd.H:47
PrimitivePatch< IndirectList< face >, const pointField & > indirectPrimitivePatch
A PrimitivePatch with an IndirectList for the faces, const reference for the point field.
ListType reorder(const labelUList &oldToNew, const ListType &input, const bool prune=false)
Reorder the elements of a list.
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
label newPointi
Definition: readKivaGrid.H:496
volScalarField & e
Definition: createFields.H:11
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333
#define forAllConstIters(container, iter)
Iterate across all elements of the container object with const access.
Definition: stdFoam.H:278
Random rndGen
Definition: createFields.H:23
pointField points0(pointIOField(IOobject("points", mesh.time().constant(), polyMesh::meshSubDir, mesh, IOobject::MUST_READ, IOobject::NO_WRITE, false)))