Skip to content
cfd-lab:~/zh/posts/2026-08-27-coriolis-roth…● online

本文译自作者的韩语原文。 阅读原文(한국어)

NOTE #141DAY THU 유체역학DATE 2026.08.27UPDATED 2026.10.19READ 6 min read#Rothalpy#Rotating-Frame#Coriolis#Turbomachinery#Historical

科里奥利力做的功是零,叶轮却加了1800 J/kg — 旋转坐标系里的能量与转焓

旋转坐标系中科里奥利力的功率严格为零。扬程仍然出现,是因为那个力正是叶片必须顶住的对象。

定义了"功"的人,他的力却不做功

把力学中的"功"与"动能"整理成今天形式的人是加斯帕尔·科里奥利。他1829年著作的标题是《论机器效果的计算》。那是一位工程师面对水车、清点投入与产出而写下的书。

然而以他命名的力并不做功。无论量级多大都一样。一台离心叶轮就够把这本账重记一遍。科里奥利加速度达到290 g,账本上却只写着 10−1610^{-16} J/kg,而流体的总焓上升了1800 J/kg。这1800从哪里来,以及旋转坐标系求解器漏掉对应项时会失去什么,数字自己会说。

1832年,从水车计算走向旋转坐标系#

韩国的连载《历史中的流体力学》这样记述当年的巴黎。1830年七月革命把保王派的柯西赶了出去,共和派的纳维叶被任命到巴黎综合理工学院。随后在1832年,纳维叶开始与科里奥利合作研究。

科里奥利此前一直啃的题目是水车。在旋转的流体机械里,能量与功该怎么清点?要做这笔账,人就得坐到旋转的轴上去。旋转坐标系的研究由此而来,1835年的论文让今天的科里奥利力为世人所知。

顺序很重要。科里奥利力不是从地球自转或气象学里长出来的。它是为了对上旋转流体机械的能量账本才出现的。把 frame 按钮从相对系拨到绝对系,只有橙色轨迹换形状,右侧绿线保持水平。

Flip the frame switch: the orange trail changes shape completely, yet the green rothalpy line never tilts. At the current radius 50 mm, I = 0.0 J/kg against h0 = 0.0 J/kg. Across the whole channel h0 climbs by 1800.0 J/kg — exactly U2·c_theta2 − U1·c_theta1 = 1800.0. Raise omega and only the orange line responds.

橙色轨迹跟着镜头弯,绿线不倾斜。把 omega 拉高,变陡的只有 h0h_0 那条曲线。

垂直的力上不了账

在以角速度 Ω⃗\vec{\Omega} 旋转的坐标系中,相对速度为 w⃗\vec{w} 的流体质点满足

Dw⃗Dt=−∇pρ−2Ω⃗×w⃗−Ω⃗×(Ω⃗×r⃗)\frac{D\vec{w}}{Dt} = -\frac{\nabla p}{\rho} - 2\vec{\Omega}\times\vec{w} - \vec{\Omega}\times(\vec{\Omega}\times\vec{r})

右端第二项是科里奥利项,第三项是离心力项。两者都是把坐标系转起来所付的惯性代价。这两项如何进入动量方程,在旋转参考系与MRF中讲过。这里只看能量一侧。

要看能量,就把各个力与 w⃗\vec{w} 点乘。科里奥利项当场死掉。

(−2Ω⃗×w⃗)⋅w⃗=0(-2\vec{\Omega}\times\vec{w})\cdot\vec{w} = 0

因为叉乘的结果与两个因子都垂直。这是恒等式,没有近似也没有条件。Ω\Omega 取多少、w⃗\vec{w} 朝哪个方向,都是零。

离心力不同。它等于 Ω2r r^\Omega^2 r\,\hat{r},只要有径向速度,点乘就活着。作为交换,这一项拥有势函数。

Ω2r r^=−∇(−U22),U=Ωr\Omega^2 r\,\hat{r} = -\nabla\left(-\frac{U^2}{2}\right), \qquad U = \Omega r

有势函数意味着它可以挪到方程左端,折进常数里面。

所以守恒的是 II,不是 h0h_0#

在定常、无粘条件下沿流线积分上式,得到的就是转焓(rothalpy,rotational + enthalpy)。

I=h+w22−U22=constI = h + \frac{w^2}{2} - \frac{U^2}{2} = \text{const}

hh 是静焓,ww 是相对速度大小,U=ΩrU=\Omega r 是叶片圆周速度。最后一项的负号就是离心势。科里奥利项因为点乘本来就是零,在这个式子里连痕迹都不留。

