oldCyclicPolyPatch.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) 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 "oldCyclicPolyPatch.H"
31#include "polyBoundaryMesh.H"
32#include "polyMesh.H"
33#include "OFstream.H"
34#include "patchZones.H"
35#include "matchPoints.H"
36#include "Time.H"
37#include "transformList.H"
38#include "cyclicPolyPatch.H"
39
40// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
41
42namespace Foam
43{
45
48}
49
50// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
51
52Foam::pointField Foam::oldCyclicPolyPatch::calcFaceCentres
53(
54 const UList<face>& faces,
55 const pointField& points
56)
57{
58 pointField ctrs(faces.size());
59
60 forAll(faces, facei)
61 {
62 ctrs[facei] = faces[facei].centre(points);
63 }
64
65 return ctrs;
66}
67
68
69Foam::pointField Foam::oldCyclicPolyPatch::getAnchorPoints
70(
71 const UList<face>& faces,
72 const pointField& points
73)
74{
75 pointField anchors(faces.size());
76
77 forAll(faces, facei)
78 {
79 anchors[facei] = points[faces[facei][0]];
80 }
81
82 return anchors;
83}
84
85
86Foam::label Foam::oldCyclicPolyPatch::findMaxArea
87(
88 const pointField& points,
89 const faceList& faces
90)
91{
92 label maxI = -1;
93 scalar maxAreaSqr = -GREAT;
94
95 forAll(faces, facei)
96 {
97 scalar areaSqr = magSqr(faces[facei].areaNormal(points));
98
99 if (maxAreaSqr < areaSqr)
100 {
101 maxAreaSqr = areaSqr;
102 maxI = facei;
103 }
104 }
105 return maxI;
106}
107
108
109bool Foam::oldCyclicPolyPatch::getGeometricHalves
110(
111 const primitivePatch& pp,
112 labelList& half0ToPatch,
113 labelList& half1ToPatch
114) const
115{
116 // Get geometric zones of patch by looking at normals.
117 // Method 1: any edge with sharpish angle is edge between two halves.
118 // (this will handle e.g. wedge geometries).
119 // Also two fully disconnected regions will be handled this way.
120 // Method 2: sort faces into two halves based on face normal.
121
122 // Calculate normals
123 const vectorField& faceNormals = pp.faceNormals();
124
125 // Find edges with sharp angles.
126 boolList regionEdge(pp.nEdges(), false);
127
128 const labelListList& edgeFaces = pp.edgeFaces();
129
130 label nRegionEdges = 0;
131
132 forAll(edgeFaces, edgeI)
133 {
134 const labelList& eFaces = edgeFaces[edgeI];
135
136 // Check manifold edges for sharp angle.
137 // (Non-manifold already handled by patchZones)
138 if (eFaces.size() == 2)
139 {
140 if ((faceNormals[eFaces[0]] & faceNormals[eFaces[1]])< featureCos_)
141 {
142 regionEdge[edgeI] = true;
143
144 nRegionEdges++;
145 }
146 }
147 }
148
149
150 // For every face determine zone it is connected to (without crossing
151 // any regionEdge)
152 patchZones ppZones(pp, regionEdge);
153
154 if (debug)
155 {
156 Pout<< "oldCyclicPolyPatch::getGeometricHalves : "
157 << "Found " << nRegionEdges << " edges on patch " << name()
158 << " where the cos of the angle between two connected faces"
159 << " was less than " << featureCos_ << nl
160 << "Patch divided by these and by single sides edges into "
161 << ppZones.nZones() << " parts." << endl;
162
163
164 // Dumping zones to obj files.
165
166 labelList nZoneFaces(ppZones.nZones());
167
168 for (label zoneI = 0; zoneI < ppZones.nZones(); zoneI++)
169 {
170 OFstream stream
171 (
172 boundaryMesh().mesh().time().path()
173 /name()+"_zone_"+Foam::name(zoneI)+".obj"
174 );
175 Pout<< "oldCyclicPolyPatch::getGeometricHalves : Writing zone "
176 << zoneI << " face centres to OBJ file " << stream.name()
177 << endl;
178
179 labelList zoneFaces(findIndices(ppZones, zoneI));
180
181 forAll(zoneFaces, i)
182 {
183 writeOBJ(stream, pp[zoneFaces[i]].centre(pp.points()));
184 }
185
186 nZoneFaces[zoneI] = zoneFaces.size();
187 }
188 }
189
190
191 if (ppZones.nZones() == 2)
192 {
193 half0ToPatch = findIndices(ppZones, 0);
194 half1ToPatch = findIndices(ppZones, 1);
195 }
196 else
197 {
198 if (debug)
199 {
200 Pout<< "oldCyclicPolyPatch::getGeometricHalves :"
201 << " falling back to face-normal comparison" << endl;
202 }
203 label n0Faces = 0;
204 half0ToPatch.setSize(pp.size());
205
206 label n1Faces = 0;
207 half1ToPatch.setSize(pp.size());
208
209 // Compare to face 0 normal.
210 forAll(faceNormals, facei)
211 {
212 if ((faceNormals[facei] & faceNormals[0]) > 0)
213 {
214 half0ToPatch[n0Faces++] = facei;
215 }
216 else
217 {
218 half1ToPatch[n1Faces++] = facei;
219 }
220 }
221 half0ToPatch.setSize(n0Faces);
222 half1ToPatch.setSize(n1Faces);
223
224 if (debug)
225 {
226 Pout<< "oldCyclicPolyPatch::getGeometricHalves :"
227 << " Number of faces per zone:("
228 << n0Faces << ' ' << n1Faces << ')' << endl;
229 }
230 }
231
232 if (half0ToPatch.size() != half1ToPatch.size())
233 {
234 fileName casePath(boundaryMesh().mesh().time().path());
235
236 // Dump halves
237 {
238 fileName nm0(casePath/name()+"_half0_faces.obj");
239 Pout<< "oldCyclicPolyPatch::getGeometricHalves : Writing half0"
240 << " faces to OBJ file " << nm0 << endl;
241 writeOBJ(nm0, UIndirectList<face>(pp, half0ToPatch)(), pp.points());
242
243 fileName nm1(casePath/name()+"_half1_faces.obj");
244 Pout<< "oldCyclicPolyPatch::getGeometricHalves : Writing half1"
245 << " faces to OBJ file " << nm1 << endl;
246 writeOBJ(nm1, UIndirectList<face>(pp, half1ToPatch)(), pp.points());
247 }
248
249 // Dump face centres
250 {
251 OFstream str0(casePath/name()+"_half0.obj");
252 Pout<< "oldCyclicPolyPatch::getGeometricHalves : Writing half0"
253 << " face centres to OBJ file " << str0.name() << endl;
254
255 forAll(half0ToPatch, i)
256 {
257 writeOBJ(str0, pp[half0ToPatch[i]].centre(pp.points()));
258 }
259
260 OFstream str1(casePath/name()+"_half1.obj");
261 Pout<< "oldCyclicPolyPatch::getGeometricHalves : Writing half1"
262 << " face centres to OBJ file " << str1.name() << endl;
263 forAll(half1ToPatch, i)
264 {
265 writeOBJ(str1, pp[half1ToPatch[i]].centre(pp.points()));
266 }
267 }
268
270 << "Patch " << name() << " gets decomposed in two zones of"
271 << "inequal size: " << half0ToPatch.size()
272 << " and " << half1ToPatch.size() << endl
273 << "This means that the patch is either not two separate regions"
274 << " or one region where the angle between the different regions"
275 << " is not sufficiently sharp." << endl
276 << "Please adapt the featureCos setting." << endl
277 << "Continuing with incorrect face ordering from now on!" << endl;
278
279 return false;
280 }
281
282 return true;
283}
284
285
286void Foam::oldCyclicPolyPatch::getCentresAndAnchors
287(
288 const primitivePatch& pp,
289 const faceList& half0Faces,
290 const faceList& half1Faces,
291
292 pointField& ppPoints,
293 pointField& half0Ctrs,
294 pointField& half1Ctrs,
295 pointField& anchors0,
296 scalarField& tols
297) const
298{
299 // Get geometric data on both halves.
300 half0Ctrs = calcFaceCentres(half0Faces, pp.points());
301 anchors0 = getAnchorPoints(half0Faces, pp.points());
302 half1Ctrs = calcFaceCentres(half1Faces, pp.points());
303
304 switch (transform())
305 {
306 case ROTATIONAL:
307 {
308 label face0 = getConsistentRotationFace(half0Ctrs);
309 label face1 = getConsistentRotationFace(half1Ctrs);
310
311 const vector n0 =
313 (
314 (half0Ctrs[face0] - rotationCentre_) ^ rotationAxis_
315 );
316
317 const vector n1 =
319 (
320 (half1Ctrs[face1] - rotationCentre_) ^ -rotationAxis_
321 );
322
323 if (debug)
324 {
325 Pout<< "oldCyclicPolyPatch::getCentresAndAnchors :"
326 << " Specified rotation :"
327 << " n0:" << n0 << " n1:" << n1 << endl;
328 }
329
330 // Rotation (around origin)
331 const tensor reverseT(rotationTensor(n0, -n1));
332
333 // Rotation
334 forAll(half0Ctrs, facei)
335 {
336 half0Ctrs[facei] = Foam::transform(reverseT, half0Ctrs[facei]);
337 anchors0[facei] = Foam::transform(reverseT, anchors0[facei]);
338 }
339
340 ppPoints = Foam::transform(reverseT, pp.points());
341
342 break;
343 }
344 //- Problem: usually specified translation is not accurate enough
345 //- To get proper match so keep automatic determination over here.
346 //case TRANSLATIONAL:
347 //{
348 // // Transform 0 points.
349 //
350 // if (debug)
351 // {
352 // Pout<< "oldCyclicPolyPatch::getCentresAndAnchors :"
353 // << "Specified translation : " << separationVector_
354 // << endl;
355 // }
356 //
357 // half0Ctrs += separationVector_;
358 // anchors0 += separationVector_;
359 // break;
360 //}
361 default:
362 {
363 // Assumes that cyclic is planar. This is also the initial
364 // condition for patches without faces.
365
366 // Determine the face with max area on both halves. These
367 // two faces are used to determine the transformation tensors
368 label max0I = findMaxArea(pp.points(), half0Faces);
369 const vector n0 = half0Faces[max0I].unitNormal(pp.points());
370
371 label max1I = findMaxArea(pp.points(), half1Faces);
372 const vector n1 = half1Faces[max1I].unitNormal(pp.points());
373
374 if (mag(n0 & n1) < 1-matchTolerance())
375 {
376 if (debug)
377 {
378 Pout<< "oldCyclicPolyPatch::getCentresAndAnchors :"
379 << " Detected rotation :"
380 << " n0:" << n0 << " n1:" << n1 << endl;
381 }
382
383 // Rotation (around origin)
384 const tensor reverseT(rotationTensor(n0, -n1));
385
386 // Rotation
387 forAll(half0Ctrs, facei)
388 {
389 half0Ctrs[facei] = Foam::transform
390 (
391 reverseT,
392 half0Ctrs[facei]
393 );
394 anchors0[facei] = Foam::transform
395 (
396 reverseT,
397 anchors0[facei]
398 );
399 }
400 ppPoints = Foam::transform(reverseT, pp.points());
401 }
402 else
403 {
404 // Parallel translation. Get average of all used points.
405
406 primitiveFacePatch half0(half0Faces, pp.points());
407 const pointField& half0Pts = half0.localPoints();
408 const point ctr0(sum(half0Pts)/half0Pts.size());
409
410 primitiveFacePatch half1(half1Faces, pp.points());
411 const pointField& half1Pts = half1.localPoints();
412 const point ctr1(sum(half1Pts)/half1Pts.size());
413
414 if (debug)
415 {
416 Pout<< "oldCyclicPolyPatch::getCentresAndAnchors :"
417 << " Detected translation :"
418 << " n0:" << n0 << " n1:" << n1
419 << " ctr0:" << ctr0 << " ctr1:" << ctr1 << endl;
420 }
421
422 half0Ctrs += ctr1 - ctr0;
423 anchors0 += ctr1 - ctr0;
424 ppPoints = pp.points() + ctr1 - ctr0;
425 }
426 break;
427 }
428 }
429
430
431 // Calculate typical distance per face
432 tols = matchTolerance()*calcFaceTol(half1Faces, pp.points(), half1Ctrs);
433}
434
435
436bool Foam::oldCyclicPolyPatch::matchAnchors
437(
438 const bool report,
439 const primitivePatch& pp,
440 const labelList& half0ToPatch,
441 const pointField& anchors0,
442
443 const labelList& half1ToPatch,
444 const faceList& half1Faces,
445 const labelList& from1To0,
446
447 const scalarField& tols,
448
450 labelList& rotation
451) const
452{
453 // Set faceMap such that half0 faces get first and corresponding half1
454 // faces last.
455
456 forAll(half0ToPatch, half0Facei)
457 {
458 // Label in original patch
459 label patchFacei = half0ToPatch[half0Facei];
460
461 faceMap[patchFacei] = half0Facei;
462
463 // No rotation
464 rotation[patchFacei] = 0;
465 }
466
467 bool fullMatch = true;
468
469 forAll(from1To0, half1Facei)
470 {
471 label patchFacei = half1ToPatch[half1Facei];
472
473 // This face has to match the corresponding one on half0.
474 label half0Facei = from1To0[half1Facei];
475
476 label newFacei = half0Facei + pp.size()/2;
477
478 faceMap[patchFacei] = newFacei;
479
480 // Rotate patchFacei such that its f[0] aligns with that of
481 // the corresponding face
482 // (which after shuffling will be at position half0Facei)
483
484 const point& wantedAnchor = anchors0[half0Facei];
485
486 rotation[newFacei] = getRotation
487 (
488 pp.points(),
489 half1Faces[half1Facei],
490 wantedAnchor,
491 tols[half1Facei]
492 );
493
494 if (rotation[newFacei] == -1)
495 {
496 fullMatch = false;
497
498 if (report)
499 {
500 const face& f = half1Faces[half1Facei];
502 << "Patch:" << name() << " : "
503 << "Cannot find point on face " << f
504 << " with vertices:"
505 << UIndirectList<point>(pp.points(), f)
506 << " that matches point " << wantedAnchor
507 << " when matching the halves of cyclic patch " << name()
508 << endl
509 << "Continuing with incorrect face ordering from now on!"
510 << endl;
511 }
512 }
513 }
514 return fullMatch;
515}
516
517
518Foam::label Foam::oldCyclicPolyPatch::getConsistentRotationFace
519(
520 const pointField& faceCentres
521) const
522{
523 const scalarField magRadSqr
524 (
525 magSqr((faceCentres - rotationCentre_) ^ rotationAxis_)
526 );
527 scalarField axisLen
528 (
529 (faceCentres - rotationCentre_) & rotationAxis_
530 );
531 axisLen = axisLen - min(axisLen);
532 const scalarField magLenSqr
533 (
534 magRadSqr + axisLen*axisLen
535 );
536
537 label rotFace = -1;
538 scalar maxMagLenSqr = -GREAT;
539 scalar maxMagRadSqr = -GREAT;
540 forAll(faceCentres, i)
541 {
542 if (magLenSqr[i] >= maxMagLenSqr)
543 {
544 if (magRadSqr[i] > maxMagRadSqr)
545 {
546 rotFace = i;
547 maxMagLenSqr = magLenSqr[i];
548 maxMagRadSqr = magRadSqr[i];
549 }
550 }
551 }
552
553 if (debug)
554 {
555 Info<< "getConsistentRotationFace(const pointField&)" << nl
556 << " rotFace = " << rotFace << nl
557 << " point = " << faceCentres[rotFace] << endl;
558 }
559
560 return rotFace;
561}
562
563
564// * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * * * * * //
565
567(
568 const word& name,
569 const label size,
570 const label start,
571 const label index,
572 const polyBoundaryMesh& bm,
573 const word& patchType,
575)
576:
577 coupledPolyPatch(name, size, start, index, bm, patchType, transform),
578 featureCos_(0.9),
579 rotationAxis_(Zero),
580 rotationCentre_(Zero),
581 separationVector_(Zero)
582{}
583
584
586(
587 const word& name,
588 const dictionary& dict,
589 const label index,
590 const polyBoundaryMesh& bm,
591 const word& patchType
592)
593:
594 coupledPolyPatch(name, dict, index, bm, patchType),
595 featureCos_(0.9),
596 rotationAxis_(Zero),
597 rotationCentre_(Zero),
598 separationVector_(Zero)
599{
600 if (dict.found("neighbourPatch"))
601 {
603 << "Found \"neighbourPatch\" entry when reading cyclic patch "
604 << name << endl
605 << "Is this mesh already with split cyclics?" << endl
606 << "If so run a newer version that supports it"
607 << ", if not comment out the \"neighbourPatch\" entry and re-run"
608 << exit(FatalIOError);
609 }
610
611 dict.readIfPresent("featureCos", featureCos_);
612
613 switch (transform())
614 {
615 case ROTATIONAL:
616 {
617 dict.readEntry("rotationAxis", rotationAxis_);
618 dict.readEntry("rotationCentre", rotationCentre_);
619 break;
620 }
621 case TRANSLATIONAL:
622 {
623 dict.readEntry("separationVector", separationVector_);
624 break;
625 }
626 default:
627 {
628 // no additional info required
629 }
630 }
631}
632
633
635(
636 const oldCyclicPolyPatch& pp,
637 const polyBoundaryMesh& bm
638)
639:
640 coupledPolyPatch(pp, bm),
641 featureCos_(pp.featureCos_),
642 rotationAxis_(pp.rotationAxis_),
643 rotationCentre_(pp.rotationCentre_),
644 separationVector_(pp.separationVector_)
645{}
646
647
649(
650 const oldCyclicPolyPatch& pp,
651 const polyBoundaryMesh& bm,
652 const label index,
653 const label newSize,
654 const label newStart
655)
656:
657 coupledPolyPatch(pp, bm, index, newSize, newStart),
658 featureCos_(pp.featureCos_),
659 rotationAxis_(pp.rotationAxis_),
660 rotationCentre_(pp.rotationCentre_),
661 separationVector_(pp.separationVector_)
662{}
663
664
665// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
666
668{}
669
670
671// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
672
674{
676}
677
678
680(
681 const primitivePatch& referPatch,
682 const pointField& thisCtrs,
683 const vectorField& thisAreas,
684 const pointField& thisCc,
685 const pointField& nbrCtrs,
686 const vectorField& nbrAreas,
687 const pointField& nbrCc
688)
689{}
690
691
693{}
694
695
697(
698 PstreamBuffers& pBufs,
699 const pointField& p
700)
701{
703}
704
705
707(
708 PstreamBuffers& pBufs,
709 const pointField& p
710)
711{
712 polyPatch::movePoints(pBufs, p);
713}
714
715
717{
719}
720
721
723{
725}
726
727
729(
731 const primitivePatch& pp
732) const
733{}
734
735
736// Return new ordering. Ordering is -faceMap: for every face index
737// the new face -rotation:for every new face the clockwise shift
738// of the original face. Return false if nothing changes (faceMap
739// is identity, rotation is 0)
741(
743 const primitivePatch& pp,
745 labelList& rotation
746) const
747{
748 faceMap.setSize(pp.size());
749 faceMap = -1;
750
751 rotation.setSize(pp.size());
752 rotation = 0;
753
754 if (pp.empty())
755 {
756 // No faces, nothing to change.
757 return false;
758 }
759
760 if (pp.size()&1)
761 {
763 << "Size of cyclic " << name() << " should be a multiple of 2"
764 << ". It is " << pp.size() << abort(FatalError);
765 }
766
767 label halfSize = pp.size()/2;
768
769 // Supplied primitivePatch already with new points.
770 // Cyclics are limited to one transformation tensor
771 // currently anyway (i.e. straight plane) so should not be too big a
772 // problem.
773
774
775 // Indices of faces on half0
776 labelList half0ToPatch;
777 // Indices of faces on half1
778 labelList half1ToPatch;
779
780
781 // 1. Test if already correctly oriented by starting from trivial ordering.
782 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
783
784 half0ToPatch = identity(halfSize);
785 half1ToPatch = half0ToPatch + halfSize;
786
787 // Get faces
788 faceList half0Faces(UIndirectList<face>(pp, half0ToPatch));
789 faceList half1Faces(UIndirectList<face>(pp, half1ToPatch));
790
791 // Get geometric quantities
792 pointField half0Ctrs, half1Ctrs, anchors0, ppPoints;
793 scalarField tols;
794 getCentresAndAnchors
795 (
796 pp,
797 half0Faces,
798 half1Faces,
799
800 ppPoints,
801 half0Ctrs,
802 half1Ctrs,
803 anchors0,
804 tols
805 );
806
807 // Geometric match of face centre vectors
808 labelList from1To0;
809 bool matchedAll = matchPoints
810 (
811 half1Ctrs,
812 half0Ctrs,
813 tols,
814 false,
815 from1To0
816 );
817
818 if (debug)
819 {
820 Pout<< "oldCyclicPolyPatch::order : test if already ordered:"
821 << matchedAll << endl;
822
823 // Dump halves
824 fileName nm0("match1_"+name()+"_half0_faces.obj");
825 Pout<< "oldCyclicPolyPatch::order : Writing half0"
826 << " faces to OBJ file " << nm0 << endl;
827 writeOBJ(nm0, half0Faces, ppPoints);
828
829 fileName nm1("match1_"+name()+"_half1_faces.obj");
830 Pout<< "oldCyclicPolyPatch::order : Writing half1"
831 << " faces to OBJ file " << nm1 << endl;
832 writeOBJ(nm1, half1Faces, pp.points());
833
834 OFstream ccStr
835 (
836 boundaryMesh().mesh().time().path()
837 /"match1_"+ name() + "_faceCentres.obj"
838 );
839 Pout<< "oldCyclicPolyPatch::order : "
840 << "Dumping currently found cyclic match as lines between"
841 << " corresponding face centres to file " << ccStr.name()
842 << endl;
843
844 // Recalculate untransformed face centres
845 //pointField rawHalf0Ctrs = calcFaceCentres(half0Faces, pp.points());
846 label vertI = 0;
847
848 forAll(half1Ctrs, i)
849 {
850 //if (from1To0[i] != -1)
851 {
852 // Write edge between c1 and c0
853 //const point& c0 = rawHalf0Ctrs[from1To0[i]];
854 //const point& c0 = half0Ctrs[from1To0[i]];
855 const point& c0 = half0Ctrs[i];
856 const point& c1 = half1Ctrs[i];
857 writeOBJ(ccStr, c0, c1, vertI);
858 }
859 }
860 }
861
862
863 // 2. Ordered in pairs (so 0,1 coupled and 2,3 etc.)
864 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
865
866 if (!matchedAll)
867 {
868 label facei = 0;
869 for (label i = 0; i < halfSize; i++)
870 {
871 half0ToPatch[i] = facei++;
872 half1ToPatch[i] = facei++;
873 }
874
875 // And redo all matching
876 half0Faces = UIndirectList<face>(pp, half0ToPatch);
877 half1Faces = UIndirectList<face>(pp, half1ToPatch);
878
879 getCentresAndAnchors
880 (
881 pp,
882 half0Faces,
883 half1Faces,
884
885 ppPoints,
886 half0Ctrs,
887 half1Ctrs,
888 anchors0,
889 tols
890 );
891
892 // Geometric match of face centre vectors
893 matchedAll = matchPoints
894 (
895 half1Ctrs,
896 half0Ctrs,
897 tols,
898 false,
899 from1To0
900 );
901
902 if (debug)
903 {
904 Pout<< "oldCyclicPolyPatch::order : test if pairwise ordered:"
905 << matchedAll << endl;
906 // Dump halves
907 fileName nm0("match2_"+name()+"_half0_faces.obj");
908 Pout<< "oldCyclicPolyPatch::order : Writing half0"
909 << " faces to OBJ file " << nm0 << endl;
910 writeOBJ(nm0, half0Faces, ppPoints);
911
912 fileName nm1("match2_"+name()+"_half1_faces.obj");
913 Pout<< "oldCyclicPolyPatch::order : Writing half1"
914 << " faces to OBJ file " << nm1 << endl;
915 writeOBJ(nm1, half1Faces, pp.points());
916
917 OFstream ccStr
918 (
919 boundaryMesh().mesh().time().path()
920 /"match2_"+name()+"_faceCentres.obj"
921 );
922 Pout<< "oldCyclicPolyPatch::order : "
923 << "Dumping currently found cyclic match as lines between"
924 << " corresponding face centres to file " << ccStr.name()
925 << endl;
926
927 // Recalculate untransformed face centres
928 label vertI = 0;
929
930 forAll(half1Ctrs, i)
931 {
932 if (from1To0[i] != -1)
933 {
934 // Write edge between c1 and c0
935 const point& c0 = half0Ctrs[from1To0[i]];
936 const point& c1 = half1Ctrs[i];
937 writeOBJ(ccStr, c0, c1, vertI);
938 }
939 }
940 }
941 }
942
943
944 // 3. Baffles(coincident faces) converted into cyclics (e.g. jump)
945 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
946
947 if (!matchedAll)
948 {
949 label baffleI = 0;
950
951 forAll(pp, facei)
952 {
953 const face& f = pp.localFaces()[facei];
954 const labelList& pFaces = pp.pointFaces()[f[0]];
955
956 label matchedFacei = -1;
957
958 forAll(pFaces, i)
959 {
960 label otherFacei = pFaces[i];
961
962 if (otherFacei > facei)
963 {
964 const face& otherF = pp.localFaces()[otherFacei];
965
966 // Note: might pick up two similar oriented faces
967 // (but that is illegal anyway)
968 if (f == otherF)
969 {
970 matchedFacei = otherFacei;
971 break;
972 }
973 }
974 }
975
976 if (matchedFacei != -1)
977 {
978 half0ToPatch[baffleI] = facei;
979 half1ToPatch[baffleI] = matchedFacei;
980 baffleI++;
981 }
982 }
983
984 if (baffleI == halfSize)
985 {
986 // And redo all matching
987 half0Faces = UIndirectList<face>(pp, half0ToPatch);
988 half1Faces = UIndirectList<face>(pp, half1ToPatch);
989
990 getCentresAndAnchors
991 (
992 pp,
993 half0Faces,
994 half1Faces,
995
996 ppPoints,
997 half0Ctrs,
998 half1Ctrs,
999 anchors0,
1000 tols
1001 );
1002
1003 // Geometric match of face centre vectors
1004 matchedAll = matchPoints
1005 (
1006 half1Ctrs,
1007 half0Ctrs,
1008 tols,
1009 false,
1010 from1To0
1011 );
1012
1013 if (debug)
1014 {
1015 Pout<< "oldCyclicPolyPatch::order : test if baffles:"
1016 << matchedAll << endl;
1017 // Dump halves
1018 fileName nm0("match3_"+name()+"_half0_faces.obj");
1019 Pout<< "oldCyclicPolyPatch::order : Writing half0"
1020 << " faces to OBJ file " << nm0 << endl;
1021 writeOBJ(nm0, half0Faces, ppPoints);
1022
1023 fileName nm1("match3_"+name()+"_half1_faces.obj");
1024 Pout<< "oldCyclicPolyPatch::order : Writing half1"
1025 << " faces to OBJ file " << nm1 << endl;
1026 writeOBJ(nm1, half1Faces, pp.points());
1027
1028 OFstream ccStr
1029 (
1030 boundaryMesh().mesh().time().path()
1031 /"match3_"+ name() + "_faceCentres.obj"
1032 );
1033 Pout<< "oldCyclicPolyPatch::order : "
1034 << "Dumping currently found cyclic match as lines between"
1035 << " corresponding face centres to file " << ccStr.name()
1036 << endl;
1037
1038 // Recalculate untransformed face centres
1039 label vertI = 0;
1040
1041 forAll(half1Ctrs, i)
1042 {
1043 if (from1To0[i] != -1)
1044 {
1045 // Write edge between c1 and c0
1046 const point& c0 = half0Ctrs[from1To0[i]];
1047 const point& c1 = half1Ctrs[i];
1048 writeOBJ(ccStr, c0, c1, vertI);
1049 }
1050 }
1051 }
1052 }
1053 }
1054
1055
1056 // 4. Automatic geometric ordering
1057 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1058
1059 if (!matchedAll)
1060 {
1061 // Split faces according to feature angle or topology
1062 bool okSplit = getGeometricHalves(pp, half0ToPatch, half1ToPatch);
1063
1064 if (!okSplit)
1065 {
1066 // Did not split into two equal parts.
1067 return false;
1068 }
1069
1070 // And redo all matching
1071 half0Faces = UIndirectList<face>(pp, half0ToPatch);
1072 half1Faces = UIndirectList<face>(pp, half1ToPatch);
1073
1074 getCentresAndAnchors
1075 (
1076 pp,
1077 half0Faces,
1078 half1Faces,
1079
1080 ppPoints,
1081 half0Ctrs,
1082 half1Ctrs,
1083 anchors0,
1084 tols
1085 );
1086
1087 // Geometric match of face centre vectors
1088 matchedAll = matchPoints
1089 (
1090 half1Ctrs,
1091 half0Ctrs,
1092 tols,
1093 false,
1094 from1To0
1095 );
1096
1097 if (debug)
1098 {
1099 Pout<< "oldCyclicPolyPatch::order : automatic ordering result:"
1100 << matchedAll << endl;
1101 // Dump halves
1102 fileName nm0("match4_"+name()+"_half0_faces.obj");
1103 Pout<< "oldCyclicPolyPatch::order : Writing half0"
1104 << " faces to OBJ file " << nm0 << endl;
1105 writeOBJ(nm0, half0Faces, ppPoints);
1106
1107 fileName nm1("match4_"+name()+"_half1_faces.obj");
1108 Pout<< "oldCyclicPolyPatch::order : Writing half1"
1109 << " faces to OBJ file " << nm1 << endl;
1110 writeOBJ(nm1, half1Faces, pp.points());
1111
1112 OFstream ccStr
1113 (
1114 boundaryMesh().mesh().time().path()
1115 /"match4_"+ name() + "_faceCentres.obj"
1116 );
1117 Pout<< "oldCyclicPolyPatch::order : "
1118 << "Dumping currently found cyclic match as lines between"
1119 << " corresponding face centres to file " << ccStr.name()
1120 << endl;
1121
1122 // Recalculate untransformed face centres
1123 label vertI = 0;
1124
1125 forAll(half1Ctrs, i)
1126 {
1127 if (from1To0[i] != -1)
1128 {
1129 // Write edge between c1 and c0
1130 const point& c0 = half0Ctrs[from1To0[i]];
1131 const point& c1 = half1Ctrs[i];
1132 writeOBJ(ccStr, c0, c1, vertI);
1133 }
1134 }
1135 }
1136 }
1137
1138
1139 if (!matchedAll || debug)
1140 {
1141 // Dump halves
1142 fileName nm0(name()+"_half0_faces.obj");
1143 Pout<< "oldCyclicPolyPatch::order : Writing half0"
1144 << " faces to OBJ file " << nm0 << endl;
1145 writeOBJ(nm0, half0Faces, pp.points());
1146
1147 fileName nm1(name()+"_half1_faces.obj");
1148 Pout<< "oldCyclicPolyPatch::order : Writing half1"
1149 << " faces to OBJ file " << nm1 << endl;
1150 writeOBJ(nm1, half1Faces, pp.points());
1151
1152 OFstream ccStr
1153 (
1154 boundaryMesh().mesh().time().path()
1155 /name() + "_faceCentres.obj"
1156 );
1157 Pout<< "oldCyclicPolyPatch::order : "
1158 << "Dumping currently found cyclic match as lines between"
1159 << " corresponding face centres to file " << ccStr.name()
1160 << endl;
1161
1162 // Recalculate untransformed face centres
1163 //pointField rawHalf0Ctrs = calcFaceCentres(half0Faces, pp.points());
1164 label vertI = 0;
1165
1166 forAll(half1Ctrs, i)
1167 {
1168 if (from1To0[i] != -1)
1169 {
1170 // Write edge between c1 and c0
1171 //const point& c0 = rawHalf0Ctrs[from1To0[i]];
1172 const point& c0 = half0Ctrs[from1To0[i]];
1173 const point& c1 = half1Ctrs[i];
1174 writeOBJ(ccStr, c0, c1, vertI);
1175 }
1176 }
1177 }
1178
1179
1180 if (!matchedAll)
1181 {
1183 << "Patch:" << name() << " : "
1184 << "Cannot match vectors to faces on both sides of patch" << endl
1185 << " Perhaps your faces do not match?"
1186 << " The obj files written contain the current match." << endl
1187 << " Continuing with incorrect face ordering from now on!"
1188 << endl;
1189
1190 return false;
1191 }
1192
1193
1194 // Set faceMap such that half0 faces get first and corresponding half1
1195 // faces last.
1196 matchAnchors
1197 (
1198 true, // report if anchor matching error
1199 pp,
1200 half0ToPatch,
1201 anchors0,
1202 half1ToPatch,
1203 half1Faces,
1204 from1To0,
1205 tols,
1206 faceMap,
1207 rotation
1208 );
1209
1210 // Return false if no change necessary, true otherwise.
1211
1212 forAll(faceMap, facei)
1213 {
1214 if (faceMap[facei] != facei || rotation[facei] != 0)
1215 {
1216 return true;
1217 }
1218 }
1219
1220 return false;
1221}
1222
1223
1225{
1226 // Replacement of polyPatch::write to write 'cyclic' instead of type():
1229 os.writeEntry("nFaces", size());
1230 os.writeEntry("startFace", start());
1231
1232
1233 os.writeEntry("featureCos", featureCos_);
1234 switch (transform())
1235 {
1236 case ROTATIONAL:
1237 {
1238 os.writeEntry("rotationAxis", rotationAxis_);
1239 os.writeEntry("rotationCentre", rotationCentre_);
1240 break;
1241 }
1242 case TRANSLATIONAL:
1243 {
1244 os.writeEntry("separationVector", separationVector_);
1245 break;
1246 }
1247 default:
1248 {
1249 // no additional info to write
1250 }
1251 }
1252
1254 << "Please run foamUpgradeCyclics to convert these old-style"
1255 << " cyclics into two separate cyclics patches."
1256 << endl;
1257}
1258
1259
1260// ************************************************************************* //
Macros for easy insertion into run-time selection tables.
#define addToRunTimeSelectionTable(baseType, thisType, argNames)
Add to construction table with typeName as the key.
void setSize(const label n)
Alias for resize()
Definition: List.H:218
Output to file stream, using an OSstream.
Definition: OFstream.H:57
virtual const fileName & name() const
Read/write access to the name of the stream.
Definition: OSstream.H:107
virtual const fileName & name() const
Get the name of the stream.
Definition: OSstream.H:107
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:62
Ostream & writeEntry(const keyType &key, const T &value)
Write a keyword/value entry.
Definition: Ostream.H:239
A list of faces which address into the list of points.
const Field< point_type > & points() const noexcept
Return reference to global points.
const labelListList & pointFaces() const
Return point-face addressing.
const List< face_type > & localFaces() const
Return patch faces addressing into local point list.
Buffers for inter-processor communications streams (UOPstream, UIPstream).
A List with indirect addressing. Like IndirectList but does not store addressing.
Definition: IndirectList.H:79
Addressing for all faces on surface of mesh. Can either be read from polyMesh or from triSurface....
Definition: boundaryMesh.H:63
The coupledPolyPatch is an abstract base class for patches that couple regions of the computational d...
virtual transformType transform() const
Type of transform.
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
bool found(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Search for an entry (const access) with the given keyword.
Definition: dictionaryI.H:87
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
bool readEntry(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX, bool mandatory=true) const
void calcGeometry()
Calculate the geometry for the patches.
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:75
A class for handling file names.
Definition: fileName.H:76
virtual bool write()
Write the output fields.
virtual void initMovePoints()
Initialise the patches for moving points.
Definition: fvPatch.C:188
order
Enumeration specifying required accuracy.
Definition: meshToMesh0.H:148
void movePoints()
Update for new mesh geometry.
void updateMesh()
Update for new mesh topology.
'old' style cyclic polyPatch with all faces in single patch. Does ordering but cannot be used to run....
virtual void initGeometry(PstreamBuffers &)
Initialise the calculation of the patch geometry.
virtual void initOrder(PstreamBuffers &, const primitivePatch &) const
Initialize ordering for primitivePatch. Does not.
virtual void initUpdateMesh(PstreamBuffers &)
Initialise the update of the patch topology.
const word & name() const noexcept
The patch name.
A polyBoundaryMesh is a polyPatch list with additional search methods and registered IO.
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:75
virtual void initGeometry(PstreamBuffers &)
Initialise the calculation of the patch geometry.
Definition: polyPatch.H:102
virtual void initUpdateMesh(PstreamBuffers &)
Initialise the update of the patch topology.
Definition: polyPatch.H:117
Tensor of scalars, i.e. Tensor<scalar>.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
A class for handling words, derived from Foam::string.
Definition: word.H:68
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition: className.H:121
fileName path(UMean.rootPath()/UMean.caseName()/"graphs"/UMean.instance())
volScalarField & p
dynamicFvMesh & mesh
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:473
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
OBJstream os(runTime.globalPath()/outputName)
const pointField & points
surfaceVectorField faceNormals(mesh.Sf()/mesh.magSf())
Determine correspondence between points. See below.
#define WarningInFunction
Report a warning using Foam::Warning.
#define SeriousErrorInFunction
Report an error message using Foam::SeriousError.
void writeOBJ(Ostream &os, const point &pt)
Write obj representation of a point.
Definition: meshTools.C:203
Namespace for OpenFOAM.
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i)
Definition: labelList.C:38
dimensionSet transform(const dimensionSet &ds)
Return the argument; transformations do not change the dimensions.
Definition: dimensionSet.C:536
List< label > labelList
A List of labels.
Definition: List.H:66
quaternion normalised(const quaternion &q)
Return the normalised (unit) quaternion of the given quaternion.
Definition: quaternionI.H:680
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
tensor rotationTensor(const vector &n1, const vector &n2)
Rotational transformation tensor from vector n1 to n2.
Definition: transform.H:51
messageStream Info
Information stream (stdout output on master, null elsewhere)
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.
PrimitivePatch< SubList< face >, const pointField & > primitivePatch
A PrimitivePatch with a SubList addressing for the faces, const reference for the point field.
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
Field< vector > vectorField
Specialisation of Field<T> for vector.
IOerror FatalIOError
List< bool > boolList
A List of bools.
Definition: List.H:64
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
PrimitivePatch< List< face >, const pointField & > primitiveFacePatch
A PrimitivePatch with List storage for the faces, const reference for the point field.
error FatalError
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
List< face > faceList
A List of faces.
Definition: faceListFwd.H:47
labelList findIndices(const ListType &input, typename ListType::const_reference val, label start=0)
Linear search to find all occurrences of given element.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
Info<< "Finished reading KIVA file"<< endl;cellShapeList cellShapes(nPoints);labelList cellZoning(nPoints, -1);const cellModel &hex=cellModel::ref(cellModel::HEX);labelList hexLabels(8);label activeCells=0;labelList pointMap(nPoints);forAll(pointMap, i){ pointMap[i]=i;}for(label i=0;i< nPoints;i++){ if(f[i] > 0.0) { hexLabels[0]=i;hexLabels[1]=i1tab[i];hexLabels[2]=i3tab[i1tab[i]];hexLabels[3]=i3tab[i];hexLabels[4]=i8tab[i];hexLabels[5]=i1tab[i8tab[i]];hexLabels[6]=i3tab[i1tab[i8tab[i]]];hexLabels[7]=i3tab[i8tab[i]];cellShapes[activeCells].reset(hex, hexLabels);edgeList edges=cellShapes[activeCells].edges();forAll(edges, ei) { if(edges[ei].mag(points)< SMALL) { label start=pointMap[edges[ei].start()];while(start !=pointMap[start]) { start=pointMap[start];} label end=pointMap[edges[ei].end()];while(end !=pointMap[end]) { end=pointMap[end];} label minLabel=min(start, end);pointMap[start]=pointMap[end]=minLabel;} } cellZoning[activeCells]=idreg[i];activeCells++;}}cellShapes.setSize(activeCells);cellZoning.setSize(activeCells);forAll(cellShapes, celli){ cellShape &cs=cellShapes[celli];forAll(cs, i) { cs[i]=pointMap[cs[i]];} cs.collapse();}label bcIDs[11]={-1, 0, 2, 4, -1, 5, -1, 6, 7, 8, 9};const label nBCs=12;const word *kivaPatchTypes[nBCs]={ &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &symmetryPolyPatch::typeName, &wedgePolyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &symmetryPolyPatch::typeName, &oldCyclicPolyPatch::typeName};enum patchTypeNames{ PISTON, VALVE, LINER, CYLINDERHEAD, AXIS, WEDGE, INFLOW, OUTFLOW, PRESIN, PRESOUT, SYMMETRYPLANE, CYCLIC};const char *kivaPatchNames[nBCs]={ "piston", "valve", "liner", "cylinderHead", "axis", "wedge", "inflow", "outflow", "presin", "presout", "symmetryPlane", "cyclic"};List< SLList< face > > pFaces[nBCs]
Definition: readKivaGrid.H:235
labelList f(nPoints)
dictionary dict
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333
static const char *const typeName
The type name used in ensight case files.
Spatial transformation functions for list of values and primitive fields.