OpenFOAM 源码深度剖析 · 第二季-02 燃烧与化学反应:combustionModel、化学求解器与 Arrhenius 反应率
燃烧建模是 OpenFOAM 里"物理层次最多"的一块:它同时叠了化学机理(反应+速率) 、湍流-化学相互作用(燃烧模型) 、化学积分与加速(chemistryModel/chemistrySolver/TDAC/ISAT) 、组分输运与热力学(reactionThermo/specie) 四层。本章逐层拆开,所有结论都给出真实文件路径 + 行号区间 + 代码摘录 ,并穿插完整数学推导链 、可直接粘贴的字典模板 、调参表 与报错排查表。
1. 总览:四层结构与一次燃烧步的调用链
1.1 四层结构
swift
① 燃烧模型层(湍流-化学相互作用)
combustionModel (IOdictionary)
└─ CombustionModel<ReactionThermo>(RTS 选择表)
├─ ChemistryCombustion<ReactionThermo> { chemistryPtr_ }
│ └─ laminar → { EDC, PaSR } ,以及 diffusionMulticomponent
└─ ThermoCombustion<ReactionThermo> { thermo_ }
└─ singleStepCombustion
├─ diffusion / infinitelyFastChemistry
└─ eddyDissipationModelBase → EDM
└─ FSD
└─ zoneCombustion / none(直接挂 CombustionModel)
② 化学模型层(反应积分 + 加速)
basicChemistryModel (IOdictionary)
└─ BasicChemistryModel<ReactionThermo>
├─ StandardChemistryModel (TypeName "standard", 继承 ODESystem)
└─ TDACChemistryModel (TypeName "TDAC")
├─ reduction: DRG / DRGEP / PFA / EFA / DAC / none
└─ tabulation: ISAT / none
化学求解器 chemistrySolver<ChemistryModel>:ode / EulerImplicit / none
③ 反应与速率层
Reaction<ThermoType> → { Irreversible / Reversible / NonEquilibriumReversible }
+ ReactionRate → { Arrhenius / thirdBodyArrhenius / FallOff<Troe|SRI|Lindemann>
/ ChemicallyActivated / LangmuirHinshelwood / powerSeries
/ Janev / LandauTeller / infinite }
ReactionList<ThermoType>:从字典读整组反应
④ 组分与热物理层
reactionThermo: rho / psi / psiu / heheuPsi
mixtures: basicSpecieMixture / basicCombustionMixture / reactingMixture /
singleStepReactingMixture / ...
chemistryReaders: foamChemistryReader / chemkinReader
1.2 一次燃烧步的调用链(以 reactingFoam 为例)
主循环 reactingFoam/reactingFoam.C(第 101--124 行)依次执行 UEqn.H、YEqn.H、EEqn.H。组分方程 YEqn.H(第 12--44 行)第一步是 reaction->correct(),随后每条组分方程:
cpp
reaction->correct(); // 推进化学:更新组分、温度、Qdot
Qdot = reaction->Qdot(); // 热释放率 [kg/m/s3]
...
fvScalarMatrix YiEqn
(
fvm::ddt(rho, Yi)
+ mvConvection->fvmDiv(phi, Yi)
- fvm::laplacian(turbulence->muEff(), Yi)
==
reaction->R(Yi) // 该组分的化学源项
+ fvOptions(rho, Yi)
);
...
Yi.clamp_min(0); // 组分不得为负
Yt += Yi;
...
Y[inertIndex] = scalar(1) - Yt; // 惰性组分填充
Y[inertIndex].clamp_min(0);
reaction 的声明在 reactingFoam/createFields.H(第 64--68 行):
cpp
autoPtr<CombustionModel<psiReactionThermo>> reaction
(
CombustionModel<psiReactionThermo>::New(thermo, turbulence())
);
调用链:reaction->correct() → 燃烧模型内部调用 chemistryPtr_->solve(deltaT)(第②层化学积分),EDC/PaSR 还先用湍流信息算反应分数 kappa_ 缩比;reaction->R(Yi) 把化学模型的 RR_[i] 包装成 fvScalarMatrix;reaction->Qdot() 进能量方程 EEqn.H(第 20 行 == Qdot);最后 thermo.correct()(EEqn.H 第 37 行)由新温度反算密度/热物性。
1.3 RTS 注册与运行期选择
本章涉及 4 套独立 RTS 表:
| 层 | 选择表基类 | 选择键(字典) | 注册宏 |
|---|---|---|---|
| 燃烧模型 | CombustionModel<ReactionThermo> |
combustionModel |
makeCombustionTypes/makeCombustionTypesThermo |
| 化学模型+求解器 | BasicChemistryModel<ReactionThermo> |
chemistryType { solver; method; } |
makeChemistrySolverTypes |
| 机理简化 | chemistryReductionMethod |
reduction { method; } |
makeChemistryReductionMethods |
| 化学查表 | chemistryTabulationMethod |
tabulation { method; } |
makeChemistryTabulationMethods |
chemistrySolver/chemistrySolver/makeChemistrySolverTypes.H(第 42--96 行)把"求解器名 < 化学模型名 < 热物性名 >>" 拼成长类型名注册,同一个求解器同时注册成 standard与TDAC` 两个化学模型版本:
cpp
#define makeChemistrySolverType(SS, Comp, Thermo) \
typedef SS<StandardChemistryModel<Comp, Thermo>> SS##Comp##Thermo; \
... \
typedef SS<TDACChemistryModel<Comp, Thermo>> TDAC##SS##Comp##Thermo; \
...
#define makeChemistrySolverTypes(Comp, Thermo) \
makeChemistrySolverType(noChemistrySolver, Comp, Thermo); \
makeChemistrySolverType(EulerImplicit, Comp, Thermo); \
makeChemistrySolverType(ode, Comp, Thermo);
2. 燃烧模型层(第 ① 层)
2.1 基类 combustionModel
文件 :src/combustionModels/CombustionModel/combustionModel.H(第 40--180 行)、combustionModel.C(第 29--114 行)。
cpp
class combustionModel : public IOdictionary // combustionModel.H 第 55-57 行
{
protected:
const fvMesh& mesh_; // 第 80 行
const compressibleTurbulenceModel& turb_; // 第 83 行:湍流模型引用
Switch active_; // 第 86 行:燃烧总开关
dictionary coeffs_; // 第 89 行:${modelType}Coeffs
const word modelType_; // 第 92 行
public:
TypeName("combustionModel"); // 第 98 行
static const word combustionPropertiesName; // 第 101 行
virtual void correct() = 0; // 第 153 行
virtual tmp<fvScalarMatrix> R(volScalarField& Y) const = 0; // 第 156 行
virtual tmp<volScalarField> Qdot() const = 0; // 第 159 行
virtual bool read(); // 第 162 行
};
逐行:
- 第 57 行继承
IOdictionary:燃烧模型自己就是constant/combustionProperties读进来的字典对象,成员可直接get<Switch>("active")、optionalSubDict(...)。 - 第 83 行
turb_:持有湍流模型引用,提供epsilon()/k()/mu()/muEff()/alphaRhoPhi(),是湍流-化学相互作用的输入。combustionModelI.H(第 30--64 行)的mesh()/turbulence()/rho()/phi()/active()/coeffs()都转发到这些成员。 - 第 86/101 行 :
active与字典名"combustionProperties"(后者在combustionModel.C第 38--41 行定义为常量)。 - 第 153/156/159 行三个纯虚 :
correct()/R()/Qdot(),所有燃烧模型必须实现。
构造函数与 IO 对象(combustionModel.C):
cpp
Foam::IOobject Foam::combustionModel::createIOobject
( basicThermo& thermo, const word& combustionProperties ) const
{
IOobject io
(
thermo.phasePropertyName(combustionProperties), // 第 54 行
thermo.db().time().constant(), // 第 55 行:constant/
thermo.db(),
IOobject::MUST_READ,
IOobject::NO_WRITE
);
if (io.typeHeaderOk<IOdictionary>(true)) { io.readOpt(IOobject::MUST_READ_IF_MODIFIED); } // 第 61-64 行
else { io.readOpt(IOobject::NO_READ); } // 第 67 行
return io;
}
Foam::combustionModel::combustionModel(...)
:
IOdictionary(createIOobject(thermo, combustionProperties)),
mesh_(thermo.p().mesh()),
turb_(turb),
active_(getOrDefault<Switch>("active", true)), // 第 87 行
coeffs_(optionalSubDict(modelType + "Coeffs")), // 第 88 行
modelType_(modelType)
{}
- 第 61--68 行
typeHeaderOk:文件不存在则NO_READ,不报错------燃烧模型可缺省。 - 第 87 行 :
active缺省true;常见做法是先设false跑冷态。 read()(第 101--111 行)重读active与${modelType}Coeffs。
2.2 选择器 combustionModel::New
文件 :CombustionModel/combustionModelTemplates.C(第 31--191 行)。
cpp
word combModelName("none"); // 第 49 行
if (combIO.typeHeaderOk<IOdictionary>(false))
{
IOdictionary(combIO).readEntry("combustionModel", combModelName); // 第 52 行
}
else { Info<< "Combustion model not active: " ... << endl; } // 第 56-58 行
const word compCombModelName
( combModelName + '<' + CombustionModel::reactionThermo::typeName + '>' ); // 第 77-80 行
const word thermoCombModelName
( combModelName + '<' + CombustionModel::reactionThermo::typeName + ','
+ thermo.thermoName() + '>' ); // 第 82-86 行
auto ctorIter = cnstrTable.cfind(thermoCombModelName); // 第 91 行
if (!ctorIter.good()) { ctorIter = cnstrTable.cfind(compCombModelName); } // 第 95 行
...
ctorIter.val()(combModelName, thermo, turb, combustionProperties); // 第 189 行
- 第 49 行 :缺省
"none";字典缺失时选noCombustion(TypeName("none"),noCombustion.H第 72 行)。 - 第 52 行 :字典键是
combustionModel(不是type)。 - 第 63--74 行 (未贴)对老式
laminar<psiReactionThermo>写法给出警告:模板参数已不再需要。 - 第 77--96 行 两级查找:先带热物性名的长名,再短名------对应
makeCombustionTypesThermo与makeCombustionTypes(makeCombustionTypes.H第 61--99 行)。
2.3 模板派生与两条并列分支
CombustionModel<ReactionThermo> (CombustionModel/CombustionModel/CombustionModel.H 第 55--133 行):
cpp
template<class ReactionThermo>
class CombustionModel : public combustionModel
{
public:
typedef ReactionThermo reactionThermo; // 第 72 行
TypeName("CombustionModel"); // 第 76 行
declareRunTimeSelectionTable( autoPtr, CombustionModel, dictionary, ...); // 第 80-92 行
static autoPtr<CombustionModel> New(...); // 第 108-113 行
virtual ReactionThermo& thermo() = 0; // 第 123 行
virtual const ReactionThermo& thermo() const = 0; // 第 126 行
};
ChemistryCombustion<ReactionThermo> (ChemistryCombustion/ChemistryCombustion.H 第 53--103 行)------持化学模型指针:
cpp
template<class ReactionThermo>
class ChemistryCombustion : public CombustionModel<ReactionThermo>
{
protected:
autoPtr<BasicChemistryModel<ReactionThermo>> chemistryPtr_; // 第 75 行
...
};
ThermoCombustion<ReactionThermo> (ThermoCombustion/ThermoCombustion.H 第 52--98 行)------持热物性引用:
cpp
template<class ReactionThermo>
class ThermoCombustion : public CombustionModel<ReactionThermo>
{
protected:
ReactionThermo& thermo_; // 第 71 行
...
};
重要纠正 :常见的"燃烧模型二分表"把
diffusion/diffusionMulticomponent归到ChemistryCombustion、把singleStepCombustion归到ThermoCombustion,读源码后证明是错的 :diffusion继承singleStepCombustion,而singleStepCombustion.H(第 59--62 行)继承ThermoCombustion;diffusionMulticomponent.H(第 93--96 行)才继承ChemistryCombustion。singleStepCombustion与eddyDissipationModelBase都没有TypeName,是抽象基类,不能在字典里直接选。
正确的可选项与归属 (以 src/combustionModels 全量 TypeName 检索为准):
| 字典可选名 | 类 | 继承链 | 分支 |
|---|---|---|---|
laminar |
laminar<ReactionThermo> |
ChemistryCombustion |
有限速率 |
EDC |
EDC<ReactionThermo> |
laminar→ChemistryCombustion |
有限速率+细结构 |
PaSR |
PaSR<ReactionThermo> |
laminar→ChemistryCombustion |
有限速率+混合时间 |
diffusionMulticomponent |
diffusionMulticomponent<RTh,TTh> |
ChemistryCombustion |
有限速率+扩散/高斯 |
diffusion |
diffusion<RTh,TTh> |
singleStepCombustion→ThermoCombustion |
混合即燃 |
EDM |
eddyDissipationDiffusionModel<RTh,TTh> |
eddyDissipationModelBase→singleStepCombustion→ThermoCombustion |
涡耗散 |
infinitelyFastChemistry |
infinitelyFastChemistry<RTh,TTh> |
singleStepCombustion→ThermoCombustion |
无限快 |
FSD |
FSD<RTh,TTh> |
singleStepCombustion→ThermoCombustion |
火焰面 |
zoneCombustion |
zoneCombustion<ReactionThermo> |
CombustionModel(直接) |
区域过滤 |
none |
noCombustion<ReactionThermo> |
CombustionModel(直接) |
关闭 |
2.4 laminar:最小的有限速率化学模型
文件 :src/combustionModels/laminar/laminar.H(第 40--138 行)、laminar.C(第 29--176 行)。
cpp
template<class ReactionThermo>
class laminar : public ChemistryCombustion<ReactionThermo>
{
bool integrateReactionRate_; // laminar.H 第 65 行
protected:
tmp<volScalarField> tc() const; // 第 72 行
public:
TypeName("laminar"); // 第 88 行
virtual void correct(); // 第 110 行
virtual tmp<fvScalarMatrix> R(volScalarField& Y) const; // 第 113 行
virtual tmp<volScalarField> Qdot() const; // 第 116 行
virtual bool read(); // 第 119 行
};
laminar.C 构造函数(第 36--64 行)读取 integrateReactionRate(缺省 true,第 53 行),并打印 using integrated/instantaneous reaction rate(第 58/62 行)。correct()(第 85--119 行)------LTS 感知的化学积分:
cpp
if (this->active())
{
if (integrateReactionRate_)
{
if (fv::localEulerDdt::enabled(this->mesh())) // 第 91 行:LTS
{
const scalarField& rDeltaT = fv::localEulerDdt::localRDeltaT(this->mesh());
scalar maxTime;
if (this->coeffs().readIfPresent("maxIntegrationTime", maxTime))
{
this->chemistryPtr_->solve(min(1.0/rDeltaT, maxTime)()); // 第 101 行
}
else { this->chemistryPtr_->solve((1.0/rDeltaT)()); } // 第 106 行
}
else { this->chemistryPtr_->solve(this->mesh().time().deltaTValue()); } // 第 111 行
}
else { this->chemistryPtr_->calculate(); } // 第 116 行
}
integrateReactionRate=true:用 ODE 求解器把反应速率在整个时间步内积分(精度高);false:只算瞬时速率(chemistryPtr_->calculate())。- LTS 开启时用逐单元的
1/rDeltaT作为化学步长(第 91--108 行),可选maxIntegrationTime上限。
R()(第 122--138 行):
cpp
const label specieI = this->thermo().composition().species().find(Y.member()); // 第 132 行
Su += this->chemistryPtr_->RR(specieI); // 第 134 行
把化学模型的组分源项作为显式源 加入矩阵右端。Qdot()(第 141--159 行)取 chemistryPtr_->Qdot()。
2.5 EDC:涡耗散概念(细结构)+ 推导
文件 :src/combustionModels/EDC/EDC.H(第 26--214 行)、EDC.C(第 31--249 行)。
类注释(EDC.H 第 29--94 行)说明:反应只发生在湍动能耗散区(细结构),细结构质量分数与平均停留时间由能量级串模型给出;版本 v1981/v1996/v2005/v2016。
cpp
enum class EDCversions { v1981, v1996, v2005, v2016 }; // 第 115-121 行
const scalar EDCexp1[] = {3, 2, 2, 2}; // 第 126 行
const scalar EDCexp2[] = {3, 3, 2, 2}; // 第 127 行
template<class ReactionThermo>
class EDC : public laminar<ReactionThermo>
{
EDCversions version_; // 第 141 行
scalar C1_, C2_, Cgamma_, Ctau_, exp1_, exp2_; // 第 143-148 行
volScalarField kappa_; // 第 151 行:反应分数
...
};
构造函数(EDC.C 第 33--72 行)从 EDCCoeffs 取 version(缺省 EDCdefaultVersion,第 43--51 行)、C1=0.05774(第 52 行)、C2=0.5(第 53 行)、Cgamma=2.1377(第 54 行)、Ctau=0.4083(第 55 行)、exp1/exp2 按版本取(第 56--57 行),并注册 kappa 场(第 62 行,AUTO_WRITE 第 66 行)。
correct()(第 84--189 行)核心(v2016 分支第 103--149 行,其它版本第 151--181 行):
cpp
// v2016(第 110-127 行)
const scalar nu = mu[i]/(rho[i] + SMALL);
const scalar Da = clamp( sqrt(nu/(epsilon[i] + SMALL))/tc[i], scalar(1e-10), scalar(10) );
const scalar ReT = sqr(k[i])/(nu*epsilon[i] + SMALL);
const scalar CtauI = min(C1_/(Da*sqrt(ReT + 1)), 2.1377);
const scalar CgammaI = clamp(C2_*sqrt(Da*(ReT + 1)), scalar(0.4082), scalar(5));
const scalar gammaL = CgammaI*pow025(nu*epsilon[i]/(sqr(k[i]) + SMALL));
tauStar[i] = CtauI*sqrt(nu/(epsilon[i] + SMALL));
// 非 v2016(第 156-159 行)
const scalar gammaL = Cgamma_*pow025(nu*epsilon[i]/(sqr(k[i]) + SMALL));
tauStar[i] = Ctau_*sqrt(nu/(epsilon[i] + SMALL));
// 反应分数(两个分支共用,第 131-145 / 160-176 行)
if (gammaL >= 1) { kappa_[i] = 1; }
else { kappa_[i] = max( min( pow(gammaL, exp1_)/(1 - pow(gammaL, exp2_)), 1 ), 0 ); }
...
this->chemistryPtr_->solve(tauStar); // 第 187 行:用 tauStar 作为化学时间
R()(第 192--197 行)与 Qdot()(第 200--218 行)用 kappa_ 缩比 laminar 结果:
cpp
return kappa_*laminar<ReactionThermo>::R(Y); // 第 196 行
...
tQdot.ref() = kappa_*this->chemistryPtr_->Qdot(); // 第 214 行
数学推导 A:EDC 的细结构时间尺度与反应分数
设湍动能 k、耗散率 ϵ、运动黏度 ν,Kolmogorov 时间尺度 τη=(ν/ϵ)1/2。
- 细结构质量分数 (源码
gammaL = Cgamma*(nu*epsilon/k^2)^{1/4}):
γL=Cγ(k2νϵ)1/4
γL→1 表示整个控制体都是细结构、全部参与反应。
- 细结构停留时间 (源码
tauStar = Ctau*sqrt(nu/epsilon)):
τ∗=Cττη=Cτ(ν/ϵ)1/2
- 反应分数 (源码
kappa = gammaL^exp1/(1-gammaL^exp2)):
κ=1−γLe2γLe1∈0,1
(e1,e2):v2005 取 (2,2)、v1981 取 (3,3)、v1996 取 (2,3)、v2016 取 (2,2)。
- v2016 的 Da/Re 局部化 :用化学时间 τc=tc 定义湍流丹姆科勒数 Da=ν/ϵ /τc、湍流雷诺数 ReT=k2/(νϵ),把常数换成局部值:
Cτ,I=min(DaReT+1 C1,2.1377),Cγ,I=clamp(C2Da(ReT+1) ,0.4082,5)
- 源项缩比 : ω˙EDC=κω˙laminar,即化学只在其时空占比为 κ 的细结构里发生。
2.6 PaSR:部分搅拌反应器 + 推导
文件 :src/combustionModels/PaSR/PaSR.H(第 26--136 行)、PaSR.C(第 31--144 行)。
cpp
template<class ReactionThermo>
class PaSR : public laminar<ReactionThermo>
{
scalar Cmix_; // PaSR.H 第 68 行:混合常数
volScalarField kappa_; // 第 71 行
public:
TypeName("PaSR"); // 第 86 行
...
};
构造函数(PaSR.C 第 33--58 行):Cmix_(this->coeffs().getScalar("Cmix"))(第 43 行,必填 )。correct()(第 70--107 行):
cpp
if (this->active())
{
laminar<ReactionThermo>::correct(); // 第 75 行:先做化学积分
const auto& epsilon = this->turbulence().epsilon();
const auto& muEff = this->turbulence().muEff();
const auto& tc = this->tc();
const auto& rho = this->rho();
forAll(epsilon, i)
{
const scalar tk = Cmix_*sqrt(max(muEff[i]/rho[i]/(epsilon[i] + SMALL), 0)); // 第 91-92 行
if (tk > SMALL) { kappa_[i] = tc[i]/(tc[i] + tk); } // 第 96 行
else { kappa_[i] = 1.0; } // 第 100 行
}
kappa_.correctBoundaryConditions();
}
R()(第 110--115 行)return kappa_*laminar<ReactionThermo>::R(Y);;Qdot()(第 118--128 行)同样用 kappa_ 缩比。
数学推导 B:PaSR 的细化分区与混合时间尺度
PaSR 把每个控制体视为"部分搅拌反应器":一部分物质处于反应区,其余仍在未混合状态。湍流混合时间尺度:
τmix=Cmixρϵμeff =Cmixϵνeff (源码第 91-92 行 'tk')
反应分数取"反应时间占(反应+混合)时间"之比:
κ=τc+τmixτc(源码第 96 行)
- τc≪τmix(混合控制): κ→0,速率由湍流决定;
- τc≫τmix(有限速率控制): κ→1,退化为
laminar。
EDC 用细结构 γL,τ∗ 构造 κ,PaSR 用 τc/(τc+τmix);二者最终都做 kappa_*laminar::R。Cmix 越大, τmix 越大、 κ 越接近 1;网格越细应越小(PaSR.H 第 35--37 行注释)。
2.7 singleStepCombustion / EDM / diffusion
singleStepCombustion (singleStepCombustion/singleStepCombustion.H 第 58--114 行)是单步反应基类,继承 ThermoCombustion:
cpp
protected:
singleStepReactingMixture<ThermoType>* singleMixturePtr_; // 第 77 行
volScalarField wFuel_; // 第 80 行
bool semiImplicit_; // 第 83 行(必填)
构造函数(.C 第 39--90 行)第 65--79 行用 isA<singleStepReactingMixture<ThermoType>> 校验热物性,否则 FatalError。R()(第 102--126 行):
cpp
volScalarField wSpecie( wFuel_*singleMixturePtr_->specieStoichCoeffs()[specieI] ); // 第 113 行
if (semiImplicit_)
{
const label fNorm = singleMixturePtr_->specieProd()[specieI]; // 第 118 行
const volScalarField fres(singleMixturePtr_->fres(specieI)); // 第 119 行
wSpecie /= max(fNorm*(Y - fres), scalar(1e-2)); // 第 120 行
return -fNorm*wSpecie*fres + scalar(fNorm)*fvm::Sp(wSpecie, Y); // 第 122 行
}
return wSpecie + fvm::Sp(0.0*wSpecie, Y); // 第 125 行
semiImplicit 用 fvm::Sp 把源项线性化为隐式对角,稳定性更好;否则纯显式。Qdot()(第 129--137 行):-singleMixturePtr_->qFuel()*(R(YFuel) & YFuel)。
eddyDissipationModelBase (eddyDissipationModelBase.H 第 56--116 行)------TypeName 无(抽象基类),CEDC_(第 64 行)必填。rtTurb()(.C 第 67--78 行)= CEDC*epsilon/max(k,SMALL)。correct()(第 81--113 行):
cpp
this->wFuel_ == Zero; // 第 84 行
if (this->active())
{
this->singleMixturePtr_->fresCorrect();
const label fuelI = this->singleMixturePtr_->fuelIndex();
const volScalarField& YFuel = this->thermo_.composition().Y()[fuelI];
const dimensionedScalar s = this->singleMixturePtr_->s();
if (this->thermo_.composition().contains("O2"))
{
const volScalarField& YO2 = this->thermo_.composition().Y("O2");
this->wFuel_ == this->rho() * min(YFuel, YO2/s.value()) * timeScale(); // 第 100-103 行
}
else { FatalErrorInFunction << "You selected a combustion model that requires O2 mass ..." ; } // 第 107-110 行
}
即 EDM 经典形式 wF=ρmin(YF,YO/s)τmix−1。
eddyDissipationDiffusionModel (eddyDissipationDiffusionModel.H 第 60--118 行):TypeName("EDM")(第 86 行)、Cd_(第 68 行)、timeScale()(第 108 行)、rtDiff()(第 111 行)。注释第 34--35 行:时间尺度取湍流与扩散二者的较小者。
diffusion (diffusion/diffusion.H 第 58--110 行、.C 第 69--94 行):继承 singleStepCombustion,TypeName("diffusion")(第 84 行),C_/oxidantName_(第 66/69 行,oxidant 缺省 "O2" 第 56 行)。correct():
cpp
this->wFuel_ ==
C_*this->turbulence().muEff() // 第 89 行
*mag(fvc::grad(YFuel) & fvc::grad(YO2)) // 第 90 行
*pos0(YFuel)*pos0(YO2); // 第 91 行
即"混合即燃":速率正比于燃料与氧化剂梯度的重叠,pos0 保证只在两者都为正时反应。
2.8 其它模型
-
infinitelyFastChemistry(infinitelyFastChemistry/infinitelyFastChemistry.H第 58--107 行):继承singleStepCombustion,TypeName("infinitelyFastChemistry")(第 81 行),常数C_(第 66 行)。 -
diffusionMulticomponent(diffusionMulticomponent/diffusionMulticomponent.H第 92--203 行):唯一进入ChemistryCombustion的扩散型模型,TypeName("diffusionMulticomponent")(第 166 行)。类注释(第 34--65 行)给出字典:cppdiffusionMulticomponentCoeffs { Ci (1.0 1.0); // Default to 1 fuels (CH4 CO); oxidants (O2 O2); YoxStream (0.23 0.23); // Default to 0.23 YfStream (1.0 1.0); // Default to 1.0 sigma (0.02 0.02); // Default to 0.02 oxidantRes (0.025 0.005); ftCorr (0.0 0.0); // Default to 0.0 laminarIgn false; // Default false }每个反应算层流有限速率源项,与交叉扩散速率 D⋅prob⋅μeff⋅∣∇Yi⋅∇Yj∣ 取小; D=Cf02, f02=1+(O2/oxidantRes)2。
-
FSD(FSD/FSD.H第 85--164 行):火焰面密度,继承singleStepCombustion,TypeName("FSD")(第 138 行)。燃料源项 =mgftpcωFuel(类注释第 32--62 行);消耗速度由松弛方程逼近平衡值。下设reactionRateFlameAreaModels/relaxation(relaxation.H第 54--112 行,持consumptionSpeed与C_/alpha_)与consumptionSpeed(consumptionSpeed.H第 52--130 行,参数 ω0,η,σExt,ωMin)。注释强调 FSD 目前只在 LES 框架下工作。 -
zoneCombustion(zoneCombustion/zoneCombustion.H第 56--126 行):包装器,持combustionModelPtr_(第 64 行)与zoneNames_(第 67 行),filter()(第 73/76 行)按 cellZone 掩膜,只在指定区域激活反应。TypeName("zoneCombustion")(第 88 行)。 -
none/noCombustion(noCombustion/noCombustion.H第 72 行):R()/Qdot()返回零。
2.9 燃烧模型对比与选型表
| 模型 | 分支 | 有限速率化学 | 关键常数 | 机制 | 典型用途 |
|---|---|---|---|---|---|
laminar |
ChemistryCombustion | 是 | integrateReactionRate |
直接 ODE 积分 | 层流火焰、chemFoam |
EDC |
laminar | 是 | version/C1/C2/Cgamma/Ctau/exp1/exp2 |
细结构 κ 缩比 | 非预混/部分预混湍流火焰 |
PaSR |
laminar | 是 | Cmix |
时间尺度比 τc/(τc+τmix) | 燃气轮机/内燃机 |
diffusionMulticomponent |
ChemistryCombustion | 是 | Ci/sigma/oxidantRes/laminarIgn |
层流速率与交叉扩散取小 | 多燃料扩散燃烧 |
diffusion |
singleStep→Thermo | 否 | C/oxidant |
混合即燃 | 简单扩散火焰、教学 |
EDM |
eddyDissipation→singleStep→Thermo | 否 | CEDC/Cd/semiImplicit |
涡耗散 | 工程简化 |
infinitelyFastChemistry |
singleStep→Thermo | 否 | C |
无限快+混合 | 快速混合极限 |
FSD |
singleStep→Thermo | 否 | Cv/C/ftMin/ftMax/ftDim |
火焰面密度 | LES 预混 |
zoneCombustion |
CombustionModel | 取决于被包装 | 被包装模型 + zones | 区域过滤 | 局部点火 |
none |
CombustionModel | 否 | --- | 关闭 | 冷态启动 |
选型:要机理忠实解→laminar+ode+seulex;湍流非预混细节机理→PaSR/EDC;只要混合控制火焰→EDM/diffusion+singleStepReactingMixture;LES 火焰面→FSD;局部燃烧→zoneCombustion。
官方教程的模型/求解器组合(真实文件):
| 教程 | combustionModel |
化学求解器 | 关键设置 |
|---|---|---|---|
reactingFoam/laminar/counterFlowFlame2D_GRI |
laminar |
ode(seulex) |
combustionProperties 第 17 行、chemistryProperties 第 19/28 行 |
reactingFoam/laminar/counterFlowFlame2D_GRI_TDAC |
laminar |
ode+TDAC |
加 reduction/tabulation |
reactingFoam/RAS/chokedNozzle |
EDC(active false) |
ode |
先冷态(combustionProperties 第 17/19 行 EDC/active false,EDCCoeffs{version v2005}) |
reactingFoam/RAS/DLR_A_LTS |
EDC |
ode+TDAC |
EDCCoeffs{version v2005; active true};chemistryType{solver ode; method TDAC} + tabulation{method ISAT} |
fireFoam/LES/smallPoolFire3D |
EDM |
noChemistrySolver |
combustionProperties 第 17--24 行 EDM/EDMCoeffs{semiImplicit no; Cd 1; CEDC 1};chemistryProperties 第 19 行 solver noChemistrySolver |
chemFoam/ic8h18_TDAC |
无(单胞) | ode+TDAC |
chemistryType{solver ode; method TDAC};reduction{method DAC; initialSet{CO;IC8H18;HO2}};tabulation{method none} |
chemFoam/gri / chemFoam/h2 |
无(单胞) | ode |
直接驱动机理 |
从这张表能读出一个规律:只要用 EDM/diffusion/单步模型,化学求解器就退化成 noChemistrySolver (因为单步模型的反应速率由湍流混合决定,不需要做有限速率化学积分);而用 laminar/EDC/PaSR 时化学求解器才真正干活(ode 或 EulerImplicit)。这也解释了 §2.9 里"是否有 chemistryPtr_"的分支差异。
3. 化学模型层(第 ② 层)
3.1 basicChemistryModel
文件 :chemistryModel/BasicChemistryModel/BasicChemistryModel.H(第 26--207 行)、.C(第 27--87 行)、BasicChemistryModelI.H(第 28--57 行)。
cpp
class basicChemistryModel : public IOdictionary // .H 第 59-61 行
{
protected:
const fvMesh& mesh_; // 第 77 行
Switch chemistry_; // 第 80 行:化学总开关
const scalar deltaTChemIni_; // 第 83 行:初始化学步长
const scalar deltaTChemMax_; // 第 86 行:最大化学步长
volScalarField::Internal deltaTChem_; // 第 89 行:逐单元当前步长
public:
TypeName("basicChemistryModel"); // 第 105 行
virtual label nSpecie() const = 0; // 第 137 行
virtual label nReaction() const = 0; // 第 140 行
virtual const volScalarField::Internal& RR(const label i) const = 0; // 第 151 行
virtual tmp<volScalarField::Internal> calculateRR(label reactioni, label speciei) const = 0; // 第 163-167 行
virtual void calculate() = 0; // 第 173 行
virtual scalar solve(const scalar deltaT) = 0; // 第 177 行
virtual scalar solve(const scalarField& deltaT) = 0; // 第 181 行
virtual tmp<volScalarField> tc() const = 0; // 第 184 行
virtual tmp<volScalarField> Qdot() const = 0; // 第 187 行
};
构造函数(.C 第 48--78 行):
cpp
IOdictionary( IOobject( thermo.phasePropertyName("chemistryProperties"), // 第 54 行
thermo.db().time().constant(), thermo.db(),
IOobject::MUST_READ_IF_MODIFIED, IOobject::NO_WRITE ) ),
mesh_(thermo.p().mesh()),
chemistry_(get<Switch>("chemistry")), // 第 62 行:必填
deltaTChemIni_(get<scalar>("initialChemicalTimeStep")), // 第 63 行:必填
deltaTChemMax_(getOrDefault<scalar>("maxChemicalTimeStep", GREAT)), // 第 64 行
deltaTChem_( IOobject(...), mesh(), dimensionedScalar("deltaTChem0", dimTime, deltaTChemIni_) ) // 第 65-77 行
三个时间尺度 :deltaTChemIni_(初始化学步长,字典 initialChemicalTimeStep)→ deltaTChem_ 的初值;deltaTChemMax_(字典 maxChemicalTimeStep,缺省 GREAT)防止化学积分跨过反应区;deltaTChem_ 是逐单元自适应步长。chemistry 与燃烧模型的 active 是两个独立开关。
3.2 选择器 basicChemistryModel::New
文件 :chemistryModel/BasicChemistryModel/basicChemistryModelTemplates.C(第 34--193 行)。
cpp
const dictionary* subDictPtr = chemistryDict.findDict("chemistryType"); // 第 53 行
if (!subDictPtr) { FatalErrorInFunction << "Template parameter based chemistry solver "
"selection is no longer supported. Please create a chemistryType dictionary" ...; } // 第 55-67 行
const word solverName = chemistryTypeDict.getCompat<word>("solver", {{"chemistrySolver", -1712}}); // 第 71-78 行
const word methodName = chemistryTypeDict.getOrDefault<word>("method",
chemistryTypeDict.getOrDefault<bool>("TDAC", false) ? "TDAC" : "standard"); // 第 80-89 行
const word chemSolverCompThermoName
( solverName + '<' + methodName + '<'
+ ChemistryModel::reactionThermo::typeName + ',' + thermo.thermoName() + ">>" ); // 第 100-105 行
auto* ctorPtr = cnstrTable.lookup(chemSolverCompThermoName, nullptr); // 第 110 行
- 第 53 行 :强制要求
chemistryType子字典,否则FatalError并打印正确写法(solver ode; method standard;)。 - 第 71--78 行
getCompat:solver为首选键,chemistrySolver为兼容旧名。 - 第 80--89 行 :
method优先;旧布尔TDAC键则true→"TDAC"。 - 第 100--105 行 :拼成
ode<TDAC<psiReactionThermo,psiThermo...>>,与 §1.3 注册一致。
3.3 StandardChemistryModel
文件 :chemistryModel/StandardChemistryModel/StandardChemistryModel.H(第 26--297 行)、.C(第 36--757 行)。
cpp
template<class ReactionThermo, class ThermoType>
class StandardChemistryModel
:
public BasicChemistryModel<ReactionThermo>, // .H 第 64 行
public ODESystem // 第 65 行:提供 derivatives/jacobian
{
template<class DeltaTType> scalar solve(const DeltaTType& deltaT); // 第 71-72 行
protected:
PtrList<volScalarField>& Y_; // 第 95 行
const PtrList<Reaction<ThermoType>>& reactions_; // 第 98 行
const PtrList<ThermoType>& specieThermo_; // 第 101 行
label nSpecie_, nReaction_; // 第 104/107 行
scalar Treact_; // 第 110 行:低于此温度不反应
PtrList<volScalarField::Internal> RR_; // 第 113 行:组分源项
mutable scalarField c_, dcdt_; // 第 116/119 行
public:
TypeName("standard"); // 第 132 行
virtual void omega(const scalarField& c, const scalar T, const scalar p, scalarField& dcdt) const; // 第 166-172 行
virtual scalar omega(const Reaction<ThermoType>& r, ..., scalar& pf, scalar& cf,
label& lRef, scalar& pr, scalar& cr, label& rRef) const; // 第 176-188 行
virtual void derivatives(const scalar t, const scalarField& c, scalarField& dcdt) const; // 第 253-258 行
virtual void jacobian(const scalar t, const scalarField& c, scalarField& dcdt,
scalarSquareMatrix& dfdc) const; // 第 260-266 行
virtual void solve(scalarField& c, scalar& T, scalar& p, scalar& deltaT, scalar& subDeltaT) const = 0; // 第 268-275 行
};
双重继承 (BasicChemistryModel + ODESystem)是骨架:前者管字典/时间步/源项,后者提供 nEqns()/derivatives()/jacobian()。第 268--275 行的 solve 是纯虚 ,由第③层化学求解器(ode/EulerImplicit)实现------这就是第③层通过模板多继承"插"进第②层的方式。
构造函数(.C 第 36--93 行):Y_(this->thermo().composition().Y())(第 44 行)、reactions_(dynamic_cast<const reactingMixture<ThermoType>&>(this->thermo()))(第 47 行,要求 mixture reactingMixture )、specieThermo_(...speciesData())(第 50--53 行)、Treact_(getOrDefault<scalar>("Treact", 0.0))(第 57--64 行),并为每个组分创建 RR_ 场(第 70--89 行),末尾打印 Number of species / reactions(第 91--92 行)。
omega(c,T,p,dcdt)(第 106--143 行)------遍历所有反应累积源项:
cpp
dcdt = Zero;
forAll(reactions_, i)
{
const Reaction<ThermoType>& R = reactions_[i];
scalar omegai = omega(R, c, T, p, pf, cf, lRef, pr, cr, rRef); // 第 124-127 行
forAll(R.lhs(), s) { dcdt[R.lhs()[s].index] -= R.lhs()[s].stoichCoeff*omegai; } // 第 133 行
forAll(R.rhs(), s) { dcdt[R.rhs()[s].index] += R.rhs()[s].stoichCoeff*omegai; } // 第 140 行
}
omega(R,...) (第 167--276 行)------单反应的净速率(反应源项的心脏):
cpp
const scalar kf = R.kf(p, T, c); // 第 182 行:正向速率常数(→ Arrhenius)
const scalar kr = R.kr(kf, p, T, c); // 第 183 行:逆向速率常数
pf = 1.0; pr = 1.0;
label slRef = 0; lRef = R.lhs()[slRef].index;
pf = kf;
for (label s = 1; s < Nl; s++) // 第 195-211 行:累积正向浓度幂,并追踪最小浓度组分
{
const label si = R.lhs()[s].index;
if (c[si] < c[lRef]) { pf *= pow(max(c[lRef],0.0), R.lhs()[slRef].exponent); lRef = si; slRef = s; }
else { pf *= pow(max(c[si],0.0), R.lhs()[s].exponent); }
}
cf = max(c[lRef], 0.0); // 第 212 行:参考组分浓度(最小者)
{ /* 对参考组分做分数级数处理,第 214-231 行 */ }
... 逆向 pr/cr 同理(第 233-273 行) ...
return pf*cf - pr*cr; // 第 275 行:净反应速率
lRef/rRef 是"参考组分"(反应物里浓度最小者)。把最小浓度组分单独拉出来,既便于后续隐式化(EulerImplicit 与半隐式源项用到),也减少 pow 调用。分数级数 e<1 且浓度趋 0 时把 pf 置零,避免 0负数 发散。
数学推导 C:Arrhenius 速率 + 反应源项
- 速率常数 (
ArrheniusReactionRateI.H第 57--77 行,见 §5.4):
kf(T)=ATβexp(−TTa),Ta=RuEa
逆向由平衡常数给出(ReversibleReaction.C 第 131 行): kr=kf/Kc。
- 质量作用定律 :对 ∑jνj′Sj⇌∑jνj′′Sj,
ωr=kfj∏cjνj′−krj∏cjνj′′(源码第 275 行)
- 组分摩尔浓度源项:
dtdci=r∑(νi,r′′−νi,r′)ωr(源码第 133/140 行)
- 质量源项 (
calculate第 652 行,calculateRR第 615 行):
RRi=dtdciWi,ci=WiρYi
derivatives(第 279--324 行)------组分 + 温度 ODE 右端:
cpp
const scalar T = c[nSpecie_]; // 第 287 行
const scalar p = c[nSpecie_ + 1]; // 第 288 行
forAll(c_, i) { c_[i] = max(c[i], 0.0); } // 第 290-293 行:浓度非负
omega(c_, T, p, dcdt); // 第 295 行
scalar rho = 0.0; for (i) { rho += W_i*c_[i]; } // 第 299-304 行
scalar cp = 0.0; for (i) { cp += c_[i]*specieThermo_[i].cp(p,T); } cp /= rho; // 第 305-310 行
scalar dT = 0.0; for (i) { dT += specieThermo_[i].ha(p,T)*dcdt[i]; } dT /= rho*cp; // 第 312-318 行
dcdt[nSpecie_] = -dT; // 第 320 行:温度在状态向量里,导数为 -dT
dcdt[nSpecie_ + 1] = 0.0; // 第 323 行:定压
数学推导 D:能量 ODE 与定压假设
定压(dp/dt=0)、绝热( dh=0), h=∑Yihi(T):
0=dtdh=i∑hidtdYi+cˉpdtdT
用 Yi=ciWi/ρ 换算并对摩尔浓度求导,得
dtdT=−ρcˉp1i∑hidcdti
源码 dT = Σ h_i·dcdt[i]/(rho*cp) 后写 dcdt[nSpecie_] = -dT(温度在状态向量第 nSpecie_ 位,导数为 −dT)。
jacobian(第 327--466 行)------刚性积分用的雅可比:
cpp
dfdc = Zero; // 第 344 行
omega(c_, T, p, dcdt); // 第 347 行
forAll(reactions_, ri)
{
const Reaction<ThermoType>& R = reactions_[ri];
const scalar kf0 = R.kf(p, T, c_);
const scalar kr0 = R.kr(kf0, p, T, c_);
forAll(R.lhs(), j) // 对反应物 j 求导:第 356-400 行
{
scalar kf = kf0;
forAll(R.lhs(), i)
{
if (i == j) { kf *= R.lhs()[i].exponent*pow(c_[si], el-1.0); } // 指数减一
else { kf *= pow(c_[si], el); }
}
forAll(R.lhs(), i) { dfdc(si, sj) -= sl*kf; } // 第 392 行
forAll(R.rhs(), i) { dfdc(si, sj) += sr*kf; } // 第 398 行
}
forAll(R.rhs(), j) { ... kr 同理 ... } // 第 402-446 行
}
const scalar delta = 1.0e-3; // 第 450 行:温度列用中心差分
omega(c_, T + delta, p, dcdt_); ... omega(c_, T - delta, p, dcdt_);
dfdc(i, nSpecie_) = 0.5*(dfdc(i, nSpecie_) - dcdt_[i])/delta; // 第 461 行
dfdc(nSpecie_, nSpecie_) = 0; // 第 464 行
对 kf∏ciei 求 ∂/∂cj:被求导组分的指数减一(el-1),其余保持幂------链式法则在质量作用定律上的直接实现。温度列用 ±10−3 中心差分(含 exp(−Ta/T) 的导数较长)。温度/压力自身导数为 0。
tc()(第 469--529 行)------化学时间尺度,供 EDC/PaSR:
cpp
c_[i] = rhoi*Y_[i][celli]/specieThermo_[i].W(); cSum += c_[i]; // 第 506 行
forAll(reactions_, i) { omega(R, c_, Ti, pi, ...); forAll(R.rhs(), s) { tc[celli] += R.rhs()[s].stoichCoeff*pf*cf; } } // 第 518 行
tc[celli] = nReaction*cSum/tc[celli]; // 第 522 行
Qdot() (第 532--560 行):Qdot[celli] -= specieThermo_[i].Hc()*RR_[i][celli](第 553 行),即 Q˙=−∑iHc,iRRi。
calculate() (第 622--655 行):只算瞬时速率不推进时间(laminar::correct() 的 integrateReactionRate=false 路径):c_[i]=rhoi*Yi/W(第 645 行)、omega(c_,Ti,pi,dcdt_)(第 648 行)、RR_[i][celli]=dcdt_[i]*W(第 652 行)。
solve<DeltaTType>(第 658--729 行)------每个单元做化学积分:
cpp
BasicChemistryModel<ReactionThermo>::correct(); // 第 665 行
if (!this->chemistry_) { return deltaTMin; } // 第 669 行
forAll(rho, celli)
{
scalar Ti = T[celli];
if (Ti > Treact_) // 第 686 行
{
for (label i=0;i<nSpecie_;i++) { c_[i]=rhoi*Y_[i][celli]/W; c0[i]=c_[i]; }
scalar timeLeft = deltaT[celli]; // 第 698 行
while (timeLeft > SMALL) // 第 701 行:子步循环
{
scalar dt = timeLeft;
this->solve(c_, Ti, pi, dt, this->deltaTChem_[celli]); // 第 704 行
timeLeft -= dt;
}
deltaTMin = min(this->deltaTChem_[celli], deltaTMin); // 第 708 行
this->deltaTChem_[celli] = min(this->deltaTChem_[celli], this->deltaTChemMax_); // 第 710-711 行
for (label i=0;i<nSpecie_;i++)
{ RR_[i][celli] = (c_[i]-c0[i])*specieThermo_[i].W()/deltaT[celli]; } // 第 715-716 行
}
else { for (label i=0;i<nSpecie_;i++) { RR_[i][celli]=0; } } // 第 721-724 行
}
- 第 701--706 行 :流动时间步内做化学子循环 ;求解器把
dt缩到稳定值并回写deltaTChem_。 - 第 715--716 行 :源项用"实际浓度变化 / 流动时间步"的差分 RRi=(cinew−ciold)Wi/Δt,保证化学积分与输运方程守恒自洽。
两个入口(第 732--754 行):solve(scalar) 用 UniformField,并 min(..., 2*deltaT)(时间步最多变化 2 倍,第 741 行);solve(scalarField) 直接转发给逐单元版本(LTS 用)。
calculateRR(reactionI,speciei) (第 563--619 行)只算单反应单组分:RR[celli]=w*specieThermo_[si].W()(第 615 行),供 specieReactionRates functionObject。
3.4 TDACChemistryModel:加速调度
文件 :chemistryModel/TDACChemistryModel/TDACChemistryModel.H(第 26--313 行)、.C(第 36--948 行)。
cpp
template<class ReactionThermo, class ThermoType>
class TDACChemistryModel : public StandardChemistryModel<ReactionThermo, ThermoType>
{
bool variableTimeStep_; // .H 第 90 行
label timeSteps_; // 第 92 行
label NsDAC_; // 第 95 行:简化后组分数
scalarField completeC_, simplifiedC_; // 第 96-97 行
Field<bool> reactionsDisabled_; // 第 98 行
Field<label> completeToSimplifiedIndex_; // 第 100 行
DynamicList<label> simplifiedToCompleteIndex_; // 第 101 行
autoPtr<chemistryReductionMethod<...>> mechRed_; // 第 102-103 行
autoPtr<chemistryTabulationMethod<...>> tabulation_; // 第 106-107 行
volScalarField tabulationResults_; // 第 131 行:0=add 1=grow 2=retrieve
public:
TypeName("TDAC"); // 第 152 行
};
构造函数(.C 第 36--142 行):
cpp
variableTimeStep_( this->mesh().time().controlDict().getOrDefault("adjustTimeStep", false)
|| fv::localEulerDdt::enabled(this->mesh()) ) // 第 44-51 行
...
mechRed_ = chemistryReductionMethod<...>::New(*this, *this); // 第 89-93 行
if (mechRed_->active())
{
forAll(this->Y(), i)
{
IOobject header( this->Y()[i].name(), ..., IOobject::NO_READ );
if (!header.typeHeaderOk<volScalarField>(true))
{
composition.setInactive(i); // 第 113 行:缺场文件的组分设非激活
this->Y()[i].writeOpt(IOobject::NO_WRITE);
}
}
}
tabulation_ = chemistryTabulationMethod<...>::New(*this, *this); // 第 119-123 行
反直觉点 :若某组分的场文件不存在,
TDAC会把它setInactive并禁写盘(第 111--115 行)------简化掉的组分可以不出现在0/。机理声明与0/场文件不一致时会被"静默简化",务必核对。
omega(第 154--205 行)在 StandardChemistryModel::omega 基础上加简化映射:跳过 reactionsDisabled_[i] 的反应(第 172 行),并把完整索引 si 映射到简化索引 completeToSimplifiedIndex_[si](第 186 行)。derivatives/jacobian(第 317--582 行)同样做"完整 c ↔ 简化 c"映射,用 completeC_ 保存完整浓度,只更新简化子集;jacobian 只对未剔除反应累加(第 438 行)。
solve<DeltaTType>(第 604--890 行)调度:
cpp
timeSteps_++;
forAll(rho, celli)
{
for (label i=0;i<nSpecie_;i++) { phiq[i] = Yi[celli]; } // 第 680 行:Yi
phiq[nSpecie()] = Ti; phiq[nSpecie()+1] = pi; // 第 682-683 行:T,p
if (tabulation_->variableTimeStep()) { phiq[nSpecie()+2] = deltaT[celli]; } // 第 684-687 行
if (tabulation_->active() && tabulation_->retrieve(phiq, Rphiq)) // 第 701 行:先查表
{
for (label i=0;i<nSpecie();i++) { c[i] = rhoi*Rphiq[i]/W; } // 第 706 行
}
else
{
if (reduced) { mechRed_->reduceMechanism(c, Ti, pi); } // 第 723 行:简化机理
while (timeLeft > SMALL) // 第 732 行:化学子步
{
scalar dt = timeLeft;
if (reduced)
{
completeC_ = c;
this->solve(simplifiedC_, Ti, pi, dt, this->deltaTChem_[celli]); // 第 742-745 行
for (label i=0;i<NsDAC_;++i) { c[simplifiedToCompleteIndex_[i]] = simplifiedC_[i]; }
}
else { this->solve(c, Ti, pi, dt, this->deltaTChem_[celli]); } // 第 754 行
timeLeft -= dt;
}
if (tabulation_->active()) // 第 767 行:写回查表
{
forAll(c, i) { Rphiq[i] = c[i]/rhoi*W; }
...
label growOrAdd = tabulation_->add(phiq, Rphiq, rhoi, deltaT[celli]); // 第 784-785 行
if (growOrAdd) { this->setTabulationResultsAdd(celli); } // 第 789 行 → 0.0
else { this->setTabulationResultsGrow(celli); } // 第 794 行 → 1.0
}
if (reduced) { this->nSpecie_ = mechRed_->nSpecie(); } // 第 805 行:恢复总数
deltaTMin = min(this->deltaTChem_[celli], deltaTMin);
this->deltaTChem_[celli] = min(this->deltaTChem_[celli], this->deltaTChemMax_);
}
for (label i=0;i<nSpecie_;++i)
{ this->RR_[i][celli] = (c[i]-c0[i])*W/deltaT[celli]; } // 第 816-817 行:源项差分
}
setTabulationResultsAdd/Grow/Retrieve(第 918--945 行)把每单元的 ISAT 命运写成 tabulationResults_ 场(0=新增、1=grow、2=检索),便于统计命中率。
4. 化学求解器层(第 ③ 层的"插头")
4.1 抽象接口 chemistrySolver
文件 :chemistrySolver/chemistrySolver/chemistrySolver.H(第 26--98 行)、.C(第 28--49 行)。
cpp
template<class ChemistryModel>
class chemistrySolver : public ChemistryModel // .H 第 55 行
{
public:
chemistrySolver(typename ChemistryModel::reactionThermo& thermo); // 第 63 行
virtual ~chemistrySolver(); // 第 67 行
virtual void solve( scalarField &c, scalar& T, scalar& p,
scalar& deltaT, scalar& subDeltaT ) const = 0; // 第 73-80 行
};
// .C 第 33-39 行
Foam::chemistrySolver<ChemistryModel>::chemistrySolver(...) : ChemistryModel(thermo) {}
- 第 55 行
public ChemistryModel:ode<StandardChemistryModel<...>>就是"继承StandardChemistryModel并实现其纯虚solve"------用模板继承代替策略模式,编译期绑定。 - 第 73--80 行签名 :
c/T/p以引用原地更新;deltaT建议步长,subDeltaT回写可用子步长。
4.2 ode
文件 :chemistrySolver/ode/ode.H(第 26--111 行)、ode.C(第 32--89 行)。
cpp
template<class ChemistryModel>
class ode : public chemistrySolver<ChemistryModel>
{
dictionary coeffsDict_; // .H 第 59 行
mutable autoPtr<ODESolver> odeSolver_; // 第 61 行
mutable scalarField cTp_; // 第 64 行:浓度 + T + p
public:
TypeName("ode"); // 第 70 行
};
// .C
coeffsDict_(this->subDict("odeCoeffs")), // 第 36 行
odeSolver_(ODESolver::New(*this, coeffsDict_)), // 第 37 行
cTp_(this->nEqns()) // 第 38 行
...
if (odeSolver_->resize()) { odeSolver_->resizeField(cTp_); } // 第 63-66 行:机理缩放时同步
for (int i=0;i<nSpecie;i++) { cTp_[i]=c[i]; } // 第 71-74 行
cTp_[nSpecie]=T; cTp_[nSpecie+1]=p; // 第 75-76 行
odeSolver_->solve(0, deltaT, cTp_, subDeltaT); // 第 78 行
for (int i=0;i<nSpecie;i++) { c[i]=max(0.0,cTp_[i]); } // 第 80-83 行
T=cTp_[nSpecie]; p=cTp_[nSpecie+1]; // 第 84-85 行
- 第 36 行
subDict("odeCoeffs"):子求解器与容差写在这里。 - 第 37 行
ODESolver::New(*this, coeffsDict_):*this即odes_,ODESolver通过它回调nEqns()/derivatives()/jacobian()。 - 第 63--66 行
resize():机理简化(TDAC)改变组分数时同步工作数组。 - 第 71--76 行 :把 (c,T,p) 打包进
cTp_,与StandardChemistryModel状态向量布局(c[nSpecie_]=T、c[nSpecie_+1]=p)严格一致。
4.3 EulerImplicit + 推导
文件 :chemistrySolver/EulerImplicit/EulerImplicit.H(第 26--135 行)、.C(第 29--201 行)。
cpp
template<class ChemistryModel>
class EulerImplicit : public chemistrySolver<ChemistryModel>
{
dictionary coeffsDict_; // .H 第 63 行
scalar cTauChem_; // 第 69 行:化学时间尺度系数(必填)
Switch eqRateLimiter_; // 第 72 行:平衡速率限制器(必填)
mutable scalarField cTp_; // 第 75 行
public:
TypeName("EulerImplicit"); // 第 81 行
void updateRRInReactionI(const label index, const scalar pr, const scalar pf,
const scalar corr, const label lRef, const label rRef,
const scalar p, const scalar T, simpleMatrix<scalar>& RR) const; // 第 96-107 行
virtual void solve(scalarField& c, scalar& T, scalar& p, scalar& deltaT, scalar& subDeltaT) const; // 第 110-117 行
};
构造函数(.C 第 36--47 行):coeffsDict_(this->subDict("EulerImplicitCoeffs"))(第 43 行)、cTauChem_(coeffsDict_.get<scalar>("cTauChem"))(第 44 行)、eqRateLimiter_(coeffsDict_.get<Switch>("equilibriumRateLimiter"))(第 45 行)。
updateRRInReactionI(第 59--91 行)把单反应对速率矩阵 RR 的贡献累加:
cpp
forAll(R.lhs(), s) { RR[si][rRef] -= sl*pr*corr; RR[si][lRef] += sl*pf*corr; } // 第 80-81 行
forAll(R.rhs(), s) { RR[si][lRef] -= sr*pf*corr; RR[si][rRef] += sr*pr*corr; } // 第 88-89 行
solve(第 94--198 行):
cpp
simpleMatrix<scalar> RR(nSpecie, 0, 0); // 第 105 行
for (label i=0;i<nSpecie;i++) { c[i] = max(0, c[i]); } // 第 107-110 行
const scalar cTot = sum(c); // 第 113 行
... mixture 由组分热物性合成,得到绝对焓 ...
const scalar ha = mixture.Ha(p, T); // 第 122 行
const scalar deltaTEst = min(deltaT, subDeltaT); // 第 123 行
forAll(this->reactions(), i)
{
const scalar omegai = this->omegaI(i, c, T, p, pf, cf, lRef, pr, cr, rRef); // 第 130-131 行
scalar corr = 1;
if (eqRateLimiter_)
{
if (omegai < 0) { corr = 1/(1 + pr*deltaTEst); } // 第 138 行
else { corr = 1/(1 + pf*deltaTEst); } // 第 142 行
}
updateRRInReactionI(i, pr, pf, corr, lRef, rRef, p, T, RR); // 第 146 行
}
scalar tMin = GREAT; // 第 150 行
for (label i=0;i<nSpecie;i++)
{
scalar d = 0; for (label j=0;j<nSpecie;j++) { d -= RR(i,j)*c[j]; } // 第 157 行
if (d < -SMALL) { tMin = min(tMin, -(c[i] + SMALL)/d); } // 第 162 行
else { d = max(d, SMALL); tMin = min(tMin, max(cTot - c[i], 1e-5)/d); } // 第 166-168 行
}
subDeltaT = cTauChem_*tMin; // 第 172 行
deltaT = min(deltaT, subDeltaT); // 第 173 行
for (label i=0;i<nSpecie;i++) { RR(i,i) += 1/deltaT; RR.source()[i] = c[i]/deltaT; } // 第 178-179 行
c = RR.LUsolve(); // 第 183 行:解线性系统
for (label i=0;i<nSpecie;i++) { c[i] = max(0, c[i]); } // 第 186-189 行
... mixture 用新 c 重建 ...
T = mixture.THa(ha, p, T); // 第 197 行:以焓守恒反解温度
数学推导 E:隐式欧拉与化学系统线性化
对 dci/dt=ω˙i(c),隐式欧拉为
cin+1=cin+Δtω˙i(cn+1)
在 cn 处一阶展开 ω˙i(cn+1)≈ω˙i(cn)+∑jJij(cjn+1−cjn),整理为线性系统:
(ΔtI−J)cn+1=Δtcn+ω˙(cn)
源码:矩阵 (I/Δt−J) 由 RR(i,i) += 1/deltaT(第 178 行)与 updateRRInReactionI 的 −J 部分构成;右端 RR.source()[i]=c[i]/deltaT(第 179 行)对应 cn/Δt(显式 ω˙ 项以 corr 速率限制近似,第 134--144 行)。稳定时间步 (第 150--168 行):净消耗时 Δt<ci/∣ω˙i∣,净生成时 Δt<(ctot−ci)/ω˙i,取最小后乘 cTauChem。解出新 c 后由 ∑iYihi(T)=ha 用 THa 反解温度(第 197 行)。优点:一次 LU、无条件稳定;代价:一阶精度。
4.4 noChemistrySolver
chemistrySolver/noChemistrySolver/noChemistrySolver.H(第 38--102 行):TypeName("none")(第 61 行),solve 空实现。选择名 none。
4.5 求解器对比与 ODE 子求解器列表
| 求解器 | TypeName | 刚性处理 | 精度 | 稳定性 | 典型场合 |
|---|---|---|---|---|---|
ode |
ode |
委托 ODESolver |
高(自适应) | 好 | 详细机理、验证、laminar/PaSR |
EulerImplicit |
EulerImplicit |
一次 LU + 速率限制 | 一阶 | 无条件稳定 | 大机理工程算例 |
noChemistrySolver |
none |
--- | --- | --- | 关闭化学 |
ode 可选子求解器 (src/ODE/ODESolvers/,由 odeCoeffs { solver ...; } 选择):seulex(隐式外推)、Rosenbrock34/23/12、rodas23/34(隐式 Rosenbrock,含 Jacobian)、SIBS/EulerSI(半隐式)、Euler/Trapezoid(显式)、RKF45/RKDP45/RKCK45(显式 RK)。工程中几乎总用 seulex 或 Rosenbrock34/rodas23。
4.6 单胞求解器 chemFoam 如何直接驱动化学
chemFoam 把化学模型从网格输运中"拎出来"单独驱动,是验证机理与求解器的标准工具(chemFoam/chemFoam.C 第 32--36 行注释:"designed for use on single cell cases to provide comparison against other chemistry solvers")。它用一个单胞网格(第 61--67 行 #define CREATE_MESH createSingleCellMesh.H),主循环每个时间步依次执行 solveChemistry.H / YEqn.H / hEqn.H / pEqn.H(第 86--89 行)。
其中 chemFoam/solveChemistry.H(全文仅 3 行)直接调用化学模型:
cpp
dtChem = chemistry.solve(runTime.deltaTValue()); // 第 1 行:化学积分一个时间步
scalar Qdot = chemistry.Qdot()()[0]/rho[0]; // 第 2 行:取单胞热释放率
integratedHeat += Qdot*runTime.deltaTValue(); // 第 3 行:累积积分热
- 第 1 行 :
chemistry是BasicChemistryModel派生对象(由chemistryProperties选出),solve(deltaT)返回可用化学时间步。这里没有燃烧模型、没有combustionProperties,是最纯粹的"机理+求解器"测试。 - 第 2 行 :
Qdot()()[0]取第 0 个单元的热释放率(volScalarField的operator()返回标量场),除以密度得到单位质量热释放。 - 第 3 行:把热释放按时间积分累加,用于与文献点火延迟/温度历程对比。
与 reactingFoam 的对照 :reactingFoam 里化学被包在燃烧模型里(ChemistryCombustion::chemistryPtr_),还叠加了湍流-化学修正 kappa;chemFoam 则把化学摆在"第一性"的位置,故它是研究 chemistrySolver(ode vs EulerImplicit)与机理本身的首选算例。tutorials/combustion/chemFoam/ 下提供了 gri、h2、nc7h16、ic8h18、ic8h18_TDAC 等用例,其中 ic8h18_TDAC 的 chemistryProperties 同时配置了 reduction{DAC} 与 tabulation{none}(该用例注释说明"Tabulation is not effective for single-cell ignition calculations"------单胞点火不需查表)。
5. 反应与反应速率层
5.1 Reaction 基类
文件 :specie/reaction/Reactions/Reaction/Reaction.H(第 26--362 行)、Reaction.C(第 32--460 行)。
cpp
template<class ReactionThermo>
class Reaction : public ReactionThermo::thermoType // .H 第 70-73 行
{
public:
static label nUnNamedReactions; // 第 80 行
struct specieCoeffs { label index; scalar stoichCoeff; scalar exponent; ... }; // 第 87-124 行
private:
const word name_; // 第 132 行
const speciesTable& species_; // 第 135 行
List<specieCoeffs> lhs_, rhs_; // 第 138/141 行
public:
TypeName("Reaction"); // 第 181 行
declareRunTimeSelectionTable( autoPtr, Reaction, dictionary, ... ); // 第 186-197 行
static autoPtr<Reaction<ReactionThermo>> New(...); // 第 251-256 行
virtual scalar kf(const scalar p, const scalar T, const scalarField& c) const; // 第 303-308 行
virtual scalar kr(const scalar kfwd, const scalar p, const scalar T, const scalarField& c) const; // 第 311-317 行
};
- 第 73 行 :
Reaction继承ReactionThermo::thermoType(如janaf<perfectGas<specie>>),于是每条反应自带热物性,可算 Δh 与 Kc。 - 第 87--124 行
specieCoeffs:把"组分索引 / 化学计量 / 浓度指数"打包,exponent与stoichCoeff分开以支持分数级数(如O2^0.5)。 - 第 186--197 行 RTS 表 :反应类型也由字典
type选择。
Reaction.C 关键实现:
反应式解析 setLRhs(第 246--318 行):
cpp
dlrhs.push_back(specieCoeffs(species, is, failUnknownSpecie)); // 第 262 行:读一个组分项
...
else if (t == token::ASSIGN) // '=' 切换右侧
{ lhs = dlrhs; dlrhs.clear(); parsingRight = true; } // 第 286-288 行
else { rhs = dlrhs; is.putBack(t); return; } // 第 299-302 行
specieCoeffs 构造(第 193--243 行)解析 "2O2" 或 "H2O^0.5":
cpp
if (t.isNumber()) { stoichCoeff = t.number(); is >> t; } // 第 202-206 行
else { stoichCoeff = 1.0; } // 第 208 行
exponent = stoichCoeff; // 第 212 行
const size_t i = specieName.find('^'); // 第 218 行
if (i != word::npos) { exponent = atof(specieName.substr(i+1).c_str()); specieName.resize(i); } // 第 221-223 行
index = species.find(specieName); // 第 227 行
if (failUnknownSpecie && index < 0)
{ FatalErrorInFunction << "Unknown specie " << specieName << nl << "Not in " << flatOutput(species) << endl << exit(FatalError); } // 第 229-235 行
高频报错 :第 229--235 行
Unknown specie------反应里写了species列表外的组分,CHEMKIN 转换/手改机理时最常见。
setThermo (第 111--149 行):按化学计量合成左右侧混合物热物性,ReactionThermo::thermoType::operator=(lhsThermo == rhsThermo)(第 148 行),用于反应焓与 Kc。
Reaction::New (第 355--383 行):const word reactionTypeName(dict.get<word>("type"))(第 364 行),查 dictionaryConstructorTable,失败则 FatalIOErrorInLookup(第 368--377 行)。基类 kf/kr(第 396--430 行)全返回 0.0(抽象零速率)。
5.2 ReactionList
文件 :Reactions/ReactionList/ReactionList.H(第 38--128 行)、.C(第 33--115 行)。
cpp
template<class ThermoType>
class ReactionList : public SLPtrList<Reaction<ThermoType>> // .H 第 56-59 行
{
const speciesTable& species_; // 第 64 行
const ReactionTable<ThermoType>& thermoDb_; // 第 67 行
const dictionary dict_; // 第 70 行
public:
bool readReactionDict(); // 第 109 行
};
// .C 第 76-93 行
for (const entry& dEntry : dict_.subDict("reactions")) // 第 79 行
{
this->append( Reaction<ThermoType>::New(species_, thermoDb_, dEntry.dict()).ptr() );
}
reactions 子字典每个条目都是一条反应,其 .dict() 交给 Reaction::New 构造。
5.3 makeReaction:类型名怎么拼
文件 :Reactions/makeReaction.H(第 62--155 行)。
cpp
const word ReactionType##Thermo##ReactionRate::typeName
( ReactionType::typeName_() /* "irreversible" */
+ ReactionRate::type() /* "Arrhenius" */
+ Reaction##Thermo::typeName_() /* "Reaction" */ ); // 第 69-75 行
addToRunTimeSelectionTable(Reaction##Thermo, ReactionType##Thermo##ReactionRate, dictionary); // 第 77-82 行
于是得到 "irreversibleArrheniusReaction"、"reversibleArrheniusReaction"、"irreversiblethirdBodyArrheniusReaction"、"reversibleTroeFallOffReaction" 等。makePressureDependentReactions(第 119--155 行)为压力相关反应注册 4 种组合(FallOffReactionRate/ChemicallyActivatedReactionRate × irreversible/reversible)。
5.4 Arrhenius 反应率 + 推导
文件 :reactionRate/ArrheniusReactionRate/ArrheniusReactionRate.H(第 26--132 行)、ArrheniusReactionRateI.H(第 28--99 行)。
cpp
class ArrheniusReactionRate
{
scalar A_, beta_, Ta_; // .H 第 65-67 行
public:
inline ArrheniusReactionRate(const speciesTable& species, const dictionary& dict); // 第 83-87 行
static word type() { return "Arrhenius"; } // 第 92-96 行
inline scalar operator()(const scalar p, const scalar T, const scalarField& c) const; // 第 98-103 行
};
// .I.H
A_(dict.get<scalar>("A")), beta_(dict.get<scalar>("beta")), Ta_(dict.get<scalar>("Ta")) // 第 49-51 行:必填
scalar ak = A_; // 第 64 行
if (mag(beta_) > VSMALL) { ak *= pow(T, beta_); } // 第 66-69 行:T^β
if (mag(Ta_) > VSMALL) { ak *= exp(-Ta_/T); } // 第 71-74 行:exp(-Ta/T)
return ak; // 第 76 行
- 第 49--51 行 :
A/beta/Ta三键必填。 - 第 66--74 行 :仅当 ∣β∣>VSMALL 才
pow(T,beta),仅当 ∣Ta∣>VSMALL 才exp(-Ta/T)------化学内层循环的微优化。
数学推导 F:Arrhenius 速率(略,见推导 C 第 1 步)
k(T)=ATβexp(−Ta/T),Ta=Ea/Ru
由 dlnk/dT=β/T+Ta/T2,当 Ta≫T 时该导数极大------这是刚性 的根源。jacobian(StandardChemistryModel.C 第 349--462 行)对浓度用解析导数、对温度用中心差分构造 J。
数值演算(以教程 membrane 的甲烷一步反应为例) :该反应 A=5.2e16, beta=0, Ta=14906(tutorials/combustion/reactingFoam/RAS/membrane/constant/reactions 第 32--34 行)。在 T=1500K:
k=Ae−Ta/T=5.2×1016⋅e−14906/1500=5.2×1016⋅e−9.9373≈5.2×1016⋅4.83×10−5≈2.51×1012
在 T=2000K: e−14906/2000=e−7.453≈5.79×10−4, k≈3.01×1013。温度升高 500 K 速率常数增大 12 倍 ------这正是"刚性/强非线性"在数值上的直观体现,也解释了为什么必须给化学模型一个小而自适应的 deltaTChem(尤其点火初期温度从 300 K 上升到 1500 K 时, k 会跨越十几个数量级)。
Treact 的作用 :StandardChemistryModel::solve(第 686 行)用 if (Ti > Treact_) 决定是否积分。若把 Treact 设得偏高(例如 800 K),未着火区域会被"跳过",既省算力又避免低温下的不必要刚性;设错(过高)则可能漏掉真实着火。
A 的量纲陷阱 :A 的单位随反应分子数变化(一级反应 s−1、二级 m3/(mol⋅s) 常写成 e16 量级)。CHEMKIN 与 OpenFOAM 对 A 的单位约定不同(CHEMKIN 用 cm-mol-s,OpenFOAM 用 m-kmol-s),因此从 CHEMKIN 手抄参数时必须换算 ,否则点火延迟会差若干量级------这是"机理能跑但结果离谱"的最常见原因之一。chemkinReader 会自动做该换算(其内部 reactionTypes.H 与单位换算逻辑),但手写 reactions 字典时不会。
5.5 IrreversibleReaction / ReversibleReaction + 平衡常数推导
文件 :IrreversibleReaction/IrreversibleReaction.H(第 56--175 行)、.C(第 33--129 行);ReversibleReaction/ReversibleReaction.H(第 56--190 行)、.C(第 33--178 行)。
cpp
// IrreversibleReaction.H
class IrreversibleReaction : public ReactionType<ReactionThermo>
{ ReactionRate k_; ... TypeName("irreversible"); ... }; // 第 62-88 行
// .C:kf(p,T,c) { return k_(p, T, c); } // 第 108 行:无逆向
// ReversibleReaction.H
class ReversibleReaction : public ReactionType<ReactionThermo>
{ ReactionRate k_; ... TypeName("reversible"); ... }; // 第 62-88 行
// .C
Foam::scalar ...::kf(...) const { return k_(p, T, c); } // 第 108 行
Foam::scalar ...::kr(const scalar kfwd, ...) const { return kfwd/max(this->Kc(p,T), VSMALL); } // 第 131 行
Foam::scalar ...::kr(const scalar p, ...) const { return kr(kf(p,T,c), p, T, c); } // 第 153 行
数学推导 G:可逆反应与平衡常数
平衡时正逆速率相等, kf∏cj,eqνj′=kr∏cj,eqνj′′,故
krkf=j∏cj,eqνj′′−νj′=Kc(T) ⟹ kr=Kckf
源码 kfwd/max(Kc,VSMALL)(第 131 行)。 Kc 来自 Reaction::setThermo 合成的 lhs/rhs 热物性。若把可逆反应写成不可逆,温度降低后产物不会解离回落------典型"用错类型"症状。
5.6 三体反应 + 推导
文件 :thirdBodyArrheniusReactionRate/thirdBodyArrheniusReactionRate.H(第 26--130 行)、.I.H(第 28--91 行);thirdBodyEfficiencies/。
cpp
class thirdBodyArrheniusReactionRate : ArrheniusReactionRate // .H 第 59-61 行
{ thirdBodyEfficiencies thirdBodyEfficiencies_; ... static word type() { return "thirdBodyArrhenius"; } }; // 第 92-95 行
// .I.H 第 60-70 行
return thirdBodyEfficiencies_.M(c) * ArrheniusReactionRate::operator()(p, T, c);
// thirdBodyEfficienciesI.H 第 96-105 行
scalar M = 0.0; forAll(*this, i) { M += operator[](i)*c[i]; } return M;
字典构造(thirdBodyEfficienciesI.H 第 51--91 行):给全部组分 coeffs(第 61 行,个数必须等于组分数)或统一 defaultEfficiency(第 88 行)。
数学推导 H:三体反应
k3body=kArrhenius⋅M,M=i∑εici
εi 为各组分第三体效率( εH2O≈6.5、 εN2=1、 εAr≈0.7)。
报错 :
coeffs个数≠组分数(第 63--69 行 FatalIO)或含未定义组分(第 75--81 行No such species)。
5.7 Fall-Off 反应:Troe / SRI / Lindemann + 推导
文件 :FallOffReactionRate/FallOffReactionRate.H(第 26--136 行)、.I.H(第 28--116 行);fallOffFunctions/{Troe,SRI,Lindemann}FallOffFunction/;ChemicallyActivatedReactionRate/。
cpp
// FallOffReactionRate.H 第 64-102 行
class FallOffReactionRate
{
ReactionRate k0_, kInf_; // 第 69-70 行:低压/高压极限
FallOffFunction F_; // 第 71 行
thirdBodyEfficiencies thirdBodyEfficiencies_; // 第 72 行
static word type() { return ReactionRate::type() + FallOffFunction::type() + "FallOff"; } // 第 99-102 行
};
// .I.H 字典构造(四个子字典,第 55-58 行)
k0_(species, dict.subDict("k0")), kInf_(species, dict.subDict("kInf")),
F_(dict.subDict("F")), thirdBodyEfficiencies_(species, dict.subDict("thirdBodyEfficiencies"))
// .I.H operator()(第 64-79 行)
const scalar k0 = k0_(p,T,c); const scalar kInf = kInf_(p,T,c);
const scalar Pr = k0*thirdBodyEfficiencies_.M(c)/kInf; // 第 76 行:对比压力
return kInf*(Pr/(1 + Pr))*F_(T, Pr); // 第 78 行
混合函数:
cpp
// LindemannFallOffFunctionI.H 第 43-50 行
return 1.0;
// TroeFallOffFunctionI.H 第 56-77 行(参数 alpha,Tsss,Ts,Tss)
scalar logFcent = log10( max( (1-alpha_)*exp(-T/Tsss_) + alpha_*exp(-T/Ts_) + exp(-Tss_/T), SMALL ) );
scalar c = -0.4 - 0.67*logFcent; static const scalar d = 0.14; scalar n = 0.75 - 1.27*logFcent;
scalar logPr = log10(max(Pr, SMALL));
return pow(10.0, logFcent/(1.0 + sqr((logPr + c)/(n - d*(logPr + c)))));
// SRIFallOffFunctionI.H 第 59-67 行(参数 a,b,c,d,e)
scalar X = 1.0/(1.0 + sqr(log10(max(Pr, SMALL))));
return d_*pow(a_*exp(-b_/T) + exp(-T/c_), X)*pow(T, e_);
ChemicallyActivatedReactionRate (ChemicallyActivatedReactionRateI.H 第 70--88 行)是低压极限形式:
cpp
const scalar Pr = k0*thirdBodyEfficiencies_.M(c)/kInf; // 第 85 行
return k0*(1/(1 + Pr))*F_(T, Pr); // 第 87 行
数学推导 I:压力相关(fall-off)反应
Pr=k∞k0M(c),k=k∞1+PrPrF
- Pr≪1(低压): k≈k0M(三体极限);
- Pr≫1(高压): k≈k∞。
Troe 的 F 用 log10Fcent 修正曲率:
log10Fcent=log10(1−α)e−T/Tsss+αe−T/Ts+e−Tss/T
log10F=1+(log10Pr+c)/(n−d(log10Pr+c))2log10Fcent,c=−0.4−0.67log10Fcent, n=0.75−1.27log10Fcent, d=0.14
SRI 用 F=dae−b/T+e−T/cXTe, X=1/(1+(log10Pr)2)。CHEMKIN 关键字 Troe/SRI/(无修正)→Lindemann,由 makePressureDependentReactions 注册。
5.8 其余速率类型
| 速率类 | 类型名 | 要点 |
|---|---|---|
LangmuirHinshelwoodReactionRate |
"LangmuirHinshelwood" |
5 组 (Ai,Tai),含 CO/C3H6/NO 抑制项 |
powerSeriesReactionRate |
"powerSeries" |
k=ATβexp(∑ncn/Tn+1) |
JanevReactionRate |
"Janev" |
等离子体/电离半经验式 |
LandauTellerReactionRate |
"LandauTeller" |
含 (1+B/T1/3) 修正 |
infiniteReactionRate |
"infinite" |
operator() 恒返回 1 |
LangmuirHinshelwood (.H 第 63--65 行 static const label n_=5,.I.H 第 65 行 dict.lookup("coeffs") 是 5 组 (A,Ta))------速率(.I.H 第 77--91 行):
cpp
return A_[0]*exp(-Ta_[0]/T)/
( T
*sqr(1 + A_[1]*exp(-Ta_[1]/T)*c[co_] + A_[2]*exp(-Ta_[2]/T)*c[c3h6_])
*(1 + A_[3]*exp(-Ta_[3]/T)*sqr(c[co_])*sqr(c[c3h6_]))
*(1 + A_[4]*exp(-Ta_[4]/T)*pow(c[no_], 0.7)) );
报错 :
coeffs必须恰好 5 组;组分列表须含CO/C3H6/NO(.I.H第 60--62 行)。
powerSeries (powerSeriesReactionRateI.H 第 60--84 行):forAll(coeffs_, n) { expArg += coeffs_[n]/pow(T, n+1); } lta *= exp(expArg);
infinite (infiniteReactionRateI.H 第 48--56 行):return (1);
5.9 反应类型与速率类型对比表
| 反应类型 | TypeName | kf | kr |
|---|---|---|---|
IrreversibleReaction |
irreversible |
k_(p,T,c) |
0(基类) |
ReversibleReaction |
reversible |
k_(p,T,c) |
kf/Kc(第 131 行) |
NonEquilibriumReversibleReaction |
nonEquilibriumReversible |
CHEMKIN 正逆两套参数 | 直接给定 |
| 速率类型 | TypeName | 表达 | 依赖 |
|---|---|---|---|
ArrheniusReactionRate |
Arrhenius |
ATβe−Ta/T | T |
thirdBodyArrheniusReactionRate |
thirdBodyArrhenius |
M(c)⋅ATβe−Ta/T | T,c |
FallOffReactionRate<...,Troe/SRI/Lindemann> |
...TroeFallOff 等 |
k∞1+PrPrF | T,p,c |
ChemicallyActivatedReactionRate |
...ChemicallyActivatedFallOff |
k01+Pr1F | T,p,c |
LangmuirHinshelwoodReactionRate |
LangmuirHinshelwood |
5 组 (A,Ta) 抑制式 | T,c |
powerSeriesReactionRate |
powerSeries |
ATβe∑cn/Tn+1 | T |
infiniteReactionRate |
infinite |
1 | --- |
6. 组分与热物理层(第 ④ 层)
reactionThermo (thermophysicalModels/reactionThermo/):rhoReactionThermo(密度)、psiReactionThermo(可压缩性 ψ)、psiuReactionThermo/heheuPsiThermo( ψ+内能)。reactingFoam 用 psiReactionThermo(createFields.H 第 4 行)。
mixtures :basicSpecieMixture(提供 Y()/species()/active()/setActive())、basicCombustionMixture(basicCombustionMixture.H 第 54--94 行,新增 fres(ft,stoicRatio) 于第 87--93 行)、reactingMixture(StandardChemistryModel 强转目标)、singleStepReactingMixture(单步模型必需)、multiComponentMixture、egrMixture 等。
chemistryReaders :foamChemistryReader(foamChemistryReader.H 第 59--170 行,TypeName 第 107 行)读原生格式,持 chemDict_/thermoDict_/speciesTable_/speciesThermo_/reactions_;chemkinReader(chemkinReader.H 第 64--68 行)class chemkinReader : public chemistryReader<gasHThermoPhysics>, public yyFlexLexer(第 53 行 #include <FlexLexer.h>),支持 thirdBodyReactionType/TroeReactionType/SRIReactionType/... 等关键字(第 92--114 行),只支持 gasHThermoPhysics。
7. 可直接粘贴的完整字典模板
7.1 constant/combustionProperties
cpp
combustionModel laminar; // EDC / PaSR / diffusion / EDM / ...
active true;
laminarCoeffs { integrateReactionRate true; }
cpp
combustionModel EDC;
active true;
EDCCoeffs { version v2005; } // v1981 / v1996 / v2005 / v2016
cpp
combustionModel PaSR;
active true;
PaSRCoeffs { Cmix 0.03; } // 必填
cpp
combustionModel EDM;
active on;
EDMCoeffs { semiImplicit no; Cd 1; CEDC 1; }
参数说明 :combustionModel(键名,缺省 none);active(缺省 true);laminarCoeffs/integrateReactionRate(缺省 true,是否 ODE 积分);EDCCoeffs/version(缺省 v2005)、C1/C2(缺省 0.05774/0.5,仅 v2016)、Cgamma/Ctau(缺省 2.1377/0.4083)、exp1/exp2(按版本);PaSRCoeffs/Cmix(必填 );EDM 的 semiImplicit/CEDC(必填 )、Cd;diffusionCoeffs/C(必填 )、oxidant(缺省 O2)。
调参表:
| 现象 | 调整 | 理由 |
|---|---|---|
| 燃烧过慢/吹熄 | EDC 改 v2016;PaSR Cmix 增大 |
局部化; κ→1 |
| 燃烧过快/温度爆 | PaSR Cmix 减小;EDM CEDC 减小 |
增强混合控制 |
PaSR 报缺 Cmix |
补 PaSRCoeffs |
getScalar 必填 |
| EDM 报 "requires O2" | 混合加 O2 或改 oxidant |
eddyDissipationModelBase.C 第 107 行 |
7.2 constant/chemistryProperties
cpp
chemistryType { solver ode; method standard; } // 或 method TDAC
chemistry on;
initialChemicalTimeStep 1e-7; // 必填
maxChemicalTimeStep 1e-3; // 可选
Treact 0.0; // 可选
odeCoeffs { solver seulex; absTol 1e-12; relTol 1e-1; }
cpp
chemistryType { solver ode; method TDAC; }
chemistry on;
initialChemicalTimeStep 1e-7;
odeCoeffs { solver seulex; absTol 1e-12; relTol 1e-1; }
reduction
{
active on; log off; tolerance 1e-4; method DAC; // DRG/DAC/DRGEP/PFA/EFA/none
initialSet { CO; CH4; HO2; } // 搜索起始集 SIS
automaticSIS off; fuelSpecies { CH4 1; }
}
tabulation
{
active on; log off; tolerance 1e-4; method ISAT; // ISAT/none
scaleFactor { otherSpecies 1; Temperature 1000; Pressure 1e15; deltaT 0.5; }
maxNLeafs 2000; chPMaxLifeTime 100; maxGrowth 10;
checkEntireTreeInterval 5; maxDepthFactor 2; minBalanceThreshold 30;
MRURetrieve false; maxMRUSize 0; growPoints true;
maxNumNewDim 10; variableTimeStep true;
}
cpp
chemistryType { solver EulerImplicit; method standard; }
chemistry on; initialChemicalTimeStep 1e-7;
EulerImplicitCoeffs { cTauChem 0.9; equilibriumRateLimiter on; } // 均必填
cpp
chemistryType { solver none; }
chemistry off; initialChemicalTimeStep 1e-7;
参数说明 :chemistryType/solver(必填 ,ode/EulerImplicit/none);chemistryType/method(缺省 standard);chemistry(必填 );initialChemicalTimeStep(必填 );maxChemicalTimeStep(缺省 GREAT);Treact(缺省 0);odeCoeffs/solver(seulex/Rosenbrock34/rodas23...)、absTol/relTol;EulerImplicitCoeffs/cTauChem、equilibriumRateLimiter(均必填);reduction/{method,tolerance,initialSet,...};tabulation/{method,tolerance,scaleFactor,maxNLeafs,...}。
调参表:
| 现象 | 调整 | 理由 |
|---|---|---|
| 化学步极小、慢 | 放宽 relTol 到 1e-2;或改 EulerImplicit |
容差越严子步越多 |
| 化学发散 | 减小 initialChemicalTimeStep;开 EulerImplicit |
隐式更稳 |
Unknown chemistry solver |
检查拼写/补 chemistryType |
basicChemistryModelTemplates.C 第 53 行 |
| ISAT 表爆满 | 减小 chPMaxLifeTime/maxGrowth |
清理/再平衡 |
7.3 constant/thermophysicalProperties
cpp
thermoType
{
type hePsiThermo;
mixture reactingMixture; // 详细机理:reactingMixture
transport sutherland;
thermo janaf;
energy sensibleEnthalpy;
equationOfState perfectGas;
specie specie;
}
inertSpecie N2;
chemistryReader foamChemistryReader;
foamChemistryFile "<constant>/reactions";
foamChemistryThermoFile "<constant>/thermo.compressibleGas";
单步模型(EDM/diffusion)把 mixture 改为 singleStepReactingMixture。匹配矩阵 :laminar/EDC/PaSR/diffusionMulticomponent 需 reactingMixture;EDM/diffusion/infinitelyFastChemistry/FSD 需 singleStepReactingMixture。选错会 FatalError(singleStepCombustion.C 第 75--79 行;StandardChemistryModel.C 第 47 行)。
参数说明 :thermoType/{type,mixture,transport,thermo,energy,equationOfState,specie} 决定热物性组合;inertSpecie 供求解器做 1−∑Y 填充;chemistryReader + 文件路径决定机理读法(foamChemistryReader/chemkinReader)。
7.4 constant/reactions
cpp
species ( O2 H2O CH4 CO2 N2 );
reactions
{
methaneReaction
{
type irreversibleArrheniusReaction;
reaction "CH4 + 2O2 = CO2 + 2H2O";
A 5.2e16;
beta 0;
Ta 14906;
}
}
cpp
// 可逆
co2Dissociation { type reversibleArrheniusReaction; reaction "CO2 = CO + 0.5O2"; A 1.2e15; beta 0; Ta 65800; }
// 三体
hO2Combination
{
type irreversiblethirdBodyArrheniusReaction;
reaction "H + O2 + M = HO2 + M";
A 1.0e18; beta -0.8; Ta 0;
thirdBodyEfficiencies { defaultEfficiency 1; coeffs ( (H2O 6.5) (N2 1.0) (Ar 0.7) ); }
}
// 压力相关(Troe)
ohRecombination
{
type reversibleTroeFallOffReaction;
reaction "OH + OH = H2O2";
k0 { A 2.3e13; beta -0.9; Ta 0; }
kInf { A 7.5e15; beta 0; Ta 0; }
F { alpha 0.5; Tsss 1e30; Ts 1e30; Tss 1e30; }
thirdBodyEfficiencies { defaultEfficiency 1; }
}
// LangmuirHinshelwood(5 组 (A,Ta))
surfaceReaction
{
type irreversibleLangmuirHinshelwoodReaction;
reaction "CO + 0.5O2 = CO2";
coeffs ( (1.0e10 0) (1.0e3 0) (1.0e3 0) (1.0e3 0) (1.0e3 0) );
}
参数说明 :species(...)(组分明,须与 0/ 场一致);reactions{<name>}(每条反应名任意);type(必填 ,反应+速率复合名);reaction(必填 ,支持 +、=、^指数);Arrhenius 的 A/beta/Ta(必填 );三体的 thirdBodyEfficiencies/{coeffs|defaultEfficiency};FallOff 的 k0/kInf/F(F Troe 用 alpha/Tsss/Ts/Tss,SRI 用 a/b/c/d/e);LangmuirHinshelwood 的 coeffs(5 组)。
排查表:
| 现象 | 调整 | 理由 |
|---|---|---|
Unknown specie XXX |
组件名拼写/机理缺失 | Reaction.C 第 229--235 行 |
Unknown reaction type XXX |
type 名不符 |
Reaction.C 第 355--383 行 |
Number of efficiencies ... not equal |
coeffs 个数≠组分数 |
thirdBodyEfficienciesI.H 第 63--69 行 |
| 高温后产物不降 | 改可逆 | 可逆才有 kr=kf/Kc |
Multiple '=' in reaction equation |
反应式多个 = |
Reaction.C 第 280--283 行 |
7.5 一个完整算例的四字典组合(EDC + TDAC/ISAT)
把 §7.1--7.4 拼起来,就是一个可直接运行的"reactingFoam + EDC + TDAC/ISAT + GRI 机理"算例的 constant/ 目录。四个文件各司其职:
cpp
// constant/combustionProperties ------ 选燃烧模型(§7.1)
combustionModel EDC;
active true;
EDCCoeffs { version v2005; }
// constant/chemistryProperties ------ 选化学求解器与加速(§7.2)
chemistryType { solver ode; method TDAC; }
chemistry on;
initialChemicalTimeStep 1e-7;
odeCoeffs { solver seulex; absTol 1e-12; relTol 1e-1; }
reduction { active on; tolerance 1e-4; method DAC; initialSet { CO; CH4; HO2; }
automaticSIS off; fuelSpecies { CH4 1; } }
tabulation { active on; tolerance 1e-4; method ISAT;
scaleFactor { otherSpecies 1; Temperature 1000; Pressure 1e15; deltaT 0.5; }
maxNLeafs 2000; chPMaxLifeTime 100; maxGrowth 10;
checkEntireTreeInterval 5; maxDepthFactor 2; minBalanceThreshold 30;
MRURetrieve false; maxMRUSize 0; growPoints true;
maxNumNewDim 10; variableTimeStep true; }
// constant/thermophysicalProperties ------ 选热物性/机理读法(§7.3)
thermoType { type hePsiThermo; mixture reactingMixture; transport sutherland;
thermo janaf; energy sensibleEnthalpy; equationOfState perfectGas; specie specie; }
inertSpecie N2;
chemistryReader foamChemistryReader;
foamChemistryFile "<constant>/reactionsGRI";
foamChemistryThermoFile "<constant>/thermo.compressibleGasGRI";
// constant/reactions ------ 反应与速率(§7.4;大机理可用 GRI 文件替代)
species ( O2 H2O CH4 CO2 N2 ... );
reactions { ... } // 或由 foamChemistryFile 指向 external 文件
配套的其他文件 :0/ 目录下必须有与机理对应的组分场(CH4/H2O/CO2/O2/N2/...);system/controlDict 里 adjustTimeStep 决定 TDAC::variableTimeStep_(TDACChemistryModel.C 第 44--51 行);湍流模型(turbulenceProperties)提供 epsilon/k/muEff 供 EDC 的 kappa 计算(EDC.C 第 89--99 行);fvSchemes 里 div(phi,Yi_h) 用多变量对流格式(createFields.H 第 70--76 行组装 fields)。
四字典的耦合关系(一句话) :combustionProperties 决定"谁在烧",chemistryProperties 决定"怎么积分/加速",thermophysicalProperties 决定"烧什么、用什么热力学",reactions 决定"每步反应多快"。四者必须互相匹配(尤其燃烧模型 ↔ mixture,见 §7.3 匹配矩阵)。这也是 §9 报错表里绝大多数问题(#8、#11、#12、#13、#22)的根源。
8. 加速策略:TDAC / ISAT
8.1 机理简化(reduction)
抽象基类 chemistryReductionMethod(reduction/chemistryReductionMethod/chemistryReductionMethod.H 第 56--158 行):持 dict_/coeffsDict_/active_/log_/chemistry_/activeSpecies_/NsSimp_/nSpecie_/tolerance_(第 62--85 行),纯虚 reduceMechanism(c,T,p)(第 152--157 行)。
各方法(均来自各 .H):DRG(DRG.H 第 66 行)、DRGEP(第 137 行,注释第 30--85 行给出 rAB 与 R 值公式)、PFA(第 65 行)、EFA(第 63 行)、DAC(第 132 行,附 automaticSIS/phiTol/NOxThreshold/fuelSpecies 等)、none(第 59 行)。
rAB(误差传播权重,DRGEP.H 第 30--46 行):
rAB=max(PA,CA) ∑i=1NrνAiωidBi ,PA=i∑max(0,νAiωi),CA=i∑max(0,−νAiωi)
R 值(第 54--60 行): RV0(V)=maxSP∏rij,大于 tolerance 的组分保留。
8.2 化学查表(tabulation):ISAT + 推导
抽象基类 chemistryTabulationMethod(tabulation/chemistryTabulationMethod/chemistryTabulationMethod.H 第 59--174 行):tolerance_(第 77 行),纯虚 retrieve/add/update(第 153/162/173 行)。
ISAT (tabulation/ISAT/ISAT.H 第 59--245 行,TypeName("ISAT") 第 184 行):
cpp
binaryTree<CompType, ThermoType> chemisTree_; // 第 67 行:二叉搜索树
scalarField scaleFactor_; // 第 70 行
label chPMaxLifeTime_, maxGrowth_, maxMRUSize_; // 第 75/78/97 行
Switch MRURetrieve_, growPoints_; // 第 91/103 行
label nRetrieved_, nGrowth_, nAdd_; // 第 106-108 行
构造函数(.C 第 35--127 行scaleFactor_(chemistry.nEqns() + (variableTimeStep?1:0), 1) 第 48 行;读 scaleFactor{otherSpecies,Temperature,Pressure,deltaT} 第 86--108 行;nAdditionalEqns_=3/2 第 111--118 行)。
retrieve (.C 第 433--505 行):
cpp
chemisTree_.binaryTreeSearch(phiq, chemisTree_.root(), phi0); // 第 446 行:二叉搜索最近点
lastSearch_ = phi0;
if (phi0->inEOA(phiq)) { retrieved = true; } // 第 451 行:在误差椭球内
else if (chemisTree_.secondaryBTSearch(phiq, phi0)) { retrieved = true; } // 第 457 行:次级搜索
else if (MRURetrieve_) { /* 遍历 MRUList_ */ } // 第 461 行
...
calcNewC(phi0, phiq, Rphiq); // 第 496 行:线性插值
calcNewC (第 195--263 行):Rphiq[i] += gradientsMatrix(i,j)*dphi[j](第 256 行)并 max(0.0,...)(第 260 行),即 R(ϕq)≈R(ϕ0)+A(ϕq−ϕ0)。
grow/add (第 266--604 行):grow 前检 nGrowth()>maxGrowth_ → 标 toRemove(第 282--287 行);checkSolution 通过才 phi0->grow(第 292--295 行)。add 先试 grow(第 521--531 行),树满则 cleanAndBalance(第 536--583 行),再 computeA(第 588 行)并 insertNewLeaf(第 590--599 行)。
cleanAndBalance (第 302--343 行):删除超龄/超增长点(第 318 行),当 depth > maxDepthFactor*log2(size) 时 balance()(第 335 行)。
computeA(第 346--428 行)------用隐式雅可比算映射梯度矩阵:
cpp
// A = C(psi0,t0)/(I-dt*J(psi(t0+dt))) , with C(psi0,t0) = I (第 374-382 行注释)
this->chemistry_.jacobian(runTime_.value(), Rcq, A); // 第 384 行
... A(i,j) *= -dt*W_si/W_sj; A(i,i) += 1; A(i,T/p 列) *= -dt*W_si/rhoi; ... // 第 404-414 行
LUscalarMatrix LUA(A); LUA.inv(A); // 第 426-427 行:求逆
数学推导 J:ISAT 的误差控制与树结构
- 精度椭球 EOA (
chemPointISAT.H注释第 35--49 行): E={x:∥LT(x−ϕ0)∥≤1}, LTL 是 (BA/tol)T(BA/tol) 的 Cholesky 分解。查询点 ϕq∈E 时线性插值: R(ϕq)≈R(ϕ0)+A(ϕq−ϕ0) - 两级判据 (注释第 41--47 行):先比较半径 r=∥dϕ∥ 与 rmin,rmax,仅中间区做 ∥LTdϕ∥≤1 完整判据。
- 增长判据 (注释第 52--61 行): epsGrow=∥B(dR−dRl)∥,dR=R(ϕq)−R(ϕ0), dRl=Adϕ
epsGrow ≤ tolerance→ grow;否则新增叶子。 - 最小体积椭球 grow (注释第 91--101 行): p′=LT(p−ϕ0); G=I+γp′p′T, γ=(∣p′∣1−1)∣p′∣21; L′=LG(QR 保证 L′ 仍三角,
chemPointISAT.H第 218--240 行qrDecompose/qrUpdate/rotate)。 - 梯度矩阵 (
ISAT.C注释第 374--382 行):由变分方程 dC/dt=JC, C(ϕ0,t0)=I,隐式离散得 A=(I−ΔtJ)−1,源码jacobian后求逆(第 384/427 行)。 - 树结构 (
binaryTree.H第 59--243 行):binaryTreeSearch(第 195--200 行)按超平面下降到最近叶子;secondaryBTSearch(第 205 行)深度优先次级搜索;insertNewLeaf(第 180--189 行)把叶子一分为二;balance(第 220 行)在树过深时按最大方差方向重建;isFull(第 240 行)判maxNLeafs。
8.3 加速策略对比表与调参
| 策略 | 机制 | 误差参数 | 加速来源 | 风险 |
|---|---|---|---|---|
| DRG | 有向关系图 | tolerance |
降 ODE 维数 | 剔掉慢组分 |
| DRGEP | 误差传播 R 值 | tolerance |
同上,更准 | 计算 R 值开销 |
| PFA | 路径通量 | tolerance |
同上 | 类似 DRGEP |
| EFA | 元素通量 | tolerance |
同上 | 元素守恒场景 |
| DAC | 动态 SIS | tolerance/phiTol/NOxThreshold |
动态简化 | 配置复杂 |
| ISAT | 误差椭球 + 二叉搜索树 | tolerance/scaleFactor/maxNLeafs |
查表跳过积分 | 容差大误差大 |
| none | --- | --- | 无 | 大机理算不动 |
调参表:
| 现象 | 调整 | 理由 |
|---|---|---|
| TDAC 比 standard 还慢 | 机理太小;tolerance 太小 |
简化/查表有开销 |
| 结果偏差大 | 减小两个 tolerance |
保留更多组分、收紧椭球 |
| nAdd 大、内存涨 | 减小 maxNLeafs/chPMaxLifeTime;开 MRURetrieve |
控制树规模 |
| ISAT 命中率低 | 增大 tabulation tolerance;检查 scaleFactor |
椭球尺度决定命中 |
| LTS 下 ISAT 维度错 | 开 variableTimeStep true |
状态向量多一维 deltaT |
| 组分场缺失被静默简化 | 补齐 0/ 组分场 |
TDACChemistryModel.C 第 111--115 行 |
9. 常见报错/警告条目排查表(24 条)
| # | 报错/警告关键词 | 来源(文件/行) | 成因 | 解决 |
|---|---|---|---|---|
| 1 | Unknown specie XXX + Not in (...) |
Reaction.C 第 229--235 行 |
反应式写了 species 外组分 |
补组分/改反应式 |
| 2 | Unknown reaction type XXX |
Reaction.C 第 355--383 行 |
type 名不符 RTS |
改为 irreversibleArrheniusReaction 等 |
| 3 | Number of efficiencies = N ... not equal ... M |
thirdBodyEfficienciesI.H 第 63--69 行 |
三体 coeffs 个数≠组分数 |
用 defaultEfficiency 或补全 |
| 4 | No such species: ... |
thirdBodyEfficienciesI.H 第 75--81 行 |
三体效率表含未定义组分 | 改对/删除 |
| 5 | Cannot continue reading reaction data from stream |
Reaction.C 第 315--317 行 |
反应式解析中断 | 修正 A + B = C + D |
| 6 | Multiple '=' in reaction equation |
Reaction.C 第 280--283 行 |
反应式多个 = |
只留一个 |
| 7 | Expected a word but found ... |
Reaction.C 第 239--241 行 |
反应式含非法 token | 清理特殊符号 |
| 8 | Template parameter based chemistry solver selection is no longer supported |
basicChemistryModelTemplates.C 第 55--66 行 |
缺 chemistryType 字典 |
补 chemistryType { solver; method; } |
| 9 | Unknown chemistry solver type XXX + 组合表 |
basicChemistryModelTemplates.C 第 163--189 行 |
solver/method 名错 |
按打印组合改正 |
| 10 | Combustion model not active: ... not found |
combustionModelTemplates.C 第 56--58 行 |
缺 combustionProperties |
建文件或接受不激活 |
| 11 | Selecting combustion model none(非预期) |
combustionModelTemplates.C 第 49 行 |
字典缺 combustionModel 键 |
写 combustionModel EDC; |
| 12 | Unknown combustionModel type XXX + 组合表 |
combustionModelTemplates.C 第 158--185 行 |
模型名错/热物性不匹配 | 选对模型或热物性 |
| 13 | Inconsistent thermo package ... Please select a thermo package based on singleStepReactingMixture |
singleStepCombustion.C 第 75--79 行 |
EDM/diffusion 配错 mixture | 改 singleStepReactingMixture |
| 14 | You selected a combustion model that requires O2 mass ... |
eddyDissipationModelBase.C 第 107--110 行 |
混合无 O2 | 加 O2 或换模型 |
| 15 | inertSpecie XXX not found in available species |
reactingFoam/createFields.H 第 12--17 行 |
惰性组分名不在机理 | 改对 inertSpecie |
| 16 | 组分质量分数不正定(Y 负值/发散) |
YEqn.H 第 41 行 Yi.clamp_min(0) |
化学源项显式过大/时间步过大 | 开半隐式/隐式;减小化学步 |
| 17 | 化学时间步过大导致温度爆/NaN | StandardChemistryModel.C 第 710--711 行 |
未设 maxChemicalTimeStep |
设上限;减小初始步;开 EulerImplicit |
| 18 | 刚性发散(ODE 步长被压到极小) | ODESolver.H 第 73--79 行(absTol/relTol/maxSteps) |
容差过严/机理极刚 | 放宽 relTol;换 seulex;开 EulerImplicit |
| 19 | 燃烧模型未激活但以为在烧(Qdot≈0) |
combustionModel.C 第 87 行 active |
active false/缺文件 |
设 active true |
| 20 | 化学未开启(无反应) | BasicChemistryModel.C 第 62 行;solve 第 669 行 |
chemistry off |
设 chemistry on |
| 21 | ISAT 表爆满/内存涨 | ISAT.C 第 536 行 isFull() |
maxNLeafs 偏小/未清理 |
调 maxNLeafs/chPMaxLifeTime/maxGrowth |
| 22 | TDAC 组分场缺失被静默简化 | TDACChemistryModel.C 第 111--115 行 |
0/ 缺组分场文件 |
补齐场文件或改机理 |
| 23 | LangmuirHinshelwood coeffs 组数错/组分缺失 |
LangmuirHinshelwoodReactionRateI.H 第 60--65 行 |
coeffs 非 5 组或缺 CO/C3H6/NO |
补/改参数 |
| 24 | 可逆反应当不可逆、产物不回落 | ReversibleReaction.C 第 131 行 |
type 写成 irreversible... |
改 reversible... |
10. 全章小结
10.1 四层与基类索引
| 层 | 基类/入口 | 关键产物 | 小节 |
|---|---|---|---|
| 燃烧模型 | combustionModel(IOdictionary)→CombustionModel→ChemistryCombustion/ThermoCombustion |
correct()/R()/Qdot()、kappa |
§2 |
| 化学模型 | basicChemistryModel→BasicChemistryModel→StandardChemistryModel/TDACChemistryModel |
deltaTChem、RR、Qdot、tc |
§3 |
| 化学求解器 | chemistrySolver<ChemistryModel> |
solve(c,T,p,deltaT,subDeltaT) |
§4 |
| 反应与速率 | Reaction→Irreversible/Reversible;reactionRate 家族 |
kf/kr、operator() |
§5 |
| 组分/热物理 | reactionThermo+mixtures+chemistryReaders |
组分数、焓/温度、机理读入 | §6 |
| 加速 | chemistryReductionMethod/chemistryTabulationMethod |
DRG/DRGEP/PFA/EFA/DAC、ISAT |
§8 |
10.2 五个"一句话抓住本质"
- 燃烧模型三件套
correct()/R()/Qdot();EDC/PaSR只是给laminar结果乘反应分数kappa_。 - 化学模型 = 一个 ODE 系统 :
StandardChemistryModel继承ODESystem,把(c,T,p)打包成状态向量,derivatives/jacobian交ode求解器积分。 - 化学求解器通过模板继承插入化学模型 :
ode<StandardChemistryModel<...>>继承并实现其纯虚solve,零虚函数开销。 - Arrhenius 是速率层唯一基础 :
k=A T^\beta e^{-T_a/T};三体/fall-off/化学活化都是在它外面乘因子或插值。 - TDAC = reduction + tabulation:前者临时缩小机理,后者用误差椭球+二叉搜索树"算过就记住",二者正交可组合。
10.3 涉及的真实文件索引
bash
src/combustionModels/{CombustionModel,laminar,EDC,PaSR,singleStepCombustion,
eddyDissipationModelBase,eddyDissipationDiffusionModel,diffusion,
diffusionMulticomponent,infinitelyFastChemistry,FSD,zoneCombustion,noCombustion}/
src/thermophysicalModels/chemistryModel/
chemistryModel/{BasicChemistryModel,StandardChemistryModel,TDACChemistryModel}/
TDACChemistryModel/{reduction/{DRG,DRGEP,PFA,EFA,DAC},tabulation/{ISAT,chemPointISAT,binaryTree}}/
chemistrySolver/{chemistrySolver,ode,EulerImplicit,noChemistrySolver}/
src/thermophysicalModels/specie/reaction/{Reactions,reactionRate}/
src/thermophysicalModels/reactionThermo/{mixtures,chemistryReaders}/
src/ODE/ODESolvers/
applications/solvers/combustion/{reactingFoam,chemFoam}/
tutorials/combustion/
本章要点(速查)
- 燃烧 = 燃烧模型层 + 化学模型层 + 化学求解器层 + 反应/组分热物理层 ;
combustionModel本身是IOdictionary并引用湍流模型。 - 燃烧模型二分(已按源码纠正 ):
ChemistryCombustion分支 =laminar/EDC/PaSR/diffusionMulticomponent;ThermoCombustion分支 =singleStepCombustion(→diffusion/EDM/infinitelyFastChemistry)与FSD;zoneCombustion/none直接挂CombustionModel。 - 化学模型:
basicChemistryModel管理deltaTChemIni/Max/deltaTChem_;StandardChemistryModel(omega/derivatives/jacobian)直接积分;TDACChemistryModel用 reduction + tabulation(ISAT) 加速。 - 化学求解器
solve(c,T,p,deltaT,subDeltaT):ode(seulex/Rosenbrock34/rodas23...)、EulerImplicit、none。 - 反应速率:Arrhenius
k=A·T^β·exp(−Ta/T)(ArrheniusReactionRateI.H第 57--77 行),以及三体、fall-off(Troe/SRI/Lindemann)、化学活化、LangmuirHinshelwood、powerSeries、Janev、LandauTeller、infinite。 - 字典要点:
combustionModel(非type)选燃烧模型;chemistryType { solver; method; }选化学求解器/模型;reaction用type选反应+速率复合类型;reduction/tabulation配加速。