它与绝对系总焓 h0=h+c2/2h_0 = h + c^2/2 通过 c⃗=w⃗+U⃗\vec{c} = \vec{w} + \vec{U} 相连:

h0=I+Ucθh_0 = I + U c_\theta

其中 cθc_\theta 是绝对速度的旋绕分量。也就是说 II 保持常数的同时 h0h_0 可以上升,上升量恰好是 Δ(Ucθ)\Delta(U c_\theta),即欧拉透平机械方程。

用Python分别记三本账#

取一台半径从50 mm到150 mm、流道高度由20 mm收窄到8 mm的离心叶轮。Ω=300\Omega = 300 rad/s,水30 kg/s。后弯角 β\beta 看0°和30°两种。连续方程定下径向相对速度,转焓守恒定下压力。然后把科里奥利力、离心力、叶片反力所做的功分别对时间积分。

import numpy as np
 
OMEGA = 300.0            # 角速度 [rad/s]
RHO = 1000.0             # 密度 [kg/m^3]
MDOT = 30.0              # 质量流量 [kg/s]
R1, R2 = 0.05, 0.15      # 进出口半径 [m]
B1, B2 = 0.020, 0.008    # 进出口流道高度 [m]
GRID = np.linspace(R1, R2, 4001)
 
 
def channel_state(r, beta_deg):
    """半径 r 处的相对速度分量 (w_r, w_t)、叶片速度 U、绝对旋绕速度 c_t。"""
    b = B1 + (B2 - B1) * (r - R1) / (R2 - R1)
    w_r = MDOT / (RHO * 2.0 * np.pi * r * b)          # 连续方程决定的径向分量
    w_t = -w_r * np.tan(np.radians(beta_deg))         # 后弯叶片 → 与旋转反向
    u = OMEGA * r
    return w_r, w_t, u, u + w_t
 
 
def march_channel(beta_deg):
    """令转焓保持常数,沿流道建立 p/rho 与绝对总焓 h0。"""
    w_r, w_t, u, _ = channel_state(R1, beta_deg)
    i_const = 0.5 * (w_r**2 + w_t**2) - 0.5 * u**2    # 以 p1/rho = 0 为基准
    cols = []
    for r in GRID:
        w_r, w_t, u, c_t = channel_state(r, beta_deg)
        p = i_const - 0.5 * (w_r**2 + w_t**2) + 0.5 * u**2
        cols.append((u, c_t, p + 0.5 * (w_r**2 + c_t**2),
                     p + 0.5 * (w_r**2 + w_t**2) - 0.5 * u**2))
    return np.array(cols)                              # U, c_t, h0, I
 
 
def power_ledger(beta_deg):
    """沿轨迹分别对科里奥利力、离心力、叶片反力的功率作时间积分。"""
    om = np.array([0.0, 0.0, OMEGA])
    st = np.array([channel_state(r, beta_deg) for r in GRID])
    w_r, w_t = st[:, 0], st[:, 1]
    dwt_dr = np.gradient(w_t, GRID)
    p_cor = np.zeros_like(GRID)
    p_cen = np.zeros_like(GRID)
    p_bld = np.zeros_like(GRID)
    for k, r in enumerate(GRID):
        w = np.array([w_r[k], w_t[k], 0.0])            # 局部 (r, theta, z) 正交归一
        f_cor = -2.0 * np.cross(om, w)
        f_cen = -np.cross(om, np.cross(om, np.array([r, 0.0, 0.0])))
        acc_t = w_r[k] * dwt_dr[k] + w_r[k] * w_t[k] / r   # 含柱坐标曲率项
        f_bld_t = acc_t - f_cor[1]                     # 剩下的旋绕加速归叶片压力面
        p_cor[k] = f_cor @ w
        p_cen[k] = f_cen @ w
        p_bld[k] = OMEGA * r * f_bld_t                 # 力矩 × 角速度 = 绝对系功率
    dt = 1.0 / w_r                                     # dt = dr / w_r
    ints = [np.trapezoid(p * dt, GRID) for p in (p_cor, p_cen, p_bld)]
    return ints + [np.max(np.abs(p_cor))]
 
 
def transported_h0(beta_deg, with_source):
    """直接积分 h0 的输运方程。源项为 Omega * d(r c_theta)/dr。"""
    st = np.array([channel_state(r, beta_deg) for r in GRID])
    src = OMEGA * np.gradient(GRID * st[:, 3], GRID) if with_source else np.zeros_like(GRID)
    return np.trapezoid(src, GRID)
 
 
