/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2017-2020 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 "supersonicFreestreamFvPatchVectorField.H"
#include "addToRunTimeSelectionTable.H"
#include "fvPatchFieldMapper.H"
#include "volFields.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::supersonicFreestreamFvPatchVectorField::
supersonicFreestreamFvPatchVectorField
(
const fvPatch& p,
const DimensionedField& iF
)
:
mixedFvPatchVectorField(p, iF),
TName_("T"),
pName_("p"),
psiName_("thermo:psi"),
UInf_(Zero),
pInf_(0),
TInf_(0),
gamma_(0)
{
refValue() = patchInternalField();
refGrad() = Zero;
valueFraction() = 1;
}
Foam::supersonicFreestreamFvPatchVectorField::
supersonicFreestreamFvPatchVectorField
(
const fvPatch& p,
const DimensionedField& iF,
const dictionary& dict
)
:
mixedFvPatchVectorField(p, iF),
TName_(dict.getOrDefault("T", "T")),
pName_(dict.getOrDefault("p", "p")),
psiName_(dict.getOrDefault("psi", "thermo:psi")),
UInf_(dict.lookup("UInf")),
pInf_(dict.get("pInf")),
TInf_(dict.get("TInf")),
gamma_(dict.get("gamma"))
{
fvPatchFieldBase::readDict(dict);
if (!this->readValueEntry(dict))
{
this->extrapolateInternal();
}
refValue() = *this;
refGrad() = Zero;
valueFraction() = 1;
if (pInf_ < SMALL)
{
FatalIOErrorInFunction(dict)
<< " unphysical pInf specified (pInf <= 0.0)"
<< "\n on patch " << this->patch().name()
<< " of field " << this->internalField().name()
<< " in file " << this->internalField().objectPath()
<< exit(FatalIOError);
}
}
Foam::supersonicFreestreamFvPatchVectorField::
supersonicFreestreamFvPatchVectorField
(
const supersonicFreestreamFvPatchVectorField& ptf,
const fvPatch& p,
const DimensionedField& iF,
const fvPatchFieldMapper& mapper
)
:
mixedFvPatchVectorField(ptf, p, iF, mapper),
TName_(ptf.TName_),
pName_(ptf.pName_),
psiName_(ptf.psiName_),
UInf_(ptf.UInf_),
pInf_(ptf.pInf_),
TInf_(ptf.TInf_),
gamma_(ptf.gamma_)
{}
Foam::supersonicFreestreamFvPatchVectorField::
supersonicFreestreamFvPatchVectorField
(
const supersonicFreestreamFvPatchVectorField& sfspvf
)
:
mixedFvPatchVectorField(sfspvf),
TName_(sfspvf.TName_),
pName_(sfspvf.pName_),
psiName_(sfspvf.psiName_),
UInf_(sfspvf.UInf_),
pInf_(sfspvf.pInf_),
TInf_(sfspvf.TInf_),
gamma_(sfspvf.gamma_)
{}
Foam::supersonicFreestreamFvPatchVectorField::
supersonicFreestreamFvPatchVectorField
(
const supersonicFreestreamFvPatchVectorField& sfspvf,
const DimensionedField& iF
)
:
mixedFvPatchVectorField(sfspvf, iF),
TName_(sfspvf.TName_),
pName_(sfspvf.pName_),
psiName_(sfspvf.psiName_),
UInf_(sfspvf.UInf_),
pInf_(sfspvf.pInf_),
TInf_(sfspvf.TInf_),
gamma_(sfspvf.gamma_)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::supersonicFreestreamFvPatchVectorField::updateCoeffs()
{
if (!size() || updated())
{
return;
}
const auto& pT = patch().lookupPatchField(TName_);
const auto& pp = patch().lookupPatchField(pName_);
const auto& ppsi = patch().lookupPatchField(psiName_);
// Need R of the free-stream flow. Assume R is independent of location
// along patch so use face 0
scalar R = 1.0/(ppsi[0]*pT[0]);
scalar MachInf = mag(UInf_)/sqrt(gamma_*R*TInf_);
if (MachInf < 1.0)
{
FatalErrorInFunction
<< "\n on patch " << this->patch().name()
<< " of field " << this->internalField().name()
<< " in file " << this->internalField().objectPath()
<< exit(FatalError);
}
vectorField& Up = refValue();
valueFraction() = 1;
// get the near patch internal cell values
const vectorField U(patchInternalField());
// Find the component of U normal to the free-stream flow and in the
// plane of the free-stream flow and the patch normal
// Direction of the free-stream flow
vector UInfHat = UInf_/mag(UInf_);
// Normal to the plane defined by the free-stream and the patch normal
tmp nnInfHat = UInfHat ^ patch().nf();
// Normal to the free-stream in the plane defined by the free-stream
// and the patch normal
const vectorField nHatInf(nnInfHat ^ UInfHat);
// Component of U normal to the free-stream in the plane defined by the
// free-stream and the patch normal
const vectorField Un(nHatInf*(nHatInf & U));
// The tangential component is
const vectorField Ut(U - Un);
// Calculate the Prandtl-Meyer function of the free-stream
scalar nuMachInf =
sqrt((gamma_ + 1)/(gamma_ - 1))
*atan(sqrt((gamma_ - 1)/(gamma_ + 1)*(sqr(MachInf) - 1)))
- atan(sqr(MachInf) - 1);
// Set the patch boundary condition based on the Mach number and direction
// of the flow dictated by the boundary/free-stream pressure difference
forAll(Up, facei)
{
if (pp[facei] >= pInf_) // If outflow
{
// Assume supersonic outflow and calculate the boundary velocity
// according to ???
scalar fpp =
sqrt(sqr(MachInf) - 1)
/(gamma_*sqr(MachInf))*mag(Ut[facei])*log(pp[facei]/pInf_);
Up[facei] = Ut[facei] + fpp*nHatInf[facei];
// Calculate the Mach number of the boundary velocity
scalar Mach = mag(Up[facei])/sqrt(gamma_/ppsi[facei]);
if (Mach <= 1) // If subsonic
{
// Zero-gradient subsonic outflow
Up[facei] = U[facei];
valueFraction()[facei] = 0;
}
}
else // if inflow
{
// Calculate the Mach number of the boundary velocity
// from the boundary pressure assuming constant total pressure
// expansion from the free-stream
scalar Mach =
sqrt
(
(2/(gamma_ - 1))*(1 + ((gamma_ - 1)/2)*sqr(MachInf))
*pow(pp[facei]/pInf_, (1 - gamma_)/gamma_)
- 2/(gamma_ - 1)
);
if (Mach > 1) // If supersonic
{
// Supersonic inflow is assumed to occur according to the
// Prandtl-Meyer expansion process
scalar nuMachf =
sqrt((gamma_ + 1)/(gamma_ - 1))
*atan(sqrt((gamma_ - 1)/(gamma_ + 1)*(sqr(Mach) - 1)))
- atan(sqr(Mach) - 1);
scalar fpp = (nuMachInf - nuMachf)*mag(Ut[facei]);
Up[facei] = Ut[facei] + fpp*nHatInf[facei];
}
else // If subsonic
{
FatalErrorInFunction
<< "unphysical subsonic inflow has been generated"
<< "\n on patch " << this->patch().name()
<< " of field " << this->internalField().name()
<< " in file "
<< this->internalField().objectPath()
<< exit(FatalError);
}
}
}
mixedFvPatchVectorField::updateCoeffs();
}
void Foam::supersonicFreestreamFvPatchVectorField::write(Ostream& os) const
{
fvPatchField::write(os);
os.writeEntryIfDifferent("T", "T", TName_);
os.writeEntryIfDifferent("p", "p", pName_);
os.writeEntryIfDifferent("psi", "thermo:psi", psiName_);
os.writeEntry("UInf", UInf_);
os.writeEntry("pInf", pInf_);
os.writeEntry("TInf", TInf_);
os.writeEntry("gamma", gamma_);
fvPatchField::writeValueEntry(os);
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
makePatchTypeField
(
fvPatchVectorField,
supersonicFreestreamFvPatchVectorField
);
}
// ************************************************************************* //