Loading...
Searching...
No Matches
steadyParticleTracks.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-2016 OpenFOAM Foundation
9 Copyright (C) 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
27Application
28 steadyParticleTracks
29
30Group
31 grpPostProcessingUtilities
32
33Description
34 Generate a legacy VTK file of particle tracks for cases that were
35 computed using a steady-state cloud
36
37 Note:
38 - Case must be re-constructed (if running in parallel) before use
39
40\*---------------------------------------------------------------------------*/
41
42#include "argList.H"
43#include "Cloud.H"
44#include "IOdictionary.H"
45#include "fvMesh.H"
46#include "Time.H"
47#include "timeSelector.H"
48#include "OFstream.H"
49#include "labelPairHashes.H"
50#include "IOField.H"
51#include "IOobjectList.H"
52#include "SortableList.H"
55
56// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
57
58namespace Foam
59{
60
61// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
62
63// Extract list of IOobjects, modifying the input IOobjectList in the
64// process
65IOobjectList preFilterFields
66(
67 IOobjectList& cloudObjects,
68 const wordRes& acceptFields,
70)
71{
72 IOobjectList filteredObjects(cloudObjects.capacity());
74
75 // Selection here is slighly different than usual
76 // - an empty accept filter means "ignore everything"
77
78 if (!acceptFields.empty())
79 {
81
82 const wordList allNames(cloudObjects.sortedNames());
83
84 // Detect missing fields
86 {
87 if
88 (
89 acceptFields[i].isLiteral()
90 && !allNames.found(acceptFields[i])
91 )
92 {
93 missed.append(i);
94 }
95 }
96
97 for (const word& fldName : allNames)
98 {
99 const auto iter = cloudObjects.cfind(fldName);
100 if (!pred(fldName) || !iter.good())
101 {
102 continue; // reject
103 }
104
105 const IOobject& io = *(iter.val());
106
107 if (Foam::fieldTypes::is_basic(io.headerClassName()))
108 {
109 // Transfer from cloudObjects -> filteredObjects
110 filteredObjects.add(cloudObjects.remove(fldName));
111 }
112 }
113 }
114
115 if (missed.size())
116 {
118 << nl
119 << "Cannot find field file matching "
121 }
122
123 return filteredObjects;
124}
125
126
127void readFieldsAndWriteVTK
128(
129 OFstream& os,
130 const List<labelList>& particleMap,
131 const IOobjectList& filteredObjects
132)
133{
134 processFields<label>(os, particleMap, filteredObjects);
135 processFields<scalar>(os, particleMap, filteredObjects);
136 processFields<vector>(os, particleMap, filteredObjects);
137 processFields<sphericalTensor>(os, particleMap, filteredObjects);
138 processFields<symmTensor>(os, particleMap, filteredObjects);
139 processFields<tensor>(os, particleMap, filteredObjects);
140}
141
142} // End namespace Foam
143
144
145using namespace Foam;
146
147// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
148
149int main(int argc, char *argv[])
150{
152 (
153 "Generate a legacy VTK file of particle tracks for cases that were"
154 " computed using a steady-state cloud"
155 );
156
159 #include "addRegionOption.H"
161 (
162 "dict",
163 "file",
164 "Alternative particleTrackDict dictionary"
165 );
167
168 #include "setRootCase.H"
169 #include "createTime.H"
171 #include "createNamedMesh.H"
172 #include "createControls.H"
173
174 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
175
176 const fileName vtkPath(runTime.rootPath()/runTime.globalCaseName()/"VTK");
177 mkDir(vtkPath);
178
179 forAll(timeDirs, timeI)
180 {
181 runTime.setTime(timeDirs[timeI], timeI);
182 Info<< "Time = " << runTime.timeName() << endl;
183
184 const fileName vtkTimePath(vtkPath/runTime.timeName());
185 mkDir(vtkTimePath);
186
187 pointField particlePosition;
188 labelList particleToTrack;
189 label nTracks = 0;
190
191 // Transfer particles to (more convenient) list
192 {
193 Info<< " Reading particle positions" << endl;
194
196 Info<< "\n Read " << returnReduce(myCloud.size(), sumOp<label>())
197 << " particles" << endl;
198
199 const label nParticles = myCloud.size();
200
201 particlePosition.resize(nParticles);
202 particleToTrack.resize(nParticles);
203
204 LabelPairMap<label> trackTable;
205
206 label np = 0;
207 for (const passiveParticle& p : myCloud)
208 {
209 const label origId = p.origId();
210 const label origProc = p.origProc();
211 particlePosition[np] = p.position();
212
213 const labelPair key(origProc, origId);
214
215 const auto iter = trackTable.cfind(key);
216
217 if (iter.good())
218 {
219 particleToTrack[np] = *iter;
220 }
221 else
222 {
223 particleToTrack[np] = trackTable.size();
224 trackTable.insert(key, trackTable.size());
225 }
226
227 ++np;
228 }
229
230 nTracks = trackTable.size();
231 }
232
233 if (nTracks == 0)
234 {
235 Info<< "\n No track data" << endl;
236 }
237 else
238 {
239 Info<< "\n Generating " << nTracks << " tracks" << endl;
240
241 // Determine length of each track
242 labelList trackLengths(nTracks, Zero);
243 for (const label tracki : particleToTrack)
244 {
245 ++trackLengths[tracki];
246 }
247
248 // Particle "age" property used to sort the tracks
249 List<SortableList<scalar>> agePerTrack(nTracks);
250 List<List<label>> particleMap(nTracks);
251
252 forAll(trackLengths, i)
253 {
254 const label length = trackLengths[i];
255 agePerTrack[i].setSize(length);
256 particleMap[i].setSize(length);
257 }
258
259 // Store the particle age per track
260 IOobjectList cloudObjects
261 (
262 mesh,
263 runTime.timeName(),
265 );
266
267 // TODO: gather age across all procs
268 {
269 tmp<IOField<scalar>> tage =
270 readParticleField<scalar>("age", cloudObjects);
271
272 const auto& age = tage();
273
274 labelList trackSamples(nTracks, Zero);
275
276 forAll(particleToTrack, i)
277 {
278 const label tracki = particleToTrack[i];
279 const label samplei = trackSamples[tracki];
280 agePerTrack[tracki][samplei] = age[i];
281 particleMap[tracki][samplei] = i;
282 ++trackSamples[tracki];
283 }
284 }
285
286
287 const IOobjectList filteredObjects
288 (
289 preFilterFields(cloudObjects, acceptFields, excludeFields)
290 );
291
292
293 if (Pstream::master())
294 {
295 OFstream os(vtkTimePath/"particleTracks.vtk");
296
297 Info<< "\n Writing particle tracks to " << os.name() << endl;
298
299 label nPoints = sum(trackLengths);
300
301 os << "# vtk DataFile Version 2.0" << nl
302 << "particleTracks" << nl
303 << "ASCII" << nl
304 << "DATASET POLYDATA" << nl
305 << "POINTS " << nPoints << " float" << nl;
306
307 Info<< "\n Writing points" << endl;
308
309 {
310 forAll(agePerTrack, i)
311 {
312 agePerTrack[i].sort();
313
314 const labelList& ids = agePerTrack[i].indices();
315 labelList& particleIds = particleMap[i];
316
317 {
318 // Update addressing
319 List<label> sortedIds(ids);
320 forAll(sortedIds, j)
321 {
322 sortedIds[j] = particleIds[ids[j]];
323 }
324 particleIds = sortedIds;
325 }
326
327 forAll(ids, j)
328 {
329 const label localId = particleIds[j];
330 const point& pos = particlePosition[localId];
331 os << pos.x() << ' ' << pos.y() << ' ' << pos.z()
332 << nl;
333 }
334 }
335 }
336
337
338 // Write track (line) connectivity to file
339
340 Info<< "\n Writing track lines" << endl;
341 os << "\nLINES " << nTracks << ' ' << nPoints + nTracks << nl;
342
343 // Write ids of track points to file
344 {
345 label globalPtI = 0;
346 forAll(particleMap, i)
347 {
348 os << particleMap[i].size() << nl;
349
350 forAll(particleMap[i], j)
351 {
352 os << ' ' << globalPtI++;
353
354 if (((j + 1) % 10 == 0) && (j != 0))
355 {
356 os << nl;
357 }
358 }
359
360 os << nl;
361 }
362 }
363
364
365 const label nFields = filteredObjects.size();
366
367 os << "POINT_DATA " << nPoints << nl
368 << "FIELD attributes " << nFields << nl;
369
370 Info<< "\n Processing fields" << nl << endl;
371
372 readFieldsAndWriteVTK(os, particleMap, filteredObjects);
373 }
374 }
375 Info<< endl;
376 }
377
378 Info<< "End\n" << endl;
379
380 return 0;
381}
382
383
384// ************************************************************************* //
Required Classes.
wordRes excludeFields
wordRes acceptFields
const word cloudName(propsDict.get< word >("cloud"))
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition DynamicList.H:68
const_iterator cfind(const Key &key) const
Find and return an const_iterator set at the hashed entry.
Definition HashTableI.H:113
bool insert(const Key &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition HashTableI.H:152
label capacity() const noexcept
The size of the underlying table (the number of buckets).
Definition HashTable.H:363
label size() const noexcept
The number of elements in table.
Definition HashTable.H:358
List of IOobjects with searching and retrieving facilities. Implemented as a HashTable,...
wordList sortedNames() const
The sorted names of the IOobjects.
autoPtr< IOobject > remove(const IOobject &io)
Remove object from the list by its IOobject::name().
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition IOobject.H:191
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition List.H:72
void resize(const label len)
Adjust allocated size of list.
Definition ListI.H:153
Output to file stream as an OSstream, normally using std::ofstream for the actual output.
Definition OFstream.H:75
A List with indirect addressing. Like IndirectList but does not store addressing.
bool found(const T &val, label pos=0) const
Same as contains().
Definition UList.H:983
bool empty() const noexcept
True if List is empty (ie, size() is zero).
Definition UList.H:701
void size(const label n)
Older name for setAddressableSize.
Definition UList.H:118
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition UPstream.H:1714
static void addVerboseOption(const string &usage="", bool advanced=false)
Enable a 'verbose' bool option, with usage information.
Definition argList.C:535
static void noParallel()
Remove the parallel options.
Definition argList.C:599
static void addOption(const word &optName, const string &param="", const string &usage="", bool advanced=false)
Add an option to validOptions with usage information.
Definition argList.C:400
static void addNote(const string &note)
Add extra notes for the usage information.
Definition argList.C:477
static const word prefix
The prefix to local: lagrangian.
Definition cloud.H:79
A class for handling file names.
Definition fileName.H:75
A Cloud of passive particles.
Copy of base particle.
static void addOptions(const bool constant=true, const bool withZero=false)
Add timeSelector options to argList::validOptions.
static instantList select0(Time &runTime, const argList &args)
Return the set of times selected based on the argList options and also set the runTime to the first i...
A class for managing temporary objects.
Definition tmp.H:75
A List of wordRe with additional matching capabilities.
Definition wordRes.H:56
A class for handling words, derived from Foam::string.
Definition word.H:66
volScalarField & p
dynamicFvMesh & mesh
engineTime & runTime
Required Classes.
OBJstream os(runTime.globalPath()/outputName)
const auto & io
label nPoints
A HashTable to objects of type <T> with a labelPair key. The hashing is based on labelPair (FixedList...
return returnReduce(nRefine-oldNRefine, sumOp< label >())
#define WarningInFunction
Report a warning using Foam::Warning.
bool is_basic(const word &clsName)
Test if the class name appears to be a basic field.
Definition fieldTypes.C:73
constexpr auto key(const Type &t) noexcept
Helper function to return the enum value.
Namespace for OpenFOAM.
dimensionedScalar pos(const dimensionedScalar &ds)
Pair< label > labelPair
A pair of labels.
Definition Pair.H:54
List< word > wordList
List of word.
Definition fileName.H:60
List< label > labelList
A List of labels.
Definition List.H:62
HashTable< T, labelPair, Foam::Hash< labelPair > > LabelPairMap
messageStream Info
Information stream (stdout output on master, null elsewhere).
List< instant > instantList
List of instants.
Definition instantList.H:41
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
void processFields(OFstream &os, const List< labelList > &addr, const IOobjectList &cloudObjects)
vector point
Point is a vector.
Definition point.H:37
tmp< IOField< Type > > readParticleField(const word &fieldName, const IOobjectList &cloudObjects)
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &f1, const label comm)
static constexpr const zero Zero
Global zero (0).
Definition zero.H:127
vectorField pointField
pointField is a vectorField.
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
Foam::argList args(argc, argv)
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299
Functor wrapper of allow/deny lists of wordRe for filtering.
Definition wordRes.H:275
mkDir(pdfPath)