爱气象,爱气象家园! 

气象家园

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博登陆

只需一步, 快速开始

搜索
查看: 27245|回复: 5

[源代码] Python3.T-N波作用通量水平分量

[复制链接]

新浪微博达人勋

发表于 2021-5-17 09:22:36 | 显示全部楼层 |阅读模式

登录后查看更多精彩内容~

您需要 登录 才可以下载或查看,没有帐号?立即注册 新浪微博登陆

x
受“甲方”委托,我写了一个计算T-N波作用通量水平分量的Python脚本。虽然之前我从来没有听说过“T-N波作用通量”这个东西,但是好在公式里每个物理量都还算眼熟,仔细捋顺了计算细节,最终成果还是受到了“甲方”的肯定。

单位

通常来说,有公式有数据,总能通过编程算出一个结果,而结果的正确性或者说程序的正确性怎么保证呢?

单位是其中一个很好判断工具。

T-N波作用通量的公式如下

公式.png
其中
psi_prime.png
与水平分量有关的物理量如下表所示
符号
意义
单位

φ
纬度(弧度制)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上。

读取数据并标记单位
  1. import numpy as np
  2. import xarray as xr
  3. import metpy.calc as mpcalc
  4. from metpy.units import units
  5. from metpy.constants import earth_avg_radius


  6. p = 250 * units('hPa')  # 也有用300hPa的
  7. mon = 1  # 目标月
  8. time_target = f'2015-{mon:02d}-08'  # 目标日期
  9. p0 = 1000 * units('hPa')

  10. hgt_day_path = './2015年/hgt.2015.nc'
  11. hgt_mon_path = './2015年/hgt.mon.mean.nc'
  12. uwnd_day_path = './2015年/uwnd.2015.nc'
  13. uwnd_mon_path = './2015年/uwnd.mon.mean.nc'
  14. vwnd_day_path = './2015年/vwnd.2015.nc'
  15. vwnd_mon_path = './2015年/vwnd.mon.mean.nc'
  16. hgt_day = xr.open_dataset(hgt_day_path)['hgt'] * units('m')
  17. hgt_mon = xr.open_dataset(hgt_mon_path)['hgt'] * units('m')
  18. uwnd_day = xr.open_dataset(uwnd_day_path)['uwnd'] * units('m/s')
  19. uwnd_mon = xr.open_dataset(uwnd_mon_path)['uwnd'] * units('m/s')
  20. vwnd_day = xr.open_dataset(vwnd_day_path)['vwnd'] * units('m/s')
  21. vwnd_mon = xr.open_dataset(vwnd_mon_path)['vwnd'] * units('m/s')
