|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +from os.path import join |
| 4 | + |
| 5 | +import numpy as np |
| 6 | +import matplotlib |
| 7 | +matplotlib.use('Agg') |
| 8 | +import matplotlib.pyplot as plt |
| 9 | +import netCDF4 as nc4 |
| 10 | + |
| 11 | +START_DAY = 1 |
| 12 | +END_DAY = 1 |
| 13 | +TIME_STEP = 1800 |
| 14 | +assert 86400 % TIME_STEP == 0, "cannot fit even number of time steps in day" |
| 15 | +times_per_day = 86400 // TIME_STEP |
| 16 | + |
| 17 | +CASE_NAMES = [ |
| 18 | + "timestep_ctrl" # SPS: REPLACE THIS |
| 19 | +] |
| 20 | +SHORT_CASE_NAMES = [ |
| 21 | + "CTRL", |
| 22 | +] |
| 23 | +STYLES = { |
| 24 | + "CTRL": ('k', '-'), |
| 25 | +} |
| 26 | + |
| 27 | +OUTPUT_ROOT = "/p/lustre2/santos36/ACME" # SPS: REPLACE THIS |
| 28 | + |
| 29 | +OUTPUT_DIRS = ["{}/{}/run/".format(OUTPUT_ROOT, case) |
| 30 | + for case in CASE_NAMES] |
| 31 | + |
| 32 | +suffix = "_fluxdiag" |
| 33 | + |
| 34 | +log_file = open("sfc_timeseries_log{}.txt".format(suffix), 'w') |
| 35 | + |
| 36 | +out_file_template = "{}.cam.h0.0001-01-{}-{}.nc" |
| 37 | + |
| 38 | +def day_str(day): |
| 39 | + "Given an integer day, return the 2-digit day string used for file names." |
| 40 | + return "{:02d}".format(day) |
| 41 | + |
| 42 | +def time_str(time): |
| 43 | + "Given an integer time in seconds, return the 5-digit string used in file names." |
| 44 | + return "{:05d}".format(time) |
| 45 | + |
| 46 | +def get_out_file_name(icase, day, time): |
| 47 | + """Given a case index, day, and time, return CAM header file name.""" |
| 48 | + return join(OUTPUT_DIRS[icase], |
| 49 | + out_file_template.format(CASE_NAMES[icase], |
| 50 | + day_str(day), time_str(time))) |
| 51 | + |
| 52 | +first_file_name = get_out_file_name(0, 1, 0) |
| 53 | +first_file = nc4.Dataset(first_file_name, 'r') |
| 54 | +ncol = len(first_file.dimensions['ncol']) |
| 55 | +nlev = len(first_file.dimensions['lev']) |
| 56 | +lat = first_file['lat'][:] |
| 57 | +lon = first_file['lon'][:] |
| 58 | +lev = first_file['lev'][:] |
| 59 | + |
| 60 | +# Find columns in box over South America. |
| 61 | +min_lat = -20. |
| 62 | +max_lat = 10. |
| 63 | +min_lon = 280. |
| 64 | +max_lon = 315. |
| 65 | +column_set = set() |
| 66 | +for i in range(ncol): |
| 67 | + if min_lon <= lon[i] <= max_lon and min_lat <= lat[i] <= max_lat: |
| 68 | + column_set.add(i) |
| 69 | + |
| 70 | +first_file.close() |
| 71 | + |
| 72 | +ncol_sa = len(column_set) |
| 73 | + |
| 74 | +# Plot wind issues over SA. |
| 75 | +from mpl_toolkits import basemap |
| 76 | +bmap = basemap.Basemap(lon_0=180.) |
| 77 | + |
| 78 | +#TIME_CHECK = 57600 |
| 79 | +#TIME_CHECK = 79200 |
| 80 | +TIME_CHECK = 0 |
| 81 | +#TIME_CHECK = 54000 |
| 82 | +#TIME_CHECK = 12*3600 |
| 83 | + |
| 84 | +DAY_CHECK = 2 |
| 85 | + |
| 86 | +CASE_CHECK = 0 |
| 87 | + |
| 88 | +LEVEL = nlev - 1 |
| 89 | + |
| 90 | +case = SHORT_CASE_NAMES[CASE_CHECK] |
| 91 | +time_increment = TIME_STEP |
| 92 | + |
| 93 | +plot_box = [min_lon, max_lon, min_lat, max_lat] |
| 94 | +#plot_box = [285., 297., 1., 10.] |
| 95 | + |
| 96 | +mid_file_name = get_out_file_name(CASE_CHECK, DAY_CHECK, TIME_CHECK) |
| 97 | +mid_file = nc4.Dataset(mid_file_name, 'r') |
| 98 | + |
| 99 | +u1 = mid_file['U'][0,LEVEL,:] |
| 100 | +v1 = mid_file['V'][0,LEVEL,:] |
| 101 | + |
| 102 | +plt.quiver(lon, lat, u1, v1, |
| 103 | + scale=100., scale_units='height', angles='xy') |
| 104 | +bmap.drawcoastlines() |
| 105 | +plt.axis(plot_box) |
| 106 | +plt.savefig("UV_arrow1{}.png".format(suffix)) |
| 107 | +plt.close() |
| 108 | + |
| 109 | +mid_file.close() |
| 110 | + |
| 111 | +mid_file_name = get_out_file_name(CASE_CHECK, DAY_CHECK, TIME_CHECK + time_increment) |
| 112 | +mid_file = nc4.Dataset(mid_file_name, 'r') |
| 113 | + |
| 114 | +u2 = mid_file['U'][0,LEVEL,:] |
| 115 | +v2 = mid_file['V'][0,LEVEL,:] |
| 116 | + |
| 117 | +plt.quiver(lon, lat, u2, v2, |
| 118 | + scale=100., scale_units='height', angles='xy') |
| 119 | +bmap.drawcoastlines() |
| 120 | +plt.axis(plot_box) |
| 121 | +plt.savefig("UV_arrow2{}.png".format(suffix)) |
| 122 | +plt.close() |
| 123 | + |
| 124 | +mid_file.close() |
| 125 | + |
| 126 | +mid_file_name = get_out_file_name(CASE_CHECK, DAY_CHECK, TIME_CHECK + 2*time_increment) |
| 127 | +mid_file = nc4.Dataset(mid_file_name, 'r') |
| 128 | + |
| 129 | +u3 = mid_file['U'][0,LEVEL,:] |
| 130 | +v3 = mid_file['V'][0,LEVEL,:] |
| 131 | + |
| 132 | +plt.quiver(lon, lat, u3, v3, |
| 133 | + scale=100., scale_units='height', angles='xy') |
| 134 | +bmap.drawcoastlines() |
| 135 | +plt.axis(plot_box) |
| 136 | +plt.savefig("UV_arrow3{}.png".format(suffix)) |
| 137 | +plt.close() |
| 138 | + |
| 139 | +mid_file.close() |
| 140 | + |
| 141 | +ud2 = u1 - 2*u2 + u3 |
| 142 | +vd2 = v1 - 2*v2 + v3 |
| 143 | + |
| 144 | +# Find column with large oscillations in wind speed |
| 145 | +# To force focus on a particular column, just set the number here. |
| 146 | +ifocus = -1 |
| 147 | + |
| 148 | +if ifocus == -1: |
| 149 | + print("Searching for oscillatory point.", file=log_file, flush=True) |
| 150 | + maxd2 = 0. |
| 151 | + for icol in column_set: |
| 152 | + d2 = np.sqrt(ud2[icol]*ud2[icol] + vd2[icol]*vd2[icol]) |
| 153 | + if d2 > maxd2: |
| 154 | + maxd2 = d2 |
| 155 | + ifocus = icol |
| 156 | + |
| 157 | + assert ifocus >= 0, "no focus column found" |
| 158 | + |
| 159 | +print("Worst oscillations at column ", ifocus, " at lat = ", |
| 160 | + lat[ifocus], ", lon = ", lon[ifocus], file=log_file, flush=True) |
| 161 | + |
| 162 | +plt.scatter(lon[ifocus], lat[ifocus]) |
| 163 | + |
| 164 | +plt.quiver(lon, lat, ud2, vd2, |
| 165 | + scale=100., scale_units='height', angles='xy') |
| 166 | +bmap.drawcoastlines() |
| 167 | +plt.axis(plot_box) |
| 168 | +plt.savefig("UV_D2_arrow{}.png".format(suffix)) |
| 169 | +plt.close() |
| 170 | + |
| 171 | +variables = [ |
| 172 | + {'name': 'RELHUM', 'units': r'%', 'ndim': 2}, |
| 173 | + {'name': 'CLDLIQ', 'units': r'$g/kg$', 'ndim': 2, 'scale': 1000.}, |
| 174 | + {'name': 'LHFLX', 'units': r'$W/m^2$', 'ndim': 1}, |
| 175 | + {'name': 'SHFLX', 'units': r'$W/m^2$', 'ndim': 1}, |
| 176 | + {'name': 'TS', 'units': r'$K$', 'ndim': 1}, |
| 177 | + {'name': 'T', 'units': r'$K$', 'ndim': 2}, |
| 178 | + {'name': 'Q', 'units': r'$g/kg$', 'ndim': 2, 'scale': 1000.}, |
| 179 | + {'name': 'U', 'units': r'$m/s$', 'ndim': 2}, |
| 180 | + {'name': 'V', 'units': r'$m/s$', 'ndim': 2}, |
| 181 | + {'name': 'U10', 'units': r'$m/s$', 'ndim': 1}, |
| 182 | + {'name': 'PS', 'units': r'$Pa$', 'ndim': 1}, |
| 183 | + {'name': 'TAUX', 'units': r'$Pa$', 'ndim': 1}, |
| 184 | + {'name': 'TAUY', 'units': r'$Pa$', 'ndim': 1}, |
| 185 | + {'name': 'TAUGWX', 'units': r'$Pa$', 'ndim': 1}, |
| 186 | + {'name': 'TAUGWY', 'units': r'$Pa$', 'ndim': 1}, |
| 187 | +] |
| 188 | + |
| 189 | +derived_variables = [ |
| 190 | + {'name': 'TAU', 'units': r'$Pa$', 'ndim': 1, |
| 191 | + 'depends': ['TAUX', 'TAUY'], |
| 192 | + 'calc': (lambda var_dict: np.sqrt(var_dict['TAUX']**2 + var_dict['TAUY']**2)), |
| 193 | + }, |
| 194 | +] |
| 195 | + |
| 196 | +# Check that dependencies are satisfied. |
| 197 | +var_names = [var['name'] for var in variables] |
| 198 | +for derived in derived_variables: |
| 199 | + for depend in derived['depends']: |
| 200 | + assert depend in var_names |
| 201 | + |
| 202 | +ncases = len(CASE_NAMES) |
| 203 | +ntimes = (END_DAY - START_DAY + 1) * times_per_day + 1 |
| 204 | + |
| 205 | +out_vars = {} |
| 206 | +for icase in range(ncases): |
| 207 | + case = SHORT_CASE_NAMES[icase] |
| 208 | + print("Processing case ", case) |
| 209 | + case_times_per_day = times_per_day |
| 210 | + case_ntimes = ntimes |
| 211 | + case_time_step = TIME_STEP |
| 212 | + out_vars[case] = {} |
| 213 | + for var in variables: |
| 214 | + out_vars[case][var['name']] = np.zeros((case_ntimes,)) |
| 215 | + ita = 0 |
| 216 | + for day in range(START_DAY, END_DAY+1): |
| 217 | + for it in range(case_times_per_day): |
| 218 | + out_file_name = get_out_file_name(icase, day, it*case_time_step) |
| 219 | + out_file = nc4.Dataset(out_file_name, 'r') |
| 220 | + for var in variables: |
| 221 | + varname = var['name'] |
| 222 | + ndim = var['ndim'] |
| 223 | + if ndim == 1: |
| 224 | + out_vars[case][varname][ita] = out_file[varname][0,ifocus] |
| 225 | + elif ndim == 2: |
| 226 | + out_vars[case][varname][ita] = out_file[varname][0,LEVEL,ifocus] |
| 227 | + else: |
| 228 | + assert False, \ |
| 229 | + "don't know what to do with ndim={}".format(ndim) |
| 230 | + out_file.close() |
| 231 | + ita += 1 |
| 232 | + # Last file is 0-th time of the next day. |
| 233 | + out_file_name = get_out_file_name(icase, END_DAY+1, 0) |
| 234 | + out_file = nc4.Dataset(out_file_name, 'r') |
| 235 | + for var in variables: |
| 236 | + varname = var['name'] |
| 237 | + ndim = var['ndim'] |
| 238 | + if ndim == 1: |
| 239 | + out_vars[case][varname][ita] = out_file[varname][0,ifocus] |
| 240 | + elif ndim == 2: |
| 241 | + out_vars[case][varname][ita] = out_file[varname][0,LEVEL,ifocus] |
| 242 | + else: |
| 243 | + assert False, \ |
| 244 | + "don't know what to do with ndim={}".format(ndim) |
| 245 | + out_file.close() |
| 246 | + # Scale variables |
| 247 | + for var in variables: |
| 248 | + if 'scale' in var: |
| 249 | + out_vars[case][var['name']] *= var['scale'] |
| 250 | + # Calculate derived variables |
| 251 | + for derived in derived_variables: |
| 252 | + out_vars[case][derived['name']] = derived['calc'](out_vars[case]) |
| 253 | + |
| 254 | +# Assumes Venezuelan time. |
| 255 | +TIME_OFFSET = 4. |
| 256 | +times = np.linspace(0., TIME_STEP*(ntimes - 1) / 3600., ntimes) - TIME_OFFSET |
| 257 | +for var in variables + derived_variables: |
| 258 | + name = var['name'] |
| 259 | + for icase in range(ncases): |
| 260 | + case = SHORT_CASE_NAMES[icase] |
| 261 | + case_times = times |
| 262 | + plt.plot(case_times, out_vars[case][name], color=STYLES[case][0], |
| 263 | + linestyle=STYLES[case][1]) |
| 264 | + plt.axis('tight') |
| 265 | + plt.xlabel("Time (UTC-4:00)") |
| 266 | + # Bad hard-coding! |
| 267 | + if START_DAY - END_DAY + 1 == 1: |
| 268 | + plt.xticks(np.linspace(-3., 18., 8), |
| 269 | + ["2100", "0000", "0300", "0600", "0900", "1200", "1500", "1800"]) |
| 270 | + elif START_DAY - END_DAY + 1 == 2: |
| 271 | + plt.xticks(np.linspace(-3., 42., 16), |
| 272 | + ["2100", "0000", "0300", "0600", "0900", "1200", "1500", "1800", |
| 273 | + "2100", "0000", "0300", "0600", "0900", "1200", "1500", "1800"]) |
| 274 | + plt.grid(True) |
| 275 | + if 'display' in var: |
| 276 | + dname = var['display'] |
| 277 | + else: |
| 278 | + dname = name |
| 279 | + plt.ylabel("{} ({})".format(dname, var['units'])) |
| 280 | + plt.savefig("{}_time{}.png".format(name, suffix)) |
| 281 | + plt.close() |
| 282 | + |
| 283 | +log_file.close() |
0 commit comments