/*---------------------------------------------------------------------------*\
========= |
\\ / 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-2024 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 "turbulentDFSEMInletFvPatchVectorField.H"
#include "addToRunTimeSelectionTable.H"
#include "momentOfInertia.H"
#include "OFstream.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
Foam::label Foam::turbulentDFSEMInletFvPatchVectorField::seedIterMax_ = 1000;
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::turbulentDFSEMInletFvPatchVectorField::writeEddyOBJ() const
{
{
// Output the bounding box
OFstream os(db().time().path()/"eddyBox.obj");
const polyPatch& pp = this->patch().patch();
const labelList& boundaryPoints = pp.boundaryPoints();
const pointField& localPoints = pp.localPoints();
const vector offset(patchNormal_*maxSigmaX_);
forAll(boundaryPoints, i)
{
point p = localPoints[boundaryPoints[i]];
p += offset;
os << "v " << p.x() << " " << p.y() << " " << p.z() << nl;
}
forAll(boundaryPoints, i)
{
point p = localPoints[boundaryPoints[i]];
p -= offset;
os << "v " << p.x() << " " << p.y() << " " << p.z() << nl;
}
}
{
const Time& time = db().time();
OFstream os
(
time.path()/"eddies_" + Foam::name(time.timeIndex()) + ".obj"
);
label pointOffset = 0;
forAll(eddies_, eddyI)
{
const eddy& e = eddies_[eddyI];
pointOffset += e.writeSurfaceOBJ(pointOffset, patchNormal_, os);
}
}
}
void Foam::turbulentDFSEMInletFvPatchVectorField::writeLumleyCoeffs() const
{
// Output list of xi vs eta
OFstream os(db().time().path()/"lumley_interpolated.out");
os << "# xi" << token::TAB << "eta" << endl;
const scalar t = db().time().timeOutputValue();
const symmTensorField R(R_->value(t)/sqr(Uref_));
forAll(R, faceI)
{
// Normalised anisotropy tensor
const symmTensor devR(dev(R[faceI]/(tr(R[faceI]))));
// Second tensor invariant
const scalar ii = min(0, invariantII(devR));
// Third tensor invariant
const scalar iii = invariantIII(devR);
// xi, eta
// See Pope - characterization of Reynolds-stress anisotropy
const scalar xi = cbrt(0.5*iii);
const scalar eta = sqrt(-ii/3.0);
os << xi << token::TAB << eta << token::TAB
<< ii << token::TAB << iii << endl;
}
}
void Foam::turbulentDFSEMInletFvPatchVectorField::initialisePatch()
{
const vectorField nf(patch().nf());
// Patch normal points into domain
patchNormal_ = -gAverage(nf);
// Check that patch is planar
const scalar error = max(magSqr(patchNormal_ + nf));
if (error > SMALL)
{
WarningInFunction
<< "Patch " << patch().name() << " is not planar"
<< endl;
}
patchNormal_ /= mag(patchNormal_) + ROOTVSMALL;
const polyPatch& patch = this->patch().patch();
const pointField& points = patch.points();
// Triangulate the patch faces and create addressing
{
label nTris = 0;
for (const face& f : patch)
{
nTris += f.nTriangles();
}
DynamicList dynTriFace(nTris);
DynamicList tris(8); // work array
forAll(patch, facei)
{
const face& f = patch[facei];
tris.clear();
f.triangles(points, tris);
for (const auto& t : tris)
{
dynTriFace.emplace_back(t[0], t[1], t[2], facei);
}
}
// Transfer to persistent storage
triFace_.transfer(dynTriFace);
}
const label myProci = UPstream::myProcNo();
const label numProc = UPstream::nProcs();
// Calculate the cumulative triangle weights
{
triCumulativeMagSf_.resize_nocopy(triFace_.size()+1);
auto iter = triCumulativeMagSf_.begin();
// Set zero value at the start of the tri area/weight list
scalar patchArea = 0;
*iter++ = patchArea;
// Calculate cumulative and total area
for (const auto& t : triFace_)
{
patchArea += t.mag(points);
*iter++ = patchArea;
}
sumTriMagSf_.resize_nocopy(numProc+1);
sumTriMagSf_[0] = 0;
{
scalarList::subList slice(sumTriMagSf_, numProc, 1);
slice[myProci] = patchArea;
Pstream::allGatherList(slice);
}
// Convert to cumulative sum of areas per proc
for (label i = 1; i < sumTriMagSf_.size(); ++i)
{
sumTriMagSf_[i] += sumTriMagSf_[i-1];
}
}
// Global patch area (over all processors)
patchArea_ = sumTriMagSf_.back();
// Local patch bounds (this processor)
patchBounds_ = boundBox(patch.localPoints(), false);
patchBounds_.inflate(0.1);
// Determine if all eddies spawned from a single processor
singleProc_ = returnReduceOr
(
patch.size() == returnReduce(patch.size(), sumOp