Loading...
Searching...
No Matches
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 | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2011-2017 OpenFOAM Foundation
9 Copyright (C) 2020 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "KinematicParcel.H"
30#include "forceSuSp.H"
31#include "integrationScheme.H"
32#include "meshTools.H"
33#include "cloudSolution.H"
34
35// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
36
37template<class ParcelType>
38Foam::label Foam::KinematicParcel<ParcelType>::maxTrackAttempts = 1;
39
40
41// * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * //
42
43template<class ParcelType>
44template<class TrackCloudType>
46(
47 TrackCloudType& cloud,
49)
50{
51 tetIndices tetIs = this->currentTetIndices();
52
53 td.rhoc() = td.rhoInterp().interpolate(this->coordinates(), tetIs);
54
55 if (td.rhoc() < cloud.constProps().rhoMin())
56 {
57 if (debug)
58 {
60 << "Limiting observed density in cell " << this->cell()
61 << " to " << cloud.constProps().rhoMin() << nl << endl;
62 }
63
64 td.rhoc() = cloud.constProps().rhoMin();
65 }
66
67 td.Uc() = td.UInterp().interpolate(this->coordinates(), tetIs);
69 td.muc() = td.muInterp().interpolate(this->coordinates(), tetIs);
70}
71
72
73template<class ParcelType>
74template<class TrackCloudType>
76(
77 TrackCloudType& cloud,
78 trackingData& td,
79 const scalar dt
80)
81{
82 td.Uc() = cloud.dispersion().update
83 (
84 dt,
85 this->cell(),
86 U_,
87 td.Uc(),
88 UTurb_,
90 );
91}
92
93
94template<class ParcelType>
95template<class TrackCloudType>
97(
98 TrackCloudType& cloud,
99 trackingData& td,
100 const scalar dt
101)
102{
103 typename TrackCloudType::parcelType& p =
104 static_cast<typename TrackCloudType::parcelType&>(*this);
105
106 this->UCorrect_ = Zero;
107
108 this->UCorrect_ =
109 cloud.dampingModel().velocityCorrection(p, dt);
110
111 this->UCorrect_ +=
112 cloud.packingModel().velocityCorrection(p, dt);
113}
114
115
116template<class ParcelType>
117template<class TrackCloudType>
119(
120 TrackCloudType& cloud,
121 trackingData& td,
122 const scalar dt
123)
125 td.Uc() += cloud.UTrans()[this->cell()]/massCell(td);
126}
127
128
129template<class ParcelType>
130template<class TrackCloudType>
132(
133 TrackCloudType& cloud,
134 trackingData& td,
135 const scalar dt
136)
137{
138 // Define local properties at beginning of time step
139 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
140 const scalar np0 = nParticle_;
141 const scalar mass0 = mass();
142
143 // Reynolds number
144 const scalar Re = this->Re(td);
145
146
147 // Sources
148 //~~~~~~~~
149
150 // Explicit momentum source for particle
151 vector Su = Zero;
152
153 // Linearised momentum source coefficient
154 scalar Spu = 0.0;
155
156 // Momentum transfer from the particle to the carrier phase
157 vector dUTrans = Zero;
158
159
160 // Motion
161 // ~~~~~~
162
163 // Calculate new particle velocity
164 this->U_ =
165 calcVelocity(cloud, td, dt, Re, td.muc(), mass0, Su, dUTrans, Spu);
166
167 this->U_ += this->UCorrect_;
168
169 // Accumulate carrier phase source terms
170 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
171 if (cloud.solution().coupled())
172 {
173 // Update momentum transfer
174 cloud.UTrans()[this->cell()] += np0*dUTrans;
175
176 // Update momentum transfer coefficient
177 cloud.UCoeff()[this->cell()] += np0*Spu;
178 }
179}
180
181
182template<class ParcelType>
183template<class TrackCloudType>
185(
186 TrackCloudType& cloud,
187 trackingData& td,
188 const scalar dt,
189 const scalar Re,
190 const scalar mu,
191 const scalar mass,
192 const vector& Su,
193 vector& dUTrans,
194 scalar& Spu
195) const
196{
197 const typename TrackCloudType::parcelType& p =
198 static_cast<const typename TrackCloudType::parcelType&>(*this);
199 typename TrackCloudType::parcelType::trackingData& ttd =
200 static_cast<typename TrackCloudType::parcelType::trackingData&>(td);
201
202 const typename TrackCloudType::forceType& forces = cloud.forces();
203
204 // Momentum source due to particle forces
205 const forceSuSp Fcp = forces.calcCoupled(p, ttd, dt, mass, Re, mu);
206 const forceSuSp Fncp = forces.calcNonCoupled(p, ttd, dt, mass, Re, mu);
207 const scalar massEff = forces.massEff(p, ttd, mass);
208
209 /*
210 // Proper splitting ...
211 // Calculate the integration coefficients
212 const vector acp = (Fcp.Sp()*td.Uc() + Fcp.Su())/massEff;
213 const vector ancp = (Fncp.Sp()*td.Uc() + Fncp.Su() + Su)/massEff;
214 const scalar bcp = Fcp.Sp()/massEff;
215 const scalar bncp = Fncp.Sp()/massEff;
216
217 // Integrate to find the new parcel velocity
218 const vector deltaUcp =
219 cloud.UIntegrator().partialDelta
220 (
221 U_, dt, acp + ancp, bcp + bncp, acp, bcp
222 );
223 const vector deltaUncp =
224 cloud.UIntegrator().partialDelta
225 (
226 U_, dt, acp + ancp, bcp + bncp, ancp, bncp
227 );
228 const vector deltaT = deltaUcp + deltaUncp;
229 */
230
231 // Shortcut splitting assuming no implicit non-coupled force ...
232 // Calculate the integration coefficients
233 const vector acp = (Fcp.Sp()*td.Uc() + Fcp.Su())/massEff;
234 const vector ancp = (Fncp.Su() + Su)/massEff;
235 const scalar bcp = Fcp.Sp()/massEff;
236
237 // Integrate to find the new parcel velocity
238 const vector deltaU = cloud.UIntegrator().delta(U_, dt, acp + ancp, bcp);
239 const vector deltaUncp = ancp*dt;
240 const vector deltaUcp = deltaU - deltaUncp;
241
242 // Calculate the new velocity and the momentum transfer terms
243 vector Unew = U_ + deltaU;
244
245 dUTrans -= massEff*deltaUcp;
246
247 Spu = dt*Fcp.Sp();
248
249 // Apply correction to velocity and dUTrans for reduced-D cases
250 const polyMesh& mesh = cloud.pMesh();
251 meshTools::constrainDirection(mesh, mesh.solutionD(), Unew);
252 meshTools::constrainDirection(mesh, mesh.solutionD(), dUTrans);
253
254 return Unew;
255}
256
257
258// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
259
260template<class ParcelType>
262(
263 const KinematicParcel<ParcelType>& p
264)
265:
266 ParcelType(p),
267 active_(p.active_),
268 typeId_(p.typeId_),
269 nParticle_(p.nParticle_),
270 d_(p.d_),
271 dTarget_(p.dTarget_),
272 U_(p.U_),
273 rho_(p.rho_),
274 age_(p.age_),
275 tTurb_(p.tTurb_),
276 UTurb_(p.UTurb_),
277 UCorrect_(p.UCorrect_)
278{}
279
280
281template<class ParcelType>
283(
285 const polyMesh& mesh
286)
287:
288 ParcelType(p, mesh),
289 active_(p.active_),
290 typeId_(p.typeId_),
291 nParticle_(p.nParticle_),
292 d_(p.d_),
293 dTarget_(p.dTarget_),
294 U_(p.U_),
295 rho_(p.rho_),
296 age_(p.age_),
297 tTurb_(p.tTurb_),
298 UTurb_(p.UTurb_),
299 UCorrect_(p.UCorrect_)
301
302
303// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
304
305template<class ParcelType>
306template<class TrackCloudType>
308(
309 TrackCloudType& cloud,
310 trackingData& td,
311 const scalar trackTime
312)
313{
314 auto& p = static_cast<typename TrackCloudType::parcelType&>(*this);
315 auto& ttd =
316 static_cast<typename TrackCloudType::parcelType::trackingData&>(td);
317
318 ttd.switchProcessor = false;
319 ttd.keepParticle = true;
320
321 const cloudSolution& solution = cloud.solution();
322 const scalarField& cellLengthScale = cloud.cellLengthScale();
323 const scalar maxCo = solution.maxCo();
324
325 while (ttd.keepParticle && !ttd.switchProcessor && p.stepFraction() < 1)
326 {
327 // Cache the current position, cell and step-fraction
328 const point start = p.position();
329 const scalar sfrac = p.stepFraction();
330
331 // Total displacement over the time-step
332 const vector s = trackTime*U_;
333
334 // Cell length scale
335 const scalar l = cellLengthScale[p.cell()];
336
337 // Deviation from the mesh centre for reduced-D cases
338 const vector d = p.deviationFromMeshCentre();
339
340 // Fraction of the displacement to track in this loop. This is limited
341 // to ensure that the both the time and distance tracked is less than
342 // maxCo times the total value.
343 scalar f = 1 - p.stepFraction();
344 f = min(f, maxCo);
345 f = min(f, maxCo*l/max(SMALL*l, mag(s)));
346 if (p.active())
347 {
348 // Track to the next face
349 p.trackToFace(f*s - d, f);
350 }
351 else
352 {
353 // At present the only thing that sets active_ to false is a stick
354 // wall interaction. We want the position of the particle to remain
355 // the same relative to the face that it is on. The local
356 // coordinates therefore do not change. We still advance in time and
357 // perform the relevant interactions with the fixed particle.
358 p.stepFraction() += f;
359 }
360
361 const scalar dt = (p.stepFraction() - sfrac)*trackTime;
362
363 // Avoid problems with extremely small timesteps
364 if (dt > ROOTVSMALL)
365 {
366 // Update cell based properties
367 p.setCellValues(cloud, ttd);
368
369 p.calcDispersion(cloud, ttd, dt);
370
371 if (solution.cellValueSourceCorrection())
372 {
373 p.cellValueSourceCorrection(cloud, ttd, dt);
374 }
375
376 p.calcUCorrection(cloud, ttd, dt);
377
378 p.calc(cloud, ttd, dt);
379 }
380
381 p.age() += dt;
382
383 if (p.active() && p.onFace())
384 {
385 ttd.keepParticle = cloud.functions().postFace(p, ttd);
386 }
387
388 ttd.keepParticle = cloud.functions().postMove(p, dt, start, ttd);
389
390 if (p.active() && p.onFace() && ttd.keepParticle)
391 {
392 p.hitFace(s, cloud, ttd);
393 }
394 }
396 return ttd.keepParticle;
397}
398
399
400template<class ParcelType>
401template<class TrackCloudType>
403(
404 TrackCloudType& cloud,
405 trackingData& td
406)
407{
408 auto& p = static_cast<typename TrackCloudType::parcelType&>(*this);
409 auto& ttd =
410 static_cast<typename TrackCloudType::parcelType::trackingData&>(td);
411
412 const polyPatch& pp = p.mesh().boundaryMesh()[p.patch()];
413
414 // Invoke post-processing model
415 td.keepParticle = cloud.functions().postPatch(p, pp, ttd);
416
418 {
419 // Skip processor patches
420 return false;
421 }
422 else if (cloud.surfaceFilm().transferParcel(p, pp, td.keepParticle))
423 {
424 // Surface film model consumes the interaction, i.e. all
425 // interactions done
426 return true;
427 }
428 else
429 {
430 // This does not take into account the wall interaction model
431 // Just the polyPatch type. Then, a patch type which has 'rebound'
432 // interaction model will count as escaped parcel while it is not
434 {
435 cloud.patchInteraction().addToEscapedParcels(nParticle_*mass());
436 }
437
438 // Invoke patch interaction model
439 return cloud.patchInteraction().correct(p, pp, td.keepParticle);
440 }
441}
442
443
444template<class ParcelType>
445template<class TrackCloudType>
447(
448 TrackCloudType&,
449 trackingData& td
450)
452 td.switchProcessor = true;
453}
454
455
456template<class ParcelType>
457template<class TrackCloudType>
459(
460 TrackCloudType&,
461 trackingData&
463{
464 // wall interactions are handled by the generic hitPatch method
465}
466
467
468template<class ParcelType>
470{
471 ParcelType::transformProperties(T);
472
473 U_ = transform(T, U_);
474}
475
476
477template<class ParcelType>
479(
480 const vector& separation
481)
482{
483 ParcelType::transformProperties(separation);
484}
485
486
487// * * * * * * * * * * * * * * IOStream operators * * * * * * * * * * * * * //
488
489#include "KinematicParcelIO.C"
490
491// ************************************************************************* //
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
Kinematic parcel class with rotational motion (as spherical particles only) and one/two-way coupling ...
virtual void transformProperties(const tensor &T)
Transform the physical properties of the particle.
void calcUCorrection(TrackCloudType &cloud, trackingData &td, const scalar dt)
Correct U following MP-PIC sub-models.
vector UTurb_
Turbulent velocity fluctuation [m/s].
void calcDispersion(TrackCloudType &cloud, trackingData &td, const scalar dt)
Apply dispersion to the carrier phase velocity and update.
scalar d() const
Return const access to diameter.
void cellValueSourceCorrection(TrackCloudType &cloud, trackingData &td, const scalar dt)
Correct cell values using latest transfer information.
scalar Re(const trackingData &td) const
Reynolds number.
scalar tTurb_
Time spent in turbulent eddy [s].
const vector calcVelocity(TrackCloudType &cloud, trackingData &td, const scalar dt, const scalar Re, const scalar mu, const scalar mass, const vector &Su, vector &dUTrans, scalar &Spu) const
Calculate new particle velocity.
scalar massCell(const trackingData &td) const
Cell owner mass.
scalar mass() const
Particle mass.
KinematicParcel(const polyMesh &mesh, const barycentric &coordinates, const label celli, const label tetFacei, const label tetPti)
Construct from mesh, coordinates and topology.
void hitProcessorPatch(TrackCloudType &cloud, trackingData &td)
Overridable function to handle the particle hitting a.
bool hitPatch(TrackCloudType &cloud, trackingData &td)
Overridable function to handle the particle hitting a patch.
vector UCorrect_
Velocity correction due to collisions MPPIC [m/s].
void setCellValues(TrackCloudType &cloud, trackingData &td)
Set cell values.
bool move(TrackCloudType &cloud, trackingData &td, const scalar trackTime)
Move the parcel.
void hitWallPatch(TrackCloudType &cloud, trackingData &td)
Overridable function to handle the particle hitting a wallPatch.
vector U_
Velocity of Parcel [m/s].
scalar nParticle_
Number of particles in Parcel.
void calc(TrackCloudType &cloud, trackingData &td, const scalar dt)
Update parcel properties over the time interval.
A cell is defined as a list of faces with extra functionality.
Definition cell.H:56
Stores all relevant solution info for cloud.
A cloud is a registry collection of lagrangian particles.
Definition cloud.H:56
Helper container for force Su and Sp terms.
Definition forceSuSp.H:63
const vector & Su() const
Return const access to the explicit contribution [kg.m/s2].
Definition forceSuSpI.H:54
scalar Sp() const
Return const access to the implicit coefficient [kg/s].
Definition forceSuSpI.H:60
const polyMesh & mesh() const noexcept
Return the mesh database.
Definition particleI.H:110
label cell() const noexcept
Return current cell particle is in.
Definition particleI.H:122
Mesh consisting of general polyhedral cells.
Definition polyMesh.H:79
A patch is a list of labels that address the faces in the global face list.
Definition polyPatch.H:73
static bool constraintType(const word &patchType)
Return true if the given type is a constraint type.
Definition polyPatch.C:255
Selector class for relaxation factors, solver type and solution.
Definition solution.H:95
Storage and named access for the indices of a tet which is part of the decomposition of a cell.
Definition tetIndices.H:79
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
volScalarField & p
dynamicFvMesh & mesh
PtrList< coordinateSystem > coordinates(solidRegions.size())
scalar maxCo
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.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))
zeroField Su
Definition alphaSuSp.H:1
wallPoints::trackData td(isBlockedFace, regionToBlockSize)
#define WarningInFunction
Report a warning using Foam::Warning.
Namespace for handling debugging switches.
Definition debug.C:45
void constrainDirection(const polyMesh &mesh, const Vector< label > &dirs, vector &d)
Set the constrained components of directions/velocity to zero.
Definition meshTools.C:680
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:40
refinementData transform(const tensor &, const refinementData val)
No-op rotational transform for base types.
Tensor< scalar > tensor
Definition symmTensor.H:57
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
const Type * isA(const U &obj)
Attempt dynamic_cast to Type.
Definition typeInfo.H:87
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:26
vector point
Point is a vector.
Definition point.H:37
static constexpr const zero Zero
Global zero (0).
Definition zero.H:127
scalarField Re(const UList< complex > &cmplx)
Extract real component.
Vector< scalar > vector
Definition vector.H:57
void T(FieldField< Field, Type > &f1, const FieldField< Field, Type > &f2)
void Su(fvMatrix< typename Expr::value_type > &m, const Expr &expression)
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
labelList f(nPoints)