Skip to content

Commit b289705

Browse files
committed
Add sfc_flux_timeseries for diagnosing surface flux issues.
1 parent 265ae6b commit b289705

File tree

1 file changed

+283
-0
lines changed

1 file changed

+283
-0
lines changed

sfc_flux_timeseries.py

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
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

Comments
 (0)