气象编程 | 读取气象BUFR、PREPBUFR数据

学术   2024-11-28 16:20   湖南  

一、BUFR数据格式简介:

BUFR是气象数据的二进制通用表示格式(Binary Universal Form for Representation of meteorological data)。BUFR 最早于1988 年开始应用于描述卫星、航空器、风洞和热带气旋的观测数据。2002 年,WMO(世界气象组织)发布了FM94 BUFR 数据格式指导文档,对BUFR 数据的格式、编码/解码方法做了详细的描述。此后,大量的气象数据采用BUFR 格式来描述、存储和交换。BUFR 是WMO 用于描述和交换观测数据的唯一推荐编码格式,它具有以下几个显著优势:

  1. 自描述性:BUFR格式的数据本身携带元数据信息,使得数据接收方能够通过数据内部自带的元数据信息获取该数据包含的内容、属性等。

  2. 表格驱动特性:BUFR格式数据的编解码所需的大量信息都在规范表格中定义,只要编码方和解码方维护同一套表格,即可实现各类数据的统一编码和解码。

  3. 可扩展性:BUFR格式通过维护统一的码表,并基于该码表制定不同的模板,即可扩展表示多种数据类型。

  4. 可压缩性:BUFR格式本身采用二进制、以比特为单位表示数据,在数据容量方面较ASCII码数据具有明显的优势。除此之外,BUFR格式支持通过压缩方式表示多个数据子集,更大程度上缩小数据容量,尤其适用于卫星、风廓线等大数据量的资料。

  5. 平台无关性:BUFR格式为二进制编码,因此与平台无关。

这些特性使得BUFR与传统的字母数字编码形式相比,提供了更大的灵活性和扩展性,并且通过打包减少了消息大小,提高了数据传输和存储的效率。一个BUFR文件由多个BUfR message组成,每个BUFR message由6段(第0段至第5段)组成。

第0段 指示段,一般长度为8个字节。包括“bufr”字符串,message的总长度以及bufr的版本号。

第1段 标识段,包括段长、主表标识、主表版本号、报文产生中心标识。序列号以及资料类型

第2段 选编段,包括段长及供自动数据处理中心内部使用的附加项。

第3段 数据描述段,包括段长,数据子集的个数,观测资料标志,数据压缩标志,描述符集合。

第4段 数据段,包括段长和数据值。

第5段 结束段,以“7777”结束。

数据描述段中的数据描述符分为三部分:F、X、Y。F表示描述符的类型:0表示参数描述符(关联Table B);1表示复制运算符;2表示操作描述符(关联Table C);3表示序列(Sequence)描述符(关联Table D)。
BUFR数据表格分为四类:

Table A:包含数据资料类型,例如地面资料、探空资料、卫星资料等。

Table B:包含各种参数(或变量)的编码、命名、单位、比例因子、偏移等信息。

Table C定义了一系列可以应用于参数的操作。

Table D定义了一组总是一起传输的参数(就像常规的SYNOP或TEMP报告)在所谓的公共序列中。通过使用公共序列描述符,就不需要在数据描述部分每次都列出各个元素描述符。这将减少BUFR消息所需的空间量。

这些表格是将数据中的各种编码映射为人类容易理解的形式的关键,可以独立于数据文件存在,也可以嵌入到数据文件中(如 prepbufr 数据)。

二、BUFR数据解码库

由上述介绍可知BUFR数据格式灵活度很高,解码还是比较复杂的,通常会用已有的解码库进行BUFR数据解码,例如ECMWF的ecCodes库和NCEP的NCEPLIBS库等。

