probes.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2017 OpenFOAM Foundation
9  Copyright (C) 2015-2022 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "probes.H"
30 #include "dictionary.H"
31 #include "volFields.H"
32 #include "surfaceFields.H"
33 #include "Time.H"
34 #include "IOmanip.H"
35 #include "mapPolyMesh.H"
37 
38 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
39 
40 namespace Foam
41 {
42  defineTypeNameAndDebug(probes, 0);
43 
45  (
46  functionObject,
47  probes,
48  dictionary
49  );
50 }
51 
52 
53 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
54 
55 void Foam::probes::createProbeFiles(const wordList& fieldNames)
56 {
57  // Open new output streams
58 
59  bool needsNewFiles = false;
60  for (const word& fieldName : fieldNames)
61  {
62  if (!probeFilePtrs_.found(fieldName))
63  {
64  needsNewFiles = true;
65  break;
66  }
67  }
68 
69  if (needsNewFiles && Pstream::master())
70  {
71  DebugInfo
72  << "Probing fields: " << fieldNames << nl
73  << "Probing locations: " << *this << nl
74  << endl;
75 
76  // Put in undecomposed case
77  // (Note: gives problems for distributed data running)
78 
79  fileName probeDir
80  (
83  / name()/mesh_.regionName()
84  // Use startTime as the instance for output files
86  );
87  probeDir.clean(); // Remove unneeded ".."
88 
89  // Create directory if needed
90  Foam::mkDir(probeDir);
91 
92  for (const word& fieldName : fieldNames)
93  {
94  if (probeFilePtrs_.found(fieldName))
95  {
96  // Safety
97  continue;
98  }
99 
100  auto osPtr = autoPtr<OFstream>::New(probeDir/fieldName);
101  auto& os = *osPtr;
102 
103  probeFilePtrs_.insert(fieldName, osPtr);
104 
105  DebugInfo<< "open probe stream: " << os.name() << endl;
106 
107  const unsigned int width(IOstream::defaultPrecision() + 7);
108 
109  forAll(*this, probei)
110  {
111  os << "# Probe " << probei << ' ' << operator[](probei);
112 
113  if (processor_[probei] == -1)
114  {
115  os << " # Not Found";
116  }
117  // Only for patchProbes
118  else if (probei < patchIDList_.size())
119  {
120  const label patchi = patchIDList_[probei];
121  if (patchi != -1)
122  {
123  const polyBoundaryMesh& bm = mesh_.boundaryMesh();
124  if
125  (
126  patchi < bm.nNonProcessor()
127  || processor_[probei] == Pstream::myProcNo()
128  )
129  {
130  os << " at patch " << bm[patchi].name();
131  }
132  os << " with a distance of "
133  << mag(operator[](probei)-oldPoints_[probei])
134  << " m to the original point "
135  << oldPoints_[probei];
136  }
137  }
138 
139  os << nl;
140  }
141 
142  os << '#' << setw(IOstream::defaultPrecision() + 6)
143  << "Probe";
144 
145  forAll(*this, probei)
146  {
147  if (includeOutOfBounds_ || processor_[probei] != -1)
148  {
149  os << ' ' << setw(width) << probei;
150  }
151  }
152  os << nl;
153 
154  os << '#' << setw(IOstream::defaultPrecision() + 6)
155  << "Time" << endl;
156  }
157  }
158 }
159 
160 
161 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
162 
163 void Foam::probes::findElements(const fvMesh& mesh)
164 {
165  DebugInfo<< "probes: resetting sample locations" << endl;
166 
167  elementList_.resize_nocopy(pointField::size());
168  faceList_.resize_nocopy(pointField::size());
169  processor_.resize_nocopy(pointField::size());
170  processor_ = -1;
171 
172  forAll(*this, probei)
173  {
174  const point& location = (*this)[probei];
175 
176  const label celli = mesh.findCell(location);
177 
178  elementList_[probei] = celli;
179 
180  if (celli != -1)
181  {
182  const labelList& cellFaces = mesh.cells()[celli];
183  const vector& cellCentre = mesh.cellCentres()[celli];
184  scalar minDistance = GREAT;
185  label minFaceID = -1;
186  forAll(cellFaces, i)
187  {
188  label facei = cellFaces[i];
189  vector dist = mesh.faceCentres()[facei] - cellCentre;
190  if (mag(dist) < minDistance)
191  {
192  minDistance = mag(dist);
193  minFaceID = facei;
194  }
195  }
196  faceList_[probei] = minFaceID;
197  }
198  else
199  {
200  faceList_[probei] = -1;
201  }
202 
203  if (debug && (elementList_[probei] != -1 || faceList_[probei] != -1))
204  {
205  Pout<< "probes : found point " << location
206  << " in cell " << elementList_[probei]
207  << " and face " << faceList_[probei] << endl;
208  }
209  }
210 
211 
212  // Check if all probes have been found.
213  forAll(elementList_, probei)
214  {
215  const point& location = operator[](probei);
216  label celli = elementList_[probei];
217  label facei = faceList_[probei];
218 
219  processor_[probei] = (celli != -1 ? Pstream::myProcNo() : -1);
220 
221  // Check at least one processor with cell.
222  reduce(celli, maxOp<label>());
223  reduce(facei, maxOp<label>());
224  reduce(processor_[probei], maxOp<label>());
225 
226  if (celli == -1)
227  {
228  if (Pstream::master())
229  {
231  << "Did not find location " << location
232  << " in any cell. Skipping location." << endl;
233  }
234  }
235  else if (facei == -1)
236  {
237  if (Pstream::master())
238  {
240  << "Did not find location " << location
241  << " in any face. Skipping location." << endl;
242  }
243  }
244  else
245  {
246  // Make sure location not on two domains.
247  if (elementList_[probei] != -1 && elementList_[probei] != celli)
248  {
250  << "Location " << location
251  << " seems to be on multiple domains:"
252  << " cell " << elementList_[probei]
253  << " on my domain " << Pstream::myProcNo()
254  << " and cell " << celli << " on some other domain."
255  << nl
256  << "This might happen if the probe location is on"
257  << " a processor patch. Change the location slightly"
258  << " to prevent this." << endl;
259  }
260 
261  if (faceList_[probei] != -1 && faceList_[probei] != facei)
262  {
264  << "Location " << location
265  << " seems to be on multiple domains:"
266  << " cell " << faceList_[probei]
267  << " on my domain " << Pstream::myProcNo()
268  << " and face " << facei << " on some other domain."
269  << nl
270  << "This might happen if the probe location is on"
271  << " a processor patch. Change the location slightly"
272  << " to prevent this." << endl;
273  }
274  }
275  }
276 }
277 
278 
279 Foam::label Foam::probes::prepare(unsigned request)
280 {
281  // Prefilter on selection
282  HashTable<wordHashSet> selected =
283  (
284  loadFromFiles_
285  ? IOobjectList(mesh_, mesh_.time().timeName()).classes(fieldSelection_)
286  : mesh_.classes(fieldSelection_)
287  );
288 
289  // Classify and count fields
290  label nFields = 0;
291  do
292  {
293  #undef doLocalCode
294  #define doLocalCode(InputType, Target) \
295  { \
296  Target.clear(); /* Remove old values */ \
297  const auto iter = selected.cfind(InputType::typeName); \
298  if (iter.found()) \
299  { \
300  /* Add new (current) values */ \
301  Target.append(iter.val().sortedToc()); \
302  nFields += Target.size(); \
303  } \
304  }
305 
306  doLocalCode(volScalarField, scalarFields_);
307  doLocalCode(volVectorField, vectorFields_)
308  doLocalCode(volSphericalTensorField, sphericalTensorFields_);
309  doLocalCode(volSymmTensorField, symmTensorFields_);
310  doLocalCode(volTensorField, tensorFields_);
311 
312  doLocalCode(surfaceScalarField, surfaceScalarFields_);
313  doLocalCode(surfaceVectorField, surfaceVectorFields_);
314  doLocalCode(surfaceSphericalTensorField, surfaceSphericalTensorFields_);
315  doLocalCode(surfaceSymmTensorField, surfaceSymmTensorFields_);
316  doLocalCode(surfaceTensorField, surfaceTensorFields_);
317  #undef doLocalCode
318  }
319  while (false);
320 
321 
322  // Adjust file streams
323  if (Pstream::master())
324  {
325  wordHashSet currentFields(2*nFields);
326  currentFields.insert(scalarFields_);
327  currentFields.insert(vectorFields_);
328  currentFields.insert(sphericalTensorFields_);
329  currentFields.insert(symmTensorFields_);
330  currentFields.insert(tensorFields_);
331 
332  currentFields.insert(surfaceScalarFields_);
333  currentFields.insert(surfaceVectorFields_);
334  currentFields.insert(surfaceSphericalTensorFields_);
335  currentFields.insert(surfaceSymmTensorFields_);
336  currentFields.insert(surfaceTensorFields_);
337 
338  DebugInfo
339  << "Probing fields: " << currentFields << nl
340  << "Probing locations: " << *this << nl
341  << endl;
342 
343  // Close streams for fields that no longer exist
344  forAllIters(probeFilePtrs_, iter)
345  {
346  if (!currentFields.erase(iter.key()))
347  {
348  DebugInfo<< "close probe stream: " << iter()->name() << endl;
349 
350  probeFilePtrs_.remove(iter);
351  }
352  }
353 
354  if ((request & ACTION_WRITE) && !currentFields.empty())
355  {
356  createProbeFiles(currentFields.sortedToc());
357  }
358  }
359 
360  return nFields;
361 }
362 
363 
364 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
365 
366 Foam::probes::probes
367 (
368  const word& name,
369  const Time& runTime,
370  const dictionary& dict,
371  const bool loadFromFiles,
372  const bool readFields
373 )
374 :
375  functionObjects::fvMeshFunctionObject(name, runTime, dict),
376  pointField(),
377  loadFromFiles_(loadFromFiles),
378  fixedLocations_(true),
379  includeOutOfBounds_(true),
380  verbose_(false),
381  onExecute_(false),
382  fieldSelection_(),
383  samplePointScheme_("cell")
384 {
385  if (readFields)
386  {
388  }
389 }
390 
391 
392 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
393 
394 bool Foam::probes::verbose(const bool on) noexcept
395 {
396  bool old(verbose_);
397  verbose_ = on;
398  return old;
399 }
400 
401 
403 {
404  dict.readEntry("probeLocations", static_cast<pointField&>(*this));
405  dict.readEntry("fields", fieldSelection_);
406 
407  dict.readIfPresent("fixedLocations", fixedLocations_);
408  dict.readIfPresent("includeOutOfBounds", includeOutOfBounds_);
409 
410  verbose_ = dict.getOrDefault("verbose", false);
411  onExecute_ = dict.getOrDefault("sampleOnExecute", false);
412 
413  if (dict.readIfPresent("interpolationScheme", samplePointScheme_))
414  {
415  if (!fixedLocations_ && samplePointScheme_ != "cell")
416  {
418  << "Only cell interpolation can be applied when "
419  << "not using fixedLocations. InterpolationScheme "
420  << "entry will be ignored"
421  << endl;
422  }
423  }
424 
425  // Initialise cells to sample from supplied locations
426  findElements(mesh_);
427 
428  // Close old (ununsed) streams
429  prepare(ACTION_NONE);
430 
431  return true;
432 }
433 
434 
435 bool Foam::probes::performAction(unsigned request)
436 {
437  if (!pointField::empty() && request && prepare(request))
438  {
439  performAction(scalarFields_, request);
440  performAction(vectorFields_, request);
441  performAction(sphericalTensorFields_, request);
442  performAction(symmTensorFields_, request);
443  performAction(tensorFields_, request);
444 
445  performAction(surfaceScalarFields_, request);
446  performAction(surfaceVectorFields_, request);
447  performAction(surfaceSphericalTensorFields_, request);
448  performAction(surfaceSymmTensorFields_, request);
449  performAction(surfaceTensorFields_, request);
450  }
451  return true;
452 }
453 
454 
456 {
457  if (onExecute_)
458  {
459  return performAction(ACTION_ALL & ~ACTION_WRITE);
460  }
461 
462  return true;
463 }
464 
466 bool Foam::probes::write()
467 {
468  return performAction(ACTION_ALL);
469 }
470 
471 
472 void Foam::probes::updateMesh(const mapPolyMesh& mpm)
473 {
474  DebugInfo<< "probes: updateMesh" << endl;
475 
476  if (&mpm.mesh() != &mesh_)
477  {
478  return;
479  }
480 
481  if (fixedLocations_)
482  {
483  findElements(mesh_);
484  }
485  else
486  {
487  DebugInfo<< "probes: remapping sample locations" << endl;
488 
489  // 1. Update cells
490  {
491  DynamicList<label> elems(elementList_.size());
492 
493  const labelList& reverseMap = mpm.reverseCellMap();
494  forAll(elementList_, i)
495  {
496  label celli = elementList_[i];
497  if (celli != -1)
498  {
499  label newCelli = reverseMap[celli];
500  if (newCelli == -1)
501  {
502  // cell removed
503  }
504  else if (newCelli < -1)
505  {
506  // cell merged
507  elems.append(-newCelli - 2);
508  }
509  else
510  {
511  // valid new cell
512  elems.append(newCelli);
513  }
514  }
515  else
516  {
517  // Keep -1 elements so the size stays the same
518  elems.append(-1);
519  }
520  }
521 
522  elementList_.transfer(elems);
523  }
524 
525  // 2. Update faces
526  {
527  DynamicList<label> elems(faceList_.size());
528 
529  const labelList& reverseMap = mpm.reverseFaceMap();
530  for (const label facei : faceList_)
531  {
532  if (facei != -1)
533  {
534  label newFacei = reverseMap[facei];
535  if (newFacei == -1)
536  {
537  // face removed
538  }
539  else if (newFacei < -1)
540  {
541  // face merged
542  elems.append(-newFacei - 2);
543  }
544  else
545  {
546  // valid new face
547  elems.append(newFacei);
548  }
549  }
550  else
551  {
552  // Keep -1 elements
553  elems.append(-1);
554  }
555  }
557  faceList_.transfer(elems);
558  }
559  }
560 }
561 
562 
563 void Foam::probes::movePoints(const polyMesh& mesh)
564 {
565  DebugInfo<< "probes: movePoints" << endl;
566 
567  if (fixedLocations_ && &mesh == &mesh_)
568  {
569  findElements(mesh_);
570  }
571 }
572 
573 
574 // ************************************************************************* //
Foam::surfaceFields.
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:584
GeometricField< symmTensor, fvPatchField, volMesh > volSymmTensorField
Definition: volFieldsFwd.H:88
const Type & value() const noexcept
Return const reference to value.
dictionary dict
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:118
virtual bool write()
Sample and write.
Definition: probes.C:459
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:120
pointField oldPoints_
Original probes location (only used for patchProbes)
Definition: probes.H:286
virtual void updateMesh(const mapPolyMesh &)
Update for changes of mesh.
Definition: probes.C:465
void append(const T &val)
Append an element at the end of the list.
Definition: List.H:491
virtual bool execute()
Sample and store result if the sampleOnExecute is enabled.
Definition: probes.C:448
GeometricField< tensor, fvPatchField, volMesh > volTensorField
Definition: volFieldsFwd.H:89
virtual const wordRes & fieldNames() const noexcept
Return names of fields to probe.
Definition: probes.H:402
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:49
bool empty() const noexcept
True if the UList is empty (ie, size() is zero)
Definition: UListI.H:420
#define doLocalCode(InputType, Target)
virtual dimensionedScalar startTime() const
Return start time.
Definition: Time.C:841
GeometricField< vector, fvsPatchField, surfaceMesh > surfaceVectorField
GeometricField< sphericalTensor, fvsPatchField, surfaceMesh > surfaceSphericalTensorField
engineTime & runTime
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
static unsigned int defaultPrecision() noexcept
Return the default precision.
Definition: IOstream.H:416
labelList processor_
Processor holding the cell or face (-1 if point not found.
Definition: probes.H:271
const cellList & cells() const
static int myProcNo(const label communicator=worldComm)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:688
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:361
GeometricField< vector, fvPatchField, volMesh > volVectorField
Definition: volFieldsFwd.H:85
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:69
GeometricField< sphericalTensor, fvPatchField, volMesh > volSphericalTensorField
Definition: volFieldsFwd.H:87
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
Definition: mapPolyMesh.H:157
Macros for easy insertion into run-time selection tables.
const word & name() const noexcept
Return the name of this functionObject.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:413
label prepare(unsigned request)
Classify field types, close/open file streams.
Definition: probes.C:272
T & operator[](const label i)
Return element of UList.
Definition: UListI.H:292
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:84
bool readEntry(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX, IOobjectOption::readOption readOpt=IOobjectOption::MUST_READ) const
Find entry and assign to T val. FatalIOError if it is found and the number of tokens is incorrect...
dynamicFvMesh & mesh
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:567
GeometricField< symmTensor, fvsPatchField, surfaceMesh > surfaceSymmTensorField
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:52
A class for handling words, derived from Foam::string.
Definition: word.H:63
GeometricField< tensor, fvsPatchField, surfaceMesh > surfaceTensorField
#define forAllIters(container, iter)
Iterate across all elements in the container object.
Definition: stdFoam.H:328
Vector< scalar > vector
Definition: vector.H:57
HashSet< word, Hash< word > > wordHashSet
A HashSet of words, uses string hasher.
Definition: HashSet.H:73
const vectorField & cellCentres() const
#define DebugInfo
Report an information message using Foam::Info.
const direction noexcept
Definition: Scalar.H:258
Istream and Ostream manipulators taking arguments.
static word timeName(const scalar t, const int precision=precision_)
Return time name of given scalar time formatted with the given precision.
Definition: Time.C:760
bool includeOutOfBounds_
Include probes that were not found (default: true)
Definition: probes.H:206
const labelList & reverseCellMap() const
Reverse cell map.
Definition: mapPolyMesh.H:653
int debug
Static debugging option.
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
Find an entry if present, and assign to T val. FatalIOError if it is found and the number of tokens i...
OBJstream os(runTime.globalPath()/outputName)
defineTypeNameAndDebug(combustionModel, 0)
HashPtrTable< OFstream > probeFilePtrs_
Current open files (non-empty on master only)
Definition: probes.H:276
addToRunTimeSelectionTable(decompositionMethod, kahipDecomp, dictionary)
virtual void movePoints(const polyMesh &)
Update for changes of mesh.
Definition: probes.C:556
const vectorField & faceCentres() const
List< word > wordList
A List of words.
Definition: fileName.H:58
vector point
Point is a vector.
Definition: point.H:37
bool verbose(const bool on) noexcept
Enable/disable verbose output.
Definition: probes.C:387
#define WarningInFunction
Report a warning using Foam::Warning.
virtual void findElements(const fvMesh &mesh)
Find cells and faces containing probes.
Definition: probes.C:156
const polyMesh & mesh() const
Return polyMesh.
Definition: mapPolyMesh.H:437
static bool master(const label communicator=worldComm)
Am I the master rank.
Definition: UPstream.H:672
static word outputPrefix
Directory prefix.
virtual bool read(const dictionary &)
Read the probes.
Definition: probes.C:395
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
Reduce inplace (cf. MPI Allreduce) using specified communication schedule.
label findCell(const point &p, const cellDecomposition=CELL_TETS) const
Find cell enclosing this location and return index.
Definition: polyMesh.C:1505
label size() const noexcept
The number of elements in the UList.
Definition: UListI.H:413
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
T getOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a T, or return the given default value. FatalIOError if it is found and the number of...
void readFields(const typename GeoFieldType::Mesh &mesh, const IOobjectList &objects, const wordHashSet &selectedFields, LIFOStack< regIOobject *> &storedObjects)
Read the selected GeometricFields of the templated type.
List< label > labelList
A List of labels.
Definition: List.H:62
GeometricField< scalar, fvsPatchField, surfaceMesh > surfaceScalarField
static autoPtr< T > New(Args &&... args)
Construct autoPtr with forwarding arguments.
Definition: autoPtr.H:178
labelList patchIDList_
Patch IDs on which the new probes are located (for patchProbes)
Definition: probes.H:281
const fvMesh & mesh_
Reference to the fvMesh.
static const word & regionName(const word &region)
The mesh region name or word::null if polyMesh::defaultRegion.
Definition: polyMesh.C:816
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
const labelList & reverseFaceMap() const
Reverse face map.
Definition: mapPolyMesh.H:617
Namespace for OpenFOAM.
fileName globalPath() const
Return global path for the case.
Definition: TimePathsI.H:73