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-2024 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(comm()));
51  procBbs[Pstream::myProcNo(comm())] =
52  boundBox(tgtPatch.points(), tgtPatch.meshPoints(), false);
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(comm()));
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(comm()))
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
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 
93  (
94  std::move(sendMap),
95  false,
96  false,
97  comm()
98  );
99 }
100 
101 
102 Foam::autoPtr<Foam::mapDistribute> Foam::nearestFaceAMI::calcDistributed
103 (
104  const primitivePatch& src,
105  const primitivePatch& tgt,
106  labelListList& srcToTgtAddr,
107  scalarListList& srcToTgtWght
108 ) const
109 {
110  autoPtr<indexedOctree<treeType>> tgtTreePtr;
111  if (tgt.size())
112  {
113  tgtTreePtr = this->createTree(tgt);
114  }
115 
116  // Create global indexing for tgtPatch
117  globalIndex globalTgtCells(tgt.size(), comm());
118 
119 
120  const label myRank = UPstream::myProcNo(comm());
121 
122 
123  // First pass
124  // ==========
125  // For each srcPatch face, determine local match on tgtPatch
126 
127  // Identify local nearest matches
128  pointField srcCcs(src.faceCentres());
129 
130  List<nearestAndDist> localInfo(src.size());
131  if (tgt.size())
132  {
133  const auto& tgtTree = tgtTreePtr();
134 
135  forAll(srcCcs, srcCelli)
136  {
137  const point& srcCc = srcCcs[srcCelli];
138 
139  pointIndexHit& test = localInfo[srcCelli].first();
140  test = tgtTree.findNearest(srcCc, GREAT);
141 
142  if (test.hit())
143  {
144  // With a search radius2 of GREAT all cells should receive a hit
145  localInfo[srcCelli].second() = test.point().distSqr(srcCc);
146  test.setIndex(globalTgtCells.toGlobal(myRank, test.index()));
147  }
148  }
149  }
150  else
151  {
152  // No local tgtPatch faces - initialise nearest distance for all
153  // srcPatch faces to GREAT so that they [should be] set by remote
154  // tgtPatch faces
155  for (auto& info : localInfo)
156  {
157  info.second() = GREAT;
158  }
159  }
160 
161 
162  // Second pass
163  // ===========
164  // Determine remote matches
165 
166  // Map returns labels of src patch faces to send to each proc
167  autoPtr<mapDistribute> mapPtr = calcFaceMap(localInfo, src, tgt);
168  mapDistribute& map = mapPtr();
169 
170  List<nearestAndDist> remoteInfo(localInfo);
171  map.distribute(remoteInfo);
172 
173  // Note: re-using srcCcs
174  map.distribute(srcCcs);
175 
176  if (tgt.size())
177  {
178  const auto& tgtTree = tgtTreePtr();
179 
180  // Test remote srcPatch faces against local tgtPatch faces
181  nearestAndDist testInfo;
182  pointIndexHit& test = testInfo.first();
183  forAll(remoteInfo, i)
184  {
185  test = tgtTree.findNearest(srcCcs[i], remoteInfo[i].second());
186  if (test.hit())
187  {
188  test.setIndex(globalTgtCells.toGlobal(myRank, test.index()));
189  testInfo.second() = test.point().distSqr(srcCcs[i]);
190  nearestEqOp()(remoteInfo[i], testInfo);
191  }
192  }
193  }
194 
195  // Send back to originating processor. Choose best if sent to multiple
196  // processors. Note that afterwards all unused entries have the unique
197  // value nearestZero (distance < 0). This is used later on to see if
198  // the sample was sent to any processor.
199  const nearestAndDist nearestZero(pointIndexHit(), -GREAT);
201  (
204  src.size(),
205  map.constructMap(),
206  map.constructHasFlip(),
207  map.subMap(),
208  map.subHasFlip(),
209  remoteInfo,
210  nearestZero,
211  nearestEqOp(),
212  identityOp(), // No flipping
214  comm()
215  );
216 
217 
218  // Third pass
219  // ==========
220  // Combine local and remote info and filter out any connections that are
221  // further away than threshold distance squared
222 
223  srcToTgtAddr.setSize(src.size());
224  srcToTgtWght.setSize(src.size());
225  forAll(srcToTgtAddr, srcFacei)
226  {
227  nearestEqOp()(localInfo[srcFacei], remoteInfo[srcFacei]);
228  if (localInfo[srcFacei].second() < maxDistance2_)
229  {
230  const label tgtFacei = localInfo[srcFacei].first().index();
231  srcToTgtAddr[srcFacei] = labelList(1, tgtFacei);
232  srcToTgtWght[srcFacei] = scalarList(1, 1.0);
233  }
234  }
235 
236  List<Map<label>> cMap;
238  (
239  globalTgtCells,
240  srcToTgtAddr,
241  cMap,
243  comm()
244  );
245 }
246 
247 
248 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
249 
251 (
252  const dictionary& dict,
253  const bool reverseTarget
254 )
255 :
256  AMIInterpolation(dict, reverseTarget),
257  maxDistance2_(dict.getOrDefault<scalar>("maxDistance2", GREAT))
258 {}
259 
260 
262 (
263  const bool requireMatch,
264  const bool reverseTarget,
265  const scalar lowWeightCorrection
266 )
267 :
268  AMIInterpolation(requireMatch, reverseTarget, lowWeightCorrection),
269  maxDistance2_(GREAT)
270 {}
271 
272 
274 :
275  AMIInterpolation(ami),
276  maxDistance2_(ami.maxDistance2_)
277 {}
278 
279 
280 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
281 
283 (
284  const primitivePatch& srcPatch,
285  const primitivePatch& tgtPatch,
286  const autoPtr<searchableSurface>& surfPtr
287 )
288 {
289  if (upToDate_)
290  {
291  return false;
292  }
293 
294  AMIInterpolation::calculate(srcPatch, tgtPatch, surfPtr);
295 
296  const auto& src = this->srcPatch0();
297  const auto& tgt = this->tgtPatch0();
298 
299  // Set face area magnitudes
300  srcMagSf_ = mag(src.faceAreas());
301  tgtMagSf_ = mag(tgt.faceAreas());
302 
303  // TODO: implement symmetric calculation controls; assume yes for now
304  bool symmetric_ = true;
305 
306  if (this->distributed() && comm() != -1)
307  {
308  tgtMapPtr_ =
309  calcDistributed
310  (
311  src,
312  tgt,
313  srcAddress_,
314  srcWeights_
315  );
316 
317  if (symmetric_)
318  {
319  srcMapPtr_ =
320  calcDistributed
321  (
322  tgt,
323  src,
324  tgtAddress_,
325  tgtWeights_
326  );
327  }
328  }
329  else
330  {
331  srcAddress_.setSize(src.size());
332  srcWeights_.setSize(src.size());
333 
334  if (symmetric_)
335  {
336  tgtAddress_.setSize(tgt.size());
337  tgtWeights_.setSize(tgt.size());
338  }
339 
340  const pointField& srcCcs = src.faceCentres();
341  const pointField& tgtCcs = tgt.faceCentres();
342 
343  const auto tgtTreePtr = this->createTree(tgtPatch);
344  const auto& tgtTree = tgtTreePtr();
345 
346  forAll(srcCcs, srcFacei)
347  {
348  const point& srcCc = srcCcs[srcFacei];
349  const pointIndexHit hit = tgtTree.findNearest(srcCc, GREAT);
350 
351  if
352  (
353  hit.hit()
354  && (magSqr(srcCc - tgtCcs[hit.index()]) < maxDistance2_)
355  )
356  {
357  label tgtFacei = hit.index();
358  srcAddress_[srcFacei] = labelList(1, tgtFacei);
359  srcWeights_[srcFacei] = scalarList(1, 1.0);
360 
361  if (symmetric_)
362  {
363  tgtAddress_[tgtFacei] = labelList(1, srcFacei);
364  tgtWeights_[tgtFacei] = scalarList(1, 1.0);
365  }
366  }
367  else
368  {
369  if (debug)
370  {
372  << "Unable to find target face for source face "
373  << srcFacei << endl;
374  }
375  }
376  }
377 
378  if (symmetric_)
379  {
380  const auto srcTreePtr = this->createTree(srcPatch);
381  const auto& srcTree = srcTreePtr();
382 
383  // Check that all source cells have connections and populate any
384  // missing entries
385  forAll(tgtWeights_, tgtCelli)
386  {
387  if (tgtAddress_[tgtCelli].empty())
388  {
389  const point& tgtCc = tgtCcs[tgtCelli];
390  pointIndexHit hit = srcTree.findNearest(tgtCc, GREAT);
391 
392  if
393  (
394  hit.hit()
395  && (magSqr(tgtCc - srcCcs[hit.index()]) < maxDistance2_)
396  )
397  {
398  tgtAddress_[tgtCelli] = labelList(1, hit.index());
399  tgtWeights_[tgtCelli] = scalarList(1, 1.0);
400  }
401  }
402  else
403  {
404  if (debug)
405  {
407  << "Unable to find source face for target face "
408  << tgtCelli << endl;
409  }
410  }
411  }
412  }
413  }
414 
415  srcWeightsSum_.setSize(srcWeights_.size(), 1);
416  tgtWeightsSum_.setSize(tgtWeights_.size(), 1);
418  upToDate_ = true;
419 
420  return upToDate_;
421 }
422 
423 
425 {
427  os.writeEntryIfDifferent<scalar>("maxDistance2", GREAT, maxDistance2_);
428 }
429 
430 
431 // ************************************************************************* //
List< scalar > scalarList
List of scalar.
Definition: scalarList.H:32
dictionary dict
label comm() const noexcept
Communicator (local or otherwise) for parallel operations.
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:529
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:1787
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Negative if the process is not a...
Definition: UPstream.H:1611
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:286
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:1602
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...
static void allGatherList(UList< T > &values, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Gather data, but keep individual values separate. Uses MPI_Allgather or manual communication.
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" (immediate) : (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:61
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)
static void distribute(const UPstream::commsTypes commsType, const UList< 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)...
static const List< T > & null() noexcept
Return a null List (reference to a nullObject). Behaves like an empty List.
Definition: List.H:158
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
Namespace for OpenFOAM.
virtual void write(Ostream &os) const
Write AMI as a dictionary.
addToRunTimeSelectionTable(functionObject, pointHistory, dictionary)