for beta in (0.0, 30.0):
    tab = march_channel(beta)
    d_h0 = tab[-1, 2] - tab[0, 2]
    euler = tab[-1, 0] * tab[-1, 1] - tab[0, 0] * tab[0, 1]
    drift = np.max(np.abs(tab[:, 3] - tab[0, 3]))
    cor, cen, bld, cor_peak = power_ledger(beta)
    ok = transported_h0(beta, True)
    bad = transported_h0(beta, False)
    print(f"beta = {beta:4.1f} deg   U1 = {tab[0,0]:4.1f} m/s   U2 = {tab[-1,0]:4.1f} m/s")
    print(f"  delta h0 from rothalpy = {d_h0:9.2f} J/kg")
    print(f"  U*c_theta (Euler)      = {euler:9.2f} J/kg   gap {abs(d_h0-euler):.1e}")
    print(f"  rothalpy max drift     = {drift:9.1e} J/kg")
    print(f"  work by Coriolis       = {cor:9.1e} J/kg   (peak power {cor_peak:.1e} W/kg)")
    print(f"  work by centrifugal    = {cen:9.2f} J/kg")
    print(f"  work by blade torque   = {bld:9.2f} J/kg")
    print(f"  h0 transport, source   = {ok:9.2f} J/kg   head {ok/9.81:5.1f} m")
    print(f"  h0 transport, dropped  = {bad:9.2f} J/kg   head {bad/9.81:5.1f} m")
beta =  0.0 deg   U1 = 15.0 m/s   U2 = 45.0 m/s
  delta h0 from rothalpy =   1800.00 J/kg
  U*c_theta (Euler)      =   1800.00 J/kg   gap 0.0e+00
  rothalpy max drift     =   1.1e-13 J/kg
  work by Coriolis       =   0.0e+00 J/kg   (peak power 0.0e+00 W/kg)
  work by centrifugal    =    900.00 J/kg
  work by blade torque   =   1800.00 J/kg
  h0 transport, source   =   1800.00 J/kg   head 183.5 m
  h0 transport, dropped  =      0.00 J/kg   head   0.0 m
beta = 30.0 deg   U1 = 15.0 m/s   U2 = 45.0 m/s
  delta h0 from rothalpy =   1737.98 J/kg
  U*c_theta (Euler)      =   1737.98 J/kg   gap 0.0e+00
  rothalpy max drift     =   7.1e-14 J/kg
  work by Coriolis       =   2.2e-16 J/kg   (peak power 1.4e-12 W/kg)
  work by centrifugal    =    900.00 J/kg
  work by blade torque   =   1737.98 J/kg
  h0 transport, source   =   1737.98 J/kg   head 177.2 m
  h0 transport, dropped  =      0.00 J/kg   head   0.0 m

把 backsweep 滑块从0°拖到30°,只有紫色叶片功从1800.00降到1737.98 J/kg。

The orange Coriolis arrow is the longest one on screen — up to 292 g — and its account still reads 0.0e+0 J/kg. Meanwhile the blade account fills to 0 of 1800 J/kg. Drag backsweep and only the purple bar moves: the perpendicular force never pays, it only decides where the blade has to push.

橙色科里奥利箭头是画面上最长的,账本却贴在零上。把 omega 调高,那根柱条依旧不长。

转焓的漂移是 10−1310^{-13} J/kg,也就是双精度舍入的量级。科里奥利这本账在后弯角为0°时正好是 0.0,在30°时是 2.2×10−162.2\times10^{-16}。后者是浮点残渣,不是物理。

900与1800 — 另一半是谁付的#

有两个数字很显眼。离心力做的功是900 J/kg,可总焓上升了1800 J/kg。正好两倍。

∫Ω2r wr dt=∫r1r2Ω2r dr=U22−U122\int \Omega^2 r\,w_r\,dt = \int_{r_1}^{r_2} \Omega^2 r\,dr = \frac{U_2^2 - U_1^2}{2}

这900是只在相对系内部流转的钱,分头进入压力与相对动能。它和绝对系中流体真正收到的1800是两个科目。

