33Uses whatever model data can be found within the directory pp_root,
44and does not try to match the model and observed time periods.
55How to use:
6- python sst_eval.py /archive/acr/fre/NWA/2023_04/NWA12_COBALT_2023_04_kpo4-coastatten-physics/gfdl.ncrc5-intel22-prod
6+ python sst_eval.py -p /archive/acr/fre/NWA/2023_04/NWA12_COBALT_2023_04_kpo4-coastatten-physics/gfdl.ncrc5-intel22-prod -c config.yaml
77"""
88
99import cartopy .crs as ccrs
1212from mpl_toolkits .axes_grid1 import AxesGrid
1313import numpy as np
1414import xarray
15- import xesmf
15+ import logging
1616
17- from plot_common import annotate_skill , autoextend_colorbar , corners , get_map_norm , open_var
17+ from plot_common import annotate_skill , autoextend_colorbar , get_map_norm , open_var , load_config , process_oisst , process_glorys
1818
19+ # Configure logging for sst_eval
20+ logger = logging .getLogger (__name__ )
21+ logging .basicConfig (filename = "sst_eval.log" , format = '%(asctime)s %(levelname)s:%(name)s: %(message)s' ,level = logging .INFO )
1922
20- def plot_sst_eval (pp_root ):
21- model = open_var (pp_root , 'ocean_monthly' , 'tos' )
22- model_grid = xarray .open_dataset ('../data/geography/ocean_static.nc' )
23- target_grid = model_grid [['geolon' , 'geolat' , 'geolon_c' , 'geolat_c' ]].rename ({'geolon' : 'lon' , 'geolat' : 'lat' , 'geolon_c' : 'lon_b' , 'geolat_c' : 'lat_b' })
23+ def plot_sst_eval (pp_root , config ):
24+
25+ model = open_var (pp_root , config ['domain' ], 'tos' )
26+ model_grid = xarray .open_dataset ( config ['model_grid' ] )
27+ target_grid = model_grid [config ['rename_map' ].keys ()].rename (config ['rename_map' ])
2428 model_ave = model .mean ('time' ).load ()
29+ logger .info ("MODEL_GRID: %s" ,model_grid )
30+ logger .info ("MODEL_AVE: %s" ,model_ave )
31+ logger .info ("Successfully opened model grid and took mean over time" )
2532
26- glorys = xarray . open_dataset ( '/work/acr/mom6/diagnostics/glorys/glorys_sfc.nc' )[ 'thetao' ] #.rename({'longitude': 'lon', 'latitude': 'lat'})
27- glorys_lonc , glorys_latc = corners ( glorys . lon , glorys . lat )
28- glorys_ave = glorys . mean ( 'time' ). load ()
29- glorys_to_mom = xesmf . Regridder ( glorys_ave , target_grid , method = 'bilinear' , unmapped_to_nan = True )
30- glorys_rg = glorys_to_mom ( glorys_ave )
33+ # Verify that xh/yh are set as coordinates, then make sure model coordinates match grid data
34+ model_grid = model_grid . assign_coords ( { 'xh' : model_grid . xh , 'yh' : model_grid . yh } )
35+ model_ave = xarray . align ( model_grid , model_ave , join = 'override' )[ 1 ]
36+
37+ glorys_rg , glorys_ave , glorys_lonc , glorys_latc = process_glorys ( config , target_grid )
3138 delta_glorys = model_ave - glorys_rg
39+ logger .info ("GLORYS_RG: %s" ,glorys_rg )
40+ logger .info ("DELTA_GLORYS: %s" ,delta_glorys )
3241
33- oisst = (
34- xarray .open_mfdataset ([f'/work/acr/oisstv2/sst.month.mean.{ y } .nc' for y in range (1993 , 2020 )])
35- .sst
36- .sel (lat = slice (0 , 60 ), lon = slice (360 - 100 , 360 - 30 ))
37- )
38- oisst_lonc , oisst_latc = corners (oisst .lon , oisst .lat )
39- oisst_lonc -= 360
40- mom_to_oisst = xesmf .Regridder (
41- target_grid ,
42- {'lat' : oisst .lat , 'lon' : oisst .lon , 'lat_b' : oisst_latc , 'lon_b' : oisst_lonc },
43- method = 'conservative_normed' ,
44- unmapped_to_nan = True
45- )
46- oisst_ave = oisst .mean ('time' ).load ()
47- mom_rg = mom_to_oisst (model_ave )
42+ mom_rg , oisst_ave , oisst_lonc , oisst_latc = process_oisst (config , target_grid , model_ave )
4843 delta_oisst = mom_rg - oisst_ave
44+ logger .info ("OISST_AVE: %s" ,oisst_ave )
45+ logger .info ("DELTA_OISST: %s" ,delta_oisst )
46+
47+ fig = plt .figure (figsize = (config ['fig_width' ], config ['fig_height' ]))
48+
49+ # Set projection of each grid in the plot
50+ # For now, sst_eval.py will only support a projection for the arctic and a projection for all other domains
51+ if config ['projection_grid' ] == 'NorthPolarStereo' :
52+ p = ccrs .NorthPolarStereo ()
53+ else :
54+ p = ccrs .PlateCarree ()
4955
50- fig = plt .figure (figsize = (11 , 14 ))
5156 grid = AxesGrid (fig , 111 ,
52- axes_class = (GeoAxes , dict (projection = ccrs . PlateCarree () )),
57+ axes_class = (GeoAxes , dict ( projection = p )),
5358 nrows_ncols = (2 , 3 ),
5459 axes_pad = 0.3 ,
5560 cbar_location = 'bottom' ,
5661 cbar_mode = 'edge' ,
5762 cbar_pad = 0.2 ,
5863 cbar_size = '15%' ,
59- label_mode = ''
64+ label_mode = 'keep '
6065 )
66+ logger .info ("Successfully created grid" )
6167
6268 # Discrete levels and colorbar for SST plots
63- levels = np .arange (2 , 31 , 2 )
69+ levels = np .arange (config [ 'levels_min' ], config [ 'levels_max' ], config [ 'levels_step' ] )
6470 try :
6571 import cmcrameri .cm as cmc
6672 except ModuleNotFoundError :
@@ -70,55 +76,70 @@ def plot_sst_eval(pp_root):
7076 cmap , norm = get_map_norm (cmap , levels = levels )
7177
7278 # Discrete levels and colorbar for difference plots
73- bias_levels = np .arange (- 2 , 2.1 , 0.25 )
79+ bias_levels = np .arange (config [ 'bias_min' ], config [ 'bias_max' ], config [ 'bias_step' ] )
7480 bias_cmap , bias_norm = get_map_norm ('coolwarm' , levels = bias_levels )
7581 bias_common = dict (cmap = bias_cmap , norm = bias_norm )
7682
83+ # Set projection of input data files so that data is correctly tranformed when plotting
84+ # For now, sst_eval.py will only support a projection for the arctic and a projection for all other domains
85+ if config ['projection_data' ] == 'NorthPolarStereo' :
86+ proj = ccrs .NorthPolarStereo ()
87+ else :
88+ proj = ccrs .PlateCarree ()
89+
7790 # Model
78- p0 = grid [0 ].pcolormesh (model_grid .geolon_c , model_grid .geolat_c , model_ave , cmap = cmap , norm = norm )
91+ p0 = grid [0 ].pcolormesh (model_grid .geolon_c , model_grid .geolat_c , model_ave , cmap = cmap , norm = norm , transform = proj )
7992 grid [0 ].set_title ('(a) Model' )
8093 cbar1 = grid .cbar_axes [0 ].colorbar (p0 )
8194 cbar1 .ax .set_xlabel ('Mean SST (°C)' )
95+ logger .info ("Successfully plotted model data" )
8296
8397 # OISST
84- grid [1 ].pcolormesh (oisst_lonc , oisst_latc , oisst_ave , cmap = cmap , norm = norm )
98+ grid [1 ].pcolormesh (oisst_lonc , oisst_latc , oisst_ave , cmap = cmap , norm = norm , transform = proj )
8599 grid [1 ].set_title ('(b) OISST' )
100+ logger .info ("Successfully plotted oisst" )
86101
87102 # Model - OISST
88- grid [2 ].pcolormesh (oisst_lonc , oisst_latc , delta_oisst , ** bias_common )
103+ grid [2 ].pcolormesh (oisst_lonc , oisst_latc , delta_oisst , transform = proj , ** bias_common )
89104 grid [2 ].set_title ('(c) Model - OISST' )
90- annotate_skill (mom_rg , oisst_ave , grid [2 ], dim = ['lat' , 'lon' ])
105+ annotate_skill (mom_rg , oisst_ave , grid [2 ], dim = ['lat' , 'lon' ], x0 = config ['text_x' ], y0 = config ['text_y' ], xint = config ['text_xint' ], plot_lat = config ['plot_lat' ])
106+ logger .info ("Successfully plotted difference between model and oisst" )
91107
92108 # GLORYS
93- p1 = grid [4 ].pcolormesh (glorys_lonc , glorys_latc , glorys_ave , cmap = cmap , norm = norm )
109+ p1 = grid [4 ].pcolormesh (glorys_lonc , glorys_latc , glorys_ave , cmap = cmap , norm = norm , transform = proj )
94110 grid [4 ].set_title ('(d) GLORYS12' )
95111 cbar1 = autoextend_colorbar (grid .cbar_axes [1 ], p1 )
96112 cbar1 .ax .set_xlabel ('Mean SST (°C)' )
113+ logger .info ("Successfully plotted glorys" )
97114
98115 # Model - GLORYS
99- p2 = grid [5 ].pcolormesh (model_grid .geolon_c , model_grid .geolat_c , delta_glorys , ** bias_common )
116+ p2 = grid [5 ].pcolormesh (model_grid .geolon_c , model_grid .geolat_c , delta_glorys , transform = proj , ** bias_common )
100117 cbar2 = autoextend_colorbar (grid .cbar_axes [2 ], p2 )
101118 cbar2 .ax .set_xlabel ('SST difference (°C)' )
102119 cbar2 .ax .set_xticks ([- 2 , - 1 , 0 , 1 , 2 ])
103120 grid [5 ].set_title ('(e) Model - GLORYS12' )
104- annotate_skill (model_ave , glorys_rg , grid [5 ], weights = model_grid .areacello )
121+ annotate_skill (model_ave , glorys_rg , grid [5 ], weights = model_grid .areacello , x0 = config ['text_x' ], y0 = config ['text_y' ], xint = config ['text_xint' ], plot_lat = config ['plot_lat' ])
122+ logger .info ("Successfully plotted difference between glorys and model" )
105123
106124 for ax in grid :
107- ax .set_xlim (- 99 , - 35 )
108- ax .set_ylim (4 , 59 )
125+ ax .set_extent ([ config ['x' ]['min' ], config ['x' ]['max' ], config ['y' ]['min' ], config ['y' ]['max' ] ], crs = proj )
109126 ax .set_xticks ([])
110127 ax .set_yticks ([])
111128 ax .set_xticklabels ([])
112129 ax .set_yticklabels ([])
113130 for s in ax .spines .values ():
114131 s .set_visible (False )
115-
116- plt .savefig ('figures/sst_eval.png' , dpi = 300 , bbox_inches = 'tight' )
132+ logger .info ("Successfully set extent of each axis" )
133+
134+ plt .savefig (config ['figures_dir' ]+ 'sst_eval.png' , dpi = 300 , bbox_inches = 'tight' )
135+ logger .info ("Successfully saved figure" )
117136
118137
119138if __name__ == '__main__' :
120139 from argparse import ArgumentParser
121140 parser = ArgumentParser ()
122- parser .add_argument ('pp_root' , help = 'Path to postprocessed data (up to but not including /pp/)' )
141+ parser .add_argument ('-p' ,'--pp_root' , type = str , help = 'Path to postprocessed data (up to but not including /pp/)' , required = True )
142+ parser .add_argument ('-c' ,'--config' , type = str , help = 'Path to config.yaml file containing relevant paths for diagnostic scripts' , required = True )
123143 args = parser .parse_args ()
124- plot_sst_eval (args .pp_root )
144+ config = load_config (args .config )
145+ plot_sst_eval (args .pp_root , config )
0 commit comments