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-2023 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  if (treeMapPtr_.valid())
95  {
97  << "Mapping already calculated" << exit(FatalError);
98  }
99 
100  if (sameUntransformedPatch())
101  {
103  << "Patch " << patch_.name() << " is mapping itself with no "
104  << "transform. Mapping data does not need to be constructed."
105  << exit(FatalError);
106  }
107 
108  // Calculate the transform as necessary
109  transform_ =
111  (
112  patch_.name(),
113  patchFaceCentres(),
114  patchFaceAreas(),
115  transform_,
116  nbrPolyPatch().name(),
117  nbrPatchFaceCentres(),
118  nbrPatchFaceAreas(),
119  nbrPatchIsMapped()
120  ? nbrMappedPatch().transform_
121  : cyclicTransform(false),
122  matchTol_,
123  true
124  );
125 
126  // Build the mapping
127  if (usingTree_)
128  {
129  const globalIndex patchGlobalIndex(patch_.size());
130 
131  // Find processor and cell/face indices of samples
132  labelList sampleGlobalPatchFaces, sampleIndices;
133  {
134  // Gather the sample points into a single globally indexed list
135  List<point> allPoints(patchGlobalIndex.size());
136  {
137  List<pointField> procSamplePoints(Pstream::nProcs());
138  procSamplePoints[Pstream::myProcNo()] =
139  transform_.transform().invTransformPosition
140  (
141  patchFaceCentres()
142  );
143  Pstream::gatherList(procSamplePoints);
144  Pstream::scatterList(procSamplePoints);
145 
146  forAll(procSamplePoints, proci)
147  {
148  forAll(procSamplePoints[proci], procSamplei)
149  {
150  allPoints
151  [
152  patchGlobalIndex.toGlobal(proci, procSamplei)
153  ] = procSamplePoints[proci][procSamplei];
154  }
155  }
156  }
157 
158  // List of possibly remote mapped faces
159  List<RemoteData<scalar>> allNearest(patchGlobalIndex.size());
160 
161  // Find nearest face opposite every face
162  if (nbrPolyPatch().empty())
163  {
164  forAll(allPoints, alli)
165  {
166  allNearest[alli].proci = -1;
167  allNearest[alli].elementi = -1;
168  allNearest[alli].data = Foam::sqr(great);
169  }
170  }
171  else
172  {
173  const pointField nbrPoints(nbrPatchFaceCentres());
174 
175  const treeBoundBox nbrPointsBb
176  (
177  treeBoundBox(nbrPoints).extend(1e-4)
178  );
179 
181  (
182  treeDataPoint(nbrPoints),
183  nbrPointsBb, // overall search domain
184  8, // maxLevel
185  10, // leafsize
186  3.0 // duplicity
187  );
188 
189  forAll(allPoints, alli)
190  {
191  const point& p = allPoints[alli];
192 
193  const pointIndexHit pih =
194  tree.findNearest(p, magSqr(nbrPointsBb.span()));
195 
196  if (pih.hit())
197  {
198  allNearest[alli].proci = Pstream::myProcNo();
199  allNearest[alli].elementi = pih.index();
200  allNearest[alli].data =
201  magSqr(nbrPoints[pih.index()] - p);
202  }
203  }
204  }
205 
206  // Find nearest. Combine on master.
208  (
209  allNearest,
211  );
212  Pstream::listCombineScatter(allNearest);
213 
214  // Determine the number of faces for which a nearest neighbouring
215  // face was not found (no reduction necessary as this is computed
216  // from synchronised data)
217  label nNotFound = 0;
218  forAll(allPoints, alli)
219  {
220  if (allNearest[alli].proci == -1)
221  {
222  nNotFound ++;
223  }
224  }
225 
226  // If any points were not found within cells then re-search for
227  // them using a nearest test, which should not fail. Warn that this
228  // is happening. If any points were not found for some other
229  // method, then fail.
230  if (nNotFound)
231  {
233  << "Mapping failed for " << nl
234  << " patch: " << patch_.name() << nl
235  << " neighbourRegion: " << nbrRegionName() << nl
236  << " neighbourPatch: " << nbrPatchName() << nl
237  << exit(FatalError);
238  }
239 
240  // Build lists of samples
241  DynamicList<label> samplePatchGlobalFacesDyn;
242  DynamicList<label> sampleIndicesDyn;
243  forAll(allNearest, alli)
244  {
245  if (allNearest[alli].proci == Pstream::myProcNo())
246  {
247  samplePatchGlobalFacesDyn.append(alli);
248  sampleIndicesDyn.append(allNearest[alli].elementi);
249  }
250  }
251  sampleGlobalPatchFaces.transfer(samplePatchGlobalFacesDyn);
252  sampleIndices.transfer(sampleIndicesDyn);
253  }
254 
255  // Construct distribution schedule
256  List<Map<label>> compactMap;
257  treeMapPtr_.reset
258  (
259  new distributionMap
260  (
261  patchGlobalIndex,
262  sampleGlobalPatchFaces,
263  compactMap
264  )
265  );
266  const labelList oldToNew(move(sampleGlobalPatchFaces));
267  const labelList oldSampleIndices(move(sampleIndices));
268 
269  // Construct input mapping for data to be distributed
270  treeNbrPatchFaceIndices_ = labelList(treeMapPtr_->constructSize(), -1);
271  UIndirectList<label>(treeNbrPatchFaceIndices_, oldToNew) =
272  oldSampleIndices;
273 
274  // Reverse the map. This means the map is "forward" when going from
275  // the neighbour patch to this patch, which is logical.
276  treeMapPtr_.reset
277  (
278  new distributionMap
279  (
280  patch_.size(),
281  move(treeMapPtr_->constructMap()),
282  move(treeMapPtr_->subMap())
283  )
284  );
285  }
286  else
287  {
288  if (patchToPatchIsValid_)
289  {
291  << "Patch-to-patch already calculated" << exit(FatalError);
292  }
293 
294  const pointField patchLocalPoints(this->patchLocalPoints());
295  const pointField nbrPatchLocalPoints(this->nbrPatchLocalPoints());
296 
297  const primitivePatch patch
298  (
299  SubList<face>(patch_.localFaces(), patch_.size()),
300  patchLocalPoints
301  );
302  const primitivePatch nbrPatch
303  (
304  SubList<face>(nbrPolyPatch().localFaces(), nbrPolyPatch().size()),
305  nbrPatchLocalPoints
306  );
307 
308  patchToPatchPtr_->update
309  (
310  patch,
311  patch.pointNormals(),
312  nbrPatch,
313  transform_.transform()
314  );
315 
316  patchToPatchIsValid_ = true;
317  }
318 }
319 
320 
321 // * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * * * * * //
322 
324 :
325  patch_(pp),
326  coupleGroup_(),
327  nbrRegionName_(patch_.boundaryMesh().mesh().name()),
328  nbrPatchName_(patch_.name()),
329  transform_(true),
330  usingTree_(true),
331  treeMapPtr_(nullptr),
332  treeNbrPatchFaceIndices_(),
333  patchToPatchIsValid_(false),
334  patchToPatchPtr_(nullptr),
335  matchTol_(defaultMatchTol_),
336  reMapAfterMove_(true),
337  reMapNbr_(false)
338 {}
339 
340 
342 (
343  const polyPatch& pp,
344  const word& nbrRegionName,
345  const word& nbrPatchName,
347 )
348 :
349  patch_(pp),
350  coupleGroup_(),
351  nbrRegionName_(nbrRegionName),
352  nbrPatchName_(nbrPatchName),
353  transform_(transform),
354  usingTree_(true),
355  treeMapPtr_(nullptr),
356  treeNbrPatchFaceIndices_(),
357  patchToPatchIsValid_(false),
358  patchToPatchPtr_(nullptr),
359  matchTol_(defaultMatchTol_),
360  reMapAfterMove_(true),
361  reMapNbr_(false)
362 {}
363 
364 
366 (
367  const polyPatch& pp,
368  const dictionary& dict,
369  const bool defaultTransformIsNone
370 )
371 :
372  patch_(pp),
373  coupleGroup_(dict),
374  nbrRegionName_
375  (
376  coupleGroup_.valid() ? word::null
377  : dict.lookupOrDefaultBackwardsCompatible<word>
378  (
379  {"neighbourRegion", "sampleRegion"},
380  pp.boundaryMesh().mesh().name()
381  )
382  ),
383  nbrPatchName_
384  (
385  coupleGroup_.valid() ? word::null
386  : dict.lookupOrDefault<bool>("samePatch", false) ? pp.name()
387  : dict.lookupBackwardsCompatible<word>({"neighbourPatch", "samplePatch"})
388  ),
389  transform_(cyclicTransform(dict, defaultTransformIsNone)),
390  usingTree_(!dict.found("method") && !dict.found("sampleMode")),
391  treeMapPtr_(nullptr),
392  treeNbrPatchFaceIndices_(),
393  patchToPatchIsValid_(false),
394  patchToPatchPtr_
395  (
396  !usingTree_
398  (
399  dict.lookupBackwardsCompatible<word>({"method", "sampleMode"}),
400  false
401  ).ptr()
402  : nullptr
403  ),
404  matchTol_(dict.lookupOrDefault("matchTolerance", defaultMatchTol_)),
405  reMapAfterMove_(dict.lookupOrDefault<bool>("reMapAfterMove", true)),
406  reMapNbr_(false)
407 {
408  const bool haveCoupleGroup = coupleGroup_.valid();
409 
410  const bool haveNbrRegion =
411  dict.found("neighbourRegion") || dict.found("sampleRegion");
412  const bool haveNbrPatch =
413  dict.found("neighbourPatch") || dict.found("samplePatch");
414 
415  const bool isSamePatch = dict.lookupOrDefault<bool>("samePatch", false);
416 
417  if ((haveNbrRegion || haveNbrPatch || isSamePatch) && haveCoupleGroup)
418  {
420  << "Either neighbourRegion/Patch information or a coupleGroup "
421  << "should be specified, not both" << exit(FatalIOError);
422  }
423 
424  if (haveNbrPatch && isSamePatch)
425  {
427  << "Either a neighbourPatch should be specified, or samePatch "
428  << "should be set to true, not both" << exit(FatalIOError);
429  }
430 }
431 
432 
434 (
435  const polyPatch& pp,
436  const mappedPatchBase& mpb
437 )
438 :
439  patch_(pp),
440  coupleGroup_(mpb.coupleGroup_),
441  nbrRegionName_(mpb.nbrRegionName_),
442  nbrPatchName_(mpb.nbrPatchName_),
443  transform_(mpb.transform_),
444  usingTree_(mpb.usingTree_),
445  treeMapPtr_(nullptr),
446  treeNbrPatchFaceIndices_(),
447  patchToPatchIsValid_(false),
448  patchToPatchPtr_
449  (
450  !usingTree_
451  ? patchToPatch::New(mpb.patchToPatchPtr_->type(), false).ptr()
452  : nullptr
453  ),
454  matchTol_(mpb.matchTol_),
455  reMapAfterMove_(true),
456  reMapNbr_(false)
457 {}
458 
459 
460 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
461 
463 {}
464 
465 
466 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
467 
469 {
470  return patch_.boundaryMesh().mesh().time().lookupObject<polyMesh>
471  (
472  nbrRegionName()
473  );
474 }
475 
476 
478 {
479  const polyMesh& nbrMesh = this->nbrMesh();
480 
481  const label patchi = nbrMesh.boundaryMesh().findPatchID(nbrPatchName());
482 
483  if (patchi == -1)
484  {
486  << "Cannot find patch " << nbrPatchName()
487  << " in region " << nbrRegionName() << endl
488  << "Valid patches are " << nbrMesh.boundaryMesh().names()
489  << exit(FatalError);
490  }
491 
492  return nbrMesh.boundaryMesh()[patchi];
493 }
494 
495 
497 (
498  const polyPatch& patch
499 )
500 {
501  if (!isA<mappedPatchBase>(patch))
502  {
504  << "Patch " << patch.name() << " is not of type "
505  << typeName << exit(FatalError);
506  }
507 
508  return refCast<const mappedPatchBase>(patch);
509 }
510 
511 
513 {
514  if (reMapAfterMove_)
515  {
516  treeMapPtr_.clear();
517  treeNbrPatchFaceIndices_.clear();
518  patchToPatchIsValid_ = false;
519  reMapNbr_ = true;
520  }
521 }
522 
523 
525 {
526  return
527  dict.found("coupleGroup")
528  || dict.found("neighbourRegion")
529  || dict.found("sampleRegion")
530  || dict.found("neighbourPatch")
531  || dict.found("samplePatch")
532  || dict.found("samePatch");
533 }
534 
535 
537 {
538  writeEntryIfDifferent(os, "neighbourRegion", word::null, nbrRegionName_);
539  writeEntryIfDifferent(os, "neighbourPatch", word::null, nbrPatchName_);
540 
541  coupleGroup_.write(os);
542 
543  transform_.write(os);
544 
545  if (!usingTree_)
546  {
547  writeEntry(os, "method", patchToPatchPtr_->type());
548  }
549 
550  writeEntryIfDifferent(os, "matchTolerance", defaultMatchTol_, matchTol_);
551 
552  writeEntryIfDifferent<bool>(os, "reMapAfterMove", true, reMapAfterMove_);
553 }
554 
555 
556 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
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
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
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.
A list of faces which address into the list of points.
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:84
Cyclic plane transformation.
A list of keyword definitions, which are a keyword followed by any number of values (e....
Definition: dictionary.H:160
T lookupOrDefault(const word &, const T &, bool recursive=false, bool patternMatch=true) const
Find and return a T,.
ITstream & lookupBackwardsCompatible(const wordList &, bool recursive=false, bool patternMatch=true) const
Find and return an entry data stream, trying a list of keywords.
Definition: dictionary.C:871
bool found(const word &, bool recursive=false, bool patternMatch=true) const
Search dictionary for given keyword.
Definition: dictionary.C:659
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
Engine which provides mapping between two patches.
tmp< pointField > nbrPatchLocalPoints() const
Get the local points for the neighbour patch.
const polyPatch & nbrPolyPatch() const
Get the patch to map from.
static const mappedPatchBase & getMap(const polyPatch &patch)
Cast the given polyPatch to a mappedPatchBase. Handle errors.
const polyPatch & patch_
Patch to map to.
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.
const polyMesh & nbrMesh() const
Get the mesh for the region to map from.
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.
static bool specified(const dictionary &dict)
Return whether or not the given dictionary contains a.
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.
virtual void clearOut()
Clear out data on mesh change.
const Time & time() const
Return time.
const Type & lookupObject(const word &name) const
Lookup and return the object of the given Type and name.
const word & name() const
Return name.
Class to generate coupling geometry between two primitive patches.
Definition: patchToPatch.H:56
static autoPtr< patchToPatch > New(const word &patchToPatchType, const bool reverse)
Select from name.
Definition: patchToPatch.C:786
label findPatchID(const word &patchName) const
Find patch index given a name.
wordList names() const
Return a list of patch names.
const polyMesh & mesh() const
Return the mesh reference.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:80
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:403
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
static const word null
An empty word.
Definition: word.H:77
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:318
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:306
label patchi
bool valid(const PtrList< ModelType > &l)
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
const doubleScalar e
Definition: doubleScalar.H:105
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
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
void writeEntry(Ostream &os, const HashTable< T, Key, Hash > &ht)
Definition: HashTableIO.C:96
dimensionSet transform(const dimensionSet &)
Definition: dimensionSet.C:483
defineTypeNameAndDebug(combustionModel, 0)
tmp< DimensionedField< TypeR, GeoMesh > > New(const tmp< DimensionedField< TypeR, GeoMesh >> &tdf1, const word &name, const dimensionSet &dimensions)
IOerror FatalIOError
error FatalError
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47
static const char nl
Definition: Ostream.H:260
dimensioned< scalar > magSqr(const dimensioned< Type > &)
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:90