streamlinesParticle.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-2022 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 \*---------------------------------------------------------------------------*/
25 
26 #include "streamlinesParticle.H"
27 #include "streamlinesCloud.H"
28 #include "vectorFieldIOField.H"
29 #include "scalarFieldIOField.H"
30 #include "transformerIOList.H"
31 
32 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
33 
34 Foam::vector Foam::streamlinesParticle::interpolateFields
35 (
36  const trackingData& td,
37  const point& position,
38  const label celli,
39  const label facei
40 )
41 {
42  if (celli == -1)
43  {
45  << "Cell:" << celli << abort(FatalError);
46  }
47 
48  bool interpolatedU = false;
50 
51  forAll(td.scalarInterp_, fieldi)
52  {
53  #define InterpolateType(Type, nullArg) \
54  if (td.Type##Interp_.set(fieldi)) \
55  { \
56  const Type s = \
57  td.Type##Interp_[fieldi].interpolate \
58  ( \
59  position, \
60  celli, \
61  facei \
62  ); \
63  \
64  sampled##Type##s_.setSize(td.Type##Interp_.size()); \
65  sampled##Type##s_[fieldi].append \
66  ( \
67  td.trackOutside_ ? transform_.invTransform(s) : s \
68  ); \
69  }
70  FOR_ALL_FIELD_TYPES(InterpolateType);
71  #undef InterpolateType
72 
73  if
74  (
75  td.vectorInterp_.set(fieldi)
76  && &td.vectorInterp_[fieldi] == &td.UInterp_
77  )
78  {
79  interpolatedU = true;
80  U = sampledvectors_[fieldi].last();
81  }
82  }
83 
84  // Interpolate the velocity if it has not already been done
85  if (!interpolatedU)
86  {
87  U = td.UInterp_.interpolate(position, celli, facei);
88  }
89 
90  return U;
91 }
92 
93 
94 void Foam::streamlinesParticle::endTrack(trackingData& td)
95 {
96  const label n = sampledPositions_.size();
97 
98  const label trackPartIndex =
99  td.trackForward_ ? trackPartIndex_ : -1 - trackPartIndex_;
100 
101  if (!td.trackForward_) reverse(sampledPositions_);
102  td.allPositions_.append(sampledPositions_);
103  sampledPositions_.clear();
104 
105  td.allTracks_.append(List<label>(n, trackIndex_));
106  td.allTrackParts_.append(List<label>(n, trackPartIndex));
107  trackPartIndex_ ++;
108 
109  if (!td.trackForward_) reverse(sampledAges_);
110  td.allAges_.append(sampledAges_);
111  sampledAges_.clear();
112 
113  forAll(td.scalarInterp_, fieldi)
114  {
115  #define EndTrackType(Type, nullArg) \
116  if (td.Type##Interp_.set(fieldi)) \
117  { \
118  if (!td.trackForward_) reverse(sampled##Type##s_[fieldi]); \
119  td.all##Type##s_[fieldi].append(sampled##Type##s_[fieldi]); \
120  sampled##Type##s_[fieldi].clear(); \
121  }
123  #undef EndTrackType
124  }
125 }
126 
127 
128 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
129 
131 (
132  const polyMesh& mesh,
133  const vector& position,
134  const label celli,
135  const label lifeTime,
136  const label trackIndex
137 )
138 :
139  particle(mesh, position, celli),
140  lifeTime_(lifeTime),
141  trackIndex_(trackIndex),
142  trackPartIndex_(0),
143  age_(0),
144  transform_(transformer::I)
145 {}
146 
147 
149 :
150  particle(is, readFields)
151 {
152  if (readFields)
153  {
154  is >> lifeTime_ >> trackIndex_ >> trackPartIndex_ >> age_
155  >> transform_ >> sampledPositions_ >> sampledAges_;
156 
157  #define ReadSampledTypes(Type, nullArg) \
158  List<List<Type>> sampled##Type##s; \
159  is >> sampled##Type##s; \
160  sampled##Type##s_.setSize(sampled##Type##s.size()); \
161  forAll(sampled##Type##s, i) \
162  { \
163  sampled##Type##s_[i].transfer(sampled##Type##s[i]); \
164  }
165  FOR_ALL_FIELD_TYPES(ReadSampledTypes);
166  #undef ReadSampledTypes
167  }
168 
169  // Check state of Istream
170  is.check
171  (
172  "streamlinesParticle::streamlinesParticle"
173  "(const Cloud<streamlinesParticle>&, Istream&, bool)"
174  );
175 }
176 
177 
179 (
180  const streamlinesParticle& p
181 )
182 :
183  particle(p),
184  lifeTime_(p.lifeTime_),
185  trackIndex_(p.trackIndex_),
186  trackPartIndex_(p.trackPartIndex_),
187  age_(p.age_),
188  transform_(p.transform_),
189  sampledPositions_(p.sampledPositions_),
190  sampledAges_(p.sampledAges_)
191  #define SampledTypesInit(Type, nullArg) \
192  , sampled##Type##s_(p.sampled##Type##s_)
194  #undef SampledTypesInit
195 {}
196 
197 
198 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
199 
201 (
203  trackingData& td
204 )
205 {
206  td.keepParticle = true;
207  td.sendToProc = -1;
208 
209  const scalar maxDt = td.mesh.bounds().mag();
210 
211  while (td.keepParticle && td.sendToProc == -1 && lifeTime_ > 0)
212  {
213  scalar dt = maxDt;
214 
215  // Cross cell in steps:
216  // - at subiter 0 calculate dt to cross cell in nSubCycle steps
217  // - at the last subiter do all of the remaining track
218  for (label subIter = 0; subIter < max(1, td.nSubCycle_); subIter++)
219  {
220  --lifeTime_;
221 
222  // Store current position and sampled velocity.
223  sampledPositions_.append
224  (
225  td.trackOutside_
226  ? transform_.invTransformPosition(position(td.mesh))
227  : position(td.mesh)
228  );
229  sampledAges_.append(age_);
230  vector U = interpolateFields(td, position(td.mesh), cell(), face());
231 
232  if (!td.trackForward_)
233  {
234  U = -U;
235  }
236 
237  scalar magU = mag(U);
238 
239  if (magU < small)
240  {
241  // Stagnant particle. Might as well stop
242  lifeTime_ = 0;
243  break;
244  }
245 
246  U /= magU;
247 
248  if (td.trackLength_ < great)
249  {
250  // No sub-cycling. Track a set length on each step.
251  dt = td.trackLength_;
252  }
253  else if (subIter == 0)
254  {
255  // Sub-cycling. Cross the cell in nSubCycle steps.
256  particle copy(*this);
257  copy.trackToFace(td.mesh, maxDt*U, 1);
258  dt *= (copy.stepFraction() - stepFraction())/td.nSubCycle_;
259  }
260  else if (subIter == td.nSubCycle_ - 1)
261  {
262  // Sub-cycling. Track the whole cell on the last step.
263  dt = maxDt;
264  }
265 
266  age_ +=
267  (td.trackForward_ ? +1 : -1)
268  *dt/magU
269  *(1 - trackToAndHitFace(dt*U, 0, cloud, td));
270 
271  if (!td.keepParticle || td.sendToProc != -1 || lifeTime_ == 0)
272  {
273  break;
274  }
275  }
276  }
277 
278  if (!td.keepParticle || lifeTime_ == 0)
279  {
280  if (lifeTime_ == 0)
281  {
282  // Failure exit. Particle stagnated or it's life ran out.
283  if (debug)
284  {
285  Pout<< "streamlinesParticle: Removing stagnant particle:"
286  << position(td.mesh) << " sampled positions:"
287  << sampledPositions_.size() << endl;
288  }
289  td.keepParticle = false;
290  }
291  else
292  {
293  // Normal exit. Store last position and fields
294  sampledPositions_.append
295  (
296  td.trackOutside_
297  ? transform_.invTransformPosition(position(td.mesh))
298  : position(td.mesh)
299  );
300  sampledAges_.append(age_);
301  interpolateFields(td, position(td.mesh), cell(), face());
302 
303  if (debug)
304  {
305  Pout<< "streamlinesParticle: Removing particle:"
306  << position(td.mesh) << " sampled positions:"
307  << sampledPositions_.size() << endl;
308  }
309  }
310 
311  // End this track
312  endTrack(td);
313  }
314 
315  return td.keepParticle;
316 }
317 
318 
320 (
322  trackingData& td
323 )
324 {
325  // Remove particle
326  td.keepParticle = false;
327 }
328 
329 
331 (
333  trackingData& td
334 )
335 {
336  // Remove particle
337  td.keepParticle = false;
338 }
339 
340 
342 (
344  trackingData& td
345 )
346 {
347  // Remove particle
348  td.keepParticle = false;
349 }
350 
351 
353 (
355  trackingData& td
356 )
357 {
358  const cyclicPolyPatch& cpp =
359  static_cast<const cyclicPolyPatch&>
360  (
361  td.mesh.boundaryMesh()[patch(td.mesh)]
362  );
363 
364  // End this track
365  if (!td.trackOutside_ && cpp.transform().transformsPosition())
366  {
367  endTrack(td);
368  }
369 
370  // Move across the cyclic
372 }
373 
374 
376 (
377  const vector& displacement,
378  const scalar fraction,
379  const label patchi,
381  trackingData& td
382 )
383 {
384  // Move across the cyclic
385  const bool result =
387  (
388  displacement,
389  fraction,
390  patchi,
391  cloud,
392  td
393  );
394 
395  // End this track
396  if (result) endTrack(td);
397 
398  return result;
399 }
400 
402 (
404  trackingData& td
405 )
406 {
407  const processorPolyPatch& ppp =
408  static_cast<const processorPolyPatch&>
409  (
410  td.mesh.boundaryMesh()[patch(td.mesh)]
411  );
412 
413  // End this track
414  if (!td.trackOutside_ && ppp.transform().transformsPosition())
415  {
416  endTrack(td);
417  }
418 
420 }
421 
422 
424 (
426  trackingData& td
427 )
428 {
429  // Remove particle
430  td.keepParticle = false;
431 }
432 
433 
435 (
436  const transformer& transform
437 )
438 {
439  transform_ = transform & transform_;
440 }
441 
442 
444 {
445  bool valid = c.size();
446 
448 
449  IOField<label> lifeTime
450  (
451  c.fieldIOobject("lifeTime", IOobject::MUST_READ),
452  valid
453  );
454  c.checkFieldIOobject(c, lifeTime);
455 
456  IOField<label> trackIndex
457  (
458  c.fieldIOobject("trackIndex", IOobject::MUST_READ),
459  valid
460  );
461  c.checkFieldIOobject(c, trackIndex);
462 
463  IOField<label> trackPartIndex
464  (
465  c.fieldIOobject("trackPartIndex", IOobject::MUST_READ),
466  valid
467  );
468  c.checkFieldIOobject(c, trackPartIndex);
469 
470  IOField<scalar> age
471  (
472  c.fieldIOobject("age", IOobject::MUST_READ),
473  valid
474  );
475  c.checkFieldIOobject(c, age);
476 
478  (
479  c.fieldIOobject("transform", IOobject::MUST_READ),
480  valid
481  );
482  //c.checkFieldIOobject(c, transform);
483 
484  vectorFieldIOField sampledPositions
485  (
486  c.fieldIOobject("sampledPositions", IOobject::MUST_READ),
487  valid
488  );
489  c.checkFieldIOobject(c, sampledPositions);
490 
491  scalarFieldIOField sampledAges
492  (
493  c.fieldIOobject("sampledAges", IOobject::MUST_READ),
494  valid
495  );
496  c.checkFieldIOobject(c, sampledAges);
497 
498  label i = 0;
500  {
501  iter().lifeTime_ = lifeTime[i];
502  iter().trackIndex_ = trackIndex[i];
503  iter().trackPartIndex_ = trackPartIndex[i];
504  iter().age_ = age[i];
505  iter().transform_ = transform[i];
506  iter().sampledPositions_.transfer(sampledPositions[i]);
507  iter().sampledAges_.transfer(sampledAges[i]);
508  i++;
509  }
510 }
511 
512 
514 {
516 
517  label np = c.size();
518 
519  IOField<label> lifeTime
520  (
521  c.fieldIOobject("lifeTime", IOobject::NO_READ),
522  np
523  );
524 
525  IOList<label> trackIndex
526  (
527  c.fieldIOobject("trackIndex", IOobject::NO_READ),
528  np
529  );
530 
531  IOList<label> trackPartIndex
532  (
533  c.fieldIOobject("trackPartIndex", IOobject::NO_READ),
534  np
535  );
536 
537  IOField<scalar> age
538  (
539  c.fieldIOobject("age", IOobject::NO_READ),
540  np
541  );
542 
544  (
545  c.fieldIOobject("transform", IOobject::NO_READ),
546  np
547  );
548 
549  vectorFieldIOField sampledPositions
550  (
551  c.fieldIOobject("sampledPositions", IOobject::NO_READ),
552  np
553  );
554 
555  scalarFieldIOField sampledAges
556  (
557  c.fieldIOobject("sampledAges", IOobject::NO_READ),
558  np
559  );
560 
561  label i = 0;
563  {
564  lifeTime[i] = iter().lifeTime_;
565  trackIndex[i] = iter().trackIndex_;
566  trackPartIndex[i] = iter().trackPartIndex_;
567  age[i] = iter().age_;
568  transform[i] = iter().transform_;
569  sampledPositions[i] = iter().sampledPositions_;
570  sampledAges[i] = iter().sampledAges_;
571  i++;
572  }
573 
574  lifeTime.write(np > 0);
575  trackIndex.write(np > 0);
576  trackPartIndex.write(np > 0);
577  age.write(np > 0);
578  transform.write(np > 0);
579  sampledPositions.write(np > 0);
580  sampledAges.write(np > 0);
581 }
582 
583 
584 // * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
585 
587 {
588 
589  os << static_cast<const particle&>(p)
590  << token::SPACE << p.lifeTime_
591  << token::SPACE << p.trackIndex_
592  << token::SPACE << p.trackPartIndex_
593  << token::SPACE << p.age_
594  << token::SPACE << p.transform_
595  << token::SPACE << p.sampledPositions_
596  << token::SPACE << p.sampledAges_
597  #define WriteSampledTypes(Type, nullArg) \
598  << token::SPACE << p.sampled##Type##s_
600  #undef WriteSampledTypes
601 
602  // Check state of Ostream
603  os.check("Ostream& operator<<(Ostream&, const streamlinesParticle&)");
604 
605  return os;
606 }
607 
608 
609 // ************************************************************************* //
label n
#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
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:477
A primitive field of type <Type> with automated input and output.
Definition: IOField.H:53
A List of objects of type <Type> with automated input and output.
Definition: IOList.H:124
virtual bool check(const char *operation) const
Check IOstream status for given operation.
Definition: IOstream.C:92
An Istream is an abstract base class for all input systems (streams, files, token lists etc)....
Definition: Istream.H:60
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
static Form uniform(const Cmpt &s)
Return a VectorSpace with all elements = s.
Definition: VectorSpaceI.H:168
scalar mag() const
The magnitude of the bounding box span.
Definition: boundBoxI.H:90
A cell is defined as a list of faces with extra functionality.
Definition: cell.H:60
A cloud is a collection of lagrangian particles.
Definition: cloud.H:55
Cyclic plane patch.
virtual const transformer & transform() const
Return transformation between the coupled patches.
Ostream & write(Ostream &os, scalar &multiplier, const dimensionSets &) const
Write using provided units.
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:76
label sendToProc
Processor to send the particle to. -1 indicates that this.
Definition: particle.H:113
bool keepParticle
Flag to indicate whether to keep particle (false = delete)
Definition: particle.H:109
const polyMesh & mesh
Reference to the mesh.
Definition: particle.H:106
Base particle class.
Definition: particle.H:83
static void readFields(TrackCloudType &c)
Read the fields associated with the owner cloud.
void hitCyclicPatch(TrackCloudType &, trackingData &)
Overridable function to handle the particle hitting a.
void hitProcessorPatch(TrackCloudType &, trackingData &)
Overridable function to handle the particle hitting a.
scalar trackToFace(const polyMesh &mesh, const vector &displacement, const scalar fraction)
As particle::track, but stops when a face is hit.
Definition: particle.C:595
scalar stepFraction() const
Return the fraction of time-step completed.
Definition: particleI.H:164
vector position(const polyMesh &mesh) const
Return current particle position.
Definition: particleI.H:255
bool hitNonConformalCyclicPatch(const vector &displacement, const scalar fraction, const label patchi, TrackCloudType &cloud, trackingData &td)
Overridable function to handle the particle hitting an.
static void writeFields(const TrackCloudType &c)
Write the fields associated with the owner cloud.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:80
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:403
const boundBox & bounds() const
Return mesh bounding box.
Definition: polyMesh.H:409
Neighbour processor patch.
virtual const transformer & transform() const
Return null transform between processor patches.
virtual bool write(const bool write=true) const
Write using setting from DB.
A Cloud of streamlines particles.
Particle class that samples fields as it passes through. Used in streamlines calculation.
bool move(streamlinesCloud &, trackingData &)
Track all particles to their end point.
static void writeFields(const Cloud< streamlinesParticle > &)
Write.
void hitProcessorPatch(streamlinesCloud &, trackingData &)
Overridable function to handle the particle hitting a.
bool hitNonConformalCyclicPatch(const vector &displacement, const scalar fraction, const label patchi, streamlinesCloud &cloud, trackingData &td)
Overridable function to handle the particle hitting an.
void hitSymmetryPatch(streamlinesCloud &, trackingData &)
Overridable function to handle the particle hitting a.
streamlinesParticle(const polyMesh &mesh, const vector &position, const label celli, const label lifeTime, const label trackIndex)
Construct from components.
void hitCyclicPatch(streamlinesCloud &, trackingData &)
Overridable function to handle the particle hitting a cyclic.
void hitWallPatch(streamlinesCloud &, trackingData &)
Overridable function to handle the particle hitting a wallPatch.
void hitSymmetryPlanePatch(streamlinesCloud &, trackingData &)
Overridable function to handle the particle hitting a.
static void readFields(Cloud< streamlinesParticle > &)
Read.
virtual void transformProperties(const transformer &)
Transform the physical properties of the particle.
void hitWedgePatch(streamlinesCloud &, trackingData &)
Overridable function to handle the particle hitting a wedge.
Vector-tensor class used to perform translations, rotations and scaling operations in 3D space.
Definition: transformer.H:84
bool transformsPosition() const
Return true if the transformer transforms a point.
Definition: transformerI.H:146
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:306
label patchi
U
Definition: pEqn.H:72
bool valid(const PtrList< ModelType > &l)
const dimensionedScalar c
Speed of light in a vacuum.
static Type NaN()
Return a primitive with all components set to NaN.
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
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
void readFields(const typename GeoFieldType::Mesh &mesh, const IOobjectList &objects, const HashSet< word > &selectedFields, LIFOStack< regIOobject * > &storedObjects)
Read the selected GeometricFields of the specified type.
Definition: ReadFields.C:244
errorManip< error > abort(error &err)
Definition: errorManip.H:131
static const Identity< scalar > I
Definition: Identity.H:93
vector point
Point is a vector.
Definition: point.H:41
FOR_ALL_FIELD_TYPES(DefineContiguousFvWallLocationDataType)
void reverse(UList< T > &, const label n)
Definition: UListI.H:334
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
dimensionSet transform(const dimensionSet &)
Definition: dimensionSet.C:483
dimensioned< scalar > mag(const dimensioned< Type > &)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
error FatalError
Ostream & operator<<(Ostream &, const ensightPart &)
volScalarField & p
#define InterpolateType(Type, nullArg)
#define WriteSampledTypes(Type, nullArg)
#define EndTrackType(Type, nullArg)
#define SampledTypesInit(Type, nullArg)
#define ReadSampledTypes(Type, nullArg)