ParticleZoneInfo.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) 2022 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
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 "ParticleZoneInfo.H"
29 #include "DynamicField.H"
30 
31 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
32 
33 namespace Foam
34 {
35 
37 {
38  void operator()(particleInfo& p1, const particleInfo& p2) const
39  {
40  // p2 not set
41  if (p2.origID == -1)
42  {
43  return;
44  }
45 
46  // p1 not set - initialise with p2
47  if (p1.origID == -1)
48  {
49  p1 = p2;
50  return;
51  }
52 
53  // Set initial values
54  if (p2.time0 < p1.time0)
55  {
56  p1.time0 = p2.time0;
57  p1.d0 = p2.d0;
58  p1.mass0 = p2.mass0;
59  }
60 
61  // Accumulate age
62  p1.age += p2.age;
63 
64  // Set latest available values
65  if (p2.isOlderThan(p1))
66  {
67  p1.position = p2.position;
68  p1.d = p2.d;
69  p1.mass = p2.mass;
70  }
71  }
72 };
73 
74 template<class Type>
75 Field<Type> getData
76 (
79 )
80 {
81  Field<Type> result(data.size());
82 
83  forAll(data, i)
84  {
85  result[i] = data[i].*field;
86  }
87 
88  return result;
89 }
90 
91 } // End namespace Foam
92 
93 
94 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
95 
96 template<class CloudType>
98 (
99  const UList<particleInfo>& data
100 )
101 {
102  coordSet coords
103  (
104  "zoneParticles",
105  "xyz",
106  getData(data, &particleInfo::position),
107  scalarList(data.size(), Zero)
108  );
109 
110  writerPtr_->open(coords, this->baseTimeDir() / "zoneParticles");
111  writerPtr_->beginTime(this->owner().time());
112 
113 #undef writeLocal
114 #define writeLocal(field) \
115  writerPtr_->write(#field, getData(data, &particleInfo::field));
116 
117  writeLocal(origID);
118  writeLocal(origProc);
119  writeLocal(time0);
120  writeLocal(age);
121  writeLocal(d0);
122  writeLocal(d);
123  writeLocal(mass0);
124  writeLocal(mass);
125 #undef writeLocal
126 
127  writerPtr_->endTime();
128  writerPtr_->close();
129 }
130 
131 
132 template<class CloudType>
134 {
135  this->writeHeaderValue(os, "cellZone", cellZoneName_);
136  this->writeHeaderValue(os, "time", this->owner().time().timeOutputValue());
137  this->writeHeader(os, "");
138  this->writeCommented(os, "origID");
139  os << tab << "origProc"
140  << tab << "(x y z)"
141  << tab << "time0"
142  << tab << "age"
143  << tab << "d0"
144  << tab << "d"
145  << tab << "mass0"
146  << tab << "mass"
147  << endl;
148 }
149 
150 
151 template<class CloudType>
152 bool Foam::ParticleZoneInfo<CloudType>::inZone(const label celli) const
153 {
154  return this->owner().mesh().cellZones()[cellZoneId_].whichCell(celli) != -1;
155 }
156 
157 
158 template<class CloudType>
160 (
161  const particleInfo& p
162 ) const
163 {
164  forAll(data_, i)
165  {
166  const auto& d = data_[i];
167  if ((d.origProc == p.origProc) && (d.origID == p.origID))
168  {
169  return i;
170  }
171  }
172 
173  return -1;
174 }
175 
176 
177 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
178 
179 template<class CloudType>
181 (
182  const dictionary& dict,
183  CloudType& owner,
184  const word& modelName
185 )
186 :
187  CloudFunctionObject<CloudType>(dict, owner, modelName, typeName),
188  functionObjects::writeFile
189  (
190  owner,
191  this->localPath(),
192  typeName
193  ),
194  cellZoneName_(this->coeffDict().getWord("cellZone")),
195  cellZoneId_(-1),
196  data_(),
197  movedParticles_(),
198  maxIDs_(Pstream::nProcs(), Zero),
199  writerPtr_(nullptr)
200 {
201  if (Pstream::master())
202  {
203  const word writerType = this->coeffDict().getWord("writer");
204 
205  writerPtr_ = coordSetWriter::New
206  (
207  writerType,
208  coordSetWriter::formatOptions(this->coeffDict(), writerType)
209  );
210  }
211 
212  writeFile::read(this->coeffDict());
213 
214  const auto& cellZones = owner.mesh().cellZones();
215 
216  cellZoneId_ = cellZones.findZoneID(cellZoneName_);
217  if (cellZoneId_ == -1)
218  {
220  << "Unable to find cellZone " << cellZoneName_
221  << ". Available cellZones are:" << cellZones.names()
222  << exit(FatalIOError);
223  }
224 
225  Info<< " Processing cellZone" << cellZoneName_ << " with id "
226  << cellZoneId_ << endl;
227 
228  if (Pstream::master())
229  {
230  // Data was reduced on write
231  labelList maxIDs;
232 
233  if (this->getModelProperty("maxIDs", maxIDs))
234  {
235  if (maxIDs.size() == Pstream::nProcs())
236  {
237  maxIDs_ = maxIDs;
238  this->getModelProperty("data", data_);
239 
240  Info<< " Restarting with " << data_.size()
241  << " particles" << endl;
242  }
243  else
244  {
246  << "Case restarted with a different number of processors."
247  << " Restarting particle statistics." << endl;
248 
249  // TODO
250  // - use Cloud for base storage instead of local particle list?
251  }
252  }
253  }
254 }
255 
256 
257 template<class CloudType>
259 (
260  const ParticleZoneInfo<CloudType>& pzi
261 )
262 :
263  CloudFunctionObject<CloudType>(pzi),
264  writeFile(pzi),
265  cellZoneName_(pzi.cellZoneName_),
266  cellZoneId_(pzi.cellZoneId_),
267  data_(pzi.data_),
268  movedParticles_(pzi.movedParticles_),
269  maxIDs_(Pstream::nProcs()),
270  writerPtr_(nullptr)
271 {}
272 
273 
274 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
275 
276 template<class CloudType>
278 (
279  const typename parcelType::trackingData& td
280 )
281 {}
282 
283 
284 template<class CloudType>
286 (
287  const typename parcelType::trackingData& td
288 )
289 {
290  Log_<< this->type() << ":" << nl
291  << " Cell zone = " << cellZoneName_ << nl
292  << " Contributions = "
293  << returnReduce(movedParticles_.size(), sumOp<label>())
294  << endl;
295 
296  if (!this->writeTime())
297  {
298  Log_<< endl;
299  }
300 
301  for (const auto& p : movedParticles_)
302  {
303  const label id = getParticleID(p);
304 
305  if (id == -1)
306  {
307  // New particle
308  data_.append(p);
309  maxIDs_[p.origProc] = max(maxIDs_[p.origProc], p.origID);
310  }
311  else
312  {
313  // Add to existing particle
314  data_[id] += p;
315  }
316  }
317 
318  movedParticles_.clear();
319 
320  // Calls write
322 }
323 
324 
325 template<class CloudType>
327 (
328  parcelType& p,
329  const scalar dt,
330  const point&,
331  const typename parcelType::trackingData& td
332 )
333 {
334  if (inZone(p.cell()))
335  {
336  particleInfo newData;
337  newData.origID = p.origId();
338  newData.origProc = p.origProc();
339  newData.position = p.position();
340  newData.time0 = this->owner().time().value() + dt;
341  newData.age = dt;
342  newData.d0 = p.d();
343  newData.d = p.d();
344  newData.mass0 = p.mass();
345  newData.mass = newData.mass0;
346 
347  movedParticles_.append(newData);
348  }
349 
350  return true;
351 }
352 
353 
354 template<class CloudType>
356 {
357  autoPtr<OFstream> osPtr =
358  this->newFileAtTime
359  (
360  "particles",
361  this->owner().time().timeOutputValue()
362  );
363 
364  if (Pstream::parRun())
365  {
366  // Find number of particles per proc
367  labelList allMaxIDs(maxIDs_);
368  Pstream::listCombineReduce(allMaxIDs, maxEqOp<label>());
369 
370  // Combine into single list
371  label n = returnReduce(data_.size(), sumOp<label>());
372  DynamicList<particleInfo> globalParticles(n);
373  {
374  List<List<particleInfo>> procParticles(Pstream::nProcs());
375  forAll(procParticles, proci)
376  {
377  procParticles[proci].resize(allMaxIDs[proci] + 1);
378  }
379 
380  // Insert into bins for accumulation
381  for (const auto& d : data_)
382  {
383  procParticles[d.origProc][d.origID] = d;
384  }
385 
386  for (auto& particles : procParticles)
387  {
388  Pstream::listCombineGather(particles, particleInfoCombineOp());
389  for (const auto& p : particles)
390  {
391  if (p.origID != -1)
392  {
393  globalParticles.append(p);
394  }
395  }
396  }
397  }
398 
399  if (Pstream::master())
400  {
401  writeWriter(globalParticles);
402 
403  auto& os = osPtr();
404  writeFileHeader(os);
405 
406  label nData = 0;
407 
408  for (const auto& p : globalParticles)
409  {
410  if (p.origID != -1)
411  {
412  os << p << endl;
413  ++nData;
414  }
415  }
416 
417  Log_<< " Number of particles = " << nData << nl
418  << " Written data to " << os.name() << endl;
419 
420  this->setModelProperty("data", globalParticles);
421  this->setModelProperty("maxIDs", allMaxIDs);
422  }
423  else
424  {
425  // Data only present on master
426  this->setModelProperty("data", List<particleInfo>());
427  this->setModelProperty("maxIDs", labelList());
428  }
429  }
430  else
431  {
432  writeWriter(data_);
433 
434  auto& os = osPtr();
435  writeFileHeader(os);
436 
437  for (const auto& p : data_)
438  {
439  os << p << nl;
440  }
441 
442  Log_<< " Number of particles = " << data_.size() << nl
443  << " Written data to " << os.name() << endl;
444 
445  this->setModelProperty("data", data_);
446  this->setModelProperty("maxIDs", maxIDs_);
447  }
448 
449  Log_<< endl;
450 }
451 
452 
453 // ************************************************************************* //
virtual void postEvolve(const typename parcelType::trackingData &td)
Post-evolve hook.
List< scalar > scalarList
List of scalar.
Definition: scalarList.H:32
const Type & value() const noexcept
Return const reference to value.
dictionary dict
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
rDeltaTY field()
DSMCCloud< dsmcParcel > CloudType
static void writeHeader(Ostream &os, const word &fieldName)
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
virtual const fileName & name() const override
Read/write access to the name of the stream.
Definition: OSstream.H:128
ParticleZoneInfo(const dictionary &dict, CloudType &owner, const word &modelName)
Construct from dictionary.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:1049
constexpr char tab
The tab &#39;\t&#39; character(0x09)
Definition: Ostream.H:49
T returnReduce(const T &value, const BinaryOp &bop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
virtual void postEvolve(const typename parcelType::trackingData &td)
Post-evolve hook.
const CloudType & owner() const
Return const access to the owner cloud.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition: POSIX.C:799
static autoPtr< coordSetWriter > New(const word &writeFormat)
Return a reference to the selected writer.
virtual void preEvolve(const typename parcelType::trackingData &td)
Pre-evolve hook.
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator). It is 1 for serial run. ...
Definition: UPstream.H:1065
#define Log_
Report write to Foam::Info if the class log switch is true.
#define writeLocal(field)
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:137
A class for handling words, derived from Foam::string.
Definition: word.H:63
const Time & time() const noexcept
Return time registry.
Inter-processor communications stream.
Definition: Pstream.H:57
virtual void write()
Write.
scalar isOlderThan(const particleInfo &p) const
bool getModelProperty(const word &entryName, Type &value) const
Retrieve generic property from the sub-model.
static dictionary formatOptions(const dictionary &dict, const word &formatName, const word &entryName="formatOptions")
Same as fileFormats::getFormatOptions.
word getWord(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Same as get< word >(const word&, keyType::option)
Definition: dictionary.H:1667
const dictionary & coeffDict() const
Return const access to the coefficients dictionary.
Definition: subModelBase.C:122
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: HashTable.H:105
virtual bool postMove(parcelType &p, const scalar dt, const point &position0, const typename parcelType::trackingData &td)
Post-move hook.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
void read(Istream &, label &val, const dictionary &)
In-place read with dictionary lookup.
OBJstream os(runTime.globalPath()/outputName)
Reports cloud information for particles passing through a specified cell zone.
const fvMesh & mesh() const
Return reference to the mesh.
Definition: DSMCCloudI.H:37
#define WarningInFunction
Report a warning using Foam::Warning.
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:627
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1082
const cellZoneMesh & cellZones() const noexcept
Return cell zone mesh.
Definition: polyMesh.H:678
messageStream Info
Information stream (stdout output on master, null elsewhere)
label n
static void listCombineGather(const List< commsStruct > &comms, List< T > &values, const CombineOp &cop, const int tag, const label comm)
List< label > labelList
A List of labels.
Definition: List.H:62
volScalarField & p
Field< Type > getData(const Foam::UList< Foam::particleInfo > &data, Type Foam::particleInfo::*field)
Templated base class for dsmc cloud.
Definition: DSMCCloud.H:67
Templated cloud function object base class.
void operator()(particleInfo &p1, const particleInfo &p2) const
Namespace for OpenFOAM.
static void listCombineReduce(List< T > &values, const CombineOp &cop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
After completion all processors have the same data.
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127