Storm Track Analysis

The tracking algorithm is a fork of the Tint (Tint is not Titan) tracking algorithm (http://openradarscience.org/TINT/). The framework has been modified that it can be also applied to model output data that is not not stored in Py-ART radar data container.

  • The analysis is based averages of one week worth of storm cell tracking:

1. Median of storm area, duration, avg., max. rain-rates and # of storm cells

In [19]:
#Plot the Medians
um044_n = len(UM044_ens.coords['dim_0'])
um133_n = len(UM133_ens.coords['dim_0'])
cpol_n = len(OBS.dataset['total'].coords['dim_0'])
cpol_n2 = len(CPOL.coords['dim_0'])
CPOL_pdf2 = ravel(OBS.dataset['obs'])
var=['avg_area','dur', 'avg_mean', 'max_mean', 'v']
names=['Area', 'Duration', 'Avg-Rain', 'Max-Rain', 'Speed', '# Storms']
#print(CPOL_pdf[var].median())
medians = pd.DataFrame({'d': list(UM133_pdf[var].median().values)+[int(um044_n)],
                        'c': list(UM044_pdf[var].median().values)+[int(um133_n)],
                        'a': list(CPOL_pdf[var].median().values)+[int(cpol_n2)]} )
                       #index=('Area','Duration', 'Mean-Rain', 'Max-Rain'))
medians.index=names
medians.columns=['UM 1.33km', 'UM 0.44km', 'CPOL']
#print('Medians:')
                 
medians.round(2)
Out[19]:
UM 1.33km UM 0.44km CPOL
Area 76.67 61.25 107.81
Duration 60.00 50.00 60.00
Avg-Rain 4.78 5.65 4.53
Max-Rain 6.90 8.56 6.86
Speed 10.03 12.71 13.06
# Storms 73.00 50.00 42.00

2. 2D-Histograms (storm area vs duration):

In [22]:
#Create Hex-Bin plot
mpld3.disable_notebook()
sns.set_style('whitegrid', {'axes.grid':True})
var=['avg_area','dur', 'max_mean', 'avg_mean']
medians = pd.DataFrame({'UM 1.33km':UM133_pdf[var].median(),
                        'UM 0.44km':UM044_pdf[var].median(),
                        'CPOL':CPOL_pdf2[var].median()})
#medians.loc['avg_area'] /= 2.5**2
histdata =  [UM133_pdf[var].dropna(), UM044_pdf[var].dropna(), CPOL_pdf[var].dropna()]
titles = ['UM 1.33km', 'UM 0.44km',  'CPOL']
#fig = plt.figure(figsize=(10,7))
colm = colmap2.Blues
colm.set_under('w', alpha=1)
colm.set_bad('w', alpha=1)
fig, (ax1, ax2, ax3,) = plt.subplots(1, 3, sharey=True)

cbar_ax = fig.add_axes([0.15, 0.28, 0.7, 0.03])
hexbin_data = []
gridsize = 10
vmin=0.0001
vmax=0.1
nticks=10
YMax, XMax = 200, 70*2.5**2
#YMax, XMax = 50, 70*2.5**2
for i, ax in enumerate((ax1, ax2, ax3)):
    data = histdata[i][var[:2]]
    #data[var[0]] /= 2.5**2
    #data = data.loc[(data[var[0]] <= XMax) & (data[var[1]] <=YMax)]
    X = data[var[0]].values
    Y = data[var[1]].values
    _ = ax.set_ylim(1,YMax)
    _ = ax.set_xlim(0,XMax)
    hb =  ax.hexbin(X, Y,  gridsize=gridsize, extent=(0,XMax,0,YMax), alpha=0, facecolor='w', cmap=colm)
    hexbin_data.append(np.ones_like(Y, dtype=np.float) / hb.get_array().sum())
cl = plt.cla()
medians = OrderedDict()
colm.set_under('w', alpha=1)
colm.set_bad('w', alpha=1)
for i, ax in enumerate((ax1, ax2, ax3)):
    data = histdata[i][var[:2]]
    #data[var[0]] /= 2.5**2
    #data = data.loc[(data[var[0]] <= XMax) & (data[var[1]] <=YMax)]
    X = data[var[0]].values
    Y = data[var[1]].values
    ax.set_ylim(1,YMax)
    ax.set_xlim(0,XMax)
    im = ax.hexbin(X, Y,  gridsize=gridsize, cmap=colm, marginals=False, extent=(0,XMax,0,YMax), alpha=1,
                     vmin=vmin, vmax=vmax, C=hexbin_data[i], reduce_C_function=np.sum, facecolor='w')
    
    ax.set_title(titles[i], fontsize=24)
    ax.grid(color='w', linestyle='', linewidth=0)
    ax.tick_params(labelsize=fontsize)
    ax.xaxis.set_ticks(ax.xaxis.get_ticklocs()[:-1])
    x, y = histdata[i][var[0]].median(), histdata[i][var[1]].median()
    z, zz=  histdata[i][var[2]].median(), histdata[i][var[-1]].median()
    sx, sy = histdata[i][var[0]].std(), histdata[i][var[1]].std()
    medians[titles[i]] = '%2.1f km^2, %2i min (max: %2.1f mm/h, mean: %2.1f mm/h);'%(x,y, z, zz)
    ax.hlines(y*np.ones_like(histdata[i][var[0]]),0,x, 'firebrick', lw=2, alpha=0.5)
    ax.vlines(x*np.ones_like(histdata[i][var[1]]),0,y, 'firebrick', lw=2, alpha=0.5)
    ax.scatter([x], [y], marker='o', s=100, c='firebrick', alpha=0.8)
    if i == 0:
        ax.set_ylabel('Duration [min]', fontsize=fontsize)
    ax.set_xlabel('Rainfall Area [km$^2$]', fontsize=fontsize)
ary = np.ma.masked_less(im.get_array()/im.get_array().sum() * 100,vmin).filled(vmin)
ary = np.ma.masked_greater(im.get_array()/im.get_array().sum() * 100,vmax).filled(vmax)

im.set_array = ary
cbar = fig.colorbar(im, cax=cbar_ax, orientation='horizontal')
tmp = cbar.ax.tick_params(labelsize=fontsize)
tmp = cbar.set_label('Density [ ]',size=fontsize)
#cbar.set_ticks(np.linspace(vmin, vmax, nticks).round(2))
#cbar.set_ticklabels(np.linspace(vmin, vmax, nticks).round(2))
#print('Medians:')
#medians
#print(' '.join(['%s: %s'%(k, v) for (k,v) in medians.items()]))
#plt.show()
_ = fig.subplots_adjust(bottom=0.5, right=0.98, left=0.01, top=0.95, wspace=0.01)
sns.set_style("darkgrid", {'axes.grid':True, 'ticks':True})

3. Storm tracks above the 90th percentiles

  • marks start of track, colors for different ensemble member
In [27]:
#Plot the tracks
#mpld3.enable_notebook()
fig = plt.figure(figsize=(15,5.5))
sns.set_style("whitegrid", {'axes.grid':True, 'ticks':True})
fig.subplots_adjust(right=0.94, bottom=0.45, top=0.95,left=0.01, hspace=0, wspace=0)
#cbar_ax = fig.add_axes([0.12, 0.17, 0.74, 0.02])
o = namedtuple('Sim', 'dataset percentiles')
tmp_obs = o({'obs': CPOL_t.dataset['obs']}, {'obs': CPOL_t.percentiles['obs']})

with nc(CPOLF) as fnc:
    lon=fnc.variables['longitude'][:]
    lat=fnc.variables['latitude'][:]
tp = 'mean'
num=80
titels = ['UM 1.33km', 'UM 0.44km', 'CPOL']
m = None
for i, data in enumerate(((UM133_t, storm_UM133), (UM044_t, storm_UM044), (tmp_obs, storm_obs))):
    tracks, stormId = data
    ax = fig.add_subplot(1,3,i+1)
    #ax2 = fig.add_subplot(2,3,i+4)
    ax.set_title(titels[i], fontsize=fontsize)
    for ii, tr in enumerate(tracks.dataset.keys()):
        if ii == 0:
            draw_map = None
            m2 = None
        else:
            draw_map = m
        Id = [stormId.Sim[tr]]
        perc = tracks.percentiles[tr][tp][num]
        ax, m, im = plot_traj(tracks.dataset[tr], lon, lat, ax=ax, mintrace=2, thresh=('mean', perc), 
                  color=colm[ii], create_map=draw_map, basemap_res='f', lw=0.5, size=40, particles=None)
        #ax2, m2, im = plot_traj(tracks.dataset[tr], lon, lat, ax=ax2, mintrace=2, thresh=('mean', perc),
        #          color=colm[ii], create_map=m2, basemap_res='f', lw=0.5, size=20, particles=Id)
        #break
#plt.show()

4. Classification of storm area, rain-rate and duration by rain-rate quintiles

In [30]:
mpld3.disable_notebook()
variables=dict(avg_mean=('Rain-Rate [mm/h]',12), 
               dur=('Duration [min]',300),
               avg_area=('Area [km$^2$]', 100*2.5**2))

fig = plt.figure(figsize=(15,15))
fig.subplots_adjust(right=0.94, bottom=0.15, top=0.98,left=0.1, hspace=0, wspace=0)
i = 0
for y, prop in variables.items():
    label, ylim = prop
    npl = len(list(variables.keys()))
    i += 1
    ax = fig.add_subplot(npl,1,i)
    ax = sns.boxplot(x="quant", y=y, hue="run", data=df_stack, palette="muted", ax=ax,  linewidth=1)
    #ax = sns.stripplot(x="quant", y=y, hue="run", data=df_stack, jitter=True, palette="Set2", dodge=True)
    if i == 1:
        ax.legend(loc=0, fontsize=fontsize-4)
    else:
        ax.legend(loc=0, fontsize=1)
    ax.tick_params(labelsize=fontsize-4)
    ax.set_xlim(0.5,5.5)
    ax.set_ylim(0,ylim)
    ax.yaxis.set_ticks(ax.yaxis.get_ticklocs()[1:])
    ax.set_xlabel('Quintiles', fontsize=fontsize)
    ax.set_ylabel(label, fontsize=fontsize)
    #break

5. Stormtrack Percentiles vs Rain-rate

In [61]:
P = list(np.arange(0,99))+[99, 99.9, 99.99, 99.999, 100]

PERC = pd.DataFrame({'Obs':np.percentile(CPOL_pdf['avg_mean'].values, P), 
                     'UM 1.33km': np.percentile(UM133_pdf['avg_mean'].values, P),
                      'UM 0.44km': np.percentile(UM044_pdf['avg_mean'].values, P)},index=P)

from mpl_toolkits.axes_grid.inset_locator import inset_axes
#Plot the percentages
fig=plt.figure(figsize=(15, 7))
ax = fig.add_subplot(111)
plt.subplots_adjust(bottom=0.2, right=0.98, top=0.99)
#ax.plot(PERC.index, PERC['Obs'].values, linestyle='-', label='CPOL',lw=3)
ax.plot(PERC.index, PERC['UM 1.33km'].values, linestyle='-', label='UM 1.33km',lw=3)
ax.plot(PERC.index, PERC['UM 0.44km'].values, linestyle='-', label='UM 0.44km', lw=3)
#ax.fill_between(PERC.index, member.min(axis=0)-0.25, member.max(axis=0)+0.25, color='cornflowerblue', alpha=0.2)
ax.plot(PERC.index, member.mean(axis=0), linestyle='-', label='CPOL',lw=3)
ax.set_xlim(1,100)
#ax.set_ylim(10,100)
#ax.set_yscale('log')
#ax.set_xscale('log')
ax.set_xlabel('Percentile []', fontsize=fontsize)
ax.set_ylabel('Rain-Rate [mm/h]', fontsize=fontsize)
ax.legend(loc=0, fontsize=fontsize-4)
ax.tick_params(labelsize=fontsize-4)
#mpld3.enable_notebook()

6. Cold-Pools

Cold-Pool tracking by applying density potential temp. pertubation fields to tint tracking software

In [215]:
embed_vid(os.path.join(dest_dir, 'ColdPool-Ens-1.mp4'))
Out[215]:

Distribution of Cold-Pool strength, area and liftime within their quintiles

In [45]:
mpld3.disable_notebook()
sns.set_style("darkgrid", {'axes.grid':True, 'ticks':True})
sns.set_context('talk')
variables=dict(cp_mean=('Strength [K]', 2), cp_area=('Area [km$^2$]', 200), 
               cp_dur=('Lifetime [min]', 120))
fig = plt.figure(figsize=(15,15))
fig.subplots_adjust(right=0.94, bottom=0.15, top=0.98,left=0.1, hspace=0, wspace=0)
i = 0
for y, prop in variables.items():
    label, ylim = prop
    npl = len(list(variables.keys()))
    i += 1
    ax = fig.add_subplot(npl,1,i)
    ax = sns.boxplot(x="quant", y=y, hue="run", data=df_stack, palette="muted", ax=ax, linewidth=1)
    #ax = sns.stripplot(x="quant", y=y, hue="run", data=df_stack, jitter=True, palette="Set2", dodge=True)
    if i == 1:
        ax.legend(loc=0, fontsize=fontsize-4)
    else:
        ax.legend(loc=0, fontsize=1)
    ax.tick_params(labelsize=24)
    ax.set_xlim(0.5,5.5)
    if i == 3:
        ax.set_ylim(60,ylim)
    else:
        ax.set_ylim(0, ylim)
    ax.yaxis.set_ticks(ax.yaxis.get_ticklocs()[1:])
    ax.set_xlabel('Quintiles', fontsize=fontsize)
    ax.set_ylabel(label, fontsize=fontsize)
    #break
#fig.savefig(os.path.join(os.environ['HOME'], 'Todds_Rainfall_2.pdf'), bbox_inches='tight', format='pdf', dpi=300)

7. Fluxes

  • Distributions of some variables within their quintile
In [55]:
mpld3.disable_notebook()
variables=dict(omega=('Vert. Motion', (0, 7)), moistflux=('Moisture Flux', (-0.01, 0.04)), 
               momflux=('Momentum Flux', (-1.5, 1.5)))#, mfluxdiv=('Moist. Flux. Conv', (-5, 10)))
               
               #cloud_w=('Cloud Water', (0,0.005)))
fig = plt.figure(figsize=(15,15))
fig.subplots_adjust(right=0.94, bottom=0.15, top=0.98,left=0.1, hspace=0, wspace=0)
i = 0
for y, prop in variables.items():
    label, ylim = prop
    npl = len(list(variables.keys()))
    i += 1
    ax = fig.add_subplot(npl,1,i)
    ax = sns.boxplot(x="quant", y=y, hue="run", data=storm_prop, palette="muted", ax=ax, linewidth=1)
    #ax = sns.stripplot(x="quant", y=y, hue="run", data=df_stack, jitter=True, palette="Set2", dodge=True)
    if i == 1:
        ax.legend(loc=2, fontsize=fontsize-2)
    else:
        ax.legend(fontsize=1)
    ax.tick_params(labelsize=fontsize-2)
    ax.set_xlim(0.5,5.5)
    ax.set_ylim(*ylim)
    ax.yaxis.set_ticks(ax.yaxis.get_ticklocs()[1:])
    ax.set_xlabel('Quintiles', fontsize=fontsize)
    ax.set_ylabel(label, fontsize=fontsize)
  • Some vertical profiles
In [59]:
fig = plt.figure(figsize=(15,15))
from matplotlib.ticker import Formatter

from scipy import interpolate
variables=dict(omega=(('Vert. Motion'), (-9.5e-2, 8.5e-1)), mflux=('Moisture Flux', (-1.2e-4, 1.6e-3)),
               momflux=('Momentum Flux', (-1.5, 1.3)))#, mfluxdiv =('Moist. Flx Conv', (-0.5e-3,1.3e-3)))
i = 0
y = np.linspace(P[-1], P[0], 40)[::-1]

for x, prop in variables.items():
    label, xlim = prop
    npl = len(list(variables.keys()))
   
    for ii in range(1, 6):
        i += 1
        ax = fig.add_subplot(npl,5,i)
        for ff, fluxes in enumerate((fluxes133, fluxes044)):           
            flux = fluxes[x][ii]
            f = interpolate.interp1d(P, flux, kind='slinear')
            #xx = f(y)
            y = P
            xx = flux
            ax.plot(xx, y, label=('UM 1.33km', 'UM 0.44km')[ff])
            ax.set_ylim(max(y), min(y))
            ax.set_xlim(*xlim)
            if ii == 1:
                ax.set_ylabel('Pressure [hPa]', fontsize=fontsize-2)
            else:
                ax.yaxis.set_ticklabels([])   
            if i == 1:
                 ax.legend(loc=1, fontsize=fontsize-8)
            else:
                ax.legend(loc=1, fontsize=1)
            ax.set_xlabel(label, fontsize=fontsize-2)
            ax.tick_params(labelsize=fontsize-2)
            ax.ticklabel_format(axis='x', style='sci')
            if i < 6:
                ax.set_title('Quintile  %i'%ii, fontsize=fontsize-4)
            #ax.xaxis.set_major_formatter('%.1E')
            #ax.ticklabel_format(style='sci', axis='x') 
            ax.xaxis.major.formatter.set_powerlimits((0,0))
fig.subplots_adjust(right=0.98, bottom=0.1, top=0.99, left=0.01, hspace=0.3, wspace=0.04)