[Ncep.list.nems.announce] nems r92559: NEMS updates:

Samuel.Trahan at noaa.gov Samuel.Trahan at noaa.gov
Tue May 9 19:43:05 UTC 2017


1. Minor changes required for
 NEMSfv3gf...
Message-ID: <59121bc9.PqNQr61r4zaqv31u%Samuel.Trahan at noaa.gov>
User-Agent: Heirloom mailx 12.4 7/29/08
MIME-Version: 1.0
Content-Type: multipart/mixed;
 boundary="=_59121bc9.mxbY+RCbbOnLoVlWRkmLNZdlDm0kOUig//vPB7KM/+3cESLJ"

This is a multi-part message in MIME format.

--=_59121bc9.mxbY+RCbbOnLoVlWRkmLNZdlDm0kOUig//vPB7KM/+3cESLJ
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Friendly NEMS developers,

This is an automated email about a NEMS commit.

Project: nems
URL: https://svnemc.ncep.noaa.gov/projects/nems/trunk
Revision: 92559
Author:   samuel.trahan at noaa.gov
Date:     2017-05-09T19:40:36.593606Z
Message:
NEMS updates:

1. Minor changes required for NEMSfv3gfs support for jet.
   Specifically:

   1a. The compset runner can auto-detect a Jet project
       with available CPU hours.
   1b. It refuses to auto-detect a Jet scrub area.  Jet has no concept
       of "scrub areas."  The script now gives instructions for how to
       specify a scrub area if the user does not do so.

2. Allow NEMS/doc to be built without an application-level doc

3. Replace tests/rt.sh and NEMSCompsetRun with simple wrappers around
   tests/rtgen.  The tests/rtgen now understands both calling
   conventions.

4. Safeguards against using the wrong produtil

5. Capability of resuming an aborted workflow in NEMSCompsetRun by
   using the new --resume argument, instead of the two step process
   required before.

6. Point to the top of produtil

_M   .
_M   tests
M    tests/rtgen
M    tests/rt.sh
M    doc/Makefile
_M   src


See attached file for full differences.


First 4000 bytes of differences:
Index: checkout/tests/rtgen
===================================================================
--- checkout/tests/rtgen	(revision 90943)
+++ checkout/tests/rtgen	(revision 92559)
@@ -1,5 +1,7 @@
 #! /usr/bin/env python
 
+import fileinput
+import glob
 import os
 import sys
 import re
@@ -31,7 +33,7 @@
 help=NO
 sleep_time=300
 zero_exit=NO
-qoutq_more=''
+qoutql_more=''
 for arg in "$@" ; do
   case "$arg" in
     -v|--verbose) verbose=$(( verbose + 1 )) ;;
@@ -39,7 +41,7 @@
     --loop) loop=YES ;;
     --help) help=YES ;;
     --zero-exit) zero_exit=YES ;;
-    -n) qoutq_more='-n' ;;
+    -n) qoutql_more='-n' ;;
     *)
        bad="$arg: invalid argument $bad"
   esac
@@ -145,13 +147,13 @@
           log "workflow is still running and no jobs have failed."
       fi
   fi
-  if [[ "$have_qoutq" == YES ]] ; then
-      job_count=$( qoutq -UL .queue_state  $qoutq_more -Cd rtgen.$UNIQUE_ID | wc -l )
+  if [[ "$have_qoutql" == YES ]] ; then
+      job_count=$( qoutql -UL .queue_state  $qoutql_more -Cd rtgen.$UNIQUE_ID | wc -l )
       if [[ "$verbose" -gt 0 ]] ; then
           verbose "sleep 2"
           sleep 2
           verbose "get queue information"
-          qoutq -UL .queue_state $qoutq_more -Cd rtgen.$UNIQUE_ID
+          qoutql -UL .queue_state $qoutql_more -Cd rtgen.$UNIQUE_ID
       fi
       if [[ "$unchange" -gt 2 && "$job_count" < 1 && "$lostdead" -gt 0 ]] ; then
           log "Jobs have FAILED and no jobs are running or submitted."
@@ -173,28 +175,37 @@
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Attempt to get the produtil package:
 try:
+    import produtil.testing
     import produtil.setup
 except ImportError as ie:
     altpath=os.path.join(os.path.dirname(os.path.realpath(__file__)),'produtil/ush')
     if not os.path.isdir(altpath):
-        fail('%s is missing and produtil is not in PYTHONPATH.  Is your produtil external missing?'%(altpath,))
-    sys.path.append(altpath)
-    import produtil.setup
+        fail('%s is missing and a valid produtil is not in PYTHONPATH.  Is your produtil external missing?'%(altpath,))
+        sys.path.append(altpath)
+    try:
+        import produtil.testing
+        import produtil.setup
+    except ImportError as ie2:
+        if not os.path.isdir(altpath):
+            fail('%s is missing and a valid produtil is not in PYTHONPATH.  Is your produtil external missing?'%(altpath,))
 
-import produtil.run, produtil.cluster
+import produtil.run, produtil.cluster, produtil.fileop
 
 from produtil.log import jlogger
-from produtil.run import runstr, ExitStatusException, checkrun, batchexe
+from produtil.run import runstr, ExitStatusException, checkrun, batchexe, run
 from produtil.testing.testgen import TestGen
 from produtil.testing.utilities import BASELINE, EXECUTION, bashify_string
 from produtil.testing.rocoto import RocotoRunner
 from produtil.testing.setarith import ArithKeyError
 
-# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
-# Utility routines:
+########################################################################
 
-TOP_OF_USAGE_MESSAGE='''Syntax: rtgen [options] [subset]
+# Usage messages
 
+##@var RTGEN_TOP_OF_USAGE_MESSAGE
+# Text placed at the top of both the short and full rtgen usage messages
+RTGEN_TOP_OF_USAGE_MESSAGE='''Syntax: rtgen [options] [subset]
+
 Generates an NCEP three-tier workflow structure to run the specified
 regression tests or compsets.  The user must then run some scripts
 inside that directory to execute the tests and report the results.