Unidata的netCDF-Java库也包含了BUFR格式的读写功能,MeteoInfo对BUFR数据的解码就是基于这个库。但目前这个库里对BUFR格式的支持比较有限,只支持所有message都是同一种资料类型的数据文件,在读取prepbufr数据文件的时候也有一些bug存在。因此首先对netCDF-Java库的bufr模块代码进行了改进,使其能够支持多个资料类型的message,同时对于数据读取函数里的bug进行了修正,经过MeteoInfoLab读取多个BUFR数据文件的测试,给netCDF-Java项目提交了一个pull request (https://github.com/Unidata/netcdf-java/pull/1396) ,不过看起来还有一些check问题需要解决才能被项目接受。不过利用改进的库文件已经可以在MeteoInfoLab里读取prepbufr数据文件中读取各种不同类型的数据资料了(MeteoInfo 3.9.7及以上版本)。

三、MeteoInfoLab读取prepbufr数据文件示例

prepbufr是NCEP在BUFR格式基础上的一种定制格式,本质还是BUFR数据格式。prepbufr数据的表格是嵌入在数据文件中的,数据中前几个message是嵌入表格,需要先读出来才能正确解码,这些功能已经集成在库文件了。prepbufr格式通常用于数值模式的数据同化模块使用的观测数据中,例如全球资料同化系统GDAS。

GDAS(Global Data Assimilation System,全球资料同化系统)是由美国国家环境预报中心(NCEP)开发和维护的一个全球数值天气预报系统。GDAS的主要功能是将全球观测数据(如卫星、雷达、地面站、气球探测等)与数值天气预报模型结合,通过同化技术生成一个最优的初始条件,这些初始条件将用于驱动数值天气预报模型,以提高预报的准确性。GDAS通常会以6小时为一个周期进行同化和更新,生成全球范围内的分析场,这些分析场可以用来初始化数值天气预报模型。GDAS的输出产品不仅为NCEP的全球预报系统(GFS)提供初始条件,还为其他气象研究和应用提供了高质量的初始数据。

这里用一个GDAS prepbufr格式数据文件为例,演示如何用MeteoInfoLab读取数据。首先是用addfile函数打开数据文件,生成一个数据文件对象 f。需要注意的是addfile函数要添加 keepopen=True 参数,保持文件处于打开状态方便后续数据读取语句的顺利运行:

fn = 'D:/Temp/bufr/prepbufr.gdas.20230325.t00z.nr'f = addfile(fn, keepopen=True)

数据文件对象 f 的信息如下

>>> fFile Name: D:/Temp/bufr/prepbufr.gdas.20230325.t00z.nrFile type: WMO Binary Universal Form (BUFR)Dimensions: 0Global Attributes:   : :history = "Read using CDM BufrIosp2"  : :location = "D:/Temp/bufr/prepbufr.gdas.20230325.t00z.nr"  : :BUFR:categoryName = "ADPUPA   UPPER-AIR (RAOB, PIBAL, RECCO, DROPS) REPORTS"  : :BUFR:centerName = "7.3 (US National Weather Service - National Centres for Environmental Prediction (NCEP) / NCEP Central Operations)"  : :BUFR:centerId = 7  : :BUFR:subCenter = 3  : :BUFR:table = 0  : :BUFR:tableVersion = 29  : :BUFR:localTableVersion = 0  : :Conventions = "BUFR/CDM"  : :BUFR:edition = 3Variations: 8  Sequence ADPUPA);    ADPUPA: :coordinates = "YOB "  Sequence AIRCFT);    AIRCFT: :coordinates = "YOB YOB-2 "  Sequence SATWND);    SATWND: :coordinates = "YOB YOB-2 YOB-3 "  Sequence VADWND);    VADWND: :coordinates = "YOB YOB-2 YOB-3 YOB-4 "  Sequence ADPSFC);    ADPSFC: :coordinates = "YOB YOB-2 YOB-3 YOB-4 YOB-5 "  Sequence SFCSHP);    SFCSHP: :coordinates = "YOB YOB-2 YOB-3 YOB-4 YOB-5 YOB-6 "  Sequence RASSDA);    RASSDA: :coordinates = "YOB YOB-2 YOB-3 YOB-4 YOB-5 YOB-6 YOB-7 "  Sequence ASCATW);    ASCATW: :coordinates = "YOB YOB-2 YOB-3 YOB-4 YOB-5 YOB-6 YOB-7 YOB-8 "

可以看到文件中有8个根变量,每个变量表示不同的观测数据类型,例如ADPUPA是探空数据ADPSFC是陆地地面观测数据SATWND是卫星风场数据等等所有变量的数据类型都是 Sequence这种变量相当于根变量,里面包含了多个内在的变量内在的变量也可能是下一层级的根变量从而用多个层级达到负责观测数据灵活表达的目的

当然也可以用数据文件对象的varnames属性只看有哪些变量名

>>> f.varnames[ADPUPA, AIRCFT, SATWND, VADWND, ADPSFC, SFCSHP, RASSDA, ASCATW]

如果想读取陆地地表观测数据,可以先读取 ADPSFC 变量:

obs = f['ADPSFC']

然后看看该变量中有哪些内在的变量:

