ENH: outletMappedUniformInlet: add multiple fraction, offset and time delays

- Arbitrary number of outlets can be connected to a single inlet
  - Each inlet can be connected to different and arbitrary
    combination of outlets
- Each outlet-inlet connection has:
  - Optional filtration fraction as a Function1 type
  - Optional offset as a Function1 type (i.e. adding/substracting a substance)
  - Optional time delay (from outlet to inlet) as a Function1 type
- Each inlet has an optional base inlet-field as a PatchFunction1 type
This commit is contained in:
Kutalmis Bercin 2021-12-24 14:24:18 +00:00 committed by Andrew Heather
parent 32d3fabcfe
commit 009f8dd1e8
2 changed files with 419 additions and 92 deletions

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2020 OpenCFD Ltd.
Copyright (C) 2020-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -29,6 +29,7 @@ License
#include "outletMappedUniformInletFvPatchField.H"
#include "volFields.H"
#include "surfaceFields.H"
#include "interpolateXY.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
@ -41,10 +42,15 @@ outletMappedUniformInletFvPatchField
)
:
fixedValueFvPatchField<Type>(p, iF),
outletPatchName_(),
uniformValuePtr_(nullptr),
outletNames_(),
offsets_(),
fractions_(),
timeDelays_(),
mapFields_(),
mapTimes_(),
phiName_("phi"),
fraction_(1),
offset_(Zero)
curTimeIndex_(-1)
{}
@ -58,11 +64,122 @@ outletMappedUniformInletFvPatchField
)
:
fixedValueFvPatchField<Type>(p, iF, dict),
outletPatchName_(dict.get<word>("outletPatch")),
uniformValuePtr_
(
PatchFunction1<Type>::NewIfPresent
(
p.patch(),
"uniformValue",
dict
)
),
outletNames_(),
offsets_(),
fractions_(),
timeDelays_(),
mapFields_(),
mapTimes_(),
phiName_(dict.getOrDefault<word>("phi", "phi")),
fraction_(dict.getOrDefault<scalar>("fraction", 1)),
offset_(dict.getOrDefault<Type>("offset", Zero))
{}
curTimeIndex_(-1)
{
const dictionary& outletDict = dict.subDict("outlets");
if (outletDict.empty())
{
FatalIOErrorInFunction(outletDict)
<< "outlets dictionary is empty."
<< exit(FatalIOError);
}
outletNames_.setSize(outletDict.size());
offsets_.setSize(outletDict.size());
fractions_.setSize(outletDict.size());
timeDelays_.setSize(outletDict.size());
mapFields_.setSize(outletDict.size());
mapTimes_.setSize(outletDict.size());
label outleti = 0;
for (const entry& dEntry : outletDict)
{
const word& key = dEntry.keyword();
if (!dEntry.isDict())
{
FatalIOErrorInFunction(outletDict)
<< "Entry " << key << " is not a dictionary." << nl
<< exit(FatalIOError);
}
const dictionary& subDict = dEntry.dict();
outletNames_[outleti] = key;
offsets_.set
(
outleti,
Function1<Type>::NewIfPresent
(
"offset",
subDict,
word::null,
&this->db()
)
);
fractions_.set
(
outleti,
Function1<scalar>::NewIfPresent
(
"fraction",
subDict,
word::null,
&this->db()
)
);
timeDelays_.set
(
outleti,
Function1<scalar>::NewIfPresent
(
"timeDelay",
subDict,
word::null,
&this->db()
)
);
mapFields_[outleti] =
subDict.getOrDefault<DynamicList<Type>>
(
"mapField",
DynamicList<Type>()
);
mapTimes_[outleti] =
subDict.getOrDefault<DynamicList<scalar>>
(
"mapTime",
DynamicList<scalar>()
);
++outleti;
}
if (dict.found("value"))
{
fvPatchField<Type>::operator=
(
Field<Type>("value", dict, p.size())
);
}
else
{
fvPatchField<Type>::operator=(this->patchInternalField());
}
}
template<class Type>
@ -76,11 +193,26 @@ outletMappedUniformInletFvPatchField
)
:
fixedValueFvPatchField<Type>(ptf, p, iF, mapper),
outletPatchName_(ptf.outletPatchName_),
uniformValuePtr_(ptf.uniformValuePtr_.clone(p.patch())),
outletNames_(ptf.outletNames_),
offsets_(ptf.offsets_),
fractions_(ptf.fractions_),
timeDelays_(ptf.timeDelays_),
mapFields_(ptf.mapFields_),
mapTimes_(ptf.mapTimes_),
phiName_(ptf.phiName_),
fraction_(ptf.fraction_),
offset_(ptf.offset_)
{}
curTimeIndex_(-1)
{
if (mapper.direct() && !mapper.hasUnmapped())
{
// Use mapping instead of re-evaluation
this->map(ptf, mapper);
}
else
{
fvPatchField<Type>::operator=(this->patchInternalField());
}
}
template<class Type>
@ -91,10 +223,15 @@ outletMappedUniformInletFvPatchField
)
:
fixedValueFvPatchField<Type>(ptf),
outletPatchName_(ptf.outletPatchName_),
uniformValuePtr_(ptf.uniformValuePtr_.clone(this->patch().patch())),
outletNames_(ptf.outletNames_),
offsets_(ptf.offsets_),
fractions_(ptf.fractions_),
timeDelays_(ptf.timeDelays_),
mapFields_(ptf.mapFields_),
mapTimes_(ptf.mapTimes_),
phiName_(ptf.phiName_),
fraction_(ptf.fraction_),
offset_(ptf.offset_)
curTimeIndex_(-1)
{}
@ -107,15 +244,54 @@ outletMappedUniformInletFvPatchField
)
:
fixedValueFvPatchField<Type>(ptf, iF),
outletPatchName_(ptf.outletPatchName_),
uniformValuePtr_(ptf.uniformValuePtr_.clone(this->patch().patch())),
outletNames_(ptf.outletNames_),
offsets_(ptf.offsets_),
fractions_(ptf.fractions_),
timeDelays_(ptf.timeDelays_),
mapFields_(ptf.mapFields_),
mapTimes_(ptf.mapTimes_),
phiName_(ptf.phiName_),
fraction_(ptf.fraction_),
offset_(ptf.offset_)
curTimeIndex_(-1)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class Type>
void Foam::outletMappedUniformInletFvPatchField<Type>::autoMap
(
const fvPatchFieldMapper& m
)
{
fixedValueFvPatchField<Type>::autoMap(m);
if (uniformValuePtr_)
{
uniformValuePtr_->autoMap(m);
}
}
template<class Type>
void Foam::outletMappedUniformInletFvPatchField<Type>::rmap
(
const fvPatchField<Type>& ptf,
const labelList& addr
)
{
fixedValueFvPatchField<Type>::rmap(ptf, addr);
const auto& tiptf =
refCast<const outletMappedUniformInletFvPatchField>(ptf);
if (uniformValuePtr_)
{
uniformValuePtr_->rmap(tiptf.uniformValuePtr_(), addr);
}
}
template<class Type>
void Foam::outletMappedUniformInletFvPatchField<Type>::updateCoeffs()
{
@ -124,53 +300,125 @@ void Foam::outletMappedUniformInletFvPatchField<Type>::updateCoeffs()
return;
}
const GeometricField<Type, fvPatchField, volMesh>& f
(
dynamic_cast<const GeometricField<Type, fvPatchField, volMesh>&>
if (curTimeIndex_ != this->db().time().timeIndex())
{
const scalar t = this->db().time().timeOutputValue();
const GeometricField<Type, fvPatchField, volMesh>& f
(
this->internalField()
)
);
dynamic_cast<const GeometricField<Type, fvPatchField, volMesh>&>
(
this->internalField()
)
);
const fvPatch& p = this->patch();
const label outletPatchID =
p.patch().boundaryMesh().findPatchID(outletPatchName_);
const fvPatch& p = this->patch();
if (outletPatchID < 0)
{
FatalErrorInFunction
<< "Unable to find outlet patch " << outletPatchName_
<< abort(FatalError);
forAll(outletNames_, i)
{
const word& outletName = outletNames_[i];
const label outletID =
p.patch().boundaryMesh().findPatchID(outletName);
if (outletID < 0)
{
FatalErrorInFunction
<< "Unable to find outlet patch " << outletName
<< abort(FatalError);
}
// Collect the map time for this outlet patch
DynamicList<scalar>& mapTime = mapTimes_[i];
scalar timeDelay = 0;
if (timeDelays_.set(i))
{
timeDelay = max(timeDelays_[i].value(t), scalar(0));
}
mapTime.append(t + timeDelay);
// Collect the map field for this outlet patch and map time
const fvPatchField<Type>& outletFld = f.boundaryField()[outletID];
DynamicList<Type>& mapField = mapFields_[i];
const auto& phi =
this->db().objectRegistry::template
lookupObject<surfaceScalarField>(phiName_);
const scalarField& outletPhi = phi.boundaryField()[outletID];
const scalar sumOutletPhi = gSum(outletPhi);
if (sumOutletPhi > SMALL)
{
Type offset(Zero);
if (offsets_.set(i))
{
offset = offsets_[i].value(t);
}
scalar fraction = 1;
if (fractions_.set(i))
{
fraction = fractions_[i].value(t);
}
mapField.append
(
gSum(outletPhi*outletFld)/sumOutletPhi*fraction
+ offset
);
}
else
{
const fvPatch& outlet = p.boundaryMesh()[outletID];
mapField.append
(
gSum(outlet.magSf()*outletFld)/gSum(outlet.magSf())
);
}
}
// Map the stored fields onto inlet if the time condition is met
Type inletFld(Zero);
forAll(outletNames_, i)
{
DynamicList<scalar>& mapTime = mapTimes_[i];
DynamicList<Type>& mapField = mapFields_[i];
if (!mapTime.empty())
{
if (t >= mapTime.first())
{
inletFld += interpolateXY(t, mapTime, mapField);
// Remove any stored fields and times if possible
int i = 0;
while (!mapTime.empty() && t >= mapTime[i])
{
mapTime.remove(i);
mapField.remove(i);
++i;
}
}
}
}
if (uniformValuePtr_)
{
this->operator==(inletFld + uniformValuePtr_->value(t));
}
else
{
this->operator==(inletFld);
}
curTimeIndex_ = this->db().time().timeIndex();
}
const fvPatch& outletPatch = p.boundaryMesh()[outletPatchID];
const fvPatchField<Type>& outletPatchField =
f.boundaryField()[outletPatchID];
const auto& phi =
this->db().objectRegistry::template lookupObject<surfaceScalarField>
(phiName_);
const scalarField& outletPatchPhi = phi.boundaryField()[outletPatchID];
const scalar sumOutletPatchPhi = gSum(outletPatchPhi);
if (sumOutletPatchPhi > SMALL)
{
Type averageOutletField =
gSum(outletPatchPhi*outletPatchField)
/sumOutletPatchPhi;
this->operator==(averageOutletField*fraction_ + offset_);
}
else
{
Type averageOutletField =
gSum(outletPatch.magSf()*outletPatchField)
/gSum(outletPatch.magSf());
this->operator==(averageOutletField);
}
fixedValueFvPatchField<Type>::updateCoeffs();
}
@ -180,10 +428,40 @@ template<class Type>
void Foam::outletMappedUniformInletFvPatchField<Type>::write(Ostream& os) const
{
fvPatchField<Type>::write(os);
os.writeEntry("outletPatch", outletPatchName_);
if (uniformValuePtr_)
{
uniformValuePtr_->writeData(os);
}
os.beginBlock("outlets");
forAll(outletNames_, i)
{
os.beginBlock(outletNames_[i]);
if (offsets_.set(i))
{
offsets_[i].writeData(os);
}
if (fractions_.set(i))
{
fractions_[i].writeData(os);
}
if (timeDelays_.set(i))
{
timeDelays_[i].writeData(os);
}
if (!mapFields_.empty())
{
mapFields_[i].writeEntry("mapField", os);
}
if (!mapTimes_.empty())
{
mapTimes_[i].writeEntry("mapTime", os);
}
os.endBlock();
}
os.endBlock();
os.writeEntryIfDifferent<word>("phi", "phi", phiName_);
os.writeEntry("fraction", fraction_);
os.writeEntry("offset", offset_);
this->writeEntry("value", os);
}

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2018 OpenFOAM Foundation
Copyright (C) 2020 OpenCFD Ltd.
Copyright (C) 2020-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -32,15 +32,17 @@ Group
Description
The \c outletMappedUniformInlet is an inlet boundary condition that
- averages the patch field of \<Type\> over a specified "outlet" patch
- averages patch fields of specified "outlet" patches
and uniformly applies the averaged value over a specified inlet patch.
- optionally, the averaged value can be scaled
and/or offset by a pair of specified values.
and/or offset by a specified value,
and/or mapped by a specified time delay.
The governing equation of the boundary condition is:
\f[
\phi_{inlet} = f \phi_{outlet} + \phi_{offset}
\phi_{inlet} =
\sum_i \left( f_i \phi_{{outlet}_i} + \phi_{{offset}_i} \right)
\f]
where
@ -49,6 +51,7 @@ Description
\phi_{outlet} | Averaged patch-field value at an outlet patch
f | User-defined fraction value
\phi_{offset} | User-defined offset value
i | Outlet-patch index
\endvartable
Usage
@ -56,32 +59,55 @@ Usage
\verbatim
<patchName>
{
// Mandatory entries (unmodifiable)
type outletMappedFilterInlet;
outletPatch <outletPatchName>;
// Mandatory entries
type outletMappedUniformInlet;
// Optional entries (unmodifiable)
fraction 0.1;
offset 10; // (1 0 0);
outlets
{
<outletName.1>
{
fraction <Function1<scalar>>;
offset <Function1<Type>>;
timeDelay <Function1<scalar>>;
}
<outletName.2>
{
fraction <Function1<scalar>>;
offset <Function1<Type>>;
timeDelay <Function1<scalar>>;
}
...
}
// Optional entries
uniformValue <PatchFunction1<Type>>;
phi phi;
// Optional (inherited) entries
// Inherited entries
...
}
\endverbatim
where the entries mean:
\table
Property | Description | Type | Reqd | Dflt
Property | Description | Type | Reqd | Deflt
type | Type name: outletMappedUniformInlet | word | yes | -
outletPatch | Name of patch to be mapped | word | yes | -
fraction | Fraction value | scalar | no | 1
offset | Offset value | Type | no | Zero
outlets | Dictionary name: outlets | dict | yes | -
fraction | Fraction value | Function1\<scalar\> | no | 1
offset | Offset value | Function1\<Type\> | no | Zero
timeDelay | Time delay | Function1\<scalar\> | no | 0
uniformValue | Base inlet patch field | PatchFunction1\<Type\> | no | Zero
phi | Name of operand flux field | word | no | phi
\endtable
The inherited entries are elaborated in:
- \link fixedValueFvPatchFields.H \endlink
- \link PatchFunction1.H \endlink
- \link Function1.H \endlink
Note
- Any negative input of \c timeDelay entry
is forced to be zero without emitting any warnings.
See also
- Foam::fixedValueFvPatchField
@ -98,6 +124,7 @@ SourceFiles
#define outletMappedUniformInletFvPatchField_H
#include "fixedValueFvPatchFields.H"
#include "PatchFunction1.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -115,17 +142,32 @@ class outletMappedUniformInletFvPatchField
{
// Private Data
//- Name of the outlet patch to be mapped
word outletPatchName_;
//- Base inlet patch field
autoPtr<PatchFunction1<Type>> uniformValuePtr_;
//- Name of operand flux field
//- List of outlet-patch names
wordList outletNames_;
//- List of outlet-patch field offsets
PtrList<Function1<Type>> offsets_;
//- List of outlet-patch field fractions
PtrList<Function1<scalar>> fractions_;
//- List of outlet-patch field time delays
PtrList<Function1<scalar>> timeDelays_;
//- List of outlet-patch mapping fields
List<DynamicList<Type>> mapFields_;
//- List of outlet-patch mapping times
List<DynamicList<scalar>> mapTimes_;
//- Name of operand flux field
word phiName_;
//- Fraction value
scalar fraction_;
//- Offset value
Type offset_;
//- Current time index
label curTimeIndex_;
public:
@ -198,13 +240,20 @@ public:
// Member Functions
// Access
// Mapping
//- Name of the outlet patch to be mapped
const word& outletPatchName() const
{
return outletPatchName_;
}
//- Map (and resize as needed) from self given a mapping object
virtual void autoMap
(
const fvPatchFieldMapper&
);
//- Reverse map the given fvPatchField onto this fvPatchField
virtual void rmap
(
const fvPatchField<Type>&,
const labelList&
);
// Evaluation