nearestFaceAMI.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) 2020-2022 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "nearestFaceAMI.H"
30 
31 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
32 
33 namespace Foam
34 {
35  defineTypeNameAndDebug(nearestFaceAMI, 0);
36  addToRunTimeSelectionTable(AMIInterpolation, nearestFaceAMI, dict);
37  addToRunTimeSelectionTable(AMIInterpolation, nearestFaceAMI, component);
38 }
39 
40 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
41 
42 Foam::autoPtr<Foam::mapDistribute> Foam::nearestFaceAMI::calcFaceMap
43 (
44  const List<nearestAndDist>& localInfo,
45  const primitivePatch& srcPatch,
46  const primitivePatch& tgtPatch
47 ) const
48 {
49  // Generate the list of processor bounding boxes for tgtPatch
50  List<boundBox> procBbs(Pstream::nProcs());
51  procBbs[Pstream::myProcNo()] =
52  boundBox(tgtPatch.points(), tgtPatch.meshPoints(), true);
53  Pstream::allGatherList(procBbs);
54 
55  // Identify which of my local src faces intersect with each processor
56  // tgtPatch bb within the current match's search distance
57  const pointField& srcCcs = srcPatch.faceCentres();
58  List<DynamicList<label>> dynSendMap(Pstream::nProcs());
59 
60  forAll(localInfo, srcFacei)
61  {
62  // Test local srcPatch face centres against remote processor tgtPatch bb
63  // using distance from initial pass
64 
65  const scalar r2 = localInfo[srcFacei].second();
66 
67  forAll(procBbs, proci)
68  {
69  if (proci != Pstream::myProcNo())
70  {
71  if (procBbs[proci].overlaps(srcCcs[srcFacei], r2))
72  {
73  dynSendMap[proci].append(srcFacei);
74  }
75  }
76  }
77  }
78 
79  // Convert dynamicList to labelList
80  labelListList sendMap(Pstream::nProcs());
81  forAll(sendMap, proci)
82  {
83  sendMap[proci].transfer(dynSendMap[proci]);
84 
85  if (debug)
86  {
87  Pout<< "send map - to proc " << proci << " sending "
88  << sendMap[proci].size() << " elements" << endl;
89  }
90  }
91 
92  return autoPtr<mapDistribute>::New(std::move(sendMap));
93 }
94 
95 
96 Foam::autoPtr<Foam::mapDistribute> Foam::nearestFaceAMI::calcDistributed
97 (
98  const primitivePatch& src,
99  const primitivePatch& tgt,
100  labelListList& srcToTgtAddr,
101  scalarListList& srcToTgtWght
102 ) const
103 {
104  autoPtr<indexedOctree<treeType>> tgtTreePtr;
105  if (tgt.size())
106  {
107  tgtTreePtr = this->createTree(tgt);
108  }
109 
110  // Create global indexing for tgtPatch
111  globalIndex globalTgtCells(tgt.size());
112 
113 
114  const label myRank = UPstream::myProcNo(comm_);
115 
116 
117  // First pass
118  // ==========
119  // For each srcPatch face, determine local match on tgtPatch
120 
121  // Identify local nearest matches
122  pointField srcCcs(src.faceCentres());
123 
124  List<nearestAndDist> localInfo(src.size());
125  if (tgt.size())
126  {
127  const auto& tgtTree = tgtTreePtr();
128 
129  forAll(srcCcs, srcCelli)
130  {
131  const point& srcCc = srcCcs[srcCelli];
132 
133  pointIndexHit& test = localInfo[srcCelli].first();
134  test = tgtTree.findNearest(srcCc, GREAT);
135 
136  if (test.hit())
137  {
138  // With a search radius2 of GREAT all cells should receive a hit
139  localInfo[srcCelli].second() = test.point().distSqr(srcCc);
140  test.setIndex(globalTgtCells.toGlobal(myRank, test.index()));
141  }
142  }
143  }
144  else
145  {
146  // No local tgtPatch faces - initialise nearest distance for all
147  // srcPatch faces to GREAT so that they [should be] set by remote
148  // tgtPatch faces
149  for (auto& info : localInfo)
150  {
151  info.second() = GREAT;
152  }
153  }
154 
155 
156  // Second pass
157  // ===========
158  // Determine remote matches
159 
160  // Map returns labels of src patch faces to send to each proc
161  autoPtr<mapDistribute> mapPtr = calcFaceMap(localInfo, src, tgt);
162  mapDistribute& map = mapPtr();
163 
164  List<nearestAndDist> remoteInfo(localInfo);
165  map.distribute(remoteInfo);
166 
167  // Note: re-using srcCcs
168  map.distribute(srcCcs);
169 
170  if (tgt.size())
171  {
172  const auto& tgtTree = tgtTreePtr();
173 
174  // Test remote srcPatch faces against local tgtPatch faces
175  nearestAndDist testInfo;
176  pointIndexHit& test = testInfo.first();
177  forAll(remoteInfo, i)
178  {
179  test = tgtTree.findNearest(srcCcs[i], remoteInfo[i].second());
180  if (test.hit())
181  {
182  test.setIndex(globalTgtCells.toGlobal(myRank, test.index()));
183  testInfo.second() = test.point().distSqr(srcCcs[i]);
184  nearestEqOp()(remoteInfo[i], testInfo);
185  }
186  }
187  }
188 
189  // Send back to originating processor. Choose best if sent to multiple
190  // processors. Note that afterwards all unused entries have the unique
191  // value nearestZero (distance < 0). This is used later on to see if
192  // the sample was sent to any processor.
193  const nearestAndDist nearestZero(pointIndexHit(), -GREAT);
195  (
198  src.size(),
199  map.constructMap(),
200  map.constructHasFlip(),
201  map.subMap(),
202  map.subHasFlip(),
203  remoteInfo,
204  nearestZero,
205  nearestEqOp(),
206  identityOp(), // No flipping
208  comm_
209  );
210 
211 
212  // Third pass
213  // ==========
214  // Combine local and remote info and filter out any connections that are
215  // further away than threshold distance squared
216 
217  srcToTgtAddr.setSize(src.size());
218  srcToTgtWght.setSize(src.size());
219  forAll(srcToTgtAddr, srcFacei)
220  {
221  nearestEqOp()(localInfo[srcFacei], remoteInfo[srcFacei]);
222  if (localInfo[srcFacei].second() < maxDistance2_)
223  {
224  const label tgtFacei = localInfo[srcFacei].first().index();
225  srcToTgtAddr[srcFacei] = labelList(1, tgtFacei);
226  srcToTgtWght[srcFacei] = scalarList(1, 1.0);
227  }
228  }
229 
230  List<Map<label>> cMap;
232  (
233  globalTgtCells,
234  srcToTgtAddr,
235  cMap,
237  comm_
238  );
239 }
240 
241 
242 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
243 
245 (
246  const dictionary& dict,
247  const bool reverseTarget
248 )
249 :
250  AMIInterpolation(dict, reverseTarget),
251  maxDistance2_(dict.getOrDefault<scalar>("maxDistance2", GREAT))
252 {}
253 
254 
256 (
257  const bool requireMatch,
258  const bool reverseTarget,
259  const scalar lowWeightCorrection
260 )
261 :
262  AMIInterpolation(requireMatch, reverseTarget, lowWeightCorrection),
263  maxDistance2_(GREAT)
264 {}
265 
266 
268 :
269  AMIInterpolation(ami),
270  maxDistance2_(ami.maxDistance2_)
271 {}
272 
273 
274 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
275 
277 (
278  const primitivePatch& srcPatch,
279  const primitivePatch& tgtPatch,
280  const autoPtr<searchableSurface>& surfPtr
281 )
282 {
283  if (upToDate_)
284  {
285  return false;
286  }
287 
288  AMIInterpolation::calculate(srcPatch, tgtPatch, surfPtr);
289 
290  const auto& src = this->srcPatch0();
291  const auto& tgt = this->tgtPatch0();
292 
293  // Set face area magnitudes
294  srcMagSf_ = mag(src.faceAreas());
295  tgtMagSf_ = mag(tgt.faceAreas());
296 
297  // TODO: implement symmetric calculation controls; assume yes for now
298  bool symmetric_ = true;
299 
300  if (this->distributed())
301  {
302  tgtMapPtr_ =
303  calcDistributed
304  (
305  src,
306  tgt,
307  srcAddress_,
308  srcWeights_
309  );
310 
311  if (symmetric_)
312  {
313  srcMapPtr_ =
314  calcDistributed
315  (
316  tgt,
317  src,
318  tgtAddress_,
319  tgtWeights_
320  );
321  }
322  }
323  else
324  {
325  srcAddress_.setSize(src.size());
326  srcWeights_.setSize(src.size());
327 
328  if (symmetric_)
329  {
330  tgtAddress_.setSize(tgt.size());
331  tgtWeights_.setSize(tgt.size());
332  }
333 
334  const pointField& srcCcs = src.faceCentres();
335  const pointField& tgtCcs = tgt.faceCentres();
336 
337  const auto tgtTreePtr = this->createTree(tgtPatch);
338  const auto& tgtTree = tgtTreePtr();
339 
340  forAll(srcCcs, srcFacei)
341  {
342  const point& srcCc = srcCcs[srcFacei];
343  const pointIndexHit hit = tgtTree.findNearest(srcCc, GREAT);
344 
345  if
346  (
347  hit.hit()
348  && (magSqr(srcCc - tgtCcs[hit.index()]) < maxDistance2_)
349  )
350  {
351  label tgtFacei = hit.index();
352  srcAddress_[srcFacei] = labelList(1, tgtFacei);
353  srcWeights_[srcFacei] = scalarList(1, 1.0);
354 
355  if (symmetric_)
356  {
357  tgtAddress_[tgtFacei] = labelList(1, srcFacei);
358  tgtWeights_[tgtFacei] = scalarList(1, 1.0);
359  }
360  }
361  else
362  {
363  if (debug)
364  {
366  << "Unable to find target face for source face "
367  << srcFacei << endl;
368  }
369  }
370  }
371 
372  if (symmetric_)
373  {
374  const auto srcTreePtr = this->createTree(srcPatch);
375  const auto& srcTree = srcTreePtr();
376 
377  // Check that all source cells have connections and populate any
378  // missing entries
379  forAll(tgtWeights_, tgtCelli)
380  {
381  if (tgtAddress_[tgtCelli].empty())
382  {
383  const point& tgtCc = tgtCcs[tgtCelli];
384  pointIndexHit hit = srcTree.findNearest(tgtCc, GREAT);
385 
386  if
387  (
388  hit.hit()
389  && (magSqr(tgtCc - srcCcs[hit.index()]) < maxDistance2_)
390  )
391  {
392  tgtAddress_[tgtCelli] = labelList(1, hit.index());
393  tgtWeights_[tgtCelli] = scalarList(1, 1.0);
394  }
395  }
396  else
397  {
398  if (debug)
399  {
401  << "Unable to find source face for target face "
402  << tgtCelli << endl;
403  }
404  }
405  }
406  }
407  }
408 
409  srcWeightsSum_.setSize(srcWeights_.size(), 1);
410  tgtWeightsSum_.setSize(tgtWeights_.size(), 1);
412  upToDate_ = true;
413 
414  return upToDate_;
415 }
416 
417 
419 {
421  os.writeEntryIfDifferent<scalar>("maxDistance2", GREAT, maxDistance2_);
422 }
423 
424 
425 // ************************************************************************* //
List< scalar > scalarList
List of scalar.
Definition: scalarList.H:32
dictionary dict
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
PointIndexHit< point > pointIndexHit
A PointIndexHit with a 3D point.
Definition: pointIndexHit.H:58
static int & msgType() noexcept
Message tag of standard messages.
Definition: UPstream.H:1229
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Can be negative if the process i...
Definition: UPstream.H:1074
static const List< T > & null()
Return a null List.
Definition: ListI.H:130
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
Macros for easy insertion into run-time selection tables.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
static void allGatherList(List< T > &values, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Gather data, but keep individual values separate. Uses linear/tree communication. ...
A list of faces which address into the list of points.
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator). It is 1 for serial run. ...
Definition: UPstream.H:1065
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.
static autoPtr< indexedOctree< treeDataPoint > > createTree(const pointField &points)
Construct search tree for points.
List< scalarList > scalarListList
List of scalarList.
Definition: scalarList.H:35
Tuple2< pointIndexHit, scalar > nearestAndDist
Combine operator for nearest.
Nearest-face Arbitrary Mesh Interface (AMI) method.
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
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:56
int debug
Static debugging option.
OBJstream os(runTime.globalPath()/outputName)
defineTypeNameAndDebug(combustionModel, 0)
PrimitivePatch< SubList< face >, const pointField & > primitivePatch
A PrimitivePatch with a SubList addressing for the faces, const reference for the point field...
vector point
Point is a vector.
Definition: point.H:37
#define WarningInFunction
Report a warning using Foam::Warning.
Interpolation class dealing with transfer of data between two primitive patches with an arbitrary mes...
"nonBlocking" : (MPI_Isend, MPI_Irecv)
virtual bool calculate(const primitivePatch &srcPatch, const primitivePatch &tgtPatch, const autoPtr< searchableSurface > &surfPtr=nullptr)
Update addressing and weights.
virtual void write(Ostream &os) const
Write.
nearestFaceAMI(const dictionary &dict, const bool reverseTarget=false)
Construct from dictionary.
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
const nearestAndDist nearestZero(nearestAndDist(pointIndexHit(), -GREAT))
void component(FieldField< Field, typename FieldField< Field, Type >::cmptType > &sf, const FieldField< Field, Type > &f, const direction d)
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
static void distribute(const UPstream::commsTypes commsType, const List< labelPair > &schedule, const label constructSize, const labelListList &subMap, const bool subHasFlip, const labelListList &constructMap, const bool constructHasFlip, List< T > &field, const T &nullValue, const CombineOp &cop, const NegateOp &negOp, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Distribute combine data with specified combine operation and negate operator (for flips)...
Namespace for OpenFOAM.
virtual void write(Ostream &os) const
Write AMI as a dictionary.
addToRunTimeSelectionTable(functionObject, pointHistory, dictionary)