mappedInternalPatchBase.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-2026 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 
27 #include "SubField.H"
28 #include "Time.H"
29 #include "triPointRef.H"
30 #include "meshSearch.H"
31 #include "globalIndex.H"
32 #include "RemoteData.H"
33 #include "OBJstream.H"
35 
36 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
37 
38 namespace Foam
39 {
41 }
42 
45 {
46  "normal",
47  "direction"
48 };
49 
50 
51 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
52 
55 (
56  const dictionary& dict
57 ) const
58 {
59  if (dict.found("offsetMode"))
60  {
61  return offsetModeNames_.read(dict.lookup("offsetMode"));
62  }
63  else
64  {
65  const bool haveDistance = dict.found("distance");
66  const bool haveOffset = dict.found("offset");
67 
68  if (haveDistance == haveOffset)
69  {
70  // Error. Demand "offsetMode" setting to disambiguate.
71  return offsetModeNames_.read(dict.lookup("offsetMode"));
72  }
73  else if (haveDistance)
74  {
75  return NORMAL;
76  }
77  else
78  {
79  return DIRECTION;
80  }
81  }
82 }
83 
84 
86 {
87  if (mapPtr_.valid())
88  {
90  << "Mapping already calculated" << exit(FatalError);
91  }
92 
93  const polyMesh& nbrMesh = this->nbrMesh();
94 
95  const meshSearch& nbrSearchEngine = meshSearch::New(nbrMesh);
96 
97  const globalIndex patchGlobalIndex(patch_.size());
98 
99  // Find processor and cell/face indices of samples
100  labelList sampleGlobalPatchFaces, sampleIndices;
101  {
102  // Gather the sample points into a single globally indexed list
103  List<point> allPoints(patchGlobalIndex.size());
104  {
105  List<pointField> procSamplePoints(Pstream::nProcs());
106  procSamplePoints[Pstream::myProcNo()] = this->samplePoints();
107  Pstream::gatherList(procSamplePoints);
108  Pstream::scatterList(procSamplePoints);
109 
110  forAll(procSamplePoints, proci)
111  {
112  forAll(procSamplePoints[proci], procSamplei)
113  {
114  allPoints
115  [
116  patchGlobalIndex.toGlobal(proci, procSamplei)
117  ] = procSamplePoints[proci][procSamplei];
118  }
119  }
120  }
121 
122  // List of possibly remote sampling cells
123  List<RemoteData<scalar>> allNearest(patchGlobalIndex.size());
124 
125  // Find containing cell for every sampling point
126  forAll(allPoints, alli)
127  {
128  const point& p = allPoints[alli];
129 
130  const label celli = nbrSearchEngine.findCell(p);
131 
132  if (celli != -1)
133  {
134  const point& cc = nbrMesh.cellCentres()[celli];
135 
136  allNearest[alli].proci = Pstream::myProcNo();
137  allNearest[alli].elementi = celli;
138  allNearest[alli].data = magSqr(cc - p);
139  }
140  }
141 
142  // Find nearest. Combine on master.
144  (
145  allNearest,
147  );
148  Pstream::listCombineScatter(allNearest);
149 
150  // Determine the number of missing samples (no reduction necessary as
151  // this is computed from synchronised data)
152  label nNotFound = 0;
153  forAll(allPoints, alli)
154  {
155  if (allNearest[alli].proci == -1)
156  {
157  nNotFound ++;
158  }
159  }
160 
161  // If any points were not found within cells then re-search for them
162  // using a nearest test, which should not fail. Warn that this is
163  // happening. If any points were not found for some other method, then
164  // fail.
165  if (nNotFound)
166  {
168  << "Did not find a containing cell for " << nNotFound
169  << " out of " << returnReduce(patch_.size(), sumOp<label>())
170  << " total faces. Using nearest cell for these faces instead."
171  << endl << "On patch " << patch_.name() << " on region "
172  << nbrRegionName() << " with offset mode "
173  << offsetModeNames_[offsetMode_] << "." << endl;
174 
175  forAll(allPoints, alli)
176  {
177  const point& p = allPoints[alli];
178 
179  if (allNearest[alli].proci == -1)
180  {
181  const pointIndexHit pih =
182  nbrSearchEngine.cellTree().findNearest(p, sqr(great));
183 
184  allNearest[alli].proci = Pstream::myProcNo();
185  allNearest[alli].elementi = pih.index();
186  allNearest[alli].data = magSqr(pih.hitPoint() - p);
187  }
188  }
189 
191  (
192  allNearest,
194  );
195  Pstream::listCombineScatter(allNearest);
196  }
197 
198  // Build lists of samples
199  DynamicList<label> samplePatchGlobalFacesDyn;
200  DynamicList<label> sampleIndicesDyn;
201  forAll(allNearest, alli)
202  {
203  if (allNearest[alli].proci == Pstream::myProcNo())
204  {
205  samplePatchGlobalFacesDyn.append(alli);
206  sampleIndicesDyn.append(allNearest[alli].elementi);
207  }
208  }
209  sampleGlobalPatchFaces.transfer(samplePatchGlobalFacesDyn);
210  sampleIndices.transfer(sampleIndicesDyn);
211  }
212 
213  // Construct distribution schedule
214  List<Map<label>> compactMap;
215  mapPtr_.reset
216  (
217  new distributionMap
218  (
219  patchGlobalIndex,
220  sampleGlobalPatchFaces,
221  compactMap
222  )
223  );
224  const labelList oldToNew(move(sampleGlobalPatchFaces));
225  const labelList oldSampleIndices(move(sampleIndices));
226 
227  // Construct input mapping for data to be distributed
228  cellIndices_ = labelList(mapPtr_->constructSize(), -1);
229  UIndirectList<label>(cellIndices_, oldToNew) = oldSampleIndices;
230 
231  // Reverse the map. This means the map is "forward" when going from cells
232  // to this patch, which is logical.
233  mapPtr_.reset
234  (
235  new distributionMap
236  (
237  patch_.size(),
238  move(mapPtr_->constructMap()),
239  move(mapPtr_->subMap())
240  )
241  );
242 
243  // Dump connecting lines for debugging
244  if (debug)
245  {
246  const pointField::subField patchFaceCentres = patch_.faceCentres();
247 
248  OBJstream obj
249  (
250  patch_.name()
251  + "_processor"
253  + ".obj"
254  );
255 
256  // Cells -> Patch
257  pointField ccs(nbrMesh.cellCentres(), cellIndices_);
258  mapPtr_->distribute(ccs);
259  forAll(patch_, patchFacei)
260  {
261  const point& fc = patchFaceCentres[patchFacei];
262  const point mid = 0.51*fc + 0.49*ccs[patchFacei];
263  obj.write(linePointRef(fc, mid));
264  }
265 
266  // Patch -> Cells
267  pointField fcs(patchFaceCentres);
268  mapPtr_->reverseDistribute(cellIndices_.size(), fcs);
269  forAll(cellIndices_, i)
270  {
271  const label celli = cellIndices_[i];
272  if (celli == -1) continue;
273  const point& cc = nbrMesh.cellCentres()[celli];
274  const point mid = 0.51*cc + 0.49*fcs[i];
275  obj.write(linePointRef(cc, mid));
276  }
277  }
278 }
279 
280 
281 // * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * * * * * //
282 
284 (
285  const polyPatch& pp
286 )
287 :
288  patch_(pp),
289  nbrRegionName_(patch_.mesh().name()),
290  offsetMode_(NORMAL),
291  distance_(NaN),
292  offset_(vector::uniform(NaN)),
293  mapPtr_(nullptr),
294  cellIndices_()
295 {}
296 
297 
299 (
300  const polyPatch& pp,
301  const word& neighbourRegion
302 )
303 :
304  patch_(pp),
305  nbrRegionName_(neighbourRegion),
306  offsetMode_(NORMAL),
307  distance_(NaN),
308  offset_(vector::uniform(NaN)),
309  mapPtr_(nullptr),
310  cellIndices_()
311 {}
312 
313 
315 (
316  const polyPatch& pp,
317  const dictionary& dict
318 )
319 :
320  patch_(pp),
321  nbrRegionName_
322  (
323  dict.lookupOrDefaultBackwardsCompatible<word>
324  (
325  {"neighbourRegion", "sampleRegion"},
326  patch_.mesh().name()
327  )
328  ),
329  offsetMode_(readOffsetMode(dict)),
330  distance_
331  (
332  offsetMode_ == NORMAL
333  ? dict.lookup<scalar>("distance", units::length)
334  : NaN
335  ),
336  offset_
337  (
338  offsetMode_ == DIRECTION
339  ? dict.lookup<vector>("offset", units::length)
340  : vector::uniform(NaN)
341  ),
342  mapPtr_(nullptr),
343  cellIndices_()
344 {}
345 
346 
348 (
349  const polyPatch& pp,
350  const mappedInternalPatchBase& mpb
351 )
352 :
353  patch_(pp),
354  nbrRegionName_(mpb.nbrRegionName_),
355  offsetMode_(mpb.offsetMode_),
356  distance_(mpb.distance_),
357  offset_(mpb.offset_),
358  mapPtr_(nullptr),
359  cellIndices_()
360 {}
361 
362 
363 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
364 
366 {}
367 
368 
369 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
370 
372 {
373  return patch_.mesh().time().lookupObject<polyMesh>
374  (
375  nbrRegionName()
376  );
377 }
378 
379 
381 {
382  const polyMesh& mesh = patch_.mesh();
383 
384  // Force construction of min-tet decomp
385  (void)mesh.tetBasePtIs();
386 
387  // Allocate the result
388  tmp<pointField> tresult(new pointField(patch_.size()));
389  pointField& result = tresult.ref();
390 
391  // Compute the face points. Assume the face centre to begin with, and then
392  // try and find a better match on complex faces by doing a ray intersection
393  // with a triangulation of the face. This triangulation matches the
394  // tetrahedralisation used for cell searches. This maximises the chances
395  // that the point will be successfully found in a cell.
396  const pointField::subField patchFaceCentres = patch_.faceCentres();
397  forAll(patch_, patchFacei)
398  {
399  const pointField& ps = mesh.points();
400 
401  const face& f = patch_[patchFacei];
402  const point& fc = patchFaceCentres[patchFacei];
403 
404  result[patchFacei] = fc;
405 
406  if (f.size() > 3)
407  {
408  const label facei = patch_.start() + patchFacei;
409  const label celli = patch_.faceCells()[patchFacei];
410 
411  const point& cc = mesh.cellCentres()[celli];
412  const vector d = fc - cc;
413 
414  const label faceBasePtI = mesh.tetBasePtIs()[facei];
415 
416  for (label tetPtI = 1; tetPtI < f.size() - 1; tetPtI ++)
417  {
418  const label facePtI = (tetPtI + faceBasePtI) % f.size();
419  const label otherFacePtI = f.fcIndex(facePtI);
420 
421  const triPointRef tri
422  (
423  ps[f[faceBasePtI]],
424  ps[f[facePtI]],
425  ps[f[otherFacePtI]]
426  );
427 
428  const pointHit hitInfo =
430 
431  if (hitInfo.hit() && hitInfo.distance() > 0)
432  {
433  result[patchFacei] = hitInfo.hitPoint();
434  break;
435  }
436  }
437  }
438  }
439 
440  // Apply offset to get sample points
441  switch (offsetMode_)
442  {
443  case NORMAL:
444  result += distance_*patch_.faceNormals();
445  break;
446  case DIRECTION:
447  result += offset_;
448  break;
449  }
450 
451  return tresult;
452 }
453 
454 
456 {
457  mapPtr_.clear();
458  cellIndices_.clear();
459 }
460 
461 
463 {
464  return
465  dict.found("neighbourRegion")
466  || dict.found("offsetMode")
467  || dict.found("distance")
468  || dict.found("offset");
469 }
470 
471 
473 {
475  (
476  os,
477  "neighbourRegion",
478  patch_.mesh().name(),
479  nbrRegionName_
480  );
481 
482  writeEntry(os, "offsetMode", offsetModeNames_[offsetMode_]);
483 
484  switch (offsetMode_)
485  {
486  case NORMAL:
487  writeEntry(os, "distance", distance_);
488  break;
489  case DIRECTION:
490  writeEntry(os, "offset", offset_);
491  break;
492  }
493 }
494 
495 
496 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
Macros for easy insertion into run-time selection tables.
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:296
virtual const fileName & name() const
Return the name of the stream.
Definition: IOstream.H:297
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
Initialise the NamedEnum HashTable from the static list of names.
Definition: NamedEnum.H:55
Enum read(Istream &) const
Read a word from Istream and return the corresponding.
Definition: NamedEnum.C:55
OFstream which keeps track of vertices.
Definition: OBJstream.H:56
virtual Ostream & write(const char)
Write character.
Definition: OBJstream.C:81
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
const Point & hitPoint() const
Return hit point.
Definition: PointHit.H:126
scalar distance() const
Return distance to hit.
Definition: PointHit.H:139
bool hit() const
Is there a hit.
Definition: PointHit.H:120
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
Definition: PointIndexHit.H:54
const Point & hitPoint() const
Return hit point.
label index() const
Return index.
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.
Pre-declare related SubField type.
Definition: SubField.H:63
A List with indirect addressing.
Definition: UIndirectList.H:61
label fcIndex(const label i) const
Return the forward circular index, i.e. the next index.
Definition: UListI.H:58
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
static Form uniform(const Cmpt &s)
Return a VectorSpace with all elements = s.
Definition: VectorSpaceI.H:168
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
ITstream & lookup(const word &, bool recursive=false, bool patternMatch=true) const
Find and return an entry data stream.
Definition: dictionary.C:669
bool found(const word &, bool recursive=false, bool patternMatch=true) const
Search dictionary for given keyword.
Definition: dictionary.C:468
Class containing processor-to-processor mapping information.
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:76
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
Engine which provides mapping from cells to patch faces.
virtual void write(Ostream &) const
Write as a dictionary.
const polyMesh & nbrMesh() const
Get the region mesh.
tmp< pointField > samplePoints() const
Get the sample points.
virtual ~mappedInternalPatchBase()
Destructor.
static const NamedEnum< offsetMode, 2 > offsetModeNames_
offsetMode
How to project face centres.
static bool specified(const dictionary &dict)
Return whether or not the given dictionary contains a.
void calcMapping() const
Calculate mapping.
mappedInternalPatchBase(const polyPatch &)
Construct from patch.
offsetMode readOffsetMode(const dictionary &dict) const
Read the offset mode from a dictionary.
void clearOut()
Clear out data on mesh change.
Mesh object that implements searches within the local cells and faces.
Definition: meshSearch.H:59
label findCell(const point &p, const pointInCellShapes=pointInCellShapes::tets) const
Find the cell containing the given point.
Definition: meshSearch.C:173
static const meshSearch & New(const polyMesh &mesh, const pointInCellShapes=pointInCellShapes::tets)
Lookup or construct from mesh and cell decomposition option.
Definition: meshSearch.C:61
const indexedOctree< treeDataCell > & cellTree() const
Access the cell tree.
Definition: meshSearch.H:110
const Time & time() const
Return time.
const Type & lookupObject(const word &name) const
Lookup and return the object of the given Type and name.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:78
const labelIOList & tetBasePtIs() const
Return the tetBasePtIs.
Definition: polyMesh.C:1064
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1295
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:71
const vectorField & cellCentres() const
A class for managing temporary objects.
Definition: tmp.H:55
T & ref() const
Return non-const reference or generate a fatal error.
Definition: tmpI.H:197
A triangle primitive used to calculate face areas and swept volumes.
Definition: triangle.H:80
pointHit intersection(const point &p, const vector &q, const intersection::algorithm alg, const scalar tol=0.0) const
Fast intersection with a ray.
Definition: triangleI.H:424
A class for handling words, derived from string.
Definition: word.H:63
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
#define WarningInFunction
Report a warning using Foam::Warning.
const unitSet & length
Definition: units.C:137
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
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
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
line< point, const point & > linePointRef
Line using referred points.
Definition: linePointRef.H:45
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
tmp< DimensionedField< typename outerProduct< Type, Type >::type, GeoMesh, Field >> sqr(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
error FatalError
tmp< DimensionedField< scalar, GeoMesh, Field > > magSqr(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
defineTypeNameAndDebug(atmosphericBoundaryLayer, 0)
void writeEntry(Ostream &os, const word &key, const DimensionedFieldFunction< DimensionedFieldType > &f)
labelList f(nPoints)
dictionary dict
volScalarField & p
Operator to take smallest valid value.
Definition: RemoteData.H:118