流体与条件设置
主要公式
$T/T_0 = (1 + \frac{\gamma-1}{2}M^2)^{-1}$
$P/P_0 = (T/T_0)^{\gamma/(\gamma-1)}$
$A/A^* = \frac{1}{M}\left[\frac{2+(γ-1)M^2}{γ+1}\right]^{\frac{γ+1}{2(γ-1)}}$
流体特性比 (T/T₀, P/P₀, ρ/ρ₀) at M
// Nozzle flow animation
(function() {
const el = document.getElementById('nozzleAnimCanvas');
const ctx = el.getContext('2d');
const NPART = 80;
const particles = [];
let animT = 0;
function getParams() {
const M_exit_el = document.getElementById('mStatVal');
const M_exit = M_exit_el ? parseFloat(M_exit_el.textContent) || 1.5 : 1.5;
const gamma_el = document.getElementById('gamma');
const gamma = gamma_el ? parseFloat(gamma_el.value) || 1.4 : 1.4;
return { M_exit, gamma };
}
function resize() {
const dpr = window.devicePixelRatio || 1;
const w = el.parentElement.clientWidth - 36;
const H = Math.round(w * 0.4);
if (Math.abs(el.width - w * dpr) > 2 || el.height !== H * dpr) {
el.width = w * dpr;
el.height = H * dpr;
el.style.height = H + 'px';
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
particles.length = 0;
for (let i = 0; i < NPART; i++) {
particles.push({ x: Math.random() * w, y: H/2 + (Math.random()-0.5) * H * 0.3, speed: 1 });
}
}
return { W: w, H };
}
// Convergent-divergent nozzle profile
function nozzleHalfHeight(x, W, H) {
const throatX = W * 0.5;
const inletH = H * 0.42;
const throatH = H * 0.18;
const exitH = H * 0.38;
if (x <= throatX) {
const t = x / throatX;
return inletH + (throatH - inletH) * t * t;
} else {
const t = (x - throatX) / (W - throatX);
return throatH + (exitH - throatH) * t;
}
}
function machAtX(x, W, M_exit) {
// Linear interpolation: M=0.3 at inlet, M=1 at throat, M=M_exit at exit
const throatX = W * 0.5;
if (x <= throatX) {
const t = x / throatX;
return 0.3 + (1.0 - 0.3) * t;
} else {
const t = (x - throatX) / (W - throatX);
return 1.0 + (M_exit - 1.0) * t;
}
}
// Mach number to color: subsonic=blue, sonic=green, supersonic=red
function machColor(M) {
if (M < 1) {
const t = M;
return `rgb(0,${Math.round(100 + t*80)},255)`;
} else {
const t = Math.min(1, (M - 1) / 2);
return `rgb(${Math.round(t*230)},${Math.round(200 - t*200)},${Math.round(255 - t*255)})`;
}
}
function frame() {
const { W, H } = resize();
const { M_exit } = getParams();
animT += 1/60;
ctx.clearRect(0, 0, W, H);
ctx.fillStyle = '#001F3F';
ctx.fillRect(0, 0, W, H);
const cy = H / 2;
// Draw colored Mach number background
const NX = 80;
for (let i = 0; i < NX; i++) {
const x = (i / NX) * W;
const x2 = ((i+1) / NX) * W;
const M = machAtX(x, W, M_exit);
const h = nozzleHalfHeight(x, W, H);
ctx.fillStyle = machColor(M);
ctx.globalAlpha = 0.35;
ctx.fillRect(x, cy - h, x2 - x + 1, h * 2);
}
ctx.globalAlpha = 1;
// Draw nozzle walls
ctx.strokeStyle = '#007BFF';
ctx.lineWidth = 3;
ctx.beginPath();
for (let i = 0; i <= 100; i++) {
const x = (i / 100) * W;
const h = nozzleHalfHeight(x, W, H);
if (i === 0) ctx.moveTo(x, cy - h);
else ctx.lineTo(x, cy - h);
}
ctx.stroke();
ctx.beginPath();
for (let i = 0; i <= 100; i++) {
const x = (i / 100) * W;
const h = nozzleHalfHeight(x, W, H);
if (i === 0) ctx.moveTo(x, cy + h);
else ctx.lineTo(x, cy + h);
}
ctx.stroke();
// Throat marker
ctx.strokeStyle = 'rgba(255,255,255,0.5)';
ctx.lineWidth = 1;
ctx.setLineDash([3, 3]);
const throatX = W * 0.5;
const throatH = nozzleHalfHeight(throatX, W, H);
ctx.beginPath();
ctx.moveTo(throatX, cy - throatH - 8);
ctx.lineTo(throatX, cy + throatH + 8);
ctx.stroke();
ctx.setLineDash([]);
ctx.fillStyle = 'rgba(255,255,255,0.6)';
ctx.font = '9px Roboto Mono';
ctx.textAlign = 'center';
ctx.fillText('喉部 M=1', throatX, cy - throatH - 12);
// Particles
for (let p of particles) {
const M = machAtX(p.x, W, M_exit);
const h = nozzleHalfHeight(p.x, W, H);
// Check if inside nozzle
if (Math.abs(p.y - cy) > h * 0.95) {
p.x = 2;
p.y = cy + (Math.random() - 0.5) * nozzleHalfHeight(0, W, H) * 1.8;
}
const spd = 1.5 + M * 2.5;
p.x += spd;
if (p.x > W) {
p.x = 2;
p.y = cy + (Math.random() - 0.5) * nozzleHalfHeight(0, W, H) * 1.8;
}
ctx.beginPath();
ctx.arc(p.x, p.y, 1.5, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255,255,255,0.7)';
ctx.fill();
}
// Labels
ctx.fillStyle = 'rgba(255,255,255,0.8)';
ctx.font = 'bold 10px Roboto Mono';
ctx.textAlign = 'left';
ctx.fillText('M_exit = ' + M_exit.toFixed(2), 8, 16);
requestAnimationFrame(frame);
}
frame();
})();
什么是等熵喷管流动
🙋
「收缩-扩张喷管」是什么?为什么火箭发动机的尾巴是那个形状?
🎓
简单来说,它就像一个「流体加速器」。前面收缩的部分把亚音速气流加速到音速(马赫数=1),后面扩张的部分再把音速气流加速到超音速。火箭发动机需要喷出高速气体来获得巨大推力,所以就是那个形状。你可以在模拟器里选择「输入模式」,比如输入出口马赫数,就能立刻看到喷管喉部和出口的所有压力、温度变化,非常直观!
🙋
诶,真的吗?那如果我在出口把压力设得非常低,会发生什么?
🎓
在实际工程中,如果背压(喷管外的压力)远低于设计值,扩张段里可能会产生复杂的激波系,甚至导致气流分离。我们的模拟器里有一个超有用的功能——「添加正激波」。你勾选它,然后调整出口压力,就能亲眼看到一道正激波在喷管扩张段里形成和移动,激波前后的压力、温度会突然跳变,这解释了为什么设计不当的喷管效率会暴跌。
🙋
听起来好酷!那这个「等熵」是什么意思?现实中的流动真的没有能量损失吗?
🎓
好问题!「等熵」是一个理想的简化模型,意味着流动中既没有摩擦生热,也没有激波这样的不可逆过程,熵保持不变。这让我们能用一套漂亮的公式直接算出参数。虽然真实喷管有损失,但等熵计算是性能评估的黄金基准。比如在汽车涡轮增压器的喷嘴设计中,工程师会先做等熵计算得到理想目标,再用CFD软件模拟真实粘性流动,对比两者的差距来优化设计。你试着在模拟器里改变马赫数,看看温度比和压力比怎么跟着变,就能感受到这个理想模型的威力了。
物理模型与关键公式
等熵流动的核心关系式,关联了任意位置马赫数(M)与当地静参数(压力P、TemperatureT、密度ρ)相对于总参数(滞止参数,下标0)的比值。这是分析可压缩流的基础。
$$ \frac{P}{P_0}= \left(1 + \frac{\gamma - 1}{2}M^2\right)^{-\frac{\gamma}{\gamma-1}}$$
其中,$P$为静压,$P_0$为总压,$\gamma$为比热容比(空气常取1.4),$M$为马赫数。这个公式告诉你,气流速度(M)越大,静压就越低。
喷管喉部(最小截面)达到声速(M=1)的条件,由此可计算临界参数和最大质量流量。这是喷管设计的关键。
$$ \dot{m}_{max}= \frac{P_0 A_t}{\sqrt{T_0}}\sqrt{\frac{\gamma}{R}}\left( \frac{\gamma+1}{2}\right)^{-\frac{\gamma+1}{2(\gamma-1)}}$$
其中,$\dot{m}_{max}$是最大质量流量,$A_t$是喉部面积,$T_0$是总温,$R$是气体常数。一旦喷管「堵塞」,流量就只取决于上游条件,不再受下游压力影响。
现实世界中的应用
火箭发动机与航天推进:这是最经典的应用。大型液氢液氧火箭发动机的喷管巨大,其扩张段设计完全基于等熵流动计算,以在真空中将高温燃气加速到极高的马赫数(>4),从而获得最大推力。模拟器中的面积比与马赫数关系直接指导了喷管的型线设计。
涡轮机械(汽轮机/燃气轮机):蒸汽轮机或燃气轮机的末级叶片通道,本质上就是一个微型的收缩-扩张喷管阵列。工程师使用等熵计算来预估蒸汽或燃气在叶片间的膨胀过程,以及可能产生的激波位置,这对提高发电效率至关重要。
超音速风洞与试验设备:为了在实验室产生稳定的超音速气流,风洞的「试验段」上游必须配备一个收缩-扩张喷管(称为风洞喷管)。其型面设计直接决定了试验段马赫数的均匀性和准确性,是空气动力学研究的基础设施。
汽车涡轮增压与喷气引擎:涡轮增压器的涡轮喷嘴和喷气式发动机的尾喷管,都利用了这一原理。通过控制喷管喉部面积和出口压力,可以调节发动机的背压和流量,从而优化在不同飞行速度或发动机转速下的性能,实现省油与高功率的平衡。
常见误解与注意事项
模型假设:本模拟器所用数学模型基于线性、均质、各向同性等简化假设。在将计算结果直接用于设计决策之前,务必确认实际系统是否满足这些假设。
单位与量纲:许多计算错误源于单位换算错误或数量级判断失误。请时刻注意各参数输入框旁标注的单位。
结果验证:始终将模拟器输出结果与物理直觉或手算结果进行核对。若结果出乎意料,请检查输入参数或采用独立方法进行验证。
进阶学习指引
深化理论:在本工具的简化模型基础上,进一步研究非线性效应、三维行为和时间依赖现象。阅读专业教材和学术论文,掌握严格的数学推导,是提升工程解题能力的关键。
数值方法:系统学习有限元法(FEM)、有限差分法(FDM)和有限体积法(FVM),理解商业CAE求解器的内部运行机制,这将显著提升您设置有效仿真的能力。
实验验证:理论和仿真结果必须通过实验数据加以验证。养成将计算结果与测量值进行对比的习惯,这正是V&V(验证与确认)的精髓所在。
CAE工具:准备好后,可进一步探索Ansys、Abaqus、OpenFOAM、COMSOL等业界主流工具。通过本模拟器培养的物理直觉,将帮助您更有效地配置和使用这些工具。