advancingFrontAMI.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) 2013-2016 OpenFOAM Foundation
9  Copyright (C) 2015-2022 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 "advancingFrontAMI.H"
30 #include "meshTools.H"
31 #include "mapDistribute.H"
32 #include "unitConversion.H"
33 
34 #include "findNearestMaskedOp.H"
35 
36 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
37 
38 namespace Foam
39 {
40  defineTypeNameAndDebug(advancingFrontAMI, 0);
41 }
42 
43 
46 {
47  { areaNormalisationMode::project, "project" },
48  { areaNormalisationMode::mag, "mag" },
49 };
50 
51 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
52 
54 {
55  const auto& src = srcPatch();
56  const auto& tgt = tgtPatch();
57 
58  if (debug && (!src.size() || !tgt.size()))
59  {
60  Pout<< "AMI: Patches not on processor: Source faces = "
61  << src.size() << ", target faces = " << tgt.size()
62  << endl;
63  }
64 
65 
66  if (requireMatch_)
67  {
68  const scalar maxBoundsError = 0.05;
69 
70  // Check bounds of source and target
71  boundBox bbSrc(src.points(), src.meshPoints(), false);
73  (
74  bbSrc.min(),
75  minOp<point>(),
77  comm_
78  );
80  (
81  bbSrc.max(),
82  maxOp<point>(),
84  comm_
85  );
86  boundBox bbTgt(tgt.points(), tgt.meshPoints(), false);
88  (
89  bbTgt.min(),
90  minOp<point>(),
92  comm_
93  );
95  (
96  bbTgt.max(),
97  maxOp<point>(),
99  comm_
100  );
101 
102  boundBox bbTgtInf(bbTgt);
103  bbTgtInf.inflate(maxBoundsError);
104 
105  if (!bbTgtInf.contains(bbSrc))
106  {
108  << "Source and target patch bounding boxes are not similar"
109  << nl
110  << " source box span : " << bbSrc.span() << nl
111  << " target box span : " << bbTgt.span() << nl
112  << " source box : " << bbSrc << nl
113  << " target box : " << bbTgt << nl
114  << " inflated target box : " << bbTgtInf << endl;
115  }
116  }
117 }
118 
119 
121 (
122  const label srcFacei,
123  const label tgtFacei
124 ) const
125 {
126  const auto& srcPatch = this->srcPatch();
127  const auto& tgtPatch = this->tgtPatch();
128 
129  if
130  (
131  (srcMagSf_[srcFacei] < ROOTVSMALL)
132  || (tgtMagSf_[tgtFacei] < ROOTVSMALL)
133  )
134  {
135  return false;
136  }
137 
138  if (maxDistance2_ > 0)
139  {
140  const point& srcFc = srcPatch.faceCentres()[srcFacei];
141  const point& tgtFc = tgtPatch.faceCentres()[tgtFacei];
142  const vector& srcN = srcPatch.faceNormals()[srcFacei];
143 
144  const scalar normalDist((tgtFc-srcFc)&srcN);
145  //if (magSqr(srcFc-tgtFc) >= maxDistance2_)
146  if (sqr(normalDist) >= maxDistance2_)
147  {
148  return false;
149  }
150  }
151 
152  if (minCosAngle_ > -1)
153  {
154  const vector& srcN = srcPatch.faceNormals()[srcFacei];
155  vector tgtN = tgtPatch.faceNormals()[tgtFacei];
156  if (!reverseTarget_)
157  {
158  tgtN = -tgtN;
159  }
160 
161  if ((srcN & tgtN) <= minCosAngle_)
162  {
163  return false;
164  }
165  }
166 
167  return true;
168 }
169 
170 
172 {
173  // Create processor map of extended cells. This map gets (possibly
174  // remote) cells from the src mesh such that they (together) cover
175  // all of tgt
176  extendedTgtMapPtr_.reset(calcProcMap(srcPatch0(), tgtPatch0()));
177  const mapDistribute& map = extendedTgtMapPtr_();
178 
179  // Original faces from tgtPatch
180  // Note: in globalIndexing since might be remote
181  globalIndex globalTgtFaces(tgtPatch0().size(), comm_);
182  distributeAndMergePatches
183  (
184  map,
185  tgtPatch0(),
186  globalTgtFaces,
187  extendedTgtFaces_,
188  extendedTgtPoints_,
189  extendedTgtFaceIDs_
190  );
191 
192  // Create a representation of the tgt patch that is extended to overlap
193  // the src patch
194  extendedTgtPatchPtr_.reset
195  (
197  (
198  SubList<face>(extendedTgtFaces_),
199  extendedTgtPoints_
200  )
201  );
202 }
203 
204 
205 bool Foam::advancingFrontAMI::initialiseWalk(label& srcFacei, label& tgtFacei)
206 {
207  const auto& src = this->srcPatch();
208  const auto& tgt = this->tgtPatch();
209 
210  bool foundFace = false;
211 
212  // Check that patch sizes are valid
213  if (!src.size())
214  {
215  return foundFace;
216  }
217  else if (!tgt.size())
218  {
220  << src.size() << " source faces but no target faces" << endl;
221 
222  return foundFace;
223  }
224 
225  // Reset the octree
226  treePtr_.reset(createTree(tgt));
227 
228  // Find initial face match using brute force/octree search
229  if ((srcFacei == -1) || (tgtFacei == -1))
230  {
231  srcFacei = 0;
232  tgtFacei = 0;
233  forAll(src, facei)
234  {
235  tgtFacei = findTargetFace(facei);
236  if (tgtFacei >= 0)
237  {
238  srcFacei = facei;
239  foundFace = true;
240  break;
241  }
242  }
243 
244  if (!foundFace)
245  {
246  if (requireMatch_)
247  {
249  << "Unable to find initial target face"
250  << abort(FatalError);
251  }
252 
253  return foundFace;
254  }
255  }
256 
257  if (debug)
258  {
259  Pout<< "AMI: initial target face = " << tgtFacei << endl;
260  }
261 
262  return true;
263 }
264 
265 
267 (
268  const scalar area,
269  const face& f1,
270  const face& f2,
271  const pointField& f1Points,
272  const pointField& f2Points
273 ) const
274 {
275  static label count = 1;
276 
277  const pointField f1pts = f1.points(f1Points);
278  const pointField f2pts = f2.points(f2Points);
279 
280  Pout<< "Face intersection area (" << count << "):" << nl
281  << " f1 face = " << f1 << nl
282  << " f1 pts = " << f1pts << nl
283  << " f2 face = " << f2 << nl
284  << " f2 pts = " << f2pts << nl
285  << " area = " << area
286  << endl;
287 
288  OFstream os("areas" + name(count) + ".obj");
289 
290  for (const point& pt : f1pts)
291  {
292  meshTools::writeOBJ(os, pt);
293  }
294  os<< "l";
295  forAll(f1pts, i)
296  {
297  os<< " " << i + 1;
298  }
299  os<< " 1" << endl;
300 
301  for (const point& pt : f2pts)
302  {
303  meshTools::writeOBJ(os, pt);
304  }
305  os<< "l";
306  const label n = f1pts.size();
307  forAll(f2pts, i)
308  {
309  os<< " " << n + i + 1;
310  }
311  os<< " " << n + 1 << endl;
312 
313  ++count;
314 }
315 
316 
318 (
319  const label srcFacei,
320  const UList<label>& excludeFaces,
321  const label srcFacePti
322 ) const
323 {
324  const auto& src = srcPatch();
325 
326  label targetFacei = -1;
327 
328  const pointField& srcPts = src.points();
329  const face& srcFace = src[srcFacei];
330 
331  findNearestMaskedOp<primitivePatch> fnOp(*treePtr_, excludeFaces);
332 
333  const boundBox bb(srcPts, srcFace, false);
334 
335  const point srcPt =
336  srcFacePti == -1 ? bb.centre() : srcPts[srcFace[srcFacePti]];
337 
338  const pointIndexHit sample =
339  treePtr_->findNearest(srcPt, 0.25*bb.magSqr(), fnOp);
340 
341  if (sample.hit() && isCandidate(srcFacei, sample.index()))
342  {
343  targetFacei = sample.index();
344 
345  if (debug)
346  {
347  Pout<< "Source point = " << srcPt << ", Sample point = "
348  << sample.point() << ", Sample index = " << sample.index()
349  << endl;
350  }
351  }
352 
353  return targetFacei;
354 }
355 
356 
358 (
359  const label facei,
360  const primitivePatch& patch,
361  const DynamicList<label>& visitedFaces,
362  DynamicList<label>& faceIDs
363 ) const
364 {
365  static const scalar thetaCos = Foam::cos(degToRad(89.0));
366 
367  const labelList& nbrFaces = patch.faceFaces()[facei];
368 
369  // Filter out faces already visited from face neighbours
370  for (const label nbrFacei : nbrFaces)
371  {
372  // Prevent addition of face if it is not on the same plane-ish
373  if (!visitedFaces.found(nbrFacei) && !faceIDs.found(nbrFacei))
374  {
375  const vector& n1 = patch.faceNormals()[facei];
376  const vector& n2 = patch.faceNormals()[nbrFacei];
377 
378  const scalar cosI = n1 & n2;
379 
380  if (cosI > thetaCos)
381  {
382  faceIDs.append(nbrFacei);
383  }
384  }
385  }
386 }
387 
388 
390 (
391  const primitivePatch& patch,
392  List<DynamicList<face>>& tris,
393  List<scalar>& magSf
394 ) const
395 {
396  const pointField& points = patch.points();
397  tris.setSize(patch.size());
398  magSf.setSize(patch.size());
399 
400  const auto& faceNormals = patch.faceNormals();
401 
402  // Using methods that index into existing points
403  forAll(patch, facei)
404  {
405  tris[facei].clear();
406 
407  switch (triMode_)
408  {
410  {
411  faceAreaIntersect::triangleFan(patch[facei], tris[facei]);
412  break;
413  }
415  {
416  patch[facei].triangles(points, tris[facei]);
417  break;
418  }
419  }
420 
421  const DynamicList<face>& triFaces = tris[facei];
422  magSf[facei] = 0;
423 
424  switch (areaNormalisationMode_)
425  {
426  case areaNormalisationMode::project:
427  {
428  for (const face& f : triFaces)
429  {
430  magSf[facei] +=
432  (
433  points[f[0]],
434  points[f[1]],
435  points[f[2]]
436  ).areaNormal()
437  & faceNormals[facei];
438  }
439  break;
440  }
442  {
443  for (const face& f : triFaces)
444  {
445  magSf[facei] +=
447  (
448  points[f[0]],
449  points[f[1]],
450  points[f[2]]
451  ).mag();
452  }
453  break;
454  }
455  }
456  }
457 }
458 
459 
461 {
462  if (!requireMatch_ && distributed())
463  {
464  scalarList newTgtMagSf(std::move(tgtMagSf_));
465 
466  // Assign default sizes. Override selected values with calculated
467  // values. This is to support ACMI where some of the target faces
468  // are never used (so never get sent over and hence never assigned
469  // to)
470  tgtMagSf_ = tgtPatch0().magFaceAreas();
471 
472  for (const labelList& smap : this->extendedTgtMapPtr_->subMap())
473  {
474  UIndirectList<scalar>(tgtMagSf_, smap) =
475  UIndirectList<scalar>(newTgtMagSf, smap);
476  }
477  }
478 }
479 
480 
481 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
482 
484 (
485  const dictionary& dict,
486  const bool reverseTarget
487 )
488 :
489  AMIInterpolation(dict, reverseTarget),
490  maxDistance2_(dict.getOrDefault<scalar>("maxDistance2", -1)),
491  minCosAngle_(dict.getOrDefault<scalar>("minCosAngle", -1)),
492  srcTris_(),
493  tgtTris_(),
494  extendedTgtPatchPtr_(nullptr),
495  extendedTgtFaces_(),
496  extendedTgtPoints_(),
497  extendedTgtFaceIDs_(),
498  extendedTgtMapPtr_(nullptr),
499  srcNonOverlap_(),
500  triMode_
501  (
502  faceAreaIntersect::triangulationModeNames_.getOrDefault
503  (
504  "triMode",
505  dict,
506  faceAreaIntersect::tmMesh
507  )
508  ),
509  areaNormalisationMode_
510  (
511  areaNormalisationModeNames_.getOrDefault
512  (
513  "areaNormalisationMode",
514  dict,
515  areaNormalisationMode::project
516  )
517  )
518 {
519  DebugInfo
520  << "AMI: maxDistance2:" << maxDistance2_
521  << " minCosAngle:" << minCosAngle_
523  << " areaNormalisationMode:"
525  << endl;
526 }
527 
528 
530 (
531  const bool requireMatch,
532  const bool reverseTarget,
533  const scalar lowWeightCorrection,
535 )
536 :
537  AMIInterpolation(requireMatch, reverseTarget, lowWeightCorrection),
538  maxDistance2_(-1),
539  minCosAngle_(-1),
540  srcTris_(),
541  tgtTris_(),
542  extendedTgtPatchPtr_(nullptr),
543  extendedTgtFaces_(),
544  extendedTgtPoints_(),
545  extendedTgtFaceIDs_(),
546  extendedTgtMapPtr_(nullptr),
547  srcNonOverlap_(),
548  triMode_(triMode),
549  areaNormalisationMode_(areaNormalisationMode::project)
550 {}
551 
552 
554 :
555  AMIInterpolation(ami),
556  maxDistance2_(ami.maxDistance2_),
557  minCosAngle_(ami.minCosAngle_),
558  srcTris_(),
559  tgtTris_(),
560  extendedTgtPatchPtr_(nullptr),
561  extendedTgtFaces_(),
562  extendedTgtPoints_(),
563  extendedTgtFaceIDs_(),
564  extendedTgtMapPtr_(nullptr),
565  srcNonOverlap_(),
566  triMode_(ami.triMode_),
567  areaNormalisationMode_(ami.areaNormalisationMode_)
568 {}
569 
570 
571 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
572 
574 (
575  const primitivePatch& srcPatch,
576  const primitivePatch& tgtPatch,
577  const autoPtr<searchableSurface>& surfPtr
578 )
579 {
580  if (AMIInterpolation::calculate(srcPatch, tgtPatch, surfPtr))
581  {
582  // Create a representation of the target patch that covers the source
583  // patch
584  if (distributed())
585  {
586  createExtendedTgtPatch();
587  }
588 
589  const auto& src = this->srcPatch();
590  const auto& tgt = this->tgtPatch();
591 
592 
593  if (maxDistance2_ > 0)
594  {
595  // Early trigger face centre calculation
596  (void)src.faceCentres();
597  (void)tgt.faceCentres();
598  // Early trigger face normals calculation
599  (void)src.faceNormals();
600  (void)tgt.faceNormals();
601  }
602  if (minCosAngle_ > -1)
603  {
604  // Early trigger face normals calculation
605  (void)src.faceNormals();
606  (void)tgt.faceNormals();
607  }
608 
609 
610  // Initialise area magnitudes
611  srcMagSf_.setSize(src.size(), 1.0);
612  tgtMagSf_.setSize(tgt.size(), 1.0);
613 
614  // Source and target patch triangulations
615  triangulatePatch(src, srcTris_, srcMagSf_);
616  triangulatePatch(tgt, tgtTris_, tgtMagSf_);
617 
618  checkPatches();
619 
620  // Set initial sizes for weights and addressing - must be done even if
621  // returns false below
622  srcAddress_.setSize(src.size());
623  srcWeights_.setSize(src.size());
624  tgtAddress_.setSize(tgt.size());
625  tgtWeights_.setSize(tgt.size());
626 
627  return true;
628  }
629 
630  return false;
631 }
632 
633 
634 void Foam::advancingFrontAMI::write(Ostream& os) const
635 {
637  os.writeEntryIfDifferent<scalar>("maxDistance2", -1, maxDistance2_);
638  os.writeEntryIfDifferent<scalar>("minCosAngle", -1, minCosAngle_);
640  (
641  "triMode",
644  );
646  (
647  "areaNormalisationMode",
648  areaNormalisationModeNames_[areaNormalisationMode::project],
649  areaNormalisationModeNames_[areaNormalisationMode_]
650  );
651 }
652 
653 
654 // ************************************************************************* //
List< scalar > scalarList
List of scalar.
Definition: scalarList.H:32
areaNormalisationMode
Area normalisation mode.
label findTargetFace(const label srcFacei, const UList< label > &excludeFaces=UList< label >::null(), const label srcFacePti=-1) const
dictionary dict
const faceAreaIntersect::triangulationMode triMode_
Face triangulation mode.
void writeIntersectionOBJ(const scalar area, const face &f1, const face &f2, const pointField &f1Points, const pointField &f2Points) const
Write triangle intersection to OBJ file.
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:68
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
const scalar maxDistance2_
Maximum squared distance.
static const Enum< areaNormalisationMode > areaNormalisationModeNames_
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Unit conversion functions.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
areaNormalisationMode areaNormalisationMode_
Area normalisation mode; default = project.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
bool initialiseWalk(label &srcFacei, label &tgtFacei)
Initialise walk and return true if all ok.
A bounding box defined in terms of min/max extrema points.
Definition: boundBox.H:63
static int & msgType() noexcept
Message tag of standard messages.
Definition: UPstream.H:1229
This class describes the interaction of an object (often a face) and a point. It carries the info of ...
Definition: pointIndexHit.H:44
void writeOBJ(Ostream &os, const point &pt)
Write obj representation of a point.
Definition: meshTools.C:196
void createExtendedTgtPatch()
Create a map that extends tgtPatch so that it covers srcPatch.
label comm_
Communicator to use for parallel operations.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
const scalar minCosAngle_
Minimum (cos of) angle. 1 for perfectly matching.
virtual void write(Ostream &os) const
Write.
const primitivePatch & tgtPatch() const
Return const access to the target patch.
const Field< point_type > & faceNormals() const
Return face unit normals for patch.
scalar magSqr() const
The magnitude/length squared of bounding box diagonal.
Definition: boundBoxI.H:204
A list of faces which address into the list of points.
unsigned int count(const UList< bool > &bools, const bool val=true)
Count number of &#39;true&#39; entries.
Definition: BitOps.H:73
const point_type & point() const noexcept
Return point, no checks.
A List obtained as a section of another List.
Definition: SubList.H:50
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
virtual bool calculate(const primitivePatch &srcPatch, const primitivePatch &tgtPatch, const autoPtr< searchableSurface > &surfPtr=nullptr)
Update addressing, weights and (optional) centroids.
dimensionedScalar cos(const dimensionedScalar &ds)
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
const pointField & points
static autoPtr< indexedOctree< treeDataPoint > > createTree(const pointField &points)
Construct search tree for points.
const wordList area
Standard area field types (scalar, vector, tensor, etc)
const Field< point_type > & faceCentres() const
Return face centres for patch.
Vector< scalar > vector
Definition: vector.H:57
label index() const noexcept
Return the hit index.
errorManip< error > abort(error &err)
Definition: errorManip.H:139
Ostream & writeEntryIfDifferent(const word &key, const T &value1, const T &value2)
Write a keyword/value entry only when the two values differ.
Definition: Ostream.H:336
#define DebugInfo
Report an information message using Foam::Info.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
bool isCandidate(const label srcFacei, const label tgtFacei) const
Is source/target a valid pair (i.e. not too far/different.
int debug
Static debugging option.
OBJstream os(runTime.globalPath()/outputName)
defineTypeNameAndDebug(combustionModel, 0)
static void triangleFan(const face &f, DynamicList< face > &faces)
Decompose face into triangle fan.
labelList f(nPoints)
PrimitivePatch< SubList< face >, const pointField & > primitivePatch
A PrimitivePatch with a SubList addressing for the faces, const reference for the point field...
void checkPatches() const
Check AMI patch coupling.
void appendNbrFaces(const label facei, const primitivePatch &patch, const DynamicList< label > &visitedFaces, DynamicList< label > &faceIDs) const
Add faces neighbouring facei to the ID list.
virtual void nonConformalCorrection()
Correction for non-conformal interpolations, e.g. for ACMI.
virtual bool calculate(const primitivePatch &srcPatch, const primitivePatch &tgtPatch, const autoPtr< searchableSurface > &surfPtr=nullptr)
Update addressing, weights and (optional) centroids.
bool hit() const noexcept
Is there a hit?
bool requireMatch_
Flag to indicate that the two patches must be matched/an overlap exists between them.
vector point
Point is a vector.
Definition: point.H:37
#define WarningInFunction
Report a warning using Foam::Warning.
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: error.H:64
void triangulatePatch(const primitivePatch &patch, List< DynamicList< face >> &tris, List< scalar > &magSf) const
Helper function to decompose a patch.
Base class for Arbitrary Mesh Interface (AMI) methods.
Interpolation class dealing with transfer of data between two primitive patches with an arbitrary mes...
const std::string patch
OpenFOAM patch number as a std::string.
triangle< point, const point & > triPointRef
A triangle using referred points.
Definition: triangleFwd.H:39
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
Reduce inplace (cf. MPI Allreduce) using specified communication schedule.
label n
static const Enum< triangulationMode > triangulationModeNames_
Pointer management similar to std::unique_ptr, with some additional methods and type checking...
Definition: HashPtrTable.H:48
List< label > labelList
A List of labels.
Definition: List.H:62
static autoPtr< T > New(Args &&... args)
Construct autoPtr with forwarding arguments.
Definition: autoPtr.H:178
point centre() const
The centre (midpoint) of the bounding box.
Definition: boundBoxI.H:186
constexpr scalar degToRad(const scalar deg) noexcept
Conversion from degrees to radians.
const primitivePatch & srcPatch() const
Return const access to the source patch.
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Namespace for OpenFOAM.
virtual void write(Ostream &os) const
Write AMI as a dictionary.
advancingFrontAMI(const dictionary &dict, const bool reverseTarget)
Construct from components.