diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index bcf1033bcb..30d191014a 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -50,3 +50,7 @@ It is likely incomplete... - Norbert Weber - Henry Weller - Niklas Wikstrom +- Thorsten Zirwes + + + diff --git a/src/thermophysicalModels/chemistryModel/Make/files b/src/thermophysicalModels/chemistryModel/Make/files index 630137c0e6..a77950e3bd 100644 --- a/src/thermophysicalModels/chemistryModel/Make/files +++ b/src/thermophysicalModels/chemistryModel/Make/files @@ -7,5 +7,6 @@ chemistryModel/TDACChemistryModel/tabulation/makeChemistryTabulationMethods.C chemistrySolver/chemistrySolver/makeChemistrySolvers.C functionObjects/specieReactionRates/specieReactionRates.C +functionObjects/BilgerMixtureFraction/BilgerMixtureFraction.C LIB = $(FOAM_LIBBIN)/libchemistryModel diff --git a/src/thermophysicalModels/chemistryModel/functionObjects/BilgerMixtureFraction/BilgerMixtureFraction.C b/src/thermophysicalModels/chemistryModel/functionObjects/BilgerMixtureFraction/BilgerMixtureFraction.C new file mode 100644 index 0000000000..65a93a982f --- /dev/null +++ b/src/thermophysicalModels/chemistryModel/functionObjects/BilgerMixtureFraction/BilgerMixtureFraction.C @@ -0,0 +1,409 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 Thorsten Zirwes + Copyright (C) 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 "BilgerMixtureFraction.H" +#include "basicThermo.H" +#include "reactingMixture.H" +#include "thermoPhysicsTypes.H" +#include "scalarRange.H" +#include "basicChemistryModel.H" +#include "psiReactionThermo.H" +#include "rhoReactionThermo.H" +#include "BasicChemistryModel.H" +#include "addToRunTimeSelectionTable.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ +namespace functionObjects +{ + defineTypeNameAndDebug(BilgerMixtureFraction, 0); + addToRunTimeSelectionTable + ( + functionObject, + BilgerMixtureFraction, + dictionary + ); +} +} + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +void Foam::functionObjects::BilgerMixtureFraction::calcBilgerMixtureFraction() +{ + if (!mesh_.foundObject(resultName_, false)) + { + auto tCo = tmp::New + ( + IOobject + ( + resultName_, + mesh_.time().timeName(), + mesh_, + IOobject::NO_READ, + IOobject::NO_WRITE + ), + mesh_, + dimensionedScalar(dimless, Zero) + ); + mesh_.objectRegistry::store(tCo.ptr()); + } + + auto& f_Bilger = mesh_.lookupObjectRef(resultName_); + + auto& Y = thermo_.Y(); + + f_Bilger = -o2RequiredOx_; + forAll(Y, i) + { + f_Bilger += + Y[i] + *(nAtomsC_[i] + nAtomsS_[i] + 0.25*nAtomsH_[i] - 0.5*nAtomsO_[i]) + /thermo_.W(i); + } + f_Bilger /= o2RequiredFuelOx_; + f_Bilger.clip + ( + dimensionedScalar(dimless, 0), + dimensionedScalar(dimless, 1) + ); +} + + +bool Foam::functionObjects::BilgerMixtureFraction::readComposition +( + const dictionary& subDict, + scalarField& comp +) +{ + auto& Y = thermo_.Y(); + const speciesTable& speciesTab = thermo_.species(); + + // Read mass fractions of all species for the oxidiser or fuel + forAll(Y, i) + { + comp[i] = + subDict.getCheckOrDefault + ( + speciesTab[i], + 0, + scalarRange::ge0() + ); + } + + if (sum(comp) < SMALL) + { + FatalIOErrorInFunction(subDict) + << "No composition is given" << nl + << "Valid species are:" << nl + << speciesTab + << exit(FatalIOError); + + return false; + } + + const word fractionBasisType + ( + subDict.getOrDefault("fractionBasis", "mass") + ); + + if (fractionBasisType == "mass") + { + // Normalize fractionBasis to the unity + comp /= sum(comp); + } + else if (fractionBasisType == "mole") + { + // In case the fractionBasis is given in mole fractions, + // convert from mole fractions to normalized mass fractions + scalar W(0); + forAll(comp, i) + { + comp[i] *= thermo_.W(i); + W += comp[i]; + } + comp /= W; + } + else + { + FatalIOErrorInFunction(subDict) + << "The given fractionBasis type is invalid" << nl + << "Valid fractionBasis types are" << nl + << " \"mass\" (default)" << nl + << " \"mole\"" + << exit(FatalIOError); + + return false; + } + + return true; +} + + +Foam::scalar Foam::functionObjects::BilgerMixtureFraction::o2Required +( + const scalarField& comp +) const +{ + scalar o2req(0); + forAll(thermo_.Y(), i) + { + o2req += + comp[i]/thermo_.W(i)*(nAtomsC_[i] + nAtomsS_[i] + 0.25*nAtomsH_[i]); + } + + return o2req; +} + + +Foam::scalar Foam::functionObjects::BilgerMixtureFraction::o2Present +( + const scalarField& comp +) const +{ + scalar o2pres(0); + forAll(thermo_.Y(), i) + { + o2pres += comp[i]/thermo_.W(i)*nAtomsO_[i]; + } + + return 0.5*o2pres; +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::functionObjects::BilgerMixtureFraction::BilgerMixtureFraction +( + const word& name, + const Time& runTime, + const dictionary& dict +) +: + fvMeshFunctionObject(name, runTime, dict), + phaseName_(dict.getOrDefault("phase", word::null)), + resultName_ + ( + dict.getOrDefault + ( + "result", + IOobject::groupName("f_Bilger", phaseName_) + ) + ), + thermo_ + ( + mesh_.lookupObject + ( + IOobject::groupName(basicThermo::dictName, phaseName_) + ) + ), + nSpecies_(thermo_.Y().size()), + o2RequiredOx_(0), + o2RequiredFuelOx_(0), + nAtomsC_(nSpecies_, 0), + nAtomsS_(nSpecies_, 0), + nAtomsH_(nSpecies_, 0), + nAtomsO_(nSpecies_, 0), + Yoxidiser_(nSpecies_, 0), + Yfuel_(nSpecies_, 0) +{ + read(dict); + + calcBilgerMixtureFraction(); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +bool Foam::functionObjects::BilgerMixtureFraction::read(const dictionary& dict) +{ + if (!fvMeshFunctionObject::read(dict)) + { + return false; + } + + Info<< nl << type() << " " << name() << ":" << nl; + + phaseName_ = dict.getOrDefault("phase", word::null); + resultName_ = + dict.getOrDefault + ( + "result", + IOobject::groupName("f_Bilger", phaseName_) + ); + + nSpecies_ = thermo_.Y().size(); + + if (nSpecies_ == 0) + { + FatalErrorInFunction + << "Number of input species is zero" + << exit(FatalError); + } + + nAtomsC_.resize(nSpecies_, 0); + nAtomsS_.resize(nSpecies_, 0); + nAtomsH_.resize(nSpecies_, 0); + nAtomsO_.resize(nSpecies_, 0); + + auto& Y = thermo_.Y(); + const speciesTable& speciesTab = thermo_.species(); + + typedef BasicChemistryModel psiChemistryModelType; + typedef BasicChemistryModel rhoChemistryModelType; + + const auto* psiChemPtr = + mesh_.cfindObject("chemistryProperties"); + + const auto* rhoChemPtr = + mesh_.cfindObject("chemistryProperties"); + + autoPtr>> speciesCompPtr; + + if (psiChemPtr) + { + speciesCompPtr.reset((*psiChemPtr).thermo().specieComposition()); + } + else if (rhoChemPtr) + { + speciesCompPtr.reset((*rhoChemPtr).thermo().specieComposition()); + } + else + { + FatalErrorInFunction + << "BasicChemistryModel not found" + << exit(FatalError); + } + + forAll(Y, i) + { + const List& curSpecieComposition = + (speciesCompPtr.ref())[speciesTab[i]]; + + forAll(curSpecieComposition, j) + { + const word& e = curSpecieComposition[j].name(); + const label nAtoms = curSpecieComposition[j].nAtoms(); + + if (e == "C") + { + nAtomsC_[i] = nAtoms; + } + else if (e == "S") + { + nAtomsS_[i] = nAtoms; + } + else if (e == "H") + { + nAtomsH_[i] = nAtoms; + } + else if (e == "O") + { + nAtomsO_[i] = nAtoms; + } + } + } + + if (sum(nAtomsO_) == 0) + { + FatalErrorInFunction + << "No specie contains oxygen" + << exit(FatalError); + } + + Yoxidiser_.resize(nSpecies_, 0); + Yfuel_.resize(nSpecies_, 0); + + if + ( + !readComposition(dict.subDict("oxidiser"), Yoxidiser_) + || !readComposition(dict.subDict("fuel"), Yfuel_) + ) + { + return false; + } + + o2RequiredOx_ = o2Required(Yoxidiser_) - o2Present(Yoxidiser_); + + if (o2RequiredOx_ > 0) + { + FatalErrorInFunction + << "Oxidiser composition contains not enough oxygen" << endl + << "Mixed up fuel and oxidiser compositions?" + << exit(FatalError); + } + + const scalar o2RequiredFuel = o2Required(Yfuel_) - o2Present(Yfuel_); + + if (o2RequiredFuel < 0) + { + FatalErrorInFunction + << "Fuel composition contains too much oxygen" << endl + << "Mixed up fuel and oxidiser compositions?" + << exit(FatalError); + } + + o2RequiredFuelOx_ = o2RequiredFuel - o2RequiredOx_; + + if (mag(o2RequiredFuelOx_) < SMALL) + { + FatalErrorInFunction + << "Fuel and oxidiser have the same composition" + << exit(FatalError); + } + + return true; +} + + +bool Foam::functionObjects::BilgerMixtureFraction::execute() +{ + calcBilgerMixtureFraction(); + + return true; +} + + +bool Foam::functionObjects::BilgerMixtureFraction::clear() +{ + return clearObject(resultName_); +} + + +bool Foam::functionObjects::BilgerMixtureFraction::write() +{ + Log << type() << " " << name() << " write:" << nl + << " writing field " << resultName_ << endl; + + return writeObject(resultName_); +} + + +// ************************************************************************* // diff --git a/src/thermophysicalModels/chemistryModel/functionObjects/BilgerMixtureFraction/BilgerMixtureFraction.H b/src/thermophysicalModels/chemistryModel/functionObjects/BilgerMixtureFraction/BilgerMixtureFraction.H new file mode 100644 index 0000000000..66259b93fe --- /dev/null +++ b/src/thermophysicalModels/chemistryModel/functionObjects/BilgerMixtureFraction/BilgerMixtureFraction.H @@ -0,0 +1,301 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 Thorsten Zirwes + Copyright (C) 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 . + +Class + Foam::functionObjects::BilgerMixtureFraction + +Group + grpThermophysicalFunctionObjects + +Description + Calculates the Bilger mixture-fraction field (i.e. + \f$f_{\mathrm{Bilger}}\f$) based on the elemental composition + of the mixture. Elements \c C, \c H, \c S and \c O are considered. + + \f$f_{\mathrm{Bilger}}\f$ is the mass mixing ratio of fuel and oxidiser + in [kg fuel / kg mixture], and is invariant to the reaction progress of + the mixture (e.g. the same for unburnt and burnt mixtures). + + \f$f_{\mathrm{Bilger}}\f$ equals to the unity + for pure fuel and to zero for pure oxidiser. + + The Bilger mixture-fraction field is computed based on the following: + + \f[ + f_{\mathrm{Bilger}} = + \frac{\beta - \beta_{\mathrm{ox}}}{\beta_{\mathrm{fuel}} + - \beta_{\mathrm{ox}}} + \f] + + with + + \f[ + \beta = + 2\frac{Y_C}{W_C} + + 2\frac{Y_S}{W_S} + + \frac{1}{2}\frac{Y_H}{W_H} + - \frac{Y_O}{W_O} + \f] + + where + \vartable + \beta | Coupling function [kmol/kg] + Y_e | Elemental mass fraction of element, e + W_e | Atomic weight of element, e + {.}_{\mathrm{ox}} | Subscript to denote oxidiser composition + {.}_{\mathrm{fuel}} | Subscript to denote fuel composition + \endvartable + + Operands: + \table + Operand | Type | Location + input | - | - + output file | - | - + output field | volScalarField | $FOAM_CASE/\/\ + \endtable + + References: + \verbatim + Original method: + Bilger, R. W. (1979). + Turbulent jet diffusion flames. + Energy and Combustion Science, p. 109-131. + DOI:10.1016/B978-0-08-024780-9.50011-3 + + Implementation: + Zirwes, T., Zhang, F., Habisreuther, P., Hansinger, M., + Bockhorn, H., Pfitzner, M., & Trimis, D. (2019). + Quasi-DNS dataset of a piloted flame + with inhomogeneous inlet conditions. + Flow, Turbulence and Combustion, vol 104, p. 997-1027. + DOI:10.1007/s10494-019-00081-5 + \endverbatim + +Usage + Minimal example by using \c system/controlDict.functions: + \verbatim + BilgerMixtureFraction1 + { + // Mandatory entries (unmodifiable) + type BilgerMixtureFraction; + libs (fieldFunctionObjects); + + // Mandatory entries (runtime modifiable) + fuel + { + // Optional entries (runtime modifiable) + fractionBasis mass; + + // Conditional mandatory entries (runtime modifiable) + CH4 1; + } + + oxidiser + { + // Optional entries (runtime modifiable) + fractionBasis mole; + + // Conditional mandatory entries (runtime modifiable) + O2 0.23; + N2 0.77; + } + + // Optional entries (runtime modifiable) + phase ; + result ; + + // Optional (inherited) entries + ... + } + \endverbatim + + where the entries mean: + \table + Property | Description | Type | Reqd | Dflt + type | Type name: BilgerMixtureFraction | word | yes | - + libs | Library name: fieldFunctionObjects | word | yes | - + fuel | Dictionary for fuel composition | dict | yes | - + oxidiser | Dictionary for oxidiser composition | dict | yes | - + phase | Name of phase (e.g. "gas") | word | no | "" + result | Name of resulting field | word | no | f_Bilger + fractionBasis | Species-fraction interpretation method | word | no | mass + \endtable + + Options for the \c fractionBasis entry: + \verbatim + mass | Interpret species fractions as mass fractions + mole | Interpret species fractions as mole fractions + \endverbatim + + The inherited entries are elaborated in: + - \link functionObject.H \endlink + + Usage by the \c postProcess utility is not available. + +Note + - The mole or mass fractions are automatically normalized to the unity. + +See also + - Foam::functionObject + - Foam::functionObjects::fvMeshFunctionObject + +SourceFiles + BilgerMixtureFraction.C + +\*---------------------------------------------------------------------------*/ + +#ifndef BilgerMixtureFraction_H +#define BilgerMixtureFraction_H + +#include "fvMeshFunctionObject.H" +#include "specieElement.H" +#include "basicSpecieMixture.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace functionObjects +{ +/*---------------------------------------------------------------------------*\ + Class BilgerMixtureFraction Declaration +\*---------------------------------------------------------------------------*/ + +class BilgerMixtureFraction +: + public fvMeshFunctionObject +{ + // Private Data + + //- Name of the phase (e.g. "gas" for multiphase applications) + word phaseName_; + + //- Name of the resulting mixture-fraction field + word resultName_; + + //- Reference to thermo object + const basicSpecieMixture& thermo_; + + //- Number of species + label nSpecies_; + + // Amount of oxygen required to fully oxidise the oxidiser + scalar o2RequiredOx_; + + // Amount of oxygen required to oxidise the fuel minus the oxidiser + scalar o2RequiredFuelOx_; + + //- Number of carbon atoms for each species + labelField nAtomsC_; + + //- Number of sulphur atoms for each species + labelField nAtomsS_; + + //- Number of hydrogen atoms for each species + labelField nAtomsH_; + + //- Number of oxygen atoms for each species + labelField nAtomsO_; + + //- Mass fractions of species in the oxidiser + scalarField Yoxidiser_; + + //- Mass fractions of species in the fuel + scalarField Yfuel_; + + + // Private Member Functions + + //- Calculate the Bilger mixture-fraction + void calcBilgerMixtureFraction(); + + //- Read composition of fuel and oxidiser from subdictionary + bool readComposition + ( + const dictionary& subDict, + scalarField& comp + ); + + //- Compute amount of oxygen required to oxidise a mixture + scalar o2Present(const scalarField&) const; + + //- Compute amount of oxygen present in a mixture + scalar o2Required(const scalarField&) const; + + +public: + + //- Runtime type information + TypeName("BilgerMixtureFraction"); + + + // Constructors + + //- Construct from Time and dictionary + BilgerMixtureFraction + ( + const word& name, + const Time& runTime, + const dictionary& dict + ); + + //- No copy construct + BilgerMixtureFraction(const BilgerMixtureFraction&) = delete; + + //- No copy assignment + void operator=(const BilgerMixtureFraction&) = delete; + + + //- Destructor + virtual ~BilgerMixtureFraction() = default; + + + // Member Functions + + //- Read the BilgerMixtureFraction data + virtual bool read(const dictionary&); + + //- Calculate the Bilger mixture-fraction field + virtual bool execute(); + + //- Clear the Bilger mixture-fraction field from registry + virtual bool clear(); + + //- Write Bilger mixture-fraction field + virtual bool write(); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace functionObjects +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* //