@@ -201,11 +212,22 @@
 If no subset is requested, all known tests are run.
 '''
 
-SHORT_USAGE_MESSAGE=TOP_OF_USAGE_MESSAGE+'''
+##@var RTGEN_USAGE_MESSAGE
+# Text of rtgen's short usage message
+#
+# This text is sent to stderr by the rtgen_usage() function.  It tells
+# the user the purpose of the program, but does not go into details about
+# the calling conventions.
+RTGEN_SHORT_USAGE_MESSAGE=RTGEN_TOP_OF_USAGE_MESSAGE+'''
 Run with


... see attachment for the rest ...

--=_59121bc9.mxbY+RCbbOnLoVlWRkmLNZdlDm0kOUig//vPB7KM/+3cESLJ
Content-Type: text/plain;
 charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="92559.diff"

Index: checkout/tests/rtgen
===================================================================
--- checkout/tests/rtgen	(revision 90943)
+++ checkout/tests/rtgen	(revision 92559)
@@ -1,5 +1,7 @@
 #! /usr/bin/env python
 
+import fileinput
+import glob
 import os
 import sys
 import re
@@ -31,7 +33,7 @@
 help=NO
 sleep_time=300
 zero_exit=NO
-qoutq_more=''
+qoutql_more=''
 for arg in "$@" ; do
   case "$arg" in
     -v|--verbose) verbose=$(( verbose + 1 )) ;;
@@ -39,7 +41,7 @@
     --loop) loop=YES ;;
     --help) help=YES ;;
     --zero-exit) zero_exit=YES ;;
-    -n) qoutq_more='-n' ;;
+    -n) qoutql_more='-n' ;;
     *)
        bad="$arg: invalid argument $bad"
   esac
@@ -145,13 +147,13 @@
           log "workflow is still running and no jobs have failed."
       fi
   fi
-  if [[ "$have_qoutq" == YES ]] ; then
-      job_count=$( qoutq -UL .queue_state  $qoutq_more -Cd rtgen.$UNIQUE_ID | wc -l )
+  if [[ "$have_qoutql" == YES ]] ; then
+      job_count=$( qoutql -UL .queue_state  $qoutql_more -Cd rtgen.$UNIQUE_ID | wc -l )
       if [[ "$verbose" -gt 0 ]] ; then
           verbose "sleep 2"
           sleep 2
           verbose "get queue information"
-          qoutq -UL .queue_state $qoutq_more -Cd rtgen.$UNIQUE_ID
+          qoutql -UL .queue_state $qoutql_more -Cd rtgen.$UNIQUE_ID
       fi
       if [[ "$unchange" -gt 2 && "$job_count" < 1 && "$lostdead" -gt 0 ]] ; then
           log "Jobs have FAILED and no jobs are running or submitted."
@@ -173,28 +175,37 @@
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 # Attempt to get the produtil package:
 try:
+    import produtil.testing
     import produtil.setup
 except ImportError as ie:
     altpath=os.path.join(os.path.dirname(os.path.realpath(__file__)),'produtil/ush')
     if not os.path.isdir(altpath):
-        fail('%s is missing and produtil is not in PYTHONPATH.  Is your produtil external missing?'%(altpath,))
-    sys.path.append(altpath)
-    import produtil.setup
+        fail('%s is missing and a valid produtil is not in PYTHONPATH.  Is your produtil external missing?'%(altpath,))
+        sys.path.append(altpath)
+    try:
+        import produtil.testing
+        import produtil.setup
+    except ImportError as ie2:
+        if not os.path.isdir(altpath):
+            fail('%s is missing and a valid produtil is not in PYTHONPATH.  Is your produtil external missing?'%(altpath,))
 
-import produtil.run, produtil.cluster
+import produtil.run, produtil.cluster, produtil.fileop
 
 from produtil.log import jlogger
-from produtil.run import runstr, ExitStatusException, checkrun, batchexe
+from produtil.run import runstr, ExitStatusException, checkrun, batchexe, run
 from produtil.testing.testgen import TestGen
 from produtil.testing.utilities import BASELINE, EXECUTION, bashify_string
 from produtil.testing.rocoto import RocotoRunner
 from produtil.testing.setarith import ArithKeyError
 
-# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
-# Utility routines:
+########################################################################
 
-TOP_OF_USAGE_MESSAGE='''Syntax: rtgen [options] [subset]
+# Usage messages
 
+##@var RTGEN_TOP_OF_USAGE_MESSAGE
+# Text placed at the top of both the short and full rtgen usage messages
+RTGEN_TOP_OF_USAGE_MESSAGE='''Syntax: rtgen [options] [subset]
+
 Generates an NCEP three-tier workflow structure to run the specified
 regression tests or compsets.  The user must then run some scripts
 inside that directory to execute the tests and report the results.
@@ -201,11 +212,22 @@
 If no subset is requested, all known tests are run.
 '''
 
-SHORT_USAGE_MESSAGE=TOP_OF_USAGE_MESSAGE+'''
+##@var RTGEN_USAGE_MESSAGE
+# Text of rtgen's short usage message
+#
+# This text is sent to stderr by the rtgen_usage() function.  It tells
+# the user the purpose of the program, but does not go into details about
+# the calling conventions.
+RTGEN_SHORT_USAGE_MESSAGE=RTGEN_TOP_OF_USAGE_MESSAGE+'''
 Run with -h for full instructions.
 '''
 
-FULL_USAGE_MESSAGE=TOP_OF_USAGE_MESSAGE+'''
+##@var RTGEN_USAGE_MESSAGE
+# Text of rtgen's full usage message
+#
+# This text is sent to stderr by the rtgen_full_usage() function.  It
+# lists all calling convention information for the rtgen program.
+RTGEN_FULL_USAGE_MESSAGE=RTGEN_TOP_OF_USAGE_MESSAGE+'''
 --SUBSETS--
   {gfs_slg,nmm_cntrl} -- run gfs_slg and nmm_cntrl tests
   wam                 -- run all wam tests
@@ -256,27 +278,114 @@
 '''
 
 
-def full_usage():
-    print FULL_USAGE_MESSAGE
+def rtgen_full_usage():
+    """!Sends to stdout full usage information.
+
+    Prints the RTGEN_FULL_USAGE_MESSAGE to stdout, and exits with
+    status 0.  Status 0 is used, to indicate successful execution,
+    since the request to the program is to print the usage
+    information.
+
+    @returns never; exits program with status 0"""
+    print RTGEN_FULL_USAGE_MESSAGE
     sys.exit(0)
 
-def usage(reason):
-    sys.stderr.write(SHORT_USAGE_MESSAGE)
+def rtgen_usage(reason):
+    """!Sends to stderr brief usage information, possibly followed by
+    an error message. 
+
+    Sends the short usage message (RTGEN_SHORT_USAGE_MESSAGE) to
+    stderr, explaining the program's purpose and how to get more
+    information.  If a reason is given, then it is sent to stderr
+    after the string "SCRIP IS ABORTING BECAUSE," followed by non-zero
+    exit.  With no reason argument, the exit is 0
+
+    @returns never; exits program
+    @param reason the reason we are aborting, or None to indicate
+    nothing went wrong"""
+    sys.stderr.write(RTGEN_SHORT_USAGE_MESSAGE)
     if reason:
         sys.stderr.write('\nSCRIPT IS ABORTING BECAUSE: %s\n'%(reason,))
         exit(1)
     exit(0)
 
