Loading...
Searching...
No Matches
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-------------------------------------------------------------------------------
10License
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
31// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
32
33namespace Foam
34{
38}
39
40// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
41
42Foam::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
102Foam::autoPtr<Foam::mapDistribute> Foam::nearestFaceAMI::calcDistributed
103(
104 const primitivePatch& src,
105 const primitivePatch& tgt,
106 labelListList& srcToTgtAddr,
107 scalarListList& srcToTgtWght
108) const
109{
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.
201 (
204 src.size(),
205 map.constructMap(),
206 map.constructHasFlip(),
207 map.subMap(),
208 map.subHasFlip(),
209 remoteInfo,
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,
244 );
245}
246
247
248// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
249
251(
252 const dictionary& dict,
253 const bool reverseTarget
255:
257 maxDistance2_(dict.getOrDefault<scalar>("maxDistance2", GREAT))
258{}
259
260
262(
263 const bool requireMatch,
264 const bool reverseTarget,
265 const scalar lowWeightCorrection
267:
269 maxDistance2_(GREAT)
270{}
271
272
274:
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// ************************************************************************* //
Macros for easy insertion into run-time selection tables.
#define addToRunTimeSelectionTable(baseType, thisType, argNames)
Add to construction table with typeName as the key.
Interpolation class dealing with transfer of data between two primitive patches with an arbitrary mes...
bool reverseTarget() const noexcept
Access to the reverseTarget flag.
labelListList srcAddress_
Addresses of target faces per source face.
label comm() const noexcept
Communicator (local or otherwise) for parallel operations.
bool distributed() const noexcept
Distributed across processors (singlePatchProc == -1).
scalarList tgtMagSf_
Target face areas.
autoPtr< mapDistribute > srcMapPtr_
Source map pointer - parallel running only.
AMIInterpolation(const dictionary &dict, const bool reverseTarget=false)
Construct from dictionary.
virtual bool calculate(const primitivePatch &srcPatch, const primitivePatch &tgtPatch, const autoPtr< searchableSurface > &surfPtr=nullptr)
Update addressing, weights and (optional) centroids.
bool requireMatch() const noexcept
Return the requireMatch flag.
const primitivePatch & tgtPatch0() const
Return the original tgt patch with optionally updated points.
bool upToDate_
Up-to-date flag.
autoPtr< mapDistribute > tgtMapPtr_
Target map pointer - parallel running only.
virtual void write(Ostream &os) const
Write AMI as a dictionary.
labelListList tgtAddress_
Addresses of source faces per target face.
scalar lowWeightCorrection() const
Threshold weight below which interpolation is deactivated.
scalarField srcWeightsSum_
Sum of weights of target faces per source face.
scalarListList tgtWeights_
Weights of source faces per target face.
scalarListList srcWeights_
Weights of target faces per source face.
scalarList srcMagSf_
Source face areas.
const primitivePatch & srcPatch0() const
Return the original src patch with optionally updated points.
scalarField tgtWeightsSum_
Sum of weights of source faces per target face.
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition List.H:72
static const List< T > & null() noexcept
Return a null List (reference to a nullObject). Behaves like an empty List.
Definition List.H:138
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition Ostream.H:59
label index() const noexcept
Return the hit index.
bool hit() const noexcept
Is there a hit?
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.
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:1706
@ nonBlocking
"nonBlocking" (immediate) : (MPI_Isend, MPI_Irecv)
Definition UPstream.H:84
static int & msgType() noexcept
Message tag of standard messages.
Definition UPstream.H:1926
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:1697
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition autoPtr.H:65
static autoPtr< T > New(Args &&... args)
Construct autoPtr with forwarding arguments.
Definition autoPtr.H:178
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition dictionary.H:133
Calculates a non-overlapping list of offsets based on an input size (eg, number of cells) from differ...
Definition globalIndex.H:77
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).
Class containing processor-to-processor mapping information.
Nearest-face Arbitrary Mesh Interface (AMI) method.
virtual bool calculate(const primitivePatch &srcPatch, const primitivePatch &tgtPatch, const autoPtr< searchableSurface > &surfPtr=nullptr)
Update addressing and weights.
nearestFaceAMI(const dictionary &dict, const bool reverseTarget=false)
Construct from dictionary.
virtual void write(Ostream &os) const
Write.
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition className.H:142
OBJstream os(runTime.globalPath()/outputName)
#define WarningInFunction
Report a warning using Foam::Warning.
Namespace for handling debugging switches.
Definition debug.C:45
Namespace for OpenFOAM.
List< scalarList > scalarListList
List of scalarList.
Definition scalarList.H:35
List< labelList > labelListList
List of labelList.
Definition labelList.H:38
List< label > labelList
A List of labels.
Definition List.H:62
static autoPtr< indexedOctree< treeDataPoint > > createTree(const pointField &points)
Construct search tree for points.
void component(FieldField< Field, typename FieldField< Field, Type >::cmptType > &sf, const FieldField< Field, Type > &f, const direction d)
PrimitivePatch< SubList< face >, const pointField & > primitivePatch
A PrimitivePatch with a SubList addressing for the faces, const reference for the point field.
Tuple2< pointIndexHit, scalar > nearestAndDist
Combine operator for nearest.
const nearestAndDist nearestZero(nearestAndDist(pointIndexHit(), -GREAT))
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
vector point
Point is a vector.
Definition point.H:37
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
vectorField pointField
pointField is a vectorField.
PointIndexHit< point > pointIndexHit
A PointIndexHit with a 3D point.
List< scalar > scalarList
List of scalar.
Definition scalarList.H:32
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
dictionary dict
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299
A functor that returns its argument unchanged (cf. C++20 std::identity) Should never be specialized.
Definition stdFoam.H:108