diff --git a/setup.py b/setup.py
index 9913499c..590dd0c8 100755
--- a/setup.py
+++ b/setup.py
@@ -75,6 +75,8 @@
"src/python/frontend/climatology.py",
"src/python/frontend/metadiags.py",
"src/python/frontend/metadiags",
+ "src/python/frontend/multidiags.py",
+ "src/python/frontend/multidiags",
"src/python/devel/update_metrics_baselines.py",
"src/python/devel/update_metrics_baselines",
"src/python/packages/atm_tier1b/u850.ncl",
diff --git a/src/python/computation/reductions.py b/src/python/computation/reductions.py
index af190d57..42821679 100644
--- a/src/python/computation/reductions.py
+++ b/src/python/computation/reductions.py
@@ -2856,7 +2856,8 @@ def run_cdscan( fam, famfiles, cache_path=None ):
# get here.
# Another problem is that units stuck in the long_name sometimes are
# nonstandard. So fix them!
- if hasattr(f['time'],'long_name'):
+ if hasattr(f['time'],'long_name') and\
+ getattr(f['time'],'long_name',None) not in ['time',None,'']:
time_units = f['time'].long_name
else:
time_units = 'days' # probably wrong but we can't go on without something
@@ -2881,8 +2882,8 @@ def run_cdscan( fam, famfiles, cache_path=None ):
import shlex
logger.info('cdscan command line: %s', cdscan_line)
try:
- cdscan_line = shlex.split(cdscan_line)
- cdscan.main(cdscan_line)
+ cdscan_lineargs = shlex.split(cdscan_line)
+ cdscan.main(cdscan_lineargs)
except Exception,err:
print "CDSCAN RUN ERROR",err
import traceback,sys
@@ -2890,7 +2891,8 @@ def run_cdscan( fam, famfiles, cache_path=None ):
traceback.print_tb(exc_traceback)
print "END ERROR LOG"
logger.error( 'ERROR: cdscan terminated. This is usually fatal. The arguments were:%s\n',
- cdscan_line )
+ cdscan_lineargs )
+ print "original cdscan_line:",cdscan_line
except Exception,err:
print "CDSCAN IMPORT ERROR",err
import traceback
diff --git a/src/python/fileio/filetable.py b/src/python/fileio/filetable.py
index 28a55ddb..13499bf8 100755
--- a/src/python/fileio/filetable.py
+++ b/src/python/fileio/filetable.py
@@ -266,7 +266,8 @@ def addfile( self, filep, options):
filesupp = get_datafile_filefmt( dfile, options )
if self.filefmt is None:
self.filefmt = filesupp.name
- elif self.filefmt!= filesupp.name:
+ elif self.filefmt!= filesupp.name and self.filefmt.find(filesupp.name)<0 and\
+ filesupp.name.find(self.filefmt)<0:
self.filefmt = "various"
vars = filesupp.interesting_variables()
if len(vars)>0:
diff --git a/src/python/fileio/findfiles.py b/src/python/fileio/findfiles.py
index 931b63eb..743acdb1 100755
--- a/src/python/fileio/findfiles.py
+++ b/src/python/fileio/findfiles.py
@@ -209,7 +209,9 @@ def _getdatafiles( self, root, filt ):
def shortest_name(self):
if len(self._filt.mystr())>3:
- return self._filt.mystr()
+ shortname = self._filt.mystr()
+ shortname = shortname.strip('-_')
+ return shortname
else:
return self.short_name()
@@ -226,6 +228,7 @@ def short_name(self):
else:
shortname = ','.join([os.path.basename(str(r))
for r in self._root]) #
+ shortname = shortname.strip('-_')
return shortname
def long_name(self):
diff --git a/src/python/frontend/amwgmaster.py b/src/python/frontend/amwgmaster.py
index a6a8f052..aa569154 100644
--- a/src/python/frontend/amwgmaster.py
+++ b/src/python/frontend/amwgmaster.py
@@ -361,8 +361,9 @@
diags_collection['7s']['options'] = {'logo':'no'}
diags_collection['7s']['combined'] = True
diags_collection['7s']['TREFHT'] = {'plottype': '7', 'obs': ['WILLMOTT_1']}
-diags_collection['7s']['PS'] = {'plottype': '7', 'obs': ['NCEP_1']}
-diags_collection['7s']['PSL'] = {'plottype': '7', 'obs': ['JRA25_1', 'NCEP_1']}
+diags_collection['7s']['PSL'] = {'plottype': '7', 'obs': ['JRA25_1']}
+diags_collection['7s']['Z3'] = {'plottype': '7', 'obs': ['JRA25_1']}
+
# These 4 are northern only.
diff --git a/src/python/frontend/diags.py b/src/python/frontend/diags.py
index 1db88310..740675d8 100755
--- a/src/python/frontend/diags.py
+++ b/src/python/frontend/diags.py
@@ -83,6 +83,8 @@ def setManualColormap(canvas=None, level=0):
def getNames(opts, model, obs):
""" The purpose of this kludge is to get the names of the model and obs
specified on the command line for output on the graphic.
+ These particular names should be sufficiently unique and human-readable, and
+ reasonably short.
Each of model and obs should be a list of instances of basic_filetable.
This function returns a dictionary of the form {'model':modelname, 'obs':obsname}
where modelname and obsname are strings."""
@@ -107,7 +109,8 @@ def get_model_case(filetable):
nicknames['obs'] = opts['obs'][0]['name']
elif ft2 is not None:
#nicknames['obs'] = ft2.source().split('_')[-1]
- nicknames['obs'] = ft2.source().split('_')[0]
+ #nicknames['obs'] = ft2.source().split('_')[0] #doesn't work on HadISST_CL, which isn't HadISST
+ nicknames['obs'] = ft2.source()
return nicknames
def setnum( setname ):
@@ -156,7 +159,7 @@ def run_diags( opts ):
# Setup filetable arrays
modelfts = []
obsfts = []
- names = {'model':'', 'obs':''}
+ names = {}
for i in range(len(opts['model'])):
ft = path2filetable(opts, modelid=i)
if ft._id.nickname=='':
@@ -292,7 +295,11 @@ def run_diags( opts ):
filter = opts._opts['obs'][0]['filter']
obsfilter = None
if filter != None:
- obsfilter = filter.split('"')[1]
+ # Assume the filter is something simple like 'f_startswith("NCEP")', or "f_startswith('NCEP')"
+ splitfilt = filter.split('"')
+ if len(splitfilt)<=1:
+ splitfilt = filter.split("'")
+ obsfilter = splitfilt[1]
computeall = not opts['output']['table']
table = sclass( modelfts, obsfts, varid=varid, obsfilter=obsfilter, dryrun=opts['dryrun'], sbatch=opts["sbatch"], computeall=computeall, outdir=outdir)
@@ -325,7 +332,8 @@ def run_diags( opts ):
logger.warning('Could not find any of the requested variables %s among %s', opts['vars'],
pclass.list_variables(modelfts,obsfts,sname) )
logger.warning("among %s", variables)
- return {}
+ print "multidiags start here names=", names, "multidiags stop here" # do not mess with this!
+ return names
# Ok, start the next layer of work - seasons and regions
# loop over the seasons for this plot
@@ -470,6 +478,7 @@ def run_diags( opts ):
# If this were called from multidiags, the names dictionary would be helpful. In particular,
# it will help to not have to re-open a file to re-compute the case name for the model.
+ print "multidiags start here names=", names, "multidiags stop here" # do not mess with this!
return names
@@ -867,6 +876,7 @@ def makeplots(res, vcanvas, vcanvas2, varid, frname, plot, package, opts, displa
descr=descr, vname=vname, more_id='combined' )
else:
source_descr2 = list(set(source_descr2))
+ #source_descr2.sort()
fnamepng,fnamesvg,fnamepdf = form_filename( frnamebase, ('png','svg','pdf'),
modobs=list(source_descr2), more_id='combined' )
@@ -888,15 +898,21 @@ def makeplots(res, vcanvas, vcanvas2, varid, frname, plot, package, opts, displa
if __name__ == '__main__':
print "UV-CDAT Diagnostics, command-line version"
- print ' '.join(sys.argv)
- try:
- irunby = sys.argv.index('--runby')
- runby = sys.argv[irunby+1]
- o = Options(runby=runby)
- except ValueError:
- o = Options()
+ print repr(repr(' '.join(sys.argv)))
+ if sys.argv[1][:10]=='--pickopt=':
+ # special case. All options are described in the argument, a pickled Options object.
+ pickopt = sys.argv[1][10:]
+ pickopt = pickopt.replace('jfpsingleq','\'')
+ o = pickle.loads(pickopt)
+ else:
+ # Normal case, with command-line options. Their interpretation differs slightly according
+ # to how this was run, as specified in the '--runby' option.
+ try:
+ irunby = sys.argv.index('--runby')
+ runby = sys.argv[irunby+1]
+ o = Options(runby=runby)
+ except ValueError:
+ o = Options()
o.parseCmdLine()
o.verifyOptions()
- #print o._opts['levels']
- #print o._opts['displayunits']
run_diags(o)
diff --git a/src/python/frontend/metadiags.py b/src/python/frontend/metadiags.py
index 29ca2145..13d836d8 100644
--- a/src/python/frontend/metadiags.py
+++ b/src/python/frontend/metadiags.py
@@ -200,7 +200,8 @@ def makeTables(collnum, model_dict, obspath, outpath, pname, outlogdir, dryrun=F
runcmdline(cmdline, outlogdir, dryrun)
-def generatePlots(model_dict, obspath, outpath, pname, xmlflag, data_hash, colls=None, dryrun=False):
+def generatePlots(model_dict, obspath, outpath, pname, xmlflag, data_hash, colls=None,
+ dryrun=False, opts_dryrun=False):
import os
# Did the user specify a single collection? If not find out what collections we have
if colls == None:
@@ -469,7 +470,7 @@ def generatePlots(model_dict, obspath, outpath, pname, xmlflag, data_hash, colls
pstr2 = ''
cmdline = (def_executable, pstr1, pstr2, obsstr, optionsstr, packagestr, setstr, seasonstr, varstr, outstr, xmlstr, prestr, poststr, regionstr)
if collnum != 'dontrun':
- runcmdline(cmdline, outlogdir, dryrun)
+ runcmdline(cmdline, outlogdir, dryrun, opts_dryrun)
else:
message = cmdline
logger.debug('DONTRUN: %s', cmdline)
@@ -532,7 +533,7 @@ def generatePlots(model_dict, obspath, outpath, pname, xmlflag, data_hash, colls
if varopts:
cmdline += [varopts]
if collnum != 'dontrun':
- runcmdline(cmdline, outlogdir, dryrun)
+ runcmdline(cmdline, outlogdir, dryrun, opts_dryrun)
else:
logger.debug('DONTRUN: %s', cmdline)
else: # different executable; just pass all option key:values as command line options.
@@ -569,7 +570,7 @@ def generatePlots(model_dict, obspath, outpath, pname, xmlflag, data_hash, colls
execstr = execstr+' --figurebase '+ fnamebase
if execstr != def_executable:
- runcmdline([execstr], outlogdir, dryrun)
+ runcmdline([execstr], outlogdir, dryrun, opts_dryrun)
# VIEWER Code
# Build rows for this group in the index...
@@ -688,16 +689,75 @@ def generatePlots(model_dict, obspath, outpath, pname, xmlflag, data_hash, colls
pid_to_tmpfile = {}
active_processes = []
DIAG_TOTAL = 0
+optsout = None
+
+
+def dequote(s): # from stackoverflow, and a little better than the way I would have done it:
+ """
+ If a string has single or double quotes around it, remove them.
+ Make sure the pair of quotes match.
+ If a matching pair of quotes is not found, return the string unchanged.
+ """
+ if (s[0] == s[-1]) and s.startswith(("'", '"')):
+ return s[1:-1]
+ return s
+
+def cmd2opts( cmdline ):
+ """Input is a metadiags command line, a tuple or list of strings.
+ This function computes and returns the corresponding Options object."""
+ opts = Options()
+ # Some strings in cmdline contain blanks, e.g. '--seasons DJF JJA'.
+ # We have to convert this to a list of strings like sys.argv when cmdline is run, i.e. '--seasons','DJF','JJA'.
+ cmdl = ' '.join(cmdline)
+ sys.argv = [item for item in cmdl.split(' ') if item!='']
+ opts.parseCmdLine()
+ return opts
+
+def cmd2print( cmdline, opts_dryrun=True ):
+ """Input is a metadiags command line, and optionally the dryrun option. This dryrun option may
+ be the name of a file, or True or False. This function will print values of some key options.
+ If dryrun be True, they will be printed to stdout. If dryrun be the name of a file, they will
+ be written to the file. Nothing will happen if dryrun be False."""
+ global optsout
+ if opts_dryrun==False:
+ return
+ opts = cmd2opts( cmdline )
+ if optsout is None:
+ try:
+ optsout = open(opts_dryrun,'w')
+ except TypeError:
+ # happens in the common case, opts_dryrun==True
+ optsout = None # print to this means print to stdout
+ if 'modelpath' not in opts.keys():
+ opts['modelpath'] = [os.path.abspath(opts['model'][0]['path'])]
+ if 'obspath' not in opts.keys():
+ opts['obspath'] = [os.path.abspath(opts['obs'][0]['path'])]
+
+ print >>optsout, "Options instance"
+ for key in ['modelpath', 'obspath', ('obs','filter'), 'package',
+ 'sets', 'seasons', 'vars' ]:
+ try:
+ if type(key) is str:
+ print >>optsout, " ", key, "=", opts[key]
+ else: # nested dictionaries, two keys in a tuple
+ if type(opts[key[0]]) is list: # we also have to drill through a list! (happens with model,obs)
+ print >>optsout, " ", key[0], "[0][", key[1], "] =", dequote( opts[key[0]][0][key[1]] )
+ else:
+ print >>optsout, " ", key[0], "[", key[1], "] =", opts[key[0]][key[1]]
+ except KeyError:
+ print( " ", key, "undefined" )
def cmderr(popened):
logfile = pid_to_cmd[popened.pid].split(" ")[-1]
logger.error("Command \n%s\n failed with code of %d. Log file is at %s.", pid_to_cmd[popened.pid], popened.returncode, logfile)
-def runcmdline(cmdline, outlogdir, dryrun=False):
+def runcmdline(cmdline, outlogdir, dryrun=False, opts_dryrun=False):
global DIAG_TOTAL
+ cmd2print( cmdline, opts_dryrun )
+
# the following is a total KLUDGE. It's more of a KLUDGE than last time.
# I'm not proud of this but I feel threatned if I don't do it.
# there is some sort of memory leak in vcs.
@@ -928,6 +988,7 @@ def postDB(fts, dsname, package, host=None):
cmaps = opts._opts["colormaps"]
tmpDict["colormaps"] = " ".join(["%s=%s" % (k, cmaps[k]) for k in cmaps])
diags_collection[K]["options"] = tmpDict
+ print "jfp opts['dryrun']=",opts['dryrun']
if opts["dryrun"]:
fnm = os.path.join(outpath, "metadiags_commands.sh")
dryrun = open(fnm, "w")
@@ -945,6 +1006,7 @@ def postDB(fts, dsname, package, host=None):
module use /usr/common/contrib/acme/modulefiles
module load uvcdat/batch
""" % (opts["sbatch"])
+
else:
dryrun = False
@@ -960,7 +1022,8 @@ def postDB(fts, dsname, package, host=None):
data_path_hmac.update(obspath)
data_hash = data_path_hmac.hexdigest()
- menus, pages = generatePlots(model_dict, obspath, outpath, package, xmlflag, data_hash, colls=colls,dryrun=dryrun)
+ menus, pages = generatePlots(model_dict, obspath, outpath, package, xmlflag, data_hash,
+ colls=colls,dryrun=dryrun,opts_dryrun=opts['dryrun'])
for page in pages:
# Grab file metadata for every image that exists.
diff --git a/src/python/frontend/multidiags b/src/python/frontend/multidiags
new file mode 120000
index 00000000..f349c823
--- /dev/null
+++ b/src/python/frontend/multidiags
@@ -0,0 +1 @@
+multidiags.py
\ No newline at end of file
diff --git a/src/python/frontend/multidiags.py b/src/python/frontend/multidiags.py
index 5af22684..b2108317 100644
--- a/src/python/frontend/multidiags.py
+++ b/src/python/frontend/multidiags.py
@@ -1,9 +1,20 @@
+#!/usr/bin/env python
+
# Run multiple diagnostics. The user only specifies paths and a collection name.
# The collection name is a key into multimaster.py, or (>>>> TO DO >>>>) another file provided by the user.
-import logging, pdb, importlib, time, cProfile
+# ---------------- section 0: Imports and global initializations ----------------
+
+from __future__ import print_function # for new-style print( string, file=outputfile )
+
+import logging, os, pdb, importlib, time, cProfile, pickle, tempfile, subprocess
from pprint import pprint
from itertools import groupby
+from multiprocessing import Pool, current_process, Process, Queue
+import multiprocessing
+from tempfile import NamedTemporaryFile
+from os import getpid
+from subprocess import PIPE
from metrics.frontend.multimaster import * # this file is just a demo
import metrics.frontend.multimaster as multimaster
import metrics.frontend.defines as defines
@@ -13,6 +24,11 @@
from output_viewer.index import OutputIndex, OutputPage, OutputGroup, OutputRow, OutputFile, OutputMenu
logger = logging.getLogger(__name__)
+MAX_N_PROCS = 4 # maximum number of simultaneous processes.
+ # These are run with Popen or the multiprocessing module.
+ARG_MAX = subprocess.check_output(['getconf', 'ARG_MAX']) # max length of a command line
+
+# ---------------- section 1: Expand names and merge options. ----------------
def merge_option_with_its_defaults( opt, diags_collection ):
"""If opt, an Options instance, has a default_opts option set, this will import the
@@ -58,54 +74,6 @@ def expand_and_merge_with_defaults( opt ):
opts.append(newopt)
return opts
-def merge_all_options_old( opt ):
- """Merge the supplied Options object with other Options instances. Conflicts are resolved by
- ordering them in this priority:
- opt, opt['default_opts'], my_opts, my_opts['default_opts'], diags_opts, options_defaults
- At present the use of 'default_opts' cannot be recursive."""
- try:
- import my_opts # user can put my_opts.py in his PYTHONPATH
- myopts = my_opts.my_opts
- except:
- my_opts = None
- myopts = None
- try:
- import metrics.frontend.diags_opts
- diagsopts = diags_opts.diags_opts
- except:
- diags_opts = None
- diagsopts = None
- # The 'sets' or 'colls' option identifies a member (or more) of diags_collection, whose values
- # are Options instances. We have to expand it now, to a list 'opts' of real Options instances,
- # in order to perform the merger.
- # Similarly, if any 'model' or 'obs' option be a key into a collection, we have to expand it
- # now in order to perform a proper merge.
- if opt.get('sets') is None: # later use a specialized option name, e.g. 'collec' <<<<
- opts = [opt]
- else:
- opts = []
- for dset in opt['sets']:
- # Each set in opt['sets'] may be a list. And opt['sets'] may be a list of length>1.
- # Either way, we end out with a list of Options instances which should be merged with
- # opt (opt having priority) but should not be merged together.
- if isinstance(diags_collection[dset],Options):
- merge_option_with_its_defaults(diags_collection[dset],diags_collection)
- opts.append(opt)
- elif type(diags_collection[dset]) is list:
- for optset in diags_collection[dset]:
- merge_option_with_its_defaults(optset,diags_collection)
- newopt = opt.clone()
- opts.append(newopt)
- # (done in the newer version of this function): This should also be done for the myopts, diagsopts, etc.
- if my_opts is not None:
- merge_option_with_its_defaults( myopts, my_opts.diags_collection )
- for op in opts:
- merge_option_with_its_defaults( op, diags_collection )
- op.merge( myopts )
- op.merge( diagsopts )
- op.merge( options_defaults )
- return opts
-
def merge_all_options( opt ):
"""Merge the supplied Options object with other Options instances.
When two Options instances are merged and only one has a value for an option, then that
@@ -253,29 +221,132 @@ def expand_lists_collections( opt, okeys=None ):
flattened = [ o for ops in expanded_opts for o in ops ]
return flattened
+# ---------------- section 2: Higher-level run functions, including multiprocessing ----------------
+
+def run_next():
+ """Runs a diags process on the next option in the newopts list."""
+ global newopts
+ global optidx
+ global procs
+ global active
+ if None in active:
+ active[active.index(None)] = optidx
+ o = newopts[optidx]
+ ostr = pickle.dumps( o, 0 ) # easier than generating a full-length command line
+ ostr = ostr.replace('\'','jfpsingleq')
+ ostr = '\'' + ostr + '\''
+ cmd = r"diags --pickopt=%s" % ostr # don't change this syntax, c.f. bottom of diags.py
+ if len(cmd)>=ARG_MAX:
+ logger.critical("generated command line is too long for the operating sytem!")
+ quit()
+ p = subprocess.Popen([cmd],shell=True,stdout=PIPE,stderr=PIPE)
+ procs[optidx] = p
+ optidx += 1
+ elif optidx0:
+ newopts[idx]['modobs_names'] = eval( namstr )
+ active[active.index(idx)] = None
+
+def multidiags_dryrun( newopts, dryrun ):
+ """A list of Options objects newopts is set up, but we don'e want to actually run the
+ diagnostics. Just print out something about what we have to do."""
+ try:
+ outfile = open(dryrun,'w')
+ except TypeError: # happens in the common case, dryrun==True
+ outfile = None
+ for opt in newopts:
+ print( "Options instance", file=outfile )
+ #print( "keys=", sorted(opt.keys()) )
+ for key in ['modelpath', 'obspath', ('obs','filter'), 'package',
+ 'sets', 'seasons', 'vars' ]:
+ try:
+ if type(key) is str:
+ print( " ", key, "=", opt[key], file=outfile )
+ else: # nested dictionaries, two keys in a tuple
+ if type(opt[key[0]]) is list: # we also have to drill through a list! (happens with model,obs)
+ print( " ", key[0], "[0][", key[1], "] =", opt[key[0]][0][key[1]], file=outfile )
+ else:
+ print( " ", key[0], "[", key[1], "] =", opt[key[0]][key[1]], file=outfile )
+ except KeyError:
+ print( " ", key, "undefined" )
+ if outfile is not None:
+ outfile.close()
+
+def multidiags2( optslist ):
+ """Now that a list of Options objects newopts is set up, actually run the diagnostics. Then
+ set up for running the viewer."""
+ global optidx
+ global procs
+ global active
+
+ if len(optslist)==1 or MAX_N_PROCS==1:
+ # single-processing:
+ for i,o in enumerate(optslist):
+ o['modobs_names'] = run_diags(o.clone()) # run_diags messes with its input arg, though it shouldn't
+ else:
+ # multi-processing:
+ procs = [None] * len(optslist)
+ active = [None] * MAX_N_PROCS # MAX_N_PROCS is defined at the top of this file.
+ optidx = 0
+ while optidx1: where = where+'-combined'
- return form_filename( where, 'nc', descr=descr, vname=self.vars[0].id, more_id=self.more_id )
+ return form_filename( where, 'nc', descr=descr, vname=varid, more_id=self.more_id )
elif len(self.title)<=0:
fname = 'foo'+''.join([random.choice('0123456789') for _ in range(4)])+'.nc'
else:
@@ -917,7 +930,11 @@ def write_plot_data( self, format="", where="" ):
zax.filetable2id= str(zax.filetable2id) # and the named tuple ids aren't writeable as such
except:
pass
- for ax in zax.getAxisList():
+ if type(zax) is tuple:
+ axes = zax[0].getAxisList()
+ else:
+ axes = zax.getAxisList()
+ for ax in axes:
try:
del ax.filetable
except:
diff --git a/src/python/packages/amwg/amwg1.py b/src/python/packages/amwg/amwg1.py
index 02d87158..2b2bffd1 100644
--- a/src/python/packages/amwg/amwg1.py
+++ b/src/python/packages/amwg/amwg1.py
@@ -21,7 +21,7 @@
from metrics.common.utilities import *
from metrics.computation.region import *
from unidata import udunits
-import cdutil.times, numpy, pdb
+import cdutil.times, numpy, pdb, os
import logging
logger = logging.getLogger(__name__)
@@ -491,7 +491,7 @@ def write_plot_data( self, format="text", where="", fname="" ):
self.ptype = "text"
if fname != "":
logger.debug('filename was: %s', fname)
- filename = where + fname
+ filename = os.path.join( where, fname )
else:
filename = self.outfile( format, where )
writer = open( filename, 'w' )
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index f90ee209..7bb49f4e 100755
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -123,3 +123,15 @@ ${metrics_SOURCE_DIR}/test/diagsmeta.py
--datadir=${UVCMETRICS_TEST_DATA_DIRECTORY}/
--baseline=${BASELINE_DIR}/ )
#set_tests_properties(diags_meta PROPERTIES DEPENDS diags_test_15)
+
+add_test("diags_multi"
+"python"
+${metrics_SOURCE_DIR}/test/diagsmulti.py
+--datadir=${UVCMETRICS_TEST_DATA_DIRECTORY}/
+--baseline=${BASELINE_DIR}/ )
+
+add_test("diags_multimeta"
+"python"
+${metrics_SOURCE_DIR}/test/diagsmultimeta.py
+--datadir=${UVCMETRICS_TEST_DATA_DIRECTORY}/
+--baseline=${BASELINE_DIR}/ )
diff --git a/test/baselines/diagsmulti/dryrunopts b/test/baselines/diagsmulti/dryrunopts
new file mode 100644
index 00000000..4ec9e060
--- /dev/null
+++ b/test/baselines/diagsmulti/dryrunopts
@@ -0,0 +1,24 @@
+Options instance
+ modelpath = ['/test/data/cam35_data_smaller']
+ obspath = ['/test/data/obs_data_5.6']
+ obs [0][ filter ] = f_startswith('ISCCP_')
+ package = AMWG
+ sets = ['5']
+ seasons = ['DJF', 'JJA', 'ANN']
+ vars = ['FLUT', 'LWCF']
+Options instance
+ modelpath = ['/test/data/cam35_data_smaller']
+ obspath = ['/test/data/obs_data_5.6']
+ obs [0][ filter ] = f_and( f_startswith('CERES_'), f_not(f_contains('EBAF')))
+ package = AMWG
+ sets = ['5']
+ seasons = ['DJF', 'JJA', 'ANN']
+ vars = ['FLUT', 'LWCF']
+Options instance
+ modelpath = ['/test/data/cam35_data_smaller']
+ obspath = ['/test/data/obs_data_5.6']
+ obs [0][ filter ] = f_startswith('NCEP')
+ package = AMWG
+ sets = ['5']
+ seasons = ['DJF', 'JJA', 'ANN']
+ vars = ['FLUT', 'LWCF']
diff --git a/test/diagsmulti.py b/test/diagsmulti.py
new file mode 100644
index 00000000..c6707a3e
--- /dev/null
+++ b/test/diagsmulti.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+"""" This is a test of multidiags"""
+import sys, os, shutil, tempfile, subprocess, filecmp
+import argparse, pdb
+import diags_test
+
+#to run this app use
+#uvcmetrics/test/diagsmulti.py --datadir $HOME/uvcmetrics_test_data/ --baseline $HOME/uvcmetrics/test/baselines/
+
+#get commmand line args
+p = argparse.ArgumentParser(description="Basic multidiags test")
+p.add_argument("--datadir", dest="datadir", help="root directory for model and obs data")
+p.add_argument("--baseline", dest="baseline", help="directory with baseline files for comparing results")
+p.add_argument("--rebaseline", dest="rebaseline", help="instructions to rebaseline the output")
+
+args = p.parse_args(sys.argv[1:])
+datadir = args.datadir
+print 'datadir = ', datadir
+baselinepath = args.baseline + '/diagsmulti/'
+print "baselinepath = ", baselinepath
+outpath = tempfile.mkdtemp() + "/"
+print "outpath=", outpath
+rebaseline = False
+if 'rebaseline' in dir(args):
+ # Actually this never happens.
+ rebaseline = args.rebaseline
+
+test_str = 'multidiags\n'
+#run this from command line to get the files required
+command = "multidiags.py --package AMWG --set 5 --modelpath=%s/cam35_data_smaller/ --obspath=%sobs_data_5.6/ --dryrun=%s/dryrunopts --outputdir=%s "%(datadir, datadir, outpath, outpath)
+print command
+#+ "--outputdir " + outpath
+
+#run the command
+proc = subprocess.Popen([command], shell=True)
+proc_status = proc.wait()
+if proc_status!=0:
+ raise Exception("multidiags test failed")
+
+new = open(os.path.join(outpath,"dryrunopts"))
+old = open(os.path.join(baselinepath,"dryrunopts"))
+
+print "Comparing: %s %s" % (new.name, old.name)
+
+new = new.readlines()
+old = old.readlines()
+
+diff = False
+for OLD, NEW in zip(old, new):
+ # Get rid of root part of model and obs paths:
+ # This may not work if there be more than one item in the path list...
+ testfind = NEW.find('/test/')
+ if testfind>0:
+ NEW = NEW[0:NEW.find("path = [")+9] + "" + NEW[testfind:]
+ if not rebaseline:
+ if not (OLD==NEW):
+ print "OLD=",OLD
+ print "NEW=",NEW
+ raise Exception("multidiags.sh generated different files")
+if rebaseline:
+ print 'new baseline is',os.path.join(baselinepath,'dryrunopts')
diff --git a/test/diagsmultimeta.py b/test/diagsmultimeta.py
new file mode 100644
index 00000000..97aa76d9
--- /dev/null
+++ b/test/diagsmultimeta.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+"""" This is a test of whether multidiags and metadiags do the same thing"""
+import sys, os, shutil, tempfile, subprocess, filecmp
+import argparse, pdb
+import diags_test
+
+#to run this app use
+#uvcmetrics/test/diagsmultmetai.py --datadir $HOME/uvcmetrics_test_data/ --baseline $HOME/uvcmetrics/test/baselines/
+
+#get commmand line args
+p = argparse.ArgumentParser(description="Basic multidiags test")
+p.add_argument("--datadir", dest="datadir", help="root directory for model and obs data")
+p.add_argument("--baseline", dest="baseline", help="directory with baseline files for comparing results")
+p.add_argument("--rebaseline", dest="rebaseline", help="instructions to rebaseline the output")
+
+args = p.parse_args(sys.argv[1:])
+datadir = args.datadir
+print 'datadir = ', datadir
+baselinepath = args.baseline + '/diagsmultimeta/'
+print "baselinepath = ", baselinepath
+outpath = tempfile.mkdtemp() + "/"
+print "outpath=", outpath
+rebaseline = False
+if 'rebaseline' in dir(args):
+ # Actually this never happens.
+ rebaseline = args.rebaseline
+
+test_str = 'multidiags\n'
+#run this from command line to get the files required
+multi_command = "multidiags.py --package AMWG --set 7s --modelpath=%s/cam35_data_smaller/ --obspath=%sobs_data_5.6/ --dryrun=%s/multi_dryrunopts --outputdir=%s "%(datadir, datadir, outpath, outpath)
+print multi_command
+#+ "--outputdir " + outpath
+
+test_str = 'metadiags\n'
+#run this from command line to get the files required
+meta_command = "metadiags.py --package AMWG --set 7s --model path=%s/cam35_data_smaller/,climos=yes --obs path=%sobs_data_5.6/,climos=yes --dryrun=%s/meta_dryrunopts --outputdir=%s"%(datadir, datadir, outpath, outpath)
+print meta_command
+#+ "--outputdir " + outpath
+
+#run the multidiags command
+multi_proc = subprocess.Popen([multi_command], shell=True)
+
+#run the metadiags command
+meta_proc = subprocess.Popen([meta_command], shell=True)
+
+# Wait for both processes to finish.
+multi_proc_status = multi_proc.wait()
+if multi_proc_status!=0:
+ raise Exception("multidiags-metadiags test failed in multidiags")
+meta_proc_status = meta_proc.wait()
+if meta_proc_status!=0:
+ raise Exception("multidiags-metadiags test failed in metadiags")
+
+metafile = open(os.path.join(outpath,"meta_dryrunopts"))
+multifile = open(os.path.join(outpath,"multi_dryrunopts"))
+print "Comparing: %s %s" % (metafile.name, multifile.name)
+
+if filecmp.cmp( metafile.name, multifile.name ):
+ pass
+else:
+ raise Exception("metadiags and multidiags produced different files")