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-2017 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->coordinates(), 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->coordinates(), tetIs);
65 
66  muc_ = td.muInterp().interpolate(this->coordinates(), 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  while (td.keepParticle && !td.switchProcessor && p.stepFraction() < 1)
271  {
272  // Apply correction to position for reduced-D cases
273  p.constrainToMeshCentre();
274 
275  // Cache the current position, cell and step-fraction
276  const point start = p.position();
277  const label celli = p.cell();
278  const scalar sfrac = p.stepFraction();
279 
280  // Total displacement over the time-step
281  const vector s = trackTime*U_;
282 
283  // Cell length scale
284  const scalar l = cellLengthScale[p.cell()];
285 
286  // Fraction of the displacement to track in this loop. This is limited
287  // to ensure that the both the time and distance tracked is less than
288  // maxCo times the total value.
289  scalar f = 1 - p.stepFraction();
290  f = min(f, maxCo);
291  f = min(f, maxCo*l/max(SMALL*l, mag(s)));
292  if (p.active())
293  {
294  // Track to the next face
295  p.trackToFace(f*s, f, td);
296  }
297  else
298  {
299  // At present the only thing that sets active_ to false is a stick
300  // wall interaction. We want the position of the particle to remain
301  // the same relative to the face that it is on. The local
302  // coordinates therefore do not change. We still advance in time and
303  // perform the relevant interactions with the fixed particle.
304  p.stepFraction() += f;
305  }
306 
307 
308  const scalar dt = (p.stepFraction() - sfrac)*trackTime;
309 
310  // Avoid problems with extremely small timesteps
311  if (dt > ROOTVSMALL)
312  {
313  // Update cell based properties
314  p.setCellValues(td, dt, celli);
315 
316  if (td.cloud().solution().cellValueSourceCorrection())
317  {
318  p.cellValueSourceCorrection(td, dt, celli);
319  }
320 
321  p.calc(td, dt, celli);
322  }
323 
324  if (p.onBoundaryFace() && td.keepParticle)
325  {
326  if (isA<processorPolyPatch>(pbMesh[p.patch()]))
327  {
328  td.switchProcessor = true;
329  }
330  }
331 
332  p.age() += dt;
333 
334  td.cloud().functions().postMove(p, celli, dt, start, td.keepParticle);
335  }
336 
337  return td.keepParticle;
338 }
339 
340 
341 template<class ParcelType>
342 template<class TrackData>
344 {
345  typename TrackData::cloudType::parcelType& p =
346  static_cast<typename TrackData::cloudType::parcelType&>(*this);
347 
348  td.cloud().functions().postFace(p, p.face(), td.keepParticle);
349 }
350 
351 
352 template<class ParcelType>
354 {}
355 
356 
357 template<class ParcelType>
358 template<class TrackData>
360 (
361  const polyPatch& pp,
362  TrackData& td,
363  const label patchi,
364  const scalar trackFraction,
365  const tetIndices& tetIs
366 )
367 {
368  typename TrackData::cloudType::parcelType& p =
369  static_cast<typename TrackData::cloudType::parcelType&>(*this);
370 
371  // Invoke post-processing model
372  td.cloud().functions().postPatch
373  (
374  p,
375  pp,
376  trackFraction,
377  tetIs,
378  td.keepParticle
379  );
380 
381  // Invoke surface film model
382  if (td.cloud().surfaceFilm().transferParcel(p, pp, td.keepParticle))
383  {
384  // All interactions done
385  return true;
386  }
387  else if (pp.coupled())
388  {
389  // Don't apply the patchInteraction models to coupled boundaries
390  return false;
391  }
392  else
393  {
394  // Invoke patch interaction model
395  return td.cloud().patchInteraction().correct
396  (
397  p,
398  pp,
399  td.keepParticle,
400  trackFraction,
401  tetIs
402  );
403  }
404 }
405 
406 
407 template<class ParcelType>
408 template<class TrackData>
410 (
411  const processorPolyPatch&,
412  TrackData& td
413 )
414 {
415  td.switchProcessor = true;
416 }
417 
418 
419 template<class ParcelType>
420 template<class TrackData>
422 (
423  const wallPolyPatch& wpp,
424  TrackData& td,
425  const tetIndices&
426 )
427 {
428  // Wall interactions handled by generic hitPatch function
429 }
430 
431 
432 template<class ParcelType>
433 template<class TrackData>
435 (
436  const polyPatch&,
437  TrackData& td
438 )
439 {
440  td.keepParticle = false;
441 }
442 
443 
444 template<class ParcelType>
446 {
447  ParcelType::transformProperties(T);
448 
449  U_ = transform(T, U_);
450 }
451 
452 
453 template<class ParcelType>
455 (
456  const vector& separation
457 )
458 {
459  ParcelType::transformProperties(separation);
460 }
461 
462 
463 template<class ParcelType>
465 (
466  const vector&
467 ) const
468 {
469  return 0.5*d_;
470 }
471 
472 
473 // * * * * * * * * * * * * * * IOStream operators * * * * * * * * * * * * * //
474 
475 #include "KinematicParcelIO.C"
476 
477 // ************************************************************************* //
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:424
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.
zeroField Su
Definition: alphaSuSp.H:1
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.
Kinematic parcel class with rotational motion (as spherical particles only) and one/two-way coupling ...
dimensioned< Type > max(const dimensioned< Type > &, const dimensioned< Type > &)
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 barycentric &coordinates, const label celli, const label tetFacei, const label tetPti)
Construct from mesh, coordinates and topology.
vector Uc_
Velocity [m/s].
Neighbour processor patch.
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].
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().x()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().y()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
virtual bool coupled() const
Return true if this patch is geometrically coupled (i.e. faces and.
Definition: polyPatch.H:310
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
Type value() const
Return const access to the value.
scalar Sp() const
Return const access to the implicit coefficient [kg/s].
Definition: forceSuSpI.H:62
Foam::polyBoundaryMesh.
scalar maxCo
static const char nl
Definition: Ostream.H:262
scalar nParticle_
Number of particles in Parcel.
labelList f(nPoints)
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].
PtrList< coordinateSystem > coordinates(solidRegions.size())
#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
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.
scalarField Re(const UList< complex > &cf)
Definition: complexFields.C:97
scalar d_
Diameter [m].
Type average() const
Return const access to the average.
dimensionSet transform(const dimensionSet &)
Definition: dimensionSet.C:477