From 0d55aa6913c46fbd6b11592ca95f8208e9c90d20 Mon Sep 17 00:00:00 2001 From: turner Date: Wed, 7 Aug 2019 18:35:20 +0000 Subject: [PATCH 01/11] Include a new Python script to plot timeseries data. --- configuration/scripts/tests/timeseries.py | 275 ++++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100755 configuration/scripts/tests/timeseries.py diff --git a/configuration/scripts/tests/timeseries.py b/configuration/scripts/tests/timeseries.py new file mode 100755 index 000000000..47acfac5b --- /dev/null +++ b/configuration/scripts/tests/timeseries.py @@ -0,0 +1,275 @@ +#!/usr/bin/env python + +''' +This script generates timeseries plots of CICE diagnostic output. +It is generated to replicate the previous timeseries.csh script. + +Written by: Matthew Turner +Date: August, 2019 +''' + +import os +import sys +import logging +import numpy as np + +def find_logfile(log_dir): + ''' + This function searches for the most recently created log file in the provided directory. + ''' + + logger.debug('Getting a list of files in {}'.format(log_dir)) + try: + path = '{}/logs'.format(log_dir.rstrip('/')) + files = [os.path.join(path,f) for f in os.listdir('{}/logs'.format(log_dir)) \ + if f.startswith('cice.runlog')] + except: + path = log_dir + files = [os.path.join(path,f) for f in os.listdir(log_dir) if f.startswith('cice.runlog')] + + # Check if any files were found. If not, exit + if len(files) == 0: + logger.error('No cice.runlog* files found. Please make sure you are passing the \ + correct directory.') + sys.exit(1) + + # Get the most recently created file + outfile = max(files, key = os.path.getctime) + + logger.debug('List of files = {}'.format([f for f in files])) + logger.debug('Most recent file is {}'.format(outfile)) + + return outfile + +def get_data(logfile,field): + ''' + This function extracts data from a CICE log file for the specific field. + ''' + import datetime + import re + + 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) + + 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) + if m1: + # Extract the current date-time group from the file + date = m1.group(2) + seconds = int(m1.group(3)) + hours = seconds // 3600 + minutes = (seconds - hours*3600) // 60 + leftover = seconds - hours*3600 - minutes*60 + curr_date = '{}-{:02d}:{:02d}:{:02d}'.format(date,hours,minutes,leftover) + dtg.append(datetime.datetime.strptime(curr_date, '%Y%m%d-%H:%M:%S')) + logger.debug('Currently on timestep {}'.format(dtg[-1])) + + m = re.search(my_regex, line) + if m: + # Extract the data from the file + if 'E' in m.group(1) or 'e' in m.group(1): + expon = True + else: + expon = False + arctic.append(float(m.group(1))) + antarctic.append(float(m.group(3))) + logger.debug(' Arctic = {}, Antarctic = {}'.format(arctic[-1], antarctic[-1])) + + return dtg, arctic, antarctic, expon + +def plot_timeseries(log, field, dtg, arctic, antarctic, expon, dtg_base=None, arctic_base=None, \ + antarctic_base=None, base_dir=None, grid=False): + ''' + Plot the timeseries data from the CICE log file + ''' + + casename = log.rstrip('/').rstrip('/logs').split('/')[-1] + if base_dir: + base_casename = base_dir.rstrip('/').rstrip('/logs').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 + # with '-v' + logging.getLogger('matplotlib').setLevel(logging.WARNING) + import matplotlib.pyplot as plt + import matplotlib.dates as mdates + import matplotlib.ticker as ticker + + fig = plt.figure(figsize=(12,8)) + ax = fig.add_axes([0.05,0.08,0.9,0.9]) + + # Add the arctic data to the plot + ax.plot(dtg,arctic,label='Arctic') + # Add the baseline arctic data to the plot, if available + if arctic_base: + ax.plot(dtg_base,arctic_base,label='Baseline Arctic') + + # Add the antarctic data to the plot + ax.plot(dtg,antarctic,label='Antarctic') + # Add the baseline antarctic data to the plot, if available + if antarctic_base: + ax.plot(dtg_base,antarctic_base,label='Baseline Antarctic') + + ax.set_xlabel('') + ax.set_title('{} Diagnostic Output'.format(field)) + ax.set_ylabel(field) + + # Format the x-axis labels + ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y/%m/%d')) + 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) + + ax.legend(loc='upper right') + + # 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)) + + # 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')) + else: + ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.5f')) + + # Rotate and right align the x labels + for tick in ax.get_xticklabels(): + tick.set_rotation(45) + + # 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) + except: + outfile = '{}_{}.png'.format(field_tmp.replace(' ','_'), casename) + logger.info('Saving file to {}'.format(outfile)) + plt.savefig(outfile,dpi=300,bbox_inches='tight') + +def main(): + import argparse + parser = argparse.ArgumentParser(description="To generate timeseries plots, this script \ + can be passed a directory containing a logs/ subdirectory, \ + or it can be run in the directory with the log files, \ + without being passed a directory. It will pull the \ + diagnostic data from the most recently modified log file.\ + \ + If no flags are passed selecting the variables to plot, \ + then plots will be created for all available variables.") + parser.add_argument('log_dir', nargs='?', default=os.getcwd(), \ + help="Path to diagnostic output log file directory.") + parser.add_argument('--bdir',dest='base_dir', help='Path to the directory that contains \ + the log file for a baseline dataset, if desired.') + 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?', \ + action='store_true') + parser.add_argument('--extent', dest='extent', help='Create a plot for total ice extent?', \ + action='store_true') + parser.add_argument('--volume', dest='ice_volume', help='Create a plot for total ice volume?', \ + action='store_true') + parser.add_argument('--snw_vol', dest='snow_volume', help='Create a plot for total snow \ + volume?', action='store_true') + parser.add_argument('--speed', dest='speed', help='Create a plot for rms ice speed?', \ + action='store_true') + parser.add_argument('--grid',dest='grid', help='Add grid lines to the figures?', \ + action='store_true') + + # Set the defaults for the command line options + parser.set_defaults(verbose=False) + parser.set_defaults(area=False) + parser.set_defaults(extent=False) + parser.set_defaults(ice_volume=False) + parser.set_defaults(snow_volume=False) + parser.set_defaults(speed=False) + parser.set_defaults(grid=False) + + args = parser.parse_args() + + # If no fields are passed, plot all fields + if not ( args.area or args.extent or args.ice_volume or args.snow_volume or args.speed ): + args.area = True + args.extent = True + args.ice_volume = True + args.snow_volume = True + args.speed = True + + # Build the fieldlist based on which fields are passed + fieldlist = [] + if args.area: + fieldlist.append('total ice area (km^2)') + if args.extent: + fieldlist.append('total ice extent(km^2)') + if args.ice_volume: + fieldlist.append('total ice volume (m^3)') + if args.snow_volume: + fieldlist.append('total snw volume (m^3)') + if args.speed: + fieldlist.append('rms ice speed (m/s)') + + # Setup the logger + global logger + if args.verbose: + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + # Find the test and baseline log files, based on the input directories. + log = find_logfile(args.log_dir) + logger.info('Log file = {}'.format(log)) + if args.base_dir: + base_log = find_logfile(args.base_dir) + logger.info('Base Log file = {}'.format(log)) + + # Loop through each field and create the plot + for field in fieldlist: + logger.debug('Current field = {}'.format(field)) + + # Get the data from the log files + dtg, arctic, antarctic, expon = get_data(log, field) + if args.base_dir: + dtg_base, arctic_base, antarctic_base, expon_base = get_data(base_log,field) + + # Plot the data + if args.base_dir: + plot_timeseries(args.log_dir, field, dtg, arctic, antarctic, expon, dtg_base, \ + arctic_base, antarctic_base, args.base_dir, grid=args.grid) + else: + plot_timeseries(args.log_dir, field, dtg, arctic, antarctic, expon, grid=args.grid) + +if __name__ == "__main__": + main() From 66fdb9b32e05bcf7933d031123571bc0f7d4330c Mon Sep 17 00:00:00 2001 From: turner Date: Thu, 8 Aug 2019 15:26:01 +0000 Subject: [PATCH 02/11] Update documentation to reflect the new timeseries.py script. Also modify cice.setup to automatically copy the timeseries.py script to the test suite directory. --- cice.setup | 1 + doc/source/developer_guide/dg_scripts.rst | 5 +- doc/source/user_guide/ug_testing.rst | 66 ++++++++++++++++++++--- 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/cice.setup b/cice.setup index d8c67f4ca..83d73fe78 100755 --- a/cice.setup +++ b/cice.setup @@ -378,6 +378,7 @@ else endif cp -f ${ICE_SCRIPTS}/tests/report_results.csh ${tsdir} cp -f ${ICE_SCRIPTS}/tests/timeseries.csh ${tsdir} + cp -f ${ICE_SCRIPTS}/tests/timeseries.py ${tsdir} cp -f ${ICE_SCRIPTS}/tests/poll_queue.csh ${tsdir} foreach file (${tsdir}/suite.run ${tsdir}/suite.submit) diff --git a/doc/source/developer_guide/dg_scripts.rst b/doc/source/developer_guide/dg_scripts.rst index 185616eab..c8cca31ef 100755 --- a/doc/source/developer_guide/dg_scripts.rst +++ b/doc/source/developer_guide/dg_scripts.rst @@ -123,8 +123,9 @@ and the files that describe with options files are needed for each test (ie. **t A baseline test script (**baseline.script**) is also there to setup the general regression and comparison testing. That directory also contains the preset test suites (ie. **base_suite.ts**) and a file that supports post-processing on the model -output (**timeseries.csh**). There is also a script **report_results.csh** that pushes results -from test suites back to the CICE-Consortium test results wiki page. +output (**timeseries.csh** and **timeseries.py**). There is also a script +**report_results.csh** that pushes results from test suites back to the CICE-Consortium +test results wiki page. To add a new test (for example newtest), several files may be needed, diff --git a/doc/source/user_guide/ug_testing.rst b/doc/source/user_guide/ug_testing.rst index 27393e3de..9322368ee 100644 --- a/doc/source/user_guide/ug_testing.rst +++ b/doc/source/user_guide/ug_testing.rst @@ -819,6 +819,7 @@ hemispheres, and must exceed a critical value nominally set to test and the Two-Stage test described in the previous section are provided in :cite:`Hunke18`. +.. _CodeCompliance Code Compliance Testing Procedure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -945,12 +946,23 @@ If the regression comparisons fail, then you may want to run the QC test, Test Plotting ---------------- -The CICE scripts include a script (``timeseries.csh``) that will generate timeseries -figures from a diagnostic output file. -When running a test suite, the ``timeseries.csh`` script is automatically copied to the suite directory. -If the ``timeseries.csh`` script is to be used on a test or case that is not a part of a test suite, -users will need to run the ``timeseries.csh`` script from the tests directory -(``./configuration/scripts/tests/timeseries.csh ./path/``), or copy it to a local directory. +The CICE scripts include two scripts (``timeseries.csh`` and ``timeseries.py``) that will +generate timeseries figures from a diagnostic output file. + +To use the ``timeseries.py`` script, the following requirements must be met: + +* Python v2.7 or later +* numpy Python package +* matplotlib Python package +* datetime Python package + +For informatino regarding configuring the Python environment, see CodeCompliance_. + +When running a test suite, the ``timeseries.csh`` and ``timeseries.py`` scripts are automatically +copied to the suite directory. +If the timeseries scripts are to be used on a test or case that is not a part of a test suite, +users will need to run the scripts from the tests directory +(e.g., ``./configuration/scripts/tests/timeseries.csh ./path/``), or copy it to a local directory. When used with the test suites or given a path, it needs to be run in the directory above the particular case being plotted, but it can also be run on isolated log files in the same directory, without a path. @@ -968,15 +980,18 @@ $ cd testsuite.t00 Run the timeseries script on the desired case. :: $ ./timeseries.csh /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ +$ or +$ python timeseries.py /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ -The output figures are placed in the directory where the ``timeseries.csh`` script is run. +The output figures are placed in the directory where the ``timeseries.csh`` or ``timeseries.py`` +script is run. 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 - timeseries.csh $dir + python timeseries.py $dir end @@ -988,3 +1003,38 @@ This plotting script can be used to plot the following variables: - total snow volume (:math:`m^3`) - RMS ice speed (:math:`m/s`) +The Python version of the timeseries script has some additional capability that the C-Shell +version does not have. Running ``python timeseries.py -h`` prints the following help information + +.. code-block:: bash + + usage: timeseries.py [-h] [--bdir BASE_DIR] [-v] [--area] [--extent] + [--volume] [--snw_vol] [--speed] [--grid] + [log_dir] + + To generate timeseries plots, this script can be passed a directory containing + a logs/ subdirectory, or it can be run in the directory with the log files, + without being passed a directory. It will pull the diagnostic data from the + most recently modified log file. If no flags are passed selecting the + variables to plot, then plots will be created for all available variables. + + positional arguments: + log_dir Path to diagnostic output log file directory. + + optional arguments: + -h, --help show this help message and exit + --bdir BASE_DIR Path to the directory that contains the log file for a + baseline dataset, if desired. + -v, --verbose Print debug output? + --area Create a plot for total ice area? + --extent Create a plot for total ice extent? + --volume Create a plot for total ice volume? + --snw_vol Create a plot for total snow volume? + --speed Create a plot for rms ice speed? + --grid Add grid lines to the figures? + +If ``--bdir`` is specified, the script with plot both a baseline dataset and a test case dataset. +If the ``--grid`` option is specified, then grid lines will be placed on the figures. +The ``--area``, ``--extent``, ``--volume``, ``--snw_vol``, and ``speed`` options allow the +user to specify which fields are to be plotted. If no fields are specified, then all fields +will be plotted. From 36457a420bede735dcb7aa536eaa21d6712d166c Mon Sep 17 00:00:00 2001 From: turner Date: Thu, 8 Aug 2019 15:39:27 +0000 Subject: [PATCH 03/11] Fix a type in the documentation, and update a newly added link to appear correctly --- doc/source/user_guide/ug_testing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/user_guide/ug_testing.rst b/doc/source/user_guide/ug_testing.rst index 9322368ee..e05d1982e 100644 --- a/doc/source/user_guide/ug_testing.rst +++ b/doc/source/user_guide/ug_testing.rst @@ -819,7 +819,7 @@ hemispheres, and must exceed a critical value nominally set to test and the Two-Stage test described in the previous section are provided in :cite:`Hunke18`. -.. _CodeCompliance +.. _CodeCompliance: Code Compliance Testing Procedure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -956,7 +956,7 @@ To use the ``timeseries.py`` script, the following requirements must be met: * matplotlib Python package * datetime Python package -For informatino regarding configuring the Python environment, see CodeCompliance_. +For information regarding configuring the Python environment, see :ref:`CodeCompliance`. When running a test suite, the ``timeseries.csh`` and ``timeseries.py`` scripts are automatically copied to the suite directory. From d935473eb5cca42232a8f49219548e5809fcd521 Mon Sep 17 00:00:00 2001 From: turner Date: Thu, 8 Aug 2019 15:45:56 +0000 Subject: [PATCH 04/11] Remove the code block around timeseries.py help output so that no highlighting occurs. --- doc/source/user_guide/ug_testing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/user_guide/ug_testing.rst b/doc/source/user_guide/ug_testing.rst index e05d1982e..ff9d0aafb 100644 --- a/doc/source/user_guide/ug_testing.rst +++ b/doc/source/user_guide/ug_testing.rst @@ -1006,7 +1006,7 @@ This plotting script can be used to plot the following variables: The Python version of the timeseries script has some additional capability that the C-Shell version does not have. Running ``python timeseries.py -h`` prints the following help information -.. code-block:: bash +:: usage: timeseries.py [-h] [--bdir BASE_DIR] [-v] [--area] [--extent] [--volume] [--snw_vol] [--speed] [--grid] From b5cbfd784f7afc5486731c445ee1af69bb6dd7a4 Mon Sep 17 00:00:00 2001 From: turner Date: Thu, 8 Aug 2019 15:48:36 +0000 Subject: [PATCH 05/11] Small tweak to formatting of timeseries.py documentation --- doc/source/user_guide/ug_testing.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/user_guide/ug_testing.rst b/doc/source/user_guide/ug_testing.rst index ff9d0aafb..98bff041b 100644 --- a/doc/source/user_guide/ug_testing.rst +++ b/doc/source/user_guide/ug_testing.rst @@ -980,7 +980,9 @@ $ cd testsuite.t00 Run the timeseries script on the desired case. :: $ ./timeseries.csh /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ -$ or + +or :: + $ python timeseries.py /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ The output figures are placed in the directory where the ``timeseries.csh`` or ``timeseries.py`` From 6af2ca9d1cb402a1f964a561a146e094bac45455 Mon Sep 17 00:00:00 2001 From: turner Date: Mon, 12 Aug 2019 15:01:37 +0000 Subject: [PATCH 06/11] Update the timeseries.py script to correctly print the baseline log file (if present) --- configuration/scripts/tests/timeseries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration/scripts/tests/timeseries.py b/configuration/scripts/tests/timeseries.py index 47acfac5b..5e16545bd 100755 --- a/configuration/scripts/tests/timeseries.py +++ b/configuration/scripts/tests/timeseries.py @@ -253,7 +253,7 @@ def main(): logger.info('Log file = {}'.format(log)) if args.base_dir: base_log = find_logfile(args.base_dir) - logger.info('Base Log file = {}'.format(log)) + logger.info('Base Log file = {}'.format(base_log)) # Loop through each field and create the plot for field in fieldlist: From ca265962f1662777426296ea8676d7bbb4fa3255 Mon Sep 17 00:00:00 2001 From: turner Date: Mon, 19 Aug 2019 12:10:33 +0000 Subject: [PATCH 07/11] Update the timeseries.py plotting script to plot units (title and labels) using LaTeX format. Additionally, ensure that the output file has a proper name no matter what directory the script is run from. --- configuration/scripts/tests/timeseries.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/configuration/scripts/tests/timeseries.py b/configuration/scripts/tests/timeseries.py index 5e16545bd..1c3083ff6 100755 --- a/configuration/scripts/tests/timeseries.py +++ b/configuration/scripts/tests/timeseries.py @@ -85,15 +85,19 @@ def get_data(logfile,field): return dtg, arctic, antarctic, expon +def latexit(string): + s = string[::-1].replace('(','($',1) + 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): ''' Plot the timeseries data from the CICE log file ''' - casename = log.rstrip('/').rstrip('/logs').split('/')[-1] + casename = os.path.abspath(log).rstrip('/').rstrip('/logs').split('/')[-1] if base_dir: - base_casename = base_dir.rstrip('/').rstrip('/logs').split('/')[-1] + base_casename = os.path.abspath(base_dir).rstrip('/').rstrip('/logs').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 @@ -119,8 +123,8 @@ def plot_timeseries(log, field, dtg, arctic, antarctic, expon, dtg_base=None, ar ax.plot(dtg_base,antarctic_base,label='Baseline Antarctic') ax.set_xlabel('') - ax.set_title('{} Diagnostic Output'.format(field)) - ax.set_ylabel(field) + ax.set_title('{} Diagnostic Output'.format(latexit(field))) + ax.set_ylabel(latexit(field)) # Format the x-axis labels ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y/%m/%d')) From 4bcb86910f27119e86b16c7afecdd25a27e6e20c Mon Sep 17 00:00:00 2001 From: turner Date: Tue, 20 Aug 2019 15:48:29 +0000 Subject: [PATCH 08/11] Move timeseries plotting scripts from configuration/scripts/tests to configuration/scripts --- .../scripts/{tests => }/timeseries.csh | 2 +- .../scripts/{tests => }/timeseries.py | 32 ++++++++++++++----- 2 files changed, 25 insertions(+), 9 deletions(-) rename configuration/scripts/{tests => }/timeseries.csh (98%) rename configuration/scripts/{tests => }/timeseries.py (89%) diff --git a/configuration/scripts/tests/timeseries.csh b/configuration/scripts/timeseries.csh similarity index 98% rename from configuration/scripts/tests/timeseries.csh rename to configuration/scripts/timeseries.csh index 0dd4f7bc2..cdd025efc 100755 --- a/configuration/scripts/tests/timeseries.csh +++ b/configuration/scripts/timeseries.csh @@ -76,7 +76,7 @@ foreach field ($fieldlist:q) set format = "%Y%m%d-%H" set output = `echo $fieldname | sed 's/ /_/g'` - set output = "${output}_${basename}.png" + set output = "${output}_${ICE_CASENAME}.png" echo "Plotting data for '$fieldname' and saving to $output" diff --git a/configuration/scripts/tests/timeseries.py b/configuration/scripts/timeseries.py similarity index 89% rename from configuration/scripts/tests/timeseries.py rename to configuration/scripts/timeseries.py index 1c3083ff6..8a09221bc 100755 --- a/configuration/scripts/tests/timeseries.py +++ b/configuration/scripts/timeseries.py @@ -194,9 +194,13 @@ def main(): If no flags are passed selecting the variables to plot, \ then plots will be created for all available variables.") parser.add_argument('log_dir', nargs='?', default=os.getcwd(), \ - help="Path to diagnostic output log file directory.") - parser.add_argument('--bdir',dest='base_dir', help='Path to the directory that contains \ - the log file for a baseline dataset, if desired.') + help="Path to diagnostic output log file. A specific log file can \ + be passed, or a case directory. If a directory is passed, \ + the most recent log file will be used.") + parser.add_argument('--bdir',dest='base_dir', help='Path to the the log file for a baseline \ + 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('-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?', \ @@ -253,10 +257,22 @@ def main(): logger = logging.getLogger(__name__) # Find the test and baseline log files, based on the input directories. - log = find_logfile(args.log_dir) + if os.path.isdir(args.log_dir): + logger.debug('{} is a directory'.format(args.log_dir)) + log = find_logfile(args.log_dir) + log_dir = args.log_dir + else: + 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: - base_log = find_logfile(args.base_dir) + if os.path.isdir(args.base_dir): + base_log = find_logfile(args.base_dir) + base_dir = args.base_dir + else: + base_log = args.base_dir + base_dir = args.base_dir.rsplit('/',1)[0] logger.info('Base Log file = {}'.format(base_log)) # Loop through each field and create the plot @@ -270,10 +286,10 @@ def main(): # Plot the data if args.base_dir: - plot_timeseries(args.log_dir, field, dtg, arctic, antarctic, expon, dtg_base, \ - arctic_base, antarctic_base, args.base_dir, grid=args.grid) + plot_timeseries(log_dir, field, dtg, arctic, antarctic, expon, dtg_base, \ + arctic_base, antarctic_base, base_dir, grid=args.grid) else: - plot_timeseries(args.log_dir, field, dtg, arctic, antarctic, expon, grid=args.grid) + plot_timeseries(log_dir, field, dtg, arctic, antarctic, expon, grid=args.grid) if __name__ == "__main__": main() From 890fd7f1e508882062750efb8c3cf7875243752a Mon Sep 17 00:00:00 2001 From: turner Date: Tue, 20 Aug 2019 15:50:14 +0000 Subject: [PATCH 09/11] chance cice.setup to copy the timeseries plotting scripts to every case directory instead of only test suite directories --- cice.setup | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cice.setup b/cice.setup index 83d73fe78..c732ea381 100755 --- a/cice.setup +++ b/cice.setup @@ -377,8 +377,6 @@ else exit -1 endif cp -f ${ICE_SCRIPTS}/tests/report_results.csh ${tsdir} - cp -f ${ICE_SCRIPTS}/tests/timeseries.csh ${tsdir} - cp -f ${ICE_SCRIPTS}/tests/timeseries.py ${tsdir} cp -f ${ICE_SCRIPTS}/tests/poll_queue.csh ${tsdir} foreach file (${tsdir}/suite.run ${tsdir}/suite.submit) @@ -534,7 +532,7 @@ EOF endif # from basic script dir to case - foreach file (cice.build cice.settings Makefile ice_in makdep.c setup_run_dirs.csh) + foreach file (cice.build cice.settings Makefile ice_in makdep.c setup_run_dirs.csh timeseries.csh timeseries.py) if !(-e ${ICE_SCRIPTS}/$file) then echo "${0}: ERROR, ${ICE_SCRIPTS}/$file not found" exit -1 From a361ddf1f631a07444844e3a4837406b92abdf2b Mon Sep 17 00:00:00 2001 From: turner Date: Tue, 20 Aug 2019 15:50:48 +0000 Subject: [PATCH 10/11] Update documentation pertaining to the timeseries plotting scripts --- doc/source/developer_guide/dg_scripts.rst | 8 +- doc/source/user_guide/ug_running.rst | 92 +++++++++++++++++++++ doc/source/user_guide/ug_testing.rst | 99 ----------------------- 3 files changed, 96 insertions(+), 103 deletions(-) diff --git a/doc/source/developer_guide/dg_scripts.rst b/doc/source/developer_guide/dg_scripts.rst index c8cca31ef..a23e7ee0d 100755 --- a/doc/source/developer_guide/dg_scripts.rst +++ b/doc/source/developer_guide/dg_scripts.rst @@ -31,6 +31,8 @@ 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 | **tests/** scripts for configuring and running basic tests .. _dev_strategy: @@ -122,10 +124,8 @@ setup the various tests, such as smoke and restart tests (**test_smoke.script**, and the files that describe with options files are needed for each test (ie. **test_smoke.files**, **test_restart.files**). A baseline test script (**baseline.script**) is also there to setup the general regression and comparison testing. That directory also contains the preset test suites -(ie. **base_suite.ts**) and a file that supports post-processing on the model -output (**timeseries.csh** and **timeseries.py**). There is also a script -**report_results.csh** that pushes results from test suites back to the CICE-Consortium -test results wiki page. +(ie. **base_suite.ts**) and a script (**report_results.csh**) that pushes results from +test suites back to the CICE-Consortium test results wiki page. To add a new test (for example newtest), several files may be needed, diff --git a/doc/source/user_guide/ug_running.rst b/doc/source/user_guide/ug_running.rst index b22e79628..f07b340d7 100644 --- a/doc/source/user_guide/ug_running.rst +++ b/doc/source/user_guide/ug_running.rst @@ -305,3 +305,95 @@ should be rebuilt before being resubmitted. It is always recommended that users modify the scripts and input settings in the case directory, NOT the run directory. In general, files in the run directory are overwritten by versions in the case directory when the model is built, submitted, and run. + +.. _timeseries: + +Timeseries Plotting +------------------- + +The CICE scripts include two scripts (``timeseries.csh`` and ``timeseries.py``) that will +generate timeseries figures from a diagnostic output file. + +To use the ``timeseries.py`` script, the following requirements must be met: + +* Python v2.7 or later +* numpy Python package +* matplotlib Python package +* datetime Python package + +For information regarding configuring the Python environment, see :ref:`CodeCompliance`. + +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. + +For example: + +Run the timeseries script on the desired case. :: + +$ ./timeseries.csh /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ + +or :: + +$ python timeseries.py /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ + +The output figures are placed in the directory where the ``timeseries.csh`` or ``timeseries.py`` +script is run. + +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 + + +This plotting script can be used to plot the following variables: + + - total ice area (:math:`km^2`) + - total ice extent (:math:`km^2`) + - total ice volume (:math:`m^3`) + - total snow volume (:math:`m^3`) + - RMS ice speed (:math:`m/s`) + +The Python version of the timeseries script has some additional capability that the C-Shell +version does not have. Running ``python timeseries.py -h`` prints the following help information + +:: + + usage: timeseries.py [-h] [--bdir BASE_DIR] [-v] [--area] [--extent] + [--volume] [--snw_vol] [--speed] [--grid] + [log_dir] + + To generate timeseries plots, this script can be passed a directory containing + a logs/ subdirectory, or it can be run in the directory with the log files, + without being passed a directory. It will pull the diagnostic data from the + most recently modified log file. If no flags are passed selecting the + variables to plot, then plots will be created for all available variables. + + positional arguments: + log_dir Path to diagnostic output log file. A specific log file can + be passed, or a case directory. If a directory is passed, + the most recent log file will be used. + + optional arguments: + -h, --help show this help message and exit + --bdir BASE_DIR Path to the the log file for a baseline 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. + -v, --verbose Print debug output? + --area Create a plot for total ice area? + --extent Create a plot for total ice extent? + --volume Create a plot for total ice volume? + --snw_vol Create a plot for total snow volume? + --speed Create a plot for rms ice speed? + --grid Add grid lines to the figures? + +If ``--bdir`` is specified, the script with plot both a baseline dataset and a test case dataset. +If the ``--grid`` option is specified, then grid lines will be placed on the figures. +The ``--area``, ``--extent``, ``--volume``, ``--snw_vol``, and ``speed`` options allow the +user to specify which fields are to be plotted. If no fields are specified, then all fields +will be plotted. diff --git a/doc/source/user_guide/ug_testing.rst b/doc/source/user_guide/ug_testing.rst index 98bff041b..e17d83281 100644 --- a/doc/source/user_guide/ug_testing.rst +++ b/doc/source/user_guide/ug_testing.rst @@ -941,102 +941,3 @@ If the regression comparisons fail, then you may want to run the QC test, INFO:__main__:Quality Control Test PASSED -.. _testplotting: - -Test Plotting ----------------- - -The CICE scripts include two scripts (``timeseries.csh`` and ``timeseries.py``) that will -generate timeseries figures from a diagnostic output file. - -To use the ``timeseries.py`` script, the following requirements must be met: - -* Python v2.7 or later -* numpy Python package -* matplotlib Python package -* datetime Python package - -For information regarding configuring the Python environment, see :ref:`CodeCompliance`. - -When running a test suite, the ``timeseries.csh`` and ``timeseries.py`` scripts are automatically -copied to the suite directory. -If the timeseries scripts are to be used on a test or case that is not a part of a test suite, -users will need to run the scripts from the tests directory -(e.g., ``./configuration/scripts/tests/timeseries.csh ./path/``), or copy it to a local directory. -When used with the test suites or given a path, it needs to be run in the directory -above the particular case being plotted, but it can also be run on isolated log files in the same directory, -without a path. - -For example: - -Run the test suite. :: - -$ ./cice.setup -m conrad -e intel --suite base_suite --testid t00 - -Wait for suite to finish then go to the directory. :: - -$ cd testsuite.t00 - -Run the timeseries script on the desired case. :: - -$ ./timeseries.csh /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ - -or :: - -$ python timeseries.py /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ - -The output figures are placed in the directory where the ``timeseries.csh`` or ``timeseries.py`` -script is run. - -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 - - -This plotting script can be used to plot the following variables: - - - total ice area (:math:`km^2`) - - total ice extent (:math:`km^2`) - - total ice volume (:math:`m^3`) - - total snow volume (:math:`m^3`) - - RMS ice speed (:math:`m/s`) - -The Python version of the timeseries script has some additional capability that the C-Shell -version does not have. Running ``python timeseries.py -h`` prints the following help information - -:: - - usage: timeseries.py [-h] [--bdir BASE_DIR] [-v] [--area] [--extent] - [--volume] [--snw_vol] [--speed] [--grid] - [log_dir] - - To generate timeseries plots, this script can be passed a directory containing - a logs/ subdirectory, or it can be run in the directory with the log files, - without being passed a directory. It will pull the diagnostic data from the - most recently modified log file. If no flags are passed selecting the - variables to plot, then plots will be created for all available variables. - - positional arguments: - log_dir Path to diagnostic output log file directory. - - optional arguments: - -h, --help show this help message and exit - --bdir BASE_DIR Path to the directory that contains the log file for a - baseline dataset, if desired. - -v, --verbose Print debug output? - --area Create a plot for total ice area? - --extent Create a plot for total ice extent? - --volume Create a plot for total ice volume? - --snw_vol Create a plot for total snow volume? - --speed Create a plot for rms ice speed? - --grid Add grid lines to the figures? - -If ``--bdir`` is specified, the script with plot both a baseline dataset and a test case dataset. -If the ``--grid`` option is specified, then grid lines will be placed on the figures. -The ``--area``, ``--extent``, ``--volume``, ``--snw_vol``, and ``speed`` options allow the -user to specify which fields are to be plotted. If no fields are specified, then all fields -will be plotted. From 3dc49a065560682ace5c62bb55ada774d87eba95 Mon Sep 17 00:00:00 2001 From: turner Date: Thu, 22 Aug 2019 19:21:22 +0000 Subject: [PATCH 11/11] Update documentation for timeseries plotting scripts --- configuration/scripts/timeseries.py | 4 +- doc/source/user_guide/ug_running.rst | 100 +++++++++++++-------------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/configuration/scripts/timeseries.py b/configuration/scripts/timeseries.py index 8a09221bc..2b50c373a 100755 --- a/configuration/scripts/timeseries.py +++ b/configuration/scripts/timeseries.py @@ -196,7 +196,9 @@ def main(): parser.add_argument('log_dir', nargs='?', default=os.getcwd(), \ help="Path to diagnostic output log file. A specific log file can \ be passed, or a case directory. If a directory is passed, \ - the most recent log file will be used.") + the most recent log file will be used. If no directory or \ + file is passed, the script will look for a log file in the \ + current directory.") parser.add_argument('--bdir',dest='base_dir', help='Path to the the log file for a baseline \ dataset, if desired. A specific log file or case directory can \ be passed. If a directory is passed, the most recent log file \ diff --git a/doc/source/user_guide/ug_running.rst b/doc/source/user_guide/ug_running.rst index f07b340d7..69c31aa4c 100644 --- a/doc/source/user_guide/ug_running.rst +++ b/doc/source/user_guide/ug_running.rst @@ -311,8 +311,11 @@ directory when the model is built, submitted, and run. Timeseries Plotting ------------------- -The CICE scripts include two scripts (``timeseries.csh`` and ``timeseries.py``) that will -generate timeseries figures from a diagnostic output file. +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. To use the ``timeseries.py`` script, the following requirements must be met: @@ -321,25 +324,53 @@ To use the ``timeseries.py`` script, the following requirements must be met: * matplotlib Python package * datetime Python package -For information regarding configuring the Python environment, see :ref:`CodeCompliance`. +See :ref:`CodeCompliance` for additional information about how to setup the Python +environment, but we recommend using ``pip`` as follows: :: + + 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: + + - 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. + For example: Run the timeseries script on the desired case. :: -$ ./timeseries.csh /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ +$ python timeseries.py /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ -or :: +or :: -$ python timeseries.py /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/ +$ 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.csh`` or ``timeseries.py`` -script is run. +The output figures are placed in the directory where the ``timeseries.py`` script is run. + +The 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`) + - total ice extent (:math:`km^2`) + - total ice volume (:math:`m^3`) + - total snow volume (:math:`m^3`) + - RMS ice speed (:math:`m/s`) + +For example, to plot only total ice volume and total snow volume :: + +$ 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 :: @@ -349,51 +380,18 @@ To generate plots for all of the cases within a suite with a testid, create and 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. -This plotting script can be used to plot the following variables: +For the latest help information for the script, run :: - - total ice area (:math:`km^2`) - - total ice extent (:math:`km^2`) - - total ice volume (:math:`m^3`) - - total snow volume (:math:`m^3`) - - RMS ice speed (:math:`m/s`) +$ python timeseries.py -h -The Python version of the timeseries script has some additional capability that the C-Shell -version does not have. Running ``python timeseries.py -h`` prints the following help information +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. -:: +To use the C-Shell version of the script, :: - usage: timeseries.py [-h] [--bdir BASE_DIR] [-v] [--area] [--extent] - [--volume] [--snw_vol] [--speed] [--grid] - [log_dir] - - To generate timeseries plots, this script can be passed a directory containing - a logs/ subdirectory, or it can be run in the directory with the log files, - without being passed a directory. It will pull the diagnostic data from the - most recently modified log file. If no flags are passed selecting the - variables to plot, then plots will be created for all available variables. - - positional arguments: - log_dir Path to diagnostic output log file. A specific log file can - be passed, or a case directory. If a directory is passed, - the most recent log file will be used. - - optional arguments: - -h, --help show this help message and exit - --bdir BASE_DIR Path to the the log file for a baseline 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. - -v, --verbose Print debug output? - --area Create a plot for total ice area? - --extent Create a plot for total ice extent? - --volume Create a plot for total ice volume? - --snw_vol Create a plot for total snow volume? - --speed Create a plot for rms ice speed? - --grid Add grid lines to the figures? - -If ``--bdir`` is specified, the script with plot both a baseline dataset and a test case dataset. -If the ``--grid`` option is specified, then grid lines will be placed on the figures. -The ``--area``, ``--extent``, ``--volume``, ``--snw_vol``, and ``speed`` options allow the -user to specify which fields are to be plotted. If no fields are specified, then all fields -will be plotted. +$ ./timeseries.csh /p/work1/turner/CICE_RUNS/conrad_intel_smoke_col_1x1_diag1_run1year.t00/