- 积分
- 9576
- 贡献
-
- 精华
- 在线时间
- 小时
- 注册时间
- 2017-6-25
- 最后登录
- 1970-1-1
|
登录后查看更多精彩内容~
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
受“甲方”委托,我写了一个计算T-N波作用通量水平分量的Python脚本。虽然之前我从来没有听说过“T-N波作用通量”这个东西,但是好在公式里每个物理量都还算眼熟,仔细捋顺了计算细节,最终成果还是受到了“甲方”的肯定。
单位
通常来说,有公式有数据,总能通过编程算出一个结果,而结果的正确性或者说程序的正确性怎么保证呢?
单位是其中一个很好判断工具。
T-N波作用通量的公式如下
其中
与水平分量有关的物理量如下表所示
符号 意义 单位
φ
纬度(弧度制)1
λ
经度(弧度制)1
a
地球半径m
p
气压与1000hPa之比1
U
气候场基本流场U分量m/s
V
气候场基本流场V分量m/s
|U|气候场基本流场m/s
Φ'
位势的纬向偏差m2/s2
f科氏参数1/s
ψ'准地转流函数相对于
气候场的扰动m2/s2由于我也并没有学过相关的内容,以上是综合几篇文献和教程总结或者反推的。比如,一些文献中没有说是气压与1000hPa之比,但是通过T-N波作用通量单位最终为m2/s2以及其他文献判断应当是如此。
位势与位势高度
位势高度的单位是gpm位势米或者m米,gpm在metpy的单位系统中与m是等价的。
位势的单位是m2/s2,数值上约是位势高度的9.8倍。
其实就是位势高度乘以重力加速度等于位势。容易记反的话看一下单位就知道了。
千万要注意你使用的数据是位势还是位势高度!
用位势高度求位势可以用metpy中的height_to_geopotential函数来实现。
偏导
numpy、xarray、metpy都可以求偏导,我其实更喜欢metpy。之前计算水汽通量、Zwack-Okossi诊断方程时都是使用metpy进行梯度(偏导)、二阶偏导、涡度和拉普拉斯等计算,非常方便,但是T-N波作用通量却并不适合用metpy,因为metpy会“自作主张”把对弧度制经纬度的偏导转换为对水平距离的偏导,所以要用xarray或numpy这种相对来说比较“手动”的方案。
numpy的gradient函数和xarray的differentiate方法本质上是一样的,从文档上来看,differentiate应该是底层调用了gradient。用过几次后觉得differentiate更方便,直接指定dim名称即可计算对应的second order accurate central differences(二阶精度中心差商)即偏导。
脚本
完整的脚本如下,过一段时间想办法封装成函数放到GitHub上。
读取数据并标记单位
- import numpy as np
- import xarray as xr
- import metpy.calc as mpcalc
- from metpy.units import units
- from metpy.constants import earth_avg_radius
- p = 250 * units('hPa') # 也有用300hPa的
- mon = 1 # 目标月
- time_target = f'2015-{mon:02d}-08' # 目标日期
- p0 = 1000 * units('hPa')
- hgt_day_path = './2015年/hgt.2015.nc'
- hgt_mon_path = './2015年/hgt.mon.mean.nc'
- uwnd_day_path = './2015年/uwnd.2015.nc'
- uwnd_mon_path = './2015年/uwnd.mon.mean.nc'
- vwnd_day_path = './2015年/vwnd.2015.nc'
- vwnd_mon_path = './2015年/vwnd.mon.mean.nc'
- hgt_day = xr.open_dataset(hgt_day_path)['hgt'] * units('m')
- hgt_mon = xr.open_dataset(hgt_mon_path)['hgt'] * units('m')
- uwnd_day = xr.open_dataset(uwnd_day_path)['uwnd'] * units('m/s')
- uwnd_mon = xr.open_dataset(uwnd_mon_path)['uwnd'] * units('m/s')
- vwnd_day = xr.open_dataset(vwnd_day_path)['vwnd'] * units('m/s')
- vwnd_mon = xr.open_dataset(vwnd_mon_path)['vwnd'] * units('m/s')
复制代码 metpy的计算函数是接受xarray的dataarray类型的,并且似乎可以自动识别其中的units属性,所以其实并不一定要手动地乘以单位,我这样写主观上觉得更“稳”,另一方面从代码上更便于理解。
- # 位势高度转位势
- Φ_day = mpcalc.height_to_geopotential(hgt_day)
- Φ_mon = mpcalc.height_to_geopotential(hgt_mon)
- # 目标日期和目标气压的位势
- Φ = Φ_day.sel(time=time_target, level=p)
- # 目标月和目标气压位势的气候态
- Φ_climatic = Φ_mon.sel(time=Φ_mon['time.month']==mon, level=p).mean(dim='time')
- # 目标月和目标气压基本流场U分量的气候态
- u_climatic = uwnd_mon.sel(time=uwnd_mon['time.month']==mon, level=p).mean(dim='time')
- # 目标月和目标气压基本流场V分量的气候态
- v_climatic = vwnd_mon.sel(time=vwnd_mon['time.month']==mon, level=p).mean(dim='time')
- # 经纬度转为弧度制
- lon_deg = Φ['lon'].copy()
- lat_deg = Φ['lat'].copy()
- lon_rad = np.deg2rad(lon_deg) * units('1')
- lat_rad = np.deg2rad(lat_deg) * units('1')
- # 科氏参数
- f = mpcalc.coriolis_parameter(Φ['lat'])
- # 目标月和目标气压基本流场的气候态
- wind_climatic = mpcalc.wind_speed(u_climatic, v_climatic)
- cosφ = np.cos(lat_rad)
- # 位势的纬向偏差
- Φ_prime = Φ - Φ_climatic.mean(dim='lon')
- # 将需要对弧度制经纬度求偏导的量的坐标都换成弧度制经纬度
- Φ_prime = Φ_prime.assign_coords({'lon': lon_rad, 'lat': lat_rad})
- f = f.assign_coords({'lat': lat_rad})
- cosφ = cosφ.assign_coords({'lat': lat_rad})
- u_climatic = u_climatic.assign_coords({'lon': lon_rad, 'lat': lat_rad})
- v_climatic = v_climatic.assign_coords({'lon': lon_rad, 'lat': lat_rad})
- wind_climatic = wind_climatic.assign_coords({'lon': lon_rad, 'lat': lat_rad})
- # 准地转流函数相对于气候场的扰动
- Ψ_prime = Φ_prime / f
- # 一顿偏导猛如虎
- dΨ_prime_dλ = Ψ_prime.differentiate('lon')
- dΨ_prime_dφ = Ψ_prime.differentiate('lat')
- ddΨ_prime_ddλ = dΨ_prime_dλ.differentiate('lon')
- ddΨ_prime_ddφ = dΨ_prime_dφ.differentiate('lat')
- ddΨ_prime_dλφ = dΨ_prime_dλ.differentiate('lat')
- # T-N波作用通量的水平分量公共部分
- temp1 = p / p0 * cosφ / (2 * wind_climatic * earth_avg_radius**2)
- temp2 = dΨ_prime_dλ * dΨ_prime_dφ - Ψ_prime * ddΨ_prime_dλφ
- # T-N波作用通量的水平分量
- fx = temp1 * (u_climatic / cosφ**2 * (dΨ_prime_dλ**2 - Ψ_prime * ddΨ_prime_ddλ) + v_climatic / cosφ * temp2)
- fy = temp1 * (u_climatic / cosφ * temp2 + v_climatic * (dΨ_prime_dφ**2 - Ψ_prime * ddΨ_prime_ddφ))
- # 把弧度制经纬度再换成角度制便于画图
- lon = lon_deg.values
- lon[lon>180] -= 360
- fx = fx.assign_coords({'lon': lon, 'lat': lat_deg}).sortby(['lon', 'lat'])
- fy = fy.assign_coords({'lon': lon, 'lat': lat_deg}).sortby(['lon', 'lat'])
复制代码 查看一下fx可以看到,计算出来的单位确实是m2/s2
简单画个图
如有错误或者疏漏,欢迎指正。
|
|