-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add V0 Ozone Diagnostics #285
Conversation
Adds in V0 of the Ozone diagnostics package
Addresses #281 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the work @shawnusaf, this is great to have. I just have some small changes for you to fix after which should be good to go.
The biggest change requested is the move to using geocat for the hybrid to pressure interpolation. I have the updated code in the review, but let me know if you have any questions about it.
Let me know when you get the changes in and well move forward, thanks!
Functions | ||
--------- | ||
ozone_diagnostics(adfobj) | ||
use ADF object to make maps | ||
|
||
define_regions(InputRegion) | ||
|
||
define_stations(InputStation) | ||
|
||
open_process_sonde_data_simone(obsdir) | ||
|
||
Subplot_O3(ax,X0,Y0,X1,Model0,Model1,SondeMean,SondeLow,SondeHigh,PLevel,plottype,case_base,case_test,ylim,Quadrant,Compare_Obs) | ||
|
||
Plot_Seasonal_Cycle_Profile(X0,X01,Y0,X1,X11,Y1,X2,X22,Y2,X3,X33,Y3,SondeMean0,SondeMean1,SondeMean2,SondeMean3,SondeLow0,SondeLow1,SondeLow2,SondeLow3,SondeHigh0,SondeHigh1,SondeHigh2,SondeHigh3,Model0I,Model01I,Model1I,Model11I,Model2I,Model21I,Model3I,Model31I,ylim0,ylim1,ylim2,ylim3,PLevel0,PLevel1,PLevel2,PLevel3,case_base,case_test,Station,oFile,plottype,Compare_Obs) | ||
|
||
get_model_data(ClimoFile) | ||
|
||
process_model_seasonal_cycle(MinLon,MaxLon,MinLat,MaxLat,Model_Dat,pnew,intyp,kxtrp,Station_Lons,Station_Lats) | ||
|
||
process_model_profiles(Model_Dat,O3_0,PS_0,pnew,intyp,kxtrp,ILAT,ILON,lat_0,lon_0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be good to have some one line descriptions for these functions here and maybe remove the arguments for clarity?
Compare_Obs=0 #initially assumes that user is comparing two models | ||
if adfobj.compare_obs: | ||
Compare_Obs=1 #if comparing a model with observations then don't need to include the base case in the plots |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might change this to a logical boolean:
Compare_Obs=0 #initially assumes that user is comparing two models | |
if adfobj.compare_obs: | |
Compare_Obs=1 #if comparing a model with observations then don't need to include the base case in the plots | |
Compare_Obs=adfobj.compare_obs |
Then you can change where ever the Compare_Obs
check happens
if plottype == 0: | ||
ax.plot(Y0,X1,'tab:red',linestyle='dashed',linewidth=Model_linewidth) | ||
ax.plot(Y0,Model1,'tab:red',linestyle='solid',linewidth=Model_linewidth) | ||
if Compare_Obs <= 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you change Compare_Obs to a boolean (see comment in ozone_diagnostics
section), then all the checks equal to 0 can be turned into the following:
if Compare_Obs <= 0: | |
if not Compare_Obs: |
obsdir='/glade/campaign/acom/acom-climate/tilmes/obs_data/amwg/amwg_data/obs_data/cam-chem/' #location of the ozonesonde data files | ||
cam_climo_loc = adfobj.get_cam_info('cam_climo_loc',required=True)[0] | ||
baseline_climo_loc = adfobj.get_baseline_info('cam_climo_loc',required=True) | ||
ncases = len(cam_climo_loc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable ncases
doesn't seem to be used. You can remove it if you want.
O3_00=Model_Dat.o3.sel(lon=slice(MinLon+360.0,360.0),lat=slice(MinLat,MaxLat)) | ||
O3_01=Model_Dat.o3.sel(lon=slice(0,MaxLon),lat=slice(MinLat,MaxLat)) | ||
O3_0 = np.concatenate( (O3_00,O3_01),axis=3) | ||
PS_00=Model_Dat.ps.sel(lon=slice(MinLon+360.0,360.0),lat=slice(MinLat,MaxLat)) | ||
PS_01=Model_Dat.ps.sel(lon=slice(0,MaxLon),lat=slice(MinLat,MaxLat)) | ||
PS_0 = np.concatenate( (PS_00,PS_01),axis=2) | ||
lon_00=Model_Dat.lon.sel(lon=slice(MinLon+360.0,360.0)) | ||
lon_01=Model_Dat.lon.sel(lon=slice(0,MaxLon)) | ||
lon_0 = np.concatenate( (lon_00,lon_01)) | ||
|
||
#resort the arrays as needed | ||
lon_sort=lon_0.argsort() | ||
O3_0 = O3_0[:,:,:,lon_sort] | ||
PS_0 = PS_0[:,:,lon_sort] | ||
lon_0 = lon_0[lon_sort] | ||
|
||
O3_sfc=np.squeeze(O3_0[:,-1,:,:])*1.0e9 #get the lowest model surface level data | ||
|
||
else: #if the region does not cross the date line | ||
|
||
O3_0=Model_Dat.o3.sel(lon=slice(MinLon,MaxLon),lat=slice(MinLat,MaxLat)) | ||
PS_0=Model_Dat.ps.sel(lon=slice(MinLon,MaxLon),lat=slice(MinLat,MaxLat)) | ||
lon_0=Model_Dat.lon.sel(lon=slice(MinLon,MaxLon)) | ||
O3_sfc=np.squeeze(O3_0.values[:,-1,:,:])*1.0e9 | ||
|
||
|
||
lat_0=Model_Dat.lat.sel(lat=slice(MinLat,MaxLat)) | ||
|
||
O3_sfc_04=np.mean(O3_sfc,axis=(1,2)) | ||
|
||
O3_0I = Ngl.vinth2p(O3_0,Model_Dat.hyam,Model_Dat.hybm,pnew,PS_0,intyp,1000.0,1,kxtrp)*1.0e9 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use the geocat.comp.interpolation
interp_hybrid_to_pressure
function to replace the deprecated Ngl.vinth2p
function. You'll just need to make some small changes to keep everything in xarray
data arrays:
O3_00=Model_Dat.o3.sel(lon=slice(MinLon+360.0,360.0),lat=slice(MinLat,MaxLat)) | |
O3_01=Model_Dat.o3.sel(lon=slice(0,MaxLon),lat=slice(MinLat,MaxLat)) | |
O3_0 = np.concatenate( (O3_00,O3_01),axis=3) | |
PS_00=Model_Dat.ps.sel(lon=slice(MinLon+360.0,360.0),lat=slice(MinLat,MaxLat)) | |
PS_01=Model_Dat.ps.sel(lon=slice(0,MaxLon),lat=slice(MinLat,MaxLat)) | |
PS_0 = np.concatenate( (PS_00,PS_01),axis=2) | |
lon_00=Model_Dat.lon.sel(lon=slice(MinLon+360.0,360.0)) | |
lon_01=Model_Dat.lon.sel(lon=slice(0,MaxLon)) | |
lon_0 = np.concatenate( (lon_00,lon_01)) | |
#resort the arrays as needed | |
lon_sort=lon_0.argsort() | |
O3_0 = O3_0[:,:,:,lon_sort] | |
PS_0 = PS_0[:,:,lon_sort] | |
lon_0 = lon_0[lon_sort] | |
O3_sfc=np.squeeze(O3_0[:,-1,:,:])*1.0e9 #get the lowest model surface level data | |
else: #if the region does not cross the date line | |
O3_0=Model_Dat.o3.sel(lon=slice(MinLon,MaxLon),lat=slice(MinLat,MaxLat)) | |
PS_0=Model_Dat.ps.sel(lon=slice(MinLon,MaxLon),lat=slice(MinLat,MaxLat)) | |
lon_0=Model_Dat.lon.sel(lon=slice(MinLon,MaxLon)) | |
O3_sfc=np.squeeze(O3_0.values[:,-1,:,:])*1.0e9 | |
lat_0=Model_Dat.lat.sel(lat=slice(MinLat,MaxLat)) | |
O3_sfc_04=np.mean(O3_sfc,axis=(1,2)) | |
O3_0I = Ngl.vinth2p(O3_0,Model_Dat.hyam,Model_Dat.hybm,pnew,PS_0,intyp,1000.0,1,kxtrp)*1.0e9 | |
O3_00=Model_Dat.o3.sel(lon=slice(MinLon+360.0,360.0),lat=slice(MinLat,MaxLat)) | |
O3_01=Model_Dat.o3.sel(lon=slice(0,MaxLon),lat=slice(MinLat,MaxLat)) | |
O3_0 = xr.concat([O3_00, O3_01], dim='lon') | |
PS_00=Model_Dat.ps.sel(lon=slice(MinLon+360.0,360.0),lat=slice(MinLat,MaxLat)) | |
PS_01=Model_Dat.ps.sel(lon=slice(0,MaxLon),lat=slice(MinLat,MaxLat)) | |
PS_0 = xr.concat([PS_00, PS_01], dim='lon') | |
lon_00=Model_Dat.lon.sel(lon=slice(MinLon+360.0,360.0)) | |
lon_01=Model_Dat.lon.sel(lon=slice(0,MaxLon)) | |
lon_0 = xr.concat([lon_00, lon_01], dim='lon') | |
#resort the arrays as needed | |
lon_sort=np.concatenate((lon_00,lon_01)).argsort() | |
O3_0 = O3_0.isel(lon=lon_sort) | |
PS_0 = PS_0.isel(lon=lon_sort) | |
lon_0 = lon_0.isel(lon=lon_sort) | |
O3_sfc=O3_0.isel(lev=[-1]).squeeze()*1.0e9 #get the lowest model surface level data | |
else: #if the region does not cross the date line | |
O3_0=Model_Dat.o3.sel(lon=slice(MinLon,MaxLon),lat=slice(MinLat,MaxLat)) | |
PS_0=Model_Dat.ps.sel(lon=slice(MinLon,MaxLon),lat=slice(MinLat,MaxLat)) | |
lon_0=Model_Dat.lon.sel(lon=slice(MinLon,MaxLon)) | |
O3_sfc=O3_0.isel(lev=[-1]).squeeze()*1.0e9 | |
lat_0=Model_Dat.lat.sel(lat=slice(MinLat,MaxLat)) | |
O3_sfc_04=np.mean(O3_sfc,axis=(1,2)) | |
O3_0I = gcomp.interpolation.interp_hybrid_to_pressure(data=O3_0,hyam=Model_Dat.hyam,hybm=Model_Dat.hybm, | |
new_levels=np.array(pnew),ps=PS_0, | |
method='linear',p0=1000.0)*1.0e9 |
|
||
class model_dat_proc: | ||
|
||
O3_0I1 = Ngl.vinth2p(O3_0,Model_Dat.hyam,Model_Dat.hybm,pnew,PS_0,intyp,1000.0,1,kxtrp)*1.0e9 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just like above, we can change the Ngl.vinth2p
to geocat
:
O3_0I1 = Ngl.vinth2p(O3_0,Model_Dat.hyam,Model_Dat.hybm,pnew,PS_0,intyp,1000.0,1,kxtrp)*1.0e9 | |
O3_0I1 = gcomp.interpolation.interp_hybrid_to_pressure(data=O3_0,hyam=Model_Dat.hyam,hybm=Model_Dat.hybm, | |
new_levels=np.array(pnew),ps=PS_0, | |
method='linear',p0=1000.0)*1.0e9 |
import os | ||
import matplotlib.pyplot as plt | ||
import matplotlib as mpl | ||
import Ngl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of using the Ngl
package, which is deprecated, the ADF can bring in geocat.comp
which provides the equivalent functionality. See the comments further down, they will show how to make the switch:
import Ngl | |
import geocat.comp as gcomp |
plt.clf() | ||
plt.close(fig) | ||
except: | ||
print("ERROR: Could not save file ",Output_FName,flush=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Output_FName
doesn't seem to exist, should it be changed to Output_IMG
?
Changes have been addressed in the newly updated ozone_diagnostics.py file. I have also added in taylor diagrams to the code. |
config_cam_baseline_example.yaml
Outdated
user: 'USER-NAME-NOT-SET' | ||
user: 'shawnh' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This just needs to be changed back
case_base = adfobj.get_baseline_info('cam_case_name',required=True) | ||
|
||
#Grab all case nickname(s) | ||
test_nicknames = adfobj.case_nicknames["test_nicknames"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shawnusaf I would change this line so the plot legend isn't a list for the test case
@shawnusaf I left just a couple of suggestions, after that it should be good to merge in. |
Adds V0 ozone diagnostics that output ozone seasonal cycle and profile plots with model comparisons to ozone sonde climatology.