>>> obs.varnames[u'BYTCNT-5', u'SID-5', u'XOB-5', u'YOB-5', u'DHR-5', u'ELV-5', u'TYP-5', u'T29-5', u'TSB-5', u'ITP-5', u'SQN-5', u'PROCN-5', u'RPT-5', u'TCOR-5', u'RSRD_SEQ_RESTRICTIONS_ON_REDISTRIBUTION_SEQUENCE', u'CAT-5', u'P___INFO_PRESSURE_INFORMATION', u'Q___INFO_SPECIFIC_HUMIDITY_INFORMATION', u'T___INFO_TEMPERATURE_INFORMATION', u'Z___INFO_HEIGHT_INFORMATION', u'W___INFO_WIND_INFORMATION', u'W2_EVENT_WIND_{DIRECTION-SPEEDm-s}_EVENT_SEQUENCE', u'PMSL_SEQ_MEAN_SEA_LEVEL_PRESSURE_SEQUENCE', u'ALTIMSEQ_ALTIMETER_SETTING_SEQUENCE', u'SST_INFO_SEA_TEMPERATURE_INFORMATION', u'TOPC_SEQ_TOTAL_PRECIPITATION-TOTAL_WATER_EQUIVALENT_SEQUENCE', u'PREWXSEQ_PRESENT_WEATHER_SEQUENCE', u'CLOUDSEQ_OBSERVED_CLOUD_SEQUENCE_#_1', u'TMXMNSEQ_MAXIMUM-MINIMUM_TEMPERATURE_SEQUENCE', u'SWELLSEQ_SWELL_WAVE_SEQUENCE', u'VISB1SEQ_VISIBILITY_SEQUENCE_#_1', u'PSTWXSEQ_PAST_WEATHER_SEQUENCE', u'PKWNDSEQ_PEAK_WIND_SEQUENCE', u'GUST1SEQ_MAXIMUM_WIND_GUST_SEQUENCE_#_1', u'TPRECSEQ_TOTAL_PRECIPITATION_SEQUENCE', u'SUNSHSEQ_TOTAL_SUNSHINE_SEQUENCE', u'CLOU2SEQ_OBSERVED_CLOUD_SEQUENCE_#_2', u'SNOW_SEQ_SNOW_DEPTH_SEQUENCE', u'WAVE_SEQ_WAVE_SEQUENCE', u'PTENDSEQ_PRESSURE_TENDENCY_SEQUENCE', u'CLOU3SEQ_OBSERVED_CLOUD_SEQUENCE_#_3_CEILING', u'seq5']

XOB、YOB是观测站点的经纬度,SID是站点站号,每个内在变量会根据根变量加上数字后缀。可以用以下方式看某个变量的具体信息:

>>> obs['XOB-5']ADPSFC.XOB-5):  ADPSFC.XOB-5: long_name = "LONGITUDE"  ADPSFC.XOB-5: units = "DEG E"  ADPSFC.XOB-5: missing_value = 67108863  ADPSFC.XOB-5: scale_factor = 1.0E-5f  ADPSFC.XOB-5: add_offset = -180.0f  ADPSFC.XOB-5: BUFR:TableB_descriptor = "0-6-240"  ADPSFC.XOB-5: BUFR:bitWidth = 26

上述有具体信息的变量是最终变量,可以直接从变量里读取数据:

>>> lon = obs['XOB-5'][:]>>> lat = obs['YOB-5'][:]>>> sid = obs['SID-5'][:]>>> typ = obs['TYP-5'][:]

对于温度、气压等观测数据通常还有两层根变量:

>>> v = obs['T___INFO_TEMPERATURE_INFORMATION']>>> vv = v['T__EVENT_TEMPERATURE_EVENT_SEQUENCE']

每个根变量都可以用variables属性看全面信息,或者用varnames只看包含的变量名:

>>> v.varnames[u'T__EVENT_TEMPERATURE_EVENT_SEQUENCE', u'TVO-5', u'T__BACKG_TEMPERATURE_BACKGROUND_SEQUENCE', u'T__POSTP_TEMPERATURE_POSTPROCESSING_SEQUENCE']>>> vv.varnames[u'TOB-5', u'TQM-5', u'TPC-5', u'TRC-5']

vv根变量里的 TOB-5 变量是最终的温度变量:

