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-2025 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;
49  vector U = vector::uniform(NaN);
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  label& nLocateBoundaryHits,
136  const label lifeTime,
137  const label trackIndex
138 )
139 :
140  particle(mesh, position, celli, nLocateBoundaryHits),
141  lifeTime_(lifeTime),
142  trackIndex_(trackIndex),
143  trackPartIndex_(0),
144  age_(0),
145  transform_(transformer::I)
146 {}
147 
148 
150 :
151  particle(is, readFields)
152 {
153  if (readFields)
154  {
155  is >> lifeTime_ >> trackIndex_ >> trackPartIndex_ >> age_
156  >> transform_ >> sampledPositions_ >> sampledAges_;
157 
158  #define ReadSampledTypes(Type, nullArg) \
159  List<List<Type>> sampled##Type##s; \
160  is >> sampled##Type##s; \
161  sampled##Type##s_.setSize(sampled##Type##s.size()); \
162  forAll(sampled##Type##s, i) \
163  { \
164  sampled##Type##s_[i].transfer(sampled##Type##s[i]); \
165  }
166  FOR_ALL_FIELD_TYPES(ReadSampledTypes);
167  #undef ReadSampledTypes
168  }
169 
170  // Check state of Istream
171  is.check
172  (
173  "streamlinesParticle::streamlinesParticle"
174  "(const Cloud<streamlinesParticle>&, Istream&, bool)"
175  );
176 }
177 
178 
180 (
181  const streamlinesParticle& p
182 )
183 :
184  particle(p),
185  lifeTime_(p.lifeTime_),
186  trackIndex_(p.trackIndex_),
187  trackPartIndex_(p.trackPartIndex_),
188  age_(p.age_),
189  transform_(p.transform_),
190  sampledPositions_(p.sampledPositions_),
191  sampledAges_(p.sampledAges_)
192  #define SampledTypesInit(Type, nullArg) \
193  , sampled##Type##s_(p.sampled##Type##s_)
195  #undef SampledTypesInit
196 {}
197 
198 
199 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
200 
202 (
204  trackingData& td
205 )
206 {
207  td.keepParticle = true;
208  td.sendToProc = -1;
209 
210  const scalar maxDt = td.mesh.bounds().mag();
211 
212  while (td.keepParticle && td.sendToProc == -1 && lifeTime_ > 0)
213  {
214  scalar dt = maxDt;
215 
216  // Cross cell in steps:
217  // - at subiter 0 calculate dt to cross cell in nSubCycle steps
218  // - at the last subiter do all of the remaining track
219  for (label subIter = 0; subIter < max(1, td.nSubCycle_); subIter++)
220  {
221  --lifeTime_;
222 
223  // Store current position and sampled velocity.
224  sampledPositions_.append
225  (
226  td.trackOutside_
227  ? transform_.invTransformPosition(position(td.mesh))
228  : position(td.mesh)
229  );
230  sampledAges_.append(age_);
231  vector U = interpolateFields(td, position(td.mesh), cell(), face());
232 
233  if (!td.trackForward_)
234  {
235  U = -U;
236  }
237 
238  scalar magU = mag(U);
239 
240  if (magU < small)
241  {
242  // Stagnant particle. Might as well stop
243  lifeTime_ = 0;
244  break;
245  }
246 
247  U /= magU;
248 
249  if (td.trackLength_ < great)
250  {
251  // No sub-cycling. Track a set length on each step.
252  dt = td.trackLength_;
253  }
254  else if (subIter == 0)
255  {
256  // Sub-cycling. Cross the cell in nSubCycle steps.
257  particle copy(*this);
258  dt *= (1 - copy.trackToFace(td.mesh, maxDt*U, 0))/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 its 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 (
446 )
447 {
448  bool valid = c.size();
449 
451 
452  IOField<label> lifeTime
453  (
454  c.fieldIOobject("lifeTime", IOobject::MUST_READ),
455  valid
456  );
457  c.checkFieldIOobject(c, lifeTime);
458 
459  IOField<label> trackIndex
460  (
461  c.fieldIOobject("trackIndex", IOobject::MUST_READ),
462  valid
463  );
464  c.checkFieldIOobject(c, trackIndex);
465 
466  IOField<label> trackPartIndex
467  (
468  c.fieldIOobject("trackPartIndex", IOobject::MUST_READ),
469  valid
470  );
471  c.checkFieldIOobject(c, trackPartIndex);
472 
473  IOField<scalar> age
474  (
475  c.fieldIOobject("age", IOobject::MUST_READ),
476  valid
477  );
478  c.checkFieldIOobject(c, age);
479 
481  (
482  c.fieldIOobject("transform", IOobject::MUST_READ),
483  valid
484  );
485  //c.checkFieldIOobject(c, transform);
486 
487  vectorFieldIOField sampledPositions
488  (
489  c.fieldIOobject("sampledPositions", IOobject::MUST_READ),
490  valid
491  );
492  c.checkFieldIOobject(c, sampledPositions);
493 
494  scalarFieldIOField sampledAges
495  (
496  c.fieldIOobject("sampledAges", IOobject::MUST_READ),
497  valid
498  );
499  c.checkFieldIOobject(c, sampledAges);
500 
501  label i = 0;
503  {
504  iter().lifeTime_ = lifeTime[i];
505  iter().trackIndex_ = trackIndex[i];
506  iter().trackPartIndex_ = trackPartIndex[i];
507  iter().age_ = age[i];
508  iter().transform_ = transform[i];
509  iter().sampledPositions_.transfer(sampledPositions[i]);
510  iter().sampledAges_.transfer(sampledAges[i]);
511  i++;
512  }
513 }
514 
515 
517 (
519 )
520 {
522 
523  label np = c.size();
524 
525  IOField<label> lifeTime
526  (
527  c.fieldIOobject("lifeTime", IOobject::NO_READ),
528  np
529  );
530 
531  IOList<label> trackIndex
532  (
533  c.fieldIOobject("trackIndex", IOobject::NO_READ),
534  np
535  );
536 
537  IOList<label> trackPartIndex
538  (
539  c.fieldIOobject("trackPartIndex", IOobject::NO_READ),
540  np
541  );
542 
543  IOField<scalar> age
544  (
545  c.fieldIOobject("age", IOobject::NO_READ),
546  np
547  );
548 
550  (
551  c.fieldIOobject("transform", IOobject::NO_READ),
552  np
553  );
554 
555  vectorFieldIOField sampledPositions
556  (
557  c.fieldIOobject("sampledPositions", IOobject::NO_READ),
558  np
559  );
560 
561  scalarFieldIOField sampledAges
562  (
563  c.fieldIOobject("sampledAges", IOobject::NO_READ),
564  np
565  );
566 
567  label i = 0;
569  {
570  lifeTime[i] = iter().lifeTime_;
571  trackIndex[i] = iter().trackIndex_;
572  trackPartIndex[i] = iter().trackPartIndex_;
573  age[i] = iter().age_;
574  transform[i] = iter().transform_;
575  sampledPositions[i] = iter().sampledPositions_;
576  sampledAges[i] = iter().sampledAges_;
577  i++;
578  }
579 
580  lifeTime.write(np > 0);
581  trackIndex.write(np > 0);
582  trackPartIndex.write(np > 0);
583  age.write(np > 0);
584  transform.write(np > 0);
585  sampledPositions.write(np > 0);
586  sampledAges.write(np > 0);
587 }
588 
589 
590 // * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
591 
593 {
594 
595  os << static_cast<const particle&>(p)
596  << token::SPACE << p.lifeTime_
597  << token::SPACE << p.trackIndex_
598  << token::SPACE << p.trackPartIndex_
599  << token::SPACE << p.age_
600  << token::SPACE << p.transform_
601  << token::SPACE << p.sampledPositions_
602  << token::SPACE << p.sampledAges_
603  #define WriteSampledTypes(Type, nullArg) \
604  << token::SPACE << p.sampled##Type##s_
606  #undef WriteSampledTypes
607 
608  // Check state of Ostream
609  os.check("Ostream& operator<<(Ostream&, const streamlinesParticle&)");
610 
611  return os;
612 }
613 
614 
615 // ************************************************************************* //
label n
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
#define forAllIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:458
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:476
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
static direction size()
Return the number of elements in the VectorSpace = Ncmpts.
Definition: VectorSpaceI.H:83
scalar mag() const
The magnitude of the bounding box span.
Definition: boundBoxI.H:96
A cell is defined as a list of faces with extra functionality.
Definition: cell.H:60
Base class for clouds. Provides a basic evolution algorithm, models, and a database for caching deriv...
Definition: cloud.H:63
Cyclic plane patch.
virtual const transformer & transform() const
Return transformation between the coupled patches.
Ostream & write(Ostream &os) const
Write.
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:109
bool keepParticle
Flag to indicate whether to keep particle (false = delete)
Definition: particle.H:105
const polyMesh & mesh
Reference to the mesh.
Definition: particle.H:102
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:182
vector position(const polyMesh &mesh) const
Return current particle position.
Definition: particleI.H:159
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:401
const boundBox & bounds() const
Return mesh bounding box.
Definition: polyMesh.H:407
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.
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.
static void writeFields(const lagrangian::Cloud< streamlinesParticle > &)
Write.
void hitSymmetryPatch(streamlinesCloud &, trackingData &)
Overridable function to handle the particle hitting a.
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.
streamlinesParticle(const polyMesh &mesh, const vector &position, const label celli, label &nLocateBoundaryHits, const label lifeTime, const label trackIndex)
Construct from components.
virtual void transformProperties(const transformer &)
Transform the physical properties of the particle.
static void readFields(lagrangian::Cloud< streamlinesParticle > &)
Read.
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
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
label patchi
U
Definition: pEqn.H:72
bool valid(const PtrList< ModelType > &l)
const dimensionedScalar c
Speed of light in a vacuum.
point position(const polyMesh &mesh, const barycentric &coordinates, const label celli, const label facei, const label faceTrii, const scalar stepFraction)
Return the position given the coordinates and tet topology.
Definition: trackingI.H:224
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:258
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
void mag(LagrangianPatchField< scalar > &f, const LagrangianPatchField< Type > &f1)
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:501
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
Ostream & operator<<(Ostream &os, const fvConstraints &constraints)
error FatalError
FOR_ALL_FIELD_TYPES(makeFieldSourceTypedef)
volScalarField & p
#define InterpolateType(Type, nullArg)
#define WriteSampledTypes(Type, nullArg)
#define EndTrackType(Type, nullArg)
#define SampledTypesInit(Type, nullArg)
#define ReadSampledTypes(Type, nullArg)