KinematicParcel.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 | Copyright (C) 2011-2016 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 "KinematicParcel.H"
27 #include "forceSuSp.H"
28 #include "IntegrationScheme.H"
29 #include "meshTools.H"
30 
31 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
32 
33 template<class ParcelType>
35 
36 
37 // * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * //
38 
39 template<class ParcelType>
40 template<class TrackData>
42 (
43  TrackData& td,
44  const scalar dt,
45  const label celli
46 )
47 {
48  tetIndices tetIs = this->currentTetIndices();
49 
50  rhoc_ = td.rhoInterp().interpolate(this->position(), tetIs);
51 
52  if (rhoc_ < td.cloud().constProps().rhoMin())
53  {
54  if (debug)
55  {
57  << "Limiting observed density in cell " << celli << " to "
58  << td.cloud().constProps().rhoMin() << nl << endl;
59  }
60 
61  rhoc_ = td.cloud().constProps().rhoMin();
62  }
63 
64  Uc_ = td.UInterp().interpolate(this->position(), tetIs);
65 
66  muc_ = td.muInterp().interpolate(this->position(), tetIs);
67 
68  // Apply dispersion components to carrier phase velocity
69  Uc_ = td.cloud().dispersion().update
70  (
71  dt,
72  celli,
73  U_,
74  Uc_,
75  UTurb_,
76  tTurb_
77  );
78 }
79 
80 
81 template<class ParcelType>
82 template<class TrackData>
84 (
85  TrackData& td,
86  const scalar dt,
87  const label celli
88 )
89 {
90  Uc_ += td.cloud().UTrans()[celli]/massCell(celli);
91 }
92 
93 
94 template<class ParcelType>
95 template<class TrackData>
97 (
98  TrackData& td,
99  const scalar dt,
100  const label celli
101 )
102 {
103  // Define local properties at beginning of time step
104  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105  const scalar np0 = nParticle_;
106  const scalar mass0 = mass();
107 
108  // Reynolds number
109  const scalar Re = this->Re(U_, d_, rhoc_, muc_);
110 
111 
112  // Sources
113  //~~~~~~~~
114 
115  // Explicit momentum source for particle
116  vector Su = Zero;
117 
118  // Linearised momentum source coefficient
119  scalar Spu = 0.0;
120 
121  // Momentum transfer from the particle to the carrier phase
122  vector dUTrans = Zero;
123 
124 
125  // Motion
126  // ~~~~~~
127 
128  // Calculate new particle velocity
129  this->U_ = calcVelocity(td, dt, celli, Re, muc_, mass0, Su, dUTrans, Spu);
130 
131 
132  // Accumulate carrier phase source terms
133  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
134  if (td.cloud().solution().coupled())
135  {
136  // Update momentum transfer
137  td.cloud().UTrans()[celli] += np0*dUTrans;
138 
139  // Update momentum transfer coefficient
140  td.cloud().UCoeff()[celli] += np0*Spu;
141  }
142 }
143 
144 
145 template<class ParcelType>
146 template<class TrackData>
148 (
149  TrackData& td,
150  const scalar dt,
151  const label celli,
152  const scalar Re,
153  const scalar mu,
154  const scalar mass,
155  const vector& Su,
156  vector& dUTrans,
157  scalar& Spu
158 ) const
159 {
160  typedef typename TrackData::cloudType cloudType;
161  typedef typename cloudType::parcelType parcelType;
162  typedef typename cloudType::forceType forceType;
163 
164  const forceType& forces = td.cloud().forces();
165 
166  // Momentum source due to particle forces
167  const parcelType& p = static_cast<const parcelType&>(*this);
168  const forceSuSp Fcp = forces.calcCoupled(p, dt, mass, Re, mu);
169  const forceSuSp Fncp = forces.calcNonCoupled(p, dt, mass, Re, mu);
170  const forceSuSp Feff = Fcp + Fncp;
171  const scalar massEff = forces.massEff(p, mass);
172 
173 
174  // New particle velocity
175  //~~~~~~~~~~~~~~~~~~~~~~
176 
177  // Update velocity - treat as 3-D
178  const vector abp = (Feff.Sp()*Uc_ + (Feff.Su() + Su))/massEff;
179  const scalar bp = Feff.Sp()/massEff;
180 
181  Spu = dt*Feff.Sp();
182 
184  td.cloud().UIntegrator().integrate(U_, dt, abp, bp);
185 
186  vector Unew = Ures.value();
187 
188  // note: Feff.Sp() and Fc.Sp() must be the same
189  dUTrans += dt*(Feff.Sp()*(Ures.average() - Uc_) - Fcp.Su());
190 
191  // Apply correction to velocity and dUTrans for reduced-D cases
192  const polyMesh& mesh = td.cloud().pMesh();
193  meshTools::constrainDirection(mesh, mesh.solutionD(), Unew);
194  meshTools::constrainDirection(mesh, mesh.solutionD(), dUTrans);
195 
196  return Unew;
197 }
198 
199 
200 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
201 
202 template<class ParcelType>
204 (
206 )
207 :
208  ParcelType(p),
209  active_(p.active_),
210  typeId_(p.typeId_),
211  nParticle_(p.nParticle_),
212  d_(p.d_),
213  dTarget_(p.dTarget_),
214  U_(p.U_),
215  rho_(p.rho_),
216  age_(p.age_),
217  tTurb_(p.tTurb_),
218  UTurb_(p.UTurb_),
219  rhoc_(p.rhoc_),
220  Uc_(p.Uc_),
221  muc_(p.muc_)
222 {}
223 
224 
225 template<class ParcelType>
227 (
229  const polyMesh& mesh
230 )
231 :
232  ParcelType(p, mesh),
233  active_(p.active_),
234  typeId_(p.typeId_),
235  nParticle_(p.nParticle_),
236  d_(p.d_),
237  dTarget_(p.dTarget_),
238  U_(p.U_),
239  rho_(p.rho_),
240  age_(p.age_),
241  tTurb_(p.tTurb_),
242  UTurb_(p.UTurb_),
243  rhoc_(p.rhoc_),
244  Uc_(p.Uc_),
245  muc_(p.muc_)
246 {}
247 
248 
249 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
250 
251 template<class ParcelType>
252 template<class TrackData>
254 (
255  TrackData& td,
256  const scalar trackTime
257 )
258 {
259  typename TrackData::cloudType::parcelType& p =
260  static_cast<typename TrackData::cloudType::parcelType&>(*this);
261 
262  td.switchProcessor = false;
263  td.keepParticle = true;
264 
265  const polyMesh& mesh = td.cloud().pMesh();
266  const polyBoundaryMesh& pbMesh = mesh.boundaryMesh();
267  const scalarField& cellLengthScale = td.cloud().cellLengthScale();
268  const scalar maxCo = td.cloud().solution().maxCo();
269 
270  scalar tEnd = (1.0 - p.stepFraction())*trackTime;
271  scalar dtMax = trackTime;
272  if (td.cloud().solution().transient())
273  {
274  dtMax *= maxCo;
275  }
276 
277  bool tracking = true;
278  label nTrackingStalled = 0;
279 
280  while (td.keepParticle && !td.switchProcessor && tEnd > ROOTVSMALL)
281  {
282  // Apply correction to position for reduced-D cases
283  meshTools::constrainToMeshCentre(mesh, p.position());
284 
285  const point start(p.position());
286 
287  // Set the Lagrangian time-step
288  scalar dt = min(dtMax, tEnd);
289 
290  // Cache the parcel current cell as this will change if a face is hit
291  const label celli = p.cell();
292 
293  const scalar magU = mag(U_);
294  if (p.active() && tracking && (magU > ROOTVSMALL))
295  {
296  const scalar d = dt*magU;
297  const scalar dCorr = min(d, maxCo*cellLengthScale[celli]);
298  dt *=
299  dCorr/d
300  *p.trackToFace(p.position() + dCorr*U_/magU, td);
301  }
302 
303  tEnd -= dt;
304 
305  scalar newStepFraction = 1.0 - tEnd/trackTime;
306 
307  if (tracking)
308  {
309  if
310  (
311  mag(p.stepFraction() - newStepFraction)
312  < particle::minStepFractionTol
313  )
314  {
315  nTrackingStalled++;
316 
317  if (nTrackingStalled > maxTrackAttempts)
318  {
319  tracking = false;
320  }
321  }
322  else
323  {
324  nTrackingStalled = 0;
325  }
326  }
327 
328  p.stepFraction() = newStepFraction;
329 
330  bool calcParcel = true;
331  if (!tracking && td.cloud().solution().steadyState())
332  {
333  calcParcel = false;
334  }
335 
336  // Avoid problems with extremely small timesteps
337  if ((dt > ROOTVSMALL) && calcParcel)
338  {
339  // Update cell based properties
340  p.setCellValues(td, dt, celli);
341 
342  if (td.cloud().solution().cellValueSourceCorrection())
343  {
344  p.cellValueSourceCorrection(td, dt, celli);
345  }
346 
347  p.calc(td, dt, celli);
348  }
349 
350  if (p.onBoundary() && td.keepParticle)
351  {
352  if (isA<processorPolyPatch>(pbMesh[p.patch(p.face())]))
353  {
354  td.switchProcessor = true;
355  }
356  }
357 
358  p.age() += dt;
359 
360  td.cloud().functions().postMove(p, celli, dt, start, td.keepParticle);
361  }
362 
363  return td.keepParticle;
364 }
365 
366 
367 template<class ParcelType>
368 template<class TrackData>
370 {
371  typename TrackData::cloudType::parcelType& p =
372  static_cast<typename TrackData::cloudType::parcelType&>(*this);
373 
374  td.cloud().functions().postFace(p, p.face(), td.keepParticle);
375 }
376 
377 
378 template<class ParcelType>
380 {}
381 
382 
383 template<class ParcelType>
384 template<class TrackData>
386 (
387  const polyPatch& pp,
388  TrackData& td,
389  const label patchi,
390  const scalar trackFraction,
391  const tetIndices& tetIs
392 )
393 {
394  typename TrackData::cloudType::parcelType& p =
395  static_cast<typename TrackData::cloudType::parcelType&>(*this);
396 
397  // Invoke post-processing model
398  td.cloud().functions().postPatch
399  (
400  p,
401  pp,
402  trackFraction,
403  tetIs,
404  td.keepParticle
405  );
406 
407  // Invoke surface film model
408  if (td.cloud().surfaceFilm().transferParcel(p, pp, td.keepParticle))
409  {
410  // All interactions done
411  return true;
412  }
413  else
414  {
415  // Invoke patch interaction model
416  return td.cloud().patchInteraction().correct
417  (
418  p,
419  pp,
420  td.keepParticle,
421  trackFraction,
422  tetIs
423  );
424  }
425 }
426 
427 
428 template<class ParcelType>
429 template<class TrackData>
431 (
432  const processorPolyPatch&,
433  TrackData& td
434 )
435 {
436  td.switchProcessor = true;
437 }
438 
439 
440 template<class ParcelType>
441 template<class TrackData>
443 (
444  const wallPolyPatch& wpp,
445  TrackData& td,
446  const tetIndices&
447 )
448 {
449  // Wall interactions handled by generic hitPatch function
450 }
451 
452 
453 template<class ParcelType>
454 template<class TrackData>
456 (
457  const polyPatch&,
458  TrackData& td
459 )
460 {
461  td.keepParticle = false;
462 }
463 
464 
465 template<class ParcelType>
467 {
468  ParcelType::transformProperties(T);
469 
470  U_ = transform(T, U_);
471 }
472 
473 
474 template<class ParcelType>
476 (
477  const vector& separation
478 )
479 {
480  ParcelType::transformProperties(separation);
481 }
482 
483 
484 template<class ParcelType>
486 (
487  const vector&
488 ) const
489 {
490  return 0.5*d_;
491 }
492 
493 
494 // * * * * * * * * * * * * * * IOStream operators * * * * * * * * * * * * * //
495 
496 #include "KinematicParcelIO.C"
497 
498 // ************************************************************************* //
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 hitPatch(const polyPatch &p, TrackData &td, const label patchi, const scalar trackFraction, const tetIndices &tetIs)
Overridable function to handle the particle hitting a patch.
Kinematic parcel class with rotational motion (as spherical particles only) and one/two-way coupling ...
Helper class to supply results of integration.
scalar tTurb_
Time spent in turbulent eddy [s].
scalar age_
Age [s].
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:253
KinematicParcel(const polyMesh &mesh, const vector &position, const label celli, const label tetFacei, const label tetPtI)
Construct from owner, position, and cloud owner.
const vector & Su() const
Return const access to the explicit contribution [kg.m/s2].
Definition: forceSuSpI.H:56
virtual scalar wallImpactDistance(const vector &n) const
The nearest distance to a wall that the particle can be.
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:421
vector Uc_
Velocity [m/s].
Neighbour processor patch.
Type average() const
Return const access to the average.
Helper container for force Su and Sp terms.
Definition: forceSuSp.H:61
bool move(TrackData &td, const scalar trackTime)
Move the parcel.
scalar dTarget_
Target diameter [m].
Type value() const
Return const access to the value.
scalar rho_
Density [kg/m3].
virtual void transformProperties(const tensor &T)
Transform the physical properties of the particle.
scalar muc_
Viscosity [Pa.s].
void hitFace(int &td)
Overridable function to handle the particle hitting a face.
bool active_
Active flag - tracking inactive when active = false.
Foam::wallPolyPatch.
Definition: wallPolyPatch.H:48
label typeId_
Parcel type id.
vector UTurb_
Turbulent velocity fluctuation [m/s].
static const zero Zero
Definition: zero.H:91
Storage and named access for the indices of a tet which is part of the decomposition of a cell...
Definition: tetIndices.H:81
const vector calcVelocity(TrackData &td, const scalar dt, const label celli, const scalar Re, const scalar mu, const scalar mass, const vector &Su, vector &dUTrans, scalar &Spu) const
Calculate new particle velocity.
Foam::polyBoundaryMesh.
scalar maxCo
static const char nl
Definition: Ostream.H:262
scalar nParticle_
Number of particles in Parcel.
void cellValueSourceCorrection(TrackData &td, const scalar dt, const label celli)
Correct cell values using latest transfer information.
dimensioned< Type > min(const dimensioned< Type > &, const dimensioned< Type > &)
const volScalarField & T
void calc(TrackData &td, const scalar dt, const label celli)
Update parcel properties over the time interval.
scalar rhoc_
Density [kg/m3].
vector U_
Velocity of Parcel [m/s].
#define WarningInFunction
Report a warning using Foam::Warning.
void setCellValues(TrackData &td, const scalar dt, const label celli)
Set cell values.
void hitWallPatch(const wallPolyPatch &, TrackData &td, const tetIndices &)
Overridable function to handle the particle hitting a wallPatch.
dimensioned< scalar > mag(const dimensioned< Type > &)
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:74
void constrainDirection(const polyMesh &mesh, const Vector< label > &dirs, vector &d)
Set the constrained components of directions/velocity to zero.
Definition: meshTools.C:671
void hitProcessorPatch(const processorPolyPatch &, TrackData &td)
Overridable function to handle the particle hitting a.
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:66
tmp< GeometricField< Type, fvPatchField, volMesh > > Su(const GeometricField< Type, fvPatchField, volMesh > &su, const GeometricField< Type, fvPatchField, volMesh > &vf)
Definition: fvcSup.C:44
scalarField Re(const UList< complex > &cf)
Definition: complexFields.C:97
void constrainToMeshCentre(const polyMesh &mesh, point &pt)
Set the constrained components of position to mesh centre.
Definition: meshTools.C:613
scalar Sp() const
Return const access to the implicit coefficient [kg/s].
Definition: forceSuSpI.H:62
scalar d_
Diameter [m].
dimensionSet transform(const dimensionSet &)
Definition: dimensionSet.C:465