Files
openfoam/src/lagrangian/intermediate/submodels/Kinematic/InjectionModel/PatchInjection/patchInjectionBase.C
Andrew Heather 0260643c67 ENH: PatchInjectionModel - added new parcel initial velocity options
The parcel initial velocity can now be set using the new `velocityType`
entry, taking one of the following options:

- fixedValue   : (default) same as earlier versions, requires U0
- patchValue   : velocity set to seed patch face value
- zeroGradient : velocity set to seed patch face adjacent cell value

Example usage:

        model1
        {
            type            patchInjection;
            massTotal       1;
            SOI             0;
            parcelBasisType mass;
            patch           cylinder;
            duration        10;
            parcelsPerSecond 100;
            velocityType    patchValue;
            //velocityType    zeroGradient;
            //U0              (-10 0 0);
            flowRateProfile constant 1;
            sizeDistribution
            {
                type        normal;
                normalDistribution
                {
                    expectation 1e-3;
                    variance 1e-4;
                    minValue 1e-5;
                    maxValue 2e-3;
                }
            }
        }

See the new $FOAM_TUTORIALS/lagrangian/kinematicParcelFoam/spinningDisk tutorial
2021-12-15 10:46:34 +00:00

314 lines
8.8 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2013-2017 OpenFOAM Foundation
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "patchInjectionBase.H"
#include "polyMesh.H"
#include "SubField.H"
#include "Random.H"
#include "triPointRef.H"
#include "volFields.H"
#include "polyMeshTetDecomposition.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::patchInjectionBase::patchInjectionBase
(
const polyMesh& mesh,
const word& patchName
)
:
patchName_(patchName),
patchId_(mesh.boundaryMesh().findPatchID(patchName_)),
patchArea_(0.0),
patchNormal_(),
cellOwners_(),
triFace_(),
triToFace_(),
triCumulativeMagSf_(),
sumTriMagSf_(Pstream::nProcs() + 1, Zero)
{
if (patchId_ < 0)
{
FatalErrorInFunction
<< "Requested patch " << patchName_ << " not found" << nl
<< "Available patches are: " << mesh.boundaryMesh().names() << nl
<< exit(FatalError);
}
updateMesh(mesh);
}
Foam::patchInjectionBase::patchInjectionBase(const patchInjectionBase& pib)
:
patchName_(pib.patchName_),
patchId_(pib.patchId_),
patchArea_(pib.patchArea_),
patchNormal_(pib.patchNormal_),
cellOwners_(pib.cellOwners_),
triFace_(pib.triFace_),
triToFace_(pib.triToFace_),
triCumulativeMagSf_(pib.triCumulativeMagSf_),
sumTriMagSf_(pib.sumTriMagSf_)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::patchInjectionBase::updateMesh(const polyMesh& mesh)
{
// Set/cache the injector cells
const polyPatch& patch = mesh.boundaryMesh()[patchId_];
const pointField& points = patch.points();
cellOwners_ = patch.faceCells();
// Triangulate the patch faces and create addressing
DynamicList<label> triToFace(2*patch.size());
DynamicList<scalar> triMagSf(2*patch.size());
DynamicList<face> triFace(2*patch.size());
DynamicList<face> tris(5);
// Set zero value at the start of the tri area list
triMagSf.append(0.0);
forAll(patch, facei)
{
const face& f = patch[facei];
tris.clear();
f.triangles(points, tris);
forAll(tris, i)
{
triToFace.append(facei);
triFace.append(tris[i]);
triMagSf.append(tris[i].mag(points));
}
}
forAll(sumTriMagSf_, i)
{
sumTriMagSf_[i] = 0.0;
}
sumTriMagSf_[Pstream::myProcNo() + 1] = sum(triMagSf);
Pstream::listCombineGather(sumTriMagSf_, maxEqOp<scalar>());
Pstream::listCombineScatter(sumTriMagSf_);
for (label i = 1; i < triMagSf.size(); i++)
{
triMagSf[i] += triMagSf[i-1];
}
// Transfer to persistent storage
triFace_.transfer(triFace);
triToFace_.transfer(triToFace);
triCumulativeMagSf_.transfer(triMagSf);
// Convert sumTriMagSf_ into cumulative sum of areas per proc
for (label i = 1; i < sumTriMagSf_.size(); i++)
{
sumTriMagSf_[i] += sumTriMagSf_[i-1];
}
const scalarField magSf(mag(patch.faceAreas()));
patchArea_ = sum(magSf);
patchNormal_ = patch.faceAreas()/magSf;
reduce(patchArea_, sumOp<scalar>());
}
Foam::label Foam::patchInjectionBase::setPositionAndCell
(
const fvMesh& mesh,
const scalar fraction01,
Random& rnd,
vector& position,
label& cellOwner,
label& tetFacei,
label& tetPti
)
{
label facei = -1;
if (cellOwners_.size() > 0)
{
// Determine which processor to inject from
const label proci = whichProc(fraction01);
if (Pstream::myProcNo() == proci)
{
const scalar areaFraction = fraction01*patchArea_;
// Find corresponding decomposed face triangle
label trii = 0;
scalar offset = sumTriMagSf_[proci];
forAllReverse(triCumulativeMagSf_, i)
{
if (areaFraction > triCumulativeMagSf_[i] + offset)
{
trii = i;
break;
}
}
// Set cellOwner
facei = triToFace_[trii];
cellOwner = cellOwners_[facei];
// Find random point in triangle
const polyPatch& patch = mesh.boundaryMesh()[patchId_];
const pointField& points = patch.points();
const face& tf = triFace_[trii];
const triPointRef tri(points[tf[0]], points[tf[1]], points[tf[2]]);
const point pf(tri.randomPoint(rnd));
// Position perturbed away from face (into domain)
const scalar a = rnd.position(scalar(0.1), scalar(0.5));
const vector& pc = mesh.cellCentres()[cellOwner];
const vector d =
mag((pf - pc) & patchNormal_[facei])*patchNormal_[facei];
position = pf - a*d;
// Try to find tetFacei and tetPti in the current position
mesh.findTetFacePt(cellOwner, position, tetFacei, tetPti);
// tetFacei and tetPti not found, check if the cell has changed
if (tetFacei == -1 ||tetPti == -1)
{
mesh.findCellFacePt(position, cellOwner, tetFacei, tetPti);
}
// Both searches failed, choose a random position within
// the original cell
if (tetFacei == -1 ||tetPti == -1)
{
// Reset cellOwner
cellOwner = cellOwners_[facei];
const scalarField& V = mesh.V();
// Construct cell tet indices
const List<tetIndices> cellTetIs =
polyMeshTetDecomposition::cellTetIndices(mesh, cellOwner);
// Construct cell tet volume fractions
scalarList cTetVFrac(cellTetIs.size(), Zero);
for (label teti=1; teti<cellTetIs.size()-1; teti++)
{
cTetVFrac[teti] =
cTetVFrac[teti-1]
+ cellTetIs[teti].tet(mesh).mag()/V[cellOwner];
}
cTetVFrac.last() = 1;
// Set new particle position
const scalar volFrac = rnd.sample01<scalar>();
label teti = 0;
forAll(cTetVFrac, vfI)
{
if (cTetVFrac[vfI] > volFrac)
{
teti = vfI;
break;
}
}
position = cellTetIs[teti].tet(mesh).randomPoint(rnd);
tetFacei = cellTetIs[teti].face();
tetPti = cellTetIs[teti].tetPt();
}
}
else
{
cellOwner = -1;
tetFacei = -1;
tetPti = -1;
// Dummy position
position = pTraits<vector>::max;
}
}
else
{
cellOwner = -1;
tetFacei = -1;
tetPti = -1;
// Dummy position
position = pTraits<vector>::max;
}
return facei;
}
Foam::label Foam::patchInjectionBase::setPositionAndCell
(
const fvMesh& mesh,
Random& rnd,
vector& position,
label& cellOwner,
label& tetFacei,
label& tetPti
)
{
scalar fraction01 = rnd.globalSample01<scalar>();
return setPositionAndCell
(
mesh,
fraction01,
rnd,
position,
cellOwner,
tetFacei,
tetPti
);
}
Foam::label Foam::patchInjectionBase::whichProc(const scalar fraction01) const
{
const scalar areaFraction = fraction01*patchArea_;
// Determine which processor to inject from
forAllReverse(sumTriMagSf_, i)
{
if (areaFraction >= sumTriMagSf_[i])
{
return i;
}
}
return 0;
}
// ************************************************************************* //