-def initial_checks():
-    """!Performs basic sanity checks, such as whether some input files
-    are present."""
-    if not os.path.isfile('produtil/ush/testgen.py'):
-        usage('file produtil/ush/testgen.py does not exist.  Are you '
-              'missing the produtil external?')
-    if not os.path.isdir('../../NEMS/tests'):
-        usage('directory ../../NEMS/tests does not exist.  This must '
-              'be part of a NEMS app checkout.')
+RTSH_USAGE_TOP="""Usage: NEMSCompsetRun [options] [test spec [test spec [...] ] ]
 
+Runs the specified set of tests, and either generates a baseline or
+verifies against an old baseline."""
+
+RTSH_USAGE_SIMPLE=RTSH_USAGE_TOP+"""  Run with -h for full usage info.
+"""
+
+RTSH_USAGE_FULL=RTSH_USAGE_TOP+"""
+
+Special modes:
+
+  --dry-run                      = just print what would be done
+  --resume /path/to/scrub/rtgen.# 
+  -r /path/to/scrub/rtgen.#
+     = continue running a workflow in this directory
+  -h | --help                    = print this message
+ 
+Test selection options:
+
+  -r /path/rtgen.# | --resume /path/rtgen.#
+        = continue the test in /path/rtgen.# without making a new workflow
+  -f = run all tests (same as test spec '*' )
+  -s = run standard tests (same as test spec "standard")
+  -c SPEC = make baseline for SPEC (same as --baseline SPEC)
+  -t SPEC = run tests in SPEC (-t is superfluous)
+  SPEC = run these tests
+
+Usage and path options:
+
+  --mode=baseline | --baseline   = generate a new baseline instead of verifying
+  -p project | --project project = project to use for CPU time
+  --temp-dir /path/to/tmp        = scrub area for execution (parent of rtgen.#)
+  -n /path/to/baseline | --baseline-dir /path/to/baseline
+        = specify the location of the baseline to create or verify against
+
+Test SPECifications:
+
+  {test1,test2,test3} = run the tests "test1," "test2," and "test3"
+  gfs = run all tests in set "gfs"
+  * = run all tests (remember to put quotes around this!)
+  union(nested,physics) = run all tests in the "nested" and "physics" sets
+  inter(fv3,nested) = run all nested fv3 tests
+  minus(fv3,nested) = run all fv3 tests except nested tests
+
+Examples:
+
+Generate baseline for all fv3 nested tests.  Use a specified temp
+directory and baseline area:
+
+.../NEMSCompsetRun --temp-dir /lfs3/projects/hfv3gfs/$USER/scrub \\
+    --baseline --baseline-dir /lfs3/projects/hfv3gfs/$USER/new-baseline \\
+    'union(nested,fv3)'
+
+Run all gsm tests that are not wam tests.  Automatically decide temp
+areas and use default baseline location.  Run all tests in the avn project.
+
+.../NEMSComspetRun -p avn 'minus(gsm,wam)'
+"""
+
+def rtsh_usage(reason):
+    sys.stderr.write(RTSH_USAGE_SIMPLE)
+    if reason:
+        sys.stderr.write('\nSCRIPT IS ABORTING BECAUSE: %s\n'%(reason,))
+        exit(1)
+    exit(0)
+
+def rtsh_full_usage():
+    print RTSH_USAGE_FULL
+    exit(0)
+
+usage = None
+
+########################################################################
+
+########################################################################
+
 def username():
     """!Returns the current username.  This uses the process's user id
     and the pwent database, hence it is less vulnerable to errors than
@@ -285,6 +394,109 @@
     @returns the process's current username"""
     return pwd.getpwuid(os.getuid()).pw_name
 