复制代码
metpy的计算函数是接受xarray的dataarray类型的,并且似乎可以自动识别其中的units属性,所以其实并不一定要手动地乘以单位,我这样写主观上觉得更“稳”,另一方面从代码上更便于理解。
  1. # 位势高度转位势
  2. Φ_day = mpcalc.height_to_geopotential(hgt_day)
  3. Φ_mon = mpcalc.height_to_geopotential(hgt_mon)
  4. # 目标日期和目标气压的位势
  5. Φ = Φ_day.sel(time=time_target, level=p)
  6. # 目标月和目标气压位势的气候态
  7. Φ_climatic = Φ_mon.sel(time=Φ_mon['time.month']==mon, level=p).mean(dim='time')
  8. # 目标月和目标气压基本流场U分量的气候态
  9. u_climatic = uwnd_mon.sel(time=uwnd_mon['time.month']==mon, level=p).mean(dim='time')
  10. # 目标月和目标气压基本流场V分量的气候态
  11. v_climatic = vwnd_mon.sel(time=vwnd_mon['time.month']==mon, level=p).mean(dim='time')

  12. # 经纬度转为弧度制
  13. lon_deg = Φ['lon'].copy()
  14. lat_deg = Φ['lat'].copy()
  15. lon_rad = np.deg2rad(lon_deg) * units('1')
  16. lat_rad = np.deg2rad(lat_deg) * units('1')

  17. # 科氏参数
  18. f = mpcalc.coriolis_parameter(Φ['lat'])
  19. # 目标月和目标气压基本流场的气候态
  20. wind_climatic = mpcalc.wind_speed(u_climatic, v_climatic)

  21. cosφ = np.cos(lat_rad)

  22. # 位势的纬向偏差
  23. Φ_prime = Φ - Φ_climatic.mean(dim='lon')
  24. # 将需要对弧度制经纬度求偏导的量的坐标都换成弧度制经纬度
  25. Φ_prime = Φ_prime.assign_coords({'lon': lon_rad, 'lat': lat_rad})
  26. f = f.assign_coords({'lat': lat_rad})
  27. cosφ = cosφ.assign_coords({'lat': lat_rad})
  28. u_climatic = u_climatic.assign_coords({'lon': lon_rad, 'lat': lat_rad})
  29. v_climatic = v_climatic.assign_coords({'lon': lon_rad, 'lat': lat_rad})
  30. wind_climatic = wind_climatic.assign_coords({'lon': lon_rad, 'lat': lat_rad})
  31. # 准地转流函数相对于气候场的扰动
  32. Ψ_prime = Φ_prime / f

  33. # 一顿偏导猛如虎
  34. dΨ_prime_dλ = Ψ_prime.differentiate('lon')
  35. dΨ_prime_dφ = Ψ_prime.differentiate('lat')
  36. ddΨ_prime_ddλ = dΨ_prime_dλ.differentiate('lon')
  37. ddΨ_prime_ddφ = dΨ_prime_dφ.differentiate('lat')
  38. ddΨ_prime_dλφ = dΨ_prime_dλ.differentiate('lat')

  39. # T-N波作用通量的水平分量公共部分
  40. temp1 = p / p0 * cosφ / (2 * wind_climatic * earth_avg_radius**2)
  41. temp2 = dΨ_prime_dλ * dΨ_prime_dφ - Ψ_prime * ddΨ_prime_dλφ

  42. # T-N波作用通量的水平分量
  43. fx = temp1 * (u_climatic / cosφ**2 * (dΨ_prime_dλ**2 - Ψ_prime * ddΨ_prime_ddλ) + v_climatic / cosφ * temp2)
  44. fy = temp1 * (u_climatic / cosφ * temp2 + v_climatic * (dΨ_prime_dφ**2 - Ψ_prime * ddΨ_prime_ddφ))

  45. # 把弧度制经纬度再换成角度制便于画图
  46. lon = lon_deg.values
  47. lon[lon>180] -= 360
  48. fx = fx.assign_coords({'lon': lon, 'lat': lat_deg}).sortby(['lon', 'lat'])
  49. fy = fy.assign_coords({'lon': lon, 'lat': lat_deg}).sortby(['lon', 'lat'])
复制代码
查看一下fx可以看到,计算出来的单位确实是m2/s2
fx.png
简单画个图
fig.png


如有错误或者疏漏,欢迎指正。



                               
登录/注册后可看大图







密码修改失败请联系微信:mofangbao

新浪微博达人勋

发表于 2021-5-19 00:11:55 来自手机 | 显示全部楼层
清清楚楚干净利落 像你学习
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

新浪微博达人勋

发表于 2021-5-23 14:20:09 | 显示全部楼层
很好的教程,给你一个大赞
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

新浪微博达人勋

发表于 2021-5-29 16:13:52 | 显示全部楼层
这个好像不是原创吧?
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

新浪微博达人勋

 楼主| 发表于 2021-5-30 10:26:46 | 显示全部楼层
little卓忘密码 发表于 2021-5-29 16:13
这个好像不是原创吧?

保证原创
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

新浪微博达人勋

发表于 2021-6-13 22:25:21 来自手机 | 显示全部楼层
不错不错,受教了
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册 新浪微博登陆

本版积分规则

Copyright ©2011-2014 bbs.06climate.com All Rights Reserved.  Powered by Discuz! (京ICP-10201084)

本站信息均由会员发表,不代表气象家园立场,禁止在本站发表与国家法律相抵触言论

快速回复 返回顶部 返回列表