$ \nu_{ki}' = \begin{pmatrix}1 & 1 & 0 & 0 & 0 \\0 & 0 & 0 & 1 & 1 \\\end{pmatrix}, \nu_{ki}'' = \begin{pmatrix}0 & 0 & 1 & 1 & 0 \\0 & 1 & 1 & 0 & 0 \\\end{pmatrix} , \chi_k = \begin{pmatrix}N_2 \\ O \\ NO \\ N \\ O_2 \\\end{pmatrix} $
Реакции задаются в файле constant/thermophysicalProperties , где находится химический считыватель и файл, содержащий реакции и термодинамические свойства:
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v1912 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class dictionary;
location "constant";
object thermophysicalProperties;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
thermoType
{
type hePsiThermo;
mixture reactingMixture;
transport sutherland;
thermo janaf;
energy sensibleEnthalpy;
equationOfState perfectGas;
specie specie;
}
chemistryReader foamChemistryReader;
foamChemistryFile "<constant>/reactions";
foamChemistryThermoFile "<constant>/thermo";
//CHEMKINFile "<case>/chemkin/chem.inp";
//CHEMKINThermoFile "<case>/chemkin/therm.dat";
//CHEMKINTransportFile "<case>/chemkin/transportProperties";
// ************************************************************************* //
В приведённом выше примере реакции выглядят следующим образом:
elements 2(O N);
species 5(O O2 N2 N NO);
reactions
{
un-named-reaction-0
{
type reversibleArrheniusReaction;
reaction "NO^1 + N^1 = N2^1 + O^1";
A 2.1e+10;
beta 0;
Ta 0;
}
un-named-reaction-1
{
type reversibleArrheniusReaction;
reaction "N^1 + O2^1 = NO^1 + O^1";
A 5.83e+06;
beta 1.01;
Ta 3120;
}
}
В нашем примере обе реакции являются обратимыми реакциями Аррениуса. A - это per-exponent фактор, Ta - температура активации и beta - показатель температуры при расчёте скорости реакции (см. дополнительные пояснения ниже). Значения взяты из книги [3]. Заметим, что значения констант в OpenFOAM вычисляются в собственной системе единиц измерения: kmol, m3, s, K.
2.2 Скорость химической реакции
Установив химические реакции, которые желательно должны быть разрешёнными, следующим шагом является описание скорости химической реакции, т.е. изменение концентрации отдельных веществ со временем. Скорость реакции $ \omega_i $ можно записать так (смотрите также книгу [4]):
$ \omega_i = k_{fi} \prod_k [X]_k^{\nu_{ki}'} - k_{ri} \prod_k [X]_k^{\nu_{ki}''} $
$ k_{fi} $ и $ k_{ri} $ - прямая и обратная константы скорости реакции i. Обратите внимание, что в OpenFoam по-умолчанию используются значения показателей - стехиометрические коэффициенты: $ \nu_{ki}', \nu_{ki}'' $. Однако, пользователь может установить их явно, добавив значок ^ после химического символа (смотрите также https://openfoamwiki.net/images/8/8a/Christ_OFW6_slides.pdf). $ [X]_k $ - это молярная концентрация вещества k. Чтобы продолжить наш пример, скорость реакции механизма Зельдовича можно записать так:
$ \omega_1 = k_{f1}[NO][N] - k_{r1}[N2][O] $
$ \omega_2 = k_{f2}[N][O2] - k_{r2}[NO][O] $
Расчёт скорости реакции каждой реакции производится в функции omega файла $FOAM_SRC/thermophysicalModels/chemistryModel/chemistryModel/StandardChemistryModel/StandardChemistryModel.C:
template<class ReactionThermo, class ThermoType>
Foam::scalar Foam::StandardChemistryModel<ReactionThermo, ThermoType>::omega
(
const Reaction<ThermoType>& R,
const scalarField& c,
const scalar T,
const scalar p,
scalar& pf,
scalar& cf,
label& lRef,
scalar& pr,
scalar& cr,
label& rRef
) const
{
const scalar kf = R.kf(p, T, c);
const scalar kr = R.kr(kf, p, T, c);
pf = 1.0;
pr = 1.0;
const label Nl = R.lhs().size();
const label Nr = R.rhs().size();
label slRef = 0;
lRef = R.lhs()[slRef].index;
pf = kf;
for (label s = 1; s < Nl; s++)
{
const label si = R.lhs()[s].index;
if (c[si] < c[lRef])
{
const scalar exp = R.lhs()[slRef].exponent;
pf *= pow(max(c[lRef], 0.0), exp);
lRef = si;
slRef = s;
}
else
{
const scalar exp = R.lhs()[s].exponent;
pf *= pow(max(c[si], 0.0), exp);
}
}
cf = max(c[lRef], 0.0);
{
const scalar exp = R.lhs()[slRef].exponent;
if (exp < 1.0)
{
if (cf > SMALL)
{
pf *= pow(cf, exp - 1.0);
}
else
{
pf = 0.0;
}
}
else
{
pf *= pow(cf, exp - 1.0);
}
}
label srRef = 0;
rRef = R.rhs()[srRef].index;
// Find the matrix element and element position for the rhs
pr = kr;
for (label s = 1; s < Nr; s++)
{
const label si = R.rhs()[s].index;
if (c[si] < c[rRef])
{
const scalar exp = R.rhs()[srRef].exponent;
pr *= pow(max(c[rRef], 0.0), exp);
rRef = si;
srRef = s;
}
else
{
const scalar exp = R.rhs()[s].exponent;
pr *= pow(max(c[si], 0.0), exp);
}
}
cr = max(c[rRef], 0.0);
{
const scalar exp = R.rhs()[srRef].exponent;
if (exp < 1.0)
{
if (cr>SMALL)
{
pr *= pow(cr, exp - 1.0);
}
else
{
pr = 0.0;
}
}
else
{
pr *= pow(cr, exp - 1.0);
}
}
return pf*cf - pr*cr;
}
Чтобы получить скорость реакции веществ k, скорости реакции каждой реакции должны быть умножены на стехиометрический коэффициент и суммированы с положительным знаком, если вещества - продукты, и с отрицательным знаком, если вещества - реагенты. (см. также e.g. https://en.wikipedia.org/wiki/Reaction_rate).
Это может быть записано так (смотрите также https://www3.nd.edu/~powers/ame.60636/chemkin2000.pdf и другие книги здесь):
$ \omega_k = \sum_i \omega_i ({\nu_{ki}'} - {\nu_{ki}''}) $
Для нашего примера реакции мы получаем:
$ \omega_k = \frac{d [X_k]}{dt} = \begin{pmatrix}\frac{d[N_2]}{dt} \\ \frac{d[O]}{dt} \\ \frac{d[NO]}{dt} \\ \frac{d[N]}{dt} \\ \frac{d[O_2]}{dt} \\\end{pmatrix} = \begin{pmatrix} -\omega_1 \\ -\omega_1 + \omega_2 \\ \omega_1 + \omega_2 \\ \omega_1 - \omega_2 \\ \omega_2 \\\end{pmatrix} = \begin{pmatrix} -(k_{f1}[NO][N] - k_{r1}[N2][O]) \\ -(k_{f1}[NO][N] - k_{r1}[N2][O]) + k_{f2}[N][O2] - k_{r2}[NO][O] \\ k_{f1}[NO][N] - k_{r1}[N2][O] + k_{f2}[N][O2] - k_{r2}[NO][O] \\ k_{f1}[NO][N] - k_{r1}[N2][O] - (k_{f2}[N][O2] - k_{r2}[NO][O]) \\ k_{f2}[N][O2] - k_{r2}[NO][O] \\\end{pmatrix} $
Рассматривая приведённое выше уравнение, мы видим, что уравнение, описывающее изменение концентрации веществ, является связанным нелинейным дифференциальным уравнением. Расчёт скорости реакции каждого вещества производится в функции файла $FOAM_SRC/thermophysicalModels/chemistryModel/chemistryModel/StandardChemistryModel/StandardChemistryModel.C:
template<class ReactionThermo, class ThermoType>
void Foam::StandardChemistryModel<ReactionThermo, ThermoType>::omega
(
const scalarField& c,
const scalar T,
const scalar p,
scalarField& dcdt
) const
{
scalar pf, cf, pr, cr;
label lRef, rRef;
dcdt = Zero;
forAll(reactions_, i)
{
const Reaction<ThermoType>& R = reactions_[i];
scalar omegai = omega
(
R, c, T, p, pf, cf, lRef, pr, cr, rRef
);
forAll(R.lhs(), s)
{
const label si = R.lhs()[s].index;
const scalar sl = R.lhs()[s].stoichCoeff;
dcdt[si] -= sl*omegai;
}
forAll(R.rhs(), s)
{
const label si = R.rhs()[s].index;
const scalar sr = R.rhs()[s].stoichCoeff;
dcdt[si] += sr*omegai;
}
}
}
2.3 Константы скорости химической реакции
Остальные величины, которые надо определить - это константы скорости прямой и обратной реакции $ k_f $ и $ k_r $.
2.3.1 Константы скорости прямой реакции
Константа скорости прямой реакции вычисляется по уравнению Аррениуса следующим образом (смотрите также [5]):
$ k_f = A T^\beta exp(-T_a/T) $
A - это пер-экспонентный фактор, Ta - температура активации и beta - показатель температуры при расчёте скорости реакции. Код soure может быть найден в $FOAM_SRC/thermophysicalModels/specie/reaction/reactionRate/ArrheniusReactionRate/ArrheniusReactionRateI.H:
inline Foam::scalar Foam::ArrheniusReactionRate::operator()
(
const scalar p,
const scalar T,
const scalarField&
) const
{
scalar ak = A_;
if (mag(beta_) > VSMALL)
{
ak *= pow(T, beta_);
}
if (mag(Ta_) > VSMALL)
{
ak *= exp(-Ta_/T);
}
return ak;
}
2.3.2 Константы обратной скорости
Обратная скорость 1-й реакции вычисляется следующим образом (см. также https://www3.nd.edu/~powers/ame.60636/chemkin2000.pdf) :
$ k_{ri} = \frac{k_{fi}}{K_{ci}} $
Константа равновесия вычисляется следующим образом:
$ {K_{ci}} = K_{pi} (\frac{p_{atm}}{R T})^{\sum_k \nu_{ki}} $
$ \sum \nu_{ki} = \sum \nu_{ki}' - \sum \nu_{ki}'' $ является суммой стехиометрического коэффициента 1-й реакции. Кроме того
$ K_{pi} = exp \left( \sum_k \nu_{ki}( \frac{S^0_k}{R} - \frac{H^0_k}{RT}) \right) $
$ H^0_k $ и $ S^0_k $ являются молярной энтальпией и энтропией вида k. .
Константы обратной скорости подсчитываются в файле $FOAM_SRC/thermophysicalModels/specie/reaction/Reactions/ReversibleReaction/ReversibleReaction.C:
template
<
template<class> class ReactionType,
class ReactionThermo,
class ReactionRate
>
Foam::scalar Foam::ReversibleReaction
<
ReactionType,
ReactionThermo,
ReactionRate
>::kr
(
const scalar kfwd,
const scalar p,
const scalar T,
const scalarField& c
) const
{
return kfwd/max(this->Kc(p, T), 1e-6);
}
Расчёт Kc можно найти в файле $FOAM_SRC/thermophysicalModels/specie/thermo/thermo/thermoI.H.
template<class Thermo, template<class> class Type>
inline Foam::scalar
Foam::species::thermo<Thermo, Type>::K(const scalar p, const scalar T) const
{
scalar arg = -this->Y()*this->G(Pstd, T)/(RR*T);
if (arg < 600)
{
return exp(arg);
}
else
{
return VGREAT;
}
}
template<class Thermo, template<class> class Type>
inline Foam::scalar
Foam::species::thermo<Thermo, Type>::Kp(const scalar p, const scalar T) const
{
return K(p, T);
}
template<class Thermo, template<class> class Type>
inline Foam::scalar
Foam::species::thermo<Thermo, Type>::Kc(const scalar p, const scalar T) const
{
const scalar nm = this->Y()/this->W();
if (equal(nm, SMALL))
{
return Kp(p, T);
}
else
{
return Kp(p, T)*pow(Pstd/(RR*T), nm);
}
}
Не так легко увидеть, как сумма стехиометрических коэффициентов входит в расчёт константы равновесия. Поэтому это объясняется немного более подробно (см. https://www.cfd-online.com/Forums/openfoam/224970-understanding-reverse-rate-calculation-chemical-reactions.html). Для расчёта константы равновесия для каждой реакции задаётся псевдосмесь, в которой термодинамические свойства уравновешиваются стехиометрическим коэффициентом. Это делается в конструкторе объекта реакции путём вызова функции setThermo:
template<class ReactionThermo>
Foam::Reaction<ReactionThermo>::Reaction
(
const speciesTable& species,
const List<specieCoeffs>& lhs,
const List<specieCoeffs>& rhs,
const HashPtrTable<ReactionThermo>& thermoDatabase,
bool initReactionThermo
)
:
ReactionThermo::thermoType(*thermoDatabase[species[0]]),
name_("un-named-reaction-" + Foam::name(getNewReactionID())),
species_(species),
lhs_(lhs),
rhs_(rhs)
{
if (initReactionThermo)
{
setThermo(thermoDatabase);
}
}
template<class ReactionThermo>
void Foam::Reaction<ReactionThermo>::setThermo
(
const HashPtrTable<ReactionThermo>& thermoDatabase
)
{
typename ReactionThermo::thermoType rhsThermo
(
rhs_[0].stoichCoeff
*(*thermoDatabase[species_[rhs_[0].index]]).W()
*(*thermoDatabase[species_[rhs_[0].index]])
);
for (label i=1; i<rhs_.size(); ++i)
{
rhsThermo +=
rhs_[i].stoichCoeff
*(*thermoDatabase[species_[rhs_[i].index]]).W()
*(*thermoDatabase[species_[rhs_[i].index]]);
}
typename ReactionThermo::thermoType lhsThermo
(
lhs_[0].stoichCoeff
*(*thermoDatabase[species_[lhs_[0].index]]).W()
*(*thermoDatabase[species_[lhs_[0].index]])
);
for (label i=1; i<lhs_.size(); ++i)
{
lhsThermo +=
lhs_[i].stoichCoeff
*(*thermoDatabase[species_[lhs_[i].index]]).W()
*(*thermoDatabase[species_[lhs_[i].index]]);
}
ReactionThermo::thermoType::operator=(lhsThermo == rhsThermo);
}
Чтобы определить, как вычисляется константа равновесия Kc, мы должны взглянуть на операторы +=, * и == определённые в термодинамических пакетах. В приведённом выше перечне строятся виды, - термодинамические свойства сначала умножаются на стехиометрический коэффициент и молярный вес, а затем в правой и левой части химической реакции выполняется суммирование видов, для последнего шага применяется оператор == . В качестве наглядного примера для объяснения этой операции возьмем постоянные свойства термодинамического пакета. Выводы, сделанные здесь,могут быть применены и ко всем другим пакетам.
template<class EquationOfState>
inline Foam::hConstThermo<EquationOfState> Foam::operator*
(
const scalar s,
const hConstThermo<EquationOfState>& ct
)
{
return hConstThermo<EquationOfState>
(
s*static_cast<const EquationOfState&>(ct),
ct.Cp_,
ct.Hf_
);
}
template<class EquationOfState>
inline void Foam::hConstThermo<EquationOfState>::operator+=
(
const hConstThermo<EquationOfState>& ct
)
{
scalar Y1 = this->Y();
EquationOfState::operator+=(ct);
if (mag(this->Y()) > SMALL)
{
Y1 /= this->Y();
scalar Y2 = ct.Y()/this->Y();
Cp_ = Y1*Cp_ + Y2*ct.Cp_;
Hf_ = Y1*Hf_ + Y2*ct.Hf_;
}
}
template<class EquationOfState>
inline Foam::hConstThermo<EquationOfState> Foam::operator==
(
const hConstThermo<EquationOfState>& ct1,
const hConstThermo<EquationOfState>& ct2
)
{
EquationOfState eofs
(
static_cast<const EquationOfState&>(ct1)
== static_cast<const EquationOfState&>(ct2)
);
return hConstThermo<EquationOfState>
(
eofs,
ct2.Y()/eofs.Y()*ct2.Cp_
- ct1.Y()/eofs.Y()*ct1.Cp_,
ct2.Y()/eofs.Y()*ct2.Hf_
- ct1.Y()/eofs.Y()*ct1.Hf_
);
}
Первый оператор анализирует * оператор. Когда объект hConstThermo мультиплицируется скаляром s, оператор возвращает новый объект, и только атрибут equationOfState мультиплицируется этим скаляром. По этой причине мы должны посмотреть на оператор * в объекте equationOfState. В качестве примера мы рассмотрим уравнение состояния идеального газа perfectGas:
template<class Specie>
inline Foam::perfectGas<Specie> Foam::operator*
(
const scalar s,
const perfectGas<Specie>& pg
)
{
return perfectGas<Specie>(s*static_cast<const Specie&>(pg));
}
Объект perfectGas возвращается, если атрибут Specie мультиплицируется с этим скаляром. Для атрибута Specie оператор * operator возвращает вид вместе с Y_ attribute мультиплецированным по скаляру s:
inline specie operator*(const scalar s, const specie& st)
{
return specie
(
s*st.Y_,
st.molWeight_
);
}
Таким образом, применение оператора приводит только к изменению атрибута Y_ attribute как в левой, так и в правой части химической реакции:
$ Y' = \nu' W' $
$ Y'' = \nu'' W'' $
Следующий оператор, который мы рассмотрим - это оператор += . Мы видим, что сначала атрибут Y_ attribute сохраняется в переменной Y1. Затем выполняется оператор += уравнения состояния. После этого берется взвешенная сумма термодинамических свойств двух вовлеченных видов..
template<class Specie>
inline void Foam::perfectGas<Specie>::operator+=(const perfectGas<Specie>& pg)
{
Specie::operator+=(pg);
}
inline void specie::operator+=(const specie& st)
{
const scalar sumY = Y_ + st.Y_;
if (mag(sumY) > SMALL)
{
molWeight_ = sumY/(Y_/molWeight_ + st.Y_/st.molWeight_);
}
Y_ = sumY;
}
Легко заметить, что для атрибутов Y и molWeight для левой и правой частей химического уравнения мы получаем:
$ Y' = \sum_k \nu_k' W_k' $
$ Y'' = \sum_k \nu_k'' W_k'' $
$ molWeight' = \frac{\sum_k \nu_k' W_k'}{\sum_k \nu_k'} $
$ molWeight'' = \frac{\sum_k \nu_k'' W_k''}{\sum_k \nu_k''} $
Для термодинамических свойств Hf (химической энтальпии) и удельной теплоёмкости получаем:
$ Hf' = \frac{\sum_k \nu_k' W_k'Hf'_k}{\sum_k \nu_k'W_k'} $
$ Hf'' = \frac{\sum_k \nu_k'' W_k''Hf''_k}{\sum_k \nu_k''W_k''} $
$ cp' = \frac{\sum_k \nu_k' W_k'cp'_k}{\sum_k \nu_k'W_k'} $
$ cp'' = \frac{\sum_k \nu_k'' W_k''cp''_k}{\sum_k \nu_k''W_k''} $
Последний оператор, который мы рассмотрим - это == operator. Также здесь сначала выполняется уравнение состояния == operator. Также, как и для других операторов, уравнение состояния == operator выполняет == видов:
template<class Specie>
inline Foam::perfectGas<Specie> Foam::operator==
(
const perfectGas<Specie>& pg1,
const perfectGas<Specie>& pg2
)
{
return perfectGas<Specie>
(
static_cast<const Specie&>(pg1) == static_cast<const Specie&>(pg2)
);
}
inline specie operator==(const specie& st1, const specie& st2)
{
scalar diffY = st2.Y_ - st1.Y_;
if (mag(diffY) < SMALL)
{
diffY = SMALL;
}
const scalar diffRW = st2.Y_/st2.molWeight_ - st1.Y_/st1.molWeight_;
#ifdef __clang__
// Using intermediate volatile bool to prevent compiler optimising out the
// if block (above) - CLANG 3.7.1
volatile const bool valid = (mag(diffRW) > SMALL);
const scalar molWeight = valid ? diffY/diffRW : GREAT;
#else
scalar molWeight = GREAT;
if (mag(diffRW) > SMALL)
{
molWeight = diffY/diffRW;
}
#endif
return specie(diffY, molWeight);
}
Таким образом, после применения == мы получаем для Y и molWeight атрибут вида:
$ Y = \sum_k \nu_k' W_k' - \sum_k \nu_k'' W_k'' $
$ molWeight' = \frac{\sum_k \nu_k' W_k' - \sum_k \nu_k'' W_k''}{\sum_k \nu_k' - \sum_k \nu_k''} $
Для термодинамических свойств мы имеем:
$ cp = \frac{\sum_k \nu_k' W_k'cp_k' - \sum_k \nu_k'' W_k''cp_k''}{\sum_k \nu_k' W_k' - \sum_k \nu_k'' W_k'' } $
$ Hf = \frac{\sum_k \nu_k' W_k'Hf_k' - \sum_k \nu_k'' W_k''Hf_k''}{\sum_k \nu_k' W_k' - \sum_k \nu_k'' W_k'' } $
В конце концов, если мы посмотрим, как вычисляются энтальпия и энтропия для комбинации hconst и perfectGas, то очень скоро получим то же определение константы равновесия $ K_{ci} $ , что и для chemkin:
template<class Thermo, template<class> class Type>
inline Foam::scalar
Foam::species::thermo<Thermo, Type>::G(const scalar p, const scalar T) const
{
return this->Ha(p, T) - T*this->S(p, T);
}
template<class EquationOfState>
inline Foam::scalar Foam::hConstThermo<EquationOfState>::Ha
(
const scalar p, const scalar T
) const
{
return Hs(p, T) + Hc();
}
template<class EquationOfState>
inline Foam::scalar Foam::hConstThermo<EquationOfState>::Hs
(
const scalar p, const scalar T
) const
{
return Cp_*T + EquationOfState::H(p, T);
}
template<class EquationOfState>
inline Foam::scalar Foam::hConstThermo<EquationOfState>::Hc() const
{
return Hf_;
}
template<class Specie>
inline Foam::scalar Foam::perfectGas<Specie>::S(scalar p, scalar T) const
{
return -this->R()*log(p/Pstd);
}
template<class Specie>
inline Foam::scalar Foam::perfectGas<Specie>::H(scalar p, scalar T) const
{
return 0;
}
2.4 Тепло, выделяющееся в результате химических реакций
Тепло, выделяющееся в результате химических реакций, возвращается функцией Qdot() в файле $FOAM_SRC/thermophysicalModels/chemistryModel/chemistryModel/StandardChemistryModel/StandardChemistryModel.C (если выбрана стандартная химическая модель):
template<class ReactionThermo, class ThermoType>
Foam::tmp<Foam::volScalarField>
Foam::StandardChemistryModel<ReactionThermo, ThermoType>::Qdot() const
{
tmp<volScalarField> tQdot
(
new volScalarField
(
IOobject
(
"Qdot",
this->mesh_.time().timeName(),
this->mesh_,
IOobject::NO_READ,
IOobject::NO_WRITE,
false
),
this->mesh_,
dimensionedScalar(dimEnergy/dimVolume/dimTime, Zero)
)
);
if (this->chemistry_)
{
scalarField& Qdot = tQdot.ref();
forAll(Y_, i)
{
forAll(Qdot, celli)
{
const scalar hi = specieThermo_[i].Hc();
Qdot[celli] -= hi*RR_[i][celli];
}
}
}
return tQdot;
}
</cpp>
Hc является химической энтальпией и зависит от выбранной термодинамической модели.
3. Эволюция видов
{
forAll(Y, specieI)
{
volScalarField& Yi = Y[specieI];
solve
(
fvm::ddt(rho, Yi) - chemistry.RR(specieI),
mesh.solver("Yi")
);
}
}
4. Эволюция энергии
Уравнение энергии выведено из первого закона термодинамики (смотрите также https://en.wikipedia.org/wiki/Enthalpy): Для замкнутых систем изменение внутренней энергии равно количеству тепла, подаваемого в систему. Для открытых систем изменение энтальпии равно теплу, подаваемому в систему. Для первого случая мы имеем:
$ _ u = u_0 + q = u_0 + \int \dot q dt $
| (x) |
Если мы добавим с обеих сторон давление, делённое на плотность, мы получим:
$ _ u + \frac{p}{\rho} = u_0 + \frac{p}{\rho} + \int \dot q dt \ \ , \ \ h = u + \frac{p}{\rho} $
| (x) |
$ u, u_0 $ это энергия и внутренняя энергия в момент времени 0.
{
volScalarField& h = thermo.he();
if (constProp == "volume")
{
h[0] = u0 + p[0]/rho[0] + integratedHeat;
}
else
{
h[0] = h0 + integratedHeat;
}
thermo.correct();
}
С помощью приведённого выше уравнения можно подсчитать эволюцию энтальпии во времени. Зная функциональную зависимость теплоёмкости при постоянном давлении $ c_p $ , можно установить связь между энтальпией и температурой. Пример того, как это происходит в OpenFOAM с полиномиальной зависимостью теплоемкости от температуры, можно найти здесь https://caefn.com/openfoam/temperature-calculation.
5. Эволюция давления
Для расчёта давления используется модель Дальтона: В ней говорится, что термодинамическое давление может быть вычислено как сумма парциальных давлений отдельных видов (см. https://en.wikipedia.org/wiki/Dalton%27s_law):
$ _ p = \sum p_i = \sum n_i \frac{R T}{V} = \sum \frac{m_i}{M_i} \frac{R T}{V} = \sum \frac{y_i}{M_i} \underbrace{ \frac{m_{tot}}{V}}_{\rho_0} R T $
| (x) |
$ p_i, n_i, m_i, M_i, y_i, m_{tot}, \rho_0 $ это парциальное давление, число молекул, масса, молярная масса, массовая доля вида i, общая масса и начальная плотность соответственно.
{
rho = thermo.rho();
if (constProp == "volume")
{
scalar invW = 0.0;
forAll(Y, i)
{
invW += Y[i][0]/specieData[i].W();
}
Rspecific[0] = 1000.0*constant::physicoChemical::R.value()*invW;
p[0] = rho0*Rspecific[0]*thermo.T()[0];
rho[0] = rho0;
}
}
Обратите внимание, что константа 1000 в приведенном выше коде исходит из того факта, что в OpenFOAM молярный вес указан в [kg/kmol]. Смотрите файл $FOAM_SRC/thermophysicalModels/specie/specie/specie.H.
Ссылки
- ↑ Powers, Joseph. Combustion thermodynamics and dynamics. Cambridge University Press, 2016.
- ↑ Powers, Joseph. Combustion thermodynamics and dynamics. Cambridge University Press, 2016.
- ↑ Powers, Joseph. Combustion thermodynamics and dynamics. Cambridge University Press, 2016.
- ↑ Poinsot, Thierry, and Denis Veynante. Theoretical and numerical combustion. RT Edwards, Inc., 2005.
- ↑ Poinsot, Thierry, and Denis Veynante. Theoretical and numerical combustion. RT Edwards, Inc., 2005.
Ссылка на оригинал - https://openfoamwiki.net/index.php/ChemFoam |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|