PrimitivePatchProjectPoints.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-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 Description
28  For every point on the patch find the closest face on the target side.
29  Return a target face label for each patch point
30 
31 \*---------------------------------------------------------------------------*/
32 
33 #include "boolList.H"
34 #include "PointHit.H"
35 #include "objectHit.H"
36 #include "bandCompression.H"
37 
38 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
39 
40 template<class FaceList, class PointField>
41 template<class ToPatch>
44 (
45  const ToPatch& targetPatch,
46  const Field
47  <
49  >& projectionDirection,
50  const intersection::algorithm alg,
51  const intersection::direction dir
52 ) const
53 {
54  // The current patch is slave, i.e. it is being projected onto the target
55 
56  if (projectionDirection.size() != nPoints())
57  {
59  << "Projection direction field does not correspond to "
60  << "patch points." << endl
61  << "Size: " << projectionDirection.size()
62  << " Number of points: " << nPoints()
63  << abort(FatalError);
64  }
65 
66  const labelList& slavePointOrder = localPointOrder();
67 
68  const labelList& slaveMeshPoints = meshPoints();
69 
70  // Result
71  List<objectHit> result(nPoints());
72 
73  const labelListList& masterFaceFaces = targetPatch.faceFaces();
74 
75  const ToPatch& masterFaces = targetPatch;
76 
77  const Field<point_type>& masterPoints = targetPatch.points();
78 
79  // Estimate face centre of target side
80  Field<point_type> masterFaceCentres(targetPatch.size());
81 
82  forAll(masterFaceCentres, facei)
83  {
84  masterFaceCentres[facei] =
85  average(masterFaces[facei].points(masterPoints));
86  }
87 
88  // Algorithm:
89  // Loop through all points of the slave side. For every point find the
90  // radius for the current contact face. If the contact point falls inside
91  // the face and the radius is smaller than for all neighbouring faces,
92  // the contact is found. If not, visit the neighbour closest to the
93  // calculated contact point. If a single master face is visited more than
94  // twice, initiate n-squared search.
95 
96  label curFace = 0;
97  label nNSquaredSearches = 0;
98 
99  forAll(slavePointOrder, pointi)
100  {
101  // Pick up slave point and direction
102  const label curLocalPointLabel = slavePointOrder[pointi];
103 
104  const point_type& curPoint =
105  points_[slaveMeshPoints[curLocalPointLabel]];
106 
107  const point_type& curProjectionDir =
108  projectionDirection[curLocalPointLabel];
109 
110  bool closer;
111 
112  boolList visitedTargetFace(targetPatch.size(), false);
113  bool doNSquaredSearch = false;
114 
115  bool foundEligible = false;
116 
117  scalar sqrDistance = GREAT;
118 
119  // Force the full search for the first point to ensure good
120  // starting face
121  if (pointi == 0)
122  {
123  doNSquaredSearch = true;
124  }
125  else
126  {
127  do
128  {
129  closer = false;
130  doNSquaredSearch = false;
131 
132  // Calculate intersection with curFace
133  PointHit<point_type> curHit =
134  masterFaces[curFace].ray
135  (
136  curPoint,
137  curProjectionDir,
138  masterPoints,
139  alg,
140  dir
141  );
142 
143  visitedTargetFace[curFace] = true;
144 
145  if (curHit.hit())
146  {
147  result[curLocalPointLabel] = objectHit(true, curFace);
148 
149  break;
150  }
151  else
152  {
153  // If a new miss is eligible, it is closer than
154  // any previous eligible miss (due to surface walk)
155 
156  // Only grab the miss if it is eligible
157  if (curHit.eligibleMiss())
158  {
159  foundEligible = true;
160  result[curLocalPointLabel] = objectHit(false, curFace);
161  }
162 
163  // Find the next likely face for intersection
164 
165  // Calculate the miss point on the plane of the
166  // face. This is cooked (illogical!) for fastest
167  // surface walk.
168  //
169  point_type missPlanePoint =
170  curPoint + curProjectionDir*curHit.distance();
171 
172  const labelList& masterNbrs = masterFaceFaces[curFace];
173 
174  sqrDistance =
175  magSqr(missPlanePoint - masterFaceCentres[curFace]);
176 
177  forAll(masterNbrs, nbrI)
178  {
179  if
180  (
181  magSqr
182  (
183  missPlanePoint
184  - masterFaceCentres[masterNbrs[nbrI]]
185  )
186  <= sqrDistance
187  )
188  {
189  closer = true;
190  curFace = masterNbrs[nbrI];
191  }
192  }
193 
194  if (visitedTargetFace[curFace])
195  {
196  // This face has already been visited.
197  // Execute n-squared search
198  doNSquaredSearch = true;
199  break;
200  }
201  }
202 
203  if (debug) Info<< ".";
204  } while (closer);
205  }
206 
207  if
208  (
209  doNSquaredSearch || !foundEligible
210  )
211  {
212  nNSquaredSearches++;
213 
214  if (debug)
215  {
216  Info<< "p " << curLocalPointLabel << ": ";
217  }
218 
219  result[curLocalPointLabel] = objectHit(false, -1);
220  scalar minDistance = GREAT;
221 
222  forAll(masterFaces, facei)
223  {
224  PointHit<point_type> curHit =
225  masterFaces[facei].ray
226  (
227  curPoint,
228  curProjectionDir,
229  masterPoints,
230  alg,
231  dir
232  );
233 
234  if (curHit.hit())
235  {
236  result[curLocalPointLabel] = objectHit(true, facei);
237  curFace = facei;
238 
239  break;
240  }
241  else if (curHit.eligibleMiss())
242  {
243  // Calculate min distance
244  scalar missDist =
245  Foam::mag(curHit.missPoint() - curPoint);
246 
247  if (missDist < minDistance)
248  {
249  minDistance = missDist;
250 
251  result[curLocalPointLabel] = objectHit(false, facei);
252  curFace = facei;
253  }
254  }
255  }
256 
257  if (debug)
258  {
259  Info<< result[curLocalPointLabel] << nl;
260  }
261  }
262  else
263  {
264  if (debug) Info<< "x";
265  }
266  }
267 
268  if (debug)
269  {
270  Info<< nl << "Executed " << nNSquaredSearches
271  << " n-squared searches out of total of "
272  << nPoints() << endl;
273  }
274 
275  return result;
276 }
277 
278 
279 template<class FaceList, class PointField>
280 template<class ToPatch>
283 (
284  const ToPatch& targetPatch,
285  const Field
286  <
288  >& projectionDirection,
289  const intersection::algorithm alg,
290  const intersection::direction dir
291 ) const
292 {
293  // The current patch is slave, i.e. it is being projected onto the target
294 
295  if (projectionDirection.size() != this->size())
296  {
298  << "Projection direction field does not correspond to patch faces."
299  << endl << "Size: " << projectionDirection.size()
300  << " Number of points: " << this->size()
301  << abort(FatalError);
302  }
303 
304  labelList slaveFaceOrder = bandCompression(faceFaces());
305 
306  // calculate master face centres
307  Field<point_type> masterFaceCentres(targetPatch.size());
308 
309  const labelListList& masterFaceFaces = targetPatch.faceFaces();
310 
311  const ToPatch& masterFaces = targetPatch;
312 
313  const typename ToPatch::PointFieldType& masterPoints = targetPatch.points();
314 
315  forAll(masterFaceCentres, facei)
316  {
317  masterFaceCentres[facei] =
318  masterFaces[facei].centre(masterPoints);
319  }
320 
321  // Result
322  List<objectHit> result(this->size());
323 
324  const PrimitivePatch<FaceList, PointField>& slaveFaces = *this;
325 
326  const PointField& slaveGlobalPoints = points();
327 
328  // Algorithm:
329  // Loop through all points of the slave side. For every point find the
330  // radius for the current contact face. If the contact point falls inside
331  // the face and the radius is smaller than for all neighbouring faces,
332  // the contact is found. If not, visit the neighbour closest to the
333  // calculated contact point. If a single master face is visited more than
334  // twice, initiate n-squared search.
335 
336  label curFace = 0;
337  label nNSquaredSearches = 0;
338 
339  forAll(slaveFaceOrder, facei)
340  {
341  // pick up slave point and direction
342  const label curLocalFaceLabel = slaveFaceOrder[facei];
343 
344  const point& curFaceCentre =
345  slaveFaces[curLocalFaceLabel].centre(slaveGlobalPoints);
346 
347  const vector& curProjectionDir =
348  projectionDirection[curLocalFaceLabel];
349 
350  bool closer;
351 
352  boolList visitedTargetFace(targetPatch.size(), false);
353  bool doNSquaredSearch = false;
354 
355  bool foundEligible = false;
356 
357  scalar sqrDistance = GREAT;
358 
359  // Force the full search for the first point to ensure good
360  // starting face
361  if (facei == 0)
362  {
363  doNSquaredSearch = true;
364  }
365  else
366  {
367  do
368  {
369  closer = false;
370  doNSquaredSearch = false;
371 
372  // Calculate intersection with curFace
373  PointHit<point_type> curHit =
374  masterFaces[curFace].ray
375  (
376  curFaceCentre,
377  curProjectionDir,
378  masterPoints,
379  alg,
380  dir
381  );
382 
383  visitedTargetFace[curFace] = true;
384 
385  if (curHit.hit())
386  {
387  result[curLocalFaceLabel] = objectHit(true, curFace);
388 
389  break;
390  }
391  else
392  {
393  // If a new miss is eligible, it is closer than
394  // any previous eligible miss (due to surface walk)
395 
396  // Only grab the miss if it is eligible
397  if (curHit.eligibleMiss())
398  {
399  foundEligible = true;
400  result[curLocalFaceLabel] = objectHit(false, curFace);
401  }
402 
403  // Find the next likely face for intersection
404 
405  // Calculate the miss point. This is
406  // cooked (illogical!) for fastest surface walk.
407  //
408  point_type missPlanePoint =
409  curFaceCentre + curProjectionDir*curHit.distance();
410 
411  sqrDistance =
412  magSqr(missPlanePoint - masterFaceCentres[curFace]);
413 
414  const labelList& masterNbrs = masterFaceFaces[curFace];
415 
416  forAll(masterNbrs, nbrI)
417  {
418  if
419  (
420  magSqr
421  (
422  missPlanePoint
423  - masterFaceCentres[masterNbrs[nbrI]]
424  )
425  <= sqrDistance
426  )
427  {
428  closer = true;
429  curFace = masterNbrs[nbrI];
430  }
431  }
432 
433  if (visitedTargetFace[curFace])
434  {
435  // This face has already been visited.
436  // Execute n-squared search
437  doNSquaredSearch = true;
438  break;
439  }
440  }
441 
442  if (debug) Info<< ".";
443  } while (closer);
444  }
445 
446  if (doNSquaredSearch || !foundEligible)
447  {
448  nNSquaredSearches++;
449 
450  if (debug)
451  {
452  Info<< "p " << curLocalFaceLabel << ": ";
453  }
454 
455  result[curLocalFaceLabel] = objectHit(false, -1);
456  scalar minDistance = GREAT;
457 
458  forAll(masterFaces, facei)
459  {
460  PointHit<point_type> curHit =
461  masterFaces[facei].ray
462  (
463  curFaceCentre,
464  curProjectionDir,
465  masterPoints,
466  alg,
467  dir
468  );
469 
470  if (curHit.hit())
471  {
472  result[curLocalFaceLabel] = objectHit(true, facei);
473  curFace = facei;
474 
475  break;
476  }
477  else if (curHit.eligibleMiss())
478  {
479  // Calculate min distance
480  scalar missDist =
481  Foam::mag(curHit.missPoint() - curFaceCentre);
482 
483  if (missDist < minDistance)
484  {
485  minDistance = missDist;
486 
487  result[curLocalFaceLabel] = objectHit(false, facei);
488  curFace = facei;
489  }
490  }
491  }
492 
493  if (debug)
494  {
495  Info<< result[curLocalFaceLabel] << nl;
496  }
497  }
498  else
499  {
500  if (debug) Info<< "x";
501  }
502  }
503 
504  if (debug)
505  {
506  Info<< nl << "Executed " << nNSquaredSearches
507  << " n-squared searches out of total of "
508  << this->size() << endl;
509  }
510 
511  return result;
512 }
513 
514 
515 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
Foam::intersection::direction
direction
Definition: intersection.H:66
boolList.H
Foam::PointHit
Describes the interaction of a face and a point. It carries the info of a successful hit and (if succ...
Definition: PointHit.H:53
Foam::bandCompression
labelList bandCompression(const labelListList &addressing)
Renumbers the addressing to reduce the band of the matrix.
Definition: bandCompression.C:44
Foam::PointHit::distance
scalar distance() const noexcept
Return distance to hit.
Definition: PointHit.H:139
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:350
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
Foam::magSqr
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
nPoints
label nPoints
Definition: gmvOutputHeader.H:2
objectHit.H
Foam::Field
Generic templated field type.
Definition: Field.H:63
Foam::PointHit::eligibleMiss
bool eligibleMiss() const noexcept
Is this an eligible miss.
Definition: PointHit.H:127
Foam::fac::average
tmp< GeometricField< Type, faPatchField, areaMesh > > average(const GeometricField< Type, faePatchField, edgeMesh > &ssf)
Area-weighted average a edgeField creating a areaField.
Definition: facAverage.C:47
Foam::PointHit::missPoint
const point_type & missPoint() const
Return the miss point. Fatal if hit.
Definition: PointHit.H:158
Foam::Info
messageStream Info
Information stream (uses stdout - output is on the master only)
Foam::PrimitivePatch::point_type
std::remove_reference< PointField >::type::value_type point_type
The point type.
Definition: PrimitivePatch.H:100
Foam::intersection::algorithm
algorithm
Definition: intersection.H:72
Foam::PrimitivePatch::projectFaceCentres
List< objectHit > projectFaceCentres(const ToPatch &targetPatch, const Field< point_type > &projectionDirection, const intersection::algorithm=intersection::FULL_RAY, const intersection::direction=intersection::VECTOR) const
Project vertices of patch onto another patch.
Foam::FatalError
error FatalError
Foam::abort
errorManip< error > abort(error &err)
Definition: errorManip.H:144
bandCompression.H
The bandCompression function renumbers the addressing such that the band of the matrix is reduced....
Foam::PrimitivePatch::projectPoints
List< objectHit > projectPoints(const ToPatch &targetPatch, const Field< point_type > &projectionDirection, const intersection::algorithm=intersection::FULL_RAY, const intersection::direction=intersection::VECTOR) const
Project vertices of patch onto another patch.
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:381
Foam::nl
constexpr char nl
Definition: Ostream.H:385
Foam::Vector< scalar >
Foam::List< Foam::objectHit >
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
points
const pointField & points
Definition: gmvOutputHeader.H:1
PointHit.H
Foam::objectHit
This class describes a combination of target object index and success flag. Behaves somewhat like std...
Definition: objectHit.H:50
Foam::PointHit::hit
bool hit() const noexcept
Is there a hit.
Definition: PointHit.H:121
Foam::PrimitivePatch
A list of faces which address into the list of points.
Definition: PrimitivePatch.H:85