入口圆周速度是15.0 m/s,出口是45.0 m/s。代入 (U22−U12)/2(U_2^2 - U_1^2)/2,45的平方2025减去15的平方225,再除以2,得到900。输出里的离心功900.00 J/kg与这个式子逐位相符。后弯角0°时叶片扭矩功是1800.00,正好是离心功的两倍。绝对系功率是扭矩乘 Ω\Omega,相对系账上的900被计了两次。后弯到30°,叶片功降到1737.98 J/kg,欧拉项 UcθU c_\theta 也是1737.98,gap为0.0e+00。离心功仍是900.00。减少的只有旋绕。wtw_t 与旋转反向,出口 cθc_\theta 小于 UU,Δ(Ucθ)\Delta(U c_\theta) 落到1800以下。转焓漂移在0°是1.1e-13 J/kg,在30°是7.1e-14 J/kg。这是 II 作为常数留在双精度里的记录,不是新的损失。扬程183.5 m是1800.00除以9.81,177.2 m是1737.98除以同一个9.81。关掉源项后,后弯角是多少,扬程都是0.0 m。30°的科里奥利残差2.2e-16 J/kg,撼动不了这个0.0 m。开头的290 g是加速度的大小,不是功率。(−2Ω⃗×w⃗)⋅w⃗=0(-2\vec{\Omega}\times\vec{w})\cdot\vec{w} = 0 在 Ω=300\Omega = 300 rad/s时仍是恒等式,加速度达到重力的290倍也一样。质量流量30 kg/s流过从20 mm收到8 mm的流道,只是把 wrw_r 加大。箭头变长,账上的功仍是0.0e+00。半径50 mm处 U=Ωr=15.0U = \Omega r = 15.0 m/s,150 mm处是45.0 m/s。离心势差由这两个半径收口。

剩下的900由叶片支付。在径向叶片上,相对速度没有旋绕分量。要保持这一点,就必须有东西精确抵消科里奥利力 2Ωwr2\Omega w_r,那个东西就是叶片的压力面。作为反作用,流体受到同样大小的力,其力矩为 2Ωrwr2\Omega r w_r。

分岔就在这里。在相对系中,那个力垂直于 w⃗\vec{w},所以不做功。在绝对系中,叶片以 Ω\Omega 旋转,力矩乘角速度就是功率。积分得到 2×900=18002\times 900 = 1800 J/kg,也就是输出中 work by blade torque 那一行。

科里奥利力一分钱也不给流体。它只是让流体去推叶片,轴功沿着那条反作用路径进来。力只决定方向,付账的是轴。

当旋转坐标系求解器照搬输运 h0h_0#

以上是物理。代码里出事的位置是固定的。

问题在于旋转网格区域内的能量方程用什么当输运变量。被相对速度携带、且无源守恒的是 II。若要用相对速度携带 h0h_0,就必须带上源项。

w⃗⋅∇h0=Ω ∂(rcθ)∂s\vec{w}\cdot\nabla h_0 = \Omega\,\frac{\partial (r c_\theta)}{\partial s}

ss 是沿流线的坐标。漏掉这一项就得到输出的最后两行。有源项时1800 J/kg,扬程183.5 m;没有时0.00 J/kg,扬程0 m。流体穿过了叶轮,却什么都没发生。

症状之所以迷惑人,是因为残差看起来很正常。方程本身收敛得很干净,只是收敛到扬程为零的那个解。加密网格也没用,零还是零。

在OpenFOAM系求解器中,把MRF与可压缩能量方程一起用时,如果 rhothermo 一侧的总能量定义与MRF修正对不上,就是这个样子。坐标变换里漏掉一项,账就偏了。上面代码在 acc_t 一行加入曲率项 wrwθ/rw_r w_\theta / r 也是同样的理由。把它去掉,30°后弯角下叶片账目就会偏到1802.06。沿流线保持常数的是 II,h0=I+Ucθh_0 = I + U c_\theta 不是。没有旋转时总压常数在流线之间破裂的记录,写在伯努利常数穿过流线里。

扬程印成0.0 m,就是 h0h_0 丢了源项#

旋转区域里保持常数的是 II。跨进静止区域又变回 h0h_0。用相对速度携带 h0h_0,必须带上 Ω ∂(rcθ)/∂s\Omega\,\partial(rc_\theta)/\partial s。输出里关掉源项,1800.00 J/kg变成0.00,扬程也从183.5 m贴到0.0 m。残差看起来很干净。它只是收敛到了零。加密网格,零还是零。

把科里奥利项当成能量源加进去,修的是错的地方。那一项的功率印出来是0.0e+00,30°时仍是 2.2×10−162.2\times10^{-16} J/kg。该放进去的是旋绕源,不是科里奥利。他是在清点水车效率时走到旋转坐标系的。在他造的账里,冠他名字的力永远是零。190年之后把那个零留在零上,轴功才还是1800 J/kg。

如果对您有帮助,请分享。