OpenFOAM-5.x/applications/utilities/thermophysical/equilibriumFlameT/equilibriumFlameT.C
Henry Weller abc50e214c thermophysicalModels: Changed specie thermodynamics from mole to mass basis
The fundamental properties provided by the specie class hierarchy were
mole-based, i.e. provide the properties per mole whereas the fundamental
properties provided by the liquidProperties and solidProperties classes are
mass-based, i.e. per unit mass.  This inconsistency made it impossible to
instantiate the thermodynamics packages (rhoThermo, psiThermo) used by the FV
transport solvers on liquidProperties.  In order to combine VoF with film and/or
Lagrangian models it is essential that the physical propertied of the three
representations of the liquid are consistent which means that it is necessary to
instantiate the thermodynamics packages on liquidProperties.  This requires
either liquidProperties to be rewritten mole-based or the specie classes to be
rewritten mass-based.  Given that most of OpenFOAM solvers operate
mass-based (solve for mass-fractions and provide mass-fractions to sub-models it
is more consistent and efficient if the low-level thermodynamics is also
mass-based.

This commit includes all of the changes necessary for all of the thermodynamics
in OpenFOAM to operate mass-based and supports the instantiation of
thermodynamics packages on liquidProperties.

Note that most users, developers and contributors to OpenFOAM will not notice
any difference in the operation of the code except that the confusing

    nMoles     1;

entries in the thermophysicalProperties files are no longer needed or used and
have been removed in this commet.  The only substantial change to the internals
is that species thermodynamics are now "mixed" with mass rather than mole
fractions.  This is more convenient except for defining reaction equilibrium
thermodynamics for which the molar rather than mass composition is usually know.
The consequence of this can be seen in the adiabaticFlameT, equilibriumCO and
equilibriumFlameT utilities in which the species thermodynamics are
pre-multiplied by their molecular mass to effectively convert them to mole-basis
to simplify the definition of the reaction equilibrium thermodynamics, e.g. in
equilibriumCO

    // Reactants (mole-based)
    thermo FUEL(thermoData.subDict(fuelName)); FUEL *= FUEL.W();

    // Oxidant (mole-based)
    thermo O2(thermoData.subDict("O2")); O2 *= O2.W();
    thermo N2(thermoData.subDict("N2")); N2 *= N2.W();

    // Intermediates (mole-based)
    thermo H2(thermoData.subDict("H2")); H2 *= H2.W();

    // Products (mole-based)
    thermo CO2(thermoData.subDict("CO2")); CO2 *= CO2.W();
    thermo H2O(thermoData.subDict("H2O")); H2O *= H2O.W();
    thermo CO(thermoData.subDict("CO")); CO *= CO.W();

    // Product dissociation reactions

    thermo CO2BreakUp
    (
        CO2 == CO + 0.5*O2
    );

    thermo H2OBreakUp
    (
        H2O == H2 + 0.5*O2
    );

Please report any problems with this substantial but necessary rewrite of the
thermodynamic at https://bugs.openfoam.org

Henry G. Weller
CFD Direct Ltd.
2017-02-17 11:22:14 +00:00

284 lines
7.8 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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/>.
Application
equilibriumFlameT
Description
Calculates the equilibrium flame temperature for a given fuel and
pressure for a range of unburnt gas temperatures and equivalence
ratios; the effects of dissociation on O2, H2O and CO2 are included.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "dictionary.H"
#include "IFstream.H"
#include "OSspecific.H"
#include "etcFiles.H"
#include "IOmanip.H"
#include "specie.H"
#include "perfectGas.H"
#include "thermo.H"
#include "janafThermo.H"
#include "absoluteEnthalpy.H"
using namespace Foam;
typedef species::thermo<janafThermo<perfectGas<specie>>, absoluteEnthalpy>
thermo;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::validArgs.append("controlFile");
argList args(argc, argv);
const fileName controlFileName = args[1];
// Construct control dictionary
IFstream controlFile(controlFileName);
// Check controlFile stream is OK
if (!controlFile.good())
{
FatalErrorInFunction
<< "Cannot read file " << controlFileName
<< abort(FatalError);
}
dictionary control(controlFile);
scalar P(readScalar(control.lookup("P")));
const word fuelName(control.lookup("fuel"));
scalar n(readScalar(control.lookup("n")));
scalar m(readScalar(control.lookup("m")));
Info<< nl << "Reading thermodynamic data dictionary" << endl;
fileName thermoDataFileName(findEtcFile("thermoData/thermoData"));
// Construct control dictionary
IFstream thermoDataFile(thermoDataFileName);
// Check thermoData stream is OK
if (!thermoDataFile.good())
{
FatalErrorInFunction
<< "Cannot read file " << thermoDataFileName
<< abort(FatalError);
}
dictionary thermoData(thermoDataFile);
Info<< nl << "Reading thermodynamic data for relevant species"
<< nl << endl;
// Reactants (mole-based)
thermo FUEL(thermoData.subDict(fuelName)); FUEL *= FUEL.W();
// Oxidant (mole-based)
thermo O2(thermoData.subDict("O2")); O2 *= O2.W();
thermo N2(thermoData.subDict("N2")); N2 *= N2.W();
// Intermediates (mole-based)
thermo H2(thermoData.subDict("H2")); H2 *= H2.W();
// Products (mole-based)
thermo CO2(thermoData.subDict("CO2")); CO2 *= CO2.W();
thermo H2O(thermoData.subDict("H2O")); H2O *= H2O.W();
thermo CO(thermoData.subDict("CO")); CO *= CO.W();
// Product dissociation reactions
thermo CO2BreakUp
(
CO2 == CO + 0.5*O2
);
thermo H2OBreakUp
(
H2O == H2 + 0.5*O2
);
// Stoiciometric number of moles of species for one mole of fuel
scalar stoicO2 = n + m/4.0;
scalar stoicN2 = (0.79/0.21)*(n + m/4.0);
scalar stoicCO2 = n;
scalar stoicH2O = m/2.0;
// Oxidant
thermo oxidant
(
"oxidant",
stoicO2*O2
+ stoicN2*N2
);
dimensionedScalar stoichiometricAirFuelMassRatio
(
"stoichiometricAirFuelMassRatio",
dimless,
oxidant.Y()/FUEL.W()
);
Info<< "stoichiometricAirFuelMassRatio "
<< stoichiometricAirFuelMassRatio << ';' << endl;
Info<< "Equilibrium flame temperature data ("
<< P/1e5 << " bar)" << nl << nl
<< setw(3) << "Phi"
<< setw(12) << "ft"
<< setw(7) << "T0"
<< setw(12) << "Tad"
<< setw(12) << "Teq"
<< setw(12) << "Terror"
<< setw(20) << "O2res (mole frac)" << nl
<< endl;
// Loop over equivalence ratios
for (int i=0; i<16; i++)
{
scalar equiv = 0.6 + i*0.05;
scalar ft = 1/(1 + stoichiometricAirFuelMassRatio.value()/equiv);
// Loop over initial temperatures
for (int j=0; j<28; j++)
{
scalar T0 = 300.0 + j*100.0;
// Number of moles of species for one mole of fuel
scalar o2 = (1.0/equiv)*stoicO2;
scalar n2 = (0.79/0.21)*o2;
scalar fres = max(1.0 - 1.0/equiv, 0.0);
scalar fburnt = 1.0 - fres;
// Initial guess for number of moles of product species
// ignoring product dissociation
scalar oresInit = max(1.0/equiv - 1.0, 0.0)*stoicO2;
scalar co2Init = fburnt*stoicCO2;
scalar h2oInit = fburnt*stoicH2O;
scalar ores = oresInit;
scalar co2 = co2Init;
scalar h2o = h2oInit;
scalar co = 0.0;
scalar h2 = 0.0;
// Total number of moles in system
scalar N = fres + n2 + co2 + h2o + ores;
// Initial guess for adiabatic flame temperature
scalar adiabaticFlameTemperature =
T0
+ (fburnt/(1.0 + o2 + n2))/(1.0/(1.0 + (1.0 + 0.79/0.21)*stoicO2))
*2000.0;
scalar equilibriumFlameTemperature = adiabaticFlameTemperature;
// Iteration loop for adiabatic flame temperature
for (int j=0; j<20; j++)
{
if (j > 0)
{
co = co2*
min
(
CO2BreakUp.Kn(P, equilibriumFlameTemperature, N)
/::sqrt(max(ores, 0.001)),
1.0
);
h2 = h2o*
min
(
H2OBreakUp.Kn(P, equilibriumFlameTemperature, N)
/::sqrt(max(ores, 0.001)),
1.0
);
co2 = co2Init - co;
h2o = h2oInit - h2;
ores = oresInit + 0.5*co + 0.5*h2;
}
thermo reactants
(
FUEL + o2*O2 + n2*N2
);
thermo products
(
fres*FUEL + ores*O2 + n2*N2
+ co2*CO2 + h2o*H2O + co*CO + h2*H2
);
scalar equilibriumFlameTemperatureNew =
products.THa(reactants.Ha(P, T0), P, adiabaticFlameTemperature);
if (j==0)
{
adiabaticFlameTemperature = equilibriumFlameTemperatureNew;
}
else
{
equilibriumFlameTemperature = 0.5*
(
equilibriumFlameTemperature
+ equilibriumFlameTemperatureNew
);
}
}
Info<< setw(3) << equiv
<< setw(12) << ft
<< setw(7) << T0
<< setw(12) << adiabaticFlameTemperature
<< setw(12) << equilibriumFlameTemperature
<< setw(12)
<< adiabaticFlameTemperature - equilibriumFlameTemperature
<< setw(12) << ores/N
<< endl;
}
}
Info<< nl << "end" << endl;
return 0;
}
// ************************************************************************* //