+class RDHPCSAccountParams(object):
+    """!Runs the account_params program and parses the output."""
+    def __init__(self):
+        super(RDHPCSAccountParams,self).__init__()
+        self.logger=logging.getLogger('rtgen')
+        self.first_cpu_project=None
+        self.project_cpu=list()
+        self.first_disk_area=None
+        self.project_disk=list()
+        text=self.run_account_params()
+        if not text: return
+        self.parse_account_params(text)
+
+    def __bool__(self):
+        return self.first_cpu_project is not None
+
+    def __repr__(self):
+        if not self:
+            return '<RDHPCSAccountParams (None)>'
+        return ( '<RDHPCSAccountParams first_cpu_project=%s first_disk_area=%s '
+                 'available cpu=%s disk=%s>'%(
+                repr(self.first_cpu_project),repr(self.first_disk_area),
+                repr(self.project_cpu),repr(self.project_disk)))
+
+    def run_account_params(self):
+        """!Executes the account_params program
+
+        Finds account_params in the PATH and executes it.  Captures
+        any output and return value.
+
+        @return None if the exit status of account_params was non-zero.
+          Otherwise, returns the stdout output of account_params"""
+        produtil.log.jlogger.info('run account_params...')
+        try:
+            return produtil.run.runstr(
+                batchexe('account_params'),logger=self.logger)
+        except(EnvironmentError,ExitStatusException) as ee:
+            logger.warning('Cannot run account_params: '+str(ee))
+        return None
+
+    def parse_account_params(self,text):
+        logger=self.logger
+        cpu_projects=list()
+        cpu_avail=dict()
+        disk_areas=list()
+        disk_avail=dict()
+        for m in re.finditer(r'''(?isx)
+          (?:
+             \s* Allocation: \s+ \d+ \s+ (?P<cpuproj>\S+) \s+ (?P<cpuavail>[0-9.]+)
+             \s+ (?P<cpualloc>[0-9.]+) \s+ (?P<cpupct>[0-9.]+)
+           | \s* Directory \s* : \s+ (?P<diskarea>/\S+)
+             \s+ DiskInUse \s* = \s* (?P<diskused>[0-9.]+) [,a-zA-Z \t]+
+                 Quota \s* = \s* (?P<diskquota>[0-9.]+)
+           | (?P<ignore> [^\r\n]*[\r\n] | [^\r\n]*\Z ) )
+          ''',text):
+            try:
+                if not m:
+                    pass # nothing to do if match failed
+                elif m.group('cpuproj') and m.group('cpuavail'):
+                    proj=m.group('cpuproj')
+                    avail=100.0-float(m.group('cpupct'))
+                    cpu_projects.append(proj)
+                    cpu_avail[proj]=avail
+                    del proj,avail
+                elif m.group('diskarea') and m.group('diskused') and \
+                        m.group('diskquota'):
+                    area=m.group('diskarea')
+                    used=float(m.group('diskused'))
+                    quota=float(m.group('diskquota'))
+                    if quota<10000:
+                        logger.info('%s: quota<10TB ; will not use this area'%(
+                                area,))
+                        continue
+                    avail=float(max(0,quota-used))/max(1e-3,quota)
+                    disk_areas.append(area)
+                    disk_avail[area]=avail
+                    del used, quota, avail, area
+                else:
+                    logger.debug('account_params: no regex match or eoln; ignoring %s'%(
+                            repr(m.group(0).strip()),))
+            except(KeyError,ArithmeticError,ValueError,TypeError,IndexError) as e:
+                logger.debug('account_params: error (%s); ignoring %s'%(
+                        str(e),repr(m.group(0).strip())))
+
+        if cpu_projects:
+            self.project_cpu=cpu_avail
+            self.first_cpu_project=cpu_projects[0]
+        if disk_areas:
+            self.project_disk=disk_avail
+            self.first_disk_area=disk_areas[0]
+
+parsed_account_params=None
+
+def parse_account_params():
+    global parsed_account_params
+    if parsed_account_params is None:
+        parsed_account_params=RDHPCSAccountParams()
+    return parsed_account_params
+
+########################################################################
+
+# Theia project selection
+
 def decide_project_theia():
     """!Chooses which project to use when submitting jobs on Theia.
 
@@ -413,6 +625,46 @@
     logger.warning("%s: randomly chosen stmp"%(use_me,))
     return os.path.join(use_me,username())
 
+########################################################################
+
+# Jet project detection
+
+def decide_tmp_jet():
+    logger=logging.getLogger('rtgen')
+    acct=parse_account_params()
+    for preferred in [
+        '/lfs3/projects/hfv3gfs', 
+        '/lfs3/projects/hwrfv3',
+        '/lfs2/projects/gfsenkf',
+        '/pan2/projects/hwrf-vd',
+        acct.first_disk_area ]:
+        if preferred in acct.project_disk and \
+                acct.project_disk[preferred] > 0.95:
+            return os.path.join(preferred,username())
+
+    # Sort disk space by increasing availability
+    areas = [ [area,space] for area,space in acct.project_disk.iteritems() ]
+    areas.sort(lambda a,b: cmp(a[1],b[1]))
+    return os.path.join(areas[-1][0],username())
+
+def decide_project_jet():
+    logger=logging.getLogger('rtgen')
+    acct=parse_account_params()
+    for preferred in [ 'nems', 'hfv3gfs', 'hwrf-vd', 'gfsenkf',
+                       'nceplibs', 'hwrfv3' ]:
+        if preferred in acct.project_cpu and \
+                acct.project_cpu[preferred] > 0.95:
+            return preferred
+
+    # Sort disk space by increasing availability
+    projs = [ [proj,avail] for proj,avail in acct.project_cpu.iteritems() ]
+    projs.sort(lambda a,b: cmp(a[1],b[1]))
+    return projs[-1][0]
+
+########################################################################
+
+# WCOSS project detection
+
 def decide_project_wcoss():
     """!Placeholder for future development; returns "GFS-T2O" """
     return 'GFS-T2O'
@@ -494,9 +746,10 @@
 
     return os.path.join(max_area,username())
 
-# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
-# Custom test generator:
+########################################################################
 
+# Internal implementation of the test generator
+
 class RTGen(TestGen):
     def __init__(self,baseline,scratch_dir,unique_id=None,
                  logger=None,baseline_dir=None,
@@ -528,7 +781,6 @@
         if self._new_baseline:
             scope.override_local([scope],'plat%BASELINE',self._new_baseline)
         if self.project:
-            self.logger.warning('override project with '+self.project)
             scope.override_local([scope],'plat%CPU_ACCOUNT',self.project)
             scope.override_local([scope],'plat%ACCOUNT',self.project)
         else:
@@ -543,9 +795,11 @@
     def make_more(self,result,con):
         self.platform_name=self.scope.resolve('plat%PLATFORM_NAME') \
                                .string_context(con)
+        assert('/' not in self.platform_name)
         self.make_rtrun()
         self.make_rtrewind()
         self.make_rtreport()
+        self.make_info_sh()
         #if self._new_baseline:
         #    self.make_baseline_dir()
     def make_bash_load_rocoto(self,out):
@@ -557,19 +811,24 @@
             out.write('module use /hwrf/noscrub/soft/modulefiles\n')
             out.write('module load rocoto\n')
             out.write('module load ruby # workaround for libxml2 bug\n')
-            out.write('module load emc-utils ; have_qoutq=YES\n')
+            out.write('module load emc-utils ; have_qoutql=YES\n')
         elif here.name in [ 'surge', 'luna' ]:
             out.write('module load xt-lsfhpc\n')
             out.write('module use /usrx/local/emc_rocoto/modulefiles\n')
             out.write('module load rocoto/issue_8\n')
             out.write('module use /gpfs/hps/emc/hwrf/noscrub/soft/modulefiles\n')
-            out.write('module load emc-utils ; have_qoutq=YES\n')
+            out.write('module load emc-utils ; have_qoutql=YES\n')
+        elif 'jet' in here.name:
+            out.write('module load hpss rocoto\n')
+            out.write('module use /pan2/projects/hwrf-vd/soft/modulefiles\n')
+            out.write('module load emc-utils\n')
+            out.write('have_qoutql=YES\n')
         elif here.name == 'theia':
             out.write('module load rocoto\n')
             out.write('module use /scratch3/NCEPDEV/hwrf/save/Samuel.Trahan/emc-utils/modulefiles/\n')
-            out.write('module load hpss emc-utils ; have_qoutq=YES\n')
+            out.write('module load hpss emc-utils ; have_qoutql=YES\n')
         else:
-            out.write('have_qoutq=NO\n')
+            out.write('have_qoutql=NO\n')
         out.write('work=%s/rocoto\n'%(bashify_string(self.outloc),))
         out.write('cd "$work"\n')
         out.write('if [[ "$?" != 0 ]] ; then\n')
@@ -585,12 +844,35 @@
         self.logger.info('%s: make executable'%(fullpath,))
         if not self.dry_run:
             os.chmod(fullpath,0755)
+    def make_info_sh(self):
+        contents="""## This script should be sourced by an sh-like shell.
+## It sets useful variables related to the workflow being run
+PLATFORM_NAME={platform_name} ## Name of target platform
+BASELINE_DIR={baseline_dir} ## Directory with baseline data
+BASELINE_TEMPLATE={baseline_template} ## directory with template for new baselines
+UNIQUE_ID={unique_id} ## Unique id used to identify this workflow
+TEMP_AREA={temp_area} ## temporary area, auto-detected or specified at command line
+RUNDIR={run_dir} ## top directory of generated workflow
+SETS='{setarith}' ## set arithmetic specification of which sets to run
+"""
+        contents=contents.format(
+            platform_name=self.scope.resolve('plat%PLATFORM_NAME'),
+            baseline_dir=self.scope.resolve('plat%BASELINE'),
+            baseline_template=self.scope.resolve('plat%BASELINE_TEMPLATE'),
+            unique_id=self.unique_id,
+            temp_area=os.path.dirname(os.path.realpath(self.outloc)),
+            run_dir=self.outloc,
+            setarith=self.setarith
+            )
+        self.make_rtscript(self.outloc,"info.sh.inc",contents)
     def make_rtreport(self):
         out=StringIO.StringIO()
         self.make_bash_load_rocoto(out)
         out.write(r'''
+echo "Run rocotostat..." 2>&1
 rocotostat -w workflow.xml -d workflow.db -c ALL > rocotostat.txt
 timestamp=$( ls -l --time=c --time-style=+%%s workflow.xml | awk '{print $6}' )
+echo "Generate report..." 2>&1
 %s/rtreportimpl ../com rocotostat.txt "${1:-txt}" $timestamp > rtreport.txt
 cat rtreport.txt
 '''%(bashify_string(os.path.realpath(os.path.dirname(__file__))),))
@@ -640,53 +922,6 @@
                 self.new_baseline,template))
         shutil.copytree(template,self.new_baseline,symlinks=True)
 
-# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
-# Main program:
-
-def parse_arguments():
-    try:
-        optval,arglist=getopt.getopt(sys.argv[1:],'vdu:t:bn:i:p:hS')
-    except getopt.GetoptError as ge:
-        usage(str(ge))
-    verbose=0
-    dry_run=False
-    unique_id=int(os.getpid())
-    temp=None
-    baseline=False
-    baseline_dir=None
-    inputfile=None
-    project=None
-    script_mode=False
-    for opt,val in optval:
-        if opt=='-v':
-            verbose+=1
-        elif opt=='-h':
-            full_usage() # does not return
-        elif opt=='-p':
-            project=val
-        elif opt=='-d':
-            dry_run=True
-        elif opt=='-n':
-            baseline_dir=val
-        elif opt=='-u':
-            unique_id=int(val,10)
-        elif opt=='-t':
-            temp=str(val)
-        elif opt=='-b':
-            baseline=True
-        elif opt=='-i':
-            inputfile=val
-        elif opt=='-S':
-            script_mode=True
-        else:
-            usage('unknown option '+opt)
-    arglist_nowhite=list() # arguments that are not whitespace
-    for arg in arglist:
-        if not re.match('(?sx) \A \s* \Z',arg):
-            arglist_nowhite.append(arg)
-    return verbose,baseline_dir,dry_run,baseline,unique_id,temp, \
-           inputfile,arglist_nowhite,project,script_mode
-
 ########################################################################
 
 def verify_fingerprint(baseline,testgen,logger):
@@ -704,8 +939,7 @@
         'REGTEST-FINGERPRINT.md')
 
     if not os.path.exists(repo_fingerprint):
-        jlogger.warning('No fingerprint file.  Skipping fingerprint check.')
-        jlogger.warning('Expected: %s'%(repo_fingerprint,))
+        jlogger.info('No fingerprint file.  Skipping fingerprint check.')
         return
 
     with open(baseline_fingerprint,'r') as base_finger_file:
@@ -730,13 +964,208 @@
                 repo_fingerprint,))
 
 ########################################################################
+# Argument parsing
+########################################################################
 
-def main():
-    verbose,baseline_dir,dry_run,baseline,unique_id,temp, \
-        inputfile,arglist,project,script_mode = \
-            parse_arguments()
-    assert(isinstance(unique_id,int))
+have_setup_produtil=False
+def setup_produtil(jobname,verbose):
+    global have_setup_produtil
+    if have_setup_produtil: return
+    produtil.setup.setup(
+        send_dbn=False,   # avoids "dbnalert missing" warnings
+        jobname=jobname,  # set job name for jlogfile messages
+        ologlevel=logging.INFO if verbose else logging.WARNING)
+    have_setup_produtil=True
 
+def parse_rtsh_arguments():
+    """!Argument parser when this script is called as NEMSCompsetRun or rt.sh"""
+    try:
+        optval,arglist=getopt.getopt(sys.argv[1:],"c:fst:n:hr:p:b",[
+                'project=', 'mode=', 'baseline-dir=', 'baseline',
+                'dry-run', 'verbose', 'unique-id=', 'temp-dir=', 
+                'resume='])
+    except getopt.GetoptError as ge:
+        rtsh_usage(str(ge))
+
+    verbose=0
+    dry_run=False
+    unique_id=int(os.getpid())
+    temp=None
+    baseline=False
+    baseline_dir=None
+    inputfile=None
+    project=None
+    script_mode=False
+    sets=None
+    resume=None
+    platform_name=None
+    run_dir=None
+    resume_sets=None
+
+    for opt,val in optval:
+        if opt in ['-f','-s','-c','-t'] and sets is not None:
+            rtsh_usage('Only one of -c, -s, -t, or -f can be used.')
+        if opt=='--verbose':
+            verbose+=1
+        elif opt in [ '-h', '--help' ]:
+            rtsh_full_usage()
+        elif opt=='-f':
+            sets='*'
+        elif opt=='-s':
+            sets='standard'
+        elif opt in ['-b','--baseline']:
+            baseline=True
+        elif opt=='-t':
+            sets=str(val)
+        elif opt=='-c':
+            if val in [ 'ompset', 'ompsets' ]:
+                rtsh_usage('The -compset argument is no longer recognized.  Use "{compset1,compset2,compset3}" instead.  Run with -h for more information.')
+            sets=str(val)
+            baseline=True
+        elif opt in ['-n', '--baseline-dir']:
+            baseline_dir=val
+        elif opt in ['-p', '--project']:
+            project=val
+        elif opt in ['-r', '--resume']:
+            resume=val
+        elif opt=='--mode':
+            if val.lower()=='baseline':
+                baseline=True
+            elif val.lower() in ['execution', 'verify' ]:
+                baseline=False
+            else:
+                rtsh_usage('Unknown run mode '+val)
+        elif opt=='--dry-run':
+            dry_run=True
+        elif opt=='--unique-id':
+            unique_id=int(val,10)
+        elif opt=='--temp-dir':
+            temp=os.path.realpath(str(val))
+        else:
+            rtsh_usage('unknown option '+opt)
+
+    setup_produtil('NEMSCompsetRun',verbose)
+
+    if resume:
+        m=re.match('(?:[A-Z.a-z%]*:)?(\S*)',resume)
+        if not m:
+            rtsh_usage('Resume (-r opt) option must be of the format '
+                       'PLATFORM:/path/to/rtgen.#### or /path/to/rtgen.####')
+        baseline_dir=None
+        unique_id=None
+        temp=None
+        info_sh_inc=os.path.join(resume,'info.sh.inc')
+        with open(info_sh_inc,'rt') as fd:
+            for line in fd:
+                m=re.match('([A-Za-z][A-Za-z0-9_]*)=(.*?) ##',line)
+                if not m: continue
+                (var,val)=m.groups()
+                if var.lower()=='baseline_dir':
+                    baseline_dir=os.path.realpath(val)
+                    jlogger.info('Baseline directory: %s'%(repr(baseline_dir),))
+                elif var.lower()=='unique_id':
+                    unique_id=int(val,10)
+                    jlogger.info('Unique id: %s'%(repr(unique_id),))
+                elif var.lower()=='temp_area':
+                    temp=os.path.realpath(val)
+                    jlogger.info('Temp area: %s'%(repr(temp),))
+                elif var.lower()=='rundir':
+                    run_dir=os.path.realpath(val)
+                    jlogger.info('Run directory: %s'%(repr(run_dir),))
+                elif var.lower()=='platform_name':
+                    platform_name=val
+                    jlogger.info('Platform name: %s'%(repr(platform_name),))
+                    assert('/' not in platform_name)
+                elif var.lower()=='sets':
+                    resume_sets=val[1:-1] # 'set,set,set' => set,set,set
+                    jlogger.info('Set specification: %s'%(repr(resume_sets),))
+
+        if baseline_dir is None or unique_id is None or temp is None \
+                or run_dir is None or platform_name is None or resume_sets is None:
+            rtsh_usage('%s: directory has invalid or incomplete info.sh.inc file'%(
+                    info_sh_inc))
+
+    arglist_nowhite=list() # arguments that are not whitespace
+    if sets: arglist_nowhite.append(sets)
+    if resume:
+        if resume_sets:
+            arglist_nowhite.append(resume_sets)
+        else:
+            arglist_nowhite.append('*') # * = all known tests
+
+    for arg in arglist:
+        if not re.match('(?sx) \A \s* \Z',arg):
+            arglist_nowhite.append(arg)
+
+    if not arglist_nowhite and not resume:
+        rtsh_usage('You must specify which tests to run')
+
+    return verbose,baseline_dir,dry_run,baseline,unique_id,temp, \
+           inputfile,arglist_nowhite,project,script_mode,resume, \
+           platform_name, run_dir
+    
+########################################################################
+
+def parse_rtgen_arguments():
+    try:
+        optval,arglist=getopt.getopt(sys.argv[1:],'vdu:t:bn:i:p:hS',
+                'project=', 'mode=', 'baseline-dir=', 'baseline',
+                'dry-run', 'verbose', 'unique-id=', 'temp-dir=', 
+                'resume=', 'help', 'input-file')
+    except getopt.GetoptError as ge:
+        rtgen_usage(str(ge))
+
+    verbose=0
+    dry_run=False
+    unique_id=int(os.getpid())
+    temp=None
+    baseline=False
+    baseline_dir=None
+    inputfile=None
+    project=None
+    script_mode=False
+    for opt,val in optval:
+        if opt in ['-v', '--verbose']:
+            verbose+=1
+        elif opt in ['-h', '--help']:
+            rtgen_full_usage() # does not return
+        elif opt in ['-p', '--project']:
+            project=val
+        elif opt in ['-d', '--dry-run']:
+            dry_run=True
+        elif opt in ['-b', '--baseline']:
+            baseline=True
+        elif opt in ['-n', '--baseline-dir']:
+            baseline_dir=val
+        elif opt in ['-u', '--unique-id']:
+            unique_id=int(val,10)
+        elif opt in ['-t', '--temp-dir']:
+            temp=str(val)
+        elif opt in ['-b', '--baseline']:
+            baseline=True
+        elif opt in ['-i', '--input-file']:
+            inputfile=val
+        elif opt=='-S':
+            script_mode=True
+        else:
+            rtgen_usage('unknown option '+opt)
+    arglist_nowhite=list() # arguments that are not whitespace
+    for arg in arglist:
+        if not re.match('(?sx) \A \s* \Z',arg):
+            arglist_nowhite.append(arg)
+    setup_produtil('rtgen',verbose)
+    return verbose,baseline_dir,dry_run,baseline,unique_id,temp, \
+           inputfile,arglist_nowhite,project,script_mode
+
+########################################################################
+# Main program for rtgen
+########################################################################
+
+def rtgen(verbose,baseline_dir,dry_run,baseline,unique_id,temp,
+          inputfile,arglist,project,script_mode, logger,
+          send_rtrun_instructions):
+
+    ## Generate the set arithmetic string
     if len(arglist)>1:
         arith='union('+','.join(arglist)+')'
     elif arglist:
@@ -750,23 +1179,23 @@
         else:
             arith='baseline'
 
-    # Initialize the produtil package.
-    produtil.setup.setup(
-        send_dbn=False,   # avoids "dbnalert missing" warnings
-        jobname='rtgen',  # set job name for jlogfile messages
-        ologlevel=logging.INFO if verbose else logging.WARNING)
-    logger=logging.getLogger('rtgen')
 
+    # Let the user know which set we are running:
     if arith is None:
         jlogger.info('Will run all known tests.')
     else:
         jlogger.info('Test suite subset = %s'%(arith,))
 
+    ## Decide the project:
     if project is None:
         if produtil.cluster.name() == 'theia':
             project=decide_project_theia()
         elif produtil.cluster.name() in ['gyre','tide','luna','surge']:
             project=decide_project_wcoss()
+        elif produtil.cluster.name() == 'jet':
+            project=decide_project_jet()
+            assert('aoml' not in project)
+            assert('hfip' not in project)
         else:
             fail('Unknown system.  Only Theia and WCOSS Phase 1 are supported.')
         jlogger.info('Auto-chosen project for job submission is %s'%(
@@ -775,11 +1204,19 @@
         jlogger.info('User-provided project for job submission is %s'%(
                 repr(project),))
 
+    ## Decide the temp area
     if temp is None:
         if produtil.cluster.name() == 'theia':
             scratch_dir=decide_tmp_theia()
         elif produtil.cluster.name() in ['gyre','tide','luna','surge']:
             scratch_dir=decide_tmp_wcoss(produtil.cluster.where().wcoss_phase)
+        elif produtil.cluster.name() == 'jet':
+            fail('Specify the temp dir when running on Jet.  Example --temp-dir /lfs3/projects/hfv3gfs/$USER/scrub')
+            scratch_dir=decide_tmp_jet()
+            assert('aoml' not in scratch_dir)
+            assert('hfip' not in scratch_dir)
+            assert('nceplibs' not in scratch_dir)
+            assert('hwrfdata' not in scratch_dir)
         else:
             fail('Unknown system.  Only Theia and WCOSS Phase 1 are supported.')
         jlogger.info('Auto-chosen ptmp is %s'%(repr(scratch_dir),))
@@ -825,7 +1262,8 @@
     if script_mode:
         print "RUNDIR='%s' ; PLATFORM_NAME='%s'"%(
             testgen.outloc, testgen.platform_name)
-    else:
+        assert('/' not in testgen.platform_name)
+    elif send_rtrun_instructions:
         print r'''You need to run the test now.   You have three options:
 OPTION 1: Put this in your cron:
   */3 * * * * %s/rtrun --step --zero-exit > %s/rtrun-cron.log 2>&1
@@ -841,8 +1279,159 @@
         testgen.outloc,
         testgen.outloc,
         testgen.outloc)
-    # Last line must print the RUNDIR= for calling process
-    exit(0)
 
+    return testgen.platform_name, testgen.outloc, scratch_dir
+
+########################################################################
+# Utilities for rt.sh and NEMSCompsetRun modes
+########################################################################
+
+def run_rtrun(run_dir,logger,verbose):
+    cmd=batchexe(os.path.join(run_dir,'rtrun'))['--loop']
+    if verbose:       cmd=cmd['-v']
+    result=run(cmd,logger=logger)
+    return result==0
+
+def guess_app_dir():
+    here=os.path.dirname(__file__)
+    if not os.path.isabs(here):
+        here=os.path.abspath(here)
+    for rel in ['.','..','../../','../../../']:
+        trydir=os.path.join(here,rel)
+        if os.path.exists(os.path.join(trydir,'NEMS/src/conf')):
+            return trydir
+    raise Exception("Cannot find app directory (parent of NEMS).  Looked for NEMS/src/conf relative to ., .., ../.., and ../../.. but found none.")
+
+def run_rtreport(run_dir,app_dir,platform,logger):
+    jlogger.info('generate report')
+
+    # Target directory for reports:
+    log_dir=os.path.join(app_dir,'log','report-'+platform+'-log')
+    produtil.fileop.makedirs(log_dir,logger=logger)
+
+    # Copy log files to log directory
+    jlogger.info('copy build logs to %s'%(log_dir,))
+    try:
+        for src in glob.glob(os.path.join(run_dir,'tmp/log','build*')):
+            tgt=os.path.join(log_dir,os.path.basename(src))
+            produtil.fileop.deliver_file(src,tgt,logger=logger)
+    except EnvironmentError as ee:
+        logger.error('cannot copy build logs: '+str(ee))
+        return False
+
+    # Run rtreport
+    report=os.path.join(log_dir,'rtreport.txt')
+    status=run(batchexe(os.path.join(run_dir,'rtreport')) > report)
+    success=False
+    if status==0:
+        for line in fileinput.input(report):
+            if line.find('REGRESSION TEST WAS SUCCESSFUL')>=0:
+                success=True
+                break
+        if success:
+            print 'Report says test succeeded.'
+        else:
+            print 'Report says at least one test failed.'
+            print 'For details, look in %s'%(report,)
+    else:
+        print 'Non-zero exit status from rtreport.  Test failed.'
+    return success
+
+def called_as_what():
+    if len(sys.argv)>1:
+        if sys.argv[1]=='--NEMSCompsetRun':
+            sys.argv=[sys.argv[0]]+sys.argv[2:]
+            return 'NEMSCompsetRun'
+    return 'rtgen'
+
+########################################################################
+# Main entry point for all programs
+########################################################################
+
+def main():
+
+    ## Ensure we're in the NEMS/tests directory:
+    if not os.path.isdir('produtil') or not os.path.exists('rtgen'):
+        os.chdir(os.path.dirname(os.path.realpath(__file__)))
+    if not os.path.isdir('produtil') or not os.path.exists('rtgen'):
+        sys.stderr.write('Cannot find NEMS/tests directory.\nPlease try running this script from your NEMS/tests directory.\n')
+        exit(1)
+
+    # Should we behave as NEMSCompsetRun or rtgen?
+    called_as=called_as_what()
+
+    global usage
+    if called_as=='rtgen':
+        verbose,baseline_dir,dry_run,baseline,unique_id,scratch_dir, \
+            inputfile,arglist,project,script_mode = \
+            parse_rtgen_arguments()
+        usage=rtgen_usage
+        resume=False
+        assert(False)
+    else:
+        verbose,baseline_dir,dry_run,baseline,unique_id,scratch_dir, \
+            inputfile,arglist,project,script_mode,resume, \
+            platform_name, run_dir = \
+            parse_rtsh_arguments()
+        usage=rtsh_usage
+
+    assert(isinstance(unique_id,int))
+
+    # Initialize the produtil package.  This must be done after
+    # argument parsing due to the verbosity setting.
+    setup_produtil('NEMSCompsetRun',True)
+    logger=logging.getLogger(called_as)
+
+    logger.info('Running as '+called_as)
+
+#    if not verbose:
+#        sys.tracebacklimit=0
+
+    # Now we generate the workflow if that was requested.
+    ( platform_name, run_dir, scratch_dir ) = \
+        rtgen(verbose,baseline_dir,dry_run,baseline,unique_id,scratch_dir,
+              inputfile,arglist,project,script_mode,logger,
+              called_as=='rtgen')
+    assert('/' not in platform_name)
+
+    if called_as=='rtgen': exit(0)
+
+    app_dir=guess_app_dir()
+
+    # If we get to this point, we are called as rt.sh or
+    # NEMSCompsetRun, and hence we must run the test suite.
+    logger=logging.getLogger('NEMSCompsetRun')
+    # Note we use scratch_dir because it is what rtgen returned.
+    run_dir=os.path.join(scratch_dir,'rtgen.%d'%unique_id)
+
+    # In dry run mode, we just print a few messages, and we're done.
+    if dry_run:
+        logger.info('Would run rtrun in '+run_dir)
+        logger.info('Would check rtreport in '+run_dir)
+        exit(0)
+
+    if not os.path.isdir(run_dir):
+        logger.error('%s: no such directory; rtgen failed or was never run'%(
+                run_dir))
+        exit(1)
+
+    # Run the rtrun program to execute the workflow.
+    # Note: verbose is hard-coded to true to ensure -v option
+    success=run_rtrun(run_dir,logger,True)
+    if not success:
+        logger.warning('%s: rtrun exited with non-zero status'%(
+                os.path.join(run_dir,'rtrun')))
+
+    # Generate the report if we are in verification mode.
+    if not baseline:
+        if dry_run:
+            logger.info('Would check rtreport.')
+        report_success=run_rtreport(run_dir,app_dir,platform_name,logger)
+        success = success and report_success
+        print 'TEST RESULT: ' + ( 'PASS' if success else 'FAIL' )
+    else:
+        print 'BASELINE GENERATION: ' + \
+            ( 'SUCCESS' if success else 'FAILURE' )
+
 if __name__=='__main__':
     main()
Index: checkout/tests/rt.sh
===================================================================
--- checkout/tests/rt.sh	(revision 90943)
+++ checkout/tests/rt.sh	(revision 92559)
@@ -1,222 +1,18 @@
 #! /bin/bash
 
-# NOTE: This script is a bash script.  It uses bash-specific features
-# and cannot run correctly under other shells.
+for loc in . ./tests ./NEMS/tests ../tests ../NEMS/tests ; do
+    if [[ -x "$loc/rtgen" ]] ; then
+        cd $loc
+        # Get the fully qualified path:
+        real_loc=$( pwd -P )
 
-function die {
-    echo "$*" 1>&2
-    exit 1
-}
+        # Ensure our produtil is first in $PYTHONPATH
+        export PYTHONPATH=$real_loc/produtil/ush${PYTHONPATH:+:$PYTHONPATH}
 
-function usage {
-  set +x
-  echo
-  echo "Usage: $0 -c <subset> | -f | -s | -t <subset> | -n /path/to/baseline | -h"
-  echo
-  echo "  -c <subset> = create new baseline results for <subset>"
-  echo "  -f  run full suite of regression tests"
-  echo "  -s  run standard suite of regression tests (same as -t standard)"
-  echo "  -t <subset> = runs specified subset of tests"
-  echo "  -n /path/to/baseline = specify path to REGRESSION_TEST directory"
-  echo "  -h  display this help"
-  echo "  -r PLATFORM:/path/to/run"
-  echo "      rerun past suite without regenerating it"
-  echo "  -p project = set the project to use for cpu time"
-  echo
-  echo "Common <subset>s: gfs, nmm, slg, wam, debug"
-  echo "See ../../compsets/all.input for a full list"
-  echo
-  if [[ ! -z "$*" ]] ; then
-      echo "SCRIPT ABORTING: $*" 1>&2
-  fi
-  exit 1
-}
-
-if [[ -d NEMS/tests ]] ; then
-    cd NEMS/tests
-elif [[ -s tests/rtgen ]] ; then
-    cd tests
-elif [[ ! -s rtgen ]] ; then
-    die Run this script from the NEMS/tests directory.
-fi
-
-set_info=''
-cmd='./rtgen -S '
-baseline=NO
-rerun=NO
-RUNDIR=''
-PLATFORM_NAME=''
-
-export PYTHONPATH=$( pwd -P )/produtil/ush${PYTHONPATH:+:$PYTHONPATH}
-
-while getopts ":c:fst:n:hr:p:" opt; do
-    case $opt in
-        r)
-            if [[ $OPTARG =~ ^([a-zA-Z][a-zA-Z_.0-9]*):(.+)$ ]] ; then
-                PLATFORM_NAME="${BASH_REMATCH[1]}"
-                RUNDIR="${BASH_REMATCH[2]}"
-                rerun=YES
-            else
-                echo "${BASH_REMATCH[@]}"
-                usage "Rerun argument must be PLATFORM_NAME:RUNDIR"
-            fi
-            ;;
-        p)
-            cmd="$cmd -p $OPTARG" 
-            ;;
-        c)
-            if [[ ! -z "$set_info" ]] ; then
-                usage "Only one of -c, -s, -t, or -f can be used."
-            fi
-            set_info="$OPTARG"
-            cmd="$cmd -b"
-            baseline=YES
-            ;;
-        s)
-            if [[ ! -z "$set_info" ]] ; then
-                usage "Only one of -c, -s, -t, or -f can be used."
-            fi
-            set_info='standard'
-            ;;
-        f)  
-            if [[ ! -z "$set_info" ]] ; then
-                usage "Only one of -c, -s, -t, or -f can be used."
-            fi
-            set_info=' '
-            ;;
-        h)
-            usage
-            ;;
-        t)
-            if [[ ! -z "$set_info" ]] ; then
-                usage "Only one of -c, -s, -t, or -f can be used."
-            fi
-            set_info="$OPTARG"
-            ;;
-        n)
-            cmd="$cmd -n  $OPTARG"
-            ;;
-        \?)
-            usage "Unknown option -$OPTARG"
-            ;;
-        :)
-            usage "Option -$OPTARG requires an argument."
-            ;;
-    esac
+        # Run rtgen as "rt.sh" 
+        exec $real_loc/rtgen --NEMSCompsetRun "$@"
+    fi
 done
 
-if [[ ! -z "$set_info" ]] ; then
-    cmd="$cmd $set_info"
-fi
-
-if [[ -z "$set_info" && "$rerun" == NO ]] ; then
-    usage "At least one of -n, -c, -t, -f or -s must be specified."
-fi
-
-if [[ ! -z "$model" && ! -z "$fullstd" ]] ; then
-    cmd="$cmd 'union($model,$fullstd)'"
-elif [[ ! -z "$model" || ! -z "$fullstd" ]] ; then
-    cmd="$cmd '$model$fullstd'"
-fi
-
-# Arcane bash magic below.  This is what it does:
-#
-#  $cmd
-#    ^-- Runs rtgen
-#
-#  $cmd < /dev/null
-#          ^-- Workaround for Jet bug: do not send stdin on a batch node
-#
-#  $cmd < /dev/null | tee >(cat >&2)
-#                         ^--- Copy stdout to stderr
-#
-#  $cmd < /dev/null | tee >(cat >&2) | grep 'RUNDIR=' | tail -1
-#            Get the RUNDIR variable ---^---------------^
-#
-# The result is that $cmd's stdout will go to the terminal AND to the
-# "grep | tail".  The last line will end up in $result
-#
-# result='RUNDIR=/path/to/rundir  PLATFORM_NAME=wcoss.cray'
-
-SUCCESS=PASS
-
-if [[ "$rerun" == NO ]] ; then
-    # When we're not rerunning, we need to generate a new workflow area.
-
-    echo "rt.sh: will run $cmd"
-
-    result=$( $cmd < /dev/null | tee >(cat >&2)  | grep 'RUNDIR=' | tail -1 )
-    eval $result
-    
-    if [[ -z "$RUNDIR" ]] ; then
-        die "ERROR: The rtgen script failed (no RUNDIR).  See above for details."
-    fi
-    if [[ -z "$PLATFORM_NAME" ]] ; then
-        die "ERROR: The rtgen script failed (no PLATFORM_NAME).  See above for details."
-    fi
-else
-    : # if we get here, $cmd is not used
-fi
-
-cmd="$RUNDIR/rtrun --loop -v"
-echo "rt.sh: will run $cmd"
-
-$cmd # runs for a long time
-status="$?"
-
-if [[ "$status" != 0 ]] ; then
-    SUCCESS=FAIL
-fi
-
-LOGDIR="../../log/report-$PLATFORM_NAME-log"
-REPORT="$LOGDIR/rtreport.txt"
-
-if [[ "$baseline" == NO ]] ; then
-    echo "rt.sh: time to generate the report"
-    if [[ ! -d "$RUNDIR" ]] ; then
-        echo "rt.sh: run area does not exist: $RUNDIR"
-        die "rt.sh: workflow did not run there"
-    fi
-    echo "rt.sh: copy build logs to $LOGDIR"
-    mkdir -p "$LOGDIR"
-    if ( ! cp -fp "$RUNDIR/tmp/log/build"* "$LOGDIR/." ) ; then
-        die "rt.sh: could not copy build logs.  Missing file?  I/O error?"
-    fi
-    cmd="$RUNDIR/rtreport > $REPORT"
-    echo "rt.sh: run $cmd"
-    "$RUNDIR/rtreport" > "$REPORT"
-    repstatus="$?"
-    echo "rt.sh: status $repstatus"
-    if [[ "$repstatus" == 0 ]] ; then
-        if ( ! grep 'REGRESSION TEST WAS SUCCESSFUL' "$REPORT" > /dev/null ) ; then
-            echo "rt.sh: report says at least one test failed."
-            echo "rt.sh: for details, look in $( pwd )/$REPORT"
-            SUCCESS=FAIL
-        else
-            echo "rt.sh: report says test succeeded."
-        fi
-    else
-        die "rt.sh: could not copy report.  Missing file?  I/O error?"
-    fi
-fi
-
-echo
-echo "The test directory is"
-echo "    $RUNDIR"
-echo "Execution area: $RUNDIR/tmp"
-echo "Temporary log files: $RUNDIR/tmp/log"
-echo "Subversion log files: $( pwd )/$LOGDIR"
-echo "Exit status of rtrun: $status"
-echo
-echo "TEST RESULT: $SUCCESS"
-echo
-
-if [[ "$SUCCESS" == PASS ]] ; then
-    echo "Success: rejoice!  All tests passed."
-else
-    echo "I deeply apologize, but at least one test failed."
-    echo "You should fix the problem, and then use rtrewind"
-    echo "and rtrun to re-run failed tests."
-fi
-
-exit $status
+echo "Cannot find rtgen!  Try running from NEMS/tests directory."
+exit -1
\ No newline at end of file
Index: checkout/tests
===================================================================
--- checkout/tests	(revision 90943)
+++ checkout/tests	(revision 92559)

Property changes on: checkout/tests
___________________________________________________________________
Modified: svn:externals
## -1 +1 ##
-produtil -r90581 https://svnemc.ncep.noaa.gov/projects/nceplibs/produtil/branches/regtests-run
+produtil -r92558 https://svnemc.ncep.noaa.gov/projects/nceplibs/produtil/branches/regtests-run
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /nems/branches/fv3-jet/tests:r89946-92482
Index: checkout/doc/Makefile
===================================================================
--- checkout/doc/Makefile	(revision 90943)
+++ checkout/doc/Makefile	(revision 92559)
@@ -1,7 +1,12 @@
 NEMSDOC=markdown.md BUILD.md NEWTEST.md OLDTEST.md
-APPDOC=../../doc/README.md
 #MODELDOC=modeldoc.md  README.GFS.md README.NMM.md
 
+ifneq ($(wildcard ../../doc/*.md),)
+APPDOC=../../doc/*.md
+else
+APPDOC=
+endif
+
 ALLDOC=$(APPDOC) $(NEMSDOC) # $(MODELDOC)
 
 
@@ -10,7 +15,7 @@
 clean:
 	rm -f README.html
 
-README.html: $(ALLDOC)
+README.html: $(ALLDOC) Makefile
 	./md2html.py $(ALLDOC) README.html
 	head README.html
 
Index: checkout/src
===================================================================
--- checkout/src	(revision 90943)
+++ checkout/src	(revision 92559)

Property changes on: checkout/src
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /nems/branches/fv3-jet/src:r89946-92482
Index: checkout
===================================================================
--- checkout	(revision 90943)
+++ checkout	(revision 92559)

Property changes on: checkout
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /nems/branches/fv3-jet:r89946-92482

--=_59121bc9.mxbY+RCbbOnLoVlWRkmLNZdlDm0kOUig//vPB7KM/+3cESLJ--


More information about the Ncep.list.nems.announce mailing list