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  // First pass
115  // ==========
116  // For each srcPatch face, determine local match on tgtPatch
117 
118  // Identify local nearest matches
119  pointField srcCcs(src.faceCentres());
120 
121  List<nearestAndDist> localInfo(src.size());
122  if (tgt.size())
123  {
124  const auto& tgtTree = tgtTreePtr();
125 
126  forAll(srcCcs, srcCelli)
127  {
128  const point& srcCc = srcCcs[srcCelli];
129 
130  pointIndexHit& test = localInfo[srcCelli].first();
131  test = tgtTree.findNearest(srcCc, GREAT);
132 
133  if (test.hit())
134  {
135  // With a search radius2 of GREAT all cells should receive a hit
136  localInfo[srcCelli].second() = test.point().distSqr(srcCc);
137  test.setIndex(globalTgtCells.toGlobal(test.index()));
138  }
139  }
140  }
141  else
142  {
143  // No local tgtPatch faces - initialise nearest distance for all
144  // srcPatch faces to GREAT so that they [should be] set by remote
145  // tgtPatch faces
146  for (auto& info : localInfo)
147  {
148  info.second() = GREAT;
149  }
150  }
151 
152 
153  // Second pass
154  // ===========
155  // Determine remote matches
156 
157  // Map returns labels of src patch faces to send to each proc
158  autoPtr<mapDistribute> mapPtr = calcFaceMap(localInfo, src, tgt);
159  mapDistribute& map = mapPtr();
160 
161  List<nearestAndDist> remoteInfo(localInfo);
162  map.distribute(remoteInfo);
163 
164  // Note: re-using srcCcs
165  map.distribute(srcCcs);
166 
167  if (tgt.size())
168  {
169  const auto& tgtTree = tgtTreePtr();
170 
171  // Test remote srcPatch faces against local tgtPatch faces
172  nearestAndDist testInfo;
173  pointIndexHit& test = testInfo.first();
174  forAll(remoteInfo, i)
175  {
176  test = tgtTree.findNearest(srcCcs[i], remoteInfo[i].second());
177  if (test.hit())
178  {
179  test.setIndex(globalTgtCells.toGlobal(test.index()));
180  testInfo.second() = test.point().distSqr(srcCcs[i]);
181  nearestEqOp()(remoteInfo[i], testInfo);
182  }
183  }
184  }
185 
186  // Send back to originating processor. Choose best if sent to multiple
187  // processors. Note that afterwards all unused entries have the unique
188  // value nearestZero (distance < 0). This is used later on to see if
189  // the sample was sent to any processor.
190  const nearestAndDist nearestZero(pointIndexHit(), -GREAT);
192  (
195  src.size(),
196  map.constructMap(),
197  map.constructHasFlip(),
198  map.subMap(),
199  map.subHasFlip(),
200  remoteInfo,
201  nearestZero,
202  nearestEqOp(),
203  identityOp() // No flipping
204  );
205 
206 
207  // Third pass
208  // ==========
209  // Combine local and remote info and filter out any connections that are
210  // further away than threshold distance squared
211 
212  srcToTgtAddr.setSize(src.size());
213  srcToTgtWght.setSize(src.size());
214  forAll(srcToTgtAddr, srcFacei)
215  {
216  nearestEqOp()(localInfo[srcFacei], remoteInfo[srcFacei]);
217  if (localInfo[srcFacei].second() < maxDistance2_)
218  {
219  const label tgtFacei = localInfo[srcFacei].first().index();
220  srcToTgtAddr[srcFacei] = labelList(1, tgtFacei);
221  srcToTgtWght[srcFacei] = scalarList(1, 1.0);
222  }
223  }
224 
225  List<Map<label>> cMap;
226  return autoPtr<mapDistribute>::New(globalTgtCells, srcToTgtAddr, cMap);
227 }
228 
229 
230 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
231 
233 (
234  const dictionary& dict,
235  const bool reverseTarget
236 )
237 :
238  AMIInterpolation(dict, reverseTarget),
239  maxDistance2_(dict.getOrDefault<scalar>("maxDistance2", GREAT))
240 {}
241 
242 
244 (
245  const bool requireMatch,
246  const bool reverseTarget,
247  const scalar lowWeightCorrection
248 )
249 :
250  AMIInterpolation(requireMatch, reverseTarget, lowWeightCorrection),
251  maxDistance2_(GREAT)
252 {}
253 
254 
256 :
257  AMIInterpolation(ami),
258  maxDistance2_(ami.maxDistance2_)
259 {}
260 
261 
262 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
263 
265 (
266  const primitivePatch& srcPatch,
267  const primitivePatch& tgtPatch,
268  const autoPtr<searchableSurface>& surfPtr
269 )
270 {
271  if (upToDate_)
272  {
273  return false;
274  }
275 
276  AMIInterpolation::calculate(srcPatch, tgtPatch, surfPtr);
277 
278  const auto& src = this->srcPatch0();
279  const auto& tgt = this->tgtPatch0();
280 
281  // Set face area magnitudes
282  srcMagSf_ = mag(src.faceAreas());
283  tgtMagSf_ = mag(tgt.faceAreas());
284 
285  // TODO: implement symmetric calculation controls; assume yes for now
286  bool symmetric_ = true;
287 
288  if (this->distributed())
289  {
290  tgtMapPtr_ =
291  calcDistributed
292  (
293  src,
294  tgt,
295  srcAddress_,
296  srcWeights_
297  );
298 
299  if (symmetric_)
300  {
301  srcMapPtr_ =
302  calcDistributed
303  (
304  tgt,
305  src,
306  tgtAddress_,
307  tgtWeights_
308  );
309  }
310  }
311  else
312  {
313  srcAddress_.setSize(src.size());
314  srcWeights_.setSize(src.size());
315 
316  if (symmetric_)
317  {
318  tgtAddress_.setSize(tgt.size());
319  tgtWeights_.setSize(tgt.size());
320  }
321 
322  const pointField& srcCcs = src.faceCentres();
323  const pointField& tgtCcs = tgt.faceCentres();
324 
325  const auto tgtTreePtr = this->createTree(tgtPatch);
326  const auto& tgtTree = tgtTreePtr();
327 
328  forAll(srcCcs, srcFacei)
329  {
330  const point& srcCc = srcCcs[srcFacei];
331  const pointIndexHit hit = tgtTree.findNearest(srcCc, GREAT);
332 
333  if
334  (
335  hit.hit()
336  && (magSqr(srcCc - tgtCcs[hit.index()]) < maxDistance2_)
337  )
338  {
339  label tgtFacei = hit.index();
340  srcAddress_[srcFacei] = labelList(1, tgtFacei);
341  srcWeights_[srcFacei] = scalarList(1, 1.0);
342 
343  if (symmetric_)
344  {
345  tgtAddress_[tgtFacei] = labelList(1, srcFacei);
346  tgtWeights_[tgtFacei] = scalarList(1, 1.0);
347  }
348  }
349  else
350  {
351  if (debug)
352  {
354  << "Unable to find target face for source face "
355  << srcFacei << endl;
356  }
357  }
358  }
359 
360  if (symmetric_)
361  {
362  const auto srcTreePtr = this->createTree(srcPatch);
363  const auto& srcTree = srcTreePtr();
364 
365  // Check that all source cells have connections and populate any
366  // missing entries
367  forAll(tgtWeights_, tgtCelli)
368  {
369  if (tgtAddress_[tgtCelli].empty())
370  {
371  const point& tgtCc = tgtCcs[tgtCelli];
372  pointIndexHit hit = srcTree.findNearest(tgtCc, GREAT);
373 
374  if
375  (
376  hit.hit()
377  && (magSqr(tgtCc - srcCcs[hit.index()]) < maxDistance2_)
378  )
379  {
380  tgtAddress_[tgtCelli] = labelList(1, hit.index());
381  tgtWeights_[tgtCelli] = scalarList(1, 1.0);
382  }
383  }
384  else
385  {
386  if (debug)
387  {
389  << "Unable to find source face for target face "
390  << tgtCelli << endl;
391  }
392  }
393  }
394  }
395  }
396 
397  srcWeightsSum_.setSize(srcWeights_.size(), 1);
398  tgtWeightsSum_.setSize(tgtWeights_.size(), 1);
400  upToDate_ = true;
401 
402  return upToDate_;
403 }
404 
405 
407 {
409  os.writeEntryIfDifferent<scalar>("maxDistance2", GREAT, maxDistance2_);
410 }
411 
412 
413 // ************************************************************************* //
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:120
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
PointIndexHit< point > pointIndexHit
A PointIndexHit with a 3D point.
Definition: pointIndexHit.H:58
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:1029
static const List< T > & null()
Return a null List.
Definition: ListI.H:102
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:414
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:1020
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:327
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:55
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...
addToRunTimeSelectionTable(decompositionMethod, kahipDecomp, dictionary)
vector point
Point is a vector.
Definition: point.H:37
#define WarningInFunction
Report a warning using Foam::Warning.
static void distribute(const Pstream::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 NegateOp &negOp, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Distribute data with specified negate operator (for flips).
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)
Namespace for OpenFOAM.
virtual void write(Ostream &os) const
Write.