In [1]:
%matplotlib inline
NCEP BUFR (Binary Universal Form for the Representation of meteorological data) can be read two ways:
Fortran code with BUFRLIB
py-ncepbufr, which is basically Python wrappers around BUFRLIB
In this example we'll use py-ncepbufr to read a snapshot of the Argo data tank from WCOSS, show how to navigate the BUFR structure, and how to extract and plot a profile.
The py-ncepbufr library and installation instructions can be found at
In [2]:
import matplotlib.pyplot as plt # graphics library
import numpy as np
import ncepbufr # python wrappers around BUFRLIB
/dcom/us007003/201808/b031/xx005
In [3]:
bufr = ncepbufr.open('data/xx005')
bufr.advance()
bufr.load_subset()
bufr.read_subset()
bufr.rewind()
bufr.close()
There is a lot more functionality to ncepbufr, such as searching on multiple mnenomics, printing or saving the BUFR table included in the file, printing or saving the inventory and subsets, setting and using checkpoints in the file. See the ncepbufr help for more details.
Important Note: py-ncepbufr is unforgiving of mistakes. A BUFRLIB fortran error will result in an immediate exit from the Python interpreter.
In [4]:
# move down to first message - a return code of 0 indicates success
bufr.advance()
Out[4]:
In [5]:
# load the message subset -- a return code of 0 indicates success
bufr.load_subset()
Out[5]:
You can print the subset and determine the parameter names. BUFR dumps can be very verbose, so I'll just copy in the header and the first subset replication from a bufr.dump_subset()
command.
I've highlighted in red the parameters I want to plot.
MESSAGE TYPE NC031005 004001 YEAR 2018.0 YEAR YEAR 004002 MNTH 8.0 MONTH MONTH 004003 DAYS 1.0 DAY DAY 004004 HOUR 0.0 HOUR HOUR 004005 MINU 16.0 MINUTE MINUTE 035195 SEQNUM 317 ( 4)CCITT IA5 CHANNEL SEQUENCE NUMBER 035021 BUHD IOPX01 ( 6)CCITT IA5 BULLETIN BEING MONITORED (TTAAii) 035023 BORG KWBC ( 4)CCITT IA5 BULLETIN BEING MONITORED (CCCC) 035022 BULTIM 010029 ( 6)CCITT IA5 BULLETIN BEING MONITORED (YYGGgg) 035194 BBB MISSING ( 6)CCITT IA5 BULLETIN BEING MONITORED (BBB) 008202 RCTS 0.0 CODE TABLE RECEIPT TIME SIGNIFICANCE 004200 RCYR 2018.0 YEAR YEAR - TIME OF RECEIPT 004201 RCMO 8.0 MONTH MONTH - TIME OF RECEIPT 004202 RCDY 1.0 DAY DAY - TIME OF RECEIPT 004203 RCHR 0.0 HOUR HOUR - TIME OF RECEIPT 004204 RCMI 31.0 MINUTE MINUTE - TIME OF RECEIPT 033215 CORN 0.0 CODE TABLE CORRECTED REPORT INDICATOR 001087 WMOP 6903327.0 NUMERIC WMO marine observing platform extended identifie 001085 OPMM S2-X (20)CCITT IA5 Observing platform manufacturer's model 001086 OPMS 10151 ( 32)CCITT IA5 Observing platform manufacturer's serial number 002036 BUYTS 2.0 CODE TABLE Buoy type 002148 DCLS 8.0 CODE TABLE Data collection and/or location system 002149 BUYT 14.0 CODE TABLE Type of data buoy 022055 FCYN 28.0 NUMERIC Float cycle number 022056 DIPR 0.0 CODE TABLE Direction of profile 022067 IWTEMP 846.0 CODE TABLE INSTRUMENT TYPE FOR WATER TEMPERATURE PROFILE ME 005001 CLATH 59.34223 DEGREES LATITUDE (HIGH ACCURACY) 006001 CLONH -9.45180 DEGREES LONGITUDE (HIGH ACCURACY) 008080 QFQF 20.0 CODE TABLE Qualifier for GTSPP quality flag 033050 GGQF 1.0 CODE TABLE Global GTSPP quality flag (GLPFDATA) 636 REPLICATIONS ++++++ GLPFDATA REPLICATION # 1 ++++++ 007065 WPRES 10000.0 PA Water pressure 008080 QFQF 10.0 CODE TABLE Qualifier for GTSPP quality flag 033050 GGQF 1.0 CODE TABLE Global GTSPP quality flag 022045 SSTH 285.683 K Sea/water temperature 008080 QFQF 11.0 CODE TABLE Qualifier for GTSPP quality flag 033050 GGQF 1.0 CODE TABLE Global GTSPP quality flag 022064 SALNH 35.164 PART PER THOUSAND Salinity 008080 QFQF 12.0 CODE TABLE Qualifier for GTSPP quality flag 033050 GGQF 1.0 CODE TABLE Global GTSPP quality flag
In [6]:
temp = bufr.read_subset('SSTH').squeeze()-273.15 # convert from Kelvin to Celsius
sal = bufr.read_subset('SALNH').squeeze()
depth = bufr.read_subset('WPRES').squeeze()/10000. # convert from Pa to depth in meters
# observation location, date, and receipt time
lon = bufr.read_subset('CLONH')[0][0]
lat = bufr.read_subset('CLATH')[0][0]
date = bufr.msg_date
receipt = bufr.receipt_time
bufr.close()
Set up the plotting figure. But this time, just for fun, let's put both the temperature and salinity profiles on the same axes. This trick uses both the top and bottom axis for different parameters.
As these are depth profiles, we need twin x-axes and a shared y-axis for the depth.
In [7]:
fig = plt.figure(figsize = (5,4))
ax1 = plt.axes()
ax1.plot(temp, depth,'r-')
ax1.grid(axis = 'y')
ax1.invert_yaxis() # flip the y-axis for ocean depths
ax2 = ax1.twiny() # here's the second x-axis definition
ax2.plot(np.nan, 'r-', label = 'Temperature')
ax2.plot(sal, depth, 'b-', label = 'Salinity')
ax2.legend()
ax1.set_xlabel('Temperature (C)', color = 'red')
ax1.set_ylabel('Depth (m)')
ax2.set_xlabel('Salinity (PSU)', color = 'blue')
ttl='ARGO T,S Profiles at lon:{:6.2f}, lat:{:6.2f}\ntimestamp: {} received: {}\n'.format(lon,lat,date,receipt)
fig.suptitle(ttl,x = 0.5,y = 1.1,fontsize = 'large');