1
+ #Load libraries
2
+ import os
3
+ import xarray as xr
4
+ import numpy as np
5
+ import pandas as pd
6
+ #import eccodes
7
+ from sklearn .cluster import KMeans
8
+ from sklearn .decomposition import PCA
9
+ import cartopy .crs as ccrs
10
+ from cartopy import feature
11
+ import matplotlib .pyplot as plt
12
+ from matplotlib .colors import LinearSegmentedColormap
13
+ # key PyWR functions are imported here
14
+ from PyWR import *
15
+
16
+
17
+ #The data used in this diagnostic was downloaded from the IRI:
18
+ #https://iridl.ldeo.columbia.edu/?Set-Language=en
19
+ #If data of a similar nature is desired, refer to the prep_data.py file for instructions on how to download anomaly data from the http server
20
+
21
+
22
+ #HOW WOULD THE DATA FILE BE INPUT IN THIS NAME FORMAT
23
+ hgt_input_path = "{DATADIR}/day/{CASENAME}.{hgt_var}.day.nc" .format (** os .environ )
24
+ t2m_input_path = "{DATADIR}/day/{CASENAME}.{t2m_var}.day.nc" .format (** os .environ )
25
+ pr_input_path = "{DATADIR}/day/{CASENAME}.{pr_var}.day.nc" .format (** os .environ )
26
+
27
+ #Trying to write the open_ds using environment variables here for a single variable; the other variables are kept as original code for now
28
+
29
+ reanalysis = xr .open_dataset (hgt_input_path , decode_cf = True , decode_times = True ).stack (time = ['T' ], grid = ['Y' , 'X' ])
30
+ #reanalysis = xr.open_dataset('WUS/data/hgt_NNRP_rean.nc', decode_cf = True, decode_times = True).stack(time=['T'], grid=['Y', 'X'])
31
+
32
+ #old code still retained
33
+ rainfall = xr .open_dataset ('WUS/data/rainfall_cpc.nc' , decode_cf = True , decode_times = True ).stack (time = ['T' ], grid = ['Y' , 'X' ])
34
+ t2m = xr .open_dataset ('WUS/data/t2m_cpc.nc' , decode_cf = True , decode_times = True ).stack (time = ['T' ], grid = ['Y' , 'X' ])
35
+ uwnd = xr .open_dataset ('WUS/data/u_NNRP_rean.nc' , decode_cf = True , decode_times = True ).stack (time = ['T' ], grid = ['Y' , 'X' ])
36
+ vwnd = xr .open_dataset ('WUS/data/v_NNRP_rean.nc' , decode_cf = True , decode_times = True ).stack (time = ['T' ], grid = ['Y' , 'X' ])
37
+
38
+
39
+ #get rid of dummy pressure coordinate
40
+ reanalysis = reanalysis .isel (P = 0 )
41
+ uwnd = uwnd .isel (P = 0 )
42
+ vwnd = vwnd .isel (P = 0 )
43
+
44
+
45
+ #viewing the data
46
+ print (reanalysis )
47
+ print (rainfall )
48
+ print (t2m )
49
+ print (vwnd )
50
+ print (uwnd )
51
+
52
+
53
+ #DIMENSION REDUCTION: choose a percentage of variance explained that we will require
54
+ n_eof = get_number_eof (X = reanalysis ['adif' ].values , var_to_explain = 0.9 , plot = True )
55
+
56
+ #project the data onto the leading EOFs to get the principal component time series
57
+ #We will retain the PCA model for use later. The reanalysis_pc variable is now indexed [time, EOF]
58
+ pca_model = PCA (n_components = n_eof ).fit (reanalysis ['adif' ].values )
59
+ reanalysis_pc = pca_model .transform (reanalysis ['adif' ].values )
60
+
61
+
62
+ #REANALYSIS WEATHER TYPING: perform the clustering. We will manually specify the number of clusters we want to create and the number of simulations we want to run
63
+ ncluster = 6 # use 6 WTs
64
+ n_sim = 50 # typically 25-50 -- try 25 for quick preliminary computation only
65
+
66
+ centroids , wtypes = loop_kmeans (X = reanalysis_pc , n_cluster = ncluster , n_sim = n_sim )
67
+ class_idx , best_part = get_classifiability_index (centroids )
68
+ print ('The classifiability index is {}' .format (class_idx ))
69
+
70
+
71
+ #Now that we have identified a suitable partition, we can use it to keep only the corresponding centroid and set of weather type labels. Use centroids to define KMeans object
72
+ best_fit = KMeans (n_clusters = ncluster , init = centroids [best_part , :, :], n_init = 1 , max_iter = 1 ).fit (reanalysis_pc )
73
+
74
+ # start with reanalysis
75
+ reanalysis_composite = reanalysis .copy ()
76
+ model_clust = best_fit .fit_predict (reanalysis_pc ) # get centroids
77
+ weather_types = xr .DataArray (
78
+ model_clust ,
79
+ coords = {'time' : reanalysis_composite ['time' ]},
80
+ dims = 'time'
81
+ )
82
+ reanalysis_composite ['WT' ] = weather_types
83
+ reanalysis_composite = reanalysis_composite .groupby ('WT' ).mean (dim = 'time' ).unstack ('grid' )['adif' ]
84
+ reanalysis_composite ['M' ] = 0
85
+
86
+ wt_anomalies = [] # initialize empty list
87
+ wt_anomalies .append (reanalysis_composite )
88
+
89
+ wt_anomalies = xr .concat (wt_anomalies , dim = 'M' ) # join together
90
+ wt_anomalies ['WT' ] = wt_anomalies ['WT' ] + 1 # start from 1
91
+
92
+
93
+ #FIGURE: prepare a figure with rainfall and temperature composites
94
+ #Hashed out options for adding wind arrows, and additional plot labels
95
+
96
+ X , Y = np .meshgrid (reanalysis ['adif' ].X , reanalysis ['adif' ].Y )
97
+ map_proj = ccrs .PlateCarree () #ccrs.Orthographic(-110, 10)
98
+ data_proj = ccrs .PlateCarree ()
99
+ wt_unique = np .unique (wt_anomalies ['WT' ])
100
+ figsize = (14 , 8 )
101
+
102
+ #WT proportions
103
+ wt = weather_types .to_dataframe (name = 'WT' )
104
+ wt = wt + 1
105
+ #wt.to_netcdf('data/t2m_cpc.nc', format="NETCDF4")
106
+ wt_counts = wt .groupby ('WT' ).size ().div (wt ['WT' ].size )
107
+ wt_counts
108
+
109
+ xmin ,xmax = reanalysis ['X' ].min (), reanalysis ['X' ].max ()
110
+ ymin ,ymax = reanalysis ['Y' ].min (), reanalysis ['Y' ].max ()
111
+
112
+ # Set up the Figure
113
+ plt .rcParams .update ({'font.size' : 12 })
114
+ fig , axes = plt .subplots (
115
+ nrows = 3 , ncols = len (wt_unique ), subplot_kw = {'projection' : map_proj },
116
+ figsize = figsize , sharex = True , sharey = True
117
+ )
118
+
119
+ # Loop through
120
+ for i ,w in enumerate (wt_unique ):
121
+ def selector (ds ):
122
+ times = wt .loc [wt ['WT' ] == w ].index
123
+ ds = ds .sel (time = np .in1d (ds .unstack ('time' )['T' ], times ))
124
+ ds = ds .mean (dim = 'time' )
125
+ return (ds )
126
+
127
+ # Top row: geopotential height anomalies
128
+ ax = axes [0 , i ]
129
+ ax .set_title ('WT {}: {:.1%} of days' .format (w , wt_counts .values [i ]))
130
+ C0 = selector (reanalysis ['adif' ]).unstack ('grid' ).plot .contourf (
131
+ transform = data_proj ,
132
+ ax = ax ,
133
+ cmap = 'PuOr' ,
134
+ extend = "both" ,
135
+ levels = np .linspace (- 2e2 , 2e2 , 21 ),
136
+ add_colorbar = False ,
137
+ add_labels = False
138
+ )
139
+ ax .coastlines ()
140
+ ax .add_feature (feature .BORDERS )
141
+ #ax.set_extent([-95, -65, -12, 12])
142
+
143
+ # # add wind arrows
144
+ # U = selector(uwnd).adif.values
145
+ # V = selector(vwnd).adif.values
146
+ # magnitude = np.sqrt(U**2 + V**2)
147
+ # strongest = magnitude > np.percentile(magnitude, 50)
148
+ # Q = ax.quiver(
149
+ # X[strongest], Y[strongest], U[strongest], V[strongest],
150
+ # transform=data_proj,
151
+ # width=0.001, scale=0.8,units='xy'
152
+ # )
153
+
154
+ # Middle row: rainfall anomalies
155
+ ax = axes [1 , i ]
156
+ C1 = selector (rainfall ['adif' ]).unstack ('grid' ).plot .contourf (
157
+ transform = data_proj ,
158
+ ax = ax ,
159
+ cmap = 'BrBG' ,
160
+ extend = "both" ,
161
+ levels = np .linspace (- 2 , 2 , 13 ),
162
+ add_colorbar = False ,
163
+ add_labels = False
164
+ )
165
+ ax .coastlines ()
166
+ ax .add_feature (feature .BORDERS )
167
+ #ax.set_extent([-95, -75, -9, 5])
168
+
169
+ # Bottom row: tepmperature anomalies
170
+ ax = axes [2 , i ]
171
+ C2 = selector (t2m ['asum' ]).unstack ('grid' ).plot .contourf (
172
+ transform = data_proj ,
173
+ ax = ax ,
174
+ cmap = 'RdBu_r' ,
175
+ extend = "both" ,
176
+ levels = np .linspace (- 2 , 2 , 13 ),
177
+ add_colorbar = False ,
178
+ add_labels = False
179
+ )
180
+ ax .coastlines ()
181
+ ax .add_feature (feature .BORDERS )
182
+ #ax.set_extent([-95, -70, -9, 5])
183
+ ax .tick_params (colors = 'b' )
184
+
185
+ # # Add Colorbar
186
+ plt .tight_layout ()
187
+ fig .subplots_adjust (right = 0.94 )
188
+ cax0 = fig .add_axes ([0.97 , 0.65 , 0.0075 , 0.3 ])
189
+ cax1 = fig .add_axes ([0.97 , 0.33 , 0.0075 , 0.3 ])
190
+ cax2 = fig .add_axes ([0.97 , 0.01 , 0.0075 , 0.3 ])
191
+ cbar0 = fig .colorbar (C0 , cax = cax0 )
192
+ cbar0 .formatter .set_powerlimits ((4 , 4 ))
193
+ cbar0 .update_ticks ()
194
+ cbar0 .set_label (r'$zg_{500}$ anomaly [$m^2$/$s^2$]' , rotation = 270 )
195
+ cbar0 .ax .get_yaxis ().labelpad = 20
196
+ cbar1 = fig .colorbar (C1 , cax = cax1 )
197
+ cbar1 .set_label ('Precip. anomaly [mm/d]' , rotation = 270 )
198
+ cbar1 .ax .get_yaxis ().labelpad = 20
199
+ cbar2 = fig .colorbar (C2 , cax = cax2 )
200
+ cbar2 .set_label ('T2m anomaly [$^o$C]' , rotation = 270 )
201
+ cbar2 .ax .get_yaxis ().labelpad = 20
202
+
203
+ # Format these axes
204
+
205
+
206
+ #Add plot labels
207
+ # letters = string.ascii_lowercase
208
+ # for i, ax in enumerate(axes.flat):
209
+ # label = '({})'.format(letters[i])
210
+ # t = ax.text(0.05, 0.9, label, fontsize=11, transform=ax.transAxes)
211
+ # t.set_bbox(dict(facecolor='white', edgecolor='gray'))
212
+
213
+ # Add a quiver key
214
+ #k = plt.quiverkey(Q, 0.9, 0.7, 1, '1 m/s', labelpos='E', coordinates='figure')
215
+
216
+ fig .savefig ('figs/wt_composite.pdf' , bbox_inches = 'tight' ) #this needs to be changed to appropriate folder in framework
217
+ plt .show ()
0 commit comments