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