From f20f384fe1f28abbc375fbaba632ffa1fc128fb5 Mon Sep 17 00:00:00 2001 From: justin-richling Date: Sat, 4 Nov 2023 14:01:53 -0600 Subject: [PATCH 01/11] Add tape recorder plots of Q Also update web pages to lock out seasons on plot pages that don't apply --- config_amwg_default_plots.yaml | 1 + config_cam_baseline_example.yaml | 1 + lib/adf_web.py | 15 +- lib/website_templates/adf_diag.css | 12 + lib/website_templates/template.html | 18 +- lib/website_templates/template_var.html | 16 +- scripts/plotting/qbo.py | 2 +- scripts/plotting/tape_recorder.py | 543 ++++++++++++++++++++++++ 8 files changed, 590 insertions(+), 18 deletions(-) create mode 100644 scripts/plotting/tape_recorder.py diff --git a/config_amwg_default_plots.yaml b/config_amwg_default_plots.yaml index ca65101a2..b432bc7e9 100644 --- a/config_amwg_default_plots.yaml +++ b/config_amwg_default_plots.yaml @@ -297,6 +297,7 @@ plotting_scripts: - zonal_mean - polar_map - cam_taylor_diagram + #- tape_recorder #- tem #To plot TEM, please un-comment fill-out #the "tem_info" section below #- regional_map_multicase #To use this please un-comment and fill-out diff --git a/config_cam_baseline_example.yaml b/config_cam_baseline_example.yaml index 74f00d4fb..339de56fb 100644 --- a/config_cam_baseline_example.yaml +++ b/config_cam_baseline_example.yaml @@ -363,6 +363,7 @@ plotting_scripts: - polar_map - cam_taylor_diagram - qbo + #- tape_recorder #- tem #To plot TEM, please un-comment fill-out #the "tem_info" section below #- regional_map_multicase #To use this please un-comment and fill-out diff --git a/lib/adf_web.py b/lib/adf_web.py index 01216dd8f..c58a9c7c6 100644 --- a/lib/adf_web.py +++ b/lib/adf_web.py @@ -422,6 +422,9 @@ def create_website(self): #Set main title for website: main_title = "CAM Diagnostics" + #List of seasons + seasons = ["ANN","DJF","MAM","JJA","SON"] + #Determine local directory: adf_lib_dir = Path(__file__).parent @@ -589,7 +592,6 @@ def create_website(self): #Check if the mean plot type page exists for this case (or for multi-case): mean_table_file = table_pages_dir / "mean_tables.html" if not mean_table_file.exists(): - #Construct mean_table.html mean_table_tmpl = jinenv.get_template('template_mean_tables.html') mean_table_rndr = mean_table_tmpl.render(title=main_title, @@ -613,7 +615,6 @@ def create_website(self): img_pages_dir = self.__case_web_paths[web_data.case]['img_pages_dir'] img_data = [os.path.relpath(web_data.asset_path, start=img_pages_dir), web_data.asset_path.stem] - #Check if plot image already handles multiple cases: if web_data.multi_case: case1 = "Listed in plots." @@ -622,7 +623,6 @@ def create_website(self): case1 = web_data.case plot_types = plot_type_html #End if - tmpl = jinenv.get_template('template.html') #Set template rndr = tmpl.render(title=main_title, var_title=web_data.name, @@ -634,7 +634,8 @@ def create_website(self): case_yrs=case_yrs, baseline_yrs=baseline_yrs, mydata=mean_html_info[web_data.plot_type], - plot_types=plot_types) #The template rendered + plot_types=plot_types, + seasons=seasons) #The template rendered #Write HTML file: with open(web_data.html_file, 'w', encoding='utf-8') as ofil: @@ -644,7 +645,6 @@ def create_website(self): #Check if the mean plot type page exists for this case: mean_ptype_file = img_pages_dir / f"mean_diag_{web_data.plot_type}.html" if not mean_ptype_file.exists(): - #Construct individual plot type mean_diag html files, if they don't #already exist: mean_tmpl = jinenv.get_template('template_mean_diag.html') @@ -680,7 +680,8 @@ def create_website(self): baseline_yrs=baseline_yrs, mydata=mean_html_info[web_data.plot_type], curr_type=web_data.plot_type, - plot_types=plot_types) + plot_types=plot_types, + seasons=seasons) #Write mean diagnostic plots HTML file: with open(mean_ptype_plot_page,'w', encoding='utf-8') as ofil: @@ -764,4 +765,4 @@ def create_website(self): print(" ...Webpages have been generated successfully.") #++++++++++++++++++++ #End Class definition -#++++++++++++++++++++ +#++++++++++++++++++++ \ No newline at end of file diff --git a/lib/website_templates/adf_diag.css b/lib/website_templates/adf_diag.css index eb2b98641..3d5087c3f 100644 --- a/lib/website_templates/adf_diag.css +++ b/lib/website_templates/adf_diag.css @@ -65,6 +65,10 @@ a { text-decoration: none; color: navy; } +a-blocked { + text-decoration: none; + color: black; +} a:visited { color: rgb(77, 6, 18); } @@ -149,6 +153,14 @@ table.dataframe td { text-align: center; } +.grid-item-blocked { + background-color: rgba(192, 192, 192, 0.6); + border: 1px solid rgba(0, 0, 0, 0.8); + padding: 10px; + font-size: 16px; + text-align: center; +} + .grid-item-diag { background-color: rgba(255, 255, 255, 0.6); border: 1px solid rgba(0, 0, 0, 0.8); diff --git a/lib/website_templates/template.html b/lib/website_templates/template.html index cfa488ab2..b285889df 100644 --- a/lib/website_templates/template.html +++ b/lib/website_templates/template.html @@ -1,5 +1,3 @@ - - @@ -55,11 +53,19 @@

Baseline Case:

- {{ season_name }} + {{ season }} - {% endfor %} + {% else %} +
+ {{ season }} +
+ {% endif %} + + {% endfor %} {% endif %} {% endfor %} {% endfor %} @@ -75,4 +81,4 @@

Baseline Case:

Baseline Case:

- {{ season_name }} + {{ season }} - {% endfor %} + {% else %} +
+ {{ season }} +
+ {% endif %} + + {% endfor %} {% endif %} {% endfor %} {% endfor %} @@ -65,4 +73,4 @@

Baseline Case:

