var_list
\n", " var_list =
adfobj.diag_var_list
all_case_names
\n", "\n", " case_name =
adfobj.get_cam_info(\"cam_case_name\", required=True)
-> comes out as a list
baseline_name =
adfobj.get_baseline_info(\"cam_case_name\", required=True)
all_case_names =
case_name + [baseline_name]
case_ts_locs
\n", "\n", " case_ts_loc =
adfobj.get_cam_info(\"cam_ts_loc\", required=True)
-> comes out as a list
baseline_ts_loc =
adfobj.get_baseline_info(\"cam_ts_loc\", required=True)
case_ts_locs =
case_ts_loc + [baseline_ts_loc]
all_nicknames
\n", "\n", " case_nickname =
adfobj.case_nicknames[\"test_nicknames\"]
-> comes out as a list
baseline_nickname =
adfobj.case_nicknames[\"base_nickname\"]
all_nicknames =
case_nickname + [baseline_nickname]
plot_type
\n", "\n", " basic_info_dict =
adfobj.read_config_var(\"diag_basic_info\", required=True)
plot_type =
basic_info_dict.get('plot_type', 'png')
plot_location
\n", " plot_location =
adfobj.get_basic_info(\"cam_diag_plot_loc\")
adfobj.diag_var_list
"
]
},
{
"cell_type": "markdown",
"id": "288eac1a-5063-4512-9f0a-d8d33f64a34e",
"metadata": {},
"source": [
"`````{div} full-width\n",
"```python\n",
"def time_series_plot(adfobj):\n",
" \"\"\"\n",
" This script plots time series.\n",
" Compare CAM climatologies against other\n",
" climatological data (observations or baseline runs).\n",
"\n",
" Description of needed inputs from ADF:\n",
" case_name -> Name of CAM case provided by \"cam_case_name\".\n",
"\n",
" ts_loc -> Location of CAM time series files provided by \"cam_ts_loc\".\n",
"\n",
" data_name -> Name of data set CAM case is being compared against,\n",
" which is always either \"obs\" or the baseline CAM case name,\n",
" depending on whether \"compare_obs\" is true or false.\n",
"\n",
" ts_var_list -> List of CAM output variables provided by \"timeseries_var_list\".\n",
"\n",
" data_list -> List of data sets CAM will be compared against, which\n",
" is simply the baseline case name in situations when\n",
" \"compare_obs\" is false.\n",
"\n",
" plot_location -> Location where plot files will be written to, which is\n",
" specified by \"cam_diag_plot_loc\".\n",
" Notes:\n",
" * This script runs annual/seasonal and global weighting.\n",
" * It will be pretty flexible for the variables plotted and layout of figure.\n",
" * This currently only works for single case comparison\n",
" - multi-case comparison is in the works. 02/2023 - JR\n",
" \"\"\"\n",
"\n",
" #Notify user that script has started:\n",
" print(\"\\n Generating time series plots...\")\n",
"\n",
" #Extract needed quantities from ADF object:\n",
" #-----------------------------------------\n",
"\n",
" # Case names\n",
" case_names = adfobj.get_cam_info('cam_case_name', required=True)\n",
" \n",
" # Case time series file locations\n",
" case_ts_loc = adfobj.get_cam_info(\"cam_ts_loc\", required=True)\n",
"\n",
" #Grab all case nickname(s)\n",
" test_nicknames = adfobj.case_nicknames[\"test_nicknames\"]\n",
" base_nickname = adfobj.case_nicknames[\"base_nickname\"]\n",
"\n",
" #CAUTION:\n",
" #\"data\" here refers to either obs or a baseline simulation,\n",
" #Until those are both treated the same (via intake-esm or similar)\n",
" #we will do a simple check and switch options as needed:\n",
" if adfobj.get_basic_info(\"compare_obs\"):\n",
"\n",
" #Extract variable-obs dictionary:\n",
" var_obs_dict = adfobj.var_obs_dict\n",
" base_nickname = \"Obs\"\n",
"\n",
" #If dictionary is empty, then there are no observations to compare against,\n",
" #so quit here:\n",
" if not var_obs_dict:\n",
" print(\"No observations found to plot against. So just the test case will be plotted.\\n\")\n",
" #Bundle all case names\n",
" all_case_names = case_names\n",
" #Gather all nicknames\n",
" all_nicknames = test_nicknames\n",
" case_ts_locs = case_ts_loc\n",
" all_nicknames = test_nicknames\n",
"\n",
" else:\n",
" data_name = adfobj.get_baseline_info(\"cam_case_name\")\n",
" data_ts_loc = adfobj.get_baseline_info(\"cam_ts_loc\")\n",
" #Bundle all case names\n",
" all_case_names = case_names + [data_name]\n",
" case_ts_locs = case_ts_loc + [data_ts_loc]\n",
" all_nicknames = test_nicknames + [base_nickname]\n",
" #End if\n",
"\n",
" #Get number of cases for plotting\n",
" case_num = len(all_case_names)\n",
"\n",
" #ADF variable which contains the output path for plots:\n",
" plot_location = adfobj.get_basic_info(\"cam_diag_plot_loc\")\n",
" plot_location = Path(plot_location)\n",
" #Check if plot output directory exists, and if not, then create it:\n",
" if not plot_location.is_dir():\n",
" print(f\"\\t '{plot_location}' not found, making new directory\")\n",
" plot_location.mkdir(parents=True)\n",
"\n",
" #Read in info from the variable defaults yaml file\n",
" res = adfobj.variable_defaults #dict of variable-specific plot preferences\n",
" #or an empty dictionary if use_defaults was not specified in config (YAML) file.\n",
"\n",
" #Set plot file type:\n",
" #-- this should be set in basic_info_dict, but is not required\n",
" #-- So check for it, and default to png\n",
" basic_info_dict = adfobj.read_config_var(\"diag_basic_info\", required=True)\n",
" plot_type = basic_info_dict.get('plot_type', 'png')\n",
" print(f\"\\t NOTE: Plot type is set to {plot_type}\")\n",
"\n",
" #Check if existing plots need to be redone\n",
" redo_plot = adfobj.get_basic_info('redo_plot')\n",
" print(f\"\\t NOTE: redo_plot is set to '{redo_plot}'\")\n",
"\n",
"\n",
" #Set up the plots\n",
" #################\n",
"\n",
" #Add more colors as needed for number of test cases\n",
" #** Baseline is already added as green dashed line in plotting function **\n",
" #matplotlib colors here: https://matplotlib.org/stable/gallery/color/named_colors.html\n",
" colors = [\"k\", \"aqua\", \"r\", \"b\", \"magenta\",\n",
" \"orange\", \"slategrey\", \"rosybrown\"]\n",
" \n",
" #Annual global weighted\n",
" #######################\n",
" season = \"ANN\"\n",
" print(f\"\\n Generating time series for {season}...\")\n",
"\n",
" #Loop over variables:\n",
" for var in var_list:\n",
" #Check res for any variable specific options that need to be used BEFORE going to the plot:\n",
" if var in res:\n",
" vres = res[var]\n",
" #If found then notify user, assuming debug log is enabled:\n",
" adfobj.debug_log(f\"time_series: Found variable defaults for {var}\")\n",
" else:\n",
" vres = {}\n",
" #End if\n",
"\n",
" fig = plt.figure(figsize=(12,8))\n",
" ax = fig.add_subplot(111)\n",
"\n",
" title_var = \"Global\"\n",
" print(f\"\\t - time series for {var}\")\n",
"\n",
" #Loop over test cases:\n",
" #----------------------\n",
" #Create lists to hold all min/max values for var data (for each case)\n",
" mins = []\n",
" maxs = []\n",
"\n",
" for case_idx, case_name in enumerate(all_case_names):\n",
" if case_name == data_name:\n",
" color_dict = {\"color\":'g',\"marker\":\"--\"}\n",
" else:\n",
" color_dict = {\"color\":colors[case_idx],\"marker\":\"-\"}\n",
" #End if\n",
" \n",
" #Generate input file path:\n",
" input_location = Path(case_ts_locs[case_idx])\n",
" ts_filenames = f'{case_name}.*.{var}.*nc'\n",
" ts_files = sorted(input_location.glob(ts_filenames))\n",
" data = _load_data(ts_files[0], var)\n",
"\n",
" #Extract units string, if available:\n",
" if hasattr(data, 'units'):\n",
" unit_str = data.units\n",
" else:\n",
" unit_str = '--'\n",
"\n",
" #Check if variable has a vertical coordinate:\n",
" if 'lev' in data.coords or 'ilev' in data.coords:\n",
" print(f\"\\t Variable '{var}' has a vertical dimension, \"+\\\n",
" \"which is currently not supported for the AMWG Table. Skipping...\")\n",
" #Skip this variable and move to the next variable in var_list:\n",
" continue\n",
" #End if\n",
"\n",
" # we should check if we need to do area averaging:\n",
" if len(data.dims) > 1:\n",
" # flags that we have spatial dimensions\n",
" # Note: that could be 'lev' which should trigger different behavior\n",
" # Note: we should be able to handle (lat, lon) or (ncol,) cases, at least\n",
" data = spatial_average(data) # changes data \"in place\"\n",
" \n",
" \n",
" #Get yearly averages for all available years\n",
" vals_case = annual_mean(data, whole_years=True, time_name='time')\n",
"\n",
" #Grab min and max vals from each test case\n",
" mins.append(np.nanmin(vals_case))\n",
" maxs.append(np.nanmax(vals_case))\n",
"\n",
" #Get int of years for plotting on x-axis\n",
" yrs = vals_case.year.values\n",
"\n",
" name = all_nicknames[case_idx]\n",
" #If case is baseline case, add text to legend string\n",
" if case_idx == (case_num-1):\n",
" name = f\"{name} (baseline)\"\n",
"\n",
" #Add case to plot (ax)\n",
" ax.plot(yrs, vals_case, color_dict[\"marker\"], c=color_dict[\"color\"],label=name)\n",
"\n",
" #For the minor ticks, use no labels; default NullFormatter.\n",
" ax.tick_params(which='major', length=7)\n",
" ax.tick_params(which='minor', length=5)\n",
" #End for (case names)\n",
"\n",
" #Set Main title for subplots:\n",
" ax.set_title(f\"Time Series {title_var}: {var} - {season}\",loc=\"left\")\n",
" \n",
" #Minor tweak to not plot variables that have vertical levels.\n",
" #TODO: Clean up this check - JR\n",
" #mins and maxs are blank lists when trying to ignore variables...\n",
" if (mins) or (maxs):\n",
" ax = _format_yaxis(ax, case_num, unit_str, **vres)\n",
" ax = _format_xaxis(ax, yrs)\n",
"\n",
" #Set up legend\n",
" fig = _make_fig_legend(case_num, fig)\n",
"\n",
" #Save plot\n",
" #plot_name = plot_loc / f\"{var}_{season}_TimeSeries_Mean.{plot_type}\"\n",
" plot_name = f\"./{var}_{season}_TimeSeries_Mean_ADF_tutorial.{plot_type}\"\n",
"\n",
" plt.savefig(plot_name, facecolor='w')\n",
" plt.close()\n",
"```\n",
"`````"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d21c43b5-5ef0-4895-8ccc-857785da2181",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "8bab4155-66fd-4d47-b653-e8d34a0d2414",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "6ce6766d-877f-4d8b-b1f6-a989ef10601d",
"metadata": {},
"source": [
"```{tip}\n",
"\n",
"```"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "NPL 2023b",
"language": "python",
"name": "npl-2023b"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}