steadyParticleTracks.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-2023 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 Application
25  steadyParticleTracks
26 
27 Description
28  Generates a VTK file of particle tracks for cases that were computed using
29  a steady-state cloud
30  NOTE: case must be re-constructed (if running in parallel) before use
31 
32 \*---------------------------------------------------------------------------*/
33 
34 #include "argList.H"
35 #include "Cloud.H"
36 #include "IOdictionary.H"
37 #include "fvMesh.H"
38 #include "Time.H"
39 #include "timeSelector.H"
40 #include "OFstream.H"
41 #include "passiveParticleCloud.H"
42 #include "systemDict.H"
43 
44 #include "SortableList.H"
45 #include "IOobjectList.H"
46 #include "PtrList.H"
47 #include "Field.H"
49 
50 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
51 
52 using namespace Foam;
53 
54 namespace Foam
55 {
56 
57 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
58 
59 label validateFields
60 (
61  const List<word>& userFields,
62  const IOobjectList& cloudObjs
63 )
64 {
65  List<bool> ok(userFields.size(), false);
66 
67  forAll(userFields, i)
68  {
69  ok[i] = ok[i] || fieldOk<label>(cloudObjs, userFields[i]);
70  ok[i] = ok[i] || fieldOk<scalar>(cloudObjs, userFields[i]);
71  ok[i] = ok[i] || fieldOk<vector>(cloudObjs, userFields[i]);
72  ok[i] = ok[i] || fieldOk<sphericalTensor>(cloudObjs, userFields[i]);
73  ok[i] = ok[i] || fieldOk<symmTensor>(cloudObjs, userFields[i]);
74  ok[i] = ok[i] || fieldOk<tensor>(cloudObjs, userFields[i]);
75  }
76 
77  label nOk = 0;
78  forAll(ok, i)
79  {
80  if (ok[i])
81  {
82  nOk++;
83  }
84  else
85  {
86  Info << "\n*** Warning: user specified field '" << userFields[i]
87  << "' unavailable" << endl;
88  }
89  }
90 
91  return nOk;
92 }
93 
94 
95 template<>
96 void writeVTK(OFstream& os, const label& value)
97 {
98  os << value;
99 }
100 
101 
102 template<>
103 void writeVTK(OFstream& os, const scalar& value)
104 {
105  os << value;
106 }
107 
108 }
109 
110 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
111 
112 int main(int argc, char *argv[])
113 {
116  #include "addRegionOption.H"
117  #include "addDictOption.H"
118 
119  #include "setRootCase.H"
120 
121  #include "createTime.H"
123  #include "createNamedMesh.H"
124  #include "createFields.H"
125 
126  // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
127 
128  const fileName vtkPath(runTime.rootPath()/runTime.globalCaseName()/"VTK");
129  mkDir(vtkPath);
130 
131  typedef HashTable<label, labelPair, labelPair::Hash<>> trackTableType;
132 
133  forAll(timeDirs, timeI)
134  {
135  runTime.setTime(timeDirs[timeI], timeI);
136  Info<< "Time = " << runTime.userTimeName() << endl;
137 
138  const fileName vtkTimePath(vtkPath/runTime.name());
139  mkDir(vtkTimePath);
140 
141  Info<< " Reading particle positions" << endl;
142 
143  PtrList<passiveParticle> particles(0);
144 
145  // Transfer particles to (more convenient) list
146  {
147  passiveParticleCloud ppc(mesh, cloudName);
148  Info<< "\n Read " << returnReduce(ppc.size(), sumOp<label>())
149  << " particles" << endl;
150 
151  particles.setSize(ppc.size());
152 
153  label i = 0;
154  forAllIter(passiveParticleCloud, ppc, iter)
155  {
156  particles.set(i++, ppc.remove(&iter()));
157  }
158 
159  // myCloud should now be empty
160  }
161 
162  List<label> particleToTrack(particles.size());
163  label nTracks = 0;
164 
165  {
166  trackTableType trackTable;
167  forAll(particles, i)
168  {
169  const label origProc = particles[i].origProc();
170  const label origId = particles[i].origId();
171 
172  const trackTableType::const_iterator& iter =
173  trackTable.find(labelPair(origProc, origId));
174 
175  if (iter == trackTable.end())
176  {
177  particleToTrack[i] = nTracks;
178  trackTable.insert(labelPair(origProc, origId), nTracks);
179  nTracks++;
180  }
181  else
182  {
183  particleToTrack[i] = iter();
184  }
185  }
186  }
187 
188 
189  if (nTracks == 0)
190  {
191  Info<< "\n No track data" << endl;
192  }
193  else
194  {
195  Info<< "\n Generating " << nTracks << " tracks" << endl;
196 
197  // Determine length of each track
198  labelList trackLengths(nTracks, 0);
199  forAll(particleToTrack, i)
200  {
201  const label trackI = particleToTrack[i];
202  trackLengths[trackI]++;
203  }
204 
205  // Particle "age" property used to sort the tracks
206  List<SortableList<scalar>> agePerTrack(nTracks);
207  List<List<label>> particleMap(nTracks);
208 
209  forAll(trackLengths, i)
210  {
211  const label length = trackLengths[i];
212  agePerTrack[i].setSize(length);
213  particleMap[i].setSize(length);
214  }
215 
216  // Store the particle age per track
217  IOobjectList cloudObjs
218  (
219  mesh,
220  runTime.name(),
222  );
223 
224  // TODO: gather age across all procs
225  {
226  tmp<scalarField> tage =
227  readParticleField<scalar>("age", cloudObjs);
228 
229  const scalarField& age = tage();
230 
231  List<label> trackSamples(nTracks, 0);
232 
233  forAll(particleToTrack, i)
234  {
235  const label trackI = particleToTrack[i];
236  const label sampleI = trackSamples[trackI];
237  agePerTrack[trackI][sampleI] = age[i];
238  particleMap[trackI][sampleI] = i;
239  trackSamples[trackI]++;
240  }
241  tage.clear();
242  }
243 
244 
245  if (Pstream::master())
246  {
247  OFstream os(vtkTimePath/"particleTracks.vtk");
248 
249  Info<< "\n Writing particle tracks to " << os.name() << endl;
250 
251  label nPoints = sum(trackLengths);
252 
253  os << "# vtk DataFile Version 2.0" << nl
254  << "particleTracks" << nl
255  << "ASCII" << nl
256  << "DATASET POLYDATA" << nl
257  << "POINTS " << nPoints << " float" << nl;
258 
259  Info<< "\n Writing points" << endl;
260 
261  {
262  forAll(agePerTrack, i)
263  {
264  agePerTrack[i].sort();
265 
266  const labelList& ids = agePerTrack[i].indices();
267  labelList& particleIds = particleMap[i];
268 
269  {
270  // Update addressing
271  List<label> sortedIds(ids);
272  forAll(sortedIds, j)
273  {
274  sortedIds[j] = particleIds[ids[j]];
275  }
276  particleIds = sortedIds;
277  }
278 
279  forAll(ids, j)
280  {
281  const label localId = particleIds[j];
282  const vector pos =
283  particles[localId].position(mesh);
284  os << pos.x() << ' ' << pos.y() << ' ' << pos.z()
285  << nl;
286  }
287  }
288  }
289 
290 
291  // Write track (line) connectivity to file
292 
293  Info<< "\n Writing track lines" << endl;
294  os << "\nLINES " << nTracks << ' ' << nPoints + nTracks << nl;
295 
296  // Write ids of track points to file
297  {
298  label globalPtI = 0;
299  forAll(particleMap, i)
300  {
301  os << particleMap[i].size() << nl;
302 
303  forAll(particleMap[i], j)
304  {
305  os << ' ' << globalPtI++;
306 
307  if (((j + 1) % 10 == 0) && (j != 0))
308  {
309  os << nl;
310  }
311  }
312 
313  os << nl;
314  }
315  }
316 
317 
318  const label nFields = validateFields(userFields, cloudObjs);
319 
320  os << "POINT_DATA " << nPoints << nl
321  << "FIELD attributes " << nFields << nl;
322 
323  Info<< "\n Processing fields" << nl << endl;
324 
325  processFields<label>(os, particleMap, userFields, cloudObjs);
326  processFields<scalar>(os, particleMap, userFields, cloudObjs);
327  processFields<vector>(os, particleMap, userFields, cloudObjs);
328  processFields<sphericalTensor>
329  (os, particleMap, userFields, cloudObjs);
330  processFields<symmTensor>
331  (os, particleMap, userFields, cloudObjs);
332  processFields<tensor>(os, particleMap, userFields, cloudObjs);
333  }
334  }
335  Info<< endl;
336  }
337 
338  Info<< "End\n" << endl;
339 
340  return 0;
341 }
342 
343 
344 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
#define forAllIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:459
An STL-conforming hash table.
Definition: HashTable.H:127
List of IOobjects with searching and retrieving facilities.
Definition: IOobjectList.H:53
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
Output to file stream.
Definition: OFstream.H:86
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: PtrList.H:75
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:423
static void noParallel()
Remove the parallel options.
Definition: argList.C:175
static const word prefix
The prefix to local: lagrangian.
Definition: cloud.H:63
A class for handling file names.
Definition: fileName.H:82
A Cloud of passive particles.
static void addOptions(const bool constant=true, const bool withZero=false)
Add the options handled by timeSelector to argList::validOptions.
Definition: timeSelector.C:114
static instantList select0(Time &runTime, const argList &args)
Return the set of times selected based on the argList options.
Definition: timeSelector.C:252
A class for managing temporary objects.
Definition: tmp.H:55
void clear() const
If object pointer points to valid object:
Definition: tmpI.H:237
int main(int argc, char *argv[])
Definition: financialFoam.C:44
static instantList timeDirs
Definition: globalFoam.H:44
label nPoints
Namespace for OpenFOAM.
dimensionedScalar pos(const dimensionedScalar &ds)
Pair< label > labelPair
Label pair.
Definition: labelPair.H:48
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
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:251
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
messageStream Info
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
void writeVTK(OFstream &os, const Type &value)
static const char nl
Definition: Ostream.H:260
Foam::argList args(argc, argv)
const word cloudName(propsDict.lookup("cloudName"))
List< word > userFields(propsDict.lookup("fields"))