近期在计算一个探空数据的cape值时发现是0,经过一番测试整理成以下教程,希望你有所收获。
内容:通过一个具体实例,深入解析为什么对流有效位能(CAPE)和对流抑制能量(CIN)会计算为0,帮助气象学初学者理解大气稳定性的基本概念。
今天我们要学习:
首先导入必要的Python库:
# 导入必要的库
import numpy as np
import metpy.calc as mcalc
from metpy.units import units
import matplotlib.pyplot as plt
✅ 库导入完成!
# 使用之前的数据(简化版)
data = {
'p_hPa': np.array([1013.1, 1012.9, 1001.7, 1000, 987.9, 957, 949.7, 929.2, 925, 919.3, 910.5, 900, 895.6, 889.1, 884.5,
863.4, 851.7, 850, 828.7, 825.7, 824.2, 819.2, 800.7, 793.9, 788.6, 787, 756.8, 733.7, 727, 724.5,
721.1, 718.6, 711, 703.8, 700, 698.4, 687.8, 670.4, 666.2, 659, 645.4, 643.6, 643.1, 640.2, 637.3,
616.7, 605.3, 600, 592.4, 583.4, 568.5, 545.5, 544.7, 535.4, 522, 517.5, 514.4, 505.6, 500, 499.7,
477.6, 476.1, 469.8, 456.7, 435.9, 425.9, 420.9, 413.5, 405.7, 400, 396.1, 387.6, 368.4, 366.7, 353,
334.5, 318.3, 312.5, 307.4, 300, 289.7, 279.1, 263.3, 253.1, 250.6, 250, 238.8, 231.8, 226.8, 219.5,
218.3, 215.9, 213.6, 204.3, 200, 193.9, 192.5, 187.1, 182.8, 175.1, 171.4, 163.4, 159.5, 150, 149.5,
146.8, 145.6, 143.9, 137.4, 130.3, 129.6, 123.8, 121.9, 121.1, 114, 113.4, 109.3, 106.3, 102.3, 100,
99.5, 98.5, 97.1, 94.4, 93.1, 92.2, 88.2, 86.5, 80.9, 79.3, 75.6, 73.9, 72.1, 70.4, 70, 69.9, 68.7,
64.7, 63.1, 60.4, 56.4, 55.8, 53.8, 52.6, 50, 49.3, 47.8, 46.2, 44.7, 43.3, 42.5, 40.5, 40, 38.6, 37.9,
37.8, 35.6, 35.4, 34.5, 33.1, 31.2, 31, 30.5, 30, 29.1, 29, 27.8, 27, 26.7, 25.2, 25.1, 24.4, 23.5,
22.5, 21.8, 21.5, 20.7]),
'T_degC': np.array([14.7, 15.31, 14.51, 14.67, 14.67, 13.67, 13.23, 13.24, 12.94, 12.69, 12.16, 11.46, 11.59, 11.6, 11.29,
10.04, 9.09, 8.94, 7.08, 8.75, 8.97, 8.71, 7.87, 8.07, 8.54, 8.43, 5.47, 3.37, 3.4, 3.45, 4.01, 4.27,
5.47, 5.72, 5.96, 6.01, 5.7, 4.19, 3.78, 3.77, 2.44, 2.57, 2.74, 4.71, 5.26, 3.73, 2.68, 2.05, 1.15,
0, -1.5, -4.03, -4.07, -4.46, -5.88, -6.29, -5.42, -5.33, -5.78, -5.8, -8.31, -8.49, -8.14, -10.01,
-12.35, -13.77, -14.54, -14.81, -15.82, -16.23, -16.72, -16.52, -19.16, -19.37, -20.5, -23.9, -27.02,
-28.28, -28.87, -30.44, -32.34, -34.65, -37.49, -39.22, -39.75, -39.89, -42.46, -44.14, -44.99, -46.44,
-46.03, -46.14, -46.16, -48.84, -50.19, -52.11, -52.6, -53.46, -54.77, -57.31, -58.55, -61.39, -61.78,
-65.05, -65.26, -66.18, -66.18, -66.3, -68.6, -71.29, -71.25, -71.97, -71.72, -71.65, -74.59, -74.9,
-75.55, -76.74, -78.15, -78.42, -78.43, -78.52, -78.13, -78.77, -78.43, -78.74, -80.26, -80.64, -82.19,
-82.49, -81.36, -80.98, -81.57, -78.98, -77.75, -77.51, -75.07, -75.67, -76.45, -74.98, -71.62, -71.29,
-68.09, -67.74, -67.25, -66.81, -66.6, -65.15, -64.38, -64.28, -64.19, -62.42, -62.28, -61.11, -59.64,
-59.61, -60.24, -60.04, -58.6, -59.47, -59.66, -59.27, -58.41, -58.08, -57.75, -57.87, -58.94, -57.72,
-56.88, -55.48, -55.26, -55.56, -54.15, -52.6, -53.4, -53.89, -53.67]),
'Td_degC': np.array([8.17, 8.38, 7.76, 7.93, 7.68, 7.29, 7.05, 8.02, 8.09, 8.72, 9.01, 8.19, 7.89, 9.71, 9.6, 7.21, 6.44,
6.4, 5.78, 5.95, 5.65, 4.61, 2.91, 2.14, -0.21, -0.46, -2.36, -3.2, -3.65, -3.51, -5.94, -6.1, -8.81,
-9.41, -13.02, -15.06, -16.2, -17.1, -17.65, -27.54, -31.88, -33.22, -34.86, -48.57, -48.24, -49.17,
-49.82, -49.1, -50.76, -50.39, -52.4, -51.91, -52.68, -54.24, -55.13, -51.57, -54.67, -54.79, -54.04,
-55.08, -56.66, -56.78, -56.55, -57.74, -59.22, -60.13, -60.62, -60.79, -61.44, -60.73, -62.02, -61.89,
-63.59, -63.73, -64.46, -66.67, -65.94, -64.21, -69.16, -70.03, -72.23, -73.78, -75.68, -76.85, -77.2,
-76.34, -79.04, -80.18, -80.77, -81.76, -81.48, -81.55, -81.57, -83.41, -83.34, -85.67, -86.01, -86.6,
-87.52, -89.29, -90.16, -92.16, -92.44, -93.67, -94.91, -95.57, -95.57, -95.65, -97.3, -99.23, -99.2,
-99.72, -99.54, -99.49, -101.62, -101.84, -102.31, -103.18, -104.21, -103.2, -104.41, -104.48, -104.19,
-104.66, -104.41, -104.64, -105.75, -106.03, -107.17, -107.39, -106.56, -106.28, -106.72, -104.82, -102.72,
-103.74, -101.97, -102.4, -102.97, -101.9, -99.47, -99.23, -96.93, -96.68, -95.22, -96.02, -95.87, -94.83,
-94.28, -94.21, -94.15, -92.89, -91.72, -91.97, -90.93, -90.91, -91.35, -91.21, -90.2, -90.81, -90.94,
-90.67, -90.06, -88.79, -89.6, -89.69, -90.44, -89.58, -88.99, -88.01, -87.86, -88.07, -87.08, -86.01,
-86.56, -86.9, -86.75]),
}
print("📈 数据基本信息:")
print(f"• 数据点数:{len(data['p_hPa'])} 层")
print(f"• 气压范围:{data['p_hPa'][0]:.1f} - {data['p_hPa'][-1]:.1f} hPa")
print(f"• 地面温度:{data['T_degC'][0]:.1f}°C")
print(f"• 地面露点:{data['Td_degC'][0]:.1f}°C")
print(f"• 温度露点差:{data['T_degC'][0] - data['Td_degC'][0]:.1f}°C")
# 计算相对湿度
rh = mcalc.relative_humidity_from_dewpoint(
data['T_degC'][0] * units.degC,
data['Td_degC'][0] * units.degC
)
print(f"• 地面相对湿度:{rh.magnitude*100:.1f}%")
📈 数据基本信息:
🔍 关键观察:
# 准备数据
p = data['p_hPa'] * units.hPa
T = data['T_degC'] * units.degC
Td = data['Td_degC'] * units.degC
# 计算气块抬升轨迹
profile = mcalc.parcel_profile(p, T[0], Td[0]).to('degC')
# 创建图表
fig, axes = plt.subplots(1, 3, figsize=(18, 8))
# 图1:环境温度垂直分布
ax1 = axes[0]
ax1.plot(T.magnitude, p.magnitude, 'b-', linewidth=3, label='环境温度')
ax1.set_xlabel('温度 (°C)', fontsize=12)
ax1.set_ylabel('气压 (hPa)', fontsize=12)
ax1.set_title('🌡️ 环境温度垂直分布', fontsize=14, fontweight='bold')
ax1.invert_yaxis() # 气压从上往下递增
ax1.grid(True, alpha=0.3)
ax1.legend(fontsize=11)
# 添加标注
ax1.annotate('地面\n1013 hPa\n14.7°C',
xy=(T[0].magnitude, p[0].magnitude),
xytext=(20, 50),
textcoords='offset points',
arrowprops=dict(arrowstyle='->', color='red'),
fontsize=10,
bbox=dict(boxstyle="round,pad=0.3", facecolor="yellow", alpha=0.5))
# 图2:气块抬升轨迹
ax2 = axes[1]
ax2.plot(profile.magnitude, p.magnitude, 'r-', linewidth=3, label='气块温度')
ax2.set_xlabel('温度 (°C)', fontsize=12)
ax2.set_ylabel('气压 (hPa)', fontsize=12)
ax2.set_title('🚀 气块抬升轨迹', fontsize=14, fontweight='bold')
ax2.invert_yaxis()
ax2.grid(True, alpha=0.3)
ax2.legend(fontsize=11)
# 图3:两者对比
ax3 = axes[2]
ax3.plot(T.magnitude, p.magnitude, 'b-', linewidth=3, label='环境温度', alpha=0.7)
ax3.plot(profile.magnitude, p.magnitude, 'r-', linewidth=3, label='气块温度', alpha=0.7)
ax3.set_xlabel('温度 (°C)', fontsize=12)
ax3.set_ylabel('气压 (hPa)', fontsize=12)
ax3.set_title('⚖️ 环境 vs 气块温度对比', fontsize=14, fontweight='bold')
ax3.invert_yaxis()
ax3.grid(True, alpha=0.3)
ax3.legend(fontsize=11)
# 在对比图上标注关键区域
# 找出温度差最小的区域
temp_diff = profile.magnitude - T.magnitude
closest_idx = np.argmin(np.abs(temp_diff))
ax3.annotate('最接近的点\n但气块仍然更冷',
xy=(profile[closest_idx].magnitude, p[closest_idx].magnitude),
xytext=(-80, 20),
textcoords='offset points',
arrowprops=dict(arrowstyle='->', color='green'),
fontsize=10,
bbox=dict(boxstyle="round,pad=0.3", facecolor="lightgreen", alpha=0.7))
plt.tight_layout()
plt.show()

