1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
5 \\ / A nd | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation, either version 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
26 #include "FreeStream.H"
28 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
30 template <class CloudType>
31 Foam::FreeStream<CloudType>::FreeStream
33 const dictionary& dict,
37 InflowBoundaryModel<CloudType>(dict, cloud, typeName),
41 particleFluxAccumulators_()
43 // Identify which patches to use
45 DynamicList<label> patches;
47 forAll(cloud.mesh().boundaryMesh(), p)
49 const polyPatch& patch = cloud.mesh().boundaryMesh()[p];
51 if (isType<polyPatch>(patch))
57 patches_.transfer(patches);
59 const dictionary& numberDensitiesDict
61 this->coeffDict().subDict("numberDensities")
64 List<word> molecules(numberDensitiesDict.toc());
66 // Initialise the particleFluxAccumulators_
67 particleFluxAccumulators_.setSize(patches_.size());
71 const polyPatch& patch = cloud.mesh().boundaryMesh()[patches_[p]];
73 particleFluxAccumulators_[p] = List<Field<scalar> >
76 Field<scalar>(patch.size(), 0.0)
80 moleculeTypeIds_.setSize(molecules.size());
82 numberDensities_.setSize(molecules.size());
86 numberDensities_[i] = readScalar
88 numberDensitiesDict.lookup(molecules[i])
91 moleculeTypeIds_[i] = findIndex(cloud.typeIdList(), molecules[i]);
93 if (moleculeTypeIds_[i] == -1)
97 "Foam::FreeStream<CloudType>::FreeStream"
102 ) << "typeId " << molecules[i] << "not defined in cloud." << nl
103 << abort(FatalError);
107 numberDensities_ /= cloud.nParticle();
112 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
114 template <class CloudType>
115 Foam::FreeStream<CloudType>::~FreeStream()
119 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
121 template <class CloudType>
122 void Foam::FreeStream<CloudType>::inflow()
124 CloudType& cloud(this->owner());
126 const polyMesh& mesh(cloud.mesh());
128 const scalar deltaT = mesh.time().deltaTValue();
130 Random& rndGen(cloud.rndGen());
132 scalar sqrtPi = sqrt(mathematicalConstant::pi);
134 label particlesInserted = 0;
136 const volScalarField::GeometricBoundaryField& boundaryT
138 cloud.boundaryT().boundaryField()
141 const volVectorField::GeometricBoundaryField& boundaryU
143 cloud.boundaryU().boundaryField()
148 label patchI = patches_[p];
150 const polyPatch& patch = mesh.boundaryMesh()[patchI];
152 // Add mass to the accumulators. negative face area dotted with the
153 // velocity to point flux into the domain.
155 // Take a reference to the particleFluxAccumulator for this patch
156 List<Field<scalar> >& pFA = particleFluxAccumulators_[p];
160 label typeId = moleculeTypeIds_[i];
162 scalar mass = cloud.constProps(typeId).mass();
164 if (min(boundaryT[patchI]) < SMALL)
166 FatalErrorIn ("Foam::FreeStream<CloudType>::inflow()")
167 << "Zero boundary temperature detected, "
168 << "check boundaryT condition." << nl
169 << nl << abort(FatalError);
172 scalarField mostProbableSpeed
174 cloud.maxwellianMostProbableSpeed
181 // Dotting boundary velocity with the face unit normal (which points
182 // out of the domain, so it must be negated), dividing by the most
183 // probable speed to form molecularSpeedRatio * cosTheta
185 scalarField sCosTheta =
187 & -patch.faceAreas()/mag(patch.faceAreas())
190 // From Bird eqn 4.22
193 mag(patch.faceAreas())*numberDensities_[i]*deltaT
196 exp(-sqr(sCosTheta)) + sqrtPi*sCosTheta*(1 + erf(sCosTheta))
203 // Loop over all faces as the outer loop to avoid calculating
204 // geometrical properties multiple times.
206 labelList faceVertices = patch[f];
208 label nVertices = faceVertices.size();
210 label globalFaceIndex = f + patch.start();
212 label cell = mesh.faceOwner()[globalFaceIndex];
214 const vector& fC = patch.faceCentres()[f];
216 scalar fA = mag(patch.faceAreas()[f]);
218 // Cumulative triangle area fractions
219 List<scalar> cTriAFracs(nVertices);
221 scalar previousCummulativeSum = 0.0;
223 for (label v = 0; v < nVertices - 1; v++)
225 const point& vA = mesh.points()[faceVertices[v]];
227 const point& vB = mesh.points()[faceVertices[(v + 1)]];
230 0.5*mag((vA - fC)^(vB - fC))/fA
231 + previousCummulativeSum;
233 previousCummulativeSum = cTriAFracs[v];
236 // Force the last area fraction value to 1.0 to avoid any
237 // rounding/non-flat face errors giving a value < 1.0
238 cTriAFracs[nVertices - 1] = 1.0;
240 // Normal unit vector *negative* so normal is pointing into the
242 vector n = patch.faceAreas()[f];
245 // Wall tangential unit vector. Use the direction between the
246 // face centre and the first vertex in the list
247 vector t1 = fC - (mesh.points()[faceVertices[0]]);
250 // Other tangential unit vector. Rescaling in case face is not
251 // flat and n and t1 aren't perfectly orthogonal
255 scalar faceTemperature = boundaryT[patchI][f];
257 const vector& faceVelocity = boundaryU[patchI][f];
261 scalar& faceAccumulator = pFA[i][f];
263 // Number of whole particles to insert
264 label nI = max(label(faceAccumulator), 0);
266 // Add another particle with a probability proportional to the
267 // remainder of taking the integer part of faceAccumulator
268 if ((faceAccumulator - nI) > rndGen.scalar01())
273 faceAccumulator -= nI;
275 label typeId = moleculeTypeIds_[i];
277 scalar mass = cloud.constProps(typeId).mass();
279 for (label i = 0; i < nI; i++)
281 // Choose a triangle to insert on, based on their relative
284 scalar triSelection = rndGen.scalar01();
289 forAll(cTriAFracs, tri)
293 if (cTriAFracs[tri] >= triSelection)
299 // Randomly distribute the points on the triangle, using the
301 // Generating Random Points in Triangles
303 // from "Graphics Gems", Academic Press, 1990
304 // http://tog.acm.org/GraphicsGems/gems/TriPoints.c
307 const point& B = mesh.points()[faceVertices[sTri]];
309 mesh.points()[faceVertices[(sTri + 1) % nVertices]];
311 scalar s = rndGen.scalar01();
312 scalar t = sqrt(rndGen.scalar01());
314 point p = (1 - t)*A + (1 - s)*t*B + s*t*C;
316 // Velocity generation
318 scalar mostProbableSpeed
320 cloud.maxwellianMostProbableSpeed
327 scalar sCosTheta = (faceVelocity & n)/mostProbableSpeed;
329 // Coefficients required for Bird eqn 12.5
330 scalar uNormProbCoeffA =
331 sCosTheta + sqrt(sqr(sCosTheta) + 2.0);
333 scalar uNormProbCoeffB =
337 + sCosTheta*(sCosTheta - sqrt(sqr(sCosTheta) + 2.0))
340 // Equivalent to the QA value in Bird's DSMC3.FOR
341 scalar randomScaling = 3.0;
345 randomScaling = mag(sCosTheta) + 1;
350 // Normalised candidates for the normal direction velocity
353 scalar uNormalThermal;
355 // Select a velocity using Bird eqn 12.5
359 randomScaling*(2.0*rndGen.scalar01() - 1);
361 uNormal = uNormalThermal + sCosTheta;
369 P = 2.0*uNormal/uNormProbCoeffA
370 *exp(uNormProbCoeffB - sqr(uNormalThermal));
373 } while (P < rndGen.scalar01());
376 sqrt(CloudType::kb*faceTemperature/mass)
378 rndGen.GaussNormal()*t1
379 + rndGen.GaussNormal()*t2
381 + (t1 & faceVelocity)*t1
382 + (t2 & faceVelocity)*t2
383 + mostProbableSpeed*uNormal*n;
385 scalar Ei = cloud.equipartitionInternalEnergy
388 cloud.constProps(typeId).internalDegreesOfFreedom()
406 reduce(particlesInserted, sumOp<label>());
408 Info<< " Particles inserted = "
409 << particlesInserted << endl;
414 // ************************************************************************* //