From 2771786a4c8cbd9d776d5a7628eb24c911c46506 Mon Sep 17 00:00:00 2001 From: Tony Craig Date: Thu, 30 May 2024 18:30:50 -0700 Subject: [PATCH] Update CICE plotting tools. Add ciceplots.csh and ciceplots2d.py. (#958) Update CICE plotting tools. Add ciceplots.csh and ciceplots2d.py. Update timeseries.py. Remove timeseries.csh. Have the plotting tools copied into case directories. Update documentation. ciceplots2d.py generates global and polar plots for fields on CICE history files. ciceplots.csh is a general script that calls timeseries.py and ciceplots2d.py to generate plots for a user defined case. Add basemap to the cice conda environment.yml file to support the plotting packages. Add output to JRA55_files in ice_forcing.F90 to make it easier to understand when/why JRA55 files are missing. Add NO_CDF5 CPP to izumi_nag Macros file. Change 10 year production test case to 8 year test. This will serve as basis for release results. Update Icepack to #083d6e3cf42198 from May 28, 2024. Includes updates to Icepack plotting tools. --- cice.setup | 2 +- cicecore/cicedyn/general/ice_forcing.F90 | 3 + configuration/scripts/ciceplots.csh | 56 +++++++ configuration/scripts/ciceplots2d.py | 148 ++++++++++++++++++ .../scripts/machines/Macros.izumi_nag | 2 +- .../scripts/machines/environment.yml | 3 + .../scripts/options/set_nml.run8year | 7 + configuration/scripts/tests/prod_suite.ts | 2 +- configuration/scripts/timeseries.csh | 129 --------------- configuration/scripts/timeseries.py | 100 +++++++----- doc/source/developer_guide/dg_scripts.rst | 5 +- doc/source/user_guide/ug_running.rst | 104 +++++------- icepack | 2 +- 13 files changed, 323 insertions(+), 240 deletions(-) create mode 100755 configuration/scripts/ciceplots.csh create mode 100755 configuration/scripts/ciceplots2d.py create mode 100644 configuration/scripts/options/set_nml.run8year delete mode 100755 configuration/scripts/timeseries.csh diff --git a/cice.setup b/cice.setup index 2fd68cd18..0e574f803 100755 --- a/cice.setup +++ b/cice.setup @@ -838,7 +838,7 @@ EOF endif # from basic script dir to case - foreach file (cice.build cice.settings Makefile ice_in makdep.c setup_run_dirs.csh timeseries.csh timeseries.py) + foreach file (cice.build cice.settings Makefile ice_in makdep.c setup_run_dirs.csh ciceplots.csh ciceplots2d.py timeseries.py) if !(-e ${ICE_SCRIPTS}/$file) then echo "${0}: ERROR, ${ICE_SCRIPTS}/$file not found" exit -1 diff --git a/cicecore/cicedyn/general/ice_forcing.F90 b/cicecore/cicedyn/general/ice_forcing.F90 index b977f54aa..241bf8b5d 100755 --- a/cicecore/cicedyn/general/ice_forcing.F90 +++ b/cicecore/cicedyn/general/ice_forcing.F90 @@ -2276,6 +2276,9 @@ subroutine JRA55_files(yr) enddo if (.not.exists) then + write(nu_diag,*) subname,' atm_data_dir = ',trim(atm_data_dir) + write(nu_diag,*) subname,' atm_data_type_prefix = ',trim(atm_data_type_prefix) + write(nu_diag,*) subname,' atm_data_version = ',trim(atm_data_version) call abort_ice(error_message=subname//' could not find forcing file') endif diff --git a/configuration/scripts/ciceplots.csh b/configuration/scripts/ciceplots.csh new file mode 100755 index 000000000..43528b33e --- /dev/null +++ b/configuration/scripts/ciceplots.csh @@ -0,0 +1,56 @@ +#!/bin/csh -f + +source ${MODULESHOME}/init/csh + +# User defined stuff +# Set case and case directory +# Set files, notes, fstr, and fields + +set case = "CICE6.5.1" +set casedir = "/glade/derecho/scratch/tcraig/CICE_RUNS/cgx1proda" + +# setup plots + +set histdir = "${casedir}/history" + +set files = ("${histdir}/iceh.2012-03.nc" \ + "${histdir}/iceh.2012-09.nc" ) +set notes = ("2012 March Mean" \ + "2012 Sept Mean" ) +set fstrs = ("Mar12" \ + "Sep12" ) + +set fields = ("aice" "hi" "hs") + +#conda config --add channels conda-forge +#conda config --set channel_priority strict +#conda search basemap --channel conda-forge +#conda create -p /glade/u/home/tcraig/conda/envs/basemap -c conda-forge basemap=1.4.1 basemap-data basemap-data-hires netCDF4 + +module load conda +source ${NCAR_ROOT_CONDA}/etc/profile.d/conda.csh + +conda activate /glade/u/home/tcraig/conda/envs/basemap + +echo " " +echo " " + +echo ./timeseries.py \"${casedir}\" --case \"${case}\" --grid +./timeseries.py "${casedir}" --case "${case}" --grid + +echo " " + +set cnt = 0 +while ($cnt < ${#files}) + @ cnt = $cnt + 1 + set file = "${files[$cnt]}" + set note = "${notes[$cnt]}" + set fstr = "${fstrs[$cnt]}" + foreach field ($fields) + echo ./ciceplots2d.py \"$field\" \"$file\" \"$case\" \"$note\" \"$fstr\" + ./ciceplots2d.py "$field" "$file" "$case" "$note" "$fstr" + end +end + +echo "DONE" + diff --git a/configuration/scripts/ciceplots2d.py b/configuration/scripts/ciceplots2d.py new file mode 100755 index 000000000..2ad73e66f --- /dev/null +++ b/configuration/scripts/ciceplots2d.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 + +#Importing the necessary libraries +import sys +import os +import numpy as np +from netCDF4 import Dataset +import matplotlib as mpl +import matplotlib.pyplot as plt +from mpl_toolkits.basemap import Basemap + +if len(sys.argv) != 6: + print("ciceplots.py requires 5 arguments") + print(" 1. field name in file, ie. \"aice\"") + print(" 2. cice history file full path, ie. \"/glade/scratch/user/case/history/iceh.2012-03.nc\"") + print(" 3. case name, used to annotate plot, ie. \"CICE6.5.1\"") + print(" 4. notes, used to annotate plot, ie. 2012 \"March Mean\"") + print(" 5. file string, use to create unique png filenames, ie. \"Mar12\"") + quit() + +field = sys.argv[1] +pathf = sys.argv[2] +casen = sys.argv[3] +notes = sys.argv[4] +fstr = sys.argv[5] +fname = os.path.basename(pathf) +title = field + " " + notes +cfnam = casen + " " + fname +#print("field = ",field) +#print("pathf = ",pathf) +#print("casen = ",casen) +#print("notes = ",notes) +#print("fname = ",fname) +#print("title = ",title) +#print("cfnam = ",cfnam) + +#Reading the netCDF file +data = Dataset(pathf,'r') +#print (data) + +lons = data.variables['TLON'][:,:] +lats = data.variables['TLAT'][:,:] +var1 = data.variables[field][:,:,:] +var1 = var1[0,:,:] +var1[ var1==0.00 ] = np.nan +#mask = data.variables['tmask'][:,:] +#mask[ mask>0.5 ] = np.nan + +#print("lons.shape = ",lons.shape) +#print("var1.shape = ",var1.shape) + +# Lon/Lat Projection + +#print("Plot global") +#m = Basemap(projection='cyl',llcrnrlat=-90,urcrnrlat=90, +# llcrnrlon=0,urcrnrlon=360,resolution='c') +m = Basemap(projection='cyl',llcrnrlat=-90,urcrnrlat=90, + llcrnrlon=0,urcrnrlon=360,resolution='l') +fig, ax = plt.subplots() +#plt.figure(figsize=(6,4)) +m.drawcoastlines(linewidth=0.2) +m.fillcontinents(color='black',lake_color='white') +#draw parallels and meridians. +m.drawparallels(np.arange(-60.,61.,30.),labels=[1,0,0,0]) +m.drawmeridians(np.arange(0.,361.,45.),labels=[1,0,0,1]) +#draw map boundary +m.drawmapboundary(fill_color='white') +#setting colorbar +cmap = plt.get_cmap('jet') +barticks = None +norm = "linear" +if field in ['hi']: + bounds = np.arange(0,2.05,0.1) + bounds = np.append(bounds,[2.25,2.5,2.75,3.0,3.25,3.5,3.75,4.0]) + norm = mpl.colors.BoundaryNorm(bounds,cmap.N,extend='max') + barticks=[0,0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0] +if field in ['hs']: + bounds = np.arange(0,1.02,0.05) + bounds = np.append(bounds,[1.5,2.0,2.5,3.0,3.5,4.0]) + norm = mpl.colors.BoundaryNorm(bounds,cmap.N,extend='max') + barticks=[0,0.25,0.5,0.75,1.0,2.0,3.0,4.0] +#matplotlib scatter-plot +m.scatter(lons,lats,c=var1,cmap=cmap,marker='o',s=0.2,norm=norm) +m.colorbar(label=field, ticks=barticks) +plt.rcParams["figure.dpi"] = 300 +plt.title(title) +plt.text(x=0.0,y=-0.1,s=cfnam,transform=ax.transAxes,horizontalalignment='left',verticalalignment='top',fontsize='x-small') +oname = field + "_gl_" + fstr + ".png" +print('Saving file to ',oname) +plt.savefig(oname) +#plt.show() +plt.close() + +# North Polar Stereographic Projection + +#print("Plot NH") +#m = Basemap(projection='npstere',boundinglat=45,lon_0=-45,resolution='c') +m = Basemap(projection='npstere',boundinglat=45,lon_0=-45,resolution='l') +fig, ax = plt.subplots() +#plt.figure(figsize=(6,4)) +m.drawcoastlines(linewidth=0.2) +m.fillcontinents(color='black',lake_color='white') +# draw parallels and meridians. +m.drawparallels(np.arange(-60.,61.,30.),labels=[0,0,0,0]) +m.drawmeridians(np.arange(0.,361.,45.),labels=[0,0,0,0]) +m.drawmapboundary(fill_color='white') +#setting colorbar (set above) +m.scatter(lons,lats,c=var1,cmap=cmap,marker='o',s=0.2,latlon=True,norm=norm) +#m.colorbar(label=field) +m.colorbar(label=field, ticks=barticks) +plt.rcParams["figure.dpi"] = 300 +plt.title (title) +plt.text(x=0.0,y=-0.02,s=cfnam,transform=ax.transAxes,horizontalalignment='left',verticalalignment='top',fontsize='x-small') +oname = field + "_nh_" + fstr + ".png" +print('Saving file to ',oname) +plt.savefig(oname) +#plt.show() +plt.close() + +# South Polar Stereographic Projection + +#print("Plot SH") +#m = Basemap(projection='npstere',boundinglat=45,lon_0=-45,resolution='c') +m = Basemap(projection='spstere',boundinglat=-45,lon_0=180,resolution='l') +fig, ax = plt.subplots() +#plt.figure(figsize=(6,4)) +m.drawcoastlines(linewidth=0.2) +m.fillcontinents(color='black',lake_color='white') +# draw parallels and meridians. +m.drawparallels(np.arange(-60.,61.,30.),labels=[0,0,0,0]) +m.drawmeridians(np.arange(0.,361.,45.),labels=[0,0,0,0]) +m.drawmapboundary(fill_color='white') +#setting colorbar (set above) +m.scatter(lons,lats,c=var1,cmap=cmap,marker='o',s=0.2,latlon=True,norm=norm) +#m.colorbar(label=field) +m.colorbar(label=field, ticks=barticks) +plt.rcParams["figure.dpi"] = 300 +plt.title (title) +plt.text(x=0.0,y=-0.02,s=cfnam,transform=ax.transAxes,horizontalalignment='left',verticalalignment='top',fontsize='x-small') +oname = field + "_sh_" + fstr + ".png" +print('Saving file to ',oname) +plt.savefig(oname) +#plt.show() +plt.close() + +#print("Done") +quit() + diff --git a/configuration/scripts/machines/Macros.izumi_nag b/configuration/scripts/machines/Macros.izumi_nag index c12edb904..9265c9de1 100644 --- a/configuration/scripts/machines/Macros.izumi_nag +++ b/configuration/scripts/machines/Macros.izumi_nag @@ -3,7 +3,7 @@ #============================================================================== CPP := /usr/bin/cpp -CPPDEFS := -DFORTRANUNDERSCORE -DNO_R16 $(ICE_CPPDEFS) +CPPDEFS := -DFORTRANUNDERSCORE -DNO_R16 -DNO_CDF5 $(ICE_CPPDEFS) CFLAGS := -c FIXEDFLAGS := -fixed diff --git a/configuration/scripts/machines/environment.yml b/configuration/scripts/machines/environment.yml index 30ed1e148..119bf7ea0 100644 --- a/configuration/scripts/machines/environment.yml +++ b/configuration/scripts/machines/environment.yml @@ -15,6 +15,9 @@ dependencies: - matplotlib-base - cartopy - netcdf4 + - basemap=1.4.1 + - basemap-data + - basemap-data-hires # Python dependencies for building the HTML documentation - sphinx - sphinxcontrib-bibtex diff --git a/configuration/scripts/options/set_nml.run8year b/configuration/scripts/options/set_nml.run8year new file mode 100644 index 000000000..1515fa7c9 --- /dev/null +++ b/configuration/scripts/options/set_nml.run8year @@ -0,0 +1,7 @@ +npt_unit = 'y' +npt = 8 +dumpfreq = 'y' +dumpfreq_n = 1 +diagfreq = 24 +histfreq = 'm','x','x','x','x' + diff --git a/configuration/scripts/tests/prod_suite.ts b/configuration/scripts/tests/prod_suite.ts index 877fa1ce6..5e62e94ea 100644 --- a/configuration/scripts/tests/prod_suite.ts +++ b/configuration/scripts/tests/prod_suite.ts @@ -1,6 +1,6 @@ # Test Grid PEs Sets BFB-compare qcchk gx3 72x1 qc,qcchk,medium qcchk_gx3_72x1_medium_qc_qcchk qcchk gx1 144x1 qc,qcchk,medium -smoke gx1 144x2 gx1prod,long,run10year +smoke gx1 128x2 gx1prod,long,run8year qcchk gx3 72x1 qc,qcchkf,medium,alt02 qcchk_gx3_72x1_medium_qc_qcchk qcchk gx3 72x1 qc,qcchk,dt3456s,medium qcchk_gx3_72x1_medium_qc_qcchk diff --git a/configuration/scripts/timeseries.csh b/configuration/scripts/timeseries.csh deleted file mode 100755 index b6b3fcf2e..000000000 --- a/configuration/scripts/timeseries.csh +++ /dev/null @@ -1,129 +0,0 @@ -#!/bin/csh - -# Check to see if test case directory was passed -if ( $1 == "-h" ) then - echo "To generate timeseries plots, this script can be passed a directory" - echo "containing a logs/ subdirectory, or it can be run in the directory with" - echo "the log files, without being passed a directory." - echo "Example: ./timeseries.csh ./annual_gx3_conrad_4x1.t00" - echo "Example: ./timeseries.csh" - echo "It will pull the diagnostic data from the most recently modified log file." - exit -1 -endif -set basename = `echo $1 | sed -e 's#/$##' | sed -e 's/^\.\///'` - -# Set x-axis limits - # Manually set x-axis limits -#set xrange = 'set xrange ["19980101":"19981231"]' - # Let gnuplot determine x-axis limits -set xrange = '' - -# Determine if BASELINE dataset exists -if ( $1 == "" ) then -set basefile_dir = "IGNORE" -else -source $1/cice.settings -set basefile_dir = "$ICE_BASELINE/$ICE_BASECOM/$ICE_TESTNAME" -endif - -if ( -d $basefile_dir ) then - set num_basefile = `ls $basefile_dir | grep cice.runlog | wc -l` - if ( $num_basefile > 0 ) then - set baseline_exists = 1 - foreach file ($basefile_dir/cice.runlog.*) - set base_logfile = $file - end - else - set baseline_exists = 0 - endif -else - set baseline_exists = 0 -endif - -set fieldlist=("total ice area (km^2)" \ - "total ice extent(km^2)" \ - "total ice volume (m^3)" \ - "total snw volume (m^3)" \ - "rms ice speed (m/s)" ) - -# Get the filename for the latest log -if ( $1 == "" ) then -foreach file (./cice.runlog.*) - set logfile = $file -end -else -foreach file ($1/logs/cice.runlog.*) - set logfile = $file -end -endif - -# Loop through each field and create the plot -foreach field ($fieldlist:q) - # Add backslashes before (, ), and ^ for grep searches - set search_name = "`echo '$field' | sed 's/(/\\(/' | sed 's/)/\\)/' | sed 's/\^/\\^/'`" - set fieldname = `echo "$field" | sed -e 's/([^()]*)//g'` - set search = "'$search_name'\|istep1" - rm -f data.txt - foreach line ("`egrep $search $logfile`") - if ("$line" =~ *"istep1"*) then - set argv = ( $line ) - set date = $4 - @ hour = ( $6 / 3600 ) - else - set data1 = `echo $line | rev | cut -d ' ' -f2 | rev` - set data2 = `echo $line | rev | cut -d ' ' -f1 | rev` - echo "$date-$hour,$data1,$data2" >> data.txt - endif - end - set format = "%Y%m%d-%H" - - set output = `echo $fieldname | sed 's/ /_/g'` - set output = "${output}_${ICE_CASENAME}.png" - - echo "Plotting data for '$fieldname' and saving to $output" - -# Call the plotting routine, which uses the data in the data.txt file -gnuplot << EOF > $output -# Plot style -set style data points - -set datafile separator "," - -# Term type and background color, canvas size -set terminal png size 1920,960 - -# x-axis -set xdata time -set timefmt "$format" -set format x "%Y/%m/%d" - -# Axis tick marks -set xtics rotate - -set title "$field (Diagnostic Output)" -set ylabel "$field" -set xlabel "Simulation Day" - -set key left top - -# Set x-axlis limits -$xrange - -if ( $baseline_exists == 1 ) \ - plot "data_baseline.txt" using (timecolumn(1)):2 with lines lw 2 lt 2 lc 2 title \ - "Arctic - Baseline", \ - "" using (timecolumn(1)):3 with lines lw 2 lt 2 lc 5 title "Antarctic - Baseline", \ - "data.txt" using (timecolumn(1)):2 with lines lw 2 lt 1 lc 1 title "Arctic", \ - "" using (timecolumn(1)):3 with lines lw 2 lt 1 lc 3 title "Antarctic"; \ -else \ - plot "data.txt" using (timecolumn(1)):2 with lines lw 2 lt 1 lc 1 title "Arctic", \ - "" using (timecolumn(1)):3 with lines lw 2 lt 1 lc 3 title "Antarctic" \ - -EOF - -# Delete the data file -rm -f data.txt -if ( $baseline_exists ) then - rm -f data_baseline.txt -endif -end diff --git a/configuration/scripts/timeseries.py b/configuration/scripts/timeseries.py index 2c36cea73..c53106071 100755 --- a/configuration/scripts/timeseries.py +++ b/configuration/scripts/timeseries.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 ''' This script generates timeseries plots of CICE diagnostic output. @@ -51,16 +51,16 @@ def get_data(logfile,field): logger.debug('Extracting data for {}'.format(field)) # Build the regular expression to extract the data - field_regex = field.replace('(','\(').replace('^','\^').replace(')','\)') - number_regex = '[-+]?\d+\.?\d+([eE][-+]?\d+)?' - my_regex = '^{}\s+=\s+({})\s+({})'.format(field_regex,number_regex,number_regex) + field_regex = field.replace('(','\\(').replace('^','\\^').replace(')','\\)') + number_regex = r'[-+]?\d+\.?\d+([eE][-+]?\d+)?' + my_regex = r'^{}\s+=\s+({})\s+({})'.format(field_regex,number_regex,number_regex) dtg = [] arctic = [] antarctic = [] with open(logfile) as f: for line in f.readlines(): - m1 = re.search('istep1:\s+(\d+)\s+idate:\s+(\d+)\s+sec:\s+(\d+)', line) + m1 = re.search(r'istep1:\s+(\d+)\s+idate:\s+(\d+)\s+sec:\s+(\d+)', line) if m1: # Extract the current date-time group from the file date = m1.group(2) @@ -83,6 +83,11 @@ def get_data(logfile,field): antarctic.append(float(m.group(3))) logger.debug(' Arctic = {}, Antarctic = {}'.format(arctic[-1], antarctic[-1])) + # remove first few elements of dtg + if len(dtg) > len(arctic): + stind = len(dtg) - len(arctic) + dtg = dtg[stind:] + return dtg, arctic, antarctic, expon def latexit(string): @@ -90,15 +95,17 @@ def latexit(string): return (s.replace(')','$)',1))[::-1] def plot_timeseries(log, field, dtg, arctic, antarctic, expon, dtg_base=None, arctic_base=None, \ - antarctic_base=None, base_dir=None, grid=False): + antarctic_base=None, base_dir=None, grid=False, casename=None, base_casename=None): ''' Plot the timeseries data from the CICE log file ''' import re - casename = re.sub(r"/logs", "", os.path.abspath(log).rstrip('/')).split('/')[-1] + if casename is None: + casename = re.sub(r"/logs", "", os.path.abspath(log).rstrip('/')).split('/')[-1] if base_dir: - base_casename = re.sub(r"/logs", "", os.path.abspath(base_dir).rstrip('/')).split('/')[-1] + if base_casename is None: + base_casename = re.sub(r"/logs", "", os.path.abspath(base_dir).rstrip('/')).split('/')[-1] # Load the plotting libraries, but set the logging level for matplotlib # to WARNING so that matplotlib debugging info is not printed when running @@ -108,7 +115,8 @@ def plot_timeseries(log, field, dtg, arctic, antarctic, expon, dtg_base=None, ar import matplotlib.dates as mdates import matplotlib.ticker as ticker - fig = plt.figure(figsize=(12,8)) +# fig = plt.figure(figsize=(12,8)) + fig = plt.figure(figsize=(6,4)) ax = fig.add_axes([0.05,0.08,0.9,0.9]) # Add the arctic data to the plot @@ -132,55 +140,54 @@ def plot_timeseries(log, field, dtg, arctic, antarctic, expon, dtg_base=None, ar ax.xaxis.set_minor_locator(mdates.MonthLocator()) # Add a text box that prints the test case name and the baseline case name (if given) - try: - text_field = "Test/Case: {}\nBaseline: {}".format(casename,base_casename) - from matplotlib.offsetbox import AnchoredText - anchored_text = AnchoredText(text_field,loc=2) - ax.add_artist(anchored_text) - except: - text_field = "Test/Case: {}".format(casename) - from matplotlib.offsetbox import AnchoredText - anchored_text = AnchoredText(text_field,loc=2) - ax.add_artist(anchored_text) + if base_casename is None: + text_field = "{}".format(casename) + else: + text_field = "{}\n{}".format(casename,base_casename) + + from matplotlib.offsetbox import AnchoredText + anchored_text = AnchoredText(text_field,loc='upper left') + anchored_text.patch.set_alpha(0.5) + ax.add_artist(anchored_text) - ax.legend(loc='upper right') + ax.legend(loc='upper right',framealpha=0.5) # Add grid lines if the `--grid` argument was passed at the command line. if grid: ax.grid(ls='--') # Reduce the number of ticks on the y axis - nbins = 10 - try: - minval = min( \ - min(min(arctic), min(antarctic)), \ - min(min(arctic_base), min(antarctic_base))) - maxval = max( \ - max(max(arctic), max(antarctic)), \ - max(max(arctic_base), max(antarctic_base))) - except: - minval = min(min(arctic), min(antarctic)) - maxval = max(max(arctic), max(antarctic)) - step = (maxval-minval)/nbins - ax.yaxis.set_ticks(np.arange(minval, maxval+step, step)) +# nbins = 10 +# try: +# minval = min( \ +# min(min(arctic), min(antarctic)), \ +# min(min(arctic_base), min(antarctic_base))) +# maxval = max( \ +# max(max(arctic), max(antarctic)), \ +# max(max(arctic_base), max(antarctic_base))) +# except: +# minval = min(min(arctic), min(antarctic)) +# maxval = max(max(arctic), max(antarctic)) +# step = (maxval-minval)/nbins +# ax.yaxis.set_ticks(np.arange(minval, maxval+step, step)) # Format the y-axis tick labels, based on whether or not the values in the log file # are in scientific notation or float notation. if expon: - ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.3e')) + ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1e')) else: - ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.5f')) + ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.3f')) # Rotate and right align the x labels for tick in ax.get_xticklabels(): - tick.set_rotation(45) + tick.set_rotation(30) # Create an output file and save the figure field_tmp = field.split('(')[0].rstrip() try: - outfile = '{}_{}_base-{}.png'.format(field_tmp.replace(' ','_'), casename,base_casename) + outfile = '{}_{}_base-{}.png'.format(field_tmp.replace(' ','_'),casename.replace(' ','_'),base_casename.replace(' ','_')) except: - outfile = '{}_{}.png'.format(field_tmp.replace(' ','_'), casename) + outfile = '{}_{}.png'.format(field_tmp.replace(' ','_'), casename.replace(' ','_')) logger.info('Saving file to {}'.format(outfile)) plt.savefig(outfile,dpi=300,bbox_inches='tight') @@ -204,6 +211,10 @@ def main(): dataset, if desired. A specific log file or case directory can \ be passed. If a directory is passed, the most recent log file \ will be used.') + parser.add_argument('--case', dest='casename', help='User specified casename for plots.', \ + action='store') + parser.add_argument('--basecase', dest='base_casename', help='User specified base casename \ + for plots.', action='store') parser.add_argument('-v', '--verbose', dest='verbose', help='Print debug output?', \ action='store_true') parser.add_argument('--area', dest='area', help='Create a plot for total ice area?', \ @@ -227,6 +238,8 @@ def main(): parser.set_defaults(snow_volume=False) parser.set_defaults(speed=False) parser.set_defaults(grid=False) + parser.set_defaults(casename=None) + parser.set_defaults(base_casename=None) args = parser.parse_args() @@ -268,7 +281,7 @@ def main(): logger.debug('{} is a file'.format(args.log_dir)) log = args.log_dir log_dir = args.log_dir.rsplit('/',1)[0] - logger.info('Log file = {}'.format(log)) + if args.base_dir: if os.path.isdir(args.base_dir): base_log = find_logfile(args.base_dir) @@ -278,6 +291,9 @@ def main(): base_dir = args.base_dir.rsplit('/',1)[0] logger.info('Base Log file = {}'.format(base_log)) + logger.info('casename = {}'.format(args.casename)) + logger.info('Log file = {}'.format(log)) + # Loop through each field and create the plot for field in fieldlist: logger.debug('Current field = {}'.format(field)) @@ -290,9 +306,11 @@ def main(): # Plot the data if args.base_dir: plot_timeseries(log_dir, field, dtg, arctic, antarctic, expon, dtg_base, \ - arctic_base, antarctic_base, base_dir, grid=args.grid) + arctic_base, antarctic_base, base_dir, grid=args.grid, \ + casename=args.casename, base_casename=args.base_casename) else: - plot_timeseries(log_dir, field, dtg, arctic, antarctic, expon, grid=args.grid) + plot_timeseries(log_dir, field, dtg, arctic, antarctic, expon, grid=args.grid, \ + casename=args.casename) if __name__ == "__main__": main() diff --git a/doc/source/developer_guide/dg_scripts.rst b/doc/source/developer_guide/dg_scripts.rst index dac5e9a52..d4b29acbb 100644 --- a/doc/source/developer_guide/dg_scripts.rst +++ b/doc/source/developer_guide/dg_scripts.rst @@ -22,6 +22,8 @@ The directory structure under configure/scripts is as follows. | **cice.run.setup.csh** sets up the run scripts | **cice.settings** defines environment, model configuration and run settings | **cice.test.setup.csh** creates configurations for testing the model +| **ciceplots.csh** general script to generate timeseries and 2d CICE plots +| **ciceplots2d.py** python script to generate 2d CICE plots | **ice_in** namelist input data | **machines/** machine specific files to set env and Macros | **makdep.c** determines module dependencies @@ -31,8 +33,7 @@ The directory structure under configure/scripts is as follows. | **parse_settings.sh** replaces settings with command-line configuration | **setup_run_dirs.csh** creates the case run directories | **set_version_number.csh** updates the model version number from the **cice.setup** command line -| **timeseries.csh** generates PNG timeseries plots from output files, using GNUPLOT -| **timeseries.py** generates PNG timeseries plots from output files, using Python +| **timeseries.py** python script to generate timeseries plots from CICE log file | **tests/** scripts for configuring and running basic tests .. _dev_strategy: diff --git a/doc/source/user_guide/ug_running.rst b/doc/source/user_guide/ug_running.rst index 7ac37f16e..021c5bcbe 100644 --- a/doc/source/user_guide/ug_running.rst +++ b/doc/source/user_guide/ug_running.rst @@ -845,12 +845,13 @@ A few notes about the conda configuration: mpirun -np ${ntasks} --oversubscribe ./cice >&! \$ICE_RUNLOG_FILE - It is not recommeded to run other test suites than ``quick_suite`` or ``travis_suite`` on a personal computer. -- The conda environment is automatically activated when compiling or running the model using the ``./cice.build`` and ``./cice.run`` scripts in the case directory. These scripts source the file ``env.conda_{linux.macos}``, which calls ``conda activate cice``. +- If needed, the conda environment is automatically activated when compiling or running the model using the ``./cice.build`` and ``./cice.run`` scripts in the case directory. These scripts source the file ``env.conda_{linux.macos}``, which calls ``conda activate cice``. - To use the "cice" conda environment with the Python plotting (see :ref:`timeseries`) and quality control (QC) scripts (see :ref:`CodeValidation`), you must manually activate the environment: .. code-block:: bash cd ~/cice-dirs/cases/case1 + conda env create -f configuration/scripts/machines/environment.yml --force conda activate cice python timeseries.py ~/cice-dirs/cases/case1/logs conda deactivate # to deactivate the environment @@ -955,58 +956,49 @@ in shell startup files or otherwise at users discretion: .. _timeseries: -Timeseries Plotting +Plotting Tools ------------------- -The CICE scripts include two scripts that will generate timeseries figures from a -diagnostic output file, a Python version (``timeseries.py``) and a csh version -(``timeseries.csh``). Both scripts create the same set of plots, but the Python -script has more capabilities, and it's likely that the csh -script will be removed in the future. +CICE includes a couple of simple scripts to generate plots. The ``timeseries.py`` +scripts generates northern and southern hemisphere timeseries plots for several +fields from the CICE log file. The ``ciceplots2d.py`` script generates some +two-dimensional plots from CICE history files as global and polar projections. +The script ``ciceplots.csh`` is a general script that sets up the inputs for the +python plotting tools and calls them. Both python tools produce png files. -To use the ``timeseries.py`` script, the following requirements must be met: +To use the python scripts, the following python packages are required: -* Python v2.7 or later -* numpy Python package -* matplotlib Python package -* datetime Python package +* Python3 +* numpy +* matplotlib +* re +* datetime +* netcdf4 +* basemap, basemap-data, basemap-data-hires -See :ref:`CodeValidation` for additional information about how to setup the Python -environment, but we recommend using ``pip`` as follows: :: +The easist way to install the package is via the cice env file provided with CICE via conda: - pip install --user numpy - pip install --user matplotlib - pip install --user datetime - -When creating a case or test via ``cice.setup``, the ``timeseries.csh`` and -``timeseries.py`` scripts are automatically copied to the case directory. -Alternatively, the plotting scripts can be found in ``./configuration/scripts``, and can be -run from any directory. - -The Python script can be passed a directory, a specific log file, or no directory at all: + .. code-block:: bash - - If a directory is passed, the script will look either in that directory or in - directory/logs for a filename like cice.run*. As such, users can point the script - to either a case directory or the ``logs`` directory directly. The script will use - the file with the most recent creation time. - - If a specific file is passed the script parses that file, assuming that the file - matches the same form of cice.run* files. - - If nothing is passed, the script will look for log files or a ``logs`` directory in the - directory from where the script was run. + conda env create -f configuration/scripts/machines/environment.yml --force + conda activate cice -For example: +Then edit the ``ciceplots.csh`` script and run it. ``ciceplots.csh`` also demonstrates +how to call each python script separately. -Run the timeseries script on the desired case. :: +When creating a case or test via ``cice.setup``, these three plotting scripts +are automatically copied to the case directory. +Alternatively, the plotting scripts can be found in ``./configuration/scripts`` and can +be run as needed. -$ python timeseries.py /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ +Briefly, the ``timeseries.py`` script has a few options but can be called as follows: -or :: + .. code-block:: bash -$ python timeseries.py /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/logs - -The output figures are placed in the directory where the ``timeseries.py`` script is run. + ./timeseries.py /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00 --grid --case CICE6.0.1 -The plotting script will plot the following variables by default, but you can also select +The timeseries script parses the log file, so the temporal resolution is based on the log output frequency. +The timeseries plotting script will plot the following variables by default, but you can also select specific plots to create via the optional command line arguments. - total ice area (:math:`km^2`) @@ -1015,30 +1007,14 @@ specific plots to create via the optional command line arguments. - total snow volume (:math:`m^3`) - RMS ice speed (:math:`m/s`) -For example, to plot only total ice volume and total snow volume :: +The ``ciceplots2d.py`` script is called as follows: -$ python timeseries.py /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ --volume --snw_vol - -To generate plots for all of the cases within a suite with a testid, create and run a script such as :: - - #!/bin/csh - foreach dir (`ls -1 | grep testid`) - echo $dir - python timeseries.py $dir - end - -Plots are only made for a single output file at a time. The ability to plot output from -a series of cice.run* files is not currently possible, but may be added in the future. -However, using the ``--bdir`` option will plot two datasets (from log files) on the -same figure. - -For the latest help information for the script, run :: - -$ python timeseries.py -h - -The ``timeseries.csh`` script works basically the same way as the Python version, however it -does not include all of the capabilities present in the Python version. + .. code-block:: bash -To use the C-Shell version of the script, :: + ./ciceplots2d.py aice /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/history/iceh.2005-09.nc CICE6.0.1 "Sept 2005 Mean" 2005Sep -$ ./timeseries.csh /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ +In the example above, a global, northern hemisphere, and southern hemisphere plot would be created +for the aice field from iceh.2005-09.nc file. Titles on the plot would reference CICE6.0.1 and +"Sept 2005 Mean" and the png files would contain the string 2005Sep as well as the field name and region. +The two-dimensional plots are generated using the scatter feature from matplotlib, so they are fairly +primitive. diff --git a/icepack b/icepack index ae69b8069..083d6e3cf 160000 --- a/icepack +++ b/icepack @@ -1 +1 @@ -Subproject commit ae69b806990ef2412e2f714c5b4ba4c096b163b6 +Subproject commit 083d6e3cf42198bc7b4ffd1f02063c4c5b35b639