>>> vvv = vv['TOB-5']>>> vvvADPSFC.T___INFO_TEMPERATURE_INFORMATION.T__EVENT_TEMPERATURE_EVENT_SEQUENCE.TOB-5):  ADPSFC.T___INFO_TEMPERATURE_INFORMATION.T__EVENT_TEMPERATURE_EVENT_SEQUENCE.TOB-5: long_name = "TEMPERATURE OBSERVATION"  ADPSFC.T___INFO_TEMPERATURE_INFORMATION.T__EVENT_TEMPERATURE_EVENT_SEQUENCE.TOB-5: units = "DEG C"  ADPSFC.T___INFO_TEMPERATURE_INFORMATION.T__EVENT_TEMPERATURE_EVENT_SEQUENCE.TOB-5: missing_value = 16383S  ADPSFC.T___INFO_TEMPERATURE_INFORMATION.T__EVENT_TEMPERATURE_EVENT_SEQUENCE.TOB-5: scale_factor = 0.1f  ADPSFC.T___INFO_TEMPERATURE_INFORMATION.T__EVENT_TEMPERATURE_EVENT_SEQUENCE.TOB-5: add_offset = -273.2f  ADPSFC.T___INFO_TEMPERATURE_INFORMATION.T__EVENT_TEMPERATURE_EVENT_SEQUENCE.TOB-5: BUFR:TableB_descriptor = "0-12-245"  ADPSFC.T___INFO_TEMPERATURE_INFORMATION.T__EVENT_TEMPERATURE_EVENT_SEQUENCE.TOB-5: BUFR:bitWidth = 14

从温度最终变量里读取数据,需要注意的是这里用 [0] 来读取,如果用 [:] 来读取数据会将所有数据都读出来,如果是探空站每个站会有多层数据,导致读出来的数组 data 比 lon, lat 经纬度数组大很多,很难和站点匹配上。用 [0] 来读取数据可以避免这种问题。也可以用 [0:] 来读取第一个站单站的数据(包含这个站的所有这个变量的数据)。

data = vvv[0]

完整的代码如下:

fn = 'D:/Temp/bufr/prepbufr.gdas.20230325.t00z.nr'f = addfile(fn, keepopen=True)obs = f['ADPSFC']print(obs.varnames)lon = obs['XOB-5'][:]lat = obs['YOB-5'][:]sid = obs['SID-5'][:]typ = obs['TYP-5'][:]v = obs['T___INFO_TEMPERATURE_INFORMATION']vv = v['T__EVENT_TEMPERATURE_EVENT_SEQUENCE']vvv = vv['TOB-5']data = vvv[0]f.close()
#Plotaxesm()geoshow('country')levs = arange(-20, 41, 5)layer = scatter(lon, lat, data, levs, size=2, edgecolor=None, zorder=0)colorbar(layer)title('Bufr data example')

读取卫星数据的例子:

fn = 'D:/Temp/bufr/prepbufr.gdas.20230325.t00z.nr'f = addfile(fn, keepopen=True)obs = f['SATWND']print(obs.varnames)lon = obs['XOB-3'][:]lat = obs['YOB-3'][:]sid = obs['SID-3'][:]typ = obs['TYP-3'][:]v = obs['P___INFO_PRESSURE_INFORMATION']vv = v['P__EVENT_PRESSURE_EVENT_SEQUENCE']vvv = vv['POB-3']data = vvv[0]f.close()
#Plotaxesm()geoshow('country')layer = scatter(lon, lat, data, size=2, edgecolor=None, zorder=0)colorbar(layer)title('Bufr data example')





声明:欢迎转载、转发。气象学家公众号转载信息旨在传播交流,其内容由作者负责,不代表本号观点。文中部分图片来源于网络,如涉及内容、版权和其他问题,请联系小编(微信:qxxjgzh)处理。


往期推荐
 获取ERA5/ERA5-Land再分析数据(36TB/32TB)
 获取全球GPM降水数据,半小时/逐日(4TB)
 获取1998-2019 TRMM 3B42逐日降水数据
 获取最新版本CMIP6降尺度数据集30TB
 EC数据商店推出Python在线处理工具箱
★ EC打造实用气象Python工具Metview
★ 机器学习简介及在短临天气预警中的应用
★ Nature-地球系统科学领域的深度学习及理解
★ 灵魂拷问:ChatGPT对气象人的饭碗是福是祸?
★ 气象局是做啥的?气象局的薪水多少?

气象学家
【气象学家】公众号平台为您把握最新AI4Science、解读最新气象科研进展、分享气象实用编程技巧、追踪气象即时资讯。致力于提升我国天气和气候预报、预测水平。欢迎加入气象AI和气象行业交流群以及气象博士群!与12W+专业人士一起交流互动!
 最新文章