You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@climate.apache.org by pr...@apache.org on 2013/08/27 07:35:49 UTC
svn commit: r1517753 [11/33] - in /incubator/climate/branches/rcmet-2.1.1:
./ src/ src/main/ src/main/python/ src/main/python/bin/
src/main/python/docs/ src/main/python/docs/_static/
src/main/python/docs/_templates/ src/main/python/rcmes/ src/main/pyth...
Added: incubator/climate/branches/rcmet-2.1.1/src/main/python/rcmes/utils/misc.py
URL: http://svn.apache.org/viewvc/incubator/climate/branches/rcmet-2.1.1/src/main/python/rcmes/utils/misc.py?rev=1517753&view=auto
==============================================================================
--- incubator/climate/branches/rcmet-2.1.1/src/main/python/rcmes/utils/misc.py (added)
+++ incubator/climate/branches/rcmet-2.1.1/src/main/python/rcmes/utils/misc.py Tue Aug 27 05:35:42 2013
@@ -0,0 +1,1376 @@
+"""Module with a collection of helper functions"""
+
+
+import ConfigParser
+import datetime
+import glob
+import json
+import os
+import sys
+
+import numpy as np
+import numpy.ma as ma
+import netCDF4
+
+import classes
+
+from fortranfile import FortranFile
+from toolkit import process
+
+def configToDict(config):
+ """
+ Helper function to parse a configuration input and return a python dictionary
+
+ Input::
+ config - list of ('key', 'value') tuples from ConfigParser.
+ key01 - string i.e. 'value01'
+ key-2 - string i.e. 'value02'
+ Output::
+ configDict - Dictionary of Key/Value pairs
+ """
+ configDict = {}
+ for entry in config:
+ configDict[entry[0]] = entry[1]
+
+ return configDict
+
+
+def readSubRegionsFile(regionsFile):
+ """
+ Input::
+ regionsFile - Path to a subRegion configuration file
+
+ Output::
+ Ordered List of SubRegion Objects decoded from regionsFile
+ """
+ if os.path.exists(regionsFile):
+ regionsConfig = ConfigParser.SafeConfigParser()
+ regionsConfig.optionxform = str
+ regionsConfig.read(regionsFile)
+ regions = generateSubRegions(regionsConfig.items('REGIONS'))
+
+ return regions
+ else:
+ raise IOError
+
+def getSubRegionsInteractively(counts, workdir):
+ """
+ Purpose::
+ This function provides a commandline Q&A session to help users define
+ SubRegion Objects.
+ Input::
+ counts - dictionary with int counts of various metric inputs
+ i.e. {'observations': 3,
+ 'models' : 1,
+ 'times' : 120}
+ workdir - string of the current working directory where auxillary files
+ can be found. In this case the directory will be used to locate an
+ existing sub_regions.txt file.
+
+ Output::
+ subRegions = List of Parsed SubRegion Objects based on User inputs
+ """
+ do_timeseries = None
+ yes_no_list = ['y', 'n', '']
+
+ while do_timeseries not in yes_no_list:
+ do_timeseries = raw_input('Calculate area-mean timeseries for subregions? y/n: [n] \n>>>')
+ if do_timeseries not in yes_no_list:
+ print("'%s' is not a valid answer please try again" % do_timeseries)
+
+ if do_timeseries == 'y':
+ interactive_subregions = None
+ while interactive_subregions not in yes_no_list:
+ interactive_subregions = raw_input('Input Sub Region info interactively? y/n: [n] \n>>>')
+ if interactive_subregions not in yes_no_list:
+ print("'%s' is not a valid answer please try again" % interactive_subregions)
+
+ if interactive_subregions == 'y':
+ while interactive_subregions == 'y':
+ regions = []
+ region = createSubRegionObjectInteractively()
+ regions.append(region)
+ anotherRegion = None
+ while anotherRegion == None:
+ another = raw_input("Would you like to add another sub region? y/n [n] \n>>>")
+ if another not in yes_no_list:
+ print("'%s' is not a valid answer please try again" % another)
+ elif another in ['', 'n']:
+ anotherRegion = 'n'
+ interactive_subregions = 'n'
+ else:
+ anotherRegion = 'y'
+
+ else:
+ subRegionFilename = None
+ while subRegionFilename == None:
+ readDefault = raw_input('Read from a default file (workdir + "/sub_regions.txt")? y/n: [y] \n>>>')
+
+ if readDefault == 'y' or readDefault == '':
+ subRegionFilename = workdir + "/sub_regions.txt"
+ print("Attempting to parse %s..." % subRegionFilename)
+ regions = readSubRegionsFile(subRegionFilename)
+
+ elif readDefault == 'n':
+ while subRegionFilename == None:
+ # ask about using a non default filename
+ subRegionFilename = raw_input('Enter the full path to the Sub Region file to read from:\n>>>')
+ print("Attempting to parse %s..." % subRegionFilename)
+ regions = readSubRegionsFile(subRegionFilename)
+
+ elif readDefault == 'NONE':
+ subRegionFilename = 'NONE'
+ regions = []
+
+ else:
+ print("'%'s is not a valid selection. Please try again. To proceed without Sub Regions defined enter NONE at the prompt" % readDefault)
+
+ return regions
+
+def generateSubRegions(regions):
+ """ Takes in a list of ConfigParser tuples and returns a list of SubRegion objects
+
+ Input::
+ regions - Config Tuple: [('Region01', '["R01", 36.5, 29, 0.0, -10]'), ('Region02',....]
+
+ Output::
+ subRegions - list of SubRegion objects
+ """
+ subRegions = []
+ for region in regions:
+ name, latMax, latMin, lonMax, lonMin = json.loads(region[1])
+ subRegion = classes.SubRegion(name, latMin, lonMin, latMax, lonMax)
+ subRegions.append(subRegion)
+
+ return subRegions
+
+def parseSubRegions(subRegionConfig):
+ """
+ Input::
+ subRegionConfig - ConfigParser object
+
+ Output::
+ subRegions - List of SubRegion Objects. This could return an empty List if unable to parse subRegionConfig
+ """
+ subRegions = []
+ try:
+ if os.path.exists(subRegionConfig['subRegionFile']):
+ subRegions = readSubRegionsFile(subRegionConfig['subRegionFile'])
+
+ else:
+ print "SubRegion Filepath: [%s] does not exist. Check your configuration file, or comment out the SUB_REGION Section." % (subRegionConfig['subRegionFile'],)
+ print "Processing without SubRegion support"
+
+
+ except IOError:
+ print "Unable to open %s. Running without SubRegions" % (subRegionConfig['subRegionFile'],)
+ except KeyError:
+ print "subRegionFile parameter was not provided. Processing without SubRegion support"
+
+ return subRegions
+
+
+def decode_wrf_times(xtimes, base_time):
+ '''
+ Routine to convert from WRF time ('minutes since simulation start....')
+ into a python datetime structure
+ Input:
+ xtimes - list of wrf 'XTIME' in units of 'minutes since simulation start'
+ base_time - a python datetime object describing simulation start date/time
+ Output:
+ times - list of python datetime objects describing model data times
+ Peter Lean August 2010
+ '''
+ times = []
+ for xtime in xtimes:
+ dt = datetime.timedelta(minutes=xtime)
+ times.append(base_time + dt)
+ return times
+
+def calc_base_time_wrf(filename):
+ '''
+ Routine to calculate base_time (i.e. model initialization time)
+ for wrf files with timestamp in their filenames.
+ NB. Only works if includes a timestamp in format 'YYYY-MM-DD_HH:MM:SS'
+ TODO: work out a more general way of doing this...
+ Input:
+ filename - full path to WRF netCDF file.
+ Output:
+ base_time - a python datetime object describing model start time
+ Peter Lean August 2010
+ '''
+
+ # Extract time from netCDF file (in units of 'minutes since beginning of simulation')
+ f = netCDF4.Dataset(filename, mode='r')
+ timesraw = f.variables["XTIME"]
+ model_time = timesraw[0]
+
+ dt = datetime.timedelta(minutes=int(model_time))
+
+ # Extract and decode timestamp from filename
+
+ filename = os.path.basename(filename) # strip off the filepath
+
+ timestamp_string = filename[11:30] # cut out the date time stamp
+ DATE_FORMAT = '%Y-%m-%d_%H:%M:%S'
+
+ timestamp = datetime.datetime(*time.strptime(timestamp_string, DATE_FORMAT)[:6])
+
+ # Base Time = timestamp - 'minutes since beginning of simulation'
+ base_time = timestamp - dt
+
+ print 'Base Time calculated as: ', base_time
+
+ return base_time
+
+def calc_period_precip_from_running_tot(running_precip):
+ '''
+ WRF precipitation accumulations are stored as running totals from the start of the model run
+ To find out values during each output time period, you must subtract the previous total
+
+ e.g. running tot = 0,0,1,1,1,2,2,4,7,9,9,11
+ period accu = 0,0,1,0,0,1,0,2,3,2,0,2
+
+ Input:
+ running_precip - numpy array containing precipitation data at more than one time level
+ NB. assumes time dimension is the first one precip[time, lat, lon, level]
+
+ Output:
+ acc_precip - numpy array of same dimensions as input,
+ but with period accumulations instead of running total.
+
+
+ Peter Lean August 2010
+
+ '''
+
+ print 'Calculating period precip accumulations from running total'
+ shifted_running_precip = np.roll(running_precip, -1, axis=0)
+ nt = running_precip.shape[0]
+ # avoid wrapping from the end time to the start time by forcing the accumulation at final time=0
+ shifted_running_precip[nt - 1, :, :] = running_precip[nt - 1, :, :]
+ acc_precip = shifted_running_precip - running_precip
+
+ # NB. sometimes rounding errors in the model lead to very small negative period accumulations
+ # These are removed and set to zero here.
+ acc_precip = np.maximum(acc_precip, 0)
+
+ return acc_precip
+
+def decode_eraint_surf_times(xtimes):
+ '''
+ Routine to convert from ERA-Interim time ('hours since 1900...')
+ into a python datetime structure
+
+ Input:
+ xtimes - list of ERA-Interim times in units of 'hours since 1900'
+
+ Output:
+ times - list of python datetime objects describing model data times
+
+
+ Peter Lean August 2010
+
+ '''
+ base_time = datetime.datetime(1900, 1, 1, 0, 0, 0, 0)
+ times = []
+
+ for xtime in xtimes:
+ dt = datetime.timedelta(hours=xtime)
+ times.append(base_time + dt)
+
+ return times
+
+def read_total_precip_from_filelist(myfilelist):
+ '''
+ WRF outputs precipitation data under several variables:
+
+ **RAINC** = convective total precip
+
+ **RAINNC** = large scale total precip ("no convective")
+
+ **SNOWC** = convective snow
+
+ **SNOWNC** = large scale snow ("no convective")
+
+ Therefore:
+ real rain = (rainc+rainnc)-(snowc+snownc)
+ total precip = rainc+rainnc+snowc+snownc
+
+ Input:
+ myfilelist - a list of filename (including full path)
+
+ Output:
+ precip - a numpy array of total precip values
+ lat, lon - 2D array of latitude and longitude values
+ times - list of times
+
+ NB. THIS ROUTINE IS NO LONGER NEEDED... I HAD MISUNDERSTOOD HOW PRECIP DATA WAS STORED IN WRF
+ TOTAL PRECIP = RAINNC
+ **A SIMILAR ROUTINE MAY BE REQUIRED TO FIND THE INDIVIDUAL COMPONENTS THOUGH..**
+
+ '''
+ myfilelist.sort()
+
+ print 'Calculating total precipitation from individual rain/snow, convective/ls components'
+ lat, lon, times, rainnc = read_data_from_file_list(myfilelist, 'RAINC')
+ lat, lon, times, rainc = read_data_from_file_list(myfilelist, 'RAINNC')
+ precip = rainnc + rainc
+ # reset negative values to zero
+ precip[precip < 0] = 0.0
+
+ return lat, lon, times, precip
+
+
+def isDirGood(directory):
+ """
+ Purpose::
+ This function will check if a directory exists and is writeable. If the directory doesn't exist it is created.
+ Input::
+ directory - Path we need to check
+ Output::
+ directoryStatus - Boolean
+ """
+ directoryStatus = False
+
+ if os.path.exists(directory):
+ directoryStatus = os.access(directory, os.W_OK | os.X_OK)
+ if directoryStatus:
+ pass
+ else:
+ raise OSError("Unable to access the directory: %s. Check that you have the proper permissions to read and write to that directory." % directory)
+ #directoryStatus = False
+ else:
+ try:
+ os.mkdir(directory)
+ directoryStatus = True
+ except OSError:
+ raise
+
+ return directoryStatus
+
+
+def read_trmm_3b42_files(filelist, latMin, latMax, lonMin, lonMax):
+ '''
+ ** Alternate method of getting TRMM data from local repository if DB not available **
+ Reads TRMM gridded precipitation data from netCDF files in local repository.
+
+ Input:
+ filelist - list of filenames (including path)
+ latMin,latMax,lonMin,lonMax - define region to extract (in degrees)
+ Output:
+ lat, lon - 1D array of latitude and longitude values
+ timestore - list of python datetime objects
+ mdata - numpy masked array containing data from all files
+
+ NB. written specific for TRMM netCDF output files
+
+ Peter Lean June 2010
+ '''
+ filelist.sort()
+
+ # Crash nicely if 'filelist' is zero length
+ if len(filelist) == 0:
+ print 'Error: no files have been passed to read_data_from_file_list()'
+ sys.exit()
+
+ # Open the first file in the list to:
+ # i) read in lats, lons
+ # ii) find out how many timesteps in the file
+ # (assume same ntimes in each file in list)
+ # -allows you to create an empty array to store variable data for all times
+ tmp = netCDF4.Dataset(filelist[0], mode='r')
+ latsraw = tmp.variables["latitude"]
+ lonsraw = tmp.variables["longitude"]
+ lat = latsraw[:]
+ lon = lonsraw[:]
+ print 'Lats and lons read in for first file in filelist'
+
+ # Find out how many times there are in the file (should always be 1 for these TRMM files)
+ timesraw = tmp.variables["time"]
+ ntimes = len(timesraw)
+
+
+ # Convert specified longitudes into 0-360 format if required
+ if(lonMin < 0):
+ lonMin = lonMin + 360
+ if(lonMax < 0):
+ lonMax = lonMax + 360
+
+ # Create mask to extract required region only
+ # NB. longitude is slightly more complicated as can wrap the prime meridion
+ print 'Extracting for :- lats:', latMin, latMax, ' lons: ', lonMin, lonMax
+ wh_lat = np.logical_and((lat > latMin), (lat < latMax))
+ if(lonMin <= lonMax):
+ wh_lon = np.logical_and((lon > lonMin), (lon < lonMax))
+ if(lonMin > lonMax):
+ wh_lon = np.logical_or((lon > lonMin), (lon < lonMax))
+
+ sublat = lat[wh_lat]
+ sublon = lon[wh_lon]
+
+ wh_true1, wh_true2 = np.meshgrid(wh_lon, wh_lat)
+ wh = np.logical_and(wh_true1, wh_true2)
+
+ # Create empty array to store data
+ t2store = np.empty((ntimes * len(filelist), sublat.size, sublon.size))
+ timestore = []
+
+ # Now load in the data for real
+ # NB. no need to reload in the latitudes and longitudes -assume invariant
+ i = 0
+ timesaccu = 0 # a counter for number of times stored so far in t2store
+ # NB. this method allows for missing times in data files
+ # as no assumption made that same number of times in each file...
+ for ifile in filelist:
+ print 'Loading data from file: ', filelist[i]
+ f = netCDF4.Dataset(ifile, mode = 'r')
+ t2raw = f.variables['hrf']
+
+ # Read time from filename (NB. 'time' variable in netCDF always set to zero)
+ filename = os.path.basename(ifile) # strip off the filepath
+ timestamp_string = filename[11:23] # cut out the date time stamp
+ DATE_FORMAT = '%Y.%m.%d.%H'
+ mytime = datetime.datetime(*time.strptime(timestamp_string, DATE_FORMAT)[:4])
+ ntimes = 1
+ t2tmp = t2raw[0, :, :]
+ sub = t2tmp[wh].reshape(sublat.size, sublon.size)
+ t2store[timesaccu, :, :] = sub
+ timestore.append(mytime)
+ timesaccu = timesaccu + ntimes
+ i += 1
+
+ print 'Data read in successfully with dimensions: ', t2store.shape
+
+ # Create masked array using missing value flag from file
+ mdi = f.variables['hrf'].missing_value[0]
+ mdata = ma.masked_array(t2store, mask=(t2store == mdi))
+
+ return sublat, sublon, timestore, mdata
+
+def read_airs_lev3_files(filelist, myvar, latMin, latMax, lonMin, lonMax):
+ '''
+ ** For testing work before database was ready. **
+ Reads AIRS level III gridded data from netCDF files.
+
+ Input:
+ filelist - list of filenames (including path)
+ myvar - name of variable to load
+ latMin,latMax,lonMin,lonMax - define region to extract (in degrees)
+ Output:
+ lat, lon - 1D array of latitude and longitude values
+ timestore - list of python datetime objects
+ mdata - numpy masked array containing data from all files
+
+ NB. written specific for AIRS level III netCDF output files
+
+ NB. Ascending passes have local time of 1:30pm
+ NB. Descending passes have local time of 1:30am
+
+ Peter Lean June 2010
+ '''
+
+ filelist.sort()
+
+ # Crash nicely if 'filelist' is zero length
+ if len(filelist) == 0:
+ print 'Error: no files have been passed to read_data_from_file_list()'
+ sys.exit()
+
+ # Open the first file in the list to:
+ # i) read in lats, lons
+ # ii) find out how many timesteps in the file
+ # (assume same ntimes in each file in list)
+ # -allows you to create an empty array to store variable data for all times
+ tmp = netCDF4.Dataset(filelist[0], mode = 'r')
+ latsraw = tmp.variables["lat"]
+ lonsraw = tmp.variables["lon"]
+ lat = latsraw[:]
+ lon = lonsraw[:]
+ print 'Lats and lons read in for first file in filelist'
+
+ # Only one time per file in AIRS level III data
+ ntimes = 1
+
+ # Create mask to extract required region only
+ # NB. longitude is slightly more complicated as can wrap the prime meridion
+ print 'Extracting for :- lats:', latMin, latMax, ' lons: ', lonMin, lonMax
+ wh_lat = np.logical_and((lat >= latMin), (lat <= latMax))
+ if(lonMin < lonMax):
+ wh_lon = np.logical_and((lon >= lonMin), (lon <= lonMax))
+ if(lonMin > lonMax):
+ wh_lon = np.logical_or((lon >= lonMin), (lon <= lonMax))
+
+ sublat = lat[wh_lat]
+ sublon = lon[wh_lon]
+
+ wh_true1, wh_true2 = np.meshgrid(wh_lon, wh_lat)
+ wh = np.logical_and(wh_true1, wh_true2)
+
+ # Create empty array to store data
+ t2store = np.empty((ntimes * len(filelist), sublat.size, sublon.size))
+ timestore = []
+
+ # Now load in the data for real
+ # NB. no need to reload in the latitudes and longitudes -assume invariant
+ i = 0
+ timesaccu = 0 # a counter for number of times stored so far in t2store
+ # NB. this method allows for missing times in data files
+ # as no assumption made that same number of times in each file...
+ for ifile in filelist:
+ print 'Loading data from file: ', filelist[i]
+ f = netCDF4.Dataset(ifile, mode='r')
+ t2raw = f.variables[myvar]
+
+ # Read time from filename (NB. 'time' variable in netCDF always set to zero)
+ filename = os.path.basename(ifile) # strip off the filepath
+ timestamp_string = filename[5:15] # cut out the date time stamp
+ DATE_FORMAT = '%Y.%m.%d'
+ mytime = datetime.datetime(*time.strptime(timestamp_string, DATE_FORMAT)[:4])
+ print mytime
+ ntimes = 1
+ t2tmp = t2raw[:, :]
+ sub = t2tmp[wh].reshape(sublat.size, sublon.size)
+ t2store[timesaccu, :, :] = sub
+ timestore.append(mytime)
+ timesaccu = timesaccu + ntimes
+ i += 1
+
+
+ print 'Data read in successfully with dimensions: ', t2store.shape
+
+ # Create masked array using missing value flag from file
+ mdi = f.variables[myvar]._FillValue[0]
+ mdata = ma.masked_array(t2store, mask=(t2store == mdi))
+ # Rearrange array so data match lat lon values
+ mdata = np.flipud(mdata[:, ::-1])
+
+ return sublat, sublon, timestore, mdata
+
+def read_urd_files(filelist, latMin, latMax, lonMin, lonMax):
+ '''
+ Routine to load in NCEP Unified Raingauge Database binary files
+
+ Input:
+ filelist - a list of URD data files
+
+
+ Output:
+ sublats, sublons: 2d arrays of latitude and longitude values for user selected region.
+ times - a list of python datetime objects
+ subdata - precipitation data for user selected region
+
+ Peter Lean August 2010
+ '''
+
+ repository_path = '/nas/share1-hp/jinwonki/data/obs/pr25urd/daily/'
+
+ # NB. Domain: 140W - 60W; 20N - 60N; Resolution: 0.25x0.25 degrees.
+ # The grids are arranged such that
+ # longitude is from 140W eastward to 60W and latitude from 20N northward
+ # to 60N, so that the first grid is (140W,20N), the second is
+ # (139.75W,20N)......
+
+ # Parameters specific to the URD dataset
+ nlat = 161
+ nlon = 321
+
+ # Calculate the latitude and longitude arrays
+ lat = np.arange(20, 60.25, 0.25) # Hard wired lat,lon extent values for URD data files
+ lon = np.arange(-140, -59.75, 0.25)
+
+ lons, lats = np.meshgrid(lon, lat)
+
+ # Define sub-region mask
+ # NB. longitude is slightly more complicated as can wrap the prime meridion
+ print 'Extracting for :- lats:', latMin, latMax, ' lons: ', lonMin, lonMax
+ wh_lat = np.logical_and((lats >= latMin), (lats <= latMax))
+ if(lonMin < lonMax):
+ wh_lon = np.logical_and((lons >= lonMin), (lons <= lonMax))
+ if(lonMin > lonMax):
+ wh_lon = np.logical_or((lons >= lonMin), (lons <= lonMax))
+
+ # count number of latitude values in subselection (for redimensioning array)
+ wh_true = np.logical_and(wh_lat, wh_lon)
+ nsublat = np.where(np.logical_and((lat >= latMin), (lat <= latMax)))[0].size
+
+ sublats = lats[wh_true].reshape(nsublat, -1) # infers longitude dimension given latitude dimension
+ sublons = lons[wh_true].reshape(nsublat, -1) # infers longitude dimension given latitude dimension
+ nsublon = sublats.shape[1]
+
+ # Load in the daily data
+ datastore = []
+ for myfile in filelist:
+ f = FortranFile(repository_path + myfile)
+
+ # Extract month and year from filename
+ yearmonth = int(myfile[7:13])
+ year = int(str(yearmonth)[0:4])
+ month = int(str(yearmonth)[4:6])
+
+ # Find out how many days there are in that month
+ nt = calendar.monthrange(year, month)[1]
+
+ data = np.zeros((nt, nsublat, nsublon))
+ for t in np.arange(nt):
+ precip = f.readReals()
+ precip.shape = [nlat, nlon]
+ data[t, :, :] = precip[wh_true].reshape(nsublat, nsublon)
+
+ datastore.append(data)
+
+ # Make a single 3d numpy array out of my list of numpy arrays
+ nt = 0
+ for i in range(len(datastore)):
+ nt = nt + datastore[i].shape[0]
+
+ final_data = np.zeros((nt, nsublat, nsublon))
+ t = 0
+ for i in range(len(datastore)):
+ nt = datastore[i].shape[0]
+ final_data[t:t + nt, :, :] = datastore[i]
+ t = t + nt
+
+ # Load in land/sea mask
+ ls = np.fromfile("/nas/share1-hp/jinwonki/data/obs/pr25urd/s/d/lsm25.usa", sep=" ")
+ ls.shape = [nlat, nlon]
+ # Extract subregion from land/sea mask
+ subls = np.ones(final_data.shape)
+ for t in np.arange(final_data.shape[0]):
+ subls[t, :, :] = ls[wh_true].reshape(nsublat, nsublon)
+
+ # Construct a masked array of data i.e. only using data from land points
+ mdi = -1
+ mdata = ma.masked_array(final_data, mask=(subls == mdi))
+
+ # Construct datetime list from dates in filenames.
+ yearmonth = np.zeros(len(filelist))
+ i = 0
+ for filename in filelist:
+ # Extract month and year from filename
+ yearmonth[i] = int(filename[7:13])
+ i += 1
+
+ # Construct a list of datetimes between the earliest and latest yearmonth
+ firstyear = int(str(yearmonth.min())[0:4])
+ firstmonth = int(str(yearmonth.min())[4:6])
+ times = []
+
+ cur_time = datetime.datetime(firstyear, firstmonth, 1, 0, 0, 0, 0)
+
+ for i in range(final_data.shape[0]):
+ times.append(cur_time)
+ dt = datetime.timedelta(days=1)
+ cur_time = cur_time + dt
+
+ return sublats, sublons, times, mdata
+
+def read_tmp_watershed(myfile, dom_num):
+ '''
+ Routine to read watershed weighting file mapped onto WRF model grids.
+ NB.this will be superceded by proper routines to read shape files and regrid onto any model grid.
+
+ Input:
+ myfile - file name of the watershed ascii file to load
+ dom_num - WRF domain number (specific to this experiment)
+
+ Output:
+ mymask - boolean mask array saying where the watershed is
+
+ Peter Lean September 2010
+ '''
+
+ # Parameters specific to WRF domain setup required for these files
+ if(dom_num == 1):
+ nx = 190
+ ny = 130
+
+ if(dom_num == 2):
+ nx = 192
+ ny = 180
+
+ # Create an empty array to store the weights
+ myweights = np.zeros((ny, nx))
+
+ # Load in data from the mask file
+ i, j, w = np.loadtxt("/home/plean/demo/rcmes/watersheds/" + myfile, unpack=True)
+
+ for q in np.arange(len(i)):
+ myweights[j[q], i[q]] = w[q]
+
+ mymask = np.empty((ny, nx))
+ mymask[:] = True
+ mymask[(myweights > 0.5)] = False
+
+ return mymask
+
+def read_eraint_surf_files(filelist, myvar, latMin, latMax, lonMin, lonMax):
+ '''
+ ** For testing work before database was ready. **
+ Reads ERA-Interim surface netCDF files.
+
+ Input:
+ filelist - list of filenames (including path)
+ myvar - name of variable to load
+ latMin,latMax,lonMin,lonMax - define region to extract (in degrees)
+ Output:
+ lat, lon - 1D array of latitude and longitude values
+ timestore - list of python datetime objects
+ mdata - numpy masked array containing data from all files
+
+ Peter Lean September 2010
+ '''
+
+
+ # Crash nicely if 'filelist' is zero length
+ if len(filelist) == 0:
+ print 'Error: no files have been passed to read_data_from_file_list()'
+ sys.exit()
+
+ # Open the first file in the list to:
+ # i) read in lats, lons
+ # ii) find out how many timesteps in the file
+ # (assume same ntimes in each file in list)
+ # -allows you to create an empty array to store variable data for all times
+ tmp = netCDF4.Dataset(filelist[0], mode='r')
+ latsraw = tmp.variables["latitude"]
+ lonsraw = tmp.variables["longitude"]
+ lat = latsraw[:]
+ lon = lonsraw[:]
+ print 'Lats and lons read in for first file in filelist'
+
+ # Create mask to extract required region only
+ # NB. longitude is slightly more complicated as can wrap the prime meridion
+ print 'Extracting for :- lats:', latMin, latMax, ' lons: ', lonMin, lonMax
+ wh_lat = np.logical_and((lat >= latMin), (lat <= latMax))
+ if(lonMin < lonMax):
+ wh_lon = np.logical_and((lon >= lonMin), (lon <= lonMax))
+ if(lonMin > lonMax):
+ wh_lon = np.logical_or((lon >= lonMin), (lon <= lonMax))
+
+ sublat = lat[wh_lat]
+ sublon = lon[wh_lon]
+
+ wh_true1, wh_true2 = np.meshgrid(wh_lon, wh_lat)
+ wh = np.logical_and(wh_true1, wh_true2)
+
+ # Create storage lists
+ datastore = []
+ timestore = []
+
+ # Now load in the data for real
+ # NB. no need to reload in the latitudes and longitudes -assume invariant
+ i = 0
+ timesaccu = 0 # a counter for number of times stored so far in t2store
+ # NB. this method allows for missing times in data files
+ # as no assumption made that same number of times in each file...
+ for ifile in filelist:
+ print 'Loading data from file: ', filelist[i]
+ f = netCDF4.open_file(ifile, mode='r')
+ data = f.variables[myvar][:]
+ scale = f.variables[myvar].scale_factor
+ offset = f.variables[myvar].add_offset
+ data = data * scale + offset
+ times = f.variables["time"][:]
+ ntimes = times.size
+ # Decode times into python datetime object
+ sub = data[:, wh].reshape(ntimes, sublat.size, sublon.size)
+ datastore.append(sub)
+ timestore.append(times)
+ timesaccu = timesaccu + ntimes
+ i += 1
+
+ # move data from lists into correctly dimensioned arrays
+ final_data = np.zeros((timesaccu, sublat.size, sublon.size))
+ t = 0
+ for i in range(len(datastore)):
+ nt = datastore[i].shape[0]
+ final_data[t:t + nt, :, :] = datastore[i]
+ t = t + nt
+
+ times = np.zeros((timesaccu))
+ t = 0
+ for i in range(len(timestore)):
+ nt = timestore[i].shape[0]
+ times[t:t + nt] = timestore[i]
+ t = t + nt
+
+ # Decode times into python datetime objects
+ times = rcmes.process.decode_eraint_surf_times(times)
+
+ print 'Data read in successfully with dimensions: ', final_data.shape
+
+ # Create masked array using missing value flag from file
+ mdi = f.variables[myvar]._FillValue[0]
+ mdata = ma.masked_array(final_data, mask=(final_data == mdi))
+
+ # Rearrange array so data match lat lon values
+ mdata = np.flipud(mdata[:, ::-1])
+
+ return sublat, sublon, times, mdata
+
+def make_list_of_wrf_files(firstTime, lastTime, ident):
+ '''
+ Routine to make list of WRF filenames given time period.
+
+ Input:
+ firstTime - datetime object specifying start time
+ lastTime - datetime object specifying end time
+ ident - identifier for model run, e.g. 'd01'
+
+ Output:
+ filelist - list of standard format WRF filenames
+
+ Peter Lean
+ '''
+
+ dt = datetime.timedelta(hours=6)
+ filenamelist = []
+ curTime = firstTime
+
+ while curTime <= lastTime:
+ curTimeString = curTime.strftime("%Y-%m-%d_%H:%M:%S")
+ filenamelist.append('wrfout_' + ident + '_' + curTimeString)
+ curTime += dt
+
+ return filenamelist
+
+def make_list_of_trmm_files(firstTime, lastTime):
+ '''
+ Routine to make list of TRMM filenames given time period.
+
+ Input:
+ firstTime - datetime object specifying start time
+ lastTime - datetime object specifying end time
+
+ Output:
+ filelist - list of standard format WRF filenames
+
+ Peter Lean
+ '''
+ trmm_repository = '/nas/share4-cf/plean/TRMM/'
+ dt = datetime.timedelta(hours=24)
+ filenamelist = []
+ curTime = firstTime
+ while curTime <= lastTime:
+ curTimeString = curTime.strftime("%Y.%m.%d")
+ filenamelist.append(trmm_repository + '3B42_daily.' + curTimeString + '.6.nc')
+
+ curTime += dt
+
+ return filenamelist
+
+def make_list_of_airs_files(firstTime, lastTime):
+ '''
+ Routine to make list of AIRS filenames given time period.
+
+ Input:
+ firstTime - datetime object specifying start time
+ lastTime - datetime object specifying end time
+
+ Output:
+ filelist - list of standard format WRF filenames
+
+ Peter Lean
+ '''
+
+
+ airs_repository = '/nas/share4-cf/plean/AIRX3STD/'
+ dt = datetime.timedelta(hours=24)
+ filenamelist = []
+ curTime = firstTime
+ while curTime <= lastTime:
+ curTimeString = curTime.strftime("%Y.%m.%d")
+ filenamelist.append(glob.glob(airs_repository + 'AIRS.' + curTimeString + '.L3.*.nc')[0])
+
+ curTime += dt
+
+ return filenamelist
+
+def make_list_of_urd_files(firstTime, lastTime):
+ '''
+ Routine to make list of URD filenames given time period.
+
+ Input:
+ firstTime - datetime object specifying start time
+ lastTime - datetime object specifying end time
+
+ Output:
+ filelist - list of standard format WRF filenames
+
+ Peter Lean
+ '''
+
+ dt = datetime.timedelta(days=30)
+ filenamelist = []
+ newfirstTime = datetime.datetime(firstTime.year, firstTime.month, 15, 0, 0, 0)
+ newlastTime = datetime.datetime(lastTime.year, lastTime.month, 15, 0, 0, 0)
+
+ curTime = newfirstTime
+ while curTime <= newlastTime:
+ curTimeString = curTime.strftime("%Y%m")
+ filenamelist.append('pr_ncep' + curTimeString)
+ if(curTime.month == 1):
+ curTime = datetime.datetime(curTime.year, curTime.month, 15, 00, 00, 00, 00)
+
+ curTime += dt
+
+ return filenamelist
+
+def make_list_of_era_surf_files(firstTime, lastTime):
+ '''
+ Routine to make list of ERA-Interim surface filenames given time period.
+
+ Input:
+ firstTime - datetime object specifying start time
+ lastTime - datetime object specifying end time
+
+ Output:
+ filelist - list of standard format WRF filenames
+
+ Peter Lean
+ '''
+
+ import datetime
+ eraint_repository = '/data/plean/era-int/surf/'
+ filenamelist = []
+ dt = datetime.timedelta(days=30)
+ newfirstTime = datetime.datetime(firstTime.year, firstTime.month, 15, 0, 0, 0)
+ newlastTime = datetime.datetime(lastTime.year, lastTime.month, 15, 0, 0, 0)
+ curTime = newfirstTime
+
+ while curTime <= newlastTime:
+ curTimeString = curTime.strftime("%b%Y")
+ filenamelist.append(eraint_repository + 'sfc.' + curTimeString.lower() + '.nc')
+ if(curTime.month == 1):
+ curTime = datetime.datetime(curTime.year, curTime.month, 15, 00, 00, 00, 00)
+
+ curTime += dt
+
+ return filenamelist
+
+
+#
+
+def assign_subRgns_from_a_text_file(infile):
+ # Read pre-fabricated sugregion information from a text file
+ # Note: python indexing includes the beginning point but excludes the ending point
+ f = open(infile, 'r')
+ for i in np.arange(8):
+ string = f.readline()
+ print 'Line ', i, ': Content ', string
+ string = f.readline()
+ numSubRgn = int(string[20:22])
+ print 'numSubRgn = ', numSubRgn
+ for i in np.arange(3):
+ string = f.readline()
+ # Read input string and extract subRegion info (name, longs, lats) from the string
+ subRgnName = []
+ subRgnLon0 = ma.zeros((numSubRgn))
+ subRgnLon1 = ma.zeros((numSubRgn))
+ subRgnLat0 = ma.zeros((numSubRgn))
+ subRgnLat1 = ma.zeros((numSubRgn))
+ for i in np.arange(numSubRgn):
+ string = f.readline()
+ subRgnName.append(string[0:19])
+ subRgnLon0[i] = float(string[30:37])
+ subRgnLon1[i] = float(string[40:47])
+ subRgnLat0[i] = float(string[50:55])
+ subRgnLat1[i] = float(string[60:65])
+ f.close()
+ print 'subRgnName: ', subRgnName
+ print 'subRgnLon0: ', subRgnLon0
+ print 'subRgnLon1: ', subRgnLon1
+ print 'subRgnLat0: ', subRgnLat0
+ print 'subRgnLat1: ', subRgnLat1
+ return numSubRgn, subRgnName, subRgnLon0, subRgnLon1, subRgnLat0, subRgnLat1
+
+def createSubRegionObjectInteractively():
+ """
+ Purpose::
+ Mini command line program to enable users to enter SubRegion Information
+ Input::
+ None
+ Output::
+ SubRegion Object
+ """
+ rawInput = None
+ while rawInput == None:
+ userMessage = ("Enter information for 1 Sub Region using the following "
+ "pipe '|' separated format: \n"
+ "RegionName | Degrees North | Degrees South | Degrees East | Degrees West \n>>>")
+ userInput = raw_input(userMessage)
+ inputList = userInput.split('|')
+ if len(inputList) != 5:
+ badLengthMessage = ("Unable to parse %s. You should have 5 inputs "
+ "separated by 4 pipe characters. \n"
+ "Example: Region Name | 85 | 80 | 10 | -30" % userInput)
+ print(badLengthMessage)
+ else:
+ name = str(inputList[0]).strip()
+ latMax = str(inputList[1]).strip()
+ latMin = str(inputList[2]).strip()
+ lonMax = str(inputList[3]).strip()
+ lonMin = str(inputList[4]).strip()
+ subRegion = classes.SubRegion(name, latMin, lonMin, latMax, lonMax)
+ rawInput = True
+
+ return subRegion
+
+def selectSubRegion(subRegions):
+ # interactively select the sub-region ID for area-mean time-series evaluation
+ print '-'*59
+ columnTemplate = "|{0:2}|{1:10}|{2:10}|{3:10}|{4:10}|{5:10}|"
+ print columnTemplate.format("ID", "Name", "LonMin", "LonMax", "LatMin", "LatMax")
+ counter = 0
+ for subRegion in subRegions:
+ print columnTemplate.format(counter, subRegion.name, subRegion.lonMin, subRegion.lonMax, subRegion.latMin, subRegion.latMax)
+ counter += 1
+
+ print '-'*59
+ ask = 'Enter the sub-region ID to be evaluated. -9 for all sub-regions: \n'
+ rgnSelect = int(raw_input(ask))
+ if rgnSelect >= len(subRegions):
+ print 'sub-region ID out of range. Max = %s' % len(subRegions)
+ sys.exit()
+ return rgnSelect
+
+def getStartEndTimes(status, startOverLapTime, endOverLapTime):
+ '''
+ This function will get the start and end time from user.
+ It also check whether or not user enters the proper time for both start and end.
+ If user leaves the start and end time empty, it will get the largest time range as default (overlap).
+ '''
+
+ if status == "start":
+ try:
+ time = raw_input("Please Enter a Start Date [%s]: " % startOverLapTime.strftime('%Y-%m-%d'))
+ if not time:
+ time = startOverLapTime
+ print "Your time starts from [%s] " % time.strftime('%Y-%m-%d')
+ return time
+ time = datetime.datetime.strptime(time, '%Y-%m-%d')
+ if time < startOverLapTime:
+ print "WARNING: The time you inputted [%s] " % time.strftime('%Y-%m-%d') + " is before the minimum start time [%s]." % startOverLapTime.strftime('%Y-%m-%d')
+ time = getStartEndTimes("start", startOverLapTime, endOverLapTime)
+ return time
+ elif time > endOverLapTime:
+ print "WARNING: The time you inputted [%s] " % time.strftime('%Y-%m-%d') + " is after the maximum end time [%s]. " % endOverLapTime.strftime('%Y-%m-%d')
+ time = getStartEndTimes("start", startOverLapTime, endOverLapTime)
+ return time
+ else:
+ return time
+ except ValueError:
+ getStartEndTimes(status, startOverLapTime, endOverLapTime)
+
+ if status == "end":
+ try:
+ time = raw_input("Please Enter an End Date [%s]: " % endOverLapTime.strftime('%Y-%m-%d'))
+ if not time:
+ time = endOverLapTime
+ print "Your time ends by [%s] " % time.strftime('%Y-%m-%d')
+ return time
+ time = datetime.datetime.strptime(time, '%Y-%m-%d')
+ if time > endOverLapTime:
+ print "WARNING: The time you inputted [%s] " % time.strftime('%Y-%m-%d') + " is after the maximum end time [%s]. " % endOverLapTime.strftime('%Y-%m-%d')
+ time = getStartEndTimes("end", startOverLapTime, endOverLapTime)
+ return time
+ elif time < startOverLapTime:
+ print "WARNING: The time you inputted [%s] " % time.strftime('%Y-%m-%d') + " is before the minimum start time [%s]." % startOverLapTime.strftime('%Y-%m-%d')
+ time = getStartEndTimes("end", startOverLapTime, endOverLapTime)
+ return time
+ else:
+ return time
+ except ValueError:
+ getStartEndTimes(status, startOverLapTime, endOverLapTime)
+
+
+def getDirSettings():
+ """
+ This function will collect 2 parameters from the user about the RCMET run they have started.
+
+ Output::
+ dirs - Tuple of strings i.e. ('workDirPath', 'cacheDirPath')
+ """
+ workDir = os.path.abspath(raw_input('Please enter workDir:\n> '))
+ if os.path.isdir(workDir):
+ pass
+ else:
+ makeDirectory(workDir)
+
+ cacheDir= os.path.abspath(raw_input('Please enter cacheDir:\n> '))
+ if os.path.isdir(cacheDir):
+ pass
+ else:
+ makeDirectory(cacheDir)
+
+ return (workDir, cacheDir)
+
+def getModelFiles():
+ modelList = []
+ while len(modelList) < 1:
+ userInput = raw_input('Please enter model file (or specify multiple files using wildcard):\n> ')
+ modelList = glob.glob(userInput)
+ if len(modelList) == 0:
+ print("Sorry we were unable to find any files at - %s" % userInput)
+ print("Please try again, and use the asterisk * for the wildcard")
+
+ return modelList
+
+def getTemporalGrid():
+ options = ['annual', 'monthly', 'daily']
+ print("Please select one of the following Temporal Grid Options:\n")
+ for key, option in enumerate(options):
+ print("[%s] - %s" % (key, option))
+ choice = int(raw_input(">>>"))
+ try:
+ temporalGrid = options[choice]
+ except IndexError:
+ getTemporalGrid()
+ else:
+ return temporalGrid
+
+def getSpatialGrid():
+ options = ['obs', 'model', 'user']
+ print("Please select one of the following Spatial Grid Options:\n")
+ for key, option in enumerate(options):
+ print("[%s] - %s" % (key, option))
+
+ choice = int(raw_input(">>>"))
+ try:
+ spatialGrid = options[choice]
+ except IndexError:
+ getSpatialGrid()
+
+ return spatialGrid
+
+def askUserForVariableName(variableNames, targetName):
+ if targetName == "analysis":
+ print("Select the variable you want to use for analysis/metrics:")
+ else:
+ print("Select the variable that corresponds to %s:" % targetName)
+
+ for idx, variable in enumerate(variableNames):
+ print("[%s] - %s" % (idx, variable))
+ userChoice = int(raw_input(">>>"))
+ try:
+ variableName = variableNames[userChoice]
+ except IndexError:
+ askUserForVariableName(variableNames, targetName)
+
+ return variableName
+
+
+
+def getLatLonStep(settings):
+ pass
+
+def getSpatialBounds(settings):
+ pass
+
+
+def getUserSpatialSettings(settings):
+ getLatLonStep(settings)
+ getSpatialBounds(settings)
+
+def makeDirectory(directory):
+ print "%s doesn't exist. Trying to create it now." % directory
+ try:
+ os.mkdir(directory)
+ print("Created %s successfully" % directory)
+ except OSError:
+ print "This program cannot create dir: %s due to permission issues." % directory
+ sys.exit()
+
+
+def userDefinedStartEndTimes(obsSource,obsName,obsTimeName,obsList,modelList):
+ """
+ The function will interactively ask the user to select a start and end time based on the start/end times
+ of the supplied observation and model objects
+
+ Input::
+ obsList - List of Observation Objects
+ modelList - List of Model Objects
+
+ Output::
+ startTime - Python datetime object from User Input
+ endTime - Python datetime object from User Input
+ """
+ startTimes = []
+ endTimes = []
+ print '='*94
+ template = "|{0:60}|{1:15}|{2:15}|"
+ print template.format('Dataset - NAME', 'START DATE', 'END DATE')
+ if obsSource == 0: # observation from RCMED
+ for observation in obsList:
+ startTimes.append(datetime.datetime.strptime(observation['start_date'],'%Y-%m-%d'))
+ endTimes.append(datetime.datetime.strptime(observation['end_date'],'%Y-%m-%d'))
+ print template.format(observation['longname'], observation['start_date'], observation['end_date'])
+ elif obsSource == 1: # observation from user
+ nn = 0
+ for observation in obsList:
+ times, timeStep = process.getModelTimes(observation,obsTimeName)
+ #print times, timeStep
+ startTimes.append(min(times))
+ endTimes.append(max(times))
+ print template.format(obsName[nn], min(times).strftime('%Y-%m-%d'), max(times).strftime('%Y-%m-%d'))
+ nn = +1
+ print '-'*94
+ for model in modelList:
+ startTimes.append(model.minTime)
+ endTimes.append(model.maxTime)
+ print template.format(model.name, model.minTime.strftime('%Y-%m-%d'), model.maxTime.strftime('%Y-%m-%d'))
+ print '='*94
+
+ # Compute Overlap
+ maxstartTimes=max(startTimes)
+ minendTimes=min(endTimes)
+ # TODO: Do we need to start on JAN and end on DEC? Do we know someone is doing ANNUAL analysis at this point?
+# if maxstartTimes.month != 1:
+# maxstartTimes = datetime.datetime(maxstartTimes.year+1,1,maxstartTimes.day)
+# if minendTimes.month != 12:
+# minendTimes = datetime.datetime(minendTimes.year-1,12,minendTimes.day)
+ if minendTimes.year < maxstartTimes.year:
+ print 'Not enough data for overlapping years'
+ sys.exit()
+ overLap = (maxstartTimes.strftime('%Y-%m-%d'), minendTimes.strftime('%Y-%m-%d'))
+ # Present default overlap to user as default value
+ print 'Standard Overlap in the selected datasets are %s through %s' % (overLap)
+
+ startOverLapTime = datetime.datetime.strptime(overLap[0],'%Y-%m-%d')
+ endOverLapTime = datetime.datetime.strptime(overLap[1],'%Y-%m-%d')
+
+ # Enter Start Date
+ startTime = getStartEndTimes("start", startOverLapTime, endOverLapTime)
+ # Enter End Date
+ endTime = getStartEndTimes("end", startOverLapTime, endOverLapTime)
+
+ if startTime > endTime:
+ print "WARNING: The start time you entered [%s]" % startTime.strftime('%Y-%m-%d') + " is after the end time you entered [%s]." % endTime.strftime('%Y-%m-%d')
+ startTime = getStartEndTimes("start", startOverLapTime, endOverLapTime)
+ endTime = getStartEndTimes("end", startOverLapTime, endOverLapTime)
+
+ return startTime, endTime
+
+
+def reshapeMonthlyData(dataset1):
+ """
+ Purpose::
+ Returns a view of an array with shape (nMonth, ...)
+ reshaped to (nYR, 12, ...) in order to assist in monthly
+ calculations
+ Input::
+ dataset1 - an input array with the first dimension corresponding
+ to the number of months, which must be a multiple of 12
+ Output::
+ data - A view of dataset1 but with shape (nYR, 12, ...).
+ """
+
+ # Create a view of dataset1. This lets us change the shape
+ # without copying or modifying the original array.
+ data = dataset1[:]
+ ishape = data.shape
+ nMonth = ishape[0]
+ nshape = nMonth/12, 12
+
+ # Combine the number of years / months (nshape) with other dimensions
+ data.shape = tuple(list(nshape) + list(ishape[1:]))
+ return data
+
+
+def select_timOpt():
+ #---------------------------------------------
+ # Interacrively select the time scale to be evaluated
+ # e.g., the means over annual, seasonal, monthly, or a specified period (e.g., JJAS for Indian monsoon)
+ #------------------------------------------------------------------------------------
+ print 'Select the time-mean properties to be evaluated: Enter'
+ timeOption = \
+ int(raw_input('1=annual, 2=seasonal (define "season", e.g., JJAS for Indian monsoon), 3=monthly \n> '))
+ return timeOption
+
+def select_data(nDat, Times, List, sourceDat):
+ #---------------------------------------------
+ # Interacrively select a model or models for evaluation
+ #----------------------------------------------------------------------------
+ print '-----------------------------------------------'
+ if sourceDat == 'mdl':
+ print 'mdlID mdlName numMOs mdlStartTime mdlEndTime fileName'
+ elif sourceDat == 'obs':
+ print 'obsID obsName obsMOs obsStartTime obsEndTime fileName'
+ else:
+ print 'not valid data source: CRASH and restart'
+ sys.exit()
+ print '-----------------------------------------------'
+
+ for n in np.arange(len(List)):
+ print n, List[n], Times[0], Times[-1]
+ n += 1
+ print '-----------------------------------------------'
+ if sourceDat == 'mdl':
+ ask = 'Enter the model ID to be evaluated. -9 for all models (-9 is not active yet): \n'
+ elif sourceDat == 'obs':
+ ask = 'Enter the obs ID to be evaluated. -9 for all models (-9 is not active yet): \n'
+ datSelect = int(raw_input(ask))
+ if datSelect > nDat - 1:
+ print 'Data ID out of range: CRASH'
+ sys.exit()
+ return datSelect
+
+def select_data_combined(nDat, Times, List, datType):
+ '''
+ # Interacrively select a model or models for evaluation
+ '''
+ nT = len(Times)
+ print '-------------------------------------------------------'
+ print 'datID datName numStp datStartTime datEndTime dataName'
+ print '-------------------------------------------------------'
+ for n in np.arange(nDat):
+ print n, List[n], nT, Times[0], Times[nT-1], List[n]
+ n+=1
+ print '-----------------------------------------------'
+ if datType == 'ref':
+ ask = 'Enter the reference data ID.: \n'
+ elif datType == 'mdl':
+ ask = 'Enter the data ID to be evaluated. -1 for all models, -2 for all models + obs, -3 for all obs, -4 for any combinations: \n'
+ datSelect = int(raw_input(ask))
+ if datSelect > nDat-1:
+ print 'Data ID out of range: CRASH'
+ sys.exit()
+ return datSelect
+
+def select_metrics():
+ #---------------------------------------------
+ # Interacrively select an evaluation metric
+ # Input : none
+ # Output: metricOption, the indicator for selecting the metric to be calculated
+ #------------------------------------------------------------------------------
+ print 'Metric options'
+ print '[0] Bias: mean bias across full time range'
+ print '[1] Mean Absolute Error: across full time range'
+ print '[2] Spatial Pattern Correlation: results in a time series of spatial correlation coeff'
+ print '[3] Temporal Correlation: results in 2-d array of temporal correlation coefficient'
+ print '[4] Spatial Pattern Correlation between averaged fields: results in a single correlation coeff'
+ print '[5] RMSE in time: results in a 2-d array of RMSE over two time series'
+ print '[6] TODO: Probability Distribution Function similarity score'
+ print '[7] Taylor diagram for spatial variability'
+ choice = int(raw_input('Please make a selection from the options above\n> '))
+ # assign the evaluation metric to be calculated
+ if choice == 0:
+ metricOption = 'BIAS'
+ elif choice == 1:
+ metricOption = 'MAE'
+ elif choice == 2:
+ metricOption = 'PCt'
+ elif choice == 3:
+ metricOption = 'TC'
+ elif choice == 4:
+ metricOption = 'PCC'
+ elif choice == 5:
+ metricOption = 'RMSt'
+ elif choice == 6:
+ metricOption = 'pdfSkillScore'
+ elif choice == 7:
+ metricOption = 'Taylor_space'
+ print 'in subroutine metricOption = ', metricOption
+ return metricOption
+
Propchange: incubator/climate/branches/rcmet-2.1.1/src/main/python/rcmes/utils/misc.py
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/missingSubRegionParam.cfg
URL: http://svn.apache.org/viewvc/incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/missingSubRegionParam.cfg?rev=1517753&view=auto
==============================================================================
--- incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/missingSubRegionParam.cfg (added)
+++ incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/missingSubRegionParam.cfg Tue Aug 27 05:35:42 2013
@@ -0,0 +1,3 @@
+[SUB_REGION]
+# Sub Region(s) Full File Path
+
Propchange: incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/missingSubRegionParam.cfg
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/validSubRegionConfigFile.cfg
URL: http://svn.apache.org/viewvc/incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/validSubRegionConfigFile.cfg?rev=1517753&view=auto
==============================================================================
--- incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/validSubRegionConfigFile.cfg (added)
+++ incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/validSubRegionConfigFile.cfg Tue Aug 27 05:35:42 2013
@@ -0,0 +1,3 @@
+[SUB_REGION]
+# Sub Region(s) Full File Path
+subRegionFile=files/validSubRegions.cfg
Propchange: incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/validSubRegionConfigFile.cfg
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/validSubRegions.cfg
URL: http://svn.apache.org/viewvc/incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/validSubRegions.cfg?rev=1517753&view=auto
==============================================================================
--- incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/validSubRegions.cfg (added)
+++ incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/validSubRegions.cfg Tue Aug 27 05:35:42 2013
@@ -0,0 +1,7 @@
+[REGIONS]
+Region01: ["R01", 36.5, 29, 0.0, -10]
+Region02: ["R02", 37.5, 29, 10, 0]
+Region04: ["R04", 32.5, 25, 33, 20]
+
+
+
Propchange: incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/files/validSubRegions.cfg
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_files.py
URL: http://svn.apache.org/viewvc/incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_files.py?rev=1517753&view=auto
==============================================================================
--- incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_files.py (added)
+++ incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_files.py Tue Aug 27 05:35:42 2013
@@ -0,0 +1,69 @@
+import os
+import unittest
+import Nio as nio
+
+import storage.files as files
+
+
+class TestGetVariableByType(unittest.TestCase):
+
+ def setUp(self):
+ self.badFilename = '/tmp/fail.txt'
+ self.missingVariable = 'missing'
+
+ def testNioRaisesException(self):
+ with self.assertRaises(Exception):
+ files.getVariableByType(self.badFilename, 'time')
+
+ def testTimeVariableNames(self):
+ self.variableList = files.VARIABLE_NAMES['time']
+ for variable in self.variableList:
+ testFile = self.makeNioFile(variable)
+ timeVariable = files.getVariableByType(testFile, 'time')
+ os.remove(testFile)
+ self.assertEqual(timeVariable, variable)
+
+ def testLatitudeVariableNames(self):
+ self.variableList = files.VARIABLE_NAMES['latitude']
+ for variable in self.variableList:
+ testFile = self.makeNioFile(variable)
+ timeVariable = files.getVariableByType(testFile, 'latitude')
+ os.remove(testFile)
+ self.assertEqual(timeVariable, variable)
+
+ def testLongitudeVariableNames(self):
+ self.variableList = files.VARIABLE_NAMES['longitude']
+ for variable in self.variableList:
+ testFile = self.makeNioFile(variable)
+ timeVariable = files.getVariableByType(testFile, 'longitude')
+ os.remove(testFile)
+ self.assertEqual(timeVariable, variable)
+
+ def testTimeVariableMissing(self):
+ testFile = self.make5VariableNioFile()
+ testVariable = files.getVariableByType(testFile, 'time')
+ os.remove(testFile)
+ self.assertEqual(len(testVariable), 5)
+
+ def makeNioFile(self, variableName):
+ filename = '/tmp/good_%s.nc' % variableName
+ f = nio.open_file(filename, 'w')
+ f.create_dimension('test_dimension', 1)
+ f.create_variable(variableName,'l',('test_dimension',))
+ f.close()
+ return filename
+
+ def make5VariableNioFile(self):
+ filename = '/tmp/5_variables.nc'
+ f = nio.open_file(filename, 'w')
+ f.create_dimension('dimension_one', 1)
+ f.create_variable('one', 'l', ('dimension_one',))
+ f.create_variable('two', 'l', ('dimension_one',))
+ f.create_variable('three', 'l', ('dimension_one',))
+ f.create_variable('four', 'l', ('dimension_one',))
+ f.create_variable('five', 'l', ('dimension_one',))
+ f.close()
+ return filename
+
+if __name__ == '__main__':
+ unittest.main()
Propchange: incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_files.py
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_misc.py
URL: http://svn.apache.org/viewvc/incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_misc.py?rev=1517753&view=auto
==============================================================================
--- incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_misc.py (added)
+++ incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_misc.py Tue Aug 27 05:35:42 2013
@@ -0,0 +1,56 @@
+import os
+import unittest
+
+from utils import misc
+import ConfigParser
+
+class TestReadSubRegionsFile(unittest.TestCase):
+
+ def setUp(self):
+ self.badFilename = '/tmp/fail.txt'
+ self.validSubRegionsFile = './files/validSubRegions.cfg'
+
+ def testMissingFileException(self):
+ with self.assertRaises(Exception):
+ misc.readSubRegionsFile(self.badFilename)
+
+class TestParseSubRegions(unittest.TestCase):
+
+ def setUp(self):
+ self.userConfig = ConfigParser.SafeConfigParser()
+ self.userConfig.optionxform = str # This is so the case is preserved on the items in the config file
+ self.missingSubRegion = './files/missingSubRegionParam.cfg'
+
+ def testMissingSubRegionParameter(self):
+ self.userConfig.read(self.missingSubRegion)
+ self.missingSubRegionParam = misc.configToDict(self.userConfig.items('SUB_REGION'))
+ self.assertFalse(misc.parseSubRegions(self.missingSubRegionParam))
+
+class TestIsDirGood(unittest.TestCase):
+
+ def setUp(self):
+ self.goodDir = '/tmp'
+ self.unwriteableDir = '/usr'
+ self.unwritableMissingDir = '/usr/test_bad_direcory'
+ self.writeableMissingDir = '/tmp/test_good_directory'
+
+ def tearDown(self):
+ if os.path.exists(self.writeableMissingDir):
+ os.rmdir(self.writeableMissingDir)
+
+ def testGoodDirExists(self):
+ self.assertTrue(misc.isDirGood(self.goodDir))
+
+ def testGoodDirDoesNotExist(self):
+ self.assertTrue(misc.isDirGood(self.writeableMissingDir))
+
+ def testUnwriteableDir(self):
+ with self.assertRaises(OSError):
+ misc.isDirGood(self.unwriteableDir)
+
+ def testUnwritableMissingDir(self):
+ with self.assertRaises(OSError):
+ misc.isDirGood(self.unwritableMissingDir)
+
+if __name__ == '__main__':
+ unittest.main()
Propchange: incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_misc.py
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_process.py
URL: http://svn.apache.org/viewvc/incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_process.py?rev=1517753&view=auto
==============================================================================
--- incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_process.py (added)
+++ incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_process.py Tue Aug 27 05:35:42 2013
@@ -0,0 +1,26 @@
+import unittest
+from datetime import datetime, timedelta
+
+import toolkit.process as process
+
+
+class TestNormalizeDatetimes(unittest.TestCase):
+
+ def testMonthlyNormalize(self):
+ self.annualClean = [datetime(2000, x, 1) for x in range(1, 12)]
+ self.annualFifteenth = [datetime(2000, x, 15) for x in range(1, 12)]
+ self.monthly = 'monthly'
+ self.normalDates = process.normalizeDatetimes(self.annualFifteenth, self.monthly)
+ self.assertEqual(self.normalDates, self.annualClean)
+
+ def testDailyNormalize(self):
+ self.dailyClean = [datetime(2000, 1, 1, 0, 0) + timedelta(days=x) for x in range(0, 5)]
+ self.dailyNoonish = [datetime(2000, 1, 1, 12, 15) + timedelta(days=x) for x in range(0, 5)]
+ self.daily = 'daily'
+ self.normalDaily = process.normalizeDatetimes(self.dailyNoonish, self.daily)
+ self.assertEqual(self.dailyClean, self.normalDaily)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
\ No newline at end of file
Propchange: incubator/climate/branches/rcmet-2.1.1/src/main/python/tests/test_process.py
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/climate/branches/rcmet-2.1.1/src/main/resources/PyLintREADME.txt
URL: http://svn.apache.org/viewvc/incubator/climate/branches/rcmet-2.1.1/src/main/resources/PyLintREADME.txt?rev=1517753&view=auto
==============================================================================
--- incubator/climate/branches/rcmet-2.1.1/src/main/resources/PyLintREADME.txt (added)
+++ incubator/climate/branches/rcmet-2.1.1/src/main/resources/PyLintREADME.txt Tue Aug 27 05:35:42 2013
@@ -0,0 +1,26 @@
+OVERVIEW:
+
+The code within RCMES has been linted using a project specific set of parameters. We have blended Python's PEP-8
+while also including some Java formatting (namely camelCase instead of underscores).
+
+You don't really need to know all the particulars, but if you are curious you can look at the pyLintRcFile.txt in
+this directory for full details.
+
+
+HOW TO LINT THE CODE:
+
+If you are contributing to this code base then you will need to comply with the code convention standards set forth
+by the project team. We have used PyLint to check for code correctness, and the included pyLintRcFile.txt can be
+used with PyLint (http://pypi.python.org/pypi/pylint/).
+
+After you have installed pylint follow these directions. Happy linting!
+
+Assuming you want to lint the metrics.py file within lib/rcmes, do the following
+
+$> cd rcmes/rcmet/src/main/python/rcmes
+$> pylint --rcfile=../../resources/pyLintRcFile.txt cli/rcmet_ui.py
+
+You will be presented with a Report of any Warnings, Conventions, Errors, or Refactoring opportunities as well as
+variable name conventions, etc.. For more about the meaning of the report look up the PyLint documentation online.
+
+This will help the team keep the codebase clean and consistent as multiple team members contribute code.
Propchange: incubator/climate/branches/rcmet-2.1.1/src/main/resources/PyLintREADME.txt
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/climate/branches/rcmet-2.1.1/src/main/resources/pyLintRcFile.txt
URL: http://svn.apache.org/viewvc/incubator/climate/branches/rcmet-2.1.1/src/main/resources/pyLintRcFile.txt?rev=1517753&view=auto
==============================================================================
--- incubator/climate/branches/rcmet-2.1.1/src/main/resources/pyLintRcFile.txt (added)
+++ incubator/climate/branches/rcmet-2.1.1/src/main/resources/pyLintRcFile.txt Tue Aug 27 05:35:42 2013
@@ -0,0 +1,249 @@
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add files or directories to the blacklist. They should be base names, not
+# paths.
+ignore=CVS
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[MESSAGES CONTROL]
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time.
+#enable=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once).
+#disable=
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html
+output-format=text
+
+# Include message's id in output
+include-ids=no
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells whether to display a full report or only the messages
+reports=yes
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (RP0004).
+comment=no
+
+
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z][a-z0-9]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z][a-zA-Z]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z]+[A-Za-z0-9]{2,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=(([a-z]+[A-Za-z0-9]{2,30})|(__.*__))$
+
+# Regular expression which should only match correct instance attribute names
+attr-rgx=[a-z]+[A-Za-z0-9]{2,30}$
+
+# Regular expression which should only match correct argument names
+argument-rgx=[a-z][A-Za-z0-9]{2,30}$
+
+# Regular expression which should only match correct variable names
+variable-rgx=[a-z]+[A-Za-z0-9]{2,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z][A-Za-z0-9]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+no-docstring-rgx=__.*__
+
+
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=120
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+
+[TYPECHECK]
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamically set).
+ignored-classes=SQLObject
+
+# When zope mode is activated, add a predefined set of Zope acquired attributes
+# to generated-members.
+zope=no
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E0201 when accessed. Python regular
+# expressions are accepted.
+generated-members=REQUEST,acl_users,aq_parent
+
+
+[VARIABLES]
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching the beginning of the name of dummy variables
+# (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore
+ignored-argument-names=_.*
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branchs=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled)
+int-import-graph=
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "Exception"
+overgeneral-exceptions=Exception
Propchange: incubator/climate/branches/rcmet-2.1.1/src/main/resources/pyLintRcFile.txt
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/climate/branches/rcmet-2.1.1/src/main/resources/rcmet_use_case.txt
URL: http://svn.apache.org/viewvc/incubator/climate/branches/rcmet-2.1.1/src/main/resources/rcmet_use_case.txt?rev=1517753&view=auto
==============================================================================
--- incubator/climate/branches/rcmet-2.1.1/src/main/resources/rcmet_use_case.txt (added)
+++ incubator/climate/branches/rcmet-2.1.1/src/main/resources/rcmet_use_case.txt Tue Aug 27 05:35:42 2013
@@ -0,0 +1,57 @@
+Regional Climate Modeling Evaluation Toolkit Use Case
+
+User = Scientist
+System = RCMES
+
+Goal: Compute Metrics on a Local Climate Model
+Main Success Scenario
+1. User logs into System
+2. User selects a model file
+3. User selects model latitude, longitude, time and parameter
+4. User selects observation datasets and parameters
+5. System returns the time range information where Obs and Model
+parameters overlap.
+6. User selects time range.
+7. System returns VALID re-gridding options for Space and Time.
+(VALID meaning smaller grid to a larger grid step)
+8. User selects the re-gridding options
+9. Optional Processing (SEE OPEN ISSUES SECTION)
+
+
+Extensions:
+1a: User has no account setup
+ .1: User will create an account
+ .2: System will create an upload directory for the account
+ and email instructions about how to FTP the model files into
+ the new directory
+ Main Success Scenario Step 1
+
+2a: User has no models in the System to choose
+ .1: User will FTP their model file(s) into their upload directory
+ Main Success Scenario Step 2
+
+# This could be prevented if Step 4 ONLY returns Datasets with a proper
+# temporal overlap. This might be an enhancement in the future.
+5a: The Model and Observational Datasets don't have a time overlap
+ .1: System will return a message explaining the lack of overlap
+ Main Success Scenario Step 4
+
+
+OPEN QUESTIONS/ISSUES
+
+***Optional Processing***
+Masked Region: Does this work today?
+Seasonal Cycles: Does this option work?
+
+User will select the Metric they want to compute. [Should we consider limiting
+this list based on what is being selected in the model and observations]
+Metrics:
+-Bias: mean bias across full time range
+-Mean Absolute Error: across full time range
+-Difference: calculated at each time unit
+-Anomaly Correlation
+-Pattern Correlation
+*Probability Distribution Function similarity score
+-Root Mean Square Error
+
+Customize Plots: Does this work today?
Propchange: incubator/climate/branches/rcmet-2.1.1/src/main/resources/rcmet_use_case.txt
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/climate/branches/rcmet-2.1.1/src/main/ui/app/css/app.css
URL: http://svn.apache.org/viewvc/incubator/climate/branches/rcmet-2.1.1/src/main/ui/app/css/app.css?rev=1517753&view=auto
==============================================================================
--- incubator/climate/branches/rcmet-2.1.1/src/main/ui/app/css/app.css (added)
+++ incubator/climate/branches/rcmet-2.1.1/src/main/ui/app/css/app.css Tue Aug 27 05:35:42 2013
@@ -0,0 +1,27 @@
+/* app css stylesheet */
+
+#map
+{
+ height: 670px;
+ width: 80%;
+ margin-left: 10%;
+ margin-bottom: 20px;
+}
+
+#rcmetHeader
+{
+ margin-left: 10%;
+}
+
+.small-alert
+{
+ font-size: 12px;
+ color: green;
+ margin-top: 4px;
+ margin-left: 10px;
+}
+
+ul
+{
+ list-style-type: none;
+}
Propchange: incubator/climate/branches/rcmet-2.1.1/src/main/ui/app/css/app.css
------------------------------------------------------------------------------
svn:executable = *