图:温度垂直分布图
🔍 观察图表:
# 计算温度差
temp_diff = profile.magnitude - T.magnitude
print("📊 温度差统计:")
print(f"• 最小温度差:{min(temp_diff):.2f}°C")
print(f"• 最大温度差:{max(temp_diff):.2f}°C")
print(f"• 平均温度差:{np.mean(temp_diff):.2f}°C")
print(f"• 正温度差数量:{np.sum(temp_diff > 0)} 个")
print(f"• 负温度差数量:{np.sum(temp_diff < 0)} 个")
print(f"• 零温度差数量:{np.sum(temp_diff == 0)} 个")
if np.sum(temp_diff > 0) == 0:
print("\n❌ 没有正温度差!")
print(" → 气块从未比环境暖")
print(" → 没有正浮力区域")
print(" → 因此 CAPE = 0")
📊 温度差统计:
❌ 没有正温度差!
# 创建温度差图表
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 8))
# 图1:温度差垂直分布
ax1.plot(temp_diff, p.magnitude, 'g-', linewidth=3, label='温度差(气块-环境)')
ax1.axvline(x=0, color='k', linestyle='--', linewidth=2, alpha=0.5, label='零线')
ax1.set_xlabel('温度差 (°C)', fontsize=12)
ax1.set_ylabel('气压 (hPa)', fontsize=12)
ax1.set_title('📊 气块与环境温度差', fontsize=14, fontweight='bold')
ax1.invert_yaxis()
ax1.grid(True, alpha=0.3)
ax1.legend(fontsize=11)
# 填充负温度差区域(CIN区域)
ax1.fill_betweenx(p.magnitude, 0, temp_diff, where=(temp_diff < 0),
color='red', alpha=0.3, label='负浮力区域')
# 标注
ax1.annotate('全部为负值!\n没有正浮力',
xy=(min(temp_diff)/2, 500),
xytext=(-100, 0),
textcoords='offset points',
arrowprops=dict(arrowstyle='->', color='red'),
fontsize=12,
bbox=dict(boxstyle="round,pad=0.3", facecolor="red", alpha=0.2))
# 图2:温度差直方图
ax2.hist(temp_diff, bins=30, color='skyblue', edgecolor='black', alpha=0.7)
ax2.axvline(x=0, color='red', linestyle='--', linewidth=2, label='零线')
ax2.set_xlabel('温度差 (°C)', fontsize=12)
ax2.set_ylabel('频数', fontsize=12)
ax2.set_title('📈 温度差分布直方图', fontsize=14, fontweight='bold')
ax2.grid(True, alpha=0.3)
ax2.legend(fontsize=11)
# 标注直方图
ax2.annotate('全部在零线左侧!\n全是负值',
xy=(min(temp_diff)/2, 5),
xytext=(50, 30),
textcoords='offset points',
arrowprops=dict(arrowstyle='->', color='blue'),
fontsize=12,
bbox=dict(boxstyle="round,pad=0.3", facecolor="lightblue", alpha=0.5))
plt.tight_layout()
plt.show()

