probes.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 
26 #include "probes.H"
27 #include "volFields.H"
28 #include "OSspecific.H"
29 #include "writeFile.H"
30 #include "meshSearch.H"
31 #include "polyTopoChangeMap.H"
32 #include "polyMeshMap.H"
33 #include "polyDistributionMap.H"
35 
36 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
37 
38 namespace Foam
39 {
41 
43  (
45  probes,
47  );
48 }
49 
50 
51 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
52 
54 {
55  if (debug)
56  {
57  Info<< "probes: resetting sample locations" << endl;
58  }
59 
60  const meshSearch& searchEngine = meshSearch::New(mesh_);
61 
62  cellList_.clear();
64 
65  faceList_.clear();
67 
68  forAll(locations_, probei)
69  {
70  const vector& location = locations_[probei];
71 
72  const label celli = searchEngine.findCell(location);
73 
74  cellList_[probei] = celli;
75 
76  if (celli != -1)
77  {
78  const labelList& cellFaces = mesh.cells()[celli];
79  const vector& cellCentre = mesh.cellCentres()[celli];
80  scalar minDistance = great;
81  label minFaceID = -1;
82  forAll(cellFaces, i)
83  {
84  label facei = cellFaces[i];
85  vector dist = mesh.faceCentres()[facei] - cellCentre;
86  if (mag(dist) < minDistance)
87  {
88  minDistance = mag(dist);
89  minFaceID = facei;
90  }
91  }
92  faceList_[probei] = minFaceID;
93  }
94  else
95  {
96  faceList_[probei] = -1;
97  }
98 
99  if (debug && (cellList_[probei] != -1 || faceList_[probei] != -1))
100  {
101  Pout<< "probes : found point " << location
102  << " in cell " << cellList_[probei]
103  << " and face " << faceList_[probei] << endl;
104  }
105  }
106 
107 
108  // Check if all probes have been found.
109  forAll(cellList_, probei)
110  {
111  const vector& location = locations_[probei];
112  label celli = cellList_[probei];
113  label facei = faceList_[probei];
114 
115  // Check at least one processor with cell.
116  reduce(celli, maxOp<label>());
117  reduce(facei, maxOp<label>());
118 
119  if (celli == -1)
120  {
121  if (Pstream::master())
122  {
124  << "Did not find location " << location
125  << " in any cell. Skipping location." << endl;
126  }
127  }
128  else if (facei == -1)
129  {
130  if (Pstream::master())
131  {
133  << "Did not find location " << location
134  << " in any face. Skipping location." << endl;
135  }
136  }
137  else
138  {
139  // Make sure location not on two domains.
140  if (cellList_[probei] != -1 && cellList_[probei] != celli)
141  {
143  << "Location " << location
144  << " seems to be on multiple domains:"
145  << " cell " << cellList_[probei]
146  << " on my domain " << Pstream::myProcNo()
147  << " and cell " << celli << " on some other domain." << endl
148  << "This might happen if the probe location is on"
149  << " a processor patch. Change the location slightly"
150  << " to prevent this." << endl;
151  }
152 
153  if (faceList_[probei] != -1 && faceList_[probei] != facei)
154  {
156  << "Location " << location
157  << " seems to be on multiple domains:"
158  << " cell " << faceList_[probei]
159  << " on my domain " << Pstream::myProcNo()
160  << " and face " << facei << " on some other domain." << endl
161  << "This might happen if the probe location is on"
162  << " a processor patch. Change the location slightly"
163  << " to prevent this." << endl;
164  }
165  }
166  }
167 }
168 
169 
171 {
172  const label nFields = classifyFields();
173 
174  // adjust file streams
175  if (Pstream::master())
176  {
177  wordHashSet currentFields;
178 
179  currentFields.insert(scalarFields_);
180  currentFields.insert(vectorFields_);
181  currentFields.insert(sphericalTensorFields_);
182  currentFields.insert(symmTensorFields_);
183  currentFields.insert(tensorFields_);
184 
185  currentFields.insert(surfaceScalarFields_);
186  currentFields.insert(surfaceVectorFields_);
187  currentFields.insert(surfaceSphericalTensorFields_);
188  currentFields.insert(surfaceSymmTensorFields_);
189  currentFields.insert(surfaceTensorFields_);
190 
191  if (debug)
192  {
193  Info<< "Probing fields: " << currentFields << nl
194  << "Probing locations: " << locations_ << nl
195  << endl;
196  }
197 
198  const fileName probeDir =
199  mesh_.time().globalPath()
201  /(mesh_.name() != polyMesh::defaultRegion ? mesh_.name() : word())
202  /name()
203  /mesh_.time().name();
204 
205  // ignore known fields, close streams for fields that no longer exist
206  forAllIter(HashPtrTable<OFstream>, probeFilePtrs_, iter)
207  {
208  if (!currentFields.erase(iter.key()))
209  {
210  if (debug)
211  {
212  Info<< "close probe stream: " << iter()->name() << endl;
213  }
214 
215  delete probeFilePtrs_.remove(iter);
216  }
217  }
218 
219  // currentFields now just has the new fields - open streams for them
220  forAllConstIter(wordHashSet, currentFields, iter)
221  {
222  const word& fieldName = iter.key();
223 
224  // Create directory if does not exist.
225  mkDir(probeDir);
226 
227  OFstream* fPtr = new OFstream(probeDir/fieldName);
228  OFstream& os = *fPtr;
229 
230  if (debug)
231  {
232  Info<< "open probe stream: " << os.name() << endl;
233  }
234 
235  probeFilePtrs_.insert(fieldName, fPtr);
236 
237  const unsigned int w = IOstream::defaultPrecision() + 7;
238  os << setf(ios_base::left);
239 
240  forAll(locations_, probei)
241  {
242  os<< "# Probe " << probei << ' ' << locations_[probei] << endl;
243  }
244 
245  os << setw(w) << "# Time";
246 
247  forAll(locations_, probei)
248  {
249  os<< ' ' << setw(w) << probei;
250  }
251  os<< endl;
252  }
253  }
254 
255  return nFields;
256 }
257 
258 
259 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
260 
262 (
263  const word& name,
264  const Time& t,
265  const dictionary& dict,
266  const bool initialise
267 )
268 :
269  functionObject(name, t),
270  mesh_
271  (
272  refCast<const fvMesh>
273  (
274  t.lookupObject<objectRegistry>
275  (
276  dict.lookupOrDefault("region", polyMesh::defaultRegion)
277  )
278  )
279  ),
280  fields_(),
281  fixedLocations_(true),
282  interpolationScheme_("cell")
283 {
284  read(dict, initialise);
285 }
286 
287 
288 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
289 
291 {}
292 
293 
294 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
295 
296 bool Foam::probes::read(const dictionary& dict, const bool initialise)
297 {
298  dict.lookup("probeLocations") >> locations_;
299 
300  dict.lookup("fields") >> fields_;
301 
302  dict.readIfPresent("fixedLocations", fixedLocations_);
303  if (dict.readIfPresent("interpolationScheme", interpolationScheme_))
304  {
305  if (!fixedLocations_ && interpolationScheme_ != "cell")
306  {
308  << "Only cell interpolation can be applied when "
309  << "not using fixedLocations. InterpolationScheme "
310  << "entry will be ignored";
311  }
312  }
313 
314  if (initialise)
315  {
316  // Initialise cells to sample from supplied locations
317  findElements(mesh_);
318 
319  prepare();
320  }
321 
322  return true;
323 }
324 
325 
327 {
328  return read(dict, true);
329 }
330 
331 
333 {
334  return fields_;
335 }
336 
337 
339 {
340  return true;
341 }
342 
343 
345 {
346  if (locations_.size() && prepare())
347  {
348  sampleAndWrite(scalarFields_);
349  sampleAndWrite(vectorFields_);
350  sampleAndWrite(sphericalTensorFields_);
351  sampleAndWrite(symmTensorFields_);
352  sampleAndWrite(tensorFields_);
353 
354  sampleAndWriteSurfaceFields(surfaceScalarFields_);
355  sampleAndWriteSurfaceFields(surfaceVectorFields_);
356  sampleAndWriteSurfaceFields(surfaceSphericalTensorFields_);
357  sampleAndWriteSurfaceFields(surfaceSymmTensorFields_);
358  sampleAndWriteSurfaceFields(surfaceTensorFields_);
359  }
360 
361  return true;
362 }
363 
364 
366 {
367  DebugInfo<< "probes: movePoints" << endl;
368 
369  if (&mesh != &mesh_) return;
370 
371  if (fixedLocations_)
372  {
373  findElements(mesh_);
374  }
375 }
376 
377 
379 {
380  DebugInfo<< "probes: topoChange" << endl;
381 
382  if (&map.mesh() != &mesh_) return;
383 
384  if (fixedLocations_)
385  {
386  findElements(mesh_);
387  }
388  else
389  {
390  if (debug)
391  {
392  Info<< "probes: remapping sample locations" << endl;
393  }
394 
395  // 1. Update cells
396  if (!map.reverseCellMap().empty())
397  {
398  DynamicList<label> elems(cellList_.size());
399 
400  const labelList& reverseMap = map.reverseCellMap();
401  forAll(cellList_, i)
402  {
403  const label celli = cellList_[i];
404  const label newCelli = reverseMap[celli];
405 
406  if (newCelli == -1)
407  {
408  // cell removed
409  }
410  else if (newCelli < -1)
411  {
412  // cell merged
413  elems.append(-newCelli - 2);
414  }
415  else
416  {
417  // valid new cell
418  elems.append(newCelli);
419  }
420  }
421 
422  cellList_.transfer(elems);
423  }
424 
425  // 2. Update faces
426  if (!map.reverseFaceMap().empty())
427  {
428  DynamicList<label> elems(faceList_.size());
429 
430  const labelList& reverseMap = map.reverseFaceMap();
431  forAll(faceList_, i)
432  {
433  const label facei = faceList_[i];
434  const label newFacei = reverseMap[facei];
435 
436  if (newFacei == -1)
437  {
438  // face removed
439  }
440  else if (newFacei < -1)
441  {
442  // face merged
443  elems.append(-newFacei - 2);
444  }
445  else
446  {
447  // valid new face
448  elems.append(newFacei);
449  }
450  }
451 
452  faceList_.transfer(elems);
453  }
454  }
455 }
456 
457 
459 {
460  DebugInfo<< "probes: mapMesh" << endl;
461 
462  if (&map.mesh() != &mesh_) return;
463 
464  findElements(mesh_);
465 }
466 
467 
469 {
470  DebugInfo<< "probes: distribute" << endl;
471 
472  if (&map.mesh() != &mesh_) return;
473 
474  // Distribute the list of cells and faces. There might be a cheaper way of
475  // doing this than distributing a full cell/face list. But this way is easy
476  // and readable and uses the polyDistributionMap at a high level without
477  // needing to think about the actual communication or addressing. And
478  // run-distribution shouldn't be happening too often. So it's fine.
479  auto distribute = []
480  (
481  const label nOldElements,
482  const distributionMap& elementMap,
483  labelList& probeElements
484  )
485  {
486  labelList elementProbes(nOldElements, -1);
487  forAll(probeElements, probei)
488  {
489  if (probeElements[probei] != -1)
490  {
491  elementProbes[probeElements[probei]] = probei;
492  }
493  }
494 
495  elementMap.distribute(elementProbes);
496 
497  probeElements = -1;
498  forAll(elementProbes, elementi)
499  {
500  if (elementProbes[elementi] != -1)
501  {
502  probeElements[elementProbes[elementi]] = elementi;
503  }
504  }
505  };
506 
507  distribute(map.nOldCells(), map.cellMap(), cellList_);
508  distribute(map.nOldFaces(), map.faceMap(), faceList_);
509 }
510 
511 
512 // ************************************************************************* //
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
#define forAllIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:474
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:492
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
A HashTable specialisation for hashing pointers.
Definition: HashPtrTable.H:68
A HashTable with keys but without contents.
Definition: HashSet.H:62
bool insert(const Key &key)
Insert a new entry.
Definition: HashSet.H:109
bool erase(const iterator &)
Erase a hashedEntry specified by given iterator.
Definition: HashTable.C:445
static unsigned int defaultPrecision()
Return the default precision.
Definition: IOstream.H:473
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:125
void setSize(const label)
Reset size of List.
Definition: List.C:281
Output to file stream.
Definition: OFstream.H:87
const fileName & name() const
Return the name of the stream.
Definition: OFstream.H:121
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:76
bool empty() const
Return true if the UList is empty (ie, size() is zero)
Definition: UListI.H:325
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:423
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
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 readIfPresent(const word &, T &, bool recursive=false, bool patternMatch=true) const
Find an entry if present, and assign to T.
Class containing processor-to-processor mapping information.
A class for handling file names.
Definition: fileName.H:82
Abstract base-class for Time/database functionObjects.
static const word outputPrefix
Directory prefix.
Definition: writeFile.H:72
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:98
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
Registry of regIOobjects.
Class containing mesh-to-mesh mapping information after a mesh distribution where we send parts of me...
label nOldCells() const
Number of cells in mesh before distribution.
const distributionMap & cellMap() const
Cell distribute map.
const distributionMap & faceMap() const
Face distribute map.
label nOldFaces() const
Number of faces in mesh before distribution.
const polyMesh & mesh() const
Return polyMesh.
Class containing mesh-to-mesh mapping information.
Definition: polyMeshMap.H:51
const polyMesh & mesh() const
Return polyMesh.
Definition: polyMeshMap.H:75
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:78
static word defaultRegion
Return the default region name.
Definition: polyMesh.H:260
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
const labelList & reverseCellMap() const
Reverse cell map.
const labelList & reverseFaceMap() const
Reverse face map.
const polyMesh & mesh() const
Return polyMesh.
const vectorField & faceCentres() const
const vectorField & cellCentres() const
const cellList & cells() const
Set of locations to sample.
Definition: probes.H:66
virtual ~probes()
Destructor.
Definition: probes.C:290
const fvMesh & mesh_
Const reference to fvMesh.
Definition: probes.H:90
labelList cellList_
Definition: probes.H:128
pointField locations_
Probe locations.
Definition: probes.H:96
virtual wordList fields() const
Return the list of fields required.
Definition: probes.C:332
virtual void topoChange(const polyTopoChangeMap &)
Update topology using the given map.
Definition: probes.C:378
label prepare()
Classify field type and Open/close file streams,.
Definition: probes.C:170
virtual void distribute(const polyDistributionMap &)
Update using the given distribution map.
Definition: probes.C:468
probes(const word &name, const Time &time, const dictionary &dict, const bool initialise=true)
Construct from Time and dictionary.
Definition: probes.C:262
virtual void findElements(const fvMesh &)
Find cells and faces containing probes.
Definition: probes.C:53
bool read(const dictionary &, const bool initialise)
Read the probes.
Definition: probes.C:296
virtual void mapMesh(const polyMeshMap &)
Update from another mesh using the given map.
Definition: probes.C:458
virtual void movePoints(const polyMesh &)
Update topology using the given map.
Definition: probes.C:365
labelList faceList_
Definition: probes.H:131
virtual bool execute()
Execute, currently does nothing.
Definition: probes.C:338
virtual bool write()
Sample and write.
Definition: probes.C:344
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 DebugInfo
Report an information message using Foam::Info.
#define WarningInFunction
Report a warning using Foam::Warning.
Namespace for OpenFOAM.
Smanip< ios_base::fmtflags > setf(const ios_base::fmtflags flags)
Definition: IOmanip.H:164
bool read(const char *, int32_t &)
Definition: int32IO.C:85
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
addToRunTimeSelectionTable(polyPatch, mergedCyclicPolyPatch, word)
To & refCast(From &r)
Reference type cast template function.
Definition: typeInfo.H:141
bool mkDir(const fileName &, mode_t=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:290
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
messageStream Info
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
tmp< DimensionedField< scalar, GeoMesh, Field > > mag(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
defineTypeNameAndDebug(atmosphericBoundaryLayer, 0)
static const char nl
Definition: Ostream.H:297
dictionary dict