mappedPatchBase.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2024 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "mappedPatchBase.H"
27 #include "SubField.H"
28 #include "Time.H"
29 #include "triPointRef.H"
30 #include "treeDataPoint.H"
31 #include "indexedOctree.H"
32 #include "globalIndex.H"
33 #include "RemoteData.H"
34 
35 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
36 
37 namespace Foam
38 {
40 
41  const scalar mappedPatchBase::defaultMatchTol_ = 1e-4;
42 }
43 
44 
45 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
46 
48 {
49  return patch_.primitivePatch::faceAreas();
50 }
51 
52 
54 {
55  return patch_.primitivePatch::faceCentres();
56 }
57 
58 
60 {
61  return patch_.localPoints();
62 }
63 
64 
66 {
67  return
68  nbrPatchIsMapped()
69  ? nbrMappedPatch().patchFaceAreas()
70  : tmp<vectorField>(nbrPolyPatch().primitivePatch::faceAreas());
71 }
72 
73 
75 {
76  return
77  nbrPatchIsMapped()
78  ? nbrMappedPatch().patchFaceCentres()
80 }
81 
82 
84 {
85  return
86  nbrPatchIsMapped()
87  ? nbrMappedPatch().patchLocalPoints()
88  : tmp<vectorField>(nbrPolyPatch().localPoints());
89 }
90 
91 
93 {
94  const label bothMappingIsValid =
95  nbrPatchIsMapped()
96  ? min(mappingIsValid_, nbrMappedPatch().nbrMappingIsValid_)
97  : mappingIsValid_;
98 
99  return
100  (bothMappingIsValid == 2)
101  || (
102  bothMappingIsValid == 1
104  (
105  patch_,
106  nbrPolyPatch()
107  )
108  );
109 }
110 
111 
113 {
114  clearOut();
115 
116  if (sameUntransformedPatch())
117  {
119  << "Patch " << patch_.name() << " is mapping itself with no "
120  << "transform. Mapping data does not need to be constructed."
121  << exit(FatalError);
122  }
123 
124  // Calculate the transform as necessary
125  transform_ =
127  (
128  patch_.name(),
129  patchFaceCentres(),
130  patchFaceAreas(),
131  transform_,
132  nbrPolyPatch().name(),
133  nbrPatchFaceCentres(),
134  nbrPatchFaceAreas(),
135  nbrPatchIsMapped()
136  ? nbrMappedPatch().transform_
137  : cyclicTransform(false),
138  matchTol_,
139  true
140  );
141 
142  // Build the mapping
143  if (usingTree_)
144  {
145  const globalIndex patchGlobalIndex(patch_.size());
146 
147  // Find processor and cell/face indices of samples
148  labelList sampleGlobalPatchFaces, sampleIndices;
149  {
150  // Gather the sample points into a single globally indexed list
151  List<point> allPoints(patchGlobalIndex.size());
152  {
153  List<pointField> procSamplePoints(Pstream::nProcs());
154  procSamplePoints[Pstream::myProcNo()] =
155  transform_.transform().invTransformPosition
156  (
157  patchFaceCentres()
158  );
159  Pstream::gatherList(procSamplePoints);
160  Pstream::scatterList(procSamplePoints);
161 
162  forAll(procSamplePoints, proci)
163  {
164  forAll(procSamplePoints[proci], procSamplei)
165  {
166  allPoints
167  [
168  patchGlobalIndex.toGlobal(proci, procSamplei)
169  ] = procSamplePoints[proci][procSamplei];
170  }
171  }
172  }
173 
174  // List of possibly remote mapped faces
175  List<RemoteData<scalar>> allNearest(patchGlobalIndex.size());
176 
177  // Find nearest face opposite every face
178  if (nbrPolyPatch().empty())
179  {
180  forAll(allPoints, alli)
181  {
182  allNearest[alli].proci = -1;
183  allNearest[alli].elementi = -1;
184  allNearest[alli].data = Foam::sqr(great);
185  }
186  }
187  else if (nbrPolyPatch().size() == 1)
188  {
189  const pointField nbrPoints(nbrPatchFaceCentres());
190 
191  forAll(allPoints, alli)
192  {
193  const point& p = allPoints[alli];
194 
195  allNearest[alli].proci = Pstream::myProcNo();
196  allNearest[alli].elementi = 0;
197  allNearest[alli].data = magSqr(nbrPoints[0] - p);
198  }
199  }
200  else
201  {
202  const pointField nbrPoints(nbrPatchFaceCentres());
203 
204  const treeBoundBox nbrPointsBb
205  (
206  treeBoundBox(nbrPoints).extend(1e-4)
207  );
208 
210  (
211  treeDataPoint(nbrPoints),
212  nbrPointsBb, // overall search domain
213  8, // maxLevel
214  10, // leafsize
215  3.0 // duplicity
216  );
217 
218  forAll(allPoints, alli)
219  {
220  const point& p = allPoints[alli];
221 
222  const pointIndexHit pih =
223  tree.findNearest(p, magSqr(nbrPointsBb.span()));
224 
225  if (pih.hit())
226  {
227  allNearest[alli].proci = Pstream::myProcNo();
228  allNearest[alli].elementi = pih.index();
229  allNearest[alli].data =
230  magSqr(nbrPoints[pih.index()] - p);
231  }
232  }
233  }
234 
235  // Find nearest. Combine on master.
237  (
238  allNearest,
240  );
241  Pstream::listCombineScatter(allNearest);
242 
243  // Determine the number of faces for which a nearest neighbouring
244  // face was not found (no reduction necessary as this is computed
245  // from synchronised data)
246  label nNotFound = 0;
247  forAll(allPoints, alli)
248  {
249  if (allNearest[alli].proci == -1)
250  {
251  nNotFound ++;
252  }
253  }
254 
255  // If any points were not found then fail
256  if (nNotFound)
257  {
259  << "Mapping to patch '" << patch_.name();
260  if (!sameRegion())
261  {
263  << "' in region '"
264  << patch_.boundaryMesh().mesh().name();
265  }
267  << "' from patch '" << nbrPatchName();
268  if (!sameRegion())
269  {
271  << "' in region '" << nbrRegionName();
272  }
274  << "' failed " << exit(FatalError);
275  }
276 
277  // Build lists of samples
278  DynamicList<label> samplePatchGlobalFacesDyn;
279  DynamicList<label> sampleIndicesDyn;
280  forAll(allNearest, alli)
281  {
282  if (allNearest[alli].proci == Pstream::myProcNo())
283  {
284  samplePatchGlobalFacesDyn.append(alli);
285  sampleIndicesDyn.append(allNearest[alli].elementi);
286  }
287  }
288  sampleGlobalPatchFaces.transfer(samplePatchGlobalFacesDyn);
289  sampleIndices.transfer(sampleIndicesDyn);
290  }
291 
292  // Construct distribution schedule
293  List<Map<label>> compactMap;
294  treeMapPtr_.reset
295  (
296  new distributionMap
297  (
298  patchGlobalIndex,
299  sampleGlobalPatchFaces,
300  compactMap
301  )
302  );
303  const labelList oldToNew(move(sampleGlobalPatchFaces));
304  const labelList oldSampleIndices(move(sampleIndices));
305 
306  // Construct input mapping for data to be distributed
307  treeNbrPatchFaceIndices_ = labelList(treeMapPtr_->constructSize(), -1);
308  UIndirectList<label>(treeNbrPatchFaceIndices_, oldToNew) =
309  oldSampleIndices;
310 
311  // Reverse the map. This means the map is "forward" when going from
312  // the neighbour patch to this patch, which is logical.
313  treeMapPtr_.reset
314  (
315  new distributionMap
316  (
317  patch_.size(),
318  move(treeMapPtr_->constructMap()),
319  move(treeMapPtr_->subMap())
320  )
321  );
322  }
323  else
324  {
325  const pointField patchLocalPoints(this->patchLocalPoints());
326  const pointField nbrPatchLocalPoints(this->nbrPatchLocalPoints());
327 
328  const primitivePatch patch
329  (
330  SubList<face>(patch_.localFaces(), patch_.size()),
331  patchLocalPoints
332  );
333  const primitivePatch nbrPatch
334  (
335  SubList<face>(nbrPolyPatch().localFaces(), nbrPolyPatch().size()),
336  nbrPatchLocalPoints
337  );
338 
339  patchToPatchPtr_->update
340  (
341  patch,
342  patch.pointNormals(),
343  nbrPatch,
344  transform_.transform()
345  );
346 
347  // Check the validity of the mapping. Every face must be coupled.
348  auto checkPatchToPatch = [&]
349  (
350  const polyPatch& patch,
351  const polyPatch& nbrPatch,
352  const bool isSrc
353  )
354  {
355  const PackedBoolList coupled =
356  isSrc
357  ? patchToPatchPtr_->srcCoupled()
358  : patchToPatchPtr_->tgtCoupled();
359 
360  DynamicList<point> nonCoupledCfs;
361  forAll(coupled, patchFacei)
362  {
363  if (!coupled[patchFacei])
364  {
365  nonCoupledCfs.append(patch.faceCentres()[patchFacei]);
366  }
367  }
368 
369  if (nonCoupledCfs.size())
370  {
372  << "Mapping to patch '" << patch.name();
373  if (!sameRegion())
374  {
376  << "' in region '"
377  << patch.boundaryMesh().mesh().name();
378  }
380  << "' from patch '" << nbrPatch.name();
381  if (!sameRegion())
382  {
384  << "' in region '"
385  << nbrPatch.boundaryMesh().mesh().name();
386  }
388  << "' failed because faces at the following locations "
389  << "were not coupled: " << nonCoupledCfs
390  << exit(FatalError);
391  }
392  };
393 
394  checkPatchToPatch(patch_, nbrPolyPatch(), true);
395 
396  if (symmetric())
397  {
398  checkPatchToPatch(nbrPolyPatch(), patch_, false);
399  }
400  }
401 
402  mappingIsValid_ = 2;
403 
404  if (nbrPatchIsMapped()) nbrMappedPatch().nbrMappingIsValid_ = 2;
405 }
406 
407 
409 {
410  treeMapPtr_.clear();
411  treeNbrPatchFaceIndices_.clear();
412 }
413 
414 
415 // * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * * * * * //
416 
418 :
420  usingTree_(true),
421  treeMapPtr_(nullptr),
422  treeNbrPatchFaceIndices_(),
423  patchToPatchPtr_(nullptr),
424  matchTol_(defaultMatchTol_),
425  mappingIsValid_(0),
426  nbrMappingIsValid_(0)
427 {}
428 
429 
431 (
432  const polyPatch& pp,
433  const word& nbrRegionName,
434  const word& nbrPatchName,
436 )
437 :
438  mappedPatchBaseBase(pp, nbrRegionName, nbrPatchName, transform),
439  usingTree_(true),
440  treeMapPtr_(nullptr),
441  treeNbrPatchFaceIndices_(),
442  patchToPatchPtr_(nullptr),
443  matchTol_(defaultMatchTol_),
444  mappingIsValid_(0),
445  nbrMappingIsValid_(0)
446 {}
447 
448 
450 (
451  const polyPatch& pp,
452  const dictionary& dict,
453  const transformType tt
454 )
455 :
456  mappedPatchBaseBase(pp, dict, tt),
457  usingTree_(!dict.found("method") && !dict.found("sampleMode")),
458  treeMapPtr_(nullptr),
459  treeNbrPatchFaceIndices_(),
460  patchToPatchPtr_
461  (
462  !usingTree_
463  ? patchToPatch::New
464  (
465  dict.lookupBackwardsCompatible<word>({"method", "sampleMode"}),
466  false
467  ).ptr()
468  : nullptr
469  ),
470  matchTol_(dict.lookupOrDefault("matchTolerance", defaultMatchTol_)),
471  mappingIsValid_(0),
472  nbrMappingIsValid_(0)
473 {}
474 
475 
477 (
478  const polyPatch& pp,
479  const mappedPatchBase& mpb
480 )
481 :
482  mappedPatchBaseBase(pp, mpb),
483  usingTree_(mpb.usingTree_),
484  treeMapPtr_(nullptr),
485  treeNbrPatchFaceIndices_(),
486  patchToPatchPtr_
487  (
488  !usingTree_
489  ? patchToPatch::New(mpb.patchToPatchPtr_->type(), false).ptr()
490  : nullptr
491  ),
492  matchTol_(mpb.matchTol_),
493  mappingIsValid_(0),
494  nbrMappingIsValid_(0)
495 {}
496 
497 
498 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
499 
501 {}
502 
503 
504 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
505 
507 (
508  const polyPatch& patch
509 )
510 {
511  if (!isA<mappedPatchBase>(patch))
512  {
514  << "Patch " << patch.name() << " is not of type "
515  << typeName << exit(FatalError);
516  }
517 
518  return refCast<const mappedPatchBase>(patch);
519 }
520 
521 
522 void Foam::mappedPatchBase::clearOut(const bool move)
523 {
524  if (move && moveUpdate_ == moveUpdate::never)
525  {
526  // Do nothing
527  }
528  else if (move && moveUpdate_ == moveUpdate::detect)
529  {
530  mappingIsValid_ = min(mappingIsValid_, 1);
531  nbrMappingIsValid_ = min(nbrMappingIsValid_, 1);
532  }
533  else
534  {
535  clearOut();
536  mappingIsValid_ = 0;
537  nbrMappingIsValid_ = 0;
538  }
539 }
540 
541 
543 {
545 
546  if (!usingTree_)
547  {
548  writeEntry(os, "method", patchToPatchPtr_->type());
549  }
550 
551  writeEntryIfDifferent(os, "matchTolerance", defaultMatchTol_, matchTol_);
552 }
553 
554 
555 // ************************************************************************* //
bool found
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:296
void transfer(List< T > &)
Transfer the contents of the argument List into this list.
Definition: List.C:342
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
A bit-packed bool list.
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
Definition: PointIndexHit.H:54
label index() const
Return index.
bool hit() const
Is there a hit.
const Field< PointType > & pointNormals() const
Return point normals for patch.
const Field< PointType > & faceAreas() const
Return face areas for patch.
const Field< PointType > & faceCentres() const
Return face centres for patch.
static void scatterList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Scatter data. Reverse of gatherList.
static void listCombineGather(const List< commsStruct > &comms, List< T > &Value, const CombineOp &cop, const int tag, const label comm)
static void listCombineScatter(const List< commsStruct > &comms, List< T > &Value, const int tag, const label comm)
Scatter data. Reverse of combineGather.
static void gatherList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Gather data but keep individual values separate.
A List obtained as a section of another List.
Definition: SubList.H:56
A List with indirect addressing.
Definition: UIndirectList.H:60
T * data()
Return a pointer to the first data element,.
Definition: UListI.H:149
static label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:411
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
vector span() const
The bounding box span (from minimum to maximum)
Definition: boundBoxI.H:90
Cyclic plane transformation.
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
T lookupOrDefault(const word &, const T &, const bool writeDefault=writeOptionalEntries > 0) const
Find and return a T, if not found return the given default.
Class containing processor-to-processor mapping information.
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:64
label size() const
Global sum of localSizes.
Definition: globalIndexI.H:66
label toGlobal(const label i) const
From local to global.
Definition: globalIndexI.H:82
Non-pointer based hierarchical recursive searching.
Definition: indexedOctree.H:72
Base class for engines and poly patches which provide mapping between two poly patches.
const polyPatch & patch_
Patch to map to.
virtual void write(Ostream &) const
Write as a dictionary.
transformType
The type of the transformation permitted/required by this patch.
static bool moving(const polyPatch &patch, const polyPatch &nbrPatch)
Return whether or not the patches have moved.
Engine and base class for poly patches which provides interpolative mapping between two globally conf...
tmp< pointField > nbrPatchLocalPoints() const
Get the local points for the neighbour patch.
static const mappedPatchBase & getMap(const polyPatch &patch)
Cast the given polyPatch to a mappedPatchBase. Handle errors.
virtual void write(Ostream &) const
Write as a dictionary.
virtual tmp< vectorField > patchFaceAreas() const
Get the face-areas for this patch.
virtual tmp< pointField > patchLocalPoints() const
Get the local points for this patch.
virtual void clearOut() const
Clear out data on mesh change.
static const scalar defaultMatchTol_
Default Matching tolerance.
mappedPatchBase(const polyPatch &)
Construct from patch.
tmp< vectorField > nbrPatchFaceAreas() const
Get the face-areas for the neighbour patch.
bool mappingIsValid() const
Return whether or not the mapping is valid.
tmp< pointField > nbrPatchFaceCentres() const
Get the face-centres for the neighbour patch.
virtual ~mappedPatchBase()
Destructor.
void calcMapping() const
Calculate mapping.
virtual tmp< pointField > patchFaceCentres() const
Get the face-centres for this patch.
const word & name() const
Return name.
Class to generate coupling geometry between two primitive patches.
Definition: patchToPatch.H:56
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:70
A class for managing temporary objects.
Definition: tmp.H:55
Standard boundBox + extra functionality for use in octree.
Definition: treeBoundBox.H:90
Holds (reference to) pointField. Encapsulation of data needed for octree searches....
Definition: treeDataPoint.H:60
A class for handling words, derived from string.
Definition: word.H:62
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
const doubleScalar e
Definition: doubleScalar.H:106
void writeEntryIfDifferent(Ostream &os, const word &entryName, const EntryType &value1, const EntryType &value2)
Helper function to write the keyword and entry only if the.
List< label > labelList
A List of labels.
Definition: labelList.H:56
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
void writeEntry(Ostream &os, const HashTable< T, Key, Hash > &ht)
Definition: HashTableIO.C:96
dimensionSet transform(const dimensionSet &)
Definition: dimensionSet.C:501
defineTypeNameAndDebug(combustionModel, 0)
void sqr(LagrangianPatchField< typename outerProduct< Type, Type >::type > &f, const LagrangianPatchField< Type > &f1)
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
error FatalError
void magSqr(LagrangianPatchField< scalar > &f, const LagrangianPatchField< Type > &f1)
tmp< DimensionedField< TypeR, GeoMesh, Field > > New(const tmp< DimensionedField< TypeR, GeoMesh, Field >> &tdf1, const word &name, const dimensionSet &dimensions)
fileType type(const fileName &, const bool checkVariants=true, const bool followLink=true)
Return the file type: directory or file.
Definition: POSIX.C:488
dictionary dict
volScalarField & p
Operator to take smallest valid value.
Definition: RemoteData.H:118