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 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::gatherList(procBbs);
54  Pstream::scatterList(procBbs);
55 
56  // Identify which of my local src faces intersect with each processor
57  // tgtPatch bb within the current match's search distance
58  const pointField& srcCcs = srcPatch.faceCentres();
59  List<DynamicList<label>> dynSendMap(Pstream::nProcs());
60 
61  forAll(localInfo, srcFacei)
62  {
63  // Test local srcPatch face centres against remote processor tgtPatch bb
64  // using distance from initial pass
65 
66  const scalar r2 = localInfo[srcFacei].second();
67 
68  forAll(procBbs, proci)
69  {
70  if (proci != Pstream::myProcNo())
71  {
72  if (procBbs[proci].overlaps(srcCcs[srcFacei], r2))
73  {
74  dynSendMap[proci].append(srcFacei);
75  }
76  }
77  }
78  }
79 
80  // Convert dynamicList to labelList
81  labelListList sendMap(Pstream::nProcs());
82  forAll(sendMap, proci)
83  {
84  dynSendMap[proci].shrink();
85  sendMap[proci].transfer(dynSendMap[proci]);
86 
87  if (debug)
88  {
89  Pout<< "send map - to proc " << proci << " sending "
90  << sendMap[proci].size() << " elements" << endl;
91  }
92  }
93 
94  return autoPtr<mapDistribute>::New(std::move(sendMap));
95 }
96 
97 
98 Foam::autoPtr<Foam::mapDistribute> Foam::nearestFaceAMI::calcDistributed
99 (
100  const primitivePatch& src,
101  const primitivePatch& tgt,
102  labelListList& srcToTgtAddr,
103  scalarListList& srcToTgtWght
104 ) const
105 {
106  autoPtr<indexedOctree<treeType>> tgtTreePtr;
107  if (tgt.size())
108  {
109  tgtTreePtr = this->createTree(tgt);
110  }
111 
112  // Create global indexing for tgtPatch
113  globalIndex globalTgtCells(tgt.size());
114 
115 
116  // First pass
117  // ==========
118  // For each srcPatch face, determine local match on tgtPatch
119 
120  // Identify local nearest matches
121  pointField srcCcs(src.faceCentres());
122 
123  List<nearestAndDist> localInfo(src.size());
124  if (tgt.size())
125  {
126  const auto& tgtTree = tgtTreePtr();
127 
128  forAll(srcCcs, srcCelli)
129  {
130  const point& srcCc = srcCcs[srcCelli];
131 
132  pointIndexHit& test = localInfo[srcCelli].first();
133  test = tgtTree.findNearest(srcCc, GREAT);
134 
135  if (test.hit())
136  {
137  // With a search radius2 of GREAT all cells should receive a hit
138  localInfo[srcCelli].second() = magSqr(srcCc - test.hitPoint());
139  test.setIndex(globalTgtCells.toGlobal(test.index()));
140  }
141  }
142  }
143  else
144  {
145  // No local tgtPatch faces - initialise nearest distance for all
146  // srcPatch faces to GREAT so that they [should be] set by remote
147  // tgtPatch faces
148  for (auto& info : localInfo)
149  {
150  info.second() = GREAT;
151  }
152  }
153 
154 
155  // Second pass
156  // ===========
157  // Determine remote matches
158 
159  // Map returns labels of src patch faces to send to each proc
160  autoPtr<mapDistribute> mapPtr = calcFaceMap(localInfo, src, tgt);
161  mapDistribute& map = mapPtr();
162 
163  List<nearestAndDist> remoteInfo(localInfo);
164  map.distribute(remoteInfo);
165 
166  // Note: re-using srcCcs
167  map.distribute(srcCcs);
168 
169  if (tgt.size())
170  {
171  const auto& tgtTree = tgtTreePtr();
172 
173  // Test remote srcPatch faces against local tgtPatch faces
174  nearestAndDist testInfo;
175  pointIndexHit& test = testInfo.first();
176  forAll(remoteInfo, i)
177  {
178  test = tgtTree.findNearest(srcCcs[i], remoteInfo[i].second());
179  if (test.hit())
180  {
181  test.setIndex(globalTgtCells.toGlobal(test.index()));
182  testInfo.second() = magSqr(test.hitPoint() - srcCcs[i]);
183  nearestEqOp()(remoteInfo[i], testInfo);
184  }
185  }
186  }
187 
188  // Send back to originating processor. Choose best if sent to multiple
189  // processors. Note that afterwards all unused entries have the unique
190  // value nearestZero (distance < 0). This is used later on to see if
191  // the sample was sent to any processor.
192  const nearestAndDist nearestZero(pointIndexHit(), -GREAT);
194  (
196  List<labelPair>(0),
197  src.size(),
198  map.constructMap(),
199  map.constructHasFlip(),
200  map.subMap(),
201  map.subHasFlip(),
202  remoteInfo,
203  nearestZero,
204  nearestEqOp(),
205  noOp() // no flipping
206  );
207 
208 
209  // Third pass
210  // ==========
211  // Combine local and remote info and filter out any connections that are
212  // further away than threshold distance squared
213 
214  srcToTgtAddr.setSize(src.size());
215  srcToTgtWght.setSize(src.size());
216  forAll(srcToTgtAddr, srcFacei)
217  {
218  nearestEqOp()(localInfo[srcFacei], remoteInfo[srcFacei]);
219  if (localInfo[srcFacei].second() < maxDistance2_)
220  {
221  const label tgtFacei = localInfo[srcFacei].first().index();
222  srcToTgtAddr[srcFacei] = labelList(1, tgtFacei);
223  srcToTgtWght[srcFacei] = scalarList(1, 1.0);
224  }
225  }
226 
227  List<Map<label>> cMap;
228  return autoPtr<mapDistribute>::New(globalTgtCells, srcToTgtAddr, cMap);
229 }
230 
231 
232 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
233 
235 (
236  const dictionary& dict,
237  const bool reverseTarget
238 )
239 :
240  AMIInterpolation(dict, reverseTarget),
241  maxDistance2_(dict.getOrDefault<scalar>("maxDistance2", GREAT))
242 {}
243 
244 
246 (
247  const bool requireMatch,
248  const bool reverseTarget,
249  const scalar lowWeightCorrection
250 )
251 :
252  AMIInterpolation(requireMatch, reverseTarget, lowWeightCorrection),
253  maxDistance2_(GREAT)
254 {}
255 
256 
258 :
259  AMIInterpolation(ami),
260  maxDistance2_(ami.maxDistance2_)
261 {}
262 
263 
264 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
265 
267 (
268  const primitivePatch& srcPatch,
269  const primitivePatch& tgtPatch,
270  const autoPtr<searchableSurface>& surfPtr
271 )
272 {
273  if (upToDate_)
274  {
275  return false;
276  }
277 
278  AMIInterpolation::calculate(srcPatch, tgtPatch, surfPtr);
279 
280  const auto& src = this->srcPatch0();
281  const auto& tgt = this->tgtPatch0();
282 
283  // Set face area magnitudes
284  srcMagSf_ = mag(src.faceAreas());
285  tgtMagSf_ = mag(tgt.faceAreas());
286 
287  // TODO: implement symmetric calculation controls; assume yes for now
288  bool symmetric_ = true;
289 
290  if (this->distributed())
291  {
292  tgtMapPtr_ =
293  calcDistributed
294  (
295  src,
296  tgt,
297  srcAddress_,
298  srcWeights_
299  );
300 
301  if (symmetric_)
302  {
303  srcMapPtr_ =
304  calcDistributed
305  (
306  tgt,
307  src,
308  tgtAddress_,
309  tgtWeights_
310  );
311  }
312  }
313  else
314  {
315  srcAddress_.setSize(src.size());
316  srcWeights_.setSize(src.size());
317 
318  if (symmetric_)
319  {
320  tgtAddress_.setSize(tgt.size());
321  tgtWeights_.setSize(tgt.size());
322  }
323 
324  const pointField& srcCcs = src.faceCentres();
325  const pointField& tgtCcs = tgt.faceCentres();
326 
327  const auto tgtTreePtr = this->createTree(tgtPatch);
328  const auto& tgtTree = tgtTreePtr();
329 
330  forAll(srcCcs, srcFacei)
331  {
332  const point& srcCc = srcCcs[srcFacei];
333  const pointIndexHit hit = tgtTree.findNearest(srcCc, GREAT);
334 
335  if
336  (
337  hit.hit()
338  && (magSqr(srcCc - tgtCcs[hit.index()]) < maxDistance2_)
339  )
340  {
341  label tgtFacei = hit.index();
342  srcAddress_[srcFacei] = labelList(1, tgtFacei);
343  srcWeights_[srcFacei] = scalarList(1, 1.0);
344 
345  if (symmetric_)
346  {
347  tgtAddress_[tgtFacei] = labelList(1, srcFacei);
348  tgtWeights_[tgtFacei] = scalarList(1, 1.0);
349  }
350  }
351  else
352  {
353  if (debug)
354  {
356  << "Unable to find target face for source face "
357  << srcFacei << endl;
358  }
359  }
360  }
361 
362  if (symmetric_)
363  {
364  const auto srcTreePtr = this->createTree(srcPatch);
365  const auto& srcTree = srcTreePtr();
366 
367  // Check that all source cells have connections and populate any
368  // missing entries
369  forAll(tgtWeights_, tgtCelli)
370  {
371  if (tgtAddress_[tgtCelli].empty())
372  {
373  const point& tgtCc = tgtCcs[tgtCelli];
374  pointIndexHit hit = srcTree.findNearest(tgtCc, GREAT);
375 
376  if
377  (
378  hit.hit()
379  && (magSqr(tgtCc - srcCcs[hit.index()]) < maxDistance2_)
380  )
381  {
382  tgtAddress_[tgtCelli] = labelList(1, hit.index());
383  tgtWeights_[tgtCelli] = scalarList(1, 1.0);
384  }
385  }
386  else
387  {
388  if (debug)
389  {
391  << "Unable to find source face for target face "
392  << tgtCelli << endl;
393  }
394  }
395  }
396  }
397  }
398 
399  srcWeightsSum_.setSize(srcWeights_.size(), 1);
400  tgtWeightsSum_.setSize(tgtWeights_.size(), 1);
401 
402  upToDate_ = true;
403 
404  return upToDate_;
405 }
406 
407 
408 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
Foam::addToRunTimeSelectionTable
addToRunTimeSelectionTable(decompositionMethod, kahipDecomp, dictionary)
Foam::autoPtr::New
static autoPtr< T > New(Args &&... args)
Construct autoPtr of T with forwarding arguments.
Foam::labelList
List< label > labelList
A List of labels.
Definition: List.H:67
Foam::pointField
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:44
Foam::scalarListList
List< scalarList > scalarListList
A List of scalarList.
Definition: scalarList.H:66
Foam::scalarList
List< scalar > scalarList
A List of scalars.
Definition: scalarList.H:64
Foam::AMIInterpolation::calculate
virtual bool calculate(const primitivePatch &srcPatch, const primitivePatch &tgtPatch, const autoPtr< searchableSurface > &surfPtr=nullptr)
Update addressing, weights and (optional) centroids.
Definition: AMIInterpolation.C:734
Foam::component
void component(FieldField< Field, typename FieldField< Field, Type >::cmptType > &sf, const FieldField< Field, Type > &f, const direction d)
Definition: FieldFieldFunctions.C:44
Foam::nearestFaceAMI::calculate
virtual bool calculate(const primitivePatch &srcPatch, const primitivePatch &tgtPatch, const autoPtr< searchableSurface > &surfPtr=nullptr)
Update addressing and weights.
Definition: nearestFaceAMI.C:267
Foam::nearestFaceAMI::nearestFaceAMI
nearestFaceAMI(const dictionary &dict, const bool reverseTarget=false)
Construct from dictionary.
Definition: nearestFaceAMI.C:235
Foam::nearestAndDist
Tuple2< pointIndexHit, scalar > nearestAndDist
Combine operator for nearest.
Definition: distributedTriSurfaceMesh.C:114
Foam::Pstream::scatterList
static void scatterList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Scatter data. Reverse of gatherList.
Definition: gatherScatterList.C:215
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::Pout
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
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)
Foam::PointIndexHit
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
Definition: PointIndexHit.H:52
Foam::PointIndexHit::hit
bool hit() const noexcept
Is there a hit?
Definition: PointIndexHit.H:130
Foam::Field< vector >
Foam::mapDistributeBase::distribute
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 > &, const negateOp &negOp, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Distribute data. Note:schedule only used for.
Definition: mapDistributeBaseTemplates.C:122
dict
dictionary dict
Definition: searchingEngine.H:14
Foam::dictionary
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:123
addToRunTimeSelectionTable.H
Macros for easy insertion into run-time selection tables.
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::nearestZero
const nearestAndDist nearestZero(nearestAndDist(pointIndexHit(), -GREAT))
Foam::autoPtr< Foam::mapDistribute >
Foam::labelListList
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:56
Foam::Pstream::gatherList
static void gatherList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Gather data but keep individual values separate.
Definition: gatherScatterList.C:52
Foam::pointIndexHit
PointIndexHit< point > pointIndexHit
A PointIndexHit for 3D points.
Definition: pointIndexHit.H:46
Foam::UPstream::commsTypes::nonBlocking
Foam::UPstream::myProcNo
static int myProcNo(const label communicator=worldComm)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:463
Foam::Vector< scalar >
Foam::PointIndexHit::index
label index() const noexcept
Return the hit index.
Definition: PointIndexHit.H:136
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
Foam::AMIInterpolation
Interpolation class dealing with transfer of data between two primitive patches with an arbitrary mes...
Definition: AMIInterpolation.H:79
Foam::primitivePatch
PrimitivePatch< SubList< face >, const pointField & > primitivePatch
A PrimitivePatch with a SubList addressing for the faces, const reference for the point field.
Definition: primitivePatch.H:51
Foam::point
vector point
Point is a vector.
Definition: point.H:43
nearestFaceAMI.H
Foam::dictionary::getOrDefault
T getOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
Definition: dictionaryTemplates.C:148
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(combustionModel, 0)
WarningInFunction
#define WarningInFunction
Report a warning using Foam::Warning.
Definition: messageStream.H:328
Foam::UPstream::nProcs
static label nProcs(const label communicator=worldComm)
Number of processes in parallel run, and 1 for serial run.
Definition: UPstream.H:445
Foam::PrimitivePatch
A list of faces which address into the list of points.
Definition: PrimitivePatch.H:79
Foam::nearestFaceAMI
Nearest-face Arbitrary Mesh Interface (AMI) method.
Definition: nearestFaceAMI.H:51