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 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
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 
42 namespace Foam
43 {
44  defineTypeNameAndDebug(oldCyclicPolyPatch, 0);
45 
46  addToRunTimeSelectionTable(polyPatch, oldCyclicPolyPatch, word);
47  addToRunTimeSelectionTable(polyPatch, oldCyclicPolyPatch, dictionary);
48 }
49 
50 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
51 
52 Foam::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 
69 Foam::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 
86 Foam::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 
109 bool 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 
286 void 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 =
312  normalised
313  (
314  (half0Ctrs[face0] - rotationCentre_) ^ rotationAxis_
315  );
316 
317  const vector n1 =
318  normalised
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 
436 bool 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 
518 Foam::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 
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 {}
664 
665 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
666 
668 {}
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 {
713 }
714 
715 
717 {
719 }
720 
721 
723 {
724  polyPatch::updateMesh(pBufs);
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():
1227  os.writeEntry("type", cyclicPolyPatch::typeName);
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 // ************************************************************************* //
const List< face_type > & localFaces() const
Return patch faces addressing into local point list.
dictionary dict
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &f1)
A class for handling file names.
Definition: fileName.H:72
virtual bool order(PstreamBuffers &, const primitivePatch &, labelList &faceMap, labelList &rotation) const
Return new ordering for primitivePatch.
void write(Ostream &os) const
Write (physicalType, inGroups) dictionary entries (without surrounding braces)
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:68
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
virtual void initUpdateMesh(PstreamBuffers &)
Initialise the update of the patch topology.
Definition: polyPatch.H:135
Output to file stream, using an OSstream.
Definition: OFstream.H:49
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Addressing for all faces on surface of mesh. Can either be read from polyMesh or from triSurface...
Definition: boundaryMesh.H:58
Tensor< scalar > tensor
Definition: symmTensor.H:57
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
&#39;old&#39; style cyclic polyPatch with all faces in single patch. Does ordering but cannot be used to run...
The coupledPolyPatch is an abstract base class for patches that couple regions of the computational d...
Ostream & writeEntry(const keyType &key, const T &value)
Write a keyword/value entry.
Definition: Ostream.H:321
labelList findIndices(const ListType &input, typename ListType::const_reference val, label start=0)
Linear search to find all occurrences of given element.
void writeOBJ(Ostream &os, const point &pt)
Write obj representation of a point.
Definition: meshTools.C:196
quaternion normalised(const quaternion &q)
Return the normalised (unit) quaternion of the given quaternion.
Definition: quaternionI.H:674
tensor rotationTensor(const vector &n1, const vector &n2)
Rotational transformation tensor from vector n1 to n2.
Definition: transform.H:47
virtual void initMovePoints(PstreamBuffers &, const pointField &)
Initialise the patches for moving points.
Definition: polyPatch.H:124
virtual const fileName & name() const override
Get the name of the output serial stream. (eg, the name of the Fstream file name) ...
Definition: OSstream.H:128
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
virtual void calcGeometry(PstreamBuffers &)
Calculate the patch geometry.
Determine correspondence between points. See below.
Macros for easy insertion into run-time selection tables.
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
virtual void movePoints(PstreamBuffers &, const pointField &p)
Correct patches after moving points.
Definition: polyPatch.C:53
#define SeriousErrorInFunction
Report an error message using Foam::SeriousError.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
virtual void initGeometry(PstreamBuffers &)
Initialise the calculation of the patch geometry.
const Field< point_type > & faceNormals() const
Return face unit normals for patch.
List< face > faceList
List of faces.
Definition: faceListFwd.H:39
virtual void movePoints(PstreamBuffers &, const pointField &)
Correct patches after moving points.
A list of faces which address into the list of points.
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:29
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
void setSize(const label n)
Alias for resize()
Definition: List.H:316
dynamicFvMesh & mesh
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
const pointField & points
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i), works like std::iota() but returning a...
Definition: labelLists.C:44
A class for handling words, derived from Foam::string.
Definition: word.H:63
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
const labelListList & edgeFaces() const
Return edge-face addressing.
virtual void updateMesh(PstreamBuffers &)
Update of the patch topology.
Definition: polyPatch.C:59
Vector< scalar > vector
Definition: vector.H:57
const Field< point_type > & points() const noexcept
Return reference to global points.
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
errorManip< error > abort(error &err)
Definition: errorManip.H:139
virtual void initOrder(PstreamBuffers &, const primitivePatch &) const
Initialize ordering for primitivePatch. Does not.
A polyBoundaryMesh is a polyPatch list with additional search methods and registered IO...
virtual void initUpdateMesh(PstreamBuffers &)
Initialise the update of the patch topology.
const labelListList & pointFaces() const
Return point-face addressing.
const word & name() const noexcept
The patch name.
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:56
label nEdges() const
Number of edges in patch.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
int debug
Static debugging option.
OBJstream os(runTime.globalPath()/outputName)
fileName path(UMean.rootPath()/UMean.caseName()/"graphs"/UMean.instance())
defineTypeNameAndDebug(combustionModel, 0)
labelList f(nPoints)
Buffers for inter-processor communications streams (UOPstream, UIPstream).
PrimitivePatch< List< face >, const pointField & > primitiveFacePatch
A PrimitivePatch with List storage for the faces, const reference for the point field.
PrimitivePatch< SubList< face >, const pointField & > primitivePatch
A PrimitivePatch with a SubList addressing for the faces, const reference for the point field...
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
Spatial transformation functions for list of values and primitive fields.
virtual void initGeometry(PstreamBuffers &)
Initialise the calculation of the patch geometry.
Definition: polyPatch.H:112
vector point
Point is a vector.
Definition: point.H:37
#define WarningInFunction
Report a warning using Foam::Warning.
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:627
virtual void initMovePoints(PstreamBuffers &, const pointField &)
Initialise the patches for moving points.
A List with indirect addressing. Like IndirectList but does not store addressing. ...
Definition: faMatrix.H:56
virtual void updateMesh(PstreamBuffers &)
Update of the patch topology.
const dimensionedScalar c1
First radiation constant: default SI units: [W/m2].
messageStream Info
Information stream (stdout output on master, null elsewhere)
virtual transformType transform() const
Type of transform.
Field< vector > vectorField
Specialisation of Field<T> for vector.
virtual void write(Ostream &) const
Write the polyPatch data as a dictionary.
List< label > labelList
A List of labels.
Definition: List.H:62
volScalarField & p
dimensionSet transform(const dimensionSet &ds)
Return the argument; transformations do not change the dimensions.
Definition: dimensionSet.C:521
List< bool > boolList
A List of bools.
Definition: List.H:60
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
Namespace for OpenFOAM.
addToRunTimeSelectionTable(functionObject, pointHistory, dictionary)
oldCyclicPolyPatch(const word &name, const label size, const label start, const label index, const polyBoundaryMesh &bm, const word &patchType, const transformType transform=UNKNOWN)
Construct from components.
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127