/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2015 OpenFOAM Foundation
Copyright (C) 2016-2021 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see .
\*---------------------------------------------------------------------------*/
#include "eddy.H"
#include "mathematicalConstants.H"
using namespace Foam::constant;
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
Foam::label Foam::eddy::Gamma2Values[] = {1, 2, 3, 4, 5, 6, 7, 8};
Foam::UList Foam::eddy::Gamma2(&Gamma2Values[0], 8);
int Foam::eddy::debug = 0;
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
bool Foam::eddy::setScales
(
const scalar sigmaX,
const label gamma2,
const vector& e,
const vector& lambda,
vector& sigma,
vector& alpha
) const
{
// Static array of gamma^2 vs c2 coefficient (PCR:Table 1)
static const scalar gamma2VsC2[8] =
{2, 1.875, 1.737, 1.75, 0.91, 0.825, 0.806, 1.5};
const scalar gamma = Foam::sqrt(scalar(gamma2));
// c2 coefficient retrieved from array
const scalar c2 = gamma2VsC2[gamma2 - 1];
// Length scale in the largest eigenvalue direction
const label d1 = dir1_;
const label d2 = (d1 + 1) % 3;
const label d3 = (d1 + 2) % 3;
sigma[d1] = sigmaX;
// Note: sigma_average = 1/3*(sigma_x + sigma_y + sigma_z)
// Substituting for sigma_y = sigma_x/gamma and sigma_z = sigma_y
// Other length scales equal, as function of major axis length and gamma
sigma[d2] = sigma[d1]/gamma;
sigma[d3] = sigma[d2];
// (PCR:Eq. 13)
const vector sigma2(cmptMultiply(sigma, sigma));
const scalar slos2 = cmptSum(cmptDivide(lambda, sigma2));
bool ok = true;
for (label beta = 0; beta < vector::nComponents; ++beta)
{
const scalar x = slos2 - 2*lambda[beta]/sigma2[beta];
if (x < 0)
{
alpha[beta] = 0;
ok = false;
}
else
{
// (SST:Eq. 23)
alpha[beta] = e[beta]*sqrt(x/(2*c2));
}
}
if (debug > 1)
{
Pout<< "c2:" << c2
<< ", gamma2:" << gamma2
<< ", gamma:" << gamma
<< ", lambda:" << lambda
<< ", sigma2: " << sigma2
<< ", slos2: " << slos2
<< ", sigmaX:" << sigmaX
<< ", sigma:" << sigma
<< ", alpha:" << alpha
<< endl;
}
return ok;
}
// * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * * * //
Foam::eddy::eddy()
:
patchFaceI_(-1),
position0_(Zero),
x_(0),
sigma_(Zero),
alpha_(Zero),
Rpg_(tensor::I),
c1_(-1),
dir1_(0)
{}
Foam::eddy::eddy
(
const label patchFaceI,
const point& position0,
const scalar x,
const scalar sigmaX,
const symmTensor& R,
Random& rndGen
)
:
patchFaceI_(patchFaceI),
position0_(position0),
x_(x),
sigma_(Zero),
alpha_(Zero),
Rpg_(tensor::I),
c1_(-1),
dir1_(0)
{
// Principal stresses - eigenvalues returned in ascending order
const vector lambda(eigenValues(R));
// Eddy rotation from principal-to-global axes
// - given by the 3 eigenvectors of the Reynold stress tensor as rows in
// the result tensor (transposed transformation tensor)
// - returned in ascending eigenvalue order
Rpg_ = eigenVectors(R, lambda).T();
if (debug)
{
// Global->Principal transform = Rgp = Rpg.T()
// Rgp & R & Rgp.T() should have eigenvalues on its diagonal and
// zeros for all other components
Pout<< "Rpg.T() & R & Rpg: " << (Rpg_.T() & R & Rpg_) << endl;
}
// Set the eddy orientation to position of max eigenvalue
// (direction of eddy major axis, sigma_x in reference)
dir1_ = 2;
// Random vector of 1's and -1's
const vector e(epsilon(rndGen));
// Set intensities and length scales
bool found = false;
forAll(Gamma2, i)
{
// Random length scale ratio, gamma = sigmax/sigmay = sigmax/sigmaz
// - using gamma^2 to ease lookup of c2 coefficient
const label gamma2 = Gamma2[i];
if (setScales(sigmaX, gamma2, e, lambda, sigma_, alpha_))
{
found = true;
break;
}
}
// Normalisation coefficient (PCR:Eq. 11)
// Note: sqrt(10*V)/sqrt(nEddy) applied outside when computing uPrime
c1_ = cmptAv(sigma_)/cmptProduct(sigma_)*cmptMin(sigma_);
if (found)
{
// Shuffle the gamma^2 values
rndGen.shuffle(Gamma2);
}
else
{
if (debug)
{
// If not found typically means that the stress has a repeated
// eigenvalue/not covered by the selection of Gamma values, e.g.
// as seen by range of applicability on Lumley diagram
WarningInFunction
<< "Unable to set eddy intensity for eddy: " << *this
<< endl;
}
// Remove the influence of this eddy/indicate that its initialisation
// failed
patchFaceI_ = -1;
}
}
Foam::eddy::eddy(const eddy& e)
:
patchFaceI_(e.patchFaceI_),
position0_(e.position0_),
x_(e.x_),
sigma_(e.sigma_),
alpha_(e.alpha_),
Rpg_(e.Rpg_),
c1_(e.c1_),
dir1_(e.dir1_)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::vector Foam::eddy::uPrime(const point& xp, const vector& n) const
{
// Relative position inside eddy (global system) (PCR:p. 524)
const vector r(cmptDivide(xp - position(n), sigma_));
if (mag(r) >= scalar(1))
{
return vector::zero;
}
// Relative position inside eddy (eddy principal system)
const vector rp(Rpg_.T() & r);
// Shape function (eddy principal system)
const vector q(cmptMultiply(sigma_, vector::one - cmptMultiply(rp, rp)));
// Fluctuating velocity (eddy principal system) (PCR:Eq. 8)
const vector uPrimep(cmptMultiply(q, rp^alpha_));
// Convert into global system (PCR:Eq. 10)
return c1_*(Rpg_ & uPrimep);
}
void Foam::eddy::writeCentreOBJ
(
const vector& n,
Ostream& os
) const
{
const point p(position(n));
os << "v " << p.x() << " " << p.y() << " " << p.z() << nl;
}
Foam::label Foam::eddy::writeSurfaceOBJ
(
const label pointOffset,
const vector& n,
Ostream& os
) const
{
if (patchFaceI_ < 0)
{
// Invalid eddy
return 0;
}
static const label nFaceAxis = 20;
static const label nFaceTheta = 22;
static const label nEddyPoints = (nFaceAxis - 1)*nFaceTheta + 2;
static FixedList x;
static scalar dTheta = mathematical::twoPi/nFaceTheta;
static scalar dPhi = mathematical::pi/scalar(nFaceAxis);
label pointI = pointOffset;
const vector& s = sigma_;
// Unit vector
vector axisDir(Zero);
axisDir[dir1_] = 1;
const label dir2 = (dir1_ + 1) % 3;
const label dir3 = (dir1_ + 2) % 3;
// Calculate the point positions
x[0] = axisDir*s[dir1_];
x[nEddyPoints - 1] = - axisDir*s[dir1_];
label eddyPtI = 1;
for (label axisI = 1; axisI < nFaceAxis; ++axisI)
{
const scalar z = s[dir1_]*cos(axisI*dPhi);
const scalar r = sqrt(sqr(s[dir2])*(1 - sqr(z)/sqr(s[dir1_])));
for (label thetaI = 0; thetaI < nFaceTheta; ++thetaI)
{
const scalar theta = thetaI*dTheta;
point& p = x[eddyPtI++];
p[dir1_] = z;
p[dir2] = r*sin(theta);
p[dir3] = r*cos(theta);
}
}
// Write points
forAll(x, i)
{
const point p = position(n) + (Rpg_ & x[i]);
os << "v " << p.x() << " " << p.y() << " " << p.z() << nl;
}
// Write the end cap tri faces
for (label faceI = 0; faceI < nFaceTheta; ++faceI)
{
const label p1 = pointI + 1;
const label p2 = p1 + faceI + 1;
label p3 = p2 + 1;
if (faceI == nFaceTheta - 1) p3 -= nFaceTheta;
os << "f " << p1 << " " << p2 << " " << p3 << nl;
const label q1 = pointI + nEddyPoints;
const label q2 = q1 - faceI - 1;
label q3 = q2 - 1;
if (faceI == nFaceTheta - 1) q3 += nFaceTheta;
os << "f " << q1 << " " << q2 << " " << q3 << nl;
}
// Write quad faces
for (label axisI = 1; axisI < nFaceAxis - 1; ++axisI)
{
for (label thetaI = 0; thetaI < nFaceTheta; ++thetaI)
{
const label p1 = pointI + 1 + (axisI - 1)*nFaceTheta + thetaI + 1;
const label p2 = p1 + nFaceTheta;
label p3 = p2 + 1;
label p4 = p1 + 1;
if (thetaI == nFaceTheta - 1)
{
p3 -= nFaceTheta;
p4 -= nFaceTheta;
}
os << "f " << p1 << " " << p2 << " " << p3 << " " << p4 << nl;
}
}
return nEddyPoints;
}
// ************************************************************************* //