图:温度差分析
🎯 核心结论: 温度差全部 ≤ 0 → 气块始终不比环境暖 → 没有正浮力 → CAPE = 0
# 计算CAPE和CIN
cape, cin = mcalc.cape_cin(p, T, Td, profile)
📊 计算结果:
🔍 结果解读:
🎯 重要概念:LFC和EL
# 尝试获取LFC和EL信息
try:
result = mcalc.cape_cin(p, T, Td, profile, which_lfc='bottom', which_el='top')
if len(result) > 2:
print(f"LFC压力: {result[2]}")
print(f"EL压力: {result[3]}")
print("👆 看到吗?都是NaN(不存在)")
except Exception as e:
print(f"错误信息: {e}")
print("这证实了LFC和EL不存在!")
🔧 尝试获取LFC和EL信息:
📝 简单总结: 没有正浮力 → 没有CAPE → 没有LFC和EL → 大气稳定
让我们创建一个"理想"的对流案例:
# 创建一个有对流的示例数据
ideal_p = np.array([1000, 850, 700, 500, 400, 300]) * units.hPa
ideal_T = np.array([30, 20, 10, -10, -25, -50]) * units.degC # 环境温度
ideal_Td = np.array([25, 18, 8, -15, -30, -55]) * units.degC # 露点温度
# 计算气块轨迹
ideal_profile = mcalc.parcel_profile(ideal_p, ideal_T[0], ideal_Td[0]).to('degC')
# 计算CAPE和CIN
ideal_cape, ideal_cin = mcalc.cape_cin(ideal_p, ideal_T, ideal_Td, ideal_profile)
📊 理想对流案例数据:
📈 计算结果:
🔍 对比分析:
指标 | 我们的案例 | 理想案例 |
|---|---|---|
地面温度 | 14.7°C | 30.0°C |
地面露点 | 8.2°C | 25.0°C |
CAPE | 0.0 J/kg | 1878.5 J/kg |
CIN | 0.0 J/kg | -15.3 J/kg |
🎯 关键区别:
# 可视化对比
fig, axes = plt.subplots(1, 2, figsize=(16, 8))
# 我们的案例
ax1 = axes[0]
ax1.plot(T.magnitude, p.magnitude, 'b-', label='环境温度', alpha=0.7)
ax1.plot(profile.magnitude, p.magnitude, 'r-', label='气块温度', alpha=0.7)
ax1.set_xlabel('温度 (°C)')
ax1.set_ylabel('气压 (hPa)')
ax1.set_title('❄️ 我们的案例:无CAPE', fontsize=14, fontweight='bold')
ax1.invert_yaxis()
ax1.grid(True, alpha=0.3)
ax1.legend()
ax1.annotate('气块始终更冷\nCAPE=0',
xy=(10, 500),
xytext=(50, 0),
textcoords='offset points',
fontsize=12,
bbox=dict(boxstyle="round,pad=0.3", facecolor="red", alpha=0.2))
# 理想案例
ax2 = axes[1]
ax2.plot(ideal_T.magnitude, ideal_p.magnitude, 'b-', label='环境温度', alpha=0.7)
ax2.plot(ideal_profile.magnitude, ideal_p.magnitude, 'r-', label='气块温度', alpha=0.7)
ax2.set_xlabel('温度 (°C)')
ax2.set_ylabel('气压 (hPa)')
ax2.set_title('🔥 理想案例:有CAPE', fontsize=14, fontweight='bold')
ax2.invert_yaxis()
ax2.grid(True, alpha=0.3)
ax2.legend()
# 标记正浮力区域
ideal_temp_diff = ideal_profile.magnitude - ideal_T.magnitude
positive_mask = ideal_temp_diff > 0
if np.any(positive_mask):
ax2.fill_betweenx(ideal_p.magnitude, ideal_T.magnitude, ideal_profile.magnitude,
where=positive_mask, color='green', alpha=0.3, label='正浮力区域')
ax2.annotate('气块比环境暖!\n这就是CAPE区域',
xy=(ideal_profile.magnitude[2], ideal_p.magnitude[2]),
xytext=(-80, 0),
textcoords='offset points',
arrowprops=dict(arrowstyle='->', color='green'),
fontsize=12,
bbox=dict(boxstyle="round,pad=0.3", facecolor="green", alpha=0.2))
plt.tight_layout()
plt.show()

图:案例对比
💡 学习要点: 要产生CAPE,需要:
✅ 技术原因:气压数据已修复(单调递减) ✅ 计算过程:正确无误 ✅ metpy库:功能正常 ❌ 根本原因:没有正浮力区域
需要同时满足:
恭喜!您现在理解了为什么这个实例中CAPE和CIN为0
👨🏫 记住:CAPE=0不一定是计算错误,可能是大气确实稳定
🔬 气象学就是通过数据理解大气行为!