vtkCloud.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) 2018-2020 OpenCFD Ltd.
9-------------------------------------------------------------------------------
10License
11 This file is part of OpenFOAM.
12
13 OpenFOAM is free software: you can redistribute it and/or modify it
14 under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25
26\*---------------------------------------------------------------------------*/
27
28#include "vtkCloud.H"
29#include "Cloud.H"
30#include "dictionary.H"
31#include "fvMesh.H"
34#include "pointList.H"
35#include "stringOps.H"
36#include <fstream>
37
38// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
39
40namespace Foam
41{
42namespace functionObjects
43{
46}
47}
48
49
50// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
51
52void Foam::functionObjects::vtkCloud::writeVerts
53(
54 autoPtr<vtk::formatter>& format,
55 const label nTotParcels
56) const
57{
58 // No collectives - can skip on slave processors
59 if (!format) return;
60
61 // Same payload for connectivity and offsets
62 const uint64_t payLoad = vtk::sizeofData<label>(nTotParcels);
63
65
66 //
67 // 'connectivity'
68 // = linear mapping onto points
69 //
70 {
71 format().beginDataArray<label>(vtk::dataArrayAttr::CONNECTIVITY);
72 format().writeSize(payLoad);
73
74 vtk::writeIdentity(format(), nTotParcels);
75
76 format().flush();
77 format().endDataArray();
78 }
79
80 //
81 // 'offsets' (connectivity offsets)
82 // = linear mapping onto points (with 1 offset)
83 //
84 {
85 format().beginDataArray<label>(vtk::dataArrayAttr::OFFSETS);
86 format().writeSize(payLoad);
87
88 vtk::writeIdentity(format(), nTotParcels, 1);
89
90 format().flush();
91 format().endDataArray();
92 }
93
95}
96
97
98bool Foam::functionObjects::vtkCloud::writeCloud
99(
100 const fileName& file,
101 const word& cloudName
102)
103{
104 const auto* objPtr = mesh_.findObject<cloud>(cloudName);
105 if (!objPtr)
106 {
107 return false;
108 }
109
110 objectRegistry obrTmp
111 (
112 IOobject
113 (
114 "vtk::vtkCloud::" + cloudName,
115 mesh_.time().constant(),
116 mesh_,
119 false
120 )
121 );
122
123 objPtr->writeObjects(obrTmp);
124
125 const auto* pointsPtr = cloud::findIOPosition(obrTmp);
126
127 if (!pointsPtr)
128 {
129 // This should be impossible
130 return false;
131 }
132
133 applyFilter_ = calculateFilter(obrTmp, log);
134 reduce(applyFilter_, orOp<bool>());
135
136
137 // Number of parcels (locally)
138 label nParcels = (applyFilter_ ? parcelAddr_.count() : pointsPtr->size());
139
140 // Total number of parcels on all processes
141 const label nTotParcels = returnReduce(nParcels, sumOp<label>());
142
143 if (applyFilter_)
144 {
145 // Report filtered/unfiltered count
146 Log << "After filtering using " << nTotParcels << '/'
147 << (returnReduce(pointsPtr->size(), sumOp<label>()))
148 << " parcels" << nl;
149 }
150
151 if (pruneEmpty_ && !nTotParcels)
152 {
153 return false;
154 }
155
156 std::ofstream os;
157 autoPtr<vtk::formatter> format;
158
159 if (!file.hasExt("vtp"))
160 {
162 << type() << " File missing .vtp extension!" << nl << endl
163 << exit(FatalError);
164 }
165
166 if (Pstream::master())
167 {
168 mkDir(file.path());
169 os.open(file);
170
171 format = writeOpts_.newFormatter(os);
172
173 // beginFile()
174
175 // XML (inline)
176 format()
177 .xmlHeader()
178 .xmlComment
179 (
180 "case='" + time_.globalCaseName()
181 + "' cloud='" + cloudName
182 + "' time='" + time_.timeName()
183 + "' index='" + Foam::name(time_.timeIndex())
184 + "'"
185 )
186 .beginVTKFile<vtk::fileTag::POLY_DATA>();
187
188
189 // FieldData with TimeValue
190 format()
191 .beginFieldData()
192 .writeTimeValue(time_.value())
193 .endFieldData();
194
195
196 // writeGeometry()
197
198 // beginPiece()
199 if (useVerts_)
200 {
201 format()
202 .tag
203 (
207 );
208 }
209 else
210 {
211 format()
212 .tag
213 (
216 );
217 }
218
219 // writePoints()
220 {
221 const uint64_t payLoad = vtk::sizeofData<float,3>(nTotParcels);
222
224 .beginDataArray<float,3>(vtk::dataArrayAttr::POINTS);
225
226 format().writeSize(payLoad);
227 }
228 }
229
230
231 if (applyFilter_)
232 {
233 vtk::writeListParallel(format.ref(), *pointsPtr, parcelAddr_);
234 }
235 else
236 {
237 vtk::writeListParallel(format.ref(), *pointsPtr);
238 }
239
240
241 if (Pstream::master())
242 {
243 format().flush();
244 format().endDataArray();
246
247 if (useVerts_)
248 {
249 writeVerts(format, nTotParcels);
250 }
251 }
252
253
254 // Prevent any possible conversion of positions as a field
255 obrTmp.filterKeys
256 (
257 [](const word& k)
258 {
259 return k.starts_with("position") || k.starts_with("coordinate");
260 },
261 true // prune
262 );
263
264 // Restrict to specified fields
265 if (selectFields_.size())
266 {
267 obrTmp.filterKeys(selectFields_);
268 }
269
270
271 // Write fields
272
273 if (Pstream::master())
274 {
275 if (useVerts_)
276 {
277 format().beginCellData();
278 }
279 else
280 {
281 format().beginPointData();
282 }
283 }
284
285 DynamicList<word> written(obrTmp.size());
286
287 written.append(writeFields<label>(format, obrTmp, nTotParcels));
288 written.append(writeFields<scalar>(format, obrTmp, nTotParcels));
289 written.append(writeFields<vector>(format, obrTmp, nTotParcels));
290
291 if (Pstream::master())
292 {
293 if (useVerts_)
294 {
295 format().endCellData();
296 }
297 else
298 {
299 format().endPointData();
300 }
301
302 format().endPiece();
304 .endVTKFile();
305 }
306
307
308 // Record information into the state (all processors)
309 //
310 // foName
311 // {
312 // cloudName
313 // {
314 // file "<case>/postProcessing/name/cloud1_0001.vtp";
315 // fields (U T rho);
316 // }
317 // }
318
319 // Case-local file name with "<case>" to make relocatable
320 dictionary propsDict;
321 propsDict.add
322 (
323 "file",
324 time_.relativePath(file, true)
325 );
326 propsDict.add("fields", written);
327
328 setObjectProperty(name(), cloudName, propsDict);
329
330 return true;
331}
332
333
334// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
335
337(
338 const word& name,
339 const Time& runTime,
340 const dictionary& dict
341)
342:
344 writeOpts_(vtk::formatType::INLINE_BASE64),
345 printf_(),
346 useVerts_(false),
347 pruneEmpty_(false),
348 applyFilter_(false),
349 selectClouds_(),
350 selectFields_(),
351 directory_(),
352 series_()
353{
354 // May still want this? (OCT-2018)
355 // if (postProcess)
356 // {
357 // // Disable for post-process mode.
358 // // Emit as FatalError for the try/catch in the caller.
359 // FatalError
360 // << type() << " disabled in post-process mode"
361 // << exit(FatalError);
362 // }
363
364 read(dict);
365}
366
367
368// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
369
371{
373
374 // We probably cannot trust old information after a reread
375 series_.clear();
376
377 //
378 // Default format is xml base64. Legacy is not desired.
379 //
381
382 writeOpts_.ascii
383 (
386 );
387
388 writeOpts_.append(false); // No append supported
389 writeOpts_.legacy(false); // No legacy supported
390
391 writeOpts_.precision
392 (
393 dict.getOrDefault("precision", IOstream::defaultPrecision())
394 );
395
396 // Info<< type() << " " << name() << " output-format: "
397 // << writeOpts_.description() << nl;
398
399 const int padWidth = dict.getOrDefault<int>("width", 8);
400
401 // Appropriate printf format - Enforce min/max sanity limits
402 if (padWidth < 1 || padWidth > 31)
403 {
404 printf_.clear();
405 }
406 else
407 {
408 printf_ = "%0" + std::to_string(padWidth) + "d";
409 }
410
411 // useTimeName_ = dict.getOrDefault("useTimeName", false);
412
413 useVerts_ = dict.getOrDefault("cellData", false);
414 pruneEmpty_ = dict.getOrDefault("prune", false);
415
416 selectClouds_.clear();
417 dict.readIfPresent("clouds", selectClouds_);
418
419 if (selectClouds_.empty())
420 {
421 selectClouds_.resize(1);
422 selectClouds_.first() =
423 dict.getOrDefault<word>("cloud", cloud::defaultName);
424 }
425
426 selectFields_.clear();
427 dict.readIfPresent("fields", selectFields_);
428 selectFields_.uniq();
429
430 // Actions to define selection
431 parcelSelect_ = dict.subOrEmptyDict("selection");
432
433 // Output directory
434
435 directory_.clear();
436 dict.readIfPresent("directory", directory_);
437
438 if (directory_.size())
439 {
440 // User-defined output directory
441 directory_.expand();
442 if (!directory_.isAbsolute())
443 {
444 directory_ = time_.globalPath()/directory_;
445 }
446 }
447 else
448 {
449 // Standard postProcessing/ naming
450 directory_ = time_.globalPath()/functionObject::outputPrefix/name();
451 }
452 directory_.clean(); // Remove unneeded ".."
453
454 return true;
455}
456
457
459{
460 return true;
461}
462
463
465{
466 const wordList cloudNames(mesh_.sortedNames<cloud>(selectClouds_));
467
468 if (cloudNames.empty())
469 {
470 return true; // skip - not available
471 }
472
473 const scalar timeValue = time_.value();
474
475 const word timeDesc = "_" +
476 (
477 printf_.empty()
478 ? Foam::name(time_.timeIndex())
479 : word::printf(printf_, time_.timeIndex())
480 );
481
482 Log << name() << " output Time: " << time_.timeName() << nl;
483
484 // Each cloud separately
485 for (const word& cloudName : cloudNames)
486 {
487 // Legacy is not to be supported
488
489 const fileName outputName
490 (
491 directory_/cloudName + timeDesc + ".vtp"
492 );
493
494 // writeCloud() includes mkDir (on master)
495
496 if (writeCloud(outputName, cloudName))
497 {
498 Log << " cloud : "
499 << time_.relativePath(outputName) << endl;
500
501 if (Pstream::master())
502 {
503 // Add to file-series and emit as JSON
505
506 vtk::seriesWriter& series = series_(seriesName);
507
508 // First time?
509 // Load from file, verify against filesystem,
510 // prune time >= currentTime
511 if (series.empty())
512 {
513 series.load(seriesName, true, timeValue);
514 }
515
516 series.append(timeValue, outputName);
517 series.write(seriesName);
518 }
519 }
520 }
521
522 return true;
523}
524
525
526// ************************************************************************* //
label k
#define Log
Definition: PDRblock.C:35
Macros for easy insertion into run-time selection tables.
#define addToRunTimeSelectionTable(baseType, thisType, argNames)
Add to construction table with typeName as the key.
@ ASCII
"ascii" (normal default)
static streamFormat formatEnum(const word &formatName, const streamFormat deflt=streamFormat::ASCII)
static unsigned int defaultPrecision() noexcept
Return the default precision.
Definition: IOstream.H:342
virtual bool read()
Re-read model coefficients if they have changed.
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:80
bool empty() const noexcept
True if the UList is empty (ie, size() is zero)
Definition: UListI.H:427
Base functionality common to reader and writer classes.
Definition: ccmBase.H:63
A cloud is a registry collection of lagrangian particles.
Definition: cloud.H:60
static const IOField< point > * findIOPosition(const objectRegistry &obr)
Locate the "position" IOField within object registry.
Definition: cloud.H:153
static word defaultName
The default cloud name: defaultCloud.
Definition: cloud.H:90
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
A class for handling file names.
Definition: fileName.H:76
Abstract base-class for Time/database function objects.
static word outputPrefix
Directory prefix.
Specialization of Foam::functionObject for an Foam::fvMesh, providing a reference to the Foam::fvMesh...
This functionObject writes cloud(s) in VTK PolyData format (.vtp extension) with the time information...
Definition: vtkCloud.H:231
virtual bool read(const dictionary &dict)
Read the vtkCloud specification.
Definition: vtkCloud.C:370
virtual bool execute()
Execute, currently does nothing.
Definition: vtkCloud.C:458
virtual bool write()
Write fields.
Definition: vtkCloud.C:464
splitCell * master() const
Definition: splitCell.H:113
Provides a means of accumulating and generating VTK file series.
bool append(const fileNameInstant &inst)
Append the specified file instant.
bool empty() const noexcept
True if there are no data sets.
label load(const fileName &seriesName, const bool checkFiles=false, const scalar restartTime=ROOTVGREAT)
Clear contents and reload by parsing the specified file.
static void write(const fileName &base, const UList< instant > &series, const char sep='_')
Write file series (JSON format) to disk, for specified instances.
A class for handling words, derived from Foam::string.
Definition: word.H:68
static word printf(const char *fmt, const PrimitiveType &val)
Use a printf-style formatter for a primitive.
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition: className.H:121
engineTime & runTime
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
word outputName("finiteArea-edges.obj")
OBJstream os(runTime.globalPath()/outputName)
@ NUMBER_OF_VERTS
"NumberOfVerts"
@ NUMBER_OF_POINTS
"NumberOfPoints"
void writeIdentity(vtk::formatter &fmt, const label len, label start=0)
Write an identity list of labels.
Definition: foamVtkOutput.C:96
@ CONNECTIVITY
"connectivity"
@ POLY_DATA
"PolyData"
@ INLINE_BASE64
XML inline base64, base64Formatter.
void writeListParallel(vtk::formatter &fmt, const UList< Type > &values)
Write a list of values.
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
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition: MSwindows.C:598
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
error FatalError
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
T returnReduce(const T &value, const BinaryOp &bop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Reduce (copy) and return value.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
word format(conversionProperties.get< word >("format"))
dictionary dict
IOdictionary propsDict(dictIO)
const word cloudName(propsDict.get< word >("cloud"))