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-2025 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 "treeDataCell.H"
31 #include "indexedOctree.H"
32 #include "globalIndex.H"
33 #include "RemoteData.H"
34 #include "OBJstream.H"
36 
37 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
38 
39 namespace Foam
40 {
42 }
43 
46 {
47  "normal",
48  "direction"
49 };
50 
51 
52 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
53 
56 (
57  const dictionary& dict
58 ) const
59 {
60  if (dict.found("offsetMode"))
61  {
62  return offsetModeNames_.read(dict.lookup("offsetMode"));
63  }
64  else
65  {
66  const bool haveDistance = dict.found("distance");
67  const bool haveOffset = dict.found("offset");
68 
69  if (haveDistance == haveOffset)
70  {
71  // Error. Demand "offsetMode" setting to disambiguate.
72  return offsetModeNames_.read(dict.lookup("offsetMode"));
73  }
74  else if (haveDistance)
75  {
76  return NORMAL;
77  }
78  else
79  {
80  return DIRECTION;
81  }
82  }
83 }
84 
85 
87 {
88  if (mapPtr_.valid())
89  {
91  << "Mapping already calculated" << exit(FatalError);
92  }
93 
94  const polyMesh& nbrMesh = this->nbrMesh();
95 
96  const globalIndex patchGlobalIndex(patch_.size());
97 
98  // Find processor and cell/face indices of samples
99  labelList sampleGlobalPatchFaces, sampleIndices;
100  {
101  // Gather the sample points into a single globally indexed list
102  List<point> allPoints(patchGlobalIndex.size());
103  {
104  List<pointField> procSamplePoints(Pstream::nProcs());
105  procSamplePoints[Pstream::myProcNo()] = this->samplePoints();
106  Pstream::gatherList(procSamplePoints);
107  Pstream::scatterList(procSamplePoints);
108 
109  forAll(procSamplePoints, proci)
110  {
111  forAll(procSamplePoints[proci], procSamplei)
112  {
113  allPoints
114  [
115  patchGlobalIndex.toGlobal(proci, procSamplei)
116  ] = procSamplePoints[proci][procSamplei];
117  }
118  }
119  }
120 
121  // List of possibly remote sampling cells
122  List<RemoteData<scalar>> allNearest(patchGlobalIndex.size());
123 
124  // Find containing cell for every sampling point
125  const indexedOctree<Foam::treeDataCell>& tree = nbrMesh.cellTree();
126  forAll(allPoints, alli)
127  {
128  const point& p = allPoints[alli];
129 
130  const label celli = tree.findInside(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  const indexedOctree<Foam::treeDataCell>& tree = nbrMesh.cellTree();
176  forAll(allPoints, alli)
177  {
178  const point& p = allPoints[alli];
179 
180  if (allNearest[alli].proci == -1)
181  {
182  const pointIndexHit pih = tree.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  OBJstream obj
247  (
248  patch_.name()
249  + "_processor"
251  + ".obj"
252  );
253 
254  // Cells -> Patch
255  pointField ccs(nbrMesh.cellCentres(), cellIndices_);
256  mapPtr_->distribute(ccs);
257  forAll(patch_, patchFacei)
258  {
259  const point& fc = patch_.faceCentres()[patchFacei];
260  const point mid = 0.51*fc + 0.49*ccs[patchFacei];
261  obj.write(linePointRef(fc, mid));
262  }
263 
264  // Patch -> Cells
265  pointField fcs(patch_.faceCentres());
266  mapPtr_->reverseDistribute(cellIndices_.size(), fcs);
267  forAll(cellIndices_, i)
268  {
269  const label celli = cellIndices_[i];
270  if (celli == -1) continue;
271  const point& cc = nbrMesh.cellCentres()[celli];
272  const point mid = 0.51*cc + 0.49*fcs[i];
273  obj.write(linePointRef(cc, mid));
274  }
275  }
276 }
277 
278 
279 // * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * * * * * //
280 
282 (
283  const polyPatch& pp
284 )
285 :
286  patch_(pp),
287  nbrRegionName_(patch_.boundaryMesh().mesh().name()),
288  offsetMode_(NORMAL),
289  distance_(NaN),
290  offset_(vector::uniform(NaN)),
291  mapPtr_(nullptr),
292  cellIndices_()
293 {}
294 
295 
297 (
298  const polyPatch& pp,
299  const word& neighbourRegion
300 )
301 :
302  patch_(pp),
303  nbrRegionName_(neighbourRegion),
304  offsetMode_(NORMAL),
305  distance_(NaN),
306  offset_(vector::uniform(NaN)),
307  mapPtr_(nullptr),
308  cellIndices_()
309 {}
310 
311 
313 (
314  const polyPatch& pp,
315  const dictionary& dict
316 )
317 :
318  patch_(pp),
319  nbrRegionName_
320  (
321  dict.lookupOrDefaultBackwardsCompatible<word>
322  (
323  {"neighbourRegion", "sampleRegion"},
324  patch_.boundaryMesh().mesh().name()
325  )
326  ),
327  offsetMode_(readOffsetMode(dict)),
328  distance_
329  (
330  offsetMode_ == NORMAL
331  ? dict.lookup<scalar>("distance")
332  : NaN
333  ),
334  offset_
335  (
336  offsetMode_ == DIRECTION
337  ? dict.lookup<vector>("offset")
338  : vector::uniform(NaN)
339  ),
340  mapPtr_(nullptr),
341  cellIndices_()
342 {}
343 
344 
346 (
347  const polyPatch& pp,
348  const mappedInternalPatchBase& mpb
349 )
350 :
351  patch_(pp),
352  nbrRegionName_(mpb.nbrRegionName_),
353  offsetMode_(mpb.offsetMode_),
354  distance_(mpb.distance_),
355  offset_(mpb.offset_),
356  mapPtr_(nullptr),
357  cellIndices_()
358 {}
359 
360 
361 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
362 
364 {}
365 
366 
367 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
368 
370 {
371  return patch_.boundaryMesh().mesh().time().lookupObject<polyMesh>
372  (
373  nbrRegionName()
374  );
375 }
376 
377 
379 {
380  const polyMesh& mesh = patch_.boundaryMesh().mesh();
381 
382  // Force construction of min-tet decomp
383  (void)mesh.tetBasePtIs();
384 
385  // Allocate the result
386  tmp<pointField> tresult(new pointField(patch_.size()));
387  pointField& result = tresult.ref();
388 
389  // Compute the face points. Assume the face centre to begin with, and then
390  // try and find a better match on complex faces by doing a ray intersection
391  // with a triangulation of the face. This triangulation matches the
392  // tetrahedralisation used for cell searches. This maximises the chances
393  // that the point will be successfully found in a cell.
394  forAll(patch_, patchFacei)
395  {
396  const pointField& ps = mesh.points();
397 
398  const face& f = patch_[patchFacei];
399  const point& fc = patch_.faceCentres()[patchFacei];
400 
401  result[patchFacei] = fc;
402 
403  if (f.size() > 3)
404  {
405  const label facei = patch_.start() + patchFacei;
406  const label celli = patch_.faceCells()[patchFacei];
407 
408  const point& cc = mesh.cellCentres()[celli];
409  const vector d = fc - cc;
410 
411  const label faceBasePtI = mesh.tetBasePtIs()[facei];
412 
413  for (label tetPtI = 1; tetPtI < f.size() - 1; tetPtI ++)
414  {
415  const label facePtI = (tetPtI + faceBasePtI) % f.size();
416  const label otherFacePtI = f.fcIndex(facePtI);
417 
418  const triPointRef tri
419  (
420  ps[f[faceBasePtI]],
421  ps[f[facePtI]],
422  ps[f[otherFacePtI]]
423  );
424 
425  const pointHit hitInfo =
427 
428  if (hitInfo.hit() && hitInfo.distance() > 0)
429  {
430  result[patchFacei] = hitInfo.hitPoint();
431  break;
432  }
433  }
434  }
435  }
436 
437  // Apply offset to get sample points
438  switch (offsetMode_)
439  {
440  case NORMAL:
441  result += distance_*patch_.faceNormals();
442  break;
443  case DIRECTION:
444  result += offset_;
445  break;
446  }
447 
448  return tresult;
449 }
450 
451 
453 {
454  mapPtr_.clear();
455  cellIndices_.clear();
456 }
457 
458 
460 {
461  return
462  dict.found("neighbourRegion")
463  || dict.found("offsetMode")
464  || dict.found("distance")
465  || dict.found("offset");
466 }
467 
468 
470 {
472  (
473  os,
474  "neighbourRegion",
475  patch_.boundaryMesh().mesh().name(),
476  nbrRegionName_
477  );
478 
479  writeEntry(os, "offsetMode", offsetModeNames_[offsetMode_]);
480 
481  switch (offsetMode_)
482  {
483  case NORMAL:
484  writeEntry(os, "distance", distance_);
485  break;
486  case DIRECTION:
487  writeEntry(os, "offset", offset_);
488  break;
489  }
490 }
491 
492 
493 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
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:54
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.
A List with indirect addressing.
Definition: UIndirectList.H:60
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:740
bool found(const word &, bool recursive=false, bool patternMatch=true) const
Search dictionary for given keyword.
Definition: dictionary.C:539
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
label findInside(const point &) const
Find shape containing point. Only implemented for certain.
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.
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 polyMesh & mesh() const
Return the mesh reference.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:80
const labelIOList & tetBasePtIs() const
Return the tetBasePtIs.
Definition: polyMesh.C:1046
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:401
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1331
const indexedOctree< treeDataCell > & cellTree() const
Return the cell search tree.
Definition: polyMesh.C:1080
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:70
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:407
A class for handling words, derived from string.
Definition: word.H:62
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.
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:258
line< point, const point & > linePointRef
Line using referred points.
Definition: linePointRef.H:45
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
void writeEntry(Ostream &os, const HashTable< T, Key, Hash > &ht)
Definition: HashTableIO.C:96
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
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)
labelList f(nPoints)
dictionary dict
volScalarField & p
Operator to take smallest valid value.
Definition: RemoteData.H:118