= 0] + if (posneg == "neg"): + clevplot = clevs[clevs <= 0] + + ax = fig.add_axes([x1, y1, x2-x1, y2-y1]) + norm = mpl.colors.Normalize(vmin=cmin, vmax=cmax) + + if (ticks): + clb = mpl.colorbar.ColorbarBase(ax, cmap=mymap, + orientation=orient, norm=norm, values=clevplot, ticks=ticks) + else: + clb = mpl.colorbar.ColorbarBase(ax, cmap=mymap, + orientation=orient, norm=norm, values=clevplot) + + clb.ax.tick_params(labelsize=fsize) + clb.set_label(titlestr, fontsize=fsize+2) + + if (contourlines): + #clevlines = (clevs-ci/2.)*contourlinescale + clevlines = clevs*contourlinescale + clevlines = clevlines[np.abs(clevlines) > ci/2.] + if (orient=='horizontal'): + ax.vlines(clevlines[clevlines > 0],-5,5, colors='black', linestyle='solid') + ax.vlines(clevlines[clevlines < 0],-5,5, colors='black', linestyle='dashed') + if (orient=='vertical'): + ax.hlines(clevlines[clevlines > 0],-10,15, colors='black', linestyle='solid') + ax.hlines(clevlines[clevlines < 0],-10,15, colors='black', linestyle='dashed') + + return ax + + +def cosweightlat(darray, lat1, lat2): + """Calculate the weighted average for an [:,lat] array over the region + lat1 to lat2 + """ + + # flip latitudes if they are decreasing + if (darray.lat[0] > darray.lat[darray.lat.size -1]): + print("flipping latitudes") + darray = darray.sortby('lat') + + region = darray.sel(lat=slice(lat1, lat2)) + weights=np.cos(np.deg2rad(region.lat)) + regionw = region.weighted(weights) + regionm = regionw.mean("lat") + + return regionm + + +import matplotlib.pyplot as plt +import numpy as np +import sys + +def plot_pre_mon(fig, data, pre, ci, cmin, cmax, expname, x1=None, x2=None, y1=None, y2=None, + oplot=False, ax=None, cmap='precip', taxis='time', paxis='lev', climo_yrs=None): + """ + Plot seasonal cycle, pressure versus time. + """ + + # move the time axis to the first + if (data.dims[1] != taxis): + data = data.transpose(..., taxis) + + nlevs = (cmax - cmin)/ci + 1 + clevs = np.arange(cmin, cmax+ci, ci) + + if (cmap == "blue2red"): + mymap = blue2red_cmap(nlevs) + + if (cmap == "precip"): + mymap = precip_cmap(nlevs) + + if (cmap == "precip_nowhite"): + mymap = precip_cmap_nowhite(nlevs) + + # if overplotting, check for axis input + if (oplot and (not ax)): + print("This isn't going to work. If overplotting, specify axis") + sys.exit() + + plt.rcParams['font.size'] = '14' + + monticks_temp = np.arange(0,12,1) + monticks2_temp = np.arange(0,12,1)+0.5 + + monticks = monticks_temp + monticks2 = np.zeros([len(monticks2_temp)+2]) + monticks2[0] = -0.5 ; monticks2[len(monticks2)-1] = 12.5 + monticks2[1:len(monticks2)-1] = monticks2_temp + + dataplot = np.zeros([data[paxis].size,len(monticks2)]) + dataplot[:,0] = data[:,11] + dataplot[:,len(monticks2)-1] = data[:,0] + dataplot[:,1:len(monticks2)-1] = data[:,:] + + #Check for over plotting + if not oplot: + if (x1): + ax = fig.add_axes([x1, y1, x2-x1, y2-y1]) + else: + ax = fig.add_axes() + if climo_yrs: + ax.set_title(f"\n{climo_yrs}", loc='right', + fontsize=8) + + ax.contourf(monticks2, -np.log10(data[paxis]), dataplot, levels=clevs, cmap=mymap, extend='max') + ax.set_ylim(-np.log10(100),-np.log10(3)) + ax.set_yticks([-np.log10(100),-np.log10(30),-np.log10(10),-np.log10(3)]) + ax.set_yticklabels(['100','30','10','3']) + ax.set_xlim([0,12]) + ax.tick_params(which='minor', length=0) + ax.set_xticks(monticks) + ax.set_xticklabels([]) + ax.set_xticks(monticks2[1:13], minor=True) + ax.set_xticklabels(['J','F','M','A','M','J','J','A','S','O','N','D'], minor=True, fontsize=14) + ax.set_title(expname, fontsize=16) + + return ax + + From 2017ebbe6609bf5f9ebcd6d467eedfe4623fe25d Mon Sep 17 00:00:00 2001 From: justin-richling Date: Thu, 9 Nov 2023 15:18:19 -0700 Subject: [PATCH 02/11] Fix location of colorbar --- scripts/plotting/tape_recorder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/plotting/tape_recorder.py b/scripts/plotting/tape_recorder.py index f26fff3b7..e8f9770f3 100644 --- a/scripts/plotting/tape_recorder.py +++ b/scripts/plotting/tape_recorder.py @@ -181,7 +181,7 @@ def tape_recorder(adfobj): x1_loc = (x1[1]-x1[0])/2 x2_loc = ((x2[3]-x2[2])/2)+x2[2] else: - x1_loc = x1[0] + x1_loc = x1[1] x2_loc = x2[3] y1_loc = y1[count]-0.03 From a0e86935d290fb85b3a19dc18bec20d68f578ddb Mon Sep 17 00:00:00 2001 From: justin-richling Date: Thu, 9 Nov 2023 15:32:08 -0700 Subject: [PATCH 03/11] Update tape_recorder.py --- scripts/plotting/tape_recorder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/plotting/tape_recorder.py b/scripts/plotting/tape_recorder.py index e8f9770f3..f117f5cf3 100644 --- a/scripts/plotting/tape_recorder.py +++ b/scripts/plotting/tape_recorder.py @@ -522,10 +522,10 @@ def plot_pre_mon(fig, data, pre, ci, cmin, cmax, expname, x1=None, x2=None, y1=N ax = fig.add_axes([x1, y1, x2-x1, y2-y1]) else: ax = fig.add_axes() + ax.xaxis.set_label_position('top') if climo_yrs: - ax.set_title(f"\n{climo_yrs}", loc='right', - fontsize=8) - + ax.set_xlabel(f"{climo_yrs}", loc='center', + fontsize=8) ax.contourf(monticks2, -np.log10(data[paxis]), dataplot, levels=clevs, cmap=mymap, extend='max') ax.set_ylim(-np.log10(100),-np.log10(3)) ax.set_yticks([-np.log10(100),-np.log10(30),-np.log10(10),-np.log10(3)]) From 9f127bdfd0b37be5c132e57832aa70e756b5e28c Mon Sep 17 00:00:00 2001 From: justin-richling Date: Fri, 10 Nov 2023 08:53:01 -0700 Subject: [PATCH 04/11] Add exit message for TEM script --- scripts/plotting/tem.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/plotting/tem.py b/scripts/plotting/tem.py index 526721ce1..df9f9e315 100644 --- a/scripts/plotting/tem.py +++ b/scripts/plotting/tem.py @@ -188,6 +188,8 @@ def tem(adf): #Add plot to website (if enabled): adf.add_website_data(plot_name, "TEM", case_name, season=s) + print(" ...TEM plots have been generated successfully.") + # Helper functions ################## From e0f7c10222de45b0da30d5cbdfd0248848c01720 Mon Sep 17 00:00:00 2001 From: justin-richling Date: Fri, 10 Nov 2023 08:53:09 -0700 Subject: [PATCH 05/11] Update tape_recorder.py --- scripts/plotting/tape_recorder.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/scripts/plotting/tape_recorder.py b/scripts/plotting/tape_recorder.py index f117f5cf3..9af3c9974 100644 --- a/scripts/plotting/tape_recorder.py +++ b/scripts/plotting/tape_recorder.py @@ -25,15 +25,15 @@ def tape_recorder(adfobj): """ - Plot the values of Q against two sets of obseravations, MLS and ERA5, for the tropics + Calculate the weighted latitude average for the simulations and + plot the values of Q against two sets of obseravations, MLS and ERA5, for the tropics between 10S and 10N. MLS h2o data is for 04/2009-11/2021 ERA5 Q data is for 01/1980-12/2020 - This will calculate the weighted latitude average for the simulations/baseline observations in the tropics. - NOTE: If the baseline case are observations and ERA5 is set for Q in the obs location, it will be ignored - since ERA5 is used as a defualt obs in the tape recorder. + NOTE: If the baseline case are observations, it will be ignored + since a defualt set of obs are already being compared against in the tape recorder. @@ -83,8 +83,8 @@ def tape_recorder(adfobj): # Until those are both treated the same (via intake-esm or similar) # we will do a simple check and switch options as needed: if not adfobj.get_basic_info("compare_obs"): - data_name = adfobj.get_baseline_info("cam_case_name", required=True) # does not get used, is just here as a placemarker - data_list = [data_name] # gets used as just the name to search for climo files HAS TO BE LIST + data_name = adfobj.get_baseline_info("cam_case_name", required=True) + case_names = case_names + [data_name] #Grab baseline case nickname base_nickname = adfobj.get_baseline_info('case_nickname') @@ -149,14 +149,13 @@ def tape_recorder(adfobj): plot_min = 1.5e-6 plot_max = 3e-6 - #1e-7,1.5e-6,3e-6 ax = plot_pre_mon(fig, mls, mls.lev, plot_step,plot_min,plot_max,'MLS', x1[0],x2[0],y1[0],y2[0],cmap=cmap, paxis='lev', - taxis='month') + taxis='month',climo_yrs="2009-2021") ax = plot_pre_mon(fig, era5.Q, era5.pre, plot_step,plot_min,plot_max, 'ERA5',x1[1],x2[1],y1[1],y2[1], cmap=cmap, paxis='pre', - taxis='month') + taxis='month',climo_yrs="1980-2020") #Start count at 2 to account for MLS and ERA5 plots above count=2 @@ -191,7 +190,7 @@ def tape_recorder(adfobj): x1_loc, x2_loc, y1_loc, y2_loc, cmap=cmap) - plot_name = plot_loc / f"TapeRecorder_ANN_Special_Mean.{plot_type}" + plot_name = plot_loc / f"Q_ANN_TapeRecorder_Mean.{plot_type}" print(f"\t - Plotting annual tape recorder for Q") # Check redo_plot. If set to True: remove old plot, if it already exists: @@ -396,7 +395,7 @@ def plotcolorbar(fig, ci, cmin, cmax, titlestr, x1, x2, y1, y2, orient = the orientation (horizontal or vertical) posneg = if "both", both positive and negative sides are plotted if "pos", only the positive side is plotted - if "net", only the negative side is plotted + if "neg", only the negative side is plotted ticks = user specified ticklabels fsize = user specified font size contourlines = used to overplot contour lines From 8f1d74c0389c5b1378948ebc9d5acd2693727200 Mon Sep 17 00:00:00 2001 From: justin-richling Date: Tue, 14 Nov 2023 11:43:16 -0700 Subject: [PATCH 06/11] Update tape_recorder.py --- scripts/plotting/tape_recorder.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/plotting/tape_recorder.py b/scripts/plotting/tape_recorder.py index 9af3c9974..ece2319c7 100644 --- a/scripts/plotting/tape_recorder.py +++ b/scripts/plotting/tape_recorder.py @@ -210,7 +210,7 @@ def tape_recorder(adfobj): #Notify user that script has ended: print(" ...Tape recorder plots have been generated successfully.") - #End QBO plotting script: + #End tape recorder plotting script: return @@ -438,7 +438,6 @@ def plotcolorbar(fig, ci, cmin, cmax, titlestr, x1, x2, y1, y2, clb.set_label(titlestr, fontsize=fsize+2) if (contourlines): - #clevlines = (clevs-ci/2.)*contourlinescale clevlines = clevs*contourlinescale clevlines = clevlines[np.abs(clevlines) > ci/2.] if (orient=='horizontal'): @@ -521,6 +520,8 @@ def plot_pre_mon(fig, data, pre, ci, cmin, cmax, expname, x1=None, x2=None, y1=N ax = fig.add_axes([x1, y1, x2-x1, y2-y1]) else: ax = fig.add_axes() + + #Set up axis ax.xaxis.set_label_position('top') if climo_yrs: ax.set_xlabel(f"{climo_yrs}", loc='center', From 66cf3776fd670fe39bad60264fcf396256f1e2da Mon Sep 17 00:00:00 2001 From: justin-richling Date: Fri, 29 Dec 2023 14:23:24 -0700 Subject: [PATCH 07/11] Fix GitHub edits This cleans up a lot of repeated code and documentations. This also fixes issues with CESM time coords. --- scripts/plotting/tape_recorder.py | 224 ++++++++++++------------------ 1 file changed, 91 insertions(+), 133 deletions(-) diff --git a/scripts/plotting/tape_recorder.py b/scripts/plotting/tape_recorder.py index ece2319c7..cfaed75f8 100644 --- a/scripts/plotting/tape_recorder.py +++ b/scripts/plotting/tape_recorder.py @@ -1,27 +1,14 @@ -import os.path -import sys -import glob -from pathlib import Path - # Import necessary packages for the new script import numpy as np import matplotlib.pyplot as plt -from matplotlib.ticker import MultipleLocator -from matplotlib.lines import Line2D - -import xarray as xr - -# -# load libraries: ncar_pylib -# +import matplotlib.colors as mcolors +import matplotlib as mpl import xarray as xr -import numpy as np -import matplotlib.pyplot as plt -from math import nan import pandas as pd -import sys -import dask +from dateutil.relativedelta import relativedelta +import glob +from pathlib import Path def tape_recorder(adfobj): """ @@ -29,14 +16,11 @@ def tape_recorder(adfobj): plot the values of Q against two sets of obseravations, MLS and ERA5, for the tropics between 10S and 10N. - MLS h2o data is for 04/2009-11/2021 + MLS h2o data is for 09/2004-11/2021 ERA5 Q data is for 01/1980-12/2020 - NOTE: If the baseline case are observations, it will be ignored + NOTE: If the baseline case is observations, it will be ignored since a defualt set of obs are already being compared against in the tape recorder. - - - """ #Notify user that script has started: print("\n Generating tape recorder plots...") @@ -44,35 +28,15 @@ def tape_recorder(adfobj): #Special ADF variable which contains the output paths for # ADF variable which contains the output path for plots and tables: plot_location = adfobj.plot_location - if not plot_location: - plot_location = adfobj.get_basic_info("cam_diag_plot_loc") - if isinstance(plot_location, list): - for pl in plot_location: - plpth = Path(pl) - #Check if plot output directory exists, and if not, then create it: - if not plpth.is_dir(): - print(f"\t {pl} not found, making new directory") - plpth.mkdir(parents=True) - if len(plot_location) == 1: - plot_loc = Path(plot_location[0]) - else: - print(f"Ambiguous plotting location since all cases go on same plot. Will put them in first location: {plot_location[0]}") - plot_loc = Path(plot_location[0]) - else: - plot_loc = Path(plot_location) + plot_loc = Path(plot_location[0]) case_names = adfobj.get_cam_info('cam_case_name', required=True) - data_name = adfobj.get_baseline_info('cam_case_name', required=False) case_ts_locs = adfobj.get_cam_info("cam_ts_loc", required=True) - data_ts_loc = adfobj.get_baseline_info("cam_ts_loc", required=False) start_years = adfobj.climo_yrs["syears"] end_years = adfobj.climo_yrs["eyears"] - data_start_year = adfobj.climo_yrs["syear_baseline"] - data_end_year = adfobj.climo_yrs["eyear_baseline"] - #Grab test case nickname(s) test_nicknames = adfobj.get_cam_info('case_nickname') if test_nicknames == None: @@ -83,22 +47,33 @@ def tape_recorder(adfobj): # Until those are both treated the same (via intake-esm or similar) # we will do a simple check and switch options as needed: if not adfobj.get_basic_info("compare_obs"): + #Append all baseline objects to test case lists data_name = adfobj.get_baseline_info("cam_case_name", required=True) case_names = case_names + [data_name] + + data_ts_loc = adfobj.get_baseline_info("cam_ts_loc", required=True) + case_ts_locs = case_ts_locs+[data_ts_loc] - #Grab baseline case nickname base_nickname = adfobj.get_baseline_info('case_nickname') if base_nickname == None: base_nickname = data_name - case_ts_locs = case_ts_locs+[data_ts_loc] test_nicknames = test_nicknames+[base_nickname] + + data_start_year = adfobj.climo_yrs["syear_baseline"] + data_end_year = adfobj.climo_yrs["eyear_baseline"] start_years = start_years+[data_start_year] end_years = end_years+[data_end_year] #End if - + # Default colormap cmap='precip_nowhite' + #Availaible cmaps are: + "blue2red" + "precip" + "precip_nowhite" + 'red2blue' + #Set plot file type: # -- this should be set in basic_info_dict, but is not required # -- So check for it, and default to png @@ -110,6 +85,19 @@ def tape_recorder(adfobj): redo_plot = adfobj.get_basic_info('redo_plot') print(f"\t NOTE: redo_plot is set to {redo_plot}") #----------------------------------------- + + #This may have to change if other variables are desired in this plot type? + plot_name = plot_loc / f"Q_ANN_TapeRecorder_Mean.{plot_type}" + print(f"\t - Plotting annual tape recorder for Q") + + # Check redo_plot. If set to True: remove old plot, if it already exists: + if (not redo_plot) and plot_name.is_file(): + #Add already-existing plot to website (if enabled): + adfobj.add_website_data(plot_name, "tape_recorder", None, season="ANN", multi_case=True) + return + + elif (redo_plot) and plot_name.is_file(): + plot_name.unlink() runs_LT2={} for i,val in enumerate(test_nicknames): @@ -122,6 +110,7 @@ def tape_recorder(adfobj): mls['time'] = time mls = cosweightlat(mls.H2O,-10,10) mls = mls.groupby('time.month').mean('time') + # Convert values from < > to < > mls = mls*18.015280/(1e6*28.964) # ERA5 data @@ -130,9 +119,9 @@ def tape_recorder(adfobj): alldat=[] runname_LT=[] - for key in runs_LT2: + for idx,key in enumerate(runs_LT2): dat = xr.open_dataset(glob.glob(runs_LT2[key]+'/*h0.Q.*.nc')[0]) - dat = fixcesmtime(dat) + dat = fixcesmtime(dat,start_years[idx],end_years[idx]) datzm = dat.mean('lon') dat_tropics = cosweightlat(datzm.Q, -10, 10) dat_mon = dat_tropics.groupby('time.month').mean('time').load() @@ -149,11 +138,11 @@ def tape_recorder(adfobj): plot_min = 1.5e-6 plot_max = 3e-6 - ax = plot_pre_mon(fig, mls, mls.lev, plot_step,plot_min,plot_max,'MLS', + ax = plot_pre_mon(fig, mls, plot_step,plot_min,plot_max,'MLS', x1[0],x2[0],y1[0],y2[0],cmap=cmap, paxis='lev', - taxis='month',climo_yrs="2009-2021") + taxis='month',climo_yrs="2004-2021") - ax = plot_pre_mon(fig, era5.Q, era5.pre, plot_step,plot_min,plot_max, + ax = plot_pre_mon(fig, era5.Q, plot_step,plot_min,plot_max, 'ERA5',x1[1],x2[1],y1[1],y2[1], cmap=cmap, paxis='pre', taxis='month',climo_yrs="1980-2020") @@ -161,8 +150,7 @@ def tape_recorder(adfobj): count=2 for irun in np.arange(0,alldat_concat_LT.run.size,1): title = f"{alldat_concat_LT.run.isel(run=irun).values}" - ax = plot_pre_mon(fig, alldat_concat_LT.isel(run=irun), - alldat_concat_LT.isel(run=irun).lev, + ax = plot_pre_mon(fig, alldat_concat_LT.isel(run=irun), plot_step, plot_min, plot_max, title, x1[count],x2[count],y1[count],y2[count],cmap=cmap, paxis='lev', taxis='month',climo_yrs=f"{start_years[irun]}-{end_years[irun]}") @@ -190,17 +178,6 @@ def tape_recorder(adfobj): x1_loc, x2_loc, y1_loc, y2_loc, cmap=cmap) - plot_name = plot_loc / f"Q_ANN_TapeRecorder_Mean.{plot_type}" - print(f"\t - Plotting annual tape recorder for Q") - - # Check redo_plot. If set to True: remove old plot, if it already exists: - if (not redo_plot) and plot_name.is_file(): - #Add already-existing plot to website (if enabled): - adfobj.add_website_data(plot_name, "tape_recorder", None, season="ANN", multi_case=True) - - elif (redo_plot) and plot_name.is_file(): - plot_name.unlink() - #Save image fig.savefig(plot_name, bbox_inches='tight', facecolor='white') @@ -215,14 +192,13 @@ def tape_recorder(adfobj): # Helper Functions -import matplotlib.pyplot as plt -from matplotlib.colors import ListedColormap ## used to create custom colormaps -import matplotlib.colors as mcolors -import numpy as np +################### def blue2red_cmap(n, nowhite = False): - """ combine two existing color maps to create a diverging color map with white in the middle + """ + combine two existing color maps to create a diverging color map with white in the middle n = the number of contour intervals + nowhite = choice of white separating the diverging colors or not """ if (int(n/2) == n/2): @@ -247,9 +223,14 @@ def blue2red_cmap(n, nowhite = False): return mymap +######### + def red2blue_cmap(n, nowhite = False): - """ combine two existing color maps to create a diverging color map with white in the middle - n = the number of contour intervals """ + """ + combine two existing color maps to create a diverging color map with white in the middle + n = the number of contour intervals + nowhite = choice of white separating the diverging colors or not + """ if (int(n/2) == n/2): #even number of contours @@ -276,12 +257,14 @@ def red2blue_cmap(n, nowhite = False): return mymap - +######### def precip_cmap(n, nowhite=False): - """ combine two existing color maps to create a diverging color map with white in the middle. + """ + combine two existing color maps to create a diverging color map with white in the middle. browns for negative, blues for positive n = the number of contour intervals + nowhite = choice of white separating the diverging colors or not """ if (int(n/2) == n/2): # even number of contours @@ -293,58 +276,37 @@ def precip_cmap(n, nowhite=False): nneg = (n-1)/2 npos = (n-1)/2 - if (nowhite): - nwhite=0 - - colors1 = plt.cm.YlOrBr_r(np.linspace(0,1, int(nneg))) - colors2 = plt.cm.GnBu(np.linspace(0,1, int(npos))) - colorsw = np.ones((nwhite,4)) - - colors = np.vstack((colors1, colorsw, colors2)) - mymap = mcolors.LinearSegmentedColormap.from_list('my_colormap', colors) - - return mymap - -def precip_cmap_nowhite(n): - """ combine two existing color maps to create a diverging color map with white in the middle. - browns for negative, blues for positive - n = the number of contour intervals - """ - if (int(n/2) == n/2): - # even number of contours - nneg=n/2 - npos=n/2 + if nowhite: + colors1 = plt.cm.YlOrBr_r(np.linspace(0,0.8, int(nneg))) + colors2 = plt.cm.GnBu(np.linspace(0.2,1, int(npos))) + colors = np.vstack((colors1, colors2)) else: - nneg = (n-1)/2 - npos = (n-1)/2 - - colors1 = plt.cm.YlOrBr_r(np.linspace(0,0.8, int(nneg))) - colors2 = plt.cm.GnBu(np.linspace(0.2,1, int(npos))) + colors1 = plt.cm.YlOrBr_r(np.linspace(0,1, int(nneg))) + colors2 = plt.cm.GnBu(np.linspace(0,1, int(npos))) + colorsw = np.ones((nwhite,4)) + colors = np.vstack((colors1, colorsw, colors2)) - colors = np.vstack((colors1, colors2)) mymap = mcolors.LinearSegmentedColormap.from_list('my_colormap', colors) return mymap +######### - -def fixcesmtime(dat,timebndsvar='time_bnds'): - """ Fix the CESM timestamp using the average of time_bnds""" - - try: - timebndavg = np.array(dat.isel(M=0)[timebndsvar], - dtype='datetime64[s]').view('i8').mean(axis=1).astype('datetime64[s]') - dat['time'] = timebndavg - except: - timebndavg = np.array(dat[timebndsvar], - dtype='datetime64[s]').view('i8').mean(axis=1).astype('datetime64[s]') - dat['time'] = timebndavg +def fixcesmtime(dat,syear,eyear): + """ + Fix the CESM timestamp with a simple set of dates + """ + timefix = pd.date_range(start=f'1/1/{syear}', end=f'12/1/{eyear}', freq='MS') # generic time coordinate from a non-leap-year + dat = dat.assign_coords({"time":timefix}) return dat +######### def get5by5coords_zmplots(): - """ positioning for 5x5 plots """ + """ + positioning for 5x5 plots + """ x1 = [0.02,0.225,0.43,0.635,0.84, 0.02,0.225,0.43,0.635,0.84, 0.02,0.225,0.43,0.635,0.84, @@ -368,19 +330,13 @@ def get5by5coords_zmplots(): return x1, x2, y1, y2 - - -import matplotlib.pyplot as plt -import matplotlib as mpl - -import importlib - -import numpy as np +######### def plotcolorbar(fig, ci, cmin, cmax, titlestr, x1, x2, y1, y2, cmap='blue2red', orient='horizontal', posneg='both', ticks=None, fsize=14, nowhite=False, contourlines=False, contourlinescale=1): - """plot a color bar + """ + plot a color bar Input: fig = the figure identified ci = the contour interval for the color map @@ -400,6 +356,7 @@ def plotcolorbar(fig, ci, cmin, cmax, titlestr, x1, x2, y1, y2, fsize = user specified font size contourlines = used to overplot contour lines contourlinescale = scale factor for contour lines to be overplotted + nowhite = choice of white separating the diverging colors or not """ # set up contour levels and color map @@ -413,7 +370,7 @@ def plotcolorbar(fig, ci, cmin, cmax, titlestr, x1, x2, y1, y2, mymap = precip_cmap(nlevs, nowhite) if (cmap == "precip_nowhite"): - mymap = precip_cmap_nowhite(nlevs) + mymap = precip_cmap(nlevs, nowhite=True) if (cmap == 'red2blue'): mymap = red2blue_cmap(nlevs, nowhite) @@ -425,7 +382,7 @@ def plotcolorbar(fig, ci, cmin, cmax, titlestr, x1, x2, y1, y2, clevplot = clevs[clevs <= 0] ax = fig.add_axes([x1, y1, x2-x1, y2-y1]) - norm = mpl.colors.Normalize(vmin=cmin, vmax=cmax) + norm = mcolors.Normalize(vmin=cmin, vmax=cmax) if (ticks): clb = mpl.colorbar.ColorbarBase(ax, cmap=mymap, @@ -449,9 +406,11 @@ def plotcolorbar(fig, ci, cmin, cmax, titlestr, x1, x2, y1, y2, return ax +######### def cosweightlat(darray, lat1, lat2): - """Calculate the weighted average for an [:,lat] array over the region + """ + Calculate the weighted average for an [:,lat] array over the region lat1 to lat2 """ @@ -467,12 +426,9 @@ def cosweightlat(darray, lat1, lat2): return regionm +######### -import matplotlib.pyplot as plt -import numpy as np -import sys - -def plot_pre_mon(fig, data, pre, ci, cmin, cmax, expname, x1=None, x2=None, y1=None, y2=None, +def plot_pre_mon(fig, data, ci, cmin, cmax, expname, x1=None, x2=None, y1=None, y2=None, oplot=False, ax=None, cmap='precip', taxis='time', paxis='lev', climo_yrs=None): """ Plot seasonal cycle, pressure versus time. @@ -492,12 +448,12 @@ def plot_pre_mon(fig, data, pre, ci, cmin, cmax, expname, x1=None, x2=None, y1=N mymap = precip_cmap(nlevs) if (cmap == "precip_nowhite"): - mymap = precip_cmap_nowhite(nlevs) + mymap = precip_cmap(nlevs, nowhite=True) # if overplotting, check for axis input if (oplot and (not ax)): print("This isn't going to work. If overplotting, specify axis") - sys.exit() + return plt.rcParams['font.size'] = '14' @@ -540,4 +496,6 @@ def plot_pre_mon(fig, data, pre, ci, cmin, cmax, expname, x1=None, x2=None, y1=N return ax +######### +############### From ac3e7440f7a20d03a18618c4787ba4f901c57b95 Mon Sep 17 00:00:00 2001 From: justin-richling Date: Fri, 29 Dec 2023 14:45:50 -0700 Subject: [PATCH 08/11] Update tape_recorder.py --- scripts/plotting/tape_recorder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/plotting/tape_recorder.py b/scripts/plotting/tape_recorder.py index cfaed75f8..d52b4c0dd 100644 --- a/scripts/plotting/tape_recorder.py +++ b/scripts/plotting/tape_recorder.py @@ -110,7 +110,7 @@ def tape_recorder(adfobj): mls['time'] = time mls = cosweightlat(mls.H2O,-10,10) mls = mls.groupby('time.month').mean('time') - # Convert values from < > to < > + # Convert mixing ratio values from ppmv to kg/kg mls = mls*18.015280/(1e6*28.964) # ERA5 data From 3f54a42d4faf52bf4d3dfea1e9b28d219ba32ccf Mon Sep 17 00:00:00 2001 From: justin-richling Date: Wed, 3 Jan 2024 12:05:35 -0700 Subject: [PATCH 09/11] Add message if plot exists and clobber is false Prints if debug is enabled when running the ADF --- scripts/plotting/cam_taylor_diagram.py | 1 + scripts/plotting/global_latlon_map.py | 2 ++ scripts/plotting/global_latlon_vect_map.py | 2 ++ scripts/plotting/meridional_mean.py | 1 + scripts/plotting/polar_map.py | 2 ++ scripts/plotting/qbo.py | 9 +++++++-- scripts/plotting/tape_recorder.py | 21 +++++++++++++-------- scripts/plotting/tem.py | 1 + scripts/plotting/zonal_mean.py | 1 + 9 files changed, 30 insertions(+), 10 deletions(-) diff --git a/scripts/plotting/cam_taylor_diagram.py b/scripts/plotting/cam_taylor_diagram.py index 59b0d6c8b..de5f12efa 100644 --- a/scripts/plotting/cam_taylor_diagram.py +++ b/scripts/plotting/cam_taylor_diagram.py @@ -151,6 +151,7 @@ def cam_taylor_diagram(adfobj): # Check redo_plot. If set to True: remove old plot, if it already exists: if (not redo_plot) and plot_name.is_file(): #Add already-existing plot to website (if enabled): + adfobj.debug_log(f"'{plot_name}' exists and clobber is false.") adfobj.add_website_data(plot_name, "TaylorDiag", None, season=s, multi_case=True) #Continue to next iteration: diff --git a/scripts/plotting/global_latlon_map.py b/scripts/plotting/global_latlon_map.py index f077f2829..f55fed5b2 100644 --- a/scripts/plotting/global_latlon_map.py +++ b/scripts/plotting/global_latlon_map.py @@ -329,6 +329,7 @@ def global_latlon_map(adfobj): # Check redo_plot. If set to True: remove old plot, if it already exists: if (not redo_plot) and plot_name.is_file(): #Add already-existing plot to website (if enabled): + adfobj.debug_log(f"'{plot_name}' exists and clobber is false.") adfobj.add_website_data(plot_name, var, case_name, category=web_category, season=s, plot_type="LatLon") @@ -421,6 +422,7 @@ def global_latlon_map(adfobj): redo_plot = adfobj.get_basic_info('redo_plot') if (not redo_plot) and plot_name.is_file(): #Add already-existing plot to website (if enabled): + adfobj.debug_log(f"'{plot_name}' exists and clobber is false.") adfobj.add_website_data(plot_name, f"{var}_{pres}hpa", case_name, category=web_category, season=s, plot_type="LatLon") diff --git a/scripts/plotting/global_latlon_vect_map.py b/scripts/plotting/global_latlon_vect_map.py index c6c63bb0b..8a1a6462b 100644 --- a/scripts/plotting/global_latlon_vect_map.py +++ b/scripts/plotting/global_latlon_vect_map.py @@ -387,6 +387,7 @@ def global_latlon_vect_map(adfobj): # Check redo_plot. If set to True: remove old plot, if it already exists: if (not redo_plot) and plot_name.is_file(): #Add already-existing plot to website (if enabled): + adfobj.debug_log(f"'{plot_name}' exists and clobber is false.") adfobj.add_website_data(plot_name, f"{var_name}_{lv}hpa", case_name, category=web_category, season=s, plot_type="LatLon_Vector") @@ -440,6 +441,7 @@ def global_latlon_vect_map(adfobj): redo_plot = adfobj.get_basic_info('redo_plot') if (not redo_plot) and plot_name.is_file(): #Add already-existing plot to website (if enabled): + adfobj.debug_log(f"'{plot_name}' exists and clobber is false.") adfobj.add_website_data(plot_name, var_name, case_name, category=web_category, season=s, plot_type="LatLon_Vector") diff --git a/scripts/plotting/meridional_mean.py b/scripts/plotting/meridional_mean.py index aa1f90d22..f365c039c 100644 --- a/scripts/plotting/meridional_mean.py +++ b/scripts/plotting/meridional_mean.py @@ -221,6 +221,7 @@ def meridional_mean(adfobj): # Check redo_plot. If set to True: remove old plot, if it already exists: if (not redo_plot) and plot_name.is_file(): #Add already-existing plot to website (if enabled): + adfobj.debug_log(f"'{plot_name}' exists and clobber is false.") adfobj.add_website_data(plot_name, var, case_name, season=s, plot_type="Meridional") #Continue to next iteration: diff --git a/scripts/plotting/polar_map.py b/scripts/plotting/polar_map.py index a72e0b2ee..db4cd1b7e 100644 --- a/scripts/plotting/polar_map.py +++ b/scripts/plotting/polar_map.py @@ -261,6 +261,7 @@ def polar_map(adfobj): # If redo_plot set to True: remove old plot, if it already exists: if (not redo_plot) and plot_name.is_file(): #Add already-existing plot to website (if enabled): + adfobj.debug_log(f"'{plot_name}' exists and clobber is false.") adfobj.add_website_data(plot_name, var, case_name, category=web_category, season=s, plot_type=hemi_type) @@ -339,6 +340,7 @@ def polar_map(adfobj): # If redo_plot set to True: remove old plot, if it already exists: if (not redo_plot) and plot_name.is_file(): #Add already-existing plot to website (if enabled): + adfobj.debug_log(f"'{plot_name}' exists and clobber is false.") adfobj.add_website_data(plot_name, f"{var}_{pres}hpa", case_name, category=web_category, season=s, plot_type=hemi_type) diff --git a/scripts/plotting/qbo.py b/scripts/plotting/qbo.py index 628f8a3fb..6da03cea6 100644 --- a/scripts/plotting/qbo.py +++ b/scripts/plotting/qbo.py @@ -67,11 +67,16 @@ def qbo(adfobj): print(f"\t QBO plots will be saved here: {plot_locations[0]}") # Check redo_plot. If set to True: remove old plots, if they already exist: - if (not redo_plot) and plot_loc_ts.is_file() and plot_loc_amp.is_file(): + if (not redo_plot) and plot_loc_ts.is_file(): #Add already-existing plot to website (if enabled): + adfobj.debug_log(f"'{plot_loc_ts}' exists and clobber is false.") adfobj.add_website_data(plot_loc_ts, "QBO", None, season="QBOts", multi_case=True) + #Continue to next iteration: + return + if (not redo_plot) and plot_loc_amp.is_file(): + #Add already-existing plot to website (if enabled): + adfobj.debug_log(f"'{plot_loc_amp}' exists and clobber is false.") adfobj.add_website_data(plot_loc_amp, "QBO", None, season="QBOamp", multi_case=True) - #Continue to next iteration: return elif (redo_plot): diff --git a/scripts/plotting/tape_recorder.py b/scripts/plotting/tape_recorder.py index d52b4c0dd..5d747b0b1 100644 --- a/scripts/plotting/tape_recorder.py +++ b/scripts/plotting/tape_recorder.py @@ -19,21 +19,29 @@ def tape_recorder(adfobj): MLS h2o data is for 09/2004-11/2021 ERA5 Q data is for 01/1980-12/2020 + Optional built-in colormaps: + - blue2red + - precip + - precip_nowhite -> default cmap + - red2blue + NOTE: If the baseline case is observations, it will be ignored since a defualt set of obs are already being compared against in the tape recorder. """ #Notify user that script has started: print("\n Generating tape recorder plots...") - #Special ADF variable which contains the output paths for - # ADF variable which contains the output path for plots and tables: + #Special ADF variable which contains the output paths for plots: plot_location = adfobj.plot_location plot_loc = Path(plot_location[0]) + #Grab test case name(s) case_names = adfobj.get_cam_info('cam_case_name', required=True) + #Grab test case time series locs(s) case_ts_locs = adfobj.get_cam_info("cam_ts_loc", required=True) + #Grab test case climo years start_years = adfobj.climo_yrs["syears"] end_years = adfobj.climo_yrs["eyears"] @@ -47,6 +55,7 @@ def tape_recorder(adfobj): # Until those are both treated the same (via intake-esm or similar) # we will do a simple check and switch options as needed: if not adfobj.get_basic_info("compare_obs"): + #Append all baseline objects to test case lists data_name = adfobj.get_baseline_info("cam_case_name", required=True) case_names = case_names + [data_name] @@ -68,12 +77,6 @@ def tape_recorder(adfobj): # Default colormap cmap='precip_nowhite' - #Availaible cmaps are: - "blue2red" - "precip" - "precip_nowhite" - 'red2blue' - #Set plot file type: # -- this should be set in basic_info_dict, but is not required # -- So check for it, and default to png @@ -93,12 +96,14 @@ def tape_recorder(adfobj): # Check redo_plot. If set to True: remove old plot, if it already exists: if (not redo_plot) and plot_name.is_file(): #Add already-existing plot to website (if enabled): + adfobj.debug_log(f"'{plot_name}' exists and clobber is false.") adfobj.add_website_data(plot_name, "tape_recorder", None, season="ANN", multi_case=True) return elif (redo_plot) and plot_name.is_file(): plot_name.unlink() + #Make dictionary for case names and associated timeseries file locations runs_LT2={} for i,val in enumerate(test_nicknames): runs_LT2[val] = case_ts_locs[i] diff --git a/scripts/plotting/tem.py b/scripts/plotting/tem.py index df9f9e315..6abd21c0c 100644 --- a/scripts/plotting/tem.py +++ b/scripts/plotting/tem.py @@ -150,6 +150,7 @@ def tem(adf): # Check redo_plot. If set to True: remove old plot, if it already exists: if (not redo_plot) and plot_name.is_file(): #Add already-existing plot to website (if enabled): + adf.debug_log(f"'{plot_name}' exists and clobber is false.") adf.add_website_data(plot_name, "TEM", case_name, season=s) #Continue to next iteration: diff --git a/scripts/plotting/zonal_mean.py b/scripts/plotting/zonal_mean.py index 6f48b4c19..8631256f0 100644 --- a/scripts/plotting/zonal_mean.py +++ b/scripts/plotting/zonal_mean.py @@ -165,6 +165,7 @@ def zonal_mean(adfobj): if (not redo_plot) and plot_name.is_file(): zonal_skip.append(plot_name) #Add already-existing plot to website (if enabled): + adfobj.debug_log(f"'{plot_name}' exists and clobber is false.") adfobj.add_website_data(plot_name, var, case_name, season=s, plot_type="Zonal") From 333a1bfdba2562ec7cefc1a1da6c413f94bf5bb0 Mon Sep 17 00:00:00 2001 From: justin-richling Date: Wed, 3 Jan 2024 15:34:01 -0700 Subject: [PATCH 10/11] Fix check for QBO plots and clobber --- scripts/plotting/qbo.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/scripts/plotting/qbo.py b/scripts/plotting/qbo.py index 6da03cea6..9fdced028 100644 --- a/scripts/plotting/qbo.py +++ b/scripts/plotting/qbo.py @@ -67,15 +67,10 @@ def qbo(adfobj): print(f"\t QBO plots will be saved here: {plot_locations[0]}") # Check redo_plot. If set to True: remove old plots, if they already exist: - if (not redo_plot) and plot_loc_ts.is_file(): + if (not redo_plot) and plot_loc_ts.is_file() and plot_loc_amp.is_file(): #Add already-existing plot to website (if enabled): - adfobj.debug_log(f"'{plot_loc_ts}' exists and clobber is false.") + adfobj.debug_log(f"'{plot_loc_ts}' and '{plot_loc_amp}' exist and clobber is false.") adfobj.add_website_data(plot_loc_ts, "QBO", None, season="QBOts", multi_case=True) - #Continue to next iteration: - return - if (not redo_plot) and plot_loc_amp.is_file(): - #Add already-existing plot to website (if enabled): - adfobj.debug_log(f"'{plot_loc_amp}' exists and clobber is false.") adfobj.add_website_data(plot_loc_amp, "QBO", None, season="QBOamp", multi_case=True) #Continue to next iteration: return From 0bc3cdba05820be99a7da1ecc87b79cc35194d00 Mon Sep 17 00:00:00 2001 From: justin-richling Date: Tue, 9 Jan 2024 12:11:47 -0700 Subject: [PATCH 11/11] Update amwg_table.py Bring in changes from PR #240 --- scripts/analysis/amwg_table.py | 53 +++++----------------------------- 1 file changed, 8 insertions(+), 45 deletions(-) diff --git a/scripts/analysis/amwg_table.py b/scripts/analysis/amwg_table.py index e7765d3cb..97d9a2cbf 100644 --- a/scripts/analysis/amwg_table.py +++ b/scripts/analysis/amwg_table.py @@ -150,9 +150,6 @@ def amwg_table(adf): output_locs.append(output_locs[0]) #----------------------------------------- - #Create (empty) dictionary to use for the - #residual top of model (RESTOM) radiation calculation: - restom_dict = {} #Loop over CAM cases: for case_idx, case_name in enumerate(case_names): @@ -182,9 +179,6 @@ def amwg_table(adf): Path.unlink(output_csv_file) #End if - #Save case name as a new key in the RESTOM dictonary: - restom_dict[case_name] = {} - #Create/reset new variable that potentially stores the re-gridded #ocean fraction xarray data-array: ocn_frc_da = None @@ -270,14 +264,6 @@ def amwg_table(adf): # Note: we should be able to handle (lat, lon) or (ncol,) cases, at least data = pf.spatial_average(data) # changes data "in place" - #Add necessary data for RESTOM calcs below - if var == "FLNT": - restom_dict[case_name][var] = data - #Copy units for RESTOM as well: - restom_units = unit_str - if var == "FSNT": - restom_dict[case_name][var] = data - # In order to get correct statistics, average to annual or seasonal data = pf.annual_mean(data, whole_years=True, time_name='time') @@ -305,35 +291,13 @@ def amwg_table(adf): #End of var_list loop #-------------------- - if "FSNT" and "FLNT" in var_list: - #RESTOM Calcs - var = "RESTOM" #RESTOM = FSNT-FLNT - print(f"\t - Variable '{var}' being added to table") - data = restom_dict[case_name]["FSNT"] - restom_dict[case_name]["FLNT"] - # In order to get correct statistics, average to annual or seasonal - data = pf.annual_mean(data, whole_years=True, time_name='time') - # These get written to our output file: - stats_list = _get_row_vals(data) - row_values = [var, restom_units] + stats_list - # col (column) values declared above - - # Format entries: - dfentries = {c:[row_values[i]] for i,c in enumerate(cols)} - - # Add entries to Pandas structure: - df = pd.DataFrame(dfentries) - - # Check if the output CSV file exists, - # if so, then append to it: - if output_csv_file.is_file(): - df.to_csv(output_csv_file, mode='a', header=False, index=False) - else: - df.to_csv(output_csv_file, header=cols, index=False) - #End if - - else: - #Print message to debug log: - adf.debug_log("RESTOM not calculated because FSNT and/or FLNT variables not in dataset") + # Move RESTOM to top of table + #---------------------------- + if 'RESTOM' in var_list: + table_df = pd.read_csv(output_csv_file) + table_df = pd.concat([table_df[table_df['variable'] == 'RESTOM'], table_df]).reset_index(drop = True) + table_df = table_df.drop_duplicates() + table_df.to_csv(output_csv_file, header=cols, index=False) #End if # last step is to add table dataframe to website (if enabled): @@ -343,7 +307,6 @@ def amwg_table(adf): #End of model case loop #---------------------- - #Notify user that script has ended: print(" ...AMWG variable table has been generated successfully.") @@ -434,4 +397,4 @@ def _df_comp_table(adf, output_location, case_names): adf.add_website_data(df_comp, "Case Comparison", case_names[0], plot_type="Tables") ############## -#END OF SCRIPT +#END OF SCRIPT \ No newline at end of file