OpenFOAM 源码深度剖析 · 第二季-02 燃烧与化学反应:`combustionModel`、化学求解器与 Arrhenius 反应率

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 的细结构时间尺度与反应分数

设湍动能 kk k、耗散率 ϵ\epsilon ϵ、运动黏度 ν\nu ν,Kolmogorov 时间尺度 τη=(ν/ϵ)1/2 \tau_\eta=(\nu/\epsilon)^{1/2} τη=(ν/ϵ)1/2。

  1. 细结构质量分数 (源码 gammaL = Cgamma*(nu*epsilon/k^2)^{1/4}):

γL=Cγ( νϵk2 )1/4 \gamma_L=C_\gamma\Big(\frac{\nu\epsilon}{k^2}\Big)^{1/4} γL=Cγ(k2νϵ)1/4

γL→1 \gamma_L\to1 γL→1 表示整个控制体都是细结构、全部参与反应。

  1. 细结构停留时间 (源码 tauStar = Ctau*sqrt(nu/epsilon)):

τ∗=Cτ τη=Cτ(ν/ϵ)1/2 \tau^*=C_\tau\,\tau_\eta=C_\tau(\nu/\epsilon)^{1/2} τ∗=Cττη=Cτ(ν/ϵ)1/2

  1. 反应分数 (源码 kappa = gammaL^exp1/(1-gammaL^exp2)):

κ= γL  e1 1− γL  e2 ∈0,1 \kappa=\frac{\gamma_L^{\,e_1}}{1-\gamma_L^{\,e_2}}\in0,1 κ=1−γLe2γLe1∈0,1

(e1,e2) (e_1,e_2) (e1,e2):v2005 取 (2,2)(2,2) (2,2)、v1981 取 (3,3)(3,3) (3,3)、v1996 取 (2,3)(2,3) (2,3)、v2016 取 (2,2)(2,2) (2,2)。

  1. v2016 的 Da/Re 局部化 :用化学时间 τc=tc \tau_c=tc τc=tc 定义湍流丹姆科勒数 Da= ν/ϵ /τc Da=\sqrt{\nu/\epsilon}/\tau_c Da=ν/ϵ /τc、湍流雷诺数 ReT=k2/(νϵ) Re_T=k^2/(\nu\epsilon) ReT=k2/(νϵ),把常数换成局部值:

Cτ,I =min⁡ ⁣( C1 Da ReT+1 , 2.1377), Cγ,I =clamp ⁣(C2 Da(ReT+1) , 0.4082, 5) C_{\tau,I}=\min\!\Big(\frac{C_1}{Da\sqrt{Re_T+1}},\,2.1377\Big),\qquad C_{\gamma,I}=\mathrm{clamp}\!\big(C_2\sqrt{Da(Re_T+1)},\,0.4082,\,5\big) Cτ,I=min(DaReT+1 C1,2.1377),Cγ,I=clamp(C2Da(ReT+1) ,0.4082,5)

  1. 源项缩比 : ω˙EDC =κ  ω˙laminar \dot\omega_{\text{EDC}}=\kappa\,\dot\omega_{\text{laminar}} ω˙EDC=κω˙laminar,即化学只在其时空占比为 κ\kappa κ 的细结构里发生。

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') \tau_{mix}=C_{mix}\sqrt{\frac{\mu_{eff}}{\rho\,\epsilon}}=C_{mix}\sqrt{\frac{\nu_{eff}}{\epsilon}} \quad(\text{源码第 91-92 行 `tk`}) τmix=Cmixρϵμeff =Cmixϵνeff (源码第 91-92 行 'tk')

反应分数取"反应时间占(反应+混合)时间"之比:
κ= τc τc+ τmix (源码第 96 行) \kappa=\frac{\tau_c}{\tau_c+\tau_{mix}}\quad(\text{源码第 96 行}) κ=τc+τmixτc(源码第 96 行)

  • τc≪ τmix \tau_c\ll\tau_{mix} τc≪τmix(混合控制): κ→0\kappa\to0 κ→0,速率由湍流决定;
  • τc≫ τmix \tau_c\gg\tau_{mix} τc≫τmix(有限速率控制): κ→1\kappa\to1 κ→1,退化为 laminar。

EDC 用细结构 γL,τ∗ \gamma_L,\tau^* γL,τ∗ 构造 κ\kappa κ,PaSR 用 τc/(τc+ τmix ) \tau_c/(\tau_c+\tau_{mix}) τc/(τc+τmix);二者最终都做 kappa_*laminar::R。Cmix 越大, τmix \tau_{mix} τmix 越大、 κ\kappa κ 越接近 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 w_F=\rho\min(Y_F,\,Y_O/s)\,\tau_{mix}^{-1} 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 行)给出字典:

    cpp 复制代码
    diffusionMulticomponentCoeffs
    {
        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\cdot\mathrm{prob}\cdot\mu_{eff}\cdot|\nabla Y_i\cdot\nabla Y_j| D⋅prob⋅μeff⋅∣∇Yi⋅∇Yj∣ 取小; D=Cf02 D=C f_{02} D=Cf02, f02=1+(O2/oxidantRes)2 f_{02}=1+(\mathrm{O2}/\mathrm{oxidantRes})^2 f02=1+(O2/oxidantRes)2。

  • FSD (FSD/FSD.H 第 85--164 行):火焰面密度,继承 singleStepCombustion,TypeName("FSD")(第 138 行)。燃料源项 = mgft pc ωFuel ‾ =m_{gft}p_c\overline{\omega_{Fuel}} =mgftpcωFuel(类注释第 32--62 行);消耗速度由松弛方程逼近平衡值。下设 reactionRateFlameAreaModels/relaxation(relaxation.H 第 54--112 行,持 consumptionSpeed 与 C_/alpha_)与 consumptionSpeed(consumptionSpeed.H 第 52--130 行,参数 ω0,η, σExt , ωMin \omega_0,\eta,\sigma_{Ext},\omega_{Min} ω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 细结构 κ\kappa κ 缩比 非预混/部分预混湍流火焰
PaSR laminar 是 Cmix 时间尺度比 τc/(τc+ τmix ) \tau_c/(\tau_c+\tau_{mix}) τ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<1e<1 e<1 且浓度趋 0 时把 pf 置零,避免 0负数0^{负数} 0负数 发散。

数学推导 C:Arrhenius 速率 + 反应源项
  1. 速率常数 (ArrheniusReactionRateI.H 第 57--77 行,见 §5.4):

kf(T)=A Tβexp⁡ ⁣(− TaT ),Ta= EaRu k_f(T)=A\,T^{\beta}\exp\!\Big(-\frac{T_a}{T}\Big),\qquad T_a=\frac{E_a}{R_u} kf(T)=ATβexp(−TTa),Ta=RuEa

逆向由平衡常数给出(ReversibleReaction.C 第 131 行): kr=kf/Kc k_r=k_f/K_c kr=kf/Kc。

  1. 质量作用定律 :对 ∑jνj′Sj⇌∑j νj′′ Sj \sum_j\nu'_j S_j\rightleftharpoons\sum_j\nu''_j S_j ∑jνj′Sj⇌∑jνj′′Sj,

ωr=kf∏j cjνj′ −kr∏j cj νj′′ (源码第 275 行) \omega_r=k_f\prod_j c_j^{\nu'_j}-k_r\prod_j c_j^{\nu''_j}\quad(\text{源码第 275 行}) ωr=kfj∏cjνj′−krj∏cjνj′′(源码第 275 行)

  1. 组分摩尔浓度源项:

dci dt =∑r( νi,r′′ − νi,r′ )ωr(源码第 133/140 行) \frac{\mathrm{d}c_i}{\mathrm{d}t}=\sum_r\big(\nu''{i,r}-\nu'{i,r}\big)\omega_r \quad(\text{源码第 133/140 行}) dtdci=r∑(νi,r′′−νi,r′)ωr(源码第 133/140 行)

  1. 质量源项 (calculate 第 652 行,calculateRR 第 615 行):

RRi= dci dt Wi,ci= ρYi Wi RR_i=\frac{\mathrm{d}c_i}{\mathrm{d}t}W_i,\qquad c_i=\frac{\rho Y_i}{W_i} 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\mathrm{d}h=0 dh=0), h=∑Yihi(T) h=\sum Y_i h_i(T) h=∑Yihi(T):
0= dhdt =∑ihi dYi dt + cˉp dTdt 0=\frac{\mathrm{d}h}{\mathrm{d}t}=\sum_i h_i\frac{\mathrm{d}Y_i}{\mathrm{d}t}+\bar c_p\frac{\mathrm{d}T}{\mathrm{d}t} 0=dtdh=i∑hidtdYi+cˉpdtdT

用 Yi=ciWi/ρ Y_i=c_iW_i/\rho Yi=ciWi/ρ 换算并对摩尔浓度求导,得
dTdt =− 1 ρ cˉp ∑ihi  dcdti \frac{\mathrm{d}T}{\mathrm{d}t}=-\frac{1}{\rho\bar c_p}\sum_i h_i\,\mathrm{dcdt}_i dtdT=−ρcˉp1i∑hidcdti

源码 dT = Σ h_i·dcdt[i]/(rho*cp) 后写 dcdt[nSpecie_] = -dT(温度在状态向量第 nSpecie_ 位,导数为 −dT-dT −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 k_f\prod c_i^{e_i} kf∏ciei 求 ∂/∂cj \partial/\partial c_j ∂/∂cj:被求导组分的指数减一(el-1),其余保持幂------链式法则在质量作用定律上的直接实现。温度列用 ±10−3\pm10^{-3} ±10−3 中心差分(含 exp⁡(−Ta/T) \exp(-T_a/T) 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˙=−∑i Hc,i RRi \dot Q=-\sum_i H_{c,i}RR_i 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 RR_i=(c_i^{new}-c_i^{old})W_i/\Delta t 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 行)同样做"完整 cc c ↔ 简化 cc 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)(c,T,p) (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) \mathrm{d}c_i/\mathrm{d}t=\dot\omega_i(\mathbf c) dci/dt=ω˙i(c),隐式欧拉为
cin+1 =cin+Δt  ω˙i (cn+1) c_i^{n+1}=c_i^{n}+\Delta t\,\dot\omega_i(\mathbf c^{n+1}) cin+1=cin+Δtω˙i(cn+1)

在 cn\mathbf c^n cn 处一阶展开 ω˙i (cn+1)≈ ω˙i (cn)+∑j Jij ( cjn+1 −cjn) \dot\omega_i(\mathbf c^{n+1})\approx\dot\omega_i(\mathbf c^n)+\sum_j J_{ij}(c_j^{n+1}-c_j^n) ω˙i(cn+1)≈ω˙i(cn)+∑jJij(cjn+1−cjn),整理为线性系统:
( IΔt −J)cn+1= cnΔt +ω˙(cn) \Big(\frac{\mathbf I}{\Delta t}-\mathbf J\Big)\mathbf c^{n+1} =\frac{\mathbf c^{n}}{\Delta t}+\dot{\boldsymbol\omega}(\mathbf c^{n}) (ΔtI−J)cn+1=Δtcn+ω˙(cn)

源码:矩阵 (I/Δt−J)\big(\mathbf I/\Delta t-\mathbf J\big) (I/Δt−J) 由 RR(i,i) += 1/deltaT(第 178 行)与 updateRRInReactionI 的 −J-\mathbf J −J 部分构成;右端 RR.source()[i]=c[i]/deltaT(第 179 行)对应 cn/Δt\mathbf c^n/\Delta t cn/Δt(显式 ω˙ \dot\omega ω˙ 项以 corr 速率限制近似,第 134--144 行)。稳定时间步 (第 150--168 行):净消耗时 Δt<ci/∣ ω˙i ∣ \Delta t<c_i/|\dot\omega_i| Δt<ci/∣ω˙i∣,净生成时 Δt<( ctot −ci)/ ω˙i \Delta t<(c_{tot}-c_i)/\dot\omega_i Δt<(ctot−ci)/ω˙i,取最小后乘 cTauChem。解出新 c\mathbf c c 后由 ∑iYihi(T)=ha \sum_i Y_i h_i(T)=h_a ∑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\Delta h Δh 与 Kc K_c 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 K_c 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|\beta|>\text{VSMALL} ∣β∣>VSMALL 才 pow(T,beta),仅当 ∣Ta∣>VSMALL |T_a|>\text{VSMALL} ∣Ta∣>VSMALL 才 exp(-Ta/T)------化学内层循环的微优化。
数学推导 F:Arrhenius 速率(略,见推导 C 第 1 步)

 k(T)=A Tβexp⁡(−Ta/T)  ,Ta=Ea/Ru \boxed{\,k(T)=A\,T^{\beta}\exp(-T_a/T)\,},\qquad T_a=E_a/R_u k(T)=ATβexp(−Ta/T),Ta=Ea/Ru

由 dln⁡k/dT=β/T+Ta/T2\mathrm{d}\ln k/\mathrm{d}T=\beta/T+T_a/T^2 dlnk/dT=β/T+Ta/T2,当 Ta≫T T_a\gg T Ta≫T 时该导数极大------这是刚性 的根源。jacobian(StandardChemistryModel.C 第 349--462 行)对浓度用解析导数、对温度用中心差分构造 J\mathbf J J。

数值演算(以教程 membrane 的甲烷一步反应为例) :该反应 A=5.2e16, beta=0, Ta=14906(tutorials/combustion/reactingFoam/RAS/membrane/constant/reactions 第 32--34 行)。在 T=1500 KT=1500\,\mathrm{K} 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×1012k=A e^{-T_a/T}=5.2\times10^{16}\cdot e^{-14906/1500} =5.2\times10^{16}\cdot e^{-9.9373}\approx5.2\times10^{16}\cdot4.83\times10^{-5}\approx2.51\times10^{12} 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=2000 KT=2000\,\mathrm{K} T=2000K: e−14906/2000=e−7.453≈5.79×10−4e^{-14906/2000}=e^{-7.453}\approx5.79\times10^{-4} e−14906/2000=e−7.453≈5.79×10−4, k≈3.01×1013k\approx3.01\times10^{13} k≈3.01×1013。温度升高 500 K 速率常数增大 12 倍 ------这正是"刚性/强非线性"在数值上的直观体现,也解释了为什么必须给化学模型一个小而自适应的 deltaTChem(尤其点火初期温度从 300 K 上升到 1500 K 时, kk k 会跨越十几个数量级)。

Treact 的作用 :StandardChemistryModel::solve(第 686 行)用 if (Ti > Treact_) 决定是否积分。若把 Treact 设得偏高(例如 800 K),未着火区域会被"跳过",既省算力又避免低温下的不必要刚性;设错(过高)则可能漏掉真实着火。

A 的量纲陷阱 :A 的单位随反应分子数变化(一级反应 s−1s^{-1} s−1、二级 m3/(mol⋅s)m^3/(mol\cdot s) 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′′ k_f\prod c_{j,eq}^{\nu'j}=k_r\prod c{j,eq}^{\nu''j} kf∏cj,eqνj′=kr∏cj,eqνj′′,故
kfkr =∏j cj,eq νj′′ −νj′ =Kc(T) ⟹ kr= kfKc \frac{k_f}{k_r}=\prod_j c
{j,eq}^{\nu''_j-\nu'_j}=K_c(T) \ \Longrightarrow\ k_r=\frac{k_f}{K_c} krkf=j∏cj,eqνj′′−νj′=Kc(T) ⟹ kr=Kckf

源码 kfwd/max(Kc,VSMALL)(第 131 行)。 Kc K_c 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 k^{3body}=k_{Arrhenius}\cdot M,\qquad M=\sum_i\varepsilon_i c_i k3body=kArrhenius⋅M,M=i∑εici

εi \varepsilon_i εi 为各组分第三体效率( ε H2O ≈6.5 \varepsilon_{H_2O}\approx6.5 εH2O≈6.5、 εN2 =1 \varepsilon_{N_2}=1 εN2=1、 εAr ≈0.7 \varepsilon_{Ar}\approx0.7 ε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= k0 M(c) k∞ ,k=k∞ Pr 1+Pr F P_r=\frac{k_0\,M(c)}{k_\infty},\qquad k=k_\infty\frac{P_r}{1+P_r}F Pr=k∞k0M(c),k=k∞1+PrPrF

  • Pr≪1 P_r\ll1 Pr≪1(低压): k≈k0M k\approx k_0M k≈k0M(三体极限);
  • Pr≫1 P_r\gg1 Pr≫1(高压): k≈k∞ k\approx k_\infty k≈k∞。

Troe 的 FF F 用 log⁡10 Fcent \log_{10}F_{cent} log10Fcent 修正曲率:
log⁡10 Fcent = log⁡10  ⁣(1−α)e −T/ Tsss +αe −T/Ts +e − Tss /T \log_{10}F_{cent}=\log_{10}\!\Big(1-\\alpha)e\^{-T/T_{sss}}+\\alpha e\^{-T/T_s}+e\^{-T_{ss}/T}\\Big log10Fcent=log10(1−α)e−T/Tsss+αe−T/Ts+e−Tss/T
log⁡10 F= log⁡10 Fcent 1+( log⁡10 Pr+c)/(n−d( log⁡10 Pr+c))2 ,c=−0.4−0.67 log⁡10 Fcent , n=0.75−1.27 log⁡10 Fcent , d=0.14 \log_{10}F=\frac{\log_{10}F_{cent}}{1+\big(\\log_{10}P_r+c)/(n-d(\\log_{10}P_r+c))\\big^2},\quad c=-0.4-0.67\log_{10}F_{cent},\ n=0.75-1.27\log_{10}F_{cent},\ d=0.14 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/cXTeF=da e\^{-b/T}+e\^{-T/c}^{X}T^{e} F=dae−b/T+e−T/cXTe, X=1/(1+( log⁡10 Pr)2)X=1/(1+(\log_{10}P_r)^2) X=1/(1+(log10Pr)2)。CHEMKIN 关键字 Troe/SRI/(无修正)→Lindemann,由 makePressureDependentReactions 注册。

5.8 其余速率类型

速率类 类型名 要点
LangmuirHinshelwoodReactionRate "LangmuirHinshelwood" 5 组 (Ai,Tai) (A_i,Ta_i) (Ai,Tai),含 CO/C3H6/NO 抑制项
powerSeriesReactionRate "powerSeries" k=ATβexp⁡(∑ncn/Tn+1)k=A T^\beta\exp(\sum_n c_n/T^{n+1}) k=ATβexp(∑ncn/Tn+1)
JanevReactionRate "Janev" 等离子体/电离半经验式
LandauTellerReactionRate "LandauTeller" 含 (1+B/T1/3)(1+B/T^{1/3}) (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 A T^\beta e^{-T_a/T} ATβe−Ta/T T
thirdBodyArrheniusReactionRate thirdBodyArrhenius M(c)⋅ATβe −Ta/T M(c)\cdot A T^\beta e^{-T_a/T} M(c)⋅ATβe−Ta/T T,c
FallOffReactionRate<...,Troe/SRI/Lindemann> ...TroeFallOff 等 k∞ Pr 1+Pr F k_\infty\frac{P_r}{1+P_r}F k∞1+PrPrF T,p,c
ChemicallyActivatedReactionRate ...ChemicallyActivatedFallOff k0 1 1+Pr F k_0\frac{1}{1+P_r}F k01+Pr1F T,p,c
LangmuirHinshelwoodReactionRate LangmuirHinshelwood 5 组 (A,Ta) 抑制式 T,c
powerSeriesReactionRate powerSeries ATβe ∑cn/Tn+1 A T^\beta e^{\sum c_n/T^{n+1}} ATβe∑cn/Tn+1 T
infiniteReactionRate infinite 1 ---

6. 组分与热物理层(第 ④ 层)

reactionThermo (thermophysicalModels/reactionThermo/):rhoReactionThermo(密度)、psiReactionThermo(可压缩性 ψ\psi ψ)、psiuReactionThermo/heheuPsiThermo( ψ\psi ψ+内能)。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\kappa\to1 κ→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−∑Y1-\sum Y 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 k_r=k_f/K_c 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_{AB} rAB 与 R 值公式)、PFA(第 65 行)、EFA(第 63 行)、DAC(第 132 行,附 automaticSIS/phiTol/NOxThreshold/fuelSpecies 等)、none(第 59 行)。

rAB r_{AB} rAB(误差传播权重,DRGEP.H 第 30--46 行):
rAB = ∣ ∑i=1Nr νAi ωi dBi ∣ max⁡(PA,CA) ,PA=∑imax⁡(0, νAi ωi),CA=∑imax⁡(0,− νAi ωi) r_{AB}=\frac{\big|\sum_{i=1}^{N_r}\nu_{Ai}\omega_i d_{Bi}\big|}{\max(P_A,C_A)},\quad P_A=\sum_i\max(0,\nu_{Ai}\omega_i),\quad C_A=\sum_i\max(0,-\nu_{Ai}\omega_i) 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)= max⁡SP ∏ rij R_{V_0}(V)=\max_{SP}\prod r_{ij} 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) \mathcal{R}(\phi_q)\approx\mathcal{R}(\phi_0)+A(\phi_q-\phi_0) 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 的误差控制与树结构
  1. 精度椭球 EOA (chemPointISAT.H 注释第 35--49 行): E={x:∥LT(x−ϕ0)∥≤1}E=\{x:\|L^{\mathsf T}(x-\phi_0)\|\le1\} E={x:∥LT(x−ϕ0)∥≤1}, LTLL^{\mathsf T}L LTL 是 (BA/tol)T(BA/tol)\big(BA/\text{tol}\big)^{\mathsf T}\big(BA/\text{tol}\big) (BA/tol)T(BA/tol) 的 Cholesky 分解。查询点 ϕq∈E \phi_q\in E ϕq∈E 时线性插值: R(ϕq)≈R(ϕ0)+A(ϕq−ϕ0) \mathcal{R}(\phi_q)\approx\mathcal{R}(\phi_0)+A(\phi_q-\phi_0) R(ϕq)≈R(ϕ0)+A(ϕq−ϕ0)
  2. 两级判据 (注释第 41--47 行):先比较半径 r=∥dϕ∥r=\|\mathrm{d}\phi\| r=∥dϕ∥ 与 rmin⁡,rmax⁡ r_{\min},r_{\max} rmin,rmax,仅中间区做 ∥LTdϕ∥≤1\|L^{\mathsf T}\mathrm{d}\phi\|\le1 ∥LTdϕ∥≤1 完整判据。
  3. 增长判据 (注释第 52--61 行): epsGrow=∥B(dR−dRl)∥,dR=R(ϕq)−R(ϕ0), dRl=Adϕ\text{epsGrow}=\|B(\mathrm{d}R-\mathrm{d}R_l)\|,\quad \mathrm{d}R=\mathcal{R}(\phi_q)-\mathcal{R}(\phi_0),\ \mathrm{d}R_l=A\mathrm{d}\phi epsGrow=∥B(dR−dRl)∥,dR=R(ϕq)−R(ϕ0), dRl=Adϕ epsGrow ≤ tolerance → grow;否则新增叶子。
  4. 最小体积椭球 grow (注释第 91--101 行): p′=LT(p−ϕ0)p'=L^{\mathsf T}(p-\phi_0) p′=LT(p−ϕ0); G=I+γp′p′TG=I+\gamma p'p'^{\mathsf T} G=I+γp′p′T, γ=( 1∣p′∣ −1) 1∣p′∣2 \gamma=(\frac{1}{|p'|}-1)\frac{1}{|p'|^2} γ=(∣p′∣1−1)∣p′∣21; L′=LGL'=LG L′=LG(QR 保证 L′L' L′ 仍三角,chemPointISAT.H 第 218--240 行 qrDecompose/qrUpdate/rotate)。
  5. 梯度矩阵 (ISAT.C 注释第 374--382 行):由变分方程 dC/dt=JC, C(ϕ0,t0)=I\mathrm{d}C/\mathrm{d}t=J C,\ C(\phi_0,t_0)=I dC/dt=JC, C(ϕ0,t0)=I,隐式离散得 A=(I−ΔtJ)−1A=(I-\Delta t J)^{-1} A=(I−ΔtJ)−1,源码 jacobian 后求逆(第 384/427 行)。
  6. 树结构 (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 五个"一句话抓住本质"

  1. 燃烧模型三件套 correct()/R()/Qdot();EDC/PaSR 只是给 laminar 结果乘反应分数 kappa_。
  2. 化学模型 = 一个 ODE 系统 :StandardChemistryModel 继承 ODESystem,把 (c,T,p) 打包成状态向量,derivatives/jacobian 交 ode 求解器积分。
  3. 化学求解器通过模板继承插入化学模型 :ode<StandardChemistryModel<...>> 继承并实现其纯虚 solve,零虚函数开销。
  4. Arrhenius 是速率层唯一基础 :k=A T^\beta e^{-T_a/T};三体/fall-off/化学活化都是在它外面乘因子或插值。
  5. 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 配加速。
相关推荐
鶴哥只手遮天1 小时前
Gmsh 源码深度解析-第 6 章 三维体网格算法
算法·开源
鶴哥只手遮天1 小时前
Gmsh 源码深度解析-第 3 章 脚本语言与解析器
算法·开源
鶴哥只手遮天1 小时前
OpenFOAM 源码深度剖析 · 第二季-16 工程与工具链:Windows 交叉编译、性能剖析、数据交换与测试
算法·开源
鶴哥只手遮天1 小时前
Gmsh 源码深度解析-第 5 章 二维曲面网格算法
算法·开源
鶴哥只手遮天1 小时前
Gmsh 源码深度解析-第 1 章 架构总览与数据模型
算法·开源
鶴哥只手遮天1 小时前
OpenFOAM 源码深度剖析 · 第二季-12 `coded` 系列:在字典里写 C++ 并在运行期编译
算法·开源
鶴哥只手遮天1 小时前
OpenFOAM 源码深度剖析 · 第二季-01 多相流与 VOF:`interFoam`、界面压缩与 MULES
算法·开源
鶴哥只手遮天1 小时前
OpenFOAM 源码深度剖析 · 第二季-14 `functionObjects`、求解器控制框架 与 表达式系统
算法·开源
鶴哥只手遮天1 小时前
OpenFOAM 源码深度剖析 · 第二季-13 `fvOptions` / `fvConstraints` / `corrections`
算法·开源