conformalVoronoiMeshConformToSurface.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2012-2016 OpenFOAM Foundation
9  Copyright (C) 2020 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "conformalVoronoiMesh.H"
31 #include "vectorTools.H"
32 #include "indexedCellChecks.H"
33 #include "IOmanip.H"
34 #include "OBJstream.H"
35 
36 using namespace Foam::vectorTools;
37 
38 const Foam::scalar Foam::conformalVoronoiMesh::searchConeAngle
39  = Foam::cos(degToRad(30.0));
40 
41 const Foam::scalar Foam::conformalVoronoiMesh::searchAngleOppositeSurface
42  = Foam::cos(degToRad(150.0));
43 
44 
45 // * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
46 
47 void Foam::conformalVoronoiMesh::conformToSurface()
48 {
49  this->resetCellCount();
50  // Index the cells
51  for
52  (
53  Delaunay::Finite_cells_iterator cit = finite_cells_begin();
54  cit != finite_cells_end();
55  ++cit
56  )
57  {
58  cit->cellIndex() = Cb::ctUnassigned;
59  }
60 
61  if (!reconformToSurface())
62  {
63  // Reinsert stored surface conformation
64  reinsertSurfaceConformation();
65 
66  if (Pstream::parRun())
67  {
68  sync(decomposition().procBounds());
69  }
70  }
71  else
72  {
73  ptPairs_.clear();
74 
75  // Rebuild, insert and store new surface conformation
76  buildSurfaceConformation();
77 
78  if (distributeBackground(*this))
79  {
80  if (Pstream::parRun())
81  {
82  sync(decomposition().procBounds());
83  }
84  }
85 
86  // Do not store the surface conformation until after it has been
87  // (potentially) redistributed.
88  storeSurfaceConformation();
89  }
90 
91  // reportSurfaceConformationQuality();
92 }
93 
94 
95 bool Foam::conformalVoronoiMesh::reconformToSurface() const
96 {
97  if
98  (
99  runTime_.timeIndex()
100  % foamyHexMeshControls().surfaceConformationRebuildFrequency() == 0
101  )
102  {
103  return true;
104  }
105 
106  return false;
107 }
108 
109 
110 // TODO: Investigate topological tests
111 Foam::label Foam::conformalVoronoiMesh::findVerticesNearBoundaries()
112 {
113  label countNearBoundaryVertices = 0;
114 
115  for
116  (
117  Delaunay::Finite_facets_iterator fit = finite_facets_begin();
118  fit != finite_facets_end();
119  ++fit
120  )
121  {
122  Cell_handle c1 = fit->first;
123  Cell_handle c2 = fit->first->neighbor(fit->second);
124 
125  if (is_infinite(c1) || is_infinite(c2))
126  {
127  continue;
128  }
129 
130  pointFromPoint dE0 = c1->dual();
131  pointFromPoint dE1 = c2->dual();
132 
133  if (!geometryToConformTo_.findSurfaceAnyIntersection(dE0, dE1))
134  {
135  continue;
136  }
137 
138  for (label celli = 0; celli < 4; ++celli)
139  {
140  Vertex_handle v = c1->vertex(celli);
141 
142  if
143  (
144  !is_infinite(v)
145  && v->internalPoint()
146  && fit->second != celli
147  )
148  {
149  v->setNearBoundary();
150  }
151  }
152 
153  for (label celli = 0; celli < 4; ++celli)
154  {
155  Vertex_handle v = c2->vertex(celli);
156 
157  if
158  (
159  !is_infinite(v)
160  && v->internalPoint()
161  && fit->second != celli
162  )
163  {
164  v->setNearBoundary();
165  }
166  }
167  }
168 
169  for
170  (
171  Delaunay::Finite_vertices_iterator vit = finite_vertices_begin();
172  vit != finite_vertices_end();
173  ++vit
174  )
175  {
176  if (vit->nearBoundary())
177  {
178  countNearBoundaryVertices++;
179  }
180  }
181 
182  // Geometric test.
183 // for
184 // (
185 // Delaunay::Finite_vertices_iterator vit = finite_vertices_begin();
186 // vit != finite_vertices_end();
187 // ++vit
188 // )
189 // {
190 // if (vit->internalPoint() && !vit->nearBoundary())
191 // {
192 // pointFromPoint pt = topoint(vit->point());
193 //
194 // const scalar range = sqr
195 // (
196 // foamyHexMeshControls().nearBoundaryDistanceCoeff()
197 // *targetCellSize(pt)
198 // );
199 //
200 // pointIndexHit pHit;
201 // label hitSurface;
202 //
203 // geometryToConformTo_.findSurfaceNearest
204 // (
205 // pt,
206 // range,
207 // pHit,
208 // hitSurface
209 // );
210 //
211 // if (pHit.hit())
212 // {
213 // vit->setNearBoundary();
214 // countNearBoundaryVertices++;
215 // }
216 // }
217 // }
218 
219  return countNearBoundaryVertices;
220 }
221 
222 
223 void Foam::conformalVoronoiMesh::buildSurfaceConformation()
224 {
225  timeCheck("Start buildSurfaceConformation");
226 
227  Info<< nl
228  << "Rebuilding surface conformation for more iterations"
229  << endl;
230 
231  existingEdgeLocations_.clearStorage();
232  existingSurfacePtLocations_.clearStorage();
233 
234  buildEdgeLocationTree(existingEdgeLocations_);
235  buildSurfacePtLocationTree(existingSurfacePtLocations_);
236 
237  label initialTotalHits = 0;
238 
239  // Surface protrusion conformation is done in two steps.
240  // 1. the dual edges (of all internal vertices) can stretch to
241  // 'infinity' so any intersection would be badly behaved. So
242  // just find the nearest point on the geometry and insert point
243  // pairs.
244  // Now most of the surface conformation will be done with some
245  // residual protrusions / incursions.
246  // 2. find any segments of dual edges outside the geometry. Shoot
247  // ray from Delaunay vertex to middle of this segment and introduce
248  // point pairs. This will handle e.g.
249 
250  // protruding section of face:
251  //
252  // internal
253  // \ /
254  // -+-----------+-- boundary
255  // \ /
256  // --------
257  //
258  // Shoot ray and find intersection with outside segment (x) and
259  // introduce point pair (..)
260  //
261  // |
262  // \ . /
263  // -+-----|-----+-- boundary
264  // \ . /
265  // ---x----
266 
267  // Find vertices near boundaries to speed up subsequent checks.
268  label countNearBoundaryVertices = findVerticesNearBoundaries();
269 
270  Info<< " Vertices marked as being near a boundary: "
271  << returnReduce(countNearBoundaryVertices, sumOp<label>())
272  << " (estimated)" << endl;
273 
274  timeCheck("After set near boundary");
275 
276  const scalar edgeSearchDistCoeffSqr =
277  foamyHexMeshControls().edgeSearchDistCoeffSqr();
278 
279  const scalar surfacePtReplaceDistCoeffSqr =
280  foamyHexMeshControls().surfacePtReplaceDistCoeffSqr();
281 
282  const label AtoV = label(6/Foam::pow(scalar(number_of_vertices()), 3));
283 
284  // Initial surface protrusion conformation - nearest surface point
285  {
286  pointIndexHitAndFeatureDynList featureEdgeHits(AtoV/4);
287  pointIndexHitAndFeatureDynList surfaceHits(AtoV);
288  DynamicList<label> edgeToTreeShape(AtoV/4);
289  DynamicList<label> surfaceToTreeShape(AtoV);
290 
291  Map<scalar> surfacePtToEdgePtDist(AtoV/4);
292 
293  for
294  (
295  Delaunay::Finite_vertices_iterator vit = finite_vertices_begin();
296  vit != finite_vertices_end();
297  vit++
298  )
299  {
300  if (vit->nearBoundary())
301  {
302  pointIndexHitAndFeatureDynList surfaceIntersections(AtoV);
303 
304  if
305  (
306  dualCellSurfaceAllIntersections
307  (
308  vit,
309  surfaceIntersections
310  )
311  )
312  {
313  // meshTools::writeOBJ(Pout, vert);
314  // meshTools::writeOBJ(Pout, surfHit.hitPoint());
315  // Pout<< "l cr0 cr1" << endl;
316 
317  addSurfaceAndEdgeHits
318  (
319  topoint(vit->point()),
320  surfaceIntersections,
321  surfacePtReplaceDistCoeffSqr,
322  edgeSearchDistCoeffSqr,
323  surfaceHits,
324  featureEdgeHits,
325  surfaceToTreeShape,
326  edgeToTreeShape,
327  surfacePtToEdgePtDist,
328  true
329  );
330  }
331  else
332  {
333  vit->setInternal();
334  countNearBoundaryVertices--;
335  }
336  }
337  }
338 
339  Info<< " Vertices marked as being near a boundary: "
340  << returnReduce(countNearBoundaryVertices, sumOp<label>())
341  << " (after dual surface intersection)" << endl;
342 
343  label nVerts = number_of_vertices();
344  label nSurfHits = surfaceHits.size();
345  label nFeatEdHits = featureEdgeHits.size();
346 
347  if (Pstream::parRun())
348  {
349  reduce(nVerts, sumOp<label>());
350  reduce(nSurfHits, sumOp<label>());
351  reduce(nFeatEdHits, sumOp<label>());
352  }
353 
354  Info<< nl << "Initial conformation" << nl
355  << " Number of vertices " << nVerts << nl
356  << " Number of surface hits " << nSurfHits << nl
357  << " Number of edge hits " << nFeatEdHits
358  << endl;
359 
360  // In parallel, synchronise the surface trees
361  if (Pstream::parRun())
362  {
363  synchroniseSurfaceTrees(surfaceToTreeShape, surfaceHits);
364  }
365 
366  DynamicList<Vb> pts(2*surfaceHits.size() + 3*featureEdgeHits.size());
367 
368  insertSurfacePointPairs
369  (
370  surfaceHits,
371  "surfaceConformationLocations_initial.obj",
372  pts
373  );
374 
375  // In parallel, synchronise the edge trees
376  if (Pstream::parRun())
377  {
378  synchroniseEdgeTrees(edgeToTreeShape, featureEdgeHits);
379  }
380 
381  insertEdgePointGroups
382  (
383  featureEdgeHits,
384  "edgeConformationLocations_initial.obj",
385  pts
386  );
387 
388  pts.shrink();
389 
390  Map<label> oldToNewIndices = insertPointPairs(pts, true, true);
391 
392  // Re-index the point pairs
393  ptPairs_.reIndex(oldToNewIndices);
394 
395  //writePointPairs("pointPairs_initial.obj");
396 
397  // Remove location from surface/edge tree
398 
399  timeCheck("After initial conformation");
400 
401  initialTotalHits = nSurfHits + nFeatEdHits;
402  }
403 
404  // Remember which vertices were referred to each processor so only updates
405  // are sent.
406  PtrList<labelPairHashSet> referralVertices(Pstream::nProcs());
407 
408  // Store the vertices that have been received and added from each processor
409  // already so that there is no attempt to add them more than once.
410  autoPtr<labelPairHashSet> receivedVertices;
411 
412  if (Pstream::parRun())
413  {
414  forAll(referralVertices, proci)
415  {
416  if (proci != Pstream::myProcNo())
417  {
418  referralVertices.set
419  (
420  proci,
421  new labelPairHashSet(number_of_vertices()/Pstream::nProcs())
422  );
423  }
424  }
425 
426  receivedVertices.reset
427  (
428  new labelPairHashSet(number_of_vertices()/Pstream::nProcs())
429  );
430 
431  // Build the parallel interface the initial surface conformation
432  sync
433  (
434  decomposition_().procBounds(),
435  referralVertices,
436  receivedVertices()
437  );
438  }
439 
440  label iterationNo = 0;
441 
442  label maxIterations = foamyHexMeshControls().maxConformationIterations();
443 
444  scalar iterationToInitialHitRatioLimit =
445  foamyHexMeshControls().iterationToInitialHitRatioLimit();
446 
447  label hitLimit = label(iterationToInitialHitRatioLimit*initialTotalHits);
448 
449  Info<< nl << "Stopping iterations when: " << nl
450  << " total number of hits drops below "
451  << iterationToInitialHitRatioLimit
452  << " of initial hits (" << hitLimit << ")" << nl
453  << " or " << nl
454  << " maximum number of iterations (" << maxIterations
455  << ") is reached"
456  << endl;
457 
458  // Set totalHits to a large enough positive value to enter the while loop on
459  // the first iteration
460  label totalHits = initialTotalHits;
461 
462  while
463  (
464  totalHits > 0
465  && totalHits >= hitLimit
466  && iterationNo < maxIterations
467  )
468  {
469  pointIndexHitAndFeatureDynList surfaceHits(0.5*AtoV);
470  pointIndexHitAndFeatureDynList featureEdgeHits(0.25*AtoV);
471  DynamicList<label> surfaceToTreeShape(AtoV/2);
472  DynamicList<label> edgeToTreeShape(AtoV/4);
473 
474  Map<scalar> surfacePtToEdgePtDist;
475 
476  for
477  (
478  Delaunay::Finite_vertices_iterator vit = finite_vertices_begin();
479  vit != finite_vertices_end();
480  ++vit
481  )
482  {
483  // The initial surface conformation has already identified the
484  // nearBoundary set of vertices. Previously inserted boundary
485  // points and referred internal vertices from other processors can
486  // also generate protrusions and must be assessed too.
487  if
488  (
489  vit->nearBoundary()
490  || vit->internalBoundaryPoint()
491  || (vit->internalOrBoundaryPoint() && vit->referred())
492  )
493  {
494  pointIndexHitAndFeatureDynList surfaceIntersections(0.5*AtoV);
495 
496  pointIndexHit surfHit;
497  label hitSurface;
498 
499  // Find segments of dual face outside the geometry and find the
500  // the middle of this
501  dualCellLargestSurfaceProtrusion(vit, surfHit, hitSurface);
502 
503  if (surfHit.hit())
504  {
505  surfaceIntersections.append
506  (
507  pointIndexHitAndFeature(surfHit, hitSurface)
508  );
509 
510  addSurfaceAndEdgeHits
511  (
512  topoint(vit->point()),
513  surfaceIntersections,
514  surfacePtReplaceDistCoeffSqr,
515  edgeSearchDistCoeffSqr,
516  surfaceHits,
517  featureEdgeHits,
518  surfaceToTreeShape,
519  edgeToTreeShape,
520  surfacePtToEdgePtDist,
521  false
522  );
523  }
524  else
525  {
526  // No surface hit detected so if internal then don't check
527  // again
528  if (vit->nearBoundary())
529  {
530  vit->setInternal();
531  }
532  }
533  }
534  else if
535  (
536  vit->externalBoundaryPoint()
537  || (vit->externalBoundaryPoint() && vit->referred())
538  )
539  {
540  pointIndexHitAndFeatureDynList surfaceIntersections(0.5*AtoV);
541 
542  pointIndexHit surfHit;
543  label hitSurface;
544 
545  // Detect slave (external vertices) whose dual face incurs
546  // into nearby (other than originating) geometry
547  dualCellLargestSurfaceIncursion(vit, surfHit, hitSurface);
548 
549  if (surfHit.hit())
550  {
551  surfaceIntersections.append
552  (
553  pointIndexHitAndFeature(surfHit, hitSurface)
554  );
555 
556  addSurfaceAndEdgeHits
557  (
558  topoint(vit->point()),
559  surfaceIntersections,
560  surfacePtReplaceDistCoeffSqr,
561  edgeSearchDistCoeffSqr,
562  surfaceHits,
563  featureEdgeHits,
564  surfaceToTreeShape,
565  edgeToTreeShape,
566  surfacePtToEdgePtDist,
567  false
568  );
569  }
570  }
571  }
572 
573  label nVerts = number_of_vertices();
574  label nSurfHits = surfaceHits.size();
575  label nFeatEdHits = featureEdgeHits.size();
576 
577  if (Pstream::parRun())
578  {
579  reduce(nVerts, sumOp<label>());
580  reduce(nSurfHits, sumOp<label>());
581  reduce(nFeatEdHits, sumOp<label>());
582  }
583 
584  Info<< nl << "Conformation iteration " << iterationNo << nl
585  << " Number of vertices " << nVerts << nl
586  << " Number of surface hits " << nSurfHits << nl
587  << " Number of edge hits " << nFeatEdHits
588  << endl;
589 
590  totalHits = nSurfHits + nFeatEdHits;
591 
592  label nNotInserted = 0;
593 
594  if (totalHits > 0)
595  {
596  // In parallel, synchronise the surface trees
597  if (Pstream::parRun())
598  {
599  nNotInserted +=
600  synchroniseSurfaceTrees(surfaceToTreeShape, surfaceHits);
601  }
602 
603  DynamicList<Vb> pts
604  (
605  2*surfaceHits.size() + 3*featureEdgeHits.size()
606  );
607 
608  insertSurfacePointPairs
609  (
610  surfaceHits,
611  "surfaceConformationLocations_" + name(iterationNo) + ".obj",
612  pts
613  );
614 
615  // In parallel, synchronise the edge trees
616  if (Pstream::parRun())
617  {
618  nNotInserted +=
619  synchroniseEdgeTrees(edgeToTreeShape, featureEdgeHits);
620  }
621 
622  insertEdgePointGroups
623  (
624  featureEdgeHits,
625  "edgeConformationLocations_" + name(iterationNo) + ".obj",
626  pts
627  );
628 
629  pts.shrink();
630 
631  Map<label> oldToNewIndices = insertPointPairs(pts, true, true);
632 
633  // Reindex the point pairs
634  ptPairs_.reIndex(oldToNewIndices);
635 
636  //writePointPairs("pointPairs_" + name(iterationNo) + ".obj");
637 
638  if (Pstream::parRun())
639  {
640  sync
641  (
642  decomposition_().procBounds(),
643  referralVertices,
644  receivedVertices()
645  );
646  }
647  }
648 
649  timeCheck("Conformation iteration " + name(iterationNo));
650 
651  iterationNo++;
652 
653  if (iterationNo == maxIterations)
654  {
656  << "Maximum surface conformation iterations ("
657  << maxIterations << ") reached." << endl;
658  }
659 
660  if (totalHits <= nNotInserted)
661  {
662  Info<< nl << "Total hits (" << totalHits
663  << ") less than number of failed insertions (" << nNotInserted
664  << "), stopping iterations" << endl;
665  break;
666  }
667 
668  if (totalHits < hitLimit)
669  {
670  Info<< nl << "Total hits (" << totalHits
671  << ") less than limit (" << hitLimit
672  << "), stopping iterations" << endl;
673  }
674  }
675 
676  edgeLocationTreePtr_.clear();
677  surfacePtLocationTreePtr_.clear();
678 }
679 
680 
681 Foam::label Foam::conformalVoronoiMesh::synchroniseSurfaceTrees
682 (
683  const DynamicList<label>& surfaceToTreeShape,
684  pointIndexHitAndFeatureList& surfaceHits
685 )
686 {
687  Info<< " Surface tree synchronisation" << endl;
688 
689  pointIndexHitAndFeatureDynList synchronisedSurfLocations
690  (
691  surfaceHits.size()
692  );
693 
694  List<pointIndexHitAndFeatureDynList> procSurfLocations(Pstream::nProcs());
695 
696  procSurfLocations[Pstream::myProcNo()] = surfaceHits;
697 
698  Pstream::gatherList(procSurfLocations);
699  Pstream::scatterList(procSurfLocations);
700 
701  List<labelHashSet> hits(Pstream::nProcs());
702 
703  label nStoppedInsertion = 0;
704 
705  // Do the nearness tests here
706  for (label proci = 0; proci < Pstream::nProcs(); ++proci)
707  {
708  // Skip own points
709  if (proci >= Pstream::myProcNo())
710  {
711  continue;
712  }
713 
714  const pointIndexHitAndFeatureList& otherSurfEdges =
715  procSurfLocations[proci];
716 
717  forAll(otherSurfEdges, peI)
718  {
719  const Foam::point& pt = otherSurfEdges[peI].first().hitPoint();
720 
721  pointIndexHit nearest;
722  pointIsNearSurfaceLocation(pt, nearest);
723 
724  pointIndexHit nearestEdge;
725  pointIsNearFeatureEdgeLocation(pt, nearestEdge);
726 
727  if (nearest.hit() || nearestEdge.hit())
728  {
729  nStoppedInsertion++;
730 
731  if (!hits[proci].found(peI))
732  {
733  hits[proci].insert(peI);
734  }
735  }
736  }
737  }
738 
739  Pstream::listCombineGather(hits, plusEqOp<labelHashSet>());
741 
742  forAll(surfaceHits, eI)
743  {
744  if (!hits[Pstream::myProcNo()].found(eI))
745  {
746  synchronisedSurfLocations.append(surfaceHits[eI]);
747  }
748  else
749  {
750  surfacePtLocationTreePtr_().remove(surfaceToTreeShape[eI]);
751  }
752  }
753 
754 // forAll(synchronisedSurfLocations, pI)
755 // {
756 // appendToSurfacePtTree
757 // (
758 // synchronisedSurfLocations[pI].first().hitPoint()
759 // );
760 // }
761 
762  const label nNotInserted = returnReduce(nStoppedInsertion, sumOp<label>());
763 
764  Info<< " Not inserting total of " << nNotInserted << " locations"
765  << endl;
766 
767  surfaceHits = synchronisedSurfLocations;
768 
769  return nNotInserted;
770 }
771 
772 
773 Foam::label Foam::conformalVoronoiMesh::synchroniseEdgeTrees
774 (
775  const DynamicList<label>& edgeToTreeShape,
776  pointIndexHitAndFeatureList& featureEdgeHits
777 )
778 {
779  Info<< " Edge tree synchronisation" << endl;
780 
781  pointIndexHitAndFeatureDynList synchronisedEdgeLocations
782  (
783  featureEdgeHits.size()
784  );
785 
786  List<pointIndexHitAndFeatureDynList> procEdgeLocations(Pstream::nProcs());
787 
788  procEdgeLocations[Pstream::myProcNo()] = featureEdgeHits;
789 
790  Pstream::gatherList(procEdgeLocations);
791  Pstream::scatterList(procEdgeLocations);
792 
793  List<labelHashSet> hits(Pstream::nProcs());
794 
795  label nStoppedInsertion = 0;
796 
797  // Do the nearness tests here
798  for (label proci = 0; proci < Pstream::nProcs(); ++proci)
799  {
800  // Skip own points
801  if (proci >= Pstream::myProcNo())
802  {
803  continue;
804  }
805 
806  pointIndexHitAndFeatureList& otherProcEdges = procEdgeLocations[proci];
807 
808  forAll(otherProcEdges, peI)
809  {
810  const Foam::point& pt = otherProcEdges[peI].first().hitPoint();
811 
812  pointIndexHit nearest;
813  pointIsNearFeatureEdgeLocation(pt, nearest);
814 
815  if (nearest.hit())
816  {
817 // Pout<< "Not inserting " << peI << " " << pt << " "
818 // << nearest.rawPoint() << " on proc " << proci
819 // << ", near edge = " << nearest
820 // << " near ftPt = "<< info
821 // << " " << featureEdgeExclusionDistanceSqr(pt)
822 // << endl;
823 
824  nStoppedInsertion++;
825 
826  if (!hits[proci].found(peI))
827  {
828  hits[proci].insert(peI);
829  }
830  }
831  }
832  }
833 
834  Pstream::listCombineGather(hits, plusEqOp<labelHashSet>());
836 
837  forAll(featureEdgeHits, eI)
838  {
839  if (!hits[Pstream::myProcNo()].found(eI))
840  {
841  synchronisedEdgeLocations.append(featureEdgeHits[eI]);
842  }
843  else
844  {
845  edgeLocationTreePtr_().remove(edgeToTreeShape[eI]);
846  }
847  }
848 
849 // forAll(synchronisedEdgeLocations, pI)
850 // {
851 // appendToEdgeLocationTree
852 // (
853 // synchronisedEdgeLocations[pI].first().hitPoint()
854 // );
855 // }
856 
857  const label nNotInserted = returnReduce(nStoppedInsertion, sumOp<label>());
858 
859  Info<< " Not inserting total of " << nNotInserted << " locations"
860  << endl;
861 
862  featureEdgeHits = synchronisedEdgeLocations;
863 
864  return nNotInserted;
865 }
866 
867 
868 bool Foam::conformalVoronoiMesh::surfaceLocationConformsToInside
869 (
870  const pointIndexHitAndFeature& info
871 ) const
872 {
873  if (info.first().hit())
874  {
875  vectorField norm(1);
876 
877  geometryToConformTo_.getNormal
878  (
879  info.second(),
880  List<pointIndexHit>(1, info.first()),
881  norm
882  );
883 
884  const vector& n = norm[0];
885 
886  const scalar ppDist = pointPairDistance(info.first().hitPoint());
887 
888  const Foam::point innerPoint = info.first().hitPoint() - ppDist*n;
889 
890  if (!geometryToConformTo_.inside(innerPoint))
891  {
892  return false;
893  }
894 
895  return true;
896  }
897 
898  return false;
899 }
900 
901 
902 bool Foam::conformalVoronoiMesh::dualCellSurfaceAnyIntersection
903 (
904  const Delaunay::Finite_vertices_iterator& vit
905 ) const
906 {
907  std::list<Facet> facets;
908  incident_facets(vit, std::back_inserter(facets));
909 
910  for
911  (
912  std::list<Facet>::iterator fit=facets.begin();
913  fit != facets.end();
914  ++fit
915  )
916  {
917  if
918  (
919  is_infinite(fit->first)
920  || is_infinite(fit->first->neighbor(fit->second))
921  || !fit->first->hasInternalPoint()
922  || !fit->first->neighbor(fit->second)->hasInternalPoint()
923  )
924  {
925  continue;
926  }
927 
928  Foam::point dE0 = fit->first->dual();
929  Foam::point dE1 = fit->first->neighbor(fit->second)->dual();
930 
931  if (Pstream::parRun())
932  {
933  Foam::point& a = dE0;
934  Foam::point& b = dE1;
935 
936  bool inProc = clipLineToProc(topoint(vit->point()), a, b);
937 
938  // Check for the edge passing through a surface
939  if
940  (
941  inProc
942  && geometryToConformTo_.findSurfaceAnyIntersection(a, b)
943  )
944  {
945  return true;
946  }
947  }
948  else
949  {
950  if (geometryToConformTo_.findSurfaceAnyIntersection(dE0, dE1))
951  {
952  return true;
953  }
954  }
955  }
956 
957  return false;
958 }
959 
960 
961 bool Foam::conformalVoronoiMesh::dualCellSurfaceAllIntersections
962 (
963  const Delaunay::Finite_vertices_iterator& vit,
964  pointIndexHitAndFeatureDynList& infoList
965 ) const
966 {
967  bool flagIntersection = false;
968 
969  std::list<Facet> facets;
970  incident_facets(vit, std::back_inserter(facets));
971 
972  for
973  (
974  std::list<Facet>::iterator fit = facets.begin();
975  fit != facets.end();
976  ++fit
977  )
978  {
979  if
980  (
981  is_infinite(fit->first)
982  || is_infinite(fit->first->neighbor(fit->second))
983  || !fit->first->hasInternalPoint()
984  || !fit->first->neighbor(fit->second)->hasInternalPoint()
985  )
986  {
987  continue;
988  }
989 
990  // Construct the dual edge and search for intersections of the edge
991  // with the surface
992  Foam::point dE0 = fit->first->dual();
993  Foam::point dE1 = fit->first->neighbor(fit->second)->dual();
994 
995  pointIndexHit infoIntersection;
996  label hitSurfaceIntersection = -1;
997 
998  if (Pstream::parRun())
999  {
1000  bool inProc = clipLineToProc(topoint(vit->point()), dE0, dE1);
1001 
1002  if (!inProc)
1003  {
1004  continue;
1005  }
1006  }
1007 
1008  geometryToConformTo_.findSurfaceNearestIntersection
1009  (
1010  dE0,
1011  dE1,
1012  infoIntersection,
1013  hitSurfaceIntersection
1014  );
1015 
1016  if (infoIntersection.hit())
1017  {
1018  vectorField norm(1);
1019 
1020  geometryToConformTo_.getNormal
1021  (
1022  hitSurfaceIntersection,
1023  List<pointIndexHit>(1, infoIntersection),
1024  norm
1025  );
1026 
1027  const vector& n = norm[0];
1028 
1029  pointFromPoint vertex = topoint(vit->point());
1030 
1031  const plane p(infoIntersection.hitPoint(), n);
1032 
1033  const plane::ray r(vertex, n);
1034 
1035  const scalar d = p.normalIntersect(r);
1036 
1037  Foam::point newPoint = vertex + d*n;
1038 
1039  pointIndexHitAndFeature info;
1040  geometryToConformTo_.findSurfaceNearest
1041  (
1042  newPoint,
1043  4.0*magSqr(newPoint - vertex),
1044  info.first(),
1045  info.second()
1046  );
1047 
1048  bool rejectPoint = false;
1049 
1050  if (!surfaceLocationConformsToInside(info))
1051  {
1052  rejectPoint = true;
1053  }
1054 
1055  if (!rejectPoint && info.first().hit())
1056  {
1057  if (!infoList.empty())
1058  {
1059  forAll(infoList, hitI)
1060  {
1061  // Reject point if the point is already added
1062  if
1063  (
1064  infoList[hitI].first().index()
1065  == info.first().index()
1066  )
1067  {
1068  rejectPoint = true;
1069  break;
1070  }
1071 
1072  const Foam::point& p
1073  = infoList[hitI].first().hitPoint();
1074 
1075  const scalar separationDistance =
1076  mag(p - info.first().hitPoint());
1077 
1078  const scalar minSepDist =
1079  sqr
1080  (
1081  foamyHexMeshControls().removalDistCoeff()
1082  *targetCellSize(p)
1083  );
1084 
1085  // Reject the point if it is too close to another
1086  // surface point.
1087  // Could merge the points?
1088  if (separationDistance < minSepDist)
1089  {
1090  rejectPoint = true;
1091  break;
1092  }
1093  }
1094  }
1095  }
1096 
1097  // The normal ray from the vertex will not always result in a hit
1098  // because another surface may be in the way.
1099  if (!rejectPoint && info.first().hit())
1100  {
1101  flagIntersection = true;
1102  infoList.append(info);
1103  }
1104  }
1105  }
1106 
1107  return flagIntersection;
1108 }
1109 
1110 
1111 bool Foam::conformalVoronoiMesh::clipLineToProc
1112 (
1113  const Foam::point& pt,
1114  Foam::point& a,
1115  Foam::point& b
1116 ) const
1117 {
1118  bool inProc = false;
1119 
1120  pointIndexHit findAnyIntersection = decomposition_().findLine(a, b);
1121 
1122  if (!findAnyIntersection.hit())
1123  {
1124  pointIndexHit info = decomposition_().findLine(a, pt);
1125 
1126  if (!info.hit())
1127  {
1128  inProc = true;
1129  }
1130  else
1131  {
1132  inProc = false;
1133  }
1134  }
1135  else
1136  {
1137  pointIndexHit info = decomposition_().findLine(a, pt);
1138 
1139  if (!info.hit())
1140  {
1141  inProc = true;
1142  b = findAnyIntersection.hitPoint();
1143  }
1144  else
1145  {
1146  inProc = true;
1147  a = findAnyIntersection.hitPoint();
1148  }
1149  }
1150 
1151  return inProc;
1152 }
1153 
1154 
1155 void Foam::conformalVoronoiMesh::dualCellLargestSurfaceProtrusion
1156 (
1157  const Delaunay::Finite_vertices_iterator& vit,
1158  pointIndexHit& surfHitLargest,
1159  label& hitSurfaceLargest
1160 ) const
1161 {
1162  // Set no-hit data
1163  surfHitLargest = pointIndexHit();
1164  hitSurfaceLargest = -1;
1165 
1166  std::list<Facet> facets;
1167  finite_incident_facets(vit, std::back_inserter(facets));
1168 
1169  pointFromPoint vert = topoint(vit->point());
1170 
1171  scalar maxProtrusionDistance = maxSurfaceProtrusion(vert);
1172 
1173  for
1174  (
1175  std::list<Facet>::iterator fit = facets.begin();
1176  fit != facets.end();
1177  ++fit
1178  )
1179  {
1180  Cell_handle c1 = fit->first;
1181  Cell_handle c2 = fit->first->neighbor(fit->second);
1182 
1183  if
1184  (
1185  is_infinite(c1) || is_infinite(c2)
1186  || (
1187  !c1->internalOrBoundaryDualVertex()
1188  || !c2->internalOrBoundaryDualVertex()
1189  )
1190  || !c1->real() || !c2->real()
1191  )
1192  {
1193  continue;
1194  }
1195 
1196 // Foam::point endPt = 0.5*(c1->dual() + c2->dual());
1197  Foam::point endPt = c1->dual();
1198 
1199  if (magSqr(vert - c1->dual()) < magSqr(vert - c2->dual()))
1200  {
1201  endPt = c2->dual();
1202  }
1203 
1204  if
1205  (
1206  magSqr(vert - endPt)
1207  > magSqr(geometryToConformTo().globalBounds().mag())
1208  )
1209  {
1210  continue;
1211  }
1212 
1213  pointIndexHit surfHit;
1214  label hitSurface;
1215 
1216  geometryToConformTo_.findSurfaceNearestIntersection
1217  (
1218  vert,
1219  endPt,
1220  surfHit,
1221  hitSurface
1222  );
1223 
1224  if (surfHit.hit())
1225  {
1226  vectorField norm(1);
1227 
1228  allGeometry_[hitSurface].getNormal
1229  (
1230  List<pointIndexHit>(1, surfHit),
1231  norm
1232  );
1233 
1234  const vector& n = norm[0];
1235 
1236  const scalar normalProtrusionDistance
1237  (
1238  (endPt - surfHit.hitPoint()) & n
1239  );
1240 
1241  if (normalProtrusionDistance > maxProtrusionDistance)
1242  {
1243  const plane p(surfHit.hitPoint(), n);
1244 
1245  const plane::ray r(endPt, -n);
1246 
1247  const scalar d = p.normalIntersect(r);
1248 
1249  Foam::point newPoint = endPt - d*n;
1250 
1251  pointIndexHitAndFeature info;
1252  geometryToConformTo_.findSurfaceNearest
1253  (
1254  newPoint,
1255  4.0*magSqr(newPoint - endPt),
1256  info.first(),
1257  info.second()
1258  );
1259 
1260  if (info.first().hit())
1261  {
1262  if
1263  (
1264  surfaceLocationConformsToInside
1265  (
1266  pointIndexHitAndFeature(info.first(), info.second())
1267  )
1268  )
1269  {
1270  surfHitLargest = info.first();
1271  hitSurfaceLargest = info.second();
1272 
1273  maxProtrusionDistance = normalProtrusionDistance;
1274  }
1275  }
1276  }
1277  }
1278  }
1279 
1280  // Relying on short-circuit evaluation to not call for hitPoint when this
1281  // is a miss
1282  if
1283  (
1284  surfHitLargest.hit()
1285  && (
1286  Pstream::parRun()
1287  && !decomposition().positionOnThisProcessor(surfHitLargest.hitPoint())
1288  )
1289  )
1290  {
1291  // A protrusion was identified, but not penetrating on this processor,
1292  // so set no-hit data and allow the other that should have this point
1293  // referred to generate it.
1294  surfHitLargest = pointIndexHit();
1295  hitSurfaceLargest = -1;
1296  }
1297 }
1298 
1299 
1300 void Foam::conformalVoronoiMesh::dualCellLargestSurfaceIncursion
1301 (
1302  const Delaunay::Finite_vertices_iterator& vit,
1303  pointIndexHit& surfHitLargest,
1304  label& hitSurfaceLargest
1305 ) const
1306 {
1307  // Set no-hit data
1308  surfHitLargest = pointIndexHit();
1309  hitSurfaceLargest = -1;
1310 
1311  std::list<Facet> facets;
1312  finite_incident_facets(vit, std::back_inserter(facets));
1313 
1314  pointFromPoint vert = topoint(vit->point());
1315 
1316  scalar minIncursionDistance = -maxSurfaceProtrusion(vert);
1317 
1318  for
1319  (
1320  std::list<Facet>::iterator fit = facets.begin();
1321  fit != facets.end();
1322  ++fit
1323  )
1324  {
1325  Cell_handle c1 = fit->first;
1326  Cell_handle c2 = fit->first->neighbor(fit->second);
1327 
1328  if
1329  (
1330  is_infinite(c1) || is_infinite(c2)
1331  || (
1332  !c1->internalOrBoundaryDualVertex()
1333  || !c2->internalOrBoundaryDualVertex()
1334  )
1335  || !c1->real() || !c2->real()
1336  )
1337  {
1338  continue;
1339  }
1340 
1341 // Foam::point endPt = 0.5*(c1->dual() + c2->dual());
1342  Foam::point endPt = c1->dual();
1343 
1344  if (magSqr(vert - c1->dual()) < magSqr(vert - c2->dual()))
1345  {
1346  endPt = c2->dual();
1347  }
1348 
1349  if
1350  (
1351  magSqr(vert - endPt)
1352  > magSqr(geometryToConformTo().globalBounds().mag())
1353  )
1354  {
1355  continue;
1356  }
1357 
1358  pointIndexHit surfHit;
1359  label hitSurface;
1360 
1361  geometryToConformTo_.findSurfaceNearestIntersection
1362  (
1363  vert,
1364  endPt,
1365  surfHit,
1366  hitSurface
1367  );
1368 
1369  if (surfHit.hit())
1370  {
1371  vectorField norm(1);
1372 
1373  allGeometry_[hitSurface].getNormal
1374  (
1375  List<pointIndexHit>(1, surfHit),
1376  norm
1377  );
1378 
1379  const vector& n = norm[0];
1380 
1381  scalar normalIncursionDistance
1382  (
1383  (endPt - surfHit.hitPoint()) & n
1384  );
1385 
1386  if (normalIncursionDistance < minIncursionDistance)
1387  {
1388  const plane p(surfHit.hitPoint(), n);
1389 
1390  const plane::ray r(endPt, n);
1391 
1392  const scalar d = p.normalIntersect(r);
1393 
1394  Foam::point newPoint = endPt + d*n;
1395 
1396  pointIndexHitAndFeature info;
1397  geometryToConformTo_.findSurfaceNearest
1398  (
1399  newPoint,
1400  4.0*magSqr(newPoint - endPt),
1401  info.first(),
1402  info.second()
1403  );
1404 
1405  if (info.first().hit())
1406  {
1407  if
1408  (
1409  surfaceLocationConformsToInside
1410  (
1411  pointIndexHitAndFeature(info.first(), info.second())
1412  )
1413  )
1414  {
1415  surfHitLargest = info.first();
1416  hitSurfaceLargest = info.second();
1417 
1418  minIncursionDistance = normalIncursionDistance;
1419  }
1420  }
1421  }
1422  }
1423  }
1424 
1425  // Relying on short-circuit evaluation to not call for hitPoint when this
1426  // is a miss
1427  if
1428  (
1429  surfHitLargest.hit()
1430  && (
1431  Pstream::parRun()
1432  && !decomposition().positionOnThisProcessor(surfHitLargest.hitPoint())
1433  )
1434  )
1435  {
1436  // A protrusion was identified, but not penetrating on this processor,
1437  // so set no-hit data and allow the other that should have this point
1438  // referred to generate it.
1439  surfHitLargest = pointIndexHit();
1440  hitSurfaceLargest = -1;
1441  }
1442 }
1443 
1444 
1445 void Foam::conformalVoronoiMesh::reportProcessorOccupancy()
1446 {
1447  for
1448  (
1449  Delaunay::Finite_vertices_iterator vit = finite_vertices_begin();
1450  vit != finite_vertices_end();
1451  vit++
1452  )
1453  {
1454  if (vit->real())
1455  {
1456  if
1457  (
1458  Pstream::parRun()
1459  && !decomposition().positionOnThisProcessor(topoint(vit->point()))
1460  )
1461  {
1462  Pout<< topoint(vit->point()) << " is not on this processor "
1463  << endl;
1464  }
1465  }
1466  }
1467 }
1468 
1469 
1470 //void Foam::conformalVoronoiMesh::reportSurfaceConformationQuality()
1471 //{
1472 // Info<< nl << "Check surface conformation quality" << endl;
1473 //
1474 // for
1475 // (
1476 // Delaunay::Finite_vertices_iterator vit = finite_vertices_begin();
1477 // vit != finite_vertices_end();
1478 // vit++
1479 // )
1480 // {
1481 // if (vit->internalOrBoundaryPoint())
1482 // {
1483 // Foam::point vert(topoint(vit->point()));
1484 // pointIndexHit surfHit;
1485 // label hitSurface;
1486 //
1487 // dualCellLargestSurfaceProtrusion(vit, surfHit, hitSurface);
1488 //
1489 // if (surfHit.hit())
1490 // {
1491 // Pout<< nl << "Residual penetration: " << nl
1492 // << vit->index() << nl
1493 // << vit->type() << nl
1494 // << vit->ppMaster() << nl
1495 // << "nearFeaturePt "
1496 // << nearFeaturePt(surfHit.hitPoint()) << nl
1497 // << vert << nl
1498 // << surfHit.hitPoint()
1499 // << endl;
1500 // }
1501 // }
1502 // }
1503 //
1504 // {
1505 // // Assess close surface points
1506 //
1507 // setVertexSizeAndAlignment();
1508 //
1509 // for
1510 // (
1511 // Delaunay::Finite_vertices_iterator vit = finite_vertices_begin();
1512 // vit != finite_vertices_end();
1513 // vit++
1514 // )
1515 // {
1516 // if (vit->ppMaster())
1517 // {
1518 // std::list<Vertex_handle> adjacentVertices;
1519 //
1520 // adjacent_vertices(vit, std::back_inserter(adjacentVertices));
1521 //
1522 // Foam::point pt = topoint(vit->point());
1523 //
1524 // // Pout<< nl << "vit: " << vit->index() << " "
1525 // // << topoint(vit->point())
1526 // // << endl;
1527 //
1528 // // Pout<< adjacentVertices.size() << endl;
1529 //
1530 // for
1531 // (
1532 // std::list<Vertex_handle>::iterator
1533 // avit = adjacentVertices.begin();
1534 // avit != adjacentVertices.end();
1535 // ++avit
1536 // )
1537 // {
1538 // Vertex_handle avh = *avit;
1539 //
1540 // // The lower indexed vertex will perform the assessment
1541 // if
1542 // (
1543 // avh->ppMaster()
1544 // && vit->index() < avh->index()
1545 // && vit->type() != avh->type()
1546 // )
1547 // {
1548 // scalar targetSize = 0.2*averageAnyCellSize(vit, avh);
1549 //
1550 // // Pout<< "diff " << mag(pt - topoint(avh->point()))
1551 // // << " " << targetSize << endl;
1552 //
1553 // if
1554 // (
1555 // magSqr(pt - topoint(avh->point()))
1556 // < sqr(targetSize)
1557 // )
1558 // {
1559 // Pout<< nl << "vit: " << vit->index() << " "
1560 // << topoint(vit->point())
1561 // << endl;
1562 //
1563 // Pout<< " adjacent too close: "
1564 // << avh->index() << " "
1565 // << topoint(avh->point())
1566 // << endl;
1567 // }
1568 // }
1569 // }
1570 // }
1571 // }
1572 // }
1573 //}
1574 
1575 void Foam::conformalVoronoiMesh::limitDisplacement
1576 (
1577  const Delaunay::Finite_vertices_iterator& vit,
1578  vector& displacement,
1579  label callCount
1580 ) const
1581 {
1582  callCount++;
1583 
1584  // Do not allow infinite recursion
1585  if (callCount > 7)
1586  {
1587  displacement = Zero;
1588  return;
1589  }
1590 
1591  pointFromPoint pt = topoint(vit->point());
1592  Foam::point dispPt = pt + displacement;
1593 
1594  bool limit = false;
1595 
1596  pointIndexHit surfHit;
1597  label hitSurface;
1598 
1599  if (!geometryToConformTo_.globalBounds().contains(dispPt))
1600  {
1601  // If dispPt is outside bounding box then displacement cuts boundary
1602  limit = true;
1603  }
1604  else if (geometryToConformTo_.findSurfaceAnyIntersection(pt, dispPt))
1605  {
1606  // Full surface penetration test
1607  limit = true;
1608  }
1609  else
1610  {
1611  // Testing if the displaced position is too close to the surface.
1612  // Within twice the local surface point pair insertion distance is
1613  // considered "too close"
1614 
1615  scalar searchDistanceSqr = sqr
1616  (
1617  2*vit->targetCellSize()
1618  *foamyHexMeshControls().pointPairDistanceCoeff()
1619  );
1620 
1621  geometryToConformTo_.findSurfaceNearest
1622  (
1623  dispPt,
1624  searchDistanceSqr,
1625  surfHit,
1626  hitSurface
1627  );
1628 
1629  if (surfHit.hit())
1630  {
1631  limit = true;
1632 
1633  if (magSqr(pt - surfHit.hitPoint()) <= searchDistanceSqr)
1634  {
1635  // Cannot limit displacement, point closer than tolerance
1636  displacement = Zero;
1637  return;
1638  }
1639  }
1640  }
1641 
1642  if (limit)
1643  {
1644  // Halve the displacement and call this function again. Will continue
1645  // recursively until the displacement is small enough.
1646 
1647  displacement *= 0.5;
1648 
1649  limitDisplacement(vit, displacement, callCount);
1650  }
1651 }
1652 
1653 
1654 Foam::scalar Foam::conformalVoronoiMesh::angleBetweenSurfacePoints
1655 (
1656  Foam::point pA,
1657  Foam::point pB
1658 ) const
1659 {
1660  pointIndexHit pAhit;
1661  label pAsurfaceHit = -1;
1662 
1663  const scalar searchDist = 5.0*targetCellSize(pA);
1664 
1665  geometryToConformTo_.findSurfaceNearest
1666  (
1667  pA,
1668  searchDist,
1669  pAhit,
1670  pAsurfaceHit
1671  );
1672 
1673  if (!pAhit.hit())
1674  {
1676  }
1677 
1678  vectorField norm(1);
1679 
1680  allGeometry_[pAsurfaceHit].getNormal
1681  (
1682  List<pointIndexHit>(1, pAhit),
1683  norm
1684  );
1685 
1686  const vector nA = norm[0];
1687 
1688  pointIndexHit pBhit;
1689  label pBsurfaceHit = -1;
1690 
1691  geometryToConformTo_.findSurfaceNearest
1692  (
1693  pB,
1694  searchDist,
1695  pBhit,
1696  pBsurfaceHit
1697  );
1698 
1699  if (!pBhit.hit())
1700  {
1702  }
1703 
1704  allGeometry_[pBsurfaceHit].getNormal
1705  (
1706  List<pointIndexHit>(1, pBhit),
1707  norm
1708  );
1709 
1710  const vector nB = norm[0];
1711 
1712  return vectorTools::cosPhi(nA, nB);
1713 }
1714 
1715 
1716 bool Foam::conformalVoronoiMesh::nearSurfacePoint
1717 (
1718  pointIndexHitAndFeature& pHit
1719 ) const
1720 {
1721  const Foam::point& pt = pHit.first().hitPoint();
1722 
1723  pointIndexHit closePoint;
1724  const bool closeToSurfacePt = pointIsNearSurfaceLocation(pt, closePoint);
1725 
1726  if
1727  (
1728  closeToSurfacePt
1729  && (
1730  magSqr(pt - closePoint.hitPoint())
1731  > sqr(pointPairDistance(pt))
1732  )
1733  )
1734  {
1735  const scalar cosAngle =
1736  angleBetweenSurfacePoints(pt, closePoint.hitPoint());
1737 
1738  // TODO: make this tolerance run-time selectable?
1739  if (cosAngle < searchAngleOppositeSurface)
1740  {
1741  pointIndexHit pCloseHit;
1742  label pCloseSurfaceHit = -1;
1743 
1744  const scalar searchDist = targetCellSize(closePoint.hitPoint());
1745 
1746  geometryToConformTo_.findSurfaceNearest
1747  (
1748  closePoint.hitPoint(),
1749  searchDist,
1750  pCloseHit,
1751  pCloseSurfaceHit
1752  );
1753 
1754  vectorField norm(1);
1755 
1756  allGeometry_[pCloseSurfaceHit].getNormal
1757  (
1758  List<pointIndexHit>(1, pCloseHit),
1759  norm
1760  );
1761 
1762  const vector& nA = norm[0];
1763 
1764  pointIndexHit oppositeHit;
1765  label oppositeSurfaceHit = -1;
1766 
1767  geometryToConformTo_.findSurfaceNearestIntersection
1768  (
1769  closePoint.hitPoint() + 0.5*pointPairDistance(pt)*nA,
1770  closePoint.hitPoint() + 5*targetCellSize(pt)*nA,
1771  oppositeHit,
1772  oppositeSurfaceHit
1773  );
1774 
1775  if (oppositeHit.hit())
1776  {
1777  // Replace point
1778  pHit.first() = oppositeHit;
1779  pHit.second() = oppositeSurfaceHit;
1780 
1781  return !closeToSurfacePt;
1782  }
1783  }
1784  }
1785 
1786  return closeToSurfacePt;
1787 }
1788 
1789 
1790 bool Foam::conformalVoronoiMesh::appendToSurfacePtTree
1791 (
1792  const Foam::point& pt
1793 ) const
1794 {
1795  label startIndex = existingSurfacePtLocations_.size();
1796 
1797  existingSurfacePtLocations_.append(pt);
1798 
1799  label endIndex = existingSurfacePtLocations_.size();
1800 
1801  return surfacePtLocationTreePtr_().insert(startIndex, endIndex);
1802 }
1803 
1804 
1805 bool Foam::conformalVoronoiMesh::appendToEdgeLocationTree
1806 (
1807  const Foam::point& pt
1808 ) const
1809 {
1810  label startIndex = existingEdgeLocations_.size();
1811 
1812  existingEdgeLocations_.append(pt);
1813 
1814  label endIndex = existingEdgeLocations_.size();
1815 
1816  return edgeLocationTreePtr_().insert(startIndex, endIndex);
1817 }
1818 
1819 
1821 Foam::conformalVoronoiMesh::nearestFeatureEdgeLocations
1822 (
1823  const Foam::point& pt
1824 ) const
1825 {
1826  const scalar exclusionRangeSqr = featureEdgeExclusionDistanceSqr(pt);
1827 
1828  labelList elems
1829  = edgeLocationTreePtr_().findSphere(pt, exclusionRangeSqr);
1830 
1831  DynamicList<pointIndexHit> dynPointHit;
1832 
1833  forAll(elems, elemI)
1834  {
1835  label index = elems[elemI];
1836 
1837  const Foam::point& pointi
1838  = edgeLocationTreePtr_().shapes().shapePoints()[index];
1839 
1840  pointIndexHit nearHit(true, pointi, index);
1841 
1842  dynPointHit.append(nearHit);
1843  }
1844 
1845  return dynPointHit;
1846 }
1847 
1848 
1849 bool Foam::conformalVoronoiMesh::pointIsNearFeatureEdgeLocation
1850 (
1851  const Foam::point& pt
1852 ) const
1853 {
1854  const scalar exclusionRangeSqr = featureEdgeExclusionDistanceSqr(pt);
1855 
1856  pointIndexHit info
1857  = edgeLocationTreePtr_().findNearest(pt, exclusionRangeSqr);
1858 
1859  return info.hit();
1860 }
1861 
1862 
1863 bool Foam::conformalVoronoiMesh::pointIsNearFeatureEdgeLocation
1864 (
1865  const Foam::point& pt,
1866  pointIndexHit& info
1867 ) const
1868 {
1869  const scalar exclusionRangeSqr = featureEdgeExclusionDistanceSqr(pt);
1870 
1871  info = edgeLocationTreePtr_().findNearest(pt, exclusionRangeSqr);
1872 
1873  return info.hit();
1874 }
1875 
1876 
1877 bool Foam::conformalVoronoiMesh::pointIsNearSurfaceLocation
1878 (
1879  const Foam::point& pt
1880 ) const
1881 {
1882  pointIndexHit info;
1883 
1884  pointIsNearSurfaceLocation(pt, info);
1885 
1886  return info.hit();
1887 }
1888 
1889 
1890 bool Foam::conformalVoronoiMesh::pointIsNearSurfaceLocation
1891 (
1892  const Foam::point& pt,
1893  pointIndexHit& info
1894 ) const
1895 {
1896  const scalar exclusionRangeSqr = surfacePtExclusionDistanceSqr(pt);
1897 
1898  info = surfacePtLocationTreePtr_().findNearest(pt, exclusionRangeSqr);
1899 
1900  return info.hit();
1901 }
1902 
1903 
1904 bool Foam::conformalVoronoiMesh::nearFeatureEdgeLocation
1905 (
1906  const pointIndexHit& pHit,
1907  pointIndexHit& nearestEdgeHit
1908 ) const
1909 {
1910  const Foam::point& pt = pHit.hitPoint();
1911 
1912  const scalar exclusionRangeSqr = featureEdgeExclusionDistanceSqr(pt);
1913 
1914  bool closeToFeatureEdge =
1915  pointIsNearFeatureEdgeLocation(pt, nearestEdgeHit);
1916 
1917  if (closeToFeatureEdge)
1918  {
1919  List<pointIndexHit> nearHits = nearestFeatureEdgeLocations(pt);
1920 
1921  forAll(nearHits, elemI)
1922  {
1923  pointIndexHit& info = nearHits[elemI];
1924 
1925  // Check if the edge location that the new edge location is near to
1926  // "might" be on a different edge. If so, add it anyway.
1927  pointIndexHit edgeHit;
1928  label featureHit = -1;
1929 
1930  geometryToConformTo_.findEdgeNearest
1931  (
1932  pt,
1933  exclusionRangeSqr,
1934  edgeHit,
1935  featureHit
1936  );
1937 
1938  const extendedFeatureEdgeMesh& eMesh
1939  = geometryToConformTo_.features()[featureHit];
1940 
1941  const vector& edgeDir = eMesh.edgeDirections()[edgeHit.index()];
1942 
1943  const vector lineBetweenPoints = pt - info.hitPoint();
1944 
1945  const scalar cosAngle
1946  = vectorTools::cosPhi(edgeDir, lineBetweenPoints);
1947 
1948  // Allow the point to be added if it is almost at right angles to
1949  // the other point. Also check it is not the same point.
1950  // Info<< cosAngle<< " "
1951  // << radToDeg(acos(cosAngle)) << " "
1952  // << searchConeAngle << " "
1953  // << radToDeg(acos(searchConeAngle)) << endl;
1954 
1955  if
1956  (
1957  mag(cosAngle) < searchConeAngle
1958  && (mag(lineBetweenPoints) > pointPairDistance(pt))
1959  )
1960  {
1961  //pt = edgeHit.hitPoint();
1962  //pHit.setPoint(pt);
1963  closeToFeatureEdge = false;
1964  }
1965  else
1966  {
1967  closeToFeatureEdge = true;
1968  break;
1969  }
1970  }
1971  }
1972 
1973  return closeToFeatureEdge;
1974 }
1975 
1976 
1977 void Foam::conformalVoronoiMesh::buildEdgeLocationTree
1978 (
1979  const DynamicList<Foam::point>& existingEdgeLocations
1980 ) const
1981 {
1982  treeBoundBox overallBb
1983  (
1984  geometryToConformTo_.globalBounds().extend(rndGen_, 1e-4)
1985  );
1986 
1987  overallBb.min() -= Foam::point::uniform(ROOTVSMALL);
1988  overallBb.max() += Foam::point::uniform(ROOTVSMALL);
1989 
1990  edgeLocationTreePtr_.reset
1991  (
1992  new dynamicIndexedOctree<dynamicTreeDataPoint>
1993  (
1994  dynamicTreeDataPoint(existingEdgeLocations),
1995  overallBb, // overall search domain
1996  10, // max levels, n/a
1997  20.0, // maximum ratio of cubes v.s. cells
1998  100.0 // max. duplicity; n/a since no bounding boxes.
1999  )
2000  );
2001 }
2002 
2003 
2004 void Foam::conformalVoronoiMesh::buildSurfacePtLocationTree
2005 (
2006  const DynamicList<Foam::point>& existingSurfacePtLocations
2007 ) const
2008 {
2009  treeBoundBox overallBb
2010  (
2011  geometryToConformTo_.globalBounds().extend(rndGen_, 1e-4)
2012  );
2013 
2014  overallBb.min() -= Foam::point::uniform(ROOTVSMALL);
2015  overallBb.max() += Foam::point::uniform(ROOTVSMALL);
2016 
2017  surfacePtLocationTreePtr_.reset
2018  (
2019  new dynamicIndexedOctree<dynamicTreeDataPoint>
2020  (
2021  dynamicTreeDataPoint(existingSurfacePtLocations),
2022  overallBb, // overall search domain
2023  10, // max levels, n/a
2024  20.0, // maximum ratio of cubes v.s. cells
2025  100.0 // max. duplicity; n/a since no bounding boxes.
2026  )
2027  );
2028 }
2029 
2030 
2031 void Foam::conformalVoronoiMesh::addSurfaceAndEdgeHits
2032 (
2033  const Foam::point& vit,
2034  const pointIndexHitAndFeatureDynList& surfaceIntersections,
2035  scalar surfacePtReplaceDistCoeffSqr,
2036  scalar edgeSearchDistCoeffSqr,
2037  pointIndexHitAndFeatureDynList& surfaceHits,
2038  pointIndexHitAndFeatureDynList& featureEdgeHits,
2039  DynamicList<label>& surfaceToTreeShape,
2040  DynamicList<label>& edgeToTreeShape,
2041  Map<scalar>& surfacePtToEdgePtDist,
2042  bool firstPass
2043 ) const
2044 {
2045  const scalar cellSize = targetCellSize(vit);
2046  const scalar cellSizeSqr = sqr(cellSize);
2047 
2048  forAll(surfaceIntersections, sI)
2049  {
2050  pointIndexHitAndFeature surfHitI = surfaceIntersections[sI];
2051 
2052  bool keepSurfacePoint = true;
2053 
2054  if (!surfHitI.first().hit())
2055  {
2056  continue;
2057  }
2058 
2059  const Foam::point& surfPt = surfHitI.first().hitPoint();
2060 
2061  bool isNearFeaturePt = nearFeaturePt(surfPt);
2062 
2063  bool isNearFeatureEdge = surfacePtNearFeatureEdge(surfPt);
2064 
2065  bool isNearSurfacePt = nearSurfacePoint(surfHitI);
2066 
2067  if (isNearFeaturePt || isNearSurfacePt || isNearFeatureEdge)
2068  {
2069  keepSurfacePoint = false;
2070  }
2071 
2072  List<List<pointIndexHit>> edHitsByFeature;
2073 
2074  labelList featuresHit;
2075 
2076  const scalar searchRadiusSqr = edgeSearchDistCoeffSqr*cellSizeSqr;
2077 
2078  geometryToConformTo_.findAllNearestEdges
2079  (
2080  surfPt,
2081  searchRadiusSqr,
2082  edHitsByFeature,
2083  featuresHit
2084  );
2085 
2086  forAll(edHitsByFeature, i)
2087  {
2088  const label featureHit = featuresHit[i];
2089 
2090  List<pointIndexHit>& edHits = edHitsByFeature[i];
2091 
2092  forAll(edHits, eHitI)
2093  {
2094  pointIndexHit& edHit = edHits[eHitI];
2095 
2096  if (edHit.hit())
2097  {
2098  const Foam::point& edPt = edHit.hitPoint();
2099 
2100  if
2101  (
2102  Pstream::parRun()
2103  && !decomposition().positionOnThisProcessor(edPt)
2104  )
2105  {
2106  // Do not insert
2107  continue;
2108  }
2109 
2110  if (!nearFeaturePt(edPt))
2111  {
2112  if
2113  (
2114  magSqr(edPt - surfPt)
2115  < surfacePtReplaceDistCoeffSqr*cellSizeSqr
2116  )
2117  {
2118  // If the point is within a given distance of a
2119  // feature edge, give control to edge control points
2120  // instead, this will prevent "pits" forming.
2121 
2122  // Allow if different surfaces
2123 
2124 
2125  keepSurfacePoint = false;
2126  }
2127 
2128  pointIndexHit nearestEdgeHit;
2129 
2130  if
2131  (
2132 // !pointIsNearFeatureEdgeLocation
2133 // (
2134 // edPt,
2135 // nearestEdgeHit
2136 // )
2137  !nearFeatureEdgeLocation(edHit, nearestEdgeHit)
2138  )
2139  {
2140  appendToEdgeLocationTree(edPt);
2141 
2142  edgeToTreeShape.append
2143  (
2144  existingEdgeLocations_.size() - 1
2145  );
2146 
2147  // Do not place edge control points too close to a
2148  // feature point or existing edge control points
2149  featureEdgeHits.append
2150  (
2151  pointIndexHitAndFeature(edHit, featureHit)
2152  );
2153 
2154 // Info<< "Add " << existingEdgeLocations_.size() - 1
2155 // << " " << magSqr(edPt - surfPt) << endl;
2156 
2157  surfacePtToEdgePtDist.insert
2158  (
2159  existingEdgeLocations_.size() - 1,
2160  magSqr(edPt - surfPt)
2161  );
2162  }
2163  else if (firstPass)
2164  {
2165  label hitIndex = nearestEdgeHit.index();
2166 
2167 // Info<< "Close to " << nearestEdgeHit << endl;
2168 
2169  if
2170  (
2171  magSqr(edPt - surfPt)
2172  < surfacePtToEdgePtDist[hitIndex]
2173  )
2174  {
2175  featureEdgeHits[hitIndex] =
2176  pointIndexHitAndFeature(edHit, featureHit);
2177 
2178  existingEdgeLocations_[hitIndex] =
2179  edHit.hitPoint();
2180  surfacePtToEdgePtDist[hitIndex] =
2181  magSqr(edPt - surfPt);
2182 
2183  // Change edge location in featureEdgeHits
2184  // remove index from edge tree
2185  // reinsert new point into tree
2186  edgeLocationTreePtr_().remove(hitIndex);
2187  edgeLocationTreePtr_().insert
2188  (
2189  hitIndex,
2190  hitIndex + 1
2191  );
2192  }
2193  }
2194  }
2195  }
2196  }
2197  }
2198 
2199  if (keepSurfacePoint)
2200  {
2201  surfaceHits.append(surfHitI);
2202  appendToSurfacePtTree(surfPt);
2203  surfaceToTreeShape.append(existingSurfacePtLocations_.size() - 1);
2204 
2205 // addedPoints.write(surfPt);
2206  }
2207  else
2208  {
2209 // removedPoints.write(surfPt);
2210  }
2211  }
2212 }
2213 
2214 
2215 void Foam::conformalVoronoiMesh::storeSurfaceConformation()
2216 {
2217  Info<< nl << "Storing surface conformation" << endl;
2218 
2219  surfaceConformationVertices_.clear();
2220 
2221  // Use a temporary dynamic list to speed up insertion.
2222  DynamicList<Vb> tempSurfaceVertices(number_of_vertices()/10);
2223 
2224  for
2225  (
2226  Delaunay::Finite_vertices_iterator vit = finite_vertices_begin();
2227  vit != finite_vertices_end();
2228  vit++
2229  )
2230  {
2231  // Store points that are not referred, part of a pair, but not feature
2232  // points
2233  if
2234  (
2235  !vit->referred()
2236  && vit->boundaryPoint()
2237  && !vit->featurePoint()
2238  && !vit->constrained()
2239  )
2240  {
2241  tempSurfaceVertices.append
2242  (
2243  Vb
2244  (
2245  vit->point(),
2246  vit->index(),
2247  vit->type(),
2249  )
2250  );
2251  }
2252  }
2253 
2254  tempSurfaceVertices.shrink();
2255 
2256  surfaceConformationVertices_.transfer(tempSurfaceVertices);
2257 
2258  Info<< " Stored "
2259  << returnReduce
2260  (
2261  label(surfaceConformationVertices_.size()),
2262  sumOp<label>()
2263  )
2264  << " vertices" << nl << endl;
2265 }
2266 
2267 
2268 void Foam::conformalVoronoiMesh::reinsertSurfaceConformation()
2269 {
2270  Info<< nl << "Reinserting stored surface conformation" << endl;
2271 
2272  Map<label> oldToNewIndices =
2273  insertPointPairs(surfaceConformationVertices_, true, true);
2274 
2275  ptPairs_.reIndex(oldToNewIndices);
2276 
2277  bitSet selectedElems(surfaceConformationVertices_.size(), true);
2278 
2279  forAll(surfaceConformationVertices_, vI)
2280  {
2281  Vb& v = surfaceConformationVertices_[vI];
2282  label& vIndex = v.index();
2283 
2284  const auto iter = oldToNewIndices.cfind(vIndex);
2285 
2286  if (iter.found())
2287  {
2288  const label newIndex = *iter;
2289 
2290  if (newIndex != -1)
2291  {
2292  vIndex = newIndex;
2293  }
2294  else
2295  {
2296  selectedElems.unset(vI);
2297  }
2298  }
2299  }
2300 
2301  inplaceSubset<bitSet, List<Vb>>
2302  (
2303  selectedElems,
2304  surfaceConformationVertices_
2305  );
2306 }
2307 
2308 
2309 // ************************************************************************* //
Foam::labelList
List< label > labelList
A List of labels.
Definition: List.H:71
p
volScalarField & p
Definition: createFieldRefs.H:8
Foam::VectorSpace< Vector< Cmpt >, Cmpt, 3 >::uniform
static Vector< Cmpt > uniform(const Cmpt &s)
Return a VectorSpace with all elements = s.
Definition: VectorSpaceI.H:164
Foam::returnReduce
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
Definition: PstreamReduceOps.H:94
Foam::Zero
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
Foam::Pstream::scatterList
static void scatterList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Scatter data. Reverse of gatherList.
Definition: gatherScatterList.C:215
Foam::UPstream::nProcs
static label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:427
Foam::UPstream::parRun
static bool & parRun()
Is this a parallel run?
Definition: UPstream.H:415
CGAL::indexedVertex
An indexed form of CGAL::Triangulation_vertex_base_3<K> used to keep track of the Delaunay vertices i...
Definition: indexedVertex.H:54
Foam::vectorTools::cosPhi
T cosPhi(const Vector< T > &a, const Vector< T > &b, const T &tolerance=SMALL)
Calculate angle between a and b in radians.
Definition: vectorTools.H:107
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:350
Foam::Pout
prefixOSstream Pout
An Ostream wrapper for parallel output to std::cout.
Foam::topoint
pointFromPoint topoint(const Point &P)
Definition: pointConversion.H:72
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
Foam::vectorField
Field< vector > vectorField
Specialisation of Field<T> for vector.
Definition: primitiveFieldsFwd.H:54
Foam::magSqr
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
n
label n
Definition: TABSMDCalcMethod2.H:31
Foam::reduce
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
Definition: PstreamReduceOps.H:51
backgroundMeshDecomposition.H
Foam::constant::physicoChemical::c1
const dimensionedScalar c1
First radiation constant: default SI units: [W/m2].
Foam::constant::physicoChemical::b
const dimensionedScalar b
Wien displacement law constant: default SI units: [m.K].
Definition: createFields.H:27
Foam::Info
messageStream Info
Information stream (uses stdout - output is on the master only)
Foam::name
word name(const complex &c)
Return string representation of complex.
Definition: complex.C:76
CGAL::indexedVertex::index
Foam::label & index()
Definition: indexedVertexI.H:159
IOmanip.H
Istream and Ostream manipulators taking arguments.
Foam::pow
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
Definition: dimensionedScalar.C:75
Foam::constant::physicoChemical::c2
const dimensionedScalar c2
Second radiation constant: default SI units: [m.K].
Foam::vector
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:51
Foam::degToRad
constexpr scalar degToRad(const scalar deg) noexcept
Conversion from degrees to radians.
Definition: unitConversion.H:48
Foam::UPstream::myProcNo
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:445
Foam::Pstream::listCombineGather
static void listCombineGather(const List< commsStruct > &comms, List< T > &Value, const CombineOp &cop, const int tag, const label comm)
Definition: combineGatherScatter.C:290
found
bool found
Definition: TABSMDCalcMethod2.H:32
Foam::vectorTools
Collection of functions for testing relationships between two vectors.
Definition: vectorTools.H:49
Foam::Pstream::gatherList
static void gatherList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Gather data but keep individual values separate.
Definition: gatherScatterList.C:52
Foam::sqr
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Definition: dimensionedSymmTensor.C:51
Foam::pointIndexHit
PointIndexHit< point > pointIndexHit
Definition: pointIndexHit.H:45
Foam::constant::mathematical::pi
constexpr scalar pi(M_PI)
Foam::nl
constexpr char nl
Definition: Ostream.H:385
Foam::Vector< scalar >
Foam::labelPairHashSet
HashSet< labelPair, labelPair::Hash<> > labelPairHashSet
A HashSet for a labelPair. The hashing is based on labelPair (FixedList) and is thus non-commutative.
Definition: labelPairHashes.H:65
Foam::List
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: HashTable.H:102
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
Foam::constant::electromagnetic::e
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
Foam::limit
complex limit(const complex &, const complex &)
Definition: complexI.H:263
Foam::Pstream::listCombineScatter
static void listCombineScatter(const List< commsStruct > &comms, List< T > &Value, const int tag, const label comm)
Scatter data. Reverse of combineGather.
Definition: combineGatherScatter.C:432
vectorTools.H
WarningInFunction
#define WarningInFunction
Report a warning using Foam::Warning.
Definition: messageStream.H:298
OBJstream.H
conformalVoronoiMesh.H
indexedCellChecks.H
Foam::cos
dimensionedScalar cos(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:265