autoSnapDriverFeature.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 | Copyright (C) 2011-2015 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "autoSnapDriver.H"
27 #include "polyTopoChange.H"
28 #include "syncTools.H"
29 #include "fvMesh.H"
30 #include "OBJstream.H"
31 #include "motionSmoother.H"
32 #include "refinementSurfaces.H"
33 #include "refinementFeatures.H"
34 #include "unitConversion.H"
35 #include "plane.H"
36 #include "featureEdgeMesh.H"
37 #include "treeDataPoint.H"
38 #include "indexedOctree.H"
39 #include "snapParameters.H"
40 #include "PatchTools.H"
41 
42 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
43 
44 namespace Foam
45 {
46  template<class T>
48  {
49  public:
50 
51  void operator()(List<T>& x, const List<T>& y) const
52  {
53  label sz = x.size();
54  x.setSize(sz+y.size());
55  forAll(y, i)
56  {
57  x[sz++] = y[i];
58  }
59  }
60  };
61 }
62 
63 
64 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
65 
66 bool Foam::autoSnapDriver::isFeaturePoint
67 (
68  const scalar featureCos,
69  const indirectPrimitivePatch& pp,
70  const PackedBoolList& isFeatureEdge,
71  const label pointI
72 ) const
73 {
74  const pointField& points = pp.localPoints();
75  const edgeList& edges = pp.edges();
76  const labelList& pEdges = pp.pointEdges()[pointI];
77 
78  label nFeatEdges = 0;
79 
80  forAll(pEdges, i)
81  {
82  if (isFeatureEdge[pEdges[i]])
83  {
84  nFeatEdges++;
85 
86  for (label j = i+1; j < pEdges.size(); j++)
87  {
88  if (isFeatureEdge[pEdges[j]])
89  {
90  const edge& eI = edges[pEdges[i]];
91  const edge& eJ = edges[pEdges[j]];
92 
93  const point& p = points[pointI];
94  const point& pI = points[eI.otherVertex(pointI)];
95  const point& pJ = points[eJ.otherVertex(pointI)];
96 
97  vector vI = p-pI;
98  scalar vIMag = mag(vI);
99 
100  vector vJ = pJ-p;
101  scalar vJMag = mag(vJ);
102 
103  if
104  (
105  vIMag > SMALL
106  && vJMag > SMALL
107  && ((vI/vIMag & vJ/vJMag) < featureCos)
108  )
109  {
110  return true;
111  }
112  }
113  }
114  }
115  }
116 
117  if (nFeatEdges == 1)
118  {
119  // End of feature-edge string
120  return true;
121  }
122 
123  return false;
124 }
125 
126 
127 void Foam::autoSnapDriver::smoothAndConstrain
128 (
129  const PackedBoolList& isPatchMasterEdge,
130  const indirectPrimitivePatch& pp,
131  const labelList& meshEdges,
132  const List<pointConstraint>& constraints,
133  vectorField& disp
134 ) const
135 {
136  const fvMesh& mesh = meshRefiner_.mesh();
137 
138  for (label avgIter = 0; avgIter < 20; avgIter++)
139  {
140  // Calculate average displacement of neighbours
141  // - unconstrained (i.e. surface) points use average of all
142  // neighbouring points
143  // - from testing it has been observed that it is not beneficial
144  // to have edge constrained points use average of all edge or point
145  // constrained neighbours since they're already attracted to
146  // the nearest point on the feature.
147  // Having them attract to point-constrained neighbours does not
148  // make sense either since there is usually just one of them so
149  // it severely distorts it.
150  // - same for feature points. They are already attracted to the
151  // nearest feature point.
152 
153  vectorField dispSum(pp.nPoints(), vector::zero);
154  labelList dispCount(pp.nPoints(), 0);
155 
156  const labelListList& pointEdges = pp.pointEdges();
157  const edgeList& edges = pp.edges();
158 
159  forAll(pointEdges, pointI)
160  {
161  const labelList& pEdges = pointEdges[pointI];
162 
163  label nConstraints = constraints[pointI].first();
164 
165  if (nConstraints <= 1)
166  {
167  forAll(pEdges, i)
168  {
169  label edgeI = pEdges[i];
170 
171  if (isPatchMasterEdge[edgeI])
172  {
173  label nbrPointI = edges[edgeI].otherVertex(pointI);
174  if (constraints[nbrPointI].first() >= nConstraints)
175  {
176  dispSum[pointI] += disp[nbrPointI];
177  dispCount[pointI]++;
178  }
179  }
180  }
181  }
182  }
183 
185  (
186  mesh,
187  pp.meshPoints(),
188  dispSum,
189  plusEqOp<point>(),
190  vector::zero,
192  );
194  (
195  mesh,
196  pp.meshPoints(),
197  dispCount,
198  plusEqOp<label>(),
199  label(0),
201  );
202 
203  // Constraints
204  forAll(constraints, pointI)
205  {
206  if (dispCount[pointI] > 0)
207  {
208  // Mix my displacement with neighbours' displacement
209  disp[pointI] =
210  0.5
211  *(disp[pointI] + dispSum[pointI]/dispCount[pointI]);
212  }
213  }
214  }
215 }
216 
217 
218 void Foam::autoSnapDriver::calcNearestFace
219 (
220  const label iter,
221  const indirectPrimitivePatch& pp,
222  const scalarField& faceSnapDist,
223  vectorField& faceDisp,
224  vectorField& faceSurfaceNormal,
225  labelList& faceSurfaceGlobalRegion,
226  vectorField& faceRotation
227 ) const
228 {
229  const fvMesh& mesh = meshRefiner_.mesh();
230  const refinementSurfaces& surfaces = meshRefiner_.surfaces();
231 
232  // Displacement and orientation per pp face.
233  faceDisp.setSize(pp.size());
234  faceDisp = vector::zero;
235  faceSurfaceNormal.setSize(pp.size());
236  faceSurfaceNormal = vector::zero;
237  faceSurfaceGlobalRegion.setSize(pp.size());
238  faceSurfaceGlobalRegion = -1;
239 
240  // Divide surfaces into zoned and unzoned
241  const labelList zonedSurfaces =
243  const labelList unzonedSurfaces =
245 
246  // Per pp face the current surface snapped to
247  labelList snapSurf(pp.size(), -1);
248 
249 
250  // Do zoned surfaces
251  // ~~~~~~~~~~~~~~~~~
252  // Zoned faces only attract to corresponding surface
253 
254  // Extract faces per zone
255  const PtrList<surfaceZonesInfo>& surfZones = surfaces.surfZones();
256 
257  forAll(zonedSurfaces, i)
258  {
259  label zoneSurfI = zonedSurfaces[i];
260 
261  const word& faceZoneName = surfZones[zoneSurfI].faceZoneName();
262 
263  // Get indices of faces on pp that are also in zone
264  label zoneI = mesh.faceZones().findZoneID(faceZoneName);
265  if (zoneI == -1)
266  {
268  (
269  "autoSnapDriver::calcNearestFace(..)"
270  ) << "Problem. Cannot find zone " << faceZoneName
271  << exit(FatalError);
272  }
273  const faceZone& fZone = mesh.faceZones()[zoneI];
274  PackedBoolList isZonedFace(mesh.nFaces());
275  forAll(fZone, i)
276  {
277  isZonedFace[fZone[i]] = 1;
278  }
279 
280  DynamicList<label> ppFaces(fZone.size());
281  DynamicList<label> meshFaces(fZone.size());
282  forAll(pp.addressing(), i)
283  {
284  if (isZonedFace[pp.addressing()[i]])
285  {
286  snapSurf[i] = zoneSurfI;
287  ppFaces.append(i);
288  meshFaces.append(pp.addressing()[i]);
289  }
290  }
291 
292  //Pout<< "For faceZone " << fZone.name()
293  // << " found " << ppFaces.size() << " out of " << pp.size()
294  // << endl;
295 
296  pointField fc
297  (
299  (
300  IndirectList<face>(mesh.faces(), meshFaces),
301  mesh.points()
302  ).faceCentres()
303  );
304 
305  List<pointIndexHit> hitInfo;
306  labelList hitSurface;
307  labelList hitRegion;
308  vectorField hitNormal;
309  surfaces.findNearestRegion
310  (
311  labelList(1, zoneSurfI),
312  fc,
313  sqr(faceSnapDist),// sqr of attract dist
314  hitSurface,
315  hitInfo,
316  hitRegion,
317  hitNormal
318  );
319 
320  forAll(hitInfo, hitI)
321  {
322  if (hitInfo[hitI].hit())
323  {
324  label faceI = ppFaces[hitI];
325  faceDisp[faceI] = hitInfo[hitI].hitPoint() - fc[hitI];
326  faceSurfaceNormal[faceI] = hitNormal[hitI];
327  faceSurfaceGlobalRegion[faceI] = surfaces.globalRegion
328  (
329  hitSurface[hitI],
330  hitRegion[hitI]
331  );
332  }
333  else
334  {
335  WarningIn
336  (
337  "autoSnapDriver::calcNearestFace(..)"
338  ) << "Did not find surface near face centre " << fc[hitI]
339  << endl;
340  }
341  }
342  }
343 
344 
345  // Do unzoned surfaces
346  // ~~~~~~~~~~~~~~~~~~~
347  // Unzoned faces attract to any unzoned surface
348 
349  DynamicList<label> ppFaces(pp.size());
350  DynamicList<label> meshFaces(pp.size());
351  forAll(pp.addressing(), i)
352  {
353  if (snapSurf[i] == -1)
354  {
355  ppFaces.append(i);
356  meshFaces.append(pp.addressing()[i]);
357  }
358  }
359  //Pout<< "Found " << ppFaces.size() << " unzoned faces out of "
360  // << pp.size() << endl;
361 
362  pointField fc
363  (
365  (
366  IndirectList<face>(mesh.faces(), meshFaces),
367  mesh.points()
368  ).faceCentres()
369  );
370 
371  List<pointIndexHit> hitInfo;
372  labelList hitSurface;
373  labelList hitRegion;
374  vectorField hitNormal;
375  surfaces.findNearestRegion
376  (
377  unzonedSurfaces,
378  fc,
379  sqr(faceSnapDist),// sqr of attract dist
380  hitSurface,
381  hitInfo,
382  hitRegion,
383  hitNormal
384  );
385 
386  forAll(hitInfo, hitI)
387  {
388  if (hitInfo[hitI].hit())
389  {
390  label faceI = ppFaces[hitI];
391  faceDisp[faceI] = hitInfo[hitI].hitPoint() - fc[hitI];
392  faceSurfaceNormal[faceI] = hitNormal[hitI];
393  faceSurfaceGlobalRegion[faceI] = surfaces.globalRegion
394  (
395  hitSurface[hitI],
396  hitRegion[hitI]
397  );
398  }
399  else
400  {
401  WarningIn
402  (
403  "autoSnapDriver::calcNearestFace(..)"
404  ) << "Did not find surface near face centre " << fc[hitI]
405  << endl;
406  }
407  }
408 
409 
410  // Determine rotation
411  // ~~~~~~~~~~~~~~~~~~
412 
413  // Determine rotation axis
414  faceRotation.setSize(pp.size());
415  faceRotation = vector::zero;
416 
417  forAll(faceRotation, faceI)
418  {
419  // Note: extend to >180 degrees checking
420  faceRotation[faceI] =
421  pp.faceNormals()[faceI]
422  ^ faceSurfaceNormal[faceI];
423  }
424 
425  if (debug&meshRefinement::ATTRACTION)
426  {
427  dumpMove
428  (
429  mesh.time().path()
430  / "faceDisp_" + name(iter) + ".obj",
431  pp.faceCentres(),
432  pp.faceCentres() + faceDisp
433  );
434  dumpMove
435  (
436  mesh.time().path()
437  / "faceRotation_" + name(iter) + ".obj",
438  pp.faceCentres(),
439  pp.faceCentres() + faceRotation
440  );
441  }
442 }
443 
444 
445 // Collect (possibly remote) per point data of all surrounding faces
446 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
447 // - faceSurfaceNormal
448 // - faceDisp
449 // - faceCentres&faceNormal
450 void Foam::autoSnapDriver::calcNearestFacePointProperties
451 (
452  const label iter,
453  const indirectPrimitivePatch& pp,
454 
455  const vectorField& faceDisp,
456  const vectorField& faceSurfaceNormal,
457  const labelList& faceSurfaceGlobalRegion,
458 
459  List<List<point> >& pointFaceSurfNormals,
460  List<List<point> >& pointFaceDisp,
461  List<List<point> >& pointFaceCentres,
462  List<labelList>& pointFacePatchID
463 ) const
464 {
465  const fvMesh& mesh = meshRefiner_.mesh();
466 
467  const PackedBoolList isMasterFace(syncTools::getMasterFaces(mesh));
468 
469 
470  // For now just get all surrounding face data. Expensive - should just
471  // store and sync data on coupled points only
472  // (see e.g PatchToolsNormals.C)
473 
474  pointFaceSurfNormals.setSize(pp.nPoints());
475  pointFaceDisp.setSize(pp.nPoints());
476  pointFaceCentres.setSize(pp.nPoints());
477  pointFacePatchID.setSize(pp.nPoints());
478 
479  // Fill local data
480  forAll(pp.pointFaces(), pointI)
481  {
482  const labelList& pFaces = pp.pointFaces()[pointI];
483 
484  // Count valid face normals
485  label nFaces = 0;
486  forAll(pFaces, i)
487  {
488  label faceI = pFaces[i];
489  if (isMasterFace[faceI] && faceSurfaceGlobalRegion[faceI] != -1)
490  {
491  nFaces++;
492  }
493  }
494 
495 
496  List<point>& pNormals = pointFaceSurfNormals[pointI];
497  pNormals.setSize(nFaces);
498  List<point>& pDisp = pointFaceDisp[pointI];
499  pDisp.setSize(nFaces);
500  List<point>& pFc = pointFaceCentres[pointI];
501  pFc.setSize(nFaces);
502  labelList& pFid = pointFacePatchID[pointI];
503  pFid.setSize(nFaces);
504 
505  nFaces = 0;
506  forAll(pFaces, i)
507  {
508  label faceI = pFaces[i];
509  label globalRegionI = faceSurfaceGlobalRegion[faceI];
510 
511  if (isMasterFace[faceI] && globalRegionI != -1)
512  {
513  pNormals[nFaces] = faceSurfaceNormal[faceI];
514  pDisp[nFaces] = faceDisp[faceI];
515  pFc[nFaces] = pp.faceCentres()[faceI];
516  pFid[nFaces] = globalToMasterPatch_[globalRegionI];
517  nFaces++;
518  }
519  }
520  }
521 
522 
523  // Collect additionally 'normal' boundary faces for boundaryPoints of pp
524  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
525  // points on the boundary of pp should pick up non-pp normals
526  // as well for the feature-reconstruction to behave correctly.
527  // (the movement is already constrained outside correctly so it
528  // is only that the unconstrained attraction vector is calculated
529  // correctly)
530  {
531  const polyBoundaryMesh& pbm = mesh.boundaryMesh();
532  labelList patchID(pbm.patchID());
533 
534  // Unmark all non-coupled boundary faces
535  forAll(pbm, patchI)
536  {
537  const polyPatch& pp = pbm[patchI];
538 
539  if (pp.coupled() || isA<emptyPolyPatch>(pp))
540  {
541  forAll(pp, i)
542  {
543  label meshFaceI = pp.start()+i;
544  patchID[meshFaceI-mesh.nInternalFaces()] = -1;
545  }
546  }
547  }
548 
549  // Remove any meshed faces
550  forAll(pp.addressing(), i)
551  {
552  label meshFaceI = pp.addressing()[i];
553  patchID[meshFaceI-mesh.nInternalFaces()] = -1;
554  }
555 
556  // See if pp point uses any non-meshed boundary faces
557 
558  const labelList& boundaryPoints = pp.boundaryPoints();
559  forAll(boundaryPoints, i)
560  {
561  label pointI = boundaryPoints[i];
562  label meshPointI = pp.meshPoints()[pointI];
563  const point& pt = mesh.points()[meshPointI];
564  const labelList& pFaces = mesh.pointFaces()[meshPointI];
565 
566  List<point>& pNormals = pointFaceSurfNormals[pointI];
567  List<point>& pDisp = pointFaceDisp[pointI];
568  List<point>& pFc = pointFaceCentres[pointI];
569  labelList& pFid = pointFacePatchID[pointI];
570 
571  forAll(pFaces, i)
572  {
573  label meshFaceI = pFaces[i];
574  if (!mesh.isInternalFace(meshFaceI))
575  {
576  label patchI = patchID[meshFaceI-mesh.nInternalFaces()];
577 
578  if (patchI != -1)
579  {
580  vector fn = mesh.faceAreas()[meshFaceI];
581  pNormals.append(fn/mag(fn));
582  pDisp.append(mesh.faceCentres()[meshFaceI]-pt);
583  pFc.append(mesh.faceCentres()[meshFaceI]);
584  pFid.append(patchI);
585  }
586  }
587  }
588  }
589  }
590 
592  (
593  mesh,
594  pp.meshPoints(),
595  pointFaceSurfNormals,
597  List<point>(),
599  );
601  (
602  mesh,
603  pp.meshPoints(),
604  pointFaceDisp,
606  List<point>(),
608  );
610  (
611  mesh,
612  pp.meshPoints(),
613  pointFaceCentres,
615  List<point>(),
617  );
619  (
620  mesh,
621  pp.meshPoints(),
622  pointFacePatchID,
624  List<label>()
625  );
626 
627 
628  // Sort the data according to the face centres. This is only so we get
629  // consistent behaviour serial and parallel.
630  labelList visitOrder;
631  forAll(pointFaceDisp, pointI)
632  {
633  List<point>& pNormals = pointFaceSurfNormals[pointI];
634  List<point>& pDisp = pointFaceDisp[pointI];
635  List<point>& pFc = pointFaceCentres[pointI];
636  labelList& pFid = pointFacePatchID[pointI];
637 
638  sortedOrder(mag(pFc)(), visitOrder);
639 
640  pNormals = List<point>(pNormals, visitOrder);
641  pDisp = List<point>(pDisp, visitOrder);
642  pFc = List<point>(pFc, visitOrder);
643  pFid = UIndirectList<label>(pFid, visitOrder)();
644  }
645 }
646 
647 
648 // Gets passed in offset to nearest point on feature edge. Calculates
649 // if the point has a different number of faces on either side of the feature
650 // and if so attracts the point to that non-dominant plane.
651 void Foam::autoSnapDriver::correctAttraction
652 (
653  const DynamicList<point>& surfacePoints,
654  const DynamicList<label>& surfaceCounts,
655  const point& edgePt,
656  const vector& edgeNormal, // normalised normal
657  const point& pt,
658 
659  vector& edgeOffset // offset from pt to point on edge
660 ) const
661 {
662  // Tangential component along edge
663  scalar tang = ((pt-edgePt)&edgeNormal);
664 
665  labelList order;
666  Foam::sortedOrder(surfaceCounts, order);
667 
668  if (order[0] < order[1])
669  {
670  // There is a non-dominant plane. Use the point on the plane to
671  // attract to.
672  vector attractD = surfacePoints[order[0]]-edgePt;
673  // Tangential component along edge
674  scalar tang2 = (attractD&edgeNormal);
675  // Normal component
676  attractD -= tang2*edgeNormal;
677  // Calculate fraction of normal distances
678  scalar magAttractD = mag(attractD);
679  scalar fraction = magAttractD/(magAttractD+mag(edgeOffset));
680 
681  point linePt =
682  edgePt
683  + ((1.0-fraction)*tang2 + fraction*tang)*edgeNormal;
684  edgeOffset = linePt-pt;
685  }
686 }
687 
688 
689 Foam::pointIndexHit Foam::autoSnapDriver::findMultiPatchPoint
690 (
691  const point& pt,
692  const labelList& patchIDs,
693  const List<point>& faceCentres
694 ) const
695 {
696  // Determine if multiple patchIDs
697  if (patchIDs.size())
698  {
699  label patch0 = patchIDs[0];
700 
701  for (label i = 1; i < patchIDs.size(); i++)
702  {
703  if (patchIDs[i] != patch0)
704  {
705  return pointIndexHit(true, pt, labelMax);
706  }
707  }
708  }
709  return pointIndexHit(false, vector::zero, labelMax);
710 }
711 
712 
713 Foam::label Foam::autoSnapDriver::findNormal
714 (
715  const scalar featureCos,
716  const vector& n,
717  const DynamicList<vector>& surfaceNormals
718 ) const
719 {
720  label index = -1;
721 
722  forAll(surfaceNormals, j)
723  {
724  scalar cosAngle = (n&surfaceNormals[j]);
725 
726  if
727  (
728  (cosAngle >= featureCos)
729  || (cosAngle < (-1+0.001)) // triangle baffles
730  )
731  {
732  index = j;
733  break;
734  }
735  }
736  return index;
737 }
738 
739 
740 // Detect multiple patches. Returns pointIndexHit:
741 // - false, index=-1 : single patch
742 // - true , index=0 : multiple patches but on different normals planes
743 // (so geometric feature edge is also a region edge)
744 // - true , index=1 : multiple patches on same normals plane i.e. flat region
745 // edge
746 Foam::pointIndexHit Foam::autoSnapDriver::findMultiPatchPoint
747 (
748  const point& pt,
749  const labelList& patchIDs,
750  const DynamicList<vector>& surfaceNormals,
751  const labelList& faceToNormalBin
752 ) const
753 {
754  if (patchIDs.empty())
755  {
756  return pointIndexHit(false, pt, -1);
757  }
758 
759  // Detect single patch situation (to avoid allocation)
760  label patch0 = patchIDs[0];
761 
762  for (label i = 1; i < patchIDs.size(); i++)
763  {
764  if (patchIDs[i] != patch0)
765  {
766  patch0 = -1;
767  break;
768  }
769  }
770 
771  if (patch0 >= 0)
772  {
773  // Single patch
774  return pointIndexHit(false, pt, -1);
775  }
776  else
777  {
778  if (surfaceNormals.size() == 1)
779  {
780  // Same normals plane, flat region edge.
781  return pointIndexHit(true, pt, 1);
782  }
783  else
784  {
785  // Detect per normals bin
786  labelList normalToPatch(surfaceNormals.size(), -1);
787  forAll(faceToNormalBin, i)
788  {
789  if (faceToNormalBin[i] != -1)
790  {
791  label& patch = normalToPatch[faceToNormalBin[i]];
792  if (patch == -1)
793  {
794  // First occurence
795  patch = patchIDs[i];
796  }
797  else if (patch == -2)
798  {
799  // Already marked as being on multiple patches
800  }
801  else if (patch != patchIDs[i])
802  {
803  // Mark as being on multiple patches
804  patch = -2;
805  }
806  }
807  }
808 
809  forAll(normalToPatch, normalI)
810  {
811  if (normalToPatch[normalI] == -2)
812  {
813  // Multiple patches on same normals plane, flat region
814  // edge
815  return pointIndexHit(true, pt, 1);
816  }
817  }
818 
819  // All patches on either side of geometric feature anyway
820  return pointIndexHit(true, pt, 0);
821  }
822  }
823 }
824 
825 
826 void Foam::autoSnapDriver::featureAttractionUsingReconstruction
827 (
828  const label iter,
829  const scalar featureCos,
830 
831  const indirectPrimitivePatch& pp,
832  const scalarField& snapDist,
833  const vectorField& nearestDisp,
834  const label pointI,
835 
836  const List<List<point> >& pointFaceSurfNormals,
837  const List<List<point> >& pointFaceDisp,
838  const List<List<point> >& pointFaceCentres,
839  const labelListList& pointFacePatchID,
840 
841  DynamicList<point>& surfacePoints,
842  DynamicList<vector>& surfaceNormals,
843  labelList& faceToNormalBin,
844 
845  vector& patchAttraction,
846  pointConstraint& patchConstraint
847 ) const
848 {
849  patchAttraction = vector::zero;
850  patchConstraint = pointConstraint();
851 
852  const List<point>& pfSurfNormals = pointFaceSurfNormals[pointI];
853  const List<point>& pfDisp = pointFaceDisp[pointI];
854  const List<point>& pfCentres = pointFaceCentres[pointI];
855 
856  // Bin according to surface normal
857  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
858 
859  //- Bins of differing normals:
860  // - one normal : flat(tish) surface
861  // - two normals : geometric feature edge
862  // - three normals: geometric feature point
863  // - four normals : too complex a feature
864  surfacePoints.clear();
865  surfaceNormals.clear();
866 
867  //- From face to above normals bin
868  faceToNormalBin.setSize(pfDisp.size());
869  faceToNormalBin = -1;
870 
871  forAll(pfSurfNormals, i)
872  {
873  const point& fc = pfCentres[i];
874  const vector& fSNormal = pfSurfNormals[i];
875  const vector& fDisp = pfDisp[i];
876 
877  // What to do with very far attraction? For now just ignore the face
878  if (magSqr(fDisp) < sqr(snapDist[pointI]) && mag(fSNormal) > VSMALL)
879  {
880  const point pt = fc + fDisp;
881 
882  // Do we already have surface normal?
883  faceToNormalBin[i] = findNormal
884  (
885  featureCos,
886  fSNormal,
887  surfaceNormals
888  );
889 
890  if (faceToNormalBin[i] != -1)
891  {
892  // Same normal
893  }
894  else
895  {
896  // Now check if the planes go through the same edge or point
897 
898  if (surfacePoints.size() <= 1)
899  {
900  surfacePoints.append(pt);
901  faceToNormalBin[i] = surfaceNormals.size();
902  surfaceNormals.append(fSNormal);
903  }
904  else if (surfacePoints.size() == 2)
905  {
906  plane pl0(surfacePoints[0], surfaceNormals[0]);
907  plane pl1(surfacePoints[1], surfaceNormals[1]);
908  plane::ray r(pl0.planeIntersect(pl1));
909  vector featureNormal = r.dir() / mag(r.dir());
910 
911  if (mag(fSNormal&featureNormal) >= 0.001)
912  {
913  // Definitely makes a feature point
914  surfacePoints.append(pt);
915  faceToNormalBin[i] = surfaceNormals.size();
916  surfaceNormals.append(fSNormal);
917  }
918  }
919  else if (surfacePoints.size() == 3)
920  {
921  // Have already feature point. See if this new plane is
922  // the same point or not.
923  plane pl0(surfacePoints[0], surfaceNormals[0]);
924  plane pl1(surfacePoints[1], surfaceNormals[1]);
925  plane pl2(surfacePoints[2], surfaceNormals[2]);
926  point p012(pl0.planePlaneIntersect(pl1, pl2));
927 
928  plane::ray r(pl0.planeIntersect(pl1));
929  vector featureNormal = r.dir() / mag(r.dir());
930  if (mag(fSNormal&featureNormal) >= 0.001)
931  {
932  plane pl3(pt, fSNormal);
933  point p013(pl0.planePlaneIntersect(pl1, pl3));
934 
935  if (mag(p012-p013) > snapDist[pointI])
936  {
937  // Different feature point
938  surfacePoints.append(pt);
939  faceToNormalBin[i] = surfaceNormals.size();
940  surfaceNormals.append(fSNormal);
941  }
942  }
943  }
944  }
945  }
946  }
947 
948 
949  const point& pt = pp.localPoints()[pointI];
950 
951  // Check the number of directions
952  if (surfaceNormals.size() == 1)
953  {
954  // Normal distance to plane
955  vector d =
956  ((surfacePoints[0]-pt) & surfaceNormals[0])
957  *surfaceNormals[0];
958 
959  // Trim to snap distance
960  if (magSqr(d) > sqr(snapDist[pointI]))
961  {
962  d *= Foam::sqrt(sqr(snapDist[pointI])/magSqr(d));
963  }
964 
965  patchAttraction = d;
966 
967  // Store constraints
968  patchConstraint.applyConstraint(surfaceNormals[0]);
969  }
970  else if (surfaceNormals.size() == 2)
971  {
972  plane pl0(surfacePoints[0], surfaceNormals[0]);
973  plane pl1(surfacePoints[1], surfaceNormals[1]);
974  plane::ray r(pl0.planeIntersect(pl1));
975  vector n = r.dir() / mag(r.dir());
976 
977  // Get nearest point on infinite ray
978  vector d = r.refPoint()-pt;
979  d -= (d&n)*n;
980 
981  // Trim to snap distance
982  if (magSqr(d) > sqr(snapDist[pointI]))
983  {
984  d *= Foam::sqrt(sqr(snapDist[pointI])/magSqr(d));
985  }
986 
987  patchAttraction = d;
988 
989  // Store constraints
990  patchConstraint.applyConstraint(surfaceNormals[0]);
991  patchConstraint.applyConstraint(surfaceNormals[1]);
992  }
993  else if (surfaceNormals.size() == 3)
994  {
995  // Calculate point from the faces.
996  plane pl0(surfacePoints[0], surfaceNormals[0]);
997  plane pl1(surfacePoints[1], surfaceNormals[1]);
998  plane pl2(surfacePoints[2], surfaceNormals[2]);
999  point cornerPt(pl0.planePlaneIntersect(pl1, pl2));
1000  vector d = cornerPt - pt;
1001 
1002  // Trim to snap distance
1003  if (magSqr(d) > sqr(snapDist[pointI]))
1004  {
1005  d *= Foam::sqrt(sqr(snapDist[pointI])/magSqr(d));
1006  }
1007 
1008  patchAttraction = d;
1009 
1010  // Store constraints
1011  patchConstraint.applyConstraint(surfaceNormals[0]);
1012  patchConstraint.applyConstraint(surfaceNormals[1]);
1013  patchConstraint.applyConstraint(surfaceNormals[2]);
1014  }
1015 }
1016 
1017 
1018 // Special version that calculates attraction in one go
1019 void Foam::autoSnapDriver::featureAttractionUsingReconstruction
1020 (
1021  const label iter,
1022  const bool avoidSnapProblems,
1023  const scalar featureCos,
1024 
1025  const indirectPrimitivePatch& pp,
1026  const scalarField& snapDist,
1027  const vectorField& nearestDisp,
1028 
1029  const List<List<point> >& pointFaceSurfNormals,
1030  const List<List<point> >& pointFaceDisp,
1031  const List<List<point> >& pointFaceCentres,
1032  const labelListList& pointFacePatchID,
1033 
1034  vectorField& patchAttraction,
1035  List<pointConstraint>& patchConstraints
1036 ) const
1037 {
1038  autoPtr<OBJstream> feStr;
1039  autoPtr<OBJstream> fpStr;
1040  if (debug&meshRefinement::ATTRACTION)
1041  {
1042  feStr.reset
1043  (
1044  new OBJstream
1045  (
1046  meshRefiner_.mesh().time().path()
1047  / "implicitFeatureEdge_" + name(iter) + ".obj"
1048  )
1049  );
1050  Info<< "Dumping implicit feature-edge direction to "
1051  << feStr().name() << endl;
1052 
1053  fpStr.reset
1054  (
1055  new OBJstream
1056  (
1057  meshRefiner_.mesh().time().path()
1058  / "implicitFeaturePoint_" + name(iter) + ".obj"
1059  )
1060  );
1061  Info<< "Dumping implicit feature-point direction to "
1062  << fpStr().name() << endl;
1063  }
1064 
1065 
1066  DynamicList<point> surfacePoints(4);
1067  DynamicList<vector> surfaceNormals(4);
1068  labelList faceToNormalBin;
1069 
1070  forAll(pp.localPoints(), pointI)
1071  {
1072  vector attraction = vector::zero;
1073  pointConstraint constraint;
1074 
1075  featureAttractionUsingReconstruction
1076  (
1077  iter,
1078  featureCos,
1079 
1080  pp,
1081  snapDist,
1082  nearestDisp,
1083 
1084  pointI,
1085 
1086  pointFaceSurfNormals,
1087  pointFaceDisp,
1088  pointFaceCentres,
1089  pointFacePatchID,
1090 
1091  surfacePoints,
1092  surfaceNormals,
1093  faceToNormalBin,
1094 
1095  attraction,
1096  constraint
1097  );
1098 
1099  if
1100  (
1101  (constraint.first() > patchConstraints[pointI].first())
1102  || (
1103  (constraint.first() == patchConstraints[pointI].first())
1104  && (magSqr(attraction) < magSqr(patchAttraction[pointI]))
1105  )
1106  )
1107  {
1108  patchAttraction[pointI] = attraction;
1109  patchConstraints[pointI] = constraint;
1110 
1111  const point& pt = pp.localPoints()[pointI];
1112 
1113  if (patchConstraints[pointI].first() == 2 && feStr.valid())
1114  {
1115  feStr().write(linePointRef(pt, pt+patchAttraction[pointI]));
1116  }
1117  else if (patchConstraints[pointI].first() == 3 && fpStr.valid())
1118  {
1119  fpStr().write(linePointRef(pt, pt+patchAttraction[pointI]));
1120  }
1121  }
1122  }
1123 }
1124 
1125 
1126 void Foam::autoSnapDriver::stringFeatureEdges
1127 (
1128  const label iter,
1129  const scalar featureCos,
1130 
1131  const indirectPrimitivePatch& pp,
1132  const scalarField& snapDist,
1133 
1134  const vectorField& rawPatchAttraction,
1135  const List<pointConstraint>& rawPatchConstraints,
1136 
1137  vectorField& patchAttraction,
1138  List<pointConstraint>& patchConstraints
1139 ) const
1140 {
1141  // Snap edges to feature edges
1142  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
1143  // Walk existing edges and snap remaining ones (that are marked as
1144  // feature edges in rawPatchConstraints)
1145 
1146  // What this does is fill in any faces where not all points
1147  // on the face are being attracted:
1148  /*
1149  +
1150  / \
1151  / \
1152  ---+ +---
1153  \ /
1154  \ /
1155  +
1156  */
1157  // so the top and bottom will never get attracted since the nearest
1158  // back from the feature edge will always be one of the left or right
1159  // points since the face is diamond like. So here we walk the feature edges
1160  // and add any non-attracted points.
1161 
1162 
1163  while (true)
1164  {
1165  label nChanged = 0;
1166 
1167  const labelListList& pointEdges = pp.pointEdges();
1168  forAll(pointEdges, pointI)
1169  {
1170  if (patchConstraints[pointI].first() == 2)
1171  {
1172  const point& pt = pp.localPoints()[pointI];
1173  const labelList& pEdges = pointEdges[pointI];
1174  const vector& featVec = patchConstraints[pointI].second();
1175 
1176  // Detect whether there are edges in both directions.
1177  // (direction along the feature edge that is)
1178  bool hasPos = false;
1179  bool hasNeg = false;
1180 
1181  forAll(pEdges, pEdgeI)
1182  {
1183  const edge& e = pp.edges()[pEdges[pEdgeI]];
1184  label nbrPointI = e.otherVertex(pointI);
1185 
1186  if (patchConstraints[nbrPointI].first() > 1)
1187  {
1188  const point& nbrPt = pp.localPoints()[nbrPointI];
1189  const point featPt =
1190  nbrPt + patchAttraction[nbrPointI];
1191  const scalar cosAngle = (featVec & (featPt-pt));
1192 
1193  if (cosAngle > 0)
1194  {
1195  hasPos = true;
1196  }
1197  else
1198  {
1199  hasNeg = true;
1200  }
1201  }
1202  }
1203 
1204  if (!hasPos || !hasNeg)
1205  {
1206  //Pout<< "**Detected feature string end at "
1207  // << pp.localPoints()[pointI] << endl;
1208 
1209  // No string. Assign best choice on either side
1210  label bestPosPointI = -1;
1211  scalar minPosDistSqr = GREAT;
1212  label bestNegPointI = -1;
1213  scalar minNegDistSqr = GREAT;
1214 
1215  forAll(pEdges, pEdgeI)
1216  {
1217  const edge& e = pp.edges()[pEdges[pEdgeI]];
1218  label nbrPointI = e.otherVertex(pointI);
1219 
1220  if
1221  (
1222  patchConstraints[nbrPointI].first() <= 1
1223  && rawPatchConstraints[nbrPointI].first() > 1
1224  )
1225  {
1226  const vector& nbrFeatVec =
1227  rawPatchConstraints[pointI].second();
1228 
1229  if (mag(featVec&nbrFeatVec) > featureCos)
1230  {
1231  // nbrPointI attracted to sameish feature
1232  // Note: also check on position.
1233 
1234  scalar d2 = magSqr
1235  (
1236  rawPatchAttraction[nbrPointI]
1237  );
1238 
1239  const point featPt =
1240  pp.localPoints()[nbrPointI]
1241  + rawPatchAttraction[nbrPointI];
1242  const scalar cosAngle =
1243  (featVec & (featPt-pt));
1244 
1245  if (cosAngle > 0)
1246  {
1247  if (!hasPos && d2 < minPosDistSqr)
1248  {
1249  minPosDistSqr = d2;
1250  bestPosPointI = nbrPointI;
1251  }
1252  }
1253  else
1254  {
1255  if (!hasNeg && d2 < minNegDistSqr)
1256  {
1257  minNegDistSqr = d2;
1258  bestNegPointI = nbrPointI;
1259  }
1260  }
1261  }
1262  }
1263  }
1264 
1265  if (bestPosPointI != -1)
1266  {
1267  // Use reconstructed-feature attraction. Use only
1268  // part of it since not sure...
1269  //const point& bestPt =
1270  // pp.localPoints()[bestPosPointI];
1271  //Pout<< "**Overriding point " << bestPt
1272  // << " on reconstructed feature edge at "
1273  // << rawPatchAttraction[bestPosPointI]+bestPt
1274  // << " to attracted-to-feature-edge." << endl;
1275  patchAttraction[bestPosPointI] =
1276  0.5*rawPatchAttraction[bestPosPointI];
1277  patchConstraints[bestPosPointI] =
1278  rawPatchConstraints[bestPosPointI];
1279 
1280  nChanged++;
1281  }
1282  if (bestNegPointI != -1)
1283  {
1284  // Use reconstructed-feature attraction. Use only
1285  // part of it since not sure...
1286  //const point& bestPt =
1287  // pp.localPoints()[bestNegPointI];
1288  //Pout<< "**Overriding point " << bestPt
1289  // << " on reconstructed feature edge at "
1290  // << rawPatchAttraction[bestNegPointI]+bestPt
1291  // << " to attracted-to-feature-edge." << endl;
1292  patchAttraction[bestNegPointI] =
1293  0.5*rawPatchAttraction[bestNegPointI];
1294  patchConstraints[bestNegPointI] =
1295  rawPatchConstraints[bestNegPointI];
1296 
1297  nChanged++;
1298  }
1299  }
1300  }
1301  }
1302 
1303 
1304  reduce(nChanged, sumOp<label>());
1305  Info<< "Stringing feature edges : changed " << nChanged << " points"
1306  << endl;
1307  if (nChanged == 0)
1308  {
1309  break;
1310  }
1311  }
1312 }
1313 
1314 
1315 void Foam::autoSnapDriver::releasePointsNextToMultiPatch
1316 (
1317  const label iter,
1318  const scalar featureCos,
1319 
1320  const indirectPrimitivePatch& pp,
1321  const scalarField& snapDist,
1322 
1323  const List<List<point> >& pointFaceCentres,
1324  const labelListList& pointFacePatchID,
1325 
1326  const vectorField& rawPatchAttraction,
1327  const List<pointConstraint>& rawPatchConstraints,
1328 
1329  vectorField& patchAttraction,
1330  List<pointConstraint>& patchConstraints
1331 ) const
1332 {
1333  autoPtr<OBJstream> multiPatchStr;
1334  if (debug&meshRefinement::ATTRACTION)
1335  {
1336  multiPatchStr.reset
1337  (
1338  new OBJstream
1339  (
1340  meshRefiner_.mesh().time().path()
1341  / "multiPatch_" + name(iter) + ".obj"
1342  )
1343  );
1344  Info<< "Dumping removed constraints due to same-face"
1345  << " multi-patch points to "
1346  << multiPatchStr().name() << endl;
1347  }
1348 
1349 
1350  // 1. Mark points on multiple patches
1351  PackedBoolList isMultiPatchPoint(pp.size());
1352 
1353  forAll(pointFacePatchID, pointI)
1354  {
1355  pointIndexHit multiPatchPt = findMultiPatchPoint
1356  (
1357  pp.localPoints()[pointI],
1358  pointFacePatchID[pointI],
1359  pointFaceCentres[pointI]
1360  );
1361  isMultiPatchPoint[pointI] = multiPatchPt.hit();
1362  }
1363 
1364  // 2. Make sure multi-patch points are also attracted
1365  forAll(isMultiPatchPoint, pointI)
1366  {
1367  if (isMultiPatchPoint[pointI])
1368  {
1369  if
1370  (
1371  patchConstraints[pointI].first() <= 1
1372  && rawPatchConstraints[pointI].first() > 1
1373  )
1374  {
1375  patchAttraction[pointI] = rawPatchAttraction[pointI];
1376  patchConstraints[pointI] = rawPatchConstraints[pointI];
1377 
1378  //if (multiPatchStr.valid())
1379  //{
1380  // Pout<< "Adding constraint on multiPatchPoint:"
1381  // << pp.localPoints()[pointI]
1382  // << " constraint:" << patchConstraints[pointI]
1383  // << " attraction:" << patchAttraction[pointI]
1384  // << endl;
1385  //}
1386  }
1387  }
1388  }
1389 
1390  // Up to here it is all parallel ok.
1391 
1392 
1393  // 3. Knock out any attraction on faces with multi-patch points
1394  label nChanged = 0;
1395  forAll(pp.localFaces(), faceI)
1396  {
1397  const face& f = pp.localFaces()[faceI];
1398 
1399  label nMultiPatchPoints = 0;
1400  forAll(f, fp)
1401  {
1402  label pointI = f[fp];
1403  if
1404  (
1405  isMultiPatchPoint[pointI]
1406  && patchConstraints[pointI].first() > 1
1407  )
1408  {
1409  nMultiPatchPoints++;
1410  }
1411  }
1412 
1413  if (nMultiPatchPoints > 0)
1414  {
1415  forAll(f, fp)
1416  {
1417  label pointI = f[fp];
1418  if
1419  (
1420  !isMultiPatchPoint[pointI]
1421  && patchConstraints[pointI].first() > 1
1422  )
1423  {
1424  //Pout<< "Knocking out constraint"
1425  // << " on non-multiPatchPoint:"
1426  // << pp.localPoints()[pointI] << endl;
1427  patchAttraction[pointI] = vector::zero;
1428  patchConstraints[pointI] = pointConstraint();
1429  nChanged++;
1430 
1431  if (multiPatchStr.valid())
1432  {
1433  multiPatchStr().write(pp.localPoints()[pointI]);
1434  }
1435  }
1436  }
1437  }
1438  }
1439 
1440  reduce(nChanged, sumOp<label>());
1441  Info<< "Removing constraints near multi-patch points : changed "
1442  << nChanged << " points" << endl;
1443 }
1444 
1445 
1446 // If only two attractions and across face return the face indices
1447 Foam::labelPair Foam::autoSnapDriver::findDiagonalAttraction
1448 (
1449  const indirectPrimitivePatch& pp,
1450  const vectorField& patchAttraction,
1451  const List<pointConstraint>& patchConstraints,
1452  const label faceI
1453 ) const
1454 {
1455  const face& f = pp.localFaces()[faceI];
1456  // For now just detect any attraction. Improve this to look at
1457  // actual attraction position and orientation
1458 
1459  labelPair attractIndices(-1, -1);
1460 
1461  if (f.size() >= 4)
1462  {
1463  forAll(f, fp)
1464  {
1465  label pointI = f[fp];
1466  if (patchConstraints[pointI].first() >= 2)
1467  {
1468  // Attract to feature edge or point
1469  if (attractIndices[0] == -1)
1470  {
1471  // First attraction. Store
1472  attractIndices[0] = fp;
1473  }
1474  else if (attractIndices[1] == -1)
1475  {
1476  // Second attraction. Check if not consecutive to first
1477  // attraction
1478  label fp0 = attractIndices[0];
1479  if (f.fcIndex(fp0) == fp || f.fcIndex(fp) == fp0)
1480  {
1481  // Consecutive. Skip.
1482  attractIndices = labelPair(-1, -1);
1483  break;
1484  }
1485  else
1486  {
1487  attractIndices[1] = fp;
1488  }
1489  }
1490  else
1491  {
1492  // More than two attractions. Skip.
1493  attractIndices = labelPair(-1, -1);
1494  break;
1495  }
1496  }
1497  }
1498 
1499 
1500  if (attractIndices[1] == -1)
1501  {
1502  // Found only one attraction. Skip.
1503  attractIndices = labelPair(-1, -1);
1504  }
1505  }
1506  return attractIndices;
1507 }
1508 
1509 
1510 void Foam::autoSnapDriver::avoidDiagonalAttraction
1511 (
1512  const label iter,
1513  const scalar featureCos,
1514 
1515  const indirectPrimitivePatch& pp,
1516 
1517  vectorField& patchAttraction,
1518  List<pointConstraint>& patchConstraints
1519 ) const
1520 {
1521  forAll(pp.localFaces(), faceI)
1522  {
1523  const face& f = pp.localFaces()[faceI];
1524 
1525  labelPair diag = findDiagonalAttraction
1526  (
1527  pp,
1528  patchAttraction,
1529  patchConstraints,
1530  faceI
1531  );
1532 
1533  if (diag[0] != -1 && diag[1] != -1)
1534  {
1535  // Found two diagonal points that being attracted.
1536  // For now just attract my one to the average of those.
1537  const label i0 = f[diag[0]];
1538  const point pt0 =
1539  pp.localPoints()[i0]+patchAttraction[i0];
1540  const label i1 = f[diag[1]];
1541  const point pt1 =
1542  pp.localPoints()[i1]+patchAttraction[i1];
1543  const point mid = 0.5*(pt0+pt1);
1544 
1545  const scalar cosAngle = mag
1546  (
1547  patchConstraints[i0].second()
1548  & patchConstraints[i1].second()
1549  );
1550 
1551  //Pout<< "Found diagonal attraction at indices:"
1552  // << diag[0]
1553  // << " and " << diag[1]
1554  // << " with cosAngle:" << cosAngle
1555  // << " mid:" << mid << endl;
1556 
1557  if (cosAngle > featureCos)
1558  {
1559  // Add the nearest of the other two points as
1560  // attractor
1561  label minFp = -1;
1562  scalar minDistSqr = GREAT;
1563  forAll(f, fp)
1564  {
1565  label pointI = f[fp];
1566  if (patchConstraints[pointI].first() <= 1)
1567  {
1568  const point& pt = pp.localPoints()[pointI];
1569  scalar distSqr = magSqr(mid-pt);
1570  if (distSqr < minDistSqr)
1571  {
1572  distSqr = minDistSqr;
1573  minFp = fp;
1574  }
1575  }
1576  }
1577  if (minFp != -1)
1578  {
1579  label minPointI = f[minFp];
1580  patchAttraction[minPointI] =
1581  mid-pp.localPoints()[minPointI];
1582  patchConstraints[minPointI] =
1583  patchConstraints[f[diag[0]]];
1584  }
1585  }
1586  else
1587  {
1588  //Pout<< "Diagonal attractors at" << nl
1589  // << " pt0:" << pt0
1590  // << " constraint:"
1591  // << patchConstraints[i0].second() << nl
1592  // << " pt1:" << pt1
1593  // << " constraint:"
1594  // << patchConstraints[i1].second() << nl
1595  // << " make too large an angle:"
1596  // << mag
1597  // (
1598  // patchConstraints[i0].second()
1599  // & patchConstraints[i1].second()
1600  // )
1601  // << endl;
1602  }
1603  }
1604  }
1605 }
1606 
1607 
1609 Foam::autoSnapDriver::findNearFeatureEdge
1610 (
1611  const bool isRegionEdge,
1612 
1613  const indirectPrimitivePatch& pp,
1614  const scalarField& snapDist,
1615  const label pointI,
1616  const point& estimatedPt,
1617 
1618  List<List<DynamicList<point> > >& edgeAttractors,
1619  List<List<DynamicList<pointConstraint> > >& edgeConstraints,
1620  vectorField& patchAttraction,
1621  List<pointConstraint>& patchConstraints
1622 ) const
1623 {
1624  const refinementFeatures& features = meshRefiner_.features();
1625 
1626  labelList nearEdgeFeat;
1627  List<pointIndexHit> nearEdgeInfo;
1628  vectorField nearNormal;
1629 
1630  if (isRegionEdge)
1631  {
1632  features.findNearestRegionEdge
1633  (
1634  pointField(1, estimatedPt),
1635  scalarField(1, sqr(snapDist[pointI])),
1636  nearEdgeFeat,
1637  nearEdgeInfo,
1638  nearNormal
1639  );
1640  }
1641  else
1642  {
1643  features.findNearestEdge
1644  (
1645  pointField(1, estimatedPt),
1646  scalarField(1, sqr(snapDist[pointI])),
1647  nearEdgeFeat,
1648  nearEdgeInfo,
1649  nearNormal
1650  );
1651  }
1652 
1653  const pointIndexHit& nearInfo = nearEdgeInfo[0];
1654  label featI = nearEdgeFeat[0];
1655 
1656  if (nearInfo.hit())
1657  {
1658  // So we have a point on the feature edge. Use this
1659  // instead of our estimate from planes.
1660  edgeAttractors[featI][nearInfo.index()].append
1661  (
1662  nearInfo.hitPoint()
1663  );
1664  pointConstraint c(Tuple2<label, vector>(2, nearNormal[0]));
1665  edgeConstraints[featI][nearInfo.index()].append(c);
1666 
1667  // Store for later use
1668  patchAttraction[pointI] =
1669  nearInfo.hitPoint()-pp.localPoints()[pointI];
1670  patchConstraints[pointI] = c;
1671  }
1672  return Tuple2<label, pointIndexHit>(featI, nearInfo);
1673 }
1674 
1675 
1677 Foam::autoSnapDriver::findNearFeaturePoint
1678 (
1679  const bool isRegionPoint,
1680 
1681  const indirectPrimitivePatch& pp,
1682  const scalarField& snapDist,
1683  const label pointI,
1684  const point& estimatedPt,
1685 
1686  // Feature-point to pp point
1687  List<labelList>& pointAttractor,
1689  // Feature-edge to pp point
1690  List<List<DynamicList<point> > >& edgeAttractors,
1691  List<List<DynamicList<pointConstraint> > >& edgeConstraints,
1692  // pp point to nearest feature
1693  vectorField& patchAttraction,
1694  List<pointConstraint>& patchConstraints
1695 ) const
1696 {
1697  const refinementFeatures& features = meshRefiner_.features();
1698 
1699  labelList nearFeat;
1701  features.findNearestPoint
1702  (
1703  pointField(1, estimatedPt),
1704  scalarField(1, sqr(snapDist[pointI])),
1705  nearFeat,
1706  nearInfo
1707  );
1708 
1709  label featI = nearFeat[0];
1710 
1711  if (featI != -1)
1712  {
1713  const point& pt = pp.localPoints()[pointI];
1714 
1715  label featPointI = nearInfo[0].index();
1716  const point& featPt = nearInfo[0].hitPoint();
1717  scalar distSqr = magSqr(featPt-pt);
1718 
1719  // Check if already attracted
1720  label oldPointI = pointAttractor[featI][featPointI];
1721 
1722  if (oldPointI != -1)
1723  {
1724  // Check distance
1725  if (distSqr >= magSqr(featPt-pp.localPoints()[oldPointI]))
1726  {
1727  // oldPointI nearest. Keep.
1728  featI = -1;
1729  featPointI = -1;
1730  }
1731  else
1732  {
1733  // Current pointI nearer.
1734  pointAttractor[featI][featPointI] = pointI;
1735  pointConstraints[featI][featPointI].first() = 3;
1736  pointConstraints[featI][featPointI].second() = vector::zero;
1737 
1738  // Store for later use
1739  patchAttraction[pointI] = featPt-pt;
1740  patchConstraints[pointI] =
1741  pointConstraints[featI][featPointI];
1742 
1743  // Reset oldPointI to nearest on feature edge
1744  patchAttraction[oldPointI] = vector::zero;
1745  patchConstraints[oldPointI] = pointConstraint();
1746 
1747  findNearFeatureEdge
1748  (
1749  isRegionPoint, // search region edges only
1750 
1751  pp,
1752  snapDist,
1753  oldPointI,
1754  pp.localPoints()[oldPointI],
1755 
1756  edgeAttractors,
1757  edgeConstraints,
1758  patchAttraction,
1759  patchConstraints
1760  );
1761  }
1762  }
1763  else
1764  {
1765  // Current pointI nearer.
1766  pointAttractor[featI][featPointI] = pointI;
1767  pointConstraints[featI][featPointI].first() = 3;
1768  pointConstraints[featI][featPointI].second() = vector::zero;
1769 
1770  // Store for later use
1771  patchAttraction[pointI] = featPt-pt;
1772  patchConstraints[pointI] = pointConstraints[featI][featPointI];
1773  }
1774  }
1775 
1776  return Tuple2<label, pointIndexHit>(featI, nearInfo[0]);
1777 }
1778 
1779 
1780 // Determines for every pp point - that is on multiple faces that form
1781 // a feature - the nearest feature edge/point.
1782 void Foam::autoSnapDriver::determineFeatures
1783 (
1784  const label iter,
1785  const scalar featureCos,
1786  const bool multiRegionFeatureSnap,
1787 
1788  const indirectPrimitivePatch& pp,
1789  const scalarField& snapDist,
1790  const vectorField& nearestDisp,
1791 
1792  const List<List<point> >& pointFaceSurfNormals,
1793  const List<List<point> >& pointFaceDisp,
1794  const List<List<point> >& pointFaceCentres,
1795  const labelListList& pointFacePatchID,
1796 
1797  // Feature-point to pp point
1798  List<labelList>& pointAttractor,
1800  // Feature-edge to pp point
1801  List<List<DynamicList<point> > >& edgeAttractors,
1802  List<List<DynamicList<pointConstraint> > >& edgeConstraints,
1803  // pp point to nearest feature
1804  vectorField& patchAttraction,
1805  List<pointConstraint>& patchConstraints
1806 ) const
1807 {
1808  autoPtr<OBJstream> featureEdgeStr;
1809  autoPtr<OBJstream> missedEdgeStr;
1810  autoPtr<OBJstream> featurePointStr;
1811 
1812  if (debug&meshRefinement::ATTRACTION)
1813  {
1814  featureEdgeStr.reset
1815  (
1816  new OBJstream
1817  (
1818  meshRefiner_.mesh().time().path()
1819  / "featureEdge_" + name(iter) + ".obj"
1820  )
1821  );
1822  Info<< "Dumping feature-edge sampling to "
1823  << featureEdgeStr().name() << endl;
1824 
1825  missedEdgeStr.reset
1826  (
1827  new OBJstream
1828  (
1829  meshRefiner_.mesh().time().path()
1830  / "missedFeatureEdge_" + name(iter) + ".obj"
1831  )
1832  );
1833  Info<< "Dumping feature-edges that are too far away to "
1834  << missedEdgeStr().name() << endl;
1835 
1836  featurePointStr.reset
1837  (
1838  new OBJstream
1839  (
1840  meshRefiner_.mesh().time().path()
1841  / "featurePoint_" + name(iter) + ".obj"
1842  )
1843  );
1844  Info<< "Dumping feature-point sampling to "
1845  << featurePointStr().name() << endl;
1846  }
1847 
1848 
1849  DynamicList<point> surfacePoints(4);
1850  DynamicList<vector> surfaceNormals(4);
1851  labelList faceToNormalBin;
1852 
1853  forAll(pp.localPoints(), pointI)
1854  {
1855  const point& pt = pp.localPoints()[pointI];
1856 
1857  vector attraction = vector::zero;
1858  pointConstraint constraint;
1859 
1860  featureAttractionUsingReconstruction
1861  (
1862  iter,
1863  featureCos,
1864 
1865  pp,
1866  snapDist,
1867  nearestDisp,
1868 
1869  pointI,
1870 
1871  pointFaceSurfNormals,
1872  pointFaceDisp,
1873  pointFaceCentres,
1874  pointFacePatchID,
1875 
1876  surfacePoints,
1877  surfaceNormals,
1878  faceToNormalBin,
1879 
1880  attraction,
1881  constraint
1882  );
1883 
1884 
1885  if
1886  (
1887  (constraint.first() > patchConstraints[pointI].first())
1888  || (
1889  (constraint.first() == patchConstraints[pointI].first())
1890  && (magSqr(attraction) < magSqr(patchAttraction[pointI]))
1891  )
1892  )
1893  {
1894  patchAttraction[pointI] = attraction;
1895  patchConstraints[pointI] = constraint;
1896 
1897  // Check the number of directions
1898  if (patchConstraints[pointI].first() == 1)
1899  {
1900  // Flat surface. Check for different patchIDs
1901  if (multiRegionFeatureSnap)
1902  {
1903  const point estimatedPt(pt + nearestDisp[pointI]);
1904  pointIndexHit multiPatchPt
1905  (
1906  findMultiPatchPoint
1907  (
1908  estimatedPt,
1909  pointFacePatchID[pointI],
1910  surfaceNormals,
1911  faceToNormalBin
1912  )
1913  );
1914 
1915  if (multiPatchPt.hit())
1916  {
1917  // Behave like when having two surface normals so
1918  // attract to nearest feature edge (with a guess for
1919  // the multipatch point as starting point)
1921  findNearFeatureEdge
1922  (
1923  true, // isRegionPoint
1924  pp,
1925  snapDist,
1926  pointI,
1927  multiPatchPt.hitPoint(), // estimatedPt
1928 
1929  edgeAttractors,
1930  edgeConstraints,
1931 
1932  patchAttraction,
1933  patchConstraints
1934  );
1935 
1936  const pointIndexHit& info = nearInfo.second();
1937  if (info.hit())
1938  {
1939  // Dump
1940  if (featureEdgeStr.valid())
1941  {
1942  featureEdgeStr().write
1943  (
1944  linePointRef(pt, info.hitPoint())
1945  );
1946  }
1947  }
1948  else
1949  {
1950  if (missedEdgeStr.valid())
1951  {
1952  missedEdgeStr().write
1953  (
1954  linePointRef(pt, info.missPoint())
1955  );
1956  }
1957  }
1958  }
1959  }
1960  }
1961  else if (patchConstraints[pointI].first() == 2)
1962  {
1963  // Mark point on the nearest feature edge. Note that we
1964  // only search within the surrounding since the plane
1965  // reconstruction might find a feature where there isn't one.
1966  const point estimatedPt(pt + patchAttraction[pointI]);
1967 
1969 
1970  // Geometric feature edge. Check for different patchIDs
1971  bool hasSnapped = false;
1972  if (multiRegionFeatureSnap)
1973  {
1974  pointIndexHit multiPatchPt
1975  (
1976  findMultiPatchPoint
1977  (
1978  estimatedPt,
1979  pointFacePatchID[pointI],
1980  surfaceNormals,
1981  faceToNormalBin
1982  )
1983  );
1984  if (multiPatchPt.hit())
1985  {
1986  if (multiPatchPt.index() == 0)
1987  {
1988  // Geometric feature edge is also region edge
1989  nearInfo = findNearFeatureEdge
1990  (
1991  true, // isRegionPoint
1992  pp,
1993  snapDist,
1994  pointI,
1995  estimatedPt,
1996 
1997  edgeAttractors,
1998  edgeConstraints,
1999 
2000  patchAttraction,
2001  patchConstraints
2002  );
2003  hasSnapped = true;
2004  }
2005  else
2006  {
2007  // One of planes of feature contains multiple
2008  // regions
2009  nearInfo = findNearFeaturePoint
2010  (
2011  true, // isRegionPoint
2012  pp,
2013  snapDist,
2014  pointI,
2015  estimatedPt,
2016 
2017  // Feature-point to pp point
2018  pointAttractor,
2020  // Feature-edge to pp point
2021  edgeAttractors,
2022  edgeConstraints,
2023  // pp point to nearest feature
2024  patchAttraction,
2025  patchConstraints
2026  );
2027  hasSnapped = true;
2028  }
2029  }
2030  }
2031 
2032  if (!hasSnapped)
2033  {
2034  // Determine nearest point on feature edge. Store
2035  // constraint
2036  // (calculated from feature edge, alternative would be to
2037  // use constraint calculated from both surfaceNormals)
2038  nearInfo = findNearFeatureEdge
2039  (
2040  false, // isRegionPoint
2041  pp,
2042  snapDist,
2043  pointI,
2044  estimatedPt,
2045 
2046  edgeAttractors,
2047  edgeConstraints,
2048 
2049  patchAttraction,
2050  patchConstraints
2051  );
2052  hasSnapped = true;
2053  }
2054 
2055  // Dump to obj
2056  const pointIndexHit& info = nearInfo.second();
2057  if (info.hit())
2058  {
2059  if
2060  (
2061  patchConstraints[pointI].first() == 3
2062  && featurePointStr.valid()
2063  )
2064  {
2065  featurePointStr().write
2066  (
2067  linePointRef(pt, info.hitPoint())
2068  );
2069  }
2070  else if
2071  (
2072  patchConstraints[pointI].first() == 2
2073  && featureEdgeStr.valid()
2074  )
2075  {
2076  featureEdgeStr().write
2077  (
2078  linePointRef(pt, info.hitPoint())
2079  );
2080  }
2081  }
2082  else
2083  {
2084  if (missedEdgeStr.valid())
2085  {
2086  missedEdgeStr().write
2087  (
2088  linePointRef(pt, info.missPoint())
2089  );
2090  }
2091  }
2092  }
2093  else if (patchConstraints[pointI].first() == 3)
2094  {
2095  // Mark point on the nearest feature point.
2096  const point estimatedPt(pt + patchAttraction[pointI]);
2097 
2099 
2100  if (multiRegionFeatureSnap)
2101  {
2102  pointIndexHit multiPatchPt
2103  (
2104  findMultiPatchPoint
2105  (
2106  estimatedPt,
2107  pointFacePatchID[pointI],
2108  surfaceNormals,
2109  faceToNormalBin
2110  )
2111  );
2112  if (multiPatchPt.hit())
2113  {
2114  // Multiple regions
2115  nearInfo = findNearFeaturePoint
2116  (
2117  true, // isRegionPoint
2118  pp,
2119  snapDist,
2120  pointI,
2121  estimatedPt,
2122 
2123  // Feature-point to pp point
2124  pointAttractor,
2126  // Feature-edge to pp point
2127  edgeAttractors,
2128  edgeConstraints,
2129  // pp point to nearest feature
2130  patchAttraction,
2131  patchConstraints
2132  );
2133  }
2134  else
2135  {
2136  nearInfo = findNearFeaturePoint
2137  (
2138  false, // isRegionPoint
2139  pp,
2140  snapDist,
2141  pointI,
2142  estimatedPt,
2143 
2144  // Feature-point to pp point
2145  pointAttractor,
2147  // Feature-edge to pp point
2148  edgeAttractors,
2149  edgeConstraints,
2150  // pp point to nearest feature
2151  patchAttraction,
2152  patchConstraints
2153  );
2154  }
2155  }
2156  else
2157  {
2158  // No multi-patch snapping
2159  nearInfo = findNearFeaturePoint
2160  (
2161  false, // isRegionPoint
2162  pp,
2163  snapDist,
2164  pointI,
2165  estimatedPt,
2166 
2167  // Feature-point to pp point
2168  pointAttractor,
2170  // Feature-edge to pp point
2171  edgeAttractors,
2172  edgeConstraints,
2173  // pp point to nearest feature
2174  patchAttraction,
2175  patchConstraints
2176  );
2177  }
2178 
2179  const pointIndexHit& info = nearInfo.second();
2180  if (info.hit() && featurePointStr.valid())
2181  {
2182  featurePointStr().write
2183  (
2184  linePointRef(pt, info.hitPoint())
2185  );
2186  }
2187  }
2188  }
2189  }
2190 }
2191 
2192 
2193 // Baffle handling
2194 // ~~~~~~~~~~~~~~~
2195 // Override pointAttractor, edgeAttractor, patchAttration etc. to
2196 // implement 'baffle' handling.
2197 // Baffle: the mesh pp point originates from a loose standing
2198 // baffle.
2199 // Sampling the surface with the surrounding face-centres only picks up
2200 // a single triangle normal so above determineFeatures will not have
2201 // detected anything. So explicitly pick up feature edges on the pp
2202 // (after duplicating points & smoothing so will already have been
2203 // expanded) and match these to the features.
2204 void Foam::autoSnapDriver::determineBaffleFeatures
2205 (
2206  const label iter,
2207  const scalar featureCos,
2208 
2209  const indirectPrimitivePatch& pp,
2210  const scalarField& snapDist,
2211 
2212  // Feature-point to pp point
2213  List<labelList>& pointAttractor,
2215  // Feature-edge to pp point
2216  List<List<DynamicList<point> > >& edgeAttractors,
2217  List<List<DynamicList<pointConstraint> > >& edgeConstraints,
2218  // pp point to nearest feature
2219  vectorField& patchAttraction,
2220  List<pointConstraint>& patchConstraints
2221 ) const
2222 {
2223  const fvMesh& mesh = meshRefiner_.mesh();
2224  const refinementFeatures& features = meshRefiner_.features();
2225 
2226  // Calculate edge-faces
2227  List<List<point> > edgeFaceNormals(pp.nEdges());
2228 
2229  // Fill local data
2230  forAll(pp.edgeFaces(), edgeI)
2231  {
2232  const labelList& eFaces = pp.edgeFaces()[edgeI];
2233  List<point>& eFc = edgeFaceNormals[edgeI];
2234  eFc.setSize(eFaces.size());
2235  forAll(eFaces, i)
2236  {
2237  label faceI = eFaces[i];
2238  eFc[i] = pp.faceNormals()[faceI];
2239  }
2240  }
2241 
2242  {
2243  // Precalculate mesh edges for pp.edges.
2244  const labelList meshEdges
2245  (
2246  pp.meshEdges(mesh.edges(), mesh.pointEdges())
2247  );
2249  (
2250  mesh,
2251  meshEdges,
2252  edgeFaceNormals,
2254  List<point>(),
2256  );
2257  }
2258 
2259  // Detect baffle edges. Assume initial mesh will have 0,90 or 180
2260  // (baffle) degree angles so smoothing should make 0,90
2261  // to be less than 90.
2262  const scalar baffleFeatureCos = Foam::cos(degToRad(91));
2263 
2264 
2265  autoPtr<OBJstream> baffleEdgeStr;
2266  if (debug&meshRefinement::ATTRACTION)
2267  {
2268  baffleEdgeStr.reset
2269  (
2270  new OBJstream
2271  (
2272  meshRefiner_.mesh().time().path()
2273  / "baffleEdge_" + name(iter) + ".obj"
2274  )
2275  );
2276  Info<< nl << "Dumping baffle-edges to "
2277  << baffleEdgeStr().name() << endl;
2278  }
2279 
2280 
2281  // Is edge on baffle
2282  PackedBoolList isBaffleEdge(pp.nEdges());
2283  label nBaffleEdges = 0;
2284  // Is point on
2285  // 0 : baffle-edge (0)
2286  // 1 : baffle-feature-point (1)
2287  // -1 : rest
2288  labelList pointStatus(pp.nPoints(), -1);
2289 
2290  forAll(edgeFaceNormals, edgeI)
2291  {
2292  const List<point>& efn = edgeFaceNormals[edgeI];
2293 
2294  if (efn.size() == 2 && (efn[0]&efn[1]) < baffleFeatureCos)
2295  {
2296  isBaffleEdge[edgeI] = true;
2297  nBaffleEdges++;
2298  const edge& e = pp.edges()[edgeI];
2299  pointStatus[e[0]] = 0;
2300  pointStatus[e[1]] = 0;
2301 
2302  if (baffleEdgeStr.valid())
2303  {
2304  const point& p0 = pp.localPoints()[e[0]];
2305  const point& p1 = pp.localPoints()[e[1]];
2306  baffleEdgeStr().write(linePointRef(p0, p1));
2307  }
2308  }
2309  }
2310 
2311  Info<< "Detected "
2312  << returnReduce(nBaffleEdges, sumOp<label>())
2313  << " baffle edges out of "
2314  << returnReduce(pp.nEdges(), sumOp<label>())
2315  << " edges." << endl;
2316 
2317 
2318  forAll(pp.pointEdges(), pointI)
2319  {
2320  if
2321  (
2322  isFeaturePoint
2323  (
2324  featureCos,
2325  pp,
2326  isBaffleEdge,
2327  pointI
2328  )
2329  )
2330  {
2331  //Pout<< "Detected feature point:" << pp.localPoints()[pointI]
2332  // << endl;
2333  //-TEMPORARILY DISABLED:
2334  //pointStatus[pointI] = 1;
2335  }
2336  }
2337 
2338 
2339  forAll(pointStatus, pointI)
2340  {
2341  const point& pt = pp.localPoints()[pointI];
2342 
2343  if (pointStatus[pointI] == 0) // baffle edge
2344  {
2345  Tuple2<label, pointIndexHit> nearInfo = findNearFeatureEdge
2346  (
2347  false, // isRegionPoint?
2348  pp,
2349  snapDist,
2350  pointI,
2351  pt,
2352 
2353  edgeAttractors,
2354  edgeConstraints,
2355  patchAttraction,
2356  patchConstraints
2357  );
2358 
2359  if (!nearInfo.second().hit())
2360  {
2361  //Pout<< "*** Failed to find close edge to point " << pt
2362  // << endl;
2363  }
2364  }
2365  else if (pointStatus[pointI] == 1) // baffle point
2366  {
2367  labelList nearFeat;
2369  features.findNearestPoint
2370  (
2371  pointField(1, pt),
2372  scalarField(1, sqr(snapDist[pointI])),
2373  nearFeat,
2374  nearInfo
2375  );
2376 
2377  label featI = nearFeat[0];
2378 
2379  if (featI != -1)
2380  {
2381  label featPointI = nearInfo[0].index();
2382  const point& featPt = nearInfo[0].hitPoint();
2383  scalar distSqr = magSqr(featPt-pt);
2384 
2385  // Check if already attracted
2386  label oldPointI = pointAttractor[featI][featPointI];
2387 
2388  if
2389  (
2390  oldPointI == -1
2391  || (
2392  distSqr
2393  < magSqr(featPt-pp.localPoints()[oldPointI])
2394  )
2395  )
2396  {
2397  pointAttractor[featI][featPointI] = pointI;
2398  pointConstraints[featI][featPointI].first() = 3;
2399  pointConstraints[featI][featPointI].second() =
2400  vector::zero;
2401 
2402  // Store for later use
2403  patchAttraction[pointI] = featPt-pt;
2404  patchConstraints[pointI] =
2405  pointConstraints[featI][featPointI];
2406 
2407  if (oldPointI != -1)
2408  {
2409  // The current point is closer so wins. Reset
2410  // the old point to attract to nearest edge
2411  // instead.
2412  findNearFeatureEdge
2413  (
2414  false, // isRegionPoint
2415  pp,
2416  snapDist,
2417  oldPointI,
2418  pp.localPoints()[oldPointI],
2419 
2420  edgeAttractors,
2421  edgeConstraints,
2422  patchAttraction,
2423  patchConstraints
2424  );
2425  }
2426  }
2427  else
2428  {
2429  // Make it fall through to check below
2430  featI = -1;
2431  }
2432  }
2433 
2434  // Not found a feature point or another point is already
2435  // closer to that feature
2436  if (featI == -1)
2437  {
2438  //Pout<< "*** Falling back to finding nearest feature"
2439  // << " edge"
2440  // << " for baffle-feature-point " << pt
2441  // << endl;
2442 
2443  findNearFeatureEdge
2444  (
2445  false, // isRegionPoint
2446  pp,
2447  snapDist,
2448  pointI,
2449  pt, // starting point
2450 
2451  edgeAttractors,
2452  edgeConstraints,
2453  patchAttraction,
2454  patchConstraints
2455  );
2456  }
2457  }
2458  }
2459 }
2460 
2461 
2462 void Foam::autoSnapDriver::reverseAttractMeshPoints
2463 (
2464  const label iter,
2465 
2466  const indirectPrimitivePatch& pp,
2467  const scalarField& snapDist,
2468 
2469  // Feature-point to pp point
2470  const List<labelList>& pointAttractor,
2472  // Feature-edge to pp point
2473  const List<List<DynamicList<point> > >& edgeAttractors,
2474  const List<List<DynamicList<pointConstraint> > >& edgeConstraints,
2475 
2476  const vectorField& rawPatchAttraction,
2477  const List<pointConstraint>& rawPatchConstraints,
2478 
2479  // pp point to nearest feature
2480  vectorField& patchAttraction,
2481  List<pointConstraint>& patchConstraints
2482 ) const
2483 {
2484  const refinementFeatures& features = meshRefiner_.features();
2485 
2486  // Find nearest mesh point to feature edge
2487  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2488  // Reverse lookup : go through all edgeAttractors and find the
2489  // nearest point on pp
2490 
2491  // Get search domain and extend it a bit
2492  treeBoundBox bb(pp.localPoints());
2493  {
2494  // Random number generator. Bit dodgy since not exactly random ;-)
2495  Random rndGen(65431);
2496 
2497  // Slightly extended bb. Slightly off-centred just so on symmetric
2498  // geometry there are less face/edge aligned items.
2499  bb = bb.extend(rndGen, 1e-4);
2500  bb.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
2501  bb.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
2502  }
2503 
2504  // Collect candidate points for attraction
2505  DynamicList<label> attractPoints(pp.nPoints());
2506  {
2507  const fvMesh& mesh = meshRefiner_.mesh();
2508 
2509  boolList isFeatureEdgeOrPoint(pp.nPoints(), false);
2510  label nFeats = 0;
2511  forAll(rawPatchConstraints, pointI)
2512  {
2513  if (rawPatchConstraints[pointI].first() >= 2)
2514  {
2515  isFeatureEdgeOrPoint[pointI] = true;
2516  nFeats++;
2517  }
2518  }
2519 
2520  Info<< "Initially selected " << returnReduce(nFeats, sumOp<label>())
2521  << " points out of " << returnReduce(pp.nPoints(), sumOp<label>())
2522  << " for reverse attraction." << endl;
2523 
2524  // Make sure is synchronised (note: check if constraint is already
2525  // synced in which case this is not needed here)
2527  (
2528  mesh,
2529  pp.meshPoints(),
2530  isFeatureEdgeOrPoint,
2531  orEqOp<bool>(), // combine op
2532  false
2533  );
2534 
2535  for (label nGrow = 0; nGrow < 1; nGrow++)
2536  {
2537  boolList newIsFeatureEdgeOrPoint(isFeatureEdgeOrPoint);
2538 
2539  forAll(pp.localFaces(), faceI)
2540  {
2541  const face& f = pp.localFaces()[faceI];
2542 
2543  forAll(f, fp)
2544  {
2545  if (isFeatureEdgeOrPoint[f[fp]])
2546  {
2547  // Mark all points on face
2548  forAll(f, fp)
2549  {
2550  newIsFeatureEdgeOrPoint[f[fp]] = true;
2551  }
2552  break;
2553  }
2554  }
2555  }
2556 
2557  isFeatureEdgeOrPoint = newIsFeatureEdgeOrPoint;
2558 
2560  (
2561  mesh,
2562  pp.meshPoints(),
2563  isFeatureEdgeOrPoint,
2564  orEqOp<bool>(), // combine op
2565  false
2566  );
2567  }
2568 
2569 
2570  // Collect attractPoints
2571  forAll(isFeatureEdgeOrPoint, pointI)
2572  {
2573  if (isFeatureEdgeOrPoint[pointI])
2574  {
2575  attractPoints.append(pointI);
2576  }
2577  }
2578 
2579  Info<< "Selected "
2580  << returnReduce(attractPoints.size(), sumOp<label>())
2581  << " points out of " << returnReduce(pp.nPoints(), sumOp<label>())
2582  << " for reverse attraction." << endl;
2583  }
2584 
2585 
2587  (
2588  treeDataPoint(pp.localPoints(), attractPoints),
2589  bb, // overall search domain
2590  8, // maxLevel
2591  10, // leafsize
2592  3.0 // duplicity
2593  );
2594 
2595  // Per mesh point the point on nearest feature edge.
2596  patchAttraction.setSize(pp.nPoints());
2597  patchAttraction = vector::zero;
2598  patchConstraints.setSize(pp.nPoints());
2599  patchConstraints = pointConstraint();
2600 
2601  forAll(edgeAttractors, featI)
2602  {
2603  const List<DynamicList<point> >& edgeAttr = edgeAttractors[featI];
2604  const List<DynamicList<pointConstraint> >& edgeConstr =
2605  edgeConstraints[featI];
2606 
2607  forAll(edgeAttr, featEdgeI)
2608  {
2609  const DynamicList<point>& attr = edgeAttr[featEdgeI];
2610  forAll(attr, i)
2611  {
2612  // Find nearest pp point
2613  const point& featPt = attr[i];
2614  pointIndexHit nearInfo = ppTree.findNearest
2615  (
2616  featPt,
2617  sqr(GREAT)
2618  );
2619 
2620  if (nearInfo.hit())
2621  {
2622  label pointI =
2623  ppTree.shapes().pointLabels()[nearInfo.index()];
2624  const point attraction = featPt-pp.localPoints()[pointI];
2625 
2626  // Check if this point is already being attracted. If so
2627  // override it only if nearer.
2628  if
2629  (
2630  patchConstraints[pointI].first() <= 1
2631  || magSqr(attraction) < magSqr(patchAttraction[pointI])
2632  )
2633  {
2634  patchAttraction[pointI] = attraction;
2635  patchConstraints[pointI] = edgeConstr[featEdgeI][i];
2636  }
2637  }
2638  else
2639  {
2640  WarningIn
2641  (
2642  "autoSnapDriver::featureAttractionUsingFeatureEdges"
2643  "(..)"
2644  ) << "Did not find pp point near " << featPt
2645  << endl;
2646  }
2647  }
2648  }
2649  }
2650 
2651 
2652  // Different procs might have different patchAttraction,patchConstraints
2653  // however these only contain geometric information, no topology
2654  // so as long as we synchronise after overriding with feature points
2655  // there is no problem, just possibly a small error.
2656 
2657 
2658  // Find nearest mesh point to feature point
2659  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2660  // (overrides attraction to feature edge)
2661  forAll(pointAttractor, featI)
2662  {
2663  const labelList& pointAttr = pointAttractor[featI];
2664  const List<pointConstraint>& pointConstr = pointConstraints[featI];
2665 
2666  forAll(pointAttr, featPointI)
2667  {
2668  if (pointAttr[featPointI] != -1)
2669  {
2670  const point& featPt = features[featI].points()
2671  [
2672  featPointI
2673  ];
2674 
2675  // Find nearest pp point
2676  pointIndexHit nearInfo = ppTree.findNearest
2677  (
2678  featPt,
2679  sqr(GREAT)
2680  );
2681 
2682  if (nearInfo.hit())
2683  {
2684  label pointI =
2685  ppTree.shapes().pointLabels()[nearInfo.index()];
2686 
2687  const point& pt = pp.localPoints()[pointI];
2688  const point attraction = featPt-pt;
2689 
2690  // - already attracted to feature edge : point always wins
2691  // - already attracted to feature point: nearest wins
2692 
2693  if (patchConstraints[pointI].first() <= 1)
2694  {
2695  patchAttraction[pointI] = attraction;
2696  patchConstraints[pointI] = pointConstr[featPointI];
2697  }
2698  else if (patchConstraints[pointI].first() == 2)
2699  {
2700  patchAttraction[pointI] = attraction;
2701  patchConstraints[pointI] = pointConstr[featPointI];
2702  }
2703  else if (patchConstraints[pointI].first() == 3)
2704  {
2705  // Only if nearer
2706  if
2707  (
2708  magSqr(attraction)
2709  < magSqr(patchAttraction[pointI])
2710  )
2711  {
2712  patchAttraction[pointI] = attraction;
2713  patchConstraints[pointI] =
2714  pointConstr[featPointI];
2715  }
2716  }
2717  }
2718  }
2719  }
2720  }
2721 }
2722 
2723 
2724 void Foam::autoSnapDriver::featureAttractionUsingFeatureEdges
2725 (
2726  const label iter,
2727  const bool avoidSnapProblems,
2728  const scalar featureCos,
2729  const bool multiRegionFeatureSnap,
2730 
2731  const indirectPrimitivePatch& pp,
2732  const scalarField& snapDist,
2733  const vectorField& nearestDisp,
2734 
2735  const List<List<point> >& pointFaceSurfNormals,
2736  const List<List<point> >& pointFaceDisp,
2737  const List<List<point> >& pointFaceCentres,
2738  const labelListList& pointFacePatchID,
2739 
2740  vectorField& patchAttraction,
2741  List<pointConstraint>& patchConstraints
2742 ) const
2743 {
2744  const refinementFeatures& features = meshRefiner_.features();
2745 
2746  // Collect ordered attractions on feature edges
2747  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2748 
2749  // Per feature, per feature-edge a list of attraction points and their
2750  // originating vertex.
2751  List<List<DynamicList<point> > > edgeAttractors(features.size());
2752  List<List<DynamicList<pointConstraint> > > edgeConstraints
2753  (
2754  features.size()
2755  );
2756  forAll(features, featI)
2757  {
2758  label nFeatEdges = features[featI].edges().size();
2759  edgeAttractors[featI].setSize(nFeatEdges);
2760  edgeConstraints[featI].setSize(nFeatEdges);
2761  }
2762 
2763  // Per feature, per feature-point the pp point that is attracted to it.
2764  // This list is only used to subset the feature-points that are actually
2765  // used.
2766  List<labelList> pointAttractor(features.size());
2768  forAll(features, featI)
2769  {
2770  label nFeatPoints = features[featI].points().size();
2771  pointAttractor[featI].setSize(nFeatPoints, -1);
2772  pointConstraints[featI].setSize(nFeatPoints);
2773  }
2774 
2775  // Reverse: from pp point to nearest feature
2776  vectorField rawPatchAttraction(pp.nPoints(), vector::zero);
2777  List<pointConstraint> rawPatchConstraints(pp.nPoints());
2778 
2779  determineFeatures
2780  (
2781  iter,
2782  featureCos,
2783  multiRegionFeatureSnap,
2784 
2785  pp,
2786  snapDist, // per point max distance and nearest surface
2787  nearestDisp,
2788 
2789  pointFaceSurfNormals, // per face nearest surface
2790  pointFaceDisp,
2791  pointFaceCentres,
2792  pointFacePatchID,
2793 
2794  // Feature-point to pp point
2795  pointAttractor,
2797  // Feature-edge to pp point
2798  edgeAttractors,
2799  edgeConstraints,
2800  // pp point to nearest feature
2801  rawPatchAttraction,
2802  rawPatchConstraints
2803  );
2804 
2805 
2806 
2807  // Baffle handling
2808  // ~~~~~~~~~~~~~~~
2809  // Override pointAttractor, edgeAttractor, rawPatchAttration etc. to
2810  // implement 'baffle' handling.
2811  // Baffle: the mesh pp point originates from a loose standing
2812  // baffle.
2813  // Sampling the surface with the surrounding face-centres only picks up
2814  // a single triangle normal so above determineFeatures will not have
2815  // detected anything. So explicitly pick up feature edges on the pp
2816  // (after duplicating points & smoothing so will already have been
2817  // expanded) and match these to the features.
2818  determineBaffleFeatures
2819  (
2820  iter,
2821  featureCos,
2822 
2823  pp,
2824  snapDist,
2825 
2826  // Feature-point to pp point
2827  pointAttractor,
2829  // Feature-edge to pp point
2830  edgeAttractors,
2831  edgeConstraints,
2832  // pp point to nearest feature
2833  rawPatchAttraction,
2834  rawPatchConstraints
2835  );
2836 
2837 
2838 
2839  // Reverse lookup: Find nearest mesh point to feature edge
2840  // ~~~~~~~~~~~~~~~~----------------~~~~~~~~~~~~~~~~~~~~~~~
2841  // go through all edgeAttractors and find the nearest point on pp
2842 
2843  reverseAttractMeshPoints
2844  (
2845  iter,
2846 
2847  pp,
2848  snapDist,
2849 
2850  // Feature-point to pp point
2851  pointAttractor,
2853  // Feature-edge to pp point
2854  edgeAttractors,
2855  edgeConstraints,
2856 
2857  // Estimated feature point
2858  rawPatchAttraction,
2859  rawPatchConstraints,
2860 
2861  // pp point to nearest feature
2862  patchAttraction,
2863  patchConstraints
2864  );
2865 
2866 
2867  // Dump
2868  if (debug&meshRefinement::ATTRACTION)
2869  {
2870  OBJstream featureEdgeStr
2871  (
2872  meshRefiner_.mesh().time().path()
2873  / "edgeAttractors_" + name(iter) + ".obj"
2874  );
2875  Info<< "Dumping feature-edge attraction to "
2876  << featureEdgeStr.name() << endl;
2877 
2878  OBJstream featurePointStr
2879  (
2880  meshRefiner_.mesh().time().path()
2881  / "pointAttractors_" + name(iter) + ".obj"
2882  );
2883  Info<< "Dumping feature-point attraction to "
2884  << featurePointStr.name() << endl;
2885 
2886  forAll(patchConstraints, pointI)
2887  {
2888  const point& pt = pp.localPoints()[pointI];
2889  const vector& attr = patchAttraction[pointI];
2890 
2891  if (patchConstraints[pointI].first() == 2)
2892  {
2893  featureEdgeStr.write(linePointRef(pt, pt+attr));
2894  }
2895  else if (patchConstraints[pointI].first() == 3)
2896  {
2897  featurePointStr.write(linePointRef(pt, pt+attr));
2898  }
2899  }
2900  }
2901 
2902 
2903  if (avoidSnapProblems)
2904  {
2905  //MEJ: any faces that have multi-patch points only keep the multi-patch
2906  // points. The other points on the face will be dragged along
2907  // (hopefully)
2908  if (multiRegionFeatureSnap)
2909  {
2910  releasePointsNextToMultiPatch
2911  (
2912  iter,
2913  featureCos,
2914 
2915  pp,
2916  snapDist,
2917 
2918  pointFaceCentres,
2919  pointFacePatchID,
2920 
2921  rawPatchAttraction,
2922  rawPatchConstraints,
2923 
2924  patchAttraction,
2925  patchConstraints
2926  );
2927  }
2928 
2929 
2930  // Snap edges to feature edges
2931  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
2932  // Walk existing edges and snap remaining ones (that are marked as
2933  // feature edges in rawPatchConstraints)
2934 
2935  stringFeatureEdges
2936  (
2937  iter,
2938  featureCos,
2939 
2940  pp,
2941  snapDist,
2942 
2943  rawPatchAttraction,
2944  rawPatchConstraints,
2945 
2946  patchAttraction,
2947  patchConstraints
2948  );
2949 
2950 
2951  // Avoid diagonal attraction
2952  // ~~~~~~~~~~~~~~~~~~~~~~~~~
2953  // Attract one of the non-diagonal points.
2954 
2955  avoidDiagonalAttraction
2956  (
2957  iter,
2958  featureCos,
2959  pp,
2960  patchAttraction,
2961  patchConstraints
2962  );
2963  }
2964 
2965  if (debug&meshRefinement::ATTRACTION)
2966  {
2967  dumpMove
2968  (
2969  meshRefiner_.mesh().time().path()
2970  / "patchAttraction_" + name(iter) + ".obj",
2971  pp.localPoints(),
2972  pp.localPoints() + patchAttraction
2973  );
2974  }
2975 }
2976 
2977 
2978 // Correct for squeezing of face
2979 void Foam::autoSnapDriver::preventFaceSqueeze
2980 (
2981  const label iter,
2982  const scalar featureCos,
2983 
2984  const indirectPrimitivePatch& pp,
2985  const scalarField& snapDist,
2986 
2987  vectorField& patchAttraction,
2988  List<pointConstraint>& patchConstraints
2989 ) const
2990 {
2992  face singleF;
2993  forAll(pp.localFaces(), faceI)
2994  {
2995  const face& f = pp.localFaces()[faceI];
2996 
2997  if (f.size() != points.size())
2998  {
2999  points.setSize(f.size());
3000  singleF.setSize(f.size());
3001  for (label i = 0; i < f.size(); i++)
3002  {
3003  singleF[i] = i;
3004  }
3005  }
3006  label nConstraints = 0;
3007  forAll(f, fp)
3008  {
3009  label pointI = f[fp];
3010  const point& pt = pp.localPoints()[pointI];
3011 
3012  if (patchConstraints[pointI].first() > 1)
3013  {
3014  points[fp] = pt + patchAttraction[pointI];
3015  nConstraints++;
3016  }
3017  else
3018  {
3019  points[fp] = pt;
3020  }
3021  }
3022 
3023  if (nConstraints == f.size())
3024  {
3025  scalar oldArea = f.mag(pp.localPoints());
3026  scalar newArea = singleF.mag(points);
3027  if (newArea < 0.1*oldArea)
3028  {
3029  // For now remove the point with largest distance
3030  label maxFp = -1;
3031  scalar maxS = -1;
3032  forAll(f, fp)
3033  {
3034  scalar s = magSqr(patchAttraction[f[fp]]);
3035  if (s > maxS)
3036  {
3037  maxS = s;
3038  maxFp = fp;
3039  }
3040  }
3041  if (maxFp != -1)
3042  {
3043  label pointI = f[maxFp];
3044  // Lower attraction on pointI
3045  patchAttraction[pointI] *= 0.5;
3046  }
3047  }
3048  }
3049  }
3050 }
3051 
3052 
3053 Foam::vectorField Foam::autoSnapDriver::calcNearestSurfaceFeature
3054 (
3055  const snapParameters& snapParams,
3056  const bool avoidSnapProblems,
3057  const label iter,
3058  const scalar featureCos,
3059  const scalar featureAttract,
3060  const scalarField& snapDist,
3061  const vectorField& nearestDisp,
3062  motionSmoother& meshMover,
3063  vectorField& patchAttraction,
3064  List<pointConstraint>& patchConstraints
3065 ) const
3066 {
3067  const Switch implicitFeatureAttraction = snapParams.implicitFeatureSnap();
3068  const Switch explicitFeatureAttraction = snapParams.explicitFeatureSnap();
3069  const Switch multiRegionFeatureSnap = snapParams.multiRegionFeatureSnap();
3070 
3071  Info<< "Overriding displacement on features :" << nl
3072  << " implicit features : " << implicitFeatureAttraction << nl
3073  << " explicit features : " << explicitFeatureAttraction << nl
3074  << " multi-patch features : " << multiRegionFeatureSnap << nl
3075  << endl;
3076 
3077 
3078  const indirectPrimitivePatch& pp = meshMover.patch();
3079  const pointField& localPoints = pp.localPoints();
3080  const fvMesh& mesh = meshRefiner_.mesh();
3081 
3082 
3083 
3084  // Per point, per surrounding face:
3085  // - faceSurfaceNormal
3086  // - faceDisp
3087  // - faceCentres
3088  List<List<point> > pointFaceSurfNormals;
3089  List<List<point> > pointFaceDisp;
3090  List<List<point> > pointFaceCentres;
3091  List<labelList> pointFacePatchID;
3092 
3093  {
3094  // Calculate attraction distance per face (from the attraction distance
3095  // per point)
3096  scalarField faceSnapDist(pp.size(), -GREAT);
3097  forAll(pp.localFaces(), faceI)
3098  {
3099  const face& f = pp.localFaces()[faceI];
3100  forAll(f, fp)
3101  {
3102  faceSnapDist[faceI] = max(faceSnapDist[faceI], snapDist[f[fp]]);
3103  }
3104  }
3105 
3106 
3107  // Displacement and orientation per pp face
3108  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3109 
3110  // vector from point on surface back to face centre
3111  vectorField faceDisp(pp.size(), vector::zero);
3112  // normal of surface at point on surface
3113  vectorField faceSurfaceNormal(pp.size(), vector::zero);
3114  labelList faceSurfaceGlobalRegion(pp.size(), -1);
3115  vectorField faceRotation(pp.size(), vector::zero);
3116 
3117  calcNearestFace
3118  (
3119  iter,
3120  pp,
3121  faceSnapDist,
3122  faceDisp,
3123  faceSurfaceNormal,
3124  faceSurfaceGlobalRegion,
3125  faceRotation
3126  );
3127 
3128 
3129  // Collect (possibly remote) per point data of all surrounding faces
3130  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3131  // - faceSurfaceNormal
3132  // - faceDisp
3133  // - faceCentres
3134  calcNearestFacePointProperties
3135  (
3136  iter,
3137  pp,
3138 
3139  faceDisp,
3140  faceSurfaceNormal,
3141  faceSurfaceGlobalRegion,
3142 
3143  pointFaceSurfNormals,
3144  pointFaceDisp,
3145  pointFaceCentres,
3146  pointFacePatchID
3147  );
3148  }
3149 
3150 
3151  // Start off with nearest point on surface
3152  vectorField patchDisp = nearestDisp;
3153 
3154 
3155  // Main calculation
3156  // ~~~~~~~~~~~~~~~~
3157  // This is the main intelligence which calculates per point the vector to
3158  // attract it to the nearest surface. There are lots of possibilities
3159  // here.
3160 
3161  // Nearest feature
3162  patchAttraction.setSize(localPoints.size());
3163  patchAttraction = vector::zero;
3164  // Constraints at feature
3165  patchConstraints.setSize(localPoints.size());
3166  patchConstraints = pointConstraint();
3167 
3168  if (implicitFeatureAttraction)
3169  {
3170  // Sample faces around each point and see if nearest surface normal
3171  // differs. Reconstruct a feature edge/point if possible and snap to
3172  // it.
3173  featureAttractionUsingReconstruction
3174  (
3175  iter,
3176  avoidSnapProblems,
3177  featureCos,
3178 
3179  pp,
3180  snapDist,
3181  nearestDisp,
3182 
3183  pointFaceSurfNormals,
3184  pointFaceDisp,
3185  pointFaceCentres,
3186  pointFacePatchID,
3187 
3188  patchAttraction,
3189  patchConstraints
3190  );
3191  }
3192 
3193  if (explicitFeatureAttraction)
3194  {
3195  // Sample faces around each point and see if nearest surface normal
3196  // differs. For those find the nearest real feature edge/point and
3197  // store the correspondence. Then loop over feature edge/point
3198  // and attract those nearest mesh point. (the first phase just is
3199  // a subsetting of candidate points, the second makes sure that only
3200  // one mesh point gets attracted per feature)
3201  featureAttractionUsingFeatureEdges
3202  (
3203  iter,
3204  avoidSnapProblems,
3205  featureCos,
3206  multiRegionFeatureSnap,
3207 
3208  pp,
3209  snapDist,
3210  nearestDisp,
3211 
3212  pointFaceSurfNormals,
3213  pointFaceDisp,
3214  pointFaceCentres,
3215  pointFacePatchID,
3216 
3217  patchAttraction,
3218  patchConstraints
3219  );
3220  }
3221 
3222  preventFaceSqueeze
3223  (
3224  iter,
3225  featureCos,
3226 
3227  pp,
3228  snapDist,
3229 
3230  patchAttraction,
3231  patchConstraints
3232  );
3233 
3234  //const PackedBoolList isMasterPoint(syncTools::getMasterPoints(mesh));
3235  const PackedBoolList isPatchMasterPoint
3236  (
3238  (
3239  mesh,
3240  pp.meshPoints()
3241  )
3242  );
3243  {
3244  vector avgPatchDisp = meshRefinement::gAverage
3245  (
3246  isPatchMasterPoint,
3247  patchDisp
3248  );
3249  vector avgPatchAttr = meshRefinement::gAverage
3250  (
3251  isPatchMasterPoint,
3252  patchAttraction
3253  );
3254 
3255  Info<< "Attraction:" << endl
3256  << " linear : max:" << gMaxMagSqr(patchDisp)
3257  << " avg:" << avgPatchDisp << endl
3258  << " feature : max:" << gMaxMagSqr(patchAttraction)
3259  << " avg:" << avgPatchAttr << endl;
3260  }
3261 
3262  // So now we have:
3263  // - patchDisp : point movement to go to nearest point on surface
3264  // (either direct or through interpolation of
3265  // face nearest)
3266  // - patchAttraction : direct attraction to features
3267  // - patchConstraints : type of features
3268 
3269  // Use any combination of patchDisp and direct feature attraction.
3270 
3271 
3272  // Mix with direct feature attraction
3273  forAll(patchConstraints, pointI)
3274  {
3275  if (patchConstraints[pointI].first() > 1)
3276  {
3277  patchDisp[pointI] =
3278  (1.0-featureAttract)*patchDisp[pointI]
3279  + featureAttract*patchAttraction[pointI];
3280  }
3281  }
3282 
3283 
3284 
3285  // Count
3286  {
3287  label nMasterPoints = 0;
3288  label nPlanar = 0;
3289  label nEdge = 0;
3290  label nPoint = 0;
3291 
3292  forAll(patchConstraints, pointI)
3293  {
3294  if (isPatchMasterPoint[pointI])
3295  {
3296  nMasterPoints++;
3297 
3298  if (patchConstraints[pointI].first() == 1)
3299  {
3300  nPlanar++;
3301  }
3302  else if (patchConstraints[pointI].first() == 2)
3303  {
3304  nEdge++;
3305  }
3306  else if (patchConstraints[pointI].first() == 3)
3307  {
3308  nPoint++;
3309  }
3310  }
3311  }
3312 
3313  reduce(nMasterPoints, sumOp<label>());
3314  reduce(nPlanar, sumOp<label>());
3315  reduce(nEdge, sumOp<label>());
3316  reduce(nPoint, sumOp<label>());
3317  Info<< "Feature analysis : total master points:"
3318  << nMasterPoints
3319  << " attraction to :" << nl
3320  << " feature point : " << nPoint << nl
3321  << " feature edge : " << nEdge << nl
3322  << " nearest surface : " << nPlanar << nl
3323  << " rest : " << nMasterPoints-nPoint-nEdge-nPlanar
3324  << nl
3325  << endl;
3326  }
3327 
3328 
3329  // Now we have the displacement per patch point to move onto the surface
3330  // Split into tangential and normal direction.
3331  // - start off with all non-constrained points following the constrained
3332  // ones since point normals not relevant.
3333  // - finish with only tangential component smoothed.
3334  // Note: tangential is most
3335  // likely to come purely from face-centre snapping, not face rotation.
3336  // Note: could use the constraints here (constraintTransformation())
3337  // but this is not necessarily accurate and we're smoothing to
3338  // get out of problems.
3339 
3340  if (featureAttract < 1-0.001)
3341  {
3342  //const PackedBoolList isMasterEdge(syncTools::getMasterEdges(mesh));
3343  const labelList meshEdges
3344  (
3345  pp.meshEdges(mesh.edges(), mesh.pointEdges())
3346  );
3347  const PackedBoolList isPatchMasterEdge
3348  (
3350  (
3351  mesh,
3352  meshEdges
3353  )
3354  );
3355 
3356  const vectorField pointNormals
3357  (
3359  (
3360  mesh,
3361  pp
3362  )
3363  );
3364 
3365 
3366  // 1. Smoothed all displacement
3367  vectorField smoothedPatchDisp = patchDisp;
3368  smoothAndConstrain
3369  (
3370  isPatchMasterEdge,
3371  pp,
3372  meshEdges,
3373  patchConstraints,
3374  smoothedPatchDisp
3375  );
3376 
3377 
3378  // 2. Smoothed tangential component
3379  vectorField tangPatchDisp = patchDisp;
3380  tangPatchDisp -= (pointNormals & patchDisp) * pointNormals;
3381  smoothAndConstrain
3382  (
3383  isPatchMasterEdge,
3384  pp,
3385  meshEdges,
3386  patchConstraints,
3387  tangPatchDisp
3388  );
3389 
3390  // Re-add normal component
3391  tangPatchDisp += (pointNormals & patchDisp) * pointNormals;
3392 
3393  if (debug&meshRefinement::ATTRACTION)
3394  {
3395  dumpMove
3396  (
3397  mesh.time().path()
3398  / "tangPatchDispConstrained_" + name(iter) + ".obj",
3399  pp.localPoints(),
3400  pp.localPoints() + tangPatchDisp
3401  );
3402  }
3403 
3404  patchDisp =
3405  (1.0-featureAttract)*smoothedPatchDisp
3406  + featureAttract*tangPatchDisp;
3407  }
3408 
3409 
3410  const scalar relax = featureAttract;
3411  patchDisp *= relax;
3412 
3413 
3414  // Points on zones in one domain but only present as point on other
3415  // will not do condition 2 on all. Sync explicitly.
3417  (
3418  mesh,
3419  pp.meshPoints(),
3420  patchDisp,
3421  minMagSqrEqOp<point>(), // combine op
3422  vector(GREAT, GREAT, GREAT) // null value (note: cant use VGREAT)
3423  );
3424 
3425  return patchDisp;
3426 }
3427 
3428 
3429 // ************************************************************************* //
const vectorField & faceAreas() const
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
dimensionedScalar sqrt(const dimensionedScalar &ds)
UEqn relax()
A List with indirect addressing.
Definition: IndirectList.H:102
const Type2 & second() const
Return second.
Definition: Tuple2.H:106
OFstream which keeps track of vertices.
Definition: OBJstream.H:53
const pointField & points
Simple random number generator.
Definition: Random.H:49
const faceZoneMesh & faceZones() const
Return face zone mesh.
Definition: polyMesh.H:463
cachedRandom rndGen(label(0),-1)
A 2-tuple for storing two objects of different types.
Definition: Tuple2.H:47
Default transformation behaviour for position.
static PackedBoolList getMasterFaces(const polyMesh &)
Get per face whether it is uncoupled or a master of a.
Definition: syncTools.C:156
Switch implicitFeatureSnap() const
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:78
Container for data on surfaces used for surface-driven refinement. Contains all the data about the le...
void findNearestPoint(const pointField &samples, const scalarField &nearestDistSqr, labelList &nearFeature, List< pointIndexHit > &nearInfo) const
Find nearest feature point. Sets.
label size() const
Return the number of elements in the PtrList.
Definition: PtrListI.H:32
label nFaces() const
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
Definition: PointIndexHit.H:53
virtual bool coupled() const
Return true if this patch is geometrically coupled (i.e. faces and.
Definition: polyPatch.H:310
vector point
Point is a vector.
Definition: point.H:41
An ordered pair of two objects of type <T> with first() and second() elements.
Definition: contiguous.H:49
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){gmvFile<< iter().position().x()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){gmvFile<< iter().position().y()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){gmvFile<< iter().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 ))
fileName path() const
Return path.
Definition: Time.H:281
labelList meshEdges(const edgeList &allEdges, const labelListList &cellEdges, const labelList &faceCells) const
Return labels of patch edges in the global edge list using.
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: List.H:61
Tuple2< scalar, label > nearInfo
Private class for finding nearest.
dimensioned< scalar > mag(const dimensioned< Type > &)
const edgeList & edges() const
Return mesh edges. Uses calcEdges.
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47
Default transformation behaviour.
const labelListList & pointEdges() const
labelList f(nPoints)
const labelListList & edgeFaces() const
Return edge-face addressing.
bool empty() const
Return true if the UList is empty (ie, size() is zero).
Definition: UListI.H:313
dimensioned< scalar > magSqr(const dimensioned< Type > &)
void reset(T *=0)
If object pointer already set, delete object and set to given.
Definition: autoPtrI.H:114
const Type & shapes() const
Reference to shape.
bool valid() const
Return true if the autoPtr valid (ie, the pointer is set).
Definition: autoPtrI.H:83
static T gAverage(const PackedBoolList &isMasterElem, const UList< T > &values)
Helper: calculate average.
void sortedOrder(const UList< T > &, labelList &order)
Generate the (stable) sort order for the list.
const labelListList & pointFaces() const
Return point-face addressing.
A subset of mesh faces organised as a primitive patch.
Definition: faceZone.H:64
scalar mag(const pointField &) const
Magnitude of face area.
Definition: faceI.H:97
static labelList getUnnamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of unnamed surfaces (surfaces without faceZoneName)
A bit-packed bool list.
void clear()
Clear the addressed list, i.e. set the size to zero.
Definition: DynamicListI.H:242
A class for handling words, derived from string.
Definition: word.H:59
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:59
Standard boundBox + extra functionality for use in octree.
Definition: treeBoundBox.H:75
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:76
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:310
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
label fcIndex(const label i) const
Return the forward circular index, i.e. the next index.
Definition: UListI.H:58
static labelList getNamedSurfaces(const PtrList< surfaceZonesInfo > &surfList)
Get indices of named surfaces (surfaces with faceZoneName)
point planePlaneIntersect(const plane &, const plane &) const
Return the cutting point between this plane and two other planes.
Definition: plane.C:420
PointIndexHit< point > pointIndexHit
Definition: pointIndexHit.H:42
static void syncEdgeList(const polyMesh &, List< T > &, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh edges.
A simple wrapper around bool so that it can be read as a word: true/false, on/off, yes/no, y/n, t/f, or none.
Definition: Switch.H:60
const Type1 & first() const
Return first.
Definition: Tuple2.H:94
void diag(pointPatchField< vector > &, const pointPatchField< tensor > &)
Accumulates point constraints through successive applications of the applyConstraint function...
messageStream Info
bool hit() const
Is there a hit.
dynamicFvMesh & mesh
Simple container to keep together snap specific information.
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:58
T & first()
Return the first element of the list.
Definition: UListI.H:117
label index() const
Return index.
label nPoints() const
Return number of points supporting patch faces.
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
Namespace for OpenFOAM.
const Point & hitPoint() const
Return hit point.
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:66
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:243
label n
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: HashTable.H:59
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:75
const Field< PointType > & localPoints() const
Return pointField of points in patch.
static const char nl
Definition: Ostream.H:260
const double e
Elementary charge.
Definition: doubleFloat.H:78
const Field< PointType > & faceCentres() const
Return face centres for patch.
void setSize(const label)
Reset size of List.
Definition: List.C:318
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
#define WarningIn(functionName)
Report a warning using Foam::Warning.
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1035
A List with indirect addressing.
Definition: fvMatrix.H:106
Foam::polyBoundaryMesh.
volScalarField & p
Definition: createFields.H:51
dimensionSet transform(const dimensionSet &)
Definition: dimensionSet.C:465
const labelList & boundaryPoints() const
Return list of boundary points,.
dimensioned< Type > max(const dimensioned< Type > &, const dimensioned< Type > &)
Switch explicitFeatureSnap() const
const labelList & meshPoints() const
Return labelList of mesh points in patch. They are constructed.
#define forAll(list, i)
Definition: UList.H:421
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
static PackedBoolList getMasterEdges(const polyMesh &mesh, const labelList &meshEdges)
Determine master edge for subset of edges. If coupled.
dimensionedScalar cos(const dimensionedScalar &ds)
label otherVertex(const label a) const
Given one vertex, return the other.
Definition: edgeI.H:103
static PackedBoolList getMasterPoints(const polyMesh &mesh, const labelList &meshPoints)
Determine master point for subset of points. If coupled.
Unit conversion functions.
const labelListList & pointFaces() const
const Field< PointType > & faceNormals() const
Return face normals for patch.
label globalRegion(const label surfI, const label regionI) const
From surface and region on surface to global region.
static void syncPointList(const polyMesh &, List< T > &, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh points.
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:421
const indirectPrimitivePatch & patch() const
Reference to patch.
#define FatalErrorIn(functionName)
Report an error message using Foam::FatalError.
Definition: error.H:314
Non-pointer based hierarchical recursive searching.
Definition: treeDataEdge.H:47
Encapsulates queries for features.
A list of faces which address into the list of points.
static tmp< pointField > pointNormals(const polyMesh &, const PrimitivePatch< Face, FaceList, PointField, PointType > &)
Return parallel consistent point normals for patches using mesh points.
Geometric class that creates a 2D plane and can return the intersection point between a line and the ...
Definition: plane.H:60
label start() const
Return start label of this patch in the polyMesh face list.
Definition: polyPatch.H:300
label nInternalFaces() const
const labelList & patchID() const
Per boundary face label the patch index.
void findNearestRegionEdge(const pointField &samples, const scalarField &nearestDistSqr, labelList &nearFeature, List< pointIndexHit > &nearInfo, vectorField &nearNormal) const
Find nearest point on nearest region edge. Sets.
error FatalError
const labelListList & pointEdges() const
Return point-edge addressing.
scalar y
const PtrList< surfaceZonesInfo > & surfZones() const
ray planeIntersect(const plane &) const
Return the cutting line between this plane and another.
Definition: plane.C:351
Given a displacement moves the mesh by scaling the displacement back until there are no more mesh err...
List< label > labelList
A List of labels.
Definition: labelList.H:56
static const Vector zero
Definition: Vector.H:80
scalar degToRad(const scalar deg)
Conversion from degrees to radians.
Pair< label > labelPair
Label pair.
Definition: labelPair.H:48
label findZoneID(const word &zoneName) const
Find zone index given a name.
Definition: ZoneMesh.C:354
const dimensionedScalar c
Speed of light in a vacuum.
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
const List< Face > & localFaces() const
Return patch faces addressing into local point list.
void append(const T &)
Append an element at the end of the list.
Definition: ListI.H:97
line< point, const point & > linePointRef
Line using referred points.
Definition: linePointRef.H:45
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1060
Info<< "Finished reading KIVA file"<< endl;cellShapeList cellShapes(nPoints);labelList cellZoning(nPoints,-1);const cellModel &hex=*(cellModeller::lookup("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
void findNearestEdge(const pointField &samples, const scalarField &nearestDistSqr, labelList &nearFeature, List< pointIndexHit > &nearInfo, vectorField &nearNormal) const
Find nearest point on nearest feature edge. Sets.
const labelList & surfaces() const
void applyConstraint(const vector &cd)
Apply and accumulate the effect of the given constraint direction.
dimensionedSymmTensor sqr(const dimensionedVector &dv)
label nEdges() const
Return number of edges in patch.
const Point & missPoint() const
Return miss point.
Switch multiRegionFeatureSnap() const
Application of (multi-)patch point contraints.
An auto-pointer similar to the STL auto_ptr but with automatic casting to a reference to the type and...
Definition: PtrList.H:117
Holds (reference to) pointField. Encapsulation of data needed for octree searches. Used for searching for nearest point. No bounding boxes around points. Only overlaps and calcNearest are implemented, rest makes little sense.
Definition: treeDataPoint.H:59
void findNearestRegion(const labelList &surfacesToTest, const pointField &samples, const scalarField &nearestDistSqr, labelList &hitSurface, labelList &hitRegion) const
Find nearest point on surfaces. Return surface and region on.
bool isInternalFace(const label faceIndex) const
Return true if given face label is internal to the mesh.
const vectorField & faceCentres() const
static const label labelMax
Definition: label.H:62
Type gMaxMagSqr(const UList< Type > &f, const label comm)
A direction and a reference point.
Definition: plane.H:73
void operator()(List< T > &x, const List< T > &y) const