snappySnapDriverFeature.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-2015 OpenFOAM Foundation
9  Copyright (C) 2015-2020 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 "snappySnapDriver.H"
30 #include "polyTopoChange.H"
31 #include "syncTools.H"
32 #include "fvMesh.H"
33 #include "OBJstream.H"
34 #include "motionSmoother.H"
35 #include "refinementSurfaces.H"
36 #include "refinementFeatures.H"
37 #include "unitConversion.H"
38 #include "plane.H"
39 #include "featureEdgeMesh.H"
40 #include "treeDataPoint.H"
41 #include "indexedOctree.H"
42 #include "snapParameters.H"
43 #include "PatchTools.H"
44 #include "pyramidPointFaceRef.H"
45 #include "localPointRegion.H"
46 
47 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
48 
49 namespace Foam
50 {
51  template<class T>
53  {
54  public:
55 
56  void operator()(List<T>& x, const List<T>& y) const
57  {
58  label sz = x.size();
59  x.setSize(sz+y.size());
60  forAll(y, i)
61  {
62  x[sz++] = y[i];
63  }
64  }
65  };
66 }
67 
68 
69 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
70 
71 bool Foam::snappySnapDriver::isFeaturePoint
72 (
73  const scalar featureCos,
74  const indirectPrimitivePatch& pp,
75  const bitSet& isFeatureEdge,
76  const label pointi
77 ) const
78 {
79  const pointField& points = pp.localPoints();
80  const edgeList& edges = pp.edges();
81  const labelList& pEdges = pp.pointEdges()[pointi];
82 
83  label nFeatEdges = 0;
84 
85  forAll(pEdges, i)
86  {
87  if (isFeatureEdge[pEdges[i]])
88  {
89  nFeatEdges++;
90 
91  for (label j = i+1; j < pEdges.size(); j++)
92  {
93  if (isFeatureEdge[pEdges[j]])
94  {
95  const edge& ei = edges[pEdges[i]];
96  const edge& ej = edges[pEdges[j]];
97 
98  const point& p = points[pointi];
99  const point& pi = points[ei.otherVertex(pointi)];
100  const point& pj = points[ej.otherVertex(pointi)];
101 
102  vector vi = p-pi;
103  scalar viMag = mag(vi);
104 
105  vector vj = pj-p;
106  scalar vjMag = mag(vj);
107 
108  if
109  (
110  viMag > SMALL
111  && vjMag > SMALL
112  && ((vi/viMag & vj/vjMag) < featureCos)
113  )
114  {
115  return true;
116  }
117  }
118  }
119  }
120  }
121 
122  if (nFeatEdges == 1)
123  {
124  // End of feature-edge string
125  return true;
126  }
127 
128  return false;
129 }
130 
131 
132 void Foam::snappySnapDriver::smoothAndConstrain
133 (
134  const bitSet& isPatchMasterEdge,
135  const indirectPrimitivePatch& pp,
136  const labelList& meshEdges,
137  const List<pointConstraint>& constraints,
138  vectorField& disp
139 ) const
140 {
141  const fvMesh& mesh = meshRefiner_.mesh();
142 
143  for (label avgIter = 0; avgIter < 20; avgIter++)
144  {
145  // Calculate average displacement of neighbours
146  // - unconstrained (i.e. surface) points use average of all
147  // neighbouring points
148  // - from testing it has been observed that it is not beneficial
149  // to have edge constrained points use average of all edge or point
150  // constrained neighbours since they're already attracted to
151  // the nearest point on the feature.
152  // Having them attract to point-constrained neighbours does not
153  // make sense either since there is usually just one of them so
154  // it severely distorts it.
155  // - same for feature points. They are already attracted to the
156  // nearest feature point.
157 
158  vectorField dispSum(pp.nPoints(), Zero);
159  labelList dispCount(pp.nPoints(), Zero);
160 
161  const labelListList& pointEdges = pp.pointEdges();
162  const edgeList& edges = pp.edges();
163 
164  forAll(pointEdges, pointi)
165  {
166  const labelList& pEdges = pointEdges[pointi];
167 
168  label nConstraints = constraints[pointi].first();
169 
170  if (nConstraints <= 1)
171  {
172  forAll(pEdges, i)
173  {
174  label edgei = pEdges[i];
175 
176  if (isPatchMasterEdge[edgei])
177  {
178  label nbrPointi = edges[edgei].otherVertex(pointi);
179  if (constraints[nbrPointi].first() >= nConstraints)
180  {
181  dispSum[pointi] += disp[nbrPointi];
182  dispCount[pointi]++;
183  }
184  }
185  }
186  }
187  }
188 
190  (
191  mesh,
192  pp.meshPoints(),
193  dispSum,
194  plusEqOp<point>(),
195  vector::zero,
197  );
199  (
200  mesh,
201  pp.meshPoints(),
202  dispCount,
203  plusEqOp<label>(),
204  label(0),
206  );
207 
208  // Constraints
209  forAll(constraints, pointi)
210  {
211  if (dispCount[pointi] > 0)
212  {
213  // Mix my displacement with neighbours' displacement
214  disp[pointi] =
215  0.5
216  *(disp[pointi] + dispSum[pointi]/dispCount[pointi]);
217  }
218  }
219  }
220 }
221 
222 
223 void Foam::snappySnapDriver::calcNearestFace
224 (
225  const label iter,
226  const indirectPrimitivePatch& pp,
227  const scalarField& faceSnapDist,
228  vectorField& faceDisp,
229  vectorField& faceSurfaceNormal,
230  labelList& faceSurfaceGlobalRegion
231  //vectorField& faceRotation
232 ) const
233 {
234  const fvMesh& mesh = meshRefiner_.mesh();
235  const refinementSurfaces& surfaces = meshRefiner_.surfaces();
236 
237  // Displacement and orientation per pp face.
238  faceDisp.setSize(pp.size());
239  faceDisp = Zero;
240  faceSurfaceNormal.setSize(pp.size());
241  faceSurfaceNormal = Zero;
242  faceSurfaceGlobalRegion.setSize(pp.size());
243  faceSurfaceGlobalRegion = -1;
244 
245  // Divide surfaces into zoned and unzoned
246  const labelList zonedSurfaces =
247  surfaceZonesInfo::getNamedSurfaces(surfaces.surfZones());
248  const labelList unzonedSurfaces =
249  surfaceZonesInfo::getUnnamedSurfaces(surfaces.surfZones());
250 
251  // Per pp face the current surface snapped to
252  labelList snapSurf(pp.size(), -1);
253 
254 
255  // Do zoned surfaces
256  // ~~~~~~~~~~~~~~~~~
257  // Zoned faces only attract to corresponding surface
258 
259  // Extract faces per zone
260  const PtrList<surfaceZonesInfo>& surfZones = surfaces.surfZones();
261 
262  forAll(zonedSurfaces, i)
263  {
264  label zoneSurfi = zonedSurfaces[i];
265 
266  const wordList& faceZoneNames = surfZones[zoneSurfi].faceZoneNames();
267 
268  // Get indices of faces on pp that are also in zone
269  DynamicList<label> ppFaces;
270  DynamicList<label> meshFaces;
271  forAll(faceZoneNames, fzi)
272  {
273  const word& faceZoneName = faceZoneNames[fzi];
274  label zonei = mesh.faceZones().findZoneID(faceZoneName);
275  if (zonei == -1)
276  {
278  << "Problem. Cannot find zone " << faceZoneName
279  << exit(FatalError);
280  }
281  const faceZone& fZone = mesh.faceZones()[zonei];
282  const bitSet isZonedFace(mesh.nFaces(), fZone);
283 
284  ppFaces.reserve(ppFaces.capacity()+fZone.size());
285  meshFaces.reserve(meshFaces.capacity()+fZone.size());
286 
287  forAll(pp.addressing(), i)
288  {
289  if (isZonedFace[pp.addressing()[i]])
290  {
291  snapSurf[i] = zoneSurfi;
292  ppFaces.append(i);
293  meshFaces.append(pp.addressing()[i]);
294  }
295  }
296 
297  //Pout<< "For faceZone " << fZone.name()
298  // << " found " << ppFaces.size() << " out of " << pp.size()
299  // << endl;
300  }
301 
302  pointField fc
303  (
305  (
306  IndirectList<face>(mesh.faces(), meshFaces),
307  mesh.points()
308  ).faceCentres()
309  );
310 
311  List<pointIndexHit> hitInfo;
312  labelList hitSurface;
313  labelList hitRegion;
314  vectorField hitNormal;
315  surfaces.findNearestRegion
316  (
317  labelList(1, zoneSurfi),
318  fc,
319  sqr(scalarField(faceSnapDist, ppFaces)),// sqr of attract dist
320  hitSurface,
321  hitInfo,
322  hitRegion,
323  hitNormal
324  );
325 
326  forAll(hitInfo, hiti)
327  {
328  if (hitInfo[hiti].hit())
329  {
330  label facei = ppFaces[hiti];
331  faceDisp[facei] = hitInfo[hiti].hitPoint() - fc[hiti];
332  faceSurfaceNormal[facei] = hitNormal[hiti];
333  faceSurfaceGlobalRegion[facei] = surfaces.globalRegion
334  (
335  hitSurface[hiti],
336  hitRegion[hiti]
337  );
338  }
339  else
340  {
341  static label nWarn = 0;
342 
343  if (nWarn < 100)
344  {
346  << "Did not find surface near face centre " << fc[hiti]
347  << endl;
348  nWarn++;
349  if (nWarn == 100)
350  {
352  << "Reached warning limit " << nWarn
353  << ". Suppressing further warnings." << endl;
354  }
355  }
356  }
357  }
358  }
359 
360 
361  // Do unzoned surfaces
362  // ~~~~~~~~~~~~~~~~~~~
363  // Unzoned faces attract to any unzoned surface
364 
365  DynamicList<label> ppFaces(pp.size());
366  DynamicList<label> meshFaces(pp.size());
367  forAll(pp.addressing(), i)
368  {
369  if (snapSurf[i] == -1)
370  {
371  ppFaces.append(i);
372  meshFaces.append(pp.addressing()[i]);
373  }
374  }
375  //Pout<< "Found " << ppFaces.size() << " unzoned faces out of "
376  // << pp.size() << endl;
377 
378  pointField fc
379  (
381  (
382  IndirectList<face>(mesh.faces(), meshFaces),
383  mesh.points()
384  ).faceCentres()
385  );
386 
387  List<pointIndexHit> hitInfo;
388  labelList hitSurface;
389  labelList hitRegion;
390  vectorField hitNormal;
391  surfaces.findNearestRegion
392  (
393  unzonedSurfaces,
394  fc,
395  sqr(scalarField(faceSnapDist, ppFaces)),// sqr of attract dist
396  hitSurface,
397  hitInfo,
398  hitRegion,
399  hitNormal
400  );
401 
402  forAll(hitInfo, hiti)
403  {
404  if (hitInfo[hiti].hit())
405  {
406  label facei = ppFaces[hiti];
407  faceDisp[facei] = hitInfo[hiti].hitPoint() - fc[hiti];
408  faceSurfaceNormal[facei] = hitNormal[hiti];
409  faceSurfaceGlobalRegion[facei] = surfaces.globalRegion
410  (
411  hitSurface[hiti],
412  hitRegion[hiti]
413  );
414  }
415  else
416  {
417  static label nWarn = 0;
418 
419  if (nWarn < 100)
420  {
422  << "Did not find surface near face centre " << fc[hiti]
423  << endl;
424 
425  nWarn++;
426  if (nWarn == 100)
427  {
429  << "Reached warning limit " << nWarn
430  << ". Suppressing further warnings." << endl;
431  }
432  }
433  }
434  }
435 
436 
439  //
441  //faceRotation.setSize(pp.size());
442  //faceRotation = Zero;
443  //
444  //forAll(faceRotation, facei)
445  //{
446  // // Note: extend to >180 degrees checking
447  // faceRotation[facei] =
448  // pp.faceNormals()[facei]
449  // ^ faceSurfaceNormal[facei];
450  //}
451  //
452  //if (debug&meshRefinement::ATTRACTION)
453  //{
454  // dumpMove
455  // (
456  // mesh.time().path()
457  // / "faceDisp_" + name(iter) + ".obj",
458  // pp.faceCentres(),
459  // pp.faceCentres() + faceDisp
460  // );
461  // dumpMove
462  // (
463  // mesh.time().path()
464  // / "faceRotation_" + name(iter) + ".obj",
465  // pp.faceCentres(),
466  // pp.faceCentres() + faceRotation
467  // );
468  //}
469 }
470 
471 
472 // Collect (possibly remote) per point data of all surrounding faces
473 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
474 // - faceSurfaceNormal
475 // - faceDisp
476 // - faceCentres&faceNormal
477 void Foam::snappySnapDriver::calcNearestFacePointProperties
478 (
479  const label iter,
480  const indirectPrimitivePatch& pp,
481 
482  const vectorField& faceDisp,
483  const vectorField& faceSurfaceNormal,
484  const labelList& faceSurfaceGlobalRegion,
485 
486  List<List<point>>& pointFaceSurfNormals,
487  List<List<point>>& pointFaceDisp,
488  List<List<point>>& pointFaceCentres,
489  List<labelList>& pointFacePatchID
490 ) const
491 {
492  const fvMesh& mesh = meshRefiner_.mesh();
493 
494  const bitSet isMasterFace(syncTools::getMasterFaces(mesh));
495 
496 
497  // For now just get all surrounding face data. Expensive - should just
498  // store and sync data on coupled points only
499  // (see e.g PatchToolsNormals.C)
500 
501  pointFaceSurfNormals.setSize(pp.nPoints());
502  pointFaceDisp.setSize(pp.nPoints());
503  pointFaceCentres.setSize(pp.nPoints());
504  pointFacePatchID.setSize(pp.nPoints());
505 
506  // Fill local data
507  forAll(pp.pointFaces(), pointi)
508  {
509  const labelList& pFaces = pp.pointFaces()[pointi];
510 
511  // Count valid face normals
512  label nFaces = 0;
513  forAll(pFaces, i)
514  {
515  label facei = pFaces[i];
516  if (isMasterFace[facei] && faceSurfaceGlobalRegion[facei] != -1)
517  {
518  nFaces++;
519  }
520  }
521 
522 
523  List<point>& pNormals = pointFaceSurfNormals[pointi];
524  pNormals.setSize(nFaces);
525  List<point>& pDisp = pointFaceDisp[pointi];
526  pDisp.setSize(nFaces);
527  List<point>& pFc = pointFaceCentres[pointi];
528  pFc.setSize(nFaces);
529  labelList& pFid = pointFacePatchID[pointi];
530  pFid.setSize(nFaces);
531 
532  nFaces = 0;
533  forAll(pFaces, i)
534  {
535  label facei = pFaces[i];
536  label globalRegioni = faceSurfaceGlobalRegion[facei];
537 
538  if (isMasterFace[facei] && globalRegioni != -1)
539  {
540  pNormals[nFaces] = faceSurfaceNormal[facei];
541  pDisp[nFaces] = faceDisp[facei];
542  pFc[nFaces] = pp.faceCentres()[facei];
543  pFid[nFaces] = globalToMasterPatch_[globalRegioni];
544  nFaces++;
545  }
546  }
547  }
548 
549 
550  // Collect additionally 'normal' boundary faces for boundaryPoints of pp
551  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
552  // points on the boundary of pp should pick up non-pp normals
553  // as well for the feature-reconstruction to behave correctly.
554  // (the movement is already constrained outside correctly so it
555  // is only that the unconstrained attraction vector is calculated
556  // correctly)
557  {
558  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
559  labelList patchID(pbm.patchID());
560 
561  // Unmark all non-coupled boundary faces
562  forAll(pbm, patchi)
563  {
564  const polyPatch& pp = pbm[patchi];
565 
566  if (pp.coupled() || isA<emptyPolyPatch>(pp))
567  {
568  forAll(pp, i)
569  {
570  label meshFacei = pp.start()+i;
571  patchID[meshFacei-mesh.nInternalFaces()] = -1;
572  }
573  }
574  }
575 
576  // Remove any meshed faces
577  forAll(pp.addressing(), i)
578  {
579  label meshFacei = pp.addressing()[i];
580  patchID[meshFacei-mesh.nInternalFaces()] = -1;
581  }
582 
583 
584 
585  // See if edge of pp uses any non-meshed boundary faces. If so add the
586  // boundary face as additional constraint. Note that we account for
587  // both 'real' boundary edges and boundary edge of baffles
588 
589  const labelList bafflePair
590  (
592  );
593 
594 
595  // Mark all points on 'boundary' edges
596  bitSet isBoundaryPoint(pp.nPoints());
597 
598  const labelListList& edgeFaces = pp.edgeFaces();
599  const edgeList& edges = pp.edges();
600 
601  forAll(edgeFaces, edgei)
602  {
603  const edge& e = edges[edgei];
604  const labelList& eFaces = edgeFaces[edgei];
605 
606  if (eFaces.size() == 1)
607  {
608  // 'real' boundary edge
609  isBoundaryPoint.set(e[0]);
610  isBoundaryPoint.set(e[1]);
611  }
612  else if (eFaces.size() == 2 && bafflePair[eFaces[0]] == eFaces[1])
613  {
614  // 'baffle' boundary edge
615  isBoundaryPoint.set(e[0]);
616  isBoundaryPoint.set(e[1]);
617  }
618  }
619 
620 
621  // Construct labelList equivalent of meshPointMap
622  labelList meshToPatchPoint(mesh.nPoints(), -1);
623  forAll(pp.meshPoints(), pointi)
624  {
625  meshToPatchPoint[pp.meshPoints()[pointi]] = pointi;
626  }
627 
628  forAll(patchID, bFacei)
629  {
630  label patchi = patchID[bFacei];
631 
632  if (patchi != -1)
633  {
634  label facei = mesh.nInternalFaces()+bFacei;
635  const face& f = mesh.faces()[facei];
636 
637  forAll(f, fp)
638  {
639  label pointi = meshToPatchPoint[f[fp]];
640 
641  if (pointi != -1 && isBoundaryPoint.test(pointi))
642  {
643  List<point>& pNormals = pointFaceSurfNormals[pointi];
644  List<point>& pDisp = pointFaceDisp[pointi];
645  List<point>& pFc = pointFaceCentres[pointi];
646  labelList& pFid = pointFacePatchID[pointi];
647 
648  const point& pt = mesh.points()[f[fp]];
649  vector fn = mesh.faceAreas()[facei];
650 
651  pNormals.append(fn/mag(fn));
652  pDisp.append(mesh.faceCentres()[facei]-pt);
653  pFc.append(mesh.faceCentres()[facei]);
654  pFid.append(patchi);
655  }
656  }
657  }
658  }
659  }
660 
662  (
663  mesh,
664  pp.meshPoints(),
665  pointFaceSurfNormals,
666  listPlusEqOp<point>(),
667  List<point>(),
669  );
671  (
672  mesh,
673  pp.meshPoints(),
674  pointFaceDisp,
675  listPlusEqOp<point>(),
676  List<point>(),
678  );
680  (
681  mesh,
682  pp.meshPoints(),
683  pointFaceCentres,
684  listPlusEqOp<point>(),
685  List<point>(),
686  mapDistribute::transformPosition()
687  );
689  (
690  mesh,
691  pp.meshPoints(),
692  pointFacePatchID,
693  listPlusEqOp<label>(),
694  List<label>()
695  );
696 
697 
698  // Sort the data according to the face centres. This is only so we get
699  // consistent behaviour serial and parallel.
700  labelList visitOrder;
701  forAll(pointFaceDisp, pointi)
702  {
703  List<point>& pNormals = pointFaceSurfNormals[pointi];
704  List<point>& pDisp = pointFaceDisp[pointi];
705  List<point>& pFc = pointFaceCentres[pointi];
706  labelList& pFid = pointFacePatchID[pointi];
707 
708  sortedOrder(mag(pFc)(), visitOrder);
709 
710  pNormals = List<point>(pNormals, visitOrder);
711  pDisp = List<point>(pDisp, visitOrder);
712  pFc = List<point>(pFc, visitOrder);
713  pFid = labelUIndList(pFid, visitOrder)();
714  }
715 }
716 
717 
718 // Gets passed in offset to nearest point on feature edge. Calculates
719 // if the point has a different number of faces on either side of the feature
720 // and if so attracts the point to that non-dominant plane.
721 void Foam::snappySnapDriver::correctAttraction
722 (
723  const DynamicList<point>& surfacePoints,
724  const DynamicList<label>& surfaceCounts,
725  const point& edgePt,
726  const vector& edgeNormal, // normalised normal
727  const point& pt,
728 
729  vector& edgeOffset // offset from pt to point on edge
730 ) const
731 {
732  // Tangential component along edge
733  scalar tang = ((pt-edgePt)&edgeNormal);
734 
735  labelList order(sortedOrder(surfaceCounts));
736 
737  if (order[0] < order[1])
738  {
739  // There is a non-dominant plane. Use the point on the plane to
740  // attract to.
741  vector attractD = surfacePoints[order[0]]-edgePt;
742  // Tangential component along edge
743  scalar tang2 = (attractD&edgeNormal);
744  // Normal component
745  attractD -= tang2*edgeNormal;
746  // Calculate fraction of normal distances
747  scalar magAttractD = mag(attractD);
748  scalar fraction = magAttractD/(magAttractD+mag(edgeOffset));
749 
750  point linePt =
751  edgePt
752  + ((1.0-fraction)*tang2 + fraction*tang)*edgeNormal;
753  edgeOffset = linePt-pt;
754  }
755 }
756 
757 
758 Foam::pointIndexHit Foam::snappySnapDriver::findMultiPatchPoint
759 (
760  const point& pt,
761  const labelList& patchIDs,
762  const List<point>& faceCentres
763 ) const
764 {
765  // Determine if multiple patchIDs
766  if (patchIDs.size())
767  {
768  label patch0 = patchIDs[0];
769 
770  for (label i = 1; i < patchIDs.size(); i++)
771  {
772  if (patchIDs[i] != patch0)
773  {
774  return pointIndexHit(true, pt, labelMax);
775  }
776  }
777  }
778  return pointIndexHit(false, Zero, labelMax);
779 }
780 
781 
782 Foam::label Foam::snappySnapDriver::findNormal
783 (
784  const scalar featureCos,
785  const vector& n,
786  const DynamicList<vector>& surfaceNormals
787 ) const
788 {
789  label index = -1;
790 
791  forAll(surfaceNormals, j)
792  {
793  scalar cosAngle = (n&surfaceNormals[j]);
794 
795  if
796  (
797  (cosAngle >= featureCos)
798  || (cosAngle < (-1+0.001)) // triangle baffles
799  )
800  {
801  index = j;
802  break;
803  }
804  }
805  return index;
806 }
807 
808 
809 // Detect multiple patches. Returns pointIndexHit:
810 // - false, index=-1 : single patch
811 // - true , index=0 : multiple patches but on different normals planes
812 // (so geometric feature edge is also a region edge)
813 // - true , index=1 : multiple patches on same normals plane i.e. flat region
814 // edge
815 Foam::pointIndexHit Foam::snappySnapDriver::findMultiPatchPoint
816 (
817  const point& pt,
818  const labelList& patchIDs,
819  const DynamicList<vector>& surfaceNormals,
820  const labelList& faceToNormalBin
821 ) const
822 {
823  if (patchIDs.empty())
824  {
825  return pointIndexHit(false, pt, -1);
826  }
827 
828  // Detect single patch situation (to avoid allocation)
829  label patch0 = patchIDs[0];
830 
831  for (label i = 1; i < patchIDs.size(); i++)
832  {
833  if (patchIDs[i] != patch0)
834  {
835  patch0 = -1;
836  break;
837  }
838  }
839 
840  if (patch0 >= 0)
841  {
842  // Single patch
843  return pointIndexHit(false, pt, -1);
844  }
845  else
846  {
847  if (surfaceNormals.size() == 1)
848  {
849  // Same normals plane, flat region edge.
850  return pointIndexHit(true, pt, 1);
851  }
852  else
853  {
854  // Detect per normals bin
855  labelList normalToPatch(surfaceNormals.size(), -1);
856  forAll(faceToNormalBin, i)
857  {
858  if (faceToNormalBin[i] != -1)
859  {
860  label& patch = normalToPatch[faceToNormalBin[i]];
861  if (patch == -1)
862  {
863  // First occurrence
864  patch = patchIDs[i];
865  }
866  else if (patch == -2)
867  {
868  // Already marked as being on multiple patches
869  }
870  else if (patch != patchIDs[i])
871  {
872  // Mark as being on multiple patches
873  patch = -2;
874  }
875  }
876  }
877 
878  forAll(normalToPatch, normali)
879  {
880  if (normalToPatch[normali] == -2)
881  {
882  // Multiple patches on same normals plane, flat region
883  // edge
884  return pointIndexHit(true, pt, 1);
885  }
886  }
887 
888  // All patches on either side of geometric feature anyway
889  return pointIndexHit(true, pt, 0);
890  }
891  }
892 }
893 
894 
895 void Foam::snappySnapDriver::writeStats
896 (
897  const indirectPrimitivePatch& pp,
898  const bitSet& isPatchMasterPoint,
899  const List<pointConstraint>& patchConstraints
900 ) const
901 {
902  label nMasterPoints = 0;
903  label nPlanar = 0;
904  label nEdge = 0;
905  label nPoint = 0;
906 
907  forAll(patchConstraints, pointi)
908  {
909  if (isPatchMasterPoint[pointi])
910  {
911  nMasterPoints++;
912 
913  if (patchConstraints[pointi].first() == 1)
914  {
915  nPlanar++;
916  }
917  else if (patchConstraints[pointi].first() == 2)
918  {
919  nEdge++;
920  }
921  else if (patchConstraints[pointi].first() == 3)
922  {
923  nPoint++;
924  }
925  }
926  }
927 
928  reduce(nMasterPoints, sumOp<label>());
929  reduce(nPlanar, sumOp<label>());
930  reduce(nEdge, sumOp<label>());
931  reduce(nPoint, sumOp<label>());
932  Info<< "total master points :" << nMasterPoints
933  << " of which attracted to :" << nl
934  << " feature point : " << nPoint << nl
935  << " feature edge : " << nEdge << nl
936  << " nearest surface : " << nPlanar << nl
937  << " rest : " << nMasterPoints-nPoint-nEdge-nPlanar
938  << nl
939  << endl;
940 }
941 
942 
943 void Foam::snappySnapDriver::featureAttractionUsingReconstruction
944 (
945  const label iter,
946  const scalar featureCos,
947 
948  const indirectPrimitivePatch& pp,
949  const scalarField& snapDist,
950  const vectorField& nearestDisp,
951  const label pointi,
952 
953  const List<List<point>>& pointFaceSurfNormals,
954  const List<List<point>>& pointFaceDisp,
955  const List<List<point>>& pointFaceCentres,
956  const labelListList& pointFacePatchID,
957 
958  DynamicList<point>& surfacePoints,
959  DynamicList<vector>& surfaceNormals,
960  labelList& faceToNormalBin,
961 
962  vector& patchAttraction,
963  pointConstraint& patchConstraint
964 ) const
965 {
966  patchAttraction = Zero;
967  patchConstraint = pointConstraint();
968 
969  const List<point>& pfSurfNormals = pointFaceSurfNormals[pointi];
970  const List<point>& pfDisp = pointFaceDisp[pointi];
971  const List<point>& pfCentres = pointFaceCentres[pointi];
972 
973  // Bin according to surface normal
974  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
975 
976  //- Bins of differing normals:
977  // - one normal : flat(tish) surface
978  // - two normals : geometric feature edge
979  // - three normals: geometric feature point
980  // - four normals : too complex a feature
981  surfacePoints.clear();
982  surfaceNormals.clear();
983 
984  //- From face to above normals bin
985  faceToNormalBin.setSize(pfDisp.size());
986  faceToNormalBin = -1;
987 
988  forAll(pfSurfNormals, i)
989  {
990  const point& fc = pfCentres[i];
991  const vector& fSNormal = pfSurfNormals[i];
992  const vector& fDisp = pfDisp[i];
993 
994  // What to do with very far attraction? For now just ignore the face
995  if (magSqr(fDisp) < sqr(snapDist[pointi]) && mag(fSNormal) > VSMALL)
996  {
997  const point pt = fc + fDisp;
998 
999  // Do we already have surface normal?
1000  faceToNormalBin[i] = findNormal
1001  (
1002  featureCos,
1003  fSNormal,
1004  surfaceNormals
1005  );
1006 
1007  if (faceToNormalBin[i] != -1)
1008  {
1009  // Same normal
1010  }
1011  else
1012  {
1013  // Now check if the planes go through the same edge or point
1014 
1015  if (surfacePoints.size() <= 1)
1016  {
1017  surfacePoints.append(pt);
1018  faceToNormalBin[i] = surfaceNormals.size();
1019  surfaceNormals.append(fSNormal);
1020  }
1021  else if (surfacePoints.size() == 2)
1022  {
1023  plane pl0(surfacePoints[0], surfaceNormals[0]);
1024  plane pl1(surfacePoints[1], surfaceNormals[1]);
1025  plane::ray r(pl0.planeIntersect(pl1));
1026  vector featureNormal = r.dir() / mag(r.dir());
1027 
1028  if (mag(fSNormal&featureNormal) >= 0.001)
1029  {
1030  // Definitely makes a feature point
1031  surfacePoints.append(pt);
1032  faceToNormalBin[i] = surfaceNormals.size();
1033  surfaceNormals.append(fSNormal);
1034  }
1035  }
1036  else if (surfacePoints.size() == 3)
1037  {
1038  // Have already feature point. See if this new plane is
1039  // the same point or not.
1040  plane pl0(surfacePoints[0], surfaceNormals[0]);
1041  plane pl1(surfacePoints[1], surfaceNormals[1]);
1042  plane pl2(surfacePoints[2], surfaceNormals[2]);
1043  point p012(pl0.planePlaneIntersect(pl1, pl2));
1044 
1045  plane::ray r(pl0.planeIntersect(pl1));
1046  vector featureNormal = r.dir() / mag(r.dir());
1047  if (mag(fSNormal&featureNormal) >= 0.001)
1048  {
1049  plane pl3(pt, fSNormal);
1050  point p013(pl0.planePlaneIntersect(pl1, pl3));
1051 
1052  if (mag(p012-p013) > snapDist[pointi])
1053  {
1054  // Different feature point
1055  surfacePoints.append(pt);
1056  faceToNormalBin[i] = surfaceNormals.size();
1057  surfaceNormals.append(fSNormal);
1058  }
1059  }
1060  }
1061  }
1062  }
1063  }
1064 
1065 
1066  const point& pt = pp.localPoints()[pointi];
1067 
1068  // Check the number of directions
1069  if (surfaceNormals.size() == 1)
1070  {
1071  // Normal distance to plane
1072  vector d =
1073  ((surfacePoints[0]-pt) & surfaceNormals[0])
1074  *surfaceNormals[0];
1075 
1076  // Trim to snap distance
1077  if (magSqr(d) > sqr(snapDist[pointi]))
1078  {
1079  d *= Foam::sqrt(sqr(snapDist[pointi])/magSqr(d));
1080  }
1081 
1082  patchAttraction = d;
1083 
1084  // Store constraints
1085  patchConstraint.applyConstraint(surfaceNormals[0]);
1086  }
1087  else if (surfaceNormals.size() == 2)
1088  {
1089  plane pl0(surfacePoints[0], surfaceNormals[0]);
1090  plane pl1(surfacePoints[1], surfaceNormals[1]);
1091  plane::ray r(pl0.planeIntersect(pl1));
1092  vector n = r.dir() / mag(r.dir());
1093 
1094  // Get nearest point on infinite ray
1095  vector d = r.refPoint()-pt;
1096  d -= (d&n)*n;
1097 
1098  // Trim to snap distance
1099  if (magSqr(d) > sqr(snapDist[pointi]))
1100  {
1101  d *= Foam::sqrt(sqr(snapDist[pointi])/magSqr(d));
1102  }
1103 
1104  patchAttraction = d;
1105 
1106  // Store constraints
1107  patchConstraint.applyConstraint(surfaceNormals[0]);
1108  patchConstraint.applyConstraint(surfaceNormals[1]);
1109  }
1110  else if (surfaceNormals.size() == 3)
1111  {
1112  // Calculate point from the faces.
1113  plane pl0(surfacePoints[0], surfaceNormals[0]);
1114  plane pl1(surfacePoints[1], surfaceNormals[1]);
1115  plane pl2(surfacePoints[2], surfaceNormals[2]);
1116  point cornerPt(pl0.planePlaneIntersect(pl1, pl2));
1117  vector d = cornerPt - pt;
1118 
1119  // Trim to snap distance
1120  if (magSqr(d) > sqr(snapDist[pointi]))
1121  {
1122  d *= Foam::sqrt(sqr(snapDist[pointi])/magSqr(d));
1123  }
1124 
1125  patchAttraction = d;
1126 
1127  // Store constraints
1128  patchConstraint.applyConstraint(surfaceNormals[0]);
1129  patchConstraint.applyConstraint(surfaceNormals[1]);
1130  patchConstraint.applyConstraint(surfaceNormals[2]);
1131  }
1132 }
1133 
1134 
1135 // Special version that calculates attraction in one go
1136 void Foam::snappySnapDriver::featureAttractionUsingReconstruction
1137 (
1138  const label iter,
1139  const scalar featureCos,
1140 
1141  const indirectPrimitivePatch& pp,
1142  const scalarField& snapDist,
1143  const vectorField& nearestDisp,
1144 
1145  const List<List<point>>& pointFaceSurfNormals,
1146  const List<List<point>>& pointFaceDisp,
1147  const List<List<point>>& pointFaceCentres,
1148  const labelListList& pointFacePatchID,
1149 
1150  vectorField& patchAttraction,
1151  List<pointConstraint>& patchConstraints
1152 ) const
1153 {
1154  autoPtr<OBJstream> feStr;
1155  autoPtr<OBJstream> fpStr;
1157  {
1158  feStr.reset
1159  (
1160  new OBJstream
1161  (
1162  meshRefiner_.mesh().time().path()
1163  / "implicitFeatureEdge_" + name(iter) + ".obj"
1164  )
1165  );
1166  Info<< "Dumping implicit feature-edge direction to "
1167  << feStr().name() << endl;
1168 
1169  fpStr.reset
1170  (
1171  new OBJstream
1172  (
1173  meshRefiner_.mesh().time().path()
1174  / "implicitFeaturePoint_" + name(iter) + ".obj"
1175  )
1176  );
1177  Info<< "Dumping implicit feature-point direction to "
1178  << fpStr().name() << endl;
1179  }
1180 
1181 
1182  DynamicList<point> surfacePoints(4);
1183  DynamicList<vector> surfaceNormals(4);
1184  labelList faceToNormalBin;
1185 
1186  forAll(pp.localPoints(), pointi)
1187  {
1188  vector attraction = Zero;
1189  pointConstraint constraint;
1190 
1191  featureAttractionUsingReconstruction
1192  (
1193  iter,
1194  featureCos,
1195 
1196  pp,
1197  snapDist,
1198  nearestDisp,
1199 
1200  pointi,
1201 
1202  pointFaceSurfNormals,
1203  pointFaceDisp,
1204  pointFaceCentres,
1205  pointFacePatchID,
1206 
1207  surfacePoints,
1208  surfaceNormals,
1209  faceToNormalBin,
1210 
1211  attraction,
1212  constraint
1213  );
1214 
1215  if
1216  (
1217  (constraint.first() > patchConstraints[pointi].first())
1218  || (
1219  (constraint.first() == patchConstraints[pointi].first())
1220  && (magSqr(attraction) < magSqr(patchAttraction[pointi]))
1221  )
1222  )
1223  {
1224  patchAttraction[pointi] = attraction;
1225  patchConstraints[pointi] = constraint;
1226 
1227  const point& pt = pp.localPoints()[pointi];
1228 
1229  if (patchConstraints[pointi].first() == 2 && feStr.valid())
1230  {
1231  feStr().write(linePointRef(pt, pt+patchAttraction[pointi]));
1232  }
1233  else if (patchConstraints[pointi].first() == 3 && fpStr.valid())
1234  {
1235  fpStr().write(linePointRef(pt, pt+patchAttraction[pointi]));
1236  }
1237  }
1238  }
1239 }
1240 
1241 
1242 void Foam::snappySnapDriver::stringFeatureEdges
1243 (
1244  const label iter,
1245  const scalar featureCos,
1246 
1247  const indirectPrimitivePatch& pp,
1248  const scalarField& snapDist,
1249 
1250  const vectorField& rawPatchAttraction,
1251  const List<pointConstraint>& rawPatchConstraints,
1252 
1253  vectorField& patchAttraction,
1254  List<pointConstraint>& patchConstraints
1255 ) const
1256 {
1257  // Snap edges to feature edges
1258  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
1259  // Walk existing edges and snap remaining ones (that are marked as
1260  // feature edges in rawPatchConstraints)
1261 
1262  // What this does is fill in any faces where not all points
1263  // on the face are being attracted:
1264  /*
1265  +
1266  / \
1267  / \
1268  ---+ +---
1269  \ /
1270  \ /
1271  +
1272  */
1273  // so the top and bottom will never get attracted since the nearest
1274  // back from the feature edge will always be one of the left or right
1275  // points since the face is diamond like. So here we walk the feature edges
1276  // and add any non-attracted points.
1277 
1278 
1279  while (true)
1280  {
1281  label nChanged = 0;
1282 
1283  const labelListList& pointEdges = pp.pointEdges();
1284  forAll(pointEdges, pointi)
1285  {
1286  if (patchConstraints[pointi].first() == 2)
1287  {
1288  const point& pt = pp.localPoints()[pointi];
1289  const labelList& pEdges = pointEdges[pointi];
1290  const vector& featVec = patchConstraints[pointi].second();
1291 
1292  // Detect whether there are edges in both directions.
1293  // (direction along the feature edge that is)
1294  bool hasPos = false;
1295  bool hasNeg = false;
1296 
1297  forAll(pEdges, pEdgei)
1298  {
1299  const edge& e = pp.edges()[pEdges[pEdgei]];
1300  label nbrPointi = e.otherVertex(pointi);
1301 
1302  if (patchConstraints[nbrPointi].first() > 1)
1303  {
1304  const point& nbrPt = pp.localPoints()[nbrPointi];
1305  const point featPt =
1306  nbrPt + patchAttraction[nbrPointi];
1307  const scalar cosAngle = (featVec & (featPt-pt));
1308 
1309  if (cosAngle > 0)
1310  {
1311  hasPos = true;
1312  }
1313  else
1314  {
1315  hasNeg = true;
1316  }
1317  }
1318  }
1319 
1320  if (!hasPos || !hasNeg)
1321  {
1322  //Pout<< "**Detected feature string end at "
1323  // << pp.localPoints()[pointi] << endl;
1324 
1325  // No string. Assign best choice on either side
1326  label bestPosPointi = -1;
1327  scalar minPosDistSqr = GREAT;
1328  label bestNegPointi = -1;
1329  scalar minNegDistSqr = GREAT;
1330 
1331  forAll(pEdges, pEdgei)
1332  {
1333  const edge& e = pp.edges()[pEdges[pEdgei]];
1334  label nbrPointi = e.otherVertex(pointi);
1335 
1336  if
1337  (
1338  patchConstraints[nbrPointi].first() <= 1
1339  && rawPatchConstraints[nbrPointi].first() > 1
1340  )
1341  {
1342  const vector& nbrFeatVec =
1343  rawPatchConstraints[pointi].second();
1344 
1345  if (mag(featVec&nbrFeatVec) > featureCos)
1346  {
1347  // nbrPointi attracted to sameish feature
1348  // Note: also check on position.
1349 
1350  scalar d2 = magSqr
1351  (
1352  rawPatchAttraction[nbrPointi]
1353  );
1354 
1355  const point featPt =
1356  pp.localPoints()[nbrPointi]
1357  + rawPatchAttraction[nbrPointi];
1358  const scalar cosAngle =
1359  (featVec & (featPt-pt));
1360 
1361  if (cosAngle > 0)
1362  {
1363  if (!hasPos && d2 < minPosDistSqr)
1364  {
1365  minPosDistSqr = d2;
1366  bestPosPointi = nbrPointi;
1367  }
1368  }
1369  else
1370  {
1371  if (!hasNeg && d2 < minNegDistSqr)
1372  {
1373  minNegDistSqr = d2;
1374  bestNegPointi = nbrPointi;
1375  }
1376  }
1377  }
1378  }
1379  }
1380 
1381  if (bestPosPointi != -1)
1382  {
1383  // Use reconstructed-feature attraction. Use only
1384  // part of it since not sure...
1385  //const point& bestPt =
1386  // pp.localPoints()[bestPosPointi];
1387  //Pout<< "**Overriding point " << bestPt
1388  // << " on reconstructed feature edge at "
1389  // << rawPatchAttraction[bestPosPointi]+bestPt
1390  // << " to attracted-to-feature-edge." << endl;
1391  patchAttraction[bestPosPointi] =
1392  0.5*rawPatchAttraction[bestPosPointi];
1393  patchConstraints[bestPosPointi] =
1394  rawPatchConstraints[bestPosPointi];
1395 
1396  nChanged++;
1397  }
1398  if (bestNegPointi != -1)
1399  {
1400  // Use reconstructed-feature attraction. Use only
1401  // part of it since not sure...
1402  //const point& bestPt =
1403  // pp.localPoints()[bestNegPointi];
1404  //Pout<< "**Overriding point " << bestPt
1405  // << " on reconstructed feature edge at "
1406  // << rawPatchAttraction[bestNegPointi]+bestPt
1407  // << " to attracted-to-feature-edge." << endl;
1408  patchAttraction[bestNegPointi] =
1409  0.5*rawPatchAttraction[bestNegPointi];
1410  patchConstraints[bestNegPointi] =
1411  rawPatchConstraints[bestNegPointi];
1412 
1413  nChanged++;
1414  }
1415  }
1416  }
1417  }
1418 
1419 
1420  reduce(nChanged, sumOp<label>());
1421  Info<< "Stringing feature edges : changed " << nChanged << " points"
1422  << endl;
1423  if (nChanged == 0)
1424  {
1425  break;
1426  }
1427  }
1428 }
1429 
1430 
1431 void Foam::snappySnapDriver::releasePointsNextToMultiPatch
1432 (
1433  const label iter,
1434  const scalar featureCos,
1435 
1436  const indirectPrimitivePatch& pp,
1437  const scalarField& snapDist,
1438 
1439  const List<List<point>>& pointFaceCentres,
1440  const labelListList& pointFacePatchID,
1441 
1442  const vectorField& rawPatchAttraction,
1443  const List<pointConstraint>& rawPatchConstraints,
1444 
1445  vectorField& patchAttraction,
1446  List<pointConstraint>& patchConstraints
1447 ) const
1448 {
1449  autoPtr<OBJstream> multiPatchStr;
1451  {
1452  multiPatchStr.reset
1453  (
1454  new OBJstream
1455  (
1456  meshRefiner_.mesh().time().path()
1457  / "multiPatch_" + name(iter) + ".obj"
1458  )
1459  );
1460  Info<< "Dumping removed constraints due to same-face"
1461  << " multi-patch points to "
1462  << multiPatchStr().name() << endl;
1463  }
1464 
1465 
1466  // 1. Mark points on multiple patches
1467  bitSet isMultiPatchPoint(pp.size());
1468 
1469  forAll(pointFacePatchID, pointi)
1470  {
1471  pointIndexHit multiPatchPt = findMultiPatchPoint
1472  (
1473  pp.localPoints()[pointi],
1474  pointFacePatchID[pointi],
1475  pointFaceCentres[pointi]
1476  );
1477  isMultiPatchPoint.set(pointi, multiPatchPt.hit());
1478  }
1479 
1480  // 2. Make sure multi-patch points are also attracted
1481  forAll(isMultiPatchPoint, pointi)
1482  {
1483  if (isMultiPatchPoint.test(pointi))
1484  {
1485  if
1486  (
1487  patchConstraints[pointi].first() <= 1
1488  && rawPatchConstraints[pointi].first() > 1
1489  )
1490  {
1491  patchAttraction[pointi] = rawPatchAttraction[pointi];
1492  patchConstraints[pointi] = rawPatchConstraints[pointi];
1493 
1494  //if (multiPatchStr.valid())
1495  //{
1496  // Pout<< "Adding constraint on multiPatchPoint:"
1497  // << pp.localPoints()[pointi]
1498  // << " constraint:" << patchConstraints[pointi]
1499  // << " attraction:" << patchAttraction[pointi]
1500  // << endl;
1501  //}
1502  }
1503  }
1504  }
1505 
1506  // Up to here it is all parallel ok.
1507 
1508 
1509  // 3. Knock out any attraction on faces with multi-patch points
1510  label nChanged = 0;
1511  forAll(pp.localFaces(), facei)
1512  {
1513  const face& f = pp.localFaces()[facei];
1514 
1515  label nMultiPatchPoints = 0;
1516  forAll(f, fp)
1517  {
1518  label pointi = f[fp];
1519  if
1520  (
1521  isMultiPatchPoint.test(pointi)
1522  && patchConstraints[pointi].first() > 1
1523  )
1524  {
1525  ++nMultiPatchPoints;
1526  }
1527  }
1528 
1529  if (nMultiPatchPoints > 0)
1530  {
1531  forAll(f, fp)
1532  {
1533  label pointi = f[fp];
1534  if
1535  (
1536  !isMultiPatchPoint.test(pointi)
1537  && patchConstraints[pointi].first() > 1
1538  )
1539  {
1540  //Pout<< "Knocking out constraint"
1541  // << " on non-multiPatchPoint:"
1542  // << pp.localPoints()[pointi] << endl;
1543  patchAttraction[pointi] = Zero;
1544  patchConstraints[pointi] = pointConstraint();
1545  nChanged++;
1546 
1547  if (multiPatchStr.valid())
1548  {
1549  multiPatchStr().write(pp.localPoints()[pointi]);
1550  }
1551  }
1552  }
1553  }
1554  }
1555 
1556  reduce(nChanged, sumOp<label>());
1557  Info<< "Removing constraints near multi-patch points : changed "
1558  << nChanged << " points" << endl;
1559 }
1560 
1561 
1562 Foam::labelPair Foam::snappySnapDriver::findDiagonalAttraction
1563 (
1564  const indirectPrimitivePatch& pp,
1565  const vectorField& patchAttraction,
1566  const List<pointConstraint>& patchConstraints,
1567  const label facei
1568 ) const
1569 {
1570  const face& f = pp.localFaces()[facei];
1571  // For now just detect any attraction. Improve this to look at
1572  // actual attraction position and orientation
1573 
1574  labelPair attractIndices(-1, -1);
1575 
1576  if (f.size() >= 4)
1577  {
1578  for (label startFp = 0; startFp < f.size()-2; startFp++)
1579  {
1580  label minFp = f.rcIndex(startFp);
1581 
1582  for
1583  (
1584  label endFp = f.fcIndex(f.fcIndex(startFp));
1585  endFp < f.size() && endFp != minFp;
1586  endFp++
1587  )
1588  {
1589  if
1590  (
1591  patchConstraints[f[startFp]].first() >= 2
1592  && patchConstraints[f[endFp]].first() >= 2
1593  )
1594  {
1595  attractIndices = labelPair(startFp, endFp);
1596  break;
1597  }
1598  }
1599  }
1600  }
1601  return attractIndices;
1602 }
1603 
1604 
1605 bool Foam::snappySnapDriver::isSplitAlignedWithFeature
1606 (
1607  const scalar featureCos,
1608  const point& p0,
1609  const pointConstraint& pc0,
1610  const point& p1,
1611  const pointConstraint& pc1
1612 ) const
1613 {
1614  vector d(p1-p0);
1615  scalar magD = mag(d);
1616  if (magD < VSMALL)
1617  {
1618  // Two diagonal points already colocated?
1619  return false;
1620  }
1621  else
1622  {
1623  d /= magD;
1624 
1625  // Is diagonal d aligned with at least one of the feature
1626  // edges?
1627 
1628  if (pc0.first() == 2 && mag(d & pc0.second()) > featureCos)
1629  {
1630  return true;
1631  }
1632  else if (pc1.first() == 2 && mag(d & pc1.second()) > featureCos)
1633  {
1634  return true;
1635  }
1636  else
1637  {
1638  return false;
1639  }
1640  }
1641 }
1642 
1643 
1644 // Is situation very concave
1645 bool Foam::snappySnapDriver::isConcave
1646 (
1647  const point& c0,
1648  const vector& area0,
1649  const point& c1,
1650  const vector& area1,
1651  const scalar concaveCos
1652 ) const
1653 {
1654  vector n0 = area0;
1655  scalar magN0 = mag(n0);
1656  if (magN0 < VSMALL)
1657  {
1658  // Zero area face. What to return? For now disable splitting.
1659  return true;
1660  }
1661  n0 /= magN0;
1662 
1663  // Distance from c1 to plane of face0
1664  scalar d = (c1-c0)&n0;
1665 
1666  if (d <= 0)
1667  {
1668  // Convex (face1 centre on 'inside' of face0)
1669  return false;
1670  }
1671  else
1672  {
1673  // Is a bit or very concave?
1674  vector n1 = area1;
1675  scalar magN1 = mag(n1);
1676  if (magN1 < VSMALL)
1677  {
1678  // Zero area face. See above
1679  return true;
1680  }
1681  n1 /= magN1;
1682 
1683  if ((n0&n1) < concaveCos)
1684  {
1685  return true;
1686  }
1687  else
1688  {
1689  return false;
1690  }
1691  }
1692 }
1693 
1694 
1695 Foam::labelPair Foam::snappySnapDriver::findDiagonalAttraction
1696 (
1697  const scalar featureCos,
1698  const scalar concaveCos,
1699  const scalar minAreaRatio,
1700  const indirectPrimitivePatch& pp,
1701  const vectorField& patchAttr,
1702  const List<pointConstraint>& patchConstraints,
1703  const vectorField& nearestAttr,
1704  const vectorField& nearestNormal,
1705  const label facei,
1706 
1707  DynamicField<point>& points0,
1708  DynamicField<point>& points1
1709 ) const
1710 {
1711  const face& localF = pp.localFaces()[facei];
1712 
1713  labelPair attractIndices(-1, -1);
1714 
1715  if (localF.size() >= 4)
1716  {
1717  const pointField& localPts = pp.localPoints();
1718 
1722  //const polyMesh& mesh = meshRefiner_.mesh();
1723  //label meshFacei = pp.addressing()[facei];
1724  //const face& meshF = mesh.faces()[meshFacei];
1725  //label celli = mesh.faceOwner()[meshFacei];
1726  //const labelList& cPoints = mesh.cellPoints(celli);
1727  //
1728  //point cc(mesh.points()[meshF[0]]);
1729  //for (label i = 1; i < meshF.size(); i++)
1730  //{
1731  // cc += mesh.points()[meshF[i]]+patchAttr[localF[i]];
1732  //}
1733  //forAll(cPoints, i)
1734  //{
1735  // label pointi = cPoints[i];
1736  // if (!meshF.found(pointi))
1737  // {
1738  // cc += mesh.points()[pointi];
1739  // }
1740  //}
1741  //cc /= cPoints.size();
1743  //
1744  //const scalar vol = pyrVol(pp, patchAttr, localF, cc);
1745  //const scalar area = localF.mag(localPts);
1746 
1747 
1748 
1749  // Try all diagonal cuts
1750  // ~~~~~~~~~~~~~~~~~~~~~
1751 
1752  face f0(3);
1753  face f1(3);
1754 
1755  for (label startFp = 0; startFp < localF.size()-2; startFp++)
1756  {
1757  label minFp = localF.rcIndex(startFp);
1758 
1759  for
1760  (
1761  label endFp = localF.fcIndex(localF.fcIndex(startFp));
1762  endFp < localF.size() && endFp != minFp;
1763  endFp++
1764  )
1765  {
1766  label startPti = localF[startFp];
1767  label endPti = localF[endFp];
1768 
1769  const pointConstraint& startPc = patchConstraints[startPti];
1770  const pointConstraint& endPc = patchConstraints[endPti];
1771 
1772  if (startPc.first() >= 2 && endPc.first() >= 2)
1773  {
1774  if (startPc.first() == 2 || endPc.first() == 2)
1775  {
1776  // Check if
1777  // - sameish feature edge normal
1778  // - diagonal aligned with feature edge normal
1779  point start = localPts[startPti]+patchAttr[startPti];
1780  point end = localPts[endPti]+patchAttr[endPti];
1781 
1782  if
1783  (
1784  !isSplitAlignedWithFeature
1785  (
1786  featureCos,
1787  start,
1788  startPc,
1789  end,
1790  endPc
1791  )
1792  )
1793  {
1794  // Attract to different features. No need to
1795  // introduce split
1796  continue;
1797  }
1798  }
1799 
1800 
1801 
1802  // Form two faces
1803  // ~~~~~~~~~~~~~~
1804  // Predict position of faces. End points of the faces
1805  // attract to the feature
1806  // and all the other points just attract to the nearest
1807 
1808  // face0
1809 
1810  f0.setSize(endFp-startFp+1);
1811  label i0 = 0;
1812  for (label fp = startFp; fp <= endFp; fp++)
1813  {
1814  f0[i0++] = localF[fp];
1815  }
1816 
1817  // Get compact face and points
1818  const face compact0(identity(f0.size()));
1819  points0.clear();
1820  points0.append(localPts[f0[0]] + patchAttr[f0[0]]);
1821  for (label fp=1; fp < f0.size()-1; fp++)
1822  {
1823  label pi = f0[fp];
1824  points0.append(localPts[pi] + nearestAttr[pi]);
1825  }
1826  points0.append
1827  (
1828  localPts[f0.last()] + patchAttr[f0.last()]
1829  );
1830 
1831 
1832  // face1
1833 
1834  f1.setSize(localF.size()+2-f0.size());
1835  label i1 = 0;
1836  for
1837  (
1838  label fp = endFp;
1839  fp != startFp;
1840  fp = localF.fcIndex(fp)
1841  )
1842  {
1843  f1[i1++] = localF[fp];
1844  }
1845  f1[i1++] = localF[startFp];
1846 
1847 
1848  // Get compact face and points
1849  const face compact1(identity(f1.size()));
1850  points1.clear();
1851  points1.append(localPts[f1[0]] + patchAttr[f1[0]]);
1852  for (label fp=1; fp < f1.size()-1; fp++)
1853  {
1854  label pi = f1[fp];
1855  points1.append(localPts[pi] + nearestAttr[pi]);
1856  }
1857  points1.append
1858  (
1859  localPts[f1.last()] + patchAttr[f1.last()]
1860  );
1861 
1862 
1863 
1864  // Avoid splitting concave faces
1865  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1866 
1867  if
1868  (
1869  isConcave
1870  (
1871  compact0.centre(points0),
1872  compact0.areaNormal(points0),
1873  compact1.centre(points1),
1874  compact1.areaNormal(points1),
1875  concaveCos
1876  )
1877  )
1878  {
1880  //Pout<< "# f0:" << f0 << endl;
1881  //forAll(p, i)
1882  //{
1883  // meshTools::writeOBJ(Pout, points0[i]);
1884  //}
1885  //Pout<< "# f1:" << f1 << endl;
1886  //forAll(p, i)
1887  //{
1888  // meshTools::writeOBJ(Pout, points1[i]);
1889  //}
1890  }
1891  else
1892  {
1893  // Existing areas
1894  const scalar area0 = f0.mag(localPts);
1895  const scalar area1 = f1.mag(localPts);
1896 
1897  if
1898  (
1899  area0/area1 >= minAreaRatio
1900  && area1/area0 >= minAreaRatio
1901  )
1902  {
1903  attractIndices = labelPair(startFp, endFp);
1904  }
1905  }
1906  }
1907  }
1908  }
1909  }
1910  return attractIndices;
1911 }
1912 
1913 
1914 void Foam::snappySnapDriver::splitDiagonals
1915 (
1916  const scalar featureCos,
1917  const scalar concaveCos,
1918  const scalar minAreaRatio,
1919 
1920  const indirectPrimitivePatch& pp,
1921  const vectorField& nearestAttraction,
1922  const vectorField& nearestNormal,
1923 
1924  vectorField& patchAttraction,
1925  List<pointConstraint>& patchConstraints,
1926  DynamicList<label>& splitFaces,
1927  DynamicList<labelPair>& splits
1928 ) const
1929 {
1930  const labelList& bFaces = pp.addressing();
1931 
1932  splitFaces.clear();
1933  splitFaces.setCapacity(bFaces.size());
1934  splits.clear();
1935  splits.setCapacity(bFaces.size());
1936 
1937 
1938  // Work arrays for storing points of face
1939  DynamicField<point> facePoints0;
1940  DynamicField<point> facePoints1;
1941 
1942  forAll(bFaces, facei)
1943  {
1944  const labelPair split
1945  (
1946  findDiagonalAttraction
1947  (
1948  featureCos,
1949  concaveCos,
1950  minAreaRatio,
1951 
1952  pp,
1953  patchAttraction,
1954  patchConstraints,
1955 
1956  nearestAttraction,
1957  nearestNormal,
1958  facei,
1959 
1960  facePoints0,
1961  facePoints1
1962  )
1963  );
1964 
1965  if (split != labelPair(-1, -1))
1966  {
1967  splitFaces.append(bFaces[facei]);
1968  splits.append(split);
1969 
1970  const face& f = pp.localFaces()[facei];
1971 
1972  // Knock out other attractions on face
1973  forAll(f, fp)
1974  {
1975  // Knock out any other constraints
1976  if
1977  (
1978  fp != split[0]
1979  && fp != split[1]
1980  && patchConstraints[f[fp]].first() >= 2
1981  )
1982  {
1983  patchConstraints[f[fp]] = pointConstraint
1984  (
1985  Tuple2<label, vector>
1986  (
1987  1,
1988  nearestNormal[f[fp]]
1989  )
1990  );
1991  patchAttraction[f[fp]] = nearestAttraction[f[fp]];
1992  }
1993  }
1994  }
1995  }
1996 }
1997 
1998 
1999 void Foam::snappySnapDriver::avoidDiagonalAttraction
2000 (
2001  const label iter,
2002  const scalar featureCos,
2003 
2004  const indirectPrimitivePatch& pp,
2005 
2006  vectorField& patchAttraction,
2007  List<pointConstraint>& patchConstraints
2008 ) const
2009 {
2010  forAll(pp.localFaces(), facei)
2011  {
2012  const face& f = pp.localFaces()[facei];
2013 
2014  labelPair diag = findDiagonalAttraction
2015  (
2016  pp,
2017  patchAttraction,
2018  patchConstraints,
2019  facei
2020  );
2021 
2022  if (diag[0] != -1 && diag[1] != -1)
2023  {
2024  // Found two diagonal points that being attracted.
2025  // For now just attract my one to the average of those.
2026  const label i0 = f[diag[0]];
2027  const point pt0 =
2028  pp.localPoints()[i0]+patchAttraction[i0];
2029  const label i1 = f[diag[1]];
2030  const point pt1 =
2031  pp.localPoints()[i1]+patchAttraction[i1];
2032  const point mid = 0.5*(pt0+pt1);
2033 
2034  const scalar cosAngle = mag
2035  (
2036  patchConstraints[i0].second()
2037  & patchConstraints[i1].second()
2038  );
2039 
2040  //Pout<< "Found diagonal attraction at indices:"
2041  // << diag[0]
2042  // << " and " << diag[1]
2043  // << " with cosAngle:" << cosAngle
2044  // << " mid:" << mid << endl;
2045 
2046  if (cosAngle > featureCos)
2047  {
2048  // The two feature edges are roughly in the same direction.
2049  // Add the nearest of the other points in the face as
2050  // attractor
2051  label minFp = -1;
2052  scalar minDistSqr = GREAT;
2053  forAll(f, fp)
2054  {
2055  label pointi = f[fp];
2056  if (patchConstraints[pointi].first() <= 1)
2057  {
2058  const point& pt = pp.localPoints()[pointi];
2059  scalar distSqr = magSqr(mid-pt);
2060  if (distSqr < minDistSqr)
2061  {
2062  minFp = fp;
2063  }
2064  }
2065  }
2066  if (minFp != -1)
2067  {
2068  label minPointi = f[minFp];
2069  patchAttraction[minPointi] =
2070  mid-pp.localPoints()[minPointi];
2071  patchConstraints[minPointi] = patchConstraints[f[diag[0]]];
2072  }
2073  }
2074  else
2075  {
2076  //Pout<< "Diagonal attractors at" << nl
2077  // << " pt0:" << pt0
2078  // << " constraint:"
2079  // << patchConstraints[i0].second() << nl
2080  // << " pt1:" << pt1
2081  // << " constraint:"
2082  // << patchConstraints[i1].second() << nl
2083  // << " make too large an angle:"
2084  // << mag
2085  // (
2086  // patchConstraints[i0].second()
2087  // & patchConstraints[i1].second()
2088  // )
2089  // << endl;
2090  }
2091  }
2092  }
2093 }
2094 
2095 
2097 Foam::snappySnapDriver::findNearFeatureEdge
2098 (
2099  const bool isRegionEdge,
2100 
2101  const indirectPrimitivePatch& pp,
2102  const scalarField& snapDist,
2103  const label pointi,
2104  const point& estimatedPt,
2105 
2106  List<List<DynamicList<point>>>& edgeAttractors,
2107  List<List<DynamicList<pointConstraint>>>& edgeConstraints,
2108  vectorField& patchAttraction,
2109  List<pointConstraint>& patchConstraints
2110 ) const
2111 {
2112  const refinementFeatures& features = meshRefiner_.features();
2113 
2114  labelList nearEdgeFeat;
2115  List<pointIndexHit> nearEdgeInfo;
2116  vectorField nearNormal;
2117 
2118  if (isRegionEdge)
2119  {
2120  features.findNearestRegionEdge
2121  (
2122  pointField(1, estimatedPt),
2123  scalarField(1, sqr(snapDist[pointi])),
2124  nearEdgeFeat,
2125  nearEdgeInfo,
2126  nearNormal
2127  );
2128  }
2129  else
2130  {
2131  features.findNearestEdge
2132  (
2133  pointField(1, estimatedPt),
2134  scalarField(1, sqr(snapDist[pointi])),
2135  nearEdgeFeat,
2136  nearEdgeInfo,
2137  nearNormal
2138  );
2139  }
2140 
2141  const pointIndexHit& nearInfo = nearEdgeInfo[0];
2142  label feati = nearEdgeFeat[0];
2143 
2144  if (nearInfo.hit())
2145  {
2146  // So we have a point on the feature edge. Use this
2147  // instead of our estimate from planes.
2148  edgeAttractors[feati][nearInfo.index()].append
2149  (
2150  nearInfo.hitPoint()
2151  );
2152  pointConstraint c(Tuple2<label, vector>(2, nearNormal[0]));
2153  edgeConstraints[feati][nearInfo.index()].append(c);
2154 
2155  // Store for later use
2156  patchAttraction[pointi] = nearInfo.hitPoint()-pp.localPoints()[pointi];
2157  patchConstraints[pointi] = c;
2158  }
2159  return Tuple2<label, pointIndexHit>(feati, nearInfo);
2160 }
2161 
2162 
2164 Foam::snappySnapDriver::findNearFeaturePoint
2165 (
2166  const bool isRegionPoint,
2167 
2168  const indirectPrimitivePatch& pp,
2169  const scalarField& snapDist,
2170  const label pointi,
2171  const point& estimatedPt,
2172 
2173  // Feature-point to pp point
2174  List<labelList>& pointAttractor,
2175  List<List<pointConstraint>>& pointConstraints,
2176  // Feature-edge to pp point
2177  List<List<DynamicList<point>>>& edgeAttractors,
2178  List<List<DynamicList<pointConstraint>>>& edgeConstraints,
2179  // pp point to nearest feature
2180  vectorField& patchAttraction,
2181  List<pointConstraint>& patchConstraints
2182 ) const
2183 {
2184  const refinementFeatures& features = meshRefiner_.features();
2185 
2186  // Search for for featurePoints only! This ignores any non-feature points.
2187 
2188  labelList nearFeat;
2189  List<pointIndexHit> nearInfo;
2190  features.findNearestPoint
2191  (
2192  pointField(1, estimatedPt),
2193  scalarField(1, sqr(snapDist[pointi])),
2194  nearFeat,
2195  nearInfo
2196  );
2197 
2198  label feati = nearFeat[0];
2199 
2200  if (feati != -1)
2201  {
2202  const point& pt = pp.localPoints()[pointi];
2203 
2204  label featPointi = nearInfo[0].index();
2205  const point& featPt = nearInfo[0].hitPoint();
2206  scalar distSqr = magSqr(featPt-pt);
2207 
2208  // Check if already attracted
2209  label oldPointi = pointAttractor[feati][featPointi];
2210 
2211  if (oldPointi != -1)
2212  {
2213  // Check distance
2214  if (distSqr >= magSqr(featPt-pp.localPoints()[oldPointi]))
2215  {
2216  // oldPointi nearest. Keep.
2217  feati = -1;
2218  featPointi = -1;
2219  }
2220  else
2221  {
2222  // Current pointi nearer.
2223  pointAttractor[feati][featPointi] = pointi;
2224  pointConstraints[feati][featPointi].first() = 3;
2225  pointConstraints[feati][featPointi].second() = Zero;
2226 
2227  // Store for later use
2228  patchAttraction[pointi] = featPt-pt;
2229  patchConstraints[pointi] = pointConstraints[feati][featPointi];
2230 
2231  // Reset oldPointi to nearest on feature edge
2232  patchAttraction[oldPointi] = Zero;
2233  patchConstraints[oldPointi] = pointConstraint();
2234 
2235  findNearFeatureEdge
2236  (
2237  isRegionPoint, // search region edges only
2238 
2239  pp,
2240  snapDist,
2241  oldPointi,
2242  pp.localPoints()[oldPointi],
2243 
2244  edgeAttractors,
2245  edgeConstraints,
2246  patchAttraction,
2247  patchConstraints
2248  );
2249  }
2250  }
2251  else
2252  {
2253  // Current pointi nearer.
2254  pointAttractor[feati][featPointi] = pointi;
2255  pointConstraints[feati][featPointi].first() = 3;
2256  pointConstraints[feati][featPointi].second() = Zero;
2257 
2258  // Store for later use
2259  patchAttraction[pointi] = featPt-pt;
2260  patchConstraints[pointi] = pointConstraints[feati][featPointi];
2261  }
2262  }
2263 
2264  return Tuple2<label, pointIndexHit>(feati, nearInfo[0]);
2265 }
2266 
2267 
2268 // Determines for every pp point - that is on multiple faces that form
2269 // a feature - the nearest feature edge/point.
2270 void Foam::snappySnapDriver::determineFeatures
2271 (
2272  const label iter,
2273  const scalar featureCos,
2274  const bool multiRegionFeatureSnap,
2275 
2276  const indirectPrimitivePatch& pp,
2277  const scalarField& snapDist,
2278  const vectorField& nearestDisp,
2279 
2280  const List<List<point>>& pointFaceSurfNormals,
2281  const List<List<point>>& pointFaceDisp,
2282  const List<List<point>>& pointFaceCentres,
2283  const labelListList& pointFacePatchID,
2284 
2285  // Feature-point to pp point
2286  List<labelList>& pointAttractor,
2287  List<List<pointConstraint>>& pointConstraints,
2288  // Feature-edge to pp point
2289  List<List<DynamicList<point>>>& edgeAttractors,
2290  List<List<DynamicList<pointConstraint>>>& edgeConstraints,
2291  // pp point to nearest feature
2292  vectorField& patchAttraction,
2293  List<pointConstraint>& patchConstraints
2294 ) const
2295 {
2296  autoPtr<OBJstream> featureEdgeStr;
2297  autoPtr<OBJstream> missedEdgeStr;
2298  autoPtr<OBJstream> featurePointStr;
2299  autoPtr<OBJstream> missedMP0Str;
2300  autoPtr<OBJstream> missedMP1Str;
2301 
2303  {
2304  featureEdgeStr.reset
2305  (
2306  new OBJstream
2307  (
2308  meshRefiner_.mesh().time().path()
2309  / "featureEdge_" + name(iter) + ".obj"
2310  )
2311  );
2312  Info<< "Dumping feature-edge sampling to "
2313  << featureEdgeStr().name() << endl;
2314 
2315  missedEdgeStr.reset
2316  (
2317  new OBJstream
2318  (
2319  meshRefiner_.mesh().time().path()
2320  / "missedFeatureEdge_" + name(iter) + ".obj"
2321  )
2322  );
2323  Info<< "Dumping feature-edges that are too far away to "
2324  << missedEdgeStr().name() << endl;
2325 
2326  featurePointStr.reset
2327  (
2328  new OBJstream
2329  (
2330  meshRefiner_.mesh().time().path()
2331  / "featurePoint_" + name(iter) + ".obj"
2332  )
2333  );
2334  Info<< "Dumping feature-point sampling to "
2335  << featurePointStr().name() << endl;
2336 
2337  missedMP0Str.reset
2338  (
2339  new OBJstream
2340  (
2341  meshRefiner_.mesh().time().path()
2342  / "missedFeatureEdgeFromMPEdge_" + name(iter) + ".obj"
2343  )
2344  );
2345  Info<< "Dumping region-edges that are too far away to "
2346  << missedMP0Str().name() << endl;
2347 
2348  missedMP1Str.reset
2349  (
2350  new OBJstream
2351  (
2352  meshRefiner_.mesh().time().path()
2353  / "missedFeatureEdgeFromMPPoint_" + name(iter) + ".obj"
2354  )
2355  );
2356  Info<< "Dumping region-points that are too far away to "
2357  << missedMP1Str().name() << endl;
2358  }
2359 
2360 
2361  DynamicList<point> surfacePoints(4);
2362  DynamicList<vector> surfaceNormals(4);
2363  labelList faceToNormalBin;
2364 
2365  forAll(pp.localPoints(), pointi)
2366  {
2367  const point& pt = pp.localPoints()[pointi];
2368 
2369 
2370  // Determine the geometric planes the point is (approximately) on.
2371  // This is returned as a
2372  // - attraction vector
2373  // - and a constraint
2374  // (1: attract to surface, constraint is normal of plane
2375  // 2: attract to feature line, constraint is feature line direction
2376  // 3: attract to feature point, constraint is zero)
2377 
2378  vector attraction = Zero;
2379  pointConstraint constraint;
2380 
2381  featureAttractionUsingReconstruction
2382  (
2383  iter,
2384  featureCos,
2385 
2386  pp,
2387  snapDist,
2388  nearestDisp,
2389 
2390  pointi,
2391 
2392  pointFaceSurfNormals,
2393  pointFaceDisp,
2394  pointFaceCentres,
2395  pointFacePatchID,
2396 
2397  surfacePoints,
2398  surfaceNormals,
2399  faceToNormalBin,
2400 
2401  attraction,
2402  constraint
2403  );
2404 
2405  // Now combine the reconstruction with the current state of the
2406  // point. The logic is quite complicated:
2407  // - the new constraint (from reconstruction) will only win if
2408  // - the constraint is higher (feature-point wins from feature-edge
2409  // etc.)
2410  // - or the constraint is the same but the attraction distance is less
2411  //
2412  // - then this will be combined with explicit searching on the
2413  // features and optionally the analysis of the patches using the
2414  // point. This analysis can do three thing:
2415  // - the point is not on multiple patches
2416  // - the point is on multiple patches but these are also
2417  // different planes (so the region feature is also a geometric
2418  // feature)
2419  // - the point is on multiple patches some of which are on
2420  // the same plane. This is the problem one - do we assume it is
2421  // an additional constraint (feat edge upgraded to region point,
2422  // see below)?
2423  //
2424  // Reconstruction MultiRegionFeatureSnap Attraction
2425  // ------- ---------------------- -----------
2426  // surface false surface
2427  // surface true region edge
2428  // feat edge false feat edge
2429  // feat edge true and no planar regions feat edge
2430  // feat edge true and yes planar regions region point
2431  // feat point false feat point
2432  // feat point true region point
2433 
2434 
2435  if
2436  (
2437  (constraint.first() > patchConstraints[pointi].first())
2438  || (
2439  (constraint.first() == patchConstraints[pointi].first())
2440  && (magSqr(attraction) < magSqr(patchAttraction[pointi]))
2441  )
2442  )
2443  {
2444  patchAttraction[pointi] = attraction;
2445  patchConstraints[pointi] = constraint;
2446 
2447  // Check the number of directions
2448  if (patchConstraints[pointi].first() == 1)
2449  {
2450  // Flat surface. Check for different patchIDs
2451  if (multiRegionFeatureSnap)
2452  {
2453  const point estimatedPt(pt + nearestDisp[pointi]);
2454  pointIndexHit multiPatchPt
2455  (
2456  findMultiPatchPoint
2457  (
2458  estimatedPt,
2459  pointFacePatchID[pointi],
2460  surfaceNormals,
2461  faceToNormalBin
2462  )
2463  );
2464 
2465  if (multiPatchPt.hit())
2466  {
2467  // Behave like when having two surface normals so
2468  // attract to nearest feature edge (with a guess for
2469  // the multipatch point as starting point)
2470  Tuple2<label, pointIndexHit> nearInfo =
2471  findNearFeatureEdge
2472  (
2473  true, // isRegionEdge
2474  pp,
2475  snapDist,
2476  pointi,
2477  multiPatchPt.hitPoint(), // estimatedPt
2478 
2479  edgeAttractors,
2480  edgeConstraints,
2481 
2482  patchAttraction,
2483  patchConstraints
2484  );
2485 
2486  const pointIndexHit& info = nearInfo.second();
2487  if (info.hit())
2488  {
2489  // Dump
2490  if (featureEdgeStr.valid())
2491  {
2492  featureEdgeStr().write
2493  (
2494  linePointRef(pt, info.hitPoint())
2495  );
2496  }
2497  }
2498  else
2499  {
2500  if (missedEdgeStr.valid())
2501  {
2502  missedEdgeStr().write
2503  (
2504  linePointRef(pt, multiPatchPt.hitPoint())
2505  );
2506  }
2507  }
2508  }
2509  }
2510  }
2511  else if (patchConstraints[pointi].first() == 2)
2512  {
2513  // Mark point on the nearest feature edge. Note that we
2514  // only search within the surrounding since the plane
2515  // reconstruction might find a feature where there isn't one.
2516  const point estimatedPt(pt + patchAttraction[pointi]);
2517 
2518  Tuple2<label, pointIndexHit> nearInfo(-1, pointIndexHit());
2519 
2520  // Geometric feature edge. Check for different patchIDs
2521  bool hasSnapped = false;
2522  if (multiRegionFeatureSnap)
2523  {
2524  pointIndexHit multiPatchPt
2525  (
2526  findMultiPatchPoint
2527  (
2528  estimatedPt,
2529  pointFacePatchID[pointi],
2530  surfaceNormals,
2531  faceToNormalBin
2532  )
2533  );
2534  if (multiPatchPt.hit())
2535  {
2536  if (multiPatchPt.index() == 0)
2537  {
2538  // Region edge is also a geometric feature edge
2539  nearInfo = findNearFeatureEdge
2540  (
2541  true, // isRegionEdge
2542  pp,
2543  snapDist,
2544  pointi,
2545  estimatedPt,
2546 
2547  edgeAttractors,
2548  edgeConstraints,
2549 
2550  patchAttraction,
2551  patchConstraints
2552  );
2553  hasSnapped = true;
2554 
2555  // Debug: dump missed feature point
2556  if
2557  (
2558  missedMP0Str.valid()
2559  && !nearInfo.second().hit()
2560  )
2561  {
2562  missedMP0Str().write
2563  (
2564  linePointRef(pt, estimatedPt)
2565  );
2566  }
2567  }
2568  else
2569  {
2570  // One of planes of feature contains multiple
2571  // regions. We assume (contentious!) that the
2572  // separation between
2573  // the regions is not aligned with the geometric
2574  // feature so is an additional constraint on the
2575  // point -> is region-feature-point.
2576  nearInfo = findNearFeaturePoint
2577  (
2578  true, // isRegionPoint
2579  pp,
2580  snapDist,
2581  pointi,
2582  estimatedPt,
2583 
2584  // Feature-point to pp point
2585  pointAttractor,
2586  pointConstraints,
2587  // Feature-edge to pp point
2588  edgeAttractors,
2589  edgeConstraints,
2590  // pp point to nearest feature
2591  patchAttraction,
2592  patchConstraints
2593  );
2594  hasSnapped = true;
2595 
2596  // More contentious: if we don't find
2597  // a near feature point we will never find the
2598  // attraction to a feature edge either since
2599  // the edgeAttractors/edgeConstraints do not get
2600  // filled and we're using reverse attraction
2601  // Note that we're in multiRegionFeatureSnap which
2602  // where findMultiPatchPoint can decide the
2603  // wrong thing. So: if failed finding a near
2604  // feature point try for a feature edge
2605  if (!nearInfo.second().hit())
2606  {
2607  nearInfo = findNearFeatureEdge
2608  (
2609  true, // isRegionEdge
2610  pp,
2611  snapDist,
2612  pointi,
2613  estimatedPt,
2614 
2615  // Feature-edge to pp point
2616  edgeAttractors,
2617  edgeConstraints,
2618  // pp point to nearest feature
2619  patchAttraction,
2620  patchConstraints
2621  );
2622  }
2623 
2624  // Debug: dump missed feature point
2625  if
2626  (
2627  missedMP1Str.valid()
2628  && !nearInfo.second().hit()
2629  )
2630  {
2631  missedMP1Str().write
2632  (
2633  linePointRef(pt, estimatedPt)
2634  );
2635  }
2636  }
2637  }
2638  }
2639 
2640  if (!hasSnapped)
2641  {
2642  // Determine nearest point on feature edge. Store
2643  // constraint
2644  // (calculated from feature edge, alternative would be to
2645  // use constraint calculated from both surfaceNormals)
2646  nearInfo = findNearFeatureEdge
2647  (
2648  false, // isRegionPoint
2649  pp,
2650  snapDist,
2651  pointi,
2652  estimatedPt,
2653 
2654  edgeAttractors,
2655  edgeConstraints,
2656 
2657  patchAttraction,
2658  patchConstraints
2659  );
2660  hasSnapped = true;
2661  }
2662 
2663  // Dump to obj
2664  const pointIndexHit& info = nearInfo.second();
2665  if (info.hit())
2666  {
2667  if
2668  (
2669  patchConstraints[pointi].first() == 3
2670  && featurePointStr.valid()
2671  )
2672  {
2673  featurePointStr().write
2674  (
2675  linePointRef(pt, info.hitPoint())
2676  );
2677  }
2678  else if
2679  (
2680  patchConstraints[pointi].first() == 2
2681  && featureEdgeStr.valid()
2682  )
2683  {
2684  featureEdgeStr().write
2685  (
2686  linePointRef(pt, info.hitPoint())
2687  );
2688  }
2689  }
2690  else
2691  {
2692  if (missedEdgeStr.valid())
2693  {
2694  missedEdgeStr().write
2695  (
2696  linePointRef(pt, estimatedPt)
2697  );
2698  }
2699  }
2700  }
2701  else if (patchConstraints[pointi].first() == 3)
2702  {
2703  // Mark point on the nearest feature point.
2704  const point estimatedPt(pt + patchAttraction[pointi]);
2705 
2706  Tuple2<label, pointIndexHit> nearInfo(-1, pointIndexHit());
2707 
2708  if (multiRegionFeatureSnap)
2709  {
2710  pointIndexHit multiPatchPt
2711  (
2712  findMultiPatchPoint
2713  (
2714  estimatedPt,
2715  pointFacePatchID[pointi],
2716  surfaceNormals,
2717  faceToNormalBin
2718  )
2719  );
2720  if (multiPatchPt.hit())
2721  {
2722  // Multiple regions
2723  nearInfo = findNearFeaturePoint
2724  (
2725  true, // isRegionPoint
2726  pp,
2727  snapDist,
2728  pointi,
2729  estimatedPt,
2730 
2731  // Feature-point to pp point
2732  pointAttractor,
2733  pointConstraints,
2734  // Feature-edge to pp point
2735  edgeAttractors,
2736  edgeConstraints,
2737  // pp point to nearest feature
2738  patchAttraction,
2739  patchConstraints
2740  );
2741  }
2742  else
2743  {
2744  nearInfo = findNearFeaturePoint
2745  (
2746  false, // isRegionPoint
2747  pp,
2748  snapDist,
2749  pointi,
2750  estimatedPt,
2751 
2752  // Feature-point to pp point
2753  pointAttractor,
2754  pointConstraints,
2755  // Feature-edge to pp point
2756  edgeAttractors,
2757  edgeConstraints,
2758  // pp point to nearest feature
2759  patchAttraction,
2760  patchConstraints
2761  );
2762  }
2763  }
2764  else
2765  {
2766  // No multi-patch snapping
2767  nearInfo = findNearFeaturePoint
2768  (
2769  false, // isRegionPoint
2770  pp,
2771  snapDist,
2772  pointi,
2773  estimatedPt,
2774 
2775  // Feature-point to pp point
2776  pointAttractor,
2777  pointConstraints,
2778  // Feature-edge to pp point
2779  edgeAttractors,
2780  edgeConstraints,
2781  // pp point to nearest feature
2782  patchAttraction,
2783  patchConstraints
2784  );
2785  }
2786 
2787  const pointIndexHit& info = nearInfo.second();
2788  if (info.hit() && featurePointStr.valid())
2789  {
2790  featurePointStr().write
2791  (
2792  linePointRef(pt, info.hitPoint())
2793  );
2794  }
2795  }
2796  }
2797  }
2798 }
2799 
2800 
2801 // Baffle handling
2802 // ~~~~~~~~~~~~~~~
2803 // Override pointAttractor, edgeAttractor, patchAttration etc. to
2804 // implement 'baffle' handling.
2805 // Baffle: the mesh pp point originates from a loose standing
2806 // baffle.
2807 // Sampling the surface with the surrounding face-centres only picks up
2808 // a single triangle normal so above determineFeatures will not have
2809 // detected anything. So explicitly pick up feature edges on the pp
2810 // (after duplicating points & smoothing so will already have been
2811 // expanded) and match these to the features.
2812 void Foam::snappySnapDriver::determineBaffleFeatures
2813 (
2814  const label iter,
2815  const bool baffleFeaturePoints,
2816  const scalar featureCos,
2817 
2818  const indirectPrimitivePatch& pp,
2819  const scalarField& snapDist,
2820 
2821  // Feature-point to pp point
2822  List<labelList>& pointAttractor,
2823  List<List<pointConstraint>>& pointConstraints,
2824  // Feature-edge to pp point
2825  List<List<DynamicList<point>>>& edgeAttractors,
2826  List<List<DynamicList<pointConstraint>>>& edgeConstraints,
2827  // pp point to nearest feature
2828  vectorField& patchAttraction,
2829  List<pointConstraint>& patchConstraints
2830 ) const
2831 {
2832  const fvMesh& mesh = meshRefiner_.mesh();
2833  const refinementFeatures& features = meshRefiner_.features();
2834 
2835  // Calculate edge-faces
2836  List<List<point>> edgeFaceNormals(pp.nEdges());
2837 
2838  // Fill local data
2839  forAll(pp.edgeFaces(), edgei)
2840  {
2841  const labelList& eFaces = pp.edgeFaces()[edgei];
2842  List<point>& eFc = edgeFaceNormals[edgei];
2843  eFc.setSize(eFaces.size());
2844  forAll(eFaces, i)
2845  {
2846  label facei = eFaces[i];
2847  eFc[i] = pp.faceNormals()[facei];
2848  }
2849  }
2850 
2851  {
2852  // Precalculate mesh edges for pp.edges.
2853  const labelList meshEdges
2854  (
2855  pp.meshEdges(mesh.edges(), mesh.pointEdges())
2856  );
2858  (
2859  mesh,
2860  meshEdges,
2861  edgeFaceNormals,
2862  listPlusEqOp<point>(),
2863  List<point>(),
2865  );
2866  }
2867 
2868  // Detect baffle edges. Assume initial mesh will have 0,90 or 180
2869  // (baffle) degree angles so smoothing should make 0,90
2870  // to be less than 90. Choose reasonable value
2871  const scalar baffleFeatureCos = Foam::cos(degToRad(110.0));
2872 
2873 
2874  autoPtr<OBJstream> baffleEdgeStr;
2876  {
2877  baffleEdgeStr.reset
2878  (
2879  new OBJstream
2880  (
2881  meshRefiner_.mesh().time().path()
2882  / "baffleEdge_" + name(iter) + ".obj"
2883  )
2884  );
2885  Info<< nl << "Dumping baffle-edges to "
2886  << baffleEdgeStr().name() << endl;
2887  }
2888 
2889 
2890  // Is edge on baffle
2891  bitSet isBaffleEdge(pp.nEdges());
2892  label nBaffleEdges = 0;
2893  // Is point on
2894  // 0 : baffle-edge (0)
2895  // 1 : baffle-feature-point (1)
2896  // -1 : rest
2897  labelList pointStatus(pp.nPoints(), -1);
2898 
2899  forAll(edgeFaceNormals, edgei)
2900  {
2901  const List<point>& efn = edgeFaceNormals[edgei];
2902 
2903  if (efn.size() == 2 && (efn[0]&efn[1]) < baffleFeatureCos)
2904  {
2905  isBaffleEdge.set(edgei);
2906  ++nBaffleEdges;
2907  const edge& e = pp.edges()[edgei];
2908  pointStatus[e[0]] = 0;
2909  pointStatus[e[1]] = 0;
2910 
2911  if (baffleEdgeStr.valid())
2912  {
2913  const point& p0 = pp.localPoints()[e[0]];
2914  const point& p1 = pp.localPoints()[e[1]];
2915  baffleEdgeStr().write(linePointRef(p0, p1));
2916  }
2917  }
2918  }
2919 
2920  reduce(nBaffleEdges, sumOp<label>());
2921 
2922  Info<< "Detected " << nBaffleEdges
2923  << " baffle edges out of "
2924  << returnReduce(pp.nEdges(), sumOp<label>())
2925  << " edges." << endl;
2926 
2927 
2928  //- Baffle edges will be too ragged to sensibly determine feature points
2929  //forAll(pp.pointEdges(), pointi)
2930  //{
2931  // if
2932  // (
2933  // isFeaturePoint
2934  // (
2935  // featureCos,
2936  // pp,
2937  // isBaffleEdge,
2938  // pointi
2939  // )
2940  // )
2941  // {
2942  // //Pout<< "Detected feature point:" << pp.localPoints()[pointi]
2943  // // << endl;
2944  // //-TEMPORARILY DISABLED:
2945  // //pointStatus[pointi] = 1;
2946  // }
2947  //}
2948 
2949 
2950  label nBafflePoints = 0;
2951  forAll(pointStatus, pointi)
2952  {
2953  if (pointStatus[pointi] != -1)
2954  {
2955  nBafflePoints++;
2956  }
2957  }
2958  reduce(nBafflePoints, sumOp<label>());
2959 
2960 
2961  label nPointAttract = 0;
2962  label nEdgeAttract = 0;
2963 
2964  forAll(pointStatus, pointi)
2965  {
2966  const point& pt = pp.localPoints()[pointi];
2967 
2968  if (pointStatus[pointi] == 0) // baffle edge
2969  {
2970  // 1: attract to near feature edge first
2971 
2972  Tuple2<label, pointIndexHit> nearInfo = findNearFeatureEdge
2973  (
2974  false, // isRegionPoint?
2975  pp,
2976  snapDist,
2977  pointi,
2978  pt,
2979 
2980  edgeAttractors,
2981  edgeConstraints,
2982  patchAttraction,
2983  patchConstraints
2984  );
2985 
2986 
2987  //- MEJ:
2988  // 2: optionally override with nearest feature point.
2989  // On baffles we don't have enough normals to construct a feature
2990  // point so assume all feature edges are close to feature points
2991  if (nearInfo.second().hit())
2992  {
2993  nEdgeAttract++;
2994 
2995  if (baffleFeaturePoints)
2996  {
2997  nearInfo = findNearFeaturePoint
2998  (
2999  false, // isRegionPoint,
3000 
3001  pp,
3002  snapDist,
3003  pointi,
3004  pt, // estimatedPt,
3005 
3006  // Feature-point to pp point
3007  pointAttractor,
3008  pointConstraints,
3009  // Feature-edge to pp point
3010  edgeAttractors,
3011  edgeConstraints,
3012  // pp point to nearest feature
3013  patchAttraction,
3014  patchConstraints
3015  );
3016 
3017  if (nearInfo.first() != -1)
3018  {
3019  nEdgeAttract--;
3020  nPointAttract++;
3021  }
3022  }
3023  }
3024  }
3025  else if (pointStatus[pointi] == 1) // baffle point
3026  {
3027  labelList nearFeat;
3028  List<pointIndexHit> nearInfo;
3029  features.findNearestPoint
3030  (
3031  pointField(1, pt),
3032  scalarField(1, sqr(snapDist[pointi])),
3033  nearFeat,
3034  nearInfo
3035  );
3036 
3037  label feati = nearFeat[0];
3038 
3039  if (feati != -1)
3040  {
3041  nPointAttract++;
3042 
3043  label featPointi = nearInfo[0].index();
3044  const point& featPt = nearInfo[0].hitPoint();
3045  scalar distSqr = magSqr(featPt-pt);
3046 
3047  // Check if already attracted
3048  label oldPointi = pointAttractor[feati][featPointi];
3049 
3050  if
3051  (
3052  oldPointi == -1
3053  || (
3054  distSqr
3055  < magSqr(featPt-pp.localPoints()[oldPointi])
3056  )
3057  )
3058  {
3059  pointAttractor[feati][featPointi] = pointi;
3060  pointConstraints[feati][featPointi].first() = 3;
3061  pointConstraints[feati][featPointi].second() = Zero;
3062 
3063  // Store for later use
3064  patchAttraction[pointi] = featPt-pt;
3065  patchConstraints[pointi] =
3066  pointConstraints[feati][featPointi];
3067 
3068  if (oldPointi != -1)
3069  {
3070  // The current point is closer so wins. Reset
3071  // the old point to attract to nearest edge
3072  // instead.
3073  findNearFeatureEdge
3074  (
3075  false, // isRegionPoint
3076  pp,
3077  snapDist,
3078  oldPointi,
3079  pp.localPoints()[oldPointi],
3080 
3081  edgeAttractors,
3082  edgeConstraints,
3083  patchAttraction,
3084  patchConstraints
3085  );
3086  }
3087  }
3088  else
3089  {
3090  // Make it fall through to check below
3091  feati = -1;
3092  }
3093  }
3094 
3095  // Not found a feature point or another point is already
3096  // closer to that feature
3097  if (feati == -1)
3098  {
3099  //Pout<< "*** Falling back to finding nearest feature"
3100  // << " edge"
3101  // << " for baffle-feature-point " << pt
3102  // << endl;
3103 
3104  Tuple2<label, pointIndexHit> nearInfo = findNearFeatureEdge
3105  (
3106  false, // isRegionPoint
3107  pp,
3108  snapDist,
3109  pointi,
3110  pt, // starting point
3111 
3112  edgeAttractors,
3113  edgeConstraints,
3114  patchAttraction,
3115  patchConstraints
3116  );
3117 
3118  if (nearInfo.first() != -1)
3119  {
3120  nEdgeAttract++;
3121  }
3122  }
3123  }
3124  }
3125 
3126  reduce(nPointAttract, sumOp<label>());
3127  reduce(nEdgeAttract, sumOp<label>());
3128 
3129  Info<< "Baffle points : " << nBafflePoints
3130  << " of which attracted to :" << nl
3131  << " feature point : " << nPointAttract << nl
3132  << " feature edge : " << nEdgeAttract << nl
3133  << " rest : " << nBafflePoints-nPointAttract-nEdgeAttract
3134  << nl
3135  << endl;
3136 }
3137 
3138 
3139 void Foam::snappySnapDriver::reverseAttractMeshPoints
3140 (
3141  const label iter,
3142 
3143  const indirectPrimitivePatch& pp,
3144  const scalarField& snapDist,
3145 
3146  // Feature-point to pp point
3147  const List<labelList>& pointAttractor,
3148  const List<List<pointConstraint>>& pointConstraints,
3149  // Feature-edge to pp point
3150  const List<List<DynamicList<point>>>& edgeAttractors,
3151  const List<List<DynamicList<pointConstraint>>>& edgeConstraints,
3152 
3153  const vectorField& rawPatchAttraction,
3154  const List<pointConstraint>& rawPatchConstraints,
3155 
3156  // pp point to nearest feature
3157  vectorField& patchAttraction,
3158  List<pointConstraint>& patchConstraints
3159 ) const
3160 {
3161  const refinementFeatures& features = meshRefiner_.features();
3162 
3163  // Find nearest mesh point to feature edge
3164  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3165  // Reverse lookup : go through all edgeAttractors and find the
3166  // nearest point on pp
3167 
3168  // Get search domain and extend it a bit
3169  treeBoundBox bb(pp.localPoints());
3170  {
3171  // Random number generator. Bit dodgy since not exactly random ;-)
3172  Random rndGen(65431);
3173 
3174  // Slightly extended bb. Slightly off-centred just so on symmetric
3175  // geometry there are less face/edge aligned items.
3176  bb = bb.extend(rndGen, 1e-4);
3177  bb.min() -= point::uniform(ROOTVSMALL);
3178  bb.max() += point::uniform(ROOTVSMALL);
3179  }
3180 
3181  // Collect candidate points for attraction
3182  DynamicList<label> attractPoints(pp.nPoints());
3183  {
3184  const fvMesh& mesh = meshRefiner_.mesh();
3185 
3186  boolList isFeatureEdgeOrPoint(pp.nPoints(), false);
3187  label nFeats = 0;
3188  forAll(rawPatchConstraints, pointi)
3189  {
3190  if (rawPatchConstraints[pointi].first() >= 2)
3191  {
3192  isFeatureEdgeOrPoint[pointi] = true;
3193  nFeats++;
3194  }
3195  }
3196 
3197  Info<< "Initially selected " << returnReduce(nFeats, sumOp<label>())
3198  << " mesh points out of "
3199  << returnReduce(pp.nPoints(), sumOp<label>())
3200  << " for reverse attraction." << endl;
3201 
3202  // Make sure is synchronised (note: check if constraint is already
3203  // synced in which case this is not needed here)
3205  (
3206  mesh,
3207  pp.meshPoints(),
3208  isFeatureEdgeOrPoint,
3209  orEqOp<bool>(), // combine op
3210  false
3211  );
3212 
3213  for (label nGrow = 0; nGrow < 1; nGrow++)
3214  {
3215  boolList newIsFeatureEdgeOrPoint(isFeatureEdgeOrPoint);
3216 
3217  forAll(pp.localFaces(), facei)
3218  {
3219  const face& f = pp.localFaces()[facei];
3220 
3221  forAll(f, fp)
3222  {
3223  if (isFeatureEdgeOrPoint[f[fp]])
3224  {
3225  // Mark all points on face
3226  forAll(f, fp)
3227  {
3228  newIsFeatureEdgeOrPoint[f[fp]] = true;
3229  }
3230  break;
3231  }
3232  }
3233  }
3234 
3235  isFeatureEdgeOrPoint = newIsFeatureEdgeOrPoint;
3236 
3238  (
3239  mesh,
3240  pp.meshPoints(),
3241  isFeatureEdgeOrPoint,
3242  orEqOp<bool>(), // combine op
3243  false
3244  );
3245  }
3246 
3247 
3248  // Collect attractPoints
3249  forAll(isFeatureEdgeOrPoint, pointi)
3250  {
3251  if (isFeatureEdgeOrPoint[pointi])
3252  {
3253  attractPoints.append(pointi);
3254  }
3255  }
3256 
3257  Info<< "Selected "
3258  << returnReduce(attractPoints.size(), sumOp<label>())
3259  << " mesh points out of "
3260  << returnReduce(pp.nPoints(), sumOp<label>())
3261  << " for reverse attraction." << endl;
3262  }
3263 
3264 
3265  indexedOctree<treeDataPoint> ppTree
3266  (
3267  treeDataPoint(pp.localPoints(), attractPoints),
3268  bb, // overall search domain
3269  8, // maxLevel
3270  10, // leafsize
3271  3.0 // duplicity
3272  );
3273 
3274  // Per mesh point the point on nearest feature edge.
3275  patchAttraction.setSize(pp.nPoints());
3276  patchAttraction = Zero;
3277  patchConstraints.setSize(pp.nPoints());
3278  patchConstraints = pointConstraint();
3279 
3280  forAll(edgeAttractors, feati)
3281  {
3282  const List<DynamicList<point>>& edgeAttr = edgeAttractors[feati];
3283  const List<DynamicList<pointConstraint>>& edgeConstr =
3284  edgeConstraints[feati];
3285 
3286  forAll(edgeAttr, featEdgei)
3287  {
3288  const DynamicList<point>& attr = edgeAttr[featEdgei];
3289  forAll(attr, i)
3290  {
3291  // Find nearest pp point
3292  const point& featPt = attr[i];
3293  pointIndexHit nearInfo = ppTree.findNearest
3294  (
3295  featPt,
3296  sqr(GREAT)
3297  );
3298 
3299  if (nearInfo.hit())
3300  {
3301  label pointi =
3302  ppTree.shapes().pointLabels()[nearInfo.index()];
3303  const point attraction = featPt-pp.localPoints()[pointi];
3304 
3305  // Check if this point is already being attracted. If so
3306  // override it only if nearer.
3307  if
3308  (
3309  patchConstraints[pointi].first() <= 1
3310  || magSqr(attraction) < magSqr(patchAttraction[pointi])
3311  )
3312  {
3313  patchAttraction[pointi] = attraction;
3314  patchConstraints[pointi] = edgeConstr[featEdgei][i];
3315  }
3316  }
3317  else
3318  {
3319  static label nWarn = 0;
3320 
3321  if (nWarn < 100)
3322  {
3324  << "Did not find pp point near " << featPt
3325  << endl;
3326  nWarn++;
3327  if (nWarn == 100)
3328  {
3330  << "Reached warning limit " << nWarn
3331  << ". Suppressing further warnings." << endl;
3332  }
3333  }
3334 
3335  }
3336  }
3337  }
3338  }
3339 
3340 
3341  // Different procs might have different patchAttraction,patchConstraints
3342  // however these only contain geometric information, no topology
3343  // so as long as we synchronise after overriding with feature points
3344  // there is no problem, just possibly a small error.
3345 
3346 
3347  // Find nearest mesh point to feature point
3348  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3349  // (overrides attraction to feature edge)
3350  forAll(pointAttractor, feati)
3351  {
3352  const labelList& pointAttr = pointAttractor[feati];
3353  const List<pointConstraint>& pointConstr = pointConstraints[feati];
3354 
3355  forAll(pointAttr, featPointi)
3356  {
3357  if (pointAttr[featPointi] != -1)
3358  {
3359  const point& featPt = features[feati].points()
3360  [
3361  featPointi
3362  ];
3363 
3364  // Find nearest pp point
3365  pointIndexHit nearInfo = ppTree.findNearest
3366  (
3367  featPt,
3368  sqr(GREAT)
3369  );
3370 
3371  if (nearInfo.hit())
3372  {
3373  label pointi =
3374  ppTree.shapes().pointLabels()[nearInfo.index()];
3375 
3376  const point& pt = pp.localPoints()[pointi];
3377  const point attraction = featPt-pt;
3378 
3379  // - already attracted to feature edge : point always wins
3380  // - already attracted to feature point: nearest wins
3381 
3382  if (patchConstraints[pointi].first() <= 1)
3383  {
3384  patchAttraction[pointi] = attraction;
3385  patchConstraints[pointi] = pointConstr[featPointi];
3386  }
3387  else if (patchConstraints[pointi].first() == 2)
3388  {
3389  patchAttraction[pointi] = attraction;
3390  patchConstraints[pointi] = pointConstr[featPointi];
3391  }
3392  else if (patchConstraints[pointi].first() == 3)
3393  {
3394  // Only if nearer
3395  if
3396  (
3397  magSqr(attraction)
3398  < magSqr(patchAttraction[pointi])
3399  )
3400  {
3401  patchAttraction[pointi] = attraction;
3402  patchConstraints[pointi] =
3403  pointConstr[featPointi];
3404  }
3405  }
3406  }
3407  }
3408  }
3409  }
3410 }
3411 
3412 
3413 void Foam::snappySnapDriver::featureAttractionUsingFeatureEdges
3414 (
3415  const label iter,
3416  const bool multiRegionFeatureSnap,
3417 
3418  const bool detectBaffles,
3419  const bool baffleFeaturePoints,
3420 
3421  const bool releasePoints,
3422  const bool stringFeatures,
3423  const bool avoidDiagonal,
3424 
3425  const scalar featureCos,
3426 
3427  const indirectPrimitivePatch& pp,
3428  const scalarField& snapDist,
3429  const vectorField& nearestDisp,
3430  const vectorField& nearestNormal,
3431 
3432  const List<List<point>>& pointFaceSurfNormals,
3433  const List<List<point>>& pointFaceDisp,
3434  const List<List<point>>& pointFaceCentres,
3435  const labelListList& pointFacePatchID,
3436 
3437  vectorField& patchAttraction,
3438  List<pointConstraint>& patchConstraints
3439 ) const
3440 {
3441  const refinementFeatures& features = meshRefiner_.features();
3442  const fvMesh& mesh = meshRefiner_.mesh();
3443 
3444  const bitSet isPatchMasterPoint
3445  (
3447  (
3448  mesh,
3449  pp.meshPoints()
3450  )
3451  );
3452 
3453 
3454  // Collect ordered attractions on feature edges
3455  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3456 
3457  // Per feature, per feature-edge a list of attraction points and their
3458  // originating vertex.
3459  List<List<DynamicList<point>>> edgeAttractors(features.size());
3460  List<List<DynamicList<pointConstraint>>> edgeConstraints
3461  (
3462  features.size()
3463  );
3464  forAll(features, feati)
3465  {
3466  label nFeatEdges = features[feati].edges().size();
3467  edgeAttractors[feati].setSize(nFeatEdges);
3468  edgeConstraints[feati].setSize(nFeatEdges);
3469  }
3470 
3471  // Per feature, per feature-point the pp point that is attracted to it.
3472  // This list is only used to subset the feature-points that are actually
3473  // used.
3474  List<labelList> pointAttractor(features.size());
3475  List<List<pointConstraint>> pointConstraints(features.size());
3476  forAll(features, feati)
3477  {
3478  label nFeatPoints = features[feati].points().size();
3479  pointAttractor[feati].setSize(nFeatPoints, -1);
3480  pointConstraints[feati].setSize(nFeatPoints);
3481  }
3482 
3483  // Reverse: from pp point to nearest feature
3484  vectorField rawPatchAttraction(pp.nPoints(), Zero);
3485  List<pointConstraint> rawPatchConstraints(pp.nPoints());
3486 
3487  determineFeatures
3488  (
3489  iter,
3490  featureCos,
3491  multiRegionFeatureSnap,
3492 
3493  pp,
3494  snapDist, // per point max distance and nearest surface
3495  nearestDisp,
3496 
3497  pointFaceSurfNormals, // per face nearest surface
3498  pointFaceDisp,
3499  pointFaceCentres,
3500  pointFacePatchID,
3501 
3502  // Feature-point to pp point
3503  pointAttractor,
3504  pointConstraints,
3505  // Feature-edge to pp point
3506  edgeAttractors,
3507  edgeConstraints,
3508  // pp point to nearest feature
3509  rawPatchAttraction,
3510  rawPatchConstraints
3511  );
3512 
3513  // Print a bit about the attraction from patch point to feature
3514  if (debug)
3515  {
3516  Info<< "Raw geometric feature analysis : ";
3517  writeStats(pp, isPatchMasterPoint, rawPatchConstraints);
3518  }
3519 
3520  // Baffle handling
3521  // ~~~~~~~~~~~~~~~
3522  // Override pointAttractor, edgeAttractor, rawPatchAttration etc. to
3523  // implement 'baffle' handling.
3524  // Baffle: the mesh pp point originates from a loose standing
3525  // baffle.
3526  // Sampling the surface with the surrounding face-centres only picks up
3527  // a single triangle normal so above determineFeatures will not have
3528  // detected anything. So explicitly pick up feature edges on the pp
3529  // (after duplicating points & smoothing so will already have been
3530  // expanded) and match these to the features.
3531  if (detectBaffles)
3532  {
3533  determineBaffleFeatures
3534  (
3535  iter,
3536  baffleFeaturePoints,
3537  featureCos,
3538 
3539  pp,
3540  snapDist,
3541 
3542  // Feature-point to pp point
3543  pointAttractor,
3544  pointConstraints,
3545  // Feature-edge to pp point
3546  edgeAttractors,
3547  edgeConstraints,
3548  // pp point to nearest feature
3549  rawPatchAttraction,
3550  rawPatchConstraints
3551  );
3552  }
3553 
3554  // Print a bit about the attraction from patch point to feature
3555  if (debug)
3556  {
3557  Info<< "After baffle feature analysis : ";
3558  writeStats(pp, isPatchMasterPoint, rawPatchConstraints);
3559  }
3560 
3561 
3562  // Reverse lookup: Find nearest mesh point to feature edge
3563  // ~~~~~~~~~~~~~~~~----------------~~~~~~~~~~~~~~~~~~~~~~~
3564  // go through all edgeAttractors and find the nearest point on pp
3565 
3566  reverseAttractMeshPoints
3567  (
3568  iter,
3569 
3570  pp,
3571  snapDist,
3572 
3573  // Feature-point to pp point
3574  pointAttractor,
3575  pointConstraints,
3576  // Feature-edge to pp point
3577  edgeAttractors,
3578  edgeConstraints,
3579 
3580  // Estimated feature point
3581  rawPatchAttraction,
3582  rawPatchConstraints,
3583 
3584  // pp point to nearest feature
3585  patchAttraction,
3586  patchConstraints
3587  );
3588 
3589  // Print a bit about the attraction from patch point to feature
3590  if (debug)
3591  {
3592  Info<< "Reverse attract feature analysis : ";
3593  writeStats(pp, isPatchMasterPoint, patchConstraints);
3594  }
3595 
3596  // Dump
3598  {
3599  OBJstream featureEdgeStr
3600  (
3601  meshRefiner_.mesh().time().path()
3602  / "edgeAttractors_" + name(iter) + ".obj"
3603  );
3604  Info<< "Dumping feature-edge attraction to "
3605  << featureEdgeStr.name() << endl;
3606 
3607  OBJstream featurePointStr
3608  (
3609  meshRefiner_.mesh().time().path()
3610  / "pointAttractors_" + name(iter) + ".obj"
3611  );
3612  Info<< "Dumping feature-point attraction to "
3613  << featurePointStr.name() << endl;
3614 
3615  forAll(patchConstraints, pointi)
3616  {
3617  const point& pt = pp.localPoints()[pointi];
3618  const vector& attr = patchAttraction[pointi];
3619 
3620  if (patchConstraints[pointi].first() == 2)
3621  {
3622  featureEdgeStr.write(linePointRef(pt, pt+attr));
3623  }
3624  else if (patchConstraints[pointi].first() == 3)
3625  {
3626  featurePointStr.write(linePointRef(pt, pt+attr));
3627  }
3628  }
3629  }
3630 
3631 
3632  //MEJ: any faces that have multi-patch points only keep the
3633  // multi-patch
3634  // points. The other points on the face will be dragged along
3635  // (hopefully)
3636  if (releasePoints)
3637  {
3638  releasePointsNextToMultiPatch
3639  (
3640  iter,
3641  featureCos,
3642 
3643  pp,
3644  snapDist,
3645 
3646  pointFaceCentres,
3647  pointFacePatchID,
3648 
3649  rawPatchAttraction,
3650  rawPatchConstraints,
3651 
3652  patchAttraction,
3653  patchConstraints
3654  );
3655  }
3656 
3657 
3658  // Snap edges to feature edges
3659  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
3660  // Walk existing edges and snap remaining ones (that are marked as
3661  // feature edges in rawPatchConstraints)
3662  if (stringFeatures)
3663  {
3664  stringFeatureEdges
3665  (
3666  iter,
3667  featureCos,
3668 
3669  pp,
3670  snapDist,
3671 
3672  rawPatchAttraction,
3673  rawPatchConstraints,
3674 
3675  patchAttraction,
3676  patchConstraints
3677  );
3678  }
3679 
3680 
3681  // Avoid diagonal attraction
3682  // ~~~~~~~~~~~~~~~~~~~~~~~~~
3683  // Attract one of the non-diagonal points.
3684  if (avoidDiagonal)
3685  {
3686  avoidDiagonalAttraction
3687  (
3688  iter,
3689  featureCos,
3690  pp,
3691  patchAttraction,
3692  patchConstraints
3693  );
3694  }
3695 
3696 
3698  {
3699  dumpMove
3700  (
3701  meshRefiner_.mesh().time().path()
3702  / "patchAttraction_" + name(iter) + ".obj",
3703  pp.localPoints(),
3704  pp.localPoints() + patchAttraction
3705  );
3706  }
3707 }
3708 
3709 
3710 // Correct for squeezing of face
3711 void Foam::snappySnapDriver::preventFaceSqueeze
3712 (
3713  const label iter,
3714  const scalar featureCos,
3715 
3716  const indirectPrimitivePatch& pp,
3717  const scalarField& snapDist,
3718  const vectorField& nearestAttraction,
3719 
3720  vectorField& patchAttraction,
3721  List<pointConstraint>& patchConstraints
3722 ) const
3723 {
3724  autoPtr<OBJstream> strPtr;
3726  {
3727  strPtr.reset
3728  (
3729  new OBJstream
3730  (
3731  meshRefiner_.mesh().time().path()
3732  / "faceSqueeze_" + name(iter) + ".obj"
3733  )
3734  );
3735  Info<< "Dumping faceSqueeze corrections to "
3736  << strPtr().name() << endl;
3737  }
3738 
3740  face singleF;
3741  forAll(pp.localFaces(), facei)
3742  {
3743  const face& f = pp.localFaces()[facei];
3744 
3745  if (f.size() != points.size())
3746  {
3747  points.setSize(f.size());
3748  singleF.setSize(f.size());
3749  for (label i = 0; i < f.size(); i++)
3750  {
3751  singleF[i] = i;
3752  }
3753  }
3754  label nConstraints = 0;
3755  forAll(f, fp)
3756  {
3757  label pointi = f[fp];
3758  const point& pt = pp.localPoints()[pointi];
3759 
3760  if (patchConstraints[pointi].first() > 1)
3761  {
3762  points[fp] = pt + patchAttraction[pointi];
3763  nConstraints++;
3764  }
3765  else
3766  {
3767  points[fp] = pt;
3768  }
3769  }
3770 
3771  if (nConstraints == f.size())
3772  {
3773  if (f.size() == 3)
3774  {
3775  // Triangle: knock out attraction altogether
3776 
3777  // For now keep the points on the longest edge
3778  label maxFp = -1;
3779  scalar maxS = -1;
3780  forAll(f, fp)
3781  {
3782  const point& pt = pp.localPoints()[f[fp]];
3783  const point& nextPt = pp.localPoints()[f.nextLabel(fp)];
3784 
3785  scalar s = magSqr(pt-nextPt);
3786  if (s > maxS)
3787  {
3788  maxS = s;
3789  maxFp = fp;
3790  }
3791  }
3792  if (maxFp != -1)
3793  {
3794  label pointi = f.prevLabel(maxFp);
3795 
3796  // Reset attraction on pointi to nearest
3797 
3798  const point& pt = pp.localPoints()[pointi];
3799 
3800  //Pout<< "** on triangle " << pp.faceCentres()[facei]
3801  // << " knocking out attraction to " << pointi
3802  // << " at:" << pt
3803  // << endl;
3804 
3805  patchAttraction[pointi] = nearestAttraction[pointi];
3806 
3807  if (strPtr.valid())
3808  {
3809  strPtr().write
3810  (
3811  linePointRef(pt, pt+patchAttraction[pointi])
3812  );
3813  }
3814  }
3815  }
3816  else
3817  {
3818  scalar oldArea = f.mag(pp.localPoints());
3819  scalar newArea = singleF.mag(points);
3820  if (newArea < 0.1*oldArea)
3821  {
3822  // For now remove the point with largest distance
3823  label maxFp = -1;
3824  scalar maxS = -1;
3825  forAll(f, fp)
3826  {
3827  scalar s = magSqr(patchAttraction[f[fp]]);
3828  if (s > maxS)
3829  {
3830  maxS = s;
3831  maxFp = fp;
3832  }
3833  }
3834  if (maxFp != -1)
3835  {
3836  label pointi = f[maxFp];
3837  // Lower attraction on pointi
3838  patchAttraction[pointi] *= 0.5;
3839  }
3840  }
3841  }
3842  }
3843  }
3844 }
3845 
3846 
3847 Foam::vectorField Foam::snappySnapDriver::calcNearestSurfaceFeature
3848 (
3849  const snapParameters& snapParams,
3850  const bool alignMeshEdges,
3851  const label iter,
3852  const scalar featureCos,
3853  const scalar featureAttract,
3854  const scalarField& snapDist,
3855  const vectorField& nearestDisp,
3856  const vectorField& nearestNormal,
3857  motionSmoother& meshMover,
3858  vectorField& patchAttraction,
3859  List<pointConstraint>& patchConstraints,
3860 
3861  DynamicList<label>& splitFaces,
3862  DynamicList<labelPair>& splits
3863 
3864 ) const
3865 {
3866  if (dryRun_)
3867  {
3868  return nearestDisp;
3869  }
3870 
3871 
3872  const Switch implicitFeatureAttraction = snapParams.implicitFeatureSnap();
3873  const Switch explicitFeatureAttraction = snapParams.explicitFeatureSnap();
3874  const Switch multiRegionFeatureSnap = snapParams.multiRegionFeatureSnap();
3875 
3876  Info<< "Overriding displacement on features :" << nl
3877  << " implicit features : " << implicitFeatureAttraction << nl
3878  << " explicit features : " << explicitFeatureAttraction << nl
3879  << " multi-patch features : " << multiRegionFeatureSnap << nl
3880  << endl;
3881 
3882  const indirectPrimitivePatch& pp = meshMover.patch();
3883  const pointField& localPoints = pp.localPoints();
3884  const fvMesh& mesh = meshRefiner_.mesh();
3885 
3886 
3887  //const bitSet isMasterPoint(syncTools::getMasterPoints(mesh));
3888  const bitSet isPatchMasterPoint
3889  (
3891  (
3892  mesh,
3893  pp.meshPoints()
3894  )
3895  );
3896 
3897  // Per point, per surrounding face:
3898  // - faceSurfaceNormal
3899  // - faceDisp
3900  // - faceCentres
3901  List<List<point>> pointFaceSurfNormals;
3902  List<List<point>> pointFaceDisp;
3903  List<List<point>> pointFaceCentres;
3904  List<labelList> pointFacePatchID;
3905 
3906  {
3907  // Calculate attraction distance per face (from the attraction distance
3908  // per point)
3909  scalarField faceSnapDist(pp.size(), -GREAT);
3910  forAll(pp.localFaces(), facei)
3911  {
3912  const face& f = pp.localFaces()[facei];
3913  forAll(f, fp)
3914  {
3915  faceSnapDist[facei] = max(faceSnapDist[facei], snapDist[f[fp]]);
3916  }
3917  }
3918 
3919 
3920  // Displacement and orientation per pp face
3921  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3922 
3923  // vector from point on surface back to face centre
3924  vectorField faceDisp(pp.size(), Zero);
3925  // normal of surface at point on surface
3926  vectorField faceSurfaceNormal(pp.size(), Zero);
3927  labelList faceSurfaceGlobalRegion(pp.size(), -1);
3928  //vectorField faceRotation(pp.size(), Zero);
3929 
3930  calcNearestFace
3931  (
3932  iter,
3933  pp,
3934  faceSnapDist,
3935  faceDisp,
3936  faceSurfaceNormal,
3937  faceSurfaceGlobalRegion
3938  //faceRotation
3939  );
3940 
3941 
3942  // Collect (possibly remote) per point data of all surrounding faces
3943  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3944  // - faceSurfaceNormal
3945  // - faceDisp
3946  // - faceCentres
3947  calcNearestFacePointProperties
3948  (
3949  iter,
3950  pp,
3951 
3952  faceDisp,
3953  faceSurfaceNormal,
3954  faceSurfaceGlobalRegion,
3955 
3956  pointFaceSurfNormals,
3957  pointFaceDisp,
3958  pointFaceCentres,
3959  pointFacePatchID
3960  );
3961  }
3962 
3963 
3964  // Start off with nearest point on surface
3965  vectorField patchDisp = nearestDisp;
3966 
3967 
3968  // Main calculation
3969  // ~~~~~~~~~~~~~~~~
3970  // This is the main intelligence which calculates per point the vector to
3971  // attract it to the nearest surface. There are lots of possibilities
3972  // here.
3973 
3974  // Nearest feature
3975  patchAttraction.setSize(localPoints.size());
3976  patchAttraction = Zero;
3977  // Constraints at feature
3978  patchConstraints.setSize(localPoints.size());
3979  patchConstraints = pointConstraint();
3980 
3981  if (implicitFeatureAttraction)
3982  {
3983  // Sample faces around each point and see if nearest surface normal
3984  // differs. Reconstruct a feature edge/point if possible and snap to
3985  // it.
3986  featureAttractionUsingReconstruction
3987  (
3988  iter,
3989  featureCos,
3990 
3991  pp,
3992  snapDist,
3993  nearestDisp,
3994 
3995  pointFaceSurfNormals,
3996  pointFaceDisp,
3997  pointFaceCentres,
3998  pointFacePatchID,
3999 
4000  patchAttraction,
4001  patchConstraints
4002  );
4003  }
4004 
4005  if (explicitFeatureAttraction)
4006  {
4007  // Only do fancy stuff if alignMeshEdges
4008  bool releasePoints = false;
4009  bool stringFeatures = false;
4010  bool avoidDiagonal = false;
4011  if (alignMeshEdges)
4012  {
4013  releasePoints = snapParams.releasePoints();
4014  stringFeatures = snapParams.stringFeatures();
4015  avoidDiagonal = snapParams.avoidDiagonal();
4016  }
4017 
4018 
4019  // Sample faces around each point and see if nearest surface normal
4020  // differs. For those find the nearest real feature edge/point and
4021  // store the correspondence. Then loop over feature edge/point
4022  // and attract those nearest mesh point. (the first phase just is
4023  // a subsetting of candidate points, the second makes sure that only
4024  // one mesh point gets attracted per feature)
4025  featureAttractionUsingFeatureEdges
4026  (
4027  iter,
4028  multiRegionFeatureSnap,
4029 
4030  snapParams.detectBaffles(),
4031  snapParams.baffleFeaturePoints(), // all points on baffle edges
4032  // are attracted to feature pts
4033 
4034  releasePoints,
4035  stringFeatures,
4036  avoidDiagonal,
4037 
4038  featureCos,
4039 
4040  pp,
4041  snapDist,
4042  nearestDisp,
4043  nearestNormal,
4044 
4045  pointFaceSurfNormals,
4046  pointFaceDisp,
4047  pointFaceCentres,
4048  pointFacePatchID,
4049 
4050  patchAttraction,
4051  patchConstraints
4052  );
4053  }
4054 
4055  if (!alignMeshEdges)
4056  {
4057  const scalar concaveCos = Foam::cos
4058  (
4059  degToRad(snapParams.concaveAngle())
4060  );
4061  const scalar minAreaRatio = snapParams.minAreaRatio();
4062 
4063  Info<< "Experimental: introducing face splits to avoid rotating"
4064  << " mesh edges. Splitting faces when" << nl
4065  << indent << "- angle not concave by more than "
4066  << snapParams.concaveAngle() << " degrees" << nl
4067  << indent << "- resulting triangles of similar area "
4068  << " (ratio within " << minAreaRatio << ")" << nl
4069  << endl;
4070 
4071  splitDiagonals
4072  (
4073  featureCos,
4074  concaveCos,
4075  minAreaRatio,
4076  pp,
4077 
4078  nearestDisp,
4079  nearestNormal,
4080 
4081  patchAttraction,
4082  patchConstraints,
4083  splitFaces,
4084  splits
4085  );
4086 
4087  if (debug)
4088  {
4089  Info<< "Diagonal attraction feature correction : ";
4090  writeStats(pp, isPatchMasterPoint, patchConstraints);
4091  }
4092  }
4093 
4094 
4095  preventFaceSqueeze
4096  (
4097  iter,
4098  featureCos,
4099 
4100  pp,
4101  snapDist,
4102  nearestDisp,
4103 
4104  patchAttraction,
4105  patchConstraints
4106  );
4107 
4108  {
4109  vector avgPatchDisp = meshRefinement::gAverage
4110  (
4111  isPatchMasterPoint,
4112  patchDisp
4113  );
4114  vector avgPatchAttr = meshRefinement::gAverage
4115  (
4116  isPatchMasterPoint,
4117  patchAttraction
4118  );
4119 
4120  Info<< "Attraction:" << endl
4121  << " linear : max:" << gMaxMagSqr(patchDisp)
4122  << " avg:" << avgPatchDisp << endl
4123  << " feature : max:" << gMaxMagSqr(patchAttraction)
4124  << " avg:" << avgPatchAttr << endl;
4125  }
4126 
4127  // So now we have:
4128  // - patchDisp : point movement to go to nearest point on surface
4129  // (either direct or through interpolation of
4130  // face nearest)
4131  // - patchAttraction : direct attraction to features
4132  // - patchConstraints : type of features
4133 
4134  // Use any combination of patchDisp and direct feature attraction.
4135 
4136 
4137  // Mix with direct feature attraction
4138  forAll(patchConstraints, pointi)
4139  {
4140  if (patchConstraints[pointi].first() > 1)
4141  {
4142  patchDisp[pointi] =
4143  (1.0-featureAttract)*patchDisp[pointi]
4144  + featureAttract*patchAttraction[pointi];
4145  }
4146  }
4147 
4148 
4149 
4150  // Count
4151  {
4152  Info<< "Feature analysis : ";
4153  writeStats(pp, isPatchMasterPoint, patchConstraints);
4154  }
4155 
4156 
4157  // Now we have the displacement per patch point to move onto the surface
4158  // Split into tangential and normal direction.
4159  // - start off with all non-constrained points following the constrained
4160  // ones since point normals not relevant.
4161  // - finish with only tangential component smoothed.
4162  // Note: tangential is most
4163  // likely to come purely from face-centre snapping, not face rotation.
4164  // Note: could use the constraints here (constraintTransformation())
4165  // but this is not necessarily accurate and we're smoothing to
4166  // get out of problems.
4167 
4168  if (featureAttract < 1-0.001)
4169  {
4170  //const bitSet isMasterEdge(syncTools::getMasterEdges(mesh));
4171  const labelList meshEdges
4172  (
4173  pp.meshEdges(mesh.edges(), mesh.pointEdges())
4174  );
4175  const bitSet isPatchMasterEdge
4176  (
4178  (
4179  mesh,
4180  meshEdges
4181  )
4182  );
4183 
4184  const vectorField pointNormals
4185  (
4187  (
4188  mesh,
4189  pp
4190  )
4191  );
4192 
4193  // 1. Smoothed all displacement
4194  vectorField smoothedPatchDisp = patchDisp;
4195  smoothAndConstrain
4196  (
4197  isPatchMasterEdge,
4198  pp,
4199  meshEdges,
4200  patchConstraints,
4201  smoothedPatchDisp
4202  );
4203 
4204 
4205  // 2. Smoothed tangential component
4206  vectorField tangPatchDisp = patchDisp;
4207  tangPatchDisp -= (pointNormals & patchDisp) * pointNormals;
4208  smoothAndConstrain
4209  (
4210  isPatchMasterEdge,
4211  pp,
4212  meshEdges,
4213  patchConstraints,
4214  tangPatchDisp
4215  );
4216 
4217  // Re-add normal component
4218  tangPatchDisp += (pointNormals & patchDisp) * pointNormals;
4219 
4221  {
4222  dumpMove
4223  (
4224  mesh.time().path()
4225  / "tangPatchDispConstrained_" + name(iter) + ".obj",
4226  pp.localPoints(),
4227  pp.localPoints() + tangPatchDisp
4228  );
4229  }
4230 
4231  patchDisp =
4232  (1.0-featureAttract)*smoothedPatchDisp
4233  + featureAttract*tangPatchDisp;
4234  }
4235 
4236 
4237  const scalar relax = featureAttract;
4238  patchDisp *= relax;
4239 
4240 
4241  // Points on zones in one domain but only present as point on other
4242  // will not do condition 2 on all. Sync explicitly.
4244  (
4245  mesh,
4246  pp.meshPoints(),
4247  patchDisp,
4248  minMagSqrEqOp<point>(), // combine op
4249  vector(GREAT, GREAT, GREAT) // null value (note: cannot use VGREAT)
4250  );
4251 
4252  return patchDisp;
4253 }
4254 
4255 
4256 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
Foam::labelList
List< label > labelList
A List of labels.
Definition: List.H:71
Foam::pointField
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:44
Foam::polyMesh::points
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1038
Foam::scalarField
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Definition: primitiveFieldsFwd.H:52
p
volScalarField & p
Definition: createFieldRefs.H:8
Foam::PatchTools::pointNormals
static tmp< pointField > pointNormals(const polyMesh &, const PrimitivePatch< FaceList, PointField > &)
Return parallel consistent point normals for patches using mesh points.
Foam::meshRefinement::gAverage
static T gAverage(const bitSet &isMasterElem, const UList< T > &values)
Helper: calculate average.
Definition: meshRefinementTemplates.C:62
Foam::edgeList
List< edge > edgeList
A List of edges.
Definition: edgeList.H:63
snappySnapDriver.H
Foam::labelMax
constexpr label labelMax
Definition: label.H:61
Foam::VectorSpace< Vector< Cmpt >, Cmpt, 3 >::uniform
static Vector< Cmpt > uniform(const Cmpt &s)
Return a VectorSpace with all elements = s.
Definition: VectorSpaceI.H:164
Foam::returnReduce
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
Definition: PstreamReduceOps.H:94
s
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))
Definition: gmvOutputSpray.H:25
Foam::Zero
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
PatchTools.H
Foam::diag
void diag(pointPatchField< vector > &, const pointPatchField< tensor > &)
Definition: pointPatchFieldFunctions.H:287
Foam::primitiveMesh::nInternalFaces
label nInternalFaces() const
Number of internal faces.
Definition: primitiveMeshI.H:78
Foam::primitiveMesh::nFaces
label nFaces() const
Number of mesh faces.
Definition: primitiveMeshI.H:90
motionSmoother.H
polyTopoChange.H
indexedOctree.H
localPointRegion.H
Foam::meshRefinement::ATTRACTION
Definition: meshRefinement.H:97
unitConversion.H
Unit conversion functions.
Foam::boolList
List< bool > boolList
A List of bools.
Definition: List.H:69
Foam::surfaceZonesInfo::getNamedSurfaces
static labelList getNamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of named surfaces (surfaces with faceZoneName)
Definition: surfaceZonesInfo.C:263
Foam::polyMesh::boundaryMesh
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:435
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:350
Foam::primitiveMesh::edges
const edgeList & edges() const
Return mesh edges. Uses calcEdges.
Definition: primitiveMeshEdges.C:505
Foam::syncTools::getMasterFaces
static bitSet getMasterFaces(const polyMesh &mesh)
Definition: syncTools.C:126
refinementFeatures.H
Foam::primitiveMesh::pointEdges
const labelListList & pointEdges() const
Definition: primitiveMeshEdges.C:516
Foam::transform
dimensionSet transform(const dimensionSet &ds)
Return the argument; transformations do not change the dimensions.
Definition: dimensionSet.C:519
syncTools.H
Foam::syncTools::syncPointList
static void syncPointList(const polyMesh &mesh, List< T > &pointValues, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh points.
Definition: syncToolsTemplates.C:747
Foam::gMaxMagSqr
Type gMaxMagSqr(const UList< Type > &f, const label comm)
Definition: FieldFunctions.C:590
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
Foam::vectorField
Field< vector > vectorField
Specialisation of Field<T> for vector.
Definition: primitiveFieldsFwd.H:54
Foam::magSqr
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
Foam::polyMesh::faceZones
const faceZoneMesh & faceZones() const
Return face zone mesh.
Definition: polyMesh.H:477
Foam::nearInfo
Tuple2< scalar, label > nearInfo
Private class for finding nearest.
Definition: sampledMeshedSurface.C:76
Foam::meshRefinement::getMasterEdges
static bitSet getMasterEdges(const polyMesh &mesh, const labelList &meshEdges)
Determine master edge for subset of edges. If coupled.
Definition: meshRefinement.C:3154
Foam::labelPair
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:54
Foam::wordList
List< word > wordList
A List of words.
Definition: fileName.H:59
n
label n
Definition: TABSMDCalcMethod2.H:31
Foam::reduce
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
Definition: PstreamReduceOps.H:51
Foam::PointIndexHit
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
Definition: PointIndexHit.H:55
Foam::constant::physicoChemical::c1
const dimensionedScalar c1
First radiation constant: default SI units: [W/m2].
pFaces
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]=cellShape(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
Foam::Field< vector >
treeDataPoint.H
plane.H
Foam::Info
messageStream Info
Information stream (uses stdout - output is on the master only)
refinementSurfaces.H
Foam::name
word name(const complex &c)
Return string representation of complex.
Definition: complex.C:76
Foam::syncTools::syncEdgeList
static void syncEdgeList(const polyMesh &mesh, List< T > &edgeValues, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh edges.
Definition: syncToolsTemplates.C:826
patchID
label patchID
Definition: boundaryProcessorFaPatchPoints.H:5
Foam::max
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
Foam::edgeMeshTools::writeStats
void writeStats(Ostream &os, const extendedFeatureEdgeMesh &emesh)
Write some information.
Definition: edgeMeshTools.C:36
Foam::FatalError
error FatalError
mesh
dynamicFvMesh & mesh
Definition: createDynamicFvMesh.H:6
stdFoam::end
constexpr auto end(C &c) -> decltype(c.end())
Return iterator to the end of the container c.
Definition: stdFoam.H:121
Foam::localPointRegion::findDuplicateFaces
static labelList findDuplicateFaces(const primitiveMesh &, const labelList &)
Helper routine to find baffles (two boundary faces using the.
Definition: localPointRegion.C:544
fvMesh.H
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::ZoneMesh::findZoneID
label findZoneID(const word &zoneName) const
Find zone index given a name, return -1 if not found.
Definition: ZoneMesh.C:484
Foam::indent
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:320
Foam::vector
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:51
Foam::degToRad
constexpr scalar degToRad(const scalar deg) noexcept
Conversion from degrees to radians.
Definition: unitConversion.H:48
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Foam::surfaceZonesInfo::getUnnamedSurfaces
static labelList getUnnamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of unnamed surfaces (surfaces without faceZoneName)
Definition: surfaceZonesInfo.C:242
Foam::indirectPrimitivePatch
PrimitivePatch< IndirectList< face >, const pointField & > indirectPrimitivePatch
A PrimitivePatch with an IndirectList for the faces, const reference for the point field.
Definition: indirectPrimitivePatch.H:49
Foam::labelListList
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:56
points0
pointField points0(pointIOField(IOobject("points", mesh.time().constant(), polyMesh::meshSubDir, mesh, IOobject::MUST_READ, IOobject::NO_WRITE, false)))
Foam::linePointRef
line< point, const point & > linePointRef
Line using referred points.
Definition: linePointRef.H:47
Foam::polyMesh::faces
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1063
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:372
Foam::sqr
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Definition: dimensionedSymmTensor.C:51
Foam::pointIndexHit
PointIndexHit< point > pointIndexHit
Definition: pointIndexHit.H:45
Foam::constant::mathematical::pi
constexpr scalar pi(M_PI)
Foam::nl
constexpr char nl
Definition: Ostream.H:385
Foam::Pair< label >
Foam::Time::path
fileName path() const
Return path.
Definition: Time.H:358
f
labelList f(nPoints)
Foam::foamVersion::patch
const std::string patch
OpenFOAM patch number as a std::string.
Foam::List
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: HashTable.H:102
Foam::sqrt
dimensionedScalar sqrt(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:144
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
Foam::primitiveMesh::faceCentres
const vectorField & faceCentres() const
Definition: primitiveMeshFaceCentresAndAreas.C:144
points
const pointField & points
Definition: gmvOutputHeader.H:1
Foam::constant::electromagnetic::e
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
Foam::primitiveMesh::nPoints
label nPoints() const
Number of mesh points.
Definition: primitiveMeshI.H:37
Foam::identity
labelList identity(const label len, label start=0)
Create identity map of the given length with (map[i] == i)
Definition: labelList.C:38
Foam::List::clear
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:115
x
x
Definition: LISASMDCalcMethod2.H:52
snapParameters.H
Foam::List::set
std::enable_if< std::is_same< bool, TypeT >::value, bool >::type set(const label i, bool val=true)
A bitSet::set() method for a list of bool.
Definition: List.H:325
split
static bool split(const std::string &line, std::string &key, std::string &val)
Definition: cpuInfo.C:39
Foam::constant::universal::c
const dimensionedScalar c
Speed of light in a vacuum.
rndGen
Random rndGen
Definition: createFields.H:23
Foam::fvMesh::time
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:248
Foam::VectorSpace< Vector< scalar >, scalar, 3 >::zero
static const Vector< scalar > zero
Definition: VectorSpace.H:115
Foam::listPlusEqOp::operator()
void operator()(List< T > &x, const List< T > &y) const
Definition: snappySnapDriverFeature.C:56
Foam::sortedOrder
labelList sortedOrder(const UList< T > &input)
Return the (stable) sort order for the list.
p0
const volScalarField & p0
Definition: EEqn.H:36
featureEdgeMesh.H
Foam::VectorSpace::size
static constexpr direction size() noexcept
The number of elements in the VectorSpace = Ncmpts.
Definition: VectorSpace.H:176
Foam::Tuple2
A 2-tuple for storing two objects of dissimilar types. The container is similar in purpose to std::pa...
Definition: Tuple2.H:57
Foam::point
vector point
Point is a vector.
Definition: point.H:43
Foam::List::setSize
void setSize(const label newSize)
Alias for resize(const label)
Definition: ListI.H:146
WarningInFunction
#define WarningInFunction
Report a warning using Foam::Warning.
Definition: messageStream.H:298
pyramidPointFaceRef.H
Foam::listPlusEqOp
Definition: snappySnapDriverFeature.C:52
OBJstream.H
Foam::meshRefinement::getMasterPoints
static bitSet getMasterPoints(const polyMesh &mesh, const labelList &meshPoints)
Determine master point for subset of points. If coupled.
Definition: meshRefinement.C:3118
y
scalar y
Definition: LISASMDCalcMethod1.H:14
Foam::cos
dimensionedScalar cos(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:265
Foam::labelUIndList
UIndirectList< label > labelUIndList
UIndirectList of labels.
Definition: UIndirectList.H:58
relax
UEqn relax()
Foam::primitiveMesh::faceAreas
const vectorField & faceAreas() const
Definition: primitiveMeshFaceCentresAndAreas.C:155