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-------------------------------------------------------------------------------
11License
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
40namespace Foam
41{
43
45 (
47 probes,
49 );
50}
51
52
53// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
54
55void 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 {
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 (
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
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
279Foam::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
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
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 {
387 read(dict);
388 }
389}
390
391
392// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
393
394bool 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
435bool 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
465
467{
468 return performAction(ACTION_ALL);
469}
470
471
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 }
556
557 faceList_.transfer(elems);
558 }
559 }
560}
561
562
564{
565 DebugInfo<< "probes: movePoints" << endl;
566
567 if (fixedLocations_ && &mesh == &mesh_)
568 {
569 findElements(mesh_);
570 }
571}
572
573
574// ************************************************************************* //
Istream and Ostream manipulators taking arguments.
Macros for easy insertion into run-time selection tables.
#define addToRunTimeSelectionTable(baseType, thisType, argNames)
Add to construction table with typeName as the key.
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:72
void append(const T &val)
Copy append an element to the end of this list.
Definition: DynamicListI.H:503
bool insert(const Key &key)
Insert a new entry, not overwriting existing entries.
Definition: HashSet.H:191
A HashTable similar to std::unordered_map.
Definition: HashTable.H:123
List< Key > sortedToc() const
The table of contents (the keys) in sorted order.
Definition: HashTable.C:137
bool empty() const noexcept
True if the hash table is empty.
Definition: HashTableI.H:59
bool erase(const iterator &iter)
Erase an entry specified by given iterator.
Definition: HashTable.C:440
List of IOobjects with searching and retrieving facilities.
Definition: IOobjectList.H:59
HashTable< wordHashSet > classes() const
A summary hash of classes used and their associated object names.
Definition: IOobjectList.C:320
static unsigned int defaultPrecision() noexcept
Return the default precision.
Definition: IOstream.H:342
virtual const fileName & name() const
Read/write access to the name of the stream.
Definition: OSstream.H:107
virtual bool read()
Re-read model coefficients if they have changed.
fileName globalPath() const
Return global path for the case.
Definition: TimePathsI.H:80
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:80
virtual dimensionedScalar startTime() const
Return start time.
Definition: Time.C:861
static autoPtr< Time > New()
Construct (dummy) Time - no functionObjects or libraries.
Definition: Time.C:717
static word timeName(const scalar t, const int precision=precision_)
Definition: Time.C:780
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
T & operator[](const label i)
Return element of UList.
Definition: UListI.H:299
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
T getOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
bool readEntry(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX, bool mandatory=true) const
const Type & value() const
Return const reference to value.
int verbose() const noexcept
Output verbosity level.
Definition: ensightMesh.C:125
Abstract base-class for Time/database function objects.
const word & name() const noexcept
Return the name of this functionObject.
static word outputPrefix
Directory prefix.
const fvMesh & mesh_
Reference to the fvMesh.
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:91
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:290
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
Definition: mapPolyMesh.H:162
const polyMesh & mesh() const
Return polyMesh.
Definition: mapPolyMesh.H:363
const labelList & reverseCellMap() const
Reverse cell map.
Definition: mapPolyMesh.H:532
const labelList & reverseFaceMap() const
Reverse face map.
Definition: mapPolyMesh.H:501
void movePoints()
Update for new mesh geometry.
void updateMesh()
Update for new mesh topology.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:81
static const word & regionName(const word &region)
The mesh region name or word::null if polyMesh::defaultRegion.
Definition: polyMesh.C:829
label findCell(const point &p, const cellDecomposition=CELL_TETS) const
Find cell enclosing this location and return index.
Definition: polyMesh.C:1522
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:456
const vectorField & faceCentres() const
const vectorField & cellCentres() const
const cellList & cells() const
Set of locations to sample.
Definition: probes.H:166
bool includeOutOfBounds_
Include probes that were not found (default: true)
Definition: probes.H:199
HashPtrTable< OFstream > probeFilePtrs_
Current open files (non-empty on master only)
Definition: probes.H:245
virtual const wordRes & fieldNames() const noexcept
Return names of fields to probe.
Definition: probes.H:335
labelList processor_
Processor holding the cell or face (-1 if point not found.
Definition: probes.H:242
label prepare(unsigned request)
Classify field types, close/open file streams.
Definition: probes.C:279
pointField oldPoints_
Original probes location (only used for patchProbes)
Definition: probes.H:251
virtual void findElements(const fvMesh &mesh)
Find cells and faces containing probes.
Definition: probes.C:163
labelList patchIDList_
Patch IDs on which the new probes are located (for patchProbes)
Definition: probes.H:248
virtual bool execute()
Sample and store result if the sampleOnExecute is enabled.
Definition: probes.C:455
virtual bool write()
Sample and write.
Definition: probes.C:466
virtual bool read(const dictionary &)
Read the probes.
Definition: probes.C:402
int myProcNo() const noexcept
Return processor number.
splitCell * master() const
Definition: splitCell.H:113
A class for handling words, derived from Foam::string.
Definition: word.H:68
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition: className.H:121
dynamicFvMesh & mesh
engineTime & runTime
OBJstream os(runTime.globalPath()/outputName)
#define DebugInfo
Report an information message using Foam::Info.
#define WarningInFunction
Report a warning using Foam::Warning.
Namespace for OpenFOAM.
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: MSwindows.C:515
void readFields(const typename GeoFieldType::Mesh &mesh, const IOobjectList &objects, const wordHashSet &selectedFields, LIFOStack< regIOobject * > &storedObjects)
Read the selected GeometricFields of the templated type.
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
dictionary dict
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333
#define forAllIters(container, iter)
Iterate across all elements in the container object.
Definition: stdFoam.H:260
Foam::surfaceFields.
#define doLocalCode(GeoField)