Skip to content

Commit

Permalink
add new stepwise CAPE & CIN plot to 'full sounding plots'; enhance re…
Browse files Browse the repository at this point in the history
…adability of all 0-3km axes on 'full sounding plots'
  • Loading branch information
kylejgillett committed Dec 29, 2024
1 parent f39fc5f commit 2c69ecc
Show file tree
Hide file tree
Showing 13 changed files with 109 additions and 59 deletions.
Binary file modified src/sounderpy/__pycache__/acars_data.cpython-312.pyc
Binary file not shown.
Binary file modified src/sounderpy/__pycache__/bufkit_data.cpython-312.pyc
Binary file not shown.
Binary file modified src/sounderpy/__pycache__/calc.cpython-312.pyc
Binary file not shown.
Binary file modified src/sounderpy/__pycache__/model_data.cpython-312.pyc
Binary file not shown.
Binary file modified src/sounderpy/__pycache__/obs_data.cpython-312.pyc
Binary file not shown.
Binary file modified src/sounderpy/__pycache__/plot.cpython-312.pyc
Binary file not shown.
Binary file modified src/sounderpy/__pycache__/sounderpy.cpython-312.pyc
Binary file not shown.
Binary file modified src/sounderpy/__pycache__/utils.cpython-312.pyc
Binary file not shown.
2 changes: 1 addition & 1 deletion src/sounderpy/acars_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
Purpose of module:
House function for loading and parsing ACARS profiles and
House functions for loading and parsing ACARS profiles and
ACARS profile data. Functions here are explicitly called by
the user.
Expand Down
2 changes: 1 addition & 1 deletion src/sounderpy/bufkit_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
Purpose of module:
House function for loading and parsing BUFKIT model forecast data.
House functions for loading and parsing BUFKIT model forecast data.
Functions here are referenced by sounderpy.py
Expand Down
5 changes: 5 additions & 0 deletions src/sounderpy/cm1_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ def make_cm1_profile(filename, meta_data_dict):
3] # cm1 input has no surface hgt, use quadratic extrapolation to estimate sfc z
else:
elev = meta_data_dict['elev']

full_z[0] = elev

full_th[0] = float(sfc_th)
full_qv[0] = float(sfc_qv) / 1000. # convert to g/kg
full_u[0] = 1.75 * full_u[1] - full_u[2] + 0.25 * full_u[
Expand Down Expand Up @@ -123,6 +125,9 @@ def make_cm1_profile(filename, meta_data_dict):
check_sfc_hgt(full_z)
check_latlon(meta_data_dict)

# convert hgt values to include elevation
full_z[1:] = full_z[1:] + elev

#############################################
# CONSTRUCT CLEAN_DATA DICTIONARY
#############################################
Expand Down
155 changes: 100 additions & 55 deletions src/sounderpy/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -1068,7 +1068,7 @@ def sm_str(storm_motion, speeds, directions):
#################################################################
# PLOT AXIS/LOC
strmws_ax = plt.axes((0.945, -0.13, 0.065, 0.23))
plt.figtext(0.978, 0.07, f'SW ζ (%)', color=gen_txt_clr, weight='bold', fontsize=12, ha='center', alpha=0.7)
plt.figtext(0.978, 0.07, f'Streamwiseness\n of ζ (%)', color=gen_txt_clr, weight='bold', fontsize=10, ha='center', alpha=0.9)
strmws_ax.spines["top"].set_color(brdr_clr)
strmws_ax.spines["left"].set_color(brdr_clr)
strmws_ax.spines["right"].set_color(brdr_clr)
Expand All @@ -1086,20 +1086,23 @@ def sm_str(storm_motion, speeds, directions):

#XTICKS
strmws_ax.set_xlim(40, 102)
strmws_ax.set_xticks([50, 90])
strmws_ax.set_xticklabels([50, 90], weight='bold', alpha=0.5, fontstyle='italic', color=gen_txt_clr)
strmws_ax.set_xticks([50, 70, 90])
strmws_ax.set_xticklabels([50, 70, 90], weight='bold', alpha=0.5, fontstyle='italic', color=gen_txt_clr)
strmws_ax.set_xlabel(' ')

#HGT LABLES
strmws_ax.text(47, 502 , '0.5km', fontsize=8, alpha=0.6, color=gen_txt_clr)
strmws_ax.text(47, 1002, '1.0km', fontsize=8, alpha=0.6, color=gen_txt_clr)
strmws_ax.text(47, 1502, '1.5km', fontsize=8, alpha=0.6, color=gen_txt_clr)
strmws_ax.text(47, 2002, '2.0km', fontsize=8, alpha=0.6, color=gen_txt_clr)
strmws_ax.text(47, 2502, '2.5km', fontsize=8, alpha=0.6, color=gen_txt_clr)
strmws_ax.text(47, 502 , '.5 km', fontsize=10, weight='bold', alpha=1, color=gen_txt_clr)
strmws_ax.text(47, 1002, '1 km', fontsize=10, weight='bold', alpha=1, color=gen_txt_clr)
strmws_ax.text(47, 1502, '1.5 km', fontsize=10, weight='bold', alpha=1, color=gen_txt_clr)
strmws_ax.text(47, 2002, '2 km', fontsize=10, weight='bold', alpha=1, color=gen_txt_clr)
#strmws_ax.text(47, 2502, '2.5 km', fontsize=9, weight='bold', alpha=0.9, color=gen_txt_clr)

if ma.is_masked(kinem['sm_u']) == False:
plt.plot(kinem['swv_perc'][0:11], intrp['zINTRP'][0:11], color=hodo_color[0], lw=3, clip_on=True)
plt.plot(kinem['swv_perc'][10:30], intrp['zINTRP'][10:30], color=hodo_color[1], lw=3, clip_on=True)
strmws_ax.plot(kinem['swv_perc'][0:11], intrp['zINTRP'][0:11], color=hodo_color[0], lw=3, clip_on=True)
strmws_ax.plot(kinem['swv_perc'][10:31], intrp['zINTRP'][10:31], color=hodo_color[1], lw=3, clip_on=True)
strmws_ax.fill_betweenx(intrp['zINTRP'][0:31], kinem['swv_perc'][0:31],
color='cornflowerblue', linewidth=0, alpha=0.2, clip_on=True)


if ma.is_masked(kinem['eil_z'][0]) == False:
strmws_ax.fill_between(x=(40,102), y1=kinem['eil_z'][0], y2=kinem['eil_z'][1], color='lightblue', alpha=0.2)
Expand All @@ -1114,7 +1117,8 @@ def sm_str(storm_motion, speeds, directions):
### VORTICITY W/HGT ###
#################################################################
vort_ax = plt.axes((1.01, -0.13, 0.066, 0.23))
plt.figtext(1.045, 0.06, f'ζₜₒₜ & ζSW\n(/sec)', color=gen_txt_clr, weight='bold', fontsize=12, ha='center', alpha=0.7)
plt.figtext(1.043, 0.05, f'Total ζ &\n Streamwise ζ \n(/sec)', color=gen_txt_clr, weight='bold',
fontsize=12, ha='center', alpha=0.9)
vort_ax.spines["top"].set_color(brdr_clr)
vort_ax.spines["left"].set_color(brdr_clr)
vort_ax.spines["right"].set_color(brdr_clr)
Expand All @@ -1133,19 +1137,21 @@ def sm_str(storm_motion, speeds, directions):
if ma.is_masked(kinem['sm_u']) == False:
#XTICKS
vort_ax.set_xlabel(' ')
vort_max = kinem['vort'][0:30].max()+0.005
vort_min = kinem['vort'][0:30].min()-0.005

vort_ax.set_xlim(vort_min-0.002, vort_max+0.002)
vort_ax.set_xticks([(vort_min+0.005),(vort_max-0.005)])
vort_ax.set_xticklabels([(np.round(vort_min+0.002,2)),(np.round(vort_max-0.002,2))],
vort_ax.set_xlim(0, 0.06)
vort_ax.set_xticks([.01, .03, .05])
vort_ax.set_xticklabels([".01", ".03", ".05"],
weight='bold', alpha=0.5, fontstyle='italic', color=gen_txt_clr)

vort_ax.plot(kinem['swv'][0:30], intrp['zINTRP'][0:30], color='orange', linewidth=3, alpha=0.8, label='SW ζ')
vort_ax.plot(kinem['vort'][0:30], intrp['zINTRP'][0:30], color=gen_txt_clr, linewidth=4, alpha=0.4, label='Total ζ')
vort_ax.plot(kinem['swv'][0:31], intrp['zINTRP'][0:31], color='orange', linewidth=3, alpha=0.8, label='SW ζ')
vort_ax.plot(kinem['vort'][0:31], intrp['zINTRP'][0:31], color=gen_txt_clr, linewidth=4, alpha=0.4, label='Total ζ')
vort_ax.fill_betweenx(intrp['zINTRP'][0:31], kinem['vort'][0:31], where=(kinem['vort'][0:31] > kinem['swv'][0:31]),
color=gen_txt_clr, linewidth=0, alpha=0.1, clip_on=True)
vort_ax.fill_betweenx(intrp['zINTRP'][0:31], kinem['swv'][0:31],
color='orange', linewidth=0, alpha=0.2, clip_on=True)

if ma.is_masked(kinem['eil_z'][0]) == False:
vort_ax.fill_between(x=(vort_min-0.002, vort_max+0.002), y1=kinem['eil_z'][0],
vort_ax.fill_between(x=(0, 0.065), y1=kinem['eil_z'][0],
y2=kinem['eil_z'][1], color='lightblue', alpha=0.2)
else:
warnings.warn("Total Vorticity could not be plotted (no valid storm motion/not enough data)", Warning)
Expand All @@ -1159,7 +1165,7 @@ def sm_str(storm_motion, speeds, directions):
### SRW W/HGT ###
#################################################################
wind_ax = plt.axes((1.0755, -0.13, 0.066, 0.23))
plt.figtext(1.108, 0.06, f'SR Wind\n(kts)', weight='bold', color=gen_txt_clr, fontsize=12, ha='center', alpha=0.7)
plt.figtext(1.108, 0.06, f'Storm Relative\nWind (kts)', weight='bold', color=gen_txt_clr, fontsize=12, ha='center', alpha=0.9)
wind_ax.spines["top"].set_color(brdr_clr)
wind_ax.spines["left"].set_color(brdr_clr)
wind_ax.spines["right"].set_color(brdr_clr)
Expand All @@ -1176,23 +1182,30 @@ def sm_str(storm_motion, speeds, directions):
wind_ax.tick_params(axis='y', length = 0)
wind_ax.tick_params(axis="x",direction="in", pad=-12)

wind_ax.text(40, thermo['sb_lcl_z'], '-LCL-', fontsize=10, weight='bold',
alpha=1, color=gen_txt_clr, clip_on=True)
if thermo['mu_lfc_z'] < 2500:
wind_ax.text(40, thermo['mu_lfc_z'], '-LFC-', fontsize=10, weight='bold',
alpha=1, color=gen_txt_clr, clip_on=True)


if ma.is_masked(kinem['sm_u']) == False:
#XTICKS
wind_max = kinem['srw'][0:30].max()+1
wind_min = kinem['srw'][0:30].min()-1
wind_ax.set_xlim(wind_min-5, wind_max+5)
wind_ax.set_xticks([(wind_min)+2, (wind_max)-2])
wind_ax.set_xticklabels([(int(wind_min)+2), (int(wind_max)-2)], weight='bold',
wind_ax.set_xlim(10, 50)
wind_ax.set_xticks([20, 30, 40])
wind_ax.set_xticklabels([20, 30, 40], weight='bold',
alpha=0.5, fontstyle='italic', color=gen_txt_clr)

#PLOT SR WIND
wind_ax.plot(kinem['srw'][0:11], intrp['zINTRP'][0:11], color=hodo_color[0], clip_on=True,
linewidth=3, alpha=0.8, label='0-1 SR Wind')
wind_ax.plot(kinem['srw'][10:30], intrp['zINTRP'][10:30], color=hodo_color[1], clip_on=True,
wind_ax.plot(kinem['srw'][10:31], intrp['zINTRP'][10:31], color=hodo_color[1], clip_on=True,
linewidth=3, alpha=0.8, label='1-3 SR Wind')
wind_ax.fill_betweenx(intrp['zINTRP'][0:31], kinem['srw'][0:31],
color='cornflowerblue', linewidth=0, alpha=0.2, clip_on=True)

if ma.is_masked(kinem['eil_z'][0]) == False:
wind_ax.fill_between(x=(wind_min-5, wind_max+5), y1=kinem['eil_z'][0], y2=kinem['eil_z'][1],
wind_ax.fill_between(x=(5, 55), y1=kinem['eil_z'][0], y2=kinem['eil_z'][1],
color='lightblue', alpha=0.2)
else:
warnings.warn("Storm Relative Wind could not be plotted (no valid storm motion/not enough data)", Warning)
Expand All @@ -1209,16 +1222,16 @@ def sm_str(storm_motion, speeds, directions):
#############################################################
#PLOT AXES/LOC
theta_ax = plt.axes((1.141, -0.13, 0.0653, 0.23))
plt.figtext(1.175, 0.06, f'Theta-e &\nTheta (K)', weight='bold', color=gen_txt_clr, fontsize=12, ha='center', alpha=0.7)
plt.figtext(1.175, 0.06, f'Theta-e &\nTheta (K)', weight='bold', color=gen_txt_clr, fontsize=12, ha='center', alpha=0.9)
theta_ax.spines["top"].set_color(brdr_clr)
theta_ax.spines["left"].set_color(brdr_clr)
theta_ax.spines["right"].set_color(brdr_clr)
theta_ax.spines["bottom"].set_color(brdr_clr)
theta_ax.spines["bottom"].set_color(brdr_clr)
theta_ax.set_facecolor(bckgrnd_clr)

maxtheta = intrp['thetaeINTRP'][0:30].max()
mintheta = intrp['thetaINTRP'][0:30].min()
maxtheta = intrp['thetaeINTRP'][0:31].max()
mintheta = intrp['thetaINTRP'][0:31].min()

#YTICKS
theta_ax.set_ylim(intrp['zINTRP'][0], 3000)
Expand Down Expand Up @@ -1247,46 +1260,77 @@ def sm_str(storm_motion, speeds, directions):
### CIN & 3CAPE W/HGT ###
#############################################################
cin_color, cape_color = 'teal', 'tab:orange'
#PLOT AXES/LOC

mincin = intrp['cin_profileINTRP'][0:31].min()
max3cape = intrp['3cape_profileINTRP'][0:31].max()

# PLOT AXES/LOC
inflow_ax = plt.axes((1.141, -0.13, 0.0653, 0.23))
plt.figtext(1.175, 0.06, f'CIN & 3CAPE\n(J/kg)', weight='bold', color=gen_txt_clr, fontsize=12, ha='center', alpha=0.7)
plt.figtext(1.173, 0.05, f'Stepwise\n CIN & CAPE\n (J/kg)', weight='bold',
color=gen_txt_clr, fontsize=12, ha='center', alpha=0.9)
inflow_ax.spines["top"].set_color(brdr_clr)
inflow_ax.spines["left"].set_color(brdr_clr)
inflow_ax.spines["right"].set_color(brdr_clr)
inflow_ax.spines["bottom"].set_color(brdr_clr)
inflow_ax.spines["bottom"].set_color(brdr_clr)
inflow_ax.set_facecolor(bckgrnd_clr)
inflow_ax.set_facecolor(bckgrnd_clr)

mincin = intrp['cin_profileINTRP'][0:30].min()
max3cape = intrp['3cape_profileINTRP'][0:30].max()
### AXIS 1 -- CIN ###

#YTICKS
# YAXIS PARAMS AX1 & AX2
inflow_ax.set_ylim(intrp['zINTRP'][0], 3000)
inflow_ax.set_yticklabels([])
plt.ylabel(' ')
inflow_ax.tick_params(axis='y', length = 0)
inflow_ax.set_ylabel(' ')
inflow_ax.tick_params(axis='y', length=0)
inflow_ax.grid(True, axis='y')
inflow_ax.axvline(0, linestyle=':', alpha=.5)

#XTICKS
inflow_ax.set_xlim(mincin - 5, max3cape + 5)
inflow_ax.set_xticks([mincin, max3cape])
inflow_ax.set_xticklabels([int(mincin), int(max3cape)], weight='bold', alpha=0.5, fontstyle='italic', color=gen_txt_clr)

inflow_ax.tick_params(axis="x", direction="in", pad=-12)
inflow_ax.axvline(0, linestyle=':', alpha=.8)

# XAXIS PARAMS AX1
inflow_ax.set_xlim(-300, 300)
inflow_ax.set_xticks([-200, -100])
inflow_ax.set_xticklabels([-200, -100], weight='bold', alpha=0.5, rotation=60,
fontstyle='italic', color=gen_txt_clr, zorder=10)
inflow_ax.tick_params(axis="x", direction="in", pad=-25)
inflow_ax.set_xlabel(' ')
plt.xticks(rotation=45)

#PLOT CIN / 3CAPE VS HGT
plt.fill_betweenx(intrp['zINTRP'], intrp['cin_profileINTRP'], color=cin_color, linewidth=0, alpha=0.5, clip_on=True)
plt.fill_betweenx(intrp['zINTRP'], intrp['3cape_profileINTRP'], color=cape_color, linewidth=0, alpha=0.8, clip_on=True)
# PLOT CIN VS HGT
inflow_ax.fill_betweenx(intrp['zINTRP'], intrp['cin_profileINTRP'],
color=cin_color, linewidth=0, alpha=0.5, clip_on=True)

if ma.is_masked(kinem['eil_z'][0]) == False:
inflow_ax.fill_between(x=(mincin - 5, max3cape + 5), y1=kinem['eil_z'][0],
y2=kinem['eil_z'][1], color='lightblue', alpha=0.2)
#################################################################

inflow_ax.fill_between(x=(-305, 305), y1=kinem['eil_z'][0],
y2=kinem['eil_z'][1], color='lightblue', alpha=0.2)

### AXIS 2 -- CAPE ###
inflow_ax2 = inflow_ax.twiny()
inflow_ax2.spines["top"].set_color(brdr_clr)
inflow_ax2.spines["left"].set_color(brdr_clr)
inflow_ax2.spines["right"].set_color(brdr_clr)
inflow_ax2.spines["bottom"].set_color(brdr_clr)
inflow_ax2.set_facecolor(bckgrnd_clr)

# XAXIS PARAMS AX2
inflow_ax2.set_xlim(-3000, 3000)
inflow_ax2.set_xticks([0, 1000, 2000])
inflow_ax2.set_xticklabels(['0 ', '1k', '2k'], weight='bold', alpha=0.5,
fontstyle='italic', color=gen_txt_clr, zorder=10)
inflow_ax2.xaxis.set_ticks_position('bottom')
inflow_ax2.tick_params(axis="x", direction="in", pad=-20)
inflow_ax2.set_xlabel(' ')
plt.xticks(rotation=45)

# YAXIS PARAMS AX2
inflow_ax2.set_ylim(intrp['zINTRP'][0], 3000)
inflow_ax2.set_yticklabels([])
inflow_ax2.set_ylabel(' ')
inflow_ax2.tick_params(axis="y", length=0)
inflow_ax2.grid(True, axis='y')

# PLOT 3CAPE VS HGT
inflow_ax2.fill_betweenx(intrp['zINTRP'], intrp['cape_profileINTRP'],
color=cape_color, linewidth=0, alpha=0.8, clip_on=True)
#############################################################


#########################################################################
############################## TEXT PLOTS ###############################
#########################################################################
Expand Down Expand Up @@ -1962,6 +2006,7 @@ def sm_str(storm_motion, speeds, directions):
vort_ax.set_yticklabels([])
vort_ax.set_ylabel(' ')
vort_ax.tick_params(axis="x",direction="in", pad=-12)


if ma.is_masked(kinem['sm_u']) == False:
#XTICKS
Expand Down
4 changes: 2 additions & 2 deletions src/sounderpy/sounderpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
THIS RELEASE
-------
Version: 3.0.6 | December 2024
Version: 3.0.7 | December 2024
DOCUMENTATION
Expand All @@ -50,7 +50,7 @@
citation_text = f"""
## ---------------------------------- SOUNDERPY ----------------------------------- ##
## Vertical Profile Data Retrieval and Analysis Tool For Python ##
## v3.0.6 | Dec 2024 | (C) Kyle J Gillett ##
## v3.0.7dev | Dec 2024 | (C) Kyle J Gillett ##
## Docs: https://kylejgillett.github.io/sounderpy/ ##
## --------------------- THANK YOU FOR USING THIS PACKAGE! ------------------------ ##
"""
Expand Down

0 comments on commit 2c69ecc

Please sign in to comment.