surfaceHookUp.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) 2014-2017 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 Application
25  surfaceHookUp
26 
27 Description
28  Find close open edges and stitches the surface along them
29 
30 Usage
31  - surfaceHookUp hookDistance [OPTION]
32 
33 \*---------------------------------------------------------------------------*/
34 
35 #include "argList.H"
36 #include "Time.H"
37 
38 #include "triSurfaceMesh.H"
39 #include "indexedOctree.H"
40 #include "treeBoundBox.H"
41 #include "PackedBoolList.H"
42 #include "unitConversion.H"
43 #include "searchableSurfaces.H"
44 #include "IOdictionary.H"
45 
46 using namespace Foam;
47 
48 // Split facei along edgeI at position newPointi
49 void greenRefine
50 (
51  const triSurface& surf,
52  const label facei,
53  const label edgeI,
54  const label newPointi,
55  DynamicList<labelledTri>& newFaces
56 )
57 {
58  const labelledTri& f = surf.localFaces()[facei];
59  const edge& e = surf.edges()[edgeI];
60 
61  // Find index of edge in face.
62 
63  label fp0 = findIndex(f, e[0]);
64  label fp1 = f.fcIndex(fp0);
65  label fp2 = f.fcIndex(fp1);
66 
67  if (f[fp1] == e[1])
68  {
69  // Edge oriented like face
70  newFaces.append
71  (
73  (
74  f[fp0],
75  newPointi,
76  f[fp2],
77  f.region()
78  )
79  );
80  newFaces.append
81  (
83  (
84  newPointi,
85  f[fp1],
86  f[fp2],
87  f.region()
88  )
89  );
90  }
91  else
92  {
93  newFaces.append
94  (
96  (
97  f[fp2],
98  newPointi,
99  f[fp1],
100  f.region()
101  )
102  );
103  newFaces.append
104  (
106  (
107  newPointi,
108  f[fp0],
109  f[fp1],
110  f.region()
111  )
112  );
113  }
114 }
115 
116 
117 //scalar checkEdgeAngle
118 //(
119 // const triSurface& surf,
120 // const label edgeIndex,
121 // const label pointIndex,
122 // const scalar& angle
123 //)
124 //{
125 // const edge& e = surf.edges()[edgeIndex];
126 
127 // vector eVec = e.vec(surf.localPoints());
128 // eVec /= mag(eVec) + SMALL;
129 
130 // const labelList& pEdges = surf.pointEdges()[pointIndex];
131 //
132 // forAll(pEdges, eI)
133 // {
134 // const edge& nearE = surf.edges()[pEdges[eI]];
135 
136 // vector nearEVec = nearE.vec(surf.localPoints());
137 // nearEVec /= mag(nearEVec) + SMALL;
138 
139 // const scalar dot = eVec & nearEVec;
140 // const scalar minCos = degToRad(angle);
141 
142 // if (mag(dot) > minCos)
143 // {
144 // return false;
145 // }
146 // }
147 
148 // return true;
149 //}
150 
151 
152 void createBoundaryEdgeTrees
153 (
154  const PtrList<triSurfaceMesh>& surfs,
156  labelListList& treeBoundaryEdges
157 )
158 {
159  forAll(surfs, surfI)
160  {
161  const triSurface& surf = surfs[surfI];
162 
163  // Boundary edges
164  treeBoundaryEdges[surfI] =
165  labelList
166  (
167  identity(surf.nEdges() - surf.nInternalEdges())
168  + surf.nInternalEdges()
169  );
170 
171  Random rndGen(17301893);
172 
173  // Slightly extended bb. Slightly off-centred just so on symmetric
174  // geometry there are less face/edge aligned items.
175  treeBoundBox bb
176  (
178  );
179 
180  bb.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
181  bb.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
182 
183  bEdgeTrees.set
184  (
185  surfI,
187  (
189  (
190  false, // cachebb
191  surf.edges(), // edges
192  surf.localPoints(), // points
193  treeBoundaryEdges[surfI] // selected edges
194  ),
195  bb, // bb
196  8, // maxLevel
197  10, // leafsize
198  3.0 // duplicity
199  )
200  );
201  }
202 }
203 
204 
205 class findNearestOpSubset
206 {
207  const indexedOctree<treeDataEdge>& tree_;
208 
209  DynamicList<label>& shapeMask_;
210 
211 public:
212 
213  findNearestOpSubset
214  (
215  const indexedOctree<treeDataEdge>& tree,
216  DynamicList<label>& shapeMask
217  )
218  :
219  tree_(tree),
220  shapeMask_(shapeMask)
221  {}
222 
223  void operator()
224  (
225  const labelUList& indices,
226  const point& sample,
227 
228  scalar& nearestDistSqr,
229  label& minIndex,
230  point& nearestPoint
231  ) const
232  {
233  const treeDataEdge& shape = tree_.shapes();
234 
235  forAll(indices, i)
236  {
237  const label index = indices[i];
238  const label edgeIndex = shape.edgeLabels()[index];
239 
240  if
241  (
242  !shapeMask_.empty()
243  && findIndex(shapeMask_, edgeIndex) != -1
244  )
245  {
246  continue;
247  }
248 
249  const edge& e = shape.edges()[edgeIndex];
250 
251  pointHit nearHit = e.line(shape.points()).nearestDist(sample);
252 
253  // Only register hit if closest point is not an edge point
254  if (nearHit.hit())
255  {
256  scalar distSqr = sqr(nearHit.distance());
257 
258  if (distSqr < nearestDistSqr)
259  {
260  nearestDistSqr = distSqr;
261  minIndex = index;
262  nearestPoint = nearHit.rawPoint();
263  }
264  }
265  }
266  }
267 };
268 
269 
270 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
271 
272 int main(int argc, char *argv[])
273 {
275  (
276  "hook surfaces to other surfaces by moving and retriangulating their"
277  "boundary edges to match other surface boundary edges"
278  );
280  argList::validArgs.append("hookTolerance");
281 
282  #include "addDictOption.H"
283 
284  #include "setRootCase.H"
285  #include "createTime.H"
286 
287  const word dictName("surfaceHookUpDict");
289 
290  Info<< "Reading " << dictName << nl << endl;
291 
292  const IOdictionary dict(dictIO);
293 
294  const scalar dist(args.argRead<scalar>(1));
295  const scalar matchTolerance(max(1e-6*dist, SMALL));
296  const label maxIters = 100;
297 
298  Info<< "Hooking distance = " << dist << endl;
299 
300  searchableSurfaces surfs
301  (
302  IOobject
303  (
304  "surfacesToHook",
305  runTime.constant(),
306  "triSurface",
307  runTime
308  ),
309  dict,
310  true // assume single-region names get surface name
311  );
312 
313  Info<< nl << "Reading surfaces: " << endl;
314  forAll(surfs, surfI)
315  {
316  Info<< incrIndent;
317  Info<< nl << indent << "Surface = " << surfs.names()[surfI] << endl;
318 
319  const wordList& regions = surfs[surfI].regions();
320  forAll(regions, surfRegionI)
321  {
322  Info<< incrIndent;
323  Info<< indent << "Regions = " << regions[surfRegionI] << endl;
324  Info<< decrIndent;
325  }
326  Info<< decrIndent;
327  }
328 
329  PtrList<indexedOctree<treeDataEdge>> bEdgeTrees(surfs.size());
330  labelListList treeBoundaryEdges(surfs.size());
331 
332  List<DynamicList<labelledTri>> newFaces(surfs.size());
333  List<DynamicList<point>> newPoints(surfs.size());
334  List<PackedBoolList> visitedFace(surfs.size());
335 
336  PtrList<triSurfaceMesh> newSurfaces(surfs.size());
337  forAll(surfs, surfI)
338  {
339  const triSurfaceMesh& surf =
340  refCast<const triSurfaceMesh>(surfs[surfI]);
341 
342  newSurfaces.set
343  (
344  surfI,
345  new triSurfaceMesh
346  (
347  IOobject
348  (
349  "hookedSurface_" + surfs.names()[surfI],
350  runTime.constant(),
351  "triSurface",
352  runTime
353  ),
354  surf
355  )
356  );
357  }
358 
359  label nChanged = 0;
360  label nIters = 1;
361 
362  do
363  {
364  Info<< nl << "Iteration = " << nIters++ << endl;
365  nChanged = 0;
366 
367  createBoundaryEdgeTrees(newSurfaces, bEdgeTrees, treeBoundaryEdges);
368 
369  forAll(newSurfaces, surfI)
370  {
371  const triSurface& newSurf = newSurfaces[surfI];
372 
373  newFaces[surfI] = newSurf.localFaces();
374  newPoints[surfI] = newSurf.localPoints();
375  visitedFace[surfI] = PackedBoolList(newSurf.size(), false);
376  }
377 
378  forAll(newSurfaces, surfI)
379  {
380  const triSurface& surf = newSurfaces[surfI];
381 
382  List<pointIndexHit> bPointsTobEdges(surf.boundaryPoints().size());
383  labelList bPointsHitTree(surf.boundaryPoints().size(), -1);
384 
385  const labelListList& pointEdges = surf.pointEdges();
386 
387  forAll(bPointsTobEdges, bPointi)
388  {
389  pointIndexHit& nearestHit = bPointsTobEdges[bPointi];
390 
391  const label pointi = surf.boundaryPoints()[bPointi];
392  const point& samplePt = surf.localPoints()[pointi];
393 
394  const labelList& pEdges = pointEdges[pointi];
395 
396  // Add edges connected to the edge to the shapeMask
397  DynamicList<label> shapeMask;
398  shapeMask.append(pEdges);
399 
400  forAll(bEdgeTrees, treeI)
401  {
402  const indexedOctree<treeDataEdge>& bEdgeTree =
403  bEdgeTrees[treeI];
404 
405  pointIndexHit currentHit =
406  bEdgeTree.findNearest
407  (
408  samplePt,
409  sqr(dist),
410  findNearestOpSubset
411  (
412  bEdgeTree,
413  shapeMask
414  )
415  );
416 
417  if
418  (
419  currentHit.hit()
420  &&
421  (
422  !nearestHit.hit()
423  ||
424  (
425  magSqr(currentHit.hitPoint() - samplePt)
426  < magSqr(nearestHit.hitPoint() - samplePt)
427  )
428  )
429  )
430  {
431  nearestHit = currentHit;
432  bPointsHitTree[bPointi] = treeI;
433  }
434  }
435 
436  scalar dist2 = magSqr(nearestHit.rawPoint() - samplePt);
437 
438  if (nearestHit.hit())
439  {
440  // bool rejectEdge =
441  // checkEdgeAngle
442  // (
443  // surf,
444  // nearestHit.index(),
445  // pointi,
446  // 30
447  // );
448 
449  if (dist2 > Foam::sqr(dist))
450  {
451  nearestHit.setMiss();
452  }
453  }
454  }
455 
456  forAll(bPointsTobEdges, bPointi)
457  {
458  const pointIndexHit& eHit = bPointsTobEdges[bPointi];
459 
460  if (eHit.hit())
461  {
462  const label hitSurfI = bPointsHitTree[bPointi];
463  const triSurface& hitSurf = newSurfaces[hitSurfI];
464 
465  const label eIndex =
466  treeBoundaryEdges[hitSurfI][eHit.index()];
467  const edge& e = hitSurf.edges()[eIndex];
468 
469  const label pointi = surf.boundaryPoints()[bPointi];
470 
471  const labelList& eFaces = hitSurf.edgeFaces()[eIndex];
472 
473  if (eFaces.size() != 1)
474  {
476  << "Edge is attached to " << eFaces.size()
477  << " faces." << endl;
478 
479  continue;
480  }
481 
482  const label facei = eFaces[0];
483 
484  if (visitedFace[hitSurfI][facei])
485  {
486  continue;
487  }
488 
489  DynamicList<labelledTri> newFacesFromSplit(2);
490 
491  const point& pt = surf.localPoints()[pointi];
492 
493  if
494  (
495  (
496  magSqr(pt - hitSurf.localPoints()[e.start()])
497  < matchTolerance
498  )
499  || (
500  magSqr(pt - hitSurf.localPoints()[e.end()])
501  < matchTolerance
502  )
503  )
504  {
505  continue;
506  }
507 
508  nChanged++;
509 
510  label newPointi = -1;
511 
512  // Keep the points in the same place and move the edge
513  if (hitSurfI == surfI)
514  {
515  newPointi = pointi;
516  }
517  else
518  {
519  newPoints[hitSurfI].append(newPoints[surfI][pointi]);
520  newPointi = newPoints[hitSurfI].size() - 1;
521  }
522 
523  // Split the other face.
524  greenRefine
525  (
526  hitSurf,
527  facei,
528  eIndex,
529  newPointi,
530  newFacesFromSplit
531  );
532 
533  visitedFace[hitSurfI][facei] = true;
534 
535  forAll(newFacesFromSplit, newFacei)
536  {
537  const labelledTri& fN = newFacesFromSplit[newFacei];
538 
539  if (newFacei == 0)
540  {
541  newFaces[hitSurfI][facei] = fN;
542  }
543  else
544  {
545  newFaces[hitSurfI].append(fN);
546  }
547  }
548  }
549  }
550  }
551 
552  Info<< " Number of edges split = " << nChanged << endl;
553 
554  forAll(newSurfaces, surfI)
555  {
556  newSurfaces.set
557  (
558  surfI,
559  new triSurfaceMesh
560  (
561  IOobject
562  (
563  "hookedSurface_" + surfs.names()[surfI],
564  runTime.constant(),
565  "triSurface",
566  runTime
567  ),
568  triSurface
569  (
570  newFaces[surfI],
571  newSurfaces[surfI].patches(),
572  pointField(newPoints[surfI])
573  )
574  )
575  );
576  }
577 
578  } while (nChanged > 0 && nIters <= maxIters);
579 
580  Info<< endl;
581 
582  forAll(newSurfaces, surfI)
583  {
584  const triSurfaceMesh& newSurf = newSurfaces[surfI];
585 
586  Info<< "Writing hooked surface " << newSurf.searchableSurface::name()
587  << endl;
588 
589  newSurf.searchableSurface::write();
590  }
591 
592  Info<< "\nEnd\n" << endl;
593 
594  return 0;
595 }
596 
597 
598 // ************************************************************************* //
const labelListList & pointEdges() const
Return point-edge addressing.
dictionary dict
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:428
bool empty() const
Return true if the UList is empty (ie, size() is zero)
Definition: UListI.H:313
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
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:223
const pointField & points() const
Definition: treeDataEdge.H:174
dimensioned< Type > max(const dimensioned< Type > &, const dimensioned< Type > &)
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Unit conversion functions.
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:163
const labelList & boundaryPoints() const
Return list of boundary points,.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:253
Holds data for octree to work on an edges subset.
Definition: treeDataEdge.H:53
label nInternalEdges() const
Number of internal edges.
static void noParallel()
Remove the parallel options.
Definition: argList.C:148
labelList identity(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
static SLList< string > validArgs
A list of valid (mandatory) arguments.
Definition: argList.H:154
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
Definition: PointIndexHit.H:53
patches[0]
IOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO functionalit...
Definition: IOdictionary.H:52
IOoject and searching on triSurface.
const List< Face > & localFaces() const
Return patch faces addressing into local point list.
const Point & hitPoint() const
Return hit point.
label region() const
Return region label.
Definition: labelledTriI.H:68
linePointRef line(const pointField &) const
Return edge line.
Definition: edgeI.H:169
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects...
Definition: DynamicList.H:56
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:58
A class for handling words, derived from string.
Definition: word.H:59
void append(const T &)
Append an element at the end of the list.
Definition: ListI.H:184
cachedRandom rndGen(label(0), -1)
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:292
bool hit() const
Is there a hit.
Container for searchableSurfaces.
const Type & shapes() const
Reference to shape.
const labelListList & edgeFaces() const
Return edge-face addressing.
bool hit() const
Is there a hit.
Definition: PointHit.H:120
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
List< label > labelList
A List of labels.
Definition: labelList.H:56
const word dictName("particleTrackDict")
Triangle with additional region number.
Definition: labelledTri.H:57
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: HashTable.H:61
label fcIndex(const label i) const
Return the forward circular index, i.e. the next index.
Definition: FixedListI.H:135
Simple random number generator.
Definition: Random.H:49
const Point & rawPoint() const
Return point with no checking.
dimensioned< scalar > magSqr(const dimensioned< Type > &)
const Field< PointType > & localPoints() const
Return pointField of points in patch.
const Point & rawPoint() const
Return point with no checking.
Definition: PointHit.H:158
label nEdges() const
Return number of edges in patch.
static const char nl
Definition: Ostream.H:262
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:237
treeBoundBox extend(Random &, const scalar s) const
Return slightly wider bounding box.
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurence of given element and return index,.
IOobject dictIO(dictName, runTime.constant(), mesh, IOobject::MUST_READ_IF_MODIFIED, IOobject::NO_WRITE)
label size() const
Return the number of elements in the UPtrList.
Definition: UPtrListI.H:29
A bit-packed bool list.
const edgeList & edges() const
Definition: treeDataEdge.H:169
vector point
Point is a vector.
Definition: point.H:41
Non-pointer based hierarchical recursive searching.
Definition: treeDataEdge.H:47
#define WarningInFunction
Report a warning using Foam::Warning.
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: List.H:63
label end() const
Return end vertex label.
Definition: edgeI.H:92
T argRead(const label index) const
Read a value from the argument at index.
Definition: argListI.H:177
Standard boundBox + extra functionality for use in octree.
Definition: treeBoundBox.H:87
messageStream Info
scalar distance() const
Return distance to hit.
Definition: PointHit.H:139
const point & min() const
Minimum describing the bounding box.
Definition: boundBoxI.H:54
static void addNote(const string &)
Add extra notes for the usage information.
Definition: argList.C:126
Triangulated surface description with patch information.
Definition: triSurface.H:65
Foam::argList args(argc, argv)
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:230
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:92
label index() const
Return index.
const labelList & edgeLabels() const
Definition: treeDataEdge.H:179
label start() const
Return start vertex label.
Definition: edgeI.H:81
Namespace for OpenFOAM.