|
||||
File indexing completed on 2021-04-23 05:12:38 UTC
view on githubraw file Latest commit ce0d9af5 on 2021-04-21 19:30:20 UTCce0d9af5ea Jeff*0001 # import python modules 0002 import numpy as np 0003 import matplotlib.pyplot as plt 0004 import xarray as xr 0005 0006 # python pkg xarray used to load netcdf files http://xarray.pydata.org 0007 # xarray loads netcdf data and metadata into xarray structure Dataset 0008 # (containing data in xarray DataArray) which includes information 0009 # about dimensions, coordinates etc. 0010 # Xarray simplifies some basic tasks, e.g. it is not necessary to 0011 # provide axes arguments when plotting, but also more complex tasks 0012 # such as broadcasting data to fill missing dimensions in DataArray 0013 # arithmetic, for example. 0014 0015 0016 ########### load grid data ########### 0017 0018 # Load grid variables; names correspond with MITgcm source code. 0019 # (if using standard binary output instead of netcdf, these are dumped 0020 # to individual files, e.g. 'RC.data' etc.) 0021 # Assumes that output in multiple tiles has been concatenated into 0022 # global files (used script utils/python/MITgcmutils/scripts/gluemncbig) 0023 # and moved into the top directory; 0024 # see MITgcm user manual section 4.2.4.1 0025 0026 # Using a spherical polar grid, all X,Y variables in lon,lat coordinates 0027 # vertical grid provided in meters, area in m^2 0028 0029 grid = xr.open_dataset('grid.nc') 0030 0031 # We will be using the following fields from the grid file: 0032 # 1-D fields 0033 # RC: vertical grid, cell center locations (this is coordinate Z) 0034 # drF: vertical spacing of grid cells (thickness of cells) 0035 # X: 1-D version of XC data 0036 # Y: 1-D version of YC data 0037 # Xp1: x-location of gridcell lower left corner 0038 # Yp1: y-location of gridcell lower left corner 0039 # Xp1 and Yp1 are effectively 1-D versions of grid variables XG,YG with 0040 # an extra data value at the eastern and northern ends of the domain. 0041 # 2-D fields (y,x) 0042 # XC: x-location of gridcell centers 0043 # YC: y-location of gridcell centers 0044 # dyG: grid spacing in y-dim (i.e. separation between corners) 0045 # rA: surface area of gridcells 0046 # 3-D fields (z,y,x) 0047 # HFacC: vertical fraction of cell which is ocean 0048 # See MITgcm users manual section 2.11 for additional MITgcm grid info 0049 0050 # Number of gridcells in x,y for full domain: 0051 Nx = grid.X.size 0052 Ny = grid.Y.size 0053 # out-of-the-box tutorial configuration is 62x62 0054 0055 0056 ########### load diagnostics ########### 0057 0058 # unit for temperature is degrees Celsius, velocity in m/s, 0059 # surface height in m, heat flux in W/m^2 0060 # see run output file available_diagnostics.log 0061 0062 # load statistical diagnostic output (set to monthly time-avg output) 0063 # only one output region is defined: global (the default) 0064 dynStDiag = xr.open_dataset('dynStDiag.nc') 0065 dynStDiag = dynStDiag.rename({'Zmd000015':'Z'}) 0066 # includes diagnostics: 0067 # TRELAX_ave: (time, region, depth); region=0 (global), depth=0 (surf-only) 0068 # THETA_lv_ave: (time, region, depth); region=0 (global) 0069 # THETA_lv_std: (time, region, depth); region=0 (global) 0070 0071 # load 2-D and 3-D variable diagnostic output, annual mean data 0072 surfDiag = xr.open_dataset('surfDiag.nc') 0073 # TRELAX: (time, depth, y, x); depth=0 (surface-only) 0074 # ETAN: (time, depth, y, x); depth=0 (surface-only) 0075 dynDiag = xr.open_dataset('dynDiag.nc') 0076 dynDiag = dynDiag.rename({'Zmd000015':'Z'}) 0077 # UVEL: (time, depth, y, x); x dim. is Nx+1, includes eastern edge 0078 # VVEL: (time, depth, y, x); y dim. is Ny+1, includes northern edge 0079 # THETA: (time, depth, y, x) 0080 0081 # Note the Z dimension above has a different name, we rename to the 0082 # standard name 'Z' which will make life easier when we do some 0083 # calculations using the raw data. This is because pkg/diagnostics 0084 # has an option to select specific levels of a 3-D variable to be output. 0085 # If 'levels' are selected in data.diagnostics, the following 0086 # additional statement would be needed: 0087 # dynDiag['Z'] = grid['Z'][dynDiag.diag_levels.astype(int)-1] 0088 0089 0090 ########### plot diagnostics ########### 0091 0092 # figure 4.6 - time series of global mean TRELAX and THETA by level 0093 # 0094 # plot THETA at MITgcm k levels 1,5,15 (SST, -305 m, -1705 m) 0095 klevs = [0, 4, 14] 0096 0097 # MITgcm time unit (dim='T') is seconds, so create new coordinate 0098 # for (dynStDiag) monthly time averages and convert into years. 0099 # Note that the MITgcm time array values correspond to time at the end 0100 # of the time-avg periods, i.e. subtract 1/24 to plot at mid-month. 0101 Tmon = (dynStDiag['T']/(86400*360) - 1/24).assign_attrs(units='years') 0102 # and repeat to create time series for annual mean time output, 0103 # subtract 0.5 to plot at mid-year. 0104 Tann = (surfDiag['T']/(86400*360) - 0.5).assign_attrs(units='years') 0105 0106 plt.figure(figsize=(16,10)) 0107 # global mean TRELAX 0108 plt.subplot(221) 0109 # we tell xarray to plot using this new abscissa Tmon (instead of T) 0110 # xarray is smart enough to ignore the singleton dimensions 0111 # for region and depth 0112 dynStDiag.TRELAX_ave.assign_coords(T=Tmon).plot(color='b', linewidth=4) 0113 plt.grid('both') 0114 plt.xlim(0, np.ceil(Tmon[-1])) 0115 plt.ylim(-400, 0) 0116 # Alternatively, a global mean area-weighted TRELAX (annual mean) 0117 # could be computed as follows, using HfacC[0,:,:], i.e. HfacC in 0118 # the surface layer, as a land-ocean mask, using xarray .where() 0119 # First, compute total surface area of ocean points: 0120 tot_ocean_area = (grid.rA.where(grid.HFacC[0,:,:]!=0)).sum(('Y', 'X')) 0121 # broadcasting with xarray is more flexible than basic numpy 0122 # because it matches axis name (not dependent on axis position) 0123 # grid area is broadcast across the time dimension below 0124 TRELAX_ave_ann = (surfDiag.TRELAX * grid.rA.where(grid.HFacC[0,:,:]!=0) 0125 ).sum(('Y', 'X')) / tot_ocean_area 0126 TRELAX_ave_ann.assign_coords(T=Tann).plot( 0127 color='m', linewidth=4, linestyle='dashed') 0128 plt.title('a) Net Heat Flux into Ocean (TRELAX_ave)') 0129 0130 plt.subplot(223) 0131 # mean THETA by level 0132 # Here is an example of allowing xarray to do even more of the work 0133 # and labeling for you, given selected levels, using 'hue' parameter 0134 THETAmon = dynStDiag.THETA_lv_ave.assign_coords(T=Tmon, Z=grid.RC) 0135 THETAmon.isel(Z=klevs).plot(hue='Z', linewidth=4) 0136 plt.grid('both') 0137 plt.title('b) Mean Potential Temp. by Level (THETA_lv_avg)') 0138 plt.xlim(0, np.ceil(Tmon[-1])) 0139 plt.ylim(0, 30); 0140 # Note a legend (of Z depths) was included automatically. 0141 # Specifying colors is not so simple however, either change 0142 # the default a priori, e.g. ax.set_prop_cycle(color=['r','g','c']) 0143 # or after the fact, e.g. lh[0].set_color('r') etc. 0144 0145 plt.subplot(224) 0146 # standard deviation of THETA by level 0147 THETAmon = dynStDiag.THETA_lv_std.assign_coords(T=Tmon, Z=grid.RC) 0148 THETAmon.isel(Z=klevs).plot(hue='Z', linewidth=4) 0149 plt.grid('both') 0150 plt.title('c) Std. Dev. Potential Temp. by Level (THETA_lv_std)') 0151 plt.xlim(0, np.ceil(Tmon[-1])) 0152 plt.ylim(0, 8) 0153 plt.show() 0154 0155 # figure 4.7 - 2-D plot of TRELAX and contours of free surface height 0156 # (ETAN) at simulation end ( endTime = 3110400000. is t=100 yrs). 0157 ## 0158 eta_masked = surfDiag.ETAN[-1,0,:,:].where(grid.HFacC[0,:,:]!=0) 0159 plt.figure(figsize=(10,8)) 0160 surfDiag.TRELAX[-1,0,:,:].plot(cmap='RdBu_r', vmin=-250, vmax=250) 0161 plt.xlim(0, 60) 0162 plt.ylim(15, 75) 0163 eta_masked.plot.contour(levels=np.arange(-.6,.7,.1), colors='k') 0164 plt.title('Free surface height (contours, CI .1 m) and ' 0165 'TRELAX (shading, $\mathregular{W/m^2}$)') 0166 plt.show() 0167 # By default, this uses a plotting routine similar to 0168 # pcolormesh to plot TRELAX. 0169 # Note that xarray uses the coordinates of TRELAX automatically from 0170 # the netcdf metadata! With axis labels! Even though the plotted TRELAX 0171 # DataArray has coordinates Y,X (cell centers) it is plotted correctly 0172 # (effectively shading cells between Yp1,Xp1 locations, which are the 0173 # lower left corners of grid cells, i.e. YG,XG). Also note we mask the 0174 # land values when contouring the free surface height. Alternatively, 0175 # one could make a smoother color plot using contourf: 0176 # surfDiag.TRELAX[-1,0,:,:].plot.contourf( 0177 # cmap='RdBu_r',levels=np.linspace(-250,250,1000)) 0178 0179 # figure 4.8 - barotropic streamfunction, plot at simulation end 0180 # (w/overlaid labeled contours) 0181 # 0182 # here is an example where the xarray broadcasting is superior: 0183 ubt = (dynDiag.UVEL * grid.drF).sum('Z') # depth-integrated u velocity 0184 psi = (-ubt * grid.dyG).cumsum('Y').pad(Y=(1,0),constant_values=0.) / 1E6 0185 # Note psi is computed and plotted at the grid cell corners which we 0186 # compute as dimensioned (Ny,Nx+1); as noted, UVEL contains an extra 0187 # data point in x, at the eastern edge. cumsum is done in y-direction. 0188 # We have a wall at southern boundary (i.e. no reentrant flow from 0189 # north). We need to add a row of zeros to specify psi(j=0). 0190 # xarray .pad() allows one to add a row at the top and/or bottom, 0191 # we just need the bottom row (j=0) padding the computed psi array. 0192 # Thus after padding psi is shape (dynDiag.UVEL['T'].size,Ny+1,Nx+1) 0193 # Finally, since psi is computed at grid cell corners, 0194 # we need to re-assign Yp1 coordinate to psi. 0195 # As an aside comment, if data manipulation with xarray still seems a 0196 # bit cumbersome, you are not mistaken... there are tools developed 0197 # such as xgcm (https://xgcm.readthedocs.io/en/latest/) specifically 0198 # for this purpose. 0199 # Note xxx.values extracts the numpy-array data from xarray 0200 # DataArray xxx; this is done in the plot statement below: 0201 0202 plt.figure(figsize=(10,8)) 0203 psi.isel(T=-1).assign_coords(Y=grid.Yp1.values).plot.contourf( 0204 levels=np.linspace(-30, 30, 13), cmap='RdYlBu_r') 0205 cs = psi.isel(T=-1).assign_coords(Y=grid.Yp1.values).plot.contour( 0206 levels=np.arange(-35, 40, 5), colors='k') 0207 plt.clabel(cs, fmt = '%.0f') 0208 plt.xlim(0, 60) 0209 plt.ylim(15, 75) 0210 plt.title('Barotropic Streamfunction (Sv)'); 0211 plt.show() 0212 0213 # figure 4.9 - potential temp at depth and xz slice, at simulation end 0214 # 0215 # plot THETA at MITgcm k=4 (220 m depth) and j=15 (28.5N) 0216 klev = 3 0217 jloc = 14 0218 0219 theta_masked = dynDiag.THETA[-1,klev,:,:].where(grid.HFacC[klev,:,:]!=0) 0220 plt.figure(figsize=(16,6)) 0221 plt.subplot(121) 0222 # again we use pcolor for this plan view, grabs coordinates automatically 0223 dynDiag.THETA[-1,klev,:,:].plot(cmap='coolwarm', vmin=0, vmax=30 ) 0224 # and overlay contours w/masked out boundary/land cells 0225 theta_masked.plot.contour(levels=np.linspace(0, 30, 16), colors='k') 0226 plt.xlim(0, 60) 0227 plt.ylim(15, 75) 0228 plt.title('a) THETA at %g m Depth ($\mathregular{^oC}$)' %grid.RC[klev]) 0229 0230 # For the xz slice, our limited vertical resolution makes for an ugly 0231 # pcolor plot, we'll shade using contour instead, providing the centers 0232 # of the vertical grid cells and cell centers in the x-dimension. 0233 # Also mask out land cells at the boundary, which results in slight 0234 # white space at domain edges. 0235 theta_masked = dynDiag.THETA[-1,:,jloc,:].where(grid.HFacC[:,jloc,:]!=0) 0236 plt.subplot(122) 0237 theta_masked.plot.contourf(levels=np.arange(0,30,.2), cmap='coolwarm') 0238 theta_masked.plot.contour(levels=np.arange(0,30,2), colors='k') 0239 plt.title('b) THETA at %gN ($\mathregular{^oC}$)' %grid.YC[jloc,0]) 0240 plt.xlim(0, 60) 0241 plt.ylim(-1800, 0) 0242 plt.show() 0243 # One approach to avoiding the white space at boundaries/top/bottom 0244 # is to copy neighboring tracer values to land cells prior to contouring 0245 # (and don't mask), and augment a row of z=0 data at the ocean surface 0246 # and a row at the ocean bottom. 0247 # (see tutorial Southern Ocean Reentrant Channel for an example) 0248 # The white space occurs because contour uses data at cell centers, with 0249 # values masked/undefined beyond the cell centers toward boundaries. 0250 0251 # To instead plot using pcolor: 0252 # theta_masked.assign_coords(Z=grid.RC.values).plot(cmap='coolwarm')
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated from https://github.com/MITgcm/MITgcm by the 2.2.1-MITgcm-0.1 LXR engine. The LXR team |