From fb175175f19097bc6dae7c662723f1e0d3d9b301 Mon Sep 17 00:00:00 2001 From: johnrobertlawson Date: Mon, 9 Sep 2013 17:15:00 -0500 Subject: [PATCH 001/111] Main file for lazyWRF. --- lazyWRF.py | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 lazyWRF.py diff --git a/lazyWRF.py b/lazyWRF.py new file mode 100644 index 0000000..9f6454c --- /dev/null +++ b/lazyWRF.py @@ -0,0 +1,207 @@ +# This script goes through post-processing of WRF +# And submission of the job to a Rocks cluster if desired (edit to taste) +# Run it ("python lazyWRF.py") from your WPS folder. + +# John Lawson, Iowa State University 2013 +# Email john.rob.lawson@googlemail.com + +# -------------IMPORTANT NOTES--------------- +# Remove met_em files if you don't want them overwritten (or hide them) +# Make sure all SETTINGS are correct +# Then run this script +# If job submission is switched on, make sure namelist.input parameterisations are correct +# This script will sync all namelist.wps settings with namelist.output + +########################## +# IMPORTS +import os +import sys +import pdb +import glob +import subprocess +import calendar +import math +import datetime +########################### + +# Script SETTINGS +# If switched on, existing wrfout files etc will be moved to a folder +move_wrfout = 1 +pathtowrfout = './' +# If switched on, the script will submit jobs (edit as needed) +submit_job = 1 +pathtoWRF = '../WRFV3/run/' + +# WRF run SETTINGS +# Start,end date in YYYYMMDD_HHMM +idate = '20060526_0000' +fdate = '20060527_1200' +domains = 1 +e_we = (500,) # Needs to be same length as number of domains +e_sn = (500,) +dx = 3 # In km; grid spacing of largest domain +dy = 3 # Ditto +i_start = (1,) # Same length as num of domains +j_start = (1,) +parent_grid_ratio = (1,) # Same length as num of domains; ratio of each parent to its child + +# Select initial data source +# gfs, nam, gefs +init_data = 'gfs' +# Directory with initialisation data +pathtoinitdata = './gfsfiles/gfsanl' +# Intermediate file prefix +prefix = "FILE" + +############################ + +# FUNCTIONS +def edit_namelist(old,new,incolumn=1): + nwps = open('namelist.wps','r').readlines() + for idx, line in enumerate(nwps): + if old in line: + # Prefix for soil intermediate data filename + if incolumn==1: + nwps[idx] = nwps[idx][:23] + new + " \n" + else: + nwps[idx] = ' ' + old + ' = ' + new + "\n" + nameout = open('namelist.wps','w') + nameout.writelines(nwps) + nameout.close() + break + +def edit_namelist_input(old,new,pathtoWRF=pathtoWRF): + ninput = open(pathtoWRF+'namelist.input','r').readlines() + for idx, line in enumerate(ninput): + if old in line: + # Prefix for soil intermediate data filename + ninput[idx]= ninput[idx][:39] + new + " \n" + nameout = open(pathtoWRF+'namelist.input','w') + nameout.writelines(ninput) + nameout.close() + break + +############################ +############################ + +# Open the WPS namelist; copy the old one in case of bugs +os.system('cp namelist.wps{,.python_backup}') +nwps = open('namelist.wps','r').readlines() + +# Sets values depending on initialisation data +if init_data == 'gfs': + atmos_levs = 27 + soil_levs = 4 + interval = 6 # In hours + Vtable_suffix = 'GFS' +elif init_data == 'nam': + atmos_levs = 40 + soil_levs = 4 + interval = 6 # In hours + Vtable_suffix = 'NAM' +elif init_data == 'gefs': + ens = raw_input('Which ensemble member? (c00,p01...p10) ') + atmos_levs = 12 + soil_levs = 4 # Uses GFS soil data + interval = 3 # Interpolation happens after 90 h + Vtable_suffix = 'GEFSR2' + +# Prepares namelist.wps +idate_s = "'"+idate[:4]+"-"+idate[4:6]+"-"+idate[6:11]+":"+idate[11:13]+":00'," +edit_namelist("start_date",idate_s * domains, incolumn=0) +fdate_s = "'"+fdate[:4]+"-"+fdate[4:6]+"-"+fdate[6:11]+":"+fdate[11:13]+":00'," +edit_namelist("end_date",fdate_s * domains, incolumn=0) +edit_namelist("max_dom",str(domains)+',',incolumn=0) +edit_namelist("interval_seconds",str(interval*3600)+',', incolumn=0) +edit_namelist("parent_grid_ratio",', '.join([str(p) for p in parent_grid_ratio])+',') +edit_namelist("i_parent_start", ', '.join([str(i) for i in i_start])+',') +edit_namelist("j_parent_start", ', '.join([str(j) for j in j_start])+',') +edit_namelist("dx",str(dx*1000),incolumn=0) +edit_namelist("dy",str(dy*1000),incolumn=0) +edit_namelist("e_we",', '.join([str(w) for w in e_we])+',') +edit_namelist("e_sn",', '.join([str(s) for s in e_sn])+',') +edit_namelist("prefix",prefix+',',incolumn=0) +edit_namelist("fg_name",prefix+',',incolumn=0) + +# Add your own here if wanting to change e.g. domain location, dx, dy from here... + +# Link to, and ungrib, initialisation files +os.system('./link_grib.csh ' + pathtoinitdata) +os.system('ln -sf ungrib/Variable_Tables/Vtable.' + Vtable_suffix + ' Vtable') +os.system('./ungrib.exe') +os.system('./metgrid.exe') + +# Submit jobs (edit as needed) +if submit_job == 1: + # Soft link data netCDFs files from WPS to WRF + os.system('ln -sf ./met_em* ' + pathtoWRF) + + ##### Sync namelist.input with namelist.wps + # Copy original in case of bugs + os.system('cp ' + pathtoWRF + 'namelist.input{,.python_backup}') + # Compute run time + dt_1 = calendar.timegm((int(idate[0:4]),int(idate[4:6]),int(idate[6:8]), + int(idate[9:11]),int(idate[11:13]),0)) + dt_2 = calendar.timegm((int(fdate[0:4]),int(fdate[4:6]),int(fdate[6:8]), + int(fdate[9:11]),int(fdate[11:13]),0)) + dt = datetime.timedelta(seconds=(dt_2 - dt_1)) + days = dt.days + hrs = math.floor(dt.seconds/3600.0) + mins = ((dt.seconds/3600.0) - hrs) * 60.0 + secs = 0 # Assumed! + + # Compute dx,dy for each domain + dxs = [dx*1000] + if domains != 1: + for idx in range(1,domains): + child_dx = dxs[idx-1] * 1.0/parent_grid_ratio[idx] + dxs.append(child_dx) + dys = [dy*1000] + if domains != 1: + for idx in range(1,domains): + child_dy = dys[idx-1] * 1.0/parent_grid_ratio[idx] + dys.append(child_dy) + + # If all namelist values begin on column 38: + edit_namelist_input("run_days","%01u" %days) + edit_namelist_input("run_hours","%01u" %hrs) + edit_namelist_input("run_minutes","%01u" %mins) + edit_namelist_input("run_seconds","%01u" %secs) + edit_namelist_input("start_year", (idate[:4]+', ')*domains) + edit_namelist_input("start_month", (idate[4:6]+', ')*domains) + edit_namelist_input("start_day", (idate[6:8]+', ')*domains) + edit_namelist_input("start_hour", (idate[9:11]+', ')*domains) + edit_namelist_input("start_minute", (idate[11:13]+', ')*domains) + edit_namelist_input("start_second", ('00, ')*domains) + edit_namelist_input("end_year", (fdate[:4]+', ')*domains) + edit_namelist_input("end_month", (fdate[4:6]+', ')*domains) + edit_namelist_input("end_day", (fdate[6:8]+', ')*domains) + edit_namelist_input("end_hour", (fdate[9:11]+', ')*domains) + edit_namelist_input("end_minute", (fdate[11:13]+', ')*domains) + edit_namelist_input("end_second", ('00, ')*domains) + edit_namelist_input("interval_seconds", str(interval*3600)+',') + edit_namelist_input("max_dom",str(domains)+',') + edit_namelist_input("e_we", ', '.join([str(w) for w in e_we])+',') + edit_namelist_input("e_sn", ', '.join([str(s) for s in e_sn])+',') + edit_namelist_input("num_metgrid_levels", str(atmos_levs)+',') + edit_namelist_input("dx", ', '.join([str(d) for d in dxs])+',') + edit_namelist_input("dy", ', '.join([str(d) for d in dys])+',') + edit_namelist_input("i_parent_start", ', '.join([str(i) for i in i_start])+',') + edit_namelist_input("j_parent_start", ', '.join([str(j) for j in j_start])+',') + edit_namelist_input("parent_grid_ratio",', '.join([str(p) for p in parent_grid_ratio])+',') + + """ + # Run real, get ID number of job + # Change name of submission script if needed + p_real = subprocess.call([qsub,real_run.sh],cwd=pathtoWRF,shell=True, + stdout=subprocess.PIPE) + jobid = p.stdout.read()[:5] # Assuming last five digits = job ID. + # Run WRF but wait until Real has finished without errors + # Again, change name of submission script if needed + p_wrf = subprocess.call([qsub,wrf_run.sh,-W,"afterok=jobid"], + cwd=pathtoWRF,shell=True) + print "real.exe and wrf.exe submitted. Exiting Python script." + """ +else: + print "Pre-processing complete. Exiting Python script." + From 5be108b0d546389953556db9e7cdc82c4863ebe7 Mon Sep 17 00:00:00 2001 From: johnrobertlawson Date: Mon, 9 Sep 2013 17:17:12 -0500 Subject: [PATCH 002/111] Script to automate GEFS ensemble creation in WRF --- setup_gefs.py | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 setup_gefs.py diff --git a/setup_gefs.py b/setup_gefs.py new file mode 100644 index 0000000..9163dee --- /dev/null +++ b/setup_gefs.py @@ -0,0 +1,129 @@ +# Create met_em files +# Uses command-line options for: +# -number where number is the ensemble member (0-10) +# -a Automatically find last member created and do next + +# Assumes Vtables for GEFS data are from Lawson .pdf README file + +# First, manually check namelist.wps +# Then remove met_em and GRIB files not related to this ensemble +# Keep the GRIB soft links if halfway through GEFS ensemble run +# Then run this script + +import os +import sys +import pdb +import glob +import subprocess + +# FUNCTIONS +def edit_namelist(old,new): + nwps = open('namelist.wps','r').readlines() + for idx, line in enumerate(nwps): + if old in line: + # Prefix for soil intermediate data filename + nwps[idx] = new + " \n" + nameout = open('namelist.wps','w') + nameout.writelines(nwps) + nameout.close() + break + +# SETTINGS +# Directory with GEFS data +gefsdir = './gefsfiles/' +pathtosoildata = './gfsfiles/gfsanl*' +# If switched on, the script will submit jobs (edit as needed) +submit_job = 1 +# Change if WRF job submission required +pathtoWRF = '../WRFV3/run/' + +ensemblelist = ['c00'] + ['p'+str(n) for n in range(1,11)] + +# The script first checks for command-line argument +try: + arg = sys.argv[1] +except IndexError: + print "A command-line argument is required (-e or -a)" + raise Exception +#finally: +# if arg == '-a': +# print "Automatically generating next ensemble member" +# print "Using ensemble member ", arg[-1] + +# Then it opens the WPS namelist, copies the old one +os.system('cp namelist.wps{,.python_backup}') +nwps = open('namelist.wps','r').readlines() +# Get the GEFS initialisation date from here +for line in nwps: + if "start_date" in line: + datestring = line[15:19] + line[20:22] + line[23:25] + break + +# Find intermediate filename line in WPS namelist +# Link to, and ungrib, soil files if they don't exist +if not glob.glob('FILE_SOIL*'): + edit_namelist("prefix"," prefix = 'FILE_SOIL'") + os.system('./link_grib.csh ' + pathtosoildata) + os.system('ln -sf ungrib/Variable_Tables/Vtable.GFS_soilonly Vtable') + os.system('./ungrib.exe') + + +# Next, one of two things: + +# (1) if the command-line argument is automatic: +if arg == '-a': + # Grab link_grib links to grib files + griblinks = glob.glob('GRIB*') + if not griblinks: + nextens = 'c00' + # Then we must run the control first + else: + # Find the last softlinked ensemble member file + softlink = os.readlink(griblinks[0]) + # Check to see if last member was E10 + # If it is, our work here is done + if "p10" in softlink: + print "Last ensemble member already completed" + raise Exception + else: + for idx, n in enumerate(ensemblelist): + if n in softlink: + lastens = n + nextens = ensemblelist(idx+1) + +else: + # (2) if the command-line argument is set: + if arg == '-0': + nextens = 'c00' + else: + nextens = 'p' + str(arg[-1]) + +# Set path to gefs data +pathtogefsdata = gefsdir+datestring+'_'+nextens+'_f*' + +# Now change the namelist.wps to use the atmospheric data prefix +#pdb.set_trace() +edit_namelist("prefix"," prefix = 'FILE_ATMOS'") +os.system('./link_grib.csh ' + pathtogefsdata) +os.system('ln -sf ungrib/Variable_Tables/Vtable.GEFSR2 Vtable') +os.system('./ungrib.exe') + +# Combine both GEFS atmos and GFS soil data +edit_namelist("fg_name"," fg_name = 'FILE_SOIL','FILE_ATMOS'") +os.system('./metgrid.exe') + +# Submit jobs (edit as needed) +if submit_job == 1: + # Soft link data netCDFs files from WPS to WRF + os.system('ln -sf ./met_em* ' + pathtoWRF) + # Run real, get ID number of job + p_real = subprocess.call([qsub,real_run.sh],cwd=pathtoWRF,shell=True, + stdout=subprocess.PIPE) + jobid = p.stdout.read()[:5] + # Run WRF but wait until Real has finished without errors + p_wrf = subprocess.call([qsub,wrf_run.sh,-W,"afterok=jobid"], + cwd=pathtoWRF,shell=True) + print "real.exe and wrf.exe submitted. Exiting Python script." +else: + print "Pre-processing complete. Exiting Python script." + From dae790bed19d93d31cd286b1c7f6e2d3f385c2c5 Mon Sep 17 00:00:00 2001 From: johnrobertlawson Date: Mon, 9 Sep 2013 17:31:17 -0500 Subject: [PATCH 003/111] Update README --- README | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README b/README index 4169e1c..be37b39 100644 --- a/README +++ b/README @@ -1 +1,14 @@ -This is a README file. +The lazyWRF project aims to simplify the WPS/WRF process for a few reasons: + (1) It makes life easier + (2) It reduces silly mistakes that overwrite old output files (notwithstanding bugs in this code!) + (3) It can maximise efficiency when submitting jobs to a server (i.e. it can do it overnight) + +This is a work in progress, and comments as highly encouraged. The final goal is a portable and bug-free collection of standalone code that anyone can download, assuming basic Python package installs. + +Please report suggestions, issues, etc to me via GitHub and I will improve and fix the package as we go along. Hopefully there is something useful to come out of this, potentially to be presented at the American Meteorological Society Annual Meeting 2014 in Atlanta, Georgia. + +Many thanks, +John Lawson +Iowa State University +PhD Candidate +September 2013 From 2d742f41170cb31be3b39234c06496276754d23f Mon Sep 17 00:00:00 2001 From: johnrobertlawson Date: Tue, 10 Sep 2013 10:43:34 -0500 Subject: [PATCH 004/111] Update README --- README | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README b/README index be37b39..5ff54d0 100644 --- a/README +++ b/README @@ -3,6 +3,8 @@ The lazyWRF project aims to simplify the WPS/WRF process for a few reasons: (2) It reduces silly mistakes that overwrite old output files (notwithstanding bugs in this code!) (3) It can maximise efficiency when submitting jobs to a server (i.e. it can do it overnight) +Your main file is lazyWRF.py. This is the template for other clever ideas like looping, etc. + This is a work in progress, and comments as highly encouraged. The final goal is a portable and bug-free collection of standalone code that anyone can download, assuming basic Python package installs. Please report suggestions, issues, etc to me via GitHub and I will improve and fix the package as we go along. Hopefully there is something useful to come out of this, potentially to be presented at the American Meteorological Society Annual Meeting 2014 in Atlanta, Georgia. From 8cd3bb856f8c797339436b20d3f335aaed5bbbc3 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 10 Sep 2013 19:40:53 -0500 Subject: [PATCH 005/111] Latest update Sep 10 --- lazyWRF.py | 254 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 180 insertions(+), 74 deletions(-) diff --git a/lazyWRF.py b/lazyWRF.py index 9f6454c..3445888 100644 --- a/lazyWRF.py +++ b/lazyWRF.py @@ -22,20 +22,29 @@ import calendar import math import datetime +import time ########################### # Script SETTINGS -# If switched on, existing wrfout files etc will be moved to a folder -move_wrfout = 1 -pathtowrfout = './' +# If switched on, this will do pre-processing (WPS) +WPS = 0 +# If switched on, this will do WRF processing +WRF = 1 # If switched on, the script will submit jobs (edit as needed) submit_job = 1 -pathtoWRF = '../WRFV3/run/' + +# If switched on, existing wrfout files etc will be moved to a folder +move_wrfout = 0 +# Path to WRF folder (absolute) +pathtoWPS = '/ptmp/jrlawson/WPS/' +pathtoWRF = '/ptmp/jrlawson/WRFV3/run/' +# Path to move wrfout* files +pathtowrfout = '/ptmp/jrlawson/home/path/to/store/wrfout/' # WRF run SETTINGS -# Start,end date in YYYYMMDD_HHMM -idate = '20060526_0000' -fdate = '20060527_1200' +# Start,end date in (YYYY,MM,DD,H,M,S) +idate = (2006,05,26,0,0,0) +fdate = (2006,05,27,12,0,0) domains = 1 e_we = (500,) # Needs to be same length as number of domains e_sn = (500,) @@ -46,12 +55,12 @@ parent_grid_ratio = (1,) # Same length as num of domains; ratio of each parent to its child # Select initial data source -# gfs, nam, gefs +# FROM: gfs, nam, (gefs --> use setup_gefs.py script for now) init_data = 'gfs' # Directory with initialisation data -pathtoinitdata = './gfsfiles/gfsanl' -# Intermediate file prefix -prefix = "FILE" +pathtoinitdata = './gfsfiles/' +# Intermediate file prefix (usually no need to change) +int_prefix = "FILE" ############################ @@ -69,6 +78,7 @@ def edit_namelist(old,new,incolumn=1): nameout.writelines(nwps) nameout.close() break + return def edit_namelist_input(old,new,pathtoWRF=pathtoWRF): ninput = open(pathtoWRF+'namelist.input','r').readlines() @@ -80,6 +90,82 @@ def edit_namelist_input(old,new,pathtoWRF=pathtoWRF): nameout.writelines(ninput) nameout.close() break + return + +def str_from_date(date,format): + # date = Input is tuple (yyyy,mm,dd,h,m,s) + # format = choose 'list' or 'indiv' or... + # ... year, month, day, hour, minute, second output + datelist = [] + for n in date: + datelist.append("%02u" %n) + if format=='list': + return datelist + else: + year,month,day,hour,minute,second = datelist + if format=='indiv': + return year,month,day,hour,minute,second + else: + val = eval(format) + return val + +def download_data(date,initdata,pathtoinitdata): + # Download gfs, nam data from server for a timestamp + if initdata == 'gfs': + prefix = 'gfsanl' + n = '3' + suffix = '.grb' + elif initdata == 'nam': + prefix = 'namanl' + n = '218' + suffix = '.grb' + command = ('wget "http://nomads.ncdc.noaa.gov/data/' + prefix + '/' + date[:6] + '/' + date[:8] + + '/' + prefix + '_' + n + '_' + date + '_000' + suffix + '" -P ' + pathtoinitdata) + #pdb.set_trace() + os.system(command) + return + +def get_init_files(initdata,idate,fdate,pathtoinitdata): + # Let's assume all initdata possibilities start at 0000 UTC and the interval is an integer factor + if initdata=='gfs': + interval = 6.0 + prefix = 'gfsanl' + elif initdata=='nam': + interval = 6.0 + prefix = 'namanl' + + # First, convert dates to seconds-from-epoch time + idaten = calendar.timegm(idate) + fdaten = calendar.timegm(fdate) + + # The initial and final files needed: + ifiledate = idaten - idaten%(interval*3600) + ffiledate = fdaten - fdaten%(interval*3600) + (interval*3600) + + # Create a range of required file dates + required_dates = range(int(ifiledate),int(ffiledate),int(interval*3600)) + + # List all files in initialisation data folder + initfiles = glob.glob(pathtoinitdata + '*') + + # Loop through times and check to see if file exists for initialisation model + for r in required_dates: + # Tuple of date (9 items long starting with year, month, etc...) + longdate = time.gmtime(r) + fname_date = ''.join(["%02u" %x for x in longdate[0:3]]) + '_' + ''.join(["%02u" %x for x in longdate[3:5]]) + checkfiles_prefix = [] + checkfiles_date = [] + for f in initfiles: + checkfiles_prefix.append(prefix in f) + checkfiles_date.append(fname_date in f) + try: + checkfiles_prefix.index(1) + checkfiles_date.index(1) + except ValueError: + print 'Downloading required file for ' + fname_date + download_data(fname_date,init_data,pathtoinitdata) + else: + print 'Data for ' + fname_date + ' already exists.' + return ############################ ############################ @@ -94,56 +180,72 @@ def edit_namelist_input(old,new,pathtoWRF=pathtoWRF): soil_levs = 4 interval = 6 # In hours Vtable_suffix = 'GFS' + init_prefix = 'gfsanl' elif init_data == 'nam': atmos_levs = 40 soil_levs = 4 interval = 6 # In hours Vtable_suffix = 'NAM' -elif init_data == 'gefs': - ens = raw_input('Which ensemble member? (c00,p01...p10) ') - atmos_levs = 12 - soil_levs = 4 # Uses GFS soil data - interval = 3 # Interpolation happens after 90 h - Vtable_suffix = 'GEFSR2' - -# Prepares namelist.wps -idate_s = "'"+idate[:4]+"-"+idate[4:6]+"-"+idate[6:11]+":"+idate[11:13]+":00'," -edit_namelist("start_date",idate_s * domains, incolumn=0) -fdate_s = "'"+fdate[:4]+"-"+fdate[4:6]+"-"+fdate[6:11]+":"+fdate[11:13]+":00'," -edit_namelist("end_date",fdate_s * domains, incolumn=0) -edit_namelist("max_dom",str(domains)+',',incolumn=0) -edit_namelist("interval_seconds",str(interval*3600)+',', incolumn=0) -edit_namelist("parent_grid_ratio",', '.join([str(p) for p in parent_grid_ratio])+',') -edit_namelist("i_parent_start", ', '.join([str(i) for i in i_start])+',') -edit_namelist("j_parent_start", ', '.join([str(j) for j in j_start])+',') -edit_namelist("dx",str(dx*1000),incolumn=0) -edit_namelist("dy",str(dy*1000),incolumn=0) -edit_namelist("e_we",', '.join([str(w) for w in e_we])+',') -edit_namelist("e_sn",', '.join([str(s) for s in e_sn])+',') -edit_namelist("prefix",prefix+',',incolumn=0) -edit_namelist("fg_name",prefix+',',incolumn=0) - -# Add your own here if wanting to change e.g. domain location, dx, dy from here... - -# Link to, and ungrib, initialisation files -os.system('./link_grib.csh ' + pathtoinitdata) -os.system('ln -sf ungrib/Variable_Tables/Vtable.' + Vtable_suffix + ' Vtable') -os.system('./ungrib.exe') -os.system('./metgrid.exe') + init_prefix = 'namanl' +elif init_data == 'gefs': # WORK IN PROGRESS + #ens = raw_input('Which ensemble member? (c00,p01...p10) ') + #atmos_levs = 12 + #soil_levs = 4 # Uses GFS soil data + #interval = 3 # Interpolation happens after 90 h + #Vtable_suffix = 'GEFSR2' + print 'Work in progress - check back later.' + +# Get nice strings for namelist writing +y1,mth1,d1,h1,min1,s1 = str_from_date(idate,'indiv') +y2,mth2,d2,h2,min2,s2 = str_from_date(fdate,'indiv') + +if WPS: + # Prepares namelist.wps + idate_s = "'"+y1+"-"+mth1+"-"+d1+'_'+h1+':'+min1+":"+s1+"'," + edit_namelist("start_date",idate_s * domains, incolumn=0) + fdate_s = "'"+y2+"-"+mth2+"-"+d2+'_'+h2+':'+min2+":"+s2+"'," + edit_namelist("end_date",fdate_s * domains, incolumn=0) + edit_namelist("max_dom",str(domains)+',',incolumn=0) + edit_namelist("interval_seconds",str(interval*3600)+',', incolumn=0) + edit_namelist("parent_grid_ratio",', '.join([str(p) for p in parent_grid_ratio])+',') + edit_namelist("i_parent_start", ', '.join([str(i) for i in i_start])+',') + edit_namelist("j_parent_start", ', '.join([str(j) for j in j_start])+',') + edit_namelist("dx",str(dx*1000),incolumn=0) + edit_namelist("dy",str(dy*1000),incolumn=0) + edit_namelist("e_we",', '.join([str(w) for w in e_we])+',') + edit_namelist("e_sn",', '.join([str(s) for s in e_sn])+',') + edit_namelist("prefix",int_prefix+',',incolumn=0) + edit_namelist("fg_name",int_prefix+',',incolumn=0) + + # Add your own here if wanting to change e.g. domain location, dx, dy from here... + + # Run geogrid + os.system('./geogrid.exe') + + # Check to see if initialisation files exist + # If they don't, download into data directory + get_init_files(init_data,idate,fdate,pathtoinitdata) + + # Link to, and ungrib, initialisation files + os.system('./link_grib.csh ' + pathtoinitdata + init_prefix) + os.system('ln -sf ungrib/Variable_Tables/Vtable.' + Vtable_suffix + ' Vtable') + os.system('./ungrib.exe') + os.system('./metgrid.exe') # Submit jobs (edit as needed) -if submit_job == 1: +if WRF: # Soft link data netCDFs files from WPS to WRF - os.system('ln -sf ./met_em* ' + pathtoWRF) + os.system('ln -sf ' + pathtoWPS + 'met_em* ' + pathtoWRF) ##### Sync namelist.input with namelist.wps # Copy original in case of bugs os.system('cp ' + pathtoWRF + 'namelist.input{,.python_backup}') + + print 'met_em* linked. Now amending namelist.input.' + # Compute run time - dt_1 = calendar.timegm((int(idate[0:4]),int(idate[4:6]),int(idate[6:8]), - int(idate[9:11]),int(idate[11:13]),0)) - dt_2 = calendar.timegm((int(fdate[0:4]),int(fdate[4:6]),int(fdate[6:8]), - int(fdate[9:11]),int(fdate[11:13]),0)) + dt_1 = calendar.timegm(idate) + dt_2 = calendar.timegm(fdate) dt = datetime.timedelta(seconds=(dt_2 - dt_1)) days = dt.days hrs = math.floor(dt.seconds/3600.0) @@ -167,18 +269,18 @@ def edit_namelist_input(old,new,pathtoWRF=pathtoWRF): edit_namelist_input("run_hours","%01u" %hrs) edit_namelist_input("run_minutes","%01u" %mins) edit_namelist_input("run_seconds","%01u" %secs) - edit_namelist_input("start_year", (idate[:4]+', ')*domains) - edit_namelist_input("start_month", (idate[4:6]+', ')*domains) - edit_namelist_input("start_day", (idate[6:8]+', ')*domains) - edit_namelist_input("start_hour", (idate[9:11]+', ')*domains) - edit_namelist_input("start_minute", (idate[11:13]+', ')*domains) - edit_namelist_input("start_second", ('00, ')*domains) - edit_namelist_input("end_year", (fdate[:4]+', ')*domains) - edit_namelist_input("end_month", (fdate[4:6]+', ')*domains) - edit_namelist_input("end_day", (fdate[6:8]+', ')*domains) - edit_namelist_input("end_hour", (fdate[9:11]+', ')*domains) - edit_namelist_input("end_minute", (fdate[11:13]+', ')*domains) - edit_namelist_input("end_second", ('00, ')*domains) + edit_namelist_input("start_year", (y1+', ')*domains) + edit_namelist_input("start_month", (mth1+', ')*domains) + edit_namelist_input("start_day", (d1+', ')*domains) + edit_namelist_input("start_hour", (h1+', ')*domains) + edit_namelist_input("start_minute", (min1+', ')*domains) + edit_namelist_input("start_second", (s1+', ')*domains) + edit_namelist_input("end_year", (y2+', ')*domains) + edit_namelist_input("end_month", (mth2+', ')*domains) + edit_namelist_input("end_day", (d2+', ')*domains) + edit_namelist_input("end_hour", (h2+', ')*domains) + edit_namelist_input("end_minute", (min2+', ')*domains) + edit_namelist_input("end_second", (s2+', ')*domains) edit_namelist_input("interval_seconds", str(interval*3600)+',') edit_namelist_input("max_dom",str(domains)+',') edit_namelist_input("e_we", ', '.join([str(w) for w in e_we])+',') @@ -190,18 +292,22 @@ def edit_namelist_input(old,new,pathtoWRF=pathtoWRF): edit_namelist_input("j_parent_start", ', '.join([str(j) for j in j_start])+',') edit_namelist_input("parent_grid_ratio",', '.join([str(p) for p in parent_grid_ratio])+',') - """ - # Run real, get ID number of job - # Change name of submission script if needed - p_real = subprocess.call([qsub,real_run.sh],cwd=pathtoWRF,shell=True, - stdout=subprocess.PIPE) - jobid = p.stdout.read()[:5] # Assuming last five digits = job ID. - # Run WRF but wait until Real has finished without errors - # Again, change name of submission script if needed - p_wrf = subprocess.call([qsub,wrf_run.sh,-W,"afterok=jobid"], - cwd=pathtoWRF,shell=True) - print "real.exe and wrf.exe submitted. Exiting Python script." - """ -else: - print "Pre-processing complete. Exiting Python script." + + if submit_job: + print 'Namelist edited. Now submitting real.exe.' + # Run real, get ID number of job + # Change name of submission script if needed + p_real = subprocess.Popen('qsub -d'+pathtoWRF+' real_run.sh',cwd=pathtoWRF,shell=True,stdout=subprocess.PIPE) + p_real.wait() + print p_real.stdout.read() + jobid = p_real.stdout.read()[:5] # Assuming last five digits = job ID. + # Run WRF but wait until Real has finished without errors + print 'Now submitting wrf.exe.' + # Again, change name of submission script if needed + p_wrf = subprocess.Popen('qsub -d'+pathtoWRF+' wrf_run.sh -W afterok=jobid',cwd=pathtoWRF) + p_wrf.wait() + print "real.exe and wrf.exe submitted. Exiting Python script." + + else: + print "Pre-processing complete. Exiting Python script." From befee1b749f2323e42ff78ea11a3a578624cc206 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 10 Sep 2013 19:51:19 -0500 Subject: [PATCH 006/111] Trying... --- README | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README b/README index 5ff54d0..41091c5 100644 --- a/README +++ b/README @@ -4,6 +4,10 @@ The lazyWRF project aims to simplify the WPS/WRF process for a few reasons: (3) It can maximise efficiency when submitting jobs to a server (i.e. it can do it overnight) Your main file is lazyWRF.py. This is the template for other clever ideas like looping, etc. +It does the following: + + (1) + (2) This is a work in progress, and comments as highly encouraged. The final goal is a portable and bug-free collection of standalone code that anyone can download, assuming basic Python package installs. From 8e7504a5c01946fb8d682d4ab4965eaf6726ecf5 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 10 Sep 2013 20:03:14 -0500 Subject: [PATCH 007/111] Update to main script --- lazyWRF.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/lazyWRF.py b/lazyWRF.py index 3445888..3df964a 100644 --- a/lazyWRF.py +++ b/lazyWRF.py @@ -12,7 +12,6 @@ # If job submission is switched on, make sure namelist.input parameterisations are correct # This script will sync all namelist.wps settings with namelist.output -########################## # IMPORTS import os import sys @@ -23,26 +22,30 @@ import math import datetime import time -########################### -# Script SETTINGS +###################################### +### EDIT BELOW HERE ################## +###################################### + +##### Script SETTINGS ##### # If switched on, this will do pre-processing (WPS) -WPS = 0 +WPS = 1 # If switched on, this will do WRF processing WRF = 1 # If switched on, the script will submit jobs (edit as needed) submit_job = 1 # If switched on, existing wrfout files etc will be moved to a folder -move_wrfout = 0 +move_wrfout = 0 # WORK IN PROGRESS + # Path to WRF folder (absolute) pathtoWPS = '/ptmp/jrlawson/WPS/' pathtoWRF = '/ptmp/jrlawson/WRFV3/run/' # Path to move wrfout* files pathtowrfout = '/ptmp/jrlawson/home/path/to/store/wrfout/' -# WRF run SETTINGS -# Start,end date in (YYYY,MM,DD,H,M,S) +##### WRF run SETTINGS ##### +# Start and end date in (YYYY,MM,DD,H,M,S) idate = (2006,05,26,0,0,0) fdate = (2006,05,27,12,0,0) domains = 1 @@ -62,9 +65,12 @@ # Intermediate file prefix (usually no need to change) int_prefix = "FILE" -############################ +###################################### +### EDIT ABOVE HERE ################## +###################################### # FUNCTIONS + def edit_namelist(old,new,incolumn=1): nwps = open('namelist.wps','r').readlines() for idx, line in enumerate(nwps): @@ -121,7 +127,6 @@ def download_data(date,initdata,pathtoinitdata): suffix = '.grb' command = ('wget "http://nomads.ncdc.noaa.gov/data/' + prefix + '/' + date[:6] + '/' + date[:8] + '/' + prefix + '_' + n + '_' + date + '_000' + suffix + '" -P ' + pathtoinitdata) - #pdb.set_trace() os.system(command) return @@ -167,8 +172,9 @@ def get_init_files(initdata,idate,fdate,pathtoinitdata): print 'Data for ' + fname_date + ' already exists.' return -############################ -############################ +############################### +#### BEGINNING OF CODE ######## +############################### # Open the WPS namelist; copy the old one in case of bugs os.system('cp namelist.wps{,.python_backup}') From 80a6454e45157dc2d46b44f3ae0c2d168438e7aa Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 10 Sep 2013 20:08:38 -0500 Subject: [PATCH 008/111] Update to README --- README | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README b/README index 41091c5..87f234f 100644 --- a/README +++ b/README @@ -4,12 +4,23 @@ The lazyWRF project aims to simplify the WPS/WRF process for a few reasons: (3) It can maximise efficiency when submitting jobs to a server (i.e. it can do it overnight) Your main file is lazyWRF.py. This is the template for other clever ideas like looping, etc. + It does the following: - (1) - (2) + (1) Runs geogrid.exe, ungrib.exe, metgrid.exe, real.exe, and wrf.exe automatically. + (2) Syncs your namelist.wps and namelist.input so you only have to enter settings once + (i) This includes not having to duplicate everything for each domain + (ii) And you don't have to work out run_hours etc for the namelist.input + (3) Automatically downloaded GFS, NAM data you need for your run + +It will eventually: + + (*) Intelligently check your logs to alert you of errors (and kill the script at this point) + (*) Move existing wrfout*, namelist.input etc files from your previous run into a specific directory + (*) Be suitable for looping through dates, parameterisation permutations, etc etc + (*) Merge with other projects (creating ensembles with GEFS data; PyWRFPlus plotting package) -This is a work in progress, and comments as highly encouraged. The final goal is a portable and bug-free collection of standalone code that anyone can download, assuming basic Python package installs. +This is a work in progress, and comments are highly encouraged. The final goal is a portable and bug-free collection of standalone code that anyone can download, assuming basic Python package installs. Please report suggestions, issues, etc to me via GitHub and I will improve and fix the package as we go along. Hopefully there is something useful to come out of this, potentially to be presented at the American Meteorological Society Annual Meeting 2014 in Atlanta, Georgia. From 64986d0e5d23d58854454f4f082cce5a1da42955 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 11 Sep 2013 12:43:28 -0500 Subject: [PATCH 009/111] Added log-file error checking and emails upon errors --- lazyWRF.py | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/lazyWRF.py b/lazyWRF.py index 3df964a..22ef208 100644 --- a/lazyWRF.py +++ b/lazyWRF.py @@ -1,4 +1,4 @@ -# This script goes through post-processing of WRF +# This script goes through processing of WPS and WRF (either, or both) # And submission of the job to a Rocks cluster if desired (edit to taste) # Run it ("python lazyWRF.py") from your WPS folder. @@ -31,9 +31,9 @@ # If switched on, this will do pre-processing (WPS) WPS = 1 # If switched on, this will do WRF processing -WRF = 1 +WRF = 0 # If switched on, the script will submit jobs (edit as needed) -submit_job = 1 +submit_job = 0 # If switched on, existing wrfout files etc will be moved to a folder move_wrfout = 0 # WORK IN PROGRESS @@ -44,6 +44,10 @@ # Path to move wrfout* files pathtowrfout = '/ptmp/jrlawson/home/path/to/store/wrfout/' +# If you want error messages sent to an email, fill it here as a string; otherwise put '0'. +# email = 'yourname@domain.edu' +email = 0 + ##### WRF run SETTINGS ##### # Start and end date in (YYYY,MM,DD,H,M,S) idate = (2006,05,26,0,0,0) @@ -172,6 +176,21 @@ def get_init_files(initdata,idate,fdate,pathtoinitdata): print 'Data for ' + fname_date + ' already exists.' return +# This function runs a script and checks for errors; raises exception if one exists +def intelligent_run(executable,email): + # email = if you want email sent to an address, fill it here + command = './' + executable + '.exe' + os.system(command) + logfile = open(executable + '.log').readlines() + if "Successful completion" in logfile[-1]: + print '>>>>>>>> ' , executable, "has completed successfully. <<<<<<<<" + else: + print '!!!!!!!! ' , executable, "has failed. Exiting... !!!!!!!!" + if email: + os.system('tail '+logfile+' | mail -s "lazyWRF message: error in '+executable+'." '+email) + raise Exception + return + ############################### #### BEGINNING OF CODE ######## ############################### @@ -226,7 +245,7 @@ def get_init_files(initdata,idate,fdate,pathtoinitdata): # Add your own here if wanting to change e.g. domain location, dx, dy from here... # Run geogrid - os.system('./geogrid.exe') + intelligent_run('geogrid',email) # Check to see if initialisation files exist # If they don't, download into data directory @@ -235,8 +254,8 @@ def get_init_files(initdata,idate,fdate,pathtoinitdata): # Link to, and ungrib, initialisation files os.system('./link_grib.csh ' + pathtoinitdata + init_prefix) os.system('ln -sf ungrib/Variable_Tables/Vtable.' + Vtable_suffix + ' Vtable') - os.system('./ungrib.exe') - os.system('./metgrid.exe') + intelligent_run('ungrib',email) + intelligent_run('metgrid',email) # Submit jobs (edit as needed) if WRF: @@ -306,11 +325,11 @@ def get_init_files(initdata,idate,fdate,pathtoinitdata): p_real = subprocess.Popen('qsub -d'+pathtoWRF+' real_run.sh',cwd=pathtoWRF,shell=True,stdout=subprocess.PIPE) p_real.wait() print p_real.stdout.read() - jobid = p_real.stdout.read()[:5] # Assuming last five digits = job ID. + jobid = p_real.stdout.read()[:5] # Assuming first five digits = job ID. # Run WRF but wait until Real has finished without errors print 'Now submitting wrf.exe.' # Again, change name of submission script if needed - p_wrf = subprocess.Popen('qsub -d'+pathtoWRF+' wrf_run.sh -W afterok=jobid',cwd=pathtoWRF) + p_wrf = subprocess.Popen('qsub -d'+pathtoWRF+' wrf_run.sh -W depend=afterok:'+jobid,cwd=pathtoWRF,shell=True) p_wrf.wait() print "real.exe and wrf.exe submitted. Exiting Python script." From c4db8d0e736fb42a4720f6bb09149824356c2c1c Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 11 Sep 2013 12:49:13 -0500 Subject: [PATCH 010/111] Updated README --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 87f234f..e680145 100644 --- a/README +++ b/README @@ -12,10 +12,10 @@ It does the following: (i) This includes not having to duplicate everything for each domain (ii) And you don't have to work out run_hours etc for the namelist.input (3) Automatically downloaded GFS, NAM data you need for your run + (4) Intelligently check your logs to alert you via email of errors (and kill the script at this point) It will eventually: - (*) Intelligently check your logs to alert you of errors (and kill the script at this point) (*) Move existing wrfout*, namelist.input etc files from your previous run into a specific directory (*) Be suitable for looping through dates, parameterisation permutations, etc etc (*) Merge with other projects (creating ensembles with GEFS data; PyWRFPlus plotting package) From df3abbe2a232232d260c9b53e5a0a3156554e119 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 11 Sep 2013 13:05:29 -0500 Subject: [PATCH 011/111] Updated README --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index e680145..e05fe67 100644 --- a/README +++ b/README @@ -8,10 +8,10 @@ Your main file is lazyWRF.py. This is the template for other clever ideas like l It does the following: (1) Runs geogrid.exe, ungrib.exe, metgrid.exe, real.exe, and wrf.exe automatically. - (2) Syncs your namelist.wps and namelist.input so you only have to enter settings once + (2) Syncs your namelist.wps and namelist.input so you only have to enter settings once (at the top of the script) (i) This includes not having to duplicate everything for each domain (ii) And you don't have to work out run_hours etc for the namelist.input - (3) Automatically downloaded GFS, NAM data you need for your run + (3) Automatically downloaded GFS, NAM data you need for your run (others coming soon...) (4) Intelligently check your logs to alert you via email of errors (and kill the script at this point) It will eventually: From fbc8bc4b36f540d49aff481793566d78787db7bc Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 11 Sep 2013 13:12:19 -0500 Subject: [PATCH 012/111] Improved commenting --- lazyWRF.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lazyWRF.py b/lazyWRF.py index 22ef208..90bb749 100644 --- a/lazyWRF.py +++ b/lazyWRF.py @@ -8,9 +8,12 @@ # -------------IMPORTANT NOTES--------------- # Remove met_em files if you don't want them overwritten (or hide them) # Make sure all SETTINGS are correct +# Make sure you've read all the comments down to "Edit above here" +# Make sure you've set other settings in both namelists that aren't covered in this script # Then run this script # If job submission is switched on, make sure namelist.input parameterisations are correct # This script will sync all namelist.wps settings with namelist.output +# Submit issues and requests to the GitHub (https://github.com/johnrobertlawson/lazyWRF) # IMPORTS import os @@ -69,6 +72,11 @@ # Intermediate file prefix (usually no need to change) int_prefix = "FILE" +### NOTE: +# Any settings you want to change that aren't in this box, +# You need to manually change yourself in either namelist. +# Submit a github request if you think it can be automated/is commonly changed + ###################################### ### EDIT ABOVE HERE ################## ###################################### From 6df33a84cb52cc34ecd8a56b89016881be4ffe55 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 11 Sep 2013 13:54:56 -0500 Subject: [PATCH 013/111] Improved commenting; moved 'interval' to top settings, independent of initialisation model --- lazyWRF.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/lazyWRF.py b/lazyWRF.py index 90bb749..60b0ca2 100644 --- a/lazyWRF.py +++ b/lazyWRF.py @@ -41,10 +41,10 @@ # If switched on, existing wrfout files etc will be moved to a folder move_wrfout = 0 # WORK IN PROGRESS -# Path to WRF folder (absolute) +# Path to WRF, WPS folders (absolute) - end in a slash! pathtoWPS = '/ptmp/jrlawson/WPS/' pathtoWRF = '/ptmp/jrlawson/WRFV3/run/' -# Path to move wrfout* files +# Path to move wrfout* files - end in a slash! pathtowrfout = '/ptmp/jrlawson/home/path/to/store/wrfout/' # If you want error messages sent to an email, fill it here as a string; otherwise put '0'. @@ -54,6 +54,7 @@ ##### WRF run SETTINGS ##### # Start and end date in (YYYY,MM,DD,H,M,S) idate = (2006,05,26,0,0,0) +interval = 6.0 # In hours, as a float fdate = (2006,05,27,12,0,0) domains = 1 e_we = (500,) # Needs to be same length as number of domains @@ -67,14 +68,14 @@ # Select initial data source # FROM: gfs, nam, (gefs --> use setup_gefs.py script for now) init_data = 'gfs' -# Directory with initialisation data +# Directory with initialisation data (absolute, or relative to WPS) - end in a slash! pathtoinitdata = './gfsfiles/' # Intermediate file prefix (usually no need to change) int_prefix = "FILE" ### NOTE: -# Any settings you want to change that aren't in this box, -# You need to manually change yourself in either namelist. +# Any settings you want to change that aren't in this box (e.g. time step), +# you need to manually change yourself in its relevant namelist. # Submit a github request if you think it can be automated/is commonly changed ###################################### @@ -142,13 +143,11 @@ def download_data(date,initdata,pathtoinitdata): os.system(command) return -def get_init_files(initdata,idate,fdate,pathtoinitdata): +def get_init_files(initdata,idate,interval,fdate,pathtoinitdata): # Let's assume all initdata possibilities start at 0000 UTC and the interval is an integer factor if initdata=='gfs': - interval = 6.0 prefix = 'gfsanl' elif initdata=='nam': - interval = 6.0 prefix = 'namanl' # First, convert dates to seconds-from-epoch time @@ -211,20 +210,17 @@ def intelligent_run(executable,email): if init_data == 'gfs': atmos_levs = 27 soil_levs = 4 - interval = 6 # In hours Vtable_suffix = 'GFS' init_prefix = 'gfsanl' elif init_data == 'nam': atmos_levs = 40 soil_levs = 4 - interval = 6 # In hours Vtable_suffix = 'NAM' init_prefix = 'namanl' elif init_data == 'gefs': # WORK IN PROGRESS #ens = raw_input('Which ensemble member? (c00,p01...p10) ') #atmos_levs = 12 #soil_levs = 4 # Uses GFS soil data - #interval = 3 # Interpolation happens after 90 h #Vtable_suffix = 'GEFSR2' print 'Work in progress - check back later.' @@ -257,7 +253,7 @@ def intelligent_run(executable,email): # Check to see if initialisation files exist # If they don't, download into data directory - get_init_files(init_data,idate,fdate,pathtoinitdata) + get_init_files(init_data,idate,interval,fdate,pathtoinitdata) # Link to, and ungrib, initialisation files os.system('./link_grib.csh ' + pathtoinitdata + init_prefix) From d1cf9b6c4870a4bb3f91001450c44f7219cabf0d Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 12 Sep 2013 16:01:30 -0500 Subject: [PATCH 014/111] Bugfixes --- lazyWRF.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lazyWRF.py b/lazyWRF.py index 60b0ca2..d1dc968 100644 --- a/lazyWRF.py +++ b/lazyWRF.py @@ -34,9 +34,9 @@ # If switched on, this will do pre-processing (WPS) WPS = 1 # If switched on, this will do WRF processing -WRF = 0 +WRF = 1 # If switched on, the script will submit jobs (edit as needed) -submit_job = 0 +submit_job = 1 # If switched on, existing wrfout files etc will be moved to a folder move_wrfout = 0 # WORK IN PROGRESS @@ -66,7 +66,9 @@ parent_grid_ratio = (1,) # Same length as num of domains; ratio of each parent to its child # Select initial data source -# FROM: gfs, nam, (gefs --> use setup_gefs.py script for now) +# 'gfs' = GFS analyses +# 'nam' = NAM analyses +# (gefs --> use setup_gefs.py script for now) init_data = 'gfs' # Directory with initialisation data (absolute, or relative to WPS) - end in a slash! pathtoinitdata = './gfsfiles/' @@ -235,7 +237,7 @@ def intelligent_run(executable,email): fdate_s = "'"+y2+"-"+mth2+"-"+d2+'_'+h2+':'+min2+":"+s2+"'," edit_namelist("end_date",fdate_s * domains, incolumn=0) edit_namelist("max_dom",str(domains)+',',incolumn=0) - edit_namelist("interval_seconds",str(interval*3600)+',', incolumn=0) + edit_namelist("interval_seconds",str(int(interval*3600))+',', incolumn=0) edit_namelist("parent_grid_ratio",', '.join([str(p) for p in parent_grid_ratio])+',') edit_namelist("i_parent_start", ', '.join([str(i) for i in i_start])+',') edit_namelist("j_parent_start", ', '.join([str(j) for j in j_start])+',') @@ -294,10 +296,10 @@ def intelligent_run(executable,email): dys.append(child_dy) # If all namelist values begin on column 38: - edit_namelist_input("run_days","%01u" %days) - edit_namelist_input("run_hours","%01u" %hrs) - edit_namelist_input("run_minutes","%01u" %mins) - edit_namelist_input("run_seconds","%01u" %secs) + edit_namelist_input("run_days","%01u" %days + ',') + edit_namelist_input("run_hours","%01u" %hrs + ',') + edit_namelist_input("run_minutes","%01u" %mins + ',') + edit_namelist_input("run_seconds","%01u" %secs + ',') edit_namelist_input("start_year", (y1+', ')*domains) edit_namelist_input("start_month", (mth1+', ')*domains) edit_namelist_input("start_day", (d1+', ')*domains) @@ -310,7 +312,7 @@ def intelligent_run(executable,email): edit_namelist_input("end_hour", (h2+', ')*domains) edit_namelist_input("end_minute", (min2+', ')*domains) edit_namelist_input("end_second", (s2+', ')*domains) - edit_namelist_input("interval_seconds", str(interval*3600)+',') + edit_namelist_input("interval_seconds", str(int(interval*3600))+',') edit_namelist_input("max_dom",str(domains)+',') edit_namelist_input("e_we", ', '.join([str(w) for w in e_we])+',') edit_namelist_input("e_sn", ', '.join([str(s) for s in e_sn])+',') @@ -326,14 +328,13 @@ def intelligent_run(executable,email): print 'Namelist edited. Now submitting real.exe.' # Run real, get ID number of job # Change name of submission script if needed - p_real = subprocess.Popen('qsub -d'+pathtoWRF+' real_run.sh',cwd=pathtoWRF,shell=True,stdout=subprocess.PIPE) + p_real = subprocess.Popen('qsub -d '+pathtoWRF+' real_run.sh',cwd=pathtoWRF,shell=True,stdout=subprocess.PIPE) p_real.wait() - print p_real.stdout.read() jobid = p_real.stdout.read()[:5] # Assuming first five digits = job ID. # Run WRF but wait until Real has finished without errors print 'Now submitting wrf.exe.' # Again, change name of submission script if needed - p_wrf = subprocess.Popen('qsub -d'+pathtoWRF+' wrf_run.sh -W depend=afterok:'+jobid,cwd=pathtoWRF,shell=True) + p_wrf = subprocess.Popen('qsub -d '+pathtoWRF+' wrf_run.sh -W depend=afterok:'+jobid,cwd=pathtoWRF,shell=True) p_wrf.wait() print "real.exe and wrf.exe submitted. Exiting Python script." From b14674a1345d7042d950de113efa90d3530a9c19 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 16 Oct 2013 15:56:35 -0500 Subject: [PATCH 015/111] Bug fixes --- lazyWRF.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lazyWRF.py b/lazyWRF.py index d1dc968..a9f3665 100644 --- a/lazyWRF.py +++ b/lazyWRF.py @@ -10,6 +10,7 @@ # Make sure all SETTINGS are correct # Make sure you've read all the comments down to "Edit above here" # Make sure you've set other settings in both namelists that aren't covered in this script +# (This script won't remove any bogus lines in either namelist, syntax errors etc) # Then run this script # If job submission is switched on, make sure namelist.input parameterisations are correct # This script will sync all namelist.wps settings with namelist.output @@ -68,7 +69,6 @@ # Select initial data source # 'gfs' = GFS analyses # 'nam' = NAM analyses -# (gefs --> use setup_gefs.py script for now) init_data = 'gfs' # Directory with initialisation data (absolute, or relative to WPS) - end in a slash! pathtoinitdata = './gfsfiles/' @@ -219,12 +219,6 @@ def intelligent_run(executable,email): soil_levs = 4 Vtable_suffix = 'NAM' init_prefix = 'namanl' -elif init_data == 'gefs': # WORK IN PROGRESS - #ens = raw_input('Which ensemble member? (c00,p01...p10) ') - #atmos_levs = 12 - #soil_levs = 4 # Uses GFS soil data - #Vtable_suffix = 'GEFSR2' - print 'Work in progress - check back later.' # Get nice strings for namelist writing y1,mth1,d1,h1,min1,s1 = str_from_date(idate,'indiv') From fed31f5a64e3af1d8ff8f3a36e228bab659513b6 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 5 Nov 2013 16:58:21 -0600 Subject: [PATCH 016/111] Example of ensemble creation with SKEB scheme --- sensitivity_ensemble.py | 68 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 sensitivity_ensemble.py diff --git a/sensitivity_ensemble.py b/sensitivity_ensemble.py new file mode 100644 index 0000000..b5ee93b --- /dev/null +++ b/sensitivity_ensemble.py @@ -0,0 +1,68 @@ +# This is an example of creating a WRF ensemble using the stochastic kinetic energy backscatter scheme +# We assume met_em files have all been created, have been linked etc + +# Settings +datestring = '20090910' +ensembletype = 'STCH' # Change in future to expand to e.g. mixed microphysics +pathtoWRF = './' # Path to WRF folder where wrfout* files are generated + +def edit_namelist_input(old,new,pathtoWRF=pathtoWRF): + ninput = open(pathtoWRF+'namelist.input','r').readlines() + for idx, line in enumerate(ninput): + if old in line: + ninput[idx]= ninput[idx][:39] + new + " \n" + nameout = open(pathtoWRF+'namelist.input','w') + nameout.writelines(ninput) + nameout.close() + break + return + +if ensembletype == 'STCH': + ensnames = ['s' + '%02u' %n for n in range(1,11)] # Change the range to create n-member ensemble + +for n,ens in enumerate(ensnames): + # First, edit namelists + if ensembletype == 'STCH': + edit_namelist_input('nens',str(n)+',') + + # Run real.exe + p_real = subprocess.Popen('qsub -d '+pathtoWRF+' real_run.sh',cwd=pathtoWRF,shell=True,stdout=subprocess.PIPE) + p_real.wait() + jobid = p_real.stdout.read()[:5] # Assuming first five digits = job ID. + + # Run wrf.exe, but wait until real.exe has finished without errors + print 'Now submitting wrf.exe.' + # Again, change name of submission script if needed + p_wrf = subprocess.Popen('qsub -d '+pathtoWRF+' wrf_run.sh -W depend=afterok:'+jobid,cwd=pathtoWRF,shell=True) + p_wrf.wait() + print "real.exe and wrf.exe submitted." + + # Wait an hour to make sure real.exe is complete and wrf.exe is writing to file + time.sleep(60*60) + + # Check log file until wrf.exe has finished + finished = 0 + while not finished: + tailrsl = subprocess.Popen('tail '+pathtoWRF+'rsl.error.0000',shell=True,stdout=subprocess.PIPE) + tailoutput = tailrsl.stdout.read() + if "SUCCESS COMPLETE WRF" in tailoutput: + finished = 1 + print "WRF has finished; moving to next case." + else: + time.sleep(5*60) # Try again in 5 min + + # Copy namelist.input to directory + # Move wrfout file to directory + # Create directory if it doesn't exist + wrfoutdir = '/ptmp/jrlawson/predictability/'+datestring+'/wrfout/'+ensembletype+'/'+ens+'/' + + try: + os.stat(wrfoutdir) + except: + os.makedirs(wrfoutdir) + + os.system('cp ' + pathtoWRF + 'namelist.input ' + wrfoutdir) + os.system('mv' + pathtoWRF + 'wrfout* ' + wrfoutdir) + os.system('rm -f ' + pathtoWRF + 'rsl*') + + # Loop back to top From 3dbd1cc02b0c8f7936739c131422872ac94bdbe4 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 18 Dec 2013 15:26:23 +0000 Subject: [PATCH 017/111] README created. --- README.txt | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 README.txt diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..1be9a1e --- /dev/null +++ b/README.txt @@ -0,0 +1,41 @@ +=== +WEM +=== + +WRF Ensemble Management can help you create WRF ensembles from GEFS reanalysis +data via submodule "lazyWRF". It can also post-process the data (such as +computing ensemble means, creating postage-stamp plots of all members, etc) +via submodule "postWRF". + +Where do I start? +================= + +```./lazyWRF/``` contains scripts that form the basis of automating your WRF +ensemble runs. ```./postWRF/bin/``` contains examples of post-processing that +you may like to perform with the module. The other essential file you will need +to personalise for post-processing is /bin/settings.py. The class therein contains +all the settings for loading data, saving output, etc. Almost all settings can be +left as default (by not specifying a setting), other than essentials like +the path to your WRF data, the path to output figures, etc. + +Contributors & Attributions +=========================== + +Some files or methods contain attributions to other programmers whose +code has been refactored to fit this project. In summary, thanks to: + +HootPy/PulsatrixWx project +-------------- + +URL: http://www.atmos.washington.edu/~lmadaus/pyscripts.html + +* David-John Gagne +* Tim Supinie +* Luke Madaus + +PyWRF project (Monash) +---------------------- + +URL: http://code.google.com/p/pywrf/ +URL: https://github.com/scaine1/pyWRF/ + From 37c99f5466d9c4190515f8987a4f6efb98a21b32 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 18 Dec 2013 15:29:57 +0000 Subject: [PATCH 018/111] README fixed. --- README.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..1be9a1e --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +=== +WEM +=== + +WRF Ensemble Management can help you create WRF ensembles from GEFS reanalysis +data via submodule "lazyWRF". It can also post-process the data (such as +computing ensemble means, creating postage-stamp plots of all members, etc) +via submodule "postWRF". + +Where do I start? +================= + +```./lazyWRF/``` contains scripts that form the basis of automating your WRF +ensemble runs. ```./postWRF/bin/``` contains examples of post-processing that +you may like to perform with the module. The other essential file you will need +to personalise for post-processing is /bin/settings.py. The class therein contains +all the settings for loading data, saving output, etc. Almost all settings can be +left as default (by not specifying a setting), other than essentials like +the path to your WRF data, the path to output figures, etc. + +Contributors & Attributions +=========================== + +Some files or methods contain attributions to other programmers whose +code has been refactored to fit this project. In summary, thanks to: + +HootPy/PulsatrixWx project +-------------- + +URL: http://www.atmos.washington.edu/~lmadaus/pyscripts.html + +* David-John Gagne +* Tim Supinie +* Luke Madaus + +PyWRF project (Monash) +---------------------- + +URL: http://code.google.com/p/pywrf/ +URL: https://github.com/scaine1/pyWRF/ + From 37f39dee9f9c9df7024452981b29570622d043d6 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 18 Dec 2013 15:31:13 +0000 Subject: [PATCH 019/111] Delete README.txt --- README.txt | 41 ----------------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 README.txt diff --git a/README.txt b/README.txt deleted file mode 100644 index 1be9a1e..0000000 --- a/README.txt +++ /dev/null @@ -1,41 +0,0 @@ -=== -WEM -=== - -WRF Ensemble Management can help you create WRF ensembles from GEFS reanalysis -data via submodule "lazyWRF". It can also post-process the data (such as -computing ensemble means, creating postage-stamp plots of all members, etc) -via submodule "postWRF". - -Where do I start? -================= - -```./lazyWRF/``` contains scripts that form the basis of automating your WRF -ensemble runs. ```./postWRF/bin/``` contains examples of post-processing that -you may like to perform with the module. The other essential file you will need -to personalise for post-processing is /bin/settings.py. The class therein contains -all the settings for loading data, saving output, etc. Almost all settings can be -left as default (by not specifying a setting), other than essentials like -the path to your WRF data, the path to output figures, etc. - -Contributors & Attributions -=========================== - -Some files or methods contain attributions to other programmers whose -code has been refactored to fit this project. In summary, thanks to: - -HootPy/PulsatrixWx project --------------- - -URL: http://www.atmos.washington.edu/~lmadaus/pyscripts.html - -* David-John Gagne -* Tim Supinie -* Luke Madaus - -PyWRF project (Monash) ----------------------- - -URL: http://code.google.com/p/pywrf/ -URL: https://github.com/scaine1/pyWRF/ - From 3d50fab9bb11b59eaaca3c660cb5132c82e9d141 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 18 Dec 2013 15:44:03 +0000 Subject: [PATCH 020/111] Update README.md --- README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1be9a1e..08b300c 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ WEM === WRF Ensemble Management can help you create WRF ensembles from GEFS reanalysis -data via submodule "lazyWRF". It can also post-process the data (such as +data via submodule **lazyWRF**. It can also post-process the data (such as computing ensemble means, creating postage-stamp plots of all members, etc) -via submodule "postWRF". +via submodule **postWRF**. Where do I start? ================= @@ -25,7 +25,7 @@ Some files or methods contain attributions to other programmers whose code has been refactored to fit this project. In summary, thanks to: HootPy/PulsatrixWx project --------------- +-------------------------- URL: http://www.atmos.washington.edu/~lmadaus/pyscripts.html @@ -37,5 +37,13 @@ PyWRF project (Monash) ---------------------- URL: http://code.google.com/p/pywrf/ + URL: https://github.com/scaine1/pyWRF/ +PyWRFPlot project +----------------- + +URL: https://code.google.com/p/pywrfplot/ + +* Geir Arne Waagbø + From 8687ed22d9af58f5904061f5602af4e0b88dad01 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 18 Dec 2013 15:45:28 +0000 Subject: [PATCH 021/111] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 08b300c..c0c8be4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +**N.B.: VERSION 1 WILL BE 'RELEASED' IN EARLY FEBRUARY - NOT FIT FOR HUMAN +CONSUMPTION UNTIL THEN** + === WEM === From 28bfa64543c2a6caee1788b523302138b4adaddd Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 18 Dec 2013 15:48:45 +0000 Subject: [PATCH 022/111] Updated README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c0c8be4..254b128 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -**N.B.: VERSION 1 WILL BE 'RELEASED' IN EARLY FEBRUARY - NOT FIT FOR HUMAN -CONSUMPTION UNTIL THEN** +**N.B.: VERSION 1 WILL BE 'RELEASED' IN EARLY FEBRUARY** +**NOT FIT FOR HUMAN CONSUMPTION UNTIL THEN** === WEM From c8348531c10201a51c753d9759c4b531019d729f Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 19 Dec 2013 10:00:18 +0000 Subject: [PATCH 023/111] Out of sync repo. --- postWRF/README.md | 0 postWRF/bin/DKE.py | 2 +- postWRF/bin/casestudy.py | 6 +- postWRF/bin/casestudy_settings.pyc | Bin 0 -> 942 bytes postWRF/postWRF/__init__.py | 400 ++++++++++++++++ postWRF/postWRF/__init__.pyc | Bin 0 -> 12186 bytes postWRF/postWRF/axes.py | 10 + postWRF/postWRF/axes.pyc | Bin 0 -> 701 bytes postWRF/postWRF/birdseye.py | 93 ++++ postWRF/postWRF/birdseye.pyc | Bin 0 -> 2947 bytes postWRF/postWRF/colourtables.py | 735 +++++++++++++++++++++++++++++ postWRF/postWRF/defaults.py | 13 + postWRF/postWRF/defaults.pyc | Bin 0 -> 779 bytes postWRF/postWRF/figure.py | 56 +++ postWRF/postWRF/figure.pyc | Bin 0 -> 2290 bytes postWRF/postWRF/map.py | 0 postWRF/postWRF/params.py | 0 postWRF/postWRF/scales.py | 107 +++++ postWRF/postWRF/scales.pyc | Bin 0 -> 2291 bytes postWRF/postWRF/skewt.py | 0 postWRF/postWRF/ts.py | 10 + postWRF/postWRF/utils.py | 85 ++++ postWRF/postWRF/utils.pyc | Bin 0 -> 2633 bytes postWRF/postWRF/wrfout.py | 259 ++++++++++ postWRF/postWRF/wrfout.pyc | Bin 0 -> 8855 bytes postWRF/postWRF/xsection.py | 22 + postWRF/setup.py | 11 +- 27 files changed, 1799 insertions(+), 10 deletions(-) create mode 100644 postWRF/README.md create mode 100644 postWRF/bin/casestudy_settings.pyc create mode 100644 postWRF/postWRF/__init__.py create mode 100644 postWRF/postWRF/__init__.pyc create mode 100644 postWRF/postWRF/axes.py create mode 100644 postWRF/postWRF/axes.pyc create mode 100644 postWRF/postWRF/birdseye.py create mode 100644 postWRF/postWRF/birdseye.pyc create mode 100644 postWRF/postWRF/colourtables.py create mode 100644 postWRF/postWRF/defaults.py create mode 100644 postWRF/postWRF/defaults.pyc create mode 100644 postWRF/postWRF/figure.py create mode 100644 postWRF/postWRF/figure.pyc create mode 100644 postWRF/postWRF/map.py create mode 100644 postWRF/postWRF/params.py create mode 100644 postWRF/postWRF/scales.py create mode 100644 postWRF/postWRF/scales.pyc create mode 100644 postWRF/postWRF/skewt.py create mode 100644 postWRF/postWRF/ts.py create mode 100644 postWRF/postWRF/utils.py create mode 100644 postWRF/postWRF/utils.pyc create mode 100644 postWRF/postWRF/wrfout.py create mode 100644 postWRF/postWRF/wrfout.pyc create mode 100644 postWRF/postWRF/xsection.py diff --git a/postWRF/README.md b/postWRF/README.md new file mode 100644 index 0000000..e69de29 diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index 4b8df45..a82ce15 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -2,7 +2,7 @@ of data stored to disc. This is then plotted using the package. """ -from compute_settings import Settings +from DKE_settings import Settings # Initialise settings and environment config = Settings() diff --git a/postWRF/bin/casestudy.py b/postWRF/bin/casestudy.py index 4bf5e43..882c6cc 100644 --- a/postWRF/bin/casestudy.py +++ b/postWRF/bin/casestudy.py @@ -5,15 +5,15 @@ import sys sys.path.append('../') -from PyWRFPlus import PyWRFEnv +from postWRF import WRFEnviron from casestudy_settings import Settings import pdb import os # Initialise configuration -config = PyWRFSettings() +config = Settings() # Initialise plotting environment -p = PyWRFEnv(config) +p = WRFEnviron(config) #config.datafolder = os.path.join('2009091000','GEFS','CTRL','c00') diff --git a/postWRF/bin/casestudy_settings.pyc b/postWRF/bin/casestudy_settings.pyc new file mode 100644 index 0000000000000000000000000000000000000000..26568e3b1f504624dd330c3295613d2bad49e6eb GIT binary patch literal 942 zcmcIi+iKfD5FN=*eCdS*8UpazoZg{t z^0r$!TdED!4v!>FCg?Z&{F-CD8-Io;l5I6@a`I9Yv~BWAlePz(vF}-t9ByZuDwBoo zpUs4b{3+>Rv z=)j5O5(ZldKP9AjdA##d%Luu{(mNkkI>gI~XlUGZ#uh^UlpEGd=pV0UvdYD}HU=oR`|A`$B{ 8): + print("Domain is out of range. Choose number between 1 and 8 inclusive.") + raise IndexError + else: + dom = 'd{0:02d}'.format(dom) + prefix = w + '_' + dom + + wrfouts = [] + for root,dirs,files in os.walk(folder): + for fname in fnmatch.filter(files,prefix+suffix): + wrfouts.append(os.path.join(root, fname)) + return wrfouts + + def dir_from_naming(self,*args): + l = [str(a) for a in args] + path = os.path.join(self.C.output_root,*l) + return path + + def string_from_time(self,usage,t,dom=0,strlen=0,conven=0): + str = utils.string_from_time(usage=usage,t=t,dom=dom,strlen=strlen,conven=conven) + return str + + def plot_variable2D(self,va,pt,en,lv,p2p,na=0,da=0): + """Plot a longitude--latitude cross-section (bird's-eye-view). + Use Basemap to create geographical data + + ======== + REQUIRED + ======== + + va = variable(s) + pt = plot time(s) + nc = ensemble member(s) + lv = level(s) + p2p = path to plots + + ======== + OPTIONAL + ======== + + da = smaller domain area(s), needs dictionary || DEFAULT = 0 + na = naming scheme for plot files || DEFAULT = get what you're given + + """ + va = self.get_sequence(va) + pt = self.get_sequence(pt,SoS=1) + en = self.get_sequence(en) + lv = self.get_sequence(lv) + da = self.get_sequence(da) + + #perms = self.make_iterator(va,pt,en,lv,da) + #perm2 = self.make_iterator2(va,pt,en,lv,da) + #pdb.set_trace() + + # Find some way of looping over wrfout files first, avoiding need + # to create new W instances + # print("Beginning plotting of {0} figures.".format(len(list(perms)))) + pdb.set_trace() + + #for x in perms: + for x in itertools.product(va,pt,en,lv,da): + va,pt,en,lv,da = x + pdb.set_trace() + W = WRFOut(en) # wrfout file class using path + F = BirdsEye(self.C,W,p2p) # 2D figure class + F.plot2D(va,pt,en,lv,da,na) # Plot/save figure + pt_s = utils.string_from_time('title',pt) + print("Plotting from file {0}: \n variable = {1}" + " time = {2}, level = {3}, area = {4}.".format(en,va,pt_s,lv,da)) + + def make_iterator2(self,*args): + for arg in args: + for a in arg: + yield a + + def make_iterator(self,va,pt,en,lv,da): + for v in va: + for p in pt: + for e in en: + for l in lv: + for d in da: + yield v,p,e,l,d + + def get_sequence(self,x,SoS=0): + """ Returns a sequence (tuple or list) for iteration. + + SoS = 1 enables the check for a sequence of sequences (list of dates) + """ + + + if SoS: + y = x[0] + else: + y = x + + if isinstance(y, collections.Sequence) and not isinstance(y, basestring): + return x + else: + return [x] + + + def plot_cross_section(self,var,latA,lonA,latB,lonB): + xs = CrossSection() + xs.plot(var,latA,lonA,latB,lonB) + + def save_data(self,data,folder,fname): + # Ensure file suffix will be .npy + os.path.splitext(fname)[0] + # Check for folder and create if necessary + utils.trycreate(folder) + # Save in binary + fpath = os.path.join(folder,fname) + N.save(fpath,data) + + print("Save file {0}.npy to {1}.".format(fname,folder)) + + def compute_diff_energy( + self,ptype,energy,files,times,upper=None,lower=None, + d_save=1,d_return=1,d_fname='diff_energy_data'): + """ + This method computes difference kinetic energy (DKE) + or different total energy (DTE, including temp) + between WRFout files for a given depth of the + atmosphere, at given time intervals + + Inputs: + + ptype : 'sum_z' or 'sum_xyz' + energy : 'kinetic' or 'total' + upper : upper limit of vertical integration + lower : lower limit of vertical integration + files : abs paths to all wrfout files + times : times for computations - tuple format + d_save : save dictionary to folder (path to folder) + d_return: return dictionary (True or False) + d_fname : custom filename + + Outputs: + + data : time series or list of 2D arrays + + ptype 'sum_z' integrates vertically between lower and + upper hPa and creates a time series. + + ptype 'sum_xyz' integrates over the 3D space (again between + the upper and lower bounds) and creates 2D arrays. + """ + if d_save and not isinstance(d_save,basestring): + d_save = os.environ['HOME'] + + # First, save or output? Can't be neither! + if not d_save and not d_return: + print("Pick save or output, otherwise it's a waste of computer" + "power") + raise Exception + + # Look up the method to use depending on type of plot + PLOTS = {'sum_z':self.DE_z, 'sum_xyz':self.DE_xyz} + + # Creates sequence of times + ts = self.get_sequence(times) + + # Dictionary of data + DATA = {} + + # Get all permutations of files + for perm in itertools.combinations(files,2): + DATA[perm] = {} + f1, f2 = perm + W1 = WRFOut(f1) + W2 = WRFOut(f2) + + # Make sure times are the same in both files + if not W1.wrf_times == W2.wrf_times: + print("Times are not identical between input files.") + raise Exception + + # Find indices of each time + t_idx = [] + for t in ts: + t_idx.append(W1.get_time_idx(t)) + + DATA[perm]['times'] = ts + DATA[perm]['values'] = PLOTS[ptype](nc0,nc1,t_idx, + energy,lower,upper) + + if d_return and not d_save: + return DATA + elif d_save and not d_return: + self.save(DATA,d_save,d_fname) + elif d_return and d_save: + self.save(DATA,d_save,d_fname) + return DATA + + def DE_xyz(self,nc0,nc1,t_idx,energy,*args): + """ + Computation for difference kinetic energy (DKE). + Sums DKE over the 3D space, returns a time series. + + Destaggering is not enabled as it introduces + computational cost that is of miniscule value considering + the magnitudes of output values. + + Inputs: + + nc0 : netCDF file + nc1 : netCDF file + t_idx : times indices to difference + energy : kinetic or total + *args : to catch lower/upper boundary which isn't relevant here + + Outputs: + + data : time series. + """ + # Wind data + U0 = nc0.variables['U'] + V0 = nc0.variables['V'] + U1 = nc1.variables['U'] + V1 = nc1.variables['V'] + + if energy=='total': + T0 = nc0.variables['T'] + T1 = nc1.variables['T'] + R = 287.0 # Universal gas constant (J / deg K * kg) + Cp = 1004.0 # Specific heat of dry air at constant pressure (J / deg K * kg) + kappa = (R/Cp) + + xlen = U0.shape[2] + + DKE = [] + for n,t in enumerate(t_idx): + print("Finding DKE at time {0} of {1}.".format(n,len(t))) + DKE_hr = 0 # Sum up all DKE for the 3D space + for i in range(xlen): + if energy=='kinetic': + DKE_hr += N.sum(0.5*((U0[t,:,:,i]-U1[t,:,:,i])**2 + + (V0[t,:,:-1,i]-V1[t,:,:-1,i])**2)) + elif energy=='total': + DKE_hr += N.sum(0.5*((U0[t,:,:,i]-U1[t,:,:,i])**2 + + (V0[t,:,:-1,i]-V1[t,:,:-1,i])**2 + + kappa*(T0[t,:,:,i]-T1[t,:,:,i])**2)) + print("DTE at this time: {0}".format(DKE_hr)) + DKE.append(DKE_hr) + return DKE + + def DE_z(self,nc0,nc1,t_idx,energy,lower,upper): + """ + Computation for difference kinetic energy (DKE). + Sums DKE over all levels between lower and upper, + for each grid point, and returns a 2D array. + + Destaggering is not enabled as it introduces + computational cost that is of miniscule value considering + the magnitudes of output values. + + Method finds levels nearest lower/upper hPa and sums between + them inclusively. + + Inputs: + + nc0 : netCDF file + nc1 : netCDF file + t_idx : times indices to difference + energy : kinetic or total + lower : lowest level, hPa + upper : highest level, hPa + + Outputs: + + data : 2D array. + """ + + # Speed up script by only referencing data, not + # loading it to a variable yet + + # WIND + U0 = nc0.variables['U'] + V0 = nc0.variables['V'] + U1 = nc1.variables['U'] + V1 = nc1.variables['V'] + + # PERT and BASE PRESSURE + P0 = nc0.variables['P'] + PB0 = nc0.variables['PB'] + P1 = nc1.variables['P'] + PB1 = nc1.variables['PB'] + + if energy=='total': + T0 = nc0.variables['T'] + T1 = nc1.variables['T'] + R = 287.0 # Universal gas constant (J / deg K * kg) + Cp = 1004.0 # Specific heat of dry air at constant pressure (J / deg K * kg) + kappa = (R/Cp) + + xlen = U0.shape[2] # 1 less than in V + ylen = V0.shape[3] # 1 less than in U + zlen = U0.shape[1] # identical in U & V + + # Generator for lat/lon points + def latlon(nlats,nlons): + for i in nlats: + for j in nlons: + yield i,j + + gridpts = latlon((xlen,ylen)) + + DKE = [] + for n,t in enumerate(t_idx): + DKE2D = N.zeros(xlen,ylen) + for gridpt in gridpts: + i,j = gridpt + + # Find closest level to 'lower', 'upper' + P_col = P0[t,:,i,j] + PB0[t,:,i,j] + if lower: + low_idx = util.closest(P_col,lower) + else: + low_idx = None + if upper: + upp_idx = util.closest(P_col,upper) + else: + upp_idx = None + + zidx = slice(low_idx,upp_idx+1) + + if energy=='kinetic': + DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,i,j]-U1[t,zidx,i,j])**2 + + (V0[t,zidx,i-1,j]-V1[t,zidx,i-1,j])**2)) + elif energy=='total': + DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,i,j]-U1[t,zidx,i,j])**2 + + (V0[t,zidx,i-1,j]-V1[t,zidx,i-1,j])**2 + + kappa*(T0[t,zidx,i,j]-T1[t,zidx,i,j])**2)) + + DKE.append(DKE2D) + + return DKE + diff --git a/postWRF/postWRF/__init__.pyc b/postWRF/postWRF/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..162c21e296f680276c19be7d871ab745ab89dfa4 GIT binary patch literal 12186 zcmdT~-ESP%b-%MqF2xnUM2eDV$<|1AqqQT^68(`>mJ>-_N^&Dp^pG+WR?K#|GrJsW zcXl~hh%5 zXugPsihjjy)nbh-3VZhcwjY{8Uc=XR*obZ2^vglzz;ga7zb|64r0HaUq{9D zsM2V~$n2X;C8+uNTe!a!R9lgcjBD#w(~ruWg59Xtm@{7Jyp6wl69sMtk*ECwQ?};D zmoC*fomx=#?RYzCw5r>-=f`CmaF}`+mz~U4t+6dXuax}MF?-&UZ7()Ec=%( zZTmrW8&h~nL$M!4ZV=k}eDE{;Eyq;E8`Ww7>!acvl6B|Sy;f}QhxoGX>;79aL34iu zZ)F-w2Y&xaBHuV~8e0nYf~XOeaVLN5<0dZs5fUGo4^>yxnT?F7r)d6T(R}DUpF{c6 z|HJBWR&5L?PjZSSbIKc$lGqSOB!k_6Dx>N#b`#W6bdp=)sJw-i9+UrL%ESM0z}umu?nv@>M8!wc4(1Bkn3mD3Wm&zaIjJzD zK7bA3pwJkKLn0&U8pOX^;9@Svqev@_FfKKtMpM)QdnESTq9$H5h>xNGw`wU4;+n5x zii7VZ!hxDz2>jam)sGxQj)|QrHhODmqOA-5plpYwF;{ z!1}{#XH++**28Iq)qqyszJtmAhNVJFeI0c=Idu~CI#7PoQlL6e-C;Z(QQ-}Fkz*Gu zF`^z0shiOURCBbGk#?S?qVJ%k3=yJ>lBl0oCvnd233Q~qEvD7u6lq0 zkE(87b&sm{Rc`*B5!E@WI!Mty#$DVzuexKZ15G(1)zMF0bw*WZjME|>?f$D;-~9)* zjxwvV)XO3|V;3DRge>JtdI8yG*jCrxb87+2uCbh48JN~U8*?59b^i@Jj$nJMFjMoQ z0VOX$Wff2j^mQb~Mx8b+fMtahhINhHuF_jY9MpfB&YY#=eDhv5_ZoHCn4-lpByOjDDW+SMO3;@6gIJz4BW%64jKIj)3@d5?UpYQ6h_uX!pw-KT zZrwlJ{+VRYvUDY>kFE7G9_g2mC@W`;VJl^=6IRw5w(?d!GnN_0-LN&9nY1P{IqR(T zto4Fb<^(8L4(K*6{S_q8RA^^$bDByV<3?2Mn;Eq`qPkfs5^bq5j7FEU>f2@?4nh0a z=5m3HB2vRTI^5yT3rv222_-BR3~JMC#U;w5z+o7%b1-gH)uIuuq&2C1c+o((Mspxh zXwhA?s5v5qiJ!Jajmx~J$)jNudoqs+S{xxkdoW9~I@7G_}__x#XUO-{8H2Q}@SMy|j?$jqHzWbP^xHsQR% zLRdQiXc)>)o`&#==CK}+a>~LEC7hyDXf2ut$AFH)n&Jt@gG@4* z+jn;N)jBM^Sw{Uch-C$kYP&WlU^R$a9(Wumq2%l`ncgMcqk4rc@Wu=J5}4zqy};jp zab7qfY2|&GzMF<8+HnK*d^h&(s^6$aZgV>*gTRtdrsdPjo4vpEfwT0^{kNQ@V)`I0 zkv`dTK|=T3C~&uGenG#OzG#vblOfo&RD*BAGT!(hh%Bnub^Q3z;QQJh-q-v+zlNuF zzrh94WtvLPiY@DW`Z66D1;D?1D{F7vTfTAk(OU{%x!Au)!WEXHRviCZ-#B#?_cg-g@6+AV59CWUFcpk zV&u&$xZ&iOf3-WmEZ$hmZHXb3w7=W(;WkMBO>awJ2Aro9M=qX{j=|@N;zpyU1yMi? zy;eDPI8KK<-Plwb8m`Pj(Rq^(XS701zO*Q;~--b;oKa?5+ z4T9<>jcdEWM#+A$Rr~=9EG%QWz!?i;4RjEi=zwsaas!4>%$pW(Xf?3`RGuy1hatdH z->f37kVAqdOLcIl^>MDF{C9DgB^k+C|nSq0=aq zMZg(mROI;)p7!X^s3mSAEjcwrruoar7@7wfYlsF|&zyx(lC&6qs#ic9LY4uOK+M3n zW+(*bEdx=224hzyT;ui~kqtXqmc`d|$gaW%N7%{0#eCR2AbbKR<_k3IeQGR9pqn)) zW;+)1Ep{J1m|+k?on_^hMOgIhjcw=bjBTf`ky|)iO6Lj^<6#iB52Y>!c+hf*&HObQ z&{Rj2Ii4BM%vw_!gN*dgyr5Dk`Jo?G52SFJ1&`z6uL$}FFupKc(HCU*3Pps}#&|FO z1Emft7)q(J=q61Ze;`&k)kES`1Ng8=V1NjxWYz1;A<8tQUZ-6&#Ly7ZqY5I2lidEe z7l6UCfZL(N;^yD0?l@n=UG9vlYq%a@JttImk~_0AAry8>@{^K3raDtX8jmrB)35(Y zdAY$+0gSrSf<#Br0gwoGY+CK015_MWof)=+|JLqL;go}QO4oA?^T32df9B!|1!P1e zXdEp(#)`C8?1;Ly`&hjB=$-``j`3#!cEh~JULX5u8I!r%Z zf~_DF1y^srg&mh9?h4d%NKwx?6{51{=s__9K!L`(Y6JaZrYNlIfx&pfh3{CdNzCo>qD*#GDYvPV9Y}X~qXlA5X91x}aP2(t8(( z7Lsirymuij*qf3m*pr7TDpT$kY$3W3^;$IfT2K!JCiXz�%g^;S(@)oOV!a?5Fii z{&4k-+{_HzEiF790M@WUuag*$lu3J{uyk*PCQqrGb(b+|`;u*d(*#%2nqG;v$Mhw+ z0h*2;D-toW3yG^>?)xk5l_G&?3ftrcd46FnY6(=|!V>y}wJMB`h~&#H9pglS^Wn2} zU}*+{^QWvd%^LcZW<*_ZP7y#3n0f-^3q>&X$UXQB1u}Y)Xh^btsGh88?H~tIlhB-_K<{ftEB9b*xNH|Gy_7iwK?4h%qScrhT*BFR- zX-l+Lf-BhWEnhDej-eUn8%!QBVc1Ff^3B$=tk>Yz!&Aa`)casxYY7L>r4Y)(ppjHLA;t z$`$joAV2HoZhgTBl*5Sd%~K^PFTa4(d;wX$!sn= zC+^*tHI|)bFft1|YCV&gu%@ij2v82;?gZSw8LLbo&EleaLB~aZ7kUENge-+Nh^N4t zjBp5GXz~Vx%-rw|9418VvT`(_llnt=0Sq>!c!L-b!cjPZ%8@7-44wr)ATtpf4A`Cn zSs>_7srGONvNOc7xcMK}18|9BWCJ)1U{v%NL-fFP5&$w8pe@)z`^Oe!+!20J-29={ zJuXAb%g_!i5C*I=;#@cyBO5?#awzsKgyfP;=eVk1xO4S-al@Ihz8DVr)bE(9P7=E(&uJn;~vv!it_kp!$|}wN=+PIMt`k`o$hj z{`X)}gxBa+tH3l6EwDMBq8o44vt14QO!$!4QSS_#SZKDej2Py&jPnzK3P=^tbt*RE z=YcM_;J=8K#rsghIJ1Xz&cv6xTg4d?Vy05dV1uc`ZwS~A|DH<0U@)ZsSpjI~V)2%k z`m`bp>F2(WX}eH@^#vF(Q}7au29%_~25EbPOfDg|z%-$8X`z>R43RX7P%h7r41B(9 z;8Z}A;mLgjcDEt1@Is8mLiE%PW1I9eZ7a?EkI6}2Ma2EPFX_9+Vv}gg{ThrD{{C(E zy2<>?cdi>~B?#W4ET9q8E-WnN!{8;Cm!7CNs{$rHNthnOQ;80-b?4uzI9(+#I%qowd-V3-SqJm$25`!DSJlJ#tfJC+w=IBJjocA$L+)JWiR-mm{hd2l4 z(iGzx09_veVL;_{i$~~xm2{IsDR`)+6mO<@@(rv5&;jlMbg-$jKnUZ(^W1C{r2uRI zLfqtWGs_47Z;s*y-zJTz#{kijs)PS1IIAiWZ|R;AaGg`_bA#YIlX(DeMG*9Ka-<}6 z0IS_Is{K=|a|+J)Pps~m>a?T-vA_{5`A5atZAK6#E-PbCw<8h^%PsUj%WIg>P=K_uav)nDCn66gBA z7zdydbSE8WGIT^NlA-q=$*>LjJ~?g-eZ4a$Upt&J;gtUb@Xi5v8G_Q2*dVE0{a*rJ zI?QtVrBlx%b>)l`busOd9<%Sl@K+;zO3}pE12Mu8{E{vWLMJX&--xHRe_p{FfG2Cax_kc`CfL5paY$FuC{bN9~|5kWcmZ|BECT?J&%P zc?yN6rx$I=Bb~nC3#QaIJ`H?Iv9$c*Si+~Z%lQx+!}$&p0k0K-zLlHKZ}ai*AbAQ; z2nd7{-7OPb8A;})c`=aOeu?o``4S0-Zga$l;wIc45M*(NV8=iI=uN;kMD*VpqX9KyZFKG5K}A{WT^(VDkG+{(#9JA~DNg zVgf5S4Le!6BJ5-3<`w6USniLR{0WmkW%6fCeuzXydO)(m6?qRC=WqC!e1V1sU+Fr3 zj=b{`6FS~9da#;>qDagX5@9<(N?xy&Z~`T5Kxrjz!SlCnVU?ZKUv9r z&i9KBHIL`l`V5ksH99rHJB2j+G!VjS3GXfJ{>V26l--((J2{_%uOGjF{X4d8_JcX8{{v6ho UY$*PVs@R^oQK|V}5|1 znW$X}z2r_N^El_sB>LQH?|rj(hTt=Xf zNH2yVtyG?n7b9*VG%V*MKd!S<5_Vxaoo-bIzIK=00R(lbM_xxbP5s<+nu_KiTx671 zb|7p@dkBY#rr3ZXw!{&QEJijjmYyx-NMn!JU_l)GQyxn%%806hRTL77yPSpR-e)|~ z;3W6s3CB|BWF#j*+P2aFDmv#yC6nlK$n2Y%yB=;nnB{58ZRFE*R%VL=pHBHCw%n0= z|18R(QkS6=Ewd5TeX?tXo(;GvN-q2@%M@3<1G@}j*OJzWYuAZ#gfxwUDM2XmDXkkEjNgb*sd@)V)XLm{fP+U$;#ara_&#%WU7 zF9eBy!aFbgj{X+l8Tif_J1s9z+cSISc5ZXN&s+OrX<_rXU&Dl^pBkQb(d-ZCQv4KU zM90^%qCr5%HCYNMt5e>f;|3|-TBEE<`5Yb3`Ei}HdCFUK-11|nTAn1GdQm!U;sEQ+U+l6eM+Y+yb=wX!b*NV?|@42tpu& zA{VgxJXxbLeh})c{z!Z~>@{%8u?P31e-cVm+&yq?XLvB|u^Gpm)mh(hI7wOb&8djb ztA06hA?Klo<~w@>(%#!JG)cI)?<*gfU#j!Yr+%hElR28qbKHX_4dys8SBfSr8Y2w> zJqxHRxiBWU^*T-JRN$f;^!OY2`J-2ly$wF}G@!{MP1LD zE@|JO@j3^u3aO$2c5hPki{dM}LFqE3D@eEE<*9;i(c=cZi zzR4S5gNt)Za(7F5f1TSRZ}A;V8Uh;Z@C`Gfk9upUlS9KLWL~0X3R%DNeh#EQ>F?@rlWdyX?BMn^Zb> zx|@uwEAwvOWV(oRZF{>o<58G7(FM>kjW947e+&{lt$Cr{666z4M3L2QG!%DF;BF}H zwy}PyUF1xzBa>dZ7A78EBzh>0a~^p)Gr2)lqO{CoQ-~!@OT_jp9%^yJCUK^%hImE9F9+0)aPW0PA>^l#phZ$Ng_zHeve5d$xvtSngMI{NlVE&%Fc5wbWcyiiVsBs;gVHOYO*e4W^j1XXWF zrEHyZbnrm(co#zoR@9E#s;|}Bs;OE*Lp5ugwYSx#T2f23n%Y%0^+B+Q)iu=t;jY)# zR5MtsVIO98Rl?NIj;x~z0sjsGXlG)1jd4N10OZ6oWSz;0H$Z4It!`~hW?ami>sRKd zD>XvxfpWpOU|O&Zm=u3aAkyhZkg8{Fr~_ewf4u_GD;U+wOO!4$U4qL%Sy$8ob%Q#= zkW39MF)w+kGn1xgS7%BA8LCjyM*bZcemsl1YIU(X!~alk;C0T;j#BPIqbMDC1{Wd4NV z9$$%o3!sags{~vj7eV+ncY<@SJ)oQAizqd@R9Jc`6PG*@V#TXTbP>TfQ7JN%3PP%p zhNrh*9~?MNNLt0ulQOn0GX=_&6Twp!?qJ9iY+FMOc^$G2V!{v;>CYvY3{f zMV>=^)-d2i=i#Tk`!jTJgokh&J0GLjEp!y@1lwvm;JSWS?F0)7-^N?Yzw&ULQN+v< zMG{98JMV-<#Y9?;2*2E6ePeadvN7f&S*$^K|-xDQ|1xP0WOR@8SF0u@`Udv|RF+ UKK8FT>y|xks}?v5EsxM(gWIFEN|_!mJ&OPrz%C<;D@;I z4g3#3z#F%~5s}ExGvk>zZ=6hiURIC4e`{8Bd?wgmVx^}5OSA+QWKYPJ^p)rf(d`oc zgckLwq`d>EH?B3?j#YBZKCF}CjbUX*r?$yiwcEIWQ1(V;qf078WDjoT+r6ClQN>-D zR~4Uw??{*ah$;|#CEXqZ97I6@iJWCg&7zj$m4GZ6;5Amd1wi^|B2DD;0FZr+QPwCg zVV#nlVml*yPS=u$_$(KcWUFjUep8_SB1cd}82%pEZ@e^2m4FH9S#qOqc?!2Rz3=wv z0nGRQ&W(nB3~GU-d>rD~n=YF>=hqwd_mRmbLgYU=kDdGv2}uKA3uVr>u{S>O41OB} z>tT#TKEOS*`^Yl99XwxPAG*kzJ$Ei;|IQ@Wn?a8*W(>twM?ndiA+HD87xrF<--M@M^ m$fo?4l`y8o&7o8rJ_BfN;|i=W|ARFPe14GhLKLUWUj734RGmx! literal 0 HcmV?d00001 diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py new file mode 100644 index 0000000..39d3dd3 --- /dev/null +++ b/postWRF/postWRF/figure.py @@ -0,0 +1,56 @@ +"""Collection of x-y cross-section classes. + +""" + +# Imports +import numpy as N +import matplotlib as M +import matplotlib.pyplot as plt +from mpl_toolkits.basemap import Basemap +import pdb +import os + +# Custom imports +import utils + +class Figure: + def __init__(self,config,wrfout): + pass + + def create_fname(self,naming): + """Default naming should be: + Variable + time + level + """ + fname = '_'.join([str(a) for a in naming]) + return fname + + def title_time(self): + self.T = utils.padded_times(self.timeseq) + pdb.set_trace() + + def figsize(self,defwidth,defheight,fig): + width = getattr(self.C,'width',defwidth) + height = getattr(self.C,'height',defheight) + fig.set_size_inches(width,height) + return fig + + def save(self,fig,p2p,fname): + utils.trycreate(p2p) + fpath = os.path.join(p2p,fname) + #self.fig.savefig(fpath) + plt.gcf().savefig(fpath,bbox_inches='tight') + + def get_limited_domain(self,da): + if da: # Limited domain area + N_idx = self.W.get_lat_idx(da['N']) + E_idx = self.W.get_lon_idx(da['E']) + S_idx = self.W.get_lat_idx(da['S']) + W_idx = self.W.get_lon_idx(da['W']) + + lat_sl = slice(S_idx,N_idx) + lon_sl = slice(W_idx,E_idx) + else: + lat_sl = slice(None) + lon_sl = slice(None) + + return lat_sl, lon_sl diff --git a/postWRF/postWRF/figure.pyc b/postWRF/postWRF/figure.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0390d492905e03af5feb045b44a32a0e1f08d420 GIT binary patch literal 2290 zcma)7-EQMl5T0Y_XOpIMDHMS~A_5^ImhDR1AR)A%?e5-@3m35>5?GFXk~%nXHs@rw zMXk6fFTx}66o}{H0pR=QBx!Gun)Zz6jE`sL`(`|j|Jm>U{nwvKE}LJB>$h0$9HI~z zK^iF{sf>)Gtscv$vDKS0Yssjs+gPf5GVjQ!BWCkhS4Q3K(LEXMZS|gvdQ!kkONyra zE^;mM<7l6uP6usf@EOEOT~#*sWxeRvlm7X`i+;YUoqOmu#=Od$v+lUtE&s)DI52p~ z^YJH{v-51}ThO1E)Ah>kF!Ib3EcY0~0wVwlQwRX5#LR;V5{y-8GFe)-`2uosI-vWU zq;b*l36^U>6apZ$3c=M}0wYm<&JrLK($#Se_^!P9tLpKlN=oia`ku}_eZtlVn zL(=Idys|;W?3<%V=K-@Nq7y9l2LyU#^_#X#coDR5-s21aLoneViq3nrVlo7)wihO$ zT0!XTvyEVF5(OI!S_MtrPzTPX`_@(~~91ehuaESJfWp z_xs$*&u-NM=#2^dUBRjMEC2zpfzkqDLu5hAh7B7mT00Oquu%X}fSU*^f+(_5f)JMj zpaA3mh!UI^r+V777=-i60$G6OArLN{CB)%<+u*)!2zQ<4T|;niS06Eqh%<~B{ujdC zk1>LXQ4nJw#zEWUsiEv3DNili4LvGG-`QHhdt(@#Ae_>n|!l6vhZF-FB<#dTj^ zy>e}R&2_uJ$Kz>~T|ZU+CI&Hyw<$BU6mHb*Ln7Ig(SQ8zE7#VuU_q;or1Lwe*YewL z5Op;<=nT3a**TDQ-OC*C8E<)MX~|QuP=Rmowz`ux{6OMGpO+246B$oSzPk4ORCUPPOQWY6*5(oO2yMjOKv6G zuKF8C8JPrg1jS_)OFvzcVN1q%&PAdU6Il&xbe3>BI4|PVU!d5z1O_)+unRD~Yr*t; z*#8#1ZbNzwj15iapz{NOJsaZTgEll=kI#T*Sv#{}3V=Z{#$JD)1Ji_g;~m1_Tf+tF zH)0EJ>`dj6uqUQ>ZQfbHc#jd*6rovyCiYZ;9YrvQV7J%<(f(aL&W47K{ZzzcRu$!j zuM;yWpa?K;L#C!QtqUwy4RPY9ejG|Oj%ou7StkmmHg&Ev?`lT|>7bZ+ z2>=AwDSQoeC{qabc76Vdy4m^IJ7xfV6s{h1YkUU+3)!d#ri>b}(Da|FQdw*#+FY+) zTFm;s(%#|QWrtC5wTA)U}*P2pghl-Sz_fn_Qq_5nc0s7P~6u1VeOv?XI- zcr%b~qmsBbjIc80TpOZYbVk%=;Qt`HM)1=(^$ZHIM(iP^6({}cFE?#Zqs9@ zTd4&ScX4lxA0RivmNdT1QdF&aDjN#FGt|4-JxQxg7A|W$m4Rx9Z%=&s;`eXetcJH| z-~Z+6?T@SBM<3GoBvX`B~?-pKZ=M@ z+#OUWMP*Z?BUA@Q;aa|- zZ%d>$EBxKj{H6_Iv+sbR?~2Y|zp2qpxmjwIQH{EVu~^ke2BsbdBJ1z0yeGMPiJ9eo y7qWKmH%|neZzZA=$!F01%r7h*Ag{sI1;-qaFfk73brY%BoD_d=$QX6e{CdoARuCp^v zYbAaF`pA2K2M_SZf8Ymz#DCxiz<18B)2b3`BhT#FGjnd=xonF6%*~ws?UznO`Ckdo zFR|P$Y#KjBJ)-*{3iK3&B3%@sK)N*fmFa1b?iWc{n!gJ*b?X2cjrUz zr$O|?eWPvasgw0CM*_*H?$$_FQ8y^6aBeM^S1*Zz_0ktc_Gr zEntb?tc-1m-yee5CHAiM;53bakD^C-R~*5^0v$}z7|s{qF}vAzQ`nEznrrKNocYE^ zc9OIo`Yam0;BDVHbKp5h@A<|c)Y|CR2%vUyc}ukLyJL4reV{BqqcS#*iY-YHU%?I{ zfb07OO5fvTXEFRtG%iydC_XL9=|c|2UFF>0xT(t=U5vvD9hdn8QJ+QnPsGo%{NJK| zTI7GH;}W?M=_-v&EQ(jqZ1@@Jndg!aJh-a(bXHD(C0%>@RL{YQ_y|EJhc*t}z(jV(8vS`NbT;0d@XudtSSb;%+DvjwuenvWo9SH1ZLH)r zw_ft6d2~g55Zf?37=N+!a2EOK$Pm0iq2dC5wC(R~C}UE_l&i4RTe zEigdE2O2HL1WLadv^E2o6%u$K(eAap_B#YS1lH%PtI!Uj9M?0RVABhJe}Ad83><( zurjqV3O3OB==Ke?1=@TA-Hoxnfu2RTZlF2QmKzv+Xdg_8bKUwJgxeYDjN!ym&UfPnWHTNr}q@N|iXU^gFoMCo~kK;FyQoM9~Zoke6iq}Lgj39BkvzGsZO zz^Gf7IoyJo;M$&v_5+`wkWKJ-FZ6*Qp}Lcupl1$EFR(61&B4$(Z*;IdLd%=9F%#r} zLP+S}`WdLR4jq*y0~^^tftP&ztV^~5dSw4V5qd5}H%xcJ$Oz`Q@7{4NdHe3lgv~hY z_obe9P0}}LhNzu=p17(_tleYl_c9J&?j61wcX3Zb+Ts8|gP2>!Mx}XGLNR8G>3J2g zQZ5880m(=Jg1`JqcKQCJ!rnw~@SA*6Ifw2UHbLc^7~{(|g+>z#U`_0drcD7Rm_sAS zVVYiQom>D3MmXXe$Q>t_WVEEV+boR0>bseU= zC~e6?xb3o2E^={4*f1|oSa}VEIkmazn;^3SpM6Kw`JjVE9R{?+--iwlbm@i83R65S ko|_QrfB!3*#@#Rai5~XMRl%IAxmK=Ls-?vX)lzl-KLjKTPyhe` literal 0 HcmV?d00001 diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py new file mode 100644 index 0000000..b3b064b --- /dev/null +++ b/postWRF/postWRF/wrfout.py @@ -0,0 +1,259 @@ +"""Compute or load data from netCDF file. + +Dimensions of 4D variable X are X.dimensions: +(Time,bottom_top,south_north,west_east_stag) +Time, levels, latitude, longitude +""" + +from netCDF4 import Dataset +import sys +import os +import numpy as N +import calendar +import pdb + +#sys.path.append('/home/jrlawson/gitprojects/meteogeneral/') +#from meteogeneral.WRF import wrf_tools + +class WRFOut: + + def __init__(self,fpath): + #self.C = config + self.nc = Dataset(fpath,'r') + + self.wrf_times = self.nc.variables['Times'][:] + self.dx = self.nc.DX + self.dy = self.nc.DY + #self.lvs = + self.lats = self.nc.variables['XLAT'][0,...] # Might fail if only one time? + self.lons = self.nc.variables['XLONG'][0,...] + + self.cen_lat = float(self.nc.CEN_LAT) + self.cen_lon = float(self.nc.CEN_LON) + self.truelat1 = float(self.nc.TRUELAT1) + self.truelat2 = float(self.nc.TRUELAT2) + self.x_dim = len(self.nc.dimensions['west_east']) + self.y_dim = len(self.nc.dimensions['south_north']) + + def get_time_idx(self,t,tuple_format=1): + + """ + Input: + + t : time, tuple format by default + (otherwise t is in datenum) + + Output: + + time_idx : index of time in WRF file + """ + if tuple_format: + t_epoch = calendar.timegm(t) + else: + t_epoch = t + nt = self.wrf_times.shape[0] + self.wrf_times_epoch = N.zeros([nt,1]) + t = self.wrf_times # For brevity + + for i in range(nt): + yr = int(''.join(t[i,0:4])) + mth = int(''.join(t[i,5:7])) + day = int(''.join(t[i,8:10])) + hr = int(''.join(t[i,11:13])) + mins = int(''.join(t[i,14:16])) + sec = int(''.join(t[i,17:19])) + self.wrf_times_epoch[i] = calendar.timegm([yr,mth,day,hr,mins,sec]) + + # Now find closest WRF time + self.time_idx = N.where( + abs(self.wrf_times_epoch-t_epoch) == + abs(self.wrf_times_epoch-t_epoch).min() + )[0][0] + return self.time_idx + + def get(self,var,PS): + """ Fetch a numpy array containing variable data. + + Slice according to arguments. + + Destagger if required. + + Returns unstaggered, sliced data. + + var : netCDF variable name + PS : Plot Settings, keys as follows: + + t : time index + lv : level index + la : latitude slice indices + lo : longitude slice indices + + """ + # Check if computing required + # When data is loaded from nc, it is destaggered + if var=='pressure': + if lv_idx == 0: + data = self.load('PSFC',PS) + elif var=='sim_ref': + data = self.compute_comp_ref(PS) + elif var=='shear': + data = self.compute_shear(0,3,PS) + elif var=='wind': + u = self.load('U',PS) + v = self.load('V',PS) + data = N.sqrt(u**2 + v**2) + else: + data = self.load(var,PS) + + return data + + def load(self,var,PS): + + # First, check dimension that is staggered (if any) + PS['destag_dim'] = self.check_destagger(var) + + # Next, fetch dimension names + PS['dim_names'] = self.get_dims(var) + + d = self.nc.variables[var] + sl = self.create_slice(PS) + data = self.destagger(d[sl],PS['destag_dim']) + return data + + def create_slice(self,PS): + # See which dimensions are present in netCDF file variable + sl = [] + if any('Time' in p for p in PS['dim_names']): + sl.append(slice(PS['t'],PS['t']+1)) + if any('bottom' in p for p in PS['dim_names']): + sl.append(slice(PS['lv'],PS['lv']+1)) + if any('north' and 'west' in p for p in PS['dim_names']): + sl.append(PS['la']) + sl.append(PS['lo']) + + return sl + + def check_destagger(self,var): + """ Looks up dimensions of netCDF file without loading data. + + Returns dimension number that requires destaggering + """ + stag_dim = None + for n,dname in enumerate(self.nc.variables[var].dimensions): + if 'stag' in dname: + stag_dim = n + + return stag_dim + + def get_dims(self,var): + dims = self.nc.variables[var].dimensions + return dims + + def destagger(self,data,ax): + """ Destagger data which needs it doing. + + data : numpy array of data requiring destaggering + ax : axis requiring destaggering + + Theta always has unstaggered points in all three spatial dimensions (axes=1,2,3). + + Data should be 4D but just the slice required to reduce unnecessary computation time. + """ + if ax==None: + return data + else: + nd = data.ndim + sl0 = [] # Slices to take place on staggered axis + sl1 = [] + + for n in range(nd): + if n is not ax: + sl0.append(slice(None)) + sl1.append(slice(None)) + else: + sl0.append(slice(None,-1)) + sl1.append(slice(1,None)) + + data_unstag = 0.5*(data[sl0] + data[sl1]) + return data_unstag + + def compute_shear(self,lower,upper): + pass + return shear + + + + def compute_comp_ref(self,PS): + """Amend this so variables obtain at start fetch only correct date, lats, lons + All levels need to be fetched as it's composite reflectivity + """ + T2 = self.get('T2',PS) + QR = self.nc.variables['QRAIN'][PS['t'],:,PS['la'],PS['lo']] + PSFC = self.get('PSFC',PS) + try: + QS = self.nc.variables['QRAIN'][PS['t'],:,PS['la'],PS['lo']] + except: + QS = N.zeros(N.shape(QR)) + rhor = 1000.0 + rhos = 100.0 + rhog = 400.0 + rhoi = 917.0 + + no_rain = 8.0E6 + # How do I access this time? + no_snow = 2.0E6 * N.exp(-0.12*(T2-273.15)) + no_grau = 4.0E6 + + density = N.divide(PSFC,(287.0 * T2)) + Qra_all = QR + Qsn_all = QS + + for j in range(len(Qra_all[1,:,1])): + curcol_r = [] + curcol_s = [] + for i in range(len(Qra_all[1,1,:])): + maxrval = N.max(Qra_all[:,j,i]) + maxsval = N.max(Qsn_all[:,j,i]) + curcol_r.append(maxrval) + curcol_s.append(maxsval) + N_curcol_r = N.array(curcol_r) + N_curcol_s = N.array(curcol_s) + if j == 0: + Qra = N_curcol_r + Qsn = N_curcol_s + else: + Qra = N.row_stack((Qra, N_curcol_r)) + Qsn = N.row_stack((Qsn, N_curcol_s)) + + # Calculate slope factor lambda + lambr = (N.divide((3.14159 * no_rain * rhor), N.multiply(density, Qra))) ** 0.25 + lambs = N.exp(-0.0536 * (T2 - 273.15)) + + # Calculate equivalent reflectivity factor + Zer = (720.0 * no_rain * (lambr ** -7.0)) * 1E18 + Zes = (0.224 * 720.0 * no_snow * (lambr ** -7.0) * (rhos/rhoi) ** 2) * 1E18 + Zes_int = N.divide((lambs * Qsn * density), no_snow) + Zes = ((0.224 * 720 * 1E18) / (3.14159 * rhor) ** 2) * Zes_int ** 2 + + Ze = N.add(Zer, Zes) + dBZ = N.nan_to_num(10*N.log10(Ze)) + return dBZ + + def compute_simref_atlevel(self,level=1): + pass + return data + + def get_XY(self,lat,lon): + """Return grid indices for lat/lon pair. + """ + + def get_lat_idx(self,lat): + lat_idx = N.where(abs(self.lats-lat) == abs(self.lats-lat).min())[0][0] + return lat_idx + + def get_lon_idx(self,lon): + lon_idx = N.where(abs(self.lons-lon) == abs(self.lons-lon).min())[0][0] + return lon_idx + + + diff --git a/postWRF/postWRF/wrfout.pyc b/postWRF/postWRF/wrfout.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d036af4015db774bf5d0ab7570b3d37c2823ee1 GIT binary patch literal 8855 zcmcgxO>7*=b*`Qvha751O5~6fC0W}tb~m#&!(GXaVF>cBWOCQuU@eg62vf!1c;sZkQ{t4eDF278on4dY`})$ zYA$RSFc{ZdhdI$s*Asvomu+!KR69F{8z;9bv!ym;VZR`(o%jw zwJp^tsO^HX*ecD)o+1Z*G^# zYpXWJDZh?lBkpv2X<)~R-Htuq_Px}zcaykdM?t!=`HH<8wuAMVna!{hL^_NkZO6Oz zhd1p9UJ`mc?ZCckdkM;QKc~1lQ@f4E+D@FNamP*L?wXE!Y0Hh`ByFu71UhvC4~0&> z=5sSbYqx_3L0hlcZ7&Veo*$qVM@^~Dg#V3yH4EaV6nqmh>L9JBB{c3hue{z%bA1-j z^>IA<%P0cq6UtLuJv3<)`_K|a7NryFnUKolP)XEfNqV5jDXBn<(^7%%%2Jt8{)|*k z3C~lCJS)ntNaw8bXQeWy{5h%2D`%@V4}lsKj}Nzf1CRbKid3m1h-s;J3hGEH*J8P6 zsXd5XP^%Z&SIqh*gk&;nmZUkAH8G;7Rw3pp2ThN4m(V?9y2~n^$=;k&@5pp+Wvp{H z>zo_woXk9D5OIu}&BnDMMqbE`wXOYeR$oxfP-#%$G2K>_uYdz++_C}|Bkoj~pG zYd3DwE7ZEYxcl1c^-rW_v~vSWIe5&pYE-QmGG1LluY*UWd>=K0NoK#=uuOK{RAwg0 z=3Sxm59Md`P1EX&RIpDY9?VmVV7qXfR19+8>VW1Yf>o>KtlD5?ksD;1h}LG1~otqvv>gx!Nx!yyZ*S;)*v z%o}tAOC6rGpg<6S(dNC1HJDTo73RKICd(suFq~*YeXgK(;WeP*jHO42Kvz0OH1!A( zoPYWl(exukz=N4%L~yNRG65ng$B1SgAp&H~9V0rGX$~sDvf;{DCfxRvYLESTF3{X?>(EMepahm2DciacT963B8^0uo}ouuotH@g^kvgbXkt z?Kq^Dg+!Qemc<;4(=5)gSVU1{pZH6|gJ1eyA|Z*hHaiZ-i|6T<*9`>X>eBv9kihAv zYB-&N9XII4jh4Jkyr>z7tA|l4F1#0`M&S;iEAz_R(L&O}8w$%Qseu~3(7DJS3709` zvMH|F6u}^bUztweFcr#BFs`2%u4zkteXlRwtwiL&WM)VTkAE2&()T6%#38!^sQu5TFI?KN`j! zzyV+faTHLq|05OsozPDROB7P=6nLX1@}ojh5}%@PTe0 zxmDuz?FIsd2SkpVqr8im+Inu#d1b2|HUisgG~&c3eHw#Dvxn?0)sG?HM6mUm%^zGtZFiB#wbpJ49QD0|ij5gpwZ&tgLsK_ zXcM}{|Kw$A4620?e1pTn@CTXG3iJgU3zHF9<pX>Q;uTi_g;6p!!^a)H^jT*@m3f4JyGE{%^$m z>~S$YBW-M^<=kG>7p(KTU8GbZiMW?g2yTflIk!Q30yR0eK=-Gdb^(P#)^QHmhI!6d z)q*$?9f)&p;L-F;xD1@=@ETo~K$8OxAO}XedGC^XOOgznT&P227$ue1#2<{?xB35$ zm@(PvFQBkri{twUqFoy}GIp0TcF$rTglP+>Fu96h@P0y!&jNDJh{?hZVtCp@+C7Zo z8p-!CUW3&rIEgtRj@N5kwd`*_h7h!OCf-2;L>s}JLx&SmqkDpxAR>7SiqZj0e(DoQ zPqHVkW5^frXgax?ELqPYP|TXWpu*^BvKKtMQ~y~wSmjwzQn)R-52_ohm?Uac9FOK8Wi6U4l>Bp${bw|=wLm;(o~P+RTX+Dfh)P7?2`MMlVNYPT zXe_45!|0KumiNml>Ck9!{KW=<6Ko3qErhD$UHezwMF?XXqV$MXWEu8?{0@^~u}Ok6 z$W~XX0~{fz)D~vilD*Vj?3z7F{%C0SK`TV&83lolgIH?&ICeB2liLX`(jv+2#*$Q= zx8#+vKQZ6O8K5V}G&9^g#F_8+(C3zWyA?o4uYKSlFKi((9Lq=TF3(7Ee)QUH_(2i` zw#JP|=(WfFt>zsD`eT>ZuB?6NxyLMRlWNd#!M1Pj1ia?lfj92;bPASWD7kf(B{Q`~ zvfM+u-HW0CX`}WM=IXpr#msRe=g-kJa*ZiL16IO+|Mik_LT3jJ@kFGb9fx-6xGX-6 zq9(@8Bb{Unf|s7TnF2ftytH2!{$6$h(8;wJx570VI*Hx$e(W#tm;`R7VoemvR>>;i zbXI{EEEkrob9f|y1A;F79I&} z%7DWqw5bo7`aA?N;9$ZST!7dhSOJ`1+5-n*jsv^Lf7}eQ4Ts_a9Tj>jFb!N@Jj^s(YUdp zHKUk+62+1_I-~Y*SyeTtw;+urY3z5bJ6valG>~_&EbP!+kmicQZ2qEP?_A+%Sq;vr zBV;tIYX6T!_fQCWmU#)t;kbbWH*~I&Y}hHpG7a{j%6qjckhw}qXi_*3Phmq#YH@H* zSYTV0$G2s0UilD)L(eJytXi!4tJtdx%D>24y1bXScV)HYpU)`XC+yIBo_)Y1Ed_9@{4RhU;Wh^M$-8q5HBhz*aVv*^DNvkaz?YmI-YVG084%2q)ZUe=hd62z#gZgwAP68CkdHF_G=m%O9F1=PX3aMn zi5LV!Af|A@wE)mLO#eU&7{)rp2PPbN+h7crYdHdmuH=5tM__?3n`p&<6x4FxI3;P*O z-O#9??Ozu#4l#vlMxQ_bvtM5-XRLqtQgiDsE`6o9|IP23S>OJA_Ugj-n(9C5&Cb7m zbEDb+$+MTI|NaLr$vHjzA@UZvHf%ok)>r@Smo@+TsG@$Sw(cKY+N?1RbzWm3z|OQw z4(JYV#+;{7zzmU!9d;!R_MtI9FfoCLYkAr64kbW{;^c}*(zhf&;5W6#eM!(dxOfe_ z?Y@lgd|yt#kr&~EoQphf2+PgOFNmRxW!7Cz!xZe6BlqzR1BYU!pMlWf^Z8sTjXy-$e zaS8=gkv#CsVAM71a&&a_f$wUX8%#_oYPqFN=Gyi;J0?7_W@xq%u(%W3$YMAXe(_Z# z6>V&rYgqr|+b4-+n;891c(iSp3;5OddBBU^^}=M7 zFb&U~`##4{5?s-6 z$Zyabo>o5gm@A?Gm!+MC49A!D*XT1#i=+3vG&AIP<4db?5&;2MF6>-a{M>arvEOS4 zuItz!aIUftc->%)r+SdK}gi$x_;b%@Prg|_W!WJf*7^#Yrz*_ rD~(gW0hs9UevYTncKsch`=o-3%B~qDy-07+b0|u= 1.6.1", - "meteogeneral >= 0.1.0" ], ) From 55c2ac2d68273f0994e31841f2c0ba270d033f32 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 19 Dec 2013 10:06:37 +0000 Subject: [PATCH 024/111] Fixed .gitignore --- postWRF/PyWRFPlus/__init__.py | 390 ---------------- postWRF/PyWRFPlus/axes.py | 10 - postWRF/PyWRFPlus/birdseye.py | 93 ---- postWRF/PyWRFPlus/colourtables.py | 735 ------------------------------ postWRF/PyWRFPlus/defaults.py | 13 - postWRF/PyWRFPlus/figure.py | 56 --- postWRF/PyWRFPlus/map.py | 0 postWRF/PyWRFPlus/params.py | 0 postWRF/PyWRFPlus/scales.py | 107 ----- postWRF/PyWRFPlus/skewt.py | 0 postWRF/PyWRFPlus/ts.py | 10 - postWRF/PyWRFPlus/utils.py | 85 ---- postWRF/PyWRFPlus/wrfout.py | 259 ----------- postWRF/PyWRFPlus/xsection.py | 22 - postWRF/README.txt | 38 -- postWRF/bin/.gitignore | 4 + postWRF/bin/DKE.py | 18 - postWRF/bin/DKE_settings.py | 14 - postWRF/bin/casestudyexample.py | 60 --- 19 files changed, 4 insertions(+), 1910 deletions(-) delete mode 100644 postWRF/PyWRFPlus/__init__.py delete mode 100644 postWRF/PyWRFPlus/axes.py delete mode 100644 postWRF/PyWRFPlus/birdseye.py delete mode 100644 postWRF/PyWRFPlus/colourtables.py delete mode 100644 postWRF/PyWRFPlus/defaults.py delete mode 100644 postWRF/PyWRFPlus/figure.py delete mode 100644 postWRF/PyWRFPlus/map.py delete mode 100644 postWRF/PyWRFPlus/params.py delete mode 100644 postWRF/PyWRFPlus/scales.py delete mode 100644 postWRF/PyWRFPlus/skewt.py delete mode 100644 postWRF/PyWRFPlus/ts.py delete mode 100644 postWRF/PyWRFPlus/utils.py delete mode 100644 postWRF/PyWRFPlus/wrfout.py delete mode 100644 postWRF/PyWRFPlus/xsection.py delete mode 100644 postWRF/README.txt create mode 100644 postWRF/bin/.gitignore delete mode 100644 postWRF/bin/DKE.py delete mode 100644 postWRF/bin/DKE_settings.py delete mode 100644 postWRF/bin/casestudyexample.py diff --git a/postWRF/PyWRFPlus/__init__.py b/postWRF/PyWRFPlus/__init__.py deleted file mode 100644 index f3998fe..0000000 --- a/postWRF/PyWRFPlus/__init__.py +++ /dev/null @@ -1,390 +0,0 @@ -"""Take config settings and run plotting scripts. - -Classes: -C = Config = configuration settings set by user, passed from user script -D = Defaults = used when user does not specify a non-essential item -W = Wrfout = wrfout file -F = Figure = a superclass of figures - mp = Birdseye = a lat--lon slice through data with basemap - xs = CrossSection = distance--height slice through data with terrain - -""" - -import os -import matplotlib as M -M.use('Agg') -import matplotlib.pyplot as plt -import collections -import fnmatch -import pdb - -from wrfout import WRFOut -from axes import Axes -from figure import Figure -from birdseye import BirdsEye -import scales -from defaults import Defaults -import utils - -class PyWRFEnv: - def __init__(self,config): - self.C = config - - # Set defaults if they don't appear in user's settings - self.D = Defaults() - - self.font_prop = getattr(self.C,'font_prop',self.D.font_prop) - self.usetex = getattr(self.C,'usetex',self.D.usetex) - self.dpi = getattr(self.C,'dpi',self.D.dpi) - self.plot_titles = getattr(self.C,'plot_titles',self.D.plot_titles) - - # Set some general settings - M.rc('text',usetex=self.usetex) - M.rc('font',**self.font_prop) - M.rcParams['savefig.dpi'] = self.dpi - - def wrfout_files_in(self,folder,dom='notset',init_time='notset'): - w = 'wrfout' # Assume the prefix - if init_time=='notset': - suffix = '*' - # We assume the user has wrfout files in different folders for different times - else: - try: - it = self.string_from_time('wrfout',init_time) - except: - print("Not a valid wrfout initialisation time; try again.") - raise Error - suffix = '*' + it - - if dom=='notset': - # Presume all domains are desired. - prefix = w + '_d' - elif (dom == 0) | (dom > 8): - print("Domain is out of range. Choose number between 1 and 8 inclusive.") - raise IndexError - else: - dom = 'd{0:02d}'.format(dom) - prefix = w + '_' + dom - - wrfouts = [] - for root,dirs,files in os.walk(folder): - for fname in fnmatch.filter(files,prefix+suffix): - wrfouts.append(os.path.join(root, fname)) - return wrfouts - - def dir_from_naming(self,*args): - l = [str(a) for a in args] - path = os.path.join(self.C.output_root,*l) - return path - - def string_from_time(self,usage,t,dom=0,strlen=0,conven=0): - str = utils.string_from_time(usage=usage,t=t,dom=dom,strlen=strlen,conven=conven) - return str - - def plot_variable2D(self,va,pt,en,lv,p2p,na=0,da=0): - """Plot a longitude--latitude cross-section (bird's-eye-view). - Use Basemap to create geographical data - - ======== - REQUIRED - ======== - - va = variable(s) - pt = plot time(s) - nc = ensemble member(s) - lv = level(s) - p2p = path to plots - - ======== - OPTIONAL - ======== - - da = smaller domain area(s), needs dictionary || DEFAULT = 0 - na = naming scheme for plot files || DEFAULT = get what you're given - - """ - va = self.get_sequence(va) - pt = self.get_sequence(pt,SoS=1) - en = self.get_sequence(en) - lv = self.get_sequence(lv) - da = self.get_sequence(da) - - perms = self.make_iterator(va,pt,en,lv,da) - - # Find some way of looping over wrfout files first, avoiding need - # to create new W instances - # print("Beginning plotting of {0} figures.".format(len(list(perms)))) - #pdb.set_trace() - - for x in perms: - va,pt,en,lv,da = x - W = WRFOut(en) # wrfout file class using path - F = BirdsEye(self.C,W,p2p) # 2D figure class - F.plot2D(va,pt,en,lv,da,na) # Plot/save figure - pt_s = utils.string_from_time('title',pt) - print("Plotting from file {0}: \n variable = {1}" - " time = {2}, level = {3}, area = {4}.".format(en,va,pt_s,lv,da)) - - def make_iterator(self,va,pt,en,lv,da): - for v in va: - for p in pt: - for e in en: - for l in lv: - for d in da: - yield v,p,e,l,d - - def get_sequence(self,x,SoS=0): - """ Returns a sequence (tuple or list) for iteration. - - SoS = 1 enables the check for a sequence of sequences (list of dates) - """ - - - if SoS: - y = x[0] - else: - y = x - - if isinstance(y, collections.Sequence) and not isinstance(y, basestring): - return x - else: - return [x] - - - def plot_cross_section(self,var,latA,lonA,latB,lonB): - xs = CrossSection() - xs.plot(var,latA,lonA,latB,lonB) - - def save_data(self,data,folder,fname) - # Ensure file suffix will be .npy - os.path.splitext(fname)[0] - # Check for folder and create if necessary - utils.trycreate(folder) - # Save in binary - fpath = os.path.join(folder,fname) - N.save(fpath,data) - - print("Save file {0}.npy to {1}.".format(fname,folder)) - - def compute_diff_energy( - self,ptype,energy,files,times,upper=None,lower=None, - d_save=1,d_return=1,d_fname='diff_energy_data'): - """ - This method computes difference kinetic energy (DKE) - or different total energy (DTE, including temp) - between WRFout files for a given depth of the - atmosphere, at given time intervals - - Inputs: - - ptype : 'sum_z' or 'sum_xyz' - energy : 'kinetic' or 'total' - upper : upper limit of vertical integration - lower : lower limit of vertical integration - files : abs paths to all wrfout files - times : times for computations - tuple format - d_save : save dictionary to folder (path to folder) - d_return: return dictionary (True or False) - d_fname : custom filename - - Outputs: - - data : time series or list of 2D arrays - - ptype 'sum_z' integrates vertically between lower and - upper hPa and creates a time series. - - ptype 'sum_xyz' integrates over the 3D space (again between - the upper and lower bounds) and creates 2D arrays. - """ - if d_save = 1: - d_save = os.environ['HOME'] - - # First, save or output? Can't be neither! - if not d_save and not d_return: - print("Pick save or output, otherwise it's a waste of computer" - "power") - raise Exception - - # Look up the method to use depending on type of plot - PLOTS = {'sum_z':self.DE_z, 'sum_xyz':self.DE_xyz} - - # Creates sequence of times - ts = self.get_sequence(times) - - # Dictionary of data - DATA = {} - - # Get all permutations of files - for perm in itertools.combinations(files,2): - DATA[perm] = {} - f1, f2 = perm - W1 = WRFOut(f1) - W2 = WRFOut(f2) - - # Make sure times are the same in both files - if not W1.wrf_times == W2.wrf_times: - print("Times are not identical between input files.") - raise Exception - - # Find indices of each time - t_idx = [] - for t in ts: - t_idx.append(W1.get_time_idx(t)) - - DATA[perm]['times'] = ts - DATA[perm]['values'] = PLOTS[ptype](nc0,nc1,t_idx, - energy,lower,upper) - - if d_return and not d_save: - return DATA - elif d_save and not d_return: - self.save(DATA,d_save,d_fname) - elif d_return and d_save: - self.save(DATA,d_save,d_fname) - return DATA - - def DE_xyz(self,nc0,nc1,t_idx,energy,*args): - """ - Computation for difference kinetic energy (DKE). - Sums DKE over the 3D space, returns a time series. - - Destaggering is not enabled as it introduces - computational cost that is of miniscule value considering - the magnitudes of output values. - - Inputs: - - nc0 : netCDF file - nc1 : netCDF file - t_idx : times indices to difference - energy : kinetic or total - *args : to catch lower/upper boundary which isn't relevant here - - Outputs: - - data : time series. - """ - # Wind data - U0 = nc0.variables['U'] - V0 = nc0.variables['V'] - U1 = nc1.variables['U'] - V1 = nc1.variables['V'] - - if energy=='total': - T0 = nc0.variables['T'] - T1 = nc1.variables['T'] - R = 287.0 # Universal gas constant (J / deg K * kg) - Cp = 1004.0 # Specific heat of dry air at constant pressure (J / deg K * kg) - kappa = (R/Cp) - - xlen = U0.shape[2] - - DKE = [] - for n,t in enumerate(t_idx): - print("Finding DKE at time {0} of {1}.".format(n,len(t))) - DKE_hr = 0 # Sum up all DKE for the 3D space - for i in range(xlen): - if energy=='kinetic': - DKE_hr += N.sum(0.5*((U0[t,:,:,i]-U1[t,:,:,i])**2 + - (V0[t,:,:-1,i]-V1[t,:,:-1,i])**2)) - elif energy=='total': - DKE_hr += N.sum(0.5*((U0[t,:,:,i]-U1[t,:,:,i])**2 + - (V0[t,:,:-1,i]-V1[t,:,:-1,i])**2 + - kappa*(T0[t,:,:,i]-T1[t,:,:,i])**2)) - print("DTE at this time: {0}".format(DKE_hr) - DKE.append(DKE_hr) - return DKE - - def DE_z(self,nc0,nc1,t_idx,energy,lower,upper): - """ - Computation for difference kinetic energy (DKE). - Sums DKE over all levels between lower and upper, - for each grid point, and returns a 2D array. - - Destaggering is not enabled as it introduces - computational cost that is of miniscule value considering - the magnitudes of output values. - - Method finds levels nearest lower/upper hPa and sums between - them inclusively. - - Inputs: - - nc0 : netCDF file - nc1 : netCDF file - t_idx : times indices to difference - energy : kinetic or total - lower : lowest level, hPa - upper : highest level, hPa - - Outputs: - - data : 2D array. - """ - - # Speed up script by only referencing data, not - # loading it to a variable yet - - # WIND - U0 = nc0.variables['U'] - V0 = nc0.variables['V'] - U1 = nc1.variables['U'] - V1 = nc1.variables['V'] - - # PERT and BASE PRESSURE - P0 = nc0.variables['P'] - PB0 = nc0.variables['PB'] - P1 = nc1.variables['P'] - PB1 = nc1.variables['PB'] - - if energy=='total': - T0 = nc0.variables['T'] - T1 = nc1.variables['T'] - R = 287.0 # Universal gas constant (J / deg K * kg) - Cp = 1004.0 # Specific heat of dry air at constant pressure (J / deg K * kg) - kappa = (R/Cp) - - xlen = U0.shape[2] # 1 less than in V - ylen = V0.shape[3] # 1 less than in U - zlen = U0.shape[1] # identical in U & V - - # Generator for lat/lon points - def latlon(nlats,nlons): - for i in nlats: - for j in nlons: - yield i,j - - gridpts = latlon((xlen,ylen)) - - DKE = [] - for n,t in enumerate(t_idx): - DKE2D = N.zeros(xlen,ylen) - for gridpt in gridpts: - i,j = gridpt - - # Find closest level to 'lower', 'upper' - P_col = P0[t,:,i,j] + PB0[t,:,i,j] - if lower: - low_idx = util.closest(P_col,lower) - else: - low_idx = None - if upper: - upp_idx = util.closest(P_col,upper) - else: - upp_idx = None - - zidx = slice(low_idx,upp_idx+1) - - if energy=='kinetic': - DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,i,j]-U1[t,zidx,i,j])**2 + - (V0[t,zidx,i-1,j]-V1[t,zidx,i-1,j])**2)) - elif energy=='total': - DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,i,j]-U1[t,zidx,i,j])**2 + - (V0[t,zidx,i-1,j]-V1[t,zidx,i-1,j])**2 + - kappa*(T0[t,zidx,i,j]-T1[t,zidx,i,j])**2)) - - DKE.append(DKE2D) - - return DKE - \ No newline at end of file diff --git a/postWRF/PyWRFPlus/axes.py b/postWRF/PyWRFPlus/axes.py deleted file mode 100644 index 59420d1..0000000 --- a/postWRF/PyWRFPlus/axes.py +++ /dev/null @@ -1,10 +0,0 @@ -import matplotlib as M -import matplotlib.pyplot as plt - -class Axes: - def __init__(self,config): - pass - - def setup(self,config): - fig = plt.figure(figsize=(width,height)) - return fig diff --git a/postWRF/PyWRFPlus/birdseye.py b/postWRF/PyWRFPlus/birdseye.py deleted file mode 100644 index f311adc..0000000 --- a/postWRF/PyWRFPlus/birdseye.py +++ /dev/null @@ -1,93 +0,0 @@ -import pdb -import matplotlib.pyplot as plt -from mpl_toolkits.basemap import Basemap - -from defaults import Defaults -from figure import Figure -import utils - -class BirdsEye(Figure): - def __init__(self,config,wrfout,p2p): - self.C = config - self.W = wrfout - self.D = Defaults() - self.p2p = p2p - - def plot2D(self,va,pt,en,lv,da,na): - # INITIALISE - self.fig = plt.figure() - self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user - self.bmap,x,y = self.basemap_setup() - - # Get indices for time, level, lats, lons - # TIME - time_idx = self.W.get_time_idx(pt) - - # LEVEL - if lv == 2000: - lv_idx = 0 - lv = 'sfc' # For naming purposes - else: - print("Non-surface levels not supported yet.") - raise Exception - - # LAT/LON - lat_sl, lon_sl = self.get_limited_domain(da) - - # FETCH DATA - PS = {'t': time_idx, 'lv': lv_idx, 'la': lat_sl, 'lo': lon_sl} - data = self.W.get(va,PS) - la_n = data.shape[-2] - lo_n = data.shape[-1] - - # COLORBAR, CONTOURING - try: - clvs,cm = scales.get_cm(va,lv) - except: - self.bmap.contourf(x,y,data.reshape((la_n,lo_n))) - else: - self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,cmap=cm) - - # LABELS, TITLES etc - if self.C.plot_titles: - title = utils.string_from_time('title',pt) - plt.title(title) - if self.C.plot_colorbar: - plt.colorbar(orientation='horizontal') - - # SAVE FIGURE - datestr = utils.string_from_time('output',pt) - if not na: - # Use default naming scheme - na = (va,lv,datestr) - else: - # Come up with scheme... - print("Coming soon: ability to create custom filenames") - raise Exception - self.fname = self.create_fname(na) # No da variable here - self.save(self.fig,self.p2p,self.fname) - self.fig.clf() - - def basemap_setup(self): - # Fetch settings - basemap_res = getattr(self.C,'basemap_res',self.D.basemap_res) - - width_m = self.W.dx*(self.W.x_dim-1) - height_m = self.W.dy*(self.W.y_dim-1) - - m = Basemap( - projection='lcc',width=width_m,height=height_m, - lon_0=self.W.cen_lon,lat_0=self.W.cen_lat,lat_1=self.W.truelat1, - lat_2=self.W.truelat2,resolution=basemap_res,area_thresh=500 - ) - m.drawcoastlines() - m.drawstates() - m.drawcountries() - - # Draw meridians etc with wrff.lat/lon spacing - # Default should be a tenth of width of plot, rounded to sig fig - - x,y = m(self.W.lons,self.W.lats) - return m, x, y - - diff --git a/postWRF/PyWRFPlus/colourtables.py b/postWRF/PyWRFPlus/colourtables.py deleted file mode 100644 index b622377..0000000 --- a/postWRF/PyWRFPlus/colourtables.py +++ /dev/null @@ -1,735 +0,0 @@ -"""Colour tables for plotting. - -Adapted from code by Luke Madaus and David-John Gagne II -""" -from matplotlib.colors import LinearSegmentedColormap - -def RdBufloat(valrange): - # Will define a colortable that keeps zero as white - length = max(valrange)-min(valrange) - distance = 0 - min(valrange) - pct = float(distance)/float(length) - - RdBufloat_cdict ={'red':((0.00, 0.00, 0.00), - (pct, 1.00, 1.00), - (1.00, 1.00, 1.00)), - 'green': ((0.00, 0.00, 0.00), - (pct, 1.00, 1.00), - (1.00, 0.00, 0.00)), - 'blue': ((0.00, 1.00, 1.00), - (pct, 1.00, 1.00), - (1.00, 0.00, 0.00))} - RdBufloat_coltbl = LinearSegmentedColormap('RdBufloat_COLTBL',RdBufloat_cdict) - return RdBufloat_coltbl - - - -def rain1(*args): - rain_cdict ={'red': ((0.00, 0.00, 0.00), - (1.00, 0.00, 0.00)), - 'green': ((0.00, 1.00, 1.00), - (1.00, 0.20, 0.20)), - 'blue': ((0.00, 0.00, 0.00), - (1.00, 0.00, 0.00))} - rain_coltbl = LinearSegmentedColormap('RAIN_COLTBL',rain_cdict) - return rain_coltbl - -def snow1(*args): - snow_cdict ={'red': ((0.00, 0.00, 0.00), - (1.00, 0.00, 0.00)), - 'green': ((0.00, 0.00, 0.00), - (1.00, 0.00, 0.00)), - 'blue': ((0.00, 1.00, 1.00), - (1.00, 0.20, 0.20))} - snow_coltbl = LinearSegmentedColormap('SNOW_COLTBL',snow_cdict) - return snow_coltbl - -def mixprecip1(*args): - mix_cdict ={'red': ((0.00, 0.20, 0.20), - (1.00, 1.00, 1.00)), - 'green': ((0.00, 0.00, 0.00), - (1.00, 0.00, 0.00)), - 'blue': ((0.00, 0.00, 0.00), - (1.00, 0.00, 0.00))} - mix_coltbl = LinearSegmentedColormap('MIX_COLTBL',mix_cdict) - return mix_coltbl - -def grays(): - grays_cdict ={'red': ((0.00, 1.00, 1.00), - (1.00, 0.05, 0.05)), - 'green': ((0.00, 1.00, 1.00), - (1.00, 0.05, 0.05)), - 'blue': ((0.00, 1.00, 1.00), - (1.00, 0.05, 0.05))} - grays_coltbl = LinearSegmentedColormap('GRAYS_COLTBL',grays_cdict) - return grays_coltbl - -def snow2(): - snowf_cdict ={'red': ((0.00, 0.91, 0.91), - (0.06, 0.81, 0.81), - (0.12, 0.51, 0.51), - (0.18, 0.23, 0.23), - (0.24, 0.11, 0.11), - (0.30, 0.00, 0.00), - (0.36, 0.02, 0.02), - (0.42, 0.02, 0.02), - (0.48, 0.03, 0.03), - (0.54, 0.52, 0.52), - (0.60, 1.00, 1.00), - (0.66, 1.00, 1.00), - (0.72, 1.00, 1.00), - (0.78, 1.00, 1.00), - (0.84, 0.70, 0.70), - (0.90, 0.40, 0.40), - (1.00, 0.20, 0.20)), - 'green': ((0.00, 0.80, 0.80), - (0.06, 0.50, 0.50), - (0.12, 0.20, 0.20), - (0.18, 0.00, 0.00), - (0.24, 0.00, 0.00), - (0.30, 0.00, 0.00), - (0.36, 0.24, 0.24), - (0.42, 0.47, 0.47), - (0.48, 0.70, 0.70), - (0.54, 0.85, 0.85), - (0.60, 1.00, 1.00), - (0.66, 0.67, 0.67), - (0.72, 0.33, 0.33), - (0.78, 0.00, 0.00), - (0.84, 0.00, 0.00), - (0.90, 0.00, 0.00), - (1.00, 0.00, 0.00)), - 'blue': ((0.00, 0.98, 0.98), - (0.06, 0.87, 0.87), - (0.12, 0.58, 0.58), - (0.18, 0.69, 0.69), - (0.24, 0.84, 0.84), - (0.30, 1.00, 1.00), - (0.36, 0.69, 0.69), - (0.42, 0.37, 0.37), - (0.48, 0.06, 0.06), - (0.54, 0.03, 0.03), - (0.60, 0.00, 0.00), - (0.66, 0.00, 0.00), - (0.72, 0.00, 0.00), - (0.78, 0.00, 0.00), - (0.84, 0.00, 0.00), - (0.90, 0.00, 0.00), - (1.00, 0.00, 0.00))} - snowf_coltbl = LinearSegmentedColormap('SNOWF_COLTBL',snowf_cdict) - return snowf_coltbl - -def reflect(): - reflect_cdict ={'red': ((0.000, 0.40, 0.40), - (0.067, 0.20, 0.20), - (0.133, 0.00, 0.00), - (0.200, 0.00, 0.00), - (0.267, 0.00, 0.00), - (0.333, 0.00, 0.00), - (0.400, 1.00, 1.00), - (0.467, 1.00, 1.00), - (0.533, 1.00, 1.00), - (0.600, 1.00, 1.00), - (0.667, 0.80, 0.80), - (0.733, 0.60, 0.60), - (0.800, 1.00, 1.00), - (0.867, 0.60, 0.60), - (0.933, 1.00, 1.00), - (1.000, 0.00, 0.00)), - 'green': ((0.000, 1.00, 1.00), - (0.067, 0.60, 0.60), - (0.133, 0.00, 0.00), - (0.200, 1.00, 1.00), - (0.267, 0.80, 0.80), - (0.333, 0.60, 0.60), - (0.400, 1.00, 1.00), - (0.467, 0.80, 0.80), - (0.533, 0.40, 0.40), - (0.600, 0.00, 0.00), - (0.667, 0.20, 0.20), - (0.733, 0.00, 0.00), - (0.800, 0.00, 0.00), - (0.867, 0.20, 0.20), - (0.933, 1.00, 1.00), - (1.000, 1.00, 1.00)), - 'blue': ((0.000, 1.00, 1.00), - (0.067, 1.00, 1.00), - (0.133, 1.00, 1.00), - (0.200, 0.00, 0.00), - (0.267, 0.00, 0.00), - (0.333, 0.00, 0.00), - (0.400, 0.00, 0.00), - (0.467, 0.00, 0.00), - (0.533, 0.00, 0.00), - (0.600, 0.00, 0.00), - (0.667, 0.00, 0.00), - (0.733, 0.00, 0.00), - (0.800, 1.00, 1.00), - (0.867, 0.80, 0.80), - (0.933, 1.00, 1.00), - (1.000, 1.00, 1.00))} - reflect_coltbl = LinearSegmentedColormap('REFLECT_COLTBL',reflect_cdict) - return reflect_coltbl - -def reflect_ncdc(): - - - - reflect_ncdc_cdict ={'red':((0.0000, 0.000, 0.000), - (0.0714, 0.000, 0.000), - (0.1429, 0.000, 0.000), - (0.2143, 0.000, 0.000), - (0.2857, 0.000, 0.000), - (0.3571, 0.000, 0.000), - (0.4286, 1.000, 1.000), - (0.5000, 0.906, 0.906), - (0.5714, 1.000, 1.000), - (0.6429, 1.000, 1.000), - (0.7143, 0.839, 0.839), - (0.7857, 0.753, 0.753), - (0.8571, 1.000, 1.000), - (0.9286, 0.600, 0.600), - (1.000, 0.923, 0.923)), - 'green': ((0.0000, 0.925, 0.925), - (0.0714, 0.627, 0.627), - (0.1429, 0.000, 0.000), - (0.2143, 1.000, 1.000), - (0.2857, 0.784, 0.784), - (0.3571, 0.565, 0.565), - (0.4286, 1.000, 1.000), - (0.5000, 0.753, 0.753), - (0.5714, 0.565, 0.565), - (0.6429, 0.000, 0.000), - (0.7143, 0.000, 0.000), - (0.7857, 0.000, 0.000), - (0.8571, 0.000, 0.000), - (0.9286, 0.333, 0.333), - (1.000, 0.923, 0.923)), - - 'blue': ((0.0000, 0.925, 0.925), - (0.0714, 0.965, 0.965), - (0.1429, 0.965, 0.965), - (0.2143, 0.000, 0.000), - (0.2857, 0.000, 0.000), - (0.3571, 0.000, 0.000), - (0.4286, 0.000, 0.000), - (0.5000, 0.000, 0.000), - (0.5714, 0.000, 0.000), - (0.6429, 0.000, 0.000), - (0.7143, 0.000, 0.000), - (0.7857, 0.000, 0.000), - (0.8571, 1.000, 1.000), - (0.9286, 0.788, 0.788), - (1.000, 0.923, 0.923))} - reflect_ncdc_coltbl = LinearSegmentedColormap('REFLECT_NCDC_COLTBL',reflect_ncdc_cdict) - return reflect_ncdc_coltbl - -def irsat(): - irsat_cdict ={'red': ((0.000, 1.000, 0.294), - (0.067, 1.000, 1.000), - (0.133, 0.804, 0.804), - (0.200, 0.369, 0.369), - (0.267, 0.627, 0.627), - (0.333, 0.804, 0.804), - (0.400, 1.000, 1.000), - (0.567, 0.000, 0.000), - (0.667, 0.400, 0.400), - (0.700, 0.596, 0.596), - (0.800, 0.000, 0.000), - (0.867, 0.416, 0.416), - (0.933, 0.804, 0.804), - (1.000, 0.294, 0.294)), - 'green': ((0.000, 1.000, 0.000), - (0.067, 0.000, 0.000), - (0.133, 0.361, 0.361), - (0.200, 0.149, 0.149), - (0.267, 0.322, 0.322), - (0.333, 0.584, 0.584), - (0.400, 0.757, 0.757), - (0.567, 0.392, 0.392), - (0.667, 0.804, 0.804), - (0.700, 0.961, 0.961), - (0.800, 0.000, 0.000), - (0.867, 0.353, 0.353), - (0.933, 0.000, 0.000), - (1.000, 0.000, 0.000)), - 'blue': ((0.000, 1.000, 1.000), - (0.067, 0.000, 0.000), - (0.133, 0.360, 0.360), - (0.200, 0.070, 0.070), - (0.267, 0.176, 0.176), - (0.333, 0.047, 0.047), - (0.400, 0.145, 0.145), - (0.567, 0.000, 0.000), - (0.667, 0.667, 0.667), - (0.700, 1.000, 1.000), - (0.800, 0.502, 0.502), - (0.867, 0.804, 0.804), - (0.933, 0.804, 0.804), - (1.000, 0.510, 0.510))} - irsat_coltbl = LinearSegmentedColormap('IRSAT_COLTBL',irsat_cdict) - return irsat_coltbl - -def bw_irsat(): - bw_irsat_cdict ={'red': ((0.000, 1.000, 1.000), - (1.000, 0.000, 0.000)), - 'green': ((0.000, 1.000, 1.000), - (1.000, 0.000, 0.000)), - 'blue': ((0.000, 1.000, 1.000), - (1.000, 0.000, 0.000))} - bw_irsat_coltbl = LinearSegmentedColormap('BW_IRSAT_COLTBL',bw_irsat_cdict) - return bw_irsat_coltbl - - -def precip1(): - precip_cdict ={'red': ((0.000, 1.000, 1.000), - (0.004, 0.914, 0.914), - (0.012, 0.812, 0.812), - (0.020, 0.514, 0.514), - (0.040, 0.227, 0.227), - (0.060, 0.114, 0.114), - (0.080, 0.000, 0.000), - (0.100, 0.012, 0.012), - (0.120, 0.020, 0.020), - (0.160, 0.031, 0.031), - (0.200, 0.518, 0.518), - (0.240, 1.000, 1.000), - (0.280, 1.000, 1.000), - (0.320, 1.000, 1.000), - (0.360, 1.000, 1.000), - (0.400, 0.702, 0.702), - (0.500, 0.490, 0.490), - (0.600, 0.294, 0.294), - (0.700, 0.196, 0.196), - (0.800, 0.980, 0.980), - (1.000, 1.000, 1.000)), - 'green': ((0.000, 1.000, 0.000), - (0.004, 0.800, 0.800), - (0.012, 0.502, 0.502), - (0.020, 0.200, 0.200), - (0.040, 0.000, 0.000), - (0.060, 0.000, 0.000), - (0.080, 0.000, 0.000), - (0.100, 0.235, 0.235), - (0.120, 0.467, 0.467), - (0.160, 0.702, 0.702), - (0.200, 0.851, 0.851), - (0.240, 1.000, 1.000), - (0.280, 0.667, 0.667), - (0.320, 0.227, 0.227), - (0.360, 0.000, 0.000), - (0.400, 0.000, 0.000), - (0.500, 0.000, 0.000), - (0.600, 0.000, 0.000), - (0.700, 0.000, 0.000), - (0.800, 0.773, 0.773), - (1.000, 1.000, 1.000)), - 'blue': ((0.000, 1.000, 1.000), - (0.004, 0.976, 0.976), - (0.012, 0.875, 0.875), - (0.020, 0.576, 0.576), - (0.040, 0.690, 0.690), - (0.060, 0.843, 0.843), - (0.080, 1.000, 1.000), - (0.100, 0.686, 0.686), - (0.120, 0.372, 0.372), - (0.160, 0.059, 0.059), - (0.200, 0.031, 0.031), - (0.240, 0.000, 0.000), - (0.280, 0.000, 0.000), - (0.320, 0.000, 0.000), - (0.360, 0.000, 0.000), - (0.400, 0.000, 0.000), - (0.500, 0.000, 0.000), - (0.600, 0.000, 0.000), - (0.700, 0.000, 0.000), - (0.800, 0.980, 0.980), - (1.000, 1.000, 1.000))} - precip_coltbl = LinearSegmentedColormap('PRECIP_COLTBL',precip_cdict) - return precip_coltbl - -def dewpoint1(): - dwp_cdict ={'red': ((0.00, 0.60, 0.60), - (0.35, 0.70, 0.70), - (0.40, 0.80, 0.80), - (0.45, 0.90, 0.90), - (0.50, 1.00, 1.00), - (0.55, 0.90, 0.90), - (0.60, 0.76, 0.76), - (0.70, 0.64, 0.64), - (0.75, 0.52, 0.52), - (0.85, 0.42, 0.42), - (1.00, 0.32, 0.32)), - 'green': ((0.00, 0.33, 0.33), - (0.35, 0.44, 0.44), - (0.40, 0.56, 0.56), - (0.45, 0.69, 0.69), - (0.50, 0.85, 0.85), - (0.55, 1.00, 1.00), - (0.60, 0.90, 0.90), - (0.70, 0.80, 0.80), - (0.75, 0.70, 0.70), - (0.85, 0.60, 0.60), - (1.00, 0.50, 0.50)), - 'blue': ((0.00, 0.06, 0.06), - (0.35, 0.17, 0.17), - (0.40, 0.32, 0.32), - (0.45, 0.49, 0.49), - (0.50, 0.70, 0.70), - (0.55, 0.70, 0.70), - (0.60, 0.49, 0.49), - (0.70, 0.32, 0.32), - (0.75, 0.17, 0.17), - (0.85, 0.06, 0.06), - (1.00, 0.05, 0.05))} - - dwp_coltbl = LinearSegmentedColormap('DWP_COLTBL',dwp_cdict) - return dwp_coltbl - -def sftemp(): - sfc_cdict ={'red': ((0.00, 0.20, 0.20), - (0.08, 0.40, 0.40), - (0.17, 0.27, 0.27), - (0.25, 0.80, 0.80), - (0.33, 0.20, 0.20), - (0.42, 0.20, 0.20), - (0.50, 0.00, 0.00), - (0.58, 0.99, 0.99), - (0.67, 1.00, 1.00), - (0.75, 0.82, 0.82), - (0.83, 0.53, 0.53), - (0.92, 0.95, 0.95), - (1.00, 1.00, 1.00)), - - 'green': ((0.00, 0.20, 0.20), - (0.08, 0.40, 0.40), - (0.17, 0.00, 0.00), - (0.25, 0.60, 0.60), - (0.33, 0.40, 0.40), - (0.42, 0.60, 0.60), - (0.50, 0.39, 0.39), - (0.58, 0.76, 0.76), - (0.67, 0.36, 0.36), - (0.75, 0.02, 0.02), - (0.83, 0.00, 0.00), - (0.92, 0.03, 0.03), - (1.00, 0.60, 0.60)), - - 'blue': ((0.00, 0.60, 0.60), - (0.08, 0.60, 0.60), - (0.17, 0.65, 0.65), - (0.25, 1.00, 1.00), - (0.33, 1.00, 1.00), - (0.42, 0.40, 0.40), - (0.50, 0.07, 0.07), - (0.58, 0.02, 0.02), - (0.67, 0.00, 0.00), - (0.75, 0.01, 0.01), - (0.83, 0.00, 0.00), - (0.92, 0.52, 0.52), - (1.00, 0.80, 0.80))} - - - sfc_coltbl = LinearSegmentedColormap('SFC_COLTBL',sfc_cdict) - return sfc_coltbl - -def thetae(): - thte_cdict ={'red': ((0.00, 0.20, 0.20), - (0.08, 0.40, 0.40), - (0.17, 0.27, 0.27), - (0.25, 0.80, 0.80), - (0.33, 0.20, 0.20), - (0.42, 0.20, 0.20), - (0.50, 0.00, 0.00), - (0.58, 0.99, 0.99), - (0.67, 1.00, 1.00), - (0.75, 0.82, 0.82), - (0.83, 0.53, 0.53), - (0.92, 0.95, 0.95), - (1.00, 1.00, 1.00)), - - 'green': ((0.00, 0.20, 0.20), - (0.08, 0.40, 0.40), - (0.17, 0.00, 0.00), - (0.25, 0.60, 0.60), - (0.33, 0.40, 0.40), - (0.42, 0.60, 0.60), - (0.50, 0.39, 0.39), - (0.58, 0.76, 0.76), - (0.67, 0.36, 0.36), - (0.75, 0.02, 0.02), - (0.83, 0.00, 0.00), - (0.92, 0.03, 0.03), - (1.00, 0.60, 0.60)), - - 'blue': ((0.00, 0.60, 0.60), - (0.08, 0.60, 0.60), - (0.17, 0.65, 0.65), - (0.25, 1.00, 1.00), - (0.33, 1.00, 1.00), - (0.42, 0.40, 0.40), - (0.50, 0.07, 0.07), - (0.58, 0.02, 0.02), - (0.67, 0.00, 0.00), - (0.75, 0.01, 0.01), - (0.83, 0.00, 0.00), - (0.92, 0.52, 0.52), - (1.00, 0.80, 0.80))} - - thte_coltbl = LinearSegmentedColormap('THTE_COLTBL',thte_cdict) - return thte_coltbl - -def PkBlfloat(datarange): - distance = max(datarange)-min(datarange) - zeroloc = 0-min(datarange) - pct = float(zeroloc)/float(distance) - - # Now, rescale the colormap so each value - # is scaled to equal that - - PkBl_data = {'red': [(0.*pct, 0.1178, 0.1178),(0.015873*pct, 0.195857, 0.195857), - (0.031746*pct, 0.250661, 0.250661),(0.047619*pct, 0.295468, 0.295468), - (0.063492*pct, 0.334324, 0.334324),(0.079365*pct, 0.369112, 0.369112), - (0.095238*pct, 0.400892, 0.400892),(0.111111*pct, 0.430331, 0.430331), - (0.126984*pct, 0.457882, 0.457882),(0.142857*pct, 0.483867, 0.483867), - (0.158730*pct, 0.508525, 0.508525),(0.174603*pct, 0.532042, 0.532042), - (0.190476*pct, 0.554563, 0.554563),(0.206349*pct, 0.576204, 0.576204), - (0.222222*pct, 0.597061, 0.597061),(0.238095*pct, 0.617213, 0.617213), - (0.253968*pct, 0.636729, 0.636729),(0.269841*pct, 0.655663, 0.655663), - (0.285714*pct, 0.674066, 0.674066),(0.301587*pct, 0.691980, 0.691980), - (0.317460*pct, 0.709441, 0.709441),(0.333333*pct, 0.726483, 0.726483), - (0.349206*pct, 0.743134, 0.743134),(0.365079*pct, 0.759421, 0.759421), - (0.380952*pct, 0.766356, 0.766356),(0.396825*pct, 0.773229, 0.773229), - (0.412698*pct, 0.780042, 0.780042),(0.428571*pct, 0.786796, 0.786796), - (0.444444*pct, 0.793492, 0.793492),(0.460317*pct, 0.800132, 0.800132), - (0.476190*pct, 0.806718, 0.806718),(0.492063*pct, 0.813250, 0.813250), - (0.507937*pct, 0.819730, 0.819730),(0.523810*pct, 0.826160, 0.826160), - (0.539683*pct, 0.832539, 0.832539),(0.555556*pct, 0.838870, 0.838870), - (0.571429*pct, 0.845154, 0.845154),(0.587302*pct, 0.851392, 0.851392), - (0.603175*pct, 0.857584, 0.857584),(0.619048*pct, 0.863731, 0.863731), - (0.634921*pct, 0.869835, 0.869835),(0.650794*pct, 0.875897, 0.875897), - (0.666667*pct, 0.881917, 0.881917),(0.682540*pct, 0.887896, 0.887896), - (0.698413*pct, 0.893835, 0.893835),(0.714286*pct, 0.899735, 0.899735), - (0.730159*pct, 0.905597, 0.905597),(0.746032*pct, 0.911421, 0.911421), - (0.761905*pct, 0.917208, 0.917208),(0.777778*pct, 0.922958, 0.922958), - (0.793651*pct, 0.928673, 0.928673),(0.809524*pct, 0.934353, 0.934353), - (0.825397*pct, 0.939999, 0.939999),(0.841270*pct, 0.945611, 0.945611), - (0.857143*pct, 0.951190, 0.951190),(0.873016*pct, 0.956736, 0.956736), - (0.888889*pct, 0.962250, 0.962250),(0.904762*pct, 0.967733, 0.967733), - (0.920635*pct, 0.973185, 0.973185),(0.936508*pct, 0.978607, 0.978607), - (0.952381*pct, 0.983999, 0.983999),(0.968254*pct, 0.989361, 0.989361), - (0.984127*pct, 0.994695, 0.994695),(1.0*pct, 1.0, 1.0), - ((1-pct)*0.125+pct, - 0.87058824300765991, 0.87058824300765991), ((1-pct)*0.25+pct, - 0.7764706015586853, 0.7764706015586853), ((1-pct)*0.375+pct, - 0.61960786581039429, 0.61960786581039429), ((1-pct)*0.5+pct, - 0.41960784792900085, 0.41960784792900085), ((1-pct)*0.625+pct, - 0.25882354378700256, 0.25882354378700256), ((1-pct)*0.75+pct, - 0.12941177189350128, 0.12941177189350128), ((1-pct)*0.875+pct, - 0.031372550874948502, 0.031372550874948502), (1.0, - 0.031372550874948502, 0.031372550874948502)], - - - - - 'green': [(0., 0., 0.),(0.015873*pct, 0.102869, 0.102869), - (0.031746*pct, 0.145479, 0.145479),(0.047619*pct, 0.178174, 0.178174), - (0.063492*pct, 0.205738, 0.205738),(0.079365*pct, 0.230022, 0.230022), - (0.095238*pct, 0.251976, 0.251976),(0.111111*pct, 0.272166, 0.272166), - (0.126984*pct, 0.290957, 0.290957),(0.142857*pct, 0.308607, 0.308607), - (0.158730*pct, 0.325300, 0.325300),(0.174603*pct, 0.341178, 0.341178), - (0.190476*pct, 0.356348, 0.356348),(0.206349*pct, 0.370899, 0.370899), - (0.222222*pct, 0.384900, 0.384900),(0.238095*pct, 0.398410, 0.398410), - (0.253968*pct, 0.411476, 0.411476),(0.269841*pct, 0.424139, 0.424139), - (0.285714*pct, 0.436436, 0.436436),(0.301587*pct, 0.448395, 0.448395), - (0.317460*pct, 0.460044, 0.460044),(0.333333*pct, 0.471405, 0.471405), - (0.349206*pct, 0.482498, 0.482498),(0.365079*pct, 0.493342, 0.493342), - (0.380952*pct, 0.517549, 0.517549),(0.396825*pct, 0.540674, 0.540674), - (0.412698*pct, 0.562849, 0.562849),(0.428571*pct, 0.584183, 0.584183), - (0.444444*pct, 0.604765, 0.604765),(0.460317*pct, 0.624669, 0.624669), - (0.476190*pct, 0.643958, 0.643958),(0.492063*pct, 0.662687, 0.662687), - (0.507937*pct, 0.680900, 0.680900),(0.523810*pct, 0.698638, 0.698638), - (0.539683*pct, 0.715937, 0.715937),(0.555556*pct, 0.732828, 0.732828), - (0.571429*pct, 0.749338, 0.749338),(0.587302*pct, 0.765493, 0.765493), - (0.603175*pct, 0.781313, 0.781313),(0.619048*pct, 0.796819, 0.796819), - (0.634921*pct, 0.812029, 0.812029),(0.650794*pct, 0.826960, 0.826960), - (0.666667*pct, 0.841625, 0.841625),(0.682540*pct, 0.856040, 0.856040), - (0.698413*pct, 0.870216, 0.870216),(0.714286*pct, 0.884164, 0.884164), - (0.730159*pct, 0.897896, 0.897896),(0.746032*pct, 0.911421, 0.911421), - (0.761905*pct, 0.917208, 0.917208),(0.777778*pct, 0.922958, 0.922958), - (0.793651*pct, 0.928673, 0.928673),(0.809524*pct, 0.934353, 0.934353), - (0.825397*pct, 0.939999, 0.939999),(0.841270*pct, 0.945611, 0.945611), - (0.857143*pct, 0.951190, 0.951190),(0.873016*pct, 0.956736, 0.956736), - (0.888889*pct, 0.962250, 0.962250),(0.904762*pct, 0.967733, 0.967733), - (0.920635*pct, 0.973185, 0.973185),(0.936508*pct, 0.978607, 0.978607), - (0.952381*pct, 0.983999, 0.983999),(0.968254*pct, 0.989361, 0.989361), - (0.984127*pct, 0.994695, 0.994695),(1.0*pct, 1.0, 1.0), - ((1-pct)*0.125+pct, 0.92156863212585449, 0.92156863212585449), ((1-pct)*0.25+pct, - 0.85882353782653809, 0.85882353782653809), ((1-pct)*0.375+pct, - 0.7921568751335144, 0.7921568751335144), ((1-pct)*0.5+pct, - 0.68235296010971069, 0.68235296010971069), ((1-pct)*0.625+pct, - 0.57254904508590698, 0.57254904508590698), ((1-pct)*0.75+pct, - 0.44313725829124451, 0.44313725829124451), ((1-pct)*0.875+pct, - 0.31764706969261169, 0.31764706969261169), ((1-pct)*1.0+pct, - 0.18823529779911041, 0.18823529779911041)], - - - 'blue': [(0.*pct, 0., 0.),(0.015873*pct, 0.102869, 0.102869), - (0.031746*pct, 0.145479, 0.145479),(0.047619*pct, 0.178174, 0.178174), - (0.063492*pct, 0.205738, 0.205738),(0.079365*pct, 0.230022, 0.230022), - (0.095238*pct, 0.251976, 0.251976),(0.111111*pct, 0.272166, 0.272166), - (0.126984*pct, 0.290957, 0.290957),(0.142857*pct, 0.308607, 0.308607), - (0.158730*pct, 0.325300, 0.325300),(0.174603*pct, 0.341178, 0.341178), - (0.190476*pct, 0.356348, 0.356348),(0.206349*pct, 0.370899, 0.370899), - (0.222222*pct, 0.384900, 0.384900),(0.238095*pct, 0.398410, 0.398410), - (0.253968*pct, 0.411476, 0.411476),(0.269841*pct, 0.424139, 0.424139), - (0.285714*pct, 0.436436, 0.436436),(0.301587*pct, 0.448395, 0.448395), - (0.317460*pct, 0.460044, 0.460044),(0.333333*pct, 0.471405, 0.471405), - (0.349206*pct, 0.482498, 0.482498),(0.365079*pct, 0.493342, 0.493342), - (0.380952*pct, 0.503953, 0.503953),(0.396825*pct, 0.514344, 0.514344), - (0.412698*pct, 0.524531, 0.524531),(0.428571*pct, 0.534522, 0.534522), - (0.444444*pct, 0.544331, 0.544331),(0.460317*pct, 0.553966, 0.553966), - (0.476190*pct, 0.563436, 0.563436),(0.492063*pct, 0.572750, 0.572750), - (0.507937*pct, 0.581914, 0.581914),(0.523810*pct, 0.590937, 0.590937), - (0.539683*pct, 0.599824, 0.599824),(0.555556*pct, 0.608581, 0.608581), - (0.571429*pct, 0.617213, 0.617213),(0.587302*pct, 0.625727, 0.625727), - (0.603175*pct, 0.634126, 0.634126),(0.619048*pct, 0.642416, 0.642416), - (0.634921*pct, 0.650600, 0.650600),(0.650794*pct, 0.658682, 0.658682), - (0.666667*pct, 0.666667, 0.666667),(0.682540*pct, 0.674556, 0.674556), - (0.698413*pct, 0.682355, 0.682355),(0.714286*pct, 0.690066, 0.690066), - (0.730159*pct, 0.697691, 0.697691),(0.746032*pct, 0.705234, 0.705234), - (0.761905*pct, 0.727166, 0.727166),(0.777778*pct, 0.748455, 0.748455), - (0.793651*pct, 0.769156, 0.769156),(0.809524*pct, 0.789314, 0.789314), - (0.825397*pct, 0.808969, 0.808969),(0.841270*pct, 0.828159, 0.828159), - (0.857143*pct, 0.846913, 0.846913),(0.873016*pct, 0.865261, 0.865261), - (0.888889*pct, 0.883229, 0.883229),(0.904762*pct, 0.900837, 0.900837), - (0.920635*pct, 0.918109, 0.918109),(0.936508*pct, 0.935061, 0.935061), - (0.952381*pct, 0.951711, 0.951711),(0.968254*pct, 0.968075, 0.968075), - (0.984127*pct, 0.984167, 0.984167),(1.0*pct, 1.0, 1.0), - ((1-pct)*0.125+pct, 0.9686274528503418, - 0.9686274528503418), ((1-pct)*0.25+pct, 0.93725490570068359, 0.93725490570068359), - ((1-pct)*0.375+pct, 0.88235294818878174, 0.88235294818878174), ((1-pct)*0.5+pct, - 0.83921569585800171, 0.83921569585800171), ((1-pct)*0.625+pct, 0.7764706015586853, - 0.7764706015586853), ((1-pct)*0.75+pct, 0.70980393886566162, 0.70980393886566162), - ((1-pct)*0.875+pct, 0.61176472902297974, 0.61176472902297974), (1.0, - 0.41960784792900085, 0.41960784792900085)]} - - PkBl_coltbl = LinearSegmentedColormap('PKBL_COLTBL',PkBl_data) - return PkBl_coltbl - - - -def PuRdBlfloat(datarange): - distance = max(datarange)-min(datarange) - zeroloc = 0-min(datarange) - pct = float(zeroloc)/float(distance) - - - PuRdBl_data = {'blue': [ - (0.0*pct, 0.12156862765550613,0.12156862765550613), - (0.125*pct,0.26274511218070984, 0.26274511218070984), - (0.25*pct,0.33725491166114807, 0.33725491166114807), - (0.375*pct, 0.54117649793624878, 0.54117649793624878), - (0.5*pct, 0.69019609689712524, 0.69019609689712524), - (0.625*pct, 0.78039216995239258,0.78039216995239258), - (0.75*pct, 0.85490196943283081,0.85490196943283081), - (0.875*pct, 0.93725490570068359,0.93725490570068359), - (1.0*pct, 0.97647058963775635,0.97647058963775635), - ((1-pct)*0.125+pct, 0.9686274528503418, - 0.9686274528503418), ((1-pct)*0.25+pct, 0.93725490570068359, 0.93725490570068359), - ((1-pct)*0.375+pct, 0.88235294818878174, 0.88235294818878174), ((1-pct)*0.5+pct, - 0.83921569585800171, 0.83921569585800171), ((1-pct)*0.625+pct, 0.7764706015586853, - 0.7764706015586853), ((1-pct)*0.75+pct, 0.70980393886566162, 0.70980393886566162), - ((1-pct)*0.875+pct, 0.61176472902297974, 0.61176472902297974), (1.0, - 0.41960784792900085, 0.41960784792900085)], - - - - 'green': [ - (0.0*pct, 0.0, 0.0), - (0.125*pct, 0.0, 0.0), - (0.25*pct, 0.070588238537311554, 0.070588238537311554), - (0.375*pct, 0.16078431904315948, 0.16078431904315948), - (0.5*pct, 0.3960784375667572, 0.3960784375667572), - (0.625*pct, 0.58039218187332153, 0.58039218187332153), - (0.75*pct, 0.72549021244049072, 0.72549021244049072), - (0.875*pct,0.88235294818878174, 0.88235294818878174), - (1.0*pct, 0.95686274766921997, 0.95686274766921997), - ((1-pct)*0.125+pct, 0.92156863212585449, 0.92156863212585449), ((1-pct)*0.25+pct, - 0.85882353782653809, 0.85882353782653809), ((1-pct)*0.375+pct, - 0.7921568751335144, 0.7921568751335144), ((1-pct)*0.5+pct, - 0.68235296010971069, 0.68235296010971069), ((1-pct)*0.625+pct, - 0.57254904508590698, 0.57254904508590698), ((1-pct)*0.75+pct, - 0.44313725829124451, 0.44313725829124451), ((1-pct)*0.875+pct, - 0.31764706969261169, 0.31764706969261169), ((1-pct)*1.0+pct, - 0.18823529779911041, 0.18823529779911041)], - - - - 'red': [ - (0.0*pct, 0.40392157435417175, 0.40392157435417175), - (0.125*pct, 0.59607845544815063, 0.59607845544815063), - (0.25*pct, 0.80784314870834351, 0.80784314870834351), - (0.375*pct, 0.90588235855102539, 0.90588235855102539), - (0.5*pct, 0.87450981140136719, 0.87450981140136719), - (0.625*pct, 0.78823530673980713, 0.78823530673980713), - (0.75*pct, 0.83137255907058716, 0.83137255907058716), - (0.875*pct, 0.90588235855102539, 0.90588235855102539), - (1.0*pct, 0.9686274528503418, 0.9686274528503418), - ((1-pct)*0.125+pct, - 0.87058824300765991, 0.87058824300765991), ((1-pct)*0.25+pct, - 0.7764706015586853, 0.7764706015586853), ((1-pct)*0.375+pct, - 0.61960786581039429, 0.61960786581039429), ((1-pct)*0.5+pct, - 0.41960784792900085, 0.41960784792900085), ((1-pct)*0.625+pct, - 0.25882354378700256, 0.25882354378700256), ((1-pct)*0.75+pct, - 0.12941177189350128, 0.12941177189350128), ((1-pct)*0.875+pct, - 0.031372550874948502, 0.031372550874948502), (1.0, - 0.031372550874948502, 0.031372550874948502)]} - - #print PuRdBl_data - PuRdBl_coltbl = LinearSegmentedColormap('PURDBL_COLTBL',PuRdBl_data) - return PuRdBl_coltbl - - -def RdBuWH(): - RdBuWH_data = {'blue': [(0.0, 0.3803921639919281, 0.3803921639919281), - (0.10000000000000002, 0.67450982332229614, 0.67450982332229614), - (0.20000000000000004, 0.76470589637756348, 0.76470589637756348), - (0.29999999999999996, 0.87058824300765991, 0.87058824300765991), - (0.39999999999999998, 0.94117647409439087, 0.94117647409439087), - (0.45, 1.0, 1.0), - (0.55, 1.0, 1.0), - (0.60000000000000002, 0.78039216995239258, 0.78039216995239258), - (0.69999999999999999, 0.50980395078659058, 0.50980395078659058), - (0.80000000000000001, 0.30196079611778259, 0.30196079611778259), - (0.90000000000000001, 0.16862745583057404, 0.16862745583057404), - (1.0, 0.12156862765550613, 0.12156862765550613)], - - 'green': [(0.0, 0.18823529779911041, 0.18823529779911041), - (0.10000000000000002, 0.40000000596046448, 0.40000000596046448), - (0.20000000000000004, 0.57647061347961426, 0.57647061347961426), - (0.29999999999999996, 0.77254903316497803, 0.77254903316497803), - (0.39999999999999998, 0.89803922176361084, 0.89803922176361084), - (0.45, 1.0, 1.0), - (0.55, 1.0, 1.0), - (0.60000000000000002, 0.85882353782653809, 0.85882353782653809), - (0.69999999999999999, 0.64705884456634521, 0.64705884456634521), - (0.80000000000000001, 0.37647059559822083, 0.37647059559822083), - (0.90000000000000001, 0.094117648899555206, 0.094117648899555206), - (1.0, 0.0, 0.0)], - - 'red': [(0.0, 0.019607843831181526, 0.019607843831181526), - (0.10000000000000002, 0.12941177189350128, 0.12941177189350128), - (0.20000000000000004, 0.26274511218070984, 0.26274511218070984), - (0.29999999999999996, 0.57254904508590698, 0.57254904508590698), - (0.39999999999999998, 0.81960785388946533, 0.81960785388946533), - (0.45, 1.0, 1.0), - (0.55, 1.0, 1.0), - (0.60000000000000002, 0.99215686321258545, 0.99215686321258545), - (0.69999999999999999, 0.95686274766921997, 0.95686274766921997), - (0.80000000000000001, 0.83921569585800171, 0.83921569585800171), - (0.90000000000000001, 0.69803923368453979, 0.69803923368453979), - (1.0, 0.40392157435417175, 0.40392157435417175)]} - RdBuWH_coltbl = LinearSegmentedColormap('RDBUWH_COLTBL',RdBuWH_data) - return RdBuWH_coltbl - diff --git a/postWRF/PyWRFPlus/defaults.py b/postWRF/PyWRFPlus/defaults.py deleted file mode 100644 index d2a3689..0000000 --- a/postWRF/PyWRFPlus/defaults.py +++ /dev/null @@ -1,13 +0,0 @@ -""" Default settings that are used when the user does not specify their own. - -""" - -class Defaults: - def __init__(self): - self.domain = 1 - self.font_prop = {'family':'sans-serif','sans-serif':['Liberation Sans'], - 'weight':'normal','size':14} - self.usetex = 0 - self.dpi = 200 - self.plot_titles = 1 # Generate a title for each plot - self.basemap_res = 'i' # Resolution of basemap coasts etc diff --git a/postWRF/PyWRFPlus/figure.py b/postWRF/PyWRFPlus/figure.py deleted file mode 100644 index 9cd8d17..0000000 --- a/postWRF/PyWRFPlus/figure.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Collection of x-y cross-section classes. - -""" - -# Imports -import numpy as N -import matplotlib as M -import matplotlib.pyplot as plt -from mpl_toolkits.basemap import Basemap -import pdb -import os - -# Custom imports -import utils - -class Figure: - def __init__(self,config,wrfout): - pass - - def create_fname(self,naming): - """Default naming should be: - Variable + time + level - """ - fname = '_'.join([str(a) for a in naming]) - return fname - - def title_time(self): - self.T = utils.padded_times(self.timeseq) - pdb.set_trace() - - def figsize(self,defwidth,defheight,fig): - width = getattr(self.C,'width',defwidth) - height = getattr(self.C,'height',defheight) - fig.set_size_inches(width,height) - return fig - - def save(self,fig,p2p,fname): - utils.trycreate(p2p) - fpath = os.path.join(p2p,fname) - #self.fig.savefig(fpath) - plt.gcf().savefig(fpath,bbox_inches='tight') - - def get_limited_domain(self,da) - if da: # Limited domain area - N_idx = self.W.get_lat_idx(da['N']) - E_idx = self.W.get_lon_idx(da['E']) - S_idx = self.W.get_lat_idx(da['S']) - W_idx = self.W.get_lon_idx(da['W']) - - lat_sl = slice(S_idx,N_idx) - lon_sl = slice(W_idx,E_idx) - else: - lat_sl = slice(None) - lon_sl = slice(None) - - return lat_sl, lon_sl \ No newline at end of file diff --git a/postWRF/PyWRFPlus/map.py b/postWRF/PyWRFPlus/map.py deleted file mode 100644 index e69de29..0000000 diff --git a/postWRF/PyWRFPlus/params.py b/postWRF/PyWRFPlus/params.py deleted file mode 100644 index e69de29..0000000 diff --git a/postWRF/PyWRFPlus/scales.py b/postWRF/PyWRFPlus/scales.py deleted file mode 100644 index cf88482..0000000 --- a/postWRF/PyWRFPlus/scales.py +++ /dev/null @@ -1,107 +0,0 @@ -""" This file contains colorbar scales for plotting. - -INPUTS - -va : variable to be plotted -lv : level to be plotted - -OUTPUTS - -cm : colour map -clvs : contour levels - -Level = 2000 indicates surface. - -Default settings are listed at the bottom. -These can be overwritten in user's config file... somehow -""" - -from matplotlib.colors import LinearSegmentedColormap - -import colourtables as ct - -def get_cm(va,lv): - - # Variable and level determine contour levels - - try: - if len(A[va][lv]) == 3: - # This is a min-max-interval list - clvs = N.arange(*A[va][lv]) - else: - # This is an actual list of values - clvs = A[va][lv] - except: - try: - near_lv = find_nearest_level(lv) - clvs = A[va][near_lv] - except: - # Some variables don't need levels - # Like composite reflectivity - clvs = 0 - # Variable determines colour table - - try: - cm = A[va]['cmap'](clvs) - except: - def_ct = plt.cm.get_cmap("jet") - cm = LinearSegmentedColormap(def_ct) - - return cm, clvs - -def find_nearest_level(lv): - lv_type = utils.level_type(lv) - - if lv_type == 'isentropic': - pass - # 'K' needs stripping - # This will be tricky, varies a lot... - elif lv_type == 'isobaric': - pass - # Plot logarithmically closest - elif lv_type == 'surface': - raise Exception - # Shouldn't get here, surface should be covered. - elif lv_type == 'PV-surface': - pass - elif lv_type == 'geometric': - pass - else: - raise Exception - - return near_lv - -######## DEFAULT SETTINGS FOR LEVELS ######## - -A = [] - -# Wind magnitude -A['wind'] = {} -A['wind'][2000] = (5,45,5) - -# Theta-e (Equivalent potential temperature) -A['thetae'] = {'cmap':ct.thetae} - -# Simulated reflectivity -A['sim_ref'] = {'cmap':ct.reflect_ncdc} -A['sim_ref'][2000] = (5,90,5) - -# Simulated reflectivity -A['comp_ref'] = {'cmap':ct.reflect_ncdc} -A['comp_ref'][2000] = (5,90,5) - -# Precipitation -A['precip'] = {'cmap':ct.precip1} -A['precip'][2000] = [0.01,0.03,0.05,0.10,0.15,0.20,0.25,0.30,0.40,0.50,0.60, - 0.70,0.80,0.90,1.00,1.25,1.50,1.75,2.00,2.50] - -# Precipitable water -A['pwat'] = {'cmap':precip1} -A['pwat'][2000] = (0.2,2.6,0.1) - -# Snowfall -A['snow'] = {'cmap':snow2} -A['snow'][2000] = [0.25,0.5,0.75,1,1.5,2,2.5,3,4,5,6,8,10,12,14,16,18] - - - diff --git a/postWRF/PyWRFPlus/skewt.py b/postWRF/PyWRFPlus/skewt.py deleted file mode 100644 index e69de29..0000000 diff --git a/postWRF/PyWRFPlus/ts.py b/postWRF/PyWRFPlus/ts.py deleted file mode 100644 index 3e386bb..0000000 --- a/postWRF/PyWRFPlus/ts.py +++ /dev/null @@ -1,10 +0,0 @@ -""" Create time series of variable(s) for certain period at location. - -Can use .TS files (?) -Can use model output. - -""" - -class TimeSeries: - def __init__(self): - pass \ No newline at end of file diff --git a/postWRF/PyWRFPlus/utils.py b/postWRF/PyWRFPlus/utils.py deleted file mode 100644 index 73d9b6c..0000000 --- a/postWRF/PyWRFPlus/utils.py +++ /dev/null @@ -1,85 +0,0 @@ -import os - -""" A collection of useful utilities. -""" - -def trycreate(loc): - try: - os.stat(loc) - except: - os.makedirs(loc) - -def padded_times(timeseq): - padded = ['{0:04d}'.format(t) for t in timeseq] - return padded - -def string_from_time(usage,t,dom=0,strlen=0,conven=0): - if usage == 'title': - # Generates string for titles - str = '{3:02d}:{4:02d}Z on {2:02d}/{1:02d}/{0:04d}'.format(*t) - elif usage == 'wrfout': - # Generates string for wrfout file finding - # Needs dom - if not dom: - print("No domain specified; using domain #1.") - dom = 1 - str = ('wrfout_d0' + str(dom) + - '{0:04d}-{1:02d}-{2:02d}_{3:02d}:{4:02d}:{5:02d}'.format(*t)) - elif usage == 'output': - if not conven: - # No convention set, assume DD/MM (I'm biased) - conven = 'DM' - # Generates string for output file creation - if conven == 'DM': - str = '{2:02d}{1:02d}_{3:02d}{4:02d}'.format(*t) - elif conven == 'MD': - str = '{1:02d}{2:02d}_{3:02d}{4:02d}'.format(*t) - else: - print("Set convention for date format: DM or MD.") - elif usage == 'dir': - # Generates string for directory names - # Needs strlen which sets smallest scope of time for string - if not strlen: - print("No timescope strlen set; using hour as minimum.") - strlen = 'hour' - n = lookup_time(strlen) - str = "{0:04d}".format(t[0]) + ''.join( - ["{0:02d}".format(a) for a in t[1:n+1]]) - else: - print("Usage for string not valid.") - raise Exception - return str - -def lookup_time(str): - D = {'year':0, 'month':1, 'day':2, 'hour':3, 'minute':4, 'second':5} - return D[str] - -def level_type(lv): - """ Check to see what type of level is requested by user. - - """ - if lv.endswith('K'): - return 'isentropic' - elif lv < 1500: - return 'isobaric' - elif lv == 2000: - return 'surface' - elif lv.endswith('PVU'): - return 'PV-surface' - elif lv.endswith('km'): - return 'geometric' - -def closest(arr2D,val): - """ - Inputs: - val : required value - arr2D : 2D array of values - - Output: - - idx : index of closest value - - """ - idx = N.argmin(N.abs(arr2D - val)) - return idx - \ No newline at end of file diff --git a/postWRF/PyWRFPlus/wrfout.py b/postWRF/PyWRFPlus/wrfout.py deleted file mode 100644 index b3b064b..0000000 --- a/postWRF/PyWRFPlus/wrfout.py +++ /dev/null @@ -1,259 +0,0 @@ -"""Compute or load data from netCDF file. - -Dimensions of 4D variable X are X.dimensions: -(Time,bottom_top,south_north,west_east_stag) -Time, levels, latitude, longitude -""" - -from netCDF4 import Dataset -import sys -import os -import numpy as N -import calendar -import pdb - -#sys.path.append('/home/jrlawson/gitprojects/meteogeneral/') -#from meteogeneral.WRF import wrf_tools - -class WRFOut: - - def __init__(self,fpath): - #self.C = config - self.nc = Dataset(fpath,'r') - - self.wrf_times = self.nc.variables['Times'][:] - self.dx = self.nc.DX - self.dy = self.nc.DY - #self.lvs = - self.lats = self.nc.variables['XLAT'][0,...] # Might fail if only one time? - self.lons = self.nc.variables['XLONG'][0,...] - - self.cen_lat = float(self.nc.CEN_LAT) - self.cen_lon = float(self.nc.CEN_LON) - self.truelat1 = float(self.nc.TRUELAT1) - self.truelat2 = float(self.nc.TRUELAT2) - self.x_dim = len(self.nc.dimensions['west_east']) - self.y_dim = len(self.nc.dimensions['south_north']) - - def get_time_idx(self,t,tuple_format=1): - - """ - Input: - - t : time, tuple format by default - (otherwise t is in datenum) - - Output: - - time_idx : index of time in WRF file - """ - if tuple_format: - t_epoch = calendar.timegm(t) - else: - t_epoch = t - nt = self.wrf_times.shape[0] - self.wrf_times_epoch = N.zeros([nt,1]) - t = self.wrf_times # For brevity - - for i in range(nt): - yr = int(''.join(t[i,0:4])) - mth = int(''.join(t[i,5:7])) - day = int(''.join(t[i,8:10])) - hr = int(''.join(t[i,11:13])) - mins = int(''.join(t[i,14:16])) - sec = int(''.join(t[i,17:19])) - self.wrf_times_epoch[i] = calendar.timegm([yr,mth,day,hr,mins,sec]) - - # Now find closest WRF time - self.time_idx = N.where( - abs(self.wrf_times_epoch-t_epoch) == - abs(self.wrf_times_epoch-t_epoch).min() - )[0][0] - return self.time_idx - - def get(self,var,PS): - """ Fetch a numpy array containing variable data. - - Slice according to arguments. - - Destagger if required. - - Returns unstaggered, sliced data. - - var : netCDF variable name - PS : Plot Settings, keys as follows: - - t : time index - lv : level index - la : latitude slice indices - lo : longitude slice indices - - """ - # Check if computing required - # When data is loaded from nc, it is destaggered - if var=='pressure': - if lv_idx == 0: - data = self.load('PSFC',PS) - elif var=='sim_ref': - data = self.compute_comp_ref(PS) - elif var=='shear': - data = self.compute_shear(0,3,PS) - elif var=='wind': - u = self.load('U',PS) - v = self.load('V',PS) - data = N.sqrt(u**2 + v**2) - else: - data = self.load(var,PS) - - return data - - def load(self,var,PS): - - # First, check dimension that is staggered (if any) - PS['destag_dim'] = self.check_destagger(var) - - # Next, fetch dimension names - PS['dim_names'] = self.get_dims(var) - - d = self.nc.variables[var] - sl = self.create_slice(PS) - data = self.destagger(d[sl],PS['destag_dim']) - return data - - def create_slice(self,PS): - # See which dimensions are present in netCDF file variable - sl = [] - if any('Time' in p for p in PS['dim_names']): - sl.append(slice(PS['t'],PS['t']+1)) - if any('bottom' in p for p in PS['dim_names']): - sl.append(slice(PS['lv'],PS['lv']+1)) - if any('north' and 'west' in p for p in PS['dim_names']): - sl.append(PS['la']) - sl.append(PS['lo']) - - return sl - - def check_destagger(self,var): - """ Looks up dimensions of netCDF file without loading data. - - Returns dimension number that requires destaggering - """ - stag_dim = None - for n,dname in enumerate(self.nc.variables[var].dimensions): - if 'stag' in dname: - stag_dim = n - - return stag_dim - - def get_dims(self,var): - dims = self.nc.variables[var].dimensions - return dims - - def destagger(self,data,ax): - """ Destagger data which needs it doing. - - data : numpy array of data requiring destaggering - ax : axis requiring destaggering - - Theta always has unstaggered points in all three spatial dimensions (axes=1,2,3). - - Data should be 4D but just the slice required to reduce unnecessary computation time. - """ - if ax==None: - return data - else: - nd = data.ndim - sl0 = [] # Slices to take place on staggered axis - sl1 = [] - - for n in range(nd): - if n is not ax: - sl0.append(slice(None)) - sl1.append(slice(None)) - else: - sl0.append(slice(None,-1)) - sl1.append(slice(1,None)) - - data_unstag = 0.5*(data[sl0] + data[sl1]) - return data_unstag - - def compute_shear(self,lower,upper): - pass - return shear - - - - def compute_comp_ref(self,PS): - """Amend this so variables obtain at start fetch only correct date, lats, lons - All levels need to be fetched as it's composite reflectivity - """ - T2 = self.get('T2',PS) - QR = self.nc.variables['QRAIN'][PS['t'],:,PS['la'],PS['lo']] - PSFC = self.get('PSFC',PS) - try: - QS = self.nc.variables['QRAIN'][PS['t'],:,PS['la'],PS['lo']] - except: - QS = N.zeros(N.shape(QR)) - rhor = 1000.0 - rhos = 100.0 - rhog = 400.0 - rhoi = 917.0 - - no_rain = 8.0E6 - # How do I access this time? - no_snow = 2.0E6 * N.exp(-0.12*(T2-273.15)) - no_grau = 4.0E6 - - density = N.divide(PSFC,(287.0 * T2)) - Qra_all = QR - Qsn_all = QS - - for j in range(len(Qra_all[1,:,1])): - curcol_r = [] - curcol_s = [] - for i in range(len(Qra_all[1,1,:])): - maxrval = N.max(Qra_all[:,j,i]) - maxsval = N.max(Qsn_all[:,j,i]) - curcol_r.append(maxrval) - curcol_s.append(maxsval) - N_curcol_r = N.array(curcol_r) - N_curcol_s = N.array(curcol_s) - if j == 0: - Qra = N_curcol_r - Qsn = N_curcol_s - else: - Qra = N.row_stack((Qra, N_curcol_r)) - Qsn = N.row_stack((Qsn, N_curcol_s)) - - # Calculate slope factor lambda - lambr = (N.divide((3.14159 * no_rain * rhor), N.multiply(density, Qra))) ** 0.25 - lambs = N.exp(-0.0536 * (T2 - 273.15)) - - # Calculate equivalent reflectivity factor - Zer = (720.0 * no_rain * (lambr ** -7.0)) * 1E18 - Zes = (0.224 * 720.0 * no_snow * (lambr ** -7.0) * (rhos/rhoi) ** 2) * 1E18 - Zes_int = N.divide((lambs * Qsn * density), no_snow) - Zes = ((0.224 * 720 * 1E18) / (3.14159 * rhor) ** 2) * Zes_int ** 2 - - Ze = N.add(Zer, Zes) - dBZ = N.nan_to_num(10*N.log10(Ze)) - return dBZ - - def compute_simref_atlevel(self,level=1): - pass - return data - - def get_XY(self,lat,lon): - """Return grid indices for lat/lon pair. - """ - - def get_lat_idx(self,lat): - lat_idx = N.where(abs(self.lats-lat) == abs(self.lats-lat).min())[0][0] - return lat_idx - - def get_lon_idx(self,lon): - lon_idx = N.where(abs(self.lons-lon) == abs(self.lons-lon).min())[0][0] - return lon_idx - - - diff --git a/postWRF/PyWRFPlus/xsection.py b/postWRF/PyWRFPlus/xsection.py deleted file mode 100644 index 5ec0f72..0000000 --- a/postWRF/PyWRFPlus/xsection.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Create cross-sections through WRF data. - -This can be time-height or distance-height. -The height can be pressure, model, geometric, or geopotential - -The output can be saved to a pickle file. -This can be useful for creating composites - -""" - -# Imports -import numpy as N - -class CrossSection: - - def __init__(self): - pass - - def get_xy(self): - - - diff --git a/postWRF/README.txt b/postWRF/README.txt deleted file mode 100644 index c565b52..0000000 --- a/postWRF/README.txt +++ /dev/null @@ -1,38 +0,0 @@ -========= -PyWRFPlus -========= - -PyWRFPlus is an amalgamation of numerous Python packages and scripts, with -some new ideas, with the goal of using Python to pre-process (WPS and WRF to -create data) and post-process (move, organise, plot, compute data). - -Where do I start? -================= - -/bin/casestudyexample.py is an example script that shows the plots that can be -created. The other essential file you will need to personalise is -/bin/settings.py. The class therein contains all the settings for plotting. -All settings can be left as default (by not specifying a setting), but details -like path to WRF data, path to output figures, etc, are essential. - -Contributors & Attributions -=========================== - -Here is a list of people and/or projects that has inspired or contributed to -development of some part of PyWRFPlus: - -HootPy project ------------- - -URL: http://www.atmos.washington.edu/~lmadaus/pyscripts.html - -* David-John Gagne -* Tim Supinie -* Luke Madaus - -PyWRF project (Monash) ----------------------- - -URL: http://code.google.com/p/pywrf/ -URL: https://github.com/scaine1/pyWRF/ - diff --git a/postWRF/bin/.gitignore b/postWRF/bin/.gitignore new file mode 100644 index 0000000..cf0761e --- /dev/null +++ b/postWRF/bin/.gitignore @@ -0,0 +1,4 @@ +*.py +!casestudy.py +!casestudy_settings.py +!settings.py diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py deleted file mode 100644 index a82ce15..0000000 --- a/postWRF/bin/DKE.py +++ /dev/null @@ -1,18 +0,0 @@ -"""This script shows examples of using the package to create arrays -of data stored to disc. This is then plotted using the package. -""" - -from DKE_settings import Settings - -# Initialise settings and environment -config = Settings() -p = PyWRFEnv(config) - -# User settings - - -# Produce .npy data files with DKE data - - -# Plot these .npy files -p.plot2D('DKE_2D',path_to_data,path_to_plots) diff --git a/postWRF/bin/DKE_settings.py b/postWRF/bin/DKE_settings.py deleted file mode 100644 index 62adf78..0000000 --- a/postWRF/bin/DKE_settings.py +++ /dev/null @@ -1,14 +0,0 @@ -class Settings: - def __init__(self): - # Required settings: - self.output_root = '/home/jrlawson/public_html' - self.wrfout_root = '/tera9/jrlawson/' - # Optional settings: - self.DPI = 250.0 - self.plot_titles = True - self.basemap = True - self.terrain = False - if self.terrain: - self.terrain_data_path = '/path/to/terrain/data' - self.scales = {'wind':(5,65,5)} - self.colorbar = True diff --git a/postWRF/bin/casestudyexample.py b/postWRF/bin/casestudyexample.py deleted file mode 100644 index ccccfb4..0000000 --- a/postWRF/bin/casestudyexample.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Edit this file to taste. - -The class PyWRFSettings (in settings.py, same folder) is used to create config, containing all settings -""" - -import sys -sys.path.append('../') -from PyWRFPlus import PyWRFEnv -from settings import PyWRFSettings -import pdb -import os - -# Initialise configuration -config = PyWRFSettings() -# Initialise plotting environment -p = PyWRFEnv(config) - -#config.datafolder = os.path.join('2009091000','GEFS','CTRL','c00') - -#path_to_wrfouts = p.wrfout_files_in('/tera9/jrlawson/bowecho/2009091000/GEFS/CTRL/c00') -casenames = 'bowecho' -init_time = p.string_from_time('dir',(2009,9,10,0,0,0),strlen='hour') -ICs = 'GEFS' -sens = 'STCH' -IC_n = 'p09' -sens_n = 's01' -dom = 1 -wrfout_dir = p.dir_from_naming(config.wrfout_root,casenames,init_time,ICs,sens,IC_n,sens_n) -path_to_plots = p.dir_from_naming(config.output_root,casenames,init_time,ICs,sens,IC_n,sens_n) -path_to_wrfouts = p.wrfout_files_in(wrfout_dir,dom=1) -# Things to loop over: -variables = ['wind'] -plot_times = [(2009,9,11,n,0,0) for n in range(5,7)] -ens_members = path_to_wrfouts -levels = 2000 # hPa levels would require pinterp rewrite into Python? -domain_areas = 0 # sequence of dictionaries with N,E,S,W representing lat/lon limits. -""" -Perhaps load all wrfout files found in datafolder. -Try to find initialisation time in each file's name -If unambiguous, that's your file -If not, raise error: need ens_member setting to differentiate - -For postage plots, specify folder and expect all wrfout files inside to be used -Or individually name files you would like. -Title of each plot becomes folder name -If you want custom titles (e.g. "Kessler"), create dictionary passed in call? -""" - - -# I would like to plot these things -# Accepting: strings, list of strings, list of one string -# Looping over all permutations of each. - -p.plot_variable2D(variables,plot_times,ens_members,levels,path_to_plots) # naming = some scheme -#p.plot_postage(va='simref',pt=(2009,9,11,12,0,0),it=(2009,9,10,0,0,0),en=ens_members) -#p.plot_xs(contour='theta',contourf='wind',pt,it,latA,lonA,latB,lonB) -#p.plot_DTE() -#p.plot_DKE() - - From d3569643fee956f40e621ec2bd9c1f9b2e560d55 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 19 Dec 2013 10:07:49 +0000 Subject: [PATCH 025/111] Ignored all .pyc files. --- .gitignore | 1 + postWRF/bin/casestudy_settings.pyc | Bin 942 -> 0 bytes postWRF/postWRF/__init__.pyc | Bin 12186 -> 0 bytes postWRF/postWRF/axes.pyc | Bin 701 -> 0 bytes postWRF/postWRF/birdseye.pyc | Bin 2947 -> 0 bytes postWRF/postWRF/defaults.pyc | Bin 779 -> 0 bytes postWRF/postWRF/figure.pyc | Bin 2290 -> 0 bytes postWRF/postWRF/scales.pyc | Bin 2291 -> 0 bytes postWRF/postWRF/utils.pyc | Bin 2633 -> 0 bytes postWRF/postWRF/wrfout.pyc | Bin 8855 -> 0 bytes 10 files changed, 1 insertion(+) delete mode 100644 postWRF/bin/casestudy_settings.pyc delete mode 100644 postWRF/postWRF/__init__.pyc delete mode 100644 postWRF/postWRF/axes.pyc delete mode 100644 postWRF/postWRF/birdseye.pyc delete mode 100644 postWRF/postWRF/defaults.pyc delete mode 100644 postWRF/postWRF/figure.pyc delete mode 100644 postWRF/postWRF/scales.pyc delete mode 100644 postWRF/postWRF/utils.pyc delete mode 100644 postWRF/postWRF/wrfout.pyc diff --git a/.gitignore b/.gitignore index e69de29..0d20b64 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/postWRF/bin/casestudy_settings.pyc b/postWRF/bin/casestudy_settings.pyc deleted file mode 100644 index 26568e3b1f504624dd330c3295613d2bad49e6eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 942 zcmcIi+iKfD5FN=*eCdS*8UpazoZg{t z^0r$!TdED!4v!>FCg?Z&{F-CD8-Io;l5I6@a`I9Yv~BWAlePz(vF}-t9ByZuDwBoo zpUs4b{3+>Rv z=)j5O5(ZldKP9AjdA##d%Luu{(mNkkI>gI~XlUGZ#uh^UlpEGd=pV0UvdYD}HU=oR`|A`$B{mJ>-_N^&Dp^pG+WR?K#|GrJsW zcXl~hh%5 zXugPsihjjy)nbh-3VZhcwjY{8Uc=XR*obZ2^vglzz;ga7zb|64r0HaUq{9D zsM2V~$n2X;C8+uNTe!a!R9lgcjBD#w(~ruWg59Xtm@{7Jyp6wl69sMtk*ECwQ?};D zmoC*fomx=#?RYzCw5r>-=f`CmaF}`+mz~U4t+6dXuax}MF?-&UZ7()Ec=%( zZTmrW8&h~nL$M!4ZV=k}eDE{;Eyq;E8`Ww7>!acvl6B|Sy;f}QhxoGX>;79aL34iu zZ)F-w2Y&xaBHuV~8e0nYf~XOeaVLN5<0dZs5fUGo4^>yxnT?F7r)d6T(R}DUpF{c6 z|HJBWR&5L?PjZSSbIKc$lGqSOB!k_6Dx>N#b`#W6bdp=)sJw-i9+UrL%ESM0z}umu?nv@>M8!wc4(1Bkn3mD3Wm&zaIjJzD zK7bA3pwJkKLn0&U8pOX^;9@Svqev@_FfKKtMpM)QdnESTq9$H5h>xNGw`wU4;+n5x zii7VZ!hxDz2>jam)sGxQj)|QrHhODmqOA-5plpYwF;{ z!1}{#XH++**28Iq)qqyszJtmAhNVJFeI0c=Idu~CI#7PoQlL6e-C;Z(QQ-}Fkz*Gu zF`^z0shiOURCBbGk#?S?qVJ%k3=yJ>lBl0oCvnd233Q~qEvD7u6lq0 zkE(87b&sm{Rc`*B5!E@WI!Mty#$DVzuexKZ15G(1)zMF0bw*WZjME|>?f$D;-~9)* zjxwvV)XO3|V;3DRge>JtdI8yG*jCrxb87+2uCbh48JN~U8*?59b^i@Jj$nJMFjMoQ z0VOX$Wff2j^mQb~Mx8b+fMtahhINhHuF_jY9MpfB&YY#=eDhv5_ZoHCn4-lpByOjDDW+SMO3;@6gIJz4BW%64jKIj)3@d5?UpYQ6h_uX!pw-KT zZrwlJ{+VRYvUDY>kFE7G9_g2mC@W`;VJl^=6IRw5w(?d!GnN_0-LN&9nY1P{IqR(T zto4Fb<^(8L4(K*6{S_q8RA^^$bDByV<3?2Mn;Eq`qPkfs5^bq5j7FEU>f2@?4nh0a z=5m3HB2vRTI^5yT3rv222_-BR3~JMC#U;w5z+o7%b1-gH)uIuuq&2C1c+o((Mspxh zXwhA?s5v5qiJ!Jajmx~J$)jNudoqs+S{xxkdoW9~I@7G_}__x#XUO-{8H2Q}@SMy|j?$jqHzWbP^xHsQR% zLRdQiXc)>)o`&#==CK}+a>~LEC7hyDXf2ut$AFH)n&Jt@gG@4* z+jn;N)jBM^Sw{Uch-C$kYP&WlU^R$a9(Wumq2%l`ncgMcqk4rc@Wu=J5}4zqy};jp zab7qfY2|&GzMF<8+HnK*d^h&(s^6$aZgV>*gTRtdrsdPjo4vpEfwT0^{kNQ@V)`I0 zkv`dTK|=T3C~&uGenG#OzG#vblOfo&RD*BAGT!(hh%Bnub^Q3z;QQJh-q-v+zlNuF zzrh94WtvLPiY@DW`Z66D1;D?1D{F7vTfTAk(OU{%x!Au)!WEXHRviCZ-#B#?_cg-g@6+AV59CWUFcpk zV&u&$xZ&iOf3-WmEZ$hmZHXb3w7=W(;WkMBO>awJ2Aro9M=qX{j=|@N;zpyU1yMi? zy;eDPI8KK<-Plwb8m`Pj(Rq^(XS701zO*Q;~--b;oKa?5+ z4T9<>jcdEWM#+A$Rr~=9EG%QWz!?i;4RjEi=zwsaas!4>%$pW(Xf?3`RGuy1hatdH z->f37kVAqdOLcIl^>MDF{C9DgB^k+C|nSq0=aq zMZg(mROI;)p7!X^s3mSAEjcwrruoar7@7wfYlsF|&zyx(lC&6qs#ic9LY4uOK+M3n zW+(*bEdx=224hzyT;ui~kqtXqmc`d|$gaW%N7%{0#eCR2AbbKR<_k3IeQGR9pqn)) zW;+)1Ep{J1m|+k?on_^hMOgIhjcw=bjBTf`ky|)iO6Lj^<6#iB52Y>!c+hf*&HObQ z&{Rj2Ii4BM%vw_!gN*dgyr5Dk`Jo?G52SFJ1&`z6uL$}FFupKc(HCU*3Pps}#&|FO z1Emft7)q(J=q61Ze;`&k)kES`1Ng8=V1NjxWYz1;A<8tQUZ-6&#Ly7ZqY5I2lidEe z7l6UCfZL(N;^yD0?l@n=UG9vlYq%a@JttImk~_0AAry8>@{^K3raDtX8jmrB)35(Y zdAY$+0gSrSf<#Br0gwoGY+CK015_MWof)=+|JLqL;go}QO4oA?^T32df9B!|1!P1e zXdEp(#)`C8?1;Ly`&hjB=$-``j`3#!cEh~JULX5u8I!r%Z zf~_DF1y^srg&mh9?h4d%NKwx?6{51{=s__9K!L`(Y6JaZrYNlIfx&pfh3{CdNzCo>qD*#GDYvPV9Y}X~qXlA5X91x}aP2(t8(( z7Lsirymuij*qf3m*pr7TDpT$kY$3W3^;$IfT2K!JCiXz�%g^;S(@)oOV!a?5Fii z{&4k-+{_HzEiF790M@WUuag*$lu3J{uyk*PCQqrGb(b+|`;u*d(*#%2nqG;v$Mhw+ z0h*2;D-toW3yG^>?)xk5l_G&?3ftrcd46FnY6(=|!V>y}wJMB`h~&#H9pglS^Wn2} zU}*+{^QWvd%^LcZW<*_ZP7y#3n0f-^3q>&X$UXQB1u}Y)Xh^btsGh88?H~tIlhB-_K<{ftEB9b*xNH|Gy_7iwK?4h%qScrhT*BFR- zX-l+Lf-BhWEnhDej-eUn8%!QBVc1Ff^3B$=tk>Yz!&Aa`)casxYY7L>r4Y)(ppjHLA;t z$`$joAV2HoZhgTBl*5Sd%~K^PFTa4(d;wX$!sn= zC+^*tHI|)bFft1|YCV&gu%@ij2v82;?gZSw8LLbo&EleaLB~aZ7kUENge-+Nh^N4t zjBp5GXz~Vx%-rw|9418VvT`(_llnt=0Sq>!c!L-b!cjPZ%8@7-44wr)ATtpf4A`Cn zSs>_7srGONvNOc7xcMK}18|9BWCJ)1U{v%NL-fFP5&$w8pe@)z`^Oe!+!20J-29={ zJuXAb%g_!i5C*I=;#@cyBO5?#awzsKgyfP;=eVk1xO4S-al@Ihz8DVr)bE(9P7=E(&uJn;~vv!it_kp!$|}wN=+PIMt`k`o$hj z{`X)}gxBa+tH3l6EwDMBq8o44vt14QO!$!4QSS_#SZKDej2Py&jPnzK3P=^tbt*RE z=YcM_;J=8K#rsghIJ1Xz&cv6xTg4d?Vy05dV1uc`ZwS~A|DH<0U@)ZsSpjI~V)2%k z`m`bp>F2(WX}eH@^#vF(Q}7au29%_~25EbPOfDg|z%-$8X`z>R43RX7P%h7r41B(9 z;8Z}A;mLgjcDEt1@Is8mLiE%PW1I9eZ7a?EkI6}2Ma2EPFX_9+Vv}gg{ThrD{{C(E zy2<>?cdi>~B?#W4ET9q8E-WnN!{8;Cm!7CNs{$rHNthnOQ;80-b?4uzI9(+#I%qowd-V3-SqJm$25`!DSJlJ#tfJC+w=IBJjocA$L+)JWiR-mm{hd2l4 z(iGzx09_veVL;_{i$~~xm2{IsDR`)+6mO<@@(rv5&;jlMbg-$jKnUZ(^W1C{r2uRI zLfqtWGs_47Z;s*y-zJTz#{kijs)PS1IIAiWZ|R;AaGg`_bA#YIlX(DeMG*9Ka-<}6 z0IS_Is{K=|a|+J)Pps~m>a?T-vA_{5`A5atZAK6#E-PbCw<8h^%PsUj%WIg>P=K_uav)nDCn66gBA z7zdydbSE8WGIT^NlA-q=$*>LjJ~?g-eZ4a$Upt&J;gtUb@Xi5v8G_Q2*dVE0{a*rJ zI?QtVrBlx%b>)l`busOd9<%Sl@K+;zO3}pE12Mu8{E{vWLMJX&--xHRe_p{FfG2Cax_kc`CfL5paY$FuC{bN9~|5kWcmZ|BECT?J&%P zc?yN6rx$I=Bb~nC3#QaIJ`H?Iv9$c*Si+~Z%lQx+!}$&p0k0K-zLlHKZ}ai*AbAQ; z2nd7{-7OPb8A;})c`=aOeu?o``4S0-Zga$l;wIc45M*(NV8=iI=uN;kMD*VpqX9KyZFKG5K}A{WT^(VDkG+{(#9JA~DNg zVgf5S4Le!6BJ5-3<`w6USniLR{0WmkW%6fCeuzXydO)(m6?qRC=WqC!e1V1sU+Fr3 zj=b{`6FS~9da#;>qDagX5@9<(N?xy&Z~`T5Kxrjz!SlCnVU?ZKUv9r z&i9KBHIL`l`V5ksH99rHJB2j+G!VjS3GXfJ{>V26l--((J2{_%uOGjF{X4d8_JcX8{{v6ho UY$*PVs@R^oQK|V}5|1 znW$X}z2r_N^El_sB>LQH?|rj(hTt=Xf zNH2yVtyG?n7b9*VG%V*MKd!S<5_Vxaoo-bIzIK=00R(lbM_xxbP5s<+nu_KiTx671 zb|7p@dkBY#rr3ZXw!{&QEJijjmYyx-NMn!JU_l)GQyxn%%806hRTL77yPSpR-e)|~ z;3W6s3CB|BWF#j*+P2aFDmv#yC6nlK$n2Y%yB=;nnB{58ZRFE*R%VL=pHBHCw%n0= z|18R(QkS6=Ewd5TeX?tXo(;GvN-q2@%M@3<1G@}j*OJzWYuAZ#gfxwUDM2XmDXkkEjNgb*sd@)V)XLm{fP+U$;#ara_&#%WU7 zF9eBy!aFbgj{X+l8Tif_J1s9z+cSISc5ZXN&s+OrX<_rXU&Dl^pBkQb(d-ZCQv4KU zM90^%qCr5%HCYNMt5e>f;|3|-TBEE<`5Yb3`Ei}HdCFUK-11|nTAn1GdQm!U;sEQ+U+l6eM+Y+yb=wX!b*NV?|@42tpu& zA{VgxJXxbLeh})c{z!Z~>@{%8u?P31e-cVm+&yq?XLvB|u^Gpm)mh(hI7wOb&8djb ztA06hA?Klo<~w@>(%#!JG)cI)?<*gfU#j!Yr+%hElR28qbKHX_4dys8SBfSr8Y2w> zJqxHRxiBWU^*T-JRN$f;^!OY2`J-2ly$wF}G@!{MP1LD zE@|JO@j3^u3aO$2c5hPki{dM}LFqE3D@eEE<*9;i(c=cZi zzR4S5gNt)Za(7F5f1TSRZ}A;V8Uh;Z@C`Gfk9upUlS9KLWL~0X3R%DNeh#EQ>F?@rlWdyX?BMn^Zb> zx|@uwEAwvOWV(oRZF{>o<58G7(FM>kjW947e+&{lt$Cr{666z4M3L2QG!%DF;BF}H zwy}PyUF1xzBa>dZ7A78EBzh>0a~^p)Gr2)lqO{CoQ-~!@OT_jp9%^yJCUK^%hImE9F9+0)aPW0PA>^l#phZ$Ng_zHeve5d$xvtSngMI{NlVE&%Fc5wbWcyiiVsBs;gVHOYO*e4W^j1XXWF zrEHyZbnrm(co#zoR@9E#s;|}Bs;OE*Lp5ugwYSx#T2f23n%Y%0^+B+Q)iu=t;jY)# zR5MtsVIO98Rl?NIj;x~z0sjsGXlG)1jd4N10OZ6oWSz;0H$Z4It!`~hW?ami>sRKd zD>XvxfpWpOU|O&Zm=u3aAkyhZkg8{Fr~_ewf4u_GD;U+wOO!4$U4qL%Sy$8ob%Q#= zkW39MF)w+kGn1xgS7%BA8LCjyM*bZcemsl1YIU(X!~alk;C0T;j#BPIqbMDC1{Wd4NV z9$$%o3!sags{~vj7eV+ncY<@SJ)oQAizqd@R9Jc`6PG*@V#TXTbP>TfQ7JN%3PP%p zhNrh*9~?MNNLt0ulQOn0GX=_&6Twp!?qJ9iY+FMOc^$G2V!{v;>CYvY3{f zMV>=^)-d2i=i#Tk`!jTJgokh&J0GLjEp!y@1lwvm;JSWS?F0)7-^N?Yzw&ULQN+v< zMG{98JMV-<#Y9?;2*2E6ePeadvN7f&S*$^K|-xDQ|1xP0WOR@8SF0u@`Udv|RF+ UKK8FT>y|xks}?v5EsxM(gWIFEN|_!mJ&OPrz%C<;D@;I z4g3#3z#F%~5s}ExGvk>zZ=6hiURIC4e`{8Bd?wgmVx^}5OSA+QWKYPJ^p)rf(d`oc zgckLwq`d>EH?B3?j#YBZKCF}CjbUX*r?$yiwcEIWQ1(V;qf078WDjoT+r6ClQN>-D zR~4Uw??{*ah$;|#CEXqZ97I6@iJWCg&7zj$m4GZ6;5Amd1wi^|B2DD;0FZr+QPwCg zVV#nlVml*yPS=u$_$(KcWUFjUep8_SB1cd}82%pEZ@e^2m4FH9S#qOqc?!2Rz3=wv z0nGRQ&W(nB3~GU-d>rD~n=YF>=hqwd_mRmbLgYU=kDdGv2}uKA3uVr>u{S>O41OB} z>tT#TKEOS*`^Yl99XwxPAG*kzJ$Ei;|IQ@Wn?a8*W(>twM?ndiA+HD87xrF<--M@M^ m$fo?4l`y8o&7o8rJ_BfN;|i=W|ARFPe14GhLKLUWUj734RGmx! diff --git a/postWRF/postWRF/figure.pyc b/postWRF/postWRF/figure.pyc deleted file mode 100644 index 0390d492905e03af5feb045b44a32a0e1f08d420..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2290 zcma)7-EQMl5T0Y_XOpIMDHMS~A_5^ImhDR1AR)A%?e5-@3m35>5?GFXk~%nXHs@rw zMXk6fFTx}66o}{H0pR=QBx!Gun)Zz6jE`sL`(`|j|Jm>U{nwvKE}LJB>$h0$9HI~z zK^iF{sf>)Gtscv$vDKS0Yssjs+gPf5GVjQ!BWCkhS4Q3K(LEXMZS|gvdQ!kkONyra zE^;mM<7l6uP6usf@EOEOT~#*sWxeRvlm7X`i+;YUoqOmu#=Od$v+lUtE&s)DI52p~ z^YJH{v-51}ThO1E)Ah>kF!Ib3EcY0~0wVwlQwRX5#LR;V5{y-8GFe)-`2uosI-vWU zq;b*l36^U>6apZ$3c=M}0wYm<&JrLK($#Se_^!P9tLpKlN=oia`ku}_eZtlVn zL(=Idys|;W?3<%V=K-@Nq7y9l2LyU#^_#X#coDR5-s21aLoneViq3nrVlo7)wihO$ zT0!XTvyEVF5(OI!S_MtrPzTPX`_@(~~91ehuaESJfWp z_xs$*&u-NM=#2^dUBRjMEC2zpfzkqDLu5hAh7B7mT00Oquu%X}fSU*^f+(_5f)JMj zpaA3mh!UI^r+V777=-i60$G6OArLN{CB)%<+u*)!2zQ<4T|;niS06Eqh%<~B{ujdC zk1>LXQ4nJw#zEWUsiEv3DNili4LvGG-`QHhdt(@#Ae_>n|!l6vhZF-FB<#dTj^ zy>e}R&2_uJ$Kz>~T|ZU+CI&Hyw<$BU6mHb*Ln7Ig(SQ8zE7#VuU_q;or1Lwe*YewL z5Op;<=nT3a**TDQ-OC*C8E<)MX~|QuP=Rmowz`ux{6OMGpO+246B$oSzPk4ORCUPPOQWY6*5(oO2yMjOKv6G zuKF8C8JPrg1jS_)OFvzcVN1q%&PAdU6Il&xbe3>BI4|PVU!d5z1O_)+unRD~Yr*t; z*#8#1ZbNzwj15iapz{NOJsaZTgEll=kI#T*Sv#{}3V=Z{#$JD)1Ji_g;~m1_Tf+tF zH)0EJ>`dj6uqUQ>ZQfbHc#jd*6rovyCiYZ;9YrvQV7J%<(f(aL&W47K{ZzzcRu$!j zuM;yWpa?K;L#C!QtqUwy4RPY9ejG|Oj%ou7StkmmHg&Ev?`lT|>7bZ+ z2>=AwDSQoeC{qabc76Vdy4m^IJ7xfV6s{h1YkUU+3)!d#ri>b}(Da|FQdw*#+FY+) zTFm;s(%#|QWrtC5wTA)U}*P2pghl-Sz_fn_Qq_5nc0s7P~6u1VeOv?XI- zcr%b~qmsBbjIc80TpOZYbVk%=;Qt`HM)1=(^$ZHIM(iP^6({}cFE?#Zqs9@ zTd4&ScX4lxA0RivmNdT1QdF&aDjN#FGt|4-JxQxg7A|W$m4Rx9Z%=&s;`eXetcJH| z-~Z+6?T@SBM<3GoBvX`B~?-pKZ=M@ z+#OUWMP*Z?BUA@Q;aa|- zZ%d>$EBxKj{H6_Iv+sbR?~2Y|zp2qpxmjwIQH{EVu~^ke2BsbdBJ1z0yeGMPiJ9eo y7qWKmH%|neZzZA=$!F01%r7h*Ag{sI1;-qaFfk73brY%BoD_d=$QX6e{CdoARuCp^v zYbAaF`pA2K2M_SZf8Ymz#DCxiz<18B)2b3`BhT#FGjnd=xonF6%*~ws?UznO`Ckdo zFR|P$Y#KjBJ)-*{3iK3&B3%@sK)N*fmFa1b?iWc{n!gJ*b?X2cjrUz zr$O|?eWPvasgw0CM*_*H?$$_FQ8y^6aBeM^S1*Zz_0ktc_Gr zEntb?tc-1m-yee5CHAiM;53bakD^C-R~*5^0v$}z7|s{qF}vAzQ`nEznrrKNocYE^ zc9OIo`Yam0;BDVHbKp5h@A<|c)Y|CR2%vUyc}ukLyJL4reV{BqqcS#*iY-YHU%?I{ zfb07OO5fvTXEFRtG%iydC_XL9=|c|2UFF>0xT(t=U5vvD9hdn8QJ+QnPsGo%{NJK| zTI7GH;}W?M=_-v&EQ(jqZ1@@Jndg!aJh-a(bXHD(C0%>@RL{YQ_y|EJhc*t}z(jV(8vS`NbT;0d@XudtSSb;%+DvjwuenvWo9SH1ZLH)r zw_ft6d2~g55Zf?37=N+!a2EOK$Pm0iq2dC5wC(R~C}UE_l&i4RTe zEigdE2O2HL1WLadv^E2o6%u$K(eAap_B#YS1lH%PtI!Uj9M?0RVABhJe}Ad83><( zurjqV3O3OB==Ke?1=@TA-Hoxnfu2RTZlF2QmKzv+Xdg_8bKUwJgxeYDjN!ym&UfPnWHTNr}q@N|iXU^gFoMCo~kK;FyQoM9~Zoke6iq}Lgj39BkvzGsZO zz^Gf7IoyJo;M$&v_5+`wkWKJ-FZ6*Qp}Lcupl1$EFR(61&B4$(Z*;IdLd%=9F%#r} zLP+S}`WdLR4jq*y0~^^tftP&ztV^~5dSw4V5qd5}H%xcJ$Oz`Q@7{4NdHe3lgv~hY z_obe9P0}}LhNzu=p17(_tleYl_c9J&?j61wcX3Zb+Ts8|gP2>!Mx}XGLNR8G>3J2g zQZ5880m(=Jg1`JqcKQCJ!rnw~@SA*6Ifw2UHbLc^7~{(|g+>z#U`_0drcD7Rm_sAS zVVYiQom>D3MmXXe$Q>t_WVEEV+boR0>bseU= zC~e6?xb3o2E^={4*f1|oSa}VEIkmazn;^3SpM6Kw`JjVE9R{?+--iwlbm@i83R65S ko|_QrfB!3*#@#Rai5~XMRl%IAxmK=Ls-?vX)lzl-KLjKTPyhe` diff --git a/postWRF/postWRF/wrfout.pyc b/postWRF/postWRF/wrfout.pyc deleted file mode 100644 index 1d036af4015db774bf5d0ab7570b3d37c2823ee1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8855 zcmcgxO>7*=b*`Qvha751O5~6fC0W}tb~m#&!(GXaVF>cBWOCQuU@eg62vf!1c;sZkQ{t4eDF278on4dY`})$ zYA$RSFc{ZdhdI$s*Asvomu+!KR69F{8z;9bv!ym;VZR`(o%jw zwJp^tsO^HX*ecD)o+1Z*G^# zYpXWJDZh?lBkpv2X<)~R-Htuq_Px}zcaykdM?t!=`HH<8wuAMVna!{hL^_NkZO6Oz zhd1p9UJ`mc?ZCckdkM;QKc~1lQ@f4E+D@FNamP*L?wXE!Y0Hh`ByFu71UhvC4~0&> z=5sSbYqx_3L0hlcZ7&Veo*$qVM@^~Dg#V3yH4EaV6nqmh>L9JBB{c3hue{z%bA1-j z^>IA<%P0cq6UtLuJv3<)`_K|a7NryFnUKolP)XEfNqV5jDXBn<(^7%%%2Jt8{)|*k z3C~lCJS)ntNaw8bXQeWy{5h%2D`%@V4}lsKj}Nzf1CRbKid3m1h-s;J3hGEH*J8P6 zsXd5XP^%Z&SIqh*gk&;nmZUkAH8G;7Rw3pp2ThN4m(V?9y2~n^$=;k&@5pp+Wvp{H z>zo_woXk9D5OIu}&BnDMMqbE`wXOYeR$oxfP-#%$G2K>_uYdz++_C}|Bkoj~pG zYd3DwE7ZEYxcl1c^-rW_v~vSWIe5&pYE-QmGG1LluY*UWd>=K0NoK#=uuOK{RAwg0 z=3Sxm59Md`P1EX&RIpDY9?VmVV7qXfR19+8>VW1Yf>o>KtlD5?ksD;1h}LG1~otqvv>gx!Nx!yyZ*S;)*v z%o}tAOC6rGpg<6S(dNC1HJDTo73RKICd(suFq~*YeXgK(;WeP*jHO42Kvz0OH1!A( zoPYWl(exukz=N4%L~yNRG65ng$B1SgAp&H~9V0rGX$~sDvf;{DCfxRvYLESTF3{X?>(EMepahm2DciacT963B8^0uo}ouuotH@g^kvgbXkt z?Kq^Dg+!Qemc<;4(=5)gSVU1{pZH6|gJ1eyA|Z*hHaiZ-i|6T<*9`>X>eBv9kihAv zYB-&N9XII4jh4Jkyr>z7tA|l4F1#0`M&S;iEAz_R(L&O}8w$%Qseu~3(7DJS3709` zvMH|F6u}^bUztweFcr#BFs`2%u4zkteXlRwtwiL&WM)VTkAE2&()T6%#38!^sQu5TFI?KN`j! zzyV+faTHLq|05OsozPDROB7P=6nLX1@}ojh5}%@PTe0 zxmDuz?FIsd2SkpVqr8im+Inu#d1b2|HUisgG~&c3eHw#Dvxn?0)sG?HM6mUm%^zGtZFiB#wbpJ49QD0|ij5gpwZ&tgLsK_ zXcM}{|Kw$A4620?e1pTn@CTXG3iJgU3zHF9<pX>Q;uTi_g;6p!!^a)H^jT*@m3f4JyGE{%^$m z>~S$YBW-M^<=kG>7p(KTU8GbZiMW?g2yTflIk!Q30yR0eK=-Gdb^(P#)^QHmhI!6d z)q*$?9f)&p;L-F;xD1@=@ETo~K$8OxAO}XedGC^XOOgznT&P227$ue1#2<{?xB35$ zm@(PvFQBkri{twUqFoy}GIp0TcF$rTglP+>Fu96h@P0y!&jNDJh{?hZVtCp@+C7Zo z8p-!CUW3&rIEgtRj@N5kwd`*_h7h!OCf-2;L>s}JLx&SmqkDpxAR>7SiqZj0e(DoQ zPqHVkW5^frXgax?ELqPYP|TXWpu*^BvKKtMQ~y~wSmjwzQn)R-52_ohm?Uac9FOK8Wi6U4l>Bp${bw|=wLm;(o~P+RTX+Dfh)P7?2`MMlVNYPT zXe_45!|0KumiNml>Ck9!{KW=<6Ko3qErhD$UHezwMF?XXqV$MXWEu8?{0@^~u}Ok6 z$W~XX0~{fz)D~vilD*Vj?3z7F{%C0SK`TV&83lolgIH?&ICeB2liLX`(jv+2#*$Q= zx8#+vKQZ6O8K5V}G&9^g#F_8+(C3zWyA?o4uYKSlFKi((9Lq=TF3(7Ee)QUH_(2i` zw#JP|=(WfFt>zsD`eT>ZuB?6NxyLMRlWNd#!M1Pj1ia?lfj92;bPASWD7kf(B{Q`~ zvfM+u-HW0CX`}WM=IXpr#msRe=g-kJa*ZiL16IO+|Mik_LT3jJ@kFGb9fx-6xGX-6 zq9(@8Bb{Unf|s7TnF2ftytH2!{$6$h(8;wJx570VI*Hx$e(W#tm;`R7VoemvR>>;i zbXI{EEEkrob9f|y1A;F79I&} z%7DWqw5bo7`aA?N;9$ZST!7dhSOJ`1+5-n*jsv^Lf7}eQ4Ts_a9Tj>jFb!N@Jj^s(YUdp zHKUk+62+1_I-~Y*SyeTtw;+urY3z5bJ6valG>~_&EbP!+kmicQZ2qEP?_A+%Sq;vr zBV;tIYX6T!_fQCWmU#)t;kbbWH*~I&Y}hHpG7a{j%6qjckhw}qXi_*3Phmq#YH@H* zSYTV0$G2s0UilD)L(eJytXi!4tJtdx%D>24y1bXScV)HYpU)`XC+yIBo_)Y1Ed_9@{4RhU;Wh^M$-8q5HBhz*aVv*^DNvkaz?YmI-YVG084%2q)ZUe=hd62z#gZgwAP68CkdHF_G=m%O9F1=PX3aMn zi5LV!Af|A@wE)mLO#eU&7{)rp2PPbN+h7crYdHdmuH=5tM__?3n`p&<6x4FxI3;P*O z-O#9??Ozu#4l#vlMxQ_bvtM5-XRLqtQgiDsE`6o9|IP23S>OJA_Ugj-n(9C5&Cb7m zbEDb+$+MTI|NaLr$vHjzA@UZvHf%ok)>r@Smo@+TsG@$Sw(cKY+N?1RbzWm3z|OQw z4(JYV#+;{7zzmU!9d;!R_MtI9FfoCLYkAr64kbW{;^c}*(zhf&;5W6#eM!(dxOfe_ z?Y@lgd|yt#kr&~EoQphf2+PgOFNmRxW!7Cz!xZe6BlqzR1BYU!pMlWf^Z8sTjXy-$e zaS8=gkv#CsVAM71a&&a_f$wUX8%#_oYPqFN=Gyi;J0?7_W@xq%u(%W3$YMAXe(_Z# z6>V&rYgqr|+b4-+n;891c(iSp3;5OddBBU^^}=M7 zFb&U~`##4{5?s-6 z$Zyabo>o5gm@A?Gm!+MC49A!D*XT1#i=+3vG&AIP<4db?5&;2MF6>-a{M>arvEOS4 zuItz!aIUftc->%)r+SdK}gi$x_;b%@Prg|_W!WJf*7^#Yrz*_ rD~(gW0hs9UevYTncKsch`=o-3%B~qDy-07+b0|u Date: Sun, 29 Dec 2013 18:58:25 +0000 Subject: [PATCH 026/111] Changed .gitignore --- postWRF/bin/.gitignore | 2 ++ postWRF/bin/DKE.py | 18 ++++++++++++++++++ postWRF/bin/DKE_settings.py | 14 ++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 postWRF/bin/DKE.py create mode 100644 postWRF/bin/DKE_settings.py diff --git a/postWRF/bin/.gitignore b/postWRF/bin/.gitignore index cf0761e..a5eca29 100644 --- a/postWRF/bin/.gitignore +++ b/postWRF/bin/.gitignore @@ -2,3 +2,5 @@ !casestudy.py !casestudy_settings.py !settings.py +!DKE_settings.py +!DKE.py diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py new file mode 100644 index 0000000..a82ce15 --- /dev/null +++ b/postWRF/bin/DKE.py @@ -0,0 +1,18 @@ +"""This script shows examples of using the package to create arrays +of data stored to disc. This is then plotted using the package. +""" + +from DKE_settings import Settings + +# Initialise settings and environment +config = Settings() +p = PyWRFEnv(config) + +# User settings + + +# Produce .npy data files with DKE data + + +# Plot these .npy files +p.plot2D('DKE_2D',path_to_data,path_to_plots) diff --git a/postWRF/bin/DKE_settings.py b/postWRF/bin/DKE_settings.py new file mode 100644 index 0000000..62adf78 --- /dev/null +++ b/postWRF/bin/DKE_settings.py @@ -0,0 +1,14 @@ +class Settings: + def __init__(self): + # Required settings: + self.output_root = '/home/jrlawson/public_html' + self.wrfout_root = '/tera9/jrlawson/' + # Optional settings: + self.DPI = 250.0 + self.plot_titles = True + self.basemap = True + self.terrain = False + if self.terrain: + self.terrain_data_path = '/path/to/terrain/data' + self.scales = {'wind':(5,65,5)} + self.colorbar = True From 20dbb1cfc6a8b465e90d38c64868cab55627a7a5 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 6 Jan 2014 10:47:41 -0700 Subject: [PATCH 027/111] DKE work --- README.md | 2 +- postWRF/bin/DKE.py | 20 +++++++++++++++----- postWRF/postWRF/__init__.py | 21 +++++++++++++++++---- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 254b128..db96c34 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -**N.B.: VERSION 1 WILL BE 'RELEASED' IN EARLY FEBRUARY** +**N.B.: VERSION 1 WILL BE 'RELEASED' IN DUE COURSE** **NOT FIT FOR HUMAN CONSUMPTION UNTIL THEN** === diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index a82ce15..f99db6e 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -3,16 +3,26 @@ """ from DKE_settings import Settings +from ../postWRF import WRFEnviron # Initialise settings and environment config = Settings() -p = PyWRFEnv(config) +p = WRFEnviron(config) # User settings +init_time = p.string_from_time('dir',(2009,9,10,0,0,0),strlen='hour') +rootdir = '/uufs/chpc.utah.edu/common/home/horel-group2/lawson2/' +outdir = '/uufs/chpc.utah.edu/common/home/u0737349/public_html/paper2/' +for rundate in ('25','27','29'): + foldername = '201111' + rundate + '00' + runfolder = os.path.join(rootdir,foldername) + path_to_wrfouts = p.wrfout_files_in(wrfout_dir,dom=1) -# Produce .npy data files with DKE data + path_to_plots = os.path.join(outdir,runfolder) - -# Plot these .npy files -p.plot2D('DKE_2D',path_to_data,path_to_plots) + # Produce .npy data files with DKE data + p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times,upper=500, + d_save=runfolder, d_return=0,d_fname='DKE_'+foldername) + # Plot these .npy files + p.plot_diff_energy('sum_z',times,runfolder,path_to_plots) diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index 282e9cc..e13f852 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -167,15 +167,24 @@ def plot_cross_section(self,var,latA,lonA,latB,lonB): def save_data(self,data,folder,fname): # Ensure file suffix will be .npy - os.path.splitext(fname)[0] + fname2 = os.path.splitext(fname)[0] # Check for folder and create if necessary utils.trycreate(folder) # Save in binary - fpath = os.path.join(folder,fname) + fpath = os.path.join(folder,fname2) N.save(fpath,data) print("Save file {0}.npy to {1}.".format(fname,folder)) + def load_data(self,folder,fname): + # Ensure file suffix will be .npy + fname2 = os.path.splitext(fname)[0] + fpath = os.path.join(folder,fname2) + data = N.load(fpath) + + print("Loaded file {0}.npy from {1}.".format(fname,folder)) + return data + def compute_diff_energy( self,ptype,energy,files,times,upper=None,lower=None, d_save=1,d_return=1,d_fname='diff_energy_data'): @@ -249,9 +258,9 @@ def compute_diff_energy( if d_return and not d_save: return DATA elif d_save and not d_return: - self.save(DATA,d_save,d_fname) + self.save_data(DATA,d_save,d_fname) elif d_return and d_save: - self.save(DATA,d_save,d_fname) + self.save_data(DATA,d_save,d_fname) return DATA def DE_xyz(self,nc0,nc1,t_idx,energy,*args): @@ -398,3 +407,7 @@ def latlon(nlats,nlons): return DKE + def plot_diff_energy(self,ptype,times,datafolder,p2p): + DATA = N.load_data(datafolder) + # dstack for each permutation and average for each grid point? + # Plot From ec394f6c0f9cede0aa664ebf5ddb0ecc5e56a1e7 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 7 Jan 2014 08:08:13 -0700 Subject: [PATCH 028/111] Updates to DKE --- postWRF/bin/DKE.py | 19 ++++++++++++++---- postWRF/postWRF/__init__.py | 40 ++++++++++++++++++++++++++----------- postWRF/postWRF/utils.py | 3 ++- postWRF/postWRF/wrfout.py | 5 ++--- 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index f99db6e..a8f50fe 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -1,9 +1,14 @@ """This script shows examples of using the package to create arrays of data stored to disc. This is then plotted using the package. """ +import sys +import os +import pdb + +sys.path.append('../') from DKE_settings import Settings -from ../postWRF import WRFEnviron +from postWRF import WRFEnviron # Initialise settings and environment config = Settings() @@ -15,14 +20,20 @@ outdir = '/uufs/chpc.utah.edu/common/home/u0737349/public_html/paper2/' for rundate in ('25','27','29'): + print("Computing for {0} November".format(rundate)) foldername = '201111' + rundate + '00' runfolder = os.path.join(rootdir,foldername) - path_to_wrfouts = p.wrfout_files_in(wrfout_dir,dom=1) + path_to_wrfouts = p.wrfout_files_in(runfolder,dom=1) - path_to_plots = os.path.join(outdir,runfolder) + itime = (2011,11,int(rundate),0,0,0) + ftime = (2011,12,2,12,0,0) + times = p.generate_times(itime,ftime,12*3600) + path_to_plots = os.path.join(outdir,runfolder) + #pdb.set_trace() # Produce .npy data files with DKE data + print("Compute_diff_energy...") p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times,upper=500, d_save=runfolder, d_return=0,d_fname='DKE_'+foldername) # Plot these .npy files - p.plot_diff_energy('sum_z',times,runfolder,path_to_plots) + # p.plot_diff_energy('sum_z',times,runfolder,path_to_plots) diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index e13f852..f63a7a3 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -16,8 +16,10 @@ import matplotlib.pyplot as plt import collections import fnmatch +import calendar import pdb import itertools +import numpy as N from wrfout import WRFOut from axes import Axes @@ -47,7 +49,7 @@ def __init__(self,config): def wrfout_files_in(self,folder,dom='notset',init_time='notset'): w = 'wrfout' # Assume the prefix if init_time=='notset': - suffix = '*' + suffix = '*0' # We assume the user has wrfout files in different folders for different times else: try: @@ -236,23 +238,28 @@ def compute_diff_energy( # Get all permutations of files for perm in itertools.combinations(files,2): + print("Next permutation...") DATA[perm] = {} f1, f2 = perm W1 = WRFOut(f1) W2 = WRFOut(f2) - + #pdb.set_trace() # Make sure times are the same in both files - if not W1.wrf_times == W2.wrf_times: + if not N.all(N.array(W1.wrf_times) == N.array(W2.wrf_times)): print("Times are not identical between input files.") raise Exception + else: + print("Passed check for identical timestamps between" + "NetCDF files") # Find indices of each time t_idx = [] for t in ts: t_idx.append(W1.get_time_idx(t)) + print("Calculating values now...") DATA[perm]['times'] = ts - DATA[perm]['values'] = PLOTS[ptype](nc0,nc1,t_idx, + DATA[perm]['values'] = PLOTS[ptype](W1.nc,W2.nc,t_idx, energy,lower,upper) if d_return and not d_save: @@ -370,31 +377,31 @@ def DE_z(self,nc0,nc1,t_idx,energy,lower,upper): # Generator for lat/lon points def latlon(nlats,nlons): - for i in nlats: - for j in nlons: + for i in range(nlats): + for j in range(nlons): yield i,j - gridpts = latlon((xlen,ylen)) + gridpts = latlon(xlen,ylen) DKE = [] for n,t in enumerate(t_idx): - DKE2D = N.zeros(xlen,ylen) + DKE2D = N.zeros((xlen,ylen)) + print("Calculating 2D grid for time index {0}...".format(t)) for gridpt in gridpts: i,j = gridpt - + # print("Calculating for gridpoints {0} & {1}.".format(i,j)) # Find closest level to 'lower', 'upper' P_col = P0[t,:,i,j] + PB0[t,:,i,j] if lower: - low_idx = util.closest(P_col,lower) + low_idx = utils.closest(P_col,lower*100.0) else: low_idx = None if upper: - upp_idx = util.closest(P_col,upper) + upp_idx = utils.closest(P_col,upper*100.0) else: upp_idx = None zidx = slice(low_idx,upp_idx+1) - if energy=='kinetic': DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,i,j]-U1[t,zidx,i,j])**2 + (V0[t,zidx,i-1,j]-V1[t,zidx,i-1,j])**2)) @@ -411,3 +418,12 @@ def plot_diff_energy(self,ptype,times,datafolder,p2p): DATA = N.load_data(datafolder) # dstack for each permutation and average for each grid point? # Plot + + def generate_times(self,idate,fdate,interval): + """ + Interval in seconds + """ + i = calendar.timegm(idate) + f = calendar.timegm(fdate) + times = range(i,f,interval) + return times diff --git a/postWRF/postWRF/utils.py b/postWRF/postWRF/utils.py index 73d9b6c..cf1342a 100644 --- a/postWRF/postWRF/utils.py +++ b/postWRF/postWRF/utils.py @@ -1,3 +1,4 @@ +import numpy as N import os """ A collection of useful utilities. @@ -82,4 +83,4 @@ def closest(arr2D,val): """ idx = N.argmin(N.abs(arr2D - val)) return idx - \ No newline at end of file + diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index b3b064b..14363a8 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -35,13 +35,12 @@ def __init__(self,fpath): self.x_dim = len(self.nc.dimensions['west_east']) self.y_dim = len(self.nc.dimensions['south_north']) - def get_time_idx(self,t,tuple_format=1): + def get_time_idx(self,t,tuple_format=0): """ Input: - t : time, tuple format by default - (otherwise t is in datenum) + t : time, not tuple format by default Output: From faa94f13150ebb742c06577eb2b2d5260f801650 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Fri, 10 Jan 2014 17:27:01 -0700 Subject: [PATCH 029/111] Improving DKE from 30k ft --- postWRF/bin/DKE.py | 8 +++++--- postWRF/postWRF/__init__.py | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index a8f50fe..9f9b78d 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -4,6 +4,7 @@ import sys import os import pdb +import calendar sys.path.append('../') @@ -19,7 +20,8 @@ rootdir = '/uufs/chpc.utah.edu/common/home/horel-group2/lawson2/' outdir = '/uufs/chpc.utah.edu/common/home/u0737349/public_html/paper2/' -for rundate in ('25','27','29'): +#for rundate in ('25','27','29'): +for rundate in ['29']: print("Computing for {0} November".format(rundate)) foldername = '201111' + rundate + '00' runfolder = os.path.join(rootdir,foldername) @@ -27,8 +29,8 @@ itime = (2011,11,int(rundate),0,0,0) ftime = (2011,12,2,12,0,0) - times = p.generate_times(itime,ftime,12*3600) - + #times = p.generate_times(itime,ftime,12*3600) + times = (calendar.timegm((2011,12,1,12,0,0))) path_to_plots = os.path.join(outdir,runfolder) #pdb.set_trace() # Produce .npy data files with DKE data diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index f63a7a3..21c43a0 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -249,7 +249,7 @@ def compute_diff_energy( print("Times are not identical between input files.") raise Exception else: - print("Passed check for identical timestamps between" + print("Passed check for identical timestamps between " "NetCDF files") # Find indices of each time @@ -369,7 +369,7 @@ def DE_z(self,nc0,nc1,t_idx,energy,lower,upper): T1 = nc1.variables['T'] R = 287.0 # Universal gas constant (J / deg K * kg) Cp = 1004.0 # Specific heat of dry air at constant pressure (J / deg K * kg) - kappa = (R/Cp) + kappa = R/Cp xlen = U0.shape[2] # 1 less than in V ylen = V0.shape[3] # 1 less than in U @@ -381,12 +381,12 @@ def latlon(nlats,nlons): for j in range(nlons): yield i,j - gridpts = latlon(xlen,ylen) DKE = [] for n,t in enumerate(t_idx): DKE2D = N.zeros((xlen,ylen)) print("Calculating 2D grid for time index {0}...".format(t)) + gridpts = latlon(xlen,ylen) for gridpt in gridpts: i,j = gridpt # print("Calculating for gridpoints {0} & {1}.".format(i,j)) From 301c940db0e6286cab74052d3ee4d04a395ec948 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 14 Jan 2014 16:57:00 -0700 Subject: [PATCH 030/111] Optimised DKE/DTE code, thanks Daryl Herzmann! --- postWRF/bin/.DKE.py.swp | Bin 0 -> 12288 bytes postWRF/bin/DKE.py | 10 ++- postWRF/postWRF/__init__.py | 173 +++++++++++++++++++++++------------- postWRF/postWRF/utils.py | 14 +++ 4 files changed, 135 insertions(+), 62 deletions(-) create mode 100644 postWRF/bin/.DKE.py.swp diff --git a/postWRF/bin/.DKE.py.swp b/postWRF/bin/.DKE.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..27f6d2254930e5b480c035883b2efcb5d5bb7f10 GIT binary patch literal 12288 zcmeHNO>f*p7@ig`e3T+8aUOOL_EN|8ZW=-g%LRg{s-mh*C=#kHuRY$KX6+fxjFSdP zxWIuUR}TCD_yKU>FMtFRcerst{05%!N49NBp~p(&VC){<;23ZWI0hU8jseGjW56-s z7;p?Y1{?#90mr}-$UvGC^71o;{0-RO|NHO%Kb|M#Q{XO80vYfj@E-6c@CGmh1^@y6 zc!7}LfnR|ifbW5?fFqy)7_bYR2Yxz7$QQupzznzwTmb%j9(uqra0KiC8^BrMm*)ui z2KW-V4Qv5_Jxj>fz%k$h9$@o$3ZuIm1C9a5fMdWh;23ZWI0h^OQt`^r!Ocu@X{Zrm zZxB$!OBUJ>f3&8X8EsN!0y@wv6IHIm0U;EZ4CPXoBSuo9LSjj_L@FmmDob`J8 z$`hULv+2qk%@f5;t*U7p0wB^pwc!tjjG=P1+_%54c%SFhs4au{V_YEY!>-Mx`0b(FpIO zj83W0p%sO91h0I=mUFctzEx?efu(+yE?JeQswwZh^!-UZ9tYz|FbQJ&HyU+8_8Kyr z+sdZkrxE$w4nfg;jwi3KzZS=>N+Ys9RwiNB+H9h3^j6d30FCFPV#t+^I_F(ibuG~` z4ILS__es+T3DR7ZIv3@1+`2dsLghYEln@OotG??(ZhF)9Av8Q2_jv5li?j<1cU~NO zE$Mcn^p)k+oEAdSPvRr`zSw8wjHy8gNtv#48WFpsH^nRZkvG}$ZeJ$D2jspt+3*5y zvS~lIywPPs+A7+hqPi}0lr3Zy)+SwqEUzOpY$>W}AxajVyg@DN*^+0;!j#KMrs&E^ zJ6L zriW1R01MG*Q*R+^<&DUY?rNqeI@v&9ag2!DRzIA`(7AE-iQ*?t+dYj`+mdq&)CrYtfa~IoDZ8cNSbz{NqThs68nSW@~NR5F#qmY%iU&(AsF0wy9FR5C@vFyJ;zLeo|OJ RrDJ_$ieV4ZRH+nk`~#?XF$@3z literal 0 HcmV?d00001 diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index 9f9b78d..0d21073 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -5,12 +5,16 @@ import os import pdb import calendar +import time sys.path.append('../') from DKE_settings import Settings from postWRF import WRFEnviron +# Time script +scriptstart = time.time() + # Initialise settings and environment config = Settings() p = WRFEnviron(config) @@ -30,7 +34,7 @@ itime = (2011,11,int(rundate),0,0,0) ftime = (2011,12,2,12,0,0) #times = p.generate_times(itime,ftime,12*3600) - times = (calendar.timegm((2011,12,1,12,0,0))) + times = (calendar.timegm((2011,12,2,0,0,0))) path_to_plots = os.path.join(outdir,runfolder) #pdb.set_trace() # Produce .npy data files with DKE data @@ -38,4 +42,6 @@ p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times,upper=500, d_save=runfolder, d_return=0,d_fname='DKE_'+foldername) # Plot these .npy files - # p.plot_diff_energy('sum_z',times,runfolder,path_to_plots) + p.plot_diff_energy('sum_z',times,runfolder,fname='DKE_'+foldername,path_to_plots) + +print "Script took", time.time()-scriptstart, "seconds." diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index 21c43a0..7dc3c28 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -20,6 +20,9 @@ import pdb import itertools import numpy as N +import time +import json +import cPickle as pickle from wrfout import WRFOut from axes import Axes @@ -167,24 +170,45 @@ def plot_cross_section(self,var,latA,lonA,latB,lonB): xs = CrossSection() xs.plot(var,latA,lonA,latB,lonB) - def save_data(self,data,folder,fname): - # Ensure file suffix will be .npy - fname2 = os.path.splitext(fname)[0] - # Check for folder and create if necessary + def save_data(self,data,folder,fname,format='pickle'): + # Strip file extension given + fname_base = os.path.splitext(fname)[0] + # Check for folder, create if necessary utils.trycreate(folder) - # Save in binary - fpath = os.path.join(folder,fname2) - N.save(fpath,data) + # Create absolute path + fpath = os.path.join(folder,fname_base) + + if format=='pickle': + with open(fpath+'.pickle','wb') as f: + pickle.dump(data,f) + elif format=='numpy': + N.save(fpath,data) + elif format=='json': + j = json.dumps(data) + with open(fpath+'.json','w') as f: + print >> f,j + else: + print("Give suitable saving format.") + raise Exception - print("Save file {0}.npy to {1}.".format(fname,folder)) - - def load_data(self,folder,fname): - # Ensure file suffix will be .npy + print("Saved file {0} to {1}.".format(fname,folder)) + + def load_data(self,folder,fname,format='pickle'): fname2 = os.path.splitext(fname)[0] fpath = os.path.join(folder,fname2) - data = N.load(fpath) + if format=='pickle': + with open(fpath,'rb') as f: + data = pickle.load(f) + elif format=='numpy': + data = N.load(fpath) + elif format=='json': + print("JSON stuff not coded yet.") + raise Exception + else: + print("Give suitable loading format.") + raise Exception - print("Loaded file {0}.npy from {1}.".format(fname,folder)) + print("Loaded file {0} from {1}.".format(fname,folder)) return data def compute_diff_energy( @@ -237,37 +261,51 @@ def compute_diff_energy( DATA = {} # Get all permutations of files - for perm in itertools.combinations(files,2): - print("Next permutation...") - DATA[perm] = {} - f1, f2 = perm - W1 = WRFOut(f1) - W2 = WRFOut(f2) - #pdb.set_trace() - # Make sure times are the same in both files - if not N.all(N.array(W1.wrf_times) == N.array(W2.wrf_times)): - print("Times are not identical between input files.") - raise Exception + nperm = itertools.combinations(files,2).__sizeof__() + for n, perm in enumerate(itertools.combinations(files,2)): + if n<50: + print("Skipping #{0} for debugging.".format(n)) else: - print("Passed check for identical timestamps between " - "NetCDF files") - - # Find indices of each time - t_idx = [] - for t in ts: - t_idx.append(W1.get_time_idx(t)) - - print("Calculating values now...") - DATA[perm]['times'] = ts - DATA[perm]['values'] = PLOTS[ptype](W1.nc,W2.nc,t_idx, - energy,lower,upper) - + print("No. {0} from {1} permutations".format(n,nperm)) + perm_start = time.time() + DATA[str(n)] = {} + f1, f2 = perm + W1 = WRFOut(f1) + W2 = WRFOut(f2) + #pdb.set_trace() + # Make sure times are the same in both files + if not N.all(N.array(W1.wrf_times) == N.array(W2.wrf_times)): + print("Times are not identical between input files.") + raise Exception + else: + print("Passed check for identical timestamps between " + "NetCDF files") + + # Find indices of each time + t_idx = [] + for t in ts: + t_idx.append(W1.get_time_idx(t)) + + print("Calculating values now...") + DATA[str(n)]['times'] = ts + DATA[str(n)]['values'] = PLOTS[ptype](W1.nc,W2.nc,t_idx, + energy,lower,upper) + DATA[str(n)]['file1'] = f1 + DATA[str(n)]['file2'] = f2 + + print "Calculation #{0} took {1:2.2f} seconds.".format(n,time.time()-perm_start) + if d_return and not d_save: return DATA elif d_save and not d_return: - self.save_data(DATA,d_save,d_fname) + #self.save_data(DATA,d_save,d_fname) + self.pickle_data(DATA,d_save,d_fname) + #self.json_data(DATA,d_save,d_fname) + return elif d_return and d_save: - self.save_data(DATA,d_save,d_fname) + #self.save_data(DATA,d_save,d_fname) + self.pickle_data(DATA,d_save,d_fname) + #self.json_data(DATA,d_save,d_fname) return DATA def DE_xyz(self,nc0,nc1,t_idx,energy,*args): @@ -353,20 +391,20 @@ def DE_z(self,nc0,nc1,t_idx,energy,lower,upper): # loading it to a variable yet # WIND - U0 = nc0.variables['U'] - V0 = nc0.variables['V'] - U1 = nc1.variables['U'] - V1 = nc1.variables['V'] + U0 = nc0.variables['U'][:] + V0 = nc0.variables['V'][:] + U1 = nc1.variables['U'][:] + V1 = nc1.variables['V'][:] # PERT and BASE PRESSURE - P0 = nc0.variables['P'] - PB0 = nc0.variables['PB'] - P1 = nc1.variables['P'] - PB1 = nc1.variables['PB'] + P0 = nc0.variables['P'][:] + PB0 = nc0.variables['PB'][:] + P1 = nc1.variables['P'][:] + PB1 = nc1.variables['PB'][:] if energy=='total': - T0 = nc0.variables['T'] - T1 = nc1.variables['T'] + T0 = nc0.variables['T'][:] + T1 = nc1.variables['T'][:] R = 287.0 # Universal gas constant (J / deg K * kg) Cp = 1004.0 # Specific heat of dry air at constant pressure (J / deg K * kg) kappa = R/Cp @@ -391,7 +429,7 @@ def latlon(nlats,nlons): i,j = gridpt # print("Calculating for gridpoints {0} & {1}.".format(i,j)) # Find closest level to 'lower', 'upper' - P_col = P0[t,:,i,j] + PB0[t,:,i,j] + P_col = P0[t,:,j,i] + PB0[t,:,j,i] if lower: low_idx = utils.closest(P_col,lower*100.0) else: @@ -402,22 +440,37 @@ def latlon(nlats,nlons): upp_idx = None zidx = slice(low_idx,upp_idx+1) + # This needs to be a 2D array? + if energy=='kinetic': - DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,i,j]-U1[t,zidx,i,j])**2 + - (V0[t,zidx,i-1,j]-V1[t,zidx,i-1,j])**2)) - elif energy=='total': - DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,i,j]-U1[t,zidx,i,j])**2 + - (V0[t,zidx,i-1,j]-V1[t,zidx,i-1,j])**2 + - kappa*(T0[t,zidx,i,j]-T1[t,zidx,i,j])**2)) + DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + + (V0[t,zidx,j,i]-V1[t,zidx,j,i])**2)) + #DKE2D = N.sum(0.5*((U0[t,zidx,1:,:]-U1[t,zidx,1:,:])**2 + + # (V0[t,zidx,:,:]-V1[t,zidx,:,:])**2)) + elif energy=='total': # FIX THIS + DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + + (V0[t,zidx,j,i]-V1[t,zidx,j,i])**2 + + kappa*(T0[t,zidx,j,i]-T1[t,zidx,j,i])**2)) DKE.append(DKE2D) return DKE - def plot_diff_energy(self,ptype,times,datafolder,p2p): - DATA = N.load_data(datafolder) - # dstack for each permutation and average for each grid point? - # Plot + def plot_diff_energy(self,ptype,time,folder,fname,p2p): + """ + folder : directory holding computed data + fname : naming scheme of required files + """ + + DATA = N.load_data(folder,fname,format='pickle') + times = self.get_sequence(time) + for t in times: + for k,v in DATA: + # utils.dstack_loop??? + + stack_average = # N.average(stack,axis=?) + + #birdseye plot with basemap of DKE/DTE def generate_times(self,idate,fdate,interval): """ diff --git a/postWRF/postWRF/utils.py b/postWRF/postWRF/utils.py index cf1342a..26d8f9f 100644 --- a/postWRF/postWRF/utils.py +++ b/postWRF/postWRF/utils.py @@ -84,3 +84,17 @@ def closest(arr2D,val): idx = N.argmin(N.abs(arr2D - val)) return idx +def dstack_loop(data, obj): + """ + Tries to stack numpy array (data) into object (obj). + If obj doesn't exist, then initialise it + If obj does exist, stack data. + """ + try: + print obj + except NameError: + stack = data + else: + stack = N.dstack((obj,data)) + + return stack \ No newline at end of file From 773c7c302ce77116e1e35ab9f0b1fa20c3df4ddc Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 14 Jan 2014 19:39:22 -0600 Subject: [PATCH 031/111] Update. --- postWRF/postWRF/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index 7dc3c28..51f03e6 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -456,12 +456,15 @@ def latlon(nlats,nlons): return DKE - def plot_diff_energy(self,ptype,time,folder,fname,p2p): + def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p): """ folder : directory holding computed data fname : naming scheme of required files """ + va = '_'.join(ptype,energy) + en = folder+fname + DATA = N.load_data(folder,fname,format='pickle') times = self.get_sequence(time) for t in times: @@ -471,6 +474,9 @@ def plot_diff_energy(self,ptype,time,folder,fname,p2p): stack_average = # N.average(stack,axis=?) #birdseye plot with basemap of DKE/DTE + F = BirdsEye(self.C,W,p2p) # 2D figure class + F.plot2D(va,t,en,lv,da,na) # Plot/save figure + def generate_times(self,idate,fdate,interval): """ From f132c5cb84b15b16978fa9f284b16cc5e04ae586 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 14 Jan 2014 18:44:02 -0700 Subject: [PATCH 032/111] Update. --- postWRF/bin/DKE.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index 0d21073..765be8b 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -39,9 +39,8 @@ #pdb.set_trace() # Produce .npy data files with DKE data print("Compute_diff_energy...") - p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times,upper=500, - d_save=runfolder, d_return=0,d_fname='DKE_'+foldername) - # Plot these .npy files - p.plot_diff_energy('sum_z',times,runfolder,fname='DKE_'+foldername,path_to_plots) + #p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times,upper=500, + # d_save=runfolder, d_return=0,d_fname='DKE_'+foldername) + p.plot_diff_energy('sum_z','kinetic',times,runfolder,fname='DKE_'+foldername,path_to_plots) print "Script took", time.time()-scriptstart, "seconds." From 25c449dd5db53a3d9357fba59593d1db1cddf1c4 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 14 Jan 2014 21:18:23 -0700 Subject: [PATCH 033/111] DKE/DTE plots now working! --- postWRF/bin/DKE.py | 12 ++-- postWRF/postWRF/__init__.py | 109 +++++++++++++++++++----------------- postWRF/postWRF/birdseye.py | 25 +++++++++ postWRF/postWRF/figure.py | 2 +- postWRF/postWRF/utils.py | 5 +- 5 files changed, 93 insertions(+), 60 deletions(-) diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index 765be8b..a72248e 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -33,14 +33,14 @@ itime = (2011,11,int(rundate),0,0,0) ftime = (2011,12,2,12,0,0) - #times = p.generate_times(itime,ftime,12*3600) - times = (calendar.timegm((2011,12,2,0,0,0))) - path_to_plots = os.path.join(outdir,runfolder) + times = p.generate_times(itime,ftime,3*3600) + path_to_plots = os.path.join(outdir,foldername) #pdb.set_trace() # Produce .npy data files with DKE data print("Compute_diff_energy...") - #p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times,upper=500, - # d_save=runfolder, d_return=0,d_fname='DKE_'+foldername) - p.plot_diff_energy('sum_z','kinetic',times,runfolder,fname='DKE_'+foldername,path_to_plots) + p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times,upper=500, + d_save=runfolder, d_return=0,d_fname='DKE_500_'+foldername) + + #p.plot_diff_energy('sum_z','kinetic',times,runfolder,'DKE_500_'+foldername,path_to_plots) print "Script took", time.time()-scriptstart, "seconds." diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index 51f03e6..c18d642 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -197,10 +197,10 @@ def load_data(self,folder,fname,format='pickle'): fname2 = os.path.splitext(fname)[0] fpath = os.path.join(folder,fname2) if format=='pickle': - with open(fpath,'rb') as f: + with open(fpath+'.pickle','rb') as f: data = pickle.load(f) elif format=='numpy': - data = N.load(fpath) + data = N.load(fpath+'.npy') elif format=='json': print("JSON stuff not coded yet.") raise Exception @@ -263,7 +263,7 @@ def compute_diff_energy( # Get all permutations of files nperm = itertools.combinations(files,2).__sizeof__() for n, perm in enumerate(itertools.combinations(files,2)): - if n<50: + if n>99999: print("Skipping #{0} for debugging.".format(n)) else: print("No. {0} from {1} permutations".format(n,nperm)) @@ -288,8 +288,10 @@ def compute_diff_energy( print("Calculating values now...") DATA[str(n)]['times'] = ts - DATA[str(n)]['values'] = PLOTS[ptype](W1.nc,W2.nc,t_idx, - energy,lower,upper) + DATA[str(n)]['values'] = [] + for t in t_idx: + DATA[str(n)]['values'].append(PLOTS[ptype](W1.nc,W2.nc,t, + energy,lower,upper)) DATA[str(n)]['file1'] = f1 DATA[str(n)]['file2'] = f2 @@ -299,12 +301,12 @@ def compute_diff_energy( return DATA elif d_save and not d_return: #self.save_data(DATA,d_save,d_fname) - self.pickle_data(DATA,d_save,d_fname) + self.save_data(DATA,d_save,d_fname) #self.json_data(DATA,d_save,d_fname) return elif d_return and d_save: #self.save_data(DATA,d_save,d_fname) - self.pickle_data(DATA,d_save,d_fname) + self.save_data(DATA,d_save,d_fname) #self.json_data(DATA,d_save,d_fname) return DATA @@ -360,7 +362,7 @@ def DE_xyz(self,nc0,nc1,t_idx,energy,*args): DKE.append(DKE_hr) return DKE - def DE_z(self,nc0,nc1,t_idx,energy,lower,upper): + def DE_z(self,nc0,nc1,t,energy,lower,upper): """ Computation for difference kinetic energy (DKE). Sums DKE over all levels between lower and upper, @@ -377,7 +379,7 @@ def DE_z(self,nc0,nc1,t_idx,energy,lower,upper): nc0 : netCDF file nc1 : netCDF file - t_idx : times indices to difference + t : times index to difference energy : kinetic or total lower : lowest level, hPa upper : highest level, hPa @@ -419,40 +421,37 @@ def latlon(nlats,nlons): for j in range(nlons): yield i,j - DKE = [] - for n,t in enumerate(t_idx): - DKE2D = N.zeros((xlen,ylen)) - print("Calculating 2D grid for time index {0}...".format(t)) - gridpts = latlon(xlen,ylen) - for gridpt in gridpts: - i,j = gridpt - # print("Calculating for gridpoints {0} & {1}.".format(i,j)) - # Find closest level to 'lower', 'upper' - P_col = P0[t,:,j,i] + PB0[t,:,j,i] - if lower: - low_idx = utils.closest(P_col,lower*100.0) - else: - low_idx = None - if upper: - upp_idx = utils.closest(P_col,upper*100.0) - else: - upp_idx = None - - zidx = slice(low_idx,upp_idx+1) - # This needs to be a 2D array? - - if energy=='kinetic': - DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + - (V0[t,zidx,j,i]-V1[t,zidx,j,i])**2)) - #DKE2D = N.sum(0.5*((U0[t,zidx,1:,:]-U1[t,zidx,1:,:])**2 + - # (V0[t,zidx,:,:]-V1[t,zidx,:,:])**2)) - elif energy=='total': # FIX THIS - DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + - (V0[t,zidx,j,i]-V1[t,zidx,j,i])**2 + - kappa*(T0[t,zidx,j,i]-T1[t,zidx,j,i])**2)) - - DKE.append(DKE2D) + DKE2D = N.zeros((xlen,ylen)) + print_time = ''.join((nc0.variables['Times'][t])) + print("Calculating 2D grid for time {0}...".format(print_time)) + gridpts = latlon(xlen,ylen) + for gridpt in gridpts: + i,j = gridpt + # print("Calculating for gridpoints {0} & {1}.".format(i,j)) + # Find closest level to 'lower', 'upper' + P_col = P0[t,:,j,i] + PB0[t,:,j,i] + if lower: + low_idx = utils.closest(P_col,lower*100.0) + else: + low_idx = None + if upper: + upp_idx = utils.closest(P_col,upper*100.0) + else: + upp_idx = None + + zidx = slice(low_idx,upp_idx+1) + # This needs to be a 2D array? + + if energy=='kinetic': + DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + + (V0[t,zidx,j,i]-V1[t,zidx,j,i])**2)) + elif energy=='total': + DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + + (V0[t,zidx,j,i]-V1[t,zidx,j,i])**2 + + kappa*(T0[t,zidx,j,i]-T1[t,zidx,j,i])**2)) + + DKE.append(DKE2D) return DKE @@ -461,22 +460,28 @@ def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p): folder : directory holding computed data fname : naming scheme of required files """ - - va = '_'.join(ptype,energy) - en = folder+fname - - DATA = N.load_data(folder,fname,format='pickle') + sw = 0 + + DATA = self.load_data(folder,fname,format='pickle') times = self.get_sequence(time) for t in times: - for k,v in DATA: + for perm in DATA: + if sw==0: + W1 = WRFOut(DATA[perm]['file1']) + sw = 1 + stack = DATA[perm]['values'][0] + else: + stack = N.dstack((DATA[perm]['values'][0],stack)) # utils.dstack_loop??? - stack_average = # N.average(stack,axis=?) - #birdseye plot with basemap of DKE/DTE - F = BirdsEye(self.C,W,p2p) # 2D figure class - F.plot2D(va,t,en,lv,da,na) # Plot/save figure + stack_average = N.average(stack,axis=2) + + #birdseye plot with basemap of DKE/DTE + F = BirdsEye(self.C,W1,p2p) # 2D figure class + #F.plot2D(va,t,en,lv,da,na) # Plot/save figure + F.plot_data(stack_average,'contour',fname,t) def generate_times(self,idate,fdate,interval): """ diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index f311adc..e2462ce 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -12,6 +12,31 @@ def __init__(self,config,wrfout,p2p): self.W = wrfout self.D = Defaults() self.p2p = p2p + + def plot_data(self,data,mplcommand,fname,pt): + # INITIALISE + self.fig = plt.figure() + self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user + self.bmap,x,y = self.basemap_setup() + + if mplcommand == 'contour': + self.bmap.contour(x,y,data) + + # LABELS, TITLES etc + """ + Change these to hasattr! + """ + #if self.C.plot_titles: + # title = utils.string_from_time('title',pt,tupleformat=0) + # plt.title(title) + #if self.C.plot_colorbar: + # plt.colorbar(orientation='horizontal') + + # SAVE FIGURE + datestr = utils.string_from_time('output',pt,tupleformat=0) + self.fname = self.create_fname(fname) # No da variable here + self.save(self.fig,self.p2p,self.fname) + self.fig.clf() def plot2D(self,va,pt,en,lv,da,na): # INITIALISE diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index 39d3dd3..b6e8245 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -17,7 +17,7 @@ class Figure: def __init__(self,config,wrfout): pass - def create_fname(self,naming): + def create_fname(self,*naming): """Default naming should be: Variable + time + level """ diff --git a/postWRF/postWRF/utils.py b/postWRF/postWRF/utils.py index 26d8f9f..49df2ce 100644 --- a/postWRF/postWRF/utils.py +++ b/postWRF/postWRF/utils.py @@ -1,5 +1,6 @@ import numpy as N import os +import time """ A collection of useful utilities. """ @@ -14,7 +15,9 @@ def padded_times(timeseq): padded = ['{0:04d}'.format(t) for t in timeseq] return padded -def string_from_time(usage,t,dom=0,strlen=0,conven=0): +def string_from_time(usage,t,dom=0,strlen=0,conven=0,tupleformat=1): + if not tupleformat: + t = time.gmtime(t) if usage == 'title': # Generates string for titles str = '{3:02d}:{4:02d}Z on {2:02d}/{1:02d}/{0:04d}'.format(*t) From 48155903bcfdd924253fad64f639ea6588e16567 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 15 Jan 2014 10:51:22 -0700 Subject: [PATCH 034/111] Fixed horrible axis mix-up bug --- README.md | 4 +--- postWRF/bin/DKE.py | 10 ++++---- postWRF/postWRF/.__init__.py.swp | Bin 0 -> 36864 bytes postWRF/postWRF/.birdseye.py.swp | Bin 0 -> 20480 bytes postWRF/postWRF/__init__.py | 40 ++++++++++++++++++------------- postWRF/postWRF/birdseye.py | 14 +++++++---- 6 files changed, 40 insertions(+), 28 deletions(-) create mode 100644 postWRF/postWRF/.__init__.py.swp create mode 100644 postWRF/postWRF/.birdseye.py.swp diff --git a/README.md b/README.md index db96c34..889281c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ -**N.B.: VERSION 1 WILL BE 'RELEASED' IN DUE COURSE** -**NOT FIT FOR HUMAN CONSUMPTION UNTIL THEN** - +**PLEASE GET IN TOUCH TO GAIN ACCESS TO THE REPO** === WEM === diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index a72248e..4bf835f 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -33,14 +33,16 @@ itime = (2011,11,int(rundate),0,0,0) ftime = (2011,12,2,12,0,0) - times = p.generate_times(itime,ftime,3*3600) + times = p.generate_times(itime,ftime,6*3600) path_to_plots = os.path.join(outdir,foldername) #pdb.set_trace() # Produce .npy data files with DKE data print("Compute_diff_energy...") p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times,upper=500, - d_save=runfolder, d_return=0,d_fname='DKE_500_'+foldername) - - #p.plot_diff_energy('sum_z','kinetic',times,runfolder,'DKE_500_'+foldername,path_to_plots) + d_save=runfolder, d_return=0,d_fname='DKE_500_'+foldername) + # Contour fixed at these values + V = range(0,5500) + V.insert(1,100) # Extra low value for detail + p.plot_diff_energy('sum_z','kinetic',times,runfolder,'DKE_500_'+foldername,path_to_plots,V) print "Script took", time.time()-scriptstart, "seconds." diff --git a/postWRF/postWRF/.__init__.py.swp b/postWRF/postWRF/.__init__.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..27460e410c212456e19baad7d2ed7fd667aaebea GIT binary patch literal 36864 zcmeI536LCTb;sLa4r2%e2$c#18m(bwrO{}2Ww0$+fwa3avZd8pXB9#Wt(l(PmS%g# z(>;4g2p=R=z?EPQ7Y@Z`ii89RR~04@7lb1Oa@ekzP=HM+jwBSpF~JFilS;_%z3=O5 zc2-gWp%OA({_k}6cfId@@B6;@-uGHQK6ZHbUiY%+15+1kfgI+i+=34$IqF~c|}s;miZv=blWHVdK_&zGP8F}rya#d_U}r6YqcN@ z;##fRSv_0B!?Ym>h8#FQ4)o&Y=F1=FY@Ha_sXnsdneLOGc*&eDY;TtA?*)Co7ux5R{ax-$e}sLWwZGr$OMkR| z{w4dn)i#i{^GN%?X@6hPm;NaGK56Io`_dn7)A!il_w=RP*TbJ72ZkINa$v}TAqR#W z7;<39fguNm92jz7$blgT{$FyyYdDVB)DMZh%=v#}|KIs|$N6jUS#TO$2A&Qc!hruc z=zumj2~L2Qf!*Nw;A!9@@b8avobQ6KgFC?;;Dg{@;3n`I&;c(7F9aLFlfb_{)^WZA z{t^5wxE;I=ycN6@>;zYU2QMZK+z6fnE(K2j-+7GVd>Y&W-Uw#Fv%n+3KV9TFH-j6& zwO|4~9sCUrirc}h;7#BRm<40tV(<|h5VwNY01s>jlb{TKg!AI#;Cc{%1uz9%a6iob z3ivX(6>JBaz((*dI91*QPJwH|%fLRc9ZZ5tz+=F7acFJzX#q3UI}glli>R} za2^2P0)GnL1Y&RvsDjNv#`s6pFXFiN8tM4m#mJp>Bfr(GE_x^ZS`hnPFK&0sOJ1cD zSNyQjTBQF>&rhL;qW_v2dRU)%G;x?h$&NtV{qz$~q$EQ=ow%;JYxmQFk{3sfCw zADA4^GA<3KXeme0h7B9C9)yxV^cI6~-i_)D{-W|1?fP-A8@f^4O=;pDruO2X6;-)MsA|n_d$Fb*>_y(ZKdFB!@nl>$cl*Wn(LoiWdDl3NdAGR40n(s*KM^$5M_qg0AVcnO26Q8@=v)G=O`{ z-6lXhxY7l&P!lg{o$_=*9JgD$|&StgN%S(&_rm zU}aO(Yw`ma2HEAlh?q*&Jrm?OKxhQBh??Y1+qlf(|v z7MC|nn^h->T-fTin-mJ?{i-{)&~8&W?8!8DkNfem&$2u2dSS!89B$QHy@<7}x*<B(n_MIAT-T3!EUc`Hc2$gIS#%qI6mr#1*RIIbN(sFUCoz~&QZ;@ z-}HK|I4WnE3^$PsO_2GHJg+k8!x9@gOTE{Xl^Y7XrD#1yPf<79BSN5Uiku~J!bZSM zA_7!zF>71`if6*5pxX{vPlClxyBnJ=+L66kL=06nCBh6OK3k4 zwZr6IW^{7T+TLN!irm@cjVSPOyWNVC7oEoOp_#j;VIngM-gi@I}Uq>zvzfOPpLd}V`R67-6dO5ved|pdwf-Et)S*pLBmyn z)OMnJH|WI9l!&_@A?2|6AQqghEvrU$`))5(rB~K4Odcs2RUNVaFU5Ym9y?R)|2@_V z@;Pk%SAZ9T=Yb92E7fEw5Y9>PYyAN(Eo8n_Ltf)2PEJOf+^{t8=N?Dsl& z5x5fkD)>5f`uo7^03i>~5pW6kJ~sLT;0|yd5WD@6;0xI2uLSd;3ATa~xETBxUfl;i z25teuyD2cdh8*}s<^W2TUe*Ba*D43T)=aE#m?KGXH=i+inSbXbFLQIpG!h&M?XZ#Q&PhH|K8#)% z`)cJ!Mg`GoE82cWYo9sO@Qp{Kf^qNFPp0ik`_@>QW0%!+S~}#O9y?T3m@Mhcjjh2{r+ql}g4n zP}WzKQmxZ(xQ6{LkC>+Mr{GG-T!%PPMqMlh>DLy~XI6q{zP@ z29MK-dnGEAR;xabAZ)f>@3=TgaD=LX>PY4J3zkRAlapiHiu_ama99!2WohuyyTwoL zD=|6Oh~x_^aWRi!a5jcc(x4&D+Oq|3x};>Ck{;84wX#}muTj(4ZEb2ZL5(+QvSiE# zbn>(j9f`5@Fv1Yw+z0B#NjoA`K54yOW60s<<*3fBM z!PAD?-^S0ffVV=l(RzCkrz5gdm;niuvewF^u8BjVCdT;us#$G2@oLBCd_vw-gk6;a zKg5H3I5#j3MC)ZY8x{q}zR5L<4Aye1yth6D?s%p`^U98D&)3pYNvkeZ+>-V(^JxmpMiRYh%4^koa#qBm zBg^!BH)wP?{fHw~vYzc0G_0U5^{}F~+4~x$Fzd(J-Eg@(Oxe zWtBhK09my!Pad)+C6!EB+JwMrHB2GHupj&i_zJfETfsaigD+y| z-v!Xup3+q9s};c{{JBO0C+!m8+bK14Fa$L zo&qia58?y358MOp0=EG16U>2q;K|?t`~jZ?uLW%|2VC$B@Gx*Uet|y(H-Q$|2dd!5 z_yxWRz6jn4UIos87lP*l@f&;<|G@jf>p%@$0iFuJhkxKM@D6YTh`0bzB?7k}FSGj;NUxR0nfhu38vck+I-1 zn<_b!LbVgMOce(ed(W0@aPG1oTD%;Bq)pI)+zE&5LrG7|X!;cz;%rOR3!H7a698S; z`6nwD+KoiQTg%b~2Yt7o!_n&>2ONUugsteMI2N~30!3cXbgX|j~lS5#Xo z<#Z7#ExH^57C9Y8`>vWyWLEM?*%T_ewwYT;3W`Q#b*6$+gc#e)YB&Xb$}Tc{P#(pL zh}OK0WtnJ`dh|J(RVsDclQumI!&B+l_VmX#a3$x7oY@7jgb8p+@97{Ir!ufUuDvJTcOdtZLOKyNDj40+Z=1B%~?ggQtBS3S^VYAY}?4wZ}Vk| zN-s-%;dnf+`{t}x9nFbc=BU%c|GW|#vtA57N5~VQ$_xvY&3uih)^b*zSr<=&-a+7gr%wn9X#*vU8EE%&=qn0A|4F(up>??1x4X^Ci9+RT z$4BL+Qd(X!I8u*j0+ONH;QS?0^FJH!qRb4CKU8p7Wh8+JlQctBJQIW55UpJQ~R39go^Aj5`fdN~<%9Bj?{H+$V{%NYg34 z;MY&;HejVNi40<5gnpf~6k@5;{3=*V!npEk+FrAKB1(^%PL&>>I7S<)RK}N`! z**Y6!l!^xC+9{C|v_fnRHY;whZUwz>2Wxo9A1~f1cRMpitvL*{$EK;(s}W7a*y*91 zv3u96v^XVGTBO5vL%=VTz8@t!E99R?pIC_u96mT%W<#6eumm11x*x`iIhZgvbeH> z3rFQJ*9 zB<&JBCM`=0nMqjF_Jo-cNtl^bTd3Q#v%%<|lkVw>GZkwz^01W$iPJ=SPG6dHtz(~Q zZ)%Z^e9fk-)Ms}gab_D!Qo;!meQKNg5@D4Il9Yf zns=<{f^vUy&-!^f*7XQnBNWPhXdUsnlW^mT=YU$a;6jk0a;kB>$`N)g?s}{r#SRta z1%_lRf(-qN415~MP$i0|(W}dPS_nzb#APHcBo11{#SsGJBKRRsL^c?yTVSU@AB3Uy zAQ@AZ3O#nwSdJ(&yXDM9=D&K)BaT0^PL1TkS3`fc{{?Kw zJFqjw{-3or;=zW)HY3ET*-1q)y+*a*Idz5hvYJLm#A``-k9fPMcL z;8WmtKof}F|DV|P-vAN^@Mcg0hrtARED+oOpRwof1NVUUf)Ge7z>~ojvEx4tPJ{hm z3_KD10Q+5H`9BCE@GNjIw);oHZQwAdfMt7N`f;5ap%Sxs>ombAOu~xQ z1Y~Pdw-WIxflnYv((sh};!PNeOsR8N##zB+N0udedPW=uW@*OzR0-?jm010v)>)sd z*J-oTywy?Uq@(bDL<~|w{zw<4V-5IRnHT1UsWGN zO12aU38pWO+$PsqsM0m?i*)NMVIbP@_t-jd9@ z(o$J&kxnuj>uf{n2}LO%k%gFWwv+6o+a{_L%`>n^kL9yvne#ThntJ|U9^EVpV2%0H zWe-isNLmnS&`9=hlmk=QwkI^8v^jAeSvhB9VQmgR)J$4f@7Kz{dUK3>C%1d#3l&LB z&UBMwskuG-4jwqFexqaMBjeSuUO6%$f8)xU9>@$wqYJt0ASM$Qovk6vOtic(IG~)yX%L+$+UNpXsJ3*3zgfBjtDi=d!-$Jd%al z2KUSE$sBKUfJ6K|$6H>VKttLp5LS3d>NUR`X8m|-dY3AJQD5hhoQc+Gg7`A~Bxp!b zU>*0RvNZPaCZvJ-SyW0=&01?INjaP2B*|lmI4FZBs;@EIQVJ4RY#@GJ)0_UWlA6Q> z$J`@;pw&vl2pA&tLK6%#DxVG{@dXHHmeA({mD)z!y%Mt+NeHd7KnasgE8+ z@&BrC)R6-Dt6vMsw7VqN5mAL~*g!Mq7faJ~VFk_H7iCF$bG+gryIHH{j0Y3aAs>|p2?=Lp-WEk9Ww4AFj$v3W{qd+bK8=w90eA zm{-}%7Oup@CGqN+e?c>)EvGlSW(y7WTxFYX@Tf_rII&F5SJId0PMI8yxO-&p>+y9Q zQ^Fox;8-OB7@w_tC8gE_->-0xAY2f?Q`&HP<8qzP3bCOh=eK=pSh98zkFH)Z@q%zx z!y-{}nVWuhS-wyTVuJTQciD?@_d$p~qUdIem|ZxG!jw}6jJA}?hb+lz`>4v1HueH{ z%9Ae!IE)W{g2DanGt>U1H}n&ILkp^d+&A9<94)t z!I7rra`Z2M3=Io&p3;%Dx{ivUYnc-YdZ23`+d z4OT!KTmxo+ocBK*+=pM_9`IRk8+Zjc3dCn{8F&hK5dXlP;Jx5g;5R`4c7msZd+`&< zcl-f(38(@G+|8K$Id}&Uzk!TV89W~R7yg3#!RNtU0O8V01$Hup*tfIzl>{l>H$a;m{6zUXnQnK5|2av!%60IYn;Nhj$Ly^(RFf|+`3w`)xqOwo1%+VVz1rRA zo}AA{TUy*ty%U%7zxklrLm>zB-3WtY9#2wQ(OJtM6eSriP9ctn7@}=OGx6YZO*FU!sWgsiD&P@ZBvM6A#3Kd5`^&K(D4+`0WT z2M!$CKad*R))*+03Ea&;sP!e9fDO(#2_ zAZ#L+4T0nI9!=AH9wuL&wL_EvzMYrPi|e9j>M5w2e`&$=Ca)RgwSz!mxI%Afuqs^oo|0VPu`E&Hi{Yc*rzUK7UDgwDt?M?zMy z>xB!Vxos&bhFBV@Zy~Kdy%A-0(VOS1c>YZdb%Yol-}-K{W^*bJt4N*yd5^4-a{eo; z6#u455D{kSn{v9su=0v0J8kNxb8pKAq&^yrkkl=JV-gObjVdTc$Xbyr`ou-?(%{jQ zADOCawCT*5HuW=diT69iD+3&YIhUx_Yh|-e`Q~!*B5{jxTU+QBmwIcf5d$atL@ZIv zqPT7or$XvJumy`HrSkqOAG^Md_}nCL-Q1sN)VhiMrpV@TsXznPlP9TSK)kxpVTg1$ z4G37bq-eByM#_MC)LXx1!|Cs3|MR&B?sJkP#eWtA=l91XSTj?Dq@_OfOLUQ(q9i#i z9sKJZJfKJP7G+TvktapV%T!N}gd8c7TvmGIV;@=VCBI0Kn0MlaXL=U`y6i`2`D0Ka zZ|&zV; zr0MIZ9%=eIdPkamXt2_erlWBnD8_x}+G{-}Am<{;c9OKF_4NQfDx_}RmHGFfk^YD`?nM5qR*zM23a;o`t;YYqkF$7(^LnxW z(O0Rt=dNO1UYQg3>9;v^4KZuLnl1Z%v;uEufCka95=6SEbiDy=Lj8kNTn&& zjI+Q7w}LtFZg3TN{u;yh99Rd7;3hBz{`xl91}=a{z{9`;cY_i*2wuI~FkS@bzz@O0 z;3TMl8^Oy5QAY4PZ~=S`+z*z(N5LzY^mq|G4ITjl@CooCa5XpxUb@mSeh$6^J_BrU z5}4p`m|Xca_!9Uym;O_%?*;Dx&*zgb zBt1%GKBn8mXR+IMX}}2+4%v1^mu$zKj^$A?>4gE|5p~>P%P_KdzSTxNC+r0xb{Vf! zM|`-T;x(L|kaFR;yuVZamBJMFbOq(&|q1O{`7}Q&oGoMB_ zoJ<-sYKtyzyY&y&YKBZ8J7BzB%Mx-}V8g+bsqc2ALOD)lA=50UOe0H3Ueg^aGXJvY zIAwF&ZHsPQoo(OjGIy&h>d6Jqd0d~=$!ub9rI0WS2hX&i?ZF2s>FvO7yZ*#fp-P&` zP~e?B@Xl!9Jm(>q5Oa&+t%x|uW6mrO`Ib$U@w_$UR%Bix3#b=e*eL|60axj_S%+ky z;n9m!y47Pv300CEcgu3T&QJ|e&YbpHVh92xnMc!6a?=T@&x+iWWTabGdIxV@Fk6MI z1hh&@WGN(VO8PW*Y0G1z%VL(RKybv=<%~3T92Ut6w97Aw-9S{v=RzNaBRmX)V}!O` z&lLkALXzZ0oE{gUPdcv0q%C~Buux`DB8bmOFG`vZce>1HmSyERF^{uj`IC$iP!y=r zr>568+p@igcr?v*7!9Fm&l3oq*r!s?6LLEw2&HF{cA6FvM`Q80g>+FcyAy}LEnJ^f z%3)7LJyA9zQDj&mt1}x7vd}!fzS1aOqBJz}gyn=@7`JF#3DMvKR3z05x_CMr;?XM? zDZwQ5&gQJh3x%DZT?86Dg-aGERn^T*~a3kN!FTc zi;Y#;(HSx;c^K-Po|Ne=bS@}zAi`eUsqB~o`GTk=`xqLrO2wl#`jOBMs#VkR`rLF7 zq3-xaG4{Uh%T0JFEmduc!bnTgTxFARsK>a4)@?g}Qk{yZA|4d+P)C;+mse(IR!rP2 ztu3#wG?$JS?^+fJ396#1g5EhXb(fLqf=+pl?{wiSBS~YDJ{!hJOKx8+EV+FZ-k&au z@-e(#pV@AE$0TL1k7uu+JyH3D?wNbiFO*N{!MS&)ct_}r8f$a6lKGjnncTMgCX1Xz z%M6m*c&_gX*0$TBPu-x>riE#;a;!}*E-&TDc!X$$&x--S|g7{FhnRuJJf%fM4e36 zYPI24zS!7kEE*~rPWest4l;>0RRzjiE4zD!wdRRN9uAU)3F-3BFj$WAVaH8fV0NOP&V)|^>vt|kJ=QBU@2)AMTdGEvDR zVKQ_EvJW>g7$w^8$>F~{2qr`38;paOGcK4I&^?Bi>r+v$6))v=Mv2QDAxa~?_1+;u zCaC)G(c$5ehlbZJ!ARbtBgOdmc>eO{x-{5gM06R(VSEa7DVJYRH|GI{1FIp13>|wL zx%AB2KsLVQ!%LyE442m0jiN2X#g9BE^nDt%Wm7MwBUHHvW5}rT$g_FVH#J)hElF#W zcV?l1sj9tffE%czN(4g{J}W;yDbai2yxVymj`PV^)$p&2l57)x`1ipV((X8OaEILJ zA(Xr92W;C7Tw&XZZHcFu#5Iv8401fmIhmE4*Bb*?Sx}vl{6uzrIW8pMXNLSR!*`|X zuu_jCWa5*4|8bJ~WtQXLTSkN&wg^M-lq-1l`JM%xP5wHv?9i5F-{rY2?4y@=gmU?D zm-$ESmLdQDKa79)2+03`{Hm(s9G-syF|7q||@D1=q&;SR(vslmn8F&;t0CvC~ z;AO1iUjV-Z_k%NF1vJ4?Fb>ARU$CbC40sR>Km_gvRY1TPcoOUT-vXZp9l*c>I0~)@ zuVQWgJopjV0&>lN28@Fju&)1G@B?rf41fbBz)j#ESl53RJPE!IJ_hRG-wE}NC1!O-b5540= zZ9~yJPLcsk?>NyrPV|lw>~qsQPV|nGWQ4?7?>NyrPV|nGq&esvC;RU>d5uktM&6O& TX4ixC$ajY9*??khRE_@tf!!M0 literal 0 HcmV?d00001 diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index c18d642..980d27c 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -263,7 +263,7 @@ def compute_diff_energy( # Get all permutations of files nperm = itertools.combinations(files,2).__sizeof__() for n, perm in enumerate(itertools.combinations(files,2)): - if n>99999: + if n>9999: print("Skipping #{0} for debugging.".format(n)) else: print("No. {0} from {1} permutations".format(n,nperm)) @@ -417,8 +417,8 @@ def DE_z(self,nc0,nc1,t,energy,lower,upper): # Generator for lat/lon points def latlon(nlats,nlons): - for i in range(nlats): - for j in range(nlons): + for i in range(nlats): # y-axis + for j in range(nlons): # x-axis yield i,j DKE = [] @@ -444,10 +444,10 @@ def latlon(nlats,nlons): # This needs to be a 2D array? if energy=='kinetic': - DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + + DKE2D[j,i] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + (V0[t,zidx,j,i]-V1[t,zidx,j,i])**2)) elif energy=='total': - DKE2D[i,j] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + + DKE2D[j,i] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + (V0[t,zidx,j,i]-V1[t,zidx,j,i])**2 + kappa*(T0[t,zidx,j,i]-T1[t,zidx,j,i])**2)) @@ -455,33 +455,41 @@ def latlon(nlats,nlons): return DKE - def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p): + def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,V): """ folder : directory holding computed data fname : naming scheme of required files + V : constant values to contour at """ sw = 0 - + DATA = self.load_data(folder,fname,format='pickle') times = self.get_sequence(time) - for t in times: - for perm in DATA: + + for n,t in enumerate(times): + for pn,perm in enumerate(DATA): if sw==0: + # Get times and info about nc files W1 = WRFOut(DATA[perm]['file1']) + permtimes = DATA[perm]['times'] sw = 1 - stack = DATA[perm]['values'][0] - else: - stack = N.dstack((DATA[perm]['values'][0],stack)) - # utils.dstack_loop??? - + # Find array for required time + x = N.where(N.array(permtimes)==t)[0][0] + data = DATA[perm]['values'][x][0] + if not pn: + stack = data + else: + stack = N.dstack((data,stack)) stack_average = N.average(stack,axis=2) - #birdseye plot with basemap of DKE/DTE F = BirdsEye(self.C,W1,p2p) # 2D figure class #F.plot2D(va,t,en,lv,da,na) # Plot/save figure - F.plot_data(stack_average,'contour',fname,t) + fname_t = ''.join((fname,'_p{0:02d}'.format(n))) + F.plot_data(stack_average,'contour',fname_t,t,V) + print("Plotting time {0} from {1}.".format(n,len(times))) + del data, stack def generate_times(self,idate,fdate,interval): """ diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index e2462ce..39fe8e3 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -13,24 +13,28 @@ def __init__(self,config,wrfout,p2p): self.D = Defaults() self.p2p = p2p - def plot_data(self,data,mplcommand,fname,pt): + def plot_data(self,data,mplcommand,fname,pt,V=0): # INITIALISE self.fig = plt.figure() self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user self.bmap,x,y = self.basemap_setup() if mplcommand == 'contour': - self.bmap.contour(x,y,data) + if not V: + self.bmap.contour(x,y,data) + else: + self.bmap.contour(x,y,data,V) + # LABELS, TITLES etc """ Change these to hasattr! """ #if self.C.plot_titles: - # title = utils.string_from_time('title',pt,tupleformat=0) - # plt.title(title) + title = utils.string_from_time('title',pt,tupleformat=0) + plt.title(title) #if self.C.plot_colorbar: - # plt.colorbar(orientation='horizontal') + plt.colorbar(orientation='horizontal') # SAVE FIGURE datestr = utils.string_from_time('output',pt,tupleformat=0) From 1d8e83e6cbb673d24b276a5b0790b0fe19593f5b Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 15 Jan 2014 15:12:10 -0700 Subject: [PATCH 035/111] Polishing DKE/DTE computations --- postWRF/bin/DKE.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index 4bf835f..1294cff 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -38,11 +38,12 @@ #pdb.set_trace() # Produce .npy data files with DKE data print("Compute_diff_energy...") + """ p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times,upper=500, d_save=runfolder, d_return=0,d_fname='DKE_500_'+foldername) + """ # Contour fixed at these values - V = range(0,5500) - V.insert(1,100) # Extra low value for detail + V = range(0,2200,200) p.plot_diff_energy('sum_z','kinetic',times,runfolder,'DKE_500_'+foldername,path_to_plots,V) print "Script took", time.time()-scriptstart, "seconds." From 5e86d7c0856c565339ca47375ad668be6e79ca7c Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 16 Jan 2014 12:32:27 -0700 Subject: [PATCH 036/111] Polishing DKE/DTE computations --- postWRF/bin/DKE.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index 1294cff..2fbe1ce 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -25,7 +25,7 @@ outdir = '/uufs/chpc.utah.edu/common/home/u0737349/public_html/paper2/' #for rundate in ('25','27','29'): -for rundate in ['29']: +for rundate in ['25','27']: print("Computing for {0} November".format(rundate)) foldername = '201111' + rundate + '00' runfolder = os.path.join(rootdir,foldername) @@ -33,15 +33,13 @@ itime = (2011,11,int(rundate),0,0,0) ftime = (2011,12,2,12,0,0) - times = p.generate_times(itime,ftime,6*3600) + times = p.generate_times(itime,ftime,12*3600) path_to_plots = os.path.join(outdir,foldername) #pdb.set_trace() # Produce .npy data files with DKE data print("Compute_diff_energy...") - """ p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times,upper=500, d_save=runfolder, d_return=0,d_fname='DKE_500_'+foldername) - """ # Contour fixed at these values V = range(0,2200,200) p.plot_diff_energy('sum_z','kinetic',times,runfolder,'DKE_500_'+foldername,path_to_plots,V) From 1f81502672f305f13617c6828bd3ce813c372e0b Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 4 Feb 2014 10:40:36 -0500 Subject: [PATCH 037/111] Changing lazyWRF structure. --- lazyWRF/lazyWRF/__init__.py | 0 lazyWRF/{ => lazyWRF}/lazyWRF.py | 0 lazyWRF/{ => lazyWRF}/sensitivity_ensemble.py | 0 lazyWRF/{ => lazyWRF}/setup_gefs.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 lazyWRF/lazyWRF/__init__.py rename lazyWRF/{ => lazyWRF}/lazyWRF.py (100%) rename lazyWRF/{ => lazyWRF}/sensitivity_ensemble.py (100%) rename lazyWRF/{ => lazyWRF}/setup_gefs.py (100%) diff --git a/lazyWRF/lazyWRF/__init__.py b/lazyWRF/lazyWRF/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lazyWRF/lazyWRF.py b/lazyWRF/lazyWRF/lazyWRF.py similarity index 100% rename from lazyWRF/lazyWRF.py rename to lazyWRF/lazyWRF/lazyWRF.py diff --git a/lazyWRF/sensitivity_ensemble.py b/lazyWRF/lazyWRF/sensitivity_ensemble.py similarity index 100% rename from lazyWRF/sensitivity_ensemble.py rename to lazyWRF/lazyWRF/sensitivity_ensemble.py diff --git a/lazyWRF/setup_gefs.py b/lazyWRF/lazyWRF/setup_gefs.py similarity index 100% rename from lazyWRF/setup_gefs.py rename to lazyWRF/lazyWRF/setup_gefs.py From d11043d08a70477712ce9d5a2a8649b6abc0ee1c Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 4 Feb 2014 10:50:22 -0500 Subject: [PATCH 038/111] Organising lazyWRF --- lazyWRF/lazyWRF/copyfilescript.py | 65 +++++++++++++++++ lazyWRF/lazyWRF/setup_gefs_isu.py | 116 ++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 lazyWRF/lazyWRF/copyfilescript.py create mode 100644 lazyWRF/lazyWRF/setup_gefs_isu.py diff --git a/lazyWRF/lazyWRF/copyfilescript.py b/lazyWRF/lazyWRF/copyfilescript.py new file mode 100644 index 0000000..411e310 --- /dev/null +++ b/lazyWRF/lazyWRF/copyfilescript.py @@ -0,0 +1,65 @@ +### IMPORTS +import os +import pdb + +def copyfiles(pathtoWRF,outdir,ensmem,confirm_switch=0): + ### Determine values from latest namelist file + f = open(pathtoWRF+'namelist.input') + lines = f.readlines() + + # Number always at column 39 + yr = lines[5][39:43] + mth = lines[6][39:41] + day = lines[7][39:41] + hr = lines[8][39:41] + # Join into init_time + init_time = yr+mth+day+hr+'/' + # This is the folder for all GEFS forecasts on this date + + outloc = outdir+init_time+'wrfout/'+str(ensmem)+'/' + + files = ('wrfout*','namelistCOPY.input','wrfbdy*','wrfinput*','*.TS', 'tslist.txt','rsl.error.0000','*.PH','*.QV','*.TH','*.UU','*.VV') + if confirm_switch: + confirm = raw_input("Move data files to directory"+outloc+" (y/n) ? ") + + if confirm=='n': + print "Aborting." + raise Exception + elif confirm=='y': + pass + else: + print "Type y or n." + raise Exception + + ### Create commands + commandlist = [] + #outputdir = rootdir + small_dom + model + init_time + for f in files: + cmnd = 'mv ' + pathtoWRF + f + ' ' + outloc + #if model == 'GEFS/': + # cmnd += 'ensemble_no' + commandlist.append(cmnd) + + os.system('cp '+pathtoWRF+'namelist.input ' + files[1]) + os.system('cp '+pathtoWRF+'tslist ' + files[5]) + + for cInd, c in enumerate(commandlist): + dir = os.path.dirname(outloc) + print "Checking for directory for command #", cInd + try: + os.stat(dir) + except: + os.makedirs(dir) + print "Creating directory." + os.system(c) + print 'Completing command #', cInd + + # Wipe rsl.error and rsl.out files + os.system('rm -f rsl.error* rsl.out*') + +""" +pathtoWRF = '/ptmp/jrlawson/WRFV3/run/' +outdir = '/ptmp/jrlawson/predictability/' +ensmem = raw_input("Type the ensemble member and number, 'E#', or 'C0' for control.") +copyfiles(pathtoWRF,outdir,ensmem) +""" diff --git a/lazyWRF/lazyWRF/setup_gefs_isu.py b/lazyWRF/lazyWRF/setup_gefs_isu.py new file mode 100644 index 0000000..f194bdd --- /dev/null +++ b/lazyWRF/lazyWRF/setup_gefs_isu.py @@ -0,0 +1,116 @@ +# Create met_em files looping over ensemble members +# Moves output between each +# Assumes Vtables for GEFS data are from Lawson .pdf README file + +# First, manually check namelist.wps +# Then remove met_em and GRIB files not related to this ensemble +# Keep the GRIB soft links if halfway through GEFS ensemble run +# Then run this script + +import os +import sys +import pdb +import glob +import subprocess +from copyfilescript import copyfiles +import time + +linkonly = 0 # Don't submit jobs, just link met_em files + +# FUNCTIONS +def edit_namelist(old,new): + nwps = open('namelist.wps','r').readlines() + for idx, line in enumerate(nwps): + if old in line: + # Prefix for soil intermediate data filename + nwps[idx] = new + " \n" + nameout = open('namelist.wps','w') + nameout.writelines(nwps) + nameout.close() + break + +def get_datestring(): + # Then it opens the WPS namelist, copies the old one + os.system('cp namelist.wps{,.python_backup}') + nwps = open('namelist.wps','r').readlines() + # Get the GEFS initialisation date from here + for line in nwps: + if "start_date" in line: + datestring = line[15:19] + line[20:22] + line[23:25] + break + return datestring + +def gefs_soil(): + # Find intermediate filename line in WPS namelist + # Link to, and ungrib, soil files if they don't exist + if not glob.glob('FILE_SOIL*'): + edit_namelist("prefix"," prefix = 'FILE_SOIL'") + os.system('./link_grib.csh ' + pathtosoildata) + os.system('ln -sf ungrib/Variable_Tables/Vtable.GFS_soilonly Vtable') + os.system('./ungrib.exe') + +def gefs_atmos(i,nextens): + datestring = get_datestring() + print datestring + # Set path to gefs data + pathtogefsdata = gefsdir+datestring+'_'+nextens+'_f*' + + # Now change the namelist.wps to use the atmospheric data prefix + #pdb.set_trace() + edit_namelist("prefix"," prefix = 'FILE_ATMOS'") + + if i: + os.system('rm GRIB* FILE_ATMOS* met_em.d*') + os.system('./link_grib.csh ' + pathtogefsdata) + os.system('ln -sf ungrib/Variable_Tables/Vtable.GEFSR2 Vtable') + os.system('./ungrib.exe') + + # Combine both GEFS atmos and GFS soil data + edit_namelist("fg_name"," fg_name = 'FILE_SOIL','FILE_ATMOS'") + os.system('./metgrid.exe') + +def submit_job(linkonly=0): + # Soft link data netCDFs files from WPS to WRF + os.system('ln -sf ' + pathtoWPS + 'met_em* ' + pathtoWRF) + if not linkonly: + p_real = subprocess.Popen('qsub -d '+pathtoWRF+' real_run.sh',cwd=pathtoWRF,shell=True,stdout=subprocess.PIPE) + p_real.wait() + jobid = p_real.stdout.read()[:5] # Assuming first five digits = job ID. + # Run WRF but wait until Real has finished without errors + print 'Now submitting wrf.exe.' + # Again, change name of submisGsion script if needed + p_wrf = subprocess.Popen('qsub -d '+pathtoWRF+' wrf_run.sh -W depend=afterok:'+jobid,cwd=pathtoWRF,shell=True) + p_wrf.wait() + + time.sleep(1*60*60) # Wait an hour. + # Makes sure real.exe has finished and wrf.exe is writing to rsl files + + finished = 0 + while not finished: + tailrsl = subprocess.Popen('tail '+pathtoWRF+'rsl.error.0000',shell=True,stdout=subprocess.PIPE) + tailoutput = tailrsl.stdout.read() + if "SUCCESS COMPLETE WRF" in tailoutput: + finished = 1 + print "WRF has finished; moving to next case." + else: + time.sleep(5*60) # Try again in 5 min + +# SETTINGS +# Directory with GEFS data +datestr = '20060526' +pathtoWPS = '/ptmp/jrlawson/WPS/' +gefsdir = './gefsfiles/' + datestr + '/' +pathtosoildata = './gfsfiles/'+datestr+'/gfsanl*' +pathtoWRF = '/ptmp/jrlawson/WRFV3/run/' +pathtometem = '/ptmp/jrlawson/wrfout/bowecho/' +ensemblelist = ['p'+ '%02u' %n for n in range(1,11)] +#ensemblelist = ['c00'] + ['p'+ '%02u' %n for n in range(1,11)] + +for i,nextens in enumerate(ensemblelist): + if i==0: + os.system('./geogrid.exe') + gefs_soil() + gefs_atmos(i,nextens) + submit_job(linkonly=linkonly) + if not linkonly: + copyfiles(pathtoWRF,pathtometem,nextens) From e448bcf235dcc81c05920e54603c6447eb9a2ab5 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 11 Feb 2014 15:54:34 -0600 Subject: [PATCH 039/111] Streamlined plotting for one member. --- postWRF/bin/casestudy_settings.py | 2 +- postWRF/postWRF/__init__.py | 45 +- postWRF/postWRF/birdseye.py | 27 +- postWRF/postWRF/colourtables.py | 1050 ++++++++++++++--------------- postWRF/postWRF/constants.py | 3 + postWRF/postWRF/figure.py | 6 +- postWRF/postWRF/lookuptable.py | 3 + postWRF/postWRF/scales.py | 30 +- postWRF/postWRF/utils.py | 4 +- postWRF/postWRF/wrfout.py | 93 ++- 10 files changed, 688 insertions(+), 575 deletions(-) create mode 100644 postWRF/postWRF/constants.py create mode 100644 postWRF/postWRF/lookuptable.py diff --git a/postWRF/bin/casestudy_settings.py b/postWRF/bin/casestudy_settings.py index 62adf78..770d9ac 100644 --- a/postWRF/bin/casestudy_settings.py +++ b/postWRF/bin/casestudy_settings.py @@ -4,7 +4,7 @@ def __init__(self): self.output_root = '/home/jrlawson/public_html' self.wrfout_root = '/tera9/jrlawson/' # Optional settings: - self.DPI = 250.0 + self.DPI = 120.0 self.plot_titles = True self.basemap = True self.terrain = False diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index 980d27c..4688416 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -30,6 +30,7 @@ from birdseye import BirdsEye #import scales from defaults import Defaults +from lookuptable import LookUpTable import utils class WRFEnviron: @@ -39,9 +40,12 @@ def __init__(self,config): # Set defaults if they don't appear in user's settings self.D = Defaults() + # Get look-up table of variables + self.LT = LookUpTable() + self.font_prop = getattr(self.C,'font_prop',self.D.font_prop) self.usetex = getattr(self.C,'usetex',self.D.usetex) - self.dpi = getattr(self.C,'dpi',self.D.dpi) + self.dpi = getattr(self.C,'DPI',self.D.dpi) self.plot_titles = getattr(self.C,'plot_titles',self.D.plot_titles) # Set some general settings @@ -87,8 +91,42 @@ def string_from_time(self,usage,t,dom=0,strlen=0,conven=0): str = utils.string_from_time(usage=usage,t=t,dom=dom,strlen=strlen,conven=conven) return str + def plot_2D(self,vardict,times,path_to_wrfout=0): + # Loop over different wrfout files? + if not path_to_wrfout: + # Use path in config file + wrfout = self.wrfout_files_in(self.C.wrfout_root) + + self.en = self.get_sequence(wrfout) + self.pt = self.get_sequence(times) # List of plot times + + #pdb.set_trace() + for en in self.en: + #pdb.set_trace() + W = WRFOut(en) # Only load netCDF file once! + for pt in self.pt: + for va,lv in vardict.iteritems(): + print("Plotting {0} at level {1} for time {2}.".format( + va,lv,pt)) + F = BirdsEye(self.C,W) + F.plot2D(va,pt,lv) + + + def plot_variable2D(self,varlist,timelist): + self.va = self.get_sequence(varlist) # List of variables + self.pt = self.get_sequence(timelist) # List of plot times + + # Where is logic to + + for x in itertools.product(self.va,self.pt): + va, pt = x + W = WRFOut(self.C.wrfout_root) + F = BirdsEye(self.C,W) + F.plot2D(va,pt,lv=2000) + + """ def plot_variable2D(self,va,pt,en,lv,p2p,na=0,da=0): - """Plot a longitude--latitude cross-section (bird's-eye-view). + ###Plot a longitude--latitude cross-section (bird's-eye-view). Use Basemap to create geographical data ======== @@ -108,7 +146,7 @@ def plot_variable2D(self,va,pt,en,lv,p2p,na=0,da=0): da = smaller domain area(s), needs dictionary || DEFAULT = 0 na = naming scheme for plot files || DEFAULT = get what you're given - """ + ### va = self.get_sequence(va) pt = self.get_sequence(pt,SoS=1) en = self.get_sequence(en) @@ -134,6 +172,7 @@ def plot_variable2D(self,va,pt,en,lv,p2p,na=0,da=0): pt_s = utils.string_from_time('title',pt) print("Plotting from file {0}: \n variable = {1}" " time = {2}, level = {3}, area = {4}.".format(en,va,pt_s,lv,da)) + """ def make_iterator2(self,*args): for arg in args: diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 39fe8e3..24dbe68 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -1,17 +1,20 @@ import pdb +import matplotlib as M +M.use('Agg') import matplotlib.pyplot as plt from mpl_toolkits.basemap import Basemap from defaults import Defaults from figure import Figure import utils +import scales class BirdsEye(Figure): - def __init__(self,config,wrfout,p2p): + def __init__(self,config,wrfout): self.C = config self.W = wrfout self.D = Defaults() - self.p2p = p2p + self.p2p = self.C.output_root def plot_data(self,data,mplcommand,fname,pt,V=0): # INITIALISE @@ -42,8 +45,9 @@ def plot_data(self,data,mplcommand,fname,pt,V=0): self.save(self.fig,self.p2p,self.fname) self.fig.clf() - def plot2D(self,va,pt,en,lv,da,na): + def plot2D(self,va,pt,lv,da=0,na=0): # INITIALISE + #en = self.W.path self.fig = plt.figure() self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user self.bmap,x,y = self.basemap_setup() @@ -55,7 +59,7 @@ def plot2D(self,va,pt,en,lv,da,na): # LEVEL if lv == 2000: lv_idx = 0 - lv = 'sfc' # For naming purposes + lv_na = 'sfc' # For naming purposes else: print("Non-surface levels not supported yet.") raise Exception @@ -66,13 +70,14 @@ def plot2D(self,va,pt,en,lv,da,na): # FETCH DATA PS = {'t': time_idx, 'lv': lv_idx, 'la': lat_sl, 'lo': lon_sl} data = self.W.get(va,PS) + la_n = data.shape[-2] lo_n = data.shape[-1] # COLORBAR, CONTOURING - try: - clvs,cm = scales.get_cm(va,lv) - except: + cm, clvs = scales.get_cm(va,lv) + #pdb.set_trace() + if not cm: self.bmap.contourf(x,y,data.reshape((la_n,lo_n))) else: self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,cmap=cm) @@ -81,21 +86,21 @@ def plot2D(self,va,pt,en,lv,da,na): if self.C.plot_titles: title = utils.string_from_time('title',pt) plt.title(title) - if self.C.plot_colorbar: + if self.C.colorbar: plt.colorbar(orientation='horizontal') # SAVE FIGURE datestr = utils.string_from_time('output',pt) if not na: # Use default naming scheme - na = (va,lv,datestr) + na = (va,lv_na,datestr) else: # Come up with scheme... print("Coming soon: ability to create custom filenames") raise Exception - self.fname = self.create_fname(na) # No da variable here + self.fname = self.create_fname(*na) # No da variable here self.save(self.fig,self.p2p,self.fname) - self.fig.clf() + plt.close() def basemap_setup(self): # Fetch settings diff --git a/postWRF/postWRF/colourtables.py b/postWRF/postWRF/colourtables.py index b622377..af554f6 100644 --- a/postWRF/postWRF/colourtables.py +++ b/postWRF/postWRF/colourtables.py @@ -4,480 +4,480 @@ """ from matplotlib.colors import LinearSegmentedColormap -def RdBufloat(valrange): +def RdBufloat(*args): # Will define a colortable that keeps zero as white length = max(valrange)-min(valrange) distance = 0 - min(valrange) pct = float(distance)/float(length) - RdBufloat_cdict ={'red':((0.00, 0.00, 0.00), - (pct, 1.00, 1.00), - (1.00, 1.00, 1.00)), - 'green': ((0.00, 0.00, 0.00), - (pct, 1.00, 1.00), - (1.00, 0.00, 0.00)), - 'blue': ((0.00, 1.00, 1.00), - (pct, 1.00, 1.00), - (1.00, 0.00, 0.00))} - RdBufloat_coltbl = LinearSegmentedColormap('RdBufloat_COLTBL',RdBufloat_cdict) - return RdBufloat_coltbl + RdBufloat_cdict = {'red':((0.00, 0.00, 0.00), + (pct, 1.00, 1.00), + (1.00, 1.00, 1.00)), + 'green': ((0.00, 0.00, 0.00), + (pct, 1.00, 1.00), + (1.00, 0.00, 0.00)), + 'blue': ((0.00, 1.00, 1.00), + (pct, 1.00, 1.00), + (1.00, 0.00, 0.00))} + RdBufloat_coltbl = LinearSegmentedColormap('RdBufloat_COLTBL',RdBufloat_cdict) + return RdBufloat_coltbl def rain1(*args): - rain_cdict ={'red': ((0.00, 0.00, 0.00), - (1.00, 0.00, 0.00)), - 'green': ((0.00, 1.00, 1.00), - (1.00, 0.20, 0.20)), - 'blue': ((0.00, 0.00, 0.00), - (1.00, 0.00, 0.00))} - rain_coltbl = LinearSegmentedColormap('RAIN_COLTBL',rain_cdict) - return rain_coltbl + rain_cdict ={'red': ((0.00, 0.00, 0.00), + (1.00, 0.00, 0.00)), + 'green': ((0.00, 1.00, 1.00), + (1.00, 0.20, 0.20)), + 'blue': ((0.00, 0.00, 0.00), + (1.00, 0.00, 0.00))} + rain_coltbl = LinearSegmentedColormap('RAIN_COLTBL',rain_cdict) + return rain_coltbl def snow1(*args): - snow_cdict ={'red': ((0.00, 0.00, 0.00), - (1.00, 0.00, 0.00)), - 'green': ((0.00, 0.00, 0.00), - (1.00, 0.00, 0.00)), - 'blue': ((0.00, 1.00, 1.00), - (1.00, 0.20, 0.20))} - snow_coltbl = LinearSegmentedColormap('SNOW_COLTBL',snow_cdict) - return snow_coltbl + snow_cdict ={'red': ((0.00, 0.00, 0.00), + (1.00, 0.00, 0.00)), + 'green': ((0.00, 0.00, 0.00), + (1.00, 0.00, 0.00)), + 'blue': ((0.00, 1.00, 1.00), + (1.00, 0.20, 0.20))} + snow_coltbl = LinearSegmentedColormap('SNOW_COLTBL',snow_cdict) + return snow_coltbl def mixprecip1(*args): - mix_cdict ={'red': ((0.00, 0.20, 0.20), - (1.00, 1.00, 1.00)), - 'green': ((0.00, 0.00, 0.00), - (1.00, 0.00, 0.00)), - 'blue': ((0.00, 0.00, 0.00), - (1.00, 0.00, 0.00))} - mix_coltbl = LinearSegmentedColormap('MIX_COLTBL',mix_cdict) - return mix_coltbl + mix_cdict ={'red': ((0.00, 0.20, 0.20), + (1.00, 1.00, 1.00)), + 'green': ((0.00, 0.00, 0.00), + (1.00, 0.00, 0.00)), + 'blue': ((0.00, 0.00, 0.00), + (1.00, 0.00, 0.00))} + mix_coltbl = LinearSegmentedColormap('MIX_COLTBL',mix_cdict) + return mix_coltbl def grays(): - grays_cdict ={'red': ((0.00, 1.00, 1.00), - (1.00, 0.05, 0.05)), - 'green': ((0.00, 1.00, 1.00), - (1.00, 0.05, 0.05)), - 'blue': ((0.00, 1.00, 1.00), - (1.00, 0.05, 0.05))} - grays_coltbl = LinearSegmentedColormap('GRAYS_COLTBL',grays_cdict) - return grays_coltbl + grays_cdict ={'red': ((0.00, 1.00, 1.00), + (1.00, 0.05, 0.05)), + 'green': ((0.00, 1.00, 1.00), + (1.00, 0.05, 0.05)), + 'blue': ((0.00, 1.00, 1.00), + (1.00, 0.05, 0.05))} + grays_coltbl = LinearSegmentedColormap('GRAYS_COLTBL',grays_cdict) + return grays_coltbl def snow2(): - snowf_cdict ={'red': ((0.00, 0.91, 0.91), - (0.06, 0.81, 0.81), - (0.12, 0.51, 0.51), - (0.18, 0.23, 0.23), - (0.24, 0.11, 0.11), - (0.30, 0.00, 0.00), - (0.36, 0.02, 0.02), - (0.42, 0.02, 0.02), - (0.48, 0.03, 0.03), - (0.54, 0.52, 0.52), - (0.60, 1.00, 1.00), - (0.66, 1.00, 1.00), - (0.72, 1.00, 1.00), - (0.78, 1.00, 1.00), - (0.84, 0.70, 0.70), - (0.90, 0.40, 0.40), - (1.00, 0.20, 0.20)), - 'green': ((0.00, 0.80, 0.80), - (0.06, 0.50, 0.50), - (0.12, 0.20, 0.20), - (0.18, 0.00, 0.00), - (0.24, 0.00, 0.00), - (0.30, 0.00, 0.00), - (0.36, 0.24, 0.24), - (0.42, 0.47, 0.47), - (0.48, 0.70, 0.70), - (0.54, 0.85, 0.85), - (0.60, 1.00, 1.00), - (0.66, 0.67, 0.67), - (0.72, 0.33, 0.33), - (0.78, 0.00, 0.00), - (0.84, 0.00, 0.00), - (0.90, 0.00, 0.00), - (1.00, 0.00, 0.00)), - 'blue': ((0.00, 0.98, 0.98), - (0.06, 0.87, 0.87), - (0.12, 0.58, 0.58), - (0.18, 0.69, 0.69), - (0.24, 0.84, 0.84), - (0.30, 1.00, 1.00), - (0.36, 0.69, 0.69), - (0.42, 0.37, 0.37), - (0.48, 0.06, 0.06), - (0.54, 0.03, 0.03), - (0.60, 0.00, 0.00), - (0.66, 0.00, 0.00), - (0.72, 0.00, 0.00), - (0.78, 0.00, 0.00), - (0.84, 0.00, 0.00), - (0.90, 0.00, 0.00), - (1.00, 0.00, 0.00))} - snowf_coltbl = LinearSegmentedColormap('SNOWF_COLTBL',snowf_cdict) - return snowf_coltbl + snowf_cdict ={'red': ((0.00, 0.91, 0.91), + (0.06, 0.81, 0.81), + (0.12, 0.51, 0.51), + (0.18, 0.23, 0.23), + (0.24, 0.11, 0.11), + (0.30, 0.00, 0.00), + (0.36, 0.02, 0.02), + (0.42, 0.02, 0.02), + (0.48, 0.03, 0.03), + (0.54, 0.52, 0.52), + (0.60, 1.00, 1.00), + (0.66, 1.00, 1.00), + (0.72, 1.00, 1.00), + (0.78, 1.00, 1.00), + (0.84, 0.70, 0.70), + (0.90, 0.40, 0.40), + (1.00, 0.20, 0.20)), + 'green': ((0.00, 0.80, 0.80), + (0.06, 0.50, 0.50), + (0.12, 0.20, 0.20), + (0.18, 0.00, 0.00), + (0.24, 0.00, 0.00), + (0.30, 0.00, 0.00), + (0.36, 0.24, 0.24), + (0.42, 0.47, 0.47), + (0.48, 0.70, 0.70), + (0.54, 0.85, 0.85), + (0.60, 1.00, 1.00), + (0.66, 0.67, 0.67), + (0.72, 0.33, 0.33), + (0.78, 0.00, 0.00), + (0.84, 0.00, 0.00), + (0.90, 0.00, 0.00), + (1.00, 0.00, 0.00)), + 'blue': ((0.00, 0.98, 0.98), + (0.06, 0.87, 0.87), + (0.12, 0.58, 0.58), + (0.18, 0.69, 0.69), + (0.24, 0.84, 0.84), + (0.30, 1.00, 1.00), + (0.36, 0.69, 0.69), + (0.42, 0.37, 0.37), + (0.48, 0.06, 0.06), + (0.54, 0.03, 0.03), + (0.60, 0.00, 0.00), + (0.66, 0.00, 0.00), + (0.72, 0.00, 0.00), + (0.78, 0.00, 0.00), + (0.84, 0.00, 0.00), + (0.90, 0.00, 0.00), + (1.00, 0.00, 0.00))} + snowf_coltbl = LinearSegmentedColormap('SNOWF_COLTBL',snowf_cdict) + return snowf_coltbl def reflect(): - reflect_cdict ={'red': ((0.000, 0.40, 0.40), - (0.067, 0.20, 0.20), - (0.133, 0.00, 0.00), - (0.200, 0.00, 0.00), - (0.267, 0.00, 0.00), - (0.333, 0.00, 0.00), - (0.400, 1.00, 1.00), - (0.467, 1.00, 1.00), - (0.533, 1.00, 1.00), - (0.600, 1.00, 1.00), - (0.667, 0.80, 0.80), - (0.733, 0.60, 0.60), - (0.800, 1.00, 1.00), - (0.867, 0.60, 0.60), - (0.933, 1.00, 1.00), - (1.000, 0.00, 0.00)), - 'green': ((0.000, 1.00, 1.00), - (0.067, 0.60, 0.60), - (0.133, 0.00, 0.00), - (0.200, 1.00, 1.00), - (0.267, 0.80, 0.80), - (0.333, 0.60, 0.60), - (0.400, 1.00, 1.00), - (0.467, 0.80, 0.80), - (0.533, 0.40, 0.40), - (0.600, 0.00, 0.00), - (0.667, 0.20, 0.20), - (0.733, 0.00, 0.00), - (0.800, 0.00, 0.00), - (0.867, 0.20, 0.20), - (0.933, 1.00, 1.00), - (1.000, 1.00, 1.00)), - 'blue': ((0.000, 1.00, 1.00), - (0.067, 1.00, 1.00), - (0.133, 1.00, 1.00), - (0.200, 0.00, 0.00), - (0.267, 0.00, 0.00), - (0.333, 0.00, 0.00), - (0.400, 0.00, 0.00), - (0.467, 0.00, 0.00), - (0.533, 0.00, 0.00), - (0.600, 0.00, 0.00), - (0.667, 0.00, 0.00), - (0.733, 0.00, 0.00), - (0.800, 1.00, 1.00), - (0.867, 0.80, 0.80), - (0.933, 1.00, 1.00), - (1.000, 1.00, 1.00))} - reflect_coltbl = LinearSegmentedColormap('REFLECT_COLTBL',reflect_cdict) - return reflect_coltbl - -def reflect_ncdc(): - - - - reflect_ncdc_cdict ={'red':((0.0000, 0.000, 0.000), - (0.0714, 0.000, 0.000), - (0.1429, 0.000, 0.000), - (0.2143, 0.000, 0.000), - (0.2857, 0.000, 0.000), - (0.3571, 0.000, 0.000), - (0.4286, 1.000, 1.000), - (0.5000, 0.906, 0.906), - (0.5714, 1.000, 1.000), - (0.6429, 1.000, 1.000), - (0.7143, 0.839, 0.839), - (0.7857, 0.753, 0.753), - (0.8571, 1.000, 1.000), - (0.9286, 0.600, 0.600), - (1.000, 0.923, 0.923)), - 'green': ((0.0000, 0.925, 0.925), - (0.0714, 0.627, 0.627), - (0.1429, 0.000, 0.000), - (0.2143, 1.000, 1.000), - (0.2857, 0.784, 0.784), - (0.3571, 0.565, 0.565), - (0.4286, 1.000, 1.000), - (0.5000, 0.753, 0.753), - (0.5714, 0.565, 0.565), - (0.6429, 0.000, 0.000), - (0.7143, 0.000, 0.000), - (0.7857, 0.000, 0.000), - (0.8571, 0.000, 0.000), - (0.9286, 0.333, 0.333), - (1.000, 0.923, 0.923)), - - 'blue': ((0.0000, 0.925, 0.925), - (0.0714, 0.965, 0.965), - (0.1429, 0.965, 0.965), - (0.2143, 0.000, 0.000), - (0.2857, 0.000, 0.000), - (0.3571, 0.000, 0.000), - (0.4286, 0.000, 0.000), - (0.5000, 0.000, 0.000), - (0.5714, 0.000, 0.000), - (0.6429, 0.000, 0.000), - (0.7143, 0.000, 0.000), - (0.7857, 0.000, 0.000), - (0.8571, 1.000, 1.000), - (0.9286, 0.788, 0.788), - (1.000, 0.923, 0.923))} - reflect_ncdc_coltbl = LinearSegmentedColormap('REFLECT_NCDC_COLTBL',reflect_ncdc_cdict) - return reflect_ncdc_coltbl + reflect_cdict ={'red': ((0.000, 0.40, 0.40), + (0.067, 0.20, 0.20), + (0.133, 0.00, 0.00), + (0.200, 0.00, 0.00), + (0.267, 0.00, 0.00), + (0.333, 0.00, 0.00), + (0.400, 1.00, 1.00), + (0.467, 1.00, 1.00), + (0.533, 1.00, 1.00), + (0.600, 1.00, 1.00), + (0.667, 0.80, 0.80), + (0.733, 0.60, 0.60), + (0.800, 1.00, 1.00), + (0.867, 0.60, 0.60), + (0.933, 1.00, 1.00), + (1.000, 0.00, 0.00)), + 'green': ((0.000, 1.00, 1.00), + (0.067, 0.60, 0.60), + (0.133, 0.00, 0.00), + (0.200, 1.00, 1.00), + (0.267, 0.80, 0.80), + (0.333, 0.60, 0.60), + (0.400, 1.00, 1.00), + (0.467, 0.80, 0.80), + (0.533, 0.40, 0.40), + (0.600, 0.00, 0.00), + (0.667, 0.20, 0.20), + (0.733, 0.00, 0.00), + (0.800, 0.00, 0.00), + (0.867, 0.20, 0.20), + (0.933, 1.00, 1.00), + (1.000, 1.00, 1.00)), + 'blue': ((0.000, 1.00, 1.00), + (0.067, 1.00, 1.00), + (0.133, 1.00, 1.00), + (0.200, 0.00, 0.00), + (0.267, 0.00, 0.00), + (0.333, 0.00, 0.00), + (0.400, 0.00, 0.00), + (0.467, 0.00, 0.00), + (0.533, 0.00, 0.00), + (0.600, 0.00, 0.00), + (0.667, 0.00, 0.00), + (0.733, 0.00, 0.00), + (0.800, 1.00, 1.00), + (0.867, 0.80, 0.80), + (0.933, 1.00, 1.00), + (1.000, 1.00, 1.00))} + reflect_coltbl = LinearSegmentedColormap('REFLECT_COLTBL',reflect_cdict) + return reflect_coltbl + +def reflect_ncdc(*args): + + + + reflect_ncdc_cdict ={'red':((0.0000, 0.000, 0.000), + (0.0714, 0.000, 0.000), + (0.1429, 0.000, 0.000), + (0.2143, 0.000, 0.000), + (0.2857, 0.000, 0.000), + (0.3571, 0.000, 0.000), + (0.4286, 1.000, 1.000), + (0.5000, 0.906, 0.906), + (0.5714, 1.000, 1.000), + (0.6429, 1.000, 1.000), + (0.7143, 0.839, 0.839), + (0.7857, 0.753, 0.753), + (0.8571, 1.000, 1.000), + (0.9286, 0.600, 0.600), + (1.000, 0.923, 0.923)), + 'green': ((0.0000, 0.925, 0.925), + (0.0714, 0.627, 0.627), + (0.1429, 0.000, 0.000), + (0.2143, 1.000, 1.000), + (0.2857, 0.784, 0.784), + (0.3571, 0.565, 0.565), + (0.4286, 1.000, 1.000), + (0.5000, 0.753, 0.753), + (0.5714, 0.565, 0.565), + (0.6429, 0.000, 0.000), + (0.7143, 0.000, 0.000), + (0.7857, 0.000, 0.000), + (0.8571, 0.000, 0.000), + (0.9286, 0.333, 0.333), + (1.000, 0.923, 0.923)), + + 'blue': ((0.0000, 0.925, 0.925), + (0.0714, 0.965, 0.965), + (0.1429, 0.965, 0.965), + (0.2143, 0.000, 0.000), + (0.2857, 0.000, 0.000), + (0.3571, 0.000, 0.000), + (0.4286, 0.000, 0.000), + (0.5000, 0.000, 0.000), + (0.5714, 0.000, 0.000), + (0.6429, 0.000, 0.000), + (0.7143, 0.000, 0.000), + (0.7857, 0.000, 0.000), + (0.8571, 1.000, 1.000), + (0.9286, 0.788, 0.788), + (1.000, 0.923, 0.923))} + reflect_ncdc_coltbl = LinearSegmentedColormap('REFLECT_NCDC_COLTBL',reflect_ncdc_cdict) + return reflect_ncdc_coltbl def irsat(): - irsat_cdict ={'red': ((0.000, 1.000, 0.294), - (0.067, 1.000, 1.000), - (0.133, 0.804, 0.804), - (0.200, 0.369, 0.369), - (0.267, 0.627, 0.627), - (0.333, 0.804, 0.804), - (0.400, 1.000, 1.000), - (0.567, 0.000, 0.000), - (0.667, 0.400, 0.400), - (0.700, 0.596, 0.596), - (0.800, 0.000, 0.000), - (0.867, 0.416, 0.416), - (0.933, 0.804, 0.804), - (1.000, 0.294, 0.294)), - 'green': ((0.000, 1.000, 0.000), - (0.067, 0.000, 0.000), - (0.133, 0.361, 0.361), - (0.200, 0.149, 0.149), - (0.267, 0.322, 0.322), - (0.333, 0.584, 0.584), - (0.400, 0.757, 0.757), - (0.567, 0.392, 0.392), - (0.667, 0.804, 0.804), - (0.700, 0.961, 0.961), - (0.800, 0.000, 0.000), - (0.867, 0.353, 0.353), - (0.933, 0.000, 0.000), - (1.000, 0.000, 0.000)), - 'blue': ((0.000, 1.000, 1.000), - (0.067, 0.000, 0.000), - (0.133, 0.360, 0.360), - (0.200, 0.070, 0.070), - (0.267, 0.176, 0.176), - (0.333, 0.047, 0.047), - (0.400, 0.145, 0.145), - (0.567, 0.000, 0.000), - (0.667, 0.667, 0.667), - (0.700, 1.000, 1.000), - (0.800, 0.502, 0.502), - (0.867, 0.804, 0.804), - (0.933, 0.804, 0.804), - (1.000, 0.510, 0.510))} - irsat_coltbl = LinearSegmentedColormap('IRSAT_COLTBL',irsat_cdict) - return irsat_coltbl + irsat_cdict ={'red': ((0.000, 1.000, 0.294), + (0.067, 1.000, 1.000), + (0.133, 0.804, 0.804), + (0.200, 0.369, 0.369), + (0.267, 0.627, 0.627), + (0.333, 0.804, 0.804), + (0.400, 1.000, 1.000), + (0.567, 0.000, 0.000), + (0.667, 0.400, 0.400), + (0.700, 0.596, 0.596), + (0.800, 0.000, 0.000), + (0.867, 0.416, 0.416), + (0.933, 0.804, 0.804), + (1.000, 0.294, 0.294)), + 'green': ((0.000, 1.000, 0.000), + (0.067, 0.000, 0.000), + (0.133, 0.361, 0.361), + (0.200, 0.149, 0.149), + (0.267, 0.322, 0.322), + (0.333, 0.584, 0.584), + (0.400, 0.757, 0.757), + (0.567, 0.392, 0.392), + (0.667, 0.804, 0.804), + (0.700, 0.961, 0.961), + (0.800, 0.000, 0.000), + (0.867, 0.353, 0.353), + (0.933, 0.000, 0.000), + (1.000, 0.000, 0.000)), + 'blue': ((0.000, 1.000, 1.000), + (0.067, 0.000, 0.000), + (0.133, 0.360, 0.360), + (0.200, 0.070, 0.070), + (0.267, 0.176, 0.176), + (0.333, 0.047, 0.047), + (0.400, 0.145, 0.145), + (0.567, 0.000, 0.000), + (0.667, 0.667, 0.667), + (0.700, 1.000, 1.000), + (0.800, 0.502, 0.502), + (0.867, 0.804, 0.804), + (0.933, 0.804, 0.804), + (1.000, 0.510, 0.510))} + irsat_coltbl = LinearSegmentedColormap('IRSAT_COLTBL',irsat_cdict) + return irsat_coltbl def bw_irsat(): - bw_irsat_cdict ={'red': ((0.000, 1.000, 1.000), - (1.000, 0.000, 0.000)), - 'green': ((0.000, 1.000, 1.000), - (1.000, 0.000, 0.000)), - 'blue': ((0.000, 1.000, 1.000), - (1.000, 0.000, 0.000))} - bw_irsat_coltbl = LinearSegmentedColormap('BW_IRSAT_COLTBL',bw_irsat_cdict) - return bw_irsat_coltbl + bw_irsat_cdict ={'red': ((0.000, 1.000, 1.000), + (1.000, 0.000, 0.000)), + 'green': ((0.000, 1.000, 1.000), + (1.000, 0.000, 0.000)), + 'blue': ((0.000, 1.000, 1.000), + (1.000, 0.000, 0.000))} + bw_irsat_coltbl = LinearSegmentedColormap('BW_IRSAT_COLTBL',bw_irsat_cdict) + return bw_irsat_coltbl def precip1(): - precip_cdict ={'red': ((0.000, 1.000, 1.000), - (0.004, 0.914, 0.914), - (0.012, 0.812, 0.812), - (0.020, 0.514, 0.514), - (0.040, 0.227, 0.227), - (0.060, 0.114, 0.114), - (0.080, 0.000, 0.000), - (0.100, 0.012, 0.012), - (0.120, 0.020, 0.020), - (0.160, 0.031, 0.031), - (0.200, 0.518, 0.518), - (0.240, 1.000, 1.000), - (0.280, 1.000, 1.000), - (0.320, 1.000, 1.000), - (0.360, 1.000, 1.000), - (0.400, 0.702, 0.702), - (0.500, 0.490, 0.490), - (0.600, 0.294, 0.294), - (0.700, 0.196, 0.196), - (0.800, 0.980, 0.980), - (1.000, 1.000, 1.000)), - 'green': ((0.000, 1.000, 0.000), - (0.004, 0.800, 0.800), - (0.012, 0.502, 0.502), - (0.020, 0.200, 0.200), - (0.040, 0.000, 0.000), - (0.060, 0.000, 0.000), - (0.080, 0.000, 0.000), - (0.100, 0.235, 0.235), - (0.120, 0.467, 0.467), - (0.160, 0.702, 0.702), - (0.200, 0.851, 0.851), - (0.240, 1.000, 1.000), - (0.280, 0.667, 0.667), - (0.320, 0.227, 0.227), - (0.360, 0.000, 0.000), - (0.400, 0.000, 0.000), - (0.500, 0.000, 0.000), - (0.600, 0.000, 0.000), - (0.700, 0.000, 0.000), - (0.800, 0.773, 0.773), - (1.000, 1.000, 1.000)), - 'blue': ((0.000, 1.000, 1.000), - (0.004, 0.976, 0.976), - (0.012, 0.875, 0.875), - (0.020, 0.576, 0.576), - (0.040, 0.690, 0.690), - (0.060, 0.843, 0.843), - (0.080, 1.000, 1.000), - (0.100, 0.686, 0.686), - (0.120, 0.372, 0.372), - (0.160, 0.059, 0.059), - (0.200, 0.031, 0.031), - (0.240, 0.000, 0.000), - (0.280, 0.000, 0.000), - (0.320, 0.000, 0.000), - (0.360, 0.000, 0.000), - (0.400, 0.000, 0.000), - (0.500, 0.000, 0.000), - (0.600, 0.000, 0.000), - (0.700, 0.000, 0.000), - (0.800, 0.980, 0.980), - (1.000, 1.000, 1.000))} - precip_coltbl = LinearSegmentedColormap('PRECIP_COLTBL',precip_cdict) - return precip_coltbl + precip_cdict ={'red': ((0.000, 1.000, 1.000), + (0.004, 0.914, 0.914), + (0.012, 0.812, 0.812), + (0.020, 0.514, 0.514), + (0.040, 0.227, 0.227), + (0.060, 0.114, 0.114), + (0.080, 0.000, 0.000), + (0.100, 0.012, 0.012), + (0.120, 0.020, 0.020), + (0.160, 0.031, 0.031), + (0.200, 0.518, 0.518), + (0.240, 1.000, 1.000), + (0.280, 1.000, 1.000), + (0.320, 1.000, 1.000), + (0.360, 1.000, 1.000), + (0.400, 0.702, 0.702), + (0.500, 0.490, 0.490), + (0.600, 0.294, 0.294), + (0.700, 0.196, 0.196), + (0.800, 0.980, 0.980), + (1.000, 1.000, 1.000)), + 'green': ((0.000, 1.000, 0.000), + (0.004, 0.800, 0.800), + (0.012, 0.502, 0.502), + (0.020, 0.200, 0.200), + (0.040, 0.000, 0.000), + (0.060, 0.000, 0.000), + (0.080, 0.000, 0.000), + (0.100, 0.235, 0.235), + (0.120, 0.467, 0.467), + (0.160, 0.702, 0.702), + (0.200, 0.851, 0.851), + (0.240, 1.000, 1.000), + (0.280, 0.667, 0.667), + (0.320, 0.227, 0.227), + (0.360, 0.000, 0.000), + (0.400, 0.000, 0.000), + (0.500, 0.000, 0.000), + (0.600, 0.000, 0.000), + (0.700, 0.000, 0.000), + (0.800, 0.773, 0.773), + (1.000, 1.000, 1.000)), + 'blue': ((0.000, 1.000, 1.000), + (0.004, 0.976, 0.976), + (0.012, 0.875, 0.875), + (0.020, 0.576, 0.576), + (0.040, 0.690, 0.690), + (0.060, 0.843, 0.843), + (0.080, 1.000, 1.000), + (0.100, 0.686, 0.686), + (0.120, 0.372, 0.372), + (0.160, 0.059, 0.059), + (0.200, 0.031, 0.031), + (0.240, 0.000, 0.000), + (0.280, 0.000, 0.000), + (0.320, 0.000, 0.000), + (0.360, 0.000, 0.000), + (0.400, 0.000, 0.000), + (0.500, 0.000, 0.000), + (0.600, 0.000, 0.000), + (0.700, 0.000, 0.000), + (0.800, 0.980, 0.980), + (1.000, 1.000, 1.000))} + precip_coltbl = LinearSegmentedColormap('PRECIP_COLTBL',precip_cdict) + return precip_coltbl def dewpoint1(): - dwp_cdict ={'red': ((0.00, 0.60, 0.60), - (0.35, 0.70, 0.70), - (0.40, 0.80, 0.80), - (0.45, 0.90, 0.90), - (0.50, 1.00, 1.00), - (0.55, 0.90, 0.90), - (0.60, 0.76, 0.76), - (0.70, 0.64, 0.64), - (0.75, 0.52, 0.52), - (0.85, 0.42, 0.42), - (1.00, 0.32, 0.32)), - 'green': ((0.00, 0.33, 0.33), - (0.35, 0.44, 0.44), - (0.40, 0.56, 0.56), - (0.45, 0.69, 0.69), - (0.50, 0.85, 0.85), - (0.55, 1.00, 1.00), - (0.60, 0.90, 0.90), - (0.70, 0.80, 0.80), - (0.75, 0.70, 0.70), - (0.85, 0.60, 0.60), - (1.00, 0.50, 0.50)), - 'blue': ((0.00, 0.06, 0.06), - (0.35, 0.17, 0.17), - (0.40, 0.32, 0.32), - (0.45, 0.49, 0.49), - (0.50, 0.70, 0.70), - (0.55, 0.70, 0.70), - (0.60, 0.49, 0.49), - (0.70, 0.32, 0.32), - (0.75, 0.17, 0.17), - (0.85, 0.06, 0.06), - (1.00, 0.05, 0.05))} - - dwp_coltbl = LinearSegmentedColormap('DWP_COLTBL',dwp_cdict) - return dwp_coltbl + dwp_cdict ={'red': ((0.00, 0.60, 0.60), + (0.35, 0.70, 0.70), + (0.40, 0.80, 0.80), + (0.45, 0.90, 0.90), + (0.50, 1.00, 1.00), + (0.55, 0.90, 0.90), + (0.60, 0.76, 0.76), + (0.70, 0.64, 0.64), + (0.75, 0.52, 0.52), + (0.85, 0.42, 0.42), + (1.00, 0.32, 0.32)), + 'green': ((0.00, 0.33, 0.33), + (0.35, 0.44, 0.44), + (0.40, 0.56, 0.56), + (0.45, 0.69, 0.69), + (0.50, 0.85, 0.85), + (0.55, 1.00, 1.00), + (0.60, 0.90, 0.90), + (0.70, 0.80, 0.80), + (0.75, 0.70, 0.70), + (0.85, 0.60, 0.60), + (1.00, 0.50, 0.50)), + 'blue': ((0.00, 0.06, 0.06), + (0.35, 0.17, 0.17), + (0.40, 0.32, 0.32), + (0.45, 0.49, 0.49), + (0.50, 0.70, 0.70), + (0.55, 0.70, 0.70), + (0.60, 0.49, 0.49), + (0.70, 0.32, 0.32), + (0.75, 0.17, 0.17), + (0.85, 0.06, 0.06), + (1.00, 0.05, 0.05))} + + dwp_coltbl = LinearSegmentedColormap('DWP_COLTBL',dwp_cdict) + return dwp_coltbl def sftemp(): - sfc_cdict ={'red': ((0.00, 0.20, 0.20), - (0.08, 0.40, 0.40), - (0.17, 0.27, 0.27), - (0.25, 0.80, 0.80), - (0.33, 0.20, 0.20), - (0.42, 0.20, 0.20), - (0.50, 0.00, 0.00), - (0.58, 0.99, 0.99), - (0.67, 1.00, 1.00), - (0.75, 0.82, 0.82), - (0.83, 0.53, 0.53), - (0.92, 0.95, 0.95), - (1.00, 1.00, 1.00)), - - 'green': ((0.00, 0.20, 0.20), - (0.08, 0.40, 0.40), - (0.17, 0.00, 0.00), - (0.25, 0.60, 0.60), - (0.33, 0.40, 0.40), - (0.42, 0.60, 0.60), - (0.50, 0.39, 0.39), - (0.58, 0.76, 0.76), - (0.67, 0.36, 0.36), - (0.75, 0.02, 0.02), - (0.83, 0.00, 0.00), - (0.92, 0.03, 0.03), - (1.00, 0.60, 0.60)), - - 'blue': ((0.00, 0.60, 0.60), - (0.08, 0.60, 0.60), - (0.17, 0.65, 0.65), - (0.25, 1.00, 1.00), - (0.33, 1.00, 1.00), - (0.42, 0.40, 0.40), - (0.50, 0.07, 0.07), - (0.58, 0.02, 0.02), - (0.67, 0.00, 0.00), - (0.75, 0.01, 0.01), - (0.83, 0.00, 0.00), - (0.92, 0.52, 0.52), - (1.00, 0.80, 0.80))} - - - sfc_coltbl = LinearSegmentedColormap('SFC_COLTBL',sfc_cdict) - return sfc_coltbl + sfc_cdict ={'red': ((0.00, 0.20, 0.20), + (0.08, 0.40, 0.40), + (0.17, 0.27, 0.27), + (0.25, 0.80, 0.80), + (0.33, 0.20, 0.20), + (0.42, 0.20, 0.20), + (0.50, 0.00, 0.00), + (0.58, 0.99, 0.99), + (0.67, 1.00, 1.00), + (0.75, 0.82, 0.82), + (0.83, 0.53, 0.53), + (0.92, 0.95, 0.95), + (1.00, 1.00, 1.00)), + + 'green': ((0.00, 0.20, 0.20), + (0.08, 0.40, 0.40), + (0.17, 0.00, 0.00), + (0.25, 0.60, 0.60), + (0.33, 0.40, 0.40), + (0.42, 0.60, 0.60), + (0.50, 0.39, 0.39), + (0.58, 0.76, 0.76), + (0.67, 0.36, 0.36), + (0.75, 0.02, 0.02), + (0.83, 0.00, 0.00), + (0.92, 0.03, 0.03), + (1.00, 0.60, 0.60)), + + 'blue': ((0.00, 0.60, 0.60), + (0.08, 0.60, 0.60), + (0.17, 0.65, 0.65), + (0.25, 1.00, 1.00), + (0.33, 1.00, 1.00), + (0.42, 0.40, 0.40), + (0.50, 0.07, 0.07), + (0.58, 0.02, 0.02), + (0.67, 0.00, 0.00), + (0.75, 0.01, 0.01), + (0.83, 0.00, 0.00), + (0.92, 0.52, 0.52), + (1.00, 0.80, 0.80))} + + + sfc_coltbl = LinearSegmentedColormap('SFC_COLTBL',sfc_cdict) + return sfc_coltbl def thetae(): - thte_cdict ={'red': ((0.00, 0.20, 0.20), - (0.08, 0.40, 0.40), - (0.17, 0.27, 0.27), - (0.25, 0.80, 0.80), - (0.33, 0.20, 0.20), - (0.42, 0.20, 0.20), - (0.50, 0.00, 0.00), - (0.58, 0.99, 0.99), - (0.67, 1.00, 1.00), - (0.75, 0.82, 0.82), - (0.83, 0.53, 0.53), - (0.92, 0.95, 0.95), - (1.00, 1.00, 1.00)), - - 'green': ((0.00, 0.20, 0.20), - (0.08, 0.40, 0.40), - (0.17, 0.00, 0.00), - (0.25, 0.60, 0.60), - (0.33, 0.40, 0.40), - (0.42, 0.60, 0.60), - (0.50, 0.39, 0.39), - (0.58, 0.76, 0.76), - (0.67, 0.36, 0.36), - (0.75, 0.02, 0.02), - (0.83, 0.00, 0.00), - (0.92, 0.03, 0.03), - (1.00, 0.60, 0.60)), - - 'blue': ((0.00, 0.60, 0.60), - (0.08, 0.60, 0.60), - (0.17, 0.65, 0.65), - (0.25, 1.00, 1.00), - (0.33, 1.00, 1.00), - (0.42, 0.40, 0.40), - (0.50, 0.07, 0.07), - (0.58, 0.02, 0.02), - (0.67, 0.00, 0.00), - (0.75, 0.01, 0.01), - (0.83, 0.00, 0.00), - (0.92, 0.52, 0.52), - (1.00, 0.80, 0.80))} - - thte_coltbl = LinearSegmentedColormap('THTE_COLTBL',thte_cdict) - return thte_coltbl + thte_cdict ={'red': ((0.00, 0.20, 0.20), + (0.08, 0.40, 0.40), + (0.17, 0.27, 0.27), + (0.25, 0.80, 0.80), + (0.33, 0.20, 0.20), + (0.42, 0.20, 0.20), + (0.50, 0.00, 0.00), + (0.58, 0.99, 0.99), + (0.67, 1.00, 1.00), + (0.75, 0.82, 0.82), + (0.83, 0.53, 0.53), + (0.92, 0.95, 0.95), + (1.00, 1.00, 1.00)), + + 'green': ((0.00, 0.20, 0.20), + (0.08, 0.40, 0.40), + (0.17, 0.00, 0.00), + (0.25, 0.60, 0.60), + (0.33, 0.40, 0.40), + (0.42, 0.60, 0.60), + (0.50, 0.39, 0.39), + (0.58, 0.76, 0.76), + (0.67, 0.36, 0.36), + (0.75, 0.02, 0.02), + (0.83, 0.00, 0.00), + (0.92, 0.03, 0.03), + (1.00, 0.60, 0.60)), + + 'blue': ((0.00, 0.60, 0.60), + (0.08, 0.60, 0.60), + (0.17, 0.65, 0.65), + (0.25, 1.00, 1.00), + (0.33, 1.00, 1.00), + (0.42, 0.40, 0.40), + (0.50, 0.07, 0.07), + (0.58, 0.02, 0.02), + (0.67, 0.00, 0.00), + (0.75, 0.01, 0.01), + (0.83, 0.00, 0.00), + (0.92, 0.52, 0.52), + (1.00, 0.80, 0.80))} + + thte_coltbl = LinearSegmentedColormap('THTE_COLTBL',thte_cdict) + return thte_coltbl def PkBlfloat(datarange): distance = max(datarange)-min(datarange) @@ -519,15 +519,15 @@ def PkBlfloat(datarange): (0.920635*pct, 0.973185, 0.973185),(0.936508*pct, 0.978607, 0.978607), (0.952381*pct, 0.983999, 0.983999),(0.968254*pct, 0.989361, 0.989361), (0.984127*pct, 0.994695, 0.994695),(1.0*pct, 1.0, 1.0), - ((1-pct)*0.125+pct, - 0.87058824300765991, 0.87058824300765991), ((1-pct)*0.25+pct, - 0.7764706015586853, 0.7764706015586853), ((1-pct)*0.375+pct, - 0.61960786581039429, 0.61960786581039429), ((1-pct)*0.5+pct, - 0.41960784792900085, 0.41960784792900085), ((1-pct)*0.625+pct, - 0.25882354378700256, 0.25882354378700256), ((1-pct)*0.75+pct, - 0.12941177189350128, 0.12941177189350128), ((1-pct)*0.875+pct, - 0.031372550874948502, 0.031372550874948502), (1.0, - 0.031372550874948502, 0.031372550874948502)], + ((1-pct)*0.125+pct, + 0.87058824300765991, 0.87058824300765991), ((1-pct)*0.25+pct, + 0.7764706015586853, 0.7764706015586853), ((1-pct)*0.375+pct, + 0.61960786581039429, 0.61960786581039429), ((1-pct)*0.5+pct, + 0.41960784792900085, 0.41960784792900085), ((1-pct)*0.625+pct, + 0.25882354378700256, 0.25882354378700256), ((1-pct)*0.75+pct, + 0.12941177189350128, 0.12941177189350128), ((1-pct)*0.875+pct, + 0.031372550874948502, 0.031372550874948502), (1.0, + 0.031372550874948502, 0.031372550874948502)], @@ -564,14 +564,14 @@ def PkBlfloat(datarange): (0.920635*pct, 0.973185, 0.973185),(0.936508*pct, 0.978607, 0.978607), (0.952381*pct, 0.983999, 0.983999),(0.968254*pct, 0.989361, 0.989361), (0.984127*pct, 0.994695, 0.994695),(1.0*pct, 1.0, 1.0), - ((1-pct)*0.125+pct, 0.92156863212585449, 0.92156863212585449), ((1-pct)*0.25+pct, - 0.85882353782653809, 0.85882353782653809), ((1-pct)*0.375+pct, - 0.7921568751335144, 0.7921568751335144), ((1-pct)*0.5+pct, - 0.68235296010971069, 0.68235296010971069), ((1-pct)*0.625+pct, - 0.57254904508590698, 0.57254904508590698), ((1-pct)*0.75+pct, - 0.44313725829124451, 0.44313725829124451), ((1-pct)*0.875+pct, - 0.31764706969261169, 0.31764706969261169), ((1-pct)*1.0+pct, - 0.18823529779911041, 0.18823529779911041)], + ((1-pct)*0.125+pct, 0.92156863212585449, 0.92156863212585449), ((1-pct)*0.25+pct, + 0.85882353782653809, 0.85882353782653809), ((1-pct)*0.375+pct, + 0.7921568751335144, 0.7921568751335144), ((1-pct)*0.5+pct, + 0.68235296010971069, 0.68235296010971069), ((1-pct)*0.625+pct, + 0.57254904508590698, 0.57254904508590698), ((1-pct)*0.75+pct, + 0.44313725829124451, 0.44313725829124451), ((1-pct)*0.875+pct, + 0.31764706969261169, 0.31764706969261169), ((1-pct)*1.0+pct, + 0.18823529779911041, 0.18823529779911041)], 'blue': [(0.*pct, 0., 0.),(0.015873*pct, 0.102869, 0.102869), @@ -606,13 +606,13 @@ def PkBlfloat(datarange): (0.920635*pct, 0.918109, 0.918109),(0.936508*pct, 0.935061, 0.935061), (0.952381*pct, 0.951711, 0.951711),(0.968254*pct, 0.968075, 0.968075), (0.984127*pct, 0.984167, 0.984167),(1.0*pct, 1.0, 1.0), - ((1-pct)*0.125+pct, 0.9686274528503418, - 0.9686274528503418), ((1-pct)*0.25+pct, 0.93725490570068359, 0.93725490570068359), - ((1-pct)*0.375+pct, 0.88235294818878174, 0.88235294818878174), ((1-pct)*0.5+pct, - 0.83921569585800171, 0.83921569585800171), ((1-pct)*0.625+pct, 0.7764706015586853, - 0.7764706015586853), ((1-pct)*0.75+pct, 0.70980393886566162, 0.70980393886566162), - ((1-pct)*0.875+pct, 0.61176472902297974, 0.61176472902297974), (1.0, - 0.41960784792900085, 0.41960784792900085)]} + ((1-pct)*0.125+pct, 0.9686274528503418, + 0.9686274528503418), ((1-pct)*0.25+pct, 0.93725490570068359, 0.93725490570068359), + ((1-pct)*0.375+pct, 0.88235294818878174, 0.88235294818878174), ((1-pct)*0.5+pct, + 0.83921569585800171, 0.83921569585800171), ((1-pct)*0.625+pct, 0.7764706015586853, + 0.7764706015586853), ((1-pct)*0.75+pct, 0.70980393886566162, 0.70980393886566162), + ((1-pct)*0.875+pct, 0.61176472902297974, 0.61176472902297974), (1.0, + 0.41960784792900085, 0.41960784792900085)]} PkBl_coltbl = LinearSegmentedColormap('PKBL_COLTBL',PkBl_data) return PkBl_coltbl @@ -626,65 +626,65 @@ def PuRdBlfloat(datarange): PuRdBl_data = {'blue': [ - (0.0*pct, 0.12156862765550613,0.12156862765550613), - (0.125*pct,0.26274511218070984, 0.26274511218070984), - (0.25*pct,0.33725491166114807, 0.33725491166114807), - (0.375*pct, 0.54117649793624878, 0.54117649793624878), - (0.5*pct, 0.69019609689712524, 0.69019609689712524), - (0.625*pct, 0.78039216995239258,0.78039216995239258), - (0.75*pct, 0.85490196943283081,0.85490196943283081), - (0.875*pct, 0.93725490570068359,0.93725490570068359), - (1.0*pct, 0.97647058963775635,0.97647058963775635), - ((1-pct)*0.125+pct, 0.9686274528503418, - 0.9686274528503418), ((1-pct)*0.25+pct, 0.93725490570068359, 0.93725490570068359), - ((1-pct)*0.375+pct, 0.88235294818878174, 0.88235294818878174), ((1-pct)*0.5+pct, - 0.83921569585800171, 0.83921569585800171), ((1-pct)*0.625+pct, 0.7764706015586853, - 0.7764706015586853), ((1-pct)*0.75+pct, 0.70980393886566162, 0.70980393886566162), - ((1-pct)*0.875+pct, 0.61176472902297974, 0.61176472902297974), (1.0, - 0.41960784792900085, 0.41960784792900085)], - - - - 'green': [ - (0.0*pct, 0.0, 0.0), - (0.125*pct, 0.0, 0.0), - (0.25*pct, 0.070588238537311554, 0.070588238537311554), - (0.375*pct, 0.16078431904315948, 0.16078431904315948), - (0.5*pct, 0.3960784375667572, 0.3960784375667572), - (0.625*pct, 0.58039218187332153, 0.58039218187332153), - (0.75*pct, 0.72549021244049072, 0.72549021244049072), - (0.875*pct,0.88235294818878174, 0.88235294818878174), - (1.0*pct, 0.95686274766921997, 0.95686274766921997), - ((1-pct)*0.125+pct, 0.92156863212585449, 0.92156863212585449), ((1-pct)*0.25+pct, - 0.85882353782653809, 0.85882353782653809), ((1-pct)*0.375+pct, - 0.7921568751335144, 0.7921568751335144), ((1-pct)*0.5+pct, - 0.68235296010971069, 0.68235296010971069), ((1-pct)*0.625+pct, - 0.57254904508590698, 0.57254904508590698), ((1-pct)*0.75+pct, - 0.44313725829124451, 0.44313725829124451), ((1-pct)*0.875+pct, - 0.31764706969261169, 0.31764706969261169), ((1-pct)*1.0+pct, - 0.18823529779911041, 0.18823529779911041)], - - - - 'red': [ - (0.0*pct, 0.40392157435417175, 0.40392157435417175), - (0.125*pct, 0.59607845544815063, 0.59607845544815063), - (0.25*pct, 0.80784314870834351, 0.80784314870834351), - (0.375*pct, 0.90588235855102539, 0.90588235855102539), - (0.5*pct, 0.87450981140136719, 0.87450981140136719), - (0.625*pct, 0.78823530673980713, 0.78823530673980713), - (0.75*pct, 0.83137255907058716, 0.83137255907058716), - (0.875*pct, 0.90588235855102539, 0.90588235855102539), - (1.0*pct, 0.9686274528503418, 0.9686274528503418), - ((1-pct)*0.125+pct, - 0.87058824300765991, 0.87058824300765991), ((1-pct)*0.25+pct, - 0.7764706015586853, 0.7764706015586853), ((1-pct)*0.375+pct, - 0.61960786581039429, 0.61960786581039429), ((1-pct)*0.5+pct, - 0.41960784792900085, 0.41960784792900085), ((1-pct)*0.625+pct, - 0.25882354378700256, 0.25882354378700256), ((1-pct)*0.75+pct, - 0.12941177189350128, 0.12941177189350128), ((1-pct)*0.875+pct, - 0.031372550874948502, 0.031372550874948502), (1.0, - 0.031372550874948502, 0.031372550874948502)]} + (0.0*pct, 0.12156862765550613,0.12156862765550613), + (0.125*pct,0.26274511218070984, 0.26274511218070984), + (0.25*pct,0.33725491166114807, 0.33725491166114807), + (0.375*pct, 0.54117649793624878, 0.54117649793624878), + (0.5*pct, 0.69019609689712524, 0.69019609689712524), + (0.625*pct, 0.78039216995239258,0.78039216995239258), + (0.75*pct, 0.85490196943283081,0.85490196943283081), + (0.875*pct, 0.93725490570068359,0.93725490570068359), + (1.0*pct, 0.97647058963775635,0.97647058963775635), + ((1-pct)*0.125+pct, 0.9686274528503418, + 0.9686274528503418), ((1-pct)*0.25+pct, 0.93725490570068359, 0.93725490570068359), + ((1-pct)*0.375+pct, 0.88235294818878174, 0.88235294818878174), ((1-pct)*0.5+pct, + 0.83921569585800171, 0.83921569585800171), ((1-pct)*0.625+pct, 0.7764706015586853, + 0.7764706015586853), ((1-pct)*0.75+pct, 0.70980393886566162, 0.70980393886566162), + ((1-pct)*0.875+pct, 0.61176472902297974, 0.61176472902297974), (1.0, + 0.41960784792900085, 0.41960784792900085)], + + + + 'green': [ + (0.0*pct, 0.0, 0.0), + (0.125*pct, 0.0, 0.0), + (0.25*pct, 0.070588238537311554, 0.070588238537311554), + (0.375*pct, 0.16078431904315948, 0.16078431904315948), + (0.5*pct, 0.3960784375667572, 0.3960784375667572), + (0.625*pct, 0.58039218187332153, 0.58039218187332153), + (0.75*pct, 0.72549021244049072, 0.72549021244049072), + (0.875*pct,0.88235294818878174, 0.88235294818878174), + (1.0*pct, 0.95686274766921997, 0.95686274766921997), + ((1-pct)*0.125+pct, 0.92156863212585449, 0.92156863212585449), ((1-pct)*0.25+pct, + 0.85882353782653809, 0.85882353782653809), ((1-pct)*0.375+pct, + 0.7921568751335144, 0.7921568751335144), ((1-pct)*0.5+pct, + 0.68235296010971069, 0.68235296010971069), ((1-pct)*0.625+pct, + 0.57254904508590698, 0.57254904508590698), ((1-pct)*0.75+pct, + 0.44313725829124451, 0.44313725829124451), ((1-pct)*0.875+pct, + 0.31764706969261169, 0.31764706969261169), ((1-pct)*1.0+pct, + 0.18823529779911041, 0.18823529779911041)], + + + + 'red': [ + (0.0*pct, 0.40392157435417175, 0.40392157435417175), + (0.125*pct, 0.59607845544815063, 0.59607845544815063), + (0.25*pct, 0.80784314870834351, 0.80784314870834351), + (0.375*pct, 0.90588235855102539, 0.90588235855102539), + (0.5*pct, 0.87450981140136719, 0.87450981140136719), + (0.625*pct, 0.78823530673980713, 0.78823530673980713), + (0.75*pct, 0.83137255907058716, 0.83137255907058716), + (0.875*pct, 0.90588235855102539, 0.90588235855102539), + (1.0*pct, 0.9686274528503418, 0.9686274528503418), + ((1-pct)*0.125+pct, + 0.87058824300765991, 0.87058824300765991), ((1-pct)*0.25+pct, + 0.7764706015586853, 0.7764706015586853), ((1-pct)*0.375+pct, + 0.61960786581039429, 0.61960786581039429), ((1-pct)*0.5+pct, + 0.41960784792900085, 0.41960784792900085), ((1-pct)*0.625+pct, + 0.25882354378700256, 0.25882354378700256), ((1-pct)*0.75+pct, + 0.12941177189350128, 0.12941177189350128), ((1-pct)*0.875+pct, + 0.031372550874948502, 0.031372550874948502), (1.0, + 0.031372550874948502, 0.031372550874948502)]} #print PuRdBl_data PuRdBl_coltbl = LinearSegmentedColormap('PURDBL_COLTBL',PuRdBl_data) diff --git a/postWRF/postWRF/constants.py b/postWRF/postWRF/constants.py new file mode 100644 index 0000000..698dd32 --- /dev/null +++ b/postWRF/postWRF/constants.py @@ -0,0 +1,3 @@ +kappa = 0.2854 +Lv = 2500000.0 +P0 = 100000.0 diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index b6e8245..58bb8e7 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -15,13 +15,15 @@ class Figure: def __init__(self,config,wrfout): - pass + self.C = config + self.W = wrfout def create_fname(self,*naming): """Default naming should be: Variable + time + level """ - fname = '_'.join([str(a) for a in naming]) + fname = '_'.join([str(a) for a in naming]) + 'Z' + #pdb.set_trace() return fname def title_time(self): diff --git a/postWRF/postWRF/lookuptable.py b/postWRF/postWRF/lookuptable.py new file mode 100644 index 0000000..7dab609 --- /dev/null +++ b/postWRF/postWRF/lookuptable.py @@ -0,0 +1,3 @@ +class LookUpTable(): + def __init__(self): + pass \ No newline at end of file diff --git a/postWRF/postWRF/scales.py b/postWRF/postWRF/scales.py index cf88482..0e80440 100644 --- a/postWRF/postWRF/scales.py +++ b/postWRF/postWRF/scales.py @@ -15,15 +15,17 @@ Default settings are listed at the bottom. These can be overwritten in user's config file... somehow """ - +import pdb from matplotlib.colors import LinearSegmentedColormap +import numpy as N +import matplotlib.pyplot as plt import colourtables as ct def get_cm(va,lv): - # Variable and level determine contour levels - + # Variable and vertical level determine contour scale + #pdb.set_trace() try: if len(A[va][lv]) == 3: # This is a min-max-interval list @@ -32,21 +34,23 @@ def get_cm(va,lv): # This is an actual list of values clvs = A[va][lv] except: + # If no level exists, try finding a near one try: near_lv = find_nearest_level(lv) clvs = A[va][near_lv] except: - # Some variables don't need levels - # Like composite reflectivity + # Some variables don't live on a vertical level clvs = 0 # Variable determines colour table try: cm = A[va]['cmap'](clvs) + #pdb.set_trace() except: - def_ct = plt.cm.get_cmap("jet") - cm = LinearSegmentedColormap(def_ct) - + #print("Using default colourtable.") + #def_ct = plt.cm.get_cmap("jet") + cm = 0 + #cm = LinearSegmentedColormap('DEF_CT',def_ct) return cm, clvs def find_nearest_level(lv): @@ -73,7 +77,7 @@ def find_nearest_level(lv): ######## DEFAULT SETTINGS FOR LEVELS ######## -A = [] +A = {} # Wind magnitude A['wind'] = {} @@ -87,8 +91,8 @@ def find_nearest_level(lv): A['sim_ref'][2000] = (5,90,5) # Simulated reflectivity -A['comp_ref'] = {'cmap':ct.reflect_ncdc} -A['comp_ref'][2000] = (5,90,5) +A['cref'] = {'cmap':ct.reflect_ncdc} +A['cref'][2000] = (5,90,5) # Precipitation A['precip'] = {'cmap':ct.precip1} @@ -96,11 +100,11 @@ def find_nearest_level(lv): 0.70,0.80,0.90,1.00,1.25,1.50,1.75,2.00,2.50] # Precipitable water -A['pwat'] = {'cmap':precip1} +A['pwat'] = {'cmap':ct.precip1} A['pwat'][2000] = (0.2,2.6,0.1) # Snowfall -A['snow'] = {'cmap':snow2} +A['snow'] = {'cmap':ct.snow2} A['snow'][2000] = [0.25,0.5,0.75,1,1.5,2,2.5,3,4,5,6,8,10,12,14,16,18] diff --git a/postWRF/postWRF/utils.py b/postWRF/postWRF/utils.py index 49df2ce..6b4ac46 100644 --- a/postWRF/postWRF/utils.py +++ b/postWRF/postWRF/utils.py @@ -16,7 +16,9 @@ def padded_times(timeseq): return padded def string_from_time(usage,t,dom=0,strlen=0,conven=0,tupleformat=1): - if not tupleformat: + #if not tupleformat: + if isinstance(t,int): + # In this case, time is in datenum. Get it into tuple format. t = time.gmtime(t) if usage == 'title': # Generates string for titles diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 14363a8..df5a5cb 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -11,13 +11,14 @@ import numpy as N import calendar import pdb - +import constants as cc #sys.path.append('/home/jrlawson/gitprojects/meteogeneral/') #from meteogeneral.WRF import wrf_tools class WRFOut: def __init__(self,fpath): + self.path = fpath #self.C = config self.nc = Dataset(fpath,'r') @@ -35,6 +36,9 @@ def __init__(self,fpath): self.x_dim = len(self.nc.dimensions['west_east']) self.y_dim = len(self.nc.dimensions['south_north']) + # Loads variable list + self.fields = self.nc.variables.keys() + def get_time_idx(self,t,tuple_format=0): """ @@ -69,10 +73,23 @@ def get_time_idx(self,t,tuple_format=0): abs(self.wrf_times_epoch-t_epoch).min() )[0][0] return self.time_idx + + + def check_compute(self,var): + """This method returns the required variables + that need to be loaded from the netCDF file. + """ - def get(self,var,PS): + if var in self.fields: + return True + else: + return False + + def get(self,var,slices,**kwargs): """ Fetch a numpy array containing variable data. + If field isn't present in the WRFOut file, compute it. + Slice according to arguments. Destagger if required. @@ -80,29 +97,27 @@ def get(self,var,PS): Returns unstaggered, sliced data. var : netCDF variable name - PS : Plot Settings, keys as follows: + slices : dict, keys as follows: t : time index lv : level index la : latitude slice indices lo : longitude slice indices + keyword arguments might include: + + Shear between 0 and 3 km: + {'top':3, 'bottom',0} + + """ # Check if computing required # When data is loaded from nc, it is destaggered - if var=='pressure': - if lv_idx == 0: - data = self.load('PSFC',PS) - elif var=='sim_ref': - data = self.compute_comp_ref(PS) - elif var=='shear': - data = self.compute_shear(0,3,PS) - elif var=='wind': - u = self.load('U',PS) - v = self.load('V',PS) - data = N.sqrt(u**2 + v**2) + isDefault = self.check_compute(var) + if isDefault: + data = self.load(var,slices) else: - data = self.load(var,PS) + data = self.compute(var,slices,**kwargs) return data @@ -176,15 +191,55 @@ def destagger(self,data,ax): data_unstag = 0.5*(data[sl0] + data[sl1]) return data_unstag - def compute_shear(self,lower,upper): - pass - return shear + def compute(self,var,slices,**kwargs): + """ Look up method needed to return array of data + for required variable. + + Keyword arguments include settings for computation + e.g. top and bottom of shear computation + """ + + tbl = {} + tbl['shear'] = self.compute_shear + tbl['thetae'] = self.compute_thetae + tbl['cref'] = self.compute_comp_ref + tbl['wind10'] = self.compute_wind10 + tbl['wind'] = self.compute_wind + + data = tbl[var](slices,**kwargs) + return data + + def compute_wind10(self,slices): + u = self.get('U10',slices) + v = self.get('V10',slices) + data = N.sqrt(u**2 + v**2) + return data + + def compute_wind(self,slices): + u = self.get('U',slices) + v = self.get('V',slices) + data = N.sqrt(u**2 + v**2) + return data + def compute_shear(self,slices,**kwargs): + """ + kwargs['top'] + kwargs['bottom'] + """ + pass + #return shear + def compute_thetae(self,slices): + P = self.get('pressure',slices) # Computed + Drybulb = self.get('temp',slices) + Q = self.get('Q',slices) + + thetae = (Drybulb + (Q * cc.Lv/cc.cp)) * (cc.P0/P) ** cc.kappa + return thetae def compute_comp_ref(self,PS): """Amend this so variables obtain at start fetch only correct date, lats, lons - All levels need to be fetched as it's composite reflectivity + All levels need to be fetched as this is composite reflectivity """ T2 = self.get('T2',PS) QR = self.nc.variables['QRAIN'][PS['t'],:,PS['la'],PS['lo']] From f3fb382950f9e30f755057c41ea99f4e316fe7cf Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 12 Feb 2014 18:16:23 -0600 Subject: [PATCH 040/111] Aiming towards pressure interpolation --- postWRF/postWRF/__init__.py | 25 +++++++++---- postWRF/postWRF/birdseye.py | 20 +++++++--- postWRF/postWRF/colourtables.py | 9 +++++ postWRF/postWRF/scales.py | 22 +++++++---- postWRF/postWRF/wrfout.py | 65 ++++++++++++++++++++++++++++++++- 5 files changed, 119 insertions(+), 22 deletions(-) diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index 4688416..d794281 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -40,9 +40,6 @@ def __init__(self,config): # Set defaults if they don't appear in user's settings self.D = Defaults() - # Get look-up table of variables - self.LT = LookUpTable() - self.font_prop = getattr(self.C,'font_prop',self.D.font_prop) self.usetex = getattr(self.C,'usetex',self.D.usetex) self.dpi = getattr(self.C,'DPI',self.D.dpi) @@ -92,6 +89,9 @@ def string_from_time(self,usage,t,dom=0,strlen=0,conven=0): return str def plot_2D(self,vardict,times,path_to_wrfout=0): + """ + """ + # Loop over different wrfout files? if not path_to_wrfout: # Use path in config file @@ -103,12 +103,23 @@ def plot_2D(self,vardict,times,path_to_wrfout=0): #pdb.set_trace() for en in self.en: #pdb.set_trace() - W = WRFOut(en) # Only load netCDF file once! + self.W = WRFOut(en) # Only load netCDF file once! for pt in self.pt: for va,lv in vardict.iteritems(): - print("Plotting {0} at level {1} for time {2}.".format( - va,lv,pt)) - F = BirdsEye(self.C,W) + # Check for pressure levels + if lv == 2000: + pass # Standard WRF levels + elif isinstance(lv,int): + nc_path = self.W.path + fpath = self.W.interp_to_p(self.C,nc_path,va,lv) + # Execute p_interp here and reassign self.W to new file + self.W = WRFOut(fpath) + else: + print("Non-pressure levels not supported yet.") + raise Exception + + print("Plotting {0} at lv {1} for time {2}.".format(va,lv,pt)) + F = BirdsEye(self.C,self.W) F.plot2D(va,pt,lv) diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 24dbe68..e72869d 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -3,6 +3,7 @@ M.use('Agg') import matplotlib.pyplot as plt from mpl_toolkits.basemap import Basemap +import numpy as N from defaults import Defaults from figure import Figure @@ -60,8 +61,15 @@ def plot2D(self,va,pt,lv,da=0,na=0): if lv == 2000: lv_idx = 0 lv_na = 'sfc' # For naming purposes + elif isinstance(lv,int): + lv_idx = 0 # Interpolation file will only have the only level + lv_na = str(lv) + 'hPa' + nc_path = self.W.path + fpath = self.W.interp_to_p(self.C,nc_path,va,lv) + # Execute p_interp here and reassign self.W to new file + self.W = WRFOut(fpath) else: - print("Non-surface levels not supported yet.") + print("Non-pressure levels not supported yet.") raise Exception # LAT/LON @@ -76,11 +84,13 @@ def plot2D(self,va,pt,lv,da=0,na=0): # COLORBAR, CONTOURING cm, clvs = scales.get_cm(va,lv) - #pdb.set_trace() - if not cm: - self.bmap.contourf(x,y,data.reshape((la_n,lo_n))) - else: + # pdb.set_trace() + if cm: self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,cmap=cm) + elif isinstance(clvs,N.ndarray): + self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,cmap=plt.cm.jet) + else: + self.bmap.contourf(x,y,data.reshape((la_n,lo_n))) # LABELS, TITLES etc if self.C.plot_titles: diff --git a/postWRF/postWRF/colourtables.py b/postWRF/postWRF/colourtables.py index af554f6..b49e3a0 100644 --- a/postWRF/postWRF/colourtables.py +++ b/postWRF/postWRF/colourtables.py @@ -3,8 +3,17 @@ Adapted from code by Luke Madaus and David-John Gagne II """ from matplotlib.colors import LinearSegmentedColormap +import matplotlib.pyplot as plt +import pdb + +# def default(*args): + # jet_cdict = plt.cm.jet + # jet_tbl = LinearSegmentedColormap('jet_TBL',jet_cdict) + # pdb.set_trace() + # return jet_tbl def RdBufloat(*args): + valrange = args # Will define a colortable that keeps zero as white length = max(valrange)-min(valrange) distance = 0 - min(valrange) diff --git a/postWRF/postWRF/scales.py b/postWRF/postWRF/scales.py index 0e80440..c53cf9b 100644 --- a/postWRF/postWRF/scales.py +++ b/postWRF/postWRF/scales.py @@ -25,7 +25,7 @@ def get_cm(va,lv): # Variable and vertical level determine contour scale - #pdb.set_trace() + # pdb.set_trace() try: if len(A[va][lv]) == 3: # This is a min-max-interval list @@ -33,7 +33,7 @@ def get_cm(va,lv): else: # This is an actual list of values clvs = A[va][lv] - except: + except KeyError: # If no level exists, try finding a near one try: near_lv = find_nearest_level(lv) @@ -41,16 +41,22 @@ def get_cm(va,lv): except: # Some variables don't live on a vertical level clvs = 0 - # Variable determines colour table - + # except: + # raise Exception + try: cm = A[va]['cmap'](clvs) #pdb.set_trace() - except: + except TypeError: #print("Using default colourtable.") #def_ct = plt.cm.get_cmap("jet") cm = 0 #cm = LinearSegmentedColormap('DEF_CT',def_ct) + # else: + # raise Exception + # print va + # print type(cm) + # pdb.set_trace() return cm, clvs def find_nearest_level(lv): @@ -80,11 +86,11 @@ def find_nearest_level(lv): A = {} # Wind magnitude -A['wind'] = {} -A['wind'][2000] = (5,45,5) +A['wind10'] = {'cmap':0} +A['wind10'][2000] = (5,32.5,2.5) # Theta-e (Equivalent potential temperature) -A['thetae'] = {'cmap':ct.thetae} +# A['thetae'] = {'cmap':ct.thetae} # Simulated reflectivity A['sim_ref'] = {'cmap':ct.reflect_ncdc} diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index df5a5cb..26129e9 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -17,9 +17,10 @@ class WRFOut: - def __init__(self,fpath): + def __init__(self,fpath,config=0): self.path = fpath - #self.C = config + if config: + self.C = config self.nc = Dataset(fpath,'r') self.wrf_times = self.nc.variables['Times'][:] @@ -300,6 +301,7 @@ def compute_simref_atlevel(self,level=1): def get_XY(self,lat,lon): """Return grid indices for lat/lon pair. """ + pass def get_lat_idx(self,lat): lat_idx = N.where(abs(self.lats-lat) == abs(self.lats-lat).min())[0][0] @@ -309,5 +311,64 @@ def get_lon_idx(self,lon): lon_idx = N.where(abs(self.lons-lon) == abs(self.lons-lon).min())[0][0] return lon_idx + def interp_to_p(self,config,nc_path,var,lv): + """ Uses p_interp fortran code to put data onto a pressure + level specified. + + Input: + config : contains directory of p_interp files + nc_path : path to original netCDF file data + var : variable(s) to compute + lv : pressure level(s) to compute + + Returns: + fpath : path to new netCDF file with p co-ords + """ + # Fetch paths + p_interp_path = os.path.join(( + config.p_interp_root,'p_interp')) + namelist_path = os.path.join(( + config.p_interp_root,'namelist.pinterp')) + nc_root, nc_fname = os.path.split(nc_path)# Root directory of wrfout file + output_root = nc_root # Directory to dump output file (same) + + """ + Can we add a suffix to the new netCDF file? + """ + # Copy old p_interp for backup + command1 = ' '.join(('cp',p_interp_path,p_interp_path+'.bkup')) + os.system(command1) + + # Edit p_interp's namelist settings + edit_namelist(path_to_interp,'path_to_input',nc_root,col=18) + edit_namelist(path_to_interp,'input_name',nc_fname,col=18) + edit_namelist(path_to_interp,'path_to_output',output_root,col=18) + edit_namelist(path_to_interp,'process','list',col=18) + edit_namelist(path_to_interp,'fields',var,col=18) + edit_namelist(path_to_interp,'met_em_output','.FALSE.',col=18) + edit_namelist(path_to_interp,'fields',var,col=18) + + command2 = os.path.join(('./',p_interp_path)) + os.system(command2) # This should execute the script + + return fpath + + + def edit_namelist(self,fpath,old,new,incolumn=1,col=23): + """col=23 is default for wps namelists. + """ + flines = open(fpath,'r').readlines() + for idx, line in enumerate(flines): + if old in line: + # Prefix for soil intermediate data filename + if incolumn==1: + flines[idx] = flines[idx][:col] + new + " \n" + else: + flines[idx] = ' ' + old + ' = ' + new + "\n" + nameout = open(fpath,'w') + nameout.writelines(flines) + nameout.close() + break + From 711b09e70ca7448b389ecfae087ab753e0f4f069 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 13 Feb 2014 15:22:07 -0600 Subject: [PATCH 041/111] First major lazyWRF rewrite. --- lazyWRF/bin/organise.py | 18 +++ lazyWRF/bin/setup_gefs.py | 17 +++ lazyWRF/lazyWRF/__init__.py | 266 ++++++++++++++++++++++++++++++++++++ postWRF/bin/DKE.py | 4 +- postWRF/postWRF/__init__.py | 2 +- postWRF/postWRF/birdseye.py | 2 +- postWRF/postWRF/figure.py | 2 +- postWRF/postWRF/scales.py | 1 + postWRF/postWRF/wrfout.py | 10 +- utils.py | 105 ++++++++++++++ 10 files changed, 417 insertions(+), 10 deletions(-) create mode 100644 lazyWRF/bin/organise.py create mode 100644 lazyWRF/bin/setup_gefs.py create mode 100644 utils.py diff --git a/lazyWRF/bin/organise.py b/lazyWRF/bin/organise.py new file mode 100644 index 0000000..7551464 --- /dev/null +++ b/lazyWRF/bin/organise.py @@ -0,0 +1,18 @@ +class Organise: + def __init__(self): + self.path_to_WPS = '/ptmp/jrlawson/WPS' + self.path_to_WRF = '/ptmp/jrlawson/WRFV3/run' + self.path_to_GEFSR2 = os.join((path_to_WPS,'gefsfiles')) + self.path_to_soil = os.join((path_to_WPS,'gfsfiles/',casestr,'gfsanl*') + self.GEFSR2_Vtable = 'Vtable.GEFSR2' + self.soil_Vtable = 'Vtable.GFS_soilonly' + self.roughguesshr = 1 # Minimum time one WRF run would take + self.path_to_storage = '/chinook2/jrlawson/bowecho/' + + + # selected namelist.wps settings + self.maxdom = 1 + self.dx = + + # selected namelist.input settings + self.time_step = diff --git a/lazyWRF/bin/setup_gefs.py b/lazyWRF/bin/setup_gefs.py new file mode 100644 index 0000000..25506ab --- /dev/null +++ b/lazyWRF/bin/setup_gefs.py @@ -0,0 +1,17 @@ +import sys +sys.path.append('/home/jrlawson/gitprojects/WEM/') + +from WEM.lazyWRF import Lazy +from organise import Organize + +config = Organise() +L = Lazy(organise) + +case = '20110419' + +# Create ensemble +IC = 'GEFSR2' +experiment = {'ICBC','CTRL'} +ensnames = ['c00'] + ['p'+"{0:02d}".format(n) for n in range(1,11)]} + +L.go(case,IC,experiment,ensnames) diff --git a/lazyWRF/lazyWRF/__init__.py b/lazyWRF/lazyWRF/__init__.py index e69de29..63100db 100644 --- a/lazyWRF/lazyWRF/__init__.py +++ b/lazyWRF/lazyWRF/__init__.py @@ -0,0 +1,266 @@ +import os +import WEM.utils as utils + +class Lazy: + def __init__(self,config): + self.C = config + + def go(self,casestr,IC,experiment,ensnames): + """ + Inputs: (all folder names) + casestr : string of case study initialisation date/time + IC : initial condition model + experiment : dictionary. Key: ensemble type (ICBC,STCH,MXMP) + -initial condition/boundary condition + -stochastic kinetic energy backscatter + -mixed model parameterisations + Value... + -model configuration (CTRL) for ICBC + -initial condition model/ens member for others + ensnames : list of ensemble member names + - e.g. c00,p01,p02 + + """ + self.casestr = casestr + self.IC = IC + self.experiment = experiment.keys() + self.control = ensemble.values() + self.ensnames = ensnames + + self.GO = {'GEFSR2':go_GEFS,'NAMANL':go_NAMANL, + 'NAMFCST':go_NAMFCST, 'GFSANL':go_GFSANL, + 'GFSFCST':go_GFSFCST'} + + + """ + self.enstype is a list of ensemble models or types. + self.enslist is a list of each model's member names. + Both are used to name runs, and folders for output etc + + Lookup table GO contains the methods to run for each + ensemble type. + """ + + GO[IC](self.ensnames) + + def go_GEFSR2(self,ensns): + + """ + Runs WPS, WRF for one set of initial conditions + and settings based on the GEFSR2 model. + + Inputs: + ensns : names of ensemble member + """ + for n,e in ensns: + # e is the next ensemble member to run + if n==0: + run_exe('geogrid.exe') + + # Soil data + copy_namelist('wps') + edit_namelist('wps',"prefix"," prefix = 'SOIL'") + link_to_soil_data() + link_to_soil_Vtable() + run_exe('ungrib.exe') + + # Atmos data + edit_namelist("prefix"," prefix = 'GEFSR2'") + link_to_IC_data('GEFSR2') + link_to_IC_Vtable('GEFSR2') + run_exe('ungrib.exe') + + # Combine both intermediate files + edit_namelist("fg_name"," fg_name = 'SOIL','GEFSR2'") + run_exe('metgrid.exe') + + submit_job() + + to_folder = os.path.join(self.casestr,'GEFSR2',e,self.experiment) + copy_files(to_folder) + os.system('rm -f rsl.error* rsl.out*') + + + def copy_files(self,tofolder): + """ + Move wrfout* files to folder. + Create folder if it doesn't exist + + Move *.TS files if they exist + Copy namelist.input to that folder. + Copy rsl.error.0000 to the folder. + + Input(s): + args = names of folder tree, in order of depth. + """ + root = self.C.path_to_storage + topath = os.path.join(root,tofolder) + + utils.trycreate(topath) + + files = {'wrfout_d0*':'mv','namelist.input':'cp', + 'rsl.error.0000':'cp'} + + if len(glob.glob('*.TS')): + # hi-res time series files + files['*.TS'] = 'mv' + files['tslist'] = 'cp' + + for f,transfer in files.iteritems: + fs = os.path.join(self.C.path_to_WRF,f) + command = '{0} {1} {2}'.format(transfer,fs,topath) + os.system(command) + del command + + + def submit_job(self): + # Soft link data netCDFs files from WPS to WRF + link_to_met_em() + + print("Submitting real.exe.") + real_cmd = 'qsub -d {0} real_run.sh'.format(self.C.path_to_WRF) + p_real = subprocess.Popen(real_cmd,cwd=self.C.path_to_WRF,shell=True,stdout=subprocess.PIPE) + p_real.wait() + jobid = p_real.stdout.read()[:5] # Assuming first five digits = job ID. + + # Run WRF but wait until real.exe has finished without errors + print 'Now submitting wrf.exe.' + wrf_cmd = 'qsub -d {0} wrf_run.sh -W depend=afterok:{1}'.format( + self.C.path_to_WRF,jobid) + p_wrf = subprocess.Popen(wrf_cmd,cwd=pathtoWRF,shell=True) + p_wrf.wait() + + time.sleep(self.C.roughguesshr*60*60) # Wait specified hours + + # Makes sure real.exe has finished and wrf.exe is writing to rsl files + finished = 0 + while not finished: + path_to_rsl = os.path.join(self.C.path_to_WRF,'rsl.error.0000') + tail_cmd = 'tail {0}'.format(path_to_rsl) + tailrsl = subprocess.Popen(tail_cmd,shell=True,stdout=subprocess.PIPE) + tailoutput = tailrsl.stdout.read() + if "SUCCESS COMPLETE WRF" in tailoutput: + finished = 1 + print "WRF has finished; moving to next case." + else: + time.sleep(5*60) # Try again in 5 min + + def link_to_met_em(self): + path_to_met_em = os.path.join(self.C.path_to_WPS,'met_em*') + command = 'ln -sf {0} {1}'.format(path_to_met_em,self.C.path_to_WRF) + os.system(command) + + def link_to_IC_data(self,IC): + if IC == 'GEFSR2': + csh = './link_grib.csh' + command = ' '.join(csh,self.C.path_to_GEFSR2) + + os.system(command) + + def link_to_IC_Vtable(self,IC): + if IC == 'GEFSR2': + path = os.path.join(self.C.path_to_WPS,'ungrib/Variable_Tables', + self.C.GEFSR2_Vtable) + command = 'ln -sf {0} Vtable'.format(path) + + os.system(command) + + def link_to_soil_data(self): + csh = './link_grib.csh' + command = ' '.join(csh,self.C.path_to_soil) + os.system(command) + + def link_to_soil_Vtable(self): + path = os.path.join(self.C.path_to_WPS,'ungrib/Variable_Tables', + self.C.soil_Vtable) + command = 'ln -sf {0} Vtable'.format(path) + os.system(command) + + def edit_namelist(self,suffix,sett,newval,maxdom=1): + """ Method edits namelist.wps or namelist.input. + + Inputs: + suffix : which namelist needs changing + sett : setting that needs changing + newval : its new value -> currently replaces whole line + maxdom : number of domains to edit + (this is relevant for multiple columns?) + + No outputs, just changes the file. + """ + if suffix == 'wps': + f = os.path.join(self.self.C.path_to_WPS,'namelist.wps') + elif suffix == 'input: + f = os.path.join(self.self.C.path_to_WRF,'namelist.input') + flines = open(f,'r').readlines() + for idx, line in enumerate(flines): + if sett in line: + # Prefix for soil intermediate data filename + flines[idx] = newval + " \n" + nameout = open(f,'w') + nameout.writelines(flines) + nameout.close() + break + + def run_exe(self,exe): + """Run WPS executables, then check to see if it failed. + If not, return True to proceed. + + Input: + exe : .exe file name. + """ + + #if not exe.endswith('.exe'): + # f,suffix = exe.split('.') + + command = os.path.join('./',self.C.path_to_WPS,exe) + os.system(command) + + # Wait until complete, then check tail file + name,suffix = exe.split('.') + log = name + '.log' + l = open(name+'.log','r').readlines() + lastline = l[-1] + if 'Successful completion' in lastline: + pass + #return True + else: + print ('Running {0} has failed. Check {1}.'.format( + exe,log) + raise SomethingIsBrokenException + + def generate_date(self,date,outstyle='wps'): + """ Creates date string for namelist files. + + Input: + """ + pass + + def copy_namelist(self,suffix): + """Appends current time to namelist to create backup. + """ + t = time.strftime("%Y%m%d_%H%M") + if suffix == 'wps': + f = os.path.join(path_to_WPS,'namelist.wps') # Original + elif suffix == 'input': + f = os.path.join(path_to_WRF,'namelist.input') # Original + + f2 = '_'.join((f,t)) # Backup file + command = ' '.join(('cp',f,f2)) + print("Backed up namelist.{0}.".format(suffix)) + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index 2fbe1ce..daee706 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -7,10 +7,10 @@ import calendar import time -sys.path.append('../') +sys.path.append('/home/jrlawson/gitprojects/WEM/') from DKE_settings import Settings -from postWRF import WRFEnviron +from WEM.postWRF import WRFEnviron # Time script scriptstart = time.time() diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index d794281..dbb8c3c 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -31,7 +31,7 @@ #import scales from defaults import Defaults from lookuptable import LookUpTable -import utils +import WEM.utils as utils class WRFEnviron: def __init__(self,config): diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index e72869d..921c8fe 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -7,7 +7,7 @@ from defaults import Defaults from figure import Figure -import utils +import WEM.utils as utils import scales class BirdsEye(Figure): diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index 58bb8e7..678c38c 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -11,7 +11,7 @@ import os # Custom imports -import utils +import WEM.utils as utils class Figure: def __init__(self,config,wrfout): diff --git a/postWRF/postWRF/scales.py b/postWRF/postWRF/scales.py index c53cf9b..e7ceb2a 100644 --- a/postWRF/postWRF/scales.py +++ b/postWRF/postWRF/scales.py @@ -21,6 +21,7 @@ import matplotlib.pyplot as plt import colourtables as ct +import WEM.utils as utils def get_cm(va,lv): diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 26129e9..3fc7ea8 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -325,10 +325,10 @@ def interp_to_p(self,config,nc_path,var,lv): fpath : path to new netCDF file with p co-ords """ # Fetch paths - p_interp_path = os.path.join(( - config.p_interp_root,'p_interp')) - namelist_path = os.path.join(( - config.p_interp_root,'namelist.pinterp')) + p_interp_path = os.path.join( + config.p_interp_root,'p_interp') + namelist_path = os.path.join( + config.p_interp_root,'namelist.pinterp') nc_root, nc_fname = os.path.split(nc_path)# Root directory of wrfout file output_root = nc_root # Directory to dump output file (same) @@ -348,7 +348,7 @@ def interp_to_p(self,config,nc_path,var,lv): edit_namelist(path_to_interp,'met_em_output','.FALSE.',col=18) edit_namelist(path_to_interp,'fields',var,col=18) - command2 = os.path.join(('./',p_interp_path)) + command2 = os.path.join('./',p_interp_path) os.system(command2) # This should execute the script return fpath diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..6b4ac46 --- /dev/null +++ b/utils.py @@ -0,0 +1,105 @@ +import numpy as N +import os +import time + +""" A collection of useful utilities. +""" + +def trycreate(loc): + try: + os.stat(loc) + except: + os.makedirs(loc) + +def padded_times(timeseq): + padded = ['{0:04d}'.format(t) for t in timeseq] + return padded + +def string_from_time(usage,t,dom=0,strlen=0,conven=0,tupleformat=1): + #if not tupleformat: + if isinstance(t,int): + # In this case, time is in datenum. Get it into tuple format. + t = time.gmtime(t) + if usage == 'title': + # Generates string for titles + str = '{3:02d}:{4:02d}Z on {2:02d}/{1:02d}/{0:04d}'.format(*t) + elif usage == 'wrfout': + # Generates string for wrfout file finding + # Needs dom + if not dom: + print("No domain specified; using domain #1.") + dom = 1 + str = ('wrfout_d0' + str(dom) + + '{0:04d}-{1:02d}-{2:02d}_{3:02d}:{4:02d}:{5:02d}'.format(*t)) + elif usage == 'output': + if not conven: + # No convention set, assume DD/MM (I'm biased) + conven = 'DM' + # Generates string for output file creation + if conven == 'DM': + str = '{2:02d}{1:02d}_{3:02d}{4:02d}'.format(*t) + elif conven == 'MD': + str = '{1:02d}{2:02d}_{3:02d}{4:02d}'.format(*t) + else: + print("Set convention for date format: DM or MD.") + elif usage == 'dir': + # Generates string for directory names + # Needs strlen which sets smallest scope of time for string + if not strlen: + print("No timescope strlen set; using hour as minimum.") + strlen = 'hour' + n = lookup_time(strlen) + str = "{0:04d}".format(t[0]) + ''.join( + ["{0:02d}".format(a) for a in t[1:n+1]]) + else: + print("Usage for string not valid.") + raise Exception + return str + +def lookup_time(str): + D = {'year':0, 'month':1, 'day':2, 'hour':3, 'minute':4, 'second':5} + return D[str] + +def level_type(lv): + """ Check to see what type of level is requested by user. + + """ + if lv.endswith('K'): + return 'isentropic' + elif lv < 1500: + return 'isobaric' + elif lv == 2000: + return 'surface' + elif lv.endswith('PVU'): + return 'PV-surface' + elif lv.endswith('km'): + return 'geometric' + +def closest(arr2D,val): + """ + Inputs: + val : required value + arr2D : 2D array of values + + Output: + + idx : index of closest value + + """ + idx = N.argmin(N.abs(arr2D - val)) + return idx + +def dstack_loop(data, obj): + """ + Tries to stack numpy array (data) into object (obj). + If obj doesn't exist, then initialise it + If obj does exist, stack data. + """ + try: + print obj + except NameError: + stack = data + else: + stack = N.dstack((obj,data)) + + return stack \ No newline at end of file From 00285df3ad930c8ed349dc833c011f1571e8be2e Mon Sep 17 00:00:00 2001 From: John Lawson Date: Fri, 14 Feb 2014 12:13:48 -0600 Subject: [PATCH 042/111] Fixed correct GEFSR2 link_grib --- lazyWRF/bin/setup_gefs.py | 11 ++++-- lazyWRF/lazyWRF/__init__.py | 17 ++++++--- lazyWRF/lazyWRF/getgefs.py | 71 +++++++++++++++++++++++++++++++++++++ lazyWRF/lazyWRF/getgfs.py | 16 +++++++++ lazyWRF/lazyWRF/getnam.py | 52 +++++++++++++++++++++++++++ 5 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 lazyWRF/lazyWRF/getgefs.py create mode 100644 lazyWRF/lazyWRF/getgfs.py create mode 100644 lazyWRF/lazyWRF/getnam.py diff --git a/lazyWRF/bin/setup_gefs.py b/lazyWRF/bin/setup_gefs.py index 25506ab..cabfa23 100644 --- a/lazyWRF/bin/setup_gefs.py +++ b/lazyWRF/bin/setup_gefs.py @@ -1,10 +1,17 @@ +""" +Might want to put L.go into __init__ or +even just put arguments inside the constructor +""" + import sys sys.path.append('/home/jrlawson/gitprojects/WEM/') from WEM.lazyWRF import Lazy -from organise import Organize +from lazysettings import LazySettings + +# This is where you mnight run getgefs, getgfs etc -config = Organise() +config = LazySettings() L = Lazy(organise) case = '20110419' diff --git a/lazyWRF/lazyWRF/__init__.py b/lazyWRF/lazyWRF/__init__.py index 63100db..cee920b 100644 --- a/lazyWRF/lazyWRF/__init__.py +++ b/lazyWRF/lazyWRF/__init__.py @@ -28,8 +28,8 @@ def go(self,casestr,IC,experiment,ensnames): self.ensnames = ensnames self.GO = {'GEFSR2':go_GEFS,'NAMANL':go_NAMANL, - 'NAMFCST':go_NAMFCST, 'GFSANL':go_GFSANL, - 'GFSFCST':go_GFSFCST'} + 'NAMFCST':go_NAMFCST, 'GFSANL':go_GFSANL, + 'GFSFCST':go_GFSFCST'} """ @@ -150,10 +150,19 @@ def link_to_met_em(self): command = 'ln -sf {0} {1}'.format(path_to_met_em,self.C.path_to_WRF) os.system(command) - def link_to_IC_data(self,IC): + def link_to_IC_data(self,IC,*args): + """ + Inputs: + *args : e.g. ensemble member + """ if IC == 'GEFSR2': + """ + Assumes files are within a folder named casestr (YYYYMMDD) + """ csh = './link_grib.csh' - command = ' '.join(csh,self.C.path_to_GEFSR2) + gribfiles = '_'.join((self.casestr,nextens,'f*') + gribpath = os.path.join(self.C.path_to_GEFSR2,gribfiles) + command = ' '.join(csh,gribpath) os.system(command) diff --git a/lazyWRF/lazyWRF/getgefs.py b/lazyWRF/lazyWRF/getgefs.py new file mode 100644 index 0000000..9d7f0c8 --- /dev/null +++ b/lazyWRF/lazyWRF/getgefs.py @@ -0,0 +1,71 @@ +# This script downloads all variables for GEFS R2 reforecasts +# John Lawson, University of Utah +import os +import calendar +import time + +# Dates in YYYYMMDD format - all GEFS runs are 00z +# Need to be strings +# This is a list of dates; one date can be entered too. + +#dates = ['20111128','20111129','20111130'] +dates = ['20110419'] + +# Download data +download = 0 +# Split data +split = 1 + +# This switch allows GEFS data after T190 to be downloaded (lower resolution) +lowres = 0 + +# This selected all 10 perturbation ensemble members. Change ens for desired member (or mean/sprd) +ens = ['p' + '%02u' %p for p in range(1,11)] + +# This switch allows downloading of control member too. +control = 1 +if control: + ens.append('c00') + +# Choose gaussian or latlon coordinates. +coord = 'latlon' + +# Root directory of FTP site +FTP = 'ftp://ftp.cdc.noaa.gov/Projects/Reforecast2/' + +# -nc does not download a renamed multiple copy of file +# --output-document=CATNAME concatenates all files together for the big grib file +# -nd makes sure hierachy isn't downloaded too + +if download: + for d in dates: + for e in ens: + url = os.path.join(FTP, d[0:4], d[0:6], d+'00', e, coord) + fname = '/*' + e + '.grib2' + CATNAME = d + '_' + e + '.grib2' + cmnd = "wget -nc -nd --output-document=" + CATNAME + ' ' + url + fname + os.system(cmnd) + print d, e, " Downloaded." + +# This section will split the data into forecast times for WRF to read +# Using WGRIB2 +# fin : grib2 input file +# fout : smaller grib2 output file with just one forecast time +# timestr : search pattern to find the forecast time + +if split: + for d in dates: + # Convert this date to python time for later conversion + pytime_anl = calendar.timegm((int(d[:4]),int(d[4:6]),int(d[6:8]),0,0,0)) + for e in ens: + fin = ''.join((d,'_',e,'.grib2')) + fprefix = '_'.join((d,e,'f')) + for t in range(0,198,6): + ts = "%03d" %t # Gets files into chron order with padded zeroes + if t==0: + timestr = '":anl:"' + else: + timestr = ''.join(('":(',str(t),' hour fcst):"')) + fout = fprefix + ts + '.grib2' + str1 = ' '.join(('wgrib2',fin,'-match',timestr,'-grib',fout)) + os.system(str1) diff --git a/lazyWRF/lazyWRF/getgfs.py b/lazyWRF/lazyWRF/getgfs.py new file mode 100644 index 0000000..056a046 --- /dev/null +++ b/lazyWRF/lazyWRF/getgfs.py @@ -0,0 +1,16 @@ +import os +import calendar + +dates = ['20110419','20110420'] +hours = ['00','06','12','18'] + +# If date is before ####, download grib1, use this: +#newold = 'old' +newold = 'new' + +for d in dates: + for h in hours: + if newold=='new': + os.system('wget "http://nomads.ncdc.noaa.gov/data/gfsanl/'+d[:6]+'/'+ d+'/gfsanl_4_'+d+'_'+h+'00_000.grb2"') + else: + os.system('wget "http://nomads.ncdc.noaa.gov/data/gfsanl/'+d[:6]+'/'+ d+'/gfsanl_3_'+d+'_'+h+'00_000.grb"') diff --git a/lazyWRF/lazyWRF/getnam.py b/lazyWRF/lazyWRF/getnam.py new file mode 100644 index 0000000..089f86b --- /dev/null +++ b/lazyWRF/lazyWRF/getnam.py @@ -0,0 +1,52 @@ +import os +import pdb + +# Specify type of file required +data = 'fcst' +#data = 'anl' + + +# Dates of initialisation YYYYMMDD, list of strings +#dates = ['201104{0:02d}'.format(n) for n in (19,20)] +dates = ['20110419'] +# Hours of initialisation HH, list of strings +hours = ['00','12'] + +# For forecasts only +# Maximum forecast time to download (inclusive) +Tmax = 48 +# Interval (hr) to fetch forecasts +Tint = 3 +# If date is before ####, download grib1, use this: +age = 'old' + +def get_anl(dates,hours,*args): + for d in dates: + for h in hours: + if age=='new': + command = ('wget "http://nomads.ncdc.noaa.gov/data/namanl/'+ + d[:6]+'/'+ d+'/namanl_4_'+d+'_'+h+'00_000.grb2"') + elif age=='old': + command = ('wget "http://nomads.ncdc.noaa.gov/data/namanl/'+ + d[:6]+'/'+ d+'/namanl_218_'+d+'_'+h+'00_000.grb"') + os.system(command) + +def get_218fcst(dates,hours,Tmax,Tint): + for d in dates: + for h in hours: + fhs = range(0,Tmax+Tint,Tint) + for fh in fhs: + fpad = "%03d" %fh + if age == 'old': + command = ('wget "http://nomads.ncdc.noaa.gov/data/nam/'+ + d[:6]+'/'+ d+'/nam_218_'+d+'_'+h+'00_'+fpad+'.grb"') + elif age == 'new': # doesn't seem to work + command = ('wget "http://nomads.ncdc.noaa.gov/data/nam/'+ + d[:6]+'/'+ d+'/nam_4_'+d+'_'+h+'00_'+fpad+'.grb2"') + + os.system(command) + +CMND = {'fcst':get_218fcst, 'anl':get_anl} + +CMND[data](dates,hours,Tmax,Tint) + From 5d7c36b679f238f2434772b543c25ed41e840d2d Mon Sep 17 00:00:00 2001 From: John Lawson Date: Fri, 14 Feb 2014 16:04:07 -0600 Subject: [PATCH 043/111] Bugfixing workable version --- __init__.py | 1 + lazyWRF/__init__.py | 0 lazyWRF/bin/lazysettings.py | 25 ++++++++ lazyWRF/bin/setup_gefs.py | 17 ++--- lazyWRF/lazyWRF/__init__.py | 120 +++++++++++++++++++++++------------- postWRF/__init__.py | 0 6 files changed, 111 insertions(+), 52 deletions(-) create mode 100644 __init__.py create mode 100644 lazyWRF/__init__.py create mode 100644 lazyWRF/bin/lazysettings.py create mode 100644 postWRF/__init__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..9920230 --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +# Intentially blank diff --git a/lazyWRF/__init__.py b/lazyWRF/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lazyWRF/bin/lazysettings.py b/lazyWRF/bin/lazysettings.py new file mode 100644 index 0000000..e0ba858 --- /dev/null +++ b/lazyWRF/bin/lazysettings.py @@ -0,0 +1,25 @@ +import os + +class LazySettings: + def __init__(self,case): + """ + Input: + case : YYYYMMDD of event for folder string + """ + + self.path_to_WPS = '/ptmp/jrlawson/WPS' + self.path_to_WRF = '/ptmp/jrlawson/WRFV3/run' + self.path_to_GEFSR2 = os.path.join(self.path_to_WPS,'gefsfiles') + self.path_to_soil = os.path.join(self.path_to_WPS,'gfsfiles',case,'gfsanl*') + self.GEFSR2_Vtable = 'Vtable.GEFSR2' + self.soil_Vtable = 'Vtable.GFS_soilonly' + self.roughguesshr = 1 # Minimum time one WRF run would take + self.path_to_storage = '/chinook2/jrlawson/bowecho/' + + + # selected namelist.wps settings + #self.maxdom = 1 + #self.dx = + + # selected namelist.input settings + #self.time_step = diff --git a/lazyWRF/bin/setup_gefs.py b/lazyWRF/bin/setup_gefs.py index cabfa23..4955756 100644 --- a/lazyWRF/bin/setup_gefs.py +++ b/lazyWRF/bin/setup_gefs.py @@ -2,23 +2,24 @@ Might want to put L.go into __init__ or even just put arguments inside the constructor """ - +import pdb import sys -sys.path.append('/home/jrlawson/gitprojects/WEM/') +sys.path.append('/home/jrlawson/gitprojects/') -from WEM.lazyWRF import Lazy +from WEM.lazyWRF.lazyWRF import Lazy from lazysettings import LazySettings # This is where you mnight run getgefs, getgfs etc +case = '20110419' -config = LazySettings() -L = Lazy(organise) +config = LazySettings(case) +L = Lazy(config) -case = '20110419' # Create ensemble IC = 'GEFSR2' -experiment = {'ICBC','CTRL'} -ensnames = ['c00'] + ['p'+"{0:02d}".format(n) for n in range(1,11)]} +experiment = {'ICBC':'CTRL'} +#ensnames = ['c00'] + ['p'+"{0:02d}".format(n) for n in range(1,11)] +ensnames = ['c00'] + ['p'+"{0:02d}".format(n) for n in range(4,11)] L.go(case,IC,experiment,ensnames) diff --git a/lazyWRF/lazyWRF/__init__.py b/lazyWRF/lazyWRF/__init__.py index cee920b..427068f 100644 --- a/lazyWRF/lazyWRF/__init__.py +++ b/lazyWRF/lazyWRF/__init__.py @@ -1,4 +1,8 @@ import os +import glob +import pdb +import time + import WEM.utils as utils class Lazy: @@ -24,13 +28,14 @@ def go(self,casestr,IC,experiment,ensnames): self.casestr = casestr self.IC = IC self.experiment = experiment.keys() - self.control = ensemble.values() + self.control = experiment.values() self.ensnames = ensnames - self.GO = {'GEFSR2':go_GEFS,'NAMANL':go_NAMANL, - 'NAMFCST':go_NAMFCST, 'GFSANL':go_GFSANL, - 'GFSFCST':go_GFSFCST'} - + # self.GO = {'GEFSR2':go_GEFSR2,'NAMANL':go_NAMANL, + # 'NAMFCST':go_NAMFCST, 'GFSANL':go_GFSANL, + # 'GFSFCST':go_GFSFCST} + + self.GO = {'GEFSR2':self.go_GEFSR2} """ self.enstype is a list of ensemble models or types. @@ -41,7 +46,7 @@ def go(self,casestr,IC,experiment,ensnames): ensemble type. """ - GO[IC](self.ensnames) + self.GO[IC](self.ensnames) def go_GEFSR2(self,ensns): @@ -52,34 +57,58 @@ def go_GEFSR2(self,ensns): Inputs: ensns : names of ensemble member """ - for n,e in ensns: + # Creating backup! + self.copy_namelist('wps') + + # Warning: deleting old data files first. + # Back these up if important + + files = ('met_em*','GEFSR2*','geo_em.d*','SOIL*','GRIB*') + for f in files: + if len(glob.glob(f)): + command = 'rm -f {0}'.format(f) + os.system(command) + + # Starting loop + for n,e in enumerate(ensns): # e is the next ensemble member to run + if n==0: - run_exe('geogrid.exe') + # First time, generate soil data from GFS analyses + # and set up geogrid. + + self.run_exe('geogrid.exe') - # Soil data - copy_namelist('wps') - edit_namelist('wps',"prefix"," prefix = 'SOIL'") - link_to_soil_data() - link_to_soil_Vtable() - run_exe('ungrib.exe') - - # Atmos data - edit_namelist("prefix"," prefix = 'GEFSR2'") - link_to_IC_data('GEFSR2') - link_to_IC_Vtable('GEFSR2') - run_exe('ungrib.exe') + # Create soil data intermediate files - # Combine both intermediate files - edit_namelist("fg_name"," fg_name = 'SOIL','GEFSR2'") - run_exe('metgrid.exe') - - submit_job() + self.edit_namelist('wps',"prefix"," prefix = 'SOIL'") + self.link_to_soil_data() + self.link_to_soil_Vtable() + self.run_exe('ungrib.exe') + else: + # Soil data already exists + # Remove old met_em files and links + os.system('rm GRIB* GEFSR2* met_em*') + + # Create atmos intermediate files + self.edit_namelist('wps',"prefix"," prefix = 'GEFSR2'") + self.link_to_IC_data('GEFSR2',e) + self.link_to_IC_Vtable('GEFSR2') + self.run_exe('ungrib.exe') + + # Combine both intermediate files + self.edit_namelist('wps',"fg_name"," fg_name = 'SOIL','GEFSR2'") + self.run_exe('metgrid.exe') - to_folder = os.path.join(self.casestr,'GEFSR2',e,self.experiment) - copy_files(to_folder) - os.system('rm -f rsl.error* rsl.out*') - + pdb.set_trace() + # This is where the magic happens etc etc + self.submit_job()[0] + + # Move files to storage before looping back + to_folder = os.path.join(self.casestr,'GEFSR2',e,self.experiment) + self.copy_files(to_folder) + os.system('rm -f rsl.error* rsl.out*') + def copy_files(self,tofolder): """ @@ -160,9 +189,10 @@ def link_to_IC_data(self,IC,*args): Assumes files are within a folder named casestr (YYYYMMDD) """ csh = './link_grib.csh' - gribfiles = '_'.join((self.casestr,nextens,'f*') + nextens = args[0] + gribfiles = '_'.join((self.casestr,nextens,'f*')) gribpath = os.path.join(self.C.path_to_GEFSR2,gribfiles) - command = ' '.join(csh,gribpath) + command = ' '.join((csh,gribpath)) os.system(command) @@ -176,7 +206,7 @@ def link_to_IC_Vtable(self,IC): def link_to_soil_data(self): csh = './link_grib.csh' - command = ' '.join(csh,self.C.path_to_soil) + command = ' '.join((csh,self.C.path_to_soil)) os.system(command) def link_to_soil_Vtable(self): @@ -198,9 +228,9 @@ def edit_namelist(self,suffix,sett,newval,maxdom=1): No outputs, just changes the file. """ if suffix == 'wps': - f = os.path.join(self.self.C.path_to_WPS,'namelist.wps') - elif suffix == 'input: - f = os.path.join(self.self.C.path_to_WRF,'namelist.input') + f = os.path.join(self.C.path_to_WPS,'namelist.wps') + elif suffix == 'input': + f = os.path.join(self.C.path_to_WRF,'namelist.input') flines = open(f,'r').readlines() for idx, line in enumerate(flines): if sett in line: @@ -227,35 +257,37 @@ def run_exe(self,exe): # Wait until complete, then check tail file name,suffix = exe.split('.') - log = name + '.log' - l = open(name+'.log','r').readlines() + log = os.path.join(self.C.path_to_WPS,name + '.log') + l = open(log,'r').readlines() lastline = l[-1] if 'Successful completion' in lastline: pass #return True else: print ('Running {0} has failed. Check {1}.'.format( - exe,log) + exe,log)) raise SomethingIsBrokenException - def generate_date(self,date,outstyle='wps'): + def generate_date(self,date,outstyle='wps'): """ Creates date string for namelist files. Input: """ pass - def copy_namelist(self,suffix): + def copy_namelist(self,suffix): """Appends current time to namelist to create backup. """ t = time.strftime("%Y%m%d_%H%M") if suffix == 'wps': - f = os.path.join(path_to_WPS,'namelist.wps') # Original + f = os.path.join(self.C.path_to_WPS,'namelist.wps') # Original elif suffix == 'input': - f = os.path.join(path_to_WRF,'namelist.input') # Original + f = os.path.join(self.C.path_to_WRF,'namelist.input') # Original f2 = '_'.join((f,t)) # Backup file - command = ' '.join(('cp',f,f2)) + # pdb.set_trace() + command = 'cp {0} {1}'.format(f,f2) + os.system(command) print("Backed up namelist.{0}.".format(suffix)) @@ -272,4 +304,4 @@ def copy_namelist(self,suffix): - \ No newline at end of file + diff --git a/postWRF/__init__.py b/postWRF/__init__.py new file mode 100644 index 0000000..e69de29 From 06dfc18ed9a04ac56f3783683e6cdb153924a915 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 19 Feb 2014 17:26:19 -0600 Subject: [PATCH 044/111] Rolled back to 2.4.0 as minimum requirement --- lazyWRF/lazyWRF/__init__.py | 42 +++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/lazyWRF/lazyWRF/__init__.py b/lazyWRF/lazyWRF/__init__.py index 427068f..7233503 100644 --- a/lazyWRF/lazyWRF/__init__.py +++ b/lazyWRF/lazyWRF/__init__.py @@ -2,6 +2,7 @@ import glob import pdb import time +import subprocess import WEM.utils as utils @@ -66,7 +67,8 @@ def go_GEFSR2(self,ensns): files = ('met_em*','GEFSR2*','geo_em.d*','SOIL*','GRIB*') for f in files: if len(glob.glob(f)): - command = 'rm -f {0}'.format(f) + #command = 'rm -f {0}'.format(f) + command = 'rm -f %s' %f # < 2.6 edit os.system(command) # Starting loop @@ -100,7 +102,6 @@ def go_GEFSR2(self,ensns): self.edit_namelist('wps',"fg_name"," fg_name = 'SOIL','GEFSR2'") self.run_exe('metgrid.exe') - pdb.set_trace() # This is where the magic happens etc etc self.submit_job()[0] @@ -137,26 +138,25 @@ def copy_files(self,tofolder): for f,transfer in files.iteritems: fs = os.path.join(self.C.path_to_WRF,f) - command = '{0} {1} {2}'.format(transfer,fs,topath) + command = '%s %s %s' %(transfer,fs,topath) os.system(command) del command def submit_job(self): # Soft link data netCDFs files from WPS to WRF - link_to_met_em() + self.link_to_met_em() print("Submitting real.exe.") - real_cmd = 'qsub -d {0} real_run.sh'.format(self.C.path_to_WRF) + real_cmd = 'qsub -d %s real_run.sh' %(self.C.path_to_WRF) p_real = subprocess.Popen(real_cmd,cwd=self.C.path_to_WRF,shell=True,stdout=subprocess.PIPE) p_real.wait() jobid = p_real.stdout.read()[:5] # Assuming first five digits = job ID. # Run WRF but wait until real.exe has finished without errors print 'Now submitting wrf.exe.' - wrf_cmd = 'qsub -d {0} wrf_run.sh -W depend=afterok:{1}'.format( - self.C.path_to_WRF,jobid) - p_wrf = subprocess.Popen(wrf_cmd,cwd=pathtoWRF,shell=True) + wrf_cmd = 'qsub -d %s wrf_run.sh -W depend=afterok:%s' %(self.C.path_to_WRF,jobid) + p_wrf = subprocess.Popen(wrf_cmd,cwd=self.C.path_to_WRF,shell=True) p_wrf.wait() time.sleep(self.C.roughguesshr*60*60) # Wait specified hours @@ -165,18 +165,19 @@ def submit_job(self): finished = 0 while not finished: path_to_rsl = os.path.join(self.C.path_to_WRF,'rsl.error.0000') - tail_cmd = 'tail {0}'.format(path_to_rsl) + tail_cmd = 'tail %s' %(path_to_rsl) tailrsl = subprocess.Popen(tail_cmd,shell=True,stdout=subprocess.PIPE) tailoutput = tailrsl.stdout.read() if "SUCCESS COMPLETE WRF" in tailoutput: finished = 1 print "WRF has finished; moving to next case." else: + # Need to check if job has died! If so, kill script, warn user time.sleep(5*60) # Try again in 5 min def link_to_met_em(self): path_to_met_em = os.path.join(self.C.path_to_WPS,'met_em*') - command = 'ln -sf {0} {1}'.format(path_to_met_em,self.C.path_to_WRF) + command = 'ln -sf %s %s' %(path_to_met_em,self.C.path_to_WRF) os.system(command) def link_to_IC_data(self,IC,*args): @@ -191,16 +192,17 @@ def link_to_IC_data(self,IC,*args): csh = './link_grib.csh' nextens = args[0] gribfiles = '_'.join((self.casestr,nextens,'f*')) - gribpath = os.path.join(self.C.path_to_GEFSR2,gribfiles) + gribpath = os.path.join(self.C.path_to_GEFSR2,self.casestr,gribfiles) command = ' '.join((csh,gribpath)) - + + #pdb.set_trace() os.system(command) def link_to_IC_Vtable(self,IC): if IC == 'GEFSR2': path = os.path.join(self.C.path_to_WPS,'ungrib/Variable_Tables', self.C.GEFSR2_Vtable) - command = 'ln -sf {0} Vtable'.format(path) + command = 'ln -sf %s Vtable' %(path) os.system(command) @@ -212,7 +214,7 @@ def link_to_soil_data(self): def link_to_soil_Vtable(self): path = os.path.join(self.C.path_to_WPS,'ungrib/Variable_Tables', self.C.soil_Vtable) - command = 'ln -sf {0} Vtable'.format(path) + command = 'ln -sf %s Vtable' %(path) os.system(command) def edit_namelist(self,suffix,sett,newval,maxdom=1): @@ -240,7 +242,7 @@ def edit_namelist(self,suffix,sett,newval,maxdom=1): nameout.writelines(flines) nameout.close() break - + def run_exe(self,exe): """Run WPS executables, then check to see if it failed. If not, return True to proceed. @@ -253,6 +255,7 @@ def run_exe(self,exe): # f,suffix = exe.split('.') command = os.path.join('./',self.C.path_to_WPS,exe) + print command os.system(command) # Wait until complete, then check tail file @@ -264,9 +267,8 @@ def run_exe(self,exe): pass #return True else: - print ('Running {0} has failed. Check {1}.'.format( - exe,log)) - raise SomethingIsBrokenException + print ('Running %s has failed. Check %s.'%(exe,log)) + raise Exception def generate_date(self,date,outstyle='wps'): """ Creates date string for namelist files. @@ -286,9 +288,9 @@ def copy_namelist(self,suffix): f2 = '_'.join((f,t)) # Backup file # pdb.set_trace() - command = 'cp {0} {1}'.format(f,f2) + command = 'cp %s %s' %(f,f2) os.system(command) - print("Backed up namelist.{0}.".format(suffix)) + print("Backed up namelist.%s." %(suffix)) From 0819bbce8102653bfc4ee39028225d83274a95c9 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 19 Feb 2014 17:28:44 -0600 Subject: [PATCH 045/111] Made lazyWRF examples compatible with 2.4.0 --- lazyWRF/bin/setup_gefs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lazyWRF/bin/setup_gefs.py b/lazyWRF/bin/setup_gefs.py index 4955756..c018464 100644 --- a/lazyWRF/bin/setup_gefs.py +++ b/lazyWRF/bin/setup_gefs.py @@ -4,7 +4,7 @@ """ import pdb import sys -sys.path.append('/home/jrlawson/gitprojects/') +sys.path.append('/chinook/jrlawson/') from WEM.lazyWRF.lazyWRF import Lazy from lazysettings import LazySettings @@ -20,6 +20,6 @@ IC = 'GEFSR2' experiment = {'ICBC':'CTRL'} #ensnames = ['c00'] + ['p'+"{0:02d}".format(n) for n in range(1,11)] -ensnames = ['c00'] + ['p'+"{0:02d}".format(n) for n in range(4,11)] +ensnames = ['p'+"%02d" %n for n in range(3,11)] L.go(case,IC,experiment,ensnames) From 647579c74ed0e0dd7fc4ec92ffa4adeb19b6ab50 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 19 Feb 2014 17:35:16 -0600 Subject: [PATCH 046/111] Updated README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 889281c..0c5d7f9 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,12 @@ all the settings for loading data, saving output, etc. Almost all settings can b left as default (by not specifying a setting), other than essentials like the path to your WRF data, the path to output figures, etc. +To run ```lazyWRF```, the top-level script must be in your WPS folder to allow WPS +executables to see the namelist.wps. So you might need to soft-link from your WPS +directory to where you keep your top-level lazyWRF/WEM controlling scripts (e.g., +```ln -sf /path/to/WEM/scripts/``` in your WPS folder). At least, +I can't find a way around this. + Contributors & Attributions =========================== From bf0cd483b79a4bf2ca1325342d21d14d10b31034 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Fri, 21 Feb 2014 13:28:20 -0600 Subject: [PATCH 047/111] Changed order of soil/GEFS data merge in metgrid.exe --- lazyWRF/lazyWRF/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lazyWRF/lazyWRF/__init__.py b/lazyWRF/lazyWRF/__init__.py index 7233503..138365d 100644 --- a/lazyWRF/lazyWRF/__init__.py +++ b/lazyWRF/lazyWRF/__init__.py @@ -99,7 +99,7 @@ def go_GEFSR2(self,ensns): self.run_exe('ungrib.exe') # Combine both intermediate files - self.edit_namelist('wps',"fg_name"," fg_name = 'SOIL','GEFSR2'") + self.edit_namelist('wps',"fg_name"," fg_name = 'GEFSR2','SOIL' ") self.run_exe('metgrid.exe') # This is where the magic happens etc etc From fa3214da30962231612f0d3a3b41f4f57f8baebe Mon Sep 17 00:00:00 2001 From: John Lawson Date: Fri, 21 Feb 2014 21:23:39 -0600 Subject: [PATCH 048/111] Fixed rogue index bug --- lazyWRF/lazyWRF/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lazyWRF/lazyWRF/__init__.py b/lazyWRF/lazyWRF/__init__.py index 138365d..ebafa2c 100644 --- a/lazyWRF/lazyWRF/__init__.py +++ b/lazyWRF/lazyWRF/__init__.py @@ -103,7 +103,8 @@ def go_GEFSR2(self,ensns): self.run_exe('metgrid.exe') # This is where the magic happens etc etc - self.submit_job()[0] + #self.submit_job()[0] <--- why was this [0] here? + self.submit_job() # Move files to storage before looping back to_folder = os.path.join(self.casestr,'GEFSR2',e,self.experiment) From a9084d052d2eb2dc34f3c0816691a4a1f352f155 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Sun, 23 Feb 2014 10:53:00 -0600 Subject: [PATCH 049/111] Fixed experiment naming bug --- lazyWRF/lazyWRF/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lazyWRF/lazyWRF/__init__.py b/lazyWRF/lazyWRF/__init__.py index ebafa2c..eed1ab3 100644 --- a/lazyWRF/lazyWRF/__init__.py +++ b/lazyWRF/lazyWRF/__init__.py @@ -28,8 +28,8 @@ def go(self,casestr,IC,experiment,ensnames): """ self.casestr = casestr self.IC = IC - self.experiment = experiment.keys() - self.control = experiment.values() + self.experiment = experiment.keys()[0] + self.control = experiment.values()[0] self.ensnames = ensnames # self.GO = {'GEFSR2':go_GEFSR2,'NAMANL':go_NAMANL, From 8f6bac04ec15218d9820eafefe7f84fb91995ab3 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Sun, 23 Feb 2014 21:25:33 -0600 Subject: [PATCH 050/111] Fixed silly iteritems bug --- lazyWRF/lazyWRF/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lazyWRF/lazyWRF/__init__.py b/lazyWRF/lazyWRF/__init__.py index eed1ab3..364b21a 100644 --- a/lazyWRF/lazyWRF/__init__.py +++ b/lazyWRF/lazyWRF/__init__.py @@ -137,7 +137,7 @@ def copy_files(self,tofolder): files['*.TS'] = 'mv' files['tslist'] = 'cp' - for f,transfer in files.iteritems: + for f,transfer in files.iteritems(): fs = os.path.join(self.C.path_to_WRF,f) command = '%s %s %s' %(transfer,fs,topath) os.system(command) From 64adbcbf003e220dcfecf70c276f231c70bbe829 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 24 Feb 2014 11:54:12 -0600 Subject: [PATCH 051/111] Deleted duplicate of utils.py --- postWRF/postWRF/utils.py | 105 --------------------------------------- 1 file changed, 105 deletions(-) delete mode 100644 postWRF/postWRF/utils.py diff --git a/postWRF/postWRF/utils.py b/postWRF/postWRF/utils.py deleted file mode 100644 index 6b4ac46..0000000 --- a/postWRF/postWRF/utils.py +++ /dev/null @@ -1,105 +0,0 @@ -import numpy as N -import os -import time - -""" A collection of useful utilities. -""" - -def trycreate(loc): - try: - os.stat(loc) - except: - os.makedirs(loc) - -def padded_times(timeseq): - padded = ['{0:04d}'.format(t) for t in timeseq] - return padded - -def string_from_time(usage,t,dom=0,strlen=0,conven=0,tupleformat=1): - #if not tupleformat: - if isinstance(t,int): - # In this case, time is in datenum. Get it into tuple format. - t = time.gmtime(t) - if usage == 'title': - # Generates string for titles - str = '{3:02d}:{4:02d}Z on {2:02d}/{1:02d}/{0:04d}'.format(*t) - elif usage == 'wrfout': - # Generates string for wrfout file finding - # Needs dom - if not dom: - print("No domain specified; using domain #1.") - dom = 1 - str = ('wrfout_d0' + str(dom) + - '{0:04d}-{1:02d}-{2:02d}_{3:02d}:{4:02d}:{5:02d}'.format(*t)) - elif usage == 'output': - if not conven: - # No convention set, assume DD/MM (I'm biased) - conven = 'DM' - # Generates string for output file creation - if conven == 'DM': - str = '{2:02d}{1:02d}_{3:02d}{4:02d}'.format(*t) - elif conven == 'MD': - str = '{1:02d}{2:02d}_{3:02d}{4:02d}'.format(*t) - else: - print("Set convention for date format: DM or MD.") - elif usage == 'dir': - # Generates string for directory names - # Needs strlen which sets smallest scope of time for string - if not strlen: - print("No timescope strlen set; using hour as minimum.") - strlen = 'hour' - n = lookup_time(strlen) - str = "{0:04d}".format(t[0]) + ''.join( - ["{0:02d}".format(a) for a in t[1:n+1]]) - else: - print("Usage for string not valid.") - raise Exception - return str - -def lookup_time(str): - D = {'year':0, 'month':1, 'day':2, 'hour':3, 'minute':4, 'second':5} - return D[str] - -def level_type(lv): - """ Check to see what type of level is requested by user. - - """ - if lv.endswith('K'): - return 'isentropic' - elif lv < 1500: - return 'isobaric' - elif lv == 2000: - return 'surface' - elif lv.endswith('PVU'): - return 'PV-surface' - elif lv.endswith('km'): - return 'geometric' - -def closest(arr2D,val): - """ - Inputs: - val : required value - arr2D : 2D array of values - - Output: - - idx : index of closest value - - """ - idx = N.argmin(N.abs(arr2D - val)) - return idx - -def dstack_loop(data, obj): - """ - Tries to stack numpy array (data) into object (obj). - If obj doesn't exist, then initialise it - If obj does exist, stack data. - """ - try: - print obj - except NameError: - stack = data - else: - stack = N.dstack((obj,data)) - - return stack \ No newline at end of file From fffcb205fe6be47daf9c05335b46b27b30e24dad Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 24 Feb 2014 11:59:15 -0600 Subject: [PATCH 052/111] New method to compute CAPE/CIN --- postWRF/postWRF/wrfout.py | 109 ++++++++++++++++++++++++++++++++++++++ utils.py | 3 ++ 2 files changed, 112 insertions(+) diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 3fc7ea8..08ae6c5 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -298,6 +298,115 @@ def compute_simref_atlevel(self,level=1): pass return data + def compute_DCP(self): + """ + Derecho Composite Parameter (Evans and Doswell, 2001, WAF) + And info from SPC Mesoanalyses + """ + DCAPE = self.get('DCAPE') + MUCAPE = self.get('MUCAPE') + shear_0_6 = self.get('shear',0,6) + meanwind_0_6 = self.get('meanwind',0,6) + DCP = (DCAPE/980.0)*(MUCAPE/2000.0)*(shear_0_6/20.0)*(meanwind_0_6/16.0) + return DCP + + def compute_DCAPE(self): + + DCAPE = cc.g + + def compute_CAPE(self,z1,z2,th1,th2,thp1,thp2): + """ + CAPE method based on GEMPAK's pdsuml.f function + + Inputs: + + z1 : bottom of layer (m) + z2 : top of layer (m) + th1 : theta (environ) at z1 + th2 : theta (environ) at z2 + thp1 : theta (parcel) at z1 + thp2 : theta (parcel) at z2 + + + Outputs: + CAPE : convective available potential energy + CIN : convective inhibition + """ + + capeT = 0.0 + cinT = 0.0 + + dt2 = thp2 - th2 + dt1 = thp1 - th1 + + dz = z2 - z1 + + if (dt1 > 0) and (dt2 > 0): + capeT = ((dt2 + dt1)*dz)/(th2+th1) + elif dt1 > 0: + ratio = dt1/(dt1-dt2) + zeq = z1 + (dz*ratio) + teq = th1 + ((th2-th1)*ratio) + capeT = (dt1*(zeq-z1))/(th1+teq) + cinT = (dt2*(z2-zeq))/(th2+teq) + elif dt2 > 0: + ratio = dt2/(dt2-dt1) + zfc = z2-(dz*ratio) + tfc = th2-((th2-th1)*ratio) + capeT = (dt2*(z2-zfc)/(tfc+th2)) + cinT = (dt1*(zfc-z1)/(tfc+th1)) + else: + cinT = ((dt2+dt1)*dz)/(th2+th1) + + if capeT > 0: + CAPE = capeT * cc.g + else: + CAPE = 0 + + if cinT < 0: + CIN = cinT * cc.g + else: + CIN = 0 + + return CAPE,CIN + + def compute_ave(self,va,z1,z2): + """ + Compute average values for variable in layer + + Inputs: + va : variable + z1 : height at bottom + z2 : height at top + + Output: + data : the averaged variable + """ + + # Get coordinate system + vc = self.check_vcs(z1,z2) + + + + def check_vcs(self,z1,z2,exception=1): + """ + Check the vertical coordinate systems + + If identical, return the system + If not, raise an exception. + """ + + vc = utils.level_type(z1) + vc = utils.level_type(z2) + + if vc1 != vc2: + print("Vertical coordinate systems not identical.") + return False + if exception: + raise Exception + else: + return vc1 + def get_XY(self,lat,lon): """Return grid indices for lat/lon pair. """ diff --git a/utils.py b/utils.py index 6b4ac46..2eee210 100644 --- a/utils.py +++ b/utils.py @@ -74,6 +74,9 @@ def level_type(lv): return 'PV-surface' elif lv.endswith('km'): return 'geometric' + else: + print('Unknown vertical coordinate.') + raise Exception def closest(arr2D,val): """ From 0f8cdeed67ae0f34f923dd09d346351e180d8ffd Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 24 Feb 2014 14:17:36 -0600 Subject: [PATCH 053/111] New utils folder --- postWRF/postWRF/wrfout.py | 4 +- utils/utils.py | 108 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 utils/utils.py diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 08ae6c5..8842fe7 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -206,6 +206,7 @@ def compute(self,var,slices,**kwargs): tbl['cref'] = self.compute_comp_ref tbl['wind10'] = self.compute_wind10 tbl['wind'] = self.compute_wind + tbl['CAPE'] = self.compute_CAPE data = tbl[var](slices,**kwargs) return data @@ -314,12 +315,13 @@ def compute_DCAPE(self): DCAPE = cc.g - def compute_CAPE(self,z1,z2,th1,th2,thp1,thp2): + def compute_CAPE(self,slices,z1,z2,th1,th2,thp1,thp2): """ CAPE method based on GEMPAK's pdsuml.f function Inputs: + slices : dictionary of level/time/lat/lon z1 : bottom of layer (m) z2 : top of layer (m) th1 : theta (environ) at z1 diff --git a/utils/utils.py b/utils/utils.py new file mode 100644 index 0000000..2eee210 --- /dev/null +++ b/utils/utils.py @@ -0,0 +1,108 @@ +import numpy as N +import os +import time + +""" A collection of useful utilities. +""" + +def trycreate(loc): + try: + os.stat(loc) + except: + os.makedirs(loc) + +def padded_times(timeseq): + padded = ['{0:04d}'.format(t) for t in timeseq] + return padded + +def string_from_time(usage,t,dom=0,strlen=0,conven=0,tupleformat=1): + #if not tupleformat: + if isinstance(t,int): + # In this case, time is in datenum. Get it into tuple format. + t = time.gmtime(t) + if usage == 'title': + # Generates string for titles + str = '{3:02d}:{4:02d}Z on {2:02d}/{1:02d}/{0:04d}'.format(*t) + elif usage == 'wrfout': + # Generates string for wrfout file finding + # Needs dom + if not dom: + print("No domain specified; using domain #1.") + dom = 1 + str = ('wrfout_d0' + str(dom) + + '{0:04d}-{1:02d}-{2:02d}_{3:02d}:{4:02d}:{5:02d}'.format(*t)) + elif usage == 'output': + if not conven: + # No convention set, assume DD/MM (I'm biased) + conven = 'DM' + # Generates string for output file creation + if conven == 'DM': + str = '{2:02d}{1:02d}_{3:02d}{4:02d}'.format(*t) + elif conven == 'MD': + str = '{1:02d}{2:02d}_{3:02d}{4:02d}'.format(*t) + else: + print("Set convention for date format: DM or MD.") + elif usage == 'dir': + # Generates string for directory names + # Needs strlen which sets smallest scope of time for string + if not strlen: + print("No timescope strlen set; using hour as minimum.") + strlen = 'hour' + n = lookup_time(strlen) + str = "{0:04d}".format(t[0]) + ''.join( + ["{0:02d}".format(a) for a in t[1:n+1]]) + else: + print("Usage for string not valid.") + raise Exception + return str + +def lookup_time(str): + D = {'year':0, 'month':1, 'day':2, 'hour':3, 'minute':4, 'second':5} + return D[str] + +def level_type(lv): + """ Check to see what type of level is requested by user. + + """ + if lv.endswith('K'): + return 'isentropic' + elif lv < 1500: + return 'isobaric' + elif lv == 2000: + return 'surface' + elif lv.endswith('PVU'): + return 'PV-surface' + elif lv.endswith('km'): + return 'geometric' + else: + print('Unknown vertical coordinate.') + raise Exception + +def closest(arr2D,val): + """ + Inputs: + val : required value + arr2D : 2D array of values + + Output: + + idx : index of closest value + + """ + idx = N.argmin(N.abs(arr2D - val)) + return idx + +def dstack_loop(data, obj): + """ + Tries to stack numpy array (data) into object (obj). + If obj doesn't exist, then initialise it + If obj does exist, stack data. + """ + try: + print obj + except NameError: + stack = data + else: + stack = N.dstack((obj,data)) + + return stack \ No newline at end of file From 7ed3f770fa7d553ca920c5302655f1e657b1280e Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 24 Feb 2014 14:33:13 -0600 Subject: [PATCH 054/111] Removed cached file --- utils.py | 108 ------------------------------------------------------- 1 file changed, 108 deletions(-) delete mode 100644 utils.py diff --git a/utils.py b/utils.py deleted file mode 100644 index 2eee210..0000000 --- a/utils.py +++ /dev/null @@ -1,108 +0,0 @@ -import numpy as N -import os -import time - -""" A collection of useful utilities. -""" - -def trycreate(loc): - try: - os.stat(loc) - except: - os.makedirs(loc) - -def padded_times(timeseq): - padded = ['{0:04d}'.format(t) for t in timeseq] - return padded - -def string_from_time(usage,t,dom=0,strlen=0,conven=0,tupleformat=1): - #if not tupleformat: - if isinstance(t,int): - # In this case, time is in datenum. Get it into tuple format. - t = time.gmtime(t) - if usage == 'title': - # Generates string for titles - str = '{3:02d}:{4:02d}Z on {2:02d}/{1:02d}/{0:04d}'.format(*t) - elif usage == 'wrfout': - # Generates string for wrfout file finding - # Needs dom - if not dom: - print("No domain specified; using domain #1.") - dom = 1 - str = ('wrfout_d0' + str(dom) + - '{0:04d}-{1:02d}-{2:02d}_{3:02d}:{4:02d}:{5:02d}'.format(*t)) - elif usage == 'output': - if not conven: - # No convention set, assume DD/MM (I'm biased) - conven = 'DM' - # Generates string for output file creation - if conven == 'DM': - str = '{2:02d}{1:02d}_{3:02d}{4:02d}'.format(*t) - elif conven == 'MD': - str = '{1:02d}{2:02d}_{3:02d}{4:02d}'.format(*t) - else: - print("Set convention for date format: DM or MD.") - elif usage == 'dir': - # Generates string for directory names - # Needs strlen which sets smallest scope of time for string - if not strlen: - print("No timescope strlen set; using hour as minimum.") - strlen = 'hour' - n = lookup_time(strlen) - str = "{0:04d}".format(t[0]) + ''.join( - ["{0:02d}".format(a) for a in t[1:n+1]]) - else: - print("Usage for string not valid.") - raise Exception - return str - -def lookup_time(str): - D = {'year':0, 'month':1, 'day':2, 'hour':3, 'minute':4, 'second':5} - return D[str] - -def level_type(lv): - """ Check to see what type of level is requested by user. - - """ - if lv.endswith('K'): - return 'isentropic' - elif lv < 1500: - return 'isobaric' - elif lv == 2000: - return 'surface' - elif lv.endswith('PVU'): - return 'PV-surface' - elif lv.endswith('km'): - return 'geometric' - else: - print('Unknown vertical coordinate.') - raise Exception - -def closest(arr2D,val): - """ - Inputs: - val : required value - arr2D : 2D array of values - - Output: - - idx : index of closest value - - """ - idx = N.argmin(N.abs(arr2D - val)) - return idx - -def dstack_loop(data, obj): - """ - Tries to stack numpy array (data) into object (obj). - If obj doesn't exist, then initialise it - If obj does exist, stack data. - """ - try: - print obj - except NameError: - stack = data - else: - stack = N.dstack((obj,data)) - - return stack \ No newline at end of file From 45b907c6d3a7e742168722e37abd3a96ead9cb5f Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 24 Feb 2014 14:36:40 -0600 Subject: [PATCH 055/111] Added more utility scripts --- utils/PlotBasemap.py | 26 + utils/__init__.py | 0 utils/generalmet.py | 23 + utils/gridded_data.py | 74 +++ utils/mesowestscripts.py | 136 +++++ utils/metconstants.py | 10 + utils/shapefile.py | 1128 ++++++++++++++++++++++++++++++++++++++ utils/topodata.py | 66 +++ utils/unix_tools.py | 12 + utils/wrf_tools.py | 166 ++++++ 10 files changed, 1641 insertions(+) create mode 100644 utils/PlotBasemap.py create mode 100644 utils/__init__.py create mode 100644 utils/generalmet.py create mode 100644 utils/gridded_data.py create mode 100644 utils/mesowestscripts.py create mode 100644 utils/metconstants.py create mode 100644 utils/shapefile.py create mode 100644 utils/topodata.py create mode 100644 utils/unix_tools.py create mode 100644 utils/wrf_tools.py diff --git a/utils/PlotBasemap.py b/utils/PlotBasemap.py new file mode 100644 index 0000000..f73ca91 --- /dev/null +++ b/utils/PlotBasemap.py @@ -0,0 +1,26 @@ +import numpy as N +import matplotlib as M +M.use('Agg') +import matplotlib.pyplot as plt +from mpl_toolkits.basemap import Basemap + +class PlotBasemap(Basemap): + def __init__(self): + self.Nlim = 50 + self.Elim = -100 + self.Slim = 30 + self.Wlim = -130 + self.initial() + + def initial(): + (projection....) + + def changeView(self,domain): + if domain == 'UK': + pass + + def outlines(self): + # Draw coastlines + def saveBasemap(self,outdir,fname): + self.fig = plt.gcf() + plt.savefig(outdir+fname) diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/generalmet.py b/utils/generalmet.py new file mode 100644 index 0000000..1f0eaf5 --- /dev/null +++ b/utils/generalmet.py @@ -0,0 +1,23 @@ +import numpy as N +import pdb + +def decompose_wind(wspd,wdir): + # Split wind speed/wind direction into u,v + if (type(wspd) == N.array) & (type(wdir) == N.array): + uwind = N.array([-s * N.sin(N.radians(d)) if ((s>-1)&(d>-1)) else -9999 + for s,d in zip(wspd,wdir)]) + vwind = N.array([-s * N.cos(N.radians(d)) if ((s>-1)&(d>-1)) else -9999 + for s,d in zip(wspd,wdir)]) + else: + uwind = -wspd * N.sin(N.radians(wdir)) + vwind = -wspd * N.cos(N.radians(wdir)) + return uwind, vwind + +def combine_wind_components(u,v): + wdir = N.degrees(N.arctan2(u,v)) + 180 + wspd = N.sqrt(u**2 + v**2) + return wspd, wdir + +def convert_kt2ms(wspd): + wspd_ms = wspd*0.51444 + return wspd_ms diff --git a/utils/gridded_data.py b/utils/gridded_data.py new file mode 100644 index 0000000..7eee3b2 --- /dev/null +++ b/utils/gridded_data.py @@ -0,0 +1,74 @@ +import numpy as N +import pdb + +# This returns the x and y grid point for a lat/lon. +# lats/lons are from the model output +# ptlat/ptlon are desired + +def getXY(lats,lons,ptlat,ptlon): + # Find closest lat/lon in array + minlat = abs(lats-ptlat).min() + minlon = abs(lons-ptlon).min() + # Find where these are in the grid + wherelat = N.where(abs(lats-ptlat) == minlat) + wherelon = N.where(abs(lons-ptlon) == minlon) + y = N.where(lats==lats[wherelat])[0][0] + x = N.where(lons==lons[wherelon])[0][0] + exactlat = lats[wherelat] + exactlon = lons[wherelon] + return x,y, exactlat, exactlon + +def gettopo(): + fname = '/uufs/chpc.utah.edu/common/home/u0737349/dsws/topodata/globe30.bin' + f = open(fname,'r') + fdata = N.fromfile(f,dtype='int16') + # Transposes and reshapes to a lat-lon grid + # Changes negative values to 0 (sea level) + xnum = 43200.0 + ynum = 18000.0 + topodata = N.flipud(N.reshape(fdata,(ynum,xnum))).clip(0) + #topodata = ((N.reshape(fdata,(xnum,ynum))).clip(0)) + f.close(); del fdata + # Define size of pixels + xpixel = 360/xnum + ypixel = 150/ynum # Note only 150 degrees! + # Create lat/lon grid + lats = N.arange(-60,90,ypixel)#[::-1] + lons = N.arange(-180,180,xpixel)#[::-1] + print 'Topographic data has been loaded. Everest is but a mere pixel.' + return topodata, lats, lons + +def xs_distance(Alat, Alon, Blat, Blon): + phi1 = N.radians(90.0-Alat) + phi2 = N.radians(90.0-Blat) + theta1 = N.radians(Alon) + theta2 = N.radians(Blon) + arc = math.acos(math.sin(phi1)*math.sin(phi2)*math.cos(theta1-theta2) + + math.cos(phi1)*math.cos(phi2)) + xsdistance = rE * arc + return xsdistance + +# This dstacks arrays, unless it's the first time through, in which case it initialises the variable +def dstack_loop(data, Dict, Key): + # Try evaluating dict[key]. If it doesn't exist, then initialise it + # If it does exist, stack data + try: + Dict[Key] + except KeyError: + stack = data + #Dict[Key] = data + else: + stack = N.dstack((Dict[Key],data)) + return stack + pass + +# Create thinned pressure levels for skew T barb plotting +def thinned_barbs(pres): + levels = N.arange(20000.0,105000.0,5000.0) + plocs = [] + for l in levels: + ploc = N.where(abs(pres-l)==(abs(pres-l).min()))[0][0] + plocs.append(ploc) + thin_locs = N.array(plocs) + return thin_locs # Locations of data at thinned levels + diff --git a/utils/mesowestscripts.py b/utils/mesowestscripts.py new file mode 100644 index 0000000..bd1383f --- /dev/null +++ b/utils/mesowestscripts.py @@ -0,0 +1,136 @@ +# Functions useful for mesowest data + +import numpy as N +import calendar as cal +import pdb +from netCDF4 import Dataset + +def csvprocess(data,names,convert=0): + # Get stations + stationlist = N.unique(data['stid']) # Stations in the record + stnum = len(stationlist) # Number of them + + + # Create dictionary from data + D = {} # Initialise dictionary of data + for s in stationlist: + D[s] = {} # Initialise dictionary of station/s + print 'Loading station data for ' + s + those = N.where(data['stid']==s) # Indices of obs + obNum = len(those[0]) # Number of obs + + # Entries from MesoWest data + for n in names: + D[s][n] = data[n][those] + + # Sort times + year = [int(t[0:4]) for t in D[s]['tutc']] + month = [int(t[4:6]) for t in D[s]['tutc']] + day = [int(t[6:8]) for t in D[s]['tutc']] + hour = [int(t[9:11]) for t in D[s]['tutc']] + minute = [int(t[11:13]) for t in D[s]['tutc']] + + # Create python time + D[s]['pytime'] = N.empty(obNum) + for i in range(0,obNum-1): + D[s]['pytime'][i] = cal.timegm([year[i],month[i],day[i],hour[i],minute[i],0]) + + # Convert to S.I. units + if convert == 1: + D[s]['tmpc'] = (5/9.0) * (D[s]['tmpf']-32) + D[s]['dptc'] = (5/9.0) * (D[s]['dptf']-32) + D[s]['wsms'] = D[s]['wskn']*0.51444 + D[s]['wgms'] = D[s]['wgkn']*0.51444 + + ### DATA PROCESSING + # Find time of biggest wind increase (DSWS initiation) + + D[s]['dVdt'] = N.zeros(obNum) # This is our array of d(wind)/d(time) + for i in range(0,obNum-1): + if i == 0: + pass # First record is zero as there's no gradient yet + else: + D[s]['dVdt'][i] = ((D[s]['wsms'][i] - D[s]['wsms'][i-1])/ + (D[s]['pytime'][i] - D[s]['pytime'][i-1])) + if any(D[s]['dVdt']) == False: + # Data were absent (-9999) or rubbish (0) + D[s]['mwi'] = -9999 + D[s]['mwi_t'] = -9999 + else: + D[s]['mwi'] = D[s]['dVdt'].max() # max wind increase + loc = N.where(D[s]['dVdt']==D[s]['mwi']) + D[s]['mwi_t'] = D[s]['pytime'][loc][0] # time of max wind increase + + # Find time of maximum wind gust + if any(D[s]['wgms']) == False: + D[s]['mg'] = -9999 + D[s]['mg_t'] = -9999 + else: + D[s]['mg'] = D[s]['wgms'].max() # Maximum gust at the station + loc = N.where(D[s]['wgms']==D[s]['mg']) + D[s]['mg_t'] = D[s]['pytime'][loc][0] # time of max gust + + # Find time of maximum wind speed + if any(D[s]['wsms']) == False: + D[s]['ms'] = -9999 + D[s]['ms_t'] = -9999 + else: + D[s]['ms'] = D[s]['wsms'].max() # Maximum gust at the station + loc = N.where(D[s]['wsms']==D[s]['ms']) + D[s]['ms_t'] = D[s]['pytime'][loc][0] # time of max gust + + + # Frequency of observation in minutes + try: + D[s]['dt'] = (D[s]['pytime'][1] - D[s]['pytime'][0]) / 60 + except IndexError: + D[s]['dt'] = -9999 + + # Find lowest pressures + try: + D[s]['lowp'] = D[s]['alti'].min() + except IndexError: + D[s]['lowp'] = -9999 + D[s]['lowp_t'] = -9999 + finally: + if D[s]['lowp'] != -9999: + D[s]['lowp_t'] = N.where(D[s]['alti']==D[s]['lowp']) + else: + pass + + return D + +# WRF terrain in height above sea level +def WRFterrain(fileselection='control',dom=1): + if fileselection=='nouintah': + fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/wrfout/1/NAM/2011112906_nouintah/wrfout_d0'+str(dom)+'_2011-11-29_06:00:00' + elif fileselection=='withuintah': + fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/wrfout/1/NAM/2011112906_withuintah/wrfout_d0'+str(dom)+'_2011-11-29_06:00:00' + else: + fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/WRFV3/test/em_real/wrfout_1.3_wasatch/wrfout_d0'+str(dom)+'_2011-12-01_00:00:00' + nc = Dataset(fname,'r') + terrain = nc.variables['HGT'][0,:,:] + xlong = nc.variables['XLONG'][0] + xlat = nc.variables['XLAT'][0] + return terrain, xlong, xlat + +# WRF terrain in pressure coords...from first time stamp (won't vary THAT much for a tropospheric plot!) +# SLICE keyword, when true, gives 1-D vector through middle of square. + +def WRFterrain_P(fileselection='control',dom=1,slice=0): + if fileselection=='nouintah': + fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/wrfout/1/NAM/2011112906_nouintah/wrfout_d0'+str(dom)+'_2011-11-29_06:00:00' + elif fileselection=='withuintah': + fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/wrfout/1/NAM/2011112906_withuintah/wrfout_d0'+str(dom)+'_2011-11-29_06:00:00' + else: + fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/WRFV3/test/em_real/wrfout_1.3_wasatch/wrfout_d0'+str(dom)+'_2011-12-01_00:00:00' + nc = Dataset(fname,'r') + terrain = nc.variables['PSFC'][0,:,:] + xlong = nc.variables['XLONG'][0] + xlat = nc.variables['XLAT'][0] + if slice==1: + Nx = nc.getncattr('WEST-EAST_GRID_DIMENSION')-1 + Ny = nc.getncattr('SOUTH-NORTH_GRID_DIMENSION')-1 + xlong = xlong[Ny/2,:] + xlat = xlat[:,Nx/2] + return terrain, xlong, xlat diff --git a/utils/metconstants.py b/utils/metconstants.py new file mode 100644 index 0000000..ae6b121 --- /dev/null +++ b/utils/metconstants.py @@ -0,0 +1,10 @@ +Tb = 300.0 # Base temperature +Tz = 273.15 # 0 C in Kelvin +L = 2.501e6 # Latent heat of vaporisation +R = 287.04 # gas constant air +Rv = 461.5 # gas constant vapour +cp = 1005.0 +cv = 718.0 +kappa = (cp-cv)/cp +g = 9.81 +P0 = 10**5 # Reference pressure diff --git a/utils/shapefile.py b/utils/shapefile.py new file mode 100644 index 0000000..11081cd --- /dev/null +++ b/utils/shapefile.py @@ -0,0 +1,1128 @@ +""" +shapefile.py +Provides read and write support for ESRI Shapefiles. +author: jlawheadgeospatialpython.com +date: 20130622 +version: 1.1.7 +Compatible with Python versions 2.4-3.x +""" +__version__ = "1.1.7" + +from struct import pack, unpack, calcsize, error +import os +import sys +import time +import array +import tempfile +# +# Constants for shape types +NULL = 0 +POINT = 1 +POLYLINE = 3 +POLYGON = 5 +MULTIPOINT = 8 +POINTZ = 11 +POLYLINEZ = 13 +POLYGONZ = 15 +MULTIPOINTZ = 18 +POINTM = 21 +POLYLINEM = 23 +POLYGONM = 25 +MULTIPOINTM = 28 +MULTIPATCH = 31 + +PYTHON3 = sys.version_info[0] == 3 + +if PYTHON3: + xrange = range + +def b(v): + if PYTHON3: + if isinstance(v, str): + # For python 3 encode str to bytes. + return v.encode('utf-8') + elif isinstance(v, bytes): + # Already bytes. + return v + else: + # Error. + raise Exception('Unknown input type') + else: + # For python 2 assume str passed in and return str. + return v + +def u(v): + if PYTHON3: + if isinstance(v, bytes): + # For python 3 decode bytes to str. + return v.decode('utf-8') + elif isinstance(v, str): + # Already str. + return v + else: + # Error. + raise Exception('Unknown input type') + else: + # For python 2 assume str passed in and return str. + return v + +def is_string(v): + if PYTHON3: + return isinstance(v, str) + else: + return isinstance(v, basestring) + +class _Array(array.array): + """Converts python tuples to lits of the appropritate type. + Used to unpack different shapefile header parts.""" + def __repr__(self): + return str(self.tolist()) + +def signed_area(coords): + """Return the signed area enclosed by a ring using the linear time + algorithm at http://www.cgafaq.info/wiki/Polygon_Area. A value <= 0 + indicates a counter-clockwise oriented ring. + """ + xs, ys = map(list, zip(*coords)) + xs.append(xs[1]) + ys.append(ys[1]) + return sum(xs[i]*(ys[i+1]-ys[i-1]) for i in range(1, len(coords)))/2.0 + +class _Shape: + def __init__(self, shapeType=None): + """Stores the geometry of the different shape types + specified in the Shapefile spec. Shape types are + usually point, polyline, or polygons. Every shape type + except the "Null" type contains points at some level for + example verticies in a polygon. If a shape type has + multiple shapes containing points within a single + geometry record then those shapes are called parts. Parts + are designated by their starting index in geometry record's + list of shapes.""" + self.shapeType = shapeType + self.points = [] + + @property + def __geo_interface__(self): + if self.shapeType in [POINT, POINTM, POINTZ]: + return { + 'type': 'Point', + 'coordinates': tuple(self.points[0]) + } + elif self.shapeType in [MULTIPOINT, MULTIPOINTM, MULTIPOINTZ]: + return { + 'type': 'MultiPoint', + 'coordinates': tuple([tuple(p) for p in self.points]) + } + elif self.shapeType in [POLYLINE, POLYLINEM, POLYLINEZ]: + if len(self.parts) == 1: + return { + 'type': 'LineString', + 'coordinates': tuple([tuple(p) for p in self.points]) + } + else: + ps = None + coordinates = [] + for part in self.parts: + if ps == None: + ps = part + continue + else: + coordinates.append(tuple([tuple(p) for p in self.points[ps:part]])) + ps = part + else: + coordinates.append(tuple([tuple(p) for p in self.points[part:]])) + return { + 'type': 'MultiLineString', + 'coordinates': tuple(coordinates) + } + elif self.shapeType in [POLYGON, POLYGONM, POLYGONZ]: + if len(self.parts) == 1: + return { + 'type': 'Polygon', + 'coordinates': (tuple([tuple(p) for p in self.points]),) + } + else: + ps = None + coordinates = [] + for part in self.parts: + if ps == None: + ps = part + continue + else: + coordinates.append(tuple([tuple(p) for p in self.points[ps:part]])) + ps = part + else: + coordinates.append(tuple([tuple(p) for p in self.points[part:]])) + polys = [] + poly = [coordinates[0]] + for coord in coordinates[1:]: + if signed_area(coord) < 0: + polys.append(poly) + poly = [coord] + else: + poly.append(coord) + polys.append(poly) + if len(polys) == 1: + return { + 'type': 'Polygon', + 'coordinates': tuple(polys[0]) + } + elif len(polys) > 1: + return { + 'type': 'MultiPolygon', + 'coordinates': polys + } + +class _ShapeRecord: + """A shape object of any type.""" + def __init__(self, shape=None, record=None): + self.shape = shape + self.record = record + +class ShapefileException(Exception): + """An exception to handle shapefile specific problems.""" + pass + +class Reader: + """Reads the three files of a shapefile as a unit or + separately. If one of the three files (.shp, .shx, + .dbf) is missing no exception is thrown until you try + to call a method that depends on that particular file. + The .shx index file is used if available for efficiency + but is not required to read the geometry from the .shp + file. The "shapefile" argument in the constructor is the + name of the file you want to open. + + You can instantiate a Reader without specifying a shapefile + and then specify one later with the load() method. + + Only the shapefile headers are read upon loading. Content + within each file is only accessed when required and as + efficiently as possible. Shapefiles are usually not large + but they can be. + """ + def __init__(self, *args, **kwargs): + self.shp = None + self.shx = None + self.dbf = None + self.shapeName = "Not specified" + self._offsets = [] + self.shpLength = None + self.numRecords = None + self.fields = [] + self.__dbfHdrLength = 0 + # See if a shapefile name was passed as an argument + if len(args) > 0: + if is_string(args[0]): + self.load(args[0]) + return + if "shp" in kwargs.keys(): + if hasattr(kwargs["shp"], "read"): + self.shp = kwargs["shp"] + if hasattr(self.shp, "seek"): + self.shp.seek(0) + if "shx" in kwargs.keys(): + if hasattr(kwargs["shx"], "read"): + self.shx = kwargs["shx"] + if hasattr(self.shx, "seek"): + self.shx.seek(0) + if "dbf" in kwargs.keys(): + if hasattr(kwargs["dbf"], "read"): + self.dbf = kwargs["dbf"] + if hasattr(self.dbf, "seek"): + self.dbf.seek(0) + if self.shp or self.dbf: + self.load() + else: + raise ShapefileException("Shapefile Reader requires a shapefile or file-like object.") + + def load(self, shapefile=None): + """Opens a shapefile from a filename or file-like + object. Normally this method would be called by the + constructor with the file object or file name as an + argument.""" + if shapefile: + (shapeName, ext) = os.path.splitext(shapefile) + self.shapeName = shapeName + try: + self.shp = open("%s.shp" % shapeName, "rb") + except IOError: + raise ShapefileException("Unable to open %s.shp" % shapeName) + try: + self.shx = open("%s.shx" % shapeName, "rb") + except IOError: + raise ShapefileException("Unable to open %s.shx" % shapeName) + try: + self.dbf = open("%s.dbf" % shapeName, "rb") + except IOError: + raise ShapefileException("Unable to open %s.dbf" % shapeName) + if self.shp: + self.__shpHeader() + if self.dbf: + self.__dbfHeader() + + def __getFileObj(self, f): + """Checks to see if the requested shapefile file object is + available. If not a ShapefileException is raised.""" + if not f: + raise ShapefileException("Shapefile Reader requires a shapefile or file-like object.") + if self.shp and self.shpLength is None: + self.load() + if self.dbf and len(self.fields) == 0: + self.load() + return f + + def __restrictIndex(self, i): + """Provides list-like handling of a record index with a clearer + error message if the index is out of bounds.""" + if self.numRecords: + rmax = self.numRecords - 1 + if abs(i) > rmax: + raise IndexError("Shape or Record index out of range.") + if i < 0: i = range(self.numRecords)[i] + return i + + def __shpHeader(self): + """Reads the header information from a .shp or .shx file.""" + if not self.shp: + raise ShapefileException("Shapefile Reader requires a shapefile or file-like object. (no shp file found") + shp = self.shp + # File length (16-bit word * 2 = bytes) + shp.seek(24) + self.shpLength = unpack(">i", shp.read(4))[0] * 2 + # Shape type + shp.seek(32) + self.shapeType= unpack("2i", f.read(8)) + # Determine the start of the next record + next = f.tell() + (2 * recLength) + shapeType = unpack(" -10e38: + record.m.append(m) + else: + record.m.append(None) + # Read a single point + if shapeType in (1,11,21): + record.points = [_Array('d', unpack("<2d", f.read(16)))] + # Read a single Z value + if shapeType == 11: + record.z = unpack("i", shx.read(4))[0] * 2) - 100 + numRecords = shxRecordLength // 8 + # Jump to the first record. + shx.seek(100) + for r in range(numRecords): + # Offsets are 16-bit words just like the file length + self._offsets.append(unpack(">i", shx.read(4))[0] * 2) + shx.seek(shx.tell() + 4) + if not i == None: + return self._offsets[i] + + def shape(self, i=0): + """Returns a shape object for a shape in the the geometry + record file.""" + shp = self.__getFileObj(self.shp) + i = self.__restrictIndex(i) + offset = self.__shapeIndex(i) + if not offset: + # Shx index not available so iterate the full list. + for j,k in enumerate(self.iterShapes()): + if j == i: + return k + shp.seek(offset) + return self.__shape() + + def shapes(self): + """Returns all shapes in a shapefile.""" + shp = self.__getFileObj(self.shp) + shp.seek(100) + shapes = [] + while shp.tell() < self.shpLength: + shapes.append(self.__shape()) + return shapes + + def iterShapes(self): + """Serves up shapes in a shapefile as an iterator. Useful + for handling large shapefiles.""" + shp = self.__getFileObj(self.shp) + shp.seek(100) + while shp.tell() < self.shpLength: + yield self.__shape() + + def __dbfHeaderLength(self): + """Retrieves the header length of a dbf file header.""" + if not self.__dbfHdrLength: + if not self.dbf: + raise ShapefileException("Shapefile Reader requires a shapefile or file-like object. (no dbf file found)") + dbf = self.dbf + (self.numRecords, self.__dbfHdrLength) = \ + unpack("6i", 9994,0,0,0,0,0)) + # File length (Bytes / 2 = 16-bit words) + if headerType == 'shp': + f.write(pack(">i", self.__shpFileLength())) + elif headerType == 'shx': + f.write(pack('>i', ((100 + (len(self._shapes) * 8)) // 2))) + # Version, Shape type + f.write(pack("<2i", 1000, self.shapeType)) + # The shapefile's bounding box (lower left, upper right) + if self.shapeType != 0: + try: + f.write(pack("<4d", *self.bbox())) + except error: + raise ShapefileException("Failed to write shapefile bounding box. Floats required.") + else: + f.write(pack("<4d", 0,0,0,0)) + # Elevation + z = self.zbox() + # Measure + m = self.mbox() + try: + f.write(pack("<4d", z[0], z[1], m[0], m[1])) + except error: + raise ShapefileException("Failed to write shapefile elevation and measure values. Floats required.") + + def __dbfHeader(self): + """Writes the dbf header and field descriptors.""" + f = self.__getFileObj(self.dbf) + f.seek(0) + version = 3 + year, month, day = time.localtime()[:3] + year -= 1900 + # Remove deletion flag placeholder from fields + for field in self.fields: + if field[0].startswith("Deletion"): + self.fields.remove(field) + numRecs = len(self.records) + numFields = len(self.fields) + headerLength = numFields * 32 + 33 + recordLength = sum([int(field[2]) for field in self.fields]) + 1 + header = pack('2i", recNum, 0)) + recNum += 1 + start = f.tell() + # Shape Type + f.write(pack("i", length)) + f.seek(finish) + + def __shxRecords(self): + """Writes the shx records.""" + f = self.__getFileObj(self.shx) + f.seek(100) + for i in range(len(self._shapes)): + f.write(pack(">i", self._offsets[i] // 2)) + f.write(pack(">i", self._lengths[i])) + + def __dbfRecords(self): + """Writes the dbf records.""" + f = self.__getFileObj(self.dbf) + for record in self.records: + if not self.fields[0][0].startswith("Deletion"): + f.write(b(' ')) # deletion flag + for (fieldName, fieldType, size, dec), value in zip(self.fields, record): + fieldType = fieldType.upper() + size = int(size) + if fieldType.upper() == "N": + value = str(value).rjust(size) + elif fieldType == 'L': + value = str(value)[0].upper() + else: + value = str(value)[:size].ljust(size) + assert len(value) == size + value = b(value) + f.write(value) + + def null(self): + """Creates a null shape.""" + self._shapes.append(_Shape(NULL)) + + def point(self, x, y, z=0, m=0): + """Creates a point shape.""" + pointShape = _Shape(self.shapeType) + pointShape.points.append([x, y, z, m]) + self._shapes.append(pointShape) + + def line(self, parts=[], shapeType=POLYLINE): + """Creates a line shape. This method is just a convienience method + which wraps 'poly()'. + """ + self.poly(parts, shapeType, []) + + def poly(self, parts=[], shapeType=POLYGON, partTypes=[]): + """Creates a shape that has multiple collections of points (parts) + including lines, polygons, and even multipoint shapes. If no shape type + is specified it defaults to 'polygon'. If no part types are specified + (which they normally won't be) then all parts default to the shape type. + """ + polyShape = _Shape(shapeType) + polyShape.parts = [] + polyShape.points = [] + for part in parts: + # Make sure polygon is closed + if shapeType in (5,15,25,31) and part[0] != part[-1]: + part.append(part[0]) + for point in part: + # Ensure point is list + if not isinstance(point, list): + point = list(point) + # Make sure point has z and m values + while len(point) < 4: + point.append(0) + polyShape.points.append(point) + polyShape.parts.append(len(polyShape.points)) + if polyShape.shapeType == 31: + if not partTypes: + for part in parts: + partTypes.append(polyShape.shapeType) + polyShape.partTypes = partTypes + self._shapes.append(polyShape) + + def field(self, name, fieldType="C", size="50", decimal=0): + """Adds a dbf field descriptor to the shapefile.""" + self.fields.append((name, fieldType, size, decimal)) + + def record(self, *recordList, **recordDict): + """Creates a dbf attribute record. You can submit either a sequence of + field values or keyword arguments of field names and values. Before + adding records you must add fields for the record values using the + fields() method. If the record values exceed the number of fields the + extra ones won't be added. In the case of using keyword arguments to specify + field/value pairs only fields matching the already registered fields + will be added.""" + record = [] + fieldCount = len(self.fields) + # Compensate for deletion flag + if self.fields[0][0].startswith("Deletion"): fieldCount -= 1 + if recordList: + [record.append(recordList[i]) for i in range(fieldCount)] + elif recordDict: + for field in self.fields: + if field[0] in recordDict: + val = recordDict[field[0]] + if val is None: + record.append("") + else: + record.append(val) + if record: + self.records.append(record) + + def shape(self, i): + return self._shapes[i] + + def shapes(self): + """Return the current list of shapes.""" + return self._shapes + + def saveShp(self, target): + """Save an shp file.""" + if not hasattr(target, "write"): + target = os.path.splitext(target)[0] + '.shp' + if not self.shapeType: + self.shapeType = self._shapes[0].shapeType + self.shp = self.__getFileObj(target) + self.__shapefileHeader(self.shp, headerType='shp') + self.__shpRecords() + + def saveShx(self, target): + """Save an shx file.""" + if not hasattr(target, "write"): + target = os.path.splitext(target)[0] + '.shx' + if not self.shapeType: + self.shapeType = self._shapes[0].shapeType + self.shx = self.__getFileObj(target) + self.__shapefileHeader(self.shx, headerType='shx') + self.__shxRecords() + + def saveDbf(self, target): + """Save a dbf file.""" + if not hasattr(target, "write"): + target = os.path.splitext(target)[0] + '.dbf' + self.dbf = self.__getFileObj(target) + self.__dbfHeader() + self.__dbfRecords() + + def save(self, target=None, shp=None, shx=None, dbf=None): + """Save the shapefile data to three files or + three file-like objects. SHP and DBF files can also + be written exclusively using saveShp, saveShx, and saveDbf respectively. + If target is specified but not shp,shx, or dbf then the target path and + file name are used. If no options or specified, a unique base file name + is generated to save the files and the base file name is returned as a + string. + """ + # Create a unique file name if one is not defined + if shp: + self.saveShp(shp) + if shx: + self.saveShx(shx) + if dbf: + self.saveDbf(dbf) + elif not shp and not shx and not dbf: + generated = False + if not target: + temp = tempfile.NamedTemporaryFile(prefix="shapefile_",dir=os.getcwd()) + target = temp.name + generated = True + self.saveShp(target) + self.shp.close() + self.saveShx(target) + self.shx.close() + self.saveDbf(target) + self.dbf.close() + if generated: + return target + +class Editor(Writer): + def __init__(self, shapefile=None, shapeType=POINT, autoBalance=1): + self.autoBalance = autoBalance + if not shapefile: + Writer.__init__(self, shapeType) + elif is_string(shapefile): + base = os.path.splitext(shapefile)[0] + if os.path.isfile("%s.shp" % base): + r = Reader(base) + Writer.__init__(self, r.shapeType) + self._shapes = r.shapes() + self.fields = r.fields + self.records = r.records() + + def select(self, expr): + """Select one or more shapes (to be implemented)""" + # TODO: Implement expressions to select shapes. + pass + + def delete(self, shape=None, part=None, point=None): + """Deletes the specified part of any shape by specifying a shape + number, part number, or point number.""" + # shape, part, point + if shape and part and point: + del self._shapes[shape][part][point] + # shape, part + elif shape and part and not point: + del self._shapes[shape][part] + # shape + elif shape and not part and not point: + del self._shapes[shape] + # point + elif not shape and not part and point: + for s in self._shapes: + if s.shapeType == 1: + del self._shapes[point] + else: + for part in s.parts: + del s[part][point] + # part, point + elif not shape and part and point: + for s in self._shapes: + del s[part][point] + # part + elif not shape and part and not point: + for s in self._shapes: + del s[part] + + def point(self, x=None, y=None, z=None, m=None, shape=None, part=None, point=None, addr=None): + """Creates/updates a point shape. The arguments allows + you to update a specific point by shape, part, point of any + shape type.""" + # shape, part, point + if shape and part and point: + try: self._shapes[shape] + except IndexError: self._shapes.append([]) + try: self._shapes[shape][part] + except IndexError: self._shapes[shape].append([]) + try: self._shapes[shape][part][point] + except IndexError: self._shapes[shape][part].append([]) + p = self._shapes[shape][part][point] + if x: p[0] = x + if y: p[1] = y + if z: p[2] = z + if m: p[3] = m + self._shapes[shape][part][point] = p + # shape, part + elif shape and part and not point: + try: self._shapes[shape] + except IndexError: self._shapes.append([]) + try: self._shapes[shape][part] + except IndexError: self._shapes[shape].append([]) + points = self._shapes[shape][part] + for i in range(len(points)): + p = points[i] + if x: p[0] = x + if y: p[1] = y + if z: p[2] = z + if m: p[3] = m + self._shapes[shape][part][i] = p + # shape + elif shape and not part and not point: + try: self._shapes[shape] + except IndexError: self._shapes.append([]) + + # point + # part + if addr: + shape, part, point = addr + self._shapes[shape][part][point] = [x, y, z, m] + else: + Writer.point(self, x, y, z, m) + if self.autoBalance: + self.balance() + + def validate(self): + """An optional method to try and validate the shapefile + as much as possible before writing it (not implemented).""" + #TODO: Implement validation method + pass + + def balance(self): + """Adds a corresponding empty attribute or null geometry record depending + on which type of record was created to make sure all three files + are in synch.""" + if len(self.records) > len(self._shapes): + self.null() + elif len(self.records) < len(self._shapes): + self.record() + + def __fieldNorm(self, fieldName): + """Normalizes a dbf field name to fit within the spec and the + expectations of certain ESRI software.""" + if len(fieldName) > 11: fieldName = fieldName[:11] + fieldName = fieldName.upper() + fieldName.replace(' ', '_') + +# Begin Testing +def test(): + import doctest + doctest.NORMALIZE_WHITESPACE = 1 + doctest.testfile("README.txt", verbose=1) + +if __name__ == "__main__": + """ + Doctests are contained in the file 'README.txt'. This library was originally developed + using Python 2.3. Python 2.4 and above have some excellent improvements in the built-in + testing libraries but for now unit testing is done using what's available in + 2.3. + """ + test() diff --git a/utils/topodata.py b/utils/topodata.py new file mode 100644 index 0000000..42a10c3 --- /dev/null +++ b/utils/topodata.py @@ -0,0 +1,66 @@ +import numpy as N +import matplotlib as M +M.use('Agg') +import matplotlib.pyplot as plt +import pdb +import math +import sys + +sys.path.append('/uufs/chpc.utah.edu/common/home/u0737349/lawsonpython/') +import gridded_data + +# Constants +rE = 6378100 # radius of Earth in metres + +# Settings +plt.rc('text',usetex=True) +fonts = {'family':'Computer Modern','size':16} +plt.rc('font',**fonts) +height, width = (9,17) + +outdir = '/uufs/chpc.utah.edu/common/home/u0737349/public_html/thesis/topoxs/' + +# Functions +# First, if topography data is not in memory, load it +try: + dataloaded +except NameError: + topodata, lats, lons = gridded_data.gettopo() + dataloaded = 1 + +def get_map(Nlim,Elim,Slim,Wlim): + ymax,xmax = gridded_data.getXY(lats,lons,Nlim,Elim) #Here, x is lat, y is lon + ymin,xmin = gridded_data.getXY(lats,lons,Slim,Wlim) # Not sure why! + terrain = topodata[xmin:xmax,ymin:ymax] + xlat = lats[xmin:xmax] + xlon = lons[ymin:ymax] + #pdb.set_trace() + return terrain,xlat,xlon + +def get_cross_section(Alat, Alon, Blat, Blon): + # Now find cross-sections + Ax, Ay = gridded_data.getXY(lats,lons,Alat,Alon) + Bx, By = gridded_data.getXY(lats,lons,Blat,Blon) + + # Number of points along cross-section + xspt = int(N.hypot(Bx-Ax,By-Ay)) + xx = N.linspace(Ay,By,xspt).astype(int) + yy = N.linspace(Ax,Bx,xspt).astype(int) + + # Get terrain heights along this transect + heights = topodata[xx,yy] + + # Work out distance along this cross-section in m + xsdistance = gridded_data.xs_distance(Alat,Alon,Blat,Blon) + + xvals = N.linspace(0,xsdistance,xspt) + xlabels = ['%3.1f' %(x/1000) for x in xvals] + # Now plot cross-sections + fig = plt.figure(figsize=(width,height)) + plt.plot(xvals,heights) + delta = xspt/10 + plt.xticks(xvals[::delta],xlabels[::delta]) + plt.xlabel('Distance along cross-section (km)') + fname = 'test1.png' + plt.savefig(outdir+fname,bbox_inches='tight',pad_inches=0.3) + plt.clf() diff --git a/utils/unix_tools.py b/utils/unix_tools.py new file mode 100644 index 0000000..bf769a2 --- /dev/null +++ b/utils/unix_tools.py @@ -0,0 +1,12 @@ +# Scripts to help with directory, file, etc issues + +import os + +# Create folder if it doesn't exist, along with its subdirectories +def createfolder(dir): + try: + os.stat(dir) + except: + os.makedirs(dir) + print 'Creating directory',dir + diff --git a/utils/wrf_tools.py b/utils/wrf_tools.py new file mode 100644 index 0000000..785423c --- /dev/null +++ b/utils/wrf_tools.py @@ -0,0 +1,166 @@ +# Some functions to import WRF data etc +from netCDF4 import Dataset +import pdb +from mpl_toolkits.basemap import Basemap +import matplotlib.pyplot as plt +import numpy as N +#import scipy.ndimage as nd +import os +import calendar + +# datestring needs to be format yyyymmddhh +def wrf_nc_load(dom,var,ncfolder,datestr,thin,Nlim=0,Elim=0,Slim=0,Wlim=0): + datestr2 = datestr[0:4]+'-'+datestr[4:6]+'-'+datestr[6:8]+'_'+datestr[8:10]+':00:00' + fname = ncfolder+'wrfout_'+dom+'_'+datestr2 + try: + nc = Dataset(fname,'r') + except RuntimeError: + fname += '.nc' + finally: + nc = Dataset(fname,'r') + # Load in variables here - customise or automate perhaps? + u10 = nc.variables['U10'] + v10 = nc.variables['V10'] + terrain = nc.variables['HGT'][0,:,:] + times = nc.variables['Times'] + + ### PROCESSING + # x_dim and y_dim are the x and y dimensions of the model + # domain in gridpoints + x_dim = len(nc.dimensions['west_east']) + y_dim = len(nc.dimensions['south_north']) + + # Get the grid spacing + dx = float(nc.DX) + dy = float(nc.DY) + + width_meters = dx * (x_dim - 1) + height_meters = dy * (y_dim - 1) + + cen_lat = float(nc.CEN_LAT) + cen_lon = float(nc.CEN_LON) + truelat1 = float(nc.TRUELAT1) + truelat2 = float(nc.TRUELAT2) + standlon = float(nc.STAND_LON) + + # Draw the base map behind it with the lats and + # lons calculated earlier + + if Nlim: + m = Basemap(projection='lcc',lon_0=cen_lon,lat_0=cen_lat,llcrnrlat=Slim,urcrnrlat=Nlim,llcrnrlon=Wlim,urcrnrlon=Elim,rsphere=6371200.,resolution='h',area_thresh=100) + else: + m = Basemap(resolution='i',projection='lcc', + width=width_meters,height=height_meters, + lat_0=cen_lat,lon_0=cen_lon,lat_1=truelat1, + lat_2=truelat2) + xlong = nc.variables['XLONG'][0] + xlat = nc.variables['XLAT'][0] + #xlong = xlongtot[N.where((xlongtot < Elim) & (xlongtot > Wlim))] + #xlat = xlattot[N.where((xlattot < Nlim) & (xlattot > Slim))] + #x,y = m(nc.variables['XLONG'][0],nc.variables['XLAT'][0]) + #px,py = m(xlongtot,xlattot) + #x,y = N.meshgrid(px,py) + x,y = m(xlong,xlat) + # This sets a thinned out grid point structure for plotting + # wind barbs at the interval specified in "thin" + #x_th,y_th = m(nc.variables['XLONG'][0,::thin,::thin],\ + # nc.variables['XLAT'][0,::thin,::thin]) + x_th, y_th = m(xlong[::thin, ::thin],xlat[::thin,::thin]) + data = (times,terrain,u10,v10) + return m, x, y, x_th, y_th, data, nc + +def mkloop(dom='d03',fpath='./'): + print "Creating a pretty loop..." + os.system('convert -delay 50 '+fpath+dom+'*.png > -loop '+fpath+dom+'_windloop.gif') + print "Finished!" + +def p_interpol(dom,var,ncfolder,datestr): + datestr2 = datestr[0:4]+'-'+datestr[4:6]+'-'+datestr[6:8]+'_'+datestr[8:10]+':00:00' + fname = ncfolder+'wrfout_'+dom+'_'+datestr2 + nc = Dataset(fname,'r') + var_data = nc.variables[var][:] + if var == 'T': # pert. theta + var_data += 300 # Add base state + pert_pressure = nc.variables['P'][:] #This is perturbation pressure + base_pressure = nc.variables['PB'][:] # This is base pressure + pressure = pert_pressure + base_pressure # Get into absolute pressure in Pa. + p_levels = N.arange(10000,100000,1000) + var_interp = N.zeros((pressure.shape[0],len(p_levels),pressure.shape[2],pressure.shape[3])) + #pdb.set_trace() + for (t,y,x),v in N.ndenumerate(var_interp[:,0,:,:]): + var_interp[t,:,y,x] = N.interp(p_levels,pressure[t,::-1,y,x],var_data[t,::-1,y,x]) + return var_interpi + +def hgt_from_sigma(nc): + vert_coord = (nc.variables['PH'][:] + nc.variables['PHB'][:]) / 9.81 + return vert_coord + +# This converts wrf's time to python and human time +def find_time_index(wrftime,reqtimetuple,tupleformat=1): + # wrftime = WRF Times in array + # reqtime = desired time in six-tuple + + # Convert required time to Python time if required + if tupleformat: + reqtime = calendar.timegm(reqtimetuple) + else: + reqtime = reqtimetuple + nt = wrftime.shape[0] + pytime = N.zeros([nt,1]) + t = wrftime + # Now convert WRF time to Python time + for i in range(nt): + yr = int(''.join(t[i,0:4])) + mth = int(''.join(t[i,5:7])) + day = int(''.join(t[i,8:10])) + hr = int(''.join(t[i,11:13])) + min = int(''.join(t[i,14:16])) + sec = int(''.join(t[i,17:19])) + pytime[i] = calendar.timegm([yr,mth,day,hr,min,sec]) + + # Now find closest WRF time + timeInd = N.where(abs(pytime-reqtime) == abs(pytime-reqtime).min())[0][0] + return timeInd + +# Find data for certain time, locations, level, variables +# For surface data +def TimeSfcLatLon(nc,varlist,times,latslons='all'): + # Time (DateTime in string) + if times == 'all': + timeInds = range(nc.variables['Times'].shape[0]) + elif len(times)==1: # If only one time is desired + # Time is in 6-tuple format + timeInds = find_time_index(nc.variables['Times'],times) # This function is from this module + elif len(times)==2: # Find all times between A and B + timeIndA = find_time_index(nc.variables['Times'],times[0]) + timeIndB = find_time_index(nc.variables['Times'],times[1]) + timeInds = range(timeIndA,timeIndB) + # Lat/lon of interest and their grid pointd + lats = nc.variables['XLAT'][:] + lons = nc.variables['XLONG'][:] + if latslons == 'all': + latInds = range(lats.shape[-2]) + lonInds = range(lons.shape[-1]) + else: + xmin,ymax = gridded_data.getXY(lats,lons,Nlim,Wlim) + xmax,ymin = gridded_data.getXY(lats,lons,Slim,Elim) + latInds = range(ymin,ymax) + lonInds = range(xmin,xmax) + + # Return sliced data + data = {} + for v in varlist: + data[v] = nc.variables[v][timeInds,latInds,lonInds] + # Reshape if only one time + if len(times)==1: + data[v] = N.reshape(data[v],(len(latInds),len(lonInds))) + return data + +# Get lat/lon as 1D arrays from WRF +def latlon_1D(nc): + Nx = nc.getncattr('WEST-EAST_GRID_DIMENSION')-1 + Ny = nc.getncattr('SOUTH-NORTH_GRID_DIMENSION')-1 + lats = nc.variables['XLAT'][0,:,Nx/2] + lons = nc.variables['XLONG'][0,Ny/2,:] + return lats, lons + From c1bbb1c5f9329a2c0a7f6e4e8335cd5dcbad24f0 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 24 Feb 2014 15:22:05 -0600 Subject: [PATCH 056/111] In middle of CAPE methods --- postWRF/postWRF/wrfout.py | 106 ++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 43 deletions(-) diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 8842fe7..e03761d 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -315,19 +315,14 @@ def compute_DCAPE(self): DCAPE = cc.g - def compute_CAPE(self,slices,z1,z2,th1,th2,thp1,thp2): + def compute_CAPE(self,slices): """ CAPE method based on GEMPAK's pdsuml.f function Inputs: slices : dictionary of level/time/lat/lon - z1 : bottom of layer (m) - z2 : top of layer (m) - th1 : theta (environ) at z1 - th2 : theta (environ) at z2 - thp1 : theta (parcel) at z1 - thp2 : theta (parcel) at z2 + Outputs: @@ -335,42 +330,67 @@ def compute_CAPE(self,slices,z1,z2,th1,th2,thp1,thp2): CIN : convective inhibition """ - capeT = 0.0 - cinT = 0.0 - - dt2 = thp2 - th2 - dt1 = thp1 - th1 - - dz = z2 - z1 - - if (dt1 > 0) and (dt2 > 0): - capeT = ((dt2 + dt1)*dz)/(th2+th1) - elif dt1 > 0: - ratio = dt1/(dt1-dt2) - zeq = z1 + (dz*ratio) - teq = th1 + ((th2-th1)*ratio) - capeT = (dt1*(zeq-z1))/(th1+teq) - cinT = (dt2*(z2-zeq))/(th2+teq) - elif dt2 > 0: - ratio = dt2/(dt2-dt1) - zfc = z2-(dz*ratio) - tfc = th2-((th2-th1)*ratio) - capeT = (dt2*(z2-zfc)/(tfc+th2)) - cinT = (dt1*(zfc-z1)/(tfc+th1)) - else: - cinT = ((dt2+dt1)*dz)/(th2+th1) - - if capeT > 0: - CAPE = capeT * cc.g - else: - CAPE = 0 - - if cinT < 0: - CIN = cinT * cc.g - else: - CIN = 0 - - return CAPE,CIN + totalCAPE = 0 + totalCIN = 0 + + for lvidx in range(slices['lv']): + # This should loop over the levels? + """ + z1 : bottom of layer (index) + z2 : top of layer (index) + th1 : theta (environ) at z1 + th2 : theta (environ) at z2 + thp1 : theta (parcel) at z1 + thp2 : theta (parcel) at z2 + """ + + z1 = lvidx + z2 = lvidx + 1 + + th1 = self.get('theta') + th2 = self.get('theta') + thp1 = pass + thp2 = pass + + capeT = 0.0 + cinT = 0.0 + + dt2 = thp2 - th2 + dt1 = thp1 - th1 + + dz = z2 - z1 + + if (dt1 > 0) and (dt2 > 0): + capeT = ((dt2 + dt1)*dz)/(th2+th1) + elif dt1 > 0: + ratio = dt1/(dt1-dt2) + zeq = z1 + (dz*ratio) + teq = th1 + ((th2-th1)*ratio) + capeT = (dt1*(zeq-z1))/(th1+teq) + cinT = (dt2*(z2-zeq))/(th2+teq) + elif dt2 > 0: + ratio = dt2/(dt2-dt1) + zfc = z2-(dz*ratio) + tfc = th2-((th2-th1)*ratio) + capeT = (dt2*(z2-zfc)/(tfc+th2)) + cinT = (dt1*(zfc-z1)/(tfc+th1)) + else: + cinT = ((dt2+dt1)*dz)/(th2+th1) + + if capeT > 0: + CAPE = capeT * cc.g + else: + CAPE = 0 + + if cinT < 0: + CIN = cinT * cc.g + else: + CIN = 0 + + totalCAPE += CAPE + totalCIN += CIN + + return totalCAPE,totalCIN def compute_ave(self,va,z1,z2): """ From 0e61fe141af4329d1a1a099d23cd45d58e0fd55b Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 25 Feb 2014 09:00:36 -0600 Subject: [PATCH 057/111] New example postWRF script --- postWRF/bin/.gitignore | 5 ++--- postWRF/bin/20110419_plot.py | 23 +++++++++++++++++++++++ postWRF/bin/settings.py | 26 +++++++------------------- 3 files changed, 32 insertions(+), 22 deletions(-) create mode 100644 postWRF/bin/20110419_plot.py diff --git a/postWRF/bin/.gitignore b/postWRF/bin/.gitignore index a5eca29..d5751f9 100644 --- a/postWRF/bin/.gitignore +++ b/postWRF/bin/.gitignore @@ -1,6 +1,5 @@ *.py -!casestudy.py -!casestudy_settings.py -!settings.py !DKE_settings.py !DKE.py +!20110419_plot.py +!settings.py diff --git a/postWRF/bin/20110419_plot.py b/postWRF/bin/20110419_plot.py new file mode 100644 index 0000000..9a2cc7d --- /dev/null +++ b/postWRF/bin/20110419_plot.py @@ -0,0 +1,23 @@ +import os +import pdb +import sys +sys.path.append('/home/jrlawson/gitprojects/') + +from WEM.postWRF.postWRF import WRFEnviron +from settings import Settings + +config = Settings() +p = WRFEnviron(config) + +#variables = {'wind10':2000} +#variables = {'cref':2000} +variables = {'cref':2000,'wind10':2000} +#variables = {'wind':'sfc','cref':'na','thetae':850] +#variables = {'thetae':850} + +itime = (2011,4,19,16,0,0) +ftime = (2011,4,20,11,30,0) +times = p.generate_times(itime,ftime,60*60) + +p.plot_2D(variables,times) +#p.plot_upper_variable('thetae',850,times) diff --git a/postWRF/bin/settings.py b/postWRF/bin/settings.py index c65c954..6064d5b 100644 --- a/postWRF/bin/settings.py +++ b/postWRF/bin/settings.py @@ -1,21 +1,9 @@ -"""Default settings configuration file. - -It is recommended you copy this file and edit that one, to leave this -file as a reference. - -""" - class Settings: def __init__(self): - # Required settings: - self.output_root = '/home/jrlawson/public_html' - self.wrfout_root = '/tera9/jrlawson/' - # Optional settings: - self.DPI = 250.0 - self.plot_titles = True - self.basemap = True - self.terrain = False - if self.terrain: - self.terrain_data_path = '/path/to/terrain/data' - self.scales = {'wind':(5,65,5)} - self.colorbar = True + self.output_root = '/home/jrlawson/public_html/bowecho/20110419/GEFSR2/p03/' + self.wrfout_root = '/chinook2/jrlawson/bowecho/20110419/GEFSR2/p03/' + self.p_interp_root = '/home/jrlawson/fortrancode/P_INTERP/' + self.DPI = 100.0 + self.plot_titles = 1 + self.terrain = 0 + self.colorbar = 1 From 0d30ab7afed19bf6b79a27f1237965fc0c820731 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 25 Feb 2014 09:13:22 -0600 Subject: [PATCH 058/111] Bugfixes --- postWRF/bin/20110419_plot.py | 14 +++++++++++--- postWRF/bin/settings.py | 4 ++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/postWRF/bin/20110419_plot.py b/postWRF/bin/20110419_plot.py index 9a2cc7d..86e1d28 100644 --- a/postWRF/bin/20110419_plot.py +++ b/postWRF/bin/20110419_plot.py @@ -9,9 +9,14 @@ config = Settings() p = WRFEnviron(config) +case = '20110419' +IC = 'GEFSR2' +ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] +experiment = {'ICBC':'CTRL'} + #variables = {'wind10':2000} #variables = {'cref':2000} -variables = {'cref':2000,'wind10':2000} +variables = {'cref':2000,'wind10':2000,'CAPE':2000} #variables = {'wind':'sfc','cref':'na','thetae':850] #variables = {'thetae':850} @@ -19,5 +24,8 @@ ftime = (2011,4,20,11,30,0) times = p.generate_times(itime,ftime,60*60) -p.plot_2D(variables,times) -#p.plot_upper_variable('thetae',850,times) +for en in ensnames: + addpath = os.path.join(p.output_root,case,IC,en,experiment.keys()[0]) + p.output_root += addpath + p.wrfout_root += addpath + p.plot_2D(variables,times) diff --git a/postWRF/bin/settings.py b/postWRF/bin/settings.py index 6064d5b..90e0b93 100644 --- a/postWRF/bin/settings.py +++ b/postWRF/bin/settings.py @@ -1,7 +1,7 @@ class Settings: def __init__(self): - self.output_root = '/home/jrlawson/public_html/bowecho/20110419/GEFSR2/p03/' - self.wrfout_root = '/chinook2/jrlawson/bowecho/20110419/GEFSR2/p03/' + self.output_root = '/home/jrlawson/public_html/bowecho/' + self.wrfout_root = '/chinook2/jrlawson/bowecho/' self.p_interp_root = '/home/jrlawson/fortrancode/P_INTERP/' self.DPI = 100.0 self.plot_titles = 1 From 259e027b35a8dc9c938ad4b0541baadc0180bc9c Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 25 Feb 2014 10:35:12 -0600 Subject: [PATCH 059/111] Bugfixes --- postWRF/bin/20110419_plot.py | 9 ++-- postWRF/postWRF/__init__.py | 83 +++++++++++++++++++++++------------- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/postWRF/bin/20110419_plot.py b/postWRF/bin/20110419_plot.py index 86e1d28..cf507de 100644 --- a/postWRF/bin/20110419_plot.py +++ b/postWRF/bin/20110419_plot.py @@ -14,16 +14,13 @@ ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] experiment = {'ICBC':'CTRL'} -#variables = {'wind10':2000} -#variables = {'cref':2000} -variables = {'cref':2000,'wind10':2000,'CAPE':2000} -#variables = {'wind':'sfc','cref':'na','thetae':850] -#variables = {'thetae':850} - itime = (2011,4,19,16,0,0) ftime = (2011,4,20,11,30,0) times = p.generate_times(itime,ftime,60*60) +variables = {'cref':{},'wind10':{},'CAPE':{}} +variables['cref'] = {'lv':2000,'pt':times} + for en in ensnames: addpath = os.path.join(p.output_root,case,IC,en,experiment.keys()[0]) p.output_root += addpath diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index dbb8c3c..4a2a0ae 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -88,40 +88,63 @@ def string_from_time(self,usage,t,dom=0,strlen=0,conven=0): str = utils.string_from_time(usage=usage,t=t,dom=dom,strlen=strlen,conven=conven) return str - def plot_2D(self,vardict,times,path_to_wrfout=0): - """ + def plot_2D(self,dic): """ + Currently main plotting script: Feb 25 2014 - # Loop over different wrfout files? - if not path_to_wrfout: - # Use path in config file - wrfout = self.wrfout_files_in(self.C.wrfout_root) - - self.en = self.get_sequence(wrfout) - self.pt = self.get_sequence(times) # List of plot times + Path to wrfout file is in config file. + Path to plot output is also in config - #pdb.set_trace() - for en in self.en: - #pdb.set_trace() - self.W = WRFOut(en) # Only load netCDF file once! - for pt in self.pt: - for va,lv in vardict.iteritems(): - # Check for pressure levels - if lv == 2000: - pass # Standard WRF levels - elif isinstance(lv,int): - nc_path = self.W.path - fpath = self.W.interp_to_p(self.C,nc_path,va,lv) - # Execute p_interp here and reassign self.W to new file - self.W = WRFOut(fpath) - else: - print("Non-pressure levels not supported yet.") - raise Exception - - print("Plotting {0} at lv {1} for time {2}.".format(va,lv,pt)) - F = BirdsEye(self.C,self.W) - F.plot2D(va,pt,lv) + This script is top-most and decides if the variables is + built into WRF default output or needs computing. It unstaggers + and slices data from the wrfout file appropriately. + + + Inputs: + dic : nested dictionary with: + + KEY + === + va : variable to plot + nested KEY/VALUE PAIRS + ====================== + (MANDATORY FOR SOME VARIABLES) + lv : level to plot + pt : plot times + (OPTIONAL) + tla : top limit of latitude + bla : bottom limit of latitude + llo : left limit of longitude + rlo : right limit of longitude + ---> if these are missing, default to 'all points' + + + """ + + #self.en = self.get_sequence(wrfout) + #self.pt = self.get_sequence(times) # List of plot times + + + self.W = WRFOut(en) # Only load netCDF file once! + for va in dic: + for pt in dic['pt']: + # Check for pressure levels + if lv == 2000: + pass # Standard WRF levels + elif isinstance(lv,int): + nc_path = self.W.path + fpath = self.W.interp_to_p(self.C,nc_path,va,lv) + # Execute p_interp here and reassign self.W to new file + self.W = WRFOut(fpath) + else: + print("Non-pressure levels not supported yet.") + raise Exception + + print("Plotting {0} at lv {1} for time {2}.".format(va,lv,pt)) + F = BirdsEye(self.C,self.W) + F.plot2D(va,pt,lv) + def plot_variable2D(self,varlist,timelist): self.va = self.get_sequence(varlist) # List of variables From 290f7bfff85504c5da1b831f985c03272a6e440e Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 25 Feb 2014 11:18:55 -0600 Subject: [PATCH 060/111] Updates --- postWRF/bin/20110419_plot.py | 5 ++--- postWRF/postWRF/__init__.py | 39 ++++++++++++++++++++---------------- postWRF/postWRF/birdseye.py | 26 ++++++++++-------------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/postWRF/bin/20110419_plot.py b/postWRF/bin/20110419_plot.py index cf507de..28fef34 100644 --- a/postWRF/bin/20110419_plot.py +++ b/postWRF/bin/20110419_plot.py @@ -22,7 +22,6 @@ variables['cref'] = {'lv':2000,'pt':times} for en in ensnames: - addpath = os.path.join(p.output_root,case,IC,en,experiment.keys()[0]) - p.output_root += addpath - p.wrfout_root += addpath + p.C.output_root = os.path.join(p.output_root,case,IC,en,experiment.keys()[0]) + p.C.wrfout_root = os.path.join(p.wrfout_root,case,IC,en,experiment.keys()[0]) p.plot_2D(variables,times) diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index 4a2a0ae..419589f 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -128,23 +128,28 @@ def plot_2D(self,dic): self.W = WRFOut(en) # Only load netCDF file once! for va in dic: - for pt in dic['pt']: - # Check for pressure levels - if lv == 2000: - pass # Standard WRF levels - elif isinstance(lv,int): - nc_path = self.W.path - fpath = self.W.interp_to_p(self.C,nc_path,va,lv) - # Execute p_interp here and reassign self.W to new file - self.W = WRFOut(fpath) - else: - print("Non-pressure levels not supported yet.") - raise Exception - - print("Plotting {0} at lv {1} for time {2}.".format(va,lv,pt)) - F = BirdsEye(self.C,self.W) - F.plot2D(va,pt,lv) - + lv = va['lv'] # Some variables don't have levels (e.g., cref) + pt = va['pt'] # Mandatory + + vc = utils.level_type(lv) # vertical coordinate + + # Check for pressure levels + if vc == 'surface': + pass # Standard WRF levels + elif vc == 'isobaric': + nc_path = self.W.path + p_interp_fpath = self.W.interp_to_p(self.C,nc_path,va,lv) + # Edit p_interp namelist + #Execute p_interp here and reassign self.W to new file + self.W = WRFOut(p_interp_fpath) + else: + print("Non-pressure levels not supported yet.") + raise Exception + + print("Plotting {0} at lv {1} for time {2}.".format(va,lv,pt)) + F = BirdsEye(self.C,self.W) + F.plot2D(va,pt,lv) + def plot_variable2D(self,varlist,timelist): self.va = self.get_sequence(varlist) # List of variables diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 921c8fe..bd882fc 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -47,6 +47,17 @@ def plot_data(self,data,mplcommand,fname,pt,V=0): self.fig.clf() def plot2D(self,va,pt,lv,da=0,na=0): + """ + Inputs: + + da : dictionary of: + tla : top limit of latitude + bla : bottom limit of latitude + llo : left limit of longitude + rlo : right limit of longitude + + + """ # INITIALISE #en = self.W.path self.fig = plt.figure() @@ -57,21 +68,6 @@ def plot2D(self,va,pt,lv,da=0,na=0): # TIME time_idx = self.W.get_time_idx(pt) - # LEVEL - if lv == 2000: - lv_idx = 0 - lv_na = 'sfc' # For naming purposes - elif isinstance(lv,int): - lv_idx = 0 # Interpolation file will only have the only level - lv_na = str(lv) + 'hPa' - nc_path = self.W.path - fpath = self.W.interp_to_p(self.C,nc_path,va,lv) - # Execute p_interp here and reassign self.W to new file - self.W = WRFOut(fpath) - else: - print("Non-pressure levels not supported yet.") - raise Exception - # LAT/LON lat_sl, lon_sl = self.get_limited_domain(da) From 12fedb929cd59610d1f9b67b682cf60e647f3004 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 25 Feb 2014 12:17:02 -0600 Subject: [PATCH 061/111] Update to plotting procedure --- postWRF/bin/20110419_plot.py | 18 ++++++++++++------ postWRF/postWRF/.__init__.py.swp | Bin 36864 -> 36864 bytes postWRF/postWRF/__init__.py | 18 ++++++++++-------- postWRF/postWRF/birdseye.py | 15 +++++++++++++-- postWRF/postWRF/figure.py | 2 +- postWRF/postWRF/wrfout.py | 4 ++-- utils/utils.py | 27 ++++++++++++++++++++++----- 7 files changed, 60 insertions(+), 24 deletions(-) diff --git a/postWRF/bin/20110419_plot.py b/postWRF/bin/20110419_plot.py index 28fef34..143c319 100644 --- a/postWRF/bin/20110419_plot.py +++ b/postWRF/bin/20110419_plot.py @@ -14,14 +14,20 @@ ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] experiment = {'ICBC':'CTRL'} -itime = (2011,4,19,16,0,0) -ftime = (2011,4,20,11,30,0) +itime = (2011,4,19,18,0,0) +ftime = (2011,4,20,10,30,0) times = p.generate_times(itime,ftime,60*60) -variables = {'cref':{},'wind10':{},'CAPE':{}} +variables = {'cref':{}} +#variables = {'cref':{},'wind10':{},'CAPE':{}} variables['cref'] = {'lv':2000,'pt':times} +#variables['wind10'] = {'lv':2000,'pt':times} +#variables['CAPE'] = {'pt':times} for en in ensnames: - p.C.output_root = os.path.join(p.output_root,case,IC,en,experiment.keys()[0]) - p.C.wrfout_root = os.path.join(p.wrfout_root,case,IC,en,experiment.keys()[0]) - p.plot_2D(variables,times) + # Reload settings + p.C = Settings() + # Change paths to new location + p.C.output_root = os.path.join(config.output_root,case,IC,en,experiment.keys()[0]) + p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,experiment.keys()[0]) + p.plot_2D(variables) diff --git a/postWRF/postWRF/.__init__.py.swp b/postWRF/postWRF/.__init__.py.swp index 27460e410c212456e19baad7d2ed7fd667aaebea..9133e85aa1711adf97152862065930953ab1fe58 100644 GIT binary patch delta 5692 zcmd_ui+2>&9l-Gkfh4>G5t75h-Xus82uqTJXn52BiAFYnNlY4EVX`~BS+dz#cV`z< zkoDC{5fz-qqf$^1TeW%$EEarJ(JF{XEui&O!3T<3s0tRfMUL3-oy;Z#IQ9?dp7Yt6 z-Muq&=l<^FH+$oQJ#Kuk$Ec>Fy2>gs$x)*4N>`NnL;Y%2_fPMA_ee!q67s7{!+K!t zMp5i)_62mk#i4pbdcfgR!-i^T4$U2r$DQ?BkvM2;WZl4|kU1zlGuF%geYCv|+uJ02 z+tc1ox3~A5^LvW@xz*mjbI$Ln_Gi`JR@f)DH$PR<>>oV#_V?WjD9QHslk9E(9`Se6 z#|-R0jL&CeSsI305x2$m_fwRu*n$m+!3z)a&=)=MY+ruEES%}1DBt59Y{l(ZgoVh& zm%SC`4QxjPW}y_PsrF}BhZxi}C8jK9rvjNc#f`s(6;M%wF-S!UzM|n?z%8gm35wAV zM+wTGun8NXA{Ac~jFb2~Uc>Q=RHbTP!)L;fOF#u3zo(thJ)=1q$-a(pi93wb~G zW!yfO;nM?Zs7);O8O^2U#OXQa!OGq-HQ*Luzt5$Gg8*VB1-uSX zX^2RG0q*r`Au9%xt#j2j`@$mZ3i${Ydxjny*Q$pNAuH$BTs}9o^LealCE9~8%>DB+ zVsllaIng$WD{~6fAJ(ZE*A94m-p(2)N}qjmlVSQAr=JzulMuLVFc3*2y4;jMASJwt>Oga#(K2EjT%fxF^Z6h@B7gr@6kNF@faS(y|@igL{Nl}X{C?wHsV+X zFJ>Ye0-w`bJFo%kQG@aLkXGA+-FO0Rn1^Xd>MfWQX*SuEHMj`7XbIVTaom*}PwE*= zNfPFG=MZz3Gb=eKC&#>QR!00&=dfh+RQA7;b8~a!U*^1&BzMdwM@3TQ*p#JqEf{J( zS~W1uADvoKR8&-8uDCSkitaOBy_hQJfH@Jv>hg{mj*cPL7}9m4Ky(fydCI6-$gFIx zoSi>V4i5*Ptu5ajF1;utSJXFaAx%yMe%;GdX6WYr%8XuC8S{ahOoyDo{k|~g%@t0z zLib!~YPU~rZp;}nf@$O2BRXdi^Ta5TY5iQ-dFE0&kF2Y?zA7tb6#AnD@>Ala$i6m? zW?;SKu9!&n3dM&J;AU?>jLU0%foEJHnJLnZ{V z{=Ds`58Z=1upBLzhX!1M(U6(JaYo>qH~^U;Y{3KAfCk76p)YyNU&g@cVoLH8NKI;-`AxwI-L*(gxV=j(mUhJn`St23d}*%a$_Cm=$d(gjpQO)DA?!;k`>lqbskpTM=2WAUDdqSz~b9YRGM6ZXFHXt!US|2!%&=Y3H0OSIo$2)?6)C z$2ccqGKn28YtoU_IIPn*8f`%>UrwaCBC3T9pG)Ij;-19>_C9vl{K}O8J_@>nIeikml)X zW7)MyxqX3{ymnWMo!h-w(X4(&o#q5c1^v3}rk5BqW>$2h$$G%0Wp~!nE}jUPy&lSZ zI!wp8&@wrEe$m3t!#j6!t$N6XZ-Vvca^Y1^*spUA(*kn&OYOLLfjQZ8v3a0!Xl&fL zantOKI!t=cSZnps{{!B_&Mcl0kf-|nBB;|_!+D+OmDdURb-A&gsX{()GqZ%Rl_9}8 z?AHTcpAm6uidDjX^PhEqU-QKN=K)Rrj%OhG2-8He%|l7oB~;`5D;Q@T!1Ke8myU`2 zIdf|(=gcm1p1-bVvr2p=7Yo+)R=H&G`iyn_bvy}~W#0-Nb+{Z^ILTVR2QOj+7NZe^ z&-uJ_^YL~&Zbk!&@E_LpUAPmq7=jO2)1SdM$bSXyg$wgA z0>@e7KgA|^F&{bjj_}D4p5UK=T=`ExeD8uu$?=0_`}$aq2^oYmZiiU40^AYahY4c? zra&MQ?+`K>&URdfS-2YGFdTz%j7U9!C6JE?8ITWBPZOoJ&~XilTZSnldcT;kfZW3$T({gd6TJk7ETevkMRiZ!BQ-NOvMMI g7i5?|VptYi!}60U!;{UHSi70uG&a7XskG030q>W_C;$Ke delta 2659 zcmZA3eNYr-9KiACj^q99bi_(ZPQ<|hB?9$gN^&Gc5kY9enJf_!fen?NCKq2_RN_=JdxT9b2Dnl&xwH4aC)t{i;6 z-u8vJZizC)_=4RaFK5eqwi8RWy-wRZYWp~CfAfFmU9_LQw0*dC;o$X7+Vkn!{;lWF zM+j%_#NaRb`uP_K2km^Uw%?Z>_kBYD6L!LHL_`d+-tRumJIhG^BEK z4g%nYJFH?0j-UZwVIx+d1e4LD7orx6SuT!lqQ zM>qoEj;E|vJC33Xa`mDN^|hUP<;f@)rOxc5>^A%Rm=aRB?V4Qo&c3owMwDG=G z)FB&k_=e#!y-uPKi3r6|JRp<&h&_k`j6oZTj-VDv7zaP}I??$mZN7(Wwj9&pi*DL9 zp%|}2esy$`qXTllg$-EosyNN^e+i?8#qd~GQVp75=U3%lEp`LN)39JZ#x&g!+m7Fba@B1 zVMtqDy%!B;wbjKeg+l&enVJVUjgGm^UN94N#ne;F|hYblBf^X%R%5TaoC`J)- z5Ca3wk#HA^5rIc!YsROT27xmq+XV%cNWr^!8J(othnLUXLBgY`#2PF^4iYd8ax5LtPSzHjMlDui9%OPehNG9f7g2}RD1`-6V|egI8=d91 zqXq9H1oHnQ&1CrrHIUt9rm^tG6LNRqEShveaV$+$ddfy94W&k0A_tHzyR9-4~ na% Date: Tue, 25 Feb 2014 12:22:51 -0600 Subject: [PATCH 062/111] lazyWRF: copy namelist.wps too for reference --- lazyWRF/lazyWRF/__init__.py | 3 +++ postWRF/bin/20110419_plot.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/lazyWRF/lazyWRF/__init__.py b/lazyWRF/lazyWRF/__init__.py index 364b21a..86a9405 100644 --- a/lazyWRF/lazyWRF/__init__.py +++ b/lazyWRF/lazyWRF/__init__.py @@ -143,6 +143,9 @@ def copy_files(self,tofolder): os.system(command) del command + # Finally the namelist.wps. + path_to_namelistwps = os.path.join(self.C.path_to_WPS,'namelist.wps') + 'cp %s %s' %(path_to_namelistwps,topath) def submit_job(self): # Soft link data netCDFs files from WPS to WRF diff --git a/postWRF/bin/20110419_plot.py b/postWRF/bin/20110419_plot.py index 143c319..64ab1a6 100644 --- a/postWRF/bin/20110419_plot.py +++ b/postWRF/bin/20110419_plot.py @@ -31,3 +31,5 @@ p.C.output_root = os.path.join(config.output_root,case,IC,en,experiment.keys()[0]) p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,experiment.keys()[0]) p.plot_2D(variables) + +# Postage stamp plots From 684e3bdf1a13acd6308a8c74cf4a583e6d4b61b6 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 25 Feb 2014 12:55:14 -0600 Subject: [PATCH 063/111] Begin coding postage stamp plot --- postWRF/bin/20110419_plot.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/postWRF/bin/20110419_plot.py b/postWRF/bin/20110419_plot.py index 64ab1a6..e4c3aa8 100644 --- a/postWRF/bin/20110419_plot.py +++ b/postWRF/bin/20110419_plot.py @@ -33,3 +33,13 @@ p.plot_2D(variables) # Postage stamp plots +list_of_wrfouts = [] +for en in ensnames: + p.C = Settings() + list_of_wrfouts.append(os.path.join(config.output_root,case,IC,en,experiment.keys()[0])) + outdirectory = os.path.join(config.output_root,case,IC) + itime = (2011,4,19,21,0,0) + ftime = (2011,4,20,9,30,0) + times = p.generate_times(itime,ftime,3*60*60) + # CODE THIS UP v v v v v v v v v + #p.postage_stamps(variables,times,list_of_wrfouts,outdirectory) From 3fbf9d1caae6196a230d184a854b04809b3bbe46 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 27 Feb 2014 11:22:53 -0600 Subject: [PATCH 064/111] Updated README for use of SHARPpy. --- README.md | 9 ++++++++- postWRF/bin/20110419_plot.py | 17 +++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0c5d7f9..39c3fbd 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,14 @@ Contributors & Attributions =========================== Some files or methods contain attributions to other programmers whose -code has been refactored to fit this project. In summary, thanks to: +code has been refactored to fit this project (or is/will become a +prerequisite). In summary, thanks to: + +SHARPpy +------- + +* Patrick Marsh +* John Hart HootPy/PulsatrixWx project -------------------------- diff --git a/postWRF/bin/20110419_plot.py b/postWRF/bin/20110419_plot.py index e4c3aa8..ec6fc5b 100644 --- a/postWRF/bin/20110419_plot.py +++ b/postWRF/bin/20110419_plot.py @@ -9,19 +9,22 @@ config = Settings() p = WRFEnviron(config) -case = '20110419' +case = '20130815' IC = 'GEFSR2' -ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] +#ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] +ensnames = ['p'+"%02d" %n for n in range(0,0)] experiment = {'ICBC':'CTRL'} -itime = (2011,4,19,18,0,0) -ftime = (2011,4,20,10,30,0) +#itime = (2011,4,19,18,0,0) +itime = (2013,8,15,18,0,0) +#ftime = (2011,4,20,10,30,0) +ftime = (2013,8,16,12,0,0) times = p.generate_times(itime,ftime,60*60) -variables = {'cref':{}} +variables = {'cref':{}, 'wind10':{}} #variables = {'cref':{},'wind10':{},'CAPE':{}} variables['cref'] = {'lv':2000,'pt':times} -#variables['wind10'] = {'lv':2000,'pt':times} +variables['wind10'] = {'lv':2000,'pt':times} #variables['CAPE'] = {'pt':times} for en in ensnames: @@ -32,6 +35,7 @@ p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,experiment.keys()[0]) p.plot_2D(variables) +""" # Postage stamp plots list_of_wrfouts = [] for en in ensnames: @@ -43,3 +47,4 @@ times = p.generate_times(itime,ftime,3*60*60) # CODE THIS UP v v v v v v v v v #p.postage_stamps(variables,times,list_of_wrfouts,outdirectory) +""" From 0f8672c4130e3315d1eded98ff4ca73cade44797 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 3 Mar 2014 10:20:57 -0600 Subject: [PATCH 065/111] CAPE still broken --- README.md | 1 - postWRF/bin/20110419_plot.py | 9 ++-- postWRF/postWRF/__init__.py | 17 +++++-- postWRF/postWRF/birdseye.py | 4 +- postWRF/postWRF/constants.py | 1 + postWRF/postWRF/wrfout.py | 89 +++++++++++++++++++++++++++++++++--- utils/utils.py | 4 ++ 7 files changed, 107 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 39c3fbd..b1d2b5b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -**PLEASE GET IN TOUCH TO GAIN ACCESS TO THE REPO** === WEM === diff --git a/postWRF/bin/20110419_plot.py b/postWRF/bin/20110419_plot.py index ec6fc5b..4e9c109 100644 --- a/postWRF/bin/20110419_plot.py +++ b/postWRF/bin/20110419_plot.py @@ -12,7 +12,7 @@ case = '20130815' IC = 'GEFSR2' #ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] -ensnames = ['p'+"%02d" %n for n in range(0,0)] +ensnames = ['p'+"%02d" %n for n in range(8,9)] experiment = {'ICBC':'CTRL'} #itime = (2011,4,19,18,0,0) @@ -21,11 +21,12 @@ ftime = (2013,8,16,12,0,0) times = p.generate_times(itime,ftime,60*60) -variables = {'cref':{}, 'wind10':{}} +# variables = {'cref':{}, 'wind10':{}} #variables = {'cref':{},'wind10':{},'CAPE':{}} -variables['cref'] = {'lv':2000,'pt':times} -variables['wind10'] = {'lv':2000,'pt':times} +# variables['cref'] = {'lv':2000,'pt':times} +# variables['wind10'] = {'lv':2000,'pt':times} #variables['CAPE'] = {'pt':times} +variables = {'thetae':{'lv':2000,'pt':times}, 'CAPE':{'pt':times}} for en in ensnames: # Reload settings diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index b1aef67..407e3c9 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -128,11 +128,15 @@ def plot_2D(self,dic): wrfpath = self.wrfout_files_in(self.C.wrfout_root)[0] self.W = WRFOut(wrfpath) # Only load netCDF file once! for va in dic: - lv = dic[va]['lv'] # Some variables don't have levels (e.g., cref) pt = dic[va]['pt'] # Mandatory - + + if 'lv' in dic: # If levels are requested + lv = dic[va]['lv'] # Some variables don't have levels (e.g., cref) + else: + lv = 'all' + vc = utils.level_type(lv) # vertical coordinate - + # Check for pressure levels if vc == 'surface': pass # Standard WRF levels @@ -142,17 +146,20 @@ def plot_2D(self,dic): # Edit p_interp namelist #Execute p_interp here and reassign self.W to new file self.W = WRFOut(p_interp_fpath) + elif vc == 'eta': + pass else: print("Non-pressure levels not supported yet.") raise Exception + F = BirdsEye(self.C,self.W) for t in pt: disp_t = utils.string_from_time('title',t) print("Plotting {0} at lv {1} for time {2}.".format(va,lv,disp_t)) F.plot2D(va,t,lv) - + """ def plot_variable2D(self,varlist,timelist): self.va = self.get_sequence(varlist) # List of variables self.pt = self.get_sequence(timelist) # List of plot times @@ -165,7 +172,7 @@ def plot_variable2D(self,varlist,timelist): F = BirdsEye(self.C,W) F.plot2D(va,pt,lv=2000) - """ + def plot_variable2D(self,va,pt,en,lv,p2p,na=0,da=0): ###Plot a longitude--latitude cross-section (bird's-eye-view). Use Basemap to create geographical data diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 1785d3b..6a784d4 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -75,7 +75,9 @@ def plot2D(self,va,pt,lv,da=0,na=0): # LEVEL vc = utils.level_type(lv) if vc == 'surface': - lv_idx = 0 + lv_idx = 0 + elif lv == 'all': + lv_idx = 'all' else: print("Need to sort other levels") raise Exception diff --git a/postWRF/postWRF/constants.py b/postWRF/postWRF/constants.py index 698dd32..27435ba 100644 --- a/postWRF/postWRF/constants.py +++ b/postWRF/postWRF/constants.py @@ -1,3 +1,4 @@ kappa = 0.2854 Lv = 2500000.0 P0 = 100000.0 +g = 9.81 \ No newline at end of file diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index c4d80a6..44a742a 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -141,7 +141,13 @@ def create_slice(self,PS): if any('Time' in p for p in PS['dim_names']): sl.append(slice(PS['t'],PS['t']+1)) if any('bottom' in p for p in PS['dim_names']): - sl.append(slice(PS['lv'],PS['lv']+1)) + if not 'lv' in PS: # No level specified + sl.append(slice(None,None)) + elif isinstance(PS['lv'],int): + sl.append(slice(PS['lv'],PS['lv']+1)) + elif PS['lv']=='all': + sl.append(slice(None,None)) + if any('north' and 'west' in p for p in PS['dim_names']): sl.append(PS['la']) sl.append(PS['lo']) @@ -207,6 +213,10 @@ def compute(self,var,slices,**kwargs): tbl['wind10'] = self.compute_wind10 tbl['wind'] = self.compute_wind tbl['CAPE'] = self.compute_CAPE + tbl['Td'] = self.compute_Td + tbl['pressure'] = self.compute_pressure + tbl['temps'] = self.compute_temps + tbl['theta'] = self.compute_theta data = tbl[var](slices,**kwargs) return data @@ -217,6 +227,27 @@ def compute_wind10(self,slices): data = N.sqrt(u**2 + v**2) return data + def compute_pressure(self,slices): + PP = self.get('P',slices) + PB = self.get('PB',slices) + pressure = PP + PB + return pressure + + def compute_temps(self,slices,units='K'): + theta = self.get('theta',slices) + P = self.get('pressure',slices) + temps = theta*((P/1000.0)**(287.04/1004.0)) + if units=='K': + return temps + elif units=='C': + return temps-273.15 + + def compute_theta(self,slices): + theta = self.get('T',slices) + Tbase = 300.0 + theta = Tbase + theta + return theta + def compute_wind(self,slices): u = self.get('U',slices) v = self.get('V',slices) @@ -313,8 +344,31 @@ def compute_DCP(self): def compute_DCAPE(self): - DCAPE = cc.g + pass + + def compute_thetae(self,slices): + P = self.get('pressure',slices) + T = self.get('temps',slices,units='K') + Td = self.get('Td',slices) + p2, t2 = thermo.drylift(P,T,Td) + x = thermo.wetlift(p2,t2,100.0) + thetae = thermo.theta(100.0, x, 1000.0) + return thetae + + def compute_Td(self,slices): + """ + Using HootPy equation + """ + Q = self.get('Q',slices) + P = self.get('pressure',slices) + w = N.divide(Q, N.subtract(1,Q)) + e = N.divide(N.multiply(w,P), N.add(0.622,w)) + a = N.multiply(243.5,N.log(N.divide(e,6.112))) + b = N.subtract(17.67,N.log(N.divide(e,6.112))) + Td = N.divide(a,b) + return Td + def compute_CAPE(self,slices): """ CAPE method based on GEMPAK's pdsuml.f function @@ -329,11 +383,17 @@ def compute_CAPE(self,slices): CAPE : convective available potential energy CIN : convective inhibition """ - + # Make sure all levels are obtained + #slices['lv'] = slice(None,None) + slices.pop('lv',None) + totalCAPE = 0 totalCIN = 0 - for lvidx in range(slices['lv']): + theta = self.get('theta',slices) + + for lvidx in range(theta.shape[1]-1): + # This should loop over the levels? """ z1 : bottom of layer (index) @@ -347,8 +407,9 @@ def compute_CAPE(self,slices): z1 = lvidx z2 = lvidx + 1 - th1 = self.get('theta') - th2 = self.get('theta') + th1 = theta[0,lvidx,...] + th2 = theta[0,lvidx+1,...] + thp1 = 0 thp2 = 0 @@ -359,7 +420,21 @@ def compute_CAPE(self,slices): dt1 = thp1 - th1 dz = z2 - z1 - + + # + # dt1_pos = N.ma.masked_greater(dt1,0) + # dt1_neg = N.ma.masked_less(dt1,0) + + # dt2_pos = N.ma.masked_greater(dt2,0) + # dt2_neg = N.ma.masked_less(dt2,0) + + dt1_pos = N.select([dt1>0],[dt1]) + dt1_neg = N.select([dt1<0],[dt1]) + dt2_pos = N.select([dt2>0],[dt1]) + dt2_neg = N.select([dt2<0],[dt1]) + + pdb.set_trace() + if (dt1 > 0) and (dt2 > 0): capeT = ((dt2 + dt1)*dz)/(th2+th1) elif dt1 > 0: diff --git a/utils/utils.py b/utils/utils.py index 72545d0..2f0c54a 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -75,6 +75,8 @@ def get_level_naming(lv): return lv elif lv.endswith('km'): return lv + elif lv == 'all': + return 'all model levels' def level_type(lv): @@ -91,6 +93,8 @@ def level_type(lv): return 'PV-surface' elif lv.endswith('km'): return 'geometric' + elif lv == 'all': + return 'eta' else: print('Unknown vertical coordinate.') raise Exception From 7bc6880214933b3aa90fdd5f5474d2f677ff3c68 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 6 Mar 2014 13:03:22 -0600 Subject: [PATCH 066/111] Computing shear, slowly. --- postWRF/bin/20110419_plot.py | 23 ++-- postWRF/postWRF/__init__.py | 192 ++++++++++++++-------------- postWRF/postWRF/birdseye.py | 60 ++++++--- postWRF/postWRF/scales.py | 5 + postWRF/postWRF/wrfout.py | 240 ++++++++++++++++++++--------------- utils/utils.py | 8 +- 6 files changed, 300 insertions(+), 228 deletions(-) diff --git a/postWRF/bin/20110419_plot.py b/postWRF/bin/20110419_plot.py index 4e9c109..6456516 100644 --- a/postWRF/bin/20110419_plot.py +++ b/postWRF/bin/20110419_plot.py @@ -10,23 +10,26 @@ p = WRFEnviron(config) case = '20130815' -IC = 'GEFSR2' -#ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] -ensnames = ['p'+"%02d" %n for n in range(8,9)] +IC = 'GEFSR2' +ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] +# ensnames = ['p'+"%02d" %n for n in range(8,9)] +# ensnames = ['c00'] experiment = {'ICBC':'CTRL'} #itime = (2011,4,19,18,0,0) itime = (2013,8,15,18,0,0) #ftime = (2011,4,20,10,30,0) -ftime = (2013,8,16,12,0,0) +ftime = (2013,8,16,11,30,0) times = p.generate_times(itime,ftime,60*60) +shear_times = p.generate_times(itime,ftime,3*60*60) -# variables = {'cref':{}, 'wind10':{}} +#variables = {'cref':{}, 'wind10':{}} #variables = {'cref':{},'wind10':{},'CAPE':{}} -# variables['cref'] = {'lv':2000,'pt':times} -# variables['wind10'] = {'lv':2000,'pt':times} -#variables['CAPE'] = {'pt':times} -variables = {'thetae':{'lv':2000,'pt':times}, 'CAPE':{'pt':times}} +#variables['cref'] = {'lv':2000,'pt':times} +#variables['wind10'] = {'lv':2000,'pt':times} +variables = {'shear':{'pt':shear_times, 'top':3, 'bottom':0}} +# variables = {'thetae':{'lv':2000,'pt':times}, 'CAPE':{'pt':times}} +#variables = {'cref':{'lv':2000,'pt':times}} for en in ensnames: # Reload settings @@ -38,7 +41,7 @@ """ # Postage stamp plots -list_of_wrfouts = [] +list_of_wrfouts = [] for en in ensnames: p.C = Settings() list_of_wrfouts.append(os.path.join(config.output_root,case,IC,en,experiment.keys()[0])) diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index 407e3c9..d0b5895 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -6,7 +6,7 @@ W = Wrfout = wrfout file F = Figure = a superclass of figures mp = Birdseye = a lat--lon slice through data with basemap - xs = CrossSection = distance--height slice through data with terrain + xs = CrossSection = distance--height slice through data with terrain """ @@ -39,11 +39,11 @@ def __init__(self,config): # Set defaults if they don't appear in user's settings self.D = Defaults() - + self.font_prop = getattr(self.C,'font_prop',self.D.font_prop) self.usetex = getattr(self.C,'usetex',self.D.usetex) self.dpi = getattr(self.C,'DPI',self.D.dpi) - self.plot_titles = getattr(self.C,'plot_titles',self.D.plot_titles) + self.plot_titles = getattr(self.C,'plot_titles',self.D.plot_titles) # Set some general settings M.rc('text',usetex=self.usetex) @@ -91,92 +91,90 @@ def string_from_time(self,usage,t,dom=0,strlen=0,conven=0): def plot_2D(self,dic): """ Currently main plotting script: Feb 25 2014 - + Path to wrfout file is in config file. Path to plot output is also in config - + This script is top-most and decides if the variables is built into WRF default output or needs computing. It unstaggers and slices data from the wrfout file appropriately. - - + + Inputs: dic : nested dictionary with: - + KEY === va : variable to plot - + nested KEY/VALUE PAIRS ====================== (MANDATORY FOR SOME VARIABLES) lv : level to plot pt : plot times - (OPTIONAL) + (OPTIONAL) tla : top limit of latitude bla : bottom limit of latitude llo : left limit of longitude rlo : right limit of longitude ---> if these are missing, default to 'all points' - - + + """ - + #self.en = self.get_sequence(wrfout) - #self.pt = self.get_sequence(times) # List of plot times - + #self.pt = self.get_sequence(times) # List of plot times + wrfpath = self.wrfout_files_in(self.C.wrfout_root)[0] self.W = WRFOut(wrfpath) # Only load netCDF file once! for va in dic: - pt = dic[va]['pt'] # Mandatory + if not 'lv' in dic[va]: # For things like CAPE, shear. + dic[va]['lv'] = 'all' - if 'lv' in dic: # If levels are requested - lv = dic[va]['lv'] # Some variables don't have levels (e.g., cref) - else: - lv = 'all' + vc = utils.level_type(dic[va]['lv']) # vertical coordinate - vc = utils.level_type(lv) # vertical coordinate - # Check for pressure levels - if vc == 'surface': - pass # Standard WRF levels - elif vc == 'isobaric': + if vc == 'isobaric': nc_path = self.W.path p_interp_fpath = self.W.interp_to_p(self.C,nc_path,va,lv) # Edit p_interp namelist #Execute p_interp here and reassign self.W to new file self.W = WRFOut(p_interp_fpath) - elif vc == 'eta': + else: # + # print("Non-pressure levels not supported yet.") + # raise Exception pass - else: - print("Non-pressure levels not supported yet.") - raise Exception - - + F = BirdsEye(self.C,self.W) - for t in pt: + lv = dic[va]['lv'] + for t in dic[va]['pt']: + # print('Passing point. dic: \n', dic) + # pdb.set_trace() disp_t = utils.string_from_time('title',t) - print("Plotting {0} at lv {1} for time {2}.".format(va,lv,disp_t)) - F.plot2D(va,t,lv) + print("Plotting {0} at lv {1} for time {2}.".format( + va,lv,disp_t)) + dic[va]['pt'] = t + dic[va]['vc'] = vc + F.plot2D(va, dic[va]) - """ + """ def plot_variable2D(self,varlist,timelist): self.va = self.get_sequence(varlist) # List of variables self.pt = self.get_sequence(timelist) # List of plot times - - # Where is logic to - + + # Where is logic to + for x in itertools.product(self.va,self.pt): va, pt = x - W = WRFOut(self.C.wrfout_root) + W = WRFOut(self.C.wrfout_root) F = BirdsEye(self.C,W) F.plot2D(va,pt,lv=2000) - - + + def plot_variable2D(self,va,pt,en,lv,p2p,na=0,da=0): ###Plot a longitude--latitude cross-section (bird's-eye-view). Use Basemap to create geographical data - + ======== REQUIRED ======== @@ -184,7 +182,7 @@ def plot_variable2D(self,va,pt,en,lv,p2p,na=0,da=0): va = variable(s) pt = plot time(s) nc = ensemble member(s) - lv = level(s) + lv = level(s) p2p = path to plots ======== @@ -208,7 +206,7 @@ def plot_variable2D(self,va,pt,en,lv,p2p,na=0,da=0): # Find some way of looping over wrfout files first, avoiding need # to create new W instances # print("Beginning plotting of {0} figures.".format(len(list(perms)))) - pdb.set_trace() + pdb.set_trace() #for x in perms: for x in itertools.product(va,pt,en,lv,da): @@ -218,23 +216,23 @@ def plot_variable2D(self,va,pt,en,lv,p2p,na=0,da=0): F = BirdsEye(self.C,W,p2p) # 2D figure class F.plot2D(va,pt,en,lv,da,na) # Plot/save figure pt_s = utils.string_from_time('title',pt) - print("Plotting from file {0}: \n variable = {1}" + print("Plotting from file {0}: \n variable = {1}" " time = {2}, level = {3}, area = {4}.".format(en,va,pt_s,lv,da)) """ - + def make_iterator2(self,*args): for arg in args: for a in arg: yield a - - def make_iterator(self,va,pt,en,lv,da): + + def make_iterator(self,va,pt,en,lv,da): for v in va: for p in pt: for e in en: for l in lv: for d in da: yield v,p,e,l,d - + def get_sequence(self,x,SoS=0): """ Returns a sequence (tuple or list) for iteration. @@ -251,8 +249,8 @@ def get_sequence(self,x,SoS=0): return x else: return [x] - - + + def plot_cross_section(self,var,latA,lonA,latB,lonB): xs = CrossSection() xs.plot(var,latA,lonA,latB,lonB) @@ -267,7 +265,7 @@ def save_data(self,data,folder,fname,format='pickle'): if format=='pickle': with open(fpath+'.pickle','wb') as f: - pickle.dump(data,f) + pickle.dump(data,f) elif format=='numpy': N.save(fpath,data) elif format=='json': @@ -277,9 +275,9 @@ def save_data(self,data,folder,fname,format='pickle'): else: print("Give suitable saving format.") raise Exception - + print("Saved file {0} to {1}.".format(fname,folder)) - + def load_data(self,folder,fname,format='pickle'): fname2 = os.path.splitext(fname)[0] fpath = os.path.join(folder,fname2) @@ -287,10 +285,10 @@ def load_data(self,folder,fname,format='pickle'): with open(fpath+'.pickle','rb') as f: data = pickle.load(f) elif format=='numpy': - data = N.load(fpath+'.npy') + data = N.load(fpath+'.npy') elif format=='json': print("JSON stuff not coded yet.") - raise Exception + raise Exception else: print("Give suitable loading format.") raise Exception @@ -304,11 +302,11 @@ def compute_diff_energy( """ This method computes difference kinetic energy (DKE) or different total energy (DTE, including temp) - between WRFout files for a given depth of the + between WRFout files for a given depth of the atmosphere, at given time intervals - + Inputs: - + ptype : 'sum_z' or 'sum_xyz' energy : 'kinetic' or 'total' upper : upper limit of vertical integration @@ -318,35 +316,35 @@ def compute_diff_energy( d_save : save dictionary to folder (path to folder) d_return: return dictionary (True or False) d_fname : custom filename - + Outputs: - + data : time series or list of 2D arrays - + ptype 'sum_z' integrates vertically between lower and upper hPa and creates a time series. - + ptype 'sum_xyz' integrates over the 3D space (again between the upper and lower bounds) and creates 2D arrays. """ if d_save and not isinstance(d_save,basestring): d_save = os.environ['HOME'] - + # First, save or output? Can't be neither! if not d_save and not d_return: print("Pick save or output, otherwise it's a waste of computer" "power") raise Exception - + # Look up the method to use depending on type of plot PLOTS = {'sum_z':self.DE_z, 'sum_xyz':self.DE_xyz} - + # Creates sequence of times ts = self.get_sequence(times) # Dictionary of data DATA = {} - + # Get all permutations of files nperm = itertools.combinations(files,2).__sizeof__() for n, perm in enumerate(itertools.combinations(files,2)): @@ -359,7 +357,7 @@ def compute_diff_energy( f1, f2 = perm W1 = WRFOut(f1) W2 = WRFOut(f2) - #pdb.set_trace() + #pdb.set_trace() # Make sure times are the same in both files if not N.all(N.array(W1.wrf_times) == N.array(W2.wrf_times)): print("Times are not identical between input files.") @@ -367,12 +365,12 @@ def compute_diff_energy( else: print("Passed check for identical timestamps between " "NetCDF files") - + # Find indices of each time t_idx = [] for t in ts: t_idx.append(W1.get_time_idx(t)) - + print("Calculating values now...") DATA[str(n)]['times'] = ts DATA[str(n)]['values'] = [] @@ -401,21 +399,21 @@ def DE_xyz(self,nc0,nc1,t_idx,energy,*args): """ Computation for difference kinetic energy (DKE). Sums DKE over the 3D space, returns a time series. - + Destaggering is not enabled as it introduces computational cost that is of miniscule value considering the magnitudes of output values. Inputs: - + nc0 : netCDF file nc1 : netCDF file t_idx : times indices to difference energy : kinetic or total *args : to catch lower/upper boundary which isn't relevant here - + Outputs: - + data : time series. """ # Wind data @@ -447,38 +445,38 @@ def DE_xyz(self,nc0,nc1,t_idx,energy,*args): kappa*(T0[t,:,:,i]-T1[t,:,:,i])**2)) print("DTE at this time: {0}".format(DKE_hr)) DKE.append(DKE_hr) - return DKE + return DKE def DE_z(self,nc0,nc1,t,energy,lower,upper): """ Computation for difference kinetic energy (DKE). Sums DKE over all levels between lower and upper, for each grid point, and returns a 2D array. - + Destaggering is not enabled as it introduces computational cost that is of miniscule value considering the magnitudes of output values. - + Method finds levels nearest lower/upper hPa and sums between them inclusively. - + Inputs: - + nc0 : netCDF file nc1 : netCDF file t : times index to difference energy : kinetic or total lower : lowest level, hPa upper : highest level, hPa - + Outputs: - + data : 2D array. """ - - # Speed up script by only referencing data, not + + # Speed up script by only referencing data, not # loading it to a variable yet - + # WIND U0 = nc0.variables['U'][:] V0 = nc0.variables['V'][:] @@ -497,17 +495,17 @@ def DE_z(self,nc0,nc1,t,energy,lower,upper): R = 287.0 # Universal gas constant (J / deg K * kg) Cp = 1004.0 # Specific heat of dry air at constant pressure (J / deg K * kg) kappa = R/Cp - + xlen = U0.shape[2] # 1 less than in V ylen = V0.shape[3] # 1 less than in U zlen = U0.shape[1] # identical in U & V - + # Generator for lat/lon points def latlon(nlats,nlons): for i in range(nlats): # y-axis for j in range(nlons): # x-axis yield i,j - + DKE = [] DKE2D = N.zeros((xlen,ylen)) print_time = ''.join((nc0.variables['Times'][t])) @@ -526,10 +524,10 @@ def latlon(nlats,nlons): upp_idx = utils.closest(P_col,upper*100.0) else: upp_idx = None - - zidx = slice(low_idx,upp_idx+1) + + zidx = slice(low_idx,upp_idx+1) # This needs to be a 2D array? - + if energy=='kinetic': DKE2D[j,i] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + (V0[t,zidx,j,i]-V1[t,zidx,j,i])**2)) @@ -537,11 +535,11 @@ def latlon(nlats,nlons): DKE2D[j,i] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + (V0[t,zidx,j,i]-V1[t,zidx,j,i])**2 + kappa*(T0[t,zidx,j,i]-T1[t,zidx,j,i])**2)) - + DKE.append(DKE2D) - + return DKE - + def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,V): """ folder : directory holding computed data @@ -552,10 +550,10 @@ def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,V): DATA = self.load_data(folder,fname,format='pickle') times = self.get_sequence(time) - + for n,t in enumerate(times): for pn,perm in enumerate(DATA): - if sw==0: + if sw==0: # Get times and info about nc files W1 = WRFOut(DATA[perm]['file1']) permtimes = DATA[perm]['times'] @@ -563,10 +561,10 @@ def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,V): # Find array for required time x = N.where(N.array(permtimes)==t)[0][0] - data = DATA[perm]['values'][x][0] + data = DATA[perm]['values'][x][0] if not pn: stack = data - else: + else: stack = N.dstack((data,stack)) stack_average = N.average(stack,axis=2) diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 6a784d4..b683c4e 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -17,20 +17,20 @@ def __init__(self,config,wrfout): self.D = Defaults() self.p2p = self.C.output_root print self.p2p - + def plot_data(self,data,mplcommand,fname,pt,V=0): # INITIALISE self.fig = plt.figure() self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user self.bmap,x,y = self.basemap_setup() - + if mplcommand == 'contour': if not V: self.bmap.contour(x,y,data) else: self.bmap.contour(x,y,data,V) - - + + # LABELS, TITLES etc """ Change these to hasattr! @@ -46,34 +46,47 @@ def plot_data(self,data,mplcommand,fname,pt,V=0): self.fname = self.create_fname(fname) # No da variable here self.save(self.fig,self.p2p,self.fname) self.fig.clf() - - def plot2D(self,va,pt,lv,da=0,na=0): + + def plot2D(self,va,vardict,da=0,na=0): """ Inputs: - + + va : variable + + vardict : dictionary of + pt : plot time + lv : level + vc : vertical coordinate system + + Other arguments: + da : dictionary of: tla : top limit of latitude bla : bottom limit of latitude llo : left limit of longitude rlo : right limit of longitude - + """ # INITIALISE #en = self.W.path self.fig = plt.figure() self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user self.bmap,x,y = self.basemap_setup() - + + # Unpack dictionary + lv = vardict['lv'] + pt = vardict['pt'] + # Get indices for time, level, lats, lons # TIME time_idx = self.W.get_time_idx(pt) - + # LAT/LON lat_sl, lon_sl = self.get_limited_domain(da) # LEVEL - vc = utils.level_type(lv) + vc = vardict['vc'] if vc == 'surface': lv_idx = 0 elif lv == 'all': @@ -82,12 +95,21 @@ def plot2D(self,va,pt,lv,da=0,na=0): print("Need to sort other levels") raise Exception - lv_na = utils.get_level_naming(lv) + lv_na = utils.get_level_naming(lv,va,vardict) + + # Now clear dictionary of old settings + # They will be replaced with indices + # vardict.pop('pt') + # vardict.pop('lv') + # try: + # vardict.pop('da') + # except KeyError: + # pass # FETCH DATA - PS = {'t': time_idx, 'lv': lv_idx, 'la': lat_sl, 'lo': lon_sl} - data = self.W.get(va,PS) - + PS = {'t': time_idx, 'lv': lv_idx, 'la': lat_sl, 'lo': lon_sl} + data = self.W.get(va,PS,**vardict) + la_n = data.shape[-2] lo_n = data.shape[-1] @@ -100,7 +122,7 @@ def plot2D(self,va,pt,lv,da=0,na=0): self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,cmap=plt.cm.jet) else: self.bmap.contourf(x,y,data.reshape((la_n,lo_n))) - + # LABELS, TITLES etc if self.C.plot_titles: title = utils.string_from_time('title',pt) @@ -124,7 +146,7 @@ def plot2D(self,va,pt,lv,da=0,na=0): def basemap_setup(self): # Fetch settings basemap_res = getattr(self.C,'basemap_res',self.D.basemap_res) - + width_m = self.W.dx*(self.W.x_dim-1) height_m = self.W.dy*(self.W.y_dim-1) @@ -133,7 +155,7 @@ def basemap_setup(self): lon_0=self.W.cen_lon,lat_0=self.W.cen_lat,lat_1=self.W.truelat1, lat_2=self.W.truelat2,resolution=basemap_res,area_thresh=500 ) - m.drawcoastlines() + m.drawcoastlines() m.drawstates() m.drawcountries() @@ -143,4 +165,4 @@ def basemap_setup(self): x,y = m(self.W.lons,self.W.lats) return m, x, y - + diff --git a/postWRF/postWRF/scales.py b/postWRF/postWRF/scales.py index e7ceb2a..376cf0b 100644 --- a/postWRF/postWRF/scales.py +++ b/postWRF/postWRF/scales.py @@ -25,6 +25,8 @@ def get_cm(va,lv): + if lv=='all': + lv = 0 # Variable and vertical level determine contour scale # pdb.set_trace() try: @@ -114,5 +116,8 @@ def find_nearest_level(lv): A['snow'] = {'cmap':ct.snow2} A['snow'][2000] = [0.25,0.5,0.75,1,1.5,2,2.5,3,4,5,6,8,10,12,14,16,18] +A['shear'] = {'cmap':0} +A['shear'][0] = (0,33,3) + diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 44a742a..62bcc43 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -26,29 +26,30 @@ def __init__(self,fpath,config=0): self.wrf_times = self.nc.variables['Times'][:] self.dx = self.nc.DX self.dy = self.nc.DY - #self.lvs = + #self.lvs = self.lats = self.nc.variables['XLAT'][0,...] # Might fail if only one time? self.lons = self.nc.variables['XLONG'][0,...] - + self.cen_lat = float(self.nc.CEN_LAT) self.cen_lon = float(self.nc.CEN_LON) self.truelat1 = float(self.nc.TRUELAT1) self.truelat2 = float(self.nc.TRUELAT2) self.x_dim = len(self.nc.dimensions['west_east']) self.y_dim = len(self.nc.dimensions['south_north']) - - # Loads variable list + self.z_dim = len(self.nc.dimensions['bottom_top']) + + # Loads variable list self.fields = self.nc.variables.keys() - + def get_time_idx(self,t,tuple_format=0): - + """ Input: - + t : time, not tuple format by default - + Output: - + time_idx : index of time in WRF file """ if tuple_format: @@ -70,7 +71,7 @@ def get_time_idx(self,t,tuple_format=0): # Now find closest WRF time self.time_idx = N.where( - abs(self.wrf_times_epoch-t_epoch) == + abs(self.wrf_times_epoch-t_epoch) == abs(self.wrf_times_epoch-t_epoch).min() )[0][0] return self.time_idx @@ -80,23 +81,23 @@ def check_compute(self,var): """This method returns the required variables that need to be loaded from the netCDF file. """ - + if var in self.fields: return True else: return False - + def get(self,var,slices,**kwargs): """ Fetch a numpy array containing variable data. - + If field isn't present in the WRFOut file, compute it. - + Slice according to arguments. - + Destagger if required. - + Returns unstaggered, sliced data. - + var : netCDF variable name slices : dict, keys as follows: @@ -104,13 +105,13 @@ def get(self,var,slices,**kwargs): lv : level index la : latitude slice indices lo : longitude slice indices - + keyword arguments might include: - + Shear between 0 and 3 km: {'top':3, 'bottom',0} - + """ # Check if computing required # When data is loaded from nc, it is destaggered @@ -119,11 +120,11 @@ def get(self,var,slices,**kwargs): data = self.load(var,slices) else: data = self.compute(var,slices,**kwargs) - + return data def load(self,var,PS): - + # First, check dimension that is staggered (if any) PS['destag_dim'] = self.check_destagger(var) @@ -145,25 +146,25 @@ def create_slice(self,PS): sl.append(slice(None,None)) elif isinstance(PS['lv'],int): sl.append(slice(PS['lv'],PS['lv']+1)) - elif PS['lv']=='all': + elif PS['lv']=='all': sl.append(slice(None,None)) - + if any('north' and 'west' in p for p in PS['dim_names']): - sl.append(PS['la']) - sl.append(PS['lo']) - + sl.append(PS['la']) + sl.append(PS['lo']) + return sl def check_destagger(self,var): """ Looks up dimensions of netCDF file without loading data. - + Returns dimension number that requires destaggering """ stag_dim = None for n,dname in enumerate(self.nc.variables[var].dimensions): if 'stag' in dname: stag_dim = n - + return stag_dim def get_dims(self,var): @@ -172,12 +173,12 @@ def get_dims(self,var): def destagger(self,data,ax): """ Destagger data which needs it doing. - + data : numpy array of data requiring destaggering ax : axis requiring destaggering - + Theta always has unstaggered points in all three spatial dimensions (axes=1,2,3). - + Data should be 4D but just the slice required to reduce unnecessary computation time. """ if ax==None: @@ -186,7 +187,7 @@ def destagger(self,data,ax): nd = data.ndim sl0 = [] # Slices to take place on staggered axis sl1 = [] - + for n in range(nd): if n is not ax: sl0.append(slice(None)) @@ -194,18 +195,18 @@ def destagger(self,data,ax): else: sl0.append(slice(None,-1)) sl1.append(slice(1,None)) - + data_unstag = 0.5*(data[sl0] + data[sl1]) return data_unstag def compute(self,var,slices,**kwargs): """ Look up method needed to return array of data for required variable. - + Keyword arguments include settings for computation e.g. top and bottom of shear computation """ - + tbl = {} tbl['shear'] = self.compute_shear tbl['thetae'] = self.compute_thetae @@ -217,22 +218,28 @@ def compute(self,var,slices,**kwargs): tbl['pressure'] = self.compute_pressure tbl['temps'] = self.compute_temps tbl['theta'] = self.compute_theta + tbl['Z'] = self.compute_geopotential_height data = tbl[var](slices,**kwargs) return data + def compute_geopotential_height(self,slices): + geopotential = self.get('PH',slices) + self.get('PHB',slices) + Z = geopotential/9.81 + return Z + def compute_wind10(self,slices): u = self.get('U10',slices) v = self.get('V10',slices) data = N.sqrt(u**2 + v**2) return data - + def compute_pressure(self,slices): PP = self.get('P',slices) PB = self.get('PB',slices) pressure = PP + PB return pressure - + def compute_temps(self,slices,units='K'): theta = self.get('theta',slices) P = self.get('pressure',slices) @@ -241,32 +248,62 @@ def compute_temps(self,slices,units='K'): return temps elif units=='C': return temps-273.15 - + def compute_theta(self,slices): theta = self.get('T',slices) Tbase = 300.0 theta = Tbase + theta return theta - + def compute_wind(self,slices): u = self.get('U',slices) v = self.get('V',slices) - data = N.sqrt(u**2 + v**2) + data = N.sqrt(u**2 + v**2) return data def compute_shear(self,slices,**kwargs): """ + top and bottom in km. kwargs['top'] kwargs['bottom'] """ - pass - #return shear + topm = kwargs['top']*1000 + botm = kwargs['bottom']*1000 + + u = self.get('U',slices) + v = self.get('V',slices) + Z = self.get('Z',slices) + + topidx = N.zeros((450,450)) + botidx = N.zeros((450,450)) + ushear = N.zeros((450,450)) + vshear = N.zeros((450,450)) + + for i in range(self.x_dim): + for j in range(self.y_dim): + topidx[i,j] = round(N.interp( + topm,Z[0,:,i,j],range(self.z_dim))) + botidx[i,j] = round(N.interp( + botm,Z[0,:,i,j],range(self.z_dim))) + ushear[i,j] = u[0,topidx[i,j],i,j] - u[0,botidx[i,j],i,j] + vshear[i,j] = v[0,topidx[i,j],i,j] - v[0,botidx[i,j],i,j] + + # Find indices of bottom and top levels + # topidx = N.where(abs(Z-topm) == abs(Z-topm).min(axis=1)) + # botidx = N.where(abs(Z-botm) == abs(Z-botm).min(axis=1)) + + # ushear = u[0,:,topidx] - u[0,:,botidx] + # vshear = v[0,topidx,:,:] - v[0,botidx,:,:] + + shear = N.sqrt(ushear**2 + vshear**2) + # pdb.set_trace() + return shear def compute_thetae(self,slices): P = self.get('pressure',slices) # Computed Drybulb = self.get('temp',slices) Q = self.get('Q',slices) - + thetae = (Drybulb + (Q * cc.Lv/cc.cp)) * (cc.P0/P) ** cc.kappa return thetae @@ -341,11 +378,11 @@ def compute_DCP(self): meanwind_0_6 = self.get('meanwind',0,6) DCP = (DCAPE/980.0)*(MUCAPE/2000.0)*(shear_0_6/20.0)*(meanwind_0_6/16.0) return DCP - + def compute_DCAPE(self): - + pass - + def compute_thetae(self,slices): P = self.get('pressure',slices) T = self.get('temps',slices,units='K') @@ -354,8 +391,8 @@ def compute_thetae(self,slices): x = thermo.wetlift(p2,t2,100.0) thetae = thermo.theta(100.0, x, 1000.0) return thetae - - + + def compute_Td(self,slices): """ Using HootPy equation @@ -368,17 +405,19 @@ def compute_Td(self,slices): b = N.subtract(17.67,N.log(N.divide(e,6.112))) Td = N.divide(a,b) return Td - + def compute_CAPE(self,slices): """ + INCOMPLETE! + CAPE method based on GEMPAK's pdsuml.f function - + Inputs: - + slices : dictionary of level/time/lat/lon - - + + Outputs: CAPE : convective available potential energy CIN : convective inhibition @@ -386,14 +425,16 @@ def compute_CAPE(self,slices): # Make sure all levels are obtained #slices['lv'] = slice(None,None) slices.pop('lv',None) - + totalCAPE = 0 totalCIN = 0 - + theta = self.get('theta',slices) + Z = self.get('Z',slices) for lvidx in range(theta.shape[1]-1): - + if lvidx < 20: + continue # This should loop over the levels? """ z1 : bottom of layer (index) @@ -403,31 +444,30 @@ def compute_CAPE(self,slices): thp1 : theta (parcel) at z1 thp2 : theta (parcel) at z2 """ - - z1 = lvidx - z2 = lvidx + 1 - + + z1 = Z[0,lvidx,...] + z2 = Z[0,lvidx+1,...] + th1 = theta[0,lvidx,...] th2 = theta[0,lvidx+1,...] thp1 = 0 thp2 = 0 - + capeT = 0.0 cinT = 0.0 - + dt2 = thp2 - th2 dt1 = thp1 - th1 - + dz = z2 - z1 - - # - # dt1_pos = N.ma.masked_greater(dt1,0) - # dt1_neg = N.ma.masked_less(dt1,0) - - # dt2_pos = N.ma.masked_greater(dt2,0) - # dt2_neg = N.ma.masked_less(dt2,0) - + + dt1_pos_ma = N.ma.masked_greater(dt1,0) + dt1_neg_ma = N.ma.masked_less(dt1,0) + + dt2_pos_ma = N.ma.masked_greater(dt2,0) + dt2_neg_ma = N.ma.masked_less(dt2,0) + dt1_pos = N.select([dt1>0],[dt1]) dt1_neg = N.select([dt1<0],[dt1]) dt2_pos = N.select([dt2>0],[dt1]) @@ -451,51 +491,51 @@ def compute_CAPE(self,slices): cinT = (dt1*(zfc-z1)/(tfc+th1)) else: cinT = ((dt2+dt1)*dz)/(th2+th1) - + if capeT > 0: CAPE = capeT * cc.g else: CAPE = 0 - - if cinT < 0: + + if cinT < 0: CIN = cinT * cc.g else: CIN = 0 - + totalCAPE += CAPE totalCIN += CIN - + return totalCAPE,totalCIN - + def compute_ave(self,va,z1,z2): """ Compute average values for variable in layer - + Inputs: va : variable z1 : height at bottom z2 : height at top - + Output: data : the averaged variable """ - + # Get coordinate system vc = self.check_vcs(z1,z2) - - - + + + def check_vcs(self,z1,z2,exception=1): """ Check the vertical coordinate systems - + If identical, return the system If not, raise an exception. """ - + vc = utils.level_type(z1) vc = utils.level_type(z2) - + if vc1 != vc2: print("Vertical coordinate systems not identical.") return False @@ -503,30 +543,30 @@ def check_vcs(self,z1,z2,exception=1): raise Exception else: return vc1 - + def get_XY(self,lat,lon): """Return grid indices for lat/lon pair. """ pass - + def get_lat_idx(self,lat): lat_idx = N.where(abs(self.lats-lat) == abs(self.lats-lat).min())[0][0] return lat_idx - + def get_lon_idx(self,lon): lon_idx = N.where(abs(self.lons-lon) == abs(self.lons-lon).min())[0][0] return lon_idx - + def interp_to_p(self,config,nc_path,var,lv): """ Uses p_interp fortran code to put data onto a pressure level specified. - + Input: config : contains directory of p_interp files nc_path : path to original netCDF file data var : variable(s) to compute lv : pressure level(s) to compute - + Returns: fpath : path to new netCDF file with p co-ords """ @@ -537,7 +577,7 @@ def interp_to_p(self,config,nc_path,var,lv): config.p_interp_root,'namelist.pinterp') nc_root, nc_fname = os.path.split(nc_path)# Root directory of wrfout file output_root = nc_root # Directory to dump output file (same) - + """ Can we add a suffix to the new netCDF file? """ @@ -545,7 +585,7 @@ def interp_to_p(self,config,nc_path,var,lv): command1 = ' '.join(('cp',p_interp_path,p_interp_path+'.bkup')) os.system(command1) - # Edit p_interp's namelist settings + # Edit p_interp's namelist settings edit_namelist(path_to_interp,'path_to_input',nc_root,col=18) edit_namelist(path_to_interp,'input_name',nc_fname,col=18) edit_namelist(path_to_interp,'path_to_output',output_root,col=18) @@ -553,13 +593,13 @@ def interp_to_p(self,config,nc_path,var,lv): edit_namelist(path_to_interp,'fields',var,col=18) edit_namelist(path_to_interp,'met_em_output','.FALSE.',col=18) edit_namelist(path_to_interp,'fields',var,col=18) - + command2 = os.path.join('./',p_interp_path) os.system(command2) # This should execute the script - + return fpath - - + + def edit_namelist(self,fpath,old,new,incolumn=1,col=23): """col=23 is default for wps namelists. """ @@ -577,4 +617,4 @@ def edit_namelist(self,fpath,old,new,incolumn=1,col=23): break - + diff --git a/utils/utils.py b/utils/utils.py index 2f0c54a..ee0942a 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -64,7 +64,7 @@ def lookup_time(str): D = {'year':0, 'month':1, 'day':2, 'hour':3, 'minute':4, 'second':5} return D[str] -def get_level_naming(lv): +def get_level_naming(lv,va,vardict): if lv < 1500: return str(lv)+'hPa' elif lv == 2000: @@ -76,7 +76,11 @@ def get_level_naming(lv): elif lv.endswith('km'): return lv elif lv == 'all': - return 'all model levels' + if va == 'shear': + name = '{0}to{1}'.format(vardict['bottom'],vardict['top']) + return name + else: + return 'all_lev' def level_type(lv): From ec07e4e823cdaa81e039e17f4210ec6f456ae421 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Fri, 7 Mar 2014 09:30:25 -0600 Subject: [PATCH 067/111] Fixed wind (10m) plotting bug --- postWRF/postWRF/__init__.py | 31 ++++++++++++------------------- postWRF/postWRF/figure.py | 2 +- postWRF/postWRF/wrfout.py | 6 +++--- utils/utils.py | 15 ++++++++++++++- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index d0b5895..806691c 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -23,6 +23,7 @@ import time import json import cPickle as pickle +import copy from wrfout import WRFOut from axes import Axes @@ -124,14 +125,14 @@ def plot_2D(self,dic): #self.en = self.get_sequence(wrfout) #self.pt = self.get_sequence(times) # List of plot times - + Dic = copy.deepcopy(dic) wrfpath = self.wrfout_files_in(self.C.wrfout_root)[0] self.W = WRFOut(wrfpath) # Only load netCDF file once! - for va in dic: - if not 'lv' in dic[va]: # For things like CAPE, shear. - dic[va]['lv'] = 'all' + for va in Dic: + if not 'lv' in Dic[va]: # For things like CAPE, shear. + Dic[va]['lv'] = 'all' - vc = utils.level_type(dic[va]['lv']) # vertical coordinate + vc = utils.level_type(Dic[va]['lv']) # vertical coordinate # Check for pressure levels if vc == 'isobaric': @@ -146,16 +147,16 @@ def plot_2D(self,dic): pass F = BirdsEye(self.C,self.W) - lv = dic[va]['lv'] - for t in dic[va]['pt']: - # print('Passing point. dic: \n', dic) + lv = Dic[va]['lv'] + for t in Dic[va]['pt']: + # print('Passing point. Dic: \n', Dic) # pdb.set_trace() disp_t = utils.string_from_time('title',t) print("Plotting {0} at lv {1} for time {2}.".format( va,lv,disp_t)) - dic[va]['pt'] = t - dic[va]['vc'] = vc - F.plot2D(va, dic[va]) + Dic[va]['pt'] = t + Dic[va]['vc'] = vc + F.plot2D(va, Dic[va]) """ def plot_variable2D(self,varlist,timelist): @@ -576,11 +577,3 @@ def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,V): print("Plotting time {0} from {1}.".format(n,len(times))) del data, stack - def generate_times(self,idate,fdate,interval): - """ - Interval in seconds - """ - i = calendar.timegm(idate) - f = calendar.timegm(fdate) - times = range(i,f,interval) - return times diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index 254312b..f0de7f4 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -22,7 +22,7 @@ def create_fname(self,*naming): """Default naming should be: Variable + time + level """ - fname = '_'.join([str(a) for a in naming]) + 'Z' + fname = '_'.join([str(a) for a in naming]) #pdb.set_trace() return fname diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 62bcc43..d2b7004 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -228,7 +228,7 @@ def compute_geopotential_height(self,slices): Z = geopotential/9.81 return Z - def compute_wind10(self,slices): + def compute_wind10(self,slices,**kwargs): u = self.get('U10',slices) v = self.get('V10',slices) data = N.sqrt(u**2 + v**2) @@ -307,7 +307,7 @@ def compute_thetae(self,slices): thetae = (Drybulb + (Q * cc.Lv/cc.cp)) * (cc.P0/P) ** cc.kappa return thetae - def compute_comp_ref(self,PS): + def compute_comp_ref(self,PS,**kwargs): """Amend this so variables obtain at start fetch only correct date, lats, lons All levels need to be fetched as this is composite reflectivity """ @@ -350,7 +350,7 @@ def compute_comp_ref(self,PS): Qsn = N.row_stack((Qsn, N_curcol_s)) # Calculate slope factor lambda - lambr = (N.divide((3.14159 * no_rain * rhor), N.multiply(density, Qra))) ** 0.25 + lambr = (N.divide((3.14159 * no_rain * rhor), N.multiply(density, Qra)+N.nextafter(0,1))) ** 0.25 lambs = N.exp(-0.0536 * (T2 - 273.15)) # Calculate equivalent reflectivity factor diff --git a/utils/utils.py b/utils/utils.py index ee0942a..a1cdb32 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -1,6 +1,7 @@ import numpy as N import os import time +import calendar """ A collection of useful utilities. """ @@ -38,12 +39,14 @@ def string_from_time(usage,t,dom=0,strlen=0,conven=0): elif usage == 'output': if not conven: # No convention set, assume DD/MM (I'm biased) - conven = 'DM' + conven = 'full' # Generates string for output file creation if conven == 'DM': str = '{2:02d}{1:02d}_{3:02d}{4:02d}'.format(*t) elif conven == 'MD': str = '{1:02d}{2:02d}_{3:02d}{4:02d}'.format(*t) + elif conven == 'full': + str = '{0:04d}{1:02d}{2:02d}{3:02d}{4:02d}'.format(*t) else: print("Set convention for date format: DM or MD.") elif usage == 'dir': @@ -131,3 +134,13 @@ def dstack_loop(data, obj): stack = N.dstack((obj,data)) return stack + +def generate_times(idate,fdate,interval): + """ + Interval in seconds + """ + i = calendar.timegm(idate) + f = calendar.timegm(fdate) + times = range(i,f,interval) + return times + From e00f78c57fc12f8011459b83cc452f7d6c130956 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 10 Mar 2014 08:18:32 -0500 Subject: [PATCH 068/111] Working Monday morning version, yay --- postWRF/postWRF/scales.py | 6 ++++- postWRF/postWRF/wrfout.py | 51 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/postWRF/postWRF/scales.py b/postWRF/postWRF/scales.py index 376cf0b..758c630 100644 --- a/postWRF/postWRF/scales.py +++ b/postWRF/postWRF/scales.py @@ -106,7 +106,7 @@ def find_nearest_level(lv): # Precipitation A['precip'] = {'cmap':ct.precip1} A['precip'][2000] = [0.01,0.03,0.05,0.10,0.15,0.20,0.25,0.30,0.40,0.50,0.60, - 0.70,0.80,0.90,1.00,1.25,1.50,1.75,2.00,2.50] + 0.70,0.80,0.90,1.00,1.25,1.50,1.75,2.00,2.50] # Precipitable water A['pwat'] = {'cmap':ct.precip1} @@ -119,5 +119,9 @@ def find_nearest_level(lv): A['shear'] = {'cmap':0} A['shear'][0] = (0,33,3) +A['buoyancy'] = {'cmap':0} +A['buoyancy'][2000] = (-0.65,0.075,0.025) +A['dptp'] = {'cmap':0} +A['dptp'][2000] = (-15,6,1) diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index d2b7004..42fb6a3 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -219,11 +219,59 @@ def compute(self,var,slices,**kwargs): tbl['temps'] = self.compute_temps tbl['theta'] = self.compute_theta tbl['Z'] = self.compute_geopotential_height + tbl['dptp'] = self.compute_dptp #density potential temperature pert. + tbl['dpt'] = self.compute_dpt #density potential temperature pert. + tbl['buoyancy'] = self.compute_buoyancy data = tbl[var](slices,**kwargs) return data - def compute_geopotential_height(self,slices): + def compute_buoyancy(self,slices,**kwargs): + """ + Method from Adams-Selin et al., 2013, WAF + """ + theta = self.get('theta',slices) + thetabar = N.mean(theta) + qv = self.get('QVAPOR',slices) + qvbar = N.mean(qv) + + B = cc.g * ((theta-thetabar)/thetabar + 0.61*(qv - qvbar)) + return B + + def compute_mixing_ratios(self,slices,**kwargs): + qv = self.get('QVAPOR',slices) + qc = self.get('QCLOUD',slices) + qr = self.get('QRAIN',slices) + qi = self.get('QICE',slices) + qs = self.get('QSNOW',slices) + qg = self.get('QGRAUP',slices) + + rh = qc + qr + qi + qs + qg + rv = qv + + return rh, rv + + def compute_dptp(self,slices,**kwargs): + dpt = self.get('dpt',slices) + dpt_mean = N.mean(dpt) + dptp = dpt - dpt_mean + return dptp + + def compute_dpt(self,slices,**kwargs): + """ + Potential: if surface level is requested, choose sigma level just + about the surface. I don't think this affects any + other dictionaries around... + """ + # if slices['lv'] == 0: + # slices['lv'] = 0 + theta = self.get('theta',slices) + rh, rv = self.compute_mixing_ratios(slices) + + dpt = theta * (1 + 0.61*rv - rh) + return dpt + + def compute_geopotential_height(self,slices,**kwargs): geopotential = self.get('PH',slices) + self.get('PHB',slices) Z = geopotential/9.81 return Z @@ -580,6 +628,7 @@ def interp_to_p(self,config,nc_path,var,lv): """ Can we add a suffix to the new netCDF file? + Check to see if file already exists """ # Copy old p_interp for backup command1 = ' '.join(('cp',p_interp_path,p_interp_path+'.bkup')) From e0d67e7a37bd1fa3845f1a1508864ca27cf620e2 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 10 Mar 2014 15:24:35 -0500 Subject: [PATCH 069/111] Plot the strongest sfc wind between two times. --- postWRF/postWRF/__init__.py | 24 +++++---- postWRF/postWRF/birdseye.py | 29 ++++++++-- postWRF/postWRF/scales.py | 44 ++++++++------- postWRF/postWRF/wrfout.py | 27 ++++++++-- utils/utils.py | 105 +++++++++++++++++++++--------------- 5 files changed, 154 insertions(+), 75 deletions(-) diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index 806691c..d17fcfb 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -129,10 +129,18 @@ def plot_2D(self,dic): wrfpath = self.wrfout_files_in(self.C.wrfout_root)[0] self.W = WRFOut(wrfpath) # Only load netCDF file once! for va in Dic: + if not 'lv' in Dic[va]: # For things like CAPE, shear. Dic[va]['lv'] = 'all' - vc = utils.level_type(Dic[va]['lv']) # vertical coordinate + lv = Dic[va]['lv'] + vc = utils.level_type(lv) # vertical coordinate + + if not 'pt' in Dic[va]: # For averages and all times + if not 'itime' in Dic[va]: # For all times + Dic[va]['pt'] = ['all',] + else: # For specific range + Dic[va]['pt'] = ['range',] # Check for pressure levels if vc == 'isobaric': @@ -147,15 +155,12 @@ def plot_2D(self,dic): pass F = BirdsEye(self.C,self.W) - lv = Dic[va]['lv'] + for t in Dic[va]['pt']: - # print('Passing point. Dic: \n', Dic) - # pdb.set_trace() - disp_t = utils.string_from_time('title',t) - print("Plotting {0} at lv {1} for time {2}.".format( - va,lv,disp_t)) - Dic[va]['pt'] = t - Dic[va]['vc'] = vc + disp_t = utils.string_from_time('title',t,**Dic[va]) + print("Plotting {0} at lv {1} for time {2}.".format(va,lv,disp_t)) + Dic[va]['pt'] = t # Need this? + Dic[va]['vc'] = vc # Need this? F.plot2D(va, Dic[va]) """ @@ -577,3 +582,4 @@ def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,V): print("Plotting time {0} from {1}.".format(n,len(times))) del data, stack + diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index b683c4e..406a693 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -77,10 +77,18 @@ def plot2D(self,va,vardict,da=0,na=0): # Unpack dictionary lv = vardict['lv'] pt = vardict['pt'] + # pdb.set_trace() # Get indices for time, level, lats, lons # TIME - time_idx = self.W.get_time_idx(pt) + if pt == 'all': + time_idx = slice(None,None) + elif pt == 'range': + start_frame = self.W.get_time_idx(vardict['itime'], tuple_format=1) + end_frame = self.W.get_time_idx(vardict['ftime'], tuple_format=1) + time_idx = slice(start_frame,end_frame) + else: + time_idx = self.W.get_time_idx(pt) # LAT/LON lat_sl, lon_sl = self.get_limited_domain(da) @@ -97,6 +105,15 @@ def plot2D(self,va,vardict,da=0,na=0): lv_na = utils.get_level_naming(lv,va,vardict) + """ + def plot_strongest_wind(self,dic): + itime = dic['itime'] + ftime = dic['ftime'] + V = dic.get('range',(10,32.5,2.5)) + F = BirdsEye(self.C,W + + """ + # Now clear dictionary of old settings # They will be replaced with indices # vardict.pop('pt') @@ -114,18 +131,24 @@ def plot2D(self,va,vardict,da=0,na=0): lo_n = data.shape[-1] # COLORBAR, CONTOURING - cm, clvs = scales.get_cm(va,lv) + cm, clvs = scales.get_cm(va,**vardict) + # Override contour levels if specified + # clvs = vardict.get('range',clvs_default) + # pdb.set_trace() if cm: self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,cmap=cm) elif isinstance(clvs,N.ndarray): self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,cmap=plt.cm.jet) + # elif isinstance(clvs,tuple) or isinstance(clvs,list): + # N.array(clvs) + # self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,cmap=plt.cm.jet) else: self.bmap.contourf(x,y,data.reshape((la_n,lo_n))) # LABELS, TITLES etc if self.C.plot_titles: - title = utils.string_from_time('title',pt) + title = utils.string_from_time('title',pt,**vardict) plt.title(title) if self.C.colorbar: plt.colorbar(orientation='horizontal') diff --git a/postWRF/postWRF/scales.py b/postWRF/postWRF/scales.py index 758c630..d0823f9 100644 --- a/postWRF/postWRF/scales.py +++ b/postWRF/postWRF/scales.py @@ -23,30 +23,36 @@ import colourtables as ct import WEM.utils as utils -def get_cm(va,lv): +def get_cm(va,**kwargs): + lv = kwargs['lv'] + if lv=='all': lv = 0 # Variable and vertical level determine contour scale # pdb.set_trace() - try: - if len(A[va][lv]) == 3: - # This is a min-max-interval list - clvs = N.arange(*A[va][lv]) - else: - # This is an actual list of values - clvs = A[va][lv] - except KeyError: - # If no level exists, try finding a near one + + if kwargs['range']: # Custom range set by user + clvs = N.arange(*kwargs['range']) + else: try: - near_lv = find_nearest_level(lv) - clvs = A[va][near_lv] - except: - # Some variables don't live on a vertical level - clvs = 0 - # except: - # raise Exception - + if len(A[va][lv]) == 3: + # This is a min-max-interval list + clvs = N.arange(*A[va][lv]) + else: + # This is an actual list of values + clvs = A[va][lv] + except KeyError: + # If no level exists, try finding a near one + try: + near_lv = find_nearest_level(lv) + clvs = A[va][near_lv] + except: + # Some variables don't live on a vertical level + clvs = 0 + # except: + # raise Exception + try: cm = A[va]['cmap'](clvs) #pdb.set_trace() @@ -125,3 +131,5 @@ def find_nearest_level(lv): A['dptp'] = {'cmap':0} A['dptp'][2000] = (-15,6,1) +A['strongestwind'] = {'cmap':0} +A['strongestwind'][2000] = (10,32.5,2.5) diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 42fb6a3..f822585 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -12,6 +12,8 @@ import calendar import pdb import constants as cc +import scipy.ndimage + #sys.path.append('/home/jrlawson/gitprojects/meteogeneral/') #from meteogeneral.WRF import wrf_tools @@ -41,6 +43,13 @@ def __init__(self,fpath,config=0): # Loads variable list self.fields = self.nc.variables.keys() + """ + # TESTING A SMOOTHING METHOD + def test_smooth(self,data): + from scipy import ndimage + from skimage.feature + """ + def get_time_idx(self,t,tuple_format=0): """ @@ -139,8 +148,12 @@ def load(self,var,PS): def create_slice(self,PS): # See which dimensions are present in netCDF file variable sl = [] + # pdb.set_trace() if any('Time' in p for p in PS['dim_names']): - sl.append(slice(PS['t'],PS['t']+1)) + if isinstance(PS['t'],slice): + sl.append(PS['t']) + else: + sl.append(slice(PS['t'],PS['t']+1)) if any('bottom' in p for p in PS['dim_names']): if not 'lv' in PS: # No level specified sl.append(slice(None,None)) @@ -222,6 +235,7 @@ def compute(self,var,slices,**kwargs): tbl['dptp'] = self.compute_dptp #density potential temperature pert. tbl['dpt'] = self.compute_dpt #density potential temperature pert. tbl['buoyancy'] = self.compute_buoyancy + tbl['strongestwind'] = self.compute_strongest_wind data = tbl[var](slices,**kwargs) return data @@ -314,6 +328,8 @@ def compute_shear(self,slices,**kwargs): top and bottom in km. kwargs['top'] kwargs['bottom'] + + Could make this faster with numpy.digitize()? """ topm = kwargs['top']*1000 botm = kwargs['bottom']*1000 @@ -454,7 +470,7 @@ def compute_Td(self,slices): Td = N.divide(a,b) return Td - def compute_CAPE(self,slices): + def compute_CAPE(self,slices,**kwargs): """ INCOMPLETE! @@ -571,7 +587,12 @@ def compute_ave(self,va,z1,z2): # Get coordinate system vc = self.check_vcs(z1,z2) - + def compute_strongest_wind(self,slices,**kwargs): + wind = self.get('wind10',slices) + wind_max = N.amax(wind,axis=0) + # wind_max_smooth = self.test_smooth(wind_max) + # return wind_max_smooth + return wind_max def check_vcs(self,z1,z2,exception=1): """ diff --git a/utils/utils.py b/utils/utils.py index a1cdb32..27af857 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -16,52 +16,73 @@ def padded_times(timeseq): padded = ['{0:04d}'.format(t) for t in timeseq] return padded -def string_from_time(usage,t,dom=0,strlen=0,conven=0): - """ - conven : convection of MM/DD versus DD/MM - """ +def string_from_time(usage,t,dom=0,strlen=0,conven=0,**kwargs): + """ + conven : convection of MM/DD versus DD/MM + """ - #if not tupleformat: - if isinstance(t,int): - # In this case, time is in datenum. Get it into tuple format. - t = time.gmtime(t) - if usage == 'title': - # Generates string for titles - str = '{3:02d}:{4:02d}Z on {2:02d}/{1:02d}/{0:04d}'.format(*t) - elif usage == 'wrfout': - # Generates string for wrfout file finding - # Needs dom - if not dom: - print("No domain specified; using domain #1.") - dom = 1 - str = ('wrfout_d0' + str(dom) + - '{0:04d}-{1:02d}-{2:02d}_{3:02d}:{4:02d}:{5:02d}'.format(*t)) - elif usage == 'output': - if not conven: - # No convention set, assume DD/MM (I'm biased) - conven = 'full' - # Generates string for output file creation - if conven == 'DM': - str = '{2:02d}{1:02d}_{3:02d}{4:02d}'.format(*t) - elif conven == 'MD': - str = '{1:02d}{2:02d}_{3:02d}{4:02d}'.format(*t) - elif conven == 'full': - str = '{0:04d}{1:02d}{2:02d}{3:02d}{4:02d}'.format(*t) + + if isinstance(t,str): + if usage == 'output': + usage = 'skip' # Time is already a string + elif usage == 'title': + if kwargs['itime']: # For averages or maxima over time + itime = kwargs['itime'] + ftime = kwargs['ftime'] else: - print("Set convention for date format: DM or MD.") - elif usage == 'dir': - # Generates string for directory names - # Needs strlen which sets smallest scope of time for string - if not strlen: - print("No timescope strlen set; using hour as minimum.") - strlen = 'hour' - n = lookup_time(strlen) - str = "{0:04d}".format(t[0]) + ''.join( - ["{0:02d}".format(a) for a in t[1:n+1]]) + pass else: - print("Usage for string not valid.") raise Exception - return str + elif isinstance(t,int): + # In this case, time is in datenum. Get it into tuple format. + t = time.gmtime(t) + else: + pass + + if usage == 'title': + # Generates string for titles + if not itime: # i.e. for specific times + strg = '{3:02d}:{4:02d}Z on {2:02d}/{1:02d}/{0:04d}'.format(*t) + else: # i.e. for ranges (average over time) + s1 = '{3:02d}:{4:02d}Z to '.format(*itime) + s2 = '{3:02d}:{4:02d}Z'.format(*ftime) + strg = s1 + s2 + elif usage == 'wrfout': + # Generates string for wrfout file finding + # Needs dom + if not dom: + print("No domain specified; using domain #1.") + dom = 1 + strg = ('wrfout_d0' + str(dom) + + '{0:04d}-{1:02d}-{2:02d}_{3:02d}:{4:02d}:{5:02d}'.format(*t)) + elif usage == 'output': + if not conven: + # No convention set, assume DD/MM (I'm biased) + conven = 'full' + # Generates string for output file creation + if conven == 'DM': + strg = '{2:02d}{1:02d}_{3:02d}{4:02d}'.format(*t) + elif conven == 'MD': + strg = '{1:02d}{2:02d}_{3:02d}{4:02d}'.format(*t) + elif conven == 'full': + strg = '{0:04d}{1:02d}{2:02d}{3:02d}{4:02d}'.format(*t) + else: + print("Set convention for date format: DM or MD.") + elif usage == 'dir': + # Generates string for directory names + # Needs strlen which sets smallest scope of time for string + if not strlen: + print("No timescope strlen set; using hour as minimum.") + strlen = 'hour' + n = lookup_time(strlen) + strg = "{0:04d}".format(t[0]) + ''.join( + ["{0:02d}".format(a) for a in t[1:n+1]]) + elif usage == 'skip': + strg = t + else: + print("Usage for string not valid.") + raise Exception + return strg def lookup_time(str): D = {'year':0, 'month':1, 'day':2, 'hour':3, 'minute':4, 'second':5} From bf04a647fe2762d962530795292db09a6b3fb299 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 13 Mar 2014 16:17:56 -0500 Subject: [PATCH 070/111] Fixed dictionary variable lookup --- postWRF/postWRF/__init__.py | 1 + postWRF/postWRF/scales.py | 2 +- utils/utils.py | 19 +++++++++++-------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index d17fcfb..9b97f9e 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -157,6 +157,7 @@ def plot_2D(self,dic): F = BirdsEye(self.C,self.W) for t in Dic[va]['pt']: + #pdb.set_trace() disp_t = utils.string_from_time('title',t,**Dic[va]) print("Plotting {0} at lv {1} for time {2}.".format(va,lv,disp_t)) Dic[va]['pt'] = t # Need this? diff --git a/postWRF/postWRF/scales.py b/postWRF/postWRF/scales.py index d0823f9..4bda819 100644 --- a/postWRF/postWRF/scales.py +++ b/postWRF/postWRF/scales.py @@ -32,7 +32,7 @@ def get_cm(va,**kwargs): # Variable and vertical level determine contour scale # pdb.set_trace() - if kwargs['range']: # Custom range set by user + if hasattr(kwargs,'range'): # Custom range set by user clvs = N.arange(*kwargs['range']) else: try: diff --git a/utils/utils.py b/utils/utils.py index 27af857..b5e3bc2 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -2,6 +2,7 @@ import os import time import calendar +import pdb """ A collection of useful utilities. """ @@ -26,11 +27,12 @@ def string_from_time(usage,t,dom=0,strlen=0,conven=0,**kwargs): if usage == 'output': usage = 'skip' # Time is already a string elif usage == 'title': - if kwargs['itime']: # For averages or maxima over time - itime = kwargs['itime'] - ftime = kwargs['ftime'] - else: - pass + pass + # if kwargs['itime']: # For averages or maxima over time + # itime = kwargs['itime'] + # ftime = kwargs['ftime'] + # else: + # pass else: raise Exception elif isinstance(t,int): @@ -41,11 +43,12 @@ def string_from_time(usage,t,dom=0,strlen=0,conven=0,**kwargs): if usage == 'title': # Generates string for titles - if not itime: # i.e. for specific times + if not 'itime' in kwargs: # i.e. for specific times + #if not hasattr(kwargs,'itime'): # i.e. for specific times strg = '{3:02d}:{4:02d}Z on {2:02d}/{1:02d}/{0:04d}'.format(*t) else: # i.e. for ranges (average over time) - s1 = '{3:02d}:{4:02d}Z to '.format(*itime) - s2 = '{3:02d}:{4:02d}Z'.format(*ftime) + s1 = '{3:02d}:{4:02d}Z to '.format(*kwargs['itime']) + s2 = '{3:02d}:{4:02d}Z'.format(*kwargs['ftime']) strg = s1 + s2 elif usage == 'wrfout': # Generates string for wrfout file finding From 829868966714bf01bc5de0f97e920359aeb6f0f5 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 25 Mar 2014 14:27:13 -0500 Subject: [PATCH 071/111] Get RUC data methods --- lazyWRF/lazyWRF/._getdata.py | Bin 0 -> 4096 bytes lazyWRF/lazyWRF/getdata.py | 166 +++++++++++++ lazyWRF/lazyWRF/getgefs.py | 4 +- lazyWRF/lazyWRF/getruc.csh | 13 + lazyWRF/lazyWRF/getruc2.csh | 13 + postWRF/bin/ensemble.txt | 455 +++++++++++++++++++++++++++++++++++ 6 files changed, 649 insertions(+), 2 deletions(-) create mode 100644 lazyWRF/lazyWRF/._getdata.py create mode 100644 lazyWRF/lazyWRF/getdata.py create mode 100755 lazyWRF/lazyWRF/getruc.csh create mode 100755 lazyWRF/lazyWRF/getruc2.csh create mode 100644 postWRF/bin/ensemble.txt diff --git a/lazyWRF/lazyWRF/._getdata.py b/lazyWRF/lazyWRF/._getdata.py new file mode 100644 index 0000000000000000000000000000000000000000..7b2472763e378c6daf4378fec411da3d72e153a1 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xDSx z(ZNtbK>4WjXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDP$dKyfu?{k7|4ZW zWELwFr55Lx7A2=Dq~#Z7D`e)Cq~?`m=I15m4jn|NjF3u<0B# literal 0 HcmV?d00001 diff --git a/lazyWRF/lazyWRF/getdata.py b/lazyWRF/lazyWRF/getdata.py new file mode 100644 index 0000000..a70c113 --- /dev/null +++ b/lazyWRF/lazyWRF/getdata.py @@ -0,0 +1,166 @@ +import os +import calendar +import time +import pdb + +# date format: YYYYMMDD (string) + +def getgefs(dates,download=1,split=1,lowres=0,custom_ens=0,control=1, + coord='latlon'): + """This script downloads all variables for GEFS R2 reforecasts. + All runs are initialised at 0000 UTC. + + Inputs (all optional unless stated): + dates : YYYYMMDD, list of strings (mandatory) + download : whether to download the data + split : whether to split up the data + lowres : whether to download times after T+190 + custom_ens : a custom list of perturbation ensemble members + control : whether to download the control member + coord : latlon/gaussian grid + """ + + # This selected all 10 perturbation ensemble members. Change ens for desired member (or mean/sprd) + if not custom_ens: + ens = ['p' + '%02u' %p for p in range(1,11)] + else: + ens = custom_ens + + if control: + ens.append('c00') + + # Root directory of FTP site + FTP = 'ftp://ftp.cdc.noaa.gov/Projects/Reforecast2/' + + # -nc does not download a renamed multiple copy of file + # --output-document=CATNAME concatenates all files together for the big grib file + # -nd makes sure hierachy isn't downloaded too + + if download: + for d in dates: + for e in ens: + url = os.path.join(FTP, d[0:4], d[0:6], d+'00', e, coord) + fname = '/*' + e + '.grib2' + CATNAME = d + '_' + e + '.grib2' + cmnd = "wget -nc -nd --output-document=" + CATNAME + ' ' + url + fname + os.system(cmnd) + print d, e, " Downloaded." + + # This section will split the data into forecast times for WRF to read + # Using WGRIB2 + # fin : grib2 input file + # fout : smaller grib2 output file with just one forecast time + # timestr : search pattern to find the forecast time + + if split: + for d in dates: + # Convert this date to python time for later conversion + pytime_anl = calendar.timegm((int(d[:4]),int(d[4:6]),int(d[6:8]),0,0,0)) + for e in ens: + fin = ''.join((d,'_',e,'.grib2')) + fprefix = '_'.join((d,e,'f')) + for t in range(0,198,6): + ts = "%03d" %t # Gets files into chron order with padded zeroes + if t==0: + timestr = '":anl:"' + else: + timestr = ''.join(('":(',str(t),' hour fcst):"')) + fout = fprefix + ts + '.grib2' + str1 = ' '.join(('wgrib2',fin,'-match',timestr,'-grib',fout)) + os.system(str1) + +def getgfs(dates,hours): + """ Downloads GFS analysis data. + + Inputs: + dates : List of strings, YYYYMMDD + hours : List of strings, HH + """ + + # If date is before 2007, download grib1. + + for d in dates: + yr_int = int(d[:4]) + for h in hours: + if yr_int > 2006: + os.system('wget "http://nomads.ncdc.noaa.gov/data/gfsanl/'+d[:6]+'/'+ d+'/gfsanl_4_'+d+'_'+h+'00_000.grb2"') + else: + os.system('wget "http://nomads.ncdc.noaa.gov/data/gfsanl/'+d[:6]+'/'+ d+'/gfsanl_3_'+d+'_'+h+'00_000.grb"') + +def getnam(dates,hours,datatype,**kwargs): + """ Downloads NAM analysis and forecast data. + + Inputs: + dates : List of strings, YYYYMMDD + hours : List of strings, HH + datatype : analysis or forecast. + + Optional arguments for forecasts via kwargs: + tmax : maximum forecast time to download (inclusive) + tint : internal (hr) between fetched forecasts + + """ + + # If date is before ####, download grib1, use this: + age = 'old' + + def get_anl(dates,hours,*args): + for d in dates: + for h in hours: + #if age=='new': + # command = ('wget "http://nomads.ncdc.noaa.gov/data/namanl/'+ + # d[:6]+'/'+ d+'/namanl_4_'+d+'_'+h+'00_000.grb2"') + #elif age=='old': + command = ('wget "http://nomads.ncdc.noaa.gov/data/namanl/'+ + d[:6]+'/'+ d+'/namanl_218_'+d+'_'+h+'00_000.grb"') + os.system(command) + + # Where are these forecast archives? + def get_218fcst(dates,hours,Tmax,Tint): + for d in dates: + for h in hours: + fhs = range(0,Tmax+Tint,Tint) + for fh in fhs: + fpad = "%03d" %fh + if age == 'old': + command = ('wget "http://nomads.ncdc.noaa.gov/data/nam/'+ + d[:6]+'/'+ d+'/nam_218_'+d+'_'+h+'00_'+fpad+'.grb"') + elif age == 'new': # doesn't seem to work + command = ('wget "http://nomads.ncdc.noaa.gov/data/nam/'+ + d[:6]+'/'+ d+'/nam_4_'+d+'_'+h+'00_'+fpad+'.grb2"') + + os.system(command) + + CMND = {'forecast':get_218fcst, 'analysis':get_anl} + CMND[data](dates,hours,Tmax,Tint) + +def getruc(dates,hours): + + def right_url(d,h): + """To work out what URL to use for RUC/RAP data. + """ + yr = int(d[:4]) + mth = int(d[4:6]) + + + if (yr > 2012) and (mth > 3): # With a massive gap for RAP + URL_base = "http://nomads.ncdc.noaa.gov/data/rap130/" + URL = '/'.join((URL_base,d[:6],d,'rap_130_'+d+'_'+h+'00_000.grb2')) + elif (yr > 2007) and (mth > 10): # Massive gap after 2012/05 (transition to RAP). + URL_base = "http://nomads.ncdc.noaa.gov/data/rucanl/" + URL = '/'.join((URL_base,d[:6],d,'ruc2anl_130_'+d+'_'+h+'00_000.grb2')) + elif (yr>2006): + URL_base = "http://nomads.ncdc.noaa.gov/data/rucanl/" + URL = '/'.join((URL_base,d[:6],d,'ruc2anl_252_'+d+'_'+h+'00_000.grb')) + elif (yr>2004): + URL_base = "http://nomads.ncdc.noaa.gov/data/rucanl/" + URL = '/'.join((URL_base,d[:6],d,'ruc2_252_'+d+'_'+h+'00_000.grb')) + + return URL + + + for d in dates: + for h in hours: + URL = right_url(d,h) + command = 'wget ' + URL + os.system(command) diff --git a/lazyWRF/lazyWRF/getgefs.py b/lazyWRF/lazyWRF/getgefs.py index 9d7f0c8..d83f643 100644 --- a/lazyWRF/lazyWRF/getgefs.py +++ b/lazyWRF/lazyWRF/getgefs.py @@ -1,5 +1,5 @@ -# This script downloads all variables for GEFS R2 reforecasts -# John Lawson, University of Utah +"""This script downloads all variables for GEFS R2 reforecasts +""" import os import calendar import time diff --git a/lazyWRF/lazyWRF/getruc.csh b/lazyWRF/lazyWRF/getruc.csh new file mode 100755 index 0000000..a952226 --- /dev/null +++ b/lazyWRF/lazyWRF/getruc.csh @@ -0,0 +1,13 @@ +#!/bin/csh + +set echo + +set dates = (20110126 20110127 20110128 20110129 20110130 20110131) + +foreach date ($dates) + + foreach hr (00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23) + wget "http://nomads.ncdc.noaa.gov/data/rucanl/201101/${date}/ruc2anl_130_${date}_${hr}00_000.grb2" + +end +end diff --git a/lazyWRF/lazyWRF/getruc2.csh b/lazyWRF/lazyWRF/getruc2.csh new file mode 100755 index 0000000..0a1abf2 --- /dev/null +++ b/lazyWRF/lazyWRF/getruc2.csh @@ -0,0 +1,13 @@ +#!/bin/csh + +set echo + +set dates = (20110126 20110127 20110128 20110129 20110130 20110131) + +foreach date ($dates) + + foreach hr (00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23) + wget "http://nomads.ncdc.noaa.gov/data/ruc/201101/${date}/ruc2_252_${date}_${hr}00_000.grb" + +end +end diff --git a/postWRF/bin/ensemble.txt b/postWRF/bin/ensemble.txt new file mode 100644 index 0000000..1280ece --- /dev/null +++ b/postWRF/bin/ensemble.txt @@ -0,0 +1,455 @@ + +ENSEMBLE MOS FORECASTS +

MOS FORECASTS

+
+Control
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  74| 43  74| 40  76| 43  77| 43  76| 44  65| 45  68| 46  66 43 64
+ TMP  73| 46  72| 45  75| 47  77| 46  74| 47  63| 47  66| 48  65      
+ DPT  35| 33  32| 31  27| 32  30| 36  36| 40  42| 43  40| 42  39      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  OV| OV  OV| PC  PC      
+ P12   2|  3   3| 12   3|  2   0|  7   9| 23  41| 52  31| 21  16999999
+ P24    |      7|     12|      3|     13|     47|     56|     35   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  2    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 1
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  75| 42  75| 41  76| 43  78| 43  72| 45  63| 46  67| 46  67 43 64
+ TMP  73| 46  73| 45  75| 47  77| 46  71| 48  61| 48  65| 49  66      
+ DPT  35| 34  33| 33  30| 34  33| 36  38| 42  42| 43  39| 42  40      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| OV  OV| OV  OV| PC  PC      
+ P12   1|  3   5| 11   3|  2   0|  7  13| 42  41| 51  21| 16  18999999
+ P24    |      9|     11|      3|     15|     61|     51|     30   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   1|  2    |             
+ Q24    |      0|      0|      0|      0|      3|       |             
+ PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 2
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  75| 43  76| 41  76| 43  79| 45  79| 45  72| 46  70| 47  67 43 64
+ TMP  73| 47  74| 45  76| 47  78| 49  77| 48  70| 49  68| 50  65      
+ DPT  34| 30  27| 28  25| 34  30| 36  37| 39  42| 43  42| 43  40      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| OV  OV| PC  PC      
+ P12   1|  1   3|  8   3|  3   0|  7   8| 14  27| 36  35| 25  18999999
+ P24    |      5|      8|      3|     12|     30|     42|     42   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   0|  2   2|  3   1|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   1|  0   2|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   2|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 3
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  74| 43  73| 40  75| 42  77| 43  76| 44  72| 46  70| 46  65 43 64
+ TMP  73| 46  71| 44  74| 46  76| 46  75| 46  71| 48  68| 48  64      
+ DPT  35| 35  34| 33  28| 31  29| 35  35| 38  40| 42  39| 41  41      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| PC  PC| OV  PC      
+ P12   2|  2  11| 27   5|  1   0|  7   8| 14  23| 20  12| 36  29999999
+ P24    |     15|     27|      3|     13|     28|     28|     45   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  1   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 4
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  74| 42  75| 41  77| 44  77| 43  73| 46  62| 49  67| 46  68 43 64
+ TMP  72| 46  74| 45  76| 47  76| 46  71| 49  61| 51  65| 49  66      
+ DPT  35| 34  31| 32  29| 35  33| 38  39| 41  43| 47  41| 42  41      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| OV  OV| OV  OV| PC  PC      
+ P12   2|  2   4|  7   4|  3   0|  7  18| 51  45| 57  34| 25  23999999
+ P24    |      7|      7|      4|     23|     60|     58|     35   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  1   2|  2    |             
+ Q24    |      0|      0|      0|      0|      3|       |             
+ PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   1|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 5
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  75| 43  74| 40  75| 42  77| 44  77| 44  68| 46  68| 47  66 43 64
+ TMP  73| 47  72| 44  74| 46  76| 47  75| 47  67| 48  66| 50  64      
+ DPT  33| 31  30| 31  25| 30  29| 35  36| 38  40| 43  40| 44  41      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| OV  OV| OV  OV      
+ P12   1|  3   3| 10   3|  2   0|  7  10| 17  32| 38  28| 33  22999999
+ P24    |      7|     10|      3|     13|     39|     45|     45   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   0|  1   1|  1   1|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   1|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 6
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  75| 43  74| 40  75| 43  78| 42  76| 44  65| 45  68| 46  70 43 64
+ TMP  74| 47  73| 45  74| 47  77| 46  75| 47  63| 47  67| 49  69      
+ DPT  34| 33  32| 31  26| 33  31| 36  36| 40  41| 43  39| 40  38      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  OV| OV  OV| PC  CL      
+ P12   2|  2   3| 16   3|  3   0|  7  10| 31  42| 43  27| 15  15999999
+ P24    |      7|     16|      3|     13|     56|     45|     28   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   1|  1    |             
+ Q24    |      0|      0|      0|      0|      2|       |             
+ PZP   0|  0   0|  1   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 7
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  75| 43  74| 40  75| 43  76| 44  74| 46  62| 47  64| 46  66 43 64
+ TMP  74| 47  72| 44  74| 47  75| 48  72| 49  60| 49  62| 48  65      
+ DPT  34| 31  30| 30  27| 33  31| 37  39| 40  43| 46  41| 43  41      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| OV  OV| OV  OV| OV  OV      
+ P12   1|  3   3|  9   3|  2   0|  7  16| 38  44| 62  36| 29  24999999
+ P24    |      7|      9|      3|     19|     64|     66|     42   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   3|  2    |             
+ Q24    |      0|      0|      0|      0|      3|       |             
+ PZP   0|  1   1|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   0|  2   3|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 8
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  74| 43  75| 41  76| 44  76| 43  73| 44  64| 46  64| 47  66 43 64
+ TMP  73| 46  73| 45  75| 47  75| 46  71| 47  63| 49  63| 50  64      
+ DPT  34| 33  32| 31  27| 35  32| 38  38| 40  42| 44  44| 44  42      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| OV  OV| OV  OV| OV  PC      
+ P12   1|  4   4| 14   3|  3   0|  7  13| 43  39| 48  38| 38  24999999
+ P24    |      9|     14|      3|     16|     62|     55|     50   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
+ Q24    |      0|      0|      0|      0|      2|       |             
+ PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   2|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 9
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  74| 43  76| 41  76| 43  77| 43  74| 45  65| 47  67| 46  67 43 64
+ TMP  73| 46  74| 46  76| 47  76| 47  73| 48  64| 49  65| 49  65      
+ DPT  36| 34  31| 30  26| 33  31| 38  39| 41  43| 43  40| 43  41      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  OV| OV  OV| PC  PC      
+ P12   2|  3   4|  8   3|  3   0|  7  10| 33  37| 46  28| 29  24999999
+ P24    |      9|      8|      3|     13|     54|     51|     43   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
+ Q24    |      0|      0|      0|      0|      1|       |             
+ PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  1   0|  0   0|  0   3|  1   2|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 10
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  74| 42  75| 40  76| 43  78| 44  77| 44  71| 46  69| 47  67 43 64
+ TMP  72| 46  73| 45  75| 47  78| 48  76| 47  69| 49  68| 50  66      
+ DPT  36| 33  32| 32  30| 34  30| 34  36| 39  42| 43  39| 41  39      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| OV  OV| PC  PC      
+ P12   2|  1   3|  9   4|  4   0|  7   9| 14  30| 45  32| 20  17999999
+ P24    |      6|      9|      4|     12|     34|     45|     34   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  2    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  1   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 11
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  75| 43  75| 41  75| 43  76| 44  73| 46  66| 47  67| 49  67 43 64
+ TMP  74| 47  74| 46  74| 46  75| 47  72| 49  64| 49  65| 52  66      
+ DPT  33| 31  30| 31  28| 35  31| 38  39| 41  44| 44  42| 44  40      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  PC| PC  OV| OV  PC| PC  PC      
+ P12   0|  3   3|  8   4|  2   0|  7  12| 34  41| 55  45| 23  16999999
+ P24    |      7|      8|      3|     19|     56|     61|     41   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   1|  2    |             
+ Q24    |      0|      0|      0|      0|      1|       |             
+ PZP   0|  1   1|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   1|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 12
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  75| 43  76| 42  75| 42  76| 43  75| 45  67| 46  66| 47  65 43 64
+ TMP  74| 47  74| 47  74| 45  76| 47  74| 47  65| 48  65| 49  63      
+ DPT  34| 32  31| 30  28| 37  33| 38  38| 40  42| 43  42| 43  41      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  OV| OV  OV| OV  OV      
+ P12   1|  3   3|  9   3|  3   0|  7   9| 25  34| 43  35| 32  28999999
+ P24    |      7|      9|      3|     14|     46|     51|     45   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   0|  0   0|  3   1|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   2|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 13
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  74| 41  75| 40  76| 43  77| 43  74| 46  65| 47  68| 48  66 43 64
+ TMP  73| 45  73| 44  75| 47  76| 46  72| 49  64| 49  66| 50  65      
+ DPT  35| 34  31| 32  30| 35  32| 37  38| 41  45| 45  41| 44  41      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| OV  OV| OV  OV| PC  PC      
+ P12   2|  2   3| 10   4|  3   0|  7  15| 38  39| 44  35| 32  25999999
+ P24    |      7|     10|      4|     23|     61|     53|     44   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
+ Q24    |      0|      0|      0|      0|      2|       |             
+ PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 14
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  75| 43  75| 41  75| 43  78| 45  77| 45  69| 45  65| 46  64 43 64
+ TMP  73| 46  73| 45  74| 46  77| 48  76| 47  67| 47  63| 48  62      
+ DPT  33| 30  28| 29  26| 32  30| 36  38| 39  41| 42  40| 44  41      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| OV  OV| OV  PC      
+ P12   2|  2   3|  9   4|  2   0|  7   8| 14  32| 45  35| 39  26999999
+ P24    |      6|      9|      3|     11|     36|     50|     55   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  2    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   0|  2   2|  1   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   1|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 15
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  74| 44  75| 41  76| 43  76| 44  74| 44  69| 45  69| 47  70 43 64
+ TMP  72| 47  73| 45  75| 47  75| 47  73| 47  68| 48  67| 50  68      
+ DPT  36| 34  30| 28  25| 31  28| 36  37| 40  39| 41  39| 43  42      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| PC  PC| PC  PC      
+ P12   2|  4   3|  9   4|  1   0|  7  10| 27  20| 26  20| 26  21999999
+ P24    |      8|      9|      3|     11|     35|     30|     38   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 16
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  74| 42  73| 39  75| 42  75| 44  75| 44  69| 47  63| 46  62 43 64
+ TMP  73| 46  71| 43  74| 45  74| 47  74| 47  67| 49  61| 48  60      
+ DPT  34| 31  31| 32  27| 31  28| 34  36| 39  41| 43  43| 43  42      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  OV| OV  OV| OV  OV      
+ P12   1|  2   3| 13   4|  3   0|  7   9| 14  29| 50  41| 46  31999999
+ P24    |      6|     13|      3|     11|     32|     59|     58   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  2    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   0|  2   2|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 17
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  75| 43  75| 41  75| 43  79| 43  78| 45  70| 47  70| 48  68 43 64
+ TMP  73| 47  74| 46  74| 47  78| 47  77| 48  68| 49  69| 50  67      
+ DPT  34| 31  29| 30  27| 36  31| 36  36| 40  42| 44  41| 43  42      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| PC  PC| PC  PC      
+ P12   2|  1   3|  9   4|  3   0|  7   9| 14  31| 32  15| 26  21999999
+ P24    |      5|      9|      3|     13|     38|     37|     36   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   0|  1   1|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   1|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 18
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  73| 43  73| 40  76| 44  77| 43  73| 46  69| 49  68| 46  67 43 64
+ TMP  72| 46  72| 44  75| 47  77| 46  71| 49  68| 51  67| 49  65      
+ DPT  36| 36  35| 35  31| 33  31| 37  38| 41  43| 46  42| 43  41      
+ CLD  CL| PC  CL| CL  CL| CL  CL| CL  CL| PC  OV| OV  OV| OV  OV      
+ P12   2|  4   7| 20   4|  2   0|  7  19| 33  35| 40  28| 31  24999999
+ P24    |     13|     20|      3|     29|     45|     51|     42   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  1   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 19
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  75| 42  74| 40  75| 43  75| 43  68| 45  65| 47  67| 47  68 43 64
+ TMP  73| 45  72| 45  74| 46  74| 46  67| 48  64| 49  66| 50  67      
+ DPT  35| 35  33| 33  30| 35  33| 39  41| 42  43| 45  40| 42  41      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  OV| OV  OV| PC  PC| PC  PC      
+ P12   1|  4   6| 20   3|  3   0|  7  22| 52  37| 37  17| 24  23999999
+ P24    |     11|     20|      4|     31|     61|     42|     30   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  1   0|  0    |             
+ Q24    |      0|      0|      0|      0|      2|       |             
+ PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  1   0|  0   0|  0   3|  1   1|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Perturbation 20
+ KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  75| 42  75| 41  77| 44  78| 44  76| 44  68| 47  68| 48  66 43 64
+ TMP  74| 45  73| 45  76| 48  77| 47  75| 47  67| 49  66| 50  64      
+ DPT  35| 33  32| 33  30| 32  29| 35  36| 39  41| 44  42| 44  42      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| OV  OV| OV  OV      
+ P12   2|  1   3|  8   4|  2   0|  7  10| 26  30| 38  30| 34  29999999
+ P24    |      5|      8|      3|     13|     46|     47|     46   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
+ PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+                                                                      
+
+
+
+Operational
+ KRDD   GFSX MOS GUIDANCE   3/20/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
+ X/N  75| 41  77| 41  76| 39  76| 40  80| 41  67| 45  64| 42  63 43 66
+ TMP  73| 45  75| 45  75| 42  75| 43  78| 45  65| 48  62| 45  62      
+ DPT  32| 32  27| 29  24| 30  32| 36  32| 35  36| 37  40| 35  35      
+ CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| CL  PC| OV  OV| PC  PC      
+ WND   6|  6  10|  7   9|  5   5|  4   7|  7  18| 18  17| 12  10      
+ P12   2|  2   4|  6   1|  3   4|  2   2| 15  31| 37  30| 27  13999999
+ P24    |      4|      6|      4|      2|     37|     61|     29   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
+ Q24    |      0|      0|      0|      0|      1|       |             
+ T12   1|  0   2|  0   1|  0   1|  1   3|  4   4|  5   6|  3   4      
+ T24    |  1    |  2    |  1    |  1    |  4    | 11    |  8          
+ PZP   0|  1   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
+ PSN   0|  6   0| 13   0|  9   1|  7   0|  2   0|  0   1|  1   6      
+ PRS   2| 14   2|  1   2|  3   1|  4   1|  2   4|  6   5|  9   6      
+ TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ SNW    |      0|      0|      0|      0|      0|       |             
+                                                                      
+
+
+

+ +* To view other MOS products +

+ * +Go back to the Statistical Modeling Branch HomePage +



+ From b26e52ee4acdfb3699ca0ad07605927ba82f8b20 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 31 Mar 2014 11:35:07 -0500 Subject: [PATCH 072/111] Blah --- ensemble.txt | 455 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 455 insertions(+) create mode 100644 ensemble.txt diff --git a/ensemble.txt b/ensemble.txt new file mode 100644 index 0000000..d17cf70 --- /dev/null +++ b/ensemble.txt @@ -0,0 +1,455 @@ + +ENSEMBLE MOS FORECASTS +

MOS FORECASTS

+
+Control
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  3  30| 14  25| 10  28| 17  44| 26  41| 16  33| 13  30 23 41
+ TMP  15|  6  27| 15  21| 11  25| 20  41| 28  37| 18  30| 15  28      
+ DPT   1| -1  14| 10   9|  6  11| 16  28| 24  27| 14  18| 10  16      
+ CLD  PC| PC  OV| OV  OV| PC  PC| PC  PC| OV  OV| OV  PC| PC  PC      
+ P12   4|  0  13| 26  10|  8  10| 11  16| 14  21| 25  14| 12  14999999
+ P24    |     21|     26|     12|     28|     33|     25|     17   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   9|  3  14| 11  16|  2  14| 11  29| 15  27| 10  25| 16  26      
+ PSN  91| 97  85| 83  84| 96  85| 85  46| 24  36| 67  70| 68  64      
+ PRS   0|  0   1|  6   0|  0   0|  0   1| 13   6| 11   2|  2   0      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 1
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  4  29| 12  25| 11  31| 20  46| 25  38| 14  31| 14  35 23 41
+ TMP  15|  7  27| 14  21| 12  28| 22  43| 27  34| 16  29| 17  32      
+ DPT   1| -1  13|  9   8|  8  15| 19  30| 23  24| 12  17| 12  21      
+ CLD  PC| PC  OV| OV  OV| PC  PC| PC  OV| PC  OV| OV  PC| OV  PC      
+ P12   3|  0  18| 17   7|  6  10| 18  12| 16  15| 19  13| 11  16999999
+ P24    |     24|     17|     15|     22|     25|     21|     17   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   9|  4  15|  8  14|  2  15| 14  31| 18  29| 14  25| 19  27      
+ PSN  91| 96  83| 87  86| 92  84| 76  43| 28  50| 59  65| 68  58      
+ PRS   0|  0   3|  5   0|  0   2|  2   2| 14   5| 11   1|  0   1      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  Z   Z      
+                                                                      
+
+
+
+Perturbation 2
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  1  29| 14  26| 13  32| 24  44| 23  42| 21  31|  9  30 23 41
+ TMP  14|  5  27| 16  22| 14  30| 27  41| 25  37| 23  29| 11  26      
+ DPT   1| -3  13| 11  10| 10  18| 23  30| 21  25| 20  21|  6  10      
+ CLD  PC| PC  OV| OV  OV| OV  OV| PC  OV| PC  PC| OV  OV| OV  CL      
+ P12   3|  0  18| 23  12| 11  16| 23  17| 16  10| 25  27| 11   8999999
+ P24    |     23|     23|     17|     39|     22|     37|     15   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      1|      0|       |             
+ PZP   9|  3  13|  6  14|  3  16| 14  26| 15  26| 15  29|  8  25      
+ PSN  91| 97  86| 89  86| 95  78| 53  38| 35  48| 36  58| 80  66      
+ PRS   0|  0   1|  5   0|  0   0|  1   5| 13   5|  8   5|  2   0      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 3
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  4  29| 12  24| 11  32| 21  48| 28  39| 16  28| 10  34 23 41
+ TMP  15|  7  27| 13  20| 13  29| 24  44| 30  35| 18  26| 12  31      
+ DPT   1|  0  13|  8   7|  8  17| 20  32| 26  27| 13  15|  7  17      
+ CLD  PC| PC  OV| OV  OV| PC  PC| PC  PC| OV  OV| OV  PC| PC  CL      
+ P12   4|  0  25| 11   7|  4  11| 16   9| 15  29| 30  14| 11   8999999
+ P24    |     35|     16|     11|     19|     37|     30|     15   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   1|  1    |             
+ Q24    |      0|      0|      0|      0|      1|       |             
+ PZP   9|  3  13|  7  13|  3  14| 18  36| 13  29| 12  25|  8  26      
+ PSN  91| 97  85| 88  87| 96  85| 65  37| 22  38| 71  74| 81  64      
+ PRS   0|  0   2|  4   0|  0   1|  0   0| 14   8| 11   1|  2   1      
+ TYP   S|  S   S|  S   S|  S   S|  Z   Z| RS   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 4
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  17|  3  31| 12  24| 11  31| 20  43| 22  36| 13  29| 12  38 23 41
+ TMP  15|  7  28| 13  20| 12  28| 22  40| 24  32| 15  27| 14  35      
+ DPT   1| -1  14|  8   7|  8  15| 19  28| 19  22| 11  16| 10  23      
+ CLD  PC| PC  OV| OV  PC| OV  OV| OV  PC| OV  OV| OV  PC| PC  PC      
+ P12   3|  0  14| 17   5|  7  16| 21  17| 10  20| 13  15| 11  12999999
+ P24    |     14|     17|     18|     31|     31|     21|     16   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   9|  4  15|  9  13|  3  14| 15  32| 17  29| 16  29| 16  26      
+ PSN  91| 96  83| 86  87| 97  85| 73  45| 36  64| 69  68| 72  56      
+ PRS   0|  0   2|  6   0|  0   1|  0   4| 14   2|  7   0|  0   2      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 5
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  3  32| 17  28| 12  31| 21  46| 28  44| 24  36| 16  32 23 41
+ TMP  15|  6  30| 18  23| 14  28| 23  43| 30  40| 26  33| 18  29      
+ DPT   1| -1  15| 14  12|  9  16| 20  31| 26  30| 22  23| 14  17      
+ CLD  CL| PC  OV| OV  OV| PC  PC| PC  OV| PC  OV| OV  PC| OV  PC      
+ P12   3|  0  10| 25  11|  7  11| 15  10| 16  15| 25  15| 11  12999999
+ P24    |     10|     25|     14|     20|     26|     29|     22   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   9|  3  14| 14  20|  2  15| 17  36| 15  24| 11  26|  9  26      
+ PSN  91| 97  84| 78  79| 93  83| 70  42| 23  31| 48  56| 70  65      
+ PRS   0|  0   2|  8   1|  0   2|  0   0| 15   4| 15   8|  6   2      
+ TYP   S|  S   S|  S   S|  S   S|  Z   Z| RS   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 6
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  3  28| 12  24|  9  28| 17  41| 20  37| 16  36| 13  38 23 41
+ TMP  14|  6  25| 13  20| 10  25| 20  38| 22  33| 18  33| 15  34      
+ DPT   1| -1  12|  8   8|  6  11| 16  25| 17  23| 14  21| 10  19      
+ CLD  PC| PC  OV| OV  OV| PC  PC| PC  OV| OV  OV| OV  PC| PC  PC      
+ P12   3|  0  15| 40  10|  7  10| 15  17| 14  19| 18  19| 11   8999999
+ P24    |     18|     40|     17|     30|     29|     25|     14   999
+ Q12   0|  0   0|  1   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   9|  3  13|  7  13|  3  14| 13  32| 19  33| 14  25| 10  25      
+ PSN  91| 97  86| 91  87| 97  86| 73  47| 41  58| 72  69| 80  69      
+ PRS   0|  0   1|  3   0|  0   0|  0   2| 12   3|  5   0|  1   0      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 7
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  3  28| 13  25|  8  24| 12  39| 22  37| 14  30| 11  25 23 41
+ TMP  14|  6  26| 14  21|  9  21| 14  36| 24  33| 17  27| 13  22      
+ DPT   1| -1  12| 10   9|  4   7| 10  22| 19  23| 12  15|  8  11      
+ CLD  PC| PC  OV| OV  OV| OV  PC| CL  PC| PC  OV| OV  PC| OV  PC      
+ P12   5|  0  16| 15   9|  9   9|  7   9| 18  14| 18  17| 12  15999999
+ P24    |     25|     17|     10|     15|     23|     26|     21   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   9|  3  13|  8  16|  3  14|  5  21| 12  27| 10  25|  9  26      
+ PSN  91| 97  87| 89  84| 97  86| 95  63| 43  58| 78  72| 77  69      
+ PRS   0|  0   1|  3   0|  0   0|  0   4|  9   6|  5   1|  3   0      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z|  S   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 8
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  2  28| 13  25|  9  26| 14  40| 22  37| 14  35| 14  36 23 41
+ TMP  15|  5  25| 14  20| 10  23| 17  37| 24  33| 16  32| 16  33      
+ DPT   1| -2  11|  9   9|  6  10| 13  25| 19  23| 12  19| 11  20      
+ CLD  PC| PC  OV| OV  OV| OV  PC| PC  PC| OV  OV| OV  PC| PC  PC      
+ P12   4|  0  13| 27  32| 18   7|  9  18| 17  21| 19  13| 11   8999999
+ P24    |     13|     43|     18|     28|     34|     19|     14   999
+ Q12   0|  0   0|  0   1|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      0|      0|      0|       |             
+ PZP   9|  3  12| 11  19|  4  14| 11  29| 19  29|  8  24|  8  25      
+ PSN  91| 97  87| 84  81| 97  87| 87  57| 35  66| 82  73| 81  70      
+ PRS   0|  0   1|  5   1|  0   0|  0   0| 11   4|  5   0|  2   0      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 9
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  4  32| 16  25|  9  28| 16  41| 23  35| 14  29| 11  34 23 41
+ TMP  15|  7  30| 17  21| 10  25| 19  39| 25  32| 16  27| 13  31      
+ DPT   1|  0  16| 13  10|  5  12| 16  27| 20  22| 12  17|  8  17      
+ CLD  PC| PC  OV| OV  OV| PC  PC| PC  OV| OV  OV| OV  OV| PC  PC      
+ P12   3|  0  11| 50  27|  7  11| 16  16| 18  14| 25  19| 11   8999999
+ P24    |     11|     50|     13|     30|     25|     28|     15   999
+ Q12   0|  0   0|  1   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      0|      0|      0|       |             
+ PZP   9|  4  15| 13  18|  2  14| 13  32| 19  31| 16  27|  8  25      
+ PSN  91| 96  85| 77  81| 95  86| 83  50| 36  60| 74  72| 81  67      
+ PRS   0|  0   0| 10   1|  0   0|  0   1| 13   4|  7   1|  2   0      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 10
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  2  31| 16  27| 12  30| 18  45| 26  50| 29  41| 17  36 23 41
+ TMP  15|  6  29| 18  23| 13  26| 21  42| 29  46| 31  38| 19  32      
+ DPT   1| -2  15| 13  12|  8  13| 17  29| 24  35| 28  29| 15  16      
+ CLD  PC| PC  OV| OV  OV| PC  PC| PC  PC| PC  PC| OV  OV| PC  CL      
+ P12   4|  0  11| 23  23| 10   8| 12   8| 16   8| 18  23| 11   8999999
+ P24    |     11|     39|     11|     17|     22|     34|     16   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      0|      0|      0|       |             
+ PZP   9|  3  13| 18  24|  3  14| 14  33| 14  23| 14  29| 10  26      
+ PSN  91| 97  85| 76  74| 95  86| 73  41| 25  35| 37  52| 72  57      
+ PRS   0|  0   2|  6   2|  0   0|  0   0| 14   5| 12   9|  6   2      
+ TYP   S|  S   S|  S   Z|  S   S|  S   Z| RS   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 11
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  1  29| 13  25| 10  26| 15  38| 21  38| 10  30| 11  31 23 41
+ TMP  14|  5  27| 14  20| 11  23| 18  35| 23  33| 12  27| 13  28      
+ DPT   0| -3  13| 10   8|  6  12| 14  23| 18  22|  7  14|  8  17      
+ CLD  PC| PC  OV| OV  OV| OV  OV| OV  OV| OV  OV| PC  PC| OV  OV      
+ P12   4|  0  22| 27   9|  7  20| 14  22| 15  28| 18  13| 12  16999999
+ P24    |     26|     27|     27|     37|     34|     25|     20   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   1|  0    |             
+ Q24    |      0|      0|      0|      1|      0|       |             
+ PZP   9|  3  13|  9  13|  3  15|  6  22| 16  29|  9  25| 19  26      
+ PSN  91| 97  86| 86  87| 97  86| 93  64| 44  63| 77  68| 68  65      
+ PRS   0|  0   1|  6   0|  0   0|  0   3| 10   1|  5   0|  0   1      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z|  S   Z|  S   Z|  Z   Z      
+                                                                      
+
+
+
+Perturbation 12
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  3  31| 14  25|  9  29| 15  44| 25  39| 21  34| 20  48 23 41
+ TMP  14|  6  29| 15  21| 10  25| 17  40| 27  36| 22  32| 21  43      
+ DPT   1| -1  14| 11   8|  5  11| 13  25| 23  25| 19  22| 18  30      
+ CLD  PC| PC  OV| OV  OV| PC  PC| CL  PC| PC  OV| OV  OV| OV  PC      
+ P12   4|  0  12| 25   8|  7   9|  6   5| 11  14| 27  21| 11  15999999
+ P24    |     12|     25|      9|     12|     24|     33|     22   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   9|  3  14| 11  16|  3  14|  6  25| 20  33| 15  29|  9  26      
+ PSN  91| 97  84| 83  84| 96  86| 92  56| 30  36| 52  61| 72  63      
+ PRS   0|  0   2|  6   0|  0   0|  0   0|  9   6| 14   5|  5   3      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 13
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  3  31| 16  27| 13  33| 18  45| 24  41| 10  32| 11  32 23 41
+ TMP  15|  7  29| 17  23| 14  29| 20  41| 26  36| 13  28| 13  29      
+ DPT   1| -1  15| 13  11| 10  16| 16  27| 21  23|  8  11|  8  18      
+ CLD  PC| PC  OV| OV  OV| PC  PC| PC  PC| PC  OV| PC  CL| PC  PC      
+ P12   4|  0  12| 21   9|  6  11| 14   7| 11  23| 10  12| 11  13999999
+ P24    |     17|     21|     11|     18|     30|     22|     16   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   9|  3  14| 11  17|  3  16| 10  27| 19  31|  9  24| 12  26      
+ PSN  91| 97  85| 82  83| 91  83| 83  50| 30  48| 73  70| 75  67      
+ PRS   0|  0   2|  7   1|  0   2|  4   1| 10   5|  8   0|  2   1      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 14
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  17|  3  31| 16  26| 10  29| 16  42| 18  34| 13  36| 12  36 23 41
+ TMP  15|  7  29| 17  22| 11  26| 19  38| 20  30| 15  32| 14  33      
+ DPT   1|  0  15| 13  10|  6  12| 15  25| 15  19| 10  19| 10  19      
+ CLD  PC| PC  OV| OV  OV| PC  PC| PC  OV| OV  OV| OV  PC| PC  PC      
+ P12   3|  0  11| 46  29|  5  10| 13  15| 15  13| 10  14| 11   8999999
+ P24    |     14|     46|     12|     26|     25|     19|     15   999
+ Q12   0|  0   0|  1   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      0|      0|      0|       |             
+ PZP   9|  4  15| 13  18|  2  14| 12  31| 17  26|  8  23|  9  25      
+ PSN  91| 96  83| 78  81| 95  85| 76  45| 47  70| 84  69| 80  67      
+ PRS   0|  0   2|  9   1|  0   1|  0   2| 13   1|  2   0|  2   0      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 15
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  3  30| 15  26| 10  27| 19  45| 25  46| 23  38| 14  31 23 41
+ TMP  15|  6  28| 17  21| 11  25| 21  42| 28  42| 25  35| 15  28      
+ DPT   1| -1  14| 12  10|  6  13| 18  29| 23  32| 22  25| 11  13      
+ CLD  PC| PC  OV| OV  OV| PC  OV| OV  OV| OV  PC| OV  OV| PC  CL      
+ P12   3|  0  12| 50  28|  6  14| 17  13| 17  10| 20  18| 11   8999999
+ P24    |     13|     50|     15|     25|     26|     21|     15   999
+ Q12   0|  0   0|  1   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      0|      0|      0|       |             
+ PZP   9|  3  14| 14  19|  3  14| 15  33| 10  25| 14  27|  9  26      
+ PSN  91| 97  84| 77  80| 97  86| 68  43| 33  40| 45  62| 78  64      
+ PRS   0|  0   2|  9   1|  0   0|  0   0| 16  12| 12   2|  3   1      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 16
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  5  30| 12  24| 10  30| 18  47| 26  40| 15  31| 10  32 23 41
+ TMP  15|  8  27| 14  20| 11  27| 21  43| 28  36| 18  28| 12  28      
+ DPT   1|  1  13|  9   8|  7  13| 17  30| 23  26| 13  14|  7  13      
+ CLD  PC| PC  OV| OV  OV| PC  PC| PC  PC| PC  OV| OV  PC| PC  CL      
+ P12   3|  0  19| 17   6|  6  11| 12   6| 10  16| 12  10| 11   8999999
+ P24    |     28|     17|     11|     14|     25|     18|     15   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   9|  4  15|  7  14|  3  15| 10  26| 13  25|  8  24|  9  26      
+ PSN  91| 96  83| 88  86| 95  84| 81  46| 26  56| 78  72| 78  60      
+ PRS   0|  0   2|  4   0|  0   2|  3   1| 13   9|  7   1|  3   2      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 17
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  3  29| 15  26| 11  29| 16  45| 27  45| 27  46| 26  45 23 41
+ TMP  15|  6  26| 16  22| 12  25| 19  42| 29  41| 29  43| 28  42      
+ DPT   2| -1  13| 12  11|  7  12| 15  28| 25  31| 25  31| 24  30      
+ CLD  PC| PC  OV| OV  OV| OV  PC| PC  PC| PC  OV| OV  PC| PC  PC      
+ P12   5|  0  18| 53  30| 12   8|  9   9| 11  19| 13  13| 16   8999999
+ P24    |     29|     53|     12|     17|     27|     22|     19   999
+ Q12   0|  0   0|  1   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      0|      0|      0|       |             
+ PZP   9|  3  13| 13  19|  3  14| 12  32| 14  30| 18  30| 26  28      
+ PSN  91| 97  86| 81  80| 97  86| 74  39| 22  34| 35  33| 36  45      
+ PRS   0|  0   1|  6   1|  0   0|  0   0| 12   5| 15   6|  6   4      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  Z   Z|  Z   Z      
+                                                                      
+
+
+
+Perturbation 18
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  17|  4  30| 14  27| 13  29| 19  45| 27  45| 25  38| 14  29 23 41
+ TMP  15|  8  28| 16  23| 14  27| 22  42| 29  42| 27  35| 16  25      
+ DPT   2|  1  14| 11  11|  9  15| 18  31| 25  33| 24  25| 12  11      
+ CLD  PC| PC  OV| OV  OV| OV  OV| OV  OV| PC  OV| OV  OV| PC  CL      
+ P12   4|  0  15| 12   9|  8  18| 15  11| 14  19| 15  15| 11  10999999
+ P24    |     26|     17|     24|     20|     30|     23|     16   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   9|  3  14|  8  15|  2  16| 15  37| 14  23| 13  28|  9  26      
+ PSN  91| 97  83| 86  85| 95  83| 73  37| 20  31| 43  56| 73  53      
+ PRS   0|  0   3|  4   0|  0   1|  0   1| 14   4| 15   6|  5   4      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 19
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  2  27| 14  25| 11  29| 17  45| 25  45| 21  32| 17  44 23 41
+ TMP  14|  5  25| 15  21| 12  26| 20  42| 27  41| 23  30| 19  40      
+ DPT   1| -2  11| 11  10|  8  13| 17  29| 23  31| 19  18| 15  26      
+ CLD  PC| PC  OV| OV  OV| OV  PC| PC  PC| PC  OV| OV  PC| OV  PC      
+ P12   4|  0  12| 26  45| 22   8| 12   9| 13  13| 23  17| 17  11999999
+ P24    |     12|     53|     22|     16|     26|     26|     25   999
+ Q12   0|  0   0|  0   1|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      0|      0|      0|       |             
+ PZP   9|  3  12| 12  20|  3  14| 13  32| 13  24| 17  33| 14  27      
+ PSN  91| 97  87| 85  80| 97  86| 71  42| 29  48| 47  49| 56  45      
+ PRS   0|  0   1|  3   1|  0   0|  0   1| 15   7| 12   3|  6   2      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  Z   Z|  S   Z      
+                                                                      
+
+
+
+Perturbation 20
+ KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  16|  6  33| 14  25| 11  29| 17  40| 21  34| 10  26|  7  33 23 41
+ TMP  15| 10  30| 15  21| 13  26| 19  37| 23  30| 12  23| 10  29      
+ DPT   1|  2  16| 11   9|  9  16| 16  24| 18  19|  8  13|  4  15      
+ CLD  PC| PC  OV| OV  PC| OV  OV| OV  PC| PC  OV| OV  PC| OV  PC      
+ P12   3|  0  15| 31   5|  7  25| 19  15| 12  21| 33  14| 11   9999999
+ P24    |     18|     31|     34|     32|     27|     33|     14   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ PZP   9|  5  18| 12  13|  3  16|  9  29| 16  29| 12  25|  9  26      
+ PSN  91| 95  80| 80  87| 95  85| 87  62| 43  50| 76  74| 80  64      
+ PRS   0|  0   2|  9   0|  0   0|  0   2| 11   6| 10   1|  2   0      
+ TYP   S|  S   S|  S   S|  S   S|  S   Z|  S   Z|  S   Z|  S   Z      
+                                                                      
+
+
+
+Operational
+ KGFK   GFSX MOS GUIDANCE   3/25/2014  0000 UTC                       
+ FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
+ TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
+ X/N  14|  0  30|  9  21| -2  22| 11  38| 24  37| 19  27| 10  29 23 43
+ TMP   8|  6  27| 12  16|  0  20| 15  37| 26  32| 21  24| 13  27      
+ DPT  -3|  1  15|  7   5| -5   6|  9  25| 21  22| 16  11|  8  15      
+ CLD  CL| PC  OV| OV  PC| PC  PC| PC  PC| PC  OV| OV  OV| PC  OV      
+ WND  12|  7  14| 17  17| 12   9| 10  23| 15  28| 28  25| 15  21      
+ P12   0|  0  10| 26   9|  6  10| 11  19| 16  28| 27  13| 15  19999999
+ P24    |     10|     26|     11|     22|     28|     46|     24   999
+ Q12   0|  0   0|  0   0|  0   0|  0   0|  0   1|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ T12   5|  2   2|  1   1|  1   1|  2   4|  8   4|  5   4|  2   4      
+ T24    |  5    |  2    |  1    |  2    |  8    |  7    |  4          
+ PZP   1|  1   3|  1   1|  1   2|  3  18|  7   8| 12   5|  3   6      
+ PSN  98| 98  93| 93  97| 95  95| 88  45| 18  14| 34  73| 71  75      
+ PRS   0|  0   4|  3   0|  1   1|  4   2| 11  11| 21   4|  4   6      
+ TYP   S|  S   S|  S   S|  S   S|  S   S|  R   R| RS   S|  S   S      
+ SNW    |      0|      0|      0|      0|      0|       |             
+                                                                      
+
+
+

+ +* To view other MOS products +

+ * +Go back to the Statistical Modeling Branch HomePage +



+ From 5ad837ddf4cc85f85719f6a8ee56c5026482c621 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 31 Mar 2014 19:07:05 -0500 Subject: [PATCH 073/111] Added RUC plotting --- lazyWRF/lazyWRF/getdata.py | 296 ++++++++++++++++++------------------- postWRF/bin/settings.py | 2 + postWRF/postWRF/figure.py | 5 +- postWRF/postWRF/rucplot.py | 213 ++++++++++++++++++++++++++ utils/utils.py | 4 +- 5 files changed, 369 insertions(+), 151 deletions(-) create mode 100644 postWRF/postWRF/rucplot.py diff --git a/lazyWRF/lazyWRF/getdata.py b/lazyWRF/lazyWRF/getdata.py index a70c113..b2185de 100644 --- a/lazyWRF/lazyWRF/getdata.py +++ b/lazyWRF/lazyWRF/getdata.py @@ -6,161 +6,161 @@ # date format: YYYYMMDD (string) def getgefs(dates,download=1,split=1,lowres=0,custom_ens=0,control=1, - coord='latlon'): - """This script downloads all variables for GEFS R2 reforecasts. - All runs are initialised at 0000 UTC. - - Inputs (all optional unless stated): - dates : YYYYMMDD, list of strings (mandatory) - download : whether to download the data - split : whether to split up the data - lowres : whether to download times after T+190 - custom_ens : a custom list of perturbation ensemble members - control : whether to download the control member - coord : latlon/gaussian grid - """ - - # This selected all 10 perturbation ensemble members. Change ens for desired member (or mean/sprd) - if not custom_ens: - ens = ['p' + '%02u' %p for p in range(1,11)] - else: - ens = custom_ens - - if control: - ens.append('c00') - - # Root directory of FTP site - FTP = 'ftp://ftp.cdc.noaa.gov/Projects/Reforecast2/' - - # -nc does not download a renamed multiple copy of file - # --output-document=CATNAME concatenates all files together for the big grib file - # -nd makes sure hierachy isn't downloaded too - - if download: - for d in dates: - for e in ens: - url = os.path.join(FTP, d[0:4], d[0:6], d+'00', e, coord) - fname = '/*' + e + '.grib2' - CATNAME = d + '_' + e + '.grib2' - cmnd = "wget -nc -nd --output-document=" + CATNAME + ' ' + url + fname - os.system(cmnd) - print d, e, " Downloaded." - - # This section will split the data into forecast times for WRF to read - # Using WGRIB2 - # fin : grib2 input file - # fout : smaller grib2 output file with just one forecast time - # timestr : search pattern to find the forecast time - - if split: - for d in dates: - # Convert this date to python time for later conversion - pytime_anl = calendar.timegm((int(d[:4]),int(d[4:6]),int(d[6:8]),0,0,0)) - for e in ens: - fin = ''.join((d,'_',e,'.grib2')) - fprefix = '_'.join((d,e,'f')) - for t in range(0,198,6): - ts = "%03d" %t # Gets files into chron order with padded zeroes - if t==0: - timestr = '":anl:"' - else: - timestr = ''.join(('":(',str(t),' hour fcst):"')) - fout = fprefix + ts + '.grib2' - str1 = ' '.join(('wgrib2',fin,'-match',timestr,'-grib',fout)) - os.system(str1) + coord='latlon'): + """This script downloads all variables for GEFS R2 reforecasts. + All runs are initialised at 0000 UTC. + + Inputs (all optional unless stated): + dates : YYYYMMDD, list of strings (mandatory) + download : whether to download the data + split : whether to split up the data + lowres : whether to download times after T+190 + custom_ens : a custom list of perturbation ensemble members + control : whether to download the control member + coord : latlon/gaussian grid + """ + + # This selected all 10 perturbation ensemble members. Change ens for desired member (or mean/sprd) + if not custom_ens: + ens = ['p' + '%02u' %p for p in range(1,11)] + else: + ens = custom_ens + + if control: + ens.append('c00') + + # Root directory of FTP site + FTP = 'ftp://ftp.cdc.noaa.gov/Projects/Reforecast2/' + + # -nc does not download a renamed multiple copy of file + # --output-document=CATNAME concatenates all files together for the big grib file + # -nd makes sure hierachy isn't downloaded too + + if download: + for d in dates: + for e in ens: + url = os.path.join(FTP, d[0:4], d[0:6], d+'00', e, coord) + fname = '/*' + e + '.grib2' + CATNAME = d + '_' + e + '.grib2' + cmnd = "wget -nc -nd --output-document=" + CATNAME + ' ' + url + fname + os.system(cmnd) + print d, e, " Downloaded." + + # This section will split the data into forecast times for WRF to read + # Using WGRIB2 + # fin : grib2 input file + # fout : smaller grib2 output file with just one forecast time + # timestr : search pattern to find the forecast time + + if split: + for d in dates: + # Convert this date to python time for later conversion + pytime_anl = calendar.timegm((int(d[:4]),int(d[4:6]),int(d[6:8]),0,0,0)) + for e in ens: + fin = ''.join((d,'_',e,'.grib2')) + fprefix = '_'.join((d,e,'f')) + for t in range(0,198,6): + ts = "%03d" %t # Gets files into chron order with padded zeroes + if t==0: + timestr = '":anl:"' + else: + timestr = ''.join(('":(',str(t),' hour fcst):"')) + fout = fprefix + ts + '.grib2' + str1 = ' '.join(('wgrib2',fin,'-match',timestr,'-grib',fout)) + os.system(str1) def getgfs(dates,hours): - """ Downloads GFS analysis data. + """ Downloads GFS analysis data. - Inputs: - dates : List of strings, YYYYMMDD - hours : List of strings, HH - """ + Inputs: + dates : List of strings, YYYYMMDD + hours : List of strings, HH + """ - # If date is before 2007, download grib1. + # If date is before 2007, download grib1. - for d in dates: - yr_int = int(d[:4]) - for h in hours: - if yr_int > 2006: - os.system('wget "http://nomads.ncdc.noaa.gov/data/gfsanl/'+d[:6]+'/'+ d+'/gfsanl_4_'+d+'_'+h+'00_000.grb2"') - else: - os.system('wget "http://nomads.ncdc.noaa.gov/data/gfsanl/'+d[:6]+'/'+ d+'/gfsanl_3_'+d+'_'+h+'00_000.grb"') + for d in dates: + yr_int = int(d[:4]) + for h in hours: + if yr_int > 2006: + os.system('wget "http://nomads.ncdc.noaa.gov/data/gfsanl/'+d[:6]+'/'+ d+'/gfsanl_4_'+d+'_'+h+'00_000.grb2"') + else: + os.system('wget "http://nomads.ncdc.noaa.gov/data/gfsanl/'+d[:6]+'/'+ d+'/gfsanl_3_'+d+'_'+h+'00_000.grb"') def getnam(dates,hours,datatype,**kwargs): - """ Downloads NAM analysis and forecast data. - - Inputs: - dates : List of strings, YYYYMMDD - hours : List of strings, HH - datatype : analysis or forecast. - - Optional arguments for forecasts via kwargs: - tmax : maximum forecast time to download (inclusive) - tint : internal (hr) between fetched forecasts - - """ - - # If date is before ####, download grib1, use this: - age = 'old' - - def get_anl(dates,hours,*args): - for d in dates: - for h in hours: - #if age=='new': - # command = ('wget "http://nomads.ncdc.noaa.gov/data/namanl/'+ - # d[:6]+'/'+ d+'/namanl_4_'+d+'_'+h+'00_000.grb2"') - #elif age=='old': - command = ('wget "http://nomads.ncdc.noaa.gov/data/namanl/'+ - d[:6]+'/'+ d+'/namanl_218_'+d+'_'+h+'00_000.grb"') - os.system(command) - - # Where are these forecast archives? - def get_218fcst(dates,hours,Tmax,Tint): - for d in dates: - for h in hours: - fhs = range(0,Tmax+Tint,Tint) - for fh in fhs: - fpad = "%03d" %fh - if age == 'old': - command = ('wget "http://nomads.ncdc.noaa.gov/data/nam/'+ - d[:6]+'/'+ d+'/nam_218_'+d+'_'+h+'00_'+fpad+'.grb"') - elif age == 'new': # doesn't seem to work - command = ('wget "http://nomads.ncdc.noaa.gov/data/nam/'+ - d[:6]+'/'+ d+'/nam_4_'+d+'_'+h+'00_'+fpad+'.grb2"') - - os.system(command) - - CMND = {'forecast':get_218fcst, 'analysis':get_anl} - CMND[data](dates,hours,Tmax,Tint) + """ Downloads NAM analysis and forecast data. + + Inputs: + dates : List of strings, YYYYMMDD + hours : List of strings, HH + datatype : analysis or forecast. + + Optional arguments for forecasts via kwargs: + tmax : maximum forecast time to download (inclusive) + tint : internal (hr) between fetched forecasts + + """ + + # If date is before ####, download grib1, use this: + age = 'old' + + def get_anl(dates,hours,*args): + for d in dates: + for h in hours: + #if age=='new': + # command = ('wget "http://nomads.ncdc.noaa.gov/data/namanl/'+ + # d[:6]+'/'+ d+'/namanl_4_'+d+'_'+h+'00_000.grb2"') + #elif age=='old': + command = ('wget "http://nomads.ncdc.noaa.gov/data/namanl/'+ + d[:6]+'/'+ d+'/namanl_218_'+d+'_'+h+'00_000.grb"') + os.system(command) + + # Where are these forecast archives? + def get_218fcst(dates,hours,Tmax,Tint): + for d in dates: + for h in hours: + fhs = range(0,Tmax+Tint,Tint) + for fh in fhs: + fpad = "%03d" %fh + if age == 'old': + command = ('wget "http://nomads.ncdc.noaa.gov/data/nam/'+ + d[:6]+'/'+ d+'/nam_218_'+d+'_'+h+'00_'+fpad+'.grb"') + elif age == 'new': # doesn't seem to work + command = ('wget "http://nomads.ncdc.noaa.gov/data/nam/'+ + d[:6]+'/'+ d+'/nam_4_'+d+'_'+h+'00_'+fpad+'.grb2"') + + os.system(command) + + CMND = {'forecast':get_218fcst, 'analysis':get_anl} + CMND[data](dates,hours,Tmax,Tint) def getruc(dates,hours): - def right_url(d,h): - """To work out what URL to use for RUC/RAP data. - """ - yr = int(d[:4]) - mth = int(d[4:6]) - - - if (yr > 2012) and (mth > 3): # With a massive gap for RAP - URL_base = "http://nomads.ncdc.noaa.gov/data/rap130/" - URL = '/'.join((URL_base,d[:6],d,'rap_130_'+d+'_'+h+'00_000.grb2')) - elif (yr > 2007) and (mth > 10): # Massive gap after 2012/05 (transition to RAP). - URL_base = "http://nomads.ncdc.noaa.gov/data/rucanl/" - URL = '/'.join((URL_base,d[:6],d,'ruc2anl_130_'+d+'_'+h+'00_000.grb2')) - elif (yr>2006): - URL_base = "http://nomads.ncdc.noaa.gov/data/rucanl/" - URL = '/'.join((URL_base,d[:6],d,'ruc2anl_252_'+d+'_'+h+'00_000.grb')) - elif (yr>2004): - URL_base = "http://nomads.ncdc.noaa.gov/data/rucanl/" - URL = '/'.join((URL_base,d[:6],d,'ruc2_252_'+d+'_'+h+'00_000.grb')) - - return URL - - - for d in dates: - for h in hours: - URL = right_url(d,h) - command = 'wget ' + URL - os.system(command) + def right_url(d,h): + """To work out what URL to use for RUC/RAP data. + """ + yr = int(d[:4]) + mth = int(d[4:6]) + + + if (yr > 2012) and (mth > 3): # With a massive gap for RAP + URL_base = "http://nomads.ncdc.noaa.gov/data/rap130/" + URL = '/'.join((URL_base,d[:6],d,'rap_130_'+d+'_'+h+'00_000.grb2')) + elif (yr > 2007) and (mth > 10): # Massive gap after 2012/05 (transition to RAP). + URL_base = "http://nomads.ncdc.noaa.gov/data/rucanl/" + URL = '/'.join((URL_base,d[:6],d,'ruc2anl_130_'+d+'_'+h+'00_000.grb2')) + elif (yr>2006): + URL_base = "http://nomads.ncdc.noaa.gov/data/rucanl/" + URL = '/'.join((URL_base,d[:6],d,'ruc2anl_252_'+d+'_'+h+'00_000.grb')) + elif (yr>2004): + URL_base = "http://nomads.ncdc.noaa.gov/data/rucanl/" + URL = '/'.join((URL_base,d[:6],d,'ruc2_252_'+d+'_'+h+'00_000.grb')) + + return URL + + + for d in dates: + for h in hours: + URL = right_url(d,h) + command = 'wget ' + URL + os.system(command) diff --git a/postWRF/bin/settings.py b/postWRF/bin/settings.py index 90e0b93..377983a 100644 --- a/postWRF/bin/settings.py +++ b/postWRF/bin/settings.py @@ -3,7 +3,9 @@ def __init__(self): self.output_root = '/home/jrlawson/public_html/bowecho/' self.wrfout_root = '/chinook2/jrlawson/bowecho/' self.p_interp_root = '/home/jrlawson/fortrancode/P_INTERP/' + self.RUC_root = '/chinook2/jrlawson/bowecho/' self.DPI = 100.0 self.plot_titles = 1 self.terrain = 0 self.colorbar = 1 + # diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index f0de7f4..188311f 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -16,7 +16,10 @@ class Figure: def __init__(self,config,wrfout): self.C = config - self.W = wrfout + if wrfout=='RUC': + pass + else: + self.W = wrfout def create_fname(self,*naming): """Default naming should be: diff --git a/postWRF/postWRF/rucplot.py b/postWRF/postWRF/rucplot.py new file mode 100644 index 0000000..33c9c2c --- /dev/null +++ b/postWRF/postWRF/rucplot.py @@ -0,0 +1,213 @@ +import os +import matplotlib as M +M.use('Agg') +import matplotlib.pyplot as plt +import numpy as N +from netCDF4 import Dataset +import pdb +from mpl_toolkits.basemap import Basemap +import time +import glob + +import sys +#sys.path.append('/home/jrlawson/gitprojects/') +import WEM.utils.utils as utils +from figure import Figure +from defaults import Defaults +from wrfout import WRFOut + +class RUCPlot(Figure): + def __init__(self,config): + self.C = config + self.path_to_data = self.C.path_to_RUC + self.output_root = self.C.output_root + self.D = Defaults() + + def plot(self,variables,**kwargs): + for va in variables: + for t in variables[va]['pt']: + if 'lv' in variables[va]: + lv = variables[va]['lv'] + else: + lv = '' + if 'scale' in variables[va]: + kwargs['scale'] = variables[va]['scale'] + if 'top' in variables[va]: + kwargs['top'] = variables[va]['top'] + kwargs['bottom'] = variables[va]['bottom'] + self.plot_variable(va,t,lv,**kwargs) + + def plot_variable(self,va,t,lv,**kwargs): + fname = self.get_fname(t) + fpath = os.path.join(self.path_to_data,fname+'.nc') + print fpath + nc = Dataset(fpath) + + # pdb.set_trace() + #lats, lons = get_latlon(nc) + self.fig = plt.figure() + m, x, y = self.basemap_setup(nc,**kwargs) + # Scales, colourmaps in here + + data = self.get(nc,va,**kwargs) + if not 'scale' in kwargs: + m.contourf(x,y,data) + else: + m.contourf(x,y,data,N.arange(*kwargs['scale'])) + + if self.C.plot_titles: + title = utils.string_from_time('title',t) + plt.title(title) + if self.C.colorbar: + plt.colorbar(orientation='horizontal') + + # SAVE FIGURE + datestr = utils.string_from_time('output',t) + lv_na = utils.get_level_naming(lv,va) + na = (va,lv_na,datestr) + self.fname = self.create_fname(*na) + # pdb.set_trace() + self.save(self.fig,self.output_root,self.fname) + plt.close() + + def get(self,nc,va,**kwargs): + if va == 'CAPE': + data = nc.variables['CAPE_252_SFC'][:] + elif va == 'U': + data = nc.variables['U_GRD_252_ISBL'][:] + elif va == 'V': + data = nc.variables['V_GRD_252_ISBL'][:] + elif va == 'Z': + data = nc.variables['HGT_252_ISBL'][:] + elif va == 'wind10': + data = self.compute_wind10(nc) + elif va == 'shear': + data = self.compute_shear(nc,**kwargs) + elif va == 'dewpoint': + data = nc.variables['DPT_252_HTGL'][:] - 273.15 + else: + print("Choose variable") + raise Exception + return data + + def get_latlon(self,nc): + # pdb.set_trace() + lats = nc.variables['gridlat_252'] + lons = nc.variables['gridlon_252'] + return lats,lons + + def get_fname(self,t): + if isinstance(t,int): + # Time is datenum -> make sequence + ts = time.gmtime(t) + else: + # Already a sequence + ts = t + + if ts[4] is not 0: + print("No RUC data for this time") + raise Exception + # Depending on the RUC version, this might change + fname = 'ruc2_252_{0:04d}{1:02d}{2:02d}_{3:02d}00_000'.format(*ts) + return fname + + + def basemap_setup(self,nc,**kwargs): + # Fetch settings + basemap_res = getattr(self.C,'basemap_res',self.D.basemap_res) + lats, lons = self.get_latlon(nc) + lllat = lats[0,0] + lllon = lons[0,0] + urlat = lats[-1,-1] + urlon = lons[-1,-1] + + if 'Wlim' in kwargs: + lllat = kwargs['Slim'] + lllon = kwargs['Wlim'] + urlat = kwargs['Nlim'] + urlon = kwargs['Elim'] + + dx = 13.0 + dy = 13.0 + x_dim = lats.shape[0] + y_dim = lats.shape[1] + width_m = dx*(x_dim-1) + height_m = dy*(y_dim-1) + lat0 = lats[x_dim/2,y_dim/2] + lon0 = lons[x_dim/2,y_dim/2] + + m = Basemap( + projection='lcc',width=width_m,height=height_m, + llcrnrlon=lllon,llcrnrlat=lllat, + urcrnrlon=urlon,urcrnrlat=urlat, + lat_0=lat0,lon_0=lon0, + resolution=basemap_res,area_thresh=500 + ) + m.drawcoastlines() + m.drawstates() + m.drawcountries() + + # Draw meridians etc with wrff.lat/lon spacing + # Default should be a tenth of width of plot, rounded to sig fig + # pdb.set_trace() + mx, my = N.meshgrid(lons[0,:],lats[:,0]) + x,y = m(mx,my) + return m, x, y + + def colocate_WRF_map(self,wrfdir): + searchfor = os.path.join(wrfdir,'wrfout*') + # pdb.set_trace() + files = glob.glob(searchfor) + W = WRFOut(files[0]) + limits = {} + limits['Nlim'] = W.lats.max() + limits['Slim'] = W.lats.min() + limits['Wlim'] = W.lons.min() + limits['Elim'] = W.lons.max() + + return limits + + def compute_shear(self,nc,**kwargs): + """ + top and bottom in km. + kwargs['top'] + kwargs['bottom'] + + """ + topm = kwargs['top']*1000 + botm = kwargs['bottom']*1000 + + u = self.get(nc,'U')[::-1,...] + v = self.get(nc,'V')[::-1,...] + Z = self.get(nc,'Z')[::-1,...] + + topidx = N.zeros((225,301)) + botidx = N.zeros((225,301)) + ushear = N.zeros((225,301)) + vshear = N.zeros((225,301)) + + for i in range(225): + for j in range(301): + topidx[i,j] = round(N.interp( + topm,Z[:,i,j],range(37))) + botidx[i,j] = round(N.interp( + botm,Z[:,i,j],range(37))) + ushear[i,j] = u[topidx[i,j],i,j] - u[botidx[i,j],i,j] + vshear[i,j] = v[topidx[i,j],i,j] - v[botidx[i,j],i,j] + + # Find indices of bottom and top levels + # topidx = N.where(abs(Z-topm) == abs(Z-topm).min(axis=1)) + # botidx = N.where(abs(Z-botm) == abs(Z-botm).min(axis=1)) + + # ushear = u[0,:,topidx] - u[0,:,botidx] + # vshear = v[0,topidx,:,:] - v[0,botidx,:,:] + + shear = N.sqrt(ushear**2 + vshear**2) + # pdb.set_trace() + return shear + + def compute_wind10(self,nc): + u = self.get(nc,'U')[-1,...] + v = self.get(nc,'V')[-1,...] + return N.sqrt(u**2 + v**2) + diff --git a/utils/utils.py b/utils/utils.py index b5e3bc2..7172364 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -91,7 +91,7 @@ def lookup_time(str): D = {'year':0, 'month':1, 'day':2, 'hour':3, 'minute':4, 'second':5} return D[str] -def get_level_naming(lv,va,vardict): +def get_level_naming(lv,va,**kwargs): if lv < 1500: return str(lv)+'hPa' elif lv == 2000: @@ -104,7 +104,7 @@ def get_level_naming(lv,va,vardict): return lv elif lv == 'all': if va == 'shear': - name = '{0}to{1}'.format(vardict['bottom'],vardict['top']) + name = '{0}to{1}'.format(kwargs['bottom'],kwargs['top']) return name else: return 'all_lev' From 19507e1af0237c74b2536335113c940be54acae9 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 3 Apr 2014 20:09:31 -0500 Subject: [PATCH 074/111] Updating skewT stuff for Chelsea. --- postWRF/bin/ensemble.txt | 622 ++++++++++++++++++------------------ postWRF/bin/settings.py | 4 +- postWRF/postWRF/__init__.py | 6 + postWRF/postWRF/birdseye.py | 2 +- postWRF/postWRF/skewt.py | 247 ++++++++++++++ utils/generalmet.py | 23 +- utils/utils.py | 3 +- 7 files changed, 591 insertions(+), 316 deletions(-) diff --git a/postWRF/bin/ensemble.txt b/postWRF/bin/ensemble.txt index 1280ece..c4e0808 100644 --- a/postWRF/bin/ensemble.txt +++ b/postWRF/bin/ensemble.txt @@ -3,444 +3,444 @@

MOS FORECASTS

 Control
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  74| 43  74| 40  76| 43  77| 43  76| 44  65| 45  68| 46  66 43 64
- TMP  73| 46  72| 45  75| 47  77| 46  74| 47  63| 47  66| 48  65      
- DPT  35| 33  32| 31  27| 32  30| 36  36| 40  42| 43  40| 42  39      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  OV| OV  OV| PC  PC      
- P12   2|  3   3| 12   3|  2   0|  7   9| 23  41| 52  31| 21  16999999
- P24    |      7|     12|      3|     13|     47|     56|     35   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  2    |             
- Q24    |      0|      0|      0|      0|      0|       |             
- PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  32| 12  26| 17  33| 22  41| 25  55| 25  57| 31  62| 31  62 29 54
+ TMP  29| 14  24| 19  32| 24  39| 27  51| 28  51| 35  55| 34  57      
+ DPT  17| 10  14| 18  22| 22  25| 23  28| 22  27| 25  29| 25  29      
+ CLD  OV| OV  OV| OV  OV| OV  OV| PC  PC| CL  PC| PC  PC| CL  PC      
+ P12  92|  4  33| 15  30| 26  26| 15   5| 12   7| 18  18| 16  18 19 18
+ P24    |     35|     47|     34|     21|     20|     29|     23    27
+ Q12   2|  0   1|  0   1|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      1|      0|      0|       |             
+ PZP   9|  6   3|  9   7|  8   5| 11   1|  7   0|  6   2|  6   3      
+ PSN  65| 85  90| 81  71| 72  69| 54  51| 22  32| 14  19| 20  28      
+ PRS  15|  9   7|  8  14| 13  14| 22   9| 15   7| 12   8| 12  14      
+ TYP   S|  S   S|  S   S|  S   S|  S   S|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 1
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  75| 42  75| 41  76| 43  78| 43  72| 45  63| 46  67| 46  67 43 64
- TMP  73| 46  73| 45  75| 47  77| 46  71| 48  61| 48  65| 49  66      
- DPT  35| 34  33| 33  30| 34  33| 36  38| 42  42| 43  39| 42  40      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| OV  OV| OV  OV| PC  PC      
- P12   1|  3   5| 11   3|  2   0|  7  13| 42  41| 51  21| 16  18999999
- P24    |      9|     11|      3|     15|     61|     51|     30   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   1|  2    |             
- Q24    |      0|      0|      0|      0|      3|       |             
- PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  33| 12  27| 18  35| 23  44| 25  61| 28  64| 34  66| 35  66 29 54
+ TMP  30| 15  25| 20  33| 25  41| 28  55| 31  58| 37  60| 38  61      
+ DPT  18| 11  16| 19  23| 22  26| 23  29| 24  30| 29  30| 28  30      
+ CLD  OV| OV  OV| OV  OV| OV  OV| PC  CL| CL  PC| PC  PC| CL  PC      
+ P12  80|  6  39| 16  29| 24  25| 14   4| 12   6| 14  14| 16  14 19 18
+ P24    |     44|     47|     31|     19|     16|     20|     23    27
+ Q12   2|  0   1|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      1|      1|      1|      0|      0|       |             
+ PZP   8|  6   3|  9   6|  8   5| 10   0|  5   0|  6   1|  6   1      
+ PSN  67| 83  86| 76  62| 62  59| 38  24| 10  31| 10  16| 16  18      
+ PRS  14| 10   4| 10  13| 13  11| 18   8| 14   6|  8   3|  3   6      
+ TYP   S|  S   S|  S   S|  S   S| RS   R|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 2
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  75| 43  76| 41  76| 43  79| 45  79| 45  72| 46  70| 47  67 43 64
- TMP  73| 47  74| 45  76| 47  78| 49  77| 48  70| 49  68| 50  65      
- DPT  34| 30  27| 28  25| 34  30| 36  37| 39  42| 43  42| 43  40      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| OV  OV| PC  PC      
- P12   1|  1   3|  8   3|  3   0|  7   8| 14  27| 36  35| 25  18999999
- P24    |      5|      8|      3|     12|     30|     42|     42   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
- Q24    |      0|      0|      0|      0|      0|       |             
- PZP   0|  2   2|  3   1|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   1|  0   2|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   2|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  30| 10  26| 16  33| 21  43| 25  55| 25  54| 28  54| 26  56 29 54
+ TMP  26| 12  23| 17  32| 24  40| 28  51| 28  50| 30  49| 28  52      
+ DPT  16|  8  13| 16  22| 21  25| 24  28| 22  27| 26  29| 22  29      
+ CLD  OV| OV  OV| OV  OV| OV  OV| PC  PC| PC  PC| OV  OV| CL  PC      
+ P12  94|  7  32| 21  19| 22  21| 13   4| 12   8| 19  21| 17  19 19 18
+ P24    |     32|     39|     28|     20|     21|     32|     26    27
+ Q12   2|  0   1|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      0|      0|      0|       |             
+ PZP   9|  5   2|  8   3|  8   6| 11   4|  8   0|  6   2|  7   3      
+ PSN  65| 90  91| 86  76| 69  69| 55  49| 36  31| 16  21| 21  29      
+ PRS  17|  6   7|  6  12| 14  14| 22   9| 17   9| 12   7| 12  15      
+ TYP   S|  S   S|  S   S|  S   S|  S   S| RS   R|  R   R|  R   R      
                                                                       
 

 Perturbation 3
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  74| 43  73| 40  75| 42  77| 43  76| 44  72| 46  70| 46  65 43 64
- TMP  73| 46  71| 44  74| 46  76| 46  75| 46  71| 48  68| 48  64      
- DPT  35| 35  34| 33  28| 31  29| 35  35| 38  40| 42  39| 41  41      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| PC  PC| OV  PC      
- P12   2|  2  11| 27   5|  1   0|  7   8| 14  23| 20  12| 36  29999999
- P24    |     15|     27|      3|     13|     28|     28|     45   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
- Q24    |      0|      0|      0|      0|      0|       |             
- PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  1   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  32| 12  28| 18  31| 21  40| 23  54| 26  59| 32  62| 33  66 29 54
+ TMP  29| 14  25| 19  30| 22  38| 25  49| 29  53| 35  56| 37  60      
+ DPT  17| 10  15| 18  22| 21  23| 21  26| 22  28| 27  32| 29  33      
+ CLD  OV| OV  OV| OV  OV| OV  OV| PC  PC| PC  PC| PC  OV| CL  PC      
+ P12  87|  4  25| 20  35| 35  24| 13  13| 16   8| 15  20| 16  20 19 18
+ P24    |     25|     55|     35|     22|     21|     36|     25    27
+ Q12   2|  0   0|  0   1|  1   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      0|      0|      0|       |             
+ PZP   9|  6   3|  9   8|  9   5| 11   0|  5   0|  7   2|  7   3      
+ PSN  65| 85  89| 80  71| 74  71| 52  39| 14  32| 16  20| 23  29      
+ PRS  15|  9   7|  9  15| 16  14| 19   6| 15   6| 12   6| 12  15      
+ TYP   S|  S   S|  S   S|  S   S|  S   R|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 4
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  74| 42  75| 41  77| 44  77| 43  73| 46  62| 49  67| 46  68 43 64
- TMP  72| 46  74| 45  76| 47  76| 46  71| 49  61| 51  65| 49  66      
- DPT  35| 34  31| 32  29| 35  33| 38  39| 41  43| 47  41| 42  41      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| OV  OV| OV  OV| PC  PC      
- P12   2|  2   4|  7   4|  3   0|  7  18| 51  45| 57  34| 25  23999999
- P24    |      7|      7|      4|     23|     60|     58|     35   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  1   2|  2    |             
- Q24    |      0|      0|      0|      0|      3|       |             
- PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   1|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  33| 12  25| 15  35| 22  42| 24  58| 28  60| 32  61| 33  68 29 54
+ TMP  30| 15  23| 17  33| 24  40| 27  54| 31  55| 35  55| 36  62      
+ DPT  17| 11  14| 16  22| 21  25| 23  29| 24  29| 27  29| 26  32      
+ CLD  OV| OV  OV| OV  OV| OV  OV| PC  PC| CL  PC| PC  PC| CL  PC      
+ P12  81|  3  36| 11  22| 33  25| 13   4| 13  10| 17  18| 16  18 19 18
+ P24    |     38|     27|     34|     19|     22|     27|     25    27
+ Q12   2|  0   1|  0   0|  1   0|  0   0|  0   0|  0    |             
+ Q24    |      1|      0|      1|      0|      0|       |             
+ PZP  10|  5   3|  7   4|  8   5| 10   0|  5   0|  6   1|  6   3      
+ PSN  65| 83  90| 86  75| 73  68| 44  26|  9  32| 13  18| 18  29      
+ PRS  14| 10   7|  5  12| 17  10| 19   8| 14   6| 11   4| 10  15      
+ TYP   S|  S   S|  S   S|  S   S|  S   R|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 5
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  75| 43  74| 40  75| 42  77| 44  77| 44  68| 46  68| 47  66 43 64
- TMP  73| 47  72| 44  74| 46  76| 47  75| 47  67| 48  66| 50  64      
- DPT  33| 31  30| 31  25| 30  29| 35  36| 38  40| 43  40| 44  41      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| OV  OV| OV  OV      
- P12   1|  3   3| 10   3|  2   0|  7  10| 17  32| 38  28| 33  22999999
- P24    |      7|     10|      3|     13|     39|     45|     45   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
- Q24    |      0|      0|      0|      0|      0|       |             
- PZP   0|  1   1|  1   1|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   1|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  32| 11  24| 15  29| 20  45| 25  57| 26  62| 34  63| 34  69 29 54
+ TMP  29| 14  22| 17  29| 22  42| 28  52| 29  55| 37  57| 37  64      
+ DPT  17|  8  13| 16  21| 20  25| 23  27| 22  29| 30  31| 28  32      
+ CLD  OV| OV  OV| OV  OV| OV  PC| CL  PC| PC  PC| PC  PC| CL  PC      
+ P12  77|  4  38| 17  43| 28  19| 11   5| 14   8| 16  18| 16  16 19 18
+ P24    |     40|     60|     31|     18|     20|     20|     23    27
+ Q12   2|  0   1|  0   1|  1   0|  0   0|  0   0|  0    |             
+ Q24    |      1|      2|      1|      0|      0|       |             
+ PZP   5|  6   2|  8   3|  9   5| 10   0|  5   0|  7   2|  6   1      
+ PSN  74| 83  92| 84  81| 77  65| 45  37| 11  32| 16  20| 16  19      
+ PRS  15| 10   5|  8  12| 11  13| 17   7| 14   6| 12   4|  4   7      
+ TYP   S|  S   S|  S   S|  S   S|  S   R|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 6
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  75| 43  74| 40  75| 43  78| 42  76| 44  65| 45  68| 46  70 43 64
- TMP  74| 47  73| 45  74| 47  77| 46  75| 47  63| 47  67| 49  69      
- DPT  34| 33  32| 31  26| 33  31| 36  36| 40  41| 43  39| 40  38      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  OV| OV  OV| PC  CL      
- P12   2|  2   3| 16   3|  3   0|  7  10| 31  42| 43  27| 15  15999999
- P24    |      7|     16|      3|     13|     56|     45|     28   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   1|  1    |             
- Q24    |      0|      0|      0|      0|      2|       |             
- PZP   0|  0   0|  1   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  33| 13  26| 18  33| 22  45| 26  56| 26  60| 31  59| 32  61 29 54
+ TMP  30| 16  24| 19  32| 25  42| 28  51| 29  54| 35  54| 35  56      
+ DPT  18| 12  16| 19  23| 22  26| 24  28| 23  28| 26  28| 25  30      
+ CLD  OV| OV  OV| OV  OV| OV  OV| PC  PC| PC  PC| PC  PC| PC  PC      
+ P12  81|  9  44| 22  20| 28  22| 13   6| 12  11| 14  18| 16  20 19 18
+ P24    |     45|     34|     30|     20|     23|     30|     29    27
+ Q12   2|  0   1|  0   0|  1   0|  0   0|  0   0|  0    |             
+ Q24    |      1|      1|      0|      0|      0|       |             
+ PZP   8|  6   3|  8   4|  8   5| 11   2|  7   0|  7   2|  6   3      
+ PSN  65| 83  89| 84  74| 64  62| 50  40| 16  32| 14  19| 18  29      
+ PRS  15| 10   6|  8  11| 15  14| 21   8| 15   6| 12   4|  9  15      
+ TYP   S|  S   S|  S   S|  S   S|  S  RS|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 7
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  75| 43  74| 40  75| 43  76| 44  74| 46  62| 47  64| 46  66 43 64
- TMP  74| 47  72| 44  74| 47  75| 48  72| 49  60| 49  62| 48  65      
- DPT  34| 31  30| 30  27| 33  31| 37  39| 40  43| 46  41| 43  41      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| OV  OV| OV  OV| OV  OV      
- P12   1|  3   3|  9   3|  2   0|  7  16| 38  44| 62  36| 29  24999999
- P24    |      7|      9|      3|     19|     64|     66|     42   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   3|  2    |             
- Q24    |      0|      0|      0|      0|      3|       |             
- PZP   0|  1   1|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   0|  2   3|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  34| 12  30| 19  31| 19  41| 24  60| 30  62| 34  65| 31  66 29 54
+ TMP  31| 15  27| 21  30| 21  38| 27  54| 33  56| 38  58| 34  60      
+ DPT  19| 11  16| 19  22| 19  22| 22  29| 25  29| 28  30| 25  32      
+ CLD  OV| OV  OV| OV  OV| OV  OV| CL  CL| CL  PC| PC  PC| CL  PC      
+ P12  91|  2  20| 16  43| 25  23| 12   4| 12   6| 15  18| 16  19 19 18
+ P24    |     20|     56|     26|     20|     17|     27|     24    27
+ Q12   2|  0   0|  0   1|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      2|      0|      0|      0|       |             
+ PZP   5|  6   3| 11   7|  9   5| 10   0|  5   0|  6   2|  6   3      
+ PSN  60| 81  87| 76  69| 79  71| 53  27|  8  30| 12  18| 19  28      
+ PRS  21| 11   7| 12  14| 13  14| 19   6| 14   6|  9   4| 11  14      
+ TYP   S|  S   S|  S   S|  S   S|  S   R|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 8
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  74| 43  75| 41  76| 44  76| 43  73| 44  64| 46  64| 47  66 43 64
- TMP  73| 46  73| 45  75| 47  75| 46  71| 47  63| 49  63| 50  64      
- DPT  34| 33  32| 31  27| 35  32| 38  38| 40  42| 44  44| 44  42      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| OV  OV| OV  OV| OV  PC      
- P12   1|  4   4| 14   3|  3   0|  7  13| 43  39| 48  38| 38  24999999
- P24    |      9|     14|      3|     16|     62|     55|     50   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
- Q24    |      0|      0|      0|      0|      2|       |             
- PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   2|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  31| 11  29| 18  34| 24  36| 23  47| 22  51| 29  60| 32  58 29 54
+ TMP  28| 13  26| 20  33| 25  35| 25  44| 25  46| 32  54| 35  53      
+ DPT  17|  8  14| 18  23| 24  23| 22  25| 20  25| 25  29| 27  30      
+ CLD  OV| OV  OV| OV  OV| OV  OV| OV  PC| PC  PC| PC  PC| PC  PC      
+ P12  95|  3  21|  5  31| 51  43| 20  16| 12   8| 15  18| 16  20 19 18
+ P24    |     26|     43|     73|     22|     20|     23|     32    27
+ Q12   2|  0   0|  0   1|  2   1|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      2|      0|      0|       |             
+ PZP   8|  5   3| 10   8| 11   7|  8   2|  9   1|  7   2|  7   3      
+ PSN  65| 88  89| 78  66| 65  68| 68  73| 57  50| 17  19| 18  25      
+ PRS  17|  7   7|  8  14| 20  17| 18   9| 15   8| 12   4| 10  12      
+ TYP   S|  S   S|  S   S|  S   S|  S   S|  S   S|  R   R|  R   R      
                                                                       
 

 Perturbation 9
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  74| 43  76| 41  76| 43  77| 43  74| 45  65| 47  67| 46  67 43 64
- TMP  73| 46  74| 46  76| 47  76| 47  73| 48  64| 49  65| 49  65      
- DPT  36| 34  31| 30  26| 33  31| 38  39| 41  43| 43  40| 43  41      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  OV| OV  OV| PC  PC      
- P12   2|  3   4|  8   3|  3   0|  7  10| 33  37| 46  28| 29  24999999
- P24    |      9|      8|      3|     13|     54|     51|     43   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
- Q24    |      0|      0|      0|      0|      1|       |             
- PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  1   0|  0   0|  0   3|  1   2|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  30| 10  30| 18  31| 21  38| 23  53| 25  56| 30  59| 29  62 29 54
+ TMP  27| 13  26| 19  30| 23  36| 25  49| 28  50| 34  53| 32  57      
+ DPT  17|  8  14| 18  22| 21  23| 22  27| 22  27| 26  31| 25  30      
+ CLD  OV| OV  OV| OV  OV| OV  OV| OV  PC| CL  PC| PC  OV| PC  PC      
+ P12  95|  3  18| 29  35| 39  31| 18   5| 12   9| 16  21| 17  19 19 18
+ P24    |     20|     48|     48|     23|     20|     36|     26    27
+ Q12   2|  0   0|  0   1|  1   1|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      2|      0|      0|       |             
+ PZP   9|  5   3| 10   8| 10   6| 11   1|  9   0|  6   2|  7   3      
+ PSN  67| 89  89| 79  70| 73  70| 59  56| 33  35| 15  21| 25  29      
+ PRS  17|  6   7|  9  14| 17  15| 21   9| 17   8| 12   7| 13  15      
+ TYP   S|  S   S|  S   S|  S   S|  S   S| RS   R|  R   R|  R   R      
                                                                       
 

 Perturbation 10
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  74| 42  75| 40  76| 43  78| 44  77| 44  71| 46  69| 47  67 43 64
- TMP  72| 46  73| 45  75| 47  78| 48  76| 47  69| 49  68| 50  66      
- DPT  36| 33  32| 32  30| 34  30| 34  36| 39  42| 43  39| 41  39      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| OV  OV| PC  PC      
- P12   2|  1   3|  9   4|  4   0|  7   9| 14  30| 45  32| 20  17999999
- P24    |      6|      9|      4|     12|     34|     45|     34   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  2    |             
- Q24    |      0|      0|      0|      0|      0|       |             
- PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  1   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  35| 13  27| 18  34| 22  46| 26  57| 25  64| 36  69| 35  73 29 54
+ TMP  31| 16  25| 20  33| 24  43| 29  52| 28  57| 39  62| 38  67      
+ DPT  18| 12  16| 19  23| 21  26| 24  27| 21  28| 30  34| 29  34      
+ CLD  OV| OV  OV| OV  OV| OV  PC| CL  PC| CL  PC| PC  PC| CL  PC      
+ P12  82|  7  38| 24  20| 24  23| 12   4| 12   8| 14  19| 16  15 19 18
+ P24    |     43|     37|     28|     19|     19|     29|     23    27
+ Q12   2|  0   1|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      1|      1|      0|      0|      0|       |             
+ PZP   8|  6   3| 10   5|  7   4| 11   0|  5   0|  6   2|  6   1      
+ PSN  65| 79  87| 80  72| 63  63| 52  29|  9  31| 15  20| 16  18      
+ PRS  13| 12   7|  9  12| 15  14| 21   9| 14   6|  8   4|  3   6      
+ TYP   S|  S   S|  S   S|  S   S|  S   R|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 11
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  75| 43  75| 41  75| 43  76| 44  73| 46  66| 47  67| 49  67 43 64
- TMP  74| 47  74| 46  74| 46  75| 47  72| 49  64| 49  65| 52  66      
- DPT  33| 31  30| 31  28| 35  31| 38  39| 41  44| 44  42| 44  40      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  PC| PC  OV| OV  PC| PC  PC      
- P12   0|  3   3|  8   4|  2   0|  7  12| 34  41| 55  45| 23  16999999
- P24    |      7|      8|      3|     19|     56|     61|     41   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   1|  2    |             
- Q24    |      0|      0|      0|      0|      1|       |             
- PZP   0|  1   1|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   1|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  32| 11  27| 18  38| 25  43| 26  58| 25  62| 32  63| 33  69 29 54
+ TMP  29| 14  25| 20  36| 27  41| 28  52| 28  55| 35  57| 36  63      
+ DPT  18| 10  15| 19  24| 24  26| 24  29| 22  28| 28  31| 28  32      
+ CLD  OV| OV  OV| OV  OV| OV  OV| OV  CL| CL  CL| PC  PC| CL  PC      
+ P12  84|  4  39| 21  27| 26  37| 23   8| 12   5| 13  16| 16  16 19 18
+ P24    |     42|     40|     48|     23|     16|     20|     23    27
+ Q12   2|  0   1|  0   0|  0   1|  0   0|  0   0|  0    |             
+ Q24    |      1|      1|      2|      0|      0|       |             
+ PZP   7|  6   3|  9   4|  9   7| 11   1|  9   1|  6   1|  6   2      
+ PSN  69| 84  86| 76  56| 46  55| 51  47| 26  24| 10  16| 19  22      
+ PRS  17|  9   5| 10   9| 11  13| 22   8| 16   6| 11   3| 10   9      
+ TYP   S|  S   S|  S   S|  S   S|  S  RS| RS   R|  R   R|  R   R      
                                                                       
 

 Perturbation 12
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  75| 43  76| 42  75| 42  76| 43  75| 45  67| 46  66| 47  65 43 64
- TMP  74| 47  74| 47  74| 45  76| 47  74| 47  65| 48  65| 49  63      
- DPT  34| 32  31| 30  28| 37  33| 38  38| 40  42| 43  42| 43  41      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  OV| OV  OV| OV  OV      
- P12   1|  3   3|  9   3|  3   0|  7   9| 25  34| 43  35| 32  28999999
- P24    |      7|      9|      3|     14|     46|     51|     45   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
- Q24    |      0|      0|      0|      0|      0|       |             
- PZP   0|  0   0|  3   1|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   2|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  34| 12  30| 20  36| 25  41| 25  53| 23  55| 29  58| 29  59 29 54
+ TMP  30| 15  27| 22  35| 27  39| 27  48| 25  50| 32  53| 32  54      
+ DPT  18| 11  17| 20  25| 25  25| 24  27| 21  27| 25  31| 25  28      
+ CLD  OV| OV  OV| OV  OV| OV  OV| OV  PC| PC  PC| OV  PC| CL  PC      
+ P12  90|  4  30|  9  31| 33  37| 15   8| 15   9| 21  21| 16  18 19 18
+ P24    |     31|     44|     52|     21|     20|     35|     23    27
+ Q12   2|  0   0|  0   1|  1   1|  0   0|  0   0|  1    |             
+ Q24    |      0|      1|      2|      0|      0|       |             
+ PZP   7|  5   3| 11   7|  7   6| 11   1|  9   1|  6   3|  7   3      
+ PSN  64| 83  87| 73  58| 51  62| 55  54| 30  32| 17  23| 26  29      
+ PRS  15| 10   5| 11  12| 15  15| 22   8| 16   9| 12  14| 12  15      
+ TYP   S|  S   S|  S   S|  S   S|  S   S| RS   R|  R   R|  R   R      
                                                                       
 

 Perturbation 13
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  74| 41  75| 40  76| 43  77| 43  74| 46  65| 47  68| 48  66 43 64
- TMP  73| 45  73| 44  75| 47  76| 46  72| 49  64| 49  66| 50  65      
- DPT  35| 34  31| 32  30| 35  32| 37  38| 41  45| 45  41| 44  41      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| OV  OV| OV  OV| PC  PC      
- P12   2|  2   3| 10   4|  3   0|  7  15| 38  39| 44  35| 32  25999999
- P24    |      7|     10|      4|     23|     61|     53|     44   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
- Q24    |      0|      0|      0|      0|      2|       |             
- PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  31| 11  32| 19  33| 22  41| 24  57| 27  60| 32  59| 30  58 29 54
+ TMP  28| 14  28| 21  32| 24  39| 26  52| 30  54| 36  53| 33  53      
+ DPT  17| 10  15| 19  23| 22  24| 22  28| 23  27| 26  30| 25  28      
+ CLD  OV| OV  OV| OV  OV| OV  OV| PC  PC| CL  PC| PC  PC| CL  CL      
+ P12  95|  3  17|  8  50| 38  25| 13   4| 12   6| 18  19| 17  18 19 18
+ P24    |     20|     50|     41|     21|     19|     34|     25    27
+ Q12   2|  0   0|  0   1|  1   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      1|      0|      0|       |             
+ PZP   8|  5   3|  9   7|  8   5| 10   0|  7   0|  6   3|  7   4      
+ PSN  66| 87  88| 78  65| 70  66| 56  40| 14  33| 13  20| 26  29      
+ PRS  17|  8   7| 10  14| 17  15| 21   8| 15   6| 12  16| 13  15      
+ TYP   S|  S   S|  S   S|  S   S|  S  RS|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 14
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  75| 43  75| 41  75| 43  78| 45  77| 45  69| 45  65| 46  64 43 64
- TMP  73| 46  73| 45  74| 46  77| 48  76| 47  67| 47  63| 48  62      
- DPT  33| 30  28| 29  26| 32  30| 36  38| 39  41| 42  40| 44  41      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| OV  OV| OV  PC      
- P12   2|  2   3|  9   4|  2   0|  7   8| 14  32| 45  35| 39  26999999
- P24    |      6|      9|      3|     11|     36|     50|     55   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  2    |             
- Q24    |      0|      0|      0|      0|      0|       |             
- PZP   0|  2   2|  1   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   1|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  31| 12  27| 16  30| 20  37| 22  52| 25  54| 28  55| 28  51 29 54
+ TMP  28| 14  24| 17  30| 22  36| 24  48| 28  49| 30  50| 31  47      
+ DPT  18| 10  14| 16  21| 20  23| 21  27| 22  28| 26  30| 24  27      
+ CLD  OV| OV  OV| OV  OV| OV  OV| OV  PC| PC  PC| OV  PC| PC  PC      
+ P12  97|  6  32| 21  27| 41  23| 15   8| 12  12| 22  20| 17  19 19 18
+ P24    |     32|     51|     41|     22|     22|     32|     34    27
+ Q12   2|  0   1|  0   0|  1   0|  0   0|  0   0|  1    |             
+ Q24    |      0|      1|      1|      0|      0|       |             
+ PZP   9|  5   2|  8   4| 10   6| 12   2| 10   0|  6   2|  6   3      
+ PSN  58| 87  91| 85  77| 75  73| 57  61| 35  31| 17  20| 20  29      
+ PRS  22|  8   7|  6  15| 16  15| 21   9| 17   8| 12   7| 12  15      
+ TYP   S|  S   S|  S   S|  S   S|  S   S| RS   R|  R   R|  R   R      
                                                                       
 

 Perturbation 15
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  74| 44  75| 41  76| 43  76| 44  74| 44  69| 45  69| 47  70 43 64
- TMP  72| 47  73| 45  75| 47  75| 47  73| 47  68| 48  67| 50  68      
- DPT  36| 34  30| 28  25| 31  28| 36  37| 40  39| 41  39| 43  42      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| PC  PC| PC  PC      
- P12   2|  4   3|  9   4|  1   0|  7  10| 27  20| 26  20| 26  21999999
- P24    |      8|      9|      3|     11|     35|     30|     38   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
- Q24    |      0|      0|      0|      0|      0|       |             
- PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  31| 10  23| 14  34| 20  42| 24  54| 24  56| 27  58| 27  60 29 54
+ TMP  28| 13  21| 15  32| 22  40| 27  49| 27  51| 30  53| 30  55      
+ DPT  16|  8  13| 15  21| 20  24| 23  28| 21  28| 24  28| 22  29      
+ CLD  OV| OV  OV| OV  OV| OV  OV| PC  PC| PC  PC| PC  PC| PC  PC      
+ P12  87| 14  37| 14  15| 19  24| 13   4| 12   7| 22  18| 16  18 19 18
+ P24    |     43|     19|     32|     18|     20|     23|     24    27
+ Q12   2|  0   1|  0   0|  0   0|  0   0|  0   0|  1    |             
+ Q24    |      1|      0|      1|      0|      0|       |             
+ PZP   7|  5   2|  7   3|  9   6| 11   4|  9   1|  6   2|  6   3      
+ PSN  71| 87  90| 89  81| 73  68| 57  47| 21  30| 14  19| 20  26      
+ PRS  17|  8   8|  3  13| 18  14| 21  10| 15   7| 12   6| 12  13      
+ TYP   S|  S   S|  S   S|  S   S|  S  RS|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 16
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  74| 42  73| 39  75| 42  75| 44  75| 44  69| 47  63| 46  62 43 64
- TMP  73| 46  71| 43  74| 45  74| 47  74| 47  67| 49  61| 48  60      
- DPT  34| 31  31| 32  27| 31  28| 34  36| 39  41| 43  43| 43  42      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  OV| OV  OV| OV  OV      
- P12   1|  2   3| 13   4|  3   0|  7   9| 14  29| 50  41| 46  31999999
- P24    |      6|     13|      3|     11|     32|     59|     58   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  2    |             
- Q24    |      0|      0|      0|      0|      0|       |             
- PZP   0|  2   2|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  34| 13  27| 18  30| 20  42| 24  54| 24  60| 32  59| 32  63 29 54
+ TMP  31| 15  25| 19  30| 22  40| 27  50| 27  54| 35  53| 35  58      
+ DPT  19| 11  16| 18  22| 20  25| 23  27| 21  28| 27  29| 26  31      
+ CLD  OV| OV  OV| OV  OV| OV  OV| CL  PC| CL  PC| PC  PC| PC  PC      
+ P12  86|  6  41| 31  28| 29  24| 13   5| 12  10| 14  18| 16  20 19 18
+ P24    |     44|     47|     33|     19|     20|     32|     26    27
+ Q12   2|  0   1|  1   0|  1   0|  0   0|  0   0|  0    |             
+ Q24    |      1|      1|      1|      0|      0|       |             
+ PZP   5|  6   3|  9   4|  9   6| 11   1|  7   0|  7   1|  6   3      
+ PSN  64| 80  87| 77  78| 75  71| 56  56| 19  32| 12  17| 20  29      
+ PRS  17| 11   3| 12  14| 13  15| 22   7| 15   6| 12   5| 12  15      
+ TYP   S|  S   S|  S   S|  S   S|  S   S|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 17
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  75| 43  75| 41  75| 43  79| 43  78| 45  70| 47  70| 48  68 43 64
- TMP  73| 47  74| 46  74| 47  78| 47  77| 48  68| 49  69| 50  67      
- DPT  34| 31  29| 30  27| 36  31| 36  36| 40  42| 44  41| 43  42      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| PC  PC| PC  PC      
- P12   2|  1   3|  9   4|  3   0|  7   9| 14  31| 32  15| 26  21999999
- P24    |      5|      9|      3|     13|     38|     37|     36   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
- Q24    |      0|      0|      0|      0|      0|       |             
- PZP   0|  1   1|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   1|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   1|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  29| 10  26| 16  30| 20  39| 24  57| 26  58| 32  62| 37  64 29 54
+ TMP  26| 12  23| 17  30| 22  38| 26  52| 29  53| 35  57| 40  58      
+ DPT  16|  8  13| 16  21| 20  23| 22  28| 23  28| 28  31| 29  31      
+ CLD  OV| OV  OV| OV  OV| OV  OV| PC  CL| CL  PC| PC  PC| PC  PC      
+ P12  95|  7  27| 15  22| 28  27| 14   5| 12   6| 18  18| 16  19 19 18
+ P24    |     29|     36|     37|     19|     19|     23|     32    27
+ Q12   2|  0   0|  0   0|  1   1|  0   0|  0   0|  0    |             
+ Q24    |      0|      1|      1|      0|      0|       |             
+ PZP  10|  5   2|  7   8| 10   6| 11   1|  7   0|  7   2|  6   4      
+ PSN  66| 90  91| 88  73| 75  72| 53  49| 22  32| 12  18| 18  31      
+ PRS  19|  6   7|  4  15| 14  13| 21   7| 15   8| 12   4|  7  14      
+ TYP   S|  S   S|  S   S|  S   S|  S  RS|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 18
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  73| 43  73| 40  76| 44  77| 43  73| 46  69| 49  68| 46  67 43 64
- TMP  72| 46  72| 44  75| 47  77| 46  71| 49  68| 51  67| 49  65      
- DPT  36| 36  35| 35  31| 33  31| 37  38| 41  43| 46  42| 43  41      
- CLD  CL| PC  CL| CL  CL| CL  CL| CL  CL| PC  OV| OV  OV| OV  OV      
- P12   2|  4   7| 20   4|  2   0|  7  19| 33  35| 40  28| 31  24999999
- P24    |     13|     20|      3|     29|     45|     51|     42   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
- Q24    |      0|      0|      0|      0|      0|       |             
- PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  1   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  34| 13  24| 15  30| 19  42| 25  53| 28  59| 33  61| 33  64 29 54
+ TMP  31| 16  22| 16  29| 21  40| 27  49| 30  54| 36  56| 36  58      
+ DPT  18| 12  15| 16  20| 18  24| 24  29| 25  29| 29  29| 27  31      
+ CLD  OV| OV  OV| OV  OV| OV  OV| OV  PC| PC  PC| PC  PC| CL  PC      
+ P12  74|  7  46| 28  20| 22  21| 16  17| 14   9| 14  16| 16  17 19 18
+ P24    |     50|     37|     27|     25|     23|     21|     23    27
+ Q12   2|  0   1|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      1|      1|      0|      0|      0|       |             
+ PZP   7|  6   3|  8   1| 10   4| 10   0|  5   0|  6   1|  6   3      
+ PSN  67| 80  90| 86  86| 80  68| 40  32| 12  31| 12  16| 16  28      
+ PRS  16| 11   6|  6  14|  8   9| 19   7| 14   6| 12   3|  5  14      
+ TYP   S|  S   S|  S   S|  S   S|  S   R|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 19
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  75| 42  74| 40  75| 43  75| 43  68| 45  65| 47  67| 47  68 43 64
- TMP  73| 45  72| 45  74| 46  74| 46  67| 48  64| 49  66| 50  67      
- DPT  35| 35  33| 33  30| 35  33| 39  41| 42  43| 45  40| 42  41      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  OV| OV  OV| PC  PC| PC  PC      
- P12   1|  4   6| 20   3|  3   0|  7  22| 52  37| 37  17| 24  23999999
- P24    |     11|     20|      4|     31|     61|     42|     30   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  1   0|  0    |             
- Q24    |      0|      0|      0|      0|      2|       |             
- PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  1   0|  0   0|  0   3|  1   1|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  31| 11  26| 16  33| 21  44| 24  54| 25  58| 33  59| 31  64 29 54
+ TMP  28| 13  23| 17  32| 24  41| 26  50| 28  52| 37  54| 34  59      
+ DPT  17|  9  14| 16  22| 21  26| 22  27| 22  28| 28  30| 27  30      
+ CLD  OV| OV  OV| OV  OV| OV  OV| PC  PC| PC  PC| OV  PC| CL  PC      
+ P12  93|  4  32| 11  33| 23  20| 14   5| 15  10| 27  20| 16  18 19 18
+ P24    |     33|     50|     28|     19|     21|     30|     26    27
+ Q12   2|  0   1|  0   1|  0   0|  0   0|  0   0|  2    |             
+ Q24    |      0|      1|      0|      0|      0|       |             
+ PZP   8|  5   2|  7   7|  9   5| 10   0|  7   0|  6   3|  6   3      
+ PSN  64| 87  91| 85  74| 75  70| 46  41| 18  32| 20  21| 20  28      
+ PRS  17|  8   7|  6  13| 12  13| 20   8| 15   7| 12  14| 12  14      
+ TYP   S|  S   S|  S   S|  S   S|  S  RS|  R   R|  R   R|  R   R      
                                                                       
 

 Perturbation 20
- KRDD   MRF MOS GUIDANCE    3/20/2014  0000 UTC                       
+ KRAP   MRF MOS GUIDANCE    3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  75| 42  75| 41  77| 44  78| 44  76| 44  68| 47  68| 48  66 43 64
- TMP  74| 45  73| 45  76| 48  77| 47  75| 47  67| 49  66| 50  64      
- DPT  35| 33  32| 33  30| 32  29| 35  36| 39  41| 44  42| 44  42      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| PC  PC| OV  OV| OV  OV      
- P12   2|  1   3|  8   4|  2   0|  7  10| 26  30| 38  30| 34  29999999
- P24    |      5|      8|      3|     13|     46|     47|     46   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
- Q24    |      0|      0|      0|      0|      0|       |             
- PZP   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1   0      
- PSN   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PRS   0|  0   0|  0   0|  0   3|  1   0|  2   2|  3   3|  3   4      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  30| 10  26| 15  34| 21  41| 23  56| 25  58| 32  61| 31  64 29 54
+ TMP  27| 13  23| 17  33| 23  39| 26  51| 28  52| 35  55| 34  58      
+ DPT  16|  8  13| 16  21| 21  25| 22  28| 22  28| 27  31| 26  31      
+ CLD  OV| OV  OV| OV  OV| OV  OV| OV  PC| PC  PC| PC  PC| CL  PC      
+ P12  92|  8  33| 26  16| 22  24| 15   5| 13  12| 18  20| 16  19 19 18
+ P24    |     35|     26|     32|     18|     23|     33|     29    27
+ Q12   2|  0   1|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      1|      0|      0|       |             
+ PZP   8|  5   2|  7   3| 10   7| 11   1|  6   0|  7   2|  6   3      
+ PSN  69| 89  91| 87  80| 70  70| 53  38| 17  33| 16  20| 20  29      
+ PRS  18|  6   6|  5  11| 14  12| 21   7| 15   6| 12   4| 12  15      
+ TYP   S|  S   S|  S   S|  S   S|  S   R|  R   R|  R   R|  R   R      
                                                                       
 

 Operational
- KRDD   GFSX MOS GUIDANCE   3/20/2014  0000 UTC                       
+ KRAP   GFSX MOS GUIDANCE   3/31/2014  0000 UTC                       
  FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
- THU  20| FRI 21| SAT 22| SUN 23| MON 24| TUE 25| WED 26| THU 27 CLIMO
- X/N  75| 41  77| 41  76| 39  76| 40  80| 41  67| 45  64| 42  63 43 66
- TMP  73| 45  75| 45  75| 42  75| 43  78| 45  65| 48  62| 45  62      
- DPT  32| 32  27| 29  24| 30  32| 36  32| 35  36| 37  40| 35  35      
- CLD  CL| CL  CL| CL  CL| CL  CL| CL  CL| CL  PC| OV  OV| PC  PC      
- WND   6|  6  10|  7   9|  5   5|  4   7|  7  18| 18  17| 12  10      
- P12   2|  2   4|  6   1|  3   4|  2   2| 15  31| 37  30| 27  13999999
- P24    |      4|      6|      4|      2|     37|     61|     29   999
- Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
- Q24    |      0|      0|      0|      0|      1|       |             
- T12   1|  0   2|  0   1|  0   1|  1   3|  4   4|  5   6|  3   4      
- T24    |  1    |  2    |  1    |  1    |  4    | 11    |  8          
- PZP   0|  1   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0   0      
- PSN   0|  6   0| 13   0|  9   1|  7   0|  2   0|  0   1|  1   6      
- PRS   2| 14   2|  1   2|  3   1|  4   1|  2   4|  6   5|  9   6      
- TYP   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R|  R   R      
+ MON  31| TUE 01| WED 02| THU 03| FRI 04| SAT 05| SUN 06| MON 07 CLIMO
+ X/N  35| 12  36| 17  37| 21  39| 21  51| 28  58| 31  62| 35  60 29 54
+ TMP  29| 16  29| 21  36| 23  37| 22  48| 30  52| 35  56| 37  55      
+ DPT  12|  6  17| 17  23| 22  24| 21  26| 24  30| 26  32| 30  30      
+ CLD  OV| OV  OV| OV  OV| OV  OV| OV  CL| CL  PC| PC  PC| OV  OV      
+ WND  44| 18  13|  9  13| 10  13| 12   9|  8  15| 14  25| 21  21      
+ P12  80|  0  29| 10  25| 20  18| 10   4|  5   6|  9  14| 10   7 19 18
+ P24    |     29|     28|     28|     13|      8|     23|     16    27
+ Q12   2|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
+ Q24    |      0|      0|      0|      0|      0|       |             
+ T12   3|  0   2|  0   4|  1   2|  2   2|  2   3|  2   4|  4   4      
+ T24    |  3    |  2    |  4    |  2    |  2    |  5    |  5          
+ PZP   3|  2   2|  4   3|  4   3|  4   1|  3   0|  1   1|  1   0      
+ PSN  85| 91  92| 88  89| 69  59| 61  33| 24  30| 19  24| 27  34      
+ PRS   5|  4   3|  5   5| 17  18| 23  20| 12   9|  9   6|  6   6      
+ TYP   S|  S   S|  S   S|  S   S|  S  RS|  R   R|  R   R|  R   R      
  SNW    |      0|      0|      0|      0|      0|       |             
                                                                       
 
diff --git a/postWRF/bin/settings.py b/postWRF/bin/settings.py index 377983a..a20e6b4 100644 --- a/postWRF/bin/settings.py +++ b/postWRF/bin/settings.py @@ -4,8 +4,8 @@ def __init__(self): self.wrfout_root = '/chinook2/jrlawson/bowecho/' self.p_interp_root = '/home/jrlawson/fortrancode/P_INTERP/' self.RUC_root = '/chinook2/jrlawson/bowecho/' - self.DPI = 100.0 + #self.DPI = 100.0 self.plot_titles = 1 self.terrain = 0 self.colorbar = 1 - # + self.pickledir = '/home/jrlawson/data/sounding/WRFoutput/' diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index 9b97f9e..4ac7442 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -583,4 +583,10 @@ def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,V): print("Plotting time {0} from {1}.".format(n,len(times))) del data, stack + def plot_skewT(self,wrfout,plot_time,prof_lat,prof_lon,dom=1,save_output=0): + path_to_WRF = self.wrfout_files_in(C.wrfout_root) + W = WRFOut(path_to_WRF) + ST = SkewT(self.C) + ST.plot_skewT(W,plot_time,prof_lat,prof_lon,dom,save_output) + diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 406a693..e511f60 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -103,7 +103,7 @@ def plot2D(self,va,vardict,da=0,na=0): print("Need to sort other levels") raise Exception - lv_na = utils.get_level_naming(lv,va,vardict) + lv_na = utils.get_level_naming(va,**vardict) """ def plot_strongest_wind(self,dic): diff --git a/postWRF/postWRF/skewt.py b/postWRF/postWRF/skewt.py index e69de29..e21cae4 100644 --- a/postWRF/postWRF/skewt.py +++ b/postWRF/postWRF/skewt.py @@ -0,0 +1,247 @@ +# Run this script with and without uintah by changing pywrfplotParams variable 'uintah'. + +### IMPORTS +import numpy as N +import math +import matplotlib as M +M.use('Agg') +import matplotlib.pyplot as plt +import pdb +import cPickle as pickle +import sys + +# User imports + +from Params import T_zero,T_base,kappa,barb_increments,P_bot,outdir,ens +from Utils import gamma_s,td,e,openWRF,getDimensions,convert_time + +from WEM.utils import unix_tools +from WEM.utils import generalmet +from WEM.utils import gridded_data + +class SkewT(Figure): + def __init__(self,config): + self.C = config + + def plot_skewT(self,wrfout,plot_time,prof_lat,prof_lon,dom,save_output): + # OPTIONS + # plotting + self.W = wrfout + height, width = (10,10) + + datestr = utils.string_from_time('output',plot_time) + + # Pickle files are saved here: + pickledir = self.C.pickdir + pickle_fname = '_'.join('WRFsounding',datestr+'.p') + + # Top and bottom limit of Skew T plots (Pa) + P_b = 105000.0 + P_t = 20000.0 + + # Create figure + fig = plt.figure(figsize=(width,height)) + isotherms() + isobars() + dry_adiabats() + moist_adiabats() + + + t_idx = self.W.get_time_idx(plot_time,tuple_format=1) + + x,y, exact_lat, exact_lon = gridded_data.getXY(self.W.lats,self.W.lons,prof_lat,prof_lon) + P_slices = {'t': t_idx, 'lv': slice[None,None], 'la': y, 'lo': x} + H_slices = {'lv':0, 'la':y, 'lo':x} + P = self.W.get('pressure',P_slices) + elev = self.W.get('HGT',H_slices) + + thin_locs = gridded_data.thinned_barbs(P) + + ST._windbarbs(W.nc,t_idx,y,x,P,thin_locs,n=45,color='blue') + ST._temperature(nc,t_idx,y,x,P,linestyle='solid',color='blue') + ST._dewpoint(nc,t_idx,y,x,P,linestyle='dashed',color='blue') + + plt.xticks(xticks,['' if tick%10!=0 else str(tick) for tick in xticks]) + plt.yticks(yticks,ytix) + + plt.axis([-20,50,P_b,P_t]) + plt.xlabel(r'Temperature ($^{\circ}$C) at 1000\,hPa') + xticks = N.arange(-20,51,5) + plt.xticks(xticks,['' if tick%10!=0 else str(tick) for tick in xticks]) + plt.ylabel('Pressure (hPa)') + yticks = N.arange(P_bot,P_t-1,-10**4) + ytix = ["%4u" %(p/100.0) for p in yticks] + plt.yticks(yticks,ytix) + #yticks = N.arange(P_bot,P_t-1,-10**4) + #plt.yticks(yticks,yticks/100) + + plt.savefig(outdirfull+'verifSkewT_'+ens+'_'+nicetime+'.png',bbox_inches='tight',pad_inches=0.1) + plt.clf() + + # For saving Skew T data + if save_output: + u,v = ST.return_data('wind',nc,time,y,x,thin_locs) + T = ST.return_data('temp',nc,time,y,x,thin_locs,P=P) + Td = ST.return_data('dwpt',nc,time,y,x,thin_locs,P=P) + + data_dict = {'u':u,'v':v,'T':T,'Td':Td,'P':P} + with open(picklef,'wb') as p: + pickle.dump(data_dict,p) + + def skewTPlot(nest,timetuple): + """ + OLD VERSION!!!! + This is the method to use from the outside + + nest: The nesting level of the nc-file from WRF + time: The time for which to plot + """ + nc = openWRF(nest) + time = convert_time(nest,timetuple) + _Nx,_Ny,_Nz,_longs,_lats,_dx,_dy,x,y = getDimensions(nc) + + plt.figure() + _isotherms() + _isobars() + _dry_adiabats() + _moist_adiabats() + + P = nc.variables['P'][time,:,y,x] + nc.variables['PB'][time,:,y,x] + + _windbarbs(nc,time,y,x,P) + _temperature(nc,time,y,x,P) + _dewpoint(nc,time,y,x,P) + + plt.axis([-40,50,P_b,P_t]) + plt.xlabel('Temperature ($^{\circ}\! C$) at 1000hPa') + xticks = np.arange(-40,51,5) + plt.xticks(xticks,['' if tick%10!=0 else str(tick) for tick in xticks]) + plt.ylabel('Pressure (hPa)') + yticks = np.arange(P_bot,P_t-1,-10**4) + plt.yticks(yticks,yticks/100) + + sfcT = nc.variables['T2'][time,y,x]-T_zero + sfcP = nc.variables['PSFC'][time,y,x] + sfcW = nc.variables['Q2'][time,y,x] + sfcTd = td(e(sfcW,sfcP)) + plt.suptitle('Drybulb: %4.1f$^{\circ}\! C$ Dewpoint: %3.1f$^{\circ}\! C$ Pressure: %5.1f hPa' % (sfcT,sfcTd,0.01*sfcP), \ + fontsize=10, x = 0.5, y = 0.03) + + #plt.show() + plt.savefig(outdir+naming+str(nest)+'_skewT.png') + plt.title('Skew-T plot for (location)') + plt.close() + + def _skewnessTerm(P): + return skewness * np.log(P_bot/P) + + + + def _windbarbs(nc,time,y,x,P,thin_locs,n=45.0,color='black'): + uwind = 0.5*(nc.variables['U'][time,:,y,x]+nc.variables['U'][time,:,y,x+1]) + vwind = 0.5*(nc.variables['V'][time,:,y,x]+nc.variables['V'][time,:,y+1,x]) + zmax = len(uwind[thin_locs]) + delta = 1 + baraxis = [n for _j in range(0,zmax,delta)] + plt.barbs(baraxis,P[thin_locs],uwind[thin_locs],vwind[thin_locs], + barb_increments=barb_increments, linewidth = .75,color=color) + + def _windbarbs_real(uwind,vwind,P,delta=3,color='red',n=37.5): + # Is wind in kt or m/s? .... uwind* + those = N.where(uwind==-9999) # Find nonsense values + uwind = N.delete(uwind,those) + vwind = N.delete(vwind,those) + P = N.delete(P,those) + zmax = len(uwind) + # n is x-ax position on skewT for barbs. + baraxis = [n for _j in range(0,zmax,delta)] + plt.barbs(baraxis,P[0:zmax:delta],uwind[0:zmax:delta],vwind[0:zmax:delta], + barb_increments=barb_increments, linewidth = .75, barbcolor = color, flagcolor = color) + + def _temperature(nc,time,y,x,P,linestyle='solid',color='black'): + theta = nc.variables['T'][time,:,y,x] + T_base + T = theta*(P/P_bot)**kappa - T_zero # Temperatur i halvflatene (C) + plt.semilogy(T + _skewnessTerm(P), P, basey=math.e, color = color, \ + linestyle=linestyle, linewidth = 1.5) + + def _temperature_real(T,P,color='red',linestyle='dashed'): + plt.semilogy(T + _skewnessTerm(P), P, basey=math.e, color = color, \ + linestyle=linestyle, linewidth = 1.5) + + def _dewpoint(nc,time,y,x,P,linestyle='dashed',color='black'): + w = nc.variables['QVAPOR'][time,:,y,x] + plt.semilogy(td(e(w,P)) + _skewnessTerm(P), P, basey=math.e, color = color, \ + linestyle=linestyle, linewidth = 1.5) + + def _dewpoint_real(td,P,color='red',linestyle='dashed'): + plt.semilogy(td + _skewnessTerm(P), P, basey=math.e, color = color, \ + linestyle=linestyle, linewidth = 1.5) + + def return_data(whichdata,nc,time,y,x,thin_locs,P=None): + if whichdata == 'wind': + uwind = 0.5*(nc.variables['U'][time,:,y,x]+nc.variables['U'][time,:,y,x+1]) + vwind = 0.5*(nc.variables['V'][time,:,y,x]+nc.variables['V'][time,:,y+1,x]) + return uwind[thin_locs],vwind[thin_locs] + elif whichdata == 'temp': + theta = nc.variables['T'][time,:,y,x] + T_base + T = theta*(P/P_bot)**kappa - T_zero + return T + elif whichdata == 'dwpt': + w = nc.variables['QVAPOR'][time,:,y,x] + Td = td(e(w,P)) + return Td + else: + print 'Use valid variable.' + + def gettime(): + t = convert_time(dom,timetuple) + return t + + def _isotherms(): + for temp in np.arange(-140,50,10): + plt.semilogy(temp + _skewnessTerm(plevs), plevs, basey=math.e, \ + color = ('blue' if temp <= 0 else 'red'), \ + linestyle=('solid' if temp == 0 else 'dashed'), linewidth = .5) + + def _isobars(): + for n in np.arange(P_bot,P_t-1,-10**4): + plt.plot([-40,50], [n,n], color = 'black', linewidth = .5) + + def _dry_adiabats(): + for tk in T_zero+np.arange(-30,210,10): + dry_adiabat = tk * (plevs/P_bot)**kappa - T_zero + _skewnessTerm(plevs) + plt.semilogy(dry_adiabat, plevs, basey=math.e, color = 'brown', \ + linestyle='dashed', linewidth = .5) + + def _moist_adiabats(): + ps = [p for p in plevs if p<=P_bot] + for temp in np.concatenate((np.arange(-40.,10.1,5.),np.arange(12.5,45.1,2.5))): + moist_adiabat = [] + for p in ps: + temp -= dp*gamma_s(temp,p) + moist_adiabat.append(temp + _skewnessTerm(p)) + plt.semilogy(moist_adiabat, ps, basey=math.e, color = 'green', \ + linestyle = 'dotted', linewidth = .5) + + + + """ + Plots skewT-lnP-diagram from WRF-output file. + @author Geir Arne Waagbø + @see http://code.google.com/p/pywrfplot/ + + Formulas taken from Rogers&Yau: A short course in cloud physics (Third edition) + Some inspiration from: + http://www.atmos.washington.edu/~lmadaus/pyscript/plot_wrf_skewt.txt + + from Params import T_zero,T_base,kappa,barb_increments,P_bot, naming, outdir + from Utils import gamma_s,td,e,openWRF,getDimensions,convert_time + + skewness = 37.5 + # Defines the ranges of the plot, do not confuse with P_bot and P_top + P_b = 105000. + P_t = 10000. + dp = 100. + plevs = np.arange(P_b,P_t-1,-dp) + """ + diff --git a/utils/generalmet.py b/utils/generalmet.py index 1f0eaf5..f202440 100644 --- a/utils/generalmet.py +++ b/utils/generalmet.py @@ -1,7 +1,7 @@ import numpy as N import pdb -def decompose_wind(wspd,wdir): +def decompose_wind(wspd,wdir,convert=0): # Split wind speed/wind direction into u,v if (type(wspd) == N.array) & (type(wdir) == N.array): uwind = N.array([-s * N.sin(N.radians(d)) if ((s>-1)&(d>-1)) else -9999 @@ -11,6 +11,17 @@ def decompose_wind(wspd,wdir): else: uwind = -wspd * N.sin(N.radians(wdir)) vwind = -wspd * N.cos(N.radians(wdir)) + if convert == 'ms_kt': + uwind *= 1.94384449 + vwind *= 1.94384449 + elif convert == 'ms_mph': + uwind *= 2.23694 + vwind *= 2.23694 + elif convert == 'kt_ms': + uwind *= 0.51444444 + vwind *= 0.51444444 + else: + pass return uwind, vwind def combine_wind_components(u,v): @@ -21,3 +32,13 @@ def combine_wind_components(u,v): def convert_kt2ms(wspd): wspd_ms = wspd*0.51444 return wspd_ms + +def dewpoint(T,RH): # Lawrence 2005 BAMS? + #T in C + #RH in 0-100 format + es = 6.11 * (10**((7.5*T)/(237.7+T))) + e = es * RH/100.0 + alog = 0.43429*N.log(e) - 0.43429*N.log(6.11) + Td = (237.7 * alog)/(7.5-alog) + #pdb.set_trace() + return Td diff --git a/utils/utils.py b/utils/utils.py index 7172364..271702d 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -91,7 +91,8 @@ def lookup_time(str): D = {'year':0, 'month':1, 'day':2, 'hour':3, 'minute':4, 'second':5} return D[str] -def get_level_naming(lv,va,**kwargs): +def get_level_naming(va,**kwargs): + lv = kwargs['lv'] if lv < 1500: return str(lv)+'hPa' elif lv == 2000: From 6a9425d7d1524d193f610adb8e9a805a6273b3d7 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Fri, 4 Apr 2014 11:08:10 -0500 Subject: [PATCH 075/111] Plot ECMWF; fixed DKE/DTE --- postWRF/bin/.DKE.py.swo | Bin 0 -> 12288 bytes postWRF/bin/DKE.py | 48 ++++++----- postWRF/bin/DKE_settings.py | 4 +- postWRF/bin/settings.py | 2 + postWRF/postWRF/__init__.py | 125 ++++++++++++++++------------- postWRF/postWRF/birdseye.py | 13 ++- postWRF/postWRF/ecmwf.py | 153 ++++++++++++++++++++++++++++++++++++ 7 files changed, 261 insertions(+), 84 deletions(-) create mode 100644 postWRF/bin/.DKE.py.swo create mode 100644 postWRF/postWRF/ecmwf.py diff --git a/postWRF/bin/.DKE.py.swo b/postWRF/bin/.DKE.py.swo new file mode 100644 index 0000000000000000000000000000000000000000..566386d9d1058671d07b1e299a3c9a6ee230439c GIT binary patch literal 12288 zcmeI2&2Jnv7{(oLZ3#qh;b=CMX1AJnKhma==$1pefe2MXNt$vSIWwMJCp-4Y_T&R1 zdgs8A6Noc#=LCO8`4c#DMjUuNJG*Jqz@?{39_1O&*nU0F`?DP>iVu4SJCEsoHXyiG z2>Ipt!@Y~;l^f6dgdCYEKebx7VTyi%_oKAeWjSo6x^?(kk;c6_}y?8L_;nwS+nr><3bY*BR z&qPK!3{Hd<^n_05!78x+p6ruFvFH zD&b^$>H5^51)^#A89WpCi;tWqQ2jnGSK&377RqRMh)y9P6a?IcfNhC&V7pL5ccxS6aYL5u0 zoSa%V_OpaT$w^(^mcEGd`o3{zeZwQK-w@Cq^!n@ZX58P1`-8X_qp9xxV$(fe?^_oB z8Z_usve|P|=a<0dUC81_44QGxE)CDaV?azaEZ2`(^Rf5bX0-*J)~UDTEBD3d0N z7w+d4V|1JfHH;=$O_868Csr5=#t!io8DKaZy&kC(iPPH+(+-)jMcZD}6O*w@ci=(t zsl-a0k3hM~sF+0;LODWS*f)sxj+-5Y0rtKBSqDOPw$L7ZGTPqT9Yn-WoqzZIdz<}x z5h1cDwQ&?D#Mx{f>pSIkHm$^GU$-{r$5~U-dEg?iSy!yHWZ}E%GtUpc&zr@?v9MLv zj=B?Fi0-wA)}szx*z+0At)?8XGj8VZSkLoEZRbz;5NWkqMCY@eV4XJD9999: - print("Skipping #{0} for debugging.".format(n)) + print("No. {0} from {1} permutations".format(n,nperm)) + perm_start = time.time() + DATA[str(n)] = {} + f1, f2 = perm + W1 = WRFOut(f1) + W2 = WRFOut(f2) + print('WRFOuts loaded.') + #pdb.set_trace() + # Make sure times are the same in both files + if not N.all(N.array(W1.wrf_times) == N.array(W2.wrf_times)): + print("Times are not identical between input files.") + raise Exception else: - print("No. {0} from {1} permutations".format(n,nperm)) - perm_start = time.time() - DATA[str(n)] = {} - f1, f2 = perm - W1 = WRFOut(f1) - W2 = WRFOut(f2) - #pdb.set_trace() - # Make sure times are the same in both files - if not N.all(N.array(W1.wrf_times) == N.array(W2.wrf_times)): - print("Times are not identical between input files.") - raise Exception - else: - print("Passed check for identical timestamps between " - "NetCDF files") - - # Find indices of each time - t_idx = [] - for t in ts: - t_idx.append(W1.get_time_idx(t)) - - print("Calculating values now...") - DATA[str(n)]['times'] = ts - DATA[str(n)]['values'] = [] - for t in t_idx: - DATA[str(n)]['values'].append(PLOTS[ptype](W1.nc,W2.nc,t, - energy,lower,upper)) - DATA[str(n)]['file1'] = f1 - DATA[str(n)]['file2'] = f2 - - print "Calculation #{0} took {1:2.2f} seconds.".format(n,time.time()-perm_start) + print("Passed check for identical timestamps between " + "NetCDF files") + + # Find indices of each time + print('Finding time indices') + t_idx = [] + for t in ts: + t_idx.append(W1.get_time_idx(t)) + + print("Calculating values now...") + DATA[str(n)]['times'] = ts + DATA[str(n)]['values'] = [] + for t in t_idx: + DATA[str(n)]['values'].append(PLOTS[ptype](W1.nc,W2.nc,t, + energy,lower,upper)) + DATA[str(n)]['file1'] = f1 + DATA[str(n)]['file2'] = f2 + + print "Calculation #{0} took {1:2.2f} seconds.".format(n,time.time()-perm_start) if d_return and not d_save: return DATA @@ -485,27 +487,39 @@ def DE_z(self,nc0,nc1,t,energy,lower,upper): # loading it to a variable yet # WIND - U0 = nc0.variables['U'][:] - V0 = nc0.variables['V'][:] - U1 = nc1.variables['U'][:] - V1 = nc1.variables['V'][:] + U0 = nc0.variables['U'][t,...] + U1 = nc1.variables['U'][t,...] + Ud = U0 - U1 + #del U0, U1 + + V0 = nc0.variables['V'][t,...] + V1 = nc1.variables['V'][t,...] + Vd = V0 - V1 + #del V0, V1 # PERT and BASE PRESSURE - P0 = nc0.variables['P'][:] - PB0 = nc0.variables['PB'][:] - P1 = nc1.variables['P'][:] - PB1 = nc1.variables['PB'][:] + if lower or upper: + P0 = nc0.variables['P'][t,...] + PB0 = nc0.variables['PB'][t,...] + Pr = P0 + PB0 + #del P0, PB1 + # Here we assume pressure columns are + # roughly the same between the two... if energy=='total': - T0 = nc0.variables['T'][:] - T1 = nc1.variables['T'][:] + T0 = nc0.variables['T'][t,...] + T1 = nc1.variables['T'][t,...] + Td = T0 - T1 + #del T0, T1 + R = 287.0 # Universal gas constant (J / deg K * kg) Cp = 1004.0 # Specific heat of dry air at constant pressure (J / deg K * kg) kappa = R/Cp + print('Temp data has been loaded.') - xlen = U0.shape[2] # 1 less than in V - ylen = V0.shape[3] # 1 less than in U - zlen = U0.shape[1] # identical in U & V + xlen = Ud.shape[1] # 1 less than in V + ylen = Vd.shape[2] # 1 less than in U + zlen = Ud.shape[0] # identical in U & V # Generator for lat/lon points def latlon(nlats,nlons): @@ -520,28 +534,27 @@ def latlon(nlats,nlons): gridpts = latlon(xlen,ylen) for gridpt in gridpts: i,j = gridpt - # print("Calculating for gridpoints {0} & {1}.".format(i,j)) # Find closest level to 'lower', 'upper' - P_col = P0[t,:,j,i] + PB0[t,:,j,i] + if lower or upper: + P_col = Pr[:,j,i] if lower: low_idx = utils.closest(P_col,lower*100.0) else: low_idx = None if upper: - upp_idx = utils.closest(P_col,upper*100.0) + upp_idx = utils.closest(P_col,upper*100.0)+1 else: upp_idx = None - zidx = slice(low_idx,upp_idx+1) - # This needs to be a 2D array? + zidx = slice(low_idx,upp_idx) if energy=='kinetic': - DKE2D[j,i] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + - (V0[t,zidx,j,i]-V1[t,zidx,j,i])**2)) + DKE2D[j,i] = N.sum(0.5*((Ud[zidx,j,i])**2 + + (Vd[zidx,j,i])**2)) elif energy=='total': - DKE2D[j,i] = N.sum(0.5*((U0[t,zidx,j,i]-U1[t,zidx,j,i])**2 + - (V0[t,zidx,j,i]-V1[t,zidx,j,i])**2 + - kappa*(T0[t,zidx,j,i]-T1[t,zidx,j,i])**2)) + DKE2D[j,i] = N.sum(0.5*((Ud[zidx,j,i])**2 + + (Vd[zidx,j,i])**2 + + kappa*(Td[zidx,j,i])**2)) DKE.append(DKE2D) @@ -573,7 +586,7 @@ def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,V): stack = data else: stack = N.dstack((data,stack)) - stack_average = N.average(stack,axis=2) + stack_average = N.average(stack,axis=2) #birdseye plot with basemap of DKE/DTE F = BirdsEye(self.C,W1,p2p) # 2D figure class diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index e511f60..f0d96e9 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -18,7 +18,18 @@ def __init__(self,config,wrfout): self.p2p = self.C.output_root print self.p2p - def plot_data(self,data,mplcommand,fname,pt,V=0): + def plot_data(self,data,lats,lons,mplcommand,fpath,V=0): + """ + Generic method that plots any matrix of data on a map + + Inputs: + data : lat/lon matrix of data + m : basemap instance + mplcommand : contour or contourf + fpath : absolute filepath including name + V : scale for contours + + """ # INITIALISE self.fig = plt.figure() self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user diff --git a/postWRF/postWRF/ecmwf.py b/postWRF/postWRF/ecmwf.py new file mode 100644 index 0000000..d109abc --- /dev/null +++ b/postWRF/postWRF/ecmwf.py @@ -0,0 +1,153 @@ +import calendar +from netCDF4 import Dataset +import matplotlib.pyplot as plt +from defaults import Defaults +from mpl_toolkits.basemap import Basemap +import numpy as N +import WEM.utils.utils as utils +import os + +class ECMWF: + def __init__(self,fpath,config): + self.C = config + self.D = Defaults() + self.ec = Dataset(fpath,'r') + self.times = self.ecmwf_times() + # self.dx = + # self.dy = + self.lats = self.ec.variables['g0_lat_2'][:] #N to S + self.lons = self.ec.variables['g0_lon_3'][:] #W to E + self.lvs = self.ec.variables['lv_ISBL1'][:] #jet to sfc + self.fields = self.ec.variables.keys() + self.dims = self.ec.variables['Z_GDS0_ISBL'].shape + self.x_dim = self.dims[3] + self.y_dim = self.dims[2] + self.z_dim = self.dims[1] + # Times, levels, lats, lons + + def ecmwf_times(self): + ec_t = self.ec.variables['initial_time0_hours'][:] + t = (ec_t*3600.0) - (1490184.0*3600.0) + return t + + def find_level_idx(self,lv): + lvs = list(self.lvs) + lv_idx = lvs.index(lv) + return lv_idx + + def find_time_idx(self,t): + if isinstance(t,int): + pass # It's a datenum + else: + t = calendar.timegm(t) + ts = list(self.times) + return ts.index(t) + + def get_key(self,va): + keys = {} + keys['Z'] = 'Z_GDS0_ISBL' + keys['W'] = 'W_GDS0_ISBL' + return keys[va] + + def get(self,va,lv,t): + t_idx = self.find_time_idx(t) + lv_idx = self.find_level_idx(lv) + if va == 'wind': + u = self.ec.variables['U_GDS0_ISBL'][t_idx,lv_idx,...] + v = self.ec.variables['V_GDS0_ISBL'][t_idx,lv_idx,...] + data = N.sqrt(u**2 + v**2) + else: + k = self.get_key(va) + data = self.ec.variables[k][t_idx,lv_idx,...] + return data + + + def plot(self,va,lv,times,**kwargs): + for t in times: + + fig = plt.figure() + data = self.get(va,lv,t) + m, x, y = self.basemap_setup() + + if 'scale' in kwargs: + S = kwargs['scale'] + f1 = m.contour(x,y,data,S,colors='k') + else: + f1 = m.contour(x,y,data,colors='k') + + if self.C.plot_titles: + title = utils.string_from_time('title',t) + plt.title(title) + + if 'wind_overlay' in kwargs: + jet = kwargs['wind_overlay'] + wind = self.get('wind',lv,t) + windplot = m.contourf(x,y,wind,jet,alpha=0.6) + plt.colorbar(windplot) + + elif 'W_overlay' in kwargs: + Wscale = kwargs['W_overlay'] + W = self.get('W',lv,t) + windplot = m.contourf(x,y,W,alpha=0.6) + plt.colorbar(windplot) + + + # if self.C.colorbar: + # plt.colorbar(orientation='horizontal') + + datestr = utils.string_from_time('output',t) + fname = '_'.join(('ECMWF',va,str(lv),datestr)) + '.png' + + print("Plotting {0} at {1} for {2}".format( + va,lv,datestr)) + + plt.clabel(f1, inline=1, fmt='%4u', fontsize=12, colors='k') + + utils.trycreate(self.C.output_root) + plt.savefig(os.path.join(self.C.output_root,fname)) + plt.clf() + plt.close() + + + + + def basemap_setup(self,**kwargs): + # Fetch settings + basemap_res = getattr(self.C,'basemap_res',self.D.basemap_res) + lllat = self.lats[-1] + lllon = self.lons[0] + urlat = self.lats[0] + urlon = self.lons[-1] + + if 'Wlim' in kwargs: + lllat = kwargs['Slim'] + lllon = kwargs['Wlim'] + urlat = kwargs['Nlim'] + urlon = kwargs['Elim'] + + # dx = 13.0 + # dy = 13.0 + # x_dim = lats.shape[0] + # y_dim = lats.shape[1] + # width_m = dx*(x_dim-1) + # height_m = dy*(y_dim-1) + lat0 = self.lats[self.y_dim/2] + lon0 = self.lons[self.x_dim/2] + + m = Basemap( + projection='merc', + llcrnrlon=lllon,llcrnrlat=lllat, + urcrnrlon=urlon,urcrnrlat=urlat, + lat_0=lat0,lon_0=lon0, + resolution=basemap_res,area_thresh=500 + ) + m.drawcoastlines() + m.drawstates() + m.drawcountries() + + # Draw meridians etc with wrff.lat/lon spacing + # Default should be a tenth of width of plot, rounded to sig fig + # pdb.set_trace() + mx, my = N.meshgrid(self.lons,self.lats) + x,y = m(mx,my) + return m, x, y From 2061842259afea952def064bd74b119a39c8179f Mon Sep 17 00:00:00 2001 From: John Lawson Date: Sat, 5 Apr 2014 22:32:43 -0500 Subject: [PATCH 076/111] Added WPS-only option to lazyWRF --- lazyWRF/lazyWRF/__init__.py | 11 ++++++++--- postWRF/bin/DKE.py | 35 +++++++++++++++++++++++------------ postWRF/bin/DKE_settings.py | 2 +- postWRF/postWRF/__init__.py | 4 ++-- postWRF/postWRF/birdseye.py | 10 ++++++++-- 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/lazyWRF/lazyWRF/__init__.py b/lazyWRF/lazyWRF/__init__.py index 86a9405..4988a1d 100644 --- a/lazyWRF/lazyWRF/__init__.py +++ b/lazyWRF/lazyWRF/__init__.py @@ -10,7 +10,7 @@ class Lazy: def __init__(self,config): self.C = config - def go(self,casestr,IC,experiment,ensnames): + def go(self,casestr,IC,experiment,ensnames,**kwargs): """ Inputs: (all folder names) casestr : string of case study initialisation date/time @@ -24,6 +24,9 @@ def go(self,casestr,IC,experiment,ensnames): -initial condition model/ens member for others ensnames : list of ensemble member names - e.g. c00,p01,p02 + + **kwargs include: + WPS_only : stop after linking met_em files to WRF folder """ self.casestr = casestr @@ -47,9 +50,9 @@ def go(self,casestr,IC,experiment,ensnames): ensemble type. """ - self.GO[IC](self.ensnames) + self.GO[IC](self.ensnames,**kwargs) - def go_GEFSR2(self,ensns): + def go_GEFSR2(self,ensns,**kwargs): """ Runs WPS, WRF for one set of initial conditions @@ -102,6 +105,8 @@ def go_GEFSR2(self,ensns): self.edit_namelist('wps',"fg_name"," fg_name = 'GEFSR2','SOIL' ") self.run_exe('metgrid.exe') + if 'WPS_only' in kwargs: + continue # This is where the magic happens etc etc #self.submit_job()[0] <--- why was this [0] here? self.submit_job() diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index 27be0e8..8f0622c 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -13,7 +13,11 @@ from WEM.postWRF.postWRF import WRFEnviron import WEM.utils.utils as utils -case = '20130815' +#case = '20060526' +#case = '20090910' +case = '20110419' +#case = '20130815' + IC = 'GEFSR2' # Time script @@ -23,23 +27,30 @@ config = Settings() p = WRFEnviron(config) -# User settings -init_time = p.string_from_time('dir',(2013,8,15,0,0,0),strlen='hour') runfolder = os.path.join(config.wrfout_root,case,IC) path_to_wrfouts = p.wrfout_files_in(runfolder,dom=1) -itime = (2013,8,15,0,0,0) -ftime = (2013,8,16,12,0,0) -times = utils.generate_times(itime,ftime,6*3600) -path_to_plots = os.path.join(config.output_root,case,IC) +#itime = (2006,5,26,0,0,0) +#ftime = (2006,5,27,12,0,0) + +#itime = (2009,9,10,0,0,0) +#ftime = (2009,9,11,15,0,0) + +itime = (2011,4,19,0,0,0) +ftime = (2011,4,20,15,0,0) + +#itime = (2013,8,15,0,0,0) +#ftime = (2013,8,16,12,0,0) + +times = utils.generate_times(itime,ftime,3*3600) +config.output_root = os.path.join(config.output_root,case,IC) #pdb.set_trace() -# Produce .npy data files with DKE data -print("Compute_diff_energy...") -p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times,#upper=700, +p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times, d_save=runfolder, d_return=0,d_fname='DTE') + # Contour fixed at these values -#V = range(0,2200,200) -#p.plot_diff_energy('sum_z','total',times,runfolder,'DTE',path_to_plots,V) +V = range(500,5000,250) +p.plot_diff_energy('sum_z','total',times,runfolder,'DTE',config.output_root,V) print "Script took", time.time()-scriptstart, "seconds." diff --git a/postWRF/bin/DKE_settings.py b/postWRF/bin/DKE_settings.py index 245e9cd..f538188 100644 --- a/postWRF/bin/DKE_settings.py +++ b/postWRF/bin/DKE_settings.py @@ -1,7 +1,7 @@ class Settings: def __init__(self): # Required settings: - self.output_root = '/home/jrlawson/public_html/' + self.output_root = '/home/jrlawson/public_html/bowecho/' self.wrfout_root = '/chinook2/jrlawson/bowecho/' # Optional settings: self.DPI = 250.0 diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index b68f165..47250f0 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -589,10 +589,10 @@ def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,V): stack_average = N.average(stack,axis=2) #birdseye plot with basemap of DKE/DTE - F = BirdsEye(self.C,W1,p2p) # 2D figure class + F = BirdsEye(self.C,W1) # 2D figure class #F.plot2D(va,t,en,lv,da,na) # Plot/save figure fname_t = ''.join((fname,'_p{0:02d}'.format(n))) - F.plot_data(stack_average,'contour',fname_t,t,V) + F.plot_data(stack_average,'contourf',fname_t,t,V) print("Plotting time {0} from {1}.".format(n,len(times))) del data, stack diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index f0d96e9..9fc6915 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -18,7 +18,7 @@ def __init__(self,config,wrfout): self.p2p = self.C.output_root print self.p2p - def plot_data(self,data,lats,lons,mplcommand,fpath,V=0): + def plot_data(self,data,mplcommand,fpath,pt,V=0): """ Generic method that plots any matrix of data on a map @@ -40,6 +40,12 @@ def plot_data(self,data,lats,lons,mplcommand,fpath,V=0): self.bmap.contour(x,y,data) else: self.bmap.contour(x,y,data,V) + elif mplcommand == 'contourf': + if not V: + self.bmap.contourf(x,y,data,alpha=0.5) + else: + self.bmap.contourf(x,y,data,V,alpha=0.5) + # LABELS, TITLES etc @@ -54,7 +60,7 @@ def plot_data(self,data,lats,lons,mplcommand,fpath,V=0): # SAVE FIGURE datestr = utils.string_from_time('output',pt,tupleformat=0) - self.fname = self.create_fname(fname) # No da variable here + self.fname = self.create_fname(fpath) # No da variable here self.save(self.fig,self.p2p,self.fname) self.fig.clf() From b2d2590930db6a4e5ab419f22f9adf4069c15e53 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Sat, 5 Apr 2014 23:21:34 -0500 Subject: [PATCH 077/111] Rename README to README.md --- lazyWRF/{README => README.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lazyWRF/{README => README.md} (100%) diff --git a/lazyWRF/README b/lazyWRF/README.md similarity index 100% rename from lazyWRF/README rename to lazyWRF/README.md From 7ae6282bd8352645223e550853e852105f4cf0d1 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 8 Apr 2014 13:31:26 -0500 Subject: [PATCH 078/111] Added streamline plots for WRF data --- postWRF/bin/DKE.py | 25 ++++++++++----------- postWRF/postWRF/__init__.py | 12 +++++++++++ postWRF/postWRF/birdseye.py | 43 +++++++++++++++++++++++++++++++++++++ postWRF/postWRF/figure.py | 1 + 4 files changed, 69 insertions(+), 12 deletions(-) diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index 8f0622c..56dc4cd 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -15,11 +15,12 @@ #case = '20060526' #case = '20090910' -case = '20110419' -#case = '20130815' - -IC = 'GEFSR2' +#case = '20110419' +case = '20130815' +IC = 'NAM' +experiment = 'MXMP' +ens = 'anl' # Time script scriptstart = time.time() @@ -37,20 +38,20 @@ #itime = (2009,9,10,0,0,0) #ftime = (2009,9,11,15,0,0) -itime = (2011,4,19,0,0,0) -ftime = (2011,4,20,15,0,0) +#itime = (2011,4,19,0,0,0) +#ftime = (2011,4,20,15,0,0) -#itime = (2013,8,15,0,0,0) -#ftime = (2013,8,16,12,0,0) +itime = (2013,8,15,0,0,0) +ftime = (2013,8,16,12,0,0) times = utils.generate_times(itime,ftime,3*3600) config.output_root = os.path.join(config.output_root,case,IC) #pdb.set_trace() -p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times, - d_save=runfolder, d_return=0,d_fname='DTE') +#p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times, +# d_save=runfolder, d_return=0,d_fname='DTE_MXMP') # Contour fixed at these values -V = range(500,5000,250) -p.plot_diff_energy('sum_z','total',times,runfolder,'DTE',config.output_root,V) +#V = range(500,5000,250) +p.plot_diff_energy('sum_xyz','total',times,runfolder,'DTE_MXMP',config.output_root,V) print "Script took", time.time()-scriptstart, "seconds." diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index 47250f0..dc72a98 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -562,6 +562,7 @@ def latlon(nlats,nlons): def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,V): """ + folder : directory holding computed data fname : naming scheme of required files V : constant values to contour at @@ -602,4 +603,15 @@ def plot_skewT(self,wrfout,plot_time,prof_lat,prof_lon,dom=1,save_output=0): ST = SkewT(self.C) ST.plot_skewT(W,plot_time,prof_lat,prof_lon,dom,save_output) + def plot_streamlines(self,lv,times): + wrfpath = self.wrfout_files_in(self.C.wrfout_root)[0] + self.W = WRFOut(wrfpath) + self.F = BirdsEye(self.C,self.W) + for pt in times: + disp_t = utils.string_from_time('title',pt) + print("Plotting {0} at lv {1} for time {2}.".format( + 'streamlines',lv,disp_t)) + self.F.plot_streamlines(lv,pt) + + diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 9fc6915..16d7dd4 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -183,6 +183,49 @@ def plot_strongest_wind(self,dic): self.save(self.fig,self.p2p,self.fname) plt.close() + def plot_streamlines(self,lv,pt,da=0): + self.fig = plt.figure() + m,x,y = self.basemap_setup() + + time_idx = self.W.get_time_idx(pt) + + if lv==2000: + lv_idx = 0 + else: + print("Only support surface right now") + raise Exception + + lat_sl, lon_sl = self.get_limited_domain(da) + + slices = {'t': time_idx, 'lv': lv_idx, 'la': lat_sl, 'lo': lon_sl} + + u = self.W.get('U',slices)[0,0,:,:] + v = self.W.get('V',slices)[0,0,:,:] + + # pdb.set_trace() + + #div = N.sum(N.dstack((N.gradient(u)[0],N.gradient(v)[1])),axis=2)*10**4 + #vort = (N.gradient(v)[0] - N.gradient(u)[1])*10**4 + #pdb.set_trace() + lv_na = utils.get_level_naming('wind',lv=2000) + + m.streamplot(x[self.W.x_dim/2,:],y[:,self.W.y_dim/2],u,v, + density=2.5,linewidth=0.75,color='k') + #div_Cs = N.arange(-30,31,1) + #divp = m.contourf(x,y,vort,alpha=0.6) + #divp = m.contour(x,y,vort) + + #plt.colorbar(divp,orientation='horizontal') + if self.C.plot_titles: + title = utils.string_from_time('title',pt) + plt.title(title) + datestr = utils.string_from_time('output',pt) + na = ('streamlines',lv_na,datestr) + self.fname = self.create_fname(*na) + self.save(self.fig,self.p2p,self.fname) + plt.clf() + plt.close() + def basemap_setup(self): # Fetch settings basemap_res = getattr(self.C,'basemap_res',self.D.basemap_res) diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index 188311f..61271ee 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -40,6 +40,7 @@ def figsize(self,defwidth,defheight,fig): return fig def save(self,fig,p2p,fname): + # fig.tight_layout() utils.trycreate(p2p) fpath = os.path.join(p2p,fname) #self.fig.savefig(fpath) From 4a1602924084691337cc3bd54240a8dc4217c845 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Sun, 13 Apr 2014 12:04:49 -0500 Subject: [PATCH 079/111] Updates... --- "postWRF/bin/\\" | 98 ++++++++++ postWRF/postWRF/__init__.py | 2 +- postWRF/postWRF/birdseye.py | 57 ++++-- postWRF/postWRF/figure.py | 11 +- postWRF/postWRF/rucplot.py | 347 ++++++++++++++++++++++++++++-------- postWRF/postWRF/scales.py | 7 + postWRF/postWRF/wrfout.py | 15 +- utils/gridded_data.py | 6 +- 8 files changed, 439 insertions(+), 104 deletions(-) create mode 100644 "postWRF/bin/\\" diff --git "a/postWRF/bin/\\" "b/postWRF/bin/\\" new file mode 100644 index 0000000..1e0b5a5 --- /dev/null +++ "b/postWRF/bin/\\" @@ -0,0 +1,98 @@ +import os +import pdb +import sys +sys.path.append('/home/jrlawson/gitprojects/') + +from WEM.postWRF.postWRF import WRFEnviron +from settings import Settings +import WEM.utils.utils as utils +from WEM.postWRF.postWRF.rucplot import RUCPlot + +config = Settings() +p = WRFEnviron(config) + +#case = '20060526' +#case = '20090910' +#case = '20110419' +case = '20130815' + +IC = 'GEFSR2' +#IC = 'NAM' +#IC = 'RUC' +ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] +#ensnames = ['p'+"%02d" %n for n in range(8,11)] +#ensnames = ['p09',] +#ensnames = ['anl'] +#experiments = ['Kessler','Ferrier',]#'WSM5','WDM5','Lin','WDM6_Grau','WDM6_Hail','Morrison_Grau','Morrison_Hail'] +#experiments = ['WSM6_Grau','WSM6_Hail','Kessler','Ferrier','WSM5','WDM5','Lin','WDM6_Grau','WDM6_Hail','Morrison_Grau','Morrison_Hail'] +experiments = 'ICBC' +#experiment = 'VERIF' + +#itime = (2006,5,26,22,0,0) +#itime = (2009,9,10,23,0,0) +#itime = (2011,4,19,18,0,0) +itime = (2013,8,15,0,0,0) + +#ftime = (2006,5,27,11,0,0) +#ftime = (2009,9,11,14,0,0) +#ftime = (2011,4,20,10,30,0) +ftime = (2013,8,16,13,0,0) + +times = utils.generate_times(itime,ftime,60*60) +#shear_times = utils.generate_times(itime,ftime,3*60*60) +#sl_times = utils.generate_times(sl_itime,sl_ftime,1*60*60) +#thresh = 10 + +#variables = {'cref':{},'shear':{}, 'strongestwind':{}} +#variables['cref'] = {'lv':2000,'pt':times} +#variables['wind10'] = {'lv':2000,'pt':times} +#variables['buoyancy'] = {'lv':2000,'pt':times} +#variables['shear'] = {'top':3,'bottom':0,'pt':shear_times} +#variables['strongestwind'] = {'lv':2000, 'itime':itime, 'ftime':ftime, 'range':(thresh,27.5,1.25)} +# variables = {'shear':{'pt':shear_times, 'top':3, 'bottom':0}} +# variables = {'thetae':{'lv':2000,'pt':times}, 'CAPE':{'pt':times}} +# variables = {'cref':{'lv':2000,'pt':times}, 'shear':{'pt':shear_times, 'top':3, 'bottom':0}} +variables = {'PSFC':{'lv':2000,'pt':times,'plottype':'contour'}} + +#shear06 = {'shear':{'top':6,'bottom':0,'pt':shear_times}} + + +for en in ensnames: + for ex in experiments: + # Reload settings + p.C = Settings() + + # Change paths to new location + p.C.output_root = os.path.join(config.output_root,case,IC,en,ex) + p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,ex) + p.plot_2D(variables) + +""" + +for en in ensnames: + # Reload settings + p.C = Settings() + # Change paths to new location + p.C.output_root = os.path.join(config.output_root,case,IC,en,experiment) + p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,experiment) + #p.plot_2D(variables) + print p.C.wrfout_root + p.plot_streamlines(2000,sl_times) + + +# RUC file is one-per-time so .nc file is specified beforehand +en = ensnames[0] +RC = Settings() +RC.output_root = os.path.join(config.output_root,case,IC,en,experiment) +RC.path_to_RUC = os.path.join(config.RUC_root,case,IC,en,experiment) +WRF_dir = os.path.join(config.wrfout_root,case,'NAM',en,'ICBC') + +variables = ['streamlines',] +level = 2000 + +for t in sl_times: + RUC = RUCPlot(RC,t,wrfdir=WRF_dir) + #limits = RUC.colocate_WRF_map(WRF_dir) + RUC.plot(variables,level) +""" + diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index dc72a98..36c4960 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -119,7 +119,7 @@ def plot_2D(self,dic): llo : left limit of longitude rlo : right limit of longitude ---> if these are missing, default to 'all points' - + plottype : contourf by default. """ diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 16d7dd4..9e93b4e 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -83,17 +83,20 @@ def plot2D(self,va,vardict,da=0,na=0): llo : left limit of longitude rlo : right limit of longitude + plottype : contourf by default """ # INITIALISE #en = self.W.path self.fig = plt.figure() self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user - self.bmap,x,y = self.basemap_setup() + sm = vardict.get('smooth',1) + self.bmap,x,y = self.basemap_setup(smooth=sm) # Unpack dictionary lv = vardict['lv'] pt = vardict['pt'] + plottype = vardict.get('plottype','contourf') # pdb.set_trace() # Get indices for time, level, lats, lons @@ -108,7 +111,10 @@ def plot2D(self,va,vardict,da=0,na=0): time_idx = self.W.get_time_idx(pt) # LAT/LON - lat_sl, lon_sl = self.get_limited_domain(da) + #if 'smooth' in vardict: + #s = vardict['smooth'] + + lat_sl, lon_sl = self.get_limited_domain(da,smooth=sm) # LEVEL vc = vardict['vc'] @@ -149,27 +155,42 @@ def plot_strongest_wind(self,dic): # COLORBAR, CONTOURING cm, clvs = scales.get_cm(va,**vardict) + multiplier = scales.get_multiplier(va,**vardict) # Override contour levels if specified # clvs = vardict.get('range',clvs_default) # pdb.set_trace() if cm: - self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,cmap=cm) + plotargs = (x,y,data.reshape((la_n,lo_n)),clvs) + cmap = cm + #self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,{'cmap':cm}) elif isinstance(clvs,N.ndarray): - self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,cmap=plt.cm.jet) - # elif isinstance(clvs,tuple) or isinstance(clvs,list): - # N.array(clvs) - # self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,cmap=plt.cm.jet) + #self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,cmap=plt.cm.jet) + if plottype == 'contourf': + plotargs = (x,y,data.reshape((la_n,lo_n)),clvs) + cmap = plt.cm.jet + else: + plotargs = (x,y,data.reshape((la_n,lo_n)),clvs) else: - self.bmap.contourf(x,y,data.reshape((la_n,lo_n))) + plotargs = (x,y,data.reshape((la_n,lo_n))) + cmap = plt.cm.jet + #self.bmap.contourf(x,y,data.reshape((la_n,lo_n))) + + + if plottype == 'contourf': + self.bmap.contourf(*plotargs,cmap=cmap) + elif plottype == 'contour': + ctplt = self.bmap.contour(*plotargs,colors='k') + scaling_func = M.ticker.FuncFormatter(lambda x, pos:'{0:d}'.format(int(x*multiplier))) + plt.clabel(ctplt, inline=1, fmt=scaling_func, fontsize=9, colors='k') # LABELS, TITLES etc if self.C.plot_titles: title = utils.string_from_time('title',pt,**vardict) plt.title(title) - if self.C.colorbar: + if plottype == 'contourf' and self.C.colorbar: plt.colorbar(orientation='horizontal') - + # SAVE FIGURE datestr = utils.string_from_time('output',pt) if not na: @@ -190,7 +211,7 @@ def plot_streamlines(self,lv,pt,da=0): time_idx = self.W.get_time_idx(pt) if lv==2000: - lv_idx = 0 + lv_idx = None else: print("Only support surface right now") raise Exception @@ -199,9 +220,12 @@ def plot_streamlines(self,lv,pt,da=0): slices = {'t': time_idx, 'lv': lv_idx, 'la': lat_sl, 'lo': lon_sl} - u = self.W.get('U',slices)[0,0,:,:] - v = self.W.get('V',slices)[0,0,:,:] - + if lv == 2000: + u = self.W.get('U10',slices)[0,:,:] + v = self.W.get('V10',slices)[0,:,:] + else: + u = self.W.get('U',slices)[0,0,:,:] + v = self.W.get('V',slices)[0,0,:,:] # pdb.set_trace() #div = N.sum(N.dstack((N.gradient(u)[0],N.gradient(v)[1])),axis=2)*10**4 @@ -226,7 +250,7 @@ def plot_streamlines(self,lv,pt,da=0): plt.clf() plt.close() - def basemap_setup(self): + def basemap_setup(self,smooth=1): # Fetch settings basemap_res = getattr(self.C,'basemap_res',self.D.basemap_res) @@ -245,7 +269,8 @@ def basemap_setup(self): # Draw meridians etc with wrff.lat/lon spacing # Default should be a tenth of width of plot, rounded to sig fig - x,y = m(self.W.lons,self.W.lats) + s = slice(None,None,smooth) + x,y = m(self.W.lons[s,s],self.W.lats[s,s]) return m, x, y diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index 61271ee..d78ef2d 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -46,17 +46,16 @@ def save(self,fig,p2p,fname): #self.fig.savefig(fpath) plt.gcf().savefig(fpath,bbox_inches='tight') - def get_limited_domain(self,da): + def get_limited_domain(self,da,smooth=1): if da: # Limited domain area N_idx = self.W.get_lat_idx(da['N']) E_idx = self.W.get_lon_idx(da['E']) S_idx = self.W.get_lat_idx(da['S']) W_idx = self.W.get_lon_idx(da['W']) - lat_sl = slice(S_idx,N_idx) - lon_sl = slice(W_idx,E_idx) + lat_sl = slice(S_idx,N_idx,smooth) + lon_sl = slice(W_idx,E_idx,smooth) else: - lat_sl = slice(None) - lon_sl = slice(None) - + lat_sl = slice(None,None,smooth) + lon_sl = slice(None,None,smooth) return lat_sl, lon_sl diff --git a/postWRF/postWRF/rucplot.py b/postWRF/postWRF/rucplot.py index 33c9c2c..bd3871e 100644 --- a/postWRF/postWRF/rucplot.py +++ b/postWRF/postWRF/rucplot.py @@ -12,57 +12,202 @@ import sys #sys.path.append('/home/jrlawson/gitprojects/') import WEM.utils.utils as utils +import WEM.utils.gridded_data as gridded_data from figure import Figure from defaults import Defaults from wrfout import WRFOut +""" +RUC/RAP data will probably need to be cut down to fit the WRF domain +it is compared to. +""" + + class RUCPlot(Figure): - def __init__(self,config): + def __init__(self,config,t,**kwargs): + """ + config : configuration settings + t : time, datenum format + + optional key-word arguments: + wrfdir : if picked, domain is cut down + """ + self.C = config + self.D = Defaults() + self.path_to_data = self.C.path_to_RUC self.output_root = self.C.output_root - self.D = Defaults() + + self.t = t + # Convert the datenum into time sequence + self.ts = self.get_time_seq() + + self.version = self.get_version() + self.fname = self.get_fname() + self.fpath = os.path.join(self.path_to_data,self.fname+'.nc') + + self.nc = Dataset(self.fpath) + + # Original lat and lon grids + self.lats, self.lons = self.get_latlon() + # Original lat/lon 1D arrays + self.lats1D = self.lats[:,self.lats.shape[1]/2] + self.lons1D = self.lons[self.lons.shape[0]/2,:] + + if 'wrfdir' in kwargs: + # It means all data should be cut to this size + self.limits = self.colocate_WRF_map(kwargs['wrfdir']) + self.lats2D = self.cut_2D_array(self.lats) + self.lons2D = self.cut_2D_array(self.lons) + self.lats, self.lons = self.cut_lat_lon() + self.y_dim = len(self.lats) + self.x_dim = len(self.lons) + #self.lats1D = self.lats[:,self.lats.shape[1]/2] + self.lats1D = self.lats + #self.lons1D = self.lons[self.lons.shape[0]/2,:] + self.lons1D = self.lons + else: + # Leave the dimensions the way they were + self.y_dim = self.lats.shape[0] + self.x_dim = self.lats.shape[1] + + + print('RUC file loaded from {0}'.format(self.fpath)) + + def cut_lat_lon(self): + """ Return a smaller array of data + depending of specified limits + """ + lats = self.lats1D + lons = self.lons1D + lat_idx_max,lon_idx_min,a,b = gridded_data.getXY(lats,lons,self.limits['Nlim'],self.limits['Wlim']) + lat_idx_min,lon_idx_max,c,d = gridded_data.getXY(lats,lons,self.limits['Slim'],self.limits['Elim']) + + # Dummy variables for exact lat/lons returned + del a,b,c,d + lats_out = lats[lat_idx_min:lat_idx_max] + lons_out = lons[lon_idx_min:lon_idx_max] + return lats_out, lons_out + - def plot(self,variables,**kwargs): + + def cut_2D_array(self,data_in): + """ Return a smaller array of data + depending of specified limits + """ + lats = self.lats1D + lons = self.lons1D + lat_idx_max,lon_idx_min,a,b = gridded_data.getXY(lats,lons,self.limits['Nlim'],self.limits['Wlim']) + lat_idx_min,lon_idx_max,c,d = gridded_data.getXY(lats,lons,self.limits['Slim'],self.limits['Elim']) + + # Dummy variables for exact lat/lons returned + del a,b,c,d + data_out = data_in[lat_idx_min:lat_idx_max+1,lon_idx_min:lon_idx_max+1] + return data_out + + def get_version(self): + """Returns the version of RUC or RUC file + """ + yr = self.ts[0] + mth = self.ts[1] + + if (yr > 2012) and (mth > 3): # With a massive gap for RAP + version = 3 + elif (yr > 2007) and (mth > 10): # Massive gap after 2012/05 (transition to RAP). + version = 2 + elif (yr>2006): + version = 1 + elif (yr>2004): + version = 0 + return version + + + def plot(self,variables,lv,**kwargs): for va in variables: - for t in variables[va]['pt']: - if 'lv' in variables[va]: - lv = variables[va]['lv'] - else: - lv = '' - if 'scale' in variables[va]: - kwargs['scale'] = variables[va]['scale'] - if 'top' in variables[va]: - kwargs['top'] = variables[va]['top'] - kwargs['bottom'] = variables[va]['bottom'] - self.plot_variable(va,t,lv,**kwargs) - - def plot_variable(self,va,t,lv,**kwargs): - fname = self.get_fname(t) - fpath = os.path.join(self.path_to_data,fname+'.nc') - print fpath - nc = Dataset(fpath) + printtime = ('/'.join(["%02u" %s for s in self.ts[:4]]) + + "%02u" %self.ts[4] + ' UTC') + print("Plotting {0} for level {1} at time {2}".format( + va,lv,printtime)) + if 'scale' in kwargs: + kwargs['scale'] = variables[va]['scale'] + if 'top' in kwargs: + kwargs['top'] = variables[va]['top'] + kwargs['bottom'] = variables[va]['bottom'] + if va == 'streamlines': + self.plot_streamlines(va,lv,**kwargs) + else: + self.plot_variable(va,lv,**kwargs) + + def plot_streamlines(self,va,lv,**kwargs): + fig = plt.figure() + + # Scales, colourmaps in here + + # Get data + u_all = self.get('U10',**kwargs)[:] + v_all = self.get('V10',**kwargs)[:] + + u = self.cut_2D_array(u_all) + v = self.cut_2D_array(v_all) + m, x, y = self.basemap_setup(**kwargs) + """ + # Density depends on which version + # Wanting to match 3 km WRF (which had 2.5 density) + # Should work out dx, dy in __init__ method! + + WRF_density = 2.5 + WRF_res = 3.0 + + if self.version == 3: + RUC_res = 13.0 + + density = WRF_density * RUC_res/WRF_res + """ + density = 2.5 + + #x = N.array(range(u.shape[0])) + #y = N.array(range(v.shape[1])) + + #pdb.set_trace() + #m.streamplot(x[self.x_dim/2,:],y[:,self.y_dim/2],u,v,density=density,linewidth=0.75,color='k') + #m.streamplot(x[y.shape[1]/2,:],y[:,x.shape[0]/2],u,v,density=density,linewidth=0.75,color='k') + #plt.streamplot(x[:,0],y[0,:],u,v)#,density=density,linewidth=0.75,color='k') + m.streamplot(y[0,:],x[:,0],u,v,density=density,linewidth=0.75,color='k') + #m.quiver(x,y,u,v) + + if self.C.plot_titles: + title = utils.string_from_time('title',self.t) + plt.title(title) + # SAVE FIGURE + datestr = utils.string_from_time('output',self.t) + lv_na = utils.get_level_naming(va,lv=lv) + na = (va,lv_na,datestr) + self.fname = self.create_fname(*na) # pdb.set_trace() - #lats, lons = get_latlon(nc) + self.save(fig,self.output_root,self.fname) + plt.close() + + def plot_variable(self,va,lv,**kwargs): self.fig = plt.figure() m, x, y = self.basemap_setup(nc,**kwargs) # Scales, colourmaps in here - data = self.get(nc,va,**kwargs) + data = self.get(va,**kwargs) if not 'scale' in kwargs: m.contourf(x,y,data) else: m.contourf(x,y,data,N.arange(*kwargs['scale'])) if self.C.plot_titles: - title = utils.string_from_time('title',t) + title = utils.string_from_time('title',self.t) plt.title(title) if self.C.colorbar: plt.colorbar(orientation='horizontal') # SAVE FIGURE - datestr = utils.string_from_time('output',t) + datestr = utils.string_from_time('output',self.t) lv_na = utils.get_level_naming(lv,va) na = (va,lv_na,datestr) self.fname = self.create_fname(*na) @@ -70,71 +215,90 @@ def plot_variable(self,va,t,lv,**kwargs): self.save(self.fig,self.output_root,self.fname) plt.close() - def get(self,nc,va,**kwargs): - if va == 'CAPE': - data = nc.variables['CAPE_252_SFC'][:] - elif va == 'U': - data = nc.variables['U_GRD_252_ISBL'][:] - elif va == 'V': - data = nc.variables['V_GRD_252_ISBL'][:] - elif va == 'Z': - data = nc.variables['HGT_252_ISBL'][:] - elif va == 'wind10': - data = self.compute_wind10(nc) + def get(self,va,**kwargs): + if va == 'wind10': + data = self.compute_wind10(self.nc) elif va == 'shear': - data = self.compute_shear(nc,**kwargs) - elif va == 'dewpoint': - data = nc.variables['DPT_252_HTGL'][:] - 273.15 - else: - print("Choose variable") - raise Exception + data = self.compute_shear(self.nc,**kwargs) + else: + key = self.get_key(va) + data = self.nc.variables[key][:] return data - def get_latlon(self,nc): - # pdb.set_trace() - lats = nc.variables['gridlat_252'] - lons = nc.variables['gridlon_252'] + def get_latlon(self): + lat_key = self.get_key('lats') + lon_key = self.get_key('lons') + + lats = self.nc.variables[lat_key] + lons = self.nc.variables[lon_key] + return lats,lons - def get_fname(self,t): - if isinstance(t,int): + def get_time_seq(self): + """ + Makes sure time is time sequence + (YYYY,MM,DD,HH,MM,SS) + """ + if isinstance(self.t,int): # Time is datenum -> make sequence - ts = time.gmtime(t) + ts = time.gmtime(self.t) else: # Already a sequence - ts = t + ts = self.t + return ts - if ts[4] is not 0: + def get_fname(self): + if self.ts[4] is not 0: print("No RUC data for this time") raise Exception # Depending on the RUC version, this might change - fname = 'ruc2_252_{0:04d}{1:02d}{2:02d}_{3:02d}00_000'.format(*ts) + + if self.version==0: + prefix = 'ruc2_252_' + elif self.version==1: + prefix = 'ruc2anl_252_' + elif self.version==2: + prefix = 'ruc2anl_130_' + elif self.version==3: + prefix = 'rap_130_' + else: + raise Exception + + fname = '{0}{1:04d}{2:02d}{3:02d}_{4:02d}00_000'.format(prefix,*self.ts) return fname - def basemap_setup(self,nc,**kwargs): + def basemap_setup(self,**kwargs): # Fetch settings + #pdb.set_trace() basemap_res = getattr(self.C,'basemap_res',self.D.basemap_res) - lats, lons = self.get_latlon(nc) - lllat = lats[0,0] - lllon = lons[0,0] - urlat = lats[-1,-1] - urlon = lons[-1,-1] - - if 'Wlim' in kwargs: - lllat = kwargs['Slim'] - lllon = kwargs['Wlim'] - urlat = kwargs['Nlim'] - urlon = kwargs['Elim'] + #if 'lats' or 'lons' in kwargs: + # # These are 1D arrays + # lats = kwargs['lats'] + # lons = kwargs['lons'] + #else: + lats = self.lats1D + lons = self.lons1D + + try: + self.limits + except: + lllat = lats[0,0] + lllon = lons[0,0] + urlat = lats[-1,-1] + urlon = lons[-1,-1] + else: + lllat = self.limits['Slim'] + lllon = self.limits['Wlim'] + urlat = self.limits['Nlim'] + urlon = self.limits['Elim'] dx = 13.0 dy = 13.0 - x_dim = lats.shape[0] - y_dim = lats.shape[1] - width_m = dx*(x_dim-1) - height_m = dy*(y_dim-1) - lat0 = lats[x_dim/2,y_dim/2] - lon0 = lons[x_dim/2,y_dim/2] + width_m = dx*(len(lons)-1) + height_m = dy*(len(lats)-1) + lat0 = lats[len(lats)/2] + lon0 = lons[len(lons)/2] m = Basemap( projection='lcc',width=width_m,height=height_m, @@ -149,9 +313,10 @@ def basemap_setup(self,nc,**kwargs): # Draw meridians etc with wrff.lat/lon spacing # Default should be a tenth of width of plot, rounded to sig fig - # pdb.set_trace() - mx, my = N.meshgrid(lons[0,:],lats[:,0]) - x,y = m(mx,my) + + mx, my = N.meshgrid(self.lons,self.lats) + y,x = m(mx,my) + #pdb.set_trace() return m, x, y def colocate_WRF_map(self,wrfdir): @@ -177,9 +342,9 @@ def compute_shear(self,nc,**kwargs): topm = kwargs['top']*1000 botm = kwargs['bottom']*1000 - u = self.get(nc,'U')[::-1,...] - v = self.get(nc,'V')[::-1,...] - Z = self.get(nc,'Z')[::-1,...] + u = self.get('U')[::-1,...] + v = self.get('V')[::-1,...] + Z = self.get('Z')[::-1,...] topidx = N.zeros((225,301)) botidx = N.zeros((225,301)) @@ -207,7 +372,35 @@ def compute_shear(self,nc,**kwargs): return shear def compute_wind10(self,nc): - u = self.get(nc,'U')[-1,...] - v = self.get(nc,'V')[-1,...] + """ Version '3' for RAP has U10 and V10 + """ + u = self.get('U')[-1,...] + v = self.get('V')[-1,...] return N.sqrt(u**2 + v**2) + def get_key(self,va): + """ + Returns the netcdf key for the desired variable + """ + + KEYS = {} + KEYS['U'] = {0:'U_GRD_252_ISBL',1:'',2:'',3:'UGRD_P0_L100_GLC0'} + KEYS['V'] = {0:'V_GRD_252_ISBL',1:'',2:'',3:'VGRD_P0_L100_GLC0'} + KEYS['lats'] = {0:'gridlat_252',3:'gridlat_0'} + KEYS['lons'] = {0:'gridlon_252',3:'gridlon_0'} + KEYS['Z'] = {0:'HGT_252_ISBL'} + + KEYS['U10'] = {3:'UGRD_P0_L103_GLC0'} + KEYS['V10'] = {3:'VGRD_P0_L103_GLC0'} + + KEYS['Td'] = {0:'DPT_252_HTGL'} + + try: + key = KEYS[va][self.version] + except KeyError: + print("Choose variable") + raise Exception + else: + return key + + diff --git a/postWRF/postWRF/scales.py b/postWRF/postWRF/scales.py index 4bda819..6c19ce6 100644 --- a/postWRF/postWRF/scales.py +++ b/postWRF/postWRF/scales.py @@ -23,6 +23,10 @@ import colourtables as ct import WEM.utils as utils +def get_multiplier(va,**kwargs): + m = A[va].get('multiplier',1) + return m + def get_cm(va,**kwargs): lv = kwargs['lv'] @@ -133,3 +137,6 @@ def find_nearest_level(lv): A['strongestwind'] = {'cmap':0} A['strongestwind'][2000] = (10,32.5,2.5) + +A['PMSL'] = {'cmap':0,'multiplier':0.01} +A['PMSL'][2000] = (97000,103100,100) diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index f822585..52d920b 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -236,10 +236,23 @@ def compute(self,var,slices,**kwargs): tbl['dpt'] = self.compute_dpt #density potential temperature pert. tbl['buoyancy'] = self.compute_buoyancy tbl['strongestwind'] = self.compute_strongest_wind - + tbl['PMSL'] = self.compute_pmsl data = tbl[var](slices,**kwargs) return data + def compute_pmsl(self,slices,**kwargs): + P = self.get('PSFC',slices) + T2 = self.get('T2',slices) + HGT = self.get('HGT',slices) + + temp = T2 + (6.5*HGT)/1000.0 + pmsl = P*N.exp(9.81/(287.0*temp)*HGT) + + #sm = kwargs.get('smooth',1) + #data = pmsl[0,::sm,::sm] + #return data + return pmsl + def compute_buoyancy(self,slices,**kwargs): """ Method from Adams-Selin et al., 2013, WAF diff --git a/utils/gridded_data.py b/utils/gridded_data.py index 7eee3b2..68c82f3 100644 --- a/utils/gridded_data.py +++ b/utils/gridded_data.py @@ -12,11 +12,11 @@ def getXY(lats,lons,ptlat,ptlon): # Find where these are in the grid wherelat = N.where(abs(lats-ptlat) == minlat) wherelon = N.where(abs(lons-ptlon) == minlon) - y = N.where(lats==lats[wherelat])[0][0] - x = N.where(lons==lons[wherelon])[0][0] + lat_idx = N.where(lats==lats[wherelat])[0][0] + lon_idx = N.where(lons==lons[wherelon])[0][0] exactlat = lats[wherelat] exactlon = lons[wherelon] - return x,y, exactlat, exactlon + return lat_idx,lon_idx, exactlat, exactlon def gettopo(): fname = '/uufs/chpc.utah.edu/common/home/u0737349/dsws/topodata/globe30.bin' From eb74d789beb2b352c19d6d43aa703ec8a4586ba3 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 14 Apr 2014 15:07:45 -0500 Subject: [PATCH 080/111] Updates.. --- postWRF/bin/.DKE.py.swo | Bin 12288 -> 12288 bytes postWRF/bin/DKE.py | 12 ++++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/postWRF/bin/.DKE.py.swo b/postWRF/bin/.DKE.py.swo index 566386d9d1058671d07b1e299a3c9a6ee230439c..423455066aa7e7d051b6a95f524e405159b44fc5 100644 GIT binary patch delta 712 zcmZwFzi-n(6bJAZ5u~Q1G!;llMF^`NoYWWV?8FY{ry%^Q1QL*vt+;M*N~AV6_LUz( zC|x^r;rIh!0Y*^7%uopi*q9M36AKbjJN4^y38W`|q|8+Zt3;S4B{g#+*_PxKAG!h3iL5}v|6SOAtMoqiu8`T?)tF3dv} zj>8v>`We!ZH^%#!>`G4V%%03fSlFDFwqC1vjrsa@jkUsVyR&SBYa&_`UL1ykHA|#6 zm2*n$cb9{r%{c?kEL%!ll^^xX2M4usTc5coLgBAyEb_$zPlVp%xQ$K;6P?u{VWdX) zmy9G3o{0TcP%P2tZSr~fnA~FL$11ZGhw~E2v2v}aw$l!np=fc_WL0KyX4;H198f#| zl~)o%&IZd8oiJ^3^_+SW%0sz)(g4`hSVbNY7{() zf*4bGMH(7>U7D+>`)gOOHttv&{uVz;AEn_@6@JRJHByH{Ca;?4GZf-3cTN6p$lZd delta 552 zcmY+Bl2s+T3+*d~EGB4Bc;Z_1i{uej3F>MZ6S0t^bIY=-m_?t+B*=$?T5)<#17G+HN7vj&67XSbN diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index 56dc4cd..2627ae1 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -18,9 +18,9 @@ #case = '20110419' case = '20130815' -IC = 'NAM' -experiment = 'MXMP' -ens = 'anl' +IC = 'GEFSR2' +experiment = 'STCH' +ens = 'p09' # Time script scriptstart = time.time() @@ -47,11 +47,11 @@ times = utils.generate_times(itime,ftime,3*3600) config.output_root = os.path.join(config.output_root,case,IC) #pdb.set_trace() -#p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times, -# d_save=runfolder, d_return=0,d_fname='DTE_MXMP') +p.compute_diff_energy('sum_z','total',path_to_wrfouts,times, + d_save=runfolder, d_return=0,d_fname='DTE_STCH') # Contour fixed at these values #V = range(500,5000,250) -p.plot_diff_energy('sum_xyz','total',times,runfolder,'DTE_MXMP',config.output_root,V) +p.plot_diff_energy('sum_z','total',times,runfolder,'DTE_STCH',config.output_root,V) print "Script took", time.time()-scriptstart, "seconds." From 67929f8042d6c6ccb054646d17271d8708d58c8c Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 16 Apr 2014 12:39:20 -0500 Subject: [PATCH 081/111] DKE 1D plots --- postWRF/bin/DKE.py | 33 ++++++--- postWRF/postWRF/__init__.py | 130 ++++++++++++++++++++++++++++++++++-- utils/utils.py | 35 +++++++++- 3 files changed, 183 insertions(+), 15 deletions(-) diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index 2627ae1..305812e 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -19,18 +19,21 @@ case = '20130815' IC = 'GEFSR2' -experiment = 'STCH' +experiment = 'MXMP' ens = 'p09' +# MP = 'Morrison_Hail' # Time script scriptstart = time.time() +stoch_ens = ["s{0:02d}".format(n) for n in range(1,11)] +MPs = ('ICBC','WSM6_Grau','WSM6_Hail','Kessler','Ferrier','WSM5', + 'WDM5','Lin','WDM6_Grau','WDM6_Hail', + 'Morrison_Grau','Morrison_Hail') # Initialise settings and environment config = Settings() p = WRFEnviron(config) -runfolder = os.path.join(config.wrfout_root,case,IC) -path_to_wrfouts = p.wrfout_files_in(runfolder,dom=1) #itime = (2006,5,26,0,0,0) #ftime = (2006,5,27,12,0,0) @@ -45,13 +48,27 @@ ftime = (2013,8,16,12,0,0) times = utils.generate_times(itime,ftime,3*3600) -config.output_root = os.path.join(config.output_root,case,IC) -#pdb.set_trace() +#members = ['s0{0}'.format(n) for n in range(1,8)] + +# for s in members: +fpath = os.path.join(config.wrfout_root,case,IC,ens) +path_to_wrfouts = p.wrfout_files_in(fpath,dom=1,avoid=stoch_ens) + +picklefolder = fpath +p.C.output_root = os.path.join(config.output_root,case,IC,ens) +pfname = 'DTE_' + experiment + +""" +# pdb.set_trace() p.compute_diff_energy('sum_z','total',path_to_wrfouts,times, - d_save=runfolder, d_return=0,d_fname='DTE_STCH') + d_save=picklefolder, d_return=0,d_fname=pfname) # Contour fixed at these values -#V = range(500,5000,250) -p.plot_diff_energy('sum_z','total',times,runfolder,'DTE_STCH',config.output_root,V) +V = range(250,5250,250) +VV = [100,] + V +p.plot_diff_energy('sum_z','total',times,picklefolder,pfname,VV,) print "Script took", time.time()-scriptstart, "seconds." + +""" +p.plot_growth_sensitivity('DTE',picklefolder,pfname,MPs) diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index 36c4960..e1258e3 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -51,7 +51,17 @@ def __init__(self,config): M.rc('font',**self.font_prop) M.rcParams['savefig.dpi'] = self.dpi - def wrfout_files_in(self,folder,dom='notset',init_time='notset'): + def wrfout_files_in(self,folder,dom='notset',init_time='notset',**kwargs): + + avoids = [] + if 'avoid' in kwargs: + # Avoid folder names with this string + # or list of strings + avoid = self.get_sequence(kwargs['avoid']) + for a in avoid: + avoids.append('/{0}/'.format(a)) + + w = 'wrfout' # Assume the prefix if init_time=='notset': suffix = '*0' @@ -77,7 +87,17 @@ def wrfout_files_in(self,folder,dom='notset',init_time='notset'): wrfouts = [] for root,dirs,files in os.walk(folder): for fname in fnmatch.filter(files,prefix+suffix): - wrfouts.append(os.path.join(root, fname)) + skip_me = 0 + fpath = os.path.join(root,fname) + if avoids: + for a in avoids: + if a in fpath: + skip_me = 1 + else: + pass + if not skip_me: + wrfouts.append(fpath) + return wrfouts def dir_from_naming(self,*args): @@ -343,6 +363,7 @@ def compute_diff_energy( "power") raise Exception + print("Saving pickle file to {0}".format(d_save)) # Look up the method to use depending on type of plot PLOTS = {'sum_z':self.DE_z, 'sum_xyz':self.DE_xyz} @@ -355,8 +376,9 @@ def compute_diff_energy( print('Get permutations') # Get all permutations of files - nperm = itertools.combinations(files,2).__sizeof__() + nperm = len(list(itertools.combinations(files,2))) print('Start loop') + # pdb.set_trace() for n, perm in enumerate(itertools.combinations(files,2)): print("No. {0} from {1} permutations".format(n,nperm)) perm_start = time.time() @@ -515,7 +537,6 @@ def DE_z(self,nc0,nc1,t,energy,lower,upper): R = 287.0 # Universal gas constant (J / deg K * kg) Cp = 1004.0 # Specific heat of dry air at constant pressure (J / deg K * kg) kappa = R/Cp - print('Temp data has been loaded.') xlen = Ud.shape[1] # 1 less than in V ylen = Vd.shape[2] # 1 less than in U @@ -560,7 +581,7 @@ def latlon(nlats,nlons): return DKE - def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,V): + def plot_diff_energy(self,ptype,energy,time,folder,fname,V): """ folder : directory holding computed data @@ -574,9 +595,12 @@ def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,V): for n,t in enumerate(times): for pn,perm in enumerate(DATA): + f1 = DATA[perm]['file1'] + f2 = DATA[perm]['file2'] if sw==0: # Get times and info about nc files - W1 = WRFOut(DATA[perm]['file1']) + # First time to save power + W1 = WRFOut(f1) permtimes = DATA[perm]['times'] sw = 1 @@ -597,6 +621,100 @@ def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,V): print("Plotting time {0} from {1}.".format(n,len(times))) del data, stack + def plot_growth_sensitivity(self,energy,folder,fname,plotlist,**kwargs): + """Plots line graphs of DKE/DTE error growth + varying by a sensitivity - e.g. error growth involving + all members that use a certain parameterisation. + + plotlist : list of folder names to loop over + """ + + def make_1D(data,output='list'): + """ Make sure data is a time series + of 1D values, and numpy array. + + List of arrays -> Numpy array + """ + if isinstance(data,list): + + data_list = [] + for time in data: + data_list.append(N.sum(time[0])) + + if output == 'array': + data_out = N.array(data_list) + else: + data_out = data_list + + return data_out + + #elif isinstance(data,N.array): + # shape = data.shape + # if len(shape) == 1: + # data_out = data + # elif len(shape) == 2: + # data_out = N.sum(data) + + DATA = self.load_data(folder,fname,format='pickle') + + # Dictionary to hold each plot's parameterisaton focus + # Key is folder name (i.e. param) + # Nested dicts will be similar for the permutations + #SENS = {} + + for perm in DATA: + times = DATA[perm]['times'] + break + + times_tup = [time.gmtime(t) for t in times] + time_str = ["{2:02d}/{3:02d}".format(*t) for t in times_tup] + + # If data is 2D, sum over x/y to get one number + for sens in plotlist: + fig = plt.figure() + labels = [] + stack = 0 + #SENS['sens'] = {} + for perm in DATA: + f1 = DATA[perm]['file1'] + f2 = DATA[perm]['file2'] + + if sens in f1: + f = f2 + elif sens in f2: + f = f1 + else: + f = 0 + + if f: + subdirs = f.split('/') + labels.append(subdirs[-2]) + data = make_1D(DATA[perm]['values']) + + plt.plot(times,data) + # stack = utils.dstack_loop_v2(N.array(data),stack) + else: + pass + + # pdb.set_trace() + # ave = N.average(stack,axis=2) + # labels.append('Average') + # plt.plot(times,ave) + + plt.legend(labels,loc=2,fontsize=9) + plt.ylim([0,2e8]) + plt.gca().set_xticks(times[::2]) + plt.gca().set_xticklabels(time_str[::2]) + outdir = self.C.output_root + fname = '{0}_Growth_{1}.png'.format(energy,sens) + fpath = os.path.join(outdir,fname) + fig.savefig(fpath) + + plt.close() + print("Saved {0}.".format(fpath)) + + + def plot_skewT(self,wrfout,plot_time,prof_lat,prof_lon,dom=1,save_output=0): path_to_WRF = self.wrfout_files_in(C.wrfout_root) W = WRFOut(path_to_WRF) diff --git a/utils/utils.py b/utils/utils.py index 271702d..761592c 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -147,7 +147,22 @@ def closest(arr2D,val): def dstack_loop(data, obj): """ - Tries to stack numpy array (data) into object (obj). + Tries to stack numpy array (data) into 'stack' object (obj). + If obj doesn't exist, then initialise it + If obj does exist, stack data. + """ + if not obj: + stack = data + else: + stack = N.dstack((obj,data)) + + return stack + +def dstack_loop_v2(data, obj): + """ + Need to set obj = 0 at start of loop in master script + + Tries to stack numpy array (data) into 'stack' object (obj). If obj doesn't exist, then initialise it If obj does exist, stack data. """ @@ -160,6 +175,24 @@ def dstack_loop(data, obj): return stack +def vstack_loop(data, obj): + """ + Need to set obj = 0 at start of loop in master script + + Tries to stack numpy array (data) into 'stack' object (obj). + If obj doesn't exist, then initialise it + If obj does exist, stack data. + """ + try: + print obj + except NameError: + stack = data + else: + stack = N.vstack((obj,data)) + + return stack + + def generate_times(idate,fdate,interval): """ Interval in seconds From 7ca796685ba34a0a8545a11608a9fd1db806ae18 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 16 Apr 2014 13:55:35 -0500 Subject: [PATCH 082/111] DKE controlling script updated --- postWRF/bin/DKE.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index 305812e..8dec891 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -19,12 +19,12 @@ case = '20130815' IC = 'GEFSR2' -experiment = 'MXMP' +experiment = 'STCH' ens = 'p09' -# MP = 'Morrison_Hail' +MP = 'Morrison_Hail' # Time script scriptstart = time.time() -stoch_ens = ["s{0:02d}".format(n) for n in range(1,11)] +stoch_names = ["s{0:02d}".format(n) for n in range(1,11)] MPs = ('ICBC','WSM6_Grau','WSM6_Hail','Kessler','Ferrier','WSM5', 'WDM5','Lin','WDM6_Grau','WDM6_Hail', 'Morrison_Grau','Morrison_Hail') @@ -34,7 +34,6 @@ p = WRFEnviron(config) - #itime = (2006,5,26,0,0,0) #ftime = (2006,5,27,12,0,0) @@ -50,15 +49,15 @@ times = utils.generate_times(itime,ftime,3*3600) #members = ['s0{0}'.format(n) for n in range(1,8)] -# for s in members: -fpath = os.path.join(config.wrfout_root,case,IC,ens) -path_to_wrfouts = p.wrfout_files_in(fpath,dom=1,avoid=stoch_ens) +path_to_wrfouts = [] +for s in stoch_names: + fpath = os.path.join(config.wrfout_root,case,IC,ens,MP,s) + path_to_wrfouts.append(p.wrfout_files_in(fpath,dom=1)[0]) -picklefolder = fpath -p.C.output_root = os.path.join(config.output_root,case,IC,ens) +picklefolder = os.path.join(config.wrfout_root,case,IC,ens,MP) +p.C.output_root = os.path.join(config.output_root,case,IC,ens,MP) pfname = 'DTE_' + experiment -""" # pdb.set_trace() p.compute_diff_energy('sum_z','total',path_to_wrfouts,times, d_save=picklefolder, d_return=0,d_fname=pfname) @@ -66,9 +65,9 @@ # Contour fixed at these values V = range(250,5250,250) VV = [100,] + V -p.plot_diff_energy('sum_z','total',times,picklefolder,pfname,VV,) +p.plot_diff_energy('sum_z','total',times,picklefolder,pfname,VV) + +#p.plot_growth_sensitivity('DTE',picklefolder,pfname,MPs) print "Script took", time.time()-scriptstart, "seconds." -""" -p.plot_growth_sensitivity('DTE',picklefolder,pfname,MPs) From c0302d2ed8f44e25291bc613988e2c12e8b33d7f Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 1 May 2014 16:33:59 -0500 Subject: [PATCH 083/111] Skew T plotting for Chelsea and me --- postWRF/bin/DKE.py | 92 ++++-- postWRF/bin/WRFsounding_201308160000.p | 73 +++++ postWRF/bin/settings.py | 2 +- postWRF/postWRF/__init__.py | 315 +++++++++++++------ postWRF/postWRF/birdseye.py | 3 +- postWRF/postWRF/dke.py | 412 +++++++++++++++++++++++++ postWRF/postWRF/skewt.py | 287 +++++++++-------- postWRF/postWRF/wrfout.py | 26 +- utils/gridded_data.py | 1 + utils/utils.py | 27 +- 10 files changed, 966 insertions(+), 272 deletions(-) create mode 100644 postWRF/bin/WRFsounding_201308160000.p create mode 100644 postWRF/postWRF/dke.py diff --git a/postWRF/bin/DKE.py b/postWRF/bin/DKE.py index 8dec891..7f2c7b2 100644 --- a/postWRF/bin/DKE.py +++ b/postWRF/bin/DKE.py @@ -7,24 +7,30 @@ import calendar import time -sys.path.append('/home/jrlawson/gitprojects/') +sys.path.append('/home/jrlawson/gitprojects/') from DKE_settings import Settings from WEM.postWRF.postWRF import WRFEnviron import WEM.utils.utils as utils +compute = 0 +plot_2D = 1 +plot_1D = 0 + #case = '20060526' #case = '20090910' #case = '20110419' case = '20130815' IC = 'GEFSR2' -experiment = 'STCH' -ens = 'p09' -MP = 'Morrison_Hail' +experiment = 'ICBC' +ens = '' +MP = 'ICBC' # Time script scriptstart = time.time() stoch_names = ["s{0:02d}".format(n) for n in range(1,11)] +ens_names = ['c00',] + ["p{0:02d}".format(n) for n in range(1,11)] + MPs = ('ICBC','WSM6_Grau','WSM6_Hail','Kessler','Ferrier','WSM5', 'WDM5','Lin','WDM6_Grau','WDM6_Hail', 'Morrison_Grau','Morrison_Hail') @@ -33,41 +39,61 @@ config = Settings() p = WRFEnviron(config) - -#itime = (2006,5,26,0,0,0) -#ftime = (2006,5,27,12,0,0) - -#itime = (2009,9,10,0,0,0) -#ftime = (2009,9,11,15,0,0) - -#itime = (2011,4,19,0,0,0) -#ftime = (2011,4,20,15,0,0) - -itime = (2013,8,15,0,0,0) -ftime = (2013,8,16,12,0,0) +if case[:4] == '2006': + itime = (2006,5,26,0,0,0) + ftime = (2006,5,27,15,0,0) +elif case[:4] == '2009': + itime = (2009,9,10,0,0,0) + ftime = (2009,9,11,18,0,0) +elif case[:4] == '2011': + itime = (2011,4,19,0,0,0) + ftime = (2011,4,20,15,0,0) +else: + itime = (2013,8,15,0,0,0) + ftime = (2013,8,16,15,0,0) times = utils.generate_times(itime,ftime,3*3600) -#members = ['s0{0}'.format(n) for n in range(1,8)] -path_to_wrfouts = [] -for s in stoch_names: - fpath = os.path.join(config.wrfout_root,case,IC,ens,MP,s) - path_to_wrfouts.append(p.wrfout_files_in(fpath,dom=1)[0]) - -picklefolder = os.path.join(config.wrfout_root,case,IC,ens,MP) -p.C.output_root = os.path.join(config.output_root,case,IC,ens,MP) +if experiment=='STCH': + picklefolder = os.path.join(config.wrfout_root,case,IC,ens,MP) + p.C.output_root = os.path.join(config.output_root,case,IC,ens,MP) + + path_to_wrfouts = [] + for s in stoch_names: + fpath = os.path.join(config.wrfout_root,case,IC,ens,MP,s) + path_to_wrfouts.append(p.wrfout_files_in(fpath,dom=1)[0]) + sensitivity=0 + +elif experiment=='ICBC': + picklefolder = os.path.join(config.wrfout_root,case,IC) + p.C.output_root = os.path.join(config.output_root,case,IC) + sensitivity=ens_names + #fpath = os.path.join(config.wrfout_root,case,IC,) + #path_to_wrfouts = p.wrfout_files_in(fpath,dom=1) + +elif experiment=='MXMP': + picklefolder = os.path.join(config.wrfout_root,case,IC,ens) + p.C.output_root = os.path.join(config.output_root,case,IC,ens) + sensitivity = MPs +else: + print "Typo!" + raise Exception + pfname = 'DTE_' + experiment -# pdb.set_trace() -p.compute_diff_energy('sum_z','total',path_to_wrfouts,times, +if compute: + p.compute_diff_energy('sum_z','total',path_to_wrfouts,times, d_save=picklefolder, d_return=0,d_fname=pfname) -# Contour fixed at these values -V = range(250,5250,250) -VV = [100,] + V -p.plot_diff_energy('sum_z','total',times,picklefolder,pfname,VV) - -#p.plot_growth_sensitivity('DTE',picklefolder,pfname,MPs) +if plot_2D: + # Contour fixed at these values + V = range(250,5250,250) + VV = [100,] + V + p.plot_diff_energy('sum_z','total',times,picklefolder,pfname,VV) -print "Script took", time.time()-scriptstart, "seconds." +if plot_1D: + ylimits = [0,2e8] + ofname = pfname + p.plot_error_growth(ofname,picklefolder,pfname,sensitivity=sensitivity,ylimits=ylimits) +#print "Script took", time.time()-scriptstart, "seconds." diff --git a/postWRF/bin/WRFsounding_201308160000.p b/postWRF/bin/WRFsounding_201308160000.p new file mode 100644 index 0000000..8ca7bb1 --- /dev/null +++ b/postWRF/bin/WRFsounding_201308160000.p @@ -0,0 +1,73 @@ +(dp1 +S'P' +cnumpy.core.multiarray +_reconstruct +p2 +(cnumpy +ndarray +p3 +(I0 +tS'b' +tRp4 +(I1 +(I49 +tcnumpy +dtype +p5 +(S'f4' +I0 +I1 +tRp6 +(I3 +S'<' +NNNI-1 +I-1 +I0 +tbI00 +S"-\xa4\xbcG\xac\xbb\xbbG\xd5\xbd\xbaG\xbd\xbf\xb9G8\xd7\xb8G\xe4\xf0\xb7GM\x08\xb7G\xe5\n\xb6G\xea\x0b\xb5G\xae$\xb4G\n?\xb3G\xaaU\xb2G\x82n\xb1G\xe3o\xb0GMp\xafG\xda\x8a\xaeG\xa5\xa1\xadG\x0b\xbc\xacG\xf1\xbb\xabG0\xbe\xaaG\xf4\xd6\xa9G\x8c\xd8\xa8G\xdff\xa7Gn\x98\xa5Gf\xb3\xa3G\xb0\xcc\xa1GF\xe8\x9fG\xbc\x02\x9eG\x965\x9cG\xefQ\x9aGlm\x98GN\xba\x95G\x8c\n\x92G\x91D\x8eGT~\x8aG\x88\xb9\x86GQ)\x81G\x89osG-\x90dGP\xb2UGU\xd7FGk\xfe7G\x0fd%G\xf25\x0fG\xee\xb5\xf1F\xaa\x05\xc5F\xcf\xb5\x98F\xe6\x16XF'2\xf9E" +tbsS'Td' +p7 +g2 +(g3 +(I0 +tS'b' +tRp8 +(I1 +(I49 +tg6 +I00 +S'\x97\xb0\x7fA\xc8\x90uA\xe8HqA)\x0eoA%\x86mA-\x0elA\xa2\x9djA\xac\x16iA\xa9\x95gA\xb77fA\xc1\xd5dA,jcA^\xfaaA\xf1W`AQ\x8f^Ay\xc6\\A\xa3\xc6ZA\xb7\xa5XA\xfdIVA\x8d\xe7SA\xf0\xe9QA\x07\xbbOA\xc2`LA\xa5\xc7FA\xfa\xd7>A\xd6t4A-\x17)Ahb\x19AOx\x04A\x86r\xd7@\x1f+\xa1@g\xc9Y@"\xd2\xa6?m\xcf\xea\xbd\x88\xe5\x8d\xbf?\x17\x14\xc0\xbdf\xa7\xc0\'m\x1e\xc1\xe8.O\xc1S\xc6\x80\xc1\xe5v\xbb\xc1\x99S\xc4\xc1\xc4\\\xe0\xc1\xde\x0b\xf9\xc1}H\x1e\xc2x\xdfP\xc2\x15\xe3\xfb\xc2\x1b]\xcf\xc2\xda\x01\xa5\xc2' +tbsS'u' +g2 +(g3 +(I0 +tS'b' +tRp9 +(I1 +(I17 +tg6 +I00 +S'&a\xd8AQ\x1b\xc3A\xdcJ\x80AK\x82RA`\xe2DA\x86\x03SA\x84\x10?A|/\x00A\x0b\xa4\xd2@`\xa5\x9f@\xcc\xca\x91@H\xeb\xae@&\xeb\x8e@e\xa3S>\x04\xc2\x12\xc0<\x1a\x18\xc0\x120\xda\xbf' +tbsS'T' +g2 +(g3 +(I0 +tS'b' +tRp10 +(I1 +(I49 +tg6 +I00 +S'@\xcd\xb7A\x00\xc2\xb7A\x10\xd5\xb4A\xf0T\xb1Ap\x0c\xaeA\x80\xc7\xaaA\xe0u\xa7A\x80\xd4\xa3Ap)\xa0A\xe0\xd2\x9cAp\x80\x99A`\x1e\x96A0\xc4\x92A\xe0\x12\x8fApc\x8bA\x80\x1e\x88A\xc0\xd7\x84A@\xac\x81A\x00Q|A\xe0|uA@AoA\x80\x83hA`\xdf^A\xa0\xdcSA\xe0\x14IA\x80\x9c?A \xc96A@b0A\xc0{,A\xa0\x1a(A\x80\xa5"A \x91\x14A@\xcf\x00A\x80\x83\xd4@\x80\x9e\xa5@\x80 m@\x00y\xaa?\x00:\x01\xc0\x80B\xae\xc0@\x91\x0b\xc1\xc0\xa4=\xc1@\xc9\x81\xc1`c\xbb\xc1`\x7f\xfd\xc1\xa4\x93\x1f\xc2\x8c\xe2A\xc2H1`\xc2H\x18v\xc2\x8a\xf3\x87\xc2' +tbsS'v' +g2 +(g3 +(I0 +tS'b' +tRp11 +(I1 +(I17 +tg6 +I00 +S'f\xe1\x04\xc1\xc1F3\xc1\xc0(\x06\xc1<\xf2\xb9\xc0\x0eG\xad\xc0\xdc\xae\xfd\xc0\x0b\x7f/\xc1\xc6\xf1A\xc1\xb3\xb65\xc1\x14\xb4\x17\xc1\x11Z\xe4\xc0x\x06\x86\xc0\xcc\xd6\xfa\xbd\xb0\x17Z@\xce\x9b\x8b@&\xba\x95@,dW@' +tbs. \ No newline at end of file diff --git a/postWRF/bin/settings.py b/postWRF/bin/settings.py index 6e4efbc..fce9478 100644 --- a/postWRF/bin/settings.py +++ b/postWRF/bin/settings.py @@ -9,5 +9,5 @@ def __init__(self): self.plot_titles = 1 self.terrain = 0 self.colorbar = 1 - self.pickledir = '/home/jrlawson/data/sounding/WRFoutput/' + #self.pickledir = '/home/jrlawson/data/sounding/WRFoutput/' diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index e1258e3..992a97f 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -14,6 +14,7 @@ import matplotlib as M M.use('Agg') import matplotlib.pyplot as plt +from mpl_toolkits.basemap import Basemap import collections import fnmatch import calendar @@ -24,11 +25,13 @@ import json import cPickle as pickle import copy +import glob from wrfout import WRFOut from axes import Axes from figure import Figure from birdseye import BirdsEye +from skewt import SkewT #import scales from defaults import Defaults from lookuptable import LookUpTable @@ -51,8 +54,9 @@ def __init__(self,config): M.rc('font',**self.font_prop) M.rcParams['savefig.dpi'] = self.dpi - def wrfout_files_in(self,folder,dom='notset',init_time='notset',**kwargs): + def wrfout_files_in(self,folders,dom='notset',init_time='notset',descend=1,**kwargs): + folders = self.get_sequence(folders) avoids = [] if 'avoid' in kwargs: # Avoid folder names with this string @@ -85,19 +89,28 @@ def wrfout_files_in(self,folder,dom='notset',init_time='notset',**kwargs): prefix = w + '_' + dom wrfouts = [] - for root,dirs,files in os.walk(folder): - for fname in fnmatch.filter(files,prefix+suffix): - skip_me = 0 - fpath = os.path.join(root,fname) - if avoids: - for a in avoids: - if a in fpath: - skip_me = 1 - else: - pass - if not skip_me: - wrfouts.append(fpath) + if descend: + for folder in folders: + for root,dirs,files in os.walk(folder): + for fname in fnmatch.filter(files,prefix+suffix): + skip_me = 0 + fpath = os.path.join(root,fname) + if avoids: + for a in avoids: + if a in fpath: + skip_me = 1 + else: + pass + if not skip_me: + wrfouts.append(fpath) + else: + for folder in folders: + findfile = os.path.join(folder,prefix+suffix) + files = glob.glob(findfile) + # pdb.set_trace() + for f in files: + wrfouts.append(os.path.join(folder,f)) return wrfouts def dir_from_naming(self,*args): @@ -139,7 +152,7 @@ def plot_2D(self,dic): llo : left limit of longitude rlo : right limit of longitude ---> if these are missing, default to 'all points' - plottype : contourf by default. + plottype : contourf by default. """ @@ -153,7 +166,7 @@ def plot_2D(self,dic): if not 'lv' in Dic[va]: # For things like CAPE, shear. Dic[va]['lv'] = 'all' - lv = Dic[va]['lv'] + lv = Dic[va]['lv'] vc = utils.level_type(lv) # vertical coordinate if not 'pt' in Dic[va]: # For averages and all times @@ -514,7 +527,7 @@ def DE_z(self,nc0,nc1,t,energy,lower,upper): Ud = U0 - U1 #del U0, U1 - V0 = nc0.variables['V'][t,...] + V0 = nc0.variables['V'][t,...] V1 = nc1.variables['V'][t,...] Vd = V0 - V1 #del V0, V1 @@ -621,46 +634,17 @@ def plot_diff_energy(self,ptype,energy,time,folder,fname,V): print("Plotting time {0} from {1}.".format(n,len(times))) del data, stack - def plot_growth_sensitivity(self,energy,folder,fname,plotlist,**kwargs): - """Plots line graphs of DKE/DTE error growth + def plot_error_growth(self,ofname,folder,pfname,sensitivity=0,ylimits=0,**kwargs): + """Plots line graphs of DKE/DTE error growth varying by a sensitivity - e.g. error growth involving all members that use a certain parameterisation. + ofname : output filename prefix + pfname : pickle filename plotlist : list of folder names to loop over + ylim : tuple of min/max for y axis range """ - - def make_1D(data,output='list'): - """ Make sure data is a time series - of 1D values, and numpy array. - - List of arrays -> Numpy array - """ - if isinstance(data,list): - - data_list = [] - for time in data: - data_list.append(N.sum(time[0])) - - if output == 'array': - data_out = N.array(data_list) - else: - data_out = data_list - - return data_out - - #elif isinstance(data,N.array): - # shape = data.shape - # if len(shape) == 1: - # data_out = data - # elif len(shape) == 2: - # data_out = N.sum(data) - - DATA = self.load_data(folder,fname,format='pickle') - - # Dictionary to hold each plot's parameterisaton focus - # Key is folder name (i.e. param) - # Nested dicts will be similar for the permutations - #SENS = {} + DATA = self.load_data(folder,pfname,format='pickle') for perm in DATA: times = DATA[perm]['times'] @@ -669,58 +653,133 @@ def make_1D(data,output='list'): times_tup = [time.gmtime(t) for t in times] time_str = ["{2:02d}/{3:02d}".format(*t) for t in times_tup] - # If data is 2D, sum over x/y to get one number - for sens in plotlist: - fig = plt.figure() + if sensitivity: + # Plot multiple line charts for each sensitivity + # Then a final chart with all the averages + # If data is 2D, sum over x/y to get one number + + # Dictionary with average + AVE = {} + + for sens in sensitivity: + ave_stack = 0 + n_sens = len(sensitivity)-1 + colourlist = utils.generate_colours(M,n_sens) + M.rcParams['axes.color_cycle'] = colourlist + fig = plt.figure() + labels = [] + #SENS['sens'] = {} + for perm in DATA: + f1 = DATA[perm]['file1'] + f2 = DATA[perm]['file2'] + + if sens in f1: + f = f2 + elif sens in f2: + f = f1 + else: + f = 0 + + if f: + subdirs = f.split('/') + labels.append(subdirs[-2]) + data = self.make_1D(DATA[perm]['values']) + + plt.plot(times,data) + # pdb.set_trace() + ave_stack = utils.vstack_loop(N.asarray(data),ave_stack) + else: + pass + + # pdb.set_trace() + n_sens += 1 + colourlist = utils.generate_colours(M,n_sens) + M.rcParams['axes.color_cycle'] = colourlist + AVE[sens] = N.average(ave_stack,axis=0) + labels.append('Average') + plt.plot(times,AVE[sens],'k') + + plt.legend(labels,loc=2,fontsize=9) + if ylimits: + plt.ylim(ylimits) + plt.gca().set_xticks(times[::2]) + plt.gca().set_xticklabels(time_str[::2]) + outdir = self.C.output_root + fname = '{0}_Growth_{1}.png'.format(ofname,sens) + fpath = os.path.join(outdir,fname) + fig.savefig(fpath) + + plt.close() + print("Saved {0}.".format(fpath)) + + # Averages for each sensitivity labels = [] - stack = 0 - #SENS['sens'] = {} - for perm in DATA: - f1 = DATA[perm]['file1'] - f2 = DATA[perm]['file2'] - - if sens in f1: - f = f2 - elif sens in f2: - f = f1 - else: - f = 0 - - if f: - subdirs = f.split('/') - labels.append(subdirs[-2]) - data = make_1D(DATA[perm]['values']) - - plt.plot(times,data) - # stack = utils.dstack_loop_v2(N.array(data),stack) - else: - pass - - # pdb.set_trace() - # ave = N.average(stack,axis=2) - # labels.append('Average') - # plt.plot(times,ave) - + fig = plt.figure() + ave_of_ave_stack = 0 + for sens in AVE.keys(): + plt.plot(times,AVE[sens]) + labels.append(sens) + ave_of_ave_stack = utils.vstack_loop(AVE[sens],ave_of_ave_stack) + + labels.append('Average') + ave_of_ave = N.average(ave_of_ave_stack,axis=0) + plt.plot(times,ave_of_ave,'k') + plt.legend(labels,loc=2,fontsize=9) - plt.ylim([0,2e8]) + + if ylimits: + plt.ylim(ylimits) plt.gca().set_xticks(times[::2]) plt.gca().set_xticklabels(time_str[::2]) outdir = self.C.output_root - fname = '{0}_Growth_{1}.png'.format(energy,sens) + fname = '{0}_Growth_Averages.png'.format(ofname) fpath = os.path.join(outdir,fname) fig.savefig(fpath) plt.close() print("Saved {0}.".format(fpath)) + #pdb.set_trace() + + + else: + fig = plt.figure() + ave_stack = 0 + for perm in DATA: + data = self.make_1D(DATA[perm]['values']) + plt.plot(times,data,'blue') + ave_stack = utils.vstack_loop(N.asarray(data),ave_stack) + + total_ave = N.average(ave_stack,axis=0) + plt.plot(times,total_ave,'black') + + if ylimits: + plt.ylim(ylimits) + plt.gca().set_xticks(times[::2]) + plt.gca().set_xticklabels(time_str[::2]) + outdir = self.C.output_root + fname = '{0}_Growth_allmembers.png'.format(ofname) + fpath = os.path.join(outdir,fname) + fig.savefig(fpath) - - def plot_skewT(self,wrfout,plot_time,prof_lat,prof_lon,dom=1,save_output=0): - path_to_WRF = self.wrfout_files_in(C.wrfout_root) - W = WRFOut(path_to_WRF) - ST = SkewT(self.C) - ST.plot_skewT(W,plot_time,prof_lat,prof_lon,dom,save_output) - + plt.close() + print("Saved {0}.".format(fpath)) + + + def plot_skewT(self,plot_time,plot_latlon,dom=1,save_output=0,composite=0): + wrfouts = self.wrfout_files_in(self.C.wrfout_root) + for wrfout in wrfouts: + if not composite: + W = WRFOut(wrfout) + ST = SkewT(self.C,W) + ST.plot_skewT(plot_time,plot_latlon,dom,save_output) + nice_time = utils.string_from_time('title',plot_time) + print("Plotted Skew-T for time {0} at {1}".format( + nice_time,plot_latlon)) + else: + #ST = SkewT(self.C) + pass + def plot_streamlines(self,lv,times): wrfpath = self.wrfout_files_in(self.C.wrfout_root)[0] self.W = WRFOut(wrfpath) @@ -733,3 +792,77 @@ def plot_streamlines(self,lv,times): + def make_1D(self,data,output='list'): + """ Make sure data is a time series + of 1D values, and numpy array. + + List of arrays -> Numpy array or list + """ + if isinstance(data,list): + + data_list = [] + for time in data: + data_list.append(N.sum(time[0])) + + if output == 'array': + data_out = N.array(data_list) + else: + data_out = data_list + + + #elif isinstance(data,N.array): + # shape = data.shape + # if len(shape) == 1: + # data_out = data + # elif len(shape) == 2: + # data_out = N.sum(data) + return data_out + + def plot_domains(self,wrfouts,labels,latlons,colour=0): + """ + wrfouts : list of wrfout file paths + latlons : dictionary of Nlim,Elim,Slim,Wlim + for plot + """ + + fig = plt.figure() + + # Create basemap first of all + basemap_res = getattr(self.C,'basemap_res',self.D.basemap_res) + + m = Basemap( + projection='merc', + llcrnrlon=latlons['Wlim'],llcrnrlat=latlons['Slim'], + urcrnrlon=latlons['Elim'],urcrnrlat=latlons['Nlim'], + lat_0=latlons['lat0'],lon_0=latlons['lon0'], + resolution=basemap_res,area_thresh=500 + ) + + m.drawcoastlines() + m.drawstates() + m.drawcountries() + + if not isinstance(colour,collections.Sequence): + colours = ['k',] * len(wrfouts) + else: + colours = colour + # Get corners of each domain + for gridlabel,fpath,colour in zip(labels,wrfouts,colours): + W = WRFOut(fpath) + print("Plotting domain {0} for {1}".format(gridlabel,fpath)) + #Nlim, Elim, Slim, Wlim = W.get_limits() + x,y = m(W.lons,W.lats) + xl = len(x[0,:]) + midpt = len(y[0,:])/2 + plt.annotate(gridlabel,color=colour,fontsize=10,xy=(x[0,-(0.12*xl)],y[0,midpt]), + bbox=dict(fc='white'),alpha=1,va='center',ha='left') + m.plot(x[0,:],y[0,:],colour,lw=2) + plt.plot(x[:,0],y[:,0],colour,lw=2) + plt.plot(x[len(y)-1,:],y[len(y)-1,:],colour,lw=2) + plt.plot(x[:,len(x)-1],y[:,len(x)-1],colour,lw=2) + + fpath = os.path.join(self.C.output_root,'domains.png') + fig.savefig(fpath) + + + diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 9e93b4e..59e20e4 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -13,7 +13,8 @@ class BirdsEye(Figure): def __init__(self,config,wrfout): self.C = config - self.W = wrfout + if not isinstance(wrfout,list): + self.W = wrfout self.D = Defaults() self.p2p = self.C.output_root print self.p2p diff --git a/postWRF/postWRF/dke.py b/postWRF/postWRF/dke.py new file mode 100644 index 0000000..d7c5473 --- /dev/null +++ b/postWRF/postWRF/dke.py @@ -0,0 +1,412 @@ +# This script creates plots for pulsation data + +### IMPORTS +import copy +import pickle as P +import numpy as N +import pytz +import matplotlib as M +UTC = pytz.utc +import matplotlib.pyplot as plt +import datetime +import scipy.signal +import pdb +import calendar as cal +import random +import scipy.io +import scipy +import scipy.stats as stats +import scipy.stats.mstats as SSM +from matplotlib.colors import cnames +from mpl_toolkits.mplot3d import Axes3D + + +### OPTIONS/CONSTANTS +runningmeanswitch = 0 # Use butterworth filter + +### PLOTTING + +### FUNCTIONS +def estimated_autocorrelation(x): + n = len(x) + variance = x.var() + x = x-x.mean() + r = N.correlate(x, x, mode = 'full')[-n:] + #assert N.allclose(r, N.array([(x[:n-k]*x[-(n-k):]).sum() for k in range(n)])) + result = r/(variance*(N.arange(n, 0, -1))) + return result + +def runningmean(interval,windowsize): + window = N.ones(int(windowsize))/float(windowsize) + return N.convolve(interval, window, 'same') + +def window(St,End,data,time): + diffSt = abs(time-St) + diffEnd = abs(time-End) + nSt = N.where(diffSt==(N.min(diffSt)))[0][0] + nEnd = N.where(diffEnd==(N.min(diffEnd)))[0][0] + windowdata = data[nSt:nEnd] + windowtime = time[nSt:nEnd] + return windowdata,windowtime + +def period_ticks(X): + X2 = (1/X)/60 + return ["%.1f" %x for x in X2] + +def butterworth_hp(data,samplerate, cutoff): + nyq = samplerate * 0.5 #Nyquist frequency + #bN,wn = scipy.signal.buttord(wp=cutoff/nyq, ws=cutoff*1.2/nyq,gpass = 0.1, gstop = 10.0) + #bN,wn = scipy.signal.buttord(wp=0.5, ws=0.1 ,gpass = 0.1, gstop = 10.0) + #bb,ba = scipy.signal.butter(bN, wn, btype='high') + bb,ba = scipy.signal.butter(2, cutoff/nyq, btype='high') + #buttered = scipy.signal.lfilter(bb,ba, interval) + buttered = scipy.signal.filtfilt(bb,ba,data) + return buttered, bb, ba + +def confidence(Pxx,freqs,NFFT,window=mlab.window_hanning,Confidencelevel=0.95): + # Filipe PA Fernades + avfnc = N.asarray([1.]) + pad_to = NFFT + if M.cbook.iterable(window): + assert(len(window) == NFFT) + windowVals = window + else: + windowVals = window(N.ones((NFFT,), x.dtype)) + edof = (1. + (1. / N.sum(avfnc ** 2) - 1.) * N.float(NFFT) / + N.float(pad_to) * windowVals.mean()) + + a1 = (1. - Confidencelevel) / 2. + a2 = 1. - a1 + + lower = Pxx * stats.chi2.ppf(a1, 2 * edof) / stats.chi2.ppf(0.50, 2 * edof) + upper = Pxx * stats.chi2.ppf(a2, 2 * edof) / stats.chi2.ppf(0.50, 2 * edof) + + cl = N.c_[upper, lower] + return cl + +### DATA LOADING +with f as open(fname,'r'): + data = load(f,'r') + +#OR... + +# DATA +nc1 = self.W1.nc +nc2 = self.W2.nc + +### PLOTS +for s in D.keys(): + for v in vars: + fig = plt.figure(figsize=(13,8)) + ax = plt.subplot(111) + + # Matplotlib float format for dates + hrs = M.dates.HourLocator(tz=UTC) + + # Have to chop off last piece of data thanks to a weird time measurement + t = D[s]['pytime'].astype('int64')[0:-2] + dts = [datetime.datetime.fromtimestamp(tt,tz=UTC) for tt in t] + fds = M.dates.date2num(dts) + fmt = '%H' + hfmt = M.dates.DateFormatter(fmt,tz=UTC) + + ax.plot(fds,D[s][v][0:-2]) + + # Ticks + ax.xaxis.set_major_locator(hrs) + ax.xaxis.set_major_formatter(hfmt) + for tick in ax.xaxis.get_major_ticks(): + tick.label.set_fontsize(18) + for tick in ax.yaxis.get_major_ticks(): + tick.label.set_fontsize(18) + + # Labels + plt.xlabel('Hours (UTC)',fontsize=20) + plt.ylabel('Wind speed (m/s)',fontsize=20) + pname = '_'.join(('meteogram',v,s,'.png')) + plt.savefig(OUTDIR+pname) + plt.clf() +""" +g, gt = window(gSt,gEnd,glovers['wsms'][:-2],glovers['pytime'][:-2]) +c1,c1t = window(c1St,c1End,church['wsms'][:-2],church['pytime'][:-2]) +c2,c2t = window(c2St,c2End,church['wsms'][:-2],church['pytime'][:-2]) +tg, tgt = window(gSt,gEnd,glovers['tmpc'][:-2],glovers['pytime'][:-2]) +tc1,tc1t = window(c1St,c1End,church['tmpc'][:-2],church['pytime'][:-2]) +tc2,tc2t = window(c2St,c2End,church['tmpc'][:-2],church['pytime'][:-2]) +ag, agt = window(gSt,gEnd,glovers['alti'][:-2],glovers['pytime'][:-2]) +ac1,ac1t = window(c1St,c1End,church['alti'][:-2],church['pytime'][:-2]) +ac2,ac2t = window(c2St,c2End,church['alti'][:-2],church['pytime'][:-2]) + +# Export this to matlab for finding regress +gst = N.vstack((g,gt)) +scipy.io.savemat('gdata.mat', mdict={'g':gst}) + +c1st = N.vstack((c1,c1t)) +scipy.io.savemat('c1data.mat', mdict={'c1':c1st}) + +c2st = N.vstack((c2,c2t)) +scipy.io.savemat('c2data.mat', mdict={'c2':c2st}) + +""" + +pxx = [] #Power +freqs = [] # Frequencies, in Hz +#cl = [] # Confidence limit +#sflist = [] # Red noise generator +#noisef = [] # noise freqs +#iter = (g,c1,c2) +#iter = (g,tg,ag) +#times = (gt,tgt,agt) +#nm = ('glovers','churchearly','churchlate') +#nm = 'glovers'*3 +#siglist = (3.9371,1.9277,2.3655) + +if allplots == 1: + + runningmeanmatrix = [] + #for it,t,n,sige in zip(iter,times,nm,siglist): + for s in D.keys(): # Iterate over station + for w in wvars: # iterate over windowed variables + # Running mean for glovers due to non-linear detrending + if s == 'glovers': + if runningmeanswitch == 1: + ym = runningmean(D[s][w]['data'],10) + y = (D[s][w]['data']-ym)[10:-10] + x = D[s][w]['times'][10:-10] + D[s][w]['rmm'] = D[s][w]['data']-ym + else: + cutoff = 2.77777e-4 # 60 minutes in Hz + samplerate = 1/60.0 # Every 60 seconds + #samplerate = 1 # Every 60 seconds + y, bb, ba = butterworth_hp(D[s][w]['data'],samplerate,cutoff) + x = D[s][w]['times'] + # Calculate response + (ww,hh) = scipy.signal.freqz(bb,ba)#,x) + ww = ww/(2*N.pi) / 60 # Get into minutes + else: + y = scipy.signal.detrend(D[s][w]['data']) + x = D[s][w]['times'] + plt.figure() + plt.plot(x,y) + plt.savefig(OUTDIR+'_'.join(('detrended',s,w))) + plt.clf() + + plt.figure() + ax1 = plt.subplot(111) + ax1.grid(True) + + pmin, pmax = (-5,-2) + xmin, xmax = (10**pmin, 10**pmax) + + ax2 = ax1.twiny() + X2vals = N.array(N.logspace(pmin,pmax,12)) + X2ticks = N.array(N.linspace(0,1,12)) + + + ax1.semilogx(ww,N.abs(hh)) + ax1.set_xlim(xmin,xmax) + + ax2.set_xticks(X2ticks) + ax2.set_xticklabels(period_ticks(X2vals),color='red') + ax2.set_xlabel('Period (min)') + ax2.grid(True,'major',color='red') + plt.savefig(OUTDIR+'butterworthfreqresponse.png',bbox='tight') + plt.clf() + + # Estimate autocorrelation and phi + result = estimated_autocorrelation(y) + phi = result[1] + + # Set Hanning window length and observation rate + if s=='glovers': + wlen = 64 + #fs = (1/60.)/60 # Every minute + fs = 1/60. # Every minute + else: + wlen = 32 + fs = 1/300. # Every 5 minutes + #fs = (1/300.)/60 # Every 5 minutes + + # FFT with optimal Welch overlap (half the window length) + out1, out2 = plt.psd(y,NFFT=wlen,Fs=fs,detrend=mlab.detrend,window=mlab.window_hanning,scale_by_freq=True,noverlap=wlen/2) + #out1, out2, out3 = TS.psd_ci(it,NFFT=NFFT,Fs=FS,detrend=mlab.detrend,window=mlab.window_hanning,scale_by_freq=True,noverlap=NFFT/2, Confidencelevel=0.05) + D[s][w]['pxx'] = out1 + D[s][w]['freqs'] = out2 + #cl.append(out3) + + #plt.savefig(OUTDIR+'PSD_'+n+'.png') + # Calculate red noise + #phi = result[1] + #print sige + #sige = 0 + #Num = len(it) + #y = N.zeros([Num]) + #y[0] = random.random() * sige + #for i in range(1,Num): + # y[i] = (phi*y[i-1]) + random.random()*sige + #y = scipy.signal.detrend(y) + #P1, fp1, dummy1 = TS.psd_ci(y,NFFT=NFFT,window=mlab.window_hanning,Fs=FS) + + # Calculate red noise powers + sf = (1-(phi**2))/((1+(phi**2)) - 2*phi*N.cos(N.linspace(0,N.pi,len(out2)))) + sf = sf*(N.sum(out1)/N.sum(sf)) + D[s][w]['rnoise'] = sf + cl = confidence(sf,out2,wlen) + D[s][w]['cl'] = cl + #mdatan = N.vstack((fp1,sf)) + #mn = str(n)+'noise' + #scipy.io.savemat(mn+'.mat',mdict={mn : mdatan}) + + #mdatar = N.vstack((out1,out2)) + #mr = str(n) + 'real' + #scipy.io.savemat(mr+'.mat',mdict={mr : mdatar}) + fig = plt.figure(figsize=(13,8)) + ax1 = plt.subplot(111) + ax1.grid(True) + + #if s=='glovers': + # pmin, pmax = (-4,-1) + #else: + pmin, pmax = (-4,-2) + xmin, xmax = (10**pmin, 10**pmax) + # Add period x-axis too + ax2 = ax1.twiny() + X2vals = N.array(N.logspace(pmin,pmax,12)) + X2ticks = N.array(N.linspace(0,1,12)) + + ax1.loglog(out2,out1,'k') + ax1.loglog(out2,sf,'k--') + ax1.set_xlim(xmin,xmax) + + ax2.set_xticks(X2ticks) + ax2.set_xticklabels(period_ticks(X2vals),color='red') + ax2.set_xlabel('Period (min)') + + ax2.grid(True,'major',color='red') + + #plt.title(''.join(('Power spectral density for',s,w))) + pname = '_'.join(('PSD',s,w)) + plt.savefig(OUTDIR+pname) + plt.clf() + +# Now we have pxx and freqs, ready to plot all three together for the poster - per variable +D['glovers']['label'] = ('Glovers Lane','r') +D['church1']['label'] = ('Church Butte: early','g') +D['church2']['label'] = ('Church Butte: late', 'b') +iter = range(0,3) + +if allplots == 1: + for w,n in zip(wvars,varnames): + fig = plt.figure(figsize=(13,8)) + ax1 = plt.subplot(111) + ax1.grid(True) + + pmin,pmax = (-4,-2) + xmin, xmax = (10**pmin, 10**pmax) + + # Add period x-axis too + ax2 = ax1.twiny() + X2vals = N.array(N.logspace(pmin,pmax,12)) + X2ticks = N.array(N.linspace(0,1,12)) + + labellist = [] # Legend creation from dictionary + for i,s in enumerate(D.keys()): # Plot all three stations at once + ax1.loglog(D[s][w]['freqs'],D[s][w]['pxx'],D[s]['label'][1]) + labellist.append(D[s]['label'][0]) + ax1.legend(labellist) + for i,s in enumerate(D.keys()): # Plot all three stations at once + ax1.loglog(D[s][w]['freqs'],D[s][w]['rnoise'],D[s]['label'][1]+'--') + + ax1.set_xlim(xmin,xmax) + ax2.set_xticks(X2ticks) + ax2.set_xticklabels(period_ticks(X2vals),color='red') + ax2.set_xlabel('Period (min)') + ax2.grid(True,'major',color='red') + + pname = '_'.join(('PSDcombined',n,'.png')) + plt.savefig(OUTDIR+pname) + plt.clf() + +# Output mat files for harmonic analysis +if runningmeanswitch == 1: + gwind = N.vstack(([D['glovers']['wwind'][x] for x in ('data','rmm','times')])) + galti = N.vstack(([D['glovers']['walti'][x] for x in ('data','rmm')])) + gtemp = N.vstack(([D['glovers']['wtemp'][x] for x in ('data','rmm')])) + scipy.io.savemat('gloversmatrix.mat', mdict={'gwind':gwind, 'galti':galti,'gtemp':gtemp}) + + +# Combine all for Glovers Lane +pmin, pmax = (-4,-2) +cutoff = 2.77777e-4 # 60 minutes in Hz +samplerate = 1/60.0 # Every 60 seconds +xmin, xmax = (10**pmin, 10**pmax) +wlen = 64 +X2vals = N.array(N.logspace(pmin,pmax,12)) +X2ticks = N.array(N.linspace(0,1,12)) + + +# Just Glovers Lane for publication. +s = 'glovers' +fig = plt.figure(figsize=(13,8)) +ax1 = plt.subplot(111) +ax1.grid(True) +ax2 = ax1.twiny() +labellist = [] +handlist = [] + +NAMES = {'walti':'Pressure','wtemp':'Temperature','wwind':'Wind Speed'} +for w in wvars: + psp = ax1.loglog(D[s][w]['freqs'],D[s][w]['pxx']) + handlist.append(psp) + labellist.append(NAMES[w]) + ax1.loglog(D[s][w]['freqs'],D[s][w]['rnoise'],'k--') + ax1.loglog(D[s][w]['freqs'],D[s][w]['cl'][:,0],'k--') + #ax1.loglog(D[s][w]['freqs'],D[s][w]['cl'][:,1],'k--') + +ax2.set_xticks(X2ticks) +ax2.set_xticklabels(period_ticks(X2vals),color='red') +ax2.set_xlabel('Period (min)') +ax1.set_xlabel('Period (Hz)') + +ax2.grid(True,'major',color='red') + +#ax1.set_xlim(xmin,xmax) +#ax2.set_xlim(72,1.7) + +ax1.set_ylabel('Power (dB/Hz)') +ax1.legend(handlist,labellist,loc=3) +plt.savefig(OUTDIR+'GloversLanePSD.png',bbox_inches='tight',pad_inches=0.4) +plt.clf() + +""" +# Attractor for Glovers Lane? + +#names = ('g','c1','c2') +### 3D attractor +#for n in names: +#inp = input('which station?') +#for n in inp: +n = 'g' +wind = eval(n) +temp = eval('t'+n) +alti = eval('a'+n) +# Z score +w,t,a = ([scipy.signal.detrend(i) for i in (wind,temp,alti)]) +# For Glovers... +if n == 'g': + l = 10 + wrm = runningmean(wind,l) + w = (wind-wrm) +else: + l = 1 +zw = SSM.zscore(w) +zt = SSM.zscore(t) +za = SSM.zscore(a) +# Plot trajectories +fig = plt.figure() +ax = fig.gca(projection='3d') +ax.plot(zw[l:-l],zt[l:-l],za[l:-l]) +ax.legend() +plt.show() +plt.savefig(OUTDIR+'3dphase.png') +""" diff --git a/postWRF/postWRF/skewt.py b/postWRF/postWRF/skewt.py index e21cae4..92c0752 100644 --- a/postWRF/postWRF/skewt.py +++ b/postWRF/postWRF/skewt.py @@ -9,144 +9,135 @@ import pdb import cPickle as pickle import sys +import os # User imports -from Params import T_zero,T_base,kappa,barb_increments,P_bot,outdir,ens -from Utils import gamma_s,td,e,openWRF,getDimensions,convert_time +#from Params import mc.Tz,mc.Tb,mc.kappa,barb_increments,self.P_bot,outdir,ens +#from Utils import gamma_s,td,e,openWRF,getDimensions,convert_time +from figure import Figure +from wrfout import WRFOut from WEM.utils import unix_tools from WEM.utils import generalmet from WEM.utils import gridded_data +from WEM.utils import utils +import WEM.utils.metconstants as mc class SkewT(Figure): - def __init__(self,config): + def __init__(self,config,wrfout=0): self.C = config + self.path_to_WRF = self.C.wrfout_root + self.path_to_output = self.C.output_root + if wrfout: + self.W = wrfout - def plot_skewT(self,wrfout,plot_time,prof_lat,prof_lon,dom,save_output): - # OPTIONS - # plotting - self.W = wrfout - height, width = (10,10) + #self.plevs = self.W.z_dim-1 - datestr = utils.string_from_time('output',plot_time) - # Pickle files are saved here: - pickledir = self.C.pickdir - pickle_fname = '_'.join('WRFsounding',datestr+'.p') + def plot_skewT(self,plot_time,plot_latlon,dom,save_output,save_plot=1): + + + # Defines the ranges of the plot, do not confuse with self.P_bot and self.P_top + self.barb_increments = {'half': 2.5,'full':5.0,'flag':25.0} + self.skewness = 37.5 + self.P_bot = 100000. + self.P_top = 10000. + self.dp = 100. + self.plevs = N.arange(self.P_bot,self.P_top-1,-self.dp) + + # pdb.set_trace() + prof_lat, prof_lon = plot_latlon + datestr = utils.string_from_time('output',plot_time) + t_idx = self.W.get_time_idx(plot_time,tuple_format=1) + y,x, exact_lat, exact_lon = gridded_data.getXY(self.W.lats1D,self.W.lons1D,prof_lat,prof_lon) - # Top and bottom limit of Skew T plots (Pa) - P_b = 105000.0 - P_t = 20000.0 # Create figure - fig = plt.figure(figsize=(width,height)) - isotherms() - isobars() - dry_adiabats() - moist_adiabats() + if save_plot: + height, width = (10,10) + fig = plt.figure(figsize=(width,height)) + self.isotherms() + self.isobars() + self.dry_adiabats() + self.moist_adiabats() - t_idx = self.W.get_time_idx(plot_time,tuple_format=1) - x,y, exact_lat, exact_lon = gridded_data.getXY(self.W.lats,self.W.lons,prof_lat,prof_lon) - P_slices = {'t': t_idx, 'lv': slice[None,None], 'la': y, 'lo': x} - H_slices = {'lv':0, 'la':y, 'lo':x} - P = self.W.get('pressure',P_slices) - elev = self.W.get('HGT',H_slices) + P_slices = {'t': t_idx, 'la': y, 'lo': x} + H_slices = {'t':t_idx, 'lv':0, 'la':y, 'lo':x} + # pdb.set_trace() + P = self.W.get('pressure',P_slices)[0,:,0,0] + + elev = self.W.get('HGT',H_slices) + + thin_locs = gridded_data.thinned_barbs(P) - thin_locs = gridded_data.thinned_barbs(P) + self.windbarbs(self.W.nc,t_idx,y,x,P,thin_locs,n=45,color='blue') + self.temperature(self.W.nc,t_idx,y,x,P,linestyle='solid',color='blue') + self.dewpoint(self.W.nc,t_idx,y,x,P,linestyle='dashed',color='blue') - ST._windbarbs(W.nc,t_idx,y,x,P,thin_locs,n=45,color='blue') - ST._temperature(nc,t_idx,y,x,P,linestyle='solid',color='blue') - ST._dewpoint(nc,t_idx,y,x,P,linestyle='dashed',color='blue') + xticks = N.arange(-20,51,5) + yticks = N.arange(100000.0,self.P_top-1,-10**4) + ytix = ["%4u" %(p/100.0) for p in yticks] + plt.xticks(xticks,['' if tick%10!=0 else str(tick) for tick in xticks]) + plt.yticks(yticks,ytix) - plt.xticks(xticks,['' if tick%10!=0 else str(tick) for tick in xticks]) - plt.yticks(yticks,ytix) + plt.axis([-20,50,105000.0,20000.0]) + plt.xlabel(r'Temperature ($^{\circ}$C) at 1000 hPa') + plt.xticks(xticks,['' if tick%10!=0 else str(tick) for tick in xticks]) + plt.ylabel('Pressure (hPa)') + plt.yticks(yticks,ytix) + #yticks = N.arange(self.P_bot,P_t-1,-10**4) + #plt.yticks(yticks,yticks/100) - plt.axis([-20,50,P_b,P_t]) - plt.xlabel(r'Temperature ($^{\circ}$C) at 1000\,hPa') - xticks = N.arange(-20,51,5) - plt.xticks(xticks,['' if tick%10!=0 else str(tick) for tick in xticks]) - plt.ylabel('Pressure (hPa)') - yticks = N.arange(P_bot,P_t-1,-10**4) - ytix = ["%4u" %(p/100.0) for p in yticks] - plt.yticks(yticks,ytix) - #yticks = N.arange(P_bot,P_t-1,-10**4) - #plt.yticks(yticks,yticks/100) + fname = '_'.join(('skewT',datestr,'{0:03d}'.format(x),'{0:03d}'.format(y))) + '.png' + utils.trycreate(self.path_to_output) - plt.savefig(outdirfull+'verifSkewT_'+ens+'_'+nicetime+'.png',bbox_inches='tight',pad_inches=0.1) - plt.clf() + fpath = os.path.join(self.path_to_output,fname) + plt.savefig(fpath) + plt.close() # For saving Skew T data if save_output: - u,v = ST.return_data('wind',nc,time,y,x,thin_locs) - T = ST.return_data('temp',nc,time,y,x,thin_locs,P=P) - Td = ST.return_data('dwpt',nc,time,y,x,thin_locs,P=P) + + # Pickle files are saved here: + pickle_fname = '_'.join(('WRFsounding',datestr,'{0:03d}'.format(x), + '{0:03d}'.format(y)+'.p')) + pickle_path = os.path.join(self.C.pickledir,pickle_fname) + u,v = self.return_data('wind',self.W.nc,t_idx,y,x,thin_locs) + T = self.return_data('temp',self.W.nc,t_idx,y,x,thin_locs,P=P) + Td = self.return_data('dwpt',self.W.nc,t_idx,y,x,thin_locs,P=P) data_dict = {'u':u,'v':v,'T':T,'Td':Td,'P':P} - with open(picklef,'wb') as p: + with open(pickle_path,'wb') as p: pickle.dump(data_dict,p) + print("Saving data to {0}".format(pickle_path)) - def skewTPlot(nest,timetuple): - """ - OLD VERSION!!!! - This is the method to use from the outside - - nest: The nesting level of the nc-file from WRF - time: The time for which to plot - """ - nc = openWRF(nest) - time = convert_time(nest,timetuple) - _Nx,_Ny,_Nz,_longs,_lats,_dx,_dy,x,y = getDimensions(nc) - - plt.figure() - _isotherms() - _isobars() - _dry_adiabats() - _moist_adiabats() - - P = nc.variables['P'][time,:,y,x] + nc.variables['PB'][time,:,y,x] - - _windbarbs(nc,time,y,x,P) - _temperature(nc,time,y,x,P) - _dewpoint(nc,time,y,x,P) - - plt.axis([-40,50,P_b,P_t]) - plt.xlabel('Temperature ($^{\circ}\! C$) at 1000hPa') - xticks = np.arange(-40,51,5) - plt.xticks(xticks,['' if tick%10!=0 else str(tick) for tick in xticks]) - plt.ylabel('Pressure (hPa)') - yticks = np.arange(P_bot,P_t-1,-10**4) - plt.yticks(yticks,yticks/100) - - sfcT = nc.variables['T2'][time,y,x]-T_zero - sfcP = nc.variables['PSFC'][time,y,x] - sfcW = nc.variables['Q2'][time,y,x] - sfcTd = td(e(sfcW,sfcP)) - plt.suptitle('Drybulb: %4.1f$^{\circ}\! C$ Dewpoint: %3.1f$^{\circ}\! C$ Pressure: %5.1f hPa' % (sfcT,sfcTd,0.01*sfcP), \ - fontsize=10, x = 0.5, y = 0.03) - - #plt.show() - plt.savefig(outdir+naming+str(nest)+'_skewT.png') - plt.title('Skew-T plot for (location)') - plt.close() + return - def _skewnessTerm(P): - return skewness * np.log(P_bot/P) + def skewT_composite(self,): + """ + Open pickle files from multiple soundings + Find mean Td/T + """ + pass + def skewnessTerm(self,P): + return self.skewness * N.log(self.P_bot/P) - def _windbarbs(nc,time,y,x,P,thin_locs,n=45.0,color='black'): + def windbarbs(self,nc,time,y,x,P,thin_locs,n=45.0,color='black'): uwind = 0.5*(nc.variables['U'][time,:,y,x]+nc.variables['U'][time,:,y,x+1]) vwind = 0.5*(nc.variables['V'][time,:,y,x]+nc.variables['V'][time,:,y+1,x]) zmax = len(uwind[thin_locs]) delta = 1 baraxis = [n for _j in range(0,zmax,delta)] + # pdb.set_trace() plt.barbs(baraxis,P[thin_locs],uwind[thin_locs],vwind[thin_locs], - barb_increments=barb_increments, linewidth = .75,color=color) + barb_increments=self.barb_increments, linewidth = .75,color=color) - def _windbarbs_real(uwind,vwind,P,delta=3,color='red',n=37.5): + def windbarbs_real(self,uwind,vwind,P,delta=3,color='red',n=37.5): # Is wind in kt or m/s? .... uwind* those = N.where(uwind==-9999) # Find nonsense values uwind = N.delete(uwind,those) @@ -156,92 +147,122 @@ def _windbarbs_real(uwind,vwind,P,delta=3,color='red',n=37.5): # n is x-ax position on skewT for barbs. baraxis = [n for _j in range(0,zmax,delta)] plt.barbs(baraxis,P[0:zmax:delta],uwind[0:zmax:delta],vwind[0:zmax:delta], - barb_increments=barb_increments, linewidth = .75, barbcolor = color, flagcolor = color) + barb_increments=self.barb_increments, linewidth = .75, barbcolor = color, flagcolor = color) - def _temperature(nc,time,y,x,P,linestyle='solid',color='black'): - theta = nc.variables['T'][time,:,y,x] + T_base - T = theta*(P/P_bot)**kappa - T_zero # Temperatur i halvflatene (C) - plt.semilogy(T + _skewnessTerm(P), P, basey=math.e, color = color, \ + def temperature(self,nc,time,y,x,P,linestyle='solid',color='black'): + theta = nc.variables['T'][time,:,y,x] + mc.Tb + T = theta*(P/self.P_bot)**mc.kappa - mc.Tz # Temperatur i halvflatene (C) + plt.semilogy(T + self.skewnessTerm(P), P, basey=math.e, color = color, \ linestyle=linestyle, linewidth = 1.5) - def _temperature_real(T,P,color='red',linestyle='dashed'): - plt.semilogy(T + _skewnessTerm(P), P, basey=math.e, color = color, \ + def temperature_real(self,T,P,color='red',linestyle='dashed'): + plt.semilogy(T + self.skewnessTerm(P), P, basey=math.e, color = color, \ linestyle=linestyle, linewidth = 1.5) - def _dewpoint(nc,time,y,x,P,linestyle='dashed',color='black'): + def dewpoint(self,nc,time,y,x,P,linestyle='dashed',color='black'): w = nc.variables['QVAPOR'][time,:,y,x] - plt.semilogy(td(e(w,P)) + _skewnessTerm(P), P, basey=math.e, color = color, \ + plt.semilogy(self.td(self.e(w,P)) + self.skewnessTerm(P), P, basey=math.e, color = color, \ linestyle=linestyle, linewidth = 1.5) - def _dewpoint_real(td,P,color='red',linestyle='dashed'): - plt.semilogy(td + _skewnessTerm(P), P, basey=math.e, color = color, \ + def dewpoint_real(self,td,P,color='red',linestyle='dashed'): + plt.semilogy(td + self.skewnessTerm(P), P, basey=math.e, color = color, \ linestyle=linestyle, linewidth = 1.5) - def return_data(whichdata,nc,time,y,x,thin_locs,P=None): + def return_data(self,whichdata,nc,time,y,x,thin_locs,P=None): if whichdata == 'wind': uwind = 0.5*(nc.variables['U'][time,:,y,x]+nc.variables['U'][time,:,y,x+1]) vwind = 0.5*(nc.variables['V'][time,:,y,x]+nc.variables['V'][time,:,y+1,x]) return uwind[thin_locs],vwind[thin_locs] elif whichdata == 'temp': - theta = nc.variables['T'][time,:,y,x] + T_base - T = theta*(P/P_bot)**kappa - T_zero + theta = nc.variables['T'][time,:,y,x] + mc.Tb + T = theta*(P/self.P_bot)**mc.kappa - mc.Tz return T elif whichdata == 'dwpt': w = nc.variables['QVAPOR'][time,:,y,x] - Td = td(e(w,P)) + Td = self.td(self.e(w,P)) return Td else: print 'Use valid variable.' - def gettime(): + def gettime(self,): t = convert_time(dom,timetuple) return t - def _isotherms(): - for temp in np.arange(-140,50,10): - plt.semilogy(temp + _skewnessTerm(plevs), plevs, basey=math.e, \ + def isotherms(self,): + for temp in N.arange(-140,50,10): + plt.semilogy(temp + self.skewnessTerm(self.plevs), self.plevs, basey=math.e, \ color = ('blue' if temp <= 0 else 'red'), \ linestyle=('solid' if temp == 0 else 'dashed'), linewidth = .5) - def _isobars(): - for n in np.arange(P_bot,P_t-1,-10**4): + def isobars(self,): + for n in N.arange(self.P_bot,self.P_top-1,-10**4): plt.plot([-40,50], [n,n], color = 'black', linewidth = .5) - def _dry_adiabats(): - for tk in T_zero+np.arange(-30,210,10): - dry_adiabat = tk * (plevs/P_bot)**kappa - T_zero + _skewnessTerm(plevs) - plt.semilogy(dry_adiabat, plevs, basey=math.e, color = 'brown', \ + def dry_adiabats(self,): + for tk in mc.Tz+N.arange(-30,210,10): + dry_adiabat = tk * (self.plevs/self.P_bot)**mc.kappa - mc.Tz + self.skewnessTerm(self.plevs) + plt.semilogy(dry_adiabat, self.plevs, basey=math.e, color = 'brown', \ linestyle='dashed', linewidth = .5) - def _moist_adiabats(): - ps = [p for p in plevs if p<=P_bot] - for temp in np.concatenate((np.arange(-40.,10.1,5.),np.arange(12.5,45.1,2.5))): + def moist_adiabats(self,): + ps = [p for p in self.plevs if p<=self.P_bot] + for temp in N.concatenate((N.arange(-40.,10.1,5.),N.arange(12.5,45.1,2.5))): moist_adiabat = [] for p in ps: - temp -= dp*gamma_s(temp,p) - moist_adiabat.append(temp + _skewnessTerm(p)) + temp -= self.dp*self.gamma_s(temp,p) + moist_adiabat.append(temp + self.skewnessTerm(p)) plt.semilogy(moist_adiabat, ps, basey=math.e, color = 'green', \ linestyle = 'dotted', linewidth = .5) """ - Plots skewT-lnP-diagram from WRF-output file. - @author Geir Arne Waagbø - @see http://code.google.com/p/pywrfplot/ + Plots skewT-lN-diagram from WRF-output file. + author Geir Arne Waagbo + see http://code.google.com/p/pywrfplot/ Formulas taken from Rogers&Yau: A short course in cloud physics (Third edition) Some inspiration from: http://www.atmos.washington.edu/~lmadaus/pyscript/plot_wrf_skewt.txt - from Params import T_zero,T_base,kappa,barb_increments,P_bot, naming, outdir + from Params import mc.Tz,mc.Tb,mc.kappa,barb_increments,self.P_bot, naming, outdir from Utils import gamma_s,td,e,openWRF,getDimensions,convert_time skewness = 37.5 - # Defines the ranges of the plot, do not confuse with P_bot and P_top + # Defines the ranges of the plot, do not confuse with self.P_bot and self.P_top P_b = 105000. P_t = 10000. - dp = 100. - plevs = np.arange(P_b,P_t-1,-dp) + self.dp = 100. + self.plevs = N.arange(P_b,P_t-1,-self.dp) """ + def gamma_s(self,T,p): + """Calculates moist adiabatic lapse rate for T (Celsius) and p (Pa) + Note: We calculate dT/dp, not dT/dz + See formula 3.16 in Rogers&Yau for dT/dz, but this must be combined with + the dry adiabatic lapse rate (gamma = g/cp) and the + inverse of the hydrostatic equation (dz/dp = -RT/pg)""" + a = 2./7. + b = ((mc.R/mc.Rv)*(mc.L**2))/(mc.R*mc.cp) + c = a*(mc.L/mc.R) + + esat = self.es(T) + wsat = (mc.R/mc.Rv)*esat/(p-esat) # Rogers&Yau 2.18 + numer = a*(T+mc.Tz) + c*wsat + denom = p * (1 + b*wsat/((T+mc.Tz)**2)) + return numer/denom # Rogers&Yau 3.16 + + def td(self,e): + """Returns dew point temperature (C) at vapor pressure e (Pa) + Insert Td in 2.17 in Rogers&Yau and solve for Td""" + return 243.5 * N.log(e/611.2)/(17.67-N.log(e/611.2)) + + def e(self,w,p): + """Returns vapor pressure (Pa) at mixing ratio w (kg/kg) and pressure p (Pa) + Formula 2.18 in Rogers&Yau""" + return w*p/(w+(mc.R/mc.Rv)) + + def es(self,T): + """Returns saturation vapor pressure (Pascal) at temperature T (Celsius) + Formula 2.17 in Rogers&Yau""" + return 611.2*N.exp(17.67*T/(T+243.5)) diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 52d920b..9608265 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -32,6 +32,9 @@ def __init__(self,fpath,config=0): self.lats = self.nc.variables['XLAT'][0,...] # Might fail if only one time? self.lons = self.nc.variables['XLONG'][0,...] + self.lats1D = self.lats[:,len(self.lats)/2] + self.lons1D = self.lons[len(self.lons)/2,:] + self.cen_lat = float(self.nc.CEN_LAT) self.cen_lon = float(self.nc.CEN_LON) self.truelat1 = float(self.nc.TRUELAT1) @@ -162,10 +165,20 @@ def create_slice(self,PS): elif PS['lv']=='all': sl.append(slice(None,None)) - if any('north' and 'west' in p for p in PS['dim_names']): - sl.append(PS['la']) - sl.append(PS['lo']) + if any('north' in p for p in PS['dim_names']): + if isinstance(PS['la'],slice): + sl.append(PS['la']) + else: + sl.append(slice(PS['la'],PS['la']+1)) + + if any('west' in p for p in PS['dim_names']): + if isinstance(PS['lo'],slice): + sl.append(PS['lo']) + else: + sl.append(slice(PS['lo'],PS['lo']+1)) + + # pdb.set_trace() return sl def check_destagger(self,var): @@ -699,5 +712,10 @@ def edit_namelist(self,fpath,old,new,incolumn=1,col=23): nameout.close() break - + def get_limits(self): + Nlim = self.lats[-1] + Elim = self.lons[-1] + Slim = self.lats[0] + Wlim = self.lons[0] + return Nlim, Elim, Slim, Wlim diff --git a/utils/gridded_data.py b/utils/gridded_data.py index 68c82f3..29444de 100644 --- a/utils/gridded_data.py +++ b/utils/gridded_data.py @@ -12,6 +12,7 @@ def getXY(lats,lons,ptlat,ptlon): # Find where these are in the grid wherelat = N.where(abs(lats-ptlat) == minlat) wherelon = N.where(abs(lons-ptlon) == minlon) + # pdb.set_trace() lat_idx = N.where(lats==lats[wherelat])[0][0] lon_idx = N.where(lons==lons[wherelon])[0][0] exactlat = lats[wherelat] diff --git a/utils/utils.py b/utils/utils.py index 761592c..4cfaa72 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -151,13 +151,13 @@ def dstack_loop(data, obj): If obj doesn't exist, then initialise it If obj does exist, stack data. """ - if not obj: - stack = data - else: + if isinstance(obj,N.ndarray): stack = N.dstack((obj,data)) + else: + stack = data return stack - + def dstack_loop_v2(data, obj): """ Need to set obj = 0 at start of loop in master script @@ -183,12 +183,11 @@ def vstack_loop(data, obj): If obj doesn't exist, then initialise it If obj does exist, stack data. """ - try: - print obj - except NameError: - stack = data - else: + + if isinstance(obj,N.ndarray): stack = N.vstack((obj,data)) + else: + stack = data return stack @@ -202,3 +201,13 @@ def generate_times(idate,fdate,interval): times = range(i,f,interval) return times +def generate_colours(M,n): + """ + M : Matplotlib instance + n : number of colours you want + + Returns + """ + + colourlist = [M.cm.spectral(i) for i in N.linspace(0.08,0.97,n)] + return colourlist From c651f445374dc17cebf21a636951dace2a6d0f00 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 1 May 2014 16:41:02 -0500 Subject: [PATCH 084/111] Added example Skew T script --- postWRF/bin/.gitignore | 2 + postWRF/bin/bowecho_plot.py | 118 ++++++++++++++++++++++++++++++++++++ postWRF/bin/skewT.py | 52 ++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 postWRF/bin/bowecho_plot.py create mode 100644 postWRF/bin/skewT.py diff --git a/postWRF/bin/.gitignore b/postWRF/bin/.gitignore index d5751f9..ec40646 100644 --- a/postWRF/bin/.gitignore +++ b/postWRF/bin/.gitignore @@ -3,3 +3,5 @@ !DKE.py !20110419_plot.py !settings.py +!bowecho_plot.py +!skewT.py diff --git a/postWRF/bin/bowecho_plot.py b/postWRF/bin/bowecho_plot.py new file mode 100644 index 0000000..0d40bbb --- /dev/null +++ b/postWRF/bin/bowecho_plot.py @@ -0,0 +1,118 @@ +import os +import pdb +import sys +sys.path.append('/home/jrlawson/gitprojects/') + +from WEM.postWRF.postWRF import WRFEnviron +from settings import Settings +import WEM.utils.utils as utils +from WEM.postWRF.postWRF.rucplot import RUCPlot + +config = Settings() +p = WRFEnviron(config) + +#case = '20060526' +#case = '20090910' +#case = '20110419' +case = '20130815' + +IC = 'GEFSR2' +#IC = 'NAM' +#IC = 'RUC' +#ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] +#ensnames = ['p'+"%02d" %n for n in range(8,11)] +ensnames = ['p09',] +MP = 'ICBC' +#ensnames = ['anl'] +#experiments = ['Kessler','Ferrier',]#'WSM5','WDM5','Lin','WDM6_Grau','WDM6_Hail','Morrison_Grau','Morrison_Hail'] +#experiments = ['WSM6_Grau','WSM6_Hail','Kessler','Ferrier','WSM5','WDM5','Lin','WDM6_Grau','WDM6_Hail','Morrison_Grau','Morrison_Hail'] +#experiments = ['ICBC',] +#experiment = 'VERIF' +experiments = ['s'+"%02d" %n for n in range(1,11)] + +#itime = (2006,5,26,22,0,0) +#itime = (2009,9,10,23,0,0) +#itime = (2011,4,19,18,0,0) +itime = (2013,8,15,18,0,0) + +#ftime = (2006,5,27,11,0,0) +#ftime = (2009,9,11,14,0,0) +#ftime = (2011,4,20,10,30,0) +ftime = (2013,8,16,12,0,0) + +times = utils.generate_times(itime,ftime,60*60) +#shear_times = utils.generate_times(itime,ftime,3*60*60) +#sl_times = utils.generate_times(sl_itime,sl_ftime,1*60*60) +thresh = 10 +skewT_time = (2013,8,16,3,0,0) +skewT_latlon = (35.2435,-97.4708) + +variables = {'strongestwind':{}} +#variables['cref'] = {'lv':2000,'pt':times} +#variables['PMSL'] = {'lv':2000,'pt':times,'plottype':'contour','smooth':5} +#variables['wind10'] = {'lv':2000,'pt':times} +#variables['buoyancy'] = {'lv':2000,'pt':times} +#variables['shear'] = {'top':3,'bottom':0,'pt':shear_times} +variables['strongestwind'] = {'lv':2000, 'itime':itime, 'ftime':ftime, 'range':(thresh,27.5,1.25)} +# variables = {'shear':{'pt':shear_times, 'top':3, 'bottom':0}} +# variables = {'thetae':{'lv':2000,'pt':times}, 'CAPE':{'pt':times}} +# variables = {'cref':{'lv':2000,'pt':times}, 'shear':{'pt':shear_times, 'top':3, 'bottom':0}} +#variables = {'PMSL':{'lv':2000,'pt':times,'plottype':'contour','smooth':5}} + +#shear06 = {'shear':{'top':6,'bottom':0,'pt':shear_times}} + + +for en in ensnames: + for ex in experiments: + # Reload settings + p.C = Settings() + + # Change paths to new location + p.C.output_root = os.path.join(config.output_root,case,IC,en,MP,ex) + p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,MP,ex) + p.C.pickledir = os.path.join(config.wrfout_root,case,IC,en,MP,ex) + + p.plot_skewT(skewT_time,skewT_latlon,save_output=1) + +#p.plot_skewT(skewT_time,skewT_latlon,composite=1) + +""" +for en in ensnames: + for ex in experiments: + # Reload settings + p.C = Settings() + + # Change paths to new location + p.C.output_root = os.path.join(config.output_root,case,IC,en,MP,ex) + p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,MP,ex) + p.plot_2D(variables) + + + +for en in ensnames: + # Reload settings + p.C = Settings() + # Change paths to new location + p.C.output_root = os.path.join(config.output_root,case,IC,en,experiment) + p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,experiment) + #p.plot_2D(variables) + print p.C.wrfout_root + p.plot_streamlines(2000,sl_times) + + +# RUC file is one-per-time so .nc file is specified beforehand +en = ensnames[0] +RC = Settings() +RC.output_root = os.path.join(config.output_root,case,IC,en,experiment) +RC.path_to_RUC = os.path.join(config.RUC_root,case,IC,en,experiment) +WRF_dir = os.path.join(config.wrfout_root,case,'NAM',en,'ICBC') + +variables = ['streamlines',] +level = 2000 + +for t in sl_times: + RUC = RUCPlot(RC,t,wrfdir=WRF_dir) + #limits = RUC.colocate_WRF_map(WRF_dir) + RUC.plot(variables,level) +""" + diff --git a/postWRF/bin/skewT.py b/postWRF/bin/skewT.py new file mode 100644 index 0000000..fccc292 --- /dev/null +++ b/postWRF/bin/skewT.py @@ -0,0 +1,52 @@ +import os +import pdb +import sys + +# NUMBER 1: CHANGE THIS +sys.path.append('/path/to/WEM/') + +from WEM.postWRF.postWRF import WRFEnviron +from settings import Settings +import WEM.utils.utils as utils + +config = Settings() +p = WRFEnviron(config) + +# NUMBER 2: CHANGE FOLDER DETAILS HERE +case = '20130815' + +IC = 'GEFSR2' +ensnames = ['p09',] +MP = 'ICBC' +experiments = ['s'+"%02d" %n for n in range(1,11)] + +# NUMBER 3: SET THE TIME(s) YOU WANT PLOT +# Loop over times... +itime = (2013,8,15,18,0,0) +ftime = (2013,8,16,12,0,0) +times = utils.generate_times(itime,ftime,60*60) + +# ... or set just one time +skewT_time = (2013,8,16,3,0,0) + +# NUMBER 4: SET THE LAT/LON +skewT_latlon = (35.2435,-97.4708) + +# NUMBER 5: +# This is a loop over different folders +# For one plot, just use the (last line) plot_skewT command + +for en in ensnames: + for ex in experiments: + # Reload settings + p.C = Settings() + + # Change paths to new location + p.C.output_root = os.path.join(config.output_root,case,IC,en,MP,ex) + p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,MP,ex) + p.C.pickledir = os.path.join(config.wrfout_root,case,IC,en,MP,ex) + + # save_output saves pickle files for later use + p.plot_skewT(skewT_time,skewT_latlon,save_output=1) + + From 10c1f756242e7b21273b6e9b57c3bed81b9446d9 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 21 May 2014 10:37:33 -0500 Subject: [PATCH 085/111] Long time no update --- postWRF/bin/bowecho_plot.py | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/postWRF/bin/bowecho_plot.py b/postWRF/bin/bowecho_plot.py index 0d40bbb..f295ddd 100644 --- a/postWRF/bin/bowecho_plot.py +++ b/postWRF/bin/bowecho_plot.py @@ -11,34 +11,34 @@ config = Settings() p = WRFEnviron(config) -#case = '20060526' +case = '20060526' #case = '20090910' #case = '20110419' -case = '20130815' +#case = '20130815' -IC = 'GEFSR2' -#IC = 'NAM' +#IC = 'GEFSR2' +IC = 'NAM' #IC = 'RUC' #ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] #ensnames = ['p'+"%02d" %n for n in range(8,11)] -ensnames = ['p09',] -MP = 'ICBC' +ensnames = ['anl',] +MP = '' #ensnames = ['anl'] -#experiments = ['Kessler','Ferrier',]#'WSM5','WDM5','Lin','WDM6_Grau','WDM6_Hail','Morrison_Grau','Morrison_Hail'] +#experiments = ['WDM6_Grau','WDM6_Hail','Morrison_Grau','Morrison_Hail'] #experiments = ['WSM6_Grau','WSM6_Hail','Kessler','Ferrier','WSM5','WDM5','Lin','WDM6_Grau','WDM6_Hail','Morrison_Grau','Morrison_Hail'] -#experiments = ['ICBC',] +experiments = ['Morrison_Hail',] #experiment = 'VERIF' -experiments = ['s'+"%02d" %n for n in range(1,11)] +#experiments = ['s'+"%02d" %n for n in range(1,11)] -#itime = (2006,5,26,22,0,0) +itime = (2006,5,27,0,0,0) #itime = (2009,9,10,23,0,0) #itime = (2011,4,19,18,0,0) -itime = (2013,8,15,18,0,0) +#itime = (2013,8,15,18,0,0) -#ftime = (2006,5,27,11,0,0) +ftime = (2006,5,27,13,0,0) #ftime = (2009,9,11,14,0,0) #ftime = (2011,4,20,10,30,0) -ftime = (2013,8,16,12,0,0) +#ftime = (2013,8,16,12,0,0) times = utils.generate_times(itime,ftime,60*60) #shear_times = utils.generate_times(itime,ftime,3*60*60) @@ -47,13 +47,14 @@ skewT_time = (2013,8,16,3,0,0) skewT_latlon = (35.2435,-97.4708) +#variables = {'cref':{}} variables = {'strongestwind':{}} #variables['cref'] = {'lv':2000,'pt':times} +variables['strongestwind'] = {'lv':2000, 'itime':itime, 'ftime':ftime, 'range':(thresh,27.5,1.25)} #variables['PMSL'] = {'lv':2000,'pt':times,'plottype':'contour','smooth':5} #variables['wind10'] = {'lv':2000,'pt':times} #variables['buoyancy'] = {'lv':2000,'pt':times} #variables['shear'] = {'top':3,'bottom':0,'pt':shear_times} -variables['strongestwind'] = {'lv':2000, 'itime':itime, 'ftime':ftime, 'range':(thresh,27.5,1.25)} # variables = {'shear':{'pt':shear_times, 'top':3, 'bottom':0}} # variables = {'thetae':{'lv':2000,'pt':times}, 'CAPE':{'pt':times}} # variables = {'cref':{'lv':2000,'pt':times}, 'shear':{'pt':shear_times, 'top':3, 'bottom':0}} @@ -61,7 +62,7 @@ #shear06 = {'shear':{'top':6,'bottom':0,'pt':shear_times}} - +""" for en in ensnames: for ex in experiments: # Reload settings @@ -75,8 +76,8 @@ p.plot_skewT(skewT_time,skewT_latlon,save_output=1) #p.plot_skewT(skewT_time,skewT_latlon,composite=1) - """ + for en in ensnames: for ex in experiments: # Reload settings @@ -85,9 +86,11 @@ # Change paths to new location p.C.output_root = os.path.join(config.output_root,case,IC,en,MP,ex) p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,MP,ex) + print p.C.output_root + print p.C.wrfout_root p.plot_2D(variables) - +""" for en in ensnames: # Reload settings From 070dac2d5f379771a86210f25a95ed64a2935b45 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Fri, 11 Jul 2014 15:07:31 -0500 Subject: [PATCH 086/111] Cross-sections not working yet --- postWRF/bin/bowecho_plot.py | 39 ++++---- postWRF/bin/skewT.py | 19 +++- postWRF/postWRF/__init__.py | 8 +- postWRF/postWRF/figure.py | 46 +++++++++- postWRF/postWRF/protoinit.py | 2 + postWRF/postWRF/skewt.py | 132 +++++++++++++++++++++++++++ postWRF/postWRF/wrfout.py | 31 ++++++- postWRF/postWRF/xsection.py | 171 ++++++++++++++++++++++++++++++++++- 8 files changed, 413 insertions(+), 35 deletions(-) create mode 100644 postWRF/postWRF/protoinit.py diff --git a/postWRF/bin/bowecho_plot.py b/postWRF/bin/bowecho_plot.py index f295ddd..ebf9987 100644 --- a/postWRF/bin/bowecho_plot.py +++ b/postWRF/bin/bowecho_plot.py @@ -11,33 +11,34 @@ config = Settings() p = WRFEnviron(config) -case = '20060526' +enstype = 'MXMP' +#case = '20060526' #case = '20090910' -#case = '20110419' +case = '20110419' #case = '20130815' -#IC = 'GEFSR2' -IC = 'NAM' +IC = 'GEFSR2' +#IC = 'NAM' #IC = 'RUC' #ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] #ensnames = ['p'+"%02d" %n for n in range(8,11)] -ensnames = ['anl',] -MP = '' +ensnames = ['p04',] +#MP = 'WDM6_Grau' #ensnames = ['anl'] -#experiments = ['WDM6_Grau','WDM6_Hail','Morrison_Grau','Morrison_Hail'] #experiments = ['WSM6_Grau','WSM6_Hail','Kessler','Ferrier','WSM5','WDM5','Lin','WDM6_Grau','WDM6_Hail','Morrison_Grau','Morrison_Hail'] -experiments = ['Morrison_Hail',] +experiments = ['WDM5','Lin','WDM6_Grau','WDM6_Hail','Morrison_Grau','Morrison_Hail'] +#experiments = ['WSM5',] #experiment = 'VERIF' #experiments = ['s'+"%02d" %n for n in range(1,11)] -itime = (2006,5,27,0,0,0) +#itime = (2006,5,26,0,0,0) #itime = (2009,9,10,23,0,0) -#itime = (2011,4,19,18,0,0) +itime = (2011,4,19,18,0,0) #itime = (2013,8,15,18,0,0) -ftime = (2006,5,27,13,0,0) +#ftime = (2006,5,27,13,0,0) #ftime = (2009,9,11,14,0,0) -#ftime = (2011,4,20,10,30,0) +ftime = (2011,4,20,10,30,0) #ftime = (2013,8,16,12,0,0) times = utils.generate_times(itime,ftime,60*60) @@ -47,10 +48,8 @@ skewT_time = (2013,8,16,3,0,0) skewT_latlon = (35.2435,-97.4708) -#variables = {'cref':{}} -variables = {'strongestwind':{}} -#variables['cref'] = {'lv':2000,'pt':times} -variables['strongestwind'] = {'lv':2000, 'itime':itime, 'ftime':ftime, 'range':(thresh,27.5,1.25)} +variables = {'cref':{}} ; variables['cref'] = {'lv':2000,'pt':times} +#variables = {'strongestwind':{}} ; variables['strongestwind'] = {'lv':2000, 'itime':itime, 'ftime':ftime, 'range':(thresh,27.5,1.25)} #variables['PMSL'] = {'lv':2000,'pt':times,'plottype':'contour','smooth':5} #variables['wind10'] = {'lv':2000,'pt':times} #variables['buoyancy'] = {'lv':2000,'pt':times} @@ -84,8 +83,12 @@ p.C = Settings() # Change paths to new location - p.C.output_root = os.path.join(config.output_root,case,IC,en,MP,ex) - p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,MP,ex) + if ex == 'STCH': + p.C.output_root = os.path.join(config.output_root,case,IC,en,MP,ex) + p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,MP,ex) + else: + p.C.output_root = os.path.join(config.output_root,case,IC,en,ex) + p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,ex) print p.C.output_root print p.C.wrfout_root p.plot_2D(variables) diff --git a/postWRF/bin/skewT.py b/postWRF/bin/skewT.py index fccc292..0fc81a3 100644 --- a/postWRF/bin/skewT.py +++ b/postWRF/bin/skewT.py @@ -16,9 +16,12 @@ case = '20130815' IC = 'GEFSR2' -ensnames = ['p09',] -MP = 'ICBC' -experiments = ['s'+"%02d" %n for n in range(1,11)] +ICens = ['p09',] +#MP = 'ICBC' +#experiments = ['s'+"%02d" %n for n in range(1,11)] +experiments = ('ICBC','WSM6_Grau','WSM6_Hail','Kessler','Ferrier','WSM5', + 'WDM5','Lin','WDM6_Grau','WDM6_Hail', + 'Morrison_Grau','Morrison_Hail') # NUMBER 3: SET THE TIME(s) YOU WANT PLOT # Loop over times... @@ -36,6 +39,7 @@ # This is a loop over different folders # For one plot, just use the (last line) plot_skewT command +# For Skew Ts for en in ensnames: for ex in experiments: # Reload settings @@ -49,4 +53,13 @@ # save_output saves pickle files for later use p.plot_skewT(skewT_time,skewT_latlon,save_output=1) +# For composite profiles +path_to_wrfouts = [] +for ens in ICens: + for ex in experiments: + fpath = os.path.join(config.wrfout_root,case,ens,ex) + path_to_wrfouts.append(p.wrfout_files_in(fpath,dom=1)[0]) + +va = 'theta' +p.composite_profile(va,skewT_time,skewT_latlon,path_to_wrfouts,mean=1,std=1) diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index 992a97f..f22f5f5 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -32,11 +32,14 @@ from figure import Figure from birdseye import BirdsEye from skewt import SkewT +from skewt import Profile #import scales from defaults import Defaults from lookuptable import LookUpTable import WEM.utils.utils as utils +# TODO: Make this awesome + class WRFEnviron: def __init__(self,config): self.C = config @@ -765,7 +768,10 @@ def plot_error_growth(self,ofname,folder,pfname,sensitivity=0,ylimits=0,**kwargs plt.close() print("Saved {0}.".format(fpath)) - + def composite_profile(self,va,skewT_time,skewT_latlon,enspaths,dom=1,mean=0,std=0,xlim=0,ylim=0): + P = Profile(self.C) + P.composite_profile(va,skewT_time,skewT_latlon,enspaths,dom,mean,std,xlim,ylim) + def plot_skewT(self,plot_time,plot_latlon,dom=1,save_output=0,composite=0): wrfouts = self.wrfout_files_in(self.C.wrfout_root) for wrfout in wrfouts: diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index d78ef2d..8291046 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -1,4 +1,5 @@ -"""Collection of x-y cross-section classes. +""" +All matplotlib figures are subclasses of Figure. """ @@ -15,11 +16,21 @@ class Figure: def __init__(self,config,wrfout): + """ + C : configuration settings + W : data + """ + self.C = config - if wrfout=='RUC': - pass - else: - self.W = wrfout + self.W = wrfout + + #if wrfout=='RUC': + # pass + #else: + # self.W = wrfout + + # Create main figure + self.fig = plt.figure() def create_fname(self,*naming): """Default naming should be: @@ -59,3 +70,28 @@ def get_limited_domain(self,da,smooth=1): lat_sl = slice(None,None,smooth) lon_sl = slice(None,None,smooth) return lat_sl, lon_sl + + def just_one_colorbar(self,fpath): + """ + docstring for just_one_colorbar""" + try: + with open(fpath): pass + except IOError: + self.create_colorbar(fpath) + + def create_colorbar(self,fpath,fname,cf,label=''): + """ + Create colorbar. + + Inputs: + fpath : path to file + fname : filename + cf : contour filling for legend + label : colorbar label + + """ + fig = plt.figure() + CBax = fig.add_axes([0.15,0.05,0.7,0.02]) + CB = plt.colorbar(cf,cax=CBax,orientation='horizontal') + CB.set_label(label) + self.save(fig,fpath,fname) diff --git a/postWRF/postWRF/protoinit.py b/postWRF/postWRF/protoinit.py new file mode 100644 index 0000000..e7dcb3f --- /dev/null +++ b/postWRF/postWRF/protoinit.py @@ -0,0 +1,2 @@ +from macro import * + diff --git a/postWRF/postWRF/skewt.py b/postWRF/postWRF/skewt.py index 92c0752..d8c9a22 100644 --- a/postWRF/postWRF/skewt.py +++ b/postWRF/postWRF/skewt.py @@ -24,6 +24,138 @@ from WEM.utils import utils import WEM.utils.metconstants as mc +class Profile(Figure): + def __init__(self,config,wrfout=0): + self.C = config + self.path_to_WRF = self.C.wrfout_root + self.path_to_output = self.C.output_root + if wrfout: + self.W = wrfout + + def composite_profile(self,va,plot_time,plot_latlon,wrfouts,dom,mean,std,xlim,ylim): + """ + Loop over wrfout files. + Get profile of variable + Plot all members + + Optional standard deviation + Optional mean + """ + + # Set up figure + fig = plt.figure() + + # Plot settings + if xlim: + xmin, xmax, xint = xlim + if ylim: + P_bot, P_top, dp = [y*100.0 for y in ylim] + else: + P_bot = 100000.0 + P_top = 20000.0 + dp = 10000.0 + + plevs = N.arange(P_bot,P_top,dp) + + # Get wrfout prototype for information + W = WRFOut(wrfouts[0]) + + lat, lon = plot_latlon + datestr = utils.string_from_time('output',plot_time) + t_idx = W.get_time_idx(plot_time,tuple_format=1) + y, x, exact_lat, exact_lon = gridded_data.getXY(W.lats1D,W.lons1D,lat,lon) + slices = {'t': t_idx, 'la': y, 'lo': x} + #var_slices = {'t': t_idx, 'lv':0, 'la':y, 'lo':x} + + # Initialise array + # Number of levels and ensembles: + nPlevs = W.z_dim + data = W.get(va,slices) + nvarlevs = data.shape[1] + nens = len(wrfouts) + + # 2D: (profile,member) + profile_arr = N.zeros((nvarlevs,nens)) + composite_P = N.zeros((nPlevs,nens)) + + # Set up legend + labels = [] + colourlist = utils.generate_colours(M,nens) + M.rcParams['axes.color_cycle'] = colourlist + + # Collect profiles + for n,wrfout in enumerate(wrfouts): + W = WRFOut(wrfout) + + # Get pressure levels + composite_P[:,n] = W.get('pressure',slices)[0,:,0,0] + #elev = self.W.get('HGT',H_slices) + + #pdb.set_trace() + # Grab variable + profile_arr[:,n] = W.get(va,slices)[0,:,0,0] + + # Plot variable on graph + plt.plot(profile_arr[:,n],composite_P[:,n]) + + member = wrfout.split('/')[-2] + labels.append(member) + # pdb.set_trace() + + # Compute mean, std etc + if mean: + profile_mean = N.mean(profile_arr,axis=1) + profile_mean_P = N.mean(composite_P,axis=1) + plt.plot(profile_mean,profile_mean_P,color='black') + if std: + # Assume mean P across ensemble is correct level + # to plot standard deviation + profile_std = N.std(profile_arr,axis=1) + std_upper = profile_mean + profile_std + std_lower = profile_mean - profile_std + plt.plot(std_upper,profile_mean_P,'k--') + plt.plot(std_lower,profile_mean_P,'k--') + + fname = '_'.join(('profile_comp',va,datestr,'{0:03d}'.format(x),'{0:03d}'.format(y))) + '.png' + utils.trycreate(self.path_to_output) + fpath = os.path.join(self.path_to_output,fname) + + + # Set semi-log graph + plt.gca().set_yscale('log') + + # Plot limits, ticks + yticks = N.arange(P_bot,P_top,-100*100) + plt.yticks(yticks) + ytix = ["%4u" %(p/100.0) for p in yticks] + plt.yticks(yticks,ytix) + + #plt.axis([-20,50,105000.0,20000.0]) + #plt.xlabel(r'Temperature ($^{\circ}$C) at 1000 hPa') + #plt.xticks(xticks,['' if tick%10!=0 else str(tick) for tick in xticks]) + plt.ylabel('Pressure (hPa)') + #yticks = N.arange(self.P_bot,P_t-1,-10**4) + #plt.yticks(yticks,yticks/100) + + if xlim: + plt.xlim([xmin,xmax]) + xticks = N.arange(xmin,xmax+xint,xint) + plt.xticks(xticks) + + # Flip y axis + plt.ylim([P_bot,P_top]) + #plt.autoscale(enable=1,axis='x') + #ax = plt.gca() + #ax.relim() + #ax.autoscale_view() + #plt.draw() + plt.legend(labels,loc=2,fontsize=6) + + plt.savefig(fpath) + plt.close() + + + class SkewT(Figure): def __init__(self,config,wrfout=0): self.C = config diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 9608265..17dcd87 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -125,6 +125,7 @@ def get(self,var,slices,**kwargs): """ + # Check if computing required # When data is loaded from nc, it is destaggered isDefault = self.check_compute(var) @@ -185,6 +186,7 @@ def check_destagger(self,var): """ Looks up dimensions of netCDF file without loading data. Returns dimension number that requires destaggering + """ stag_dim = None for n,dname in enumerate(self.nc.variables[var].dimensions): @@ -206,7 +208,21 @@ def destagger(self,data,ax): Theta always has unstaggered points in all three spatial dimensions (axes=1,2,3). Data should be 4D but just the slice required to reduce unnecessary computation time. + + Don't destagger in x/y for columns + """ + # Check for dimensions of 1. + # If it exists, don't destagger it. + + shp = data.shape + for n,size in enumerate(shp): + if (size==1) and (n==ax): + ax = None + break + + #pdb.set_trace() + if ax==None: return data else: @@ -250,9 +266,17 @@ def compute(self,var,slices,**kwargs): tbl['buoyancy'] = self.compute_buoyancy tbl['strongestwind'] = self.compute_strongest_wind tbl['PMSL'] = self.compute_pmsl + tbl['RH'] = self.compute_RH data = tbl[var](slices,**kwargs) return data + def compute_RH(self,slices,**kwargs): + T = self.get('temps',slices,units='C') + Td = self.get('Td',slices) + RH = N.exp(0.073*(Td-T)) + # pdb.set_trace() + return RH*100.0 + def compute_pmsl(self,slices,**kwargs): P = self.get('PSFC',slices) T2 = self.get('T2',slices) @@ -331,7 +355,7 @@ def compute_pressure(self,slices): def compute_temps(self,slices,units='K'): theta = self.get('theta',slices) P = self.get('pressure',slices) - temps = theta*((P/1000.0)**(287.04/1004.0)) + temps = theta*((P/100000.0)**(287.04/1004.0)) if units=='K': return temps elif units=='C': @@ -487,13 +511,14 @@ def compute_Td(self,slices): """ Using HootPy equation """ - Q = self.get('Q',slices) + Q = self.get('QVAPOR',slices) P = self.get('pressure',slices) w = N.divide(Q, N.subtract(1,Q)) - e = N.divide(N.multiply(w,P), N.add(0.622,w)) + e = N.divide(N.multiply(w,P), N.add(0.622,w))/100.0 a = N.multiply(243.5,N.log(N.divide(e,6.112))) b = N.subtract(17.67,N.log(N.divide(e,6.112))) Td = N.divide(a,b) + # pdb.set_trace() return Td def compute_CAPE(self,slices,**kwargs): diff --git a/postWRF/postWRF/xsection.py b/postWRF/postWRF/xsection.py index 5ec0f72..c997430 100644 --- a/postWRF/postWRF/xsection.py +++ b/postWRF/postWRF/xsection.py @@ -6,17 +6,178 @@ The output can be saved to a pickle file. This can be useful for creating composites +Input lat/lon can be either specified or from x,y co-ords +calculated from manually clicking on a map with overlaid +data (reflectivity, etc). """ # Imports import numpy as N +from figure import Figure -class CrossSection: +class CrossSection(Figure): - def __init__(self): - pass + def __init__(self,latA=0,lonA=0,latB=0,lonB=0): + if latA and lonA and latB and lonB: + print("Using user-defined lat/lon transects.") + else: + print("Please click start and end points.") + latA, lonA, latB, lonB = self.popup_transect() - def get_xy(self): + self.latA = latA + self.lonA = lonA + self.latB = latB + self.lonB = lonB + + self.plot_xs() + + def popup_transect(self): + """ + Pops up window for user to select start and + end point for the cross-section. + + Optional: this map can be overlaid with data + to better guide the decision. + + Optional: the transect can be saved as an image + with/without the overlaid data, useful for publication. + """ + + def get_xy(self,lat,lon): + """ + Return x and y coordinates for given lat/lon. + """ + + return x,y + + def get_height(self,t,x,y,z,pts): + """ + Return terrain heights along cross-section + TODO: What's the diff between the outputs? + + Inputs: + t : time index, as int + x : x indices, as int + y : y imdices, as int + z : z indices, as int + pts : number of points along the x-sec + + Outputs: + terrain_z : terrain height + heighthalf : who knows? + """ + # TODO: change API of W.get() + geopot = self.W.get('geopot',[t,z,y,x]) + dryairmass = self.W.get('dryairmass',[t,y,x]) + znw = self.W.get('ZNW',[t,z]) + znu = self.W.get('ZNU',[t,z]) + + heighthalf = N.zeros((len(z),pts)) + + for i in range(pts): + pfull = dryairmass[i] * znw+self.P_top + phalf = dryairmass[i] * znu+self.P_top + for k in range(len(z)): + heighthalf[k,i] = interp(geopot[:,i],pfull[:],phalf[k])/mc.g + + terrain_z = geopot[0,:]/mc.g + # TODO: import metconstants as mc + return heighthalf, terrain_z + + def plot_xs(self): + """ + Inputs: + v : variable to plot, from this list: + parawind,perpwind,wind,U,V,W,T,RH, + + + """ + + xA, yA = get_xy(self.latA,self.lonA) + xB, yB = get_xy(self.latB,self.lonB) + hyp_pts = int(N.hypot(xB-xA,yB-yA)) + xx = N.linspace(xA,xB,hyp_pts) + yy = N.linspace(yA,yB,hyp_pts) + xint = xx.astype(int) + yint = yy.astype(int) + angle = N.arctan((yy[-1]-yy[0])/(xx[-1]-xx[0])) # In radians + + # Get terrain heights + terrain_z, heighthalf = get_height(xx,yy,Nz,hyp_pts) + + # Set up plot + # Length of x-section in km + xs_len = (1/1000.0) * N.sqrt((-1.0*hyp_pts*self.W.dy*N.cos(angle))**2 + + (hyp_pts*self.W.dy*N.sin(angle))**2) - + # Generate ticks along cross-section + xticks = N.arange(0,xs_len,xs_len/hyp_pts) + xlabels = [r"%3.0f" %t for t in xticks] + grid = N.swapaxes(N.repeat(N.array(xticks).reshape(hyp_pts,1),self.W.nz,axis=1),0,1) + + # Plotting + if nc.dx != nc.dy: + print("Square domains only here") + else: + # TODO: define zmin, zmax, dz + fig.axis([0,(hyp_pts*self.W.dx/1000.0)-1,zmin,zmax+dz]) + + # Logic time + # First, check to see if v is in the list of computable or default + # variables within WRFOut (self.W) + # If not, maybe it can be computed here. + # If not, raise Exception. + + # TODO: vailable_vars in WRFOut to check this + if v is in self.W.available_vars: + data = self.W.get(v,[tidx,:,yint,xint]) + + elif v is 'parawind': + u = self.W.get('U',[tidx,:,yint,xint] + v = self.W.get('V',[tidx,:,yint,xint] + data = N.cos(angle)*u - N.sin(angle)*v + clvs = u_wind_levels + extends = 'both' + CBlabel = r'Wind Speed (ms$^{-1}$)' + + elif v is 'perpwind': + u = self.W.get('U',[tidx,:,yint,xint] + v = self.W.get('V',[tidx,:,yint,xint] + # Note the negative here. I think it's alright? TODO + data = -N.cos(angle)*v + N.sin(angle)*u + clvs = u_wind_levels + extends = 'both' + CBlabel = r'Wind Speed (ms$^{-1}$)' + + else: + print("Unsupported variable",v) + raise Exception + + fig.plot(xticks,terrain_z,color='k') + fig.fill_between(xticks,terrain_z,0,facecolor='lightgrey') + + # What is this? TODO + labeldelta = 15 + + plt.yticks(N.arange(zmin,zmax+dz,dz) + plt.xlabel("Distance along cross-section (km)") + plt.ylabel("Height above sea level (m)") + + fpath = self.get_fpath() + fname = self.get_fname() + self.save(fig,fpath,fname) + plt.close() + + # Save a colorbar + # Only if one doesn't exist + self.just_one_colorbar() + + def just_one_colorbar(self,fpath): + """docstring for just_one_colorbar""" + try: + with open(fpath): pass + except IOError: + self.create_colorbar(fpath) + + From d2d4528b2c1c3611ae78a8d9833b0bf5b4fe1eb5 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Sat, 12 Jul 2014 01:37:06 -0500 Subject: [PATCH 087/111] Major restructuring: broken --- __init__.py | 2 +- postWRF/__init__.py | 1 + postWRF/bin/bowecho_plot.py | 6 +- postWRF/postWRF/__init__.py | 876 +------------------------------- postWRF/postWRF/birdseye.py | 2 +- postWRF/postWRF/figure.py | 2 +- postWRF/postWRF/main.py | 701 +++++++++++++++++++++++++ postWRF/postWRF/map.py | 48 ++ postWRF/postWRF/metconstants.py | 10 + postWRF/postWRF/skewt.py | 2 +- postWRF/postWRF/wrfout.py | 56 +- utils/__init__.py | 9 + utils/general_met.py | 44 ++ utils/more_utils.py | 213 ++++++++ utils/unix_tools.py | 24 +- utils/wrf_tools.py | 78 ++- 16 files changed, 1164 insertions(+), 910 deletions(-) create mode 100644 postWRF/postWRF/main.py create mode 100644 postWRF/postWRF/metconstants.py create mode 100644 utils/general_met.py create mode 100644 utils/more_utils.py diff --git a/__init__.py b/__init__.py index 9920230..16ca54a 100644 --- a/__init__.py +++ b/__init__.py @@ -1 +1 @@ -# Intentially blank +import utils.utils as utils diff --git a/postWRF/__init__.py b/postWRF/__init__.py index e69de29..338de4b 100644 --- a/postWRF/__init__.py +++ b/postWRF/__init__.py @@ -0,0 +1 @@ +from postWRF.main import WRFEnviron diff --git a/postWRF/bin/bowecho_plot.py b/postWRF/bin/bowecho_plot.py index ebf9987..ab8058f 100644 --- a/postWRF/bin/bowecho_plot.py +++ b/postWRF/bin/bowecho_plot.py @@ -3,10 +3,10 @@ import sys sys.path.append('/home/jrlawson/gitprojects/') -from WEM.postWRF.postWRF import WRFEnviron +from WEM.postWRF import WRFEnviron from settings import Settings -import WEM.utils.utils as utils -from WEM.postWRF.postWRF.rucplot import RUCPlot +import WEM.utils as utils +#from WEM.postWRF.postWRF.rucplot import RUCPlot config = Settings() p = WRFEnviron(config) diff --git a/postWRF/postWRF/__init__.py b/postWRF/postWRF/__init__.py index f22f5f5..f18ad60 100644 --- a/postWRF/postWRF/__init__.py +++ b/postWRF/postWRF/__init__.py @@ -1,874 +1,8 @@ -"""Take config settings and run plotting scripts. - -Classes: -C = Config = configuration settings set by user, passed from user script -D = Defaults = used when user does not specify a non-essential item -W = Wrfout = wrfout file -F = Figure = a superclass of figures - mp = Birdseye = a lat--lon slice through data with basemap - xs = CrossSection = distance--height slice through data with terrain - +""" +In main you will find the heart at the controls +of the public API, which hides more complicated +stuff from the user. """ -import os -import matplotlib as M -M.use('Agg') -import matplotlib.pyplot as plt -from mpl_toolkits.basemap import Basemap -import collections -import fnmatch -import calendar -import pdb -import itertools -import numpy as N -import time -import json -import cPickle as pickle -import copy -import glob - -from wrfout import WRFOut -from axes import Axes -from figure import Figure -from birdseye import BirdsEye -from skewt import SkewT -from skewt import Profile -#import scales -from defaults import Defaults -from lookuptable import LookUpTable -import WEM.utils.utils as utils - -# TODO: Make this awesome - -class WRFEnviron: - def __init__(self,config): - self.C = config - - # Set defaults if they don't appear in user's settings - self.D = Defaults() - - self.font_prop = getattr(self.C,'font_prop',self.D.font_prop) - self.usetex = getattr(self.C,'usetex',self.D.usetex) - self.dpi = getattr(self.C,'DPI',self.D.dpi) - self.plot_titles = getattr(self.C,'plot_titles',self.D.plot_titles) - - # Set some general settings - M.rc('text',usetex=self.usetex) - M.rc('font',**self.font_prop) - M.rcParams['savefig.dpi'] = self.dpi - - def wrfout_files_in(self,folders,dom='notset',init_time='notset',descend=1,**kwargs): - - folders = self.get_sequence(folders) - avoids = [] - if 'avoid' in kwargs: - # Avoid folder names with this string - # or list of strings - avoid = self.get_sequence(kwargs['avoid']) - for a in avoid: - avoids.append('/{0}/'.format(a)) - - - w = 'wrfout' # Assume the prefix - if init_time=='notset': - suffix = '*0' - # We assume the user has wrfout files in different folders for different times - else: - try: - it = self.string_from_time('wrfout',init_time) - except: - print("Not a valid wrfout initialisation time; try again.") - raise Error - suffix = '*' + it - - if dom=='notset': - # Presume all domains are desired. - prefix = w + '_d' - elif (dom == 0) | (dom > 8): - print("Domain is out of range. Choose number between 1 and 8 inclusive.") - raise IndexError - else: - dom = 'd{0:02d}'.format(dom) - prefix = w + '_' + dom - - wrfouts = [] - if descend: - for folder in folders: - for root,dirs,files in os.walk(folder): - for fname in fnmatch.filter(files,prefix+suffix): - skip_me = 0 - fpath = os.path.join(root,fname) - if avoids: - for a in avoids: - if a in fpath: - skip_me = 1 - else: - pass - if not skip_me: - wrfouts.append(fpath) - - else: - for folder in folders: - findfile = os.path.join(folder,prefix+suffix) - files = glob.glob(findfile) - # pdb.set_trace() - for f in files: - wrfouts.append(os.path.join(folder,f)) - return wrfouts - - def dir_from_naming(self,*args): - l = [str(a) for a in args] - path = os.path.join(self.C.output_root,*l) - return path - - def string_from_time(self,usage,t,dom=0,strlen=0,conven=0): - str = utils.string_from_time(usage=usage,t=t,dom=dom,strlen=strlen,conven=conven) - return str - - def plot_2D(self,dic): - """ - Currently main plotting script: Feb 25 2014 - - Path to wrfout file is in config file. - Path to plot output is also in config - - This script is top-most and decides if the variables is - built into WRF default output or needs computing. It unstaggers - and slices data from the wrfout file appropriately. - - - Inputs: - dic : nested dictionary with: - - KEY - === - va : variable to plot - - nested KEY/VALUE PAIRS - ====================== - (MANDATORY FOR SOME VARIABLES) - lv : level to plot - pt : plot times - (OPTIONAL) - tla : top limit of latitude - bla : bottom limit of latitude - llo : left limit of longitude - rlo : right limit of longitude - ---> if these are missing, default to 'all points' - plottype : contourf by default. - - """ - - #self.en = self.get_sequence(wrfout) - #self.pt = self.get_sequence(times) # List of plot times - Dic = copy.deepcopy(dic) - wrfpath = self.wrfout_files_in(self.C.wrfout_root)[0] - self.W = WRFOut(wrfpath) # Only load netCDF file once! - for va in Dic: - - if not 'lv' in Dic[va]: # For things like CAPE, shear. - Dic[va]['lv'] = 'all' - - lv = Dic[va]['lv'] - vc = utils.level_type(lv) # vertical coordinate - - if not 'pt' in Dic[va]: # For averages and all times - if not 'itime' in Dic[va]: # For all times - Dic[va]['pt'] = ['all',] - else: # For specific range - Dic[va]['pt'] = ['range',] - - # Check for pressure levels - if vc == 'isobaric': - nc_path = self.W.path - p_interp_fpath = self.W.interp_to_p(self.C,nc_path,va,lv) - # Edit p_interp namelist - #Execute p_interp here and reassign self.W to new file - self.W = WRFOut(p_interp_fpath) - else: # - # print("Non-pressure levels not supported yet.") - # raise Exception - pass - - F = BirdsEye(self.C,self.W) - - for t in Dic[va]['pt']: - #pdb.set_trace() - disp_t = utils.string_from_time('title',t,**Dic[va]) - print("Plotting {0} at lv {1} for time {2}.".format(va,lv,disp_t)) - Dic[va]['pt'] = t # Need this? - Dic[va]['vc'] = vc # Need this? - F.plot2D(va, Dic[va]) - - """ - def plot_variable2D(self,varlist,timelist): - self.va = self.get_sequence(varlist) # List of variables - self.pt = self.get_sequence(timelist) # List of plot times - - # Where is logic to - - for x in itertools.product(self.va,self.pt): - va, pt = x - W = WRFOut(self.C.wrfout_root) - F = BirdsEye(self.C,W) - F.plot2D(va,pt,lv=2000) - - - def plot_variable2D(self,va,pt,en,lv,p2p,na=0,da=0): - ###Plot a longitude--latitude cross-section (bird's-eye-view). - Use Basemap to create geographical data - - ======== - REQUIRED - ======== - - va = variable(s) - pt = plot time(s) - nc = ensemble member(s) - lv = level(s) - p2p = path to plots - - ======== - OPTIONAL - ======== - - da = smaller domain area(s), needs dictionary || DEFAULT = 0 - na = naming scheme for plot files || DEFAULT = get what you're given - - ### - va = self.get_sequence(va) - pt = self.get_sequence(pt,SoS=1) - en = self.get_sequence(en) - lv = self.get_sequence(lv) - da = self.get_sequence(da) - - #perms = self.make_iterator(va,pt,en,lv,da) - #perm2 = self.make_iterator2(va,pt,en,lv,da) - #pdb.set_trace() - - # Find some way of looping over wrfout files first, avoiding need - # to create new W instances - # print("Beginning plotting of {0} figures.".format(len(list(perms)))) - pdb.set_trace() - - #for x in perms: - for x in itertools.product(va,pt,en,lv,da): - va,pt,en,lv,da = x - pdb.set_trace() - W = WRFOut(en) # wrfout file class using path - F = BirdsEye(self.C,W,p2p) # 2D figure class - F.plot2D(va,pt,en,lv,da,na) # Plot/save figure - pt_s = utils.string_from_time('title',pt) - print("Plotting from file {0}: \n variable = {1}" - " time = {2}, level = {3}, area = {4}.".format(en,va,pt_s,lv,da)) - """ - - def make_iterator2(self,*args): - for arg in args: - for a in arg: - yield a - - def make_iterator(self,va,pt,en,lv,da): - for v in va: - for p in pt: - for e in en: - for l in lv: - for d in da: - yield v,p,e,l,d - - def get_sequence(self,x,SoS=0): - """ Returns a sequence (tuple or list) for iteration. - - SoS = 1 enables the check for a sequence of sequences (list of dates) - """ - - - if SoS: - y = x[0] - else: - y = x - - if isinstance(y, collections.Sequence) and not isinstance(y, basestring): - return x - else: - return [x] - - - def plot_cross_section(self,var,latA,lonA,latB,lonB): - xs = CrossSection() - xs.plot(var,latA,lonA,latB,lonB) - - def save_data(self,data,folder,fname,format='pickle'): - # Strip file extension given - fname_base = os.path.splitext(fname)[0] - # Check for folder, create if necessary - utils.trycreate(folder) - # Create absolute path - fpath = os.path.join(folder,fname_base) - - if format=='pickle': - with open(fpath+'.pickle','wb') as f: - pickle.dump(data,f) - elif format=='numpy': - N.save(fpath,data) - elif format=='json': - j = json.dumps(data) - with open(fpath+'.json','w') as f: - print >> f,j - else: - print("Give suitable saving format.") - raise Exception - - print("Saved file {0} to {1}.".format(fname,folder)) - - def load_data(self,folder,fname,format='pickle'): - fname2 = os.path.splitext(fname)[0] - fpath = os.path.join(folder,fname2) - if format=='pickle': - with open(fpath+'.pickle','rb') as f: - data = pickle.load(f) - elif format=='numpy': - data = N.load(fpath+'.npy') - elif format=='json': - print("JSON stuff not coded yet.") - raise Exception - else: - print("Give suitable loading format.") - raise Exception - - print("Loaded file {0} from {1}.".format(fname,folder)) - return data - - def compute_diff_energy( - self,ptype,energy,files,times,upper=None,lower=None, - d_save=1,d_return=1,d_fname='diff_energy_data'): - """ - This method computes difference kinetic energy (DKE) - or different total energy (DTE, including temp) - between WRFout files for a given depth of the - atmosphere, at given time intervals - - Inputs: - - ptype : 'sum_z' or 'sum_xyz' - energy : 'kinetic' or 'total' - upper : upper limit of vertical integration - lower : lower limit of vertical integration - files : abs paths to all wrfout files - times : times for computations - tuple format - d_save : save dictionary to folder (path to folder) - d_return: return dictionary (True or False) - d_fname : custom filename - - Outputs: - - data : time series or list of 2D arrays - - ptype 'sum_z' integrates vertically between lower and - upper hPa and creates a time series. - - ptype 'sum_xyz' integrates over the 3D space (again between - the upper and lower bounds) and creates 2D arrays. - """ - if d_save and not isinstance(d_save,basestring): - d_save = os.environ['HOME'] - - # First, save or output? Can't be neither! - if not d_save and not d_return: - print("Pick save or output, otherwise it's a waste of computer" - "power") - raise Exception - - print("Saving pickle file to {0}".format(d_save)) - # Look up the method to use depending on type of plot - PLOTS = {'sum_z':self.DE_z, 'sum_xyz':self.DE_xyz} - - print('Get sequence of time') - # Creates sequence of times - ts = self.get_sequence(times) - - # Dictionary of data - DATA = {} - - print('Get permutations') - # Get all permutations of files - nperm = len(list(itertools.combinations(files,2))) - print('Start loop') - # pdb.set_trace() - for n, perm in enumerate(itertools.combinations(files,2)): - print("No. {0} from {1} permutations".format(n,nperm)) - perm_start = time.time() - DATA[str(n)] = {} - f1, f2 = perm - W1 = WRFOut(f1) - W2 = WRFOut(f2) - print('WRFOuts loaded.') - #pdb.set_trace() - # Make sure times are the same in both files - if not N.all(N.array(W1.wrf_times) == N.array(W2.wrf_times)): - print("Times are not identical between input files.") - raise Exception - else: - print("Passed check for identical timestamps between " - "NetCDF files") - - # Find indices of each time - print('Finding time indices') - t_idx = [] - for t in ts: - t_idx.append(W1.get_time_idx(t)) - - print("Calculating values now...") - DATA[str(n)]['times'] = ts - DATA[str(n)]['values'] = [] - for t in t_idx: - DATA[str(n)]['values'].append(PLOTS[ptype](W1.nc,W2.nc,t, - energy,lower,upper)) - DATA[str(n)]['file1'] = f1 - DATA[str(n)]['file2'] = f2 - - print "Calculation #{0} took {1:2.2f} seconds.".format(n,time.time()-perm_start) - - if d_return and not d_save: - return DATA - elif d_save and not d_return: - #self.save_data(DATA,d_save,d_fname) - self.save_data(DATA,d_save,d_fname) - #self.json_data(DATA,d_save,d_fname) - return - elif d_return and d_save: - #self.save_data(DATA,d_save,d_fname) - self.save_data(DATA,d_save,d_fname) - #self.json_data(DATA,d_save,d_fname) - return DATA - - def DE_xyz(self,nc0,nc1,t_idx,energy,*args): - """ - Computation for difference kinetic energy (DKE). - Sums DKE over the 3D space, returns a time series. - - Destaggering is not enabled as it introduces - computational cost that is of miniscule value considering - the magnitudes of output values. - - Inputs: - - nc0 : netCDF file - nc1 : netCDF file - t_idx : times indices to difference - energy : kinetic or total - *args : to catch lower/upper boundary which isn't relevant here - - Outputs: - - data : time series. - """ - # Wind data - U0 = nc0.variables['U'] - V0 = nc0.variables['V'] - U1 = nc1.variables['U'] - V1 = nc1.variables['V'] - - if energy=='total': - T0 = nc0.variables['T'] - T1 = nc1.variables['T'] - R = 287.0 # Universal gas constant (J / deg K * kg) - Cp = 1004.0 # Specific heat of dry air at constant pressure (J / deg K * kg) - kappa = (R/Cp) - - xlen = U0.shape[2] - - DKE = [] - for n,t in enumerate(t_idx): - print("Finding DKE at time {0} of {1}.".format(n,len(t))) - DKE_hr = 0 # Sum up all DKE for the 3D space - for i in range(xlen): - if energy=='kinetic': - DKE_hr += N.sum(0.5*((U0[t,:,:,i]-U1[t,:,:,i])**2 + - (V0[t,:,:-1,i]-V1[t,:,:-1,i])**2)) - elif energy=='total': - DKE_hr += N.sum(0.5*((U0[t,:,:,i]-U1[t,:,:,i])**2 + - (V0[t,:,:-1,i]-V1[t,:,:-1,i])**2 + - kappa*(T0[t,:,:,i]-T1[t,:,:,i])**2)) - print("DTE at this time: {0}".format(DKE_hr)) - DKE.append(DKE_hr) - return DKE - - def DE_z(self,nc0,nc1,t,energy,lower,upper): - """ - Computation for difference kinetic energy (DKE). - Sums DKE over all levels between lower and upper, - for each grid point, and returns a 2D array. - - Destaggering is not enabled as it introduces - computational cost that is of miniscule value considering - the magnitudes of output values. - - Method finds levels nearest lower/upper hPa and sums between - them inclusively. - - Inputs: - - nc0 : netCDF file - nc1 : netCDF file - t : times index to difference - energy : kinetic or total - lower : lowest level, hPa - upper : highest level, hPa - - Outputs: - - data : 2D array. - """ - - # Speed up script by only referencing data, not - # loading it to a variable yet - - # WIND - U0 = nc0.variables['U'][t,...] - U1 = nc1.variables['U'][t,...] - Ud = U0 - U1 - #del U0, U1 - - V0 = nc0.variables['V'][t,...] - V1 = nc1.variables['V'][t,...] - Vd = V0 - V1 - #del V0, V1 - - # PERT and BASE PRESSURE - if lower or upper: - P0 = nc0.variables['P'][t,...] - PB0 = nc0.variables['PB'][t,...] - Pr = P0 + PB0 - #del P0, PB1 - # Here we assume pressure columns are - # roughly the same between the two... - - if energy=='total': - T0 = nc0.variables['T'][t,...] - T1 = nc1.variables['T'][t,...] - Td = T0 - T1 - #del T0, T1 - - R = 287.0 # Universal gas constant (J / deg K * kg) - Cp = 1004.0 # Specific heat of dry air at constant pressure (J / deg K * kg) - kappa = R/Cp - - xlen = Ud.shape[1] # 1 less than in V - ylen = Vd.shape[2] # 1 less than in U - zlen = Ud.shape[0] # identical in U & V - - # Generator for lat/lon points - def latlon(nlats,nlons): - for i in range(nlats): # y-axis - for j in range(nlons): # x-axis - yield i,j - - DKE = [] - DKE2D = N.zeros((xlen,ylen)) - print_time = ''.join((nc0.variables['Times'][t])) - print("Calculating 2D grid for time {0}...".format(print_time)) - gridpts = latlon(xlen,ylen) - for gridpt in gridpts: - i,j = gridpt - # Find closest level to 'lower', 'upper' - if lower or upper: - P_col = Pr[:,j,i] - if lower: - low_idx = utils.closest(P_col,lower*100.0) - else: - low_idx = None - if upper: - upp_idx = utils.closest(P_col,upper*100.0)+1 - else: - upp_idx = None - - zidx = slice(low_idx,upp_idx) - - if energy=='kinetic': - DKE2D[j,i] = N.sum(0.5*((Ud[zidx,j,i])**2 + - (Vd[zidx,j,i])**2)) - elif energy=='total': - DKE2D[j,i] = N.sum(0.5*((Ud[zidx,j,i])**2 + - (Vd[zidx,j,i])**2 + - kappa*(Td[zidx,j,i])**2)) - - DKE.append(DKE2D) - - return DKE - - def plot_diff_energy(self,ptype,energy,time,folder,fname,V): - """ - - folder : directory holding computed data - fname : naming scheme of required files - V : constant values to contour at - """ - sw = 0 - - DATA = self.load_data(folder,fname,format='pickle') - times = self.get_sequence(time) - - for n,t in enumerate(times): - for pn,perm in enumerate(DATA): - f1 = DATA[perm]['file1'] - f2 = DATA[perm]['file2'] - if sw==0: - # Get times and info about nc files - # First time to save power - W1 = WRFOut(f1) - permtimes = DATA[perm]['times'] - sw = 1 - - # Find array for required time - x = N.where(N.array(permtimes)==t)[0][0] - data = DATA[perm]['values'][x][0] - if not pn: - stack = data - else: - stack = N.dstack((data,stack)) - stack_average = N.average(stack,axis=2) - - #birdseye plot with basemap of DKE/DTE - F = BirdsEye(self.C,W1) # 2D figure class - #F.plot2D(va,t,en,lv,da,na) # Plot/save figure - fname_t = ''.join((fname,'_p{0:02d}'.format(n))) - F.plot_data(stack_average,'contourf',fname_t,t,V) - print("Plotting time {0} from {1}.".format(n,len(times))) - del data, stack - - def plot_error_growth(self,ofname,folder,pfname,sensitivity=0,ylimits=0,**kwargs): - """Plots line graphs of DKE/DTE error growth - varying by a sensitivity - e.g. error growth involving - all members that use a certain parameterisation. - - ofname : output filename prefix - pfname : pickle filename - plotlist : list of folder names to loop over - ylim : tuple of min/max for y axis range - """ - DATA = self.load_data(folder,pfname,format='pickle') - - for perm in DATA: - times = DATA[perm]['times'] - break - - times_tup = [time.gmtime(t) for t in times] - time_str = ["{2:02d}/{3:02d}".format(*t) for t in times_tup] - - if sensitivity: - # Plot multiple line charts for each sensitivity - # Then a final chart with all the averages - # If data is 2D, sum over x/y to get one number - - # Dictionary with average - AVE = {} - - for sens in sensitivity: - ave_stack = 0 - n_sens = len(sensitivity)-1 - colourlist = utils.generate_colours(M,n_sens) - M.rcParams['axes.color_cycle'] = colourlist - fig = plt.figure() - labels = [] - #SENS['sens'] = {} - for perm in DATA: - f1 = DATA[perm]['file1'] - f2 = DATA[perm]['file2'] - - if sens in f1: - f = f2 - elif sens in f2: - f = f1 - else: - f = 0 - - if f: - subdirs = f.split('/') - labels.append(subdirs[-2]) - data = self.make_1D(DATA[perm]['values']) - - plt.plot(times,data) - # pdb.set_trace() - ave_stack = utils.vstack_loop(N.asarray(data),ave_stack) - else: - pass - - # pdb.set_trace() - n_sens += 1 - colourlist = utils.generate_colours(M,n_sens) - M.rcParams['axes.color_cycle'] = colourlist - AVE[sens] = N.average(ave_stack,axis=0) - labels.append('Average') - plt.plot(times,AVE[sens],'k') - - plt.legend(labels,loc=2,fontsize=9) - if ylimits: - plt.ylim(ylimits) - plt.gca().set_xticks(times[::2]) - plt.gca().set_xticklabels(time_str[::2]) - outdir = self.C.output_root - fname = '{0}_Growth_{1}.png'.format(ofname,sens) - fpath = os.path.join(outdir,fname) - fig.savefig(fpath) - - plt.close() - print("Saved {0}.".format(fpath)) - - # Averages for each sensitivity - labels = [] - fig = plt.figure() - ave_of_ave_stack = 0 - for sens in AVE.keys(): - plt.plot(times,AVE[sens]) - labels.append(sens) - ave_of_ave_stack = utils.vstack_loop(AVE[sens],ave_of_ave_stack) - - labels.append('Average') - ave_of_ave = N.average(ave_of_ave_stack,axis=0) - plt.plot(times,ave_of_ave,'k') - - plt.legend(labels,loc=2,fontsize=9) - - if ylimits: - plt.ylim(ylimits) - plt.gca().set_xticks(times[::2]) - plt.gca().set_xticklabels(time_str[::2]) - outdir = self.C.output_root - fname = '{0}_Growth_Averages.png'.format(ofname) - fpath = os.path.join(outdir,fname) - fig.savefig(fpath) - - plt.close() - print("Saved {0}.".format(fpath)) - #pdb.set_trace() - - - - else: - fig = plt.figure() - ave_stack = 0 - for perm in DATA: - data = self.make_1D(DATA[perm]['values']) - plt.plot(times,data,'blue') - ave_stack = utils.vstack_loop(N.asarray(data),ave_stack) - - total_ave = N.average(ave_stack,axis=0) - plt.plot(times,total_ave,'black') - - if ylimits: - plt.ylim(ylimits) - plt.gca().set_xticks(times[::2]) - plt.gca().set_xticklabels(time_str[::2]) - outdir = self.C.output_root - fname = '{0}_Growth_allmembers.png'.format(ofname) - fpath = os.path.join(outdir,fname) - fig.savefig(fpath) - - plt.close() - print("Saved {0}.".format(fpath)) - - def composite_profile(self,va,skewT_time,skewT_latlon,enspaths,dom=1,mean=0,std=0,xlim=0,ylim=0): - P = Profile(self.C) - P.composite_profile(va,skewT_time,skewT_latlon,enspaths,dom,mean,std,xlim,ylim) - - def plot_skewT(self,plot_time,plot_latlon,dom=1,save_output=0,composite=0): - wrfouts = self.wrfout_files_in(self.C.wrfout_root) - for wrfout in wrfouts: - if not composite: - W = WRFOut(wrfout) - ST = SkewT(self.C,W) - ST.plot_skewT(plot_time,plot_latlon,dom,save_output) - nice_time = utils.string_from_time('title',plot_time) - print("Plotted Skew-T for time {0} at {1}".format( - nice_time,plot_latlon)) - else: - #ST = SkewT(self.C) - pass - - def plot_streamlines(self,lv,times): - wrfpath = self.wrfout_files_in(self.C.wrfout_root)[0] - self.W = WRFOut(wrfpath) - self.F = BirdsEye(self.C,self.W) - for pt in times: - disp_t = utils.string_from_time('title',pt) - print("Plotting {0} at lv {1} for time {2}.".format( - 'streamlines',lv,disp_t)) - self.F.plot_streamlines(lv,pt) - - - - def make_1D(self,data,output='list'): - """ Make sure data is a time series - of 1D values, and numpy array. - - List of arrays -> Numpy array or list - """ - if isinstance(data,list): - - data_list = [] - for time in data: - data_list.append(N.sum(time[0])) - - if output == 'array': - data_out = N.array(data_list) - else: - data_out = data_list - - - #elif isinstance(data,N.array): - # shape = data.shape - # if len(shape) == 1: - # data_out = data - # elif len(shape) == 2: - # data_out = N.sum(data) - return data_out - - def plot_domains(self,wrfouts,labels,latlons,colour=0): - """ - wrfouts : list of wrfout file paths - latlons : dictionary of Nlim,Elim,Slim,Wlim - for plot - """ - - fig = plt.figure() - - # Create basemap first of all - basemap_res = getattr(self.C,'basemap_res',self.D.basemap_res) - - m = Basemap( - projection='merc', - llcrnrlon=latlons['Wlim'],llcrnrlat=latlons['Slim'], - urcrnrlon=latlons['Elim'],urcrnrlat=latlons['Nlim'], - lat_0=latlons['lat0'],lon_0=latlons['lon0'], - resolution=basemap_res,area_thresh=500 - ) - - m.drawcoastlines() - m.drawstates() - m.drawcountries() - - if not isinstance(colour,collections.Sequence): - colours = ['k',] * len(wrfouts) - else: - colours = colour - # Get corners of each domain - for gridlabel,fpath,colour in zip(labels,wrfouts,colours): - W = WRFOut(fpath) - print("Plotting domain {0} for {1}".format(gridlabel,fpath)) - #Nlim, Elim, Slim, Wlim = W.get_limits() - x,y = m(W.lons,W.lats) - xl = len(x[0,:]) - midpt = len(y[0,:])/2 - plt.annotate(gridlabel,color=colour,fontsize=10,xy=(x[0,-(0.12*xl)],y[0,midpt]), - bbox=dict(fc='white'),alpha=1,va='center',ha='left') - m.plot(x[0,:],y[0,:],colour,lw=2) - plt.plot(x[:,0],y[:,0],colour,lw=2) - plt.plot(x[len(y)-1,:],y[len(y)-1,:],colour,lw=2) - plt.plot(x[:,len(x)-1],y[:,len(x)-1],colour,lw=2) - - fpath = os.path.join(self.C.output_root,'domains.png') - fig.savefig(fpath) - - +from main import WRFEnviron diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 59e20e4..de745cd 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -7,7 +7,7 @@ from defaults import Defaults from figure import Figure -import WEM.utils.utils as utils +import WEM.utils as utils import scales class BirdsEye(Figure): diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index 8291046..ca5b2e8 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -12,7 +12,7 @@ import os # Custom imports -import WEM.utils.utils as utils +import WEM.utils as utils class Figure: def __init__(self,config,wrfout): diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py new file mode 100644 index 0000000..67a2df6 --- /dev/null +++ b/postWRF/postWRF/main.py @@ -0,0 +1,701 @@ +"""Take config settings and run plotting scripts. + +Classes: +C = Config = configuration settings set by user, passed from user script +D = Defaults = used when user does not specify a non-essential item +W = Wrfout = wrfout file +F = Figure = a superclass of figures + mp = Birdseye = a lat--lon slice through data with basemap + xs = CrossSection = distance--height slice through data with terrain + +This script is API and should not be doing any hard work of +importing matplotlib etc! + +Useful/utility scripts have been moved to WEM.utils. +""" + +import os +#import matplotlib as M +#M.use('Agg') +#import matplotlib.pyplot as plt +#from mpl_toolkits.basemap import Basemap +import collections +import fnmatch +import calendar +import pdb +import itertools +import numpy as N +import time +import json +import cPickle as pickle +import copy +import glob + +from wrfout import WRFOut +from axes import Axes +from figure import Figure +from birdseye import BirdsEye +from skewt import SkewT +from skewt import Profile +#import scales +from defaults import Defaults +from lookuptable import LookUpTable +import WEM.utils as utils + +# TODO: Make this awesome + +class WRFEnviron(object): + def __init__(self,config): + # User's settings + self.C = config + + # Set defaults if they don't appear in user's settings + self.D = Defaults() + + # This stuff should be elsewhere. + + #self.font_prop = getattr(self.C,'font_prop',self.D.font_prop) + #self.usetex = getattr(self.C,'usetex',self.D.usetex) + #self.dpi = getattr(self.C,'DPI',self.D.dpi) + #self.plot_titles = getattr(self.C,'plot_titles',self.D.plot_titles) + #M.rc('text',usetex=self.usetex) + #M.rc('font',**self.font_prop) + #M.rcParams['savefig.dpi'] = self.dpi + + def plot_2D(self,request): + """ + Path to wrfout file is in config file. + Path to plot output is also in config + + This script is top-most and decides if the variables is + built into WRF default output or needs computing. It unstaggers + and slices data from the wrfout file appropriately. + + + Inputs: + request : nested dictionary with: + + KEY + === + va : variable to plot + + nested KEY/VALUE PAIRS + ====================== + (MANDATORY FOR SOME VARIABLES) + lv : level to plot + pt : plot times + (OPTIONAL) + tla : top limit of latitude + bla : bottom limit of latitude + llo : left limit of longitude + rlo : right limit of longitude + ---> if these are missing, default to 'all points' + plottype : contourf by default. + + """ + # Copy dictionary for editing + rq = copy.deepcopy(request) + + # Load netCDF file once for efficiency + wrfpath = utils.wrfout_files_in(self.C.wrfout_root)[0] + self.W = WRFOut(wrfpath) + + # Loop over all variables + for va in rq: + + # LEVELS + # Levels may not exist for CAPE, shear etc. + # Use all levels in this case. + if not 'lv' in rq[va]: + rq[va]['lv'] = 'all' + + lv = rq[va]['lv'] + vc = utils.level_type(lv) # vertical coordinate + + # TIMES + if not 'pt' in rq[va]: # For averages and all times + if not 'itime' in rq[va]: # For all times + rq[va]['pt'] = ['all',] + else: # For specific range + rq[va]['pt'] = ['range',] + + # Check for pressure levels + if vc == 'isobaric': + nc_path = self.W.path + p_interp_fpath = self.W.interp_to_p(self.C,nc_path,va,lv) + # Edit p_interp namelist + #Execute p_interp here and reassign self.W to new file + self.W = WRFOut(p_interp_fpath) + else: # + # print("Non-pressure levels not supported yet.") + # raise Exception + pass + + F = BirdsEye(self.C,self.W) + + for t in rq[va]['pt']: + #pdb.set_trace() + disp_t = utils.string_from_time('title',t,**rq[va]) + print("Plotting {0} at lv {1} for time {2}.".format(va,lv,disp_t)) + rq[va]['pt'] = t # Need this? + rq[va]['vc'] = vc # Need this? + F.plot2D(va, rq[va]) + + def get_sequence(self,x,SoS=0): + """ Returns a sequence (tuple or list) for iteration. + Avoids an error for strings/integers. + SoS = 1 enables the check for a sequence of sequences (list of dates) + """ + if SoS: + y = x[0] + else: + y = x + + if isinstance(y, collections.Sequence) and not isinstance(y, basestring): + return x + else: + return [x] + + def plot_cross_section(self,var,latA,lonA,latB,lonB): + xs = CrossSection() + xs.plot(var,latA,lonA,latB,lonB) + + def save_data(self,data,folder,fname,format='pickle'): + """ + Save array to file. + Needed by subclasses? + """ + + # Strip file extension given + fname_base = os.path.splitext(fname)[0] + # Check for folder, create if necessary + utils.trycreate(folder) + # Create absolute path + fpath = os.path.join(folder,fname_base) + + if format=='pickle': + with open(fpath+'.pickle','wb') as f: + pickle.dump(data,f) + elif format=='numpy': + N.save(fpath,data) + elif format=='json': + j = json.dumps(data) + with open(fpath+'.json','w') as f: + print >> f,j + else: + print("Give suitable saving format.") + raise Exception + + print("Saved file {0} to {1}.".format(fname,folder)) + + def load_data(self,folder,fname,format='pickle'): + """ + Load array from file. + Needed by subclasses? + """ + + fname2 = os.path.splitext(fname)[0] + fpath = os.path.join(folder,fname2) + if format=='pickle': + with open(fpath+'.pickle','rb') as f: + data = pickle.load(f) + elif format=='numpy': + data = N.load(fpath+'.npy') + elif format=='json': + print("JSON stuff not coded yet.") + raise Exception + else: + print("Give suitable loading format.") + raise Exception + + print("Loaded file {0} from {1}.".format(fname,folder)) + return data + + def compute_diff_energy( + self,ptype,energy,files,times,upper=None,lower=None, + d_save=1,d_return=1,d_fname='diff_energy_data'): + """ + This method computes difference kinetic energy (DKE) + or different total energy (DTE, including temp) + between WRFout files for a given depth of the + atmosphere, at given time intervals + + Inputs: + + ptype : 'sum_z' or 'sum_xyz' + energy : 'kinetic' or 'total' + upper : upper limit of vertical integration + lower : lower limit of vertical integration + files : abs paths to all wrfout files + times : times for computations - tuple format + d_save : save dictionary to folder (path to folder) + d_return: return dictionary (True or False) + d_fname : custom filename + + Outputs: + + data : time series or list of 2D arrays + + ptype 'sum_z' integrates vertically between lower and + upper hPa and creates a time series. + + ptype 'sum_xyz' integrates over the 3D space (again between + the upper and lower bounds) and creates 2D arrays. + """ + if d_save and not isinstance(d_save,basestring): + d_save = os.environ['HOME'] + + # First, save or output? Can't be neither! + if not d_save and not d_return: + print("Pick save or output, otherwise it's a waste of computer" + "power") + raise Exception + + print("Saving pickle file to {0}".format(d_save)) + # Look up the method to use depending on type of plot + PLOTS = {'sum_z':self.DE_z, 'sum_xyz':self.DE_xyz} + + print('Get sequence of time') + # Creates sequence of times + ts = self.get_sequence(times) + + # Dictionary of data + DATA = {} + + print('Get permutations') + # Get all permutations of files + nperm = len(list(itertools.combinations(files,2))) + print('Start loop') + # pdb.set_trace() + for n, perm in enumerate(itertools.combinations(files,2)): + print("No. {0} from {1} permutations".format(n,nperm)) + perm_start = time.time() + DATA[str(n)] = {} + f1, f2 = perm + W1 = WRFOut(f1) + W2 = WRFOut(f2) + print('WRFOuts loaded.') + #pdb.set_trace() + # Make sure times are the same in both files + if not N.all(N.array(W1.wrf_times) == N.array(W2.wrf_times)): + print("Times are not identical between input files.") + raise Exception + else: + print("Passed check for identical timestamps between " + "NetCDF files") + + # Find indices of each time + print('Finding time indices') + t_idx = [] + for t in ts: + t_idx.append(W1.get_time_idx(t)) + + print("Calculating values now...") + DATA[str(n)]['times'] = ts + DATA[str(n)]['values'] = [] + for t in t_idx: + DATA[str(n)]['values'].append(PLOTS[ptype](W1.nc,W2.nc,t, + energy,lower,upper)) + DATA[str(n)]['file1'] = f1 + DATA[str(n)]['file2'] = f2 + + print "Calculation #{0} took {1:2.2f} seconds.".format(n,time.time()-perm_start) + + if d_return and not d_save: + return DATA + elif d_save and not d_return: + #self.save_data(DATA,d_save,d_fname) + self.save_data(DATA,d_save,d_fname) + #self.json_data(DATA,d_save,d_fname) + return + elif d_return and d_save: + #self.save_data(DATA,d_save,d_fname) + self.save_data(DATA,d_save,d_fname) + #self.json_data(DATA,d_save,d_fname) + return DATA + + def DE_xyz(self,nc0,nc1,t_idx,energy,*args): + """ + Computation for difference kinetic energy (DKE). + Sums DKE over the 3D space, returns a time series. + + Destaggering is not enabled as it introduces + computational cost that is of miniscule value considering + the magnitudes of output values. + + Inputs: + + nc0 : netCDF file + nc1 : netCDF file + t_idx : times indices to difference + energy : kinetic or total + *args : to catch lower/upper boundary which isn't relevant here + + Outputs: + + data : time series. + """ + # Wind data + U0 = nc0.variables['U'] + V0 = nc0.variables['V'] + U1 = nc1.variables['U'] + V1 = nc1.variables['V'] + + if energy=='total': + T0 = nc0.variables['T'] + T1 = nc1.variables['T'] + R = 287.0 # Universal gas constant (J / deg K * kg) + Cp = 1004.0 # Specific heat of dry air at constant pressure (J / deg K * kg) + kappa = (R/Cp) + + xlen = U0.shape[2] + + DKE = [] + for n,t in enumerate(t_idx): + print("Finding DKE at time {0} of {1}.".format(n,len(t))) + DKE_hr = 0 # Sum up all DKE for the 3D space + for i in range(xlen): + if energy=='kinetic': + DKE_hr += N.sum(0.5*((U0[t,:,:,i]-U1[t,:,:,i])**2 + + (V0[t,:,:-1,i]-V1[t,:,:-1,i])**2)) + elif energy=='total': + DKE_hr += N.sum(0.5*((U0[t,:,:,i]-U1[t,:,:,i])**2 + + (V0[t,:,:-1,i]-V1[t,:,:-1,i])**2 + + kappa*(T0[t,:,:,i]-T1[t,:,:,i])**2)) + print("DTE at this time: {0}".format(DKE_hr)) + DKE.append(DKE_hr) + return DKE + + def DE_z(self,nc0,nc1,t,energy,lower,upper): + """ + Computation for difference kinetic energy (DKE). + Sums DKE over all levels between lower and upper, + for each grid point, and returns a 2D array. + + Destaggering is not enabled as it introduces + computational cost that is of miniscule value considering + the magnitudes of output values. + + Method finds levels nearest lower/upper hPa and sums between + them inclusively. + + Inputs: + + nc0 : netCDF file + nc1 : netCDF file + t : times index to difference + energy : kinetic or total + lower : lowest level, hPa + upper : highest level, hPa + + Outputs: + + data : 2D array. + """ + + # Speed up script by only referencing data, not + # loading it to a variable yet + + # WIND + U0 = nc0.variables['U'][t,...] + U1 = nc1.variables['U'][t,...] + Ud = U0 - U1 + #del U0, U1 + + V0 = nc0.variables['V'][t,...] + V1 = nc1.variables['V'][t,...] + Vd = V0 - V1 + #del V0, V1 + + # PERT and BASE PRESSURE + if lower or upper: + P0 = nc0.variables['P'][t,...] + PB0 = nc0.variables['PB'][t,...] + Pr = P0 + PB0 + #del P0, PB1 + # Here we assume pressure columns are + # roughly the same between the two... + + if energy=='total': + T0 = nc0.variables['T'][t,...] + T1 = nc1.variables['T'][t,...] + Td = T0 - T1 + #del T0, T1 + + R = 287.0 # Universal gas constant (J / deg K * kg) + Cp = 1004.0 # Specific heat of dry air at constant pressure (J / deg K * kg) + kappa = R/Cp + + xlen = Ud.shape[1] # 1 less than in V + ylen = Vd.shape[2] # 1 less than in U + zlen = Ud.shape[0] # identical in U & V + + # Generator for lat/lon points + def latlon(nlats,nlons): + for i in range(nlats): # y-axis + for j in range(nlons): # x-axis + yield i,j + + DKE = [] + DKE2D = N.zeros((xlen,ylen)) + print_time = ''.join((nc0.variables['Times'][t])) + print("Calculating 2D grid for time {0}...".format(print_time)) + gridpts = latlon(xlen,ylen) + for gridpt in gridpts: + i,j = gridpt + # Find closest level to 'lower', 'upper' + if lower or upper: + P_col = Pr[:,j,i] + if lower: + low_idx = utils.closest(P_col,lower*100.0) + else: + low_idx = None + if upper: + upp_idx = utils.closest(P_col,upper*100.0)+1 + else: + upp_idx = None + + zidx = slice(low_idx,upp_idx) + + if energy=='kinetic': + DKE2D[j,i] = N.sum(0.5*((Ud[zidx,j,i])**2 + + (Vd[zidx,j,i])**2)) + elif energy=='total': + DKE2D[j,i] = N.sum(0.5*((Ud[zidx,j,i])**2 + + (Vd[zidx,j,i])**2 + + kappa*(Td[zidx,j,i])**2)) + + DKE.append(DKE2D) + + return DKE + + def plot_diff_energy(self,ptype,energy,time,folder,fname,V): + """ + + folder : directory holding computed data + fname : naming scheme of required files + V : constant values to contour at + """ + sw = 0 + + DATA = self.load_data(folder,fname,format='pickle') + times = self.get_sequence(time) + + for n,t in enumerate(times): + for pn,perm in enumerate(DATA): + f1 = DATA[perm]['file1'] + f2 = DATA[perm]['file2'] + if sw==0: + # Get times and info about nc files + # First time to save power + W1 = WRFOut(f1) + permtimes = DATA[perm]['times'] + sw = 1 + + # Find array for required time + x = N.where(N.array(permtimes)==t)[0][0] + data = DATA[perm]['values'][x][0] + if not pn: + stack = data + else: + stack = N.dstack((data,stack)) + stack_average = N.average(stack,axis=2) + + #birdseye plot with basemap of DKE/DTE + F = BirdsEye(self.C,W1) # 2D figure class + #F.plot2D(va,t,en,lv,da,na) # Plot/save figure + fname_t = ''.join((fname,'_p{0:02d}'.format(n))) + F.plot_data(stack_average,'contourf',fname_t,t,V) + print("Plotting time {0} from {1}.".format(n,len(times))) + del data, stack + + def plot_error_growth(self,ofname,folder,pfname,sensitivity=0,ylimits=0,**kwargs): + """Plots line graphs of DKE/DTE error growth + varying by a sensitivity - e.g. error growth involving + all members that use a certain parameterisation. + + ofname : output filename prefix + pfname : pickle filename + plotlist : list of folder names to loop over + ylim : tuple of min/max for y axis range + """ + DATA = self.load_data(folder,pfname,format='pickle') + + for perm in DATA: + times = DATA[perm]['times'] + break + + times_tup = [time.gmtime(t) for t in times] + time_str = ["{2:02d}/{3:02d}".format(*t) for t in times_tup] + + if sensitivity: + # Plot multiple line charts for each sensitivity + # Then a final chart with all the averages + # If data is 2D, sum over x/y to get one number + + # Dictionary with average + AVE = {} + + for sens in sensitivity: + ave_stack = 0 + n_sens = len(sensitivity)-1 + colourlist = utils.generate_colours(M,n_sens) + M.rcParams['axes.color_cycle'] = colourlist + fig = plt.figure() + labels = [] + #SENS['sens'] = {} + for perm in DATA: + f1 = DATA[perm]['file1'] + f2 = DATA[perm]['file2'] + + if sens in f1: + f = f2 + elif sens in f2: + f = f1 + else: + f = 0 + + if f: + subdirs = f.split('/') + labels.append(subdirs[-2]) + data = self.make_1D(DATA[perm]['values']) + + plt.plot(times,data) + # pdb.set_trace() + ave_stack = utils.vstack_loop(N.asarray(data),ave_stack) + else: + pass + + # pdb.set_trace() + n_sens += 1 + colourlist = utils.generate_colours(M,n_sens) + M.rcParams['axes.color_cycle'] = colourlist + AVE[sens] = N.average(ave_stack,axis=0) + labels.append('Average') + plt.plot(times,AVE[sens],'k') + + plt.legend(labels,loc=2,fontsize=9) + if ylimits: + plt.ylim(ylimits) + plt.gca().set_xticks(times[::2]) + plt.gca().set_xticklabels(time_str[::2]) + outdir = self.C.output_root + fname = '{0}_Growth_{1}.png'.format(ofname,sens) + fpath = os.path.join(outdir,fname) + fig.savefig(fpath) + + plt.close() + print("Saved {0}.".format(fpath)) + + # Averages for each sensitivity + labels = [] + fig = plt.figure() + ave_of_ave_stack = 0 + for sens in AVE.keys(): + plt.plot(times,AVE[sens]) + labels.append(sens) + ave_of_ave_stack = utils.vstack_loop(AVE[sens],ave_of_ave_stack) + + labels.append('Average') + ave_of_ave = N.average(ave_of_ave_stack,axis=0) + plt.plot(times,ave_of_ave,'k') + + plt.legend(labels,loc=2,fontsize=9) + + if ylimits: + plt.ylim(ylimits) + plt.gca().set_xticks(times[::2]) + plt.gca().set_xticklabels(time_str[::2]) + outdir = self.C.output_root + fname = '{0}_Growth_Averages.png'.format(ofname) + fpath = os.path.join(outdir,fname) + fig.savefig(fpath) + + plt.close() + print("Saved {0}.".format(fpath)) + #pdb.set_trace() + + + + else: + fig = plt.figure() + ave_stack = 0 + for perm in DATA: + data = self.make_1D(DATA[perm]['values']) + plt.plot(times,data,'blue') + ave_stack = utils.vstack_loop(N.asarray(data),ave_stack) + + total_ave = N.average(ave_stack,axis=0) + plt.plot(times,total_ave,'black') + + if ylimits: + plt.ylim(ylimits) + plt.gca().set_xticks(times[::2]) + plt.gca().set_xticklabels(time_str[::2]) + outdir = self.C.output_root + fname = '{0}_Growth_allmembers.png'.format(ofname) + fpath = os.path.join(outdir,fname) + fig.savefig(fpath) + + plt.close() + print("Saved {0}.".format(fpath)) + + def composite_profile(self,va,skewT_time,skewT_latlon,enspaths,dom=1,mean=0,std=0,xlim=0,ylim=0): + P = Profile(self.C) + P.composite_profile(va,skewT_time,skewT_latlon,enspaths,dom,mean,std,xlim,ylim) + + def plot_skewT(self,plot_time,plot_latlon,dom=1,save_output=0,composite=0): + wrfouts = self.wrfout_files_in(self.C.wrfout_root) + for wrfout in wrfouts: + if not composite: + W = WRFOut(wrfout) + ST = SkewT(self.C,W) + ST.plot_skewT(plot_time,plot_latlon,dom,save_output) + nice_time = utils.string_from_time('title',plot_time) + print("Plotted Skew-T for time {0} at {1}".format( + nice_time,plot_latlon)) + else: + #ST = SkewT(self.C) + pass + + def plot_streamlines(self,lv,times): + wrfpath = self.wrfout_files_in(self.C.wrfout_root)[0] + self.W = WRFOut(wrfpath) + self.F = BirdsEye(self.C,self.W) + for pt in times: + disp_t = utils.string_from_time('title',pt) + print("Plotting {0} at lv {1} for time {2}.".format( + 'streamlines',lv,disp_t)) + self.F.plot_streamlines(lv,pt) + + + + def make_1D(self,data,output='list'): + """ Make sure data is a time series + of 1D values, and numpy array. + + List of arrays -> Numpy array or list + """ + if isinstance(data,list): + + data_list = [] + for time in data: + data_list.append(N.sum(time[0])) + + if output == 'array': + data_out = N.array(data_list) + else: + data_out = data_list + + + #elif isinstance(data,N.array): + # shape = data.shape + # if len(shape) == 1: + # data_out = data + # elif len(shape) == 2: + # data_out = N.sum(data) + return data_out + + + + diff --git a/postWRF/postWRF/map.py b/postWRF/postWRF/map.py index e69de29..d70cfe3 100644 --- a/postWRF/postWRF/map.py +++ b/postWRF/postWRF/map.py @@ -0,0 +1,48 @@ +from figure import Figure + +class Map(Figure): + def plot_domains(self,wrfouts,labels,latlons,colour=0): + """ + wrfouts : list of wrfout file paths + latlons : dictionary of Nlim,Elim,Slim,Wlim + for plot + """ + + fig = plt.figure() + + # Create basemap first of all + basemap_res = getattr(self.C,'basemap_res',self.D.basemap_res) + + m = Basemap( + projection='merc', + llcrnrlon=latlons['Wlim'],llcrnrlat=latlons['Slim'], + urcrnrlon=latlons['Elim'],urcrnrlat=latlons['Nlim'], + lat_0=latlons['lat0'],lon_0=latlons['lon0'], + resolution=basemap_res,area_thresh=500 + ) + + m.drawcoastlines() + m.drawstates() + m.drawcountries() + + if not isinstance(colour,collections.Sequence): + colours = ['k',] * len(wrfouts) + else: + colours = colour + # Get corners of each domain + for gridlabel,fpath,colour in zip(labels,wrfouts,colours): + W = WRFOut(fpath) + print("Plotting domain {0} for {1}".format(gridlabel,fpath)) + #Nlim, Elim, Slim, Wlim = W.get_limits() + x,y = m(W.lons,W.lats) + xl = len(x[0,:]) + midpt = len(y[0,:])/2 + plt.annotate(gridlabel,color=colour,fontsize=10,xy=(x[0,-(0.12*xl)],y[0,midpt]), + bbox=dict(fc='white'),alpha=1,va='center',ha='left') + m.plot(x[0,:],y[0,:],colour,lw=2) + plt.plot(x[:,0],y[:,0],colour,lw=2) + plt.plot(x[len(y)-1,:],y[len(y)-1,:],colour,lw=2) + plt.plot(x[:,len(x)-1],y[:,len(x)-1],colour,lw=2) + + fpath = os.path.join(self.C.output_root,'domains.png') + fig.savefig(fpath) diff --git a/postWRF/postWRF/metconstants.py b/postWRF/postWRF/metconstants.py new file mode 100644 index 0000000..ae6b121 --- /dev/null +++ b/postWRF/postWRF/metconstants.py @@ -0,0 +1,10 @@ +Tb = 300.0 # Base temperature +Tz = 273.15 # 0 C in Kelvin +L = 2.501e6 # Latent heat of vaporisation +R = 287.04 # gas constant air +Rv = 461.5 # gas constant vapour +cp = 1005.0 +cv = 718.0 +kappa = (cp-cv)/cp +g = 9.81 +P0 = 10**5 # Reference pressure diff --git a/postWRF/postWRF/skewt.py b/postWRF/postWRF/skewt.py index d8c9a22..c36f2c1 100644 --- a/postWRF/postWRF/skewt.py +++ b/postWRF/postWRF/skewt.py @@ -22,7 +22,7 @@ from WEM.utils import generalmet from WEM.utils import gridded_data from WEM.utils import utils -import WEM.utils.metconstants as mc +import metconstants as mc class Profile(Figure): def __init__(self,config,wrfout=0): diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 17dcd87..549f5e8 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -14,10 +14,9 @@ import constants as cc import scipy.ndimage -#sys.path.append('/home/jrlawson/gitprojects/meteogeneral/') -#from meteogeneral.WRF import wrf_tools +from WEM.utils import wrf_tools -class WRFOut: +class WRFOut(object): def __init__(self,fpath,config=0): self.path = fpath @@ -28,7 +27,8 @@ def __init__(self,fpath,config=0): self.wrf_times = self.nc.variables['Times'][:] self.dx = self.nc.DX self.dy = self.nc.DY - #self.lvs = + + #self.lvs = self.lats = self.nc.variables['XLAT'][0,...] # Might fail if only one time? self.lons = self.nc.variables['XLONG'][0,...] @@ -53,7 +53,26 @@ def test_smooth(self,data): from skimage.feature """ - def get_time_idx(self,t,tuple_format=0): + def wrftime_to_datenum(self,times): + """ + Convert wrf's weird Times variable to datenum time. + """ + nt = times.shape[0] + wrf_times_epoch = N.zeros([nt,1]) + + for t in range(nt): + yr = int(''.join(t[i,0:4])) + mth = int(''.join(t[i,5:7])) + day = int(''.join(t[i,8:10])) + hr = int(''.join(t[i,11:13])) + mins = int(''.join(t[i,14:16])) + sec = int(''.join(t[i,17:19])) + self.wrf_times_epoch[i] = calendar.timegm([yr,mth,day,hr,mins,sec]) + + return self.wrf_times_epoch + + + def get_time_idx(self,wrftimes,t,tuple_format=0): """ Input: @@ -64,29 +83,22 @@ def get_time_idx(self,t,tuple_format=0): time_idx : index of time in WRF file """ + # Ensure datenum format if tuple_format: t_epoch = calendar.timegm(t) else: t_epoch = t - nt = self.wrf_times.shape[0] - self.wrf_times_epoch = N.zeros([nt,1]) - t = self.wrf_times # For brevity - for i in range(nt): - yr = int(''.join(t[i,0:4])) - mth = int(''.join(t[i,5:7])) - day = int(''.join(t[i,8:10])) - hr = int(''.join(t[i,11:13])) - mins = int(''.join(t[i,14:16])) - sec = int(''.join(t[i,17:19])) - self.wrf_times_epoch[i] = calendar.timegm([yr,mth,day,hr,mins,sec]) + # Convert wrftimes to datenum times + wrf_times_epoch = self.wrftime_to_datenum(self.wrf_times) # Now find closest WRF time - self.time_idx = N.where( - abs(self.wrf_times_epoch-t_epoch) == - abs(self.wrf_times_epoch-t_epoch).min() - )[0][0] - return self.time_idx + #self.time_idx = N.where( + # abs(self.wrf_times_epoch-t_epoch) == + # abs(self.wrf_times_epoch-t_epoch).min() + # )[0][0] + time_idx = utils.closest(wrf_times_epoch,t_epoch) + return time_idx def check_compute(self,var): @@ -111,7 +123,7 @@ def get(self,var,slices,**kwargs): Returns unstaggered, sliced data. var : netCDF variable name - slices : dict, keys as follows: + slices : if dict, keys as follows: t : time index lv : level index diff --git a/utils/__init__.py b/utils/__init__.py index e69de29..c74dee3 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -0,0 +1,9 @@ +""" +References these utilities in WEM.utils. +""" + +from general_met import * +from gridded_data import * +from more_utils import * +from unix_tools import * +from wrf_tools import * \ No newline at end of file diff --git a/utils/general_met.py b/utils/general_met.py new file mode 100644 index 0000000..f202440 --- /dev/null +++ b/utils/general_met.py @@ -0,0 +1,44 @@ +import numpy as N +import pdb + +def decompose_wind(wspd,wdir,convert=0): + # Split wind speed/wind direction into u,v + if (type(wspd) == N.array) & (type(wdir) == N.array): + uwind = N.array([-s * N.sin(N.radians(d)) if ((s>-1)&(d>-1)) else -9999 + for s,d in zip(wspd,wdir)]) + vwind = N.array([-s * N.cos(N.radians(d)) if ((s>-1)&(d>-1)) else -9999 + for s,d in zip(wspd,wdir)]) + else: + uwind = -wspd * N.sin(N.radians(wdir)) + vwind = -wspd * N.cos(N.radians(wdir)) + if convert == 'ms_kt': + uwind *= 1.94384449 + vwind *= 1.94384449 + elif convert == 'ms_mph': + uwind *= 2.23694 + vwind *= 2.23694 + elif convert == 'kt_ms': + uwind *= 0.51444444 + vwind *= 0.51444444 + else: + pass + return uwind, vwind + +def combine_wind_components(u,v): + wdir = N.degrees(N.arctan2(u,v)) + 180 + wspd = N.sqrt(u**2 + v**2) + return wspd, wdir + +def convert_kt2ms(wspd): + wspd_ms = wspd*0.51444 + return wspd_ms + +def dewpoint(T,RH): # Lawrence 2005 BAMS? + #T in C + #RH in 0-100 format + es = 6.11 * (10**((7.5*T)/(237.7+T))) + e = es * RH/100.0 + alog = 0.43429*N.log(e) - 0.43429*N.log(6.11) + Td = (237.7 * alog)/(7.5-alog) + #pdb.set_trace() + return Td diff --git a/utils/more_utils.py b/utils/more_utils.py new file mode 100644 index 0000000..4cfaa72 --- /dev/null +++ b/utils/more_utils.py @@ -0,0 +1,213 @@ +import numpy as N +import os +import time +import calendar +import pdb + +""" A collection of useful utilities. +""" + +def trycreate(loc): + try: + os.stat(loc) + except: + os.makedirs(loc) + +def padded_times(timeseq): + padded = ['{0:04d}'.format(t) for t in timeseq] + return padded + +def string_from_time(usage,t,dom=0,strlen=0,conven=0,**kwargs): + """ + conven : convection of MM/DD versus DD/MM + """ + + + if isinstance(t,str): + if usage == 'output': + usage = 'skip' # Time is already a string + elif usage == 'title': + pass + # if kwargs['itime']: # For averages or maxima over time + # itime = kwargs['itime'] + # ftime = kwargs['ftime'] + # else: + # pass + else: + raise Exception + elif isinstance(t,int): + # In this case, time is in datenum. Get it into tuple format. + t = time.gmtime(t) + else: + pass + + if usage == 'title': + # Generates string for titles + if not 'itime' in kwargs: # i.e. for specific times + #if not hasattr(kwargs,'itime'): # i.e. for specific times + strg = '{3:02d}:{4:02d}Z on {2:02d}/{1:02d}/{0:04d}'.format(*t) + else: # i.e. for ranges (average over time) + s1 = '{3:02d}:{4:02d}Z to '.format(*kwargs['itime']) + s2 = '{3:02d}:{4:02d}Z'.format(*kwargs['ftime']) + strg = s1 + s2 + elif usage == 'wrfout': + # Generates string for wrfout file finding + # Needs dom + if not dom: + print("No domain specified; using domain #1.") + dom = 1 + strg = ('wrfout_d0' + str(dom) + + '{0:04d}-{1:02d}-{2:02d}_{3:02d}:{4:02d}:{5:02d}'.format(*t)) + elif usage == 'output': + if not conven: + # No convention set, assume DD/MM (I'm biased) + conven = 'full' + # Generates string for output file creation + if conven == 'DM': + strg = '{2:02d}{1:02d}_{3:02d}{4:02d}'.format(*t) + elif conven == 'MD': + strg = '{1:02d}{2:02d}_{3:02d}{4:02d}'.format(*t) + elif conven == 'full': + strg = '{0:04d}{1:02d}{2:02d}{3:02d}{4:02d}'.format(*t) + else: + print("Set convention for date format: DM or MD.") + elif usage == 'dir': + # Generates string for directory names + # Needs strlen which sets smallest scope of time for string + if not strlen: + print("No timescope strlen set; using hour as minimum.") + strlen = 'hour' + n = lookup_time(strlen) + strg = "{0:04d}".format(t[0]) + ''.join( + ["{0:02d}".format(a) for a in t[1:n+1]]) + elif usage == 'skip': + strg = t + else: + print("Usage for string not valid.") + raise Exception + return strg + +def lookup_time(str): + D = {'year':0, 'month':1, 'day':2, 'hour':3, 'minute':4, 'second':5} + return D[str] + +def get_level_naming(va,**kwargs): + lv = kwargs['lv'] + if lv < 1500: + return str(lv)+'hPa' + elif lv == 2000: + return 'sfc' + elif lv.endswith('K'): + return lv + elif lv.endswith('PVU'): + return lv + elif lv.endswith('km'): + return lv + elif lv == 'all': + if va == 'shear': + name = '{0}to{1}'.format(kwargs['bottom'],kwargs['top']) + return name + else: + return 'all_lev' + + +def level_type(lv): + """ Check to see what type of level is requested by user. + + """ + if lv < 1500: + return 'isobaric' + elif lv == 2000: + return 'surface' + elif lv.endswith('K'): + return 'isentropic' + elif lv.endswith('PVU'): + return 'PV-surface' + elif lv.endswith('km'): + return 'geometric' + elif lv == 'all': + return 'eta' + else: + print('Unknown vertical coordinate.') + raise Exception + +def closest(arr2D,val): + """ + Inputs: + val : required value + arr2D : 2D array of values + + Output: + + idx : index of closest value + + """ + idx = N.argmin(N.abs(arr2D - val)) + return idx + +def dstack_loop(data, obj): + """ + Tries to stack numpy array (data) into 'stack' object (obj). + If obj doesn't exist, then initialise it + If obj does exist, stack data. + """ + if isinstance(obj,N.ndarray): + stack = N.dstack((obj,data)) + else: + stack = data + + return stack + +def dstack_loop_v2(data, obj): + """ + Need to set obj = 0 at start of loop in master script + + Tries to stack numpy array (data) into 'stack' object (obj). + If obj doesn't exist, then initialise it + If obj does exist, stack data. + """ + try: + print obj + except NameError: + stack = data + else: + stack = N.dstack((obj,data)) + + return stack + +def vstack_loop(data, obj): + """ + Need to set obj = 0 at start of loop in master script + + Tries to stack numpy array (data) into 'stack' object (obj). + If obj doesn't exist, then initialise it + If obj does exist, stack data. + """ + + if isinstance(obj,N.ndarray): + stack = N.vstack((obj,data)) + else: + stack = data + + return stack + + +def generate_times(idate,fdate,interval): + """ + Interval in seconds + """ + i = calendar.timegm(idate) + f = calendar.timegm(fdate) + times = range(i,f,interval) + return times + +def generate_colours(M,n): + """ + M : Matplotlib instance + n : number of colours you want + + Returns + """ + + colourlist = [M.cm.spectral(i) for i in N.linspace(0.08,0.97,n)] + return colourlist diff --git a/utils/unix_tools.py b/utils/unix_tools.py index bf769a2..b3135c4 100644 --- a/utils/unix_tools.py +++ b/utils/unix_tools.py @@ -1,12 +1,18 @@ -# Scripts to help with directory, file, etc issues +""" +Utility scripts to help with directory, file, etc issues +""" import os -# Create folder if it doesn't exist, along with its subdirectories -def createfolder(dir): - try: - os.stat(dir) - except: - os.makedirs(dir) - print 'Creating directory',dir - +def dir_from_naming(self,root,*args): + """ + Generate file path from arguments + + Inputs: + root : file path base + args : list of arguments to join as separate folders + """ + l = [str(a) for a in args] + path = os.path.join(root,*l) + return path + diff --git a/utils/wrf_tools.py b/utils/wrf_tools.py index 785423c..d0edd3b 100644 --- a/utils/wrf_tools.py +++ b/utils/wrf_tools.py @@ -1,3 +1,7 @@ +""" +Utility scripts related to WRF files. +""" + # Some functions to import WRF data etc from netCDF4 import Dataset import pdb @@ -47,7 +51,10 @@ def wrf_nc_load(dom,var,ncfolder,datestr,thin,Nlim=0,Elim=0,Slim=0,Wlim=0): # lons calculated earlier if Nlim: - m = Basemap(projection='lcc',lon_0=cen_lon,lat_0=cen_lat,llcrnrlat=Slim,urcrnrlat=Nlim,llcrnrlon=Wlim,urcrnrlon=Elim,rsphere=6371200.,resolution='h',area_thresh=100) + m = Basemap(projection='lcc',lon_0=cen_lon,lat_0=cen_lat, + llcrnrlat=Slim,urcrnrlat=Nlim,llcrnrlon=Wlim, + urcrnrlon=Elim,rsphere=6371200.,resolution='h', + area_thresh=100) else: m = Basemap(resolution='i',projection='lcc', width=width_meters,height=height_meters, @@ -164,3 +171,72 @@ def latlon_1D(nc): lons = nc.variables['XLONG'][0,Ny/2,:] return lats, lons +def wrfout_files_in(self,folders,dom='notset',init_time='notset',descend=1,**kwargs): + """ + Hunt through given folder(s) to find all occurrences of wrfout + files. + + Optional: + avoid : string. if folder name includes this + term, ignore the folder. + + Returns: + wrfouts : list of absolute paths to wrfout files + """ + + folders = self.get_sequence(folders) + avoids = [] + if 'avoid' in kwargs: + # Avoid folder names with this string + # or list of strings + avoid = self.get_sequence(kwargs['avoid']) + for a in avoid: + avoids.append('/{0}/'.format(a)) + + + w = 'wrfout' # Assume the prefix + if init_time=='notset': + suffix = '*0' + # We assume the user has wrfout files in different folders for different times + else: + try: + it = self.string_from_time('wrfout',init_time) + except: + print("Not a valid wrfout initialisation time; try again.") + raise Error + suffix = '*' + it + + if dom=='notset': + # Presume all domains are desired. + prefix = w + '_d' + elif (dom == 0) | (dom > 8): + print("Domain is out of range. Choose number between 1 and 8 inclusive.") + raise IndexError + else: + dom = 'd{0:02d}'.format(dom) + prefix = w + '_' + dom + + wrfouts = [] + if descend: + for folder in folders: + for root,dirs,files in os.walk(folder): + for fname in fnmatch.filter(files,prefix+suffix): + skip_me = 0 + fpath = os.path.join(root,fname) + if avoids: + for a in avoids: + if a in fpath: + skip_me = 1 + else: + pass + if not skip_me: + wrfouts.append(fpath) + + else: + for folder in folders: + findfile = os.path.join(folder,prefix+suffix) + files = glob.glob(findfile) + # pdb.set_trace() + for f in files: + wrfouts.append(os.path.join(folder,f)) + return wrfouts From 7e83ce2bb99b724e6141650a6f67491317300bca Mon Sep 17 00:00:00 2001 From: John Lawson Date: Sat, 12 Jul 2014 01:53:43 -0500 Subject: [PATCH 088/111] Creating docs --- docs/.doctrees/index.doctree | Bin 0 -> 4928 bytes docs/.doctrees/intro.doctree | Bin 0 -> 1967 bytes docs/.doctrees/lazywrf.doctree | Bin 0 -> 1971 bytes docs/.doctrees/postwrf.doctree | Bin 0 -> 1971 bytes docs/Makefile | 177 +++++++ docs/_sources/index.txt | 25 + docs/_sources/intro.txt | 0 docs/_sources/lazywrf.txt | 0 docs/_sources/postwrf.txt | 0 docs/_static/ajax-loader.gif | Bin 0 -> 673 bytes docs/_static/basic.css | 537 +++++++++++++++++++++ docs/_static/comment-bright.png | Bin 0 -> 3500 bytes docs/_static/comment-close.png | Bin 0 -> 3578 bytes docs/_static/comment.png | Bin 0 -> 3445 bytes docs/_static/default.css | 256 ++++++++++ docs/_static/doctools.js | 238 ++++++++++ docs/_static/down-pressed.png | Bin 0 -> 368 bytes docs/_static/down.png | Bin 0 -> 363 bytes docs/_static/file.png | Bin 0 -> 392 bytes docs/_static/jquery.js | 2 + docs/_static/minus.png | Bin 0 -> 199 bytes docs/_static/plus.png | Bin 0 -> 199 bytes docs/_static/pygments.css | 62 +++ docs/_static/searchtools.js | 622 ++++++++++++++++++++++++ docs/_static/sidebar.js | 159 +++++++ docs/_static/underscore.js | 31 ++ docs/_static/up-pressed.png | Bin 0 -> 372 bytes docs/_static/up.png | Bin 0 -> 363 bytes docs/_static/websupport.js | 808 ++++++++++++++++++++++++++++++++ docs/conf.py | 265 +++++++++++ docs/genindex.html | 92 ++++ docs/index.html | 119 +++++ docs/index.rst | 25 + docs/intro.html | 107 +++++ docs/intro.rst | 0 docs/lazywrf.html | 107 +++++ docs/lazywrf.rst | 0 docs/postwrf.html | 97 ++++ docs/postwrf.rst | 0 docs/search.html | 99 ++++ ensemble.txt | 455 ------------------ 41 files changed, 3828 insertions(+), 455 deletions(-) create mode 100644 docs/.doctrees/index.doctree create mode 100644 docs/.doctrees/intro.doctree create mode 100644 docs/.doctrees/lazywrf.doctree create mode 100644 docs/.doctrees/postwrf.doctree create mode 100644 docs/Makefile create mode 100644 docs/_sources/index.txt create mode 100644 docs/_sources/intro.txt create mode 100644 docs/_sources/lazywrf.txt create mode 100644 docs/_sources/postwrf.txt create mode 100644 docs/_static/ajax-loader.gif create mode 100644 docs/_static/basic.css create mode 100644 docs/_static/comment-bright.png create mode 100644 docs/_static/comment-close.png create mode 100644 docs/_static/comment.png create mode 100644 docs/_static/default.css create mode 100644 docs/_static/doctools.js create mode 100644 docs/_static/down-pressed.png create mode 100644 docs/_static/down.png create mode 100644 docs/_static/file.png create mode 100644 docs/_static/jquery.js create mode 100644 docs/_static/minus.png create mode 100644 docs/_static/plus.png create mode 100644 docs/_static/pygments.css create mode 100644 docs/_static/searchtools.js create mode 100644 docs/_static/sidebar.js create mode 100644 docs/_static/underscore.js create mode 100644 docs/_static/up-pressed.png create mode 100644 docs/_static/up.png create mode 100644 docs/_static/websupport.js create mode 100644 docs/conf.py create mode 100644 docs/genindex.html create mode 100644 docs/index.html create mode 100644 docs/index.rst create mode 100644 docs/intro.html create mode 100644 docs/intro.rst create mode 100644 docs/lazywrf.html create mode 100644 docs/lazywrf.rst create mode 100644 docs/postwrf.html create mode 100644 docs/postwrf.rst create mode 100644 docs/search.html delete mode 100644 ensemble.txt diff --git a/docs/.doctrees/index.doctree b/docs/.doctrees/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..bd2ed2f8e6725819747ef1dcc56a6c785ee86e0d GIT binary patch literal 4928 zcmds5O>bOR8MYJKGh=(~#I2o*q|F6X>OjV0hg4J+yFewdm<>%^M5CKC_nw(^#`mk| zoa>oEiiE0C#nNE|w>u;x5DO#_5*0rH76^$A;wP|R!Ga&a0`a`}V>~mln*?@58fWI7 zkN16__j$kW_m@6?{e^}6pWX0dFEujIdr|Cjo!L4)2zg|(A7{57Wk1RuWxKZS2^si` zN7;jHS1j0?7l(6u#L9yVD@$+j5goOEdgtrka;m$Sk#XdNOdGD8o(%YH$5Wgc?mOM_ zs!oKAMy>Z#=^bmslyR{6+sruMOatfOjcx>^GQu6)QE_aX zhbH!n;{2iGOT|4SPxxN;13*x~u#zxfU^2008;T7zM0h;23t8G_c9j{UBqRYv!fZKW zA-+B!NYiBU5)}`iwQd9ZHn7)i8Tov)r!;il#`&6IebOWob-NVEh-XL3V(0(J zS812nsmXP5G{s(8z+-TdBkr$*s7t;5U zHBRkPKZv{8DIs82b0>ha!5B}r?Q+i2%&wP*C_zvtAckaQRGisbz`i#gsvf>2u{QLL z+Zu3Cve?-XgO9R~IHo_rM;XsiC&YogU8%Gn>}@?{BcCTmWJi~5JxaqaSKQBz7Shrq z@4_SR0hKkNa_u}Q?Ph6uYX~nTTxIMc&)yfeI>j}7!Up@wupnE@#H}F@Te>xqCvh8T zeY4y}l*FzlDakIP)DjH%Qb{QoAZ}NAyoiE3(?7xet#3rW^tg6dG(e_y1Bt|RR%WUoZP0U98XnFqim%P6{$0DCaOfWO z-4U|q{N@E)=V2n4mU@0^)7JYuDrh%t>hCUc`L12cMcB*Q_+TOawn!vDGk9D428R9? zU~L{^>F{AC;USfW!u#ac8WznsL3Y1PH=vT>H&cCXz|HDRo+|7bmiQxe*dXX`NynNs>?s! zDdNvlYUDZVNmq!!6h07teG>bBRoMT%#Qqx8|M#BhJA*INQWc9A2f*N3 zag1W%*^Zs(3cE35@hY-FSp%pWpiY1^pc+M0X{AVsnO*$iOnX4d^Cx92B9~89ku3xX zrR^5dcB@R=MdhegrW5*8iv?w85@74dAw{?PD6>~c2zA14k)n!zR&!&JbulemrOK6$ z4?UH4CPvIeNE3`RDnA7bV( z*<-t|coHjAXMVy1YLa%yCU^xC2es931~myjwl=^!cPM>ors{T8uwZg**{&77K$VY< z$#AG4L8KF@Wr66r>Jbkri>{XznK;HcD!J+976}suu%_v!Vd74h*MV$>8Hs2FMXu;E zM!AtIWM2gMpq4HLG4W+XMoDU>2-HYTuk}VtODr})Um5k{(&O;sMlNQWflbmoajS8y zV{gpf=oSnW2*UuaAZE1OPQ(TuNdxeiNp%J0vb`}&==sxE$RLf;P{0d8J_NoTr)JuO zxg&!N*~-3vAdQs|2UAOgaG}Py1Rcs3<2pTQ(r|>X_srxqjItA-0)llNVg`5LPeF2x zDA>#)0oswKxS&h6=n|cFXTxQ+bU5*|gJ?!)?Zf^t8cM;@-kjQHpLf$f7B^jAYGP3t zHh6Tsz?lb}-YjU)%7`h{Uk0k7I|)7{6SVD-$K7I7KDFCk5VK>Kqx%V5j47C$X}o@1 z>Yr$y`fzkex@}M_O|Bq|Hs?S_qRDvz0uf86_oiLSkrX0ucgNNd%lTW24A|8ubJB#5 z$#3v395Z;EYRQMOBA^ z$hksNyeHVABs0NkqFtkUlnY+G=+a4_6k>=rtm&fNIKI_cyUfjaVL~-PMbus)9&7X02u#bo&W#< literal 0 HcmV?d00001 diff --git a/docs/.doctrees/intro.doctree b/docs/.doctrees/intro.doctree new file mode 100644 index 0000000000000000000000000000000000000000..89c8bdc100bb0358976ad618e03f6ea4885c20e5 GIT binary patch literal 1967 zcmbVNy>A;g6t^AAmTXD>Xxt?UIu&RKTNye89b2@EQBO@f1qAQzNymgoUdVTrG#KcZ z0-Rd*Kj1v{(SugO|&$NMlML;4aIN(_~BT1+qj(KkFRHMJvl#nZ%d=)(pxiID6XA- z4>o@G`%k~jfZfk*a$74f{~RdS`lX;L&* zE&2f7;ch2$N?`(!bH6q$SE5vlQsWn<9%`FXGgYQ;cqc}qhUeFBuD!y`36h}9+-`h_ zo7481-5gF5?powpdJli^!pp}UMVHf3nnIJq6m&c>BD7Kr3nVvwE9M})d>reir_U21 zkGo2^pg}N(SGM6@3QvxdKZGwZsMZzmxW*u2VpI#XRUF7|mF7M{pLooGX{yhBPn}%$ zhJydtR}$WTY>;!0K{#vmfYipP(MU}$Qxo50k0<72<93SZ9R|9}r~vsjW}+lwv>FtY zH4D5v6NU0ALYv9KqZggXq^X69OmVp*`72mZiGKdDPzXyaC*hG{Y=oT`T%F$-O zh5xbNhDB5Njh-chXn4F$G(Hbg>RJr%VpHyyhv70Ut6uP);%NJ*ZE;eh+--Nt5gKgR zPxHpXAf&%r$|*EWh6TjF}7!nCk?!%F*WV+-;z4AX;(49CCY zmk;r;_z=Gc!@N|waAe{;R5+R&S)uqLhBIXfs;wuX(8(F8eY_1*a?UzI zCbtFdMDg37f4q8wCR&$tjJvl#nZ%d=)(pxiID6XA- z4>o@G>kmK8fZflu{C&N2^K0ihwsNszO=2~-u8Mz#(Si&)$<_hOGcx!1b1}D;jb+;5 zaI+sK0Al89A?8YhKBOEKlF75IYMeUi-X==2VJmhdIZ%-8vZlH{DjKH+Q&nr6h97u- z_4!xdpk$m%G6{uH62oUDbN%gXwp=c+>ou3wTwAx86?8vCp`U@zQaS_Ggl9*x!rfzm z7{ENq${Kthv~=;|6z(#u7$mA%<{UqSv7_2L7Mku!$D4!jX<`O*as^ z-S`eSr|mVnNt`6ywaB&f9{%2jSC2c2E~%w7g(inN=y+mCXr&kyNN)UA%t3hdIM`25 zpCv*bca?BKgJ2A=ZNs}1o*XH^3!h+Att;ShjZwtRs1|6eIFQ>aO@4wt@t6YBR3G_1 zcXHVq3jSkXNqGNTgPeN|!fC4qq&7Z{Mrv}An)oJrJTWI5w^KxKG165=1<0>46D1L& z)u5oPS>WZVD3nhT+Dr}}z3fCLO)XSpipw3zU&De*^z(<6LRecl36IPYdSB++$~!@o zR9eUaORY8Q!v=rtsVU%cm9S0IIQ1Z7ERr^dX_Mv{5G2|$xw{|9Vq9&)$|G=NgZ+7jilOvI$*t>7u{J!4HABX?^d%V~FglS>(hL!f$#unsb7^Vjm8IFI) zuOH%H@gaT{hIy%U;mE{ysBknlvO@7g3}?y|%2CdOrCSjDw!#y$G7Gdj@B7c4HB6?6 z;pjK18yw%cV5ACBqpw=UuouIGZ7wpI-)Uv;&}9HTZjIY$Ra;L&p_4OG`*<6s1v{(SWYO|&$NMlML;4aIN(_~BT1+qj(KkKfMTdUAgD-j+torMG6bP+U9v z9&G&V_n&^50lS~!LSDN0weuWXxmd9#v6@>~#XrMnL57@U>wx7MnS1=Xm|M%nGVO4< z*$)!{F>|#LbEQEaQjQAAF{(d%FE|=H!noDc0ty|0rx}TxY&%kFXoq=k?vm;sI?y*1& zV4h@U4ZaUry7+JkcbQfU5>+j8jvvF=QEeRyP4}eZ%|ZA)F@rl0HXVm4GSgI`bi^_f zOI2_gZ-(Kdl)67Q2+uoDL~=XP2N;D!-7Bv;UB_;o^04Lga>I+QMQ|1IyyaDLrNL=Z zG*vD72;SmuCv!@10+2JmHY``7REtvM7p5L+n{qQ%rfzsANTY`5S8uMo!pjM=pv>HE ze21IU_L|)!P7>}~%*n>>6wzCZbd^y7@@vdQNyKP1 zC@5VuJCR9K3l*8-a!2ylu%HtC{9&aK)>cl!BeR6wm$|m`PEaM4 z7P7!nYt8z&!C!l73b0do)ppD+9pezGR+6Pz70c+GaPmt zq1|!XpQ>m#=tVD?z)tnpSKVqzCA_Zj4QqCiE2JZh6s~BWoJa=MtV!ah6+P%yGM38G zX2XU5wc&1VkQl#W/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/WEM.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/WEM.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/WEM" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/WEM" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/_sources/index.txt b/docs/_sources/index.txt new file mode 100644 index 0000000..90509b2 --- /dev/null +++ b/docs/_sources/index.txt @@ -0,0 +1,25 @@ +.. WEM documentation master file, created by + sphinx-quickstart on Sat Jul 12 01:40:57 2014. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to WEM's documentation! +=============================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + intro + lazywrf + postwrf + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/docs/_sources/intro.txt b/docs/_sources/intro.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/_sources/lazywrf.txt b/docs/_sources/lazywrf.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/_sources/postwrf.txt b/docs/_sources/postwrf.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/_static/ajax-loader.gif b/docs/_static/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..61faf8cab23993bd3e1560bff0668bd628642330 GIT binary patch literal 673 zcmZ?wbhEHb6krfw_{6~Q|Nno%(3)e{?)x>&1u}A`t?OF7Z|1gRivOgXi&7IyQd1Pl zGfOfQ60;I3a`F>X^fL3(@);C=vM_KlFfb_o=k{|A33hf2a5d61U}gjg=>Rd%XaNQW zW@Cw{|b%Y*pl8F?4B9 zlo4Fz*0kZGJabY|>}Okf0}CCg{u4`zEPY^pV?j2@h+|igy0+Kz6p;@SpM4s6)XEMg z#3Y4GX>Hjlml5ftdH$4x0JGdn8~MX(U~_^d!Hi)=HU{V%g+mi8#UGbE-*ao8f#h+S z2a0-5+vc7MU$e-NhmBjLIC1v|)9+Im8x1yacJ7{^tLX(ZhYi^rpmXm0`@ku9b53aN zEXH@Y3JaztblgpxbJt{AtE1ad1Ca>{v$rwwvK(>{m~Gf_=-Ro7Fk{#;i~+{{>QtvI yb2P8Zac~?~=sRA>$6{!(^3;ZP0TPFR(G_-UDU(8Jl0?(IXu$~#4A!880|o%~Al1tN literal 0 HcmV?d00001 diff --git a/docs/_static/basic.css b/docs/_static/basic.css new file mode 100644 index 0000000..967e36c --- /dev/null +++ b/docs/_static/basic.css @@ -0,0 +1,537 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox input[type="text"] { + width: 170px; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + width: 30px; +} + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable dl, table.indextable dd { + margin-top: 0; + margin-bottom: 0; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- general body styles --------------------------------------------------- */ + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.field-list ul { + padding-left: 1em; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + border: 0; + border-collapse: collapse; +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.field-list td, table.field-list th { + border: 0 !important; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +dl { + margin-bottom: 15px; +} + +dd p { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dt:target, .highlighted { + background-color: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.optional { + font-size: 1.3em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +tt.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +tt.descclassname { + background-color: transparent; +} + +tt.xref, a tt { + background-color: transparent; + font-weight: bold; +} + +h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/docs/_static/comment-bright.png b/docs/_static/comment-bright.png new file mode 100644 index 0000000000000000000000000000000000000000..551517b8c83b76f734ff791f847829a760ad1903 GIT binary patch literal 3500 zcmV;d4O8-oP)Oz@Z0f2-7z;ux~O9+4z06=<WDR*FRcSTFz- zW=q650N5=6FiBTtNC2?60Km==3$g$R3;-}uh=nNt1bYBr$Ri_o0EC$U6h`t_Jn<{8 z5a%iY0C<_QJh>z}MS)ugEpZ1|S1ukX&Pf+56gFW3VVXcL!g-k)GJ!M?;PcD?0HBc- z5#WRK{dmp}uFlRjj{U%*%WZ25jX z{P*?XzTzZ-GF^d31o+^>%=Ap99M6&ogks$0k4OBs3;+Bb(;~!4V!2o<6ys46agIcq zjPo+3B8fthDa9qy|77CdEc*jK-!%ZRYCZvbku9iQV*~a}ClFY4z~c7+0P?$U!PF=S z1Au6Q;m>#f??3%Vpd|o+W=WE9003S@Bra6Svp>fO002awfhw>;8}z{#EWidF!3EsG z3;bXU&9EIRU@z1_9W=mEXoiz;4lcq~xDGvV5BgyU zp1~-*fe8db$Osc*A=-!mVv1NJjtCc-h4>-CNCXm#Bp}I%6j35eku^v$Qi@a{RY)E3 zJ#qp$hg?Rwkvqr$GJ^buyhkyVfwECO)C{#lxu`c9ghrwZ&}4KmnvWKso6vH!8a<3Q zq36)6Xb;+tK10Vaz~~qUGsJ8#F2=(`u{bOVlVi)VBCHIn#u~6ztOL7=^<&SmcLWlF zMZgI*1b0FpVIDz9SWH+>*hr`#93(Um+6gxa1B6k+CnA%mOSC4s5&6UzVlpv@SV$}* z))J2sFA#f(L&P^E5{W}HC%KRUNwK6<(h|}}(r!{C=`5+6G)NjFlgZj-YqAG9lq?`C z$c5yc>d>VnA`E_*3F2Qp##d8RZb=H01_mm@+|Cqnc9PsG(F5HIG_C zt)aG3uTh7n6Et<2In9F>NlT@zqLtGcXcuVrX|L#Xx)I%#9!{6gSJKPrN9dR61N3(c z4Tcqi$B1Vr8Jidf7-t!G7_XR2rWwr)$3XQ?}=hpK0&Z&W{| zep&sA23f;Q!%st`QJ}G3cbou<7-yIK2z4nfCCCtN2-XOGSWo##{8Q{ATurxr~;I`ytDs%xbip}RzP zziy}Qn4Z2~fSycmr`~zJ=lUFdFa1>gZThG6M+{g7vkW8#+YHVaJjFF}Z#*3@$J_By zLtVo_L#1JrVVB{Ak-5=4qt!-@Mh}c>#$4kh<88)m#-k<%CLtzEP3leVno>={htGUuD;o7bD)w_sX$S}eAxwzy?UvgBH(S?;#HZiQMoS*2K2 zT3xe7t(~nU*1N5{rxB;QPLocnp4Ml>u<^FZwyC!nu;thW+pe~4wtZn|Vi#w(#jeBd zlf9FDx_yoPJqHbk*$%56S{;6Kv~mM9!g3B(KJ}#RZ#@)!hR|78Dq|Iq-afF%KE1Brn_fm;Im z_u$xr8UFki1L{Ox>G0o)(&RAZ;=|I=wN2l97;cLaHH6leTB-XXa*h%dBOEvi`+x zi?=Txl?TadvyiL>SuF~-LZ;|cS}4~l2eM~nS7yJ>iOM;atDY;(?aZ^v+mJV$@1Ote z62cPUlD4IWOIIx&SmwQ~YB{nzae3Pc;}r!fhE@iwJh+OsDs9zItL;~pu715HdQEGA zUct(O!LkCy1<%NCg+}G`0PgpNm-?d@-hMgNe6^V+j6x$b<6@S<$+<4_1hi}Ti zncS4LsjI}fWY1>OX6feMEuLErma3QLmkw?X+1j)X-&VBk_4Y;EFPF_I+q;9dL%E~B zJh;4Nr^(LEJ3myURP{Rblsw%57T)g973R8o)DE9*xN#~;4_o$q%o z4K@u`jhx2fBXC4{U8Qn{*%*B$Ge=nny$HAYq{=vy|sI0 z_vss+H_qMky?OB#|JK!>IX&II^LlUh#rO5!7TtbwC;iULyV-Xq?ybB}ykGP{?LpZ? z-G|jbTmIbG@7#ZCz;~eY(cDM(28Dyq{*m>M4?_iynUBkc4TkHUI6gT!;y-fz>HMcd z&t%Ugo)`Y2{>!cx7B7DI)$7;J(U{Spm-3gBzioV_{p!H$8L!*M!p0uH$#^p{Ui4P` z?ZJ24cOCDe-w#jZd?0@)|7iKK^;6KN`;!@ylm7$*nDhK&GcDTy000JJOGiWi{{a60 z|De66lK=n!32;bRa{vGf6951U69E94oEQKA00(qQO+^RV2niQ93PPz|JOBU!-bqA3 zR5;6pl1pe^WfX zkSdl!omi0~*ntl;2q{jA^;J@WT8O!=A(Gck8fa>hn{#u{`Tyg)!KXI6l>4dj==iVKK6+%4zaRizy(5eryC3d2 z+5Y_D$4}k5v2=Siw{=O)SWY2HJwR3xX1*M*9G^XQ*TCNXF$Vj(kbMJXK0DaS_Sa^1 z?CEa!cFWDhcwxy%a?i@DN|G6-M#uuWU>lss@I>;$xmQ|`u3f;MQ|pYuHxxvMeq4TW;>|7Z2*AsqT=`-1O~nTm6O&pNEK?^cf9CX= zkq5|qAoE7un3V z^yy=@%6zqN^x`#qW+;e7j>th{6GV}sf*}g7{(R#T)yg-AZh0C&U;WA`AL$qz8()5^ zGFi2`g&L7!c?x+A2oOaG0c*Bg&YZt8cJ{jq_W{uTdA-<;`@iP$$=$H?gYIYc_q^*$ z#k(Key`d40R3?+GmgK8hHJcwiQ~r4By@w9*PuzR>x3#(F?YW_W5pPc(t(@-Y{psOt zz2!UE_5S)bLF)Oz@Z0f2-7z;ux~O9+4z06=<WDR*FRcSTFz- zW=q650N5=6FiBTtNC2?60Km==3$g$R3;-}uh=nNt1bYBr$Ri_o0EC$U6h`t_Jn<{8 z5a%iY0C<_QJh>z}MS)ugEpZ1|S1ukX&Pf+56gFW3VVXcL!g-k)GJ!M?;PcD?0HBc- z5#WRK{dmp}uFlRjj{U%*%WZ25jX z{P*?XzTzZ-GF^d31o+^>%=Ap99M6&ogks$0k4OBs3;+Bb(;~!4V!2o<6ys46agIcq zjPo+3B8fthDa9qy|77CdEc*jK-!%ZRYCZvbku9iQV*~a}ClFY4z~c7+0P?$U!PF=S z1Au6Q;m>#f??3%Vpd|o+W=WE9003S@Bra6Svp>fO002awfhw>;8}z{#EWidF!3EsG z3;bXU&9EIRU@z1_9W=mEXoiz;4lcq~xDGvV5BgyU zp1~-*fe8db$Osc*A=-!mVv1NJjtCc-h4>-CNCXm#Bp}I%6j35eku^v$Qi@a{RY)E3 zJ#qp$hg?Rwkvqr$GJ^buyhkyVfwECO)C{#lxu`c9ghrwZ&}4KmnvWKso6vH!8a<3Q zq36)6Xb;+tK10Vaz~~qUGsJ8#F2=(`u{bOVlVi)VBCHIn#u~6ztOL7=^<&SmcLWlF zMZgI*1b0FpVIDz9SWH+>*hr`#93(Um+6gxa1B6k+CnA%mOSC4s5&6UzVlpv@SV$}* z))J2sFA#f(L&P^E5{W}HC%KRUNwK6<(h|}}(r!{C=`5+6G)NjFlgZj-YqAG9lq?`C z$c5yc>d>VnA`E_*3F2Qp##d8RZb=H01_mm@+|Cqnc9PsG(F5HIG_C zt)aG3uTh7n6Et<2In9F>NlT@zqLtGcXcuVrX|L#Xx)I%#9!{6gSJKPrN9dR61N3(c z4Tcqi$B1Vr8Jidf7-t!G7_XR2rWwr)$3XQ?}=hpK0&Z&W{| zep&sA23f;Q!%st`QJ}G3cbou<7-yIK2z4nfCCCtN2-XOGSWo##{8Q{ATurxr~;I`ytDs%xbip}RzP zziy}Qn4Z2~fSycmr`~zJ=lUFdFa1>gZThG6M+{g7vkW8#+YHVaJjFF}Z#*3@$J_By zLtVo_L#1JrVVB{Ak-5=4qt!-@Mh}c>#$4kh<88)m#-k<%CLtzEP3leVno>={htGUuD;o7bD)w_sX$S}eAxwzy?UvgBH(S?;#HZiQMoS*2K2 zT3xe7t(~nU*1N5{rxB;QPLocnp4Ml>u<^FZwyC!nu;thW+pe~4wtZn|Vi#w(#jeBd zlf9FDx_yoPJqHbk*$%56S{;6Kv~mM9!g3B(KJ}#RZ#@)!hR|78Dq|Iq-afF%KE1Brn_fm;Im z_u$xr8UFki1L{Ox>G0o)(&RAZ;=|I=wN2l97;cLaHH6leTB-XXa*h%dBOEvi`+x zi?=Txl?TadvyiL>SuF~-LZ;|cS}4~l2eM~nS7yJ>iOM;atDY;(?aZ^v+mJV$@1Ote z62cPUlD4IWOIIx&SmwQ~YB{nzae3Pc;}r!fhE@iwJh+OsDs9zItL;~pu715HdQEGA zUct(O!LkCy1<%NCg+}G`0PgpNm-?d@-hMgNe6^V+j6x$b<6@S<$+<4_1hi}Ti zncS4LsjI}fWY1>OX6feMEuLErma3QLmkw?X+1j)X-&VBk_4Y;EFPF_I+q;9dL%E~B zJh;4Nr^(LEJ3myURP{Rblsw%57T)g973R8o)DE9*xN#~;4_o$q%o z4K@u`jhx2fBXC4{U8Qn{*%*B$Ge=nny$HAYq{=vy|sI0 z_vss+H_qMky?OB#|JK!>IX&II^LlUh#rO5!7TtbwC;iULyV-Xq?ybB}ykGP{?LpZ? z-G|jbTmIbG@7#ZCz;~eY(cDM(28Dyq{*m>M4?_iynUBkc4TkHUI6gT!;y-fz>HMcd z&t%Ugo)`Y2{>!cx7B7DI)$7;J(U{Spm-3gBzioV_{p!H$8L!*M!p0uH$#^p{Ui4P` z?ZJ24cOCDe-w#jZd?0@)|7iKK^;6KN`;!@ylm7$*nDhK&GcDTy000JJOGiWi{{a60 z|De66lK=n!32;bRa{vGf6951U69E94oEQKA00(qQO+^RV2oe()A>y0J-2easEJ;K` zR5;6Jl3z%jbr{D#&+mQTbB>-f&3W<<%ayjKi&ZjBc2N<@)`~{dMXWB0(ajbV85_gJ zf(EU`iek}4Bt%55ix|sVMm1u8KvB#hnmU~_r<Ogd(A5vg_omvd-#L!=(BMVklxVqhdT zofSj`QA^|)G*lu58>#vhvA)%0Or&dIsb%b)st*LV8`ANnOipDbh%_*c7`d6# z21*z~Xd?ovgf>zq(o0?Et~9ti+pljZC~#_KvJhA>u91WRaq|uqBBKP6V0?p-NL59w zrK0w($_m#SDPQ!Z$nhd^JO|f+7k5xca94d2OLJ&sSxlB7F%NtrF@@O7WWlkHSDtor zzD?u;b&KN$*MnHx;JDy9P~G<{4}9__s&MATBV4R+MuA8TjlZ3ye&qZMCUe8ihBnHI zhMSu zSERHwrmBb$SWVr+)Yk2k^FgTMR6mP;@FY2{}BeV|SUo=mNk<-XSOHNErw>s{^rR-bu$@aN7= zj~-qXcS2!BA*(Q**BOOl{FggkyHdCJi_Fy>?_K+G+DYwIn8`29DYPg&s4$}7D`fv? zuyJ2sMfJX(I^yrf6u!(~9anf(AqAk&ke}uL0SIb-H!SaDQvd(}07*qoM6N<$g1Ha7 A2LJ#7 literal 0 HcmV?d00001 diff --git a/docs/_static/comment.png b/docs/_static/comment.png new file mode 100644 index 0000000000000000000000000000000000000000..92feb52b8824c6b0f59b658b1196c61de9162a95 GIT binary patch literal 3445 zcmV-*4T|!KP)Oz@Z0f2-7z;ux~O9+4z06=<WDR*FRcSTFz- zW=q650N5=6FiBTtNC2?60Km==3$g$R3;-}uh=nNt1bYBr$Ri_o0EC$U6h`t_Jn<{8 z5a%iY0C<_QJh>z}MS)ugEpZ1|S1ukX&Pf+56gFW3VVXcL!g-k)GJ!M?;PcD?0HBc- z5#WRK{dmp}uFlRjj{U%*%WZ25jX z{P*?XzTzZ-GF^d31o+^>%=Ap99M6&ogks$0k4OBs3;+Bb(;~!4V!2o<6ys46agIcq zjPo+3B8fthDa9qy|77CdEc*jK-!%ZRYCZvbku9iQV*~a}ClFY4z~c7+0P?$U!PF=S z1Au6Q;m>#f??3%Vpd|o+W=WE9003S@Bra6Svp>fO002awfhw>;8}z{#EWidF!3EsG z3;bXU&9EIRU@z1_9W=mEXoiz;4lcq~xDGvV5BgyU zp1~-*fe8db$Osc*A=-!mVv1NJjtCc-h4>-CNCXm#Bp}I%6j35eku^v$Qi@a{RY)E3 zJ#qp$hg?Rwkvqr$GJ^buyhkyVfwECO)C{#lxu`c9ghrwZ&}4KmnvWKso6vH!8a<3Q zq36)6Xb;+tK10Vaz~~qUGsJ8#F2=(`u{bOVlVi)VBCHIn#u~6ztOL7=^<&SmcLWlF zMZgI*1b0FpVIDz9SWH+>*hr`#93(Um+6gxa1B6k+CnA%mOSC4s5&6UzVlpv@SV$}* z))J2sFA#f(L&P^E5{W}HC%KRUNwK6<(h|}}(r!{C=`5+6G)NjFlgZj-YqAG9lq?`C z$c5yc>d>VnA`E_*3F2Qp##d8RZb=H01_mm@+|Cqnc9PsG(F5HIG_C zt)aG3uTh7n6Et<2In9F>NlT@zqLtGcXcuVrX|L#Xx)I%#9!{6gSJKPrN9dR61N3(c z4Tcqi$B1Vr8Jidf7-t!G7_XR2rWwr)$3XQ?}=hpK0&Z&W{| zep&sA23f;Q!%st`QJ}G3cbou<7-yIK2z4nfCCCtN2-XOGSWo##{8Q{ATurxr~;I`ytDs%xbip}RzP zziy}Qn4Z2~fSycmr`~zJ=lUFdFa1>gZThG6M+{g7vkW8#+YHVaJjFF}Z#*3@$J_By zLtVo_L#1JrVVB{Ak-5=4qt!-@Mh}c>#$4kh<88)m#-k<%CLtzEP3leVno>={htGUuD;o7bD)w_sX$S}eAxwzy?UvgBH(S?;#HZiQMoS*2K2 zT3xe7t(~nU*1N5{rxB;QPLocnp4Ml>u<^FZwyC!nu;thW+pe~4wtZn|Vi#w(#jeBd zlf9FDx_yoPJqHbk*$%56S{;6Kv~mM9!g3B(KJ}#RZ#@)!hR|78Dq|Iq-afF%KE1Brn_fm;Im z_u$xr8UFki1L{Ox>G0o)(&RAZ;=|I=wN2l97;cLaHH6leTB-XXa*h%dBOEvi`+x zi?=Txl?TadvyiL>SuF~-LZ;|cS}4~l2eM~nS7yJ>iOM;atDY;(?aZ^v+mJV$@1Ote z62cPUlD4IWOIIx&SmwQ~YB{nzae3Pc;}r!fhE@iwJh+OsDs9zItL;~pu715HdQEGA zUct(O!LkCy1<%NCg+}G`0PgpNm-?d@-hMgNe6^V+j6x$b<6@S<$+<4_1hi}Ti zncS4LsjI}fWY1>OX6feMEuLErma3QLmkw?X+1j)X-&VBk_4Y;EFPF_I+q;9dL%E~B zJh;4Nr^(LEJ3myURP{Rblsw%57T)g973R8o)DE9*xN#~;4_o$q%o z4K@u`jhx2fBXC4{U8Qn{*%*B$Ge=nny$HAYq{=vy|sI0 z_vss+H_qMky?OB#|JK!>IX&II^LlUh#rO5!7TtbwC;iULyV-Xq?ybB}ykGP{?LpZ? z-G|jbTmIbG@7#ZCz;~eY(cDM(28Dyq{*m>M4?_iynUBkc4TkHUI6gT!;y-fz>HMcd z&t%Ugo)`Y2{>!cx7B7DI)$7;J(U{Spm-3gBzioV_{p!H$8L!*M!p0uH$#^p{Ui4P` z?ZJ24cOCDe-w#jZd?0@)|7iKK^;6KN`;!@ylm7$*nDhK&GcDTy000JJOGiWi{{a60 z|De66lK=n!32;bRa{vGf6951U69E94oEQKA00(qQO+^RV2nzr)JMUJvzW@LNr%6OX zR5;6Zk;`k`RTRfR-*ac2G}PGmXsUu>6ce?Lsn$m^3Q`48f|TwQ+_-Qh=t8Ra7nE)y zf@08(pjZ@22^EVjG*%30TJRMkBUC$WqZ73uoiv&J=APqX;!v%AH}`Vx`999MVjXwy z{f1-vh8P<=plv&cZ>p5jjX~Vt&W0e)wpw1RFRuRdDkwlKb01tp5 zP=trFN0gH^|L4jJkB{6sCV;Q!ewpg-D&4cza%GQ*b>R*=34#dW;ek`FEiB(vnw+U# zpOX5UMJBhIN&;D1!yQoIAySC!9zqJmmfoJqmQp}p&h*HTfMh~u9rKic2oz3sNM^#F zBIq*MRLbsMt%y{EHj8}LeqUUvoxf0=kqji62>ne+U`d#%J)abyK&Y`=eD%oA!36<)baZyK zXJh5im6umkS|_CSGXips$nI)oBHXojzBzyY_M5K*uvb0_9viuBVyV%5VtJ*Am1ag# zczbv4B?u8j68iOz<+)nDu^oWnL+$_G{PZOCcOGQ?!1VCefves~rfpaEZs-PdVYMiV z98ElaJ2}7f;htSXFY#Zv?__sQeckE^HV{ItO=)2hMQs=(_ Xn!ZpXD%P(H00000NkvXXu0mjf= 0 && !jQuery(node.parentNode).hasClass(className)) { + var span = document.createElement("span"); + span.className = className; + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this); + }); + } + } + return this.each(function() { + highlight(this); + }); +}; + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated == 'undefined') + return string; + return (typeof translated == 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated == 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash && $.browser.mozilla) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) == 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this == '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); diff --git a/docs/_static/down-pressed.png b/docs/_static/down-pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..6f7ad782782e4f8e39b0c6e15c7344700cdd2527 GIT binary patch literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*yM+OLB!qm#z$3ZNi+iKnkC`z>}Z23@f-Ava~9&<9T!#}JFtXD=!G zGdl{fK6ro2OGiOl+hKvH6i=D3%%Y^j`yIkRn!8O>@bG)IQR0{Kf+mxNd=_WScA8u_ z3;8(7x2){m9`nt+U(Nab&1G)!{`SPVpDX$w8McLTzAJ39wprG3p4XLq$06M`%}2Yk zRPPsbES*dnYm1wkGL;iioAUB*Or2kz6(-M_r_#Me-`{mj$Z%( literal 0 HcmV?d00001 diff --git a/docs/_static/down.png b/docs/_static/down.png new file mode 100644 index 0000000000000000000000000000000000000000..3003a88770de3977d47a2ba69893436a2860f9e7 GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*yM+OLB!qm#z$3ZNi+iKnkC`z>}xaV3tUZ$qnrLa#kt978NlpS`ru z&)HFc^}^>{UOEce+71h5nn>6&w6A!ieNbu1wh)UGh{8~et^#oZ1# z>T7oM=FZ~xXWnTo{qnXm$ZLOlqGswI_m2{XwVK)IJmBjW{J3-B3x@C=M{ShWt#fYS9M?R;8K$~YwlIqwf>VA7q=YKcwf2DS4Zj5inDKXXB1zl=(YO3ST6~rDq)&z z*o>z)=hxrfG-cDBW0G$!?6{M<$@{_4{m1o%Ub!naEtn|@^frU1tDnm{r-UW|!^@B8 literal 0 HcmV?d00001 diff --git a/docs/_static/file.png b/docs/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..d18082e397e7e54f20721af768c4c2983258f1b4 GIT binary patch literal 392 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP$HyOL$D9)yc9|lc|nKf<9@eUiWd>3GuTC!a5vdfWYEazjncPj5ZQX%+1 zt8B*4=d)!cdDz4wr^#OMYfqGz$1LDFF>|#>*O?AGil(WEs?wLLy{Gj2J_@opDm%`dlax3yA*@*N$G&*ukFv>P8+2CBWO(qz zD0k1@kN>hhb1_6`&wrCswzINE(evt-5C1B^STi2@PmdKI;Vst0PQB6!2kdN literal 0 HcmV?d00001 diff --git a/docs/_static/jquery.js b/docs/_static/jquery.js new file mode 100644 index 0000000..83589da --- /dev/null +++ b/docs/_static/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v1.8.3 jquery.com | jquery.org/license */ +(function(e,t){function _(e){var t=M[e]={};return v.each(e.split(y),function(e,n){t[n]=!0}),t}function H(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(P,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:+r+""===r?+r:D.test(r)?v.parseJSON(r):r}catch(s){}v.data(e,n,r)}else r=t}return r}function B(e){var t;for(t in e){if(t==="data"&&v.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function et(){return!1}function tt(){return!0}function ut(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function at(e,t){do e=e[t];while(e&&e.nodeType!==1);return e}function ft(e,t,n){t=t||0;if(v.isFunction(t))return v.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return v.grep(e,function(e,r){return e===t===n});if(typeof t=="string"){var r=v.grep(e,function(e){return e.nodeType===1});if(it.test(t))return v.filter(t,r,!n);t=v.filter(t,r)}return v.grep(e,function(e,r){return v.inArray(e,t)>=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write(""),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t
a",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="
t
",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="
",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;ti.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="
",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="

",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t0)for(i=r;i=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*\s*$/g,Nt={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X
","
"]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1>");try{for(;r1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]===""&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("
").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window); \ No newline at end of file diff --git a/docs/_static/minus.png b/docs/_static/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..da1c5620d10c047525a467a425abe9ff5269cfc2 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1SHkYJtzcHoCO|{#XvD(5N2eUHAey{$X?>< z>&kweokM_|(Po{+Q=kw>iEBiObAE1aYF-J$w=>iB1I2R$WLpMkF=>bh=@O1TaS?83{1OVknK< z>&kweokM`jkU7Va11Q8%;u=xnoS&PUnpeW`?aZ|OK(QcC7sn8Z%gHvy&v=;Q4jejg zV8NnAO`-4Z@2~&zopr02WF_WB>pF literal 0 HcmV?d00001 diff --git a/docs/_static/pygments.css b/docs/_static/pygments.css new file mode 100644 index 0000000..d79caa1 --- /dev/null +++ b/docs/_static/pygments.css @@ -0,0 +1,62 @@ +.highlight .hll { background-color: #ffffcc } +.highlight { background: #eeffcc; } +.highlight .c { color: #408090; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #007020; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #007020 } /* Comment.Preproc */ +.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #333333 } /* Generic.Output */ +.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #007020 } /* Keyword.Pseudo */ +.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #902000 } /* Keyword.Type */ +.highlight .m { color: #208050 } /* Literal.Number */ +.highlight .s { color: #4070a0 } /* Literal.String */ +.highlight .na { color: #4070a0 } /* Name.Attribute */ +.highlight .nb { color: #007020 } /* Name.Builtin */ +.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ +.highlight .no { color: #60add5 } /* Name.Constant */ +.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ +.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #007020 } /* Name.Exception */ +.highlight .nf { color: #06287e } /* Name.Function */ +.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ +.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #bb60d5 } /* Name.Variable */ +.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #208050 } /* Literal.Number.Float */ +.highlight .mh { color: #208050 } /* Literal.Number.Hex */ +.highlight .mi { color: #208050 } /* Literal.Number.Integer */ +.highlight .mo { color: #208050 } /* Literal.Number.Oct */ +.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ +.highlight .sc { color: #4070a0 } /* Literal.String.Char */ +.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4070a0 } /* Literal.String.Double */ +.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ +.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ +.highlight .sx { color: #c65d09 } /* Literal.String.Other */ +.highlight .sr { color: #235388 } /* Literal.String.Regex */ +.highlight .s1 { color: #4070a0 } /* Literal.String.Single */ +.highlight .ss { color: #517918 } /* Literal.String.Symbol */ +.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ +.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ +.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ +.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/_static/searchtools.js b/docs/_static/searchtools.js new file mode 100644 index 0000000..6e1f06b --- /dev/null +++ b/docs/_static/searchtools.js @@ -0,0 +1,622 @@ +/* + * searchtools.js_t + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilties for the full-text search. + * + * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + + + +/** + * Simple result scoring code. + */ +var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [filename, title, anchor, descr, score] + // and returns the new score. + /* + score: function(result) { + return result[4]; + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: {0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5}, // used to be unimportantResults + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + // query found in terms + term: 5 +}; + + +/** + * Search Module + */ +var Search = { + + _index : null, + _queued_query : null, + _pulse_status : -1, + + init : function() { + var params = $.getQueryParameters(); + if (params.q) { + var query = params.q[0]; + $('input[name="q"]')[0].value = query; + this.performSearch(query); + } + }, + + loadIndex : function(url) { + $.ajax({type: "GET", url: url, data: null, + dataType: "script", cache: true, + complete: function(jqxhr, textstatus) { + if (textstatus != "success") { + document.getElementById("searchindexloader").src = url; + } + }}); + }, + + setIndex : function(index) { + var q; + this._index = index; + if ((q = this._queued_query) !== null) { + this._queued_query = null; + Search.query(q); + } + }, + + hasIndex : function() { + return this._index !== null; + }, + + deferQuery : function(query) { + this._queued_query = query; + }, + + stopPulse : function() { + this._pulse_status = 0; + }, + + startPulse : function() { + if (this._pulse_status >= 0) + return; + function pulse() { + var i; + Search._pulse_status = (Search._pulse_status + 1) % 4; + var dotString = ''; + for (i = 0; i < Search._pulse_status; i++) + dotString += '.'; + Search.dots.text(dotString); + if (Search._pulse_status > -1) + window.setTimeout(pulse, 500); + } + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch : function(query) { + // create the required interface elements + this.out = $('#search-results'); + this.title = $('

' + _('Searching') + '

').appendTo(this.out); + this.dots = $('').appendTo(this.title); + this.status = $('

').appendTo(this.out); + this.output = $('
'); + } + // Prettify the comment rating. + comment.pretty_rating = comment.rating + ' point' + + (comment.rating == 1 ? '' : 's'); + // Make a class (for displaying not yet moderated comments differently) + comment.css_class = comment.displayed ? '' : ' moderate'; + // Create a div for this comment. + var context = $.extend({}, opts, comment); + var div = $(renderTemplate(commentTemplate, context)); + + // If the user has voted on this comment, highlight the correct arrow. + if (comment.vote) { + var direction = (comment.vote == 1) ? 'u' : 'd'; + div.find('#' + direction + 'v' + comment.id).hide(); + div.find('#' + direction + 'u' + comment.id).show(); + } + + if (opts.moderator || comment.text != '[deleted]') { + div.find('a.reply').show(); + if (comment.proposal_diff) + div.find('#sp' + comment.id).show(); + if (opts.moderator && !comment.displayed) + div.find('#cm' + comment.id).show(); + if (opts.moderator || (opts.username == comment.username)) + div.find('#dc' + comment.id).show(); + } + return div; + } + + /** + * A simple template renderer. Placeholders such as <%id%> are replaced + * by context['id'] with items being escaped. Placeholders such as <#id#> + * are not escaped. + */ + function renderTemplate(template, context) { + var esc = $(document.createElement('div')); + + function handle(ph, escape) { + var cur = context; + $.each(ph.split('.'), function() { + cur = cur[this]; + }); + return escape ? esc.text(cur || "").html() : cur; + } + + return template.replace(/<([%#])([\w\.]*)\1>/g, function() { + return handle(arguments[2], arguments[1] == '%' ? true : false); + }); + } + + /** Flash an error message briefly. */ + function showError(message) { + $(document.createElement('div')).attr({'class': 'popup-error'}) + .append($(document.createElement('div')) + .attr({'class': 'error-message'}).text(message)) + .appendTo('body') + .fadeIn("slow") + .delay(2000) + .fadeOut("slow"); + } + + /** Add a link the user uses to open the comments popup. */ + $.fn.comment = function() { + return this.each(function() { + var id = $(this).attr('id').substring(1); + var count = COMMENT_METADATA[id]; + var title = count + ' comment' + (count == 1 ? '' : 's'); + var image = count > 0 ? opts.commentBrightImage : opts.commentImage; + var addcls = count == 0 ? ' nocomment' : ''; + $(this) + .append( + $(document.createElement('a')).attr({ + href: '#', + 'class': 'sphinx-comment-open' + addcls, + id: 'ao' + id + }) + .append($(document.createElement('img')).attr({ + src: image, + alt: 'comment', + title: title + })) + .click(function(event) { + event.preventDefault(); + show($(this).attr('id').substring(2)); + }) + ) + .append( + $(document.createElement('a')).attr({ + href: '#', + 'class': 'sphinx-comment-close hidden', + id: 'ah' + id + }) + .append($(document.createElement('img')).attr({ + src: opts.closeCommentImage, + alt: 'close', + title: 'close' + })) + .click(function(event) { + event.preventDefault(); + hide($(this).attr('id').substring(2)); + }) + ); + }); + }; + + var opts = { + processVoteURL: '/_process_vote', + addCommentURL: '/_add_comment', + getCommentsURL: '/_get_comments', + acceptCommentURL: '/_accept_comment', + deleteCommentURL: '/_delete_comment', + commentImage: '/static/_static/comment.png', + closeCommentImage: '/static/_static/comment-close.png', + loadingImage: '/static/_static/ajax-loader.gif', + commentBrightImage: '/static/_static/comment-bright.png', + upArrow: '/static/_static/up.png', + downArrow: '/static/_static/down.png', + upArrowPressed: '/static/_static/up-pressed.png', + downArrowPressed: '/static/_static/down-pressed.png', + voting: false, + moderator: false + }; + + if (typeof COMMENT_OPTIONS != "undefined") { + opts = jQuery.extend(opts, COMMENT_OPTIONS); + } + + var popupTemplate = '\ +
\ +

\ + Sort by:\ + best rated\ + newest\ + oldest\ +

\ +
Comments
\ +
\ + loading comments...
\ +
    \ +
    \ +

    Add a comment\ + (markup):

    \ +
    \ + reStructured text markup: *emph*, **strong**, \ + ``code``, \ + code blocks: :: and an indented block after blank line
    \ +
    \ + \ +

    \ + \ + Propose a change ▹\ + \ + \ + Propose a change ▿\ + \ +

    \ + \ + \ + \ + \ + \ +
    \ +
    '; + + var commentTemplate = '\ +
    \ +
    \ +
    \ + \ + \ + \ + \ + \ + \ +
    \ +
    \ + \ + \ + \ + \ + \ + \ +
    \ +
    \ +
    \ +

    \ + <%username%>\ + <%pretty_rating%>\ + <%time.delta%>\ +

    \ +
    <#text#>
    \ +

    \ + \ + reply ▿\ + proposal ▹\ + proposal ▿\ + \ + \ +

    \ +
    \
    +<#proposal_diff#>\
    +        
    \ +
      \ +
      \ +
      \ +
      \ + '; + + var replyTemplate = '\ +
    • \ +
      \ +
      \ + \ + \ + \ + \ + \ + \ +
      \ +
    • '; + + $(document).ready(function() { + init(); + }); +})(jQuery); + +$(document).ready(function() { + // add comment anchors for all paragraphs that are commentable + $('.sphinx-has-comment').comment(); + + // highlight search words in search results + $("div.context").each(function() { + var params = $.getQueryParameters(); + var terms = (params.q) ? params.q[0].split(/\s+/) : []; + var result = $(this); + $.each(terms, function() { + result.highlightText(this.toLowerCase(), 'highlighted'); + }); + }); + + // directly open comment window if requested + var anchor = document.location.hash; + if (anchor.substring(0, 9) == '#comment-') { + $('#ao' + anchor.substring(9)).click(); + document.location.hash = '#s' + anchor.substring(9); + } +}); diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..2edd68a --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# WEM documentation build configuration file, created by +# sphinx-quickstart on Sat Jul 12 01:40:57 2014. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.pngmath', + 'sphinx.ext.viewcode', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'WEM' +copyright = '2014, John Lawson' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.0.1' +# The full version, including alpha/beta/rc tags. +release = '0.0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'WEMdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'WEM.tex', 'WEM Documentation', + 'John Lawson', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'wem', 'WEM Documentation', + ['John Lawson'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'WEM', 'WEM Documentation', + 'John Lawson', 'WEM', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/docs/genindex.html b/docs/genindex.html new file mode 100644 index 0000000..cd5ad84 --- /dev/null +++ b/docs/genindex.html @@ -0,0 +1,92 @@ + + + + + + + + + Index — WEM 0.0.1 documentation + + + + + + + + + + + + + +
      +
      +
      +
      + + +

      Index

      + +
      + +
      + + +
      +
      +
      +
      +
      + + + + + +
      +
      +
      +
      + + + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..9e65d44 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,119 @@ + + + + + + + + Welcome to WEM’s documentation! — WEM 0.0.1 documentation + + + + + + + + + + + + + + +
      +
      +
      +
      + +
      +

      Welcome to WEM’s documentation!¶

      +

      Contents:

      +
      +
        +
      +
      +
      +
      +

      Indices and tables¶

      + +
      + + +
      +
      +
      +
      +
      +

      Table Of Contents

      + + +

      Next topic

      +

      <no title>

      +

      This Page

      + + + +
      +
      +
      +
      + + + + \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..90509b2 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,25 @@ +.. WEM documentation master file, created by + sphinx-quickstart on Sat Jul 12 01:40:57 2014. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to WEM's documentation! +=============================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + intro + lazywrf + postwrf + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/docs/intro.html b/docs/intro.html new file mode 100644 index 0000000..e73c9a0 --- /dev/null +++ b/docs/intro.html @@ -0,0 +1,107 @@ + + + + + + + + <no title> — WEM 0.0.1 documentation + + + + + + + + + + + + + + + +
      +
      +
      +
      + + + +
      +
      +
      +
      +
      +

      Previous topic

      +

      Welcome to WEM’s documentation!

      +

      Next topic

      +

      <no title>

      +

      This Page

      + + + +
      +
      +
      +
      + + + + \ No newline at end of file diff --git a/docs/intro.rst b/docs/intro.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/lazywrf.html b/docs/lazywrf.html new file mode 100644 index 0000000..1d284dd --- /dev/null +++ b/docs/lazywrf.html @@ -0,0 +1,107 @@ + + + + + + + + <no title> — WEM 0.0.1 documentation + + + + + + + + + + + + + + + +
      +
      +
      +
      + + + +
      +
      +
      +
      +
      +

      Previous topic

      +

      <no title>

      +

      Next topic

      +

      <no title>

      +

      This Page

      + + + +
      +
      +
      +
      + + + + \ No newline at end of file diff --git a/docs/lazywrf.rst b/docs/lazywrf.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/postwrf.html b/docs/postwrf.html new file mode 100644 index 0000000..f2a04d5 --- /dev/null +++ b/docs/postwrf.html @@ -0,0 +1,97 @@ + + + + + + + + <no title> — WEM 0.0.1 documentation + + + + + + + + + + + + + + +
      +
      +
      +
      + + + +
      +
      +
      +
      +
      +

      Previous topic

      +

      <no title>

      +

      This Page

      + + + +
      +
      +
      +
      + + + + \ No newline at end of file diff --git a/docs/postwrf.rst b/docs/postwrf.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/search.html b/docs/search.html new file mode 100644 index 0000000..5d3cf48 --- /dev/null +++ b/docs/search.html @@ -0,0 +1,99 @@ + + + + + + + + Search — WEM 0.0.1 documentation + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      + +

      Search

      +
      + +

      + Please activate JavaScript to enable the search + functionality. +

      +
      +

      + From here you can search these documents. Enter your search + words into the box below and click "search". Note that the search + function will automatically search for all of the words. Pages + containing fewer words won't appear in the result list. +

      +
      + + + + + +
      + +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      + + + + \ No newline at end of file diff --git a/ensemble.txt b/ensemble.txt deleted file mode 100644 index d17cf70..0000000 --- a/ensemble.txt +++ /dev/null @@ -1,455 +0,0 @@ - -ENSEMBLE MOS FORECASTS -

      MOS FORECASTS

      -
      -Control
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  3  30| 14  25| 10  28| 17  44| 26  41| 16  33| 13  30 23 41
      - TMP  15|  6  27| 15  21| 11  25| 20  41| 28  37| 18  30| 15  28      
      - DPT   1| -1  14| 10   9|  6  11| 16  28| 24  27| 14  18| 10  16      
      - CLD  PC| PC  OV| OV  OV| PC  PC| PC  PC| OV  OV| OV  PC| PC  PC      
      - P12   4|  0  13| 26  10|  8  10| 11  16| 14  21| 25  14| 12  14999999
      - P24    |     21|     26|     12|     28|     33|     25|     17   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      0|      0|      0|      0|       |             
      - PZP   9|  3  14| 11  16|  2  14| 11  29| 15  27| 10  25| 16  26      
      - PSN  91| 97  85| 83  84| 96  85| 85  46| 24  36| 67  70| 68  64      
      - PRS   0|  0   1|  6   0|  0   0|  0   1| 13   6| 11   2|  2   0      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 1
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  4  29| 12  25| 11  31| 20  46| 25  38| 14  31| 14  35 23 41
      - TMP  15|  7  27| 14  21| 12  28| 22  43| 27  34| 16  29| 17  32      
      - DPT   1| -1  13|  9   8|  8  15| 19  30| 23  24| 12  17| 12  21      
      - CLD  PC| PC  OV| OV  OV| PC  PC| PC  OV| PC  OV| OV  PC| OV  PC      
      - P12   3|  0  18| 17   7|  6  10| 18  12| 16  15| 19  13| 11  16999999
      - P24    |     24|     17|     15|     22|     25|     21|     17   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      0|      0|      0|      0|       |             
      - PZP   9|  4  15|  8  14|  2  15| 14  31| 18  29| 14  25| 19  27      
      - PSN  91| 96  83| 87  86| 92  84| 76  43| 28  50| 59  65| 68  58      
      - PRS   0|  0   3|  5   0|  0   2|  2   2| 14   5| 11   1|  0   1      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  Z   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 2
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  1  29| 14  26| 13  32| 24  44| 23  42| 21  31|  9  30 23 41
      - TMP  14|  5  27| 16  22| 14  30| 27  41| 25  37| 23  29| 11  26      
      - DPT   1| -3  13| 11  10| 10  18| 23  30| 21  25| 20  21|  6  10      
      - CLD  PC| PC  OV| OV  OV| OV  OV| PC  OV| PC  PC| OV  OV| OV  CL      
      - P12   3|  0  18| 23  12| 11  16| 23  17| 16  10| 25  27| 11   8999999
      - P24    |     23|     23|     17|     39|     22|     37|     15   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      0|      0|      1|      0|       |             
      - PZP   9|  3  13|  6  14|  3  16| 14  26| 15  26| 15  29|  8  25      
      - PSN  91| 97  86| 89  86| 95  78| 53  38| 35  48| 36  58| 80  66      
      - PRS   0|  0   1|  5   0|  0   0|  1   5| 13   5|  8   5|  2   0      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 3
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  4  29| 12  24| 11  32| 21  48| 28  39| 16  28| 10  34 23 41
      - TMP  15|  7  27| 13  20| 13  29| 24  44| 30  35| 18  26| 12  31      
      - DPT   1|  0  13|  8   7|  8  17| 20  32| 26  27| 13  15|  7  17      
      - CLD  PC| PC  OV| OV  OV| PC  PC| PC  PC| OV  OV| OV  PC| PC  CL      
      - P12   4|  0  25| 11   7|  4  11| 16   9| 15  29| 30  14| 11   8999999
      - P24    |     35|     16|     11|     19|     37|     30|     15   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   1|  1    |             
      - Q24    |      0|      0|      0|      0|      1|       |             
      - PZP   9|  3  13|  7  13|  3  14| 18  36| 13  29| 12  25|  8  26      
      - PSN  91| 97  85| 88  87| 96  85| 65  37| 22  38| 71  74| 81  64      
      - PRS   0|  0   2|  4   0|  0   1|  0   0| 14   8| 11   1|  2   1      
      - TYP   S|  S   S|  S   S|  S   S|  Z   Z| RS   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 4
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  17|  3  31| 12  24| 11  31| 20  43| 22  36| 13  29| 12  38 23 41
      - TMP  15|  7  28| 13  20| 12  28| 22  40| 24  32| 15  27| 14  35      
      - DPT   1| -1  14|  8   7|  8  15| 19  28| 19  22| 11  16| 10  23      
      - CLD  PC| PC  OV| OV  PC| OV  OV| OV  PC| OV  OV| OV  PC| PC  PC      
      - P12   3|  0  14| 17   5|  7  16| 21  17| 10  20| 13  15| 11  12999999
      - P24    |     14|     17|     18|     31|     31|     21|     16   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      0|      0|      0|      0|       |             
      - PZP   9|  4  15|  9  13|  3  14| 15  32| 17  29| 16  29| 16  26      
      - PSN  91| 96  83| 86  87| 97  85| 73  45| 36  64| 69  68| 72  56      
      - PRS   0|  0   2|  6   0|  0   1|  0   4| 14   2|  7   0|  0   2      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 5
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  3  32| 17  28| 12  31| 21  46| 28  44| 24  36| 16  32 23 41
      - TMP  15|  6  30| 18  23| 14  28| 23  43| 30  40| 26  33| 18  29      
      - DPT   1| -1  15| 14  12|  9  16| 20  31| 26  30| 22  23| 14  17      
      - CLD  CL| PC  OV| OV  OV| PC  PC| PC  OV| PC  OV| OV  PC| OV  PC      
      - P12   3|  0  10| 25  11|  7  11| 15  10| 16  15| 25  15| 11  12999999
      - P24    |     10|     25|     14|     20|     26|     29|     22   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      0|      0|      0|      0|       |             
      - PZP   9|  3  14| 14  20|  2  15| 17  36| 15  24| 11  26|  9  26      
      - PSN  91| 97  84| 78  79| 93  83| 70  42| 23  31| 48  56| 70  65      
      - PRS   0|  0   2|  8   1|  0   2|  0   0| 15   4| 15   8|  6   2      
      - TYP   S|  S   S|  S   S|  S   S|  Z   Z| RS   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 6
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  3  28| 12  24|  9  28| 17  41| 20  37| 16  36| 13  38 23 41
      - TMP  14|  6  25| 13  20| 10  25| 20  38| 22  33| 18  33| 15  34      
      - DPT   1| -1  12|  8   8|  6  11| 16  25| 17  23| 14  21| 10  19      
      - CLD  PC| PC  OV| OV  OV| PC  PC| PC  OV| OV  OV| OV  PC| PC  PC      
      - P12   3|  0  15| 40  10|  7  10| 15  17| 14  19| 18  19| 11   8999999
      - P24    |     18|     40|     17|     30|     29|     25|     14   999
      - Q12   0|  0   0|  1   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      0|      0|      0|      0|       |             
      - PZP   9|  3  13|  7  13|  3  14| 13  32| 19  33| 14  25| 10  25      
      - PSN  91| 97  86| 91  87| 97  86| 73  47| 41  58| 72  69| 80  69      
      - PRS   0|  0   1|  3   0|  0   0|  0   2| 12   3|  5   0|  1   0      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 7
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  3  28| 13  25|  8  24| 12  39| 22  37| 14  30| 11  25 23 41
      - TMP  14|  6  26| 14  21|  9  21| 14  36| 24  33| 17  27| 13  22      
      - DPT   1| -1  12| 10   9|  4   7| 10  22| 19  23| 12  15|  8  11      
      - CLD  PC| PC  OV| OV  OV| OV  PC| CL  PC| PC  OV| OV  PC| OV  PC      
      - P12   5|  0  16| 15   9|  9   9|  7   9| 18  14| 18  17| 12  15999999
      - P24    |     25|     17|     10|     15|     23|     26|     21   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      0|      0|      0|      0|       |             
      - PZP   9|  3  13|  8  16|  3  14|  5  21| 12  27| 10  25|  9  26      
      - PSN  91| 97  87| 89  84| 97  86| 95  63| 43  58| 78  72| 77  69      
      - PRS   0|  0   1|  3   0|  0   0|  0   4|  9   6|  5   1|  3   0      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z|  S   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 8
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  2  28| 13  25|  9  26| 14  40| 22  37| 14  35| 14  36 23 41
      - TMP  15|  5  25| 14  20| 10  23| 17  37| 24  33| 16  32| 16  33      
      - DPT   1| -2  11|  9   9|  6  10| 13  25| 19  23| 12  19| 11  20      
      - CLD  PC| PC  OV| OV  OV| OV  PC| PC  PC| OV  OV| OV  PC| PC  PC      
      - P12   4|  0  13| 27  32| 18   7|  9  18| 17  21| 19  13| 11   8999999
      - P24    |     13|     43|     18|     28|     34|     19|     14   999
      - Q12   0|  0   0|  0   1|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      1|      0|      0|      0|       |             
      - PZP   9|  3  12| 11  19|  4  14| 11  29| 19  29|  8  24|  8  25      
      - PSN  91| 97  87| 84  81| 97  87| 87  57| 35  66| 82  73| 81  70      
      - PRS   0|  0   1|  5   1|  0   0|  0   0| 11   4|  5   0|  2   0      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 9
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  4  32| 16  25|  9  28| 16  41| 23  35| 14  29| 11  34 23 41
      - TMP  15|  7  30| 17  21| 10  25| 19  39| 25  32| 16  27| 13  31      
      - DPT   1|  0  16| 13  10|  5  12| 16  27| 20  22| 12  17|  8  17      
      - CLD  PC| PC  OV| OV  OV| PC  PC| PC  OV| OV  OV| OV  OV| PC  PC      
      - P12   3|  0  11| 50  27|  7  11| 16  16| 18  14| 25  19| 11   8999999
      - P24    |     11|     50|     13|     30|     25|     28|     15   999
      - Q12   0|  0   0|  1   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      1|      0|      0|      0|       |             
      - PZP   9|  4  15| 13  18|  2  14| 13  32| 19  31| 16  27|  8  25      
      - PSN  91| 96  85| 77  81| 95  86| 83  50| 36  60| 74  72| 81  67      
      - PRS   0|  0   0| 10   1|  0   0|  0   1| 13   4|  7   1|  2   0      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 10
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  2  31| 16  27| 12  30| 18  45| 26  50| 29  41| 17  36 23 41
      - TMP  15|  6  29| 18  23| 13  26| 21  42| 29  46| 31  38| 19  32      
      - DPT   1| -2  15| 13  12|  8  13| 17  29| 24  35| 28  29| 15  16      
      - CLD  PC| PC  OV| OV  OV| PC  PC| PC  PC| PC  PC| OV  OV| PC  CL      
      - P12   4|  0  11| 23  23| 10   8| 12   8| 16   8| 18  23| 11   8999999
      - P24    |     11|     39|     11|     17|     22|     34|     16   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      1|      0|      0|      0|       |             
      - PZP   9|  3  13| 18  24|  3  14| 14  33| 14  23| 14  29| 10  26      
      - PSN  91| 97  85| 76  74| 95  86| 73  41| 25  35| 37  52| 72  57      
      - PRS   0|  0   2|  6   2|  0   0|  0   0| 14   5| 12   9|  6   2      
      - TYP   S|  S   S|  S   Z|  S   S|  S   Z| RS   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 11
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  1  29| 13  25| 10  26| 15  38| 21  38| 10  30| 11  31 23 41
      - TMP  14|  5  27| 14  20| 11  23| 18  35| 23  33| 12  27| 13  28      
      - DPT   0| -3  13| 10   8|  6  12| 14  23| 18  22|  7  14|  8  17      
      - CLD  PC| PC  OV| OV  OV| OV  OV| OV  OV| OV  OV| PC  PC| OV  OV      
      - P12   4|  0  22| 27   9|  7  20| 14  22| 15  28| 18  13| 12  16999999
      - P24    |     26|     27|     27|     37|     34|     25|     20   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   1|  0    |             
      - Q24    |      0|      0|      0|      1|      0|       |             
      - PZP   9|  3  13|  9  13|  3  15|  6  22| 16  29|  9  25| 19  26      
      - PSN  91| 97  86| 86  87| 97  86| 93  64| 44  63| 77  68| 68  65      
      - PRS   0|  0   1|  6   0|  0   0|  0   3| 10   1|  5   0|  0   1      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z|  S   Z|  S   Z|  Z   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 12
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  3  31| 14  25|  9  29| 15  44| 25  39| 21  34| 20  48 23 41
      - TMP  14|  6  29| 15  21| 10  25| 17  40| 27  36| 22  32| 21  43      
      - DPT   1| -1  14| 11   8|  5  11| 13  25| 23  25| 19  22| 18  30      
      - CLD  PC| PC  OV| OV  OV| PC  PC| CL  PC| PC  OV| OV  OV| OV  PC      
      - P12   4|  0  12| 25   8|  7   9|  6   5| 11  14| 27  21| 11  15999999
      - P24    |     12|     25|      9|     12|     24|     33|     22   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
      - Q24    |      0|      0|      0|      0|      0|       |             
      - PZP   9|  3  14| 11  16|  3  14|  6  25| 20  33| 15  29|  9  26      
      - PSN  91| 97  84| 83  84| 96  86| 92  56| 30  36| 52  61| 72  63      
      - PRS   0|  0   2|  6   0|  0   0|  0   0|  9   6| 14   5|  5   3      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 13
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  3  31| 16  27| 13  33| 18  45| 24  41| 10  32| 11  32 23 41
      - TMP  15|  7  29| 17  23| 14  29| 20  41| 26  36| 13  28| 13  29      
      - DPT   1| -1  15| 13  11| 10  16| 16  27| 21  23|  8  11|  8  18      
      - CLD  PC| PC  OV| OV  OV| PC  PC| PC  PC| PC  OV| PC  CL| PC  PC      
      - P12   4|  0  12| 21   9|  6  11| 14   7| 11  23| 10  12| 11  13999999
      - P24    |     17|     21|     11|     18|     30|     22|     16   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      0|      0|      0|      0|       |             
      - PZP   9|  3  14| 11  17|  3  16| 10  27| 19  31|  9  24| 12  26      
      - PSN  91| 97  85| 82  83| 91  83| 83  50| 30  48| 73  70| 75  67      
      - PRS   0|  0   2|  7   1|  0   2|  4   1| 10   5|  8   0|  2   1      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 14
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  17|  3  31| 16  26| 10  29| 16  42| 18  34| 13  36| 12  36 23 41
      - TMP  15|  7  29| 17  22| 11  26| 19  38| 20  30| 15  32| 14  33      
      - DPT   1|  0  15| 13  10|  6  12| 15  25| 15  19| 10  19| 10  19      
      - CLD  PC| PC  OV| OV  OV| PC  PC| PC  OV| OV  OV| OV  PC| PC  PC      
      - P12   3|  0  11| 46  29|  5  10| 13  15| 15  13| 10  14| 11   8999999
      - P24    |     14|     46|     12|     26|     25|     19|     15   999
      - Q12   0|  0   0|  1   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      1|      0|      0|      0|       |             
      - PZP   9|  4  15| 13  18|  2  14| 12  31| 17  26|  8  23|  9  25      
      - PSN  91| 96  83| 78  81| 95  85| 76  45| 47  70| 84  69| 80  67      
      - PRS   0|  0   2|  9   1|  0   1|  0   2| 13   1|  2   0|  2   0      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z|  Z   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 15
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  3  30| 15  26| 10  27| 19  45| 25  46| 23  38| 14  31 23 41
      - TMP  15|  6  28| 17  21| 11  25| 21  42| 28  42| 25  35| 15  28      
      - DPT   1| -1  14| 12  10|  6  13| 18  29| 23  32| 22  25| 11  13      
      - CLD  PC| PC  OV| OV  OV| PC  OV| OV  OV| OV  PC| OV  OV| PC  CL      
      - P12   3|  0  12| 50  28|  6  14| 17  13| 17  10| 20  18| 11   8999999
      - P24    |     13|     50|     15|     25|     26|     21|     15   999
      - Q12   0|  0   0|  1   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      1|      0|      0|      0|       |             
      - PZP   9|  3  14| 14  19|  3  14| 15  33| 10  25| 14  27|  9  26      
      - PSN  91| 97  84| 77  80| 97  86| 68  43| 33  40| 45  62| 78  64      
      - PRS   0|  0   2|  9   1|  0   0|  0   0| 16  12| 12   2|  3   1      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 16
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  5  30| 12  24| 10  30| 18  47| 26  40| 15  31| 10  32 23 41
      - TMP  15|  8  27| 14  20| 11  27| 21  43| 28  36| 18  28| 12  28      
      - DPT   1|  1  13|  9   8|  7  13| 17  30| 23  26| 13  14|  7  13      
      - CLD  PC| PC  OV| OV  OV| PC  PC| PC  PC| PC  OV| OV  PC| PC  CL      
      - P12   3|  0  19| 17   6|  6  11| 12   6| 10  16| 12  10| 11   8999999
      - P24    |     28|     17|     11|     14|     25|     18|     15   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      0|      0|      0|      0|       |             
      - PZP   9|  4  15|  7  14|  3  15| 10  26| 13  25|  8  24|  9  26      
      - PSN  91| 96  83| 88  86| 95  84| 81  46| 26  56| 78  72| 78  60      
      - PRS   0|  0   2|  4   0|  0   2|  3   1| 13   9|  7   1|  3   2      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 17
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  3  29| 15  26| 11  29| 16  45| 27  45| 27  46| 26  45 23 41
      - TMP  15|  6  26| 16  22| 12  25| 19  42| 29  41| 29  43| 28  42      
      - DPT   2| -1  13| 12  11|  7  12| 15  28| 25  31| 25  31| 24  30      
      - CLD  PC| PC  OV| OV  OV| OV  PC| PC  PC| PC  OV| OV  PC| PC  PC      
      - P12   5|  0  18| 53  30| 12   8|  9   9| 11  19| 13  13| 16   8999999
      - P24    |     29|     53|     12|     17|     27|     22|     19   999
      - Q12   0|  0   0|  1   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      1|      0|      0|      0|       |             
      - PZP   9|  3  13| 13  19|  3  14| 12  32| 14  30| 18  30| 26  28      
      - PSN  91| 97  86| 81  80| 97  86| 74  39| 22  34| 35  33| 36  45      
      - PRS   0|  0   1|  6   1|  0   0|  0   0| 12   5| 15   6|  6   4      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  Z   Z|  Z   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 18
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  17|  4  30| 14  27| 13  29| 19  45| 27  45| 25  38| 14  29 23 41
      - TMP  15|  8  28| 16  23| 14  27| 22  42| 29  42| 27  35| 16  25      
      - DPT   2|  1  14| 11  11|  9  15| 18  31| 25  33| 24  25| 12  11      
      - CLD  PC| PC  OV| OV  OV| OV  OV| OV  OV| PC  OV| OV  OV| PC  CL      
      - P12   4|  0  15| 12   9|  8  18| 15  11| 14  19| 15  15| 11  10999999
      - P24    |     26|     17|     24|     20|     30|     23|     16   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      0|      0|      0|      0|       |             
      - PZP   9|  3  14|  8  15|  2  16| 15  37| 14  23| 13  28|  9  26      
      - PSN  91| 97  83| 86  85| 95  83| 73  37| 20  31| 43  56| 73  53      
      - PRS   0|  0   3|  4   0|  0   1|  0   1| 14   4| 15   6|  5   4      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 19
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  2  27| 14  25| 11  29| 17  45| 25  45| 21  32| 17  44 23 41
      - TMP  14|  5  25| 15  21| 12  26| 20  42| 27  41| 23  30| 19  40      
      - DPT   1| -2  11| 11  10|  8  13| 17  29| 23  31| 19  18| 15  26      
      - CLD  PC| PC  OV| OV  OV| OV  PC| PC  PC| PC  OV| OV  PC| OV  PC      
      - P12   4|  0  12| 26  45| 22   8| 12   9| 13  13| 23  17| 17  11999999
      - P24    |     12|     53|     22|     16|     26|     26|     25   999
      - Q12   0|  0   0|  0   1|  0   0|  0   0|  0   0|  0    |             
      - Q24    |      0|      1|      0|      0|      0|       |             
      - PZP   9|  3  12| 12  20|  3  14| 13  32| 13  24| 17  33| 14  27      
      - PSN  91| 97  87| 85  80| 97  86| 71  42| 29  48| 47  49| 56  45      
      - PRS   0|  0   1|  3   1|  0   0|  0   1| 15   7| 12   3|  6   2      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z| RS   Z|  Z   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Perturbation 20
      - KGFK   MRF MOS GUIDANCE    3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  16|  6  33| 14  25| 11  29| 17  40| 21  34| 10  26|  7  33 23 41
      - TMP  15| 10  30| 15  21| 13  26| 19  37| 23  30| 12  23| 10  29      
      - DPT   1|  2  16| 11   9|  9  16| 16  24| 18  19|  8  13|  4  15      
      - CLD  PC| PC  OV| OV  PC| OV  OV| OV  PC| PC  OV| OV  PC| OV  PC      
      - P12   3|  0  15| 31   5|  7  25| 19  15| 12  21| 33  14| 11   9999999
      - P24    |     18|     31|     34|     32|     27|     33|     14   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   0|  1    |             
      - Q24    |      0|      0|      0|      0|      0|       |             
      - PZP   9|  5  18| 12  13|  3  16|  9  29| 16  29| 12  25|  9  26      
      - PSN  91| 95  80| 80  87| 95  85| 87  62| 43  50| 76  74| 80  64      
      - PRS   0|  0   2|  9   0|  0   0|  0   2| 11   6| 10   1|  2   0      
      - TYP   S|  S   S|  S   S|  S   S|  S   Z|  S   Z|  S   Z|  S   Z      
      -                                                                      
      -
      -
      -
      -Operational
      - KGFK   GFSX MOS GUIDANCE   3/25/2014  0000 UTC                       
      - FHR  24| 36  48| 60  72| 84  96|108 120|132 144|156 168|180 192      
      - TUE  25| WED 26| THU 27| FRI 28| SAT 29| SUN 30| MON 31| TUE 01 CLIMO
      - X/N  14|  0  30|  9  21| -2  22| 11  38| 24  37| 19  27| 10  29 23 43
      - TMP   8|  6  27| 12  16|  0  20| 15  37| 26  32| 21  24| 13  27      
      - DPT  -3|  1  15|  7   5| -5   6|  9  25| 21  22| 16  11|  8  15      
      - CLD  CL| PC  OV| OV  PC| PC  PC| PC  PC| PC  OV| OV  OV| PC  OV      
      - WND  12|  7  14| 17  17| 12   9| 10  23| 15  28| 28  25| 15  21      
      - P12   0|  0  10| 26   9|  6  10| 11  19| 16  28| 27  13| 15  19999999
      - P24    |     10|     26|     11|     22|     28|     46|     24   999
      - Q12   0|  0   0|  0   0|  0   0|  0   0|  0   1|  0    |             
      - Q24    |      0|      0|      0|      0|      0|       |             
      - T12   5|  2   2|  1   1|  1   1|  2   4|  8   4|  5   4|  2   4      
      - T24    |  5    |  2    |  1    |  2    |  8    |  7    |  4          
      - PZP   1|  1   3|  1   1|  1   2|  3  18|  7   8| 12   5|  3   6      
      - PSN  98| 98  93| 93  97| 95  95| 88  45| 18  14| 34  73| 71  75      
      - PRS   0|  0   4|  3   0|  1   1|  4   2| 11  11| 21   4|  4   6      
      - TYP   S|  S   S|  S   S|  S   S|  S   S|  R   R| RS   S|  S   S      
      - SNW    |      0|      0|      0|      0|      0|       |             
      -                                                                      
      -
      -
      -

      - -* To view other MOS products -

      - * -Go back to the Statistical Modeling Branch HomePage -



      - From 58c670a7c15fd91b92088341767cd139934d4b09 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 15 Jul 2014 11:59:29 -0500 Subject: [PATCH 089/111] More mangling --- postWRF/postWRF/birdseye.py | 33 ++++++---------- postWRF/postWRF/figure.py | 3 +- postWRF/postWRF/main.py | 79 +++++++++++++++++++++++-------------- 3 files changed, 63 insertions(+), 52 deletions(-) diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index de745cd..f385e3d 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -12,12 +12,7 @@ class BirdsEye(Figure): def __init__(self,config,wrfout): - self.C = config - if not isinstance(wrfout,list): - self.W = wrfout - self.D = Defaults() - self.p2p = self.C.output_root - print self.p2p + super().__init__(config,wrfout) def plot_data(self,data,mplcommand,fpath,pt,V=0): """ @@ -65,37 +60,33 @@ def plot_data(self,data,mplcommand,fpath,pt,V=0): self.save(self.fig,self.p2p,self.fname) self.fig.clf() - def plot2D(self,va,vardict,da=0,na=0): + def plot2D(self,va,**kwargs): """ Inputs: va : variable - vardict : dictionary of - pt : plot time - lv : level - vc : vertical coordinate system + keyword arguments: - Other arguments: + pt : plot time + lv : level - da : dictionary of: - tla : top limit of latitude - bla : bottom limit of latitude - llo : left limit of longitude - rlo : right limit of longitude + tla : top limit of latitude + bla : bottom limit of latitude + llo : left limit of longitude + rlo : right limit of longitude plottype : contourf by default """ # INITIALISE #en = self.W.path - self.fig = plt.figure() - self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user - sm = vardict.get('smooth',1) + self.fig.set_size_inches() + sm = kwargs.get('smooth',1) self.bmap,x,y = self.basemap_setup(smooth=sm) # Unpack dictionary - lv = vardict['lv'] + lv = kwargs['lv'] pt = vardict['pt'] plottype = vardict.get('plottype','contourf') # pdb.set_trace() diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index ca5b2e8..6d69619 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -23,7 +23,8 @@ def __init__(self,config,wrfout): self.C = config self.W = wrfout - + self.D = Defaults() + self.output_fpath = self.C.output_root #if wrfout=='RUC': # pass #else: diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index 67a2df6..259b5f8 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -62,7 +62,7 @@ def __init__(self,config): #M.rc('font',**self.font_prop) #M.rcParams['savefig.dpi'] = self.dpi - def plot_2D(self,request): + def plot_2D(self,request,wrfout,output,f_prefix=0,f_suffix=0): """ Path to wrfout file is in config file. Path to plot output is also in config @@ -92,25 +92,31 @@ def plot_2D(self,request): ---> if these are missing, default to 'all points' plottype : contourf by default. + wrfout : path to wrfout file + output : path to output .png. + + OPTIONAL + f_prefix : custom filename prefix + f_suffix : custom filename suffix """ - # Copy dictionary for editing - rq = copy.deepcopy(request) + # Create dictionary for editing + #rq = copy.deepcopy(request) # Load netCDF file once for efficiency - wrfpath = utils.wrfout_files_in(self.C.wrfout_root)[0] + #wrfpath = utils.wrfout_files_in(self.C.wrfout_root)[0] self.W = WRFOut(wrfpath) # Loop over all variables - for va in rq: + for va in request: # LEVELS # Levels may not exist for CAPE, shear etc. - # Use all levels in this case. - if not 'lv' in rq[va]: - rq[va]['lv'] = 'all' - - lv = rq[va]['lv'] - vc = utils.level_type(lv) # vertical coordinate + # Use the '1' code in this case. + #if not 'lv' in rq[va]: + # rq[va]['lv'] = 'all' + #lvs = getattr(request[va],'lv',1) + lvs = self.get_list(request[va],'lv',(1,)) + #vc = utils.level_type(lv) # vertical coordinate # TIMES if not 'pt' in rq[va]: # For averages and all times @@ -120,26 +126,27 @@ def plot_2D(self,request): rq[va]['pt'] = ['range',] # Check for pressure levels - if vc == 'isobaric': - nc_path = self.W.path - p_interp_fpath = self.W.interp_to_p(self.C,nc_path,va,lv) - # Edit p_interp namelist - #Execute p_interp here and reassign self.W to new file - self.W = WRFOut(p_interp_fpath) - else: # - # print("Non-pressure levels not supported yet.") - # raise Exception - pass - - F = BirdsEye(self.C,self.W) + #if vc == 'isobaric': + # nc_path = self.W.path + # p_interp_fpath = self.W.interp_to_p(self.C,nc_path,va,lv) + # # Edit p_interp namelist + # #Execute p_interp here and reassign self.W to new file + # self.W = WRFOut(p_interp_fpath) + #else: # + # # print("Non-pressure levels not supported yet.") + # # raise Exception + # pass - for t in rq[va]['pt']: - #pdb.set_trace() - disp_t = utils.string_from_time('title',t,**rq[va]) - print("Plotting {0} at lv {1} for time {2}.".format(va,lv,disp_t)) - rq[va]['pt'] = t # Need this? - rq[va]['vc'] = vc # Need this? - F.plot2D(va, rq[va]) + for t in ts: + for lv in lvs: + # For each time, variable, and level: + # Create figure + F = BirdsEye(self.C,self.W) + #disp_t = utils.string_from_time('title',t,**rq[va]) + #print("Plotting {0} at lv {1} for time {2}.".format(va,lv,disp_t)) + #rq[va]['pt'] = t # Need this? + #rq[va]['vc'] = vc # Need this? + F.plot2D(va, t, lv,) def get_sequence(self,x,SoS=0): """ Returns a sequence (tuple or list) for iteration. @@ -697,5 +704,17 @@ def make_1D(self,data,output='list'): return data_out + def get_list(self,dic,key,default): + """Fetch value from dictionary. + + If it doesn't exist, use default. + If the value is an integer, make a list of one. + """ + val = getattr(dic,key,default) + if isinstance(val,'int'): + lst = (val,) + else: + lst = val + return lst From 1ad4e179de0c7952a031241d2c681c765f1f0d1f Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 16 Jul 2014 16:56:39 -0500 Subject: [PATCH 090/111] Got plot2D working again --- __init__.py | 2 +- docs/index.rst | 1 + docs/postwrf_index.md | 38 ++ docs/postwrf_index.rst | 38 ++ postWRF/bin/settings.py | 2 +- postWRF/postWRF/birdseye.py | 157 +++---- postWRF/postWRF/figure.py | 21 +- postWRF/postWRF/main.py | 227 ++++++---- postWRF/postWRF/scales.py | 223 +++++----- postWRF/postWRF/skewt.py | 19 +- postWRF/postWRF/wrfout.py | 58 +-- postWRF/postWRF/xsection.py | 15 +- utils/GIS_tools.py | 826 ++++++++++++++++++++++++++++++++++++ utils/__init__.py | 6 +- utils/unix_tools.py | 13 +- 15 files changed, 1283 insertions(+), 363 deletions(-) create mode 100644 docs/postwrf_index.md create mode 100644 docs/postwrf_index.rst create mode 100644 utils/GIS_tools.py diff --git a/__init__.py b/__init__.py index 16ca54a..7bd5dde 100644 --- a/__init__.py +++ b/__init__.py @@ -1 +1 @@ -import utils.utils as utils +#import utils \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 90509b2..d938374 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,6 +14,7 @@ Contents: intro lazywrf postwrf + postwrf_index Indices and tables diff --git a/docs/postwrf_index.md b/docs/postwrf_index.md new file mode 100644 index 0000000..1e937bb --- /dev/null +++ b/docs/postwrf_index.md @@ -0,0 +1,38 @@ +postWRF: post-processing +======================== + +Create basic plots from your WRF runs. + +First steps +----------- +You first need to create a script that contains your general settings. Name it something like `settings.py`. Include these four lines at the top. + + class Settings: + def __init__(self): + self.output_root = '/home/user/images/path/' + self.wrfout_root = '/home/user/data/path' + +The `output_root` is the location you'd like .png images to be saved to. The `wrfout_root` specifies the location of the netCDF wrfout files. Both are only roots, and you can modify subfolders and filenames later on-the-fly. Next, create a script that calls the plotting functions. Perhaps call it `plot.py`. + + from settings import Settings + from WEM.postWRF import WRFEnviron + + config = Settings() + p = WRFEnviron(config) + +This creates a configuration that gets passed into the WRF Environment (called `p` for plotting in this case). + +Dictionaries +------------ + +Whereas your `settings.py` file contains constant settings, more flexible settings go into a dictionary. Let's create one in `plot.py`. Each variable we want plotting should be a nested dictionary too, as so: + + variables = {'cref':{}, 'wind':{}} + +We now specify the required level(s) and time(s) to plot. A bounding box can be specified but this is covered later. Here's an example: + + variables['cref'] = {'lv':2000, 'pt':(2011,,4,19,18,0,0)} + +This will plot simulated composite reflectivity at the surface (2000) for the time specified by `pt`. + + \ No newline at end of file diff --git a/docs/postwrf_index.rst b/docs/postwrf_index.rst new file mode 100644 index 0000000..7a5cb59 --- /dev/null +++ b/docs/postwrf_index.rst @@ -0,0 +1,38 @@ +postWRF: post-processing +======================== + +Create basic plots from your WRF runs. + +First steps +----------- +You first need to create a script that contains your general settings. Name it something like `settings.py`. Include these four lines at the top.:: + + class Settings: + def __init__(self): + self.output_root = '/home/user/images/path/' + self.wrfout_root = '/home/user/data/path' + +The `output_root` is the location you'd like .png images to be saved to. The `wrfout_root` specifies the location of the netCDF wrfout files. Both are only roots, and you can modify subfolders and filenames later on-the-fly. Next, create a script that calls the plotting functions. Perhaps call it `plot.py`.:: + + from settings import Settings + from WEM.postWRF import WRFEnviron + + config = Settings() + p = WRFEnviron(config) + +This creates a configuration that gets passed into the WRF Environment (called `p` for plotting in this case). + +Dictionaries +------------ + +Whereas your `settings.py` file contains constant settings, more flexible settings go into a dictionary. Let's create one in `plot.py`. Each variable we want plotting should be a nested dictionary too, as so:: + + variables = {'cref':{}, 'wind':{}} + +We now specify the required level(s) and time(s) to plot. A bounding box can be specified but this is covered later. Here's an example:: + + variables['cref'] = {'lv':2000, 'pt':(2011,,4,19,18,0,0)} + +This will plot simulated composite reflectivity at the surface (2000) for the time specified by `pt`. + + \ No newline at end of file diff --git a/postWRF/bin/settings.py b/postWRF/bin/settings.py index fce9478..b0574e7 100644 --- a/postWRF/bin/settings.py +++ b/postWRF/bin/settings.py @@ -5,7 +5,7 @@ def __init__(self): self.ecmwf_root = '/chinook2/jrlawson/bowecho/' self.p_interp_root = '/home/jrlawson/fortrancode/P_INTERP/' self.RUC_root = '/chinook2/jrlawson/bowecho/' - #self.DPI = 100.0 + self.DPI = 300.0 self.plot_titles = 1 self.terrain = 0 self.colorbar = 1 diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index f385e3d..fa24599 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -8,11 +8,11 @@ from defaults import Defaults from figure import Figure import WEM.utils as utils -import scales +from scales import Scales class BirdsEye(Figure): def __init__(self,config,wrfout): - super().__init__(config,wrfout) + super(BirdsEye,self).__init__(config,wrfout) def plot_data(self,data,mplcommand,fpath,pt,V=0): """ @@ -60,113 +60,71 @@ def plot_data(self,data,mplcommand,fpath,pt,V=0): self.save(self.fig,self.p2p,self.fname) self.fig.clf() - def plot2D(self,va,**kwargs): + #def plot2D(self,va,**kwargs): + def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1,plottype='contourf'): """ Inputs: - va : variable - - keyword arguments: - - pt : plot time - lv : level - - tla : top limit of latitude - bla : bottom limit of latitude - llo : left limit of longitude - rlo : right limit of longitude - - plottype : contourf by default - + vrbl : variable string + t : date/time in (YYYY,MM,DD,HH,MM,SS) format + lv : level + dom : domain + outpath : absolute path to output + bounding : list of four floats (Nlim, Elim, Slim, Wlim): + Nlim : northern limit + Elim : eastern limit + Slim : southern limit + Wlim : western limit + smooth : smoothing. 1 is off. integer greater than one is + the degree of smoothing, to be specified. + """ # INITIALISE - #en = self.W.path - self.fig.set_size_inches() - sm = kwargs.get('smooth',1) - self.bmap,x,y = self.basemap_setup(smooth=sm) - - # Unpack dictionary - lv = kwargs['lv'] - pt = vardict['pt'] - plottype = vardict.get('plottype','contourf') - # pdb.set_trace() + self.fig.set_size_inches(8,8) + self.bmap,self.x,self.y = self.basemap_setup(smooth=smooth) + + # Make sure smooth=0 is corrected to 1 + # They are both essentially 'off'. + if smooth==0: + smooth = 1 # Get indices for time, level, lats, lons - # TIME - if pt == 'all': - time_idx = slice(None,None) - elif pt == 'range': - start_frame = self.W.get_time_idx(vardict['itime'], tuple_format=1) - end_frame = self.W.get_time_idx(vardict['ftime'], tuple_format=1) - time_idx = slice(start_frame,end_frame) - else: - time_idx = self.W.get_time_idx(pt) - - # LAT/LON - #if 'smooth' in vardict: - #s = vardict['smooth'] - - lat_sl, lon_sl = self.get_limited_domain(da,smooth=sm) - - # LEVEL - vc = vardict['vc'] - if vc == 'surface': - lv_idx = 0 - elif lv == 'all': - lv_idx = 'all' - else: - print("Need to sort other levels") - raise Exception - - lv_na = utils.get_level_naming(va,**vardict) - - """ - def plot_strongest_wind(self,dic): - itime = dic['itime'] - ftime = dic['ftime'] - V = dic.get('range',(10,32.5,2.5)) - F = BirdsEye(self.C,W - - """ - - # Now clear dictionary of old settings - # They will be replaced with indices - # vardict.pop('pt') - # vardict.pop('lv') - # try: - # vardict.pop('da') - # except KeyError: - # pass + tidx = self.W.get_time_idx(t) + lvidx = 0 + latidx, lonidx = self.get_limited_domain(bounding,smooth=smooth) + + # if vc == 'surface': + # lv_idx = 0 + # elif lv == 'all': + # lv_idx = 'all' + # else: + # print("Need to sort other levels") + # raise Exception # FETCH DATA - PS = {'t': time_idx, 'lv': lv_idx, 'la': lat_sl, 'lo': lon_sl} - data = self.W.get(va,PS,**vardict) + ncidx = {'t': tidx, 'lv': lvidx, 'la': latidx, 'lo': lonidx} + data = self.W.get(vrbl,ncidx)#,**vardict) la_n = data.shape[-2] lo_n = data.shape[-1] # COLORBAR, CONTOURING - cm, clvs = scales.get_cm(va,**vardict) - multiplier = scales.get_multiplier(va,**vardict) - # Override contour levels if specified - # clvs = vardict.get('range',clvs_default) + S = Scales(vrbl,lv) - # pdb.set_trace() - if cm: - plotargs = (x,y,data.reshape((la_n,lo_n)),clvs) - cmap = cm - #self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,{'cmap':cm}) - elif isinstance(clvs,N.ndarray): - #self.bmap.contourf(x,y,data.reshape((la_n,lo_n)),clvs,cmap=plt.cm.jet) + multiplier = S.get_multiplier(vrbl,lv) + + if S.cm: + plotargs = (self.x,self.y,data.reshape((la_n,lo_n)),S.clvs) + cmap = S.cm + elif isinstance(S.clvs,N.ndarray): if plottype == 'contourf': - plotargs = (x,y,data.reshape((la_n,lo_n)),clvs) + plotargs = (x,y,data.reshape((la_n,lo_n)),S.clvs) cmap = plt.cm.jet else: - plotargs = (x,y,data.reshape((la_n,lo_n)),clvs) + plotargs = (x,y,data.reshape((la_n,lo_n)),S.clvs) else: plotargs = (x,y,data.reshape((la_n,lo_n))) cmap = plt.cm.jet - #self.bmap.contourf(x,y,data.reshape((la_n,lo_n))) if plottype == 'contourf': @@ -178,22 +136,19 @@ def plot_strongest_wind(self,dic): # LABELS, TITLES etc if self.C.plot_titles: - title = utils.string_from_time('title',pt,**vardict) + title = utils.string_from_time('title',t) plt.title(title) if plottype == 'contourf' and self.C.colorbar: plt.colorbar(orientation='horizontal') # SAVE FIGURE - datestr = utils.string_from_time('output',pt) - if not na: - # Use default naming scheme - na = (va,lv_na,datestr) - else: - # Come up with scheme... - print("Coming soon: ability to create custom filenames") - raise Exception - self.fname = self.create_fname(*na) # No da variable here - self.save(self.fig,self.p2p,self.fname) + lv_na = utils.get_level_naming(vrbl,lv) + datestr = utils.string_from_time('output',t) + naming = [vrbl,lv_na,datestr] + if dom: + naming.append(dom) + self.fname = self.create_fname(*naming) + self.save(self.fig,outpath,self.fname) plt.close() def plot_streamlines(self,lv,pt,da=0): @@ -206,7 +161,7 @@ def plot_streamlines(self,lv,pt,da=0): lv_idx = None else: print("Only support surface right now") - raise Exception + raise Exception lat_sl, lon_sl = self.get_limited_domain(da) @@ -237,7 +192,7 @@ def plot_streamlines(self,lv,pt,da=0): plt.title(title) datestr = utils.string_from_time('output',pt) na = ('streamlines',lv_na,datestr) - self.fname = self.create_fname(*na) + self.fname = self.create_fname(*na) self.save(self.fig,self.p2p,self.fname) plt.clf() plt.close() diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index 6d69619..3dd2d1e 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -13,36 +13,42 @@ # Custom imports import WEM.utils as utils +from defaults import Defaults -class Figure: +class Figure(object): def __init__(self,config,wrfout): """ C : configuration settings - W : data + W : data """ self.C = config self.W = wrfout self.D = Defaults() self.output_fpath = self.C.output_root + #if wrfout=='RUC': # pass #else: # self.W = wrfout + # Get settings for figure + dpi = getattr(self.C,'DPI',self.D.dpi) + # Create main figure self.fig = plt.figure() + self.fig.set_dpi(dpi) def create_fname(self,*naming): """Default naming should be: Variable + time + level """ - fname = '_'.join([str(a) for a in naming]) + fname = '_'.join([str(a) for a in naming]) #pdb.set_trace() - return fname + return fname def title_time(self): - self.T = utils.padded_times(self.timeseq) + self.T = utils.padded_times(self.timeseq) pdb.set_trace() def figsize(self,defwidth,defheight,fig): @@ -56,10 +62,11 @@ def save(self,fig,p2p,fname): utils.trycreate(p2p) fpath = os.path.join(p2p,fname) #self.fig.savefig(fpath) - plt.gcf().savefig(fpath,bbox_inches='tight') + fig.savefig(fpath,bbox_inches='tight') + print("Saving figure {0}".format(fpath)) def get_limited_domain(self,da,smooth=1): - if da: # Limited domain area + if da: # Limited domain area N_idx = self.W.get_lat_idx(da['N']) E_idx = self.W.get_lon_idx(da['E']) S_idx = self.W.get_lat_idx(da['S']) diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index 259b5f8..33518d6 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -19,7 +19,6 @@ #M.use('Agg') #import matplotlib.pyplot as plt #from mpl_toolkits.basemap import Basemap -import collections import fnmatch import calendar import pdb @@ -30,6 +29,7 @@ import cPickle as pickle import copy import glob +import itertools from wrfout import WRFOut from axes import Axes @@ -41,6 +41,7 @@ from defaults import Defaults from lookuptable import LookUpTable import WEM.utils as utils +from xsection import CrossSection # TODO: Make this awesome @@ -56,13 +57,13 @@ def __init__(self,config): #self.font_prop = getattr(self.C,'font_prop',self.D.font_prop) #self.usetex = getattr(self.C,'usetex',self.D.usetex) - #self.dpi = getattr(self.C,'DPI',self.D.dpi) #self.plot_titles = getattr(self.C,'plot_titles',self.D.plot_titles) #M.rc('text',usetex=self.usetex) #M.rc('font',**self.font_prop) #M.rcParams['savefig.dpi'] = self.dpi - def plot_2D(self,request,wrfout,output,f_prefix=0,f_suffix=0): + def plot2D(self,vrbl,times,levels,wrf_sd=0,wrf_nc=0,out_sd=0,f_prefix=0,f_suffix=0, + bounding=0,dom=0): """ Path to wrfout file is in config file. Path to plot output is also in config @@ -73,96 +74,118 @@ def plot_2D(self,request,wrfout,output,f_prefix=0,f_suffix=0): Inputs: - request : nested dictionary with: - - KEY - === - va : variable to plot - - nested KEY/VALUE PAIRS - ====================== - (MANDATORY FOR SOME VARIABLES) - lv : level to plot - pt : plot times - (OPTIONAL) - tla : top limit of latitude - bla : bottom limit of latitude - llo : left limit of longitude - rlo : right limit of longitude - ---> if these are missing, default to 'all points' - plottype : contourf by default. - - wrfout : path to wrfout file - output : path to output .png. - - OPTIONAL + vrbl : string of variable name + times : one or more date/times. + Can be tuple format (YYYY,MM,DD,HH,MM,SS - calendar.timegm) + Can be integer of datenum. (time.gmtime) + Can be a tuple or list of either. + levels : one or more levels. + Lowest model level is integer 2000. + Pressure level is integer in hPa, e.g. 850 + Isentropic surface is a string + K, e.g. '320K' + Geometric height is a string + m, e.g. '4000m' + wrf_sd : string - subdirectory of wrfout file + wrf_nc : filename of wrf file requested. + If no wrfout file is explicitly specified, the + netCDF file in that folder is chosen if unambiguous. + out_sd : subdirectory of output .png. f_prefix : custom filename prefix f_suffix : custom filename suffix + bounding : list of four floats (Nlim, Elim, Slim, Wlim): + Nlim : northern limit + Elim : eastern limit + Slim : southern limit + Wlim : western limit + smooth : smoothing. 0 is off. non-zero integer is the degree + of smoothing, to be specified. + dom : domain for plotting. If zero, the only netCDF file present + will be plotted. If list of integers, the script will loop over domains. + + """ - # Create dictionary for editing - #rq = copy.deepcopy(request) - - # Load netCDF file once for efficiency - #wrfpath = utils.wrfout_files_in(self.C.wrfout_root)[0] - self.W = WRFOut(wrfpath) - - # Loop over all variables - for va in request: - - # LEVELS - # Levels may not exist for CAPE, shear etc. - # Use the '1' code in this case. - #if not 'lv' in rq[va]: - # rq[va]['lv'] = 'all' - #lvs = getattr(request[va],'lv',1) - lvs = self.get_list(request[va],'lv',(1,)) - #vc = utils.level_type(lv) # vertical coordinate - - # TIMES - if not 'pt' in rq[va]: # For averages and all times - if not 'itime' in rq[va]: # For all times - rq[va]['pt'] = ['all',] - else: # For specific range - rq[va]['pt'] = ['range',] - - # Check for pressure levels - #if vc == 'isobaric': - # nc_path = self.W.path - # p_interp_fpath = self.W.interp_to_p(self.C,nc_path,va,lv) - # # Edit p_interp namelist - # #Execute p_interp here and reassign self.W to new file - # self.W = WRFOut(p_interp_fpath) - #else: # - # # print("Non-pressure levels not supported yet.") - # # raise Exception - # pass + self.W = get_wrfout(wrf_sd,wrf_nc,dom=dom,unambiguous=1) - for t in ts: - for lv in lvs: - # For each time, variable, and level: - # Create figure - F = BirdsEye(self.C,self.W) - #disp_t = utils.string_from_time('title',t,**rq[va]) - #print("Plotting {0} at lv {1} for time {2}.".format(va,lv,disp_t)) - #rq[va]['pt'] = t # Need this? - #rq[va]['vc'] = vc # Need this? - F.plot2D(va, t, lv,) - - def get_sequence(self,x,SoS=0): - """ Returns a sequence (tuple or list) for iteration. - Avoids an error for strings/integers. - SoS = 1 enables the check for a sequence of sequences (list of dates) - """ - if SoS: - y = x[0] + if out_sd: + outpath = os.path.join(self.C.output_root,out_sd) else: - y = x - - if isinstance(y, collections.Sequence) and not isinstance(y, basestring): - return x + outpath = self.C.output_root + + # Make sure times are in datenum format and sequence. + t_list = utils.ensure_sequence_datenum(times) + + d_list = utils.get_sequence(dom) + lv_list = utils.get_sequence(levels) + # pdb.set_trace() + for t, l, d in itertools.product(t_list,lv_list,d_list): + F = BirdsEye(self.C,self.W) + F.plot2D(vrbl,t,l,d,outpath,bounding=bounding) + + # LEVELS + # Levels may not exist for CAPE, shear etc. + # Use the '1' code in this case. + #if not 'lv' in rq[va]: + # rq[va]['lv'] = 'all' + #lvs = getattr(request[va],'lv',1) + #lvs = self.get_list(request[va],'lv',(1,)) + #vc = utils.level_type(lv) # vertical coordinate + + # TIMES + # if not 'pt' in rq[va]: # For averages and all times + # if not 'itime' in rq[va]: # For all times + # rq[va]['pt'] = ['all',] + # else: # For specific range + # rq[va]['pt'] = ['range',] + + # Check for pressure levels + #if vc == 'isobaric': + # nc_path = self.W.path + # p_interp_fpath = self.W.interp_to_p(self.C,nc_path,va,lv) + # # Edit p_interp namelist + # #Execute p_interp here and reassign self.W to new file + # self.W = WRFOut(p_interp_fpath) + #else: # + # # print("Non-pressure levels not supported yet.") + # # raise Exception + # pass + + # for t in ts: + # for lv in lvs: + # For each time, variable, and level: + # Create figure + # F = BirdsEye(self.C,self.W) + #disp_t = utils.string_from_time('title',t,**rq[va]) + #print("Plotting {0} at lv {1} for time {2}.".format(va,lv,disp_t)) + #rq[va]['pt'] = t # Need this? + #rq[va]['vc'] = vc # Need this? + # F.plot2D(va, t, lv, ) + + def get_wrfout(self,wrf_sd=0,wrf_nc=0,dom=0) + """Returns the WRFOut instance, given arguments: + + Optional inputs: + wrf_sd : subdirectory for wrf file + wrf_nc : filename for wrf file + dom : domain for wrf file + """ + + if wrf_sd and wrf_nc: + wrfpath = os.path.join(self.C.wrfout_root,wrf_sd,wrf_nc) + elif wrf_sd: + wrfdir = os.path.join(self.C.wrfout_root,wrf_sd) + wrfpath = utils.wrfout_files_in(wrfdir,dom=dom,unambiguous=1) else: - return [x] + wrfdir = os.path.join(self.C.wrfout_root) + wrfpath = utils.wrfout_files_in(wrfdir,dom=dom,unambiguous=1) + + return WRFOut(wrfpath) + def generate_times(self,itime,ftime,x): + """ + Wrapper for utility method. + """ + y = utils.generate_times(itime,ftime,x) + return y + def plot_cross_section(self,var,latA,lonA,latB,lonB): xs = CrossSection() xs.plot(var,latA,lonA,latB,lonB) @@ -718,3 +741,35 @@ def get_list(self,dic,key,default): lst = val return lst + def plot_xs(self,vrbl,times,latA=0,lonA=0,latB=0,lonB=0, + wrf_sd=0,wrf_nc=0,out_sd=0,f_prefix=0,f_suffix=0,dom=0,): + """Plot cross-section. + + If no lat/lon transect is indicated, a popup appears for the user + to pick points. The popup can have an overlaid field such as reflectivity + to help with the process. + + Inputs: + vrbl : variable to be plotted + times : times to be plotted + latA : start latitude of transect + lonA : start longitude of transect + latB : end lat... + lonB : end lon... + wrf_sd : string - subdirectory of wrfout file + wrf_nc : filename of wrf file requested. + If no wrfout file is explicitly specified, the + netCDF file in that folder is chosen if unambiguous. + out_sd : subdirectory of output .png. + f_prefix : custom filename prefix + f_suffix : custom filename suffix + + + """ + self.W = get_wrfout(wrf_sd,wrf_nc,dom=dom,unambiguous=1) + + XS = CrossSection(self.C,self.W,latA,lonA,latB,lonB) + + t_list = utils.ensure_sequence_datenum(times) + for t in t_list: + XS.plot_xs(vrbl,t) \ No newline at end of file diff --git a/postWRF/postWRF/scales.py b/postWRF/postWRF/scales.py index 6c19ce6..8c59264 100644 --- a/postWRF/postWRF/scales.py +++ b/postWRF/postWRF/scales.py @@ -23,120 +23,115 @@ import colourtables as ct import WEM.utils as utils -def get_multiplier(va,**kwargs): - m = A[va].get('multiplier',1) - return m - -def get_cm(va,**kwargs): - - lv = kwargs['lv'] - - if lv=='all': - lv = 0 - # Variable and vertical level determine contour scale - # pdb.set_trace() - - if hasattr(kwargs,'range'): # Custom range set by user - clvs = N.arange(*kwargs['range']) - else: - try: - if len(A[va][lv]) == 3: - # This is a min-max-interval list - clvs = N.arange(*A[va][lv]) - else: - # This is an actual list of values - clvs = A[va][lv] - except KeyError: - # If no level exists, try finding a near one +class Scales(object): + def __init__(self,vrbl,lv,clvs=0): + self.A = self.get_dict_of_levels() + # Variable and vertical level determine contour scale + + if clvs: + # Custom range set by user + self.clvs = N.arange(*clvs) + else: try: - near_lv = find_nearest_level(lv) - clvs = A[va][near_lv] - except: - # Some variables don't live on a vertical level - clvs = 0 - # except: - # raise Exception - - try: - cm = A[va]['cmap'](clvs) - #pdb.set_trace() - except TypeError: - #print("Using default colourtable.") - #def_ct = plt.cm.get_cmap("jet") - cm = 0 - #cm = LinearSegmentedColormap('DEF_CT',def_ct) - # else: - # raise Exception - # print va - # print type(cm) - # pdb.set_trace() - return cm, clvs - -def find_nearest_level(lv): - lv_type = utils.level_type(lv) - - if lv_type == 'isentropic': - pass - # 'K' needs stripping - # This will be tricky, varies a lot... - elif lv_type == 'isobaric': - pass - # Plot logarithmically closest - elif lv_type == 'surface': - raise Exception - # Shouldn't get here, surface should be covered. - elif lv_type == 'PV-surface': - pass - elif lv_type == 'geometric': - pass - else: - raise Exception - - return near_lv - -######## DEFAULT SETTINGS FOR LEVELS ######## - -A = {} - -# Wind magnitude -A['wind10'] = {'cmap':0} -A['wind10'][2000] = (5,32.5,2.5) - -# Theta-e (Equivalent potential temperature) -# A['thetae'] = {'cmap':ct.thetae} - -# Simulated reflectivity -A['sim_ref'] = {'cmap':ct.reflect_ncdc} -A['sim_ref'][2000] = (5,90,5) - -# Simulated reflectivity -A['cref'] = {'cmap':ct.reflect_ncdc} -A['cref'][2000] = (5,90,5) - -# Precipitation -A['precip'] = {'cmap':ct.precip1} -A['precip'][2000] = [0.01,0.03,0.05,0.10,0.15,0.20,0.25,0.30,0.40,0.50,0.60, - 0.70,0.80,0.90,1.00,1.25,1.50,1.75,2.00,2.50] + if len(self.A[vrbl][lv]) == 3: + # This is a min-max-interval list + self.clvs = N.arange(*self.A[vrbl][lv]) + else: + # This is an actual list of values + self.clvs = self.A[vrbl][lv] + except KeyError: + # If no level exists, try finding a near one + try: + near_lv = find_nearest_level(lv) + self.clvs = self.A[vrbl][near_lv] + except: + # Some variables don't live on a vertical level + self.clvs = 0 + # except: + # raise Exception -# Precipitable water -A['pwat'] = {'cmap':ct.precip1} -A['pwat'][2000] = (0.2,2.6,0.1) - -# Snowfall -A['snow'] = {'cmap':ct.snow2} -A['snow'][2000] = [0.25,0.5,0.75,1,1.5,2,2.5,3,4,5,6,8,10,12,14,16,18] - -A['shear'] = {'cmap':0} -A['shear'][0] = (0,33,3) - -A['buoyancy'] = {'cmap':0} -A['buoyancy'][2000] = (-0.65,0.075,0.025) - -A['dptp'] = {'cmap':0} -A['dptp'][2000] = (-15,6,1) - -A['strongestwind'] = {'cmap':0} -A['strongestwind'][2000] = (10,32.5,2.5) + try: + self.cm = self.A[vrbl]['cmap'](clvs) + #pdb.set_trace() + except TypeError: + #print("Using default colourtable.") + #def_ct = plt.cm.get_cmap("jet") + self.cm = 0 + #cm = LinearSegmentedColormap('DEF_CT',def_ct) -A['PMSL'] = {'cmap':0,'multiplier':0.01} -A['PMSL'][2000] = (97000,103100,100) + + def get_multiplier(self,vrbl,lv): + m = self.A[vrbl].get('multiplier',1) + return m + + def find_nearest_level(self,lv): + lv_type = utils.level_type(lv) + + if lv_type == 'isentropic': + pass + # 'K' needs stripping + # This will be tricky, varies a lot... + elif lv_type == 'isobaric': + pass + # Plot logarithmically closest + elif lv_type == 'surface': + raise Exception + # Shouldn't get here, surface should be covered. + elif lv_type == 'PV-surface': + pass + elif lv_type == 'geometric': + pass + else: + raise Exception + + return near_lv + + ######## DEFAULT SETTINGS FOR LEVELS ######## + + def get_dict_of_levels(self): + A = {} + + # Wind magnitude + A['wind10'] = {'cmap':0} + A['wind10'][2000] = (5,32.5,2.5) + + # Theta-e (Equivalent potential temperature) + # A['thetae'] = {'cmap':ct.thetae} + + # Simulated reflectivity + A['sim_ref'] = {'cmap':ct.reflect_ncdc} + A['sim_ref'][2000] = (5,90,5) + + # Simulated reflectivity + A['cref'] = {'cmap':ct.reflect_ncdc} + A['cref'][2000] = (5,90,5) + + # Precipitation + A['precip'] = {'cmap':ct.precip1} + A['precip'][2000] = [0.01,0.03,0.05,0.10,0.15,0.20,0.25,0.30,0.40,0.50,0.60, + 0.70,0.80,0.90,1.00,1.25,1.50,1.75,2.00,2.50] + + # Precipitable water + A['pwat'] = {'cmap':ct.precip1} + A['pwat'][2000] = (0.2,2.6,0.1) + + # Snowfall + A['snow'] = {'cmap':ct.snow2} + A['snow'][2000] = [0.25,0.5,0.75,1,1.5,2,2.5,3,4,5,6,8,10,12,14,16,18] + + A['shear'] = {'cmap':0} + A['shear'][0] = (0,33,3) + + A['buoyancy'] = {'cmap':0} + A['buoyancy'][2000] = (-0.65,0.075,0.025) + + A['dptp'] = {'cmap':0} + A['dptp'][2000] = (-15,6,1) + + A['strongestwind'] = {'cmap':0} + A['strongestwind'][2000] = (10,32.5,2.5) + + A['PMSL'] = {'cmap':0,'multiplier':0.01} + A['PMSL'][2000] = (97000,103100,100) + + return A diff --git a/postWRF/postWRF/skewt.py b/postWRF/postWRF/skewt.py index c36f2c1..4283140 100644 --- a/postWRF/postWRF/skewt.py +++ b/postWRF/postWRF/skewt.py @@ -18,10 +18,7 @@ from figure import Figure from wrfout import WRFOut -from WEM.utils import unix_tools -from WEM.utils import generalmet -from WEM.utils import gridded_data -from WEM.utils import utils +import WEM.utils as utils import metconstants as mc class Profile(Figure): @@ -52,7 +49,7 @@ def composite_profile(self,va,plot_time,plot_latlon,wrfouts,dom,mean,std,xlim,yl P_bot, P_top, dp = [y*100.0 for y in ylim] else: P_bot = 100000.0 - P_top = 20000.0 + P_top = 20000.0 dp = 10000.0 plevs = N.arange(P_bot,P_top,dp) @@ -174,7 +171,7 @@ def plot_skewT(self,plot_time,plot_latlon,dom,save_output,save_plot=1): self.barb_increments = {'half': 2.5,'full':5.0,'flag':25.0} self.skewness = 37.5 self.P_bot = 100000. - self.P_top = 10000. + self.P_top = 10000. self.dp = 100. self.plevs = N.arange(self.P_bot,self.P_top-1,-self.dp) @@ -278,11 +275,11 @@ def windbarbs_real(self,uwind,vwind,P,delta=3,color='red',n=37.5): zmax = len(uwind) # n is x-ax position on skewT for barbs. baraxis = [n for _j in range(0,zmax,delta)] - plt.barbs(baraxis,P[0:zmax:delta],uwind[0:zmax:delta],vwind[0:zmax:delta], + plt.barbs(baraxis,P[0:zmax:delta],uwind[0:zmax:delta],vwind[0:zmax:delta], barb_increments=self.barb_increments, linewidth = .75, barbcolor = color, flagcolor = color) def temperature(self,nc,time,y,x,P,linestyle='solid',color='black'): - theta = nc.variables['T'][time,:,y,x] + mc.Tb + theta = nc.variables['T'][time,:,y,x] + mc.Tb T = theta*(P/self.P_bot)**mc.kappa - mc.Tz # Temperatur i halvflatene (C) plt.semilogy(T + self.skewnessTerm(P), P, basey=math.e, color = color, \ linestyle=linestyle, linewidth = 1.5) @@ -306,7 +303,7 @@ def return_data(self,whichdata,nc,time,y,x,thin_locs,P=None): vwind = 0.5*(nc.variables['V'][time,:,y,x]+nc.variables['V'][time,:,y+1,x]) return uwind[thin_locs],vwind[thin_locs] elif whichdata == 'temp': - theta = nc.variables['T'][time,:,y,x] + mc.Tb + theta = nc.variables['T'][time,:,y,x] + mc.Tb T = theta*(P/self.P_bot)**mc.kappa - mc.Tz return T elif whichdata == 'dwpt': @@ -363,7 +360,7 @@ def moist_adiabats(self,): skewness = 37.5 # Defines the ranges of the plot, do not confuse with self.P_bot and self.P_top P_b = 105000. - P_t = 10000. + P_t = 10000. self.dp = 100. self.plevs = N.arange(P_b,P_t-1,-self.dp) """ @@ -372,7 +369,7 @@ def gamma_s(self,T,p): """Calculates moist adiabatic lapse rate for T (Celsius) and p (Pa) Note: We calculate dT/dp, not dT/dz See formula 3.16 in Rogers&Yau for dT/dz, but this must be combined with - the dry adiabatic lapse rate (gamma = g/cp) and the + the dry adiabatic lapse rate (gamma = g/cp) and the inverse of the hydrostatic equation (dz/dp = -RT/pg)""" a = 2./7. b = ((mc.R/mc.Rv)*(mc.L**2))/(mc.R*mc.cp) diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 549f5e8..0598958 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -13,8 +13,9 @@ import pdb import constants as cc import scipy.ndimage +import collections -from WEM.utils import wrf_tools +import WEM.utils as utils class WRFOut(object): @@ -28,7 +29,7 @@ def __init__(self,fpath,config=0): self.dx = self.nc.DX self.dy = self.nc.DY - #self.lvs = + #self.lvs = self.lats = self.nc.variables['XLAT'][0,...] # Might fail if only one time? self.lons = self.nc.variables['XLONG'][0,...] @@ -56,23 +57,28 @@ def test_smooth(self,data): def wrftime_to_datenum(self,times): """ Convert wrf's weird Times variable to datenum time. + + Input: + t : wrf's time """ - nt = times.shape[0] - wrf_times_epoch = N.zeros([nt,1]) + wrf_times_epoch = N.zeros([times.shape[0]]) - for t in range(nt): - yr = int(''.join(t[i,0:4])) - mth = int(''.join(t[i,5:7])) - day = int(''.join(t[i,8:10])) - hr = int(''.join(t[i,11:13])) - mins = int(''.join(t[i,14:16])) - sec = int(''.join(t[i,17:19])) - self.wrf_times_epoch[i] = calendar.timegm([yr,mth,day,hr,mins,sec]) + for n,t in enumerate(times): + tstr = ''.join(t) - return self.wrf_times_epoch + yr = int(tstr[0:4]) + mth = int(tstr[5:7]) + day = int(tstr[8:10]) + hr = int(tstr[11:13]) + mins = int(tstr[14:16]) + sec = int(tstr[17:19]) + + wrf_times_epoch[n] = calendar.timegm([yr,mth,day,hr,mins,sec]) + + return wrf_times_epoch - def get_time_idx(self,wrftimes,t,tuple_format=0): + def get_time_idx(self,t): """ Input: @@ -84,11 +90,14 @@ def get_time_idx(self,wrftimes,t,tuple_format=0): time_idx : index of time in WRF file """ # Ensure datenum format - if tuple_format: + if isinstance(t,int): + t_epoch = t + elif isinstance(t,collections.Sequence) and len(t) == 6: t_epoch = calendar.timegm(t) else: - t_epoch = t - + print("Time {0} is not correct format.".format(t)) + raise Exception + # Convert wrftimes to datenum times wrf_times_epoch = self.wrftime_to_datenum(self.wrf_times) @@ -97,6 +106,7 @@ def get_time_idx(self,wrftimes,t,tuple_format=0): # abs(self.wrf_times_epoch-t_epoch) == # abs(self.wrf_times_epoch-t_epoch).min() # )[0][0] + # pdb.set_trace() time_idx = utils.closest(wrf_times_epoch,t_epoch) return time_idx @@ -224,7 +234,7 @@ def destagger(self,data,ax): Don't destagger in x/y for columns """ - # Check for dimensions of 1. + # Check for dimensions of 1. # If it exists, don't destagger it. shp = data.shape @@ -295,9 +305,9 @@ def compute_pmsl(self,slices,**kwargs): HGT = self.get('HGT',slices) temp = T2 + (6.5*HGT)/1000.0 - pmsl = P*N.exp(9.81/(287.0*temp)*HGT) + pmsl = P*N.exp(9.81/(287.0*temp)*HGT) - #sm = kwargs.get('smooth',1) + #sm = kwargs.get('smooth',1) #data = pmsl[0,::sm,::sm] #return data return pmsl @@ -325,7 +335,7 @@ def compute_mixing_ratios(self,slices,**kwargs): rh = qc + qr + qi + qs + qg rv = qv - return rh, rv + return rh, rv def compute_dptp(self,slices,**kwargs): dpt = self.get('dpt',slices) @@ -344,7 +354,7 @@ def compute_dpt(self,slices,**kwargs): theta = self.get('theta',slices) rh, rv = self.compute_mixing_ratios(slices) - dpt = theta * (1 + 0.61*rv - rh) + dpt = theta * (1 + 0.61*rv - rh) return dpt def compute_geopotential_height(self,slices,**kwargs): @@ -411,8 +421,8 @@ def compute_shear(self,slices,**kwargs): topm,Z[0,:,i,j],range(self.z_dim))) botidx[i,j] = round(N.interp( botm,Z[0,:,i,j],range(self.z_dim))) - ushear[i,j] = u[0,topidx[i,j],i,j] - u[0,botidx[i,j],i,j] - vshear[i,j] = v[0,topidx[i,j],i,j] - v[0,botidx[i,j],i,j] + ushear[i,j] = u[0,topidx[i,j],i,j] - u[0,botidx[i,j],i,j] + vshear[i,j] = v[0,topidx[i,j],i,j] - v[0,botidx[i,j],i,j] # Find indices of bottom and top levels # topidx = N.where(abs(Z-topm) == abs(Z-topm).min(axis=1)) diff --git a/postWRF/postWRF/xsection.py b/postWRF/postWRF/xsection.py index c997430..5de836e 100644 --- a/postWRF/postWRF/xsection.py +++ b/postWRF/postWRF/xsection.py @@ -1,6 +1,6 @@ """Create cross-sections through WRF data. -This can be time-height or distance-height. +This can be time-height or distance-height. The height can be pressure, model, geometric, or geopotential The output can be saved to a pickle file. @@ -17,7 +17,9 @@ class CrossSection(Figure): - def __init__(self,latA=0,lonA=0,latB=0,lonB=0): + def __init__(self,config,wrfout,latA=0,lonA=0,latB=0,lonB=0): + super(BirdsEye,self).__init__(config,wrfout) + if latA and lonA and latB and lonB: print("Using user-defined lat/lon transects.") else: @@ -28,8 +30,7 @@ def __init__(self,latA=0,lonA=0,latB=0,lonB=0): self.lonA = lonA self.latB = latB self.lonB = lonB - - self.plot_xs() + def popup_transect(self): """ @@ -85,7 +86,7 @@ def get_height(self,t,x,y,z,pts): # TODO: import metconstants as mc return heighthalf, terrain_z - def plot_xs(self): + def plot_xs(self,v): """ Inputs: v : variable to plot, from this list: @@ -104,7 +105,7 @@ def plot_xs(self): angle = N.arctan((yy[-1]-yy[0])/(xx[-1]-xx[0])) # In radians # Get terrain heights - terrain_z, heighthalf = get_height(xx,yy,Nz,hyp_pts) + terrain_z, heighthalf = get_height(xx,yy,Nz,hyp_pts) # Set up plot # Length of x-section in km @@ -113,7 +114,7 @@ def plot_xs(self): # Generate ticks along cross-section xticks = N.arange(0,xs_len,xs_len/hyp_pts) - xlabels = [r"%3.0f" %t for t in xticks] + xlabels = [r"%3.0f" %t for t in xticks] grid = N.swapaxes(N.repeat(N.array(xticks).reshape(hyp_pts,1),self.W.nz,axis=1),0,1) # Plotting diff --git a/utils/GIS_tools.py b/utils/GIS_tools.py new file mode 100644 index 0000000..96714af --- /dev/null +++ b/utils/GIS_tools.py @@ -0,0 +1,826 @@ +#import scipy.ndimage as nd +from netCDF4 import Dataset +import calendar +import collections +import fnmatch +import math +import matplotlib as M +import numpy as N +import os +import pdb +import sys +import time + +import unix_tools as utils + +def decompose_wind(wspd,wdir,convert=0): + # Split wind speed/wind direction into u,v + if (type(wspd) == N.array) & (type(wdir) == N.array): + uwind = N.array([-s * N.sin(N.radians(d)) if ((s>-1)&(d>-1)) else -9999 + for s,d in zip(wspd,wdir)]) + vwind = N.array([-s * N.cos(N.radians(d)) if ((s>-1)&(d>-1)) else -9999 + for s,d in zip(wspd,wdir)]) + else: + uwind = -wspd * N.sin(N.radians(wdir)) + vwind = -wspd * N.cos(N.radians(wdir)) + if convert == 'ms_kt': + uwind *= 1.94384449 + vwind *= 1.94384449 + elif convert == 'ms_mph': + uwind *= 2.23694 + vwind *= 2.23694 + elif convert == 'kt_ms': + uwind *= 0.51444444 + vwind *= 0.51444444 + else: + pass + return uwind, vwind + +def combine_wind_components(u,v): + wdir = N.degrees(N.arctan2(u,v)) + 180 + wspd = N.sqrt(u**2 + v**2) + return wspd, wdir + +def convert_kt2ms(wspd): + wspd_ms = wspd*0.51444 + return wspd_ms + +def dewpoint(T,RH): # Lawrence 2005 BAMS? + #T in C + #RH in 0-100 format + es = 6.11 * (10**((7.5*T)/(237.7+T))) + e = es * RH/100.0 + alog = 0.43429*N.log(e) - 0.43429*N.log(6.11) + Td = (237.7 * alog)/(7.5-alog) + #pdb.set_trace() + return Td + +def csvprocess(data,names,convert=0): + # Get stations + stationlist = N.unique(data['stid']) # Stations in the record + stnum = len(stationlist) # Number of them + + + # Create dictionary from data + D = {} # Initialise dictionary of data + for s in stationlist: + D[s] = {} # Initialise dictionary of station/s + print 'Loading station data for ' + s + those = N.where(data['stid']==s) # Indices of obs + obNum = len(those[0]) # Number of obs + + # Entries from MesoWest data + for n in names: + D[s][n] = data[n][those] + + # Sort times + year = [int(t[0:4]) for t in D[s]['tutc']] + month = [int(t[4:6]) for t in D[s]['tutc']] + day = [int(t[6:8]) for t in D[s]['tutc']] + hour = [int(t[9:11]) for t in D[s]['tutc']] + minute = [int(t[11:13]) for t in D[s]['tutc']] + + # Create python time + D[s]['pytime'] = N.empty(obNum) + for i in range(0,obNum-1): + D[s]['pytime'][i] = cal.timegm([year[i],month[i],day[i],hour[i],minute[i],0]) + + # Convert to S.I. units + if convert == 1: + D[s]['tmpc'] = (5/9.0) * (D[s]['tmpf']-32) + D[s]['dptc'] = (5/9.0) * (D[s]['dptf']-32) + D[s]['wsms'] = D[s]['wskn']*0.51444 + D[s]['wgms'] = D[s]['wgkn']*0.51444 + + ### DATA PROCESSING + # Find time of biggest wind increase (DSWS initiation) + + D[s]['dVdt'] = N.zeros(obNum) # This is our array of d(wind)/d(time) + for i in range(0,obNum-1): + if i == 0: + pass # First record is zero as there's no gradient yet + else: + D[s]['dVdt'][i] = ((D[s]['wsms'][i] - D[s]['wsms'][i-1])/ + (D[s]['pytime'][i] - D[s]['pytime'][i-1])) + if any(D[s]['dVdt']) == False: + # Data were absent (-9999) or rubbish (0) + D[s]['mwi'] = -9999 + D[s]['mwi_t'] = -9999 + else: + D[s]['mwi'] = D[s]['dVdt'].max() # max wind increase + loc = N.where(D[s]['dVdt']==D[s]['mwi']) + D[s]['mwi_t'] = D[s]['pytime'][loc][0] # time of max wind increase + + # Find time of maximum wind gust + if any(D[s]['wgms']) == False: + D[s]['mg'] = -9999 + D[s]['mg_t'] = -9999 + else: + D[s]['mg'] = D[s]['wgms'].max() # Maximum gust at the station + loc = N.where(D[s]['wgms']==D[s]['mg']) + D[s]['mg_t'] = D[s]['pytime'][loc][0] # time of max gust + + # Find time of maximum wind speed + if any(D[s]['wsms']) == False: + D[s]['ms'] = -9999 + D[s]['ms_t'] = -9999 + else: + D[s]['ms'] = D[s]['wsms'].max() # Maximum gust at the station + loc = N.where(D[s]['wsms']==D[s]['ms']) + D[s]['ms_t'] = D[s]['pytime'][loc][0] # time of max gust + + + # Frequency of observation in minutes + try: + D[s]['dt'] = (D[s]['pytime'][1] - D[s]['pytime'][0]) / 60 + except IndexError: + D[s]['dt'] = -9999 + + # Find lowest pressures + try: + D[s]['lowp'] = D[s]['alti'].min() + except IndexError: + D[s]['lowp'] = -9999 + D[s]['lowp_t'] = -9999 + finally: + if D[s]['lowp'] != -9999: + D[s]['lowp_t'] = N.where(D[s]['alti']==D[s]['lowp']) + else: + pass + + return D + +# WRF terrain in height above sea level +def WRFterrain(fileselection='control',dom=1): + if fileselection=='nouintah': + fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/wrfout/1/NAM/2011112906_nouintah/wrfout_d0'+str(dom)+'_2011-11-29_06:00:00' + elif fileselection=='withuintah': + fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/wrfout/1/NAM/2011112906_withuintah/wrfout_d0'+str(dom)+'_2011-11-29_06:00:00' + else: + fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/WRFV3/test/em_real/wrfout_1.3_wasatch/wrfout_d0'+str(dom)+'_2011-12-01_00:00:00' + nc = Dataset(fname,'r') + terrain = nc.variables['HGT'][0,:,:] + xlong = nc.variables['XLONG'][0] + xlat = nc.variables['XLAT'][0] + return terrain, xlong, xlat + +# WRF terrain in pressure coords...from first time stamp (won't vary THAT much for a tropospheric plot!) +# SLICE keyword, when true, gives 1-D vector through middle of square. + +def WRFterrain_P(fileselection='control',dom=1,slice=0): + if fileselection=='nouintah': + fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/wrfout/1/NAM/2011112906_nouintah/wrfout_d0'+str(dom)+'_2011-11-29_06:00:00' + elif fileselection=='withuintah': + fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/wrfout/1/NAM/2011112906_withuintah/wrfout_d0'+str(dom)+'_2011-11-29_06:00:00' + else: + fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/WRFV3/test/em_real/wrfout_1.3_wasatch/wrfout_d0'+str(dom)+'_2011-12-01_00:00:00' + nc = Dataset(fname,'r') + terrain = nc.variables['PSFC'][0,:,:] + xlong = nc.variables['XLONG'][0] + xlat = nc.variables['XLAT'][0] + if slice==1: + Nx = nc.getncattr('WEST-EAST_GRID_DIMENSION')-1 + Ny = nc.getncattr('SOUTH-NORTH_GRID_DIMENSION')-1 + xlong = xlong[Ny/2,:] + xlat = xlat[:,Nx/2] + return terrain, xlong, xlat + +# Constants +rE = 6378100 # radius of Earth in metres + +# Settings +# plt.rc('text',usetex=True) +# fonts = {'family':'Computer Modern','size':16} +# plt.rc('font',**fonts) +# height, width = (9,17) + +# outdir = '/uufs/chpc.utah.edu/common/home/u0737349/public_html/thesis/topoxs/' + +# Functions +# First, if topography data is not in memory, load it +# try: + # dataloaded +# except NameError: + # topodata, lats, lons = gridded_data.gettopo() + # dataloaded = 1 + +def get_map(Nlim,Elim,Slim,Wlim): + ymax,xmax = gridded_data.getXY(lats,lons,Nlim,Elim) #Here, x is lat, y is lon + ymin,xmin = gridded_data.getXY(lats,lons,Slim,Wlim) # Not sure why! + terrain = topodata[xmin:xmax,ymin:ymax] + xlat = lats[xmin:xmax] + xlon = lons[ymin:ymax] + #pdb.set_trace() + return terrain,xlat,xlon + +def get_cross_section(Alat, Alon, Blat, Blon): + # Now find cross-sections + Ax, Ay = gridded_data.getXY(lats,lons,Alat,Alon) + Bx, By = gridded_data.getXY(lats,lons,Blat,Blon) + + # Number of points along cross-section + xspt = int(N.hypot(Bx-Ax,By-Ay)) + xx = N.linspace(Ay,By,xspt).astype(int) + yy = N.linspace(Ax,Bx,xspt).astype(int) + + # Get terrain heights along this transect + heights = topodata[xx,yy] + + # Work out distance along this cross-section in m + xsdistance = gridded_data.xs_distance(Alat,Alon,Blat,Blon) + + xvals = N.linspace(0,xsdistance,xspt) + xlabels = ['%3.1f' %(x/1000) for x in xvals] + # Now plot cross-sections + fig = plt.figure(figsize=(width,height)) + plt.plot(xvals,heights) + delta = xspt/10 + plt.xticks(xvals[::delta],xlabels[::delta]) + plt.xlabel('Distance along cross-section (km)') + fname = 'test1.png' + plt.savefig(outdir+fname,bbox_inches='tight',pad_inches=0.3) + plt.clf() + +# datestring needs to be format yyyymmddhh +def wrf_nc_load(dom,var,ncfolder,datestr,thin,Nlim=0,Elim=0,Slim=0,Wlim=0): + datestr2 = datestr[0:4]+'-'+datestr[4:6]+'-'+datestr[6:8]+'_'+datestr[8:10]+':00:00' + fname = ncfolder+'wrfout_'+dom+'_'+datestr2 + try: + nc = Dataset(fname,'r') + except RuntimeError: + fname += '.nc' + finally: + nc = Dataset(fname,'r') + # Load in variables here - customise or automate perhaps? + u10 = nc.variables['U10'] + v10 = nc.variables['V10'] + terrain = nc.variables['HGT'][0,:,:] + times = nc.variables['Times'] + + ### PROCESSING + # x_dim and y_dim are the x and y dimensions of the model + # domain in gridpoints + x_dim = len(nc.dimensions['west_east']) + y_dim = len(nc.dimensions['south_north']) + + # Get the grid spacing + dx = float(nc.DX) + dy = float(nc.DY) + + width_meters = dx * (x_dim - 1) + height_meters = dy * (y_dim - 1) + + cen_lat = float(nc.CEN_LAT) + cen_lon = float(nc.CEN_LON) + truelat1 = float(nc.TRUELAT1) + truelat2 = float(nc.TRUELAT2) + standlon = float(nc.STAND_LON) + + # Draw the base map behind it with the lats and + # lons calculated earlier + + if Nlim: + m = Basemap(projection='lcc',lon_0=cen_lon,lat_0=cen_lat, + llcrnrlat=Slim,urcrnrlat=Nlim,llcrnrlon=Wlim, + urcrnrlon=Elim,rsphere=6371200.,resolution='h', + area_thresh=100) + else: + m = Basemap(resolution='i',projection='lcc', + width=width_meters,height=height_meters, + lat_0=cen_lat,lon_0=cen_lon,lat_1=truelat1, + lat_2=truelat2) + xlong = nc.variables['XLONG'][0] + xlat = nc.variables['XLAT'][0] + #xlong = xlongtot[N.where((xlongtot < Elim) & (xlongtot > Wlim))] + #xlat = xlattot[N.where((xlattot < Nlim) & (xlattot > Slim))] + #x,y = m(nc.variables['XLONG'][0],nc.variables['XLAT'][0]) + #px,py = m(xlongtot,xlattot) + #x,y = N.meshgrid(px,py) + x,y = m(xlong,xlat) + # This sets a thinned out grid point structure for plotting + # wind barbs at the interval specified in "thin" + #x_th,y_th = m(nc.variables['XLONG'][0,::thin,::thin],\ + # nc.variables['XLAT'][0,::thin,::thin]) + x_th, y_th = m(xlong[::thin, ::thin],xlat[::thin,::thin]) + data = (times,terrain,u10,v10) + return m, x, y, x_th, y_th, data, nc + +def mkloop(dom='d03',fpath='./'): + print "Creating a pretty loop..." + os.system('convert -delay 50 '+fpath+dom+'*.png > -loop '+fpath+dom+'_windloop.gif') + print "Finished!" + +def p_interpol(dom,var,ncfolder,datestr): + datestr2 = datestr[0:4]+'-'+datestr[4:6]+'-'+datestr[6:8]+'_'+datestr[8:10]+':00:00' + fname = ncfolder+'wrfout_'+dom+'_'+datestr2 + nc = Dataset(fname,'r') + var_data = nc.variables[var][:] + if var == 'T': # pert. theta + var_data += 300 # Add base state + pert_pressure = nc.variables['P'][:] #This is perturbation pressure + base_pressure = nc.variables['PB'][:] # This is base pressure + pressure = pert_pressure + base_pressure # Get into absolute pressure in Pa. + p_levels = N.arange(10000,100000,1000) + var_interp = N.zeros((pressure.shape[0],len(p_levels),pressure.shape[2],pressure.shape[3])) + #pdb.set_trace() + for (t,y,x),v in N.ndenumerate(var_interp[:,0,:,:]): + var_interp[t,:,y,x] = N.interp(p_levels,pressure[t,::-1,y,x],var_data[t,::-1,y,x]) + return var_interpi + +def hgt_from_sigma(nc): + vert_coord = (nc.variables['PH'][:] + nc.variables['PHB'][:]) / 9.81 + return vert_coord + +# This converts wrf's time to python and human time +def find_time_index(wrftime,reqtimetuple,tupleformat=1): + # wrftime = WRF Times in array + # reqtime = desired time in six-tuple + + # Convert required time to Python time if required + if tupleformat: + reqtime = calendar.timegm(reqtimetuple) + else: + reqtime = reqtimetuple + nt = wrftime.shape[0] + pytime = N.zeros([nt,1]) + t = wrftime + # Now convert WRF time to Python time + for i in range(nt): + yr = int(''.join(t[i,0:4])) + mth = int(''.join(t[i,5:7])) + day = int(''.join(t[i,8:10])) + hr = int(''.join(t[i,11:13])) + min = int(''.join(t[i,14:16])) + sec = int(''.join(t[i,17:19])) + pytime[i] = calendar.timegm([yr,mth,day,hr,min,sec]) + + # Now find closest WRF time + timeInd = N.where(abs(pytime-reqtime) == abs(pytime-reqtime).min())[0][0] + return timeInd + +# Find data for certain time, locations, level, variables +# For surface data +def TimeSfcLatLon(nc,varlist,times,latslons='all'): + # Time (DateTime in string) + if times == 'all': + timeInds = range(nc.variables['Times'].shape[0]) + elif len(times)==1: # If only one time is desired + # Time is in 6-tuple format + timeInds = find_time_index(nc.variables['Times'],times) # This function is from this module + elif len(times)==2: # Find all times between A and B + timeIndA = find_time_index(nc.variables['Times'],times[0]) + timeIndB = find_time_index(nc.variables['Times'],times[1]) + timeInds = range(timeIndA,timeIndB) + # Lat/lon of interest and their grid pointd + lats = nc.variables['XLAT'][:] + lons = nc.variables['XLONG'][:] + if latslons == 'all': + latInds = range(lats.shape[-2]) + lonInds = range(lons.shape[-1]) + else: + xmin,ymax = gridded_data.getXY(lats,lons,Nlim,Wlim) + xmax,ymin = gridded_data.getXY(lats,lons,Slim,Elim) + latInds = range(ymin,ymax) + lonInds = range(xmin,xmax) + + # Return sliced data + data = {} + for v in varlist: + data[v] = nc.variables[v][timeInds,latInds,lonInds] + # Reshape if only one time + if len(times)==1: + data[v] = N.reshape(data[v],(len(latInds),len(lonInds))) + return data + +# Get lat/lon as 1D arrays from WRF +def latlon_1D(nc): + Nx = nc.getncattr('WEST-EAST_GRID_DIMENSION')-1 + Ny = nc.getncattr('SOUTH-NORTH_GRID_DIMENSION')-1 + lats = nc.variables['XLAT'][0,:,Nx/2] + lons = nc.variables['XLONG'][0,Ny/2,:] + return lats, lons + +def wrfout_files_in(folders,dom=0,init_time='notset',descend=1,avoid=0, + unambiguous=0): + """ + Hunt through given folder(s) to find all occurrences of wrfout + files. + + Inputs: + folders : list of absolute paths to directories + dom : specify domain. None specified if zero. + init_time : tuple of initialisation time + descend : boolean: go into subfolders + avoid : string of filenames. if a subfolder contains + the string, do not descend into this one. + unambiguous : only return a single absolute path, else throw + an Exception. + + Returns: + wrfouts : list of absolute paths to wrfout files + """ + + folders = get_sequence(folders) + avoids = [] + if 'avoid': + # Avoid folder names with this string + # or list of strings + avoid = get_sequence(avoid) + for a in avoid: + avoids.append('/{0}/'.format(a)) + + + w = 'wrfout' # Assume the prefix + if init_time=='notset': + suffix = '*0' + # We assume the user has wrfout files in different folders for different times + else: + try: + it = utils.string_from_time('wrfout',init_time) + except: + print("Not a valid wrfout initialisation time; try again.") + raise Error + suffix = '*' + it + + if not dom: + # Presume all domains are desired. + prefix = w + '_d' + elif (dom > 8): + print("Domain is out of range. Choose number between 1 and 8 inclusive.") + raise IndexError + else: + dom = 'd{0:02d}'.format(dom) + prefix = w + '_' + dom + + wrfouts = [] + if descend: + for folder in folders: + for root,dirs,files in os.walk(folder): + for fname in fnmatch.filter(files,prefix+suffix): + skip_me = 0 + fpath = os.path.join(root,fname) + if avoids: + for a in avoids: + if a in fpath: + skip_me = 1 + else: + pass + if not skip_me: + wrfouts.append(fpath) + + else: + for folder in folders: + findfile = os.path.join(folder,prefix+suffix) + files = glob.glob(findfile) + # pdb.set_trace() + for f in files: + wrfouts.append(os.path.join(folder,f)) + # pdb.set_trace() + if unambiguous: + if not len(wrfouts) == 1: + print("Found {0} wrfout files.".format(len(wrfouts))) + raise Exception + else: + return wrfouts[0] + else: + return wrfouts + +def getXY(lats,lons,ptlat,ptlon): + # Find closest lat/lon in array + minlat = abs(lats-ptlat).min() + minlon = abs(lons-ptlon).min() + # Find where these are in the grid + wherelat = N.where(abs(lats-ptlat) == minlat) + wherelon = N.where(abs(lons-ptlon) == minlon) + # pdb.set_trace() + lat_idx = N.where(lats==lats[wherelat])[0][0] + lon_idx = N.where(lons==lons[wherelon])[0][0] + exactlat = lats[wherelat] + exactlon = lons[wherelon] + return lat_idx,lon_idx, exactlat, exactlon + +def gettopo(): + fname = '/uufs/chpc.utah.edu/common/home/u0737349/dsws/topodata/globe30.bin' + f = open(fname,'r') + fdata = N.fromfile(f,dtype='int16') + # Transposes and reshapes to a lat-lon grid + # Changes negative values to 0 (sea level) + xnum = 43200.0 + ynum = 18000.0 + topodata = N.flipud(N.reshape(fdata,(ynum,xnum))).clip(0) + #topodata = ((N.reshape(fdata,(xnum,ynum))).clip(0)) + f.close(); del fdata + # Define size of pixels + xpixel = 360/xnum + ypixel = 150/ynum # Note only 150 degrees! + # Create lat/lon grid + lats = N.arange(-60,90,ypixel)#[::-1] + lons = N.arange(-180,180,xpixel)#[::-1] + print 'Topographic data has been loaded. Everest is but a mere pixel.' + return topodata, lats, lons + +def xs_distance(Alat, Alon, Blat, Blon): + phi1 = N.radians(90.0-Alat) + phi2 = N.radians(90.0-Blat) + theta1 = N.radians(Alon) + theta2 = N.radians(Blon) + arc = math.acos(math.sin(phi1)*math.sin(phi2)*math.cos(theta1-theta2) + + math.cos(phi1)*math.cos(phi2)) + xsdistance = rE * arc + return xsdistance + +# This dstacks arrays, unless it's the first time through, in which case it initialises the variable +def dstack_loop(data, Dict, Key): + # Try evaluating dict[key]. If it doesn't exist, then initialise it + # If it does exist, stack data + try: + Dict[Key] + except KeyError: + stack = data + #Dict[Key] = data + else: + stack = N.dstack((Dict[Key],data)) + return stack + pass + +# Create thinned pressure levels for skew T barb plotting +def thinned_barbs(pres): + levels = N.arange(20000.0,105000.0,5000.0) + plocs = [] + for l in levels: + ploc = N.where(abs(pres-l)==(abs(pres-l).min()))[0][0] + plocs.append(ploc) + thin_locs = N.array(plocs) + return thin_locs # Locations of data at thinned levels + +def trycreate(loc): + try: + os.stat(loc) + except: + os.makedirs(loc) + +def padded_times(timeseq): + padded = ['{0:04d}'.format(t) for t in timeseq] + return padded + +def string_from_time(usage,t,dom=0,strlen=0,conven=0,**kwargs): + """ + conven : convection of MM/DD versus DD/MM + """ + + + if isinstance(t,str): + if usage == 'output': + usage = 'skip' # Time is already a string + elif usage == 'title': + pass + # if kwargs['itime']: # For averages or maxima over time + # itime = kwargs['itime'] + # ftime = kwargs['ftime'] + # else: + # pass + else: + raise Exception + elif isinstance(t,int): + # In this case, time is in datenum. Get it into tuple format. + t = time.gmtime(t) + else: + pass + + if usage == 'title': + # Generates string for titles + if not 'itime' in kwargs: # i.e. for specific times + #if not hasattr(kwargs,'itime'): # i.e. for specific times + strg = '{3:02d}:{4:02d}Z on {2:02d}/{1:02d}/{0:04d}'.format(*t) + else: # i.e. for ranges (average over time) + s1 = '{3:02d}:{4:02d}Z to '.format(*kwargs['itime']) + s2 = '{3:02d}:{4:02d}Z'.format(*kwargs['ftime']) + strg = s1 + s2 + elif usage == 'wrfout': + # Generates string for wrfout file finding + # Needs dom + if not dom: + print("No domain specified; using domain #1.") + dom = 1 + strg = ('wrfout_d0' + str(dom) + + '{0:04d}-{1:02d}-{2:02d}_{3:02d}:{4:02d}:{5:02d}'.format(*t)) + elif usage == 'output': + if not conven: + # No convention set, assume DD/MM (I'm biased) + conven = 'full' + # Generates string for output file creation + if conven == 'DM': + strg = '{2:02d}{1:02d}_{3:02d}{4:02d}'.format(*t) + elif conven == 'MD': + strg = '{1:02d}{2:02d}_{3:02d}{4:02d}'.format(*t) + elif conven == 'full': + strg = '{0:04d}{1:02d}{2:02d}{3:02d}{4:02d}'.format(*t) + else: + print("Set convention for date format: DM or MD.") + elif usage == 'dir': + # Generates string for directory names + # Needs strlen which sets smallest scope of time for string + if not strlen: + print("No timescope strlen set; using hour as minimum.") + strlen = 'hour' + n = lookup_time(strlen) + strg = "{0:04d}".format(t[0]) + ''.join( + ["{0:02d}".format(a) for a in t[1:n+1]]) + elif usage == 'skip': + strg = t + else: + print("Usage for string not valid.") + raise Exception + return strg + +def lookup_time(str): + D = {'year':0, 'month':1, 'day':2, 'hour':3, 'minute':4, 'second':5} + return D[str] + +def get_level_naming(va,lv,**kwargs): + #lv = kwargs['lv'] + if lv < 1500: + return str(lv)+'hPa' + elif lv == 2000: + return 'sfc' + elif lv.endswith('K'): + return lv + elif lv.endswith('PVU'): + return lv + elif lv.endswith('km'): + return lv + elif lv == 'all': + if va == 'shear': + name = '{0}to{1}'.format(kwargs['bottom'],kwargs['top']) + return name + else: + return 'all_lev' + + +def level_type(lv): + """ Check to see what type of level is requested by user. + + """ + if lv < 1500: + return 'isobaric' + elif lv == 2000: + return 'surface' + elif lv.endswith('K'): + return 'isentropic' + elif lv.endswith('PVU'): + return 'PV-surface' + elif lv.endswith('km'): + return 'geometric' + elif lv == 'all': + return 'eta' + else: + print('Unknown vertical coordinate.') + raise Exception + +def closest(arr,val): + """ + Find index of closest value. + Only working on 1D array right now. + + Inputs: + val : required value + arr : array of values + + Output: + + idx : index of closest value + + """ + idx = N.argmin(N.abs(arr - val)) + return idx + +def dstack_loop(data, obj): + """ + Tries to stack numpy array (data) into 'stack' object (obj). + If obj doesn't exist, then initialise it + If obj does exist, stack data. + """ + if isinstance(obj,N.ndarray): + stack = N.dstack((obj,data)) + else: + stack = data + + return stack + +def dstack_loop_v2(data, obj): + """ + Need to set obj = 0 at start of loop in master script + + Tries to stack numpy array (data) into 'stack' object (obj). + If obj doesn't exist, then initialise it + If obj does exist, stack data. + """ + try: + print obj + except NameError: + stack = data + else: + stack = N.dstack((obj,data)) + + return stack + +def vstack_loop(data, obj): + """ + Need to set obj = 0 at start of loop in master script + + Tries to stack numpy array (data) into 'stack' object (obj). + If obj doesn't exist, then initialise it + If obj does exist, stack data. + """ + + if isinstance(obj,N.ndarray): + stack = N.vstack((obj,data)) + else: + stack = data + + return stack + + +def generate_times(idate,fdate,interval): + """ + Interval in seconds + """ + i = calendar.timegm(idate) + f = calendar.timegm(fdate) + times = range(i,f,interval) + return times + +def generate_colours(M,n): + """ + M : Matplotlib instance + n : number of colours you want + + Returns + """ + + colourlist = [M.cm.spectral(i) for i in N.linspace(0.08,0.97,n)] + return colourlist + +def get_sequence(x,sos=0): + """ Returns a sequence (tuple or list) for iteration. + Avoids an error for strings/integers. + SoS = 1 enables the check for a sequence of sequences (list of dates) + """ + # If sos is True, then use its first element as an example. + if sos: + y = x[0] + else: + y = x + + if isinstance(y, collections.Sequence) and not isinstance(y, basestring): + # i.e., if y is a list or tuple + return x + else: + # make a one-element list + return [x,] + +def convert_tuple_to_dntimes(times): + """ + Convert tuple or tuple of tuples to datenum date format. + """ + timelist = get_sequence(times,sos=1) + + dntimes = [] + for t in timelist: + dntimes.append(calendar.timegm(t)) + + return dntimes + +def ensure_sequence_datenum(times): + """ + Make sure times are in list-of-datenums format. + If not, convert them. + + Possibilities: + times = 123456 #1 + times = (123456,) #2 + times = (123456,234567) #3 + times = (2011,12,1,18,0,0) #4 + times = ((2011,12,1,18,0,0),(2011,12,2,6,0,0)) #5 + + Output: + dntimes = (123456,) or (123456,234567) + """ + if isinstance(times,int): + dntimes = [times,] #1 + elif isinstance(times,basestring): + print("Don't give me strings...") + raise Exception + elif isinstance(times,collections.Sequence): #2,3,4,5 + if not isinstance(times[0],int): #5 + dntimes = convert_tuple_to_dntimes(times) + elif times[0]<3000: #4 + dntimes = convert_tuple_to_dntimes(times) + else: #2,3 + dntimes = times + + return dntimes + + + + \ No newline at end of file diff --git a/utils/__init__.py b/utils/__init__.py index c74dee3..b921187 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,9 +1,5 @@ """ References these utilities in WEM.utils. """ - -from general_met import * -from gridded_data import * -from more_utils import * from unix_tools import * -from wrf_tools import * \ No newline at end of file +from GIS_tools import * \ No newline at end of file diff --git a/utils/unix_tools.py b/utils/unix_tools.py index b3135c4..7bc08fe 100644 --- a/utils/unix_tools.py +++ b/utils/unix_tools.py @@ -3,15 +3,16 @@ """ import os +import GIS_tools as utils def dir_from_naming(self,root,*args): - """ - Generate file path from arguments + """ + Generate file path from arguments - Inputs: - root : file path base - args : list of arguments to join as separate folders - """ + Inputs: + root : file path base + args : list of arguments to join as separate folders + """ l = [str(a) for a in args] path = os.path.join(root,*l) return path From 81a765b4a2fc08d7a6eaac415d53653e0dd336e4 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 16 Jul 2014 17:42:49 -0500 Subject: [PATCH 091/111] cross section mods --- postWRF/postWRF/main.py | 13 ++++----- postWRF/postWRF/wrfout.py | 45 +++++++++++++++++++------------ postWRF/postWRF/xsection.py | 53 +++++++++++++++++++++---------------- 3 files changed, 65 insertions(+), 46 deletions(-) diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index 33518d6..cab7035 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -103,7 +103,7 @@ def plot2D(self,vrbl,times,levels,wrf_sd=0,wrf_nc=0,out_sd=0,f_prefix=0,f_suffix """ - self.W = get_wrfout(wrf_sd,wrf_nc,dom=dom,unambiguous=1) + self.W = self.get_wrfout(wrf_sd,wrf_nc,dom=dom) if out_sd: outpath = os.path.join(self.C.output_root,out_sd) @@ -159,7 +159,7 @@ def plot2D(self,vrbl,times,levels,wrf_sd=0,wrf_nc=0,out_sd=0,f_prefix=0,f_suffix #rq[va]['vc'] = vc # Need this? # F.plot2D(va, t, lv, ) - def get_wrfout(self,wrf_sd=0,wrf_nc=0,dom=0) + def get_wrfout(self,wrf_sd=0,wrf_nc=0,dom=0): """Returns the WRFOut instance, given arguments: Optional inputs: @@ -743,7 +743,8 @@ def get_list(self,dic,key,default): def plot_xs(self,vrbl,times,latA=0,lonA=0,latB=0,lonB=0, wrf_sd=0,wrf_nc=0,out_sd=0,f_prefix=0,f_suffix=0,dom=0,): - """Plot cross-section. + """ + Plot cross-section. If no lat/lon transect is indicated, a popup appears for the user to pick points. The popup can have an overlaid field such as reflectivity @@ -766,10 +767,10 @@ def plot_xs(self,vrbl,times,latA=0,lonA=0,latB=0,lonB=0, """ - self.W = get_wrfout(wrf_sd,wrf_nc,dom=dom,unambiguous=1) - + self.W = self.get_wrfout(wrf_sd,wrf_nc,dom=dom) + XS = CrossSection(self.C,self.W,latA,lonA,latB,lonB) t_list = utils.ensure_sequence_datenum(times) for t in t_list: - XS.plot_xs(vrbl,t) \ No newline at end of file + XS.plot_xs(vrbl,t) diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 0598958..107cff7 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -44,15 +44,16 @@ def __init__(self,fpath,config=0): self.y_dim = len(self.nc.dimensions['south_north']) self.z_dim = len(self.nc.dimensions['bottom_top']) - # Loads variable list + # Loads variable lists self.fields = self.nc.variables.keys() - - """ + self.computed_fields = self.return_tbl().keys() + #import pdb; pdb.set_trace() + self.available_vrbls = self.fields + self.computed_fields + # TESTING A SMOOTHING METHOD - def test_smooth(self,data): - from scipy import ndimage - from skimage.feature - """ + #def test_smooth(self,data): + # from scipy import ndimage + # from skimage.feature def wrftime_to_datenum(self,times): """ @@ -263,14 +264,7 @@ def destagger(self,data,ax): data_unstag = 0.5*(data[sl0] + data[sl1]) return data_unstag - def compute(self,var,slices,**kwargs): - """ Look up method needed to return array of data - for required variable. - - Keyword arguments include settings for computation - e.g. top and bottom of shear computation - """ - + def return_tbl(self): tbl = {} tbl['shear'] = self.compute_shear tbl['thetae'] = self.compute_thetae @@ -289,8 +283,25 @@ def compute(self,var,slices,**kwargs): tbl['strongestwind'] = self.compute_strongest_wind tbl['PMSL'] = self.compute_pmsl tbl['RH'] = self.compute_RH - data = tbl[var](slices,**kwargs) - return data + + return tbl + + def compute(self,var,slices,lookup=0,**kwargs): + """ Look up method needed to return array of data + for required variable. + + Keyword arguments include settings for computation + e.g. top and bottom of shear computation + + lookup : enables a check to see if something can be + computed. Returns true or false. + """ + tbl = self.return_tbl() + if not lookup: + response = tbl[var](slices,**kwargs) + else: + response = lookup in tbl + return response def compute_RH(self,slices,**kwargs): T = self.get('temps',slices,units='C') diff --git a/postWRF/postWRF/xsection.py b/postWRF/postWRF/xsection.py index 5de836e..ab6e57e 100644 --- a/postWRF/postWRF/xsection.py +++ b/postWRF/postWRF/xsection.py @@ -15,10 +15,12 @@ import numpy as N from figure import Figure +import WEM.utils as utils + class CrossSection(Figure): def __init__(self,config,wrfout,latA=0,lonA=0,latB=0,lonB=0): - super(BirdsEye,self).__init__(config,wrfout) + super(CrossSection,self).__init__(config,wrfout) if latA and lonA and latB and lonB: print("Using user-defined lat/lon transects.") @@ -47,18 +49,19 @@ def popup_transect(self): def get_xy(self,lat,lon): """ Return x and y coordinates for given lat/lon. - """ + exactlat, exactlon : exact coordinates of closest x/y + """ + x,y,exactlat,exactlon = utils.getXY(self.W.lats1D,self.W.lons1D,lat,lon) return x,y - def get_height(self,t,x,y,z,pts): + def get_height(self,x,y,z,pts): """ Return terrain heights along cross-section TODO: What's the diff between the outputs? Inputs: - t : time index, as int x : x indices, as int y : y imdices, as int z : z indices, as int @@ -67,12 +70,14 @@ def get_height(self,t,x,y,z,pts): Outputs: terrain_z : terrain height heighthalf : who knows? + + Assuming t=0 """ # TODO: change API of W.get() - geopot = self.W.get('geopot',[t,z,y,x]) - dryairmass = self.W.get('dryairmass',[t,y,x]) - znw = self.W.get('ZNW',[t,z]) - znu = self.W.get('ZNU',[t,z]) + geopot = self.W.get('geopot',[0,z,y,x]) + dryairmass = self.W.get('dryairmass',[0,y,x]) + znw = self.W.get('ZNW',[0,z]) + znu = self.W.get('ZNU',[0,z]) heighthalf = N.zeros((len(z),pts)) @@ -86,7 +91,7 @@ def get_height(self,t,x,y,z,pts): # TODO: import metconstants as mc return heighthalf, terrain_z - def plot_xs(self,v): + def plot_xs(self,v,t): """ Inputs: v : variable to plot, from this list: @@ -94,9 +99,10 @@ def plot_xs(self,v): """ + tidx = self.W.get_time_idx(t) - xA, yA = get_xy(self.latA,self.lonA) - xB, yB = get_xy(self.latB,self.lonB) + xA, yA = self.get_xy(self.latA,self.lonA) + xB, yB = self.get_xy(self.latB,self.lonB) hyp_pts = int(N.hypot(xB-xA,yB-yA)) xx = N.linspace(xA,xB,hyp_pts) yy = N.linspace(yA,yB,hyp_pts) @@ -105,7 +111,7 @@ def plot_xs(self,v): angle = N.arctan((yy[-1]-yy[0])/(xx[-1]-xx[0])) # In radians # Get terrain heights - terrain_z, heighthalf = get_height(xx,yy,Nz,hyp_pts) + terrain_z, heighthalf = self.get_height(xx,yy,self.W.z_dim,hyp_pts) # Set up plot # Length of x-section in km @@ -115,7 +121,7 @@ def plot_xs(self,v): # Generate ticks along cross-section xticks = N.arange(0,xs_len,xs_len/hyp_pts) xlabels = [r"%3.0f" %t for t in xticks] - grid = N.swapaxes(N.repeat(N.array(xticks).reshape(hyp_pts,1),self.W.nz,axis=1),0,1) + grid = N.swapaxes(N.repeat(N.array(xticks).reshape(hyp_pts,1),self.W.z_dim,axis=1),0,1) # Plotting if nc.dx != nc.dy: @@ -131,20 +137,21 @@ def plot_xs(self,v): # If not, raise Exception. # TODO: vailable_vars in WRFOut to check this - if v is in self.W.available_vars: - data = self.W.get(v,[tidx,:,yint,xint]) - + ps = {'t':tidx, 'la':yint, 'la':xint} + + if v in self.W.available_vrbls: + data = self.W.get(v,ps) elif v is 'parawind': - u = self.W.get('U',[tidx,:,yint,xint] - v = self.W.get('V',[tidx,:,yint,xint] + u = self.W.get('U',ps) + v = self.W.get('V',ps) data = N.cos(angle)*u - N.sin(angle)*v clvs = u_wind_levels extends = 'both' CBlabel = r'Wind Speed (ms$^{-1}$)' elif v is 'perpwind': - u = self.W.get('U',[tidx,:,yint,xint] - v = self.W.get('V',[tidx,:,yint,xint] + u = self.W.get('U',ps) + v = self.W.get('V',ps) # Note the negative here. I think it's alright? TODO data = -N.cos(angle)*v + N.sin(angle)*u clvs = u_wind_levels @@ -161,9 +168,9 @@ def plot_xs(self,v): # What is this? TODO labeldelta = 15 - plt.yticks(N.arange(zmin,zmax+dz,dz) - plt.xlabel("Distance along cross-section (km)") - plt.ylabel("Height above sea level (m)") + fig.set_yticks(N.arange(zmin,zmax+dz,dz)) + fig.set_xlabel("Distance along cross-section (km)") + fig.set_ylabel("Height above sea level (m)") fpath = self.get_fpath() fname = self.get_fname() From 1a7d4388287cac16f06c36d780a3f6777a2100fc Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 24 Jul 2014 16:01:32 -0500 Subject: [PATCH 092/111] broken lazyWRF fixed --- lazyWRF/bin/sensitivity_ensemble.py | 174 ++++++++++++++++++ lazyWRF/lazyWRF/sensitivity_ensemble_old.py | 68 +++++++ postWRF/bin/bowecho_plot.py | 30 +-- postWRF/postWRF/defaults.py | 8 +- postWRF/postWRF/figure.py | 1 + postWRF/postWRF/main.py | 18 +- postWRF/postWRF/scales.py | 4 + postWRF/postWRF/wrfout.py | 44 ++++- postWRF/postWRF/xsection.py | 191 ++++++++++++++------ utils/GIS_tools.py | 1 - 10 files changed, 455 insertions(+), 84 deletions(-) create mode 100644 lazyWRF/bin/sensitivity_ensemble.py create mode 100644 lazyWRF/lazyWRF/sensitivity_ensemble_old.py diff --git a/lazyWRF/bin/sensitivity_ensemble.py b/lazyWRF/bin/sensitivity_ensemble.py new file mode 100644 index 0000000..edf7b70 --- /dev/null +++ b/lazyWRF/bin/sensitivity_ensemble.py @@ -0,0 +1,174 @@ +# Run ensemble of SKEB, microphysics, or both +# We assume met_em files have all been created + +# RUN THIS ON CHINOOK +# ROCKS JOBS ARE SUBMITTED VIA DERECHO (SSH) +# Files are moved to /tera9/ for storage +# Quicklook images are created after each run +# Script is executed from /home/jrlawson/pythoncode/plotting/scripts/ +# Saved to /home/jrlawson/pythoncode/plotting/output/ + +# Imports +import subprocess +import pdb +import os +import time +import sys +import pexpect +from sshls import ssh_command + +sys.path.append('/home/jrlawson/pythoncode/plotting/scripts/') +import plot_quicklooks + +# Settings +pause_to_check = 0 # To check namelists before job submission +#datestring = '2009091000' # Case +datestring = '20110419' +skipfirst = 0 # Needing to start halfway through an ensemble +#datestring = '20130815' +ICens = 'p04' # Initial conditions for ensembles +IC = 'GEFSR2' # Model for initial/boundary conditions in WRF +experiment = 'STCH' #STCH or MXMP or STCH_MXMP +pathtoWRF = '/ptmp/jrlawson/WRFV3/run/' # Run this file in WRF directory +MPn = 8 # For SKEB: pick constant MP scheme +GHn = 0 # For SKEB: pick constant graupel/hail option +MP = 'ICBC' # For SKEB: name of MP scheme + +def edit_namelist_input(old,new,pathtoWRF=pathtoWRF): + # Pad old and new with a space before and after + # to avoid partial matches + + old = ' '+old+' ' + #new = ' '+new+' ' + ninput = open(pathtoWRF+'namelist.input','r').readlines() + exists = 0 + for idx, line in enumerate(ninput): + if old in line: + exists = 1 + ninput[idx]= ninput[idx][:39] + new + " \n" + nameout = open(pathtoWRF+'namelist.input','w') + nameout.writelines(ninput) + nameout.close() + break + if not exists: + print("Line %s not found" % old) + raise Exception + else: + return + +def create_quicklook(datestring,ICmodel,ensembletype,ICens,ens,pathtonc): + varlist = ['cref','wind'] # Plot simulated reflectivity and 10m wind + for v in varlist: + plot_quicklooks.plotquick(datestring,ICmodel,ensembletype,ICens,ens,v,pathtonc) + return + +ensembletype = experiment # Same thing, whatever + +if ensembletype == 'STCH': # 10 members + ensnames = ['s' + '%02u' %n for n in range(1,11)] +elif ensembletype == 'MXMP': # 11 members + # See p1194 of Adams-Selin "Sensitivity of Bow-Echo Sim..." 2013 WAF + # With adaptations to method thanks to pers. comm. 2013 + # Setting graupel_param to zero is graupel; to one, it is hail (see Fortran code for MP schemes) + ensnames = ['WSM6_Grau','WSM6_Hail','Kessler','Ferrier','WSM5','WDM5','Lin','WDM6_Grau','WDM6_Hail','Morrison_Grau','Morrison_Hail'] + scheme_no = [6,6,1,5,4,14,2,16,16,10,10] + graupel_param = [0,1,0,0,0,0,0,0,1,0,1] +elif ensembletype == 'STCH_MXMP': # 11 members + ensnames = ['WSM6_Grau_STCH','WSM6_Hail_STCH','Kessler_STCH','Ferrier_STCH','WSM5_STCH','WDM5_STCH','Lin_STCH','WDM6_Grau_STCH','WDM6_Hail_STCH','Morrison_Grau_STCH','Morrison_Hail_STCH'] + scheme_no = [6,6,1,5,4,14,2,16,16,10,10] + graupel_param = [0,1,0,0,0,0,0,0,1,0,1] + +for n,ens in enumerate(ensnames): + if n 1 and pres[k-1] <= p): + k = k-1 + + if (pres[k] > p): + w = 0.0 + elif (pres[k-1] < p): + w = 1.0 + else: + w = (p-pres[k])/(pres[k-1]-pres[k]) + + return (1.0-w)*geopot[k] + w*geopot[k-1] + def plot_xs(self,vrbl,ttime,outpath): """ - tidx = self.W.get_time_idx(t) + Inputs: + vrbl : variable to plot, from this list: + parawind,perpwind,wind,U,V,W,T,RH, + ttime : time in ... format + outpath : absolute path to directory to save output - xA, yA = self.get_xy(self.latA,self.lonA) - xB, yB = self.get_xy(self.latB,self.lonB) + """ + # pdb.set_trace() + tidx = self.W.get_time_idx(ttime) + xA, yA = self.get_xy_from_latlon(self.latA,self.lonA) + xB, yB = self.get_xy_from_latlon(self.latB,self.lonB) hyp_pts = int(N.hypot(xB-xA,yB-yA)) xx = N.linspace(xA,xB,hyp_pts) yy = N.linspace(yA,yB,hyp_pts) xint = xx.astype(int) yint = yy.astype(int) angle = N.arctan((yy[-1]-yy[0])/(xx[-1]-xx[0])) # In radians + # pdb.set_trace() # Get terrain heights - terrain_z, heighthalf = self.get_height(xx,yy,self.W.z_dim,hyp_pts) + terrain_z, heighthalf = self.get_height(tidx,xint,yint,self.W.z_dim,hyp_pts) # Set up plot # Length of x-section in km @@ -124,24 +179,26 @@ def plot_xs(self,v,t): grid = N.swapaxes(N.repeat(N.array(xticks).reshape(hyp_pts,1),self.W.z_dim,axis=1),0,1) # Plotting - if nc.dx != nc.dy: + if self.W.dx != self.W.dy: print("Square domains only here") else: - # TODO: define zmin, zmax, dz - fig.axis([0,(hyp_pts*self.W.dx/1000.0)-1,zmin,zmax+dz]) + # TODO: allow easier change of defaults? + self.fig.gca().axis([0,(hyp_pts*self.W.dx/1000.0)-1,self.D.plot_zmin,self.D.plot_zmax+self.D.plot_dz]) # Logic time # First, check to see if v is in the list of computable or default # variables within WRFOut (self.W) # If not, maybe it can be computed here. # If not, raise Exception. - + # pdb.set_trace() # TODO: vailable_vars in WRFOut to check this - ps = {'t':tidx, 'la':yint, 'la':xint} + ps = {'t':tidx, 'la':yint, 'lo':xint} - if v in self.W.available_vrbls: - data = self.W.get(v,ps) - elif v is 'parawind': + if vrbl in self.W.available_vrbls: + data = self.W.get(vrbl,ps) + + + elif vrbl is 'parawind': u = self.W.get('U',ps) v = self.W.get('V',ps) data = N.cos(angle)*u - N.sin(angle)*v @@ -149,7 +206,7 @@ def plot_xs(self,v,t): extends = 'both' CBlabel = r'Wind Speed (ms$^{-1}$)' - elif v is 'perpwind': + elif vrbl is 'perpwind': u = self.W.get('U',ps) v = self.W.get('V',ps) # Note the negative here. I think it's alright? TODO @@ -159,33 +216,57 @@ def plot_xs(self,v,t): CBlabel = r'Wind Speed (ms$^{-1}$)' else: - print("Unsupported variable",v) + print("Unsupported variable",vrbl) raise Exception - fig.plot(xticks,terrain_z,color='k') - fig.fill_between(xticks,terrain_z,0,facecolor='lightgrey') + # lv = '2000' # Very hacky, setting surface level + # S = Scales(vrbl,lv) + + # multiplier = S.get_multiplier(vrbl,lv) + + # This is awful.... + # if S.cm: + # cmap = S.cm + # elif isinstance(S.clvs,N.ndarray): + # if plottype == 'contourf': + # cmap = plt.cm.jet + # else: + # pass + # else: + # cmap = plt.cm.jet + + + cf = self.ax.contourf(grid,terrain_z,data,alpha=0.6,levels=clvs, + extend='both')#, cmap=cmap) + #norm=, + + # pdb.set_trace() + self.ax.plot(xticks,terrain_z,color='k',) + self.ax.fill_between(xticks,terrain_z,0,facecolor='lightgrey') # What is this? TODO labeldelta = 15 - fig.set_yticks(N.arange(zmin,zmax+dz,dz)) - fig.set_xlabel("Distance along cross-section (km)") - fig.set_ylabel("Height above sea level (m)") - - fpath = self.get_fpath() - fname = self.get_fname() - self.save(fig,fpath,fname) - plt.close() + self.ax.set_yticks(N.arange(self.D.plot_zmin, + self.D.plot_zmax+self.D.plot_dz,self.D.plot_dz)) + self.ax.set_xlabel("Distance along cross-section (km)") + self.ax.set_ylabel("Height above sea level (m)") + + datestr = utils.string_from_time('output',ttime) + naming = [vrbl,'xs',datestr] + fname = self.create_fname(*naming) + self.save(self.fig,outpath,fname) + #self.close() # Save a colorbar # Only if one doesn't exist - self.just_one_colorbar() + self.just_one_colorbar(outpath,fname+'CB',cf,label=CBlabel) - def just_one_colorbar(self,fpath): + def just_one_colorbar(self,fpath,fname,cf,label): """docstring for just_one_colorbar""" try: with open(fpath): pass except IOError: - self.create_colorbar(fpath) + self.create_colorbar(fpath,fname,cf,label=CBlabel) diff --git a/utils/GIS_tools.py b/utils/GIS_tools.py index 96714af..252a31e 100644 --- a/utils/GIS_tools.py +++ b/utils/GIS_tools.py @@ -568,7 +568,6 @@ def string_from_time(usage,t,dom=0,strlen=0,conven=0,**kwargs): conven : convection of MM/DD versus DD/MM """ - if isinstance(t,str): if usage == 'output': usage = 'skip' # Time is already a string From 99902a5aafa618077b69e608e287eb1dcd9e37ea Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 11 Aug 2014 16:08:01 -0500 Subject: [PATCH 093/111] blah --- postWRF/bin/bowecho_plot.py | 3 +- postWRF/postWRF/birdseye.py | 33 ++++++++++++++----- postWRF/postWRF/main.py | 63 ++++++++++++++++++++++++++++++------- utils/GIS_tools.py | 1 + 4 files changed, 80 insertions(+), 20 deletions(-) diff --git a/postWRF/bin/bowecho_plot.py b/postWRF/bin/bowecho_plot.py index e72f9c6..bbbdb89 100644 --- a/postWRF/bin/bowecho_plot.py +++ b/postWRF/bin/bowecho_plot.py @@ -93,7 +93,8 @@ wrf_sd = os.path.join(case,IC,en,ex) #print out_sd #print wrf_sd - p.plot2D('cref',times,levels,wrf_sd=wrf_sd,out_sd=out_sd) + p.plot_strongest_wind(itime,ftime,2000,wrf_sd=wrf_sd,out_sd=out_sd) + #p.plot2D('cref',times,levels,wrf_sd=wrf_sd,out_sd=out_sd) """ diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index fa24599..bacc0d0 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -4,6 +4,7 @@ import matplotlib.pyplot as plt from mpl_toolkits.basemap import Basemap import numpy as N +import collections from defaults import Defaults from figure import Figure @@ -66,7 +67,9 @@ def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1,plottype='contourf'): Inputs: vrbl : variable string - t : date/time in (YYYY,MM,DD,HH,MM,SS) format + t : date/time in (YYYY,MM,DD,HH,MM,SS) or datenum format + If tuple of two dates, it's start time and + end time, e.g. for finding max/average. lv : level dom : domain outpath : absolute path to output @@ -89,7 +92,24 @@ def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1,plottype='contourf'): smooth = 1 # Get indices for time, level, lats, lons - tidx = self.W.get_time_idx(t) + + if isinstance(t,collections.Sequence) and len(t)!=6: + # List of two dates, start and end + # pdb.set_trace() + + it_idx = self.W.get_time_idx(t[0]) + ft_idx = self.W.get_time_idx(t[1]) + assert ft_idx > it_idx + tidx = slice(it_idx,ft_idx,None) + title = "range" + datestr = "range" + else: + tidx = self.W.get_time_idx(t) + title = utils.string_from_time('title',t) + datestr = utils.string_from_time('output',t) + + + # Until pressure coordinates are fixed TODO lvidx = 0 latidx, lonidx = self.get_limited_domain(bounding,smooth=smooth) @@ -118,12 +138,12 @@ def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1,plottype='contourf'): cmap = S.cm elif isinstance(S.clvs,N.ndarray): if plottype == 'contourf': - plotargs = (x,y,data.reshape((la_n,lo_n)),S.clvs) + plotargs = (self.x,self.y,data.reshape((la_n,lo_n)),S.clvs) cmap = plt.cm.jet else: - plotargs = (x,y,data.reshape((la_n,lo_n)),S.clvs) + plotargs = (self.x,self.y,data.reshape((la_n,lo_n)),S.clvs) else: - plotargs = (x,y,data.reshape((la_n,lo_n))) + plotargs = (self.x,self.y,data.reshape((la_n,lo_n))) cmap = plt.cm.jet @@ -136,14 +156,13 @@ def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1,plottype='contourf'): # LABELS, TITLES etc if self.C.plot_titles: - title = utils.string_from_time('title',t) plt.title(title) if plottype == 'contourf' and self.C.colorbar: plt.colorbar(orientation='horizontal') # SAVE FIGURE + # pdb.set_trace() lv_na = utils.get_level_naming(vrbl,lv) - datestr = utils.string_from_time('output',t) naming = [vrbl,lv_na,datestr] if dom: naming.append(dom) diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index bb658ff..4a9afb0 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -14,22 +14,18 @@ Useful/utility scripts have been moved to WEM.utils. """ -import os -#import matplotlib as M -#M.use('Agg') -#import matplotlib.pyplot as plt -#from mpl_toolkits.basemap import Basemap -import fnmatch import calendar -import pdb -import itertools -import numpy as N -import time -import json -import cPickle as pickle +import collections import copy +import cPickle as pickle +import fnmatch import glob import itertools +import json +import numpy as N +import os +import pdb +import time from wrfout import WRFOut from axes import Axes @@ -695,7 +691,50 @@ def plot_streamlines(self,lv,times): 'streamlines',lv,disp_t)) self.F.plot_streamlines(lv,pt) + def plot_strongest_wind(self,itime,ftime,levels,wrf_sd=0,wrf_nc=0,out_sd=0,f_prefix=0,f_suffix=0, + bounding=0,dom=0): + """ + Plot strongest wind at level lv between itime and ftime. + + Path to wrfout file is in config file. + Path to plot output is also in config + + Inputs: + levels : level(s) for wind + wrf_sd : string - subdirectory of wrfout file + wrf_nc : filename of wrf file requested. + If no wrfout file is explicitly specified, the + netCDF file in that folder is chosen if unambiguous. + out_sd : subdirectory of output .png. + f_prefix : custom filename prefix + f_suffix : custom filename suffix + bounding : list of four floats (Nlim, Elim, Slim, Wlim): + Nlim : northern limit + Elim : eastern limit + Slim : southern limit + Wlim : western limit + smooth : smoothing. 0 is off. non-zero integer is the degree + of smoothing, to be specified. + dom : domain for plotting. If zero, the only netCDF file present + will be plotted. If list of integers, the script will loop over domains. + + + """ + self.W = self.get_wrfout(wrf_sd,wrf_nc,dom=dom) + + outpath = self.get_outpath(out_sd) + + # Make sure times are in datenum format and sequence. + it = utils.ensure_sequence_datenum(itime) + ft = utils.ensure_sequence_datenum(ftime) + + d_list = utils.get_sequence(dom) + lv_list = utils.get_sequence(levels) + + for l, d in itertools.product(lv_list,d_list): + F = BirdsEye(self.C,self.W) + F.plot2D('strongestwind',it+ft,l,d,outpath,bounding=bounding) def make_1D(self,data,output='list'): """ Make sure data is a time series diff --git a/utils/GIS_tools.py b/utils/GIS_tools.py index 252a31e..576d756 100644 --- a/utils/GIS_tools.py +++ b/utils/GIS_tools.py @@ -638,6 +638,7 @@ def lookup_time(str): def get_level_naming(va,lv,**kwargs): #lv = kwargs['lv'] + if lv < 1500: return str(lv)+'hPa' elif lv == 2000: From 53ea873cec5db97e797134ee6aa8f9a7f286d2f0 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 11 Aug 2014 17:47:22 -0500 Subject: [PATCH 094/111] Fixed cross section --- postWRF/postWRF/birdseye.py | 6 +++--- postWRF/postWRF/main.py | 8 +++++--- postWRF/postWRF/wrfout.py | 7 ++++++- postWRF/postWRF/xsection.py | 38 +++++++++++++++++++++++++++++-------- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index bacc0d0..37d8dd4 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -222,12 +222,12 @@ def basemap_setup(self,smooth=1): width_m = self.W.dx*(self.W.x_dim-1) height_m = self.W.dy*(self.W.y_dim-1) - + m = Basemap( projection='lcc',width=width_m,height=height_m, lon_0=self.W.cen_lon,lat_0=self.W.cen_lat,lat_1=self.W.truelat1, - lat_2=self.W.truelat2,resolution=basemap_res,area_thresh=500 - ) + lat_2=self.W.truelat2,resolution=basemap_res,area_thresh=500, + ax=self.ax) m.drawcoastlines() m.drawstates() m.drawcountries() diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index 4a9afb0..91372b9 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -785,7 +785,8 @@ def get_outpath(self,out_sd): return outpath def plot_xs(self,vrbl,times,latA=0,lonA=0,latB=0,lonB=0, - wrf_sd=0,wrf_nc=0,out_sd=0,f_prefix=0,f_suffix=0,dom=0,): + wrf_sd=0,wrf_nc=0,out_sd=0,f_prefix=0,f_suffix=0,dom=0, + clvs=0,ztop=0): """ Plot cross-section. @@ -807,7 +808,8 @@ def plot_xs(self,vrbl,times,latA=0,lonA=0,latB=0,lonB=0, out_sd : subdirectory of output .png. f_prefix : custom filename prefix f_suffix : custom filename suffix - + clvs : custom contour levels + ztop : highest km to plot. """ self.W = self.get_wrfout(wrf_sd,wrf_nc,dom=dom) @@ -818,4 +820,4 @@ def plot_xs(self,vrbl,times,latA=0,lonA=0,latB=0,lonB=0, t_list = utils.ensure_sequence_datenum(times) for t in t_list: - XS.plot_xs(vrbl,t,outpath) + XS.plot_xs(vrbl,t,outpath,clvs=clvs,ztop=ztop) diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index d07a1a4..3b455ad 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -297,12 +297,13 @@ def return_tbl(self): tbl['geopot'] = self.compute_geopotential tbl['Z'] = self.compute_geopotential_height tbl['dptp'] = self.compute_dptp #density potential temperature pert. - tbl['dpt'] = self.compute_dpt #density potential temperature pert. + tbl['dpt'] = self.compute_dpt #density potential temperature . tbl['buoyancy'] = self.compute_buoyancy tbl['strongestwind'] = self.compute_strongest_wind tbl['PMSL'] = self.compute_pmsl tbl['RH'] = self.compute_RH tbl['dryairmass'] = self.compute_dryairmass + tbl['QTOTAL'] = self.compute_qtotal return tbl @@ -372,6 +373,10 @@ def compute_mixing_ratios(self,slices,**kwargs): rv = qv return rh, rv + + def compute_qtotal(self,slices,**kwargs): + qtotal, _ = self.compute_mixing_ratios(slices) + return qtotal def compute_dptp(self,slices,**kwargs): dpt = self.get('dpt',slices) diff --git a/postWRF/postWRF/xsection.py b/postWRF/postWRF/xsection.py index 8883ca6..ca54e19 100644 --- a/postWRF/postWRF/xsection.py +++ b/postWRF/postWRF/xsection.py @@ -15,10 +15,12 @@ import numpy as N from figure import Figure import pdb +import matplotlib.pyplot as plt import WEM.utils as utils import metconstants as mc from scales import Scales +from birdseye import BirdsEye # from defaults import Defaults class CrossSection(Figure): @@ -144,7 +146,7 @@ def interp(self,geopot, pres, p): return (1.0-w)*geopot[k] + w*geopot[k-1] - def plot_xs(self,vrbl,ttime,outpath): + def plot_xs(self,vrbl,ttime,outpath,clvs=0,ztop=0): """ Inputs: vrbl : variable to plot, from this list: @@ -202,7 +204,7 @@ def plot_xs(self,vrbl,ttime,outpath): u = self.W.get('U',ps) v = self.W.get('V',ps) data = N.cos(angle)*u - N.sin(angle)*v - clvs = u_wind_levels + # clvs = u_wind_levels extends = 'both' CBlabel = r'Wind Speed (ms$^{-1}$)' @@ -235,15 +237,23 @@ def plot_xs(self,vrbl,ttime,outpath): # else: # cmap = plt.cm.jet - - cf = self.ax.contourf(grid,terrain_z,data,alpha=0.6,levels=clvs, - extend='both')#, cmap=cmap) + # if clvs: pass # This is where to get clvs... + #clvs = N.arange(-25,30,5) + kwargs = {} + kwargs['alpha'] = 0.6 + #kwargs['extend'] = 'both' + if isinstance(clvs,N.ndarray): + kwargs['levels'] = clvs + + cf = self.ax.contourf(grid,heighthalf,data[0,...],**kwargs)#, + # cf = self.ax.contourf(grid[0,:],grid[:,0],data[0,...],alpha=0.6,extend='both')#levels=clvs, + # extend='both')#, cmap=cmap) #norm=, + # cf = self.ax.contourf(data[0,...]) # pdb.set_trace() self.ax.plot(xticks,terrain_z,color='k',) self.ax.fill_between(xticks,terrain_z,0,facecolor='lightgrey') - # What is this? TODO labeldelta = 15 @@ -253,20 +263,32 @@ def plot_xs(self,vrbl,ttime,outpath): self.ax.set_ylabel("Height above sea level (m)") datestr = utils.string_from_time('output',ttime) + + if ztop: + self.ax.set_ylim([0,ztop*1000]) naming = [vrbl,'xs',datestr] fname = self.create_fname(*naming) self.save(self.fig,outpath,fname) #self.close() - + + CBlabel = str(vrbl) + # Save a colorbar # Only if one doesn't exist self.just_one_colorbar(outpath,fname+'CB',cf,label=CBlabel) + + self.draw_transect(outpath,fname+'Tsct') def just_one_colorbar(self,fpath,fname,cf,label): """docstring for just_one_colorbar""" try: with open(fpath): pass except IOError: - self.create_colorbar(fpath,fname,cf,label=CBlabel) + self.create_colorbar(fpath,fname,cf,label=label) + def draw_transect(self,outpath,fname): + B = BirdsEye(self.C,self.W) + m,x,y = B.basemap_setup() + m.drawgreatcircle(self.lonA,self.latA,self.lonB,self.latB) + self.save(B.fig,outpath,fname) \ No newline at end of file From e5dda93089ae8a8a0528b6ae559e1bec10e0e479 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 13 Aug 2014 12:33:41 -0500 Subject: [PATCH 095/111] Improving cross-sections --- postWRF/postWRF/wrfout.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 3b455ad..5fda6e3 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -811,4 +811,15 @@ def get_limits(self): Slim = self.lats[0] Wlim = self.lons[0] return Nlim, Elim, Slim, Wlim + + def cold_pool_strength(self): + pass + + def cold_pool_height(self): + pass + + + + + From 01a912b311b4ee851c6264ec18655b9a92441d3d Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 14 Aug 2014 11:40:10 -0600 Subject: [PATCH 096/111] Enabling axis object returned to top level --- postWRF/postWRF/birdseye.py | 44 +++++++++++++--------- postWRF/postWRF/figure.py | 8 ++-- postWRF/postWRF/main.py | 75 ++++++++++++++++++++++--------------- 3 files changed, 77 insertions(+), 50 deletions(-) diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 37d8dd4..f18be88 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -5,6 +5,7 @@ from mpl_toolkits.basemap import Basemap import numpy as N import collections +import os from defaults import Defaults from figure import Figure @@ -12,10 +13,10 @@ from scales import Scales class BirdsEye(Figure): - def __init__(self,config,wrfout): - super(BirdsEye,self).__init__(config,wrfout) + def __init__(self,config,wrfout,ax=0): + super(BirdsEye,self).__init__(config,wrfout,ax=ax) - def plot_data(self,data,mplcommand,fpath,pt,V=0): + def plot_data(self,data,mplcommand,p2p,fname,pt,V=0,no_title=0,save=1): """ Generic method that plots any matrix of data on a map @@ -23,25 +24,28 @@ def plot_data(self,data,mplcommand,fpath,pt,V=0): data : lat/lon matrix of data m : basemap instance mplcommand : contour or contourf - fpath : absolute filepath including name + p2p : path to plots + fname : filename for plot V : scale for contours - + no_title : switch to turn off title + save : whether to save to file """ # INITIALISE - self.fig = plt.figure() - self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user - self.bmap,x,y = self.basemap_setup() + # self.fig = plt.figure() + # self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user + self.fig.set_size_inches(5,5) + self.bmap,x,y = self.basemap_setup()#ax=self.ax) if mplcommand == 'contour': if not V: - self.bmap.contour(x,y,data) + f1 = self.bmap.contour(x,y,data) else: - self.bmap.contour(x,y,data,V) + f1 = self.bmap.contour(x,y,data,V) elif mplcommand == 'contourf': if not V: - self.bmap.contourf(x,y,data,alpha=0.5) + f1 = self.bmap.contourf(x,y,data,alpha=0.5) else: - self.bmap.contourf(x,y,data,V,alpha=0.5) + f1 = self.bmap.contourf(x,y,data,V,alpha=0.5) @@ -51,15 +55,21 @@ def plot_data(self,data,mplcommand,fpath,pt,V=0): """ #if self.C.plot_titles: title = utils.string_from_time('title',pt,tupleformat=0) - plt.title(title) + if not no_title: + plt.title(title) #if self.C.plot_colorbar: - plt.colorbar(orientation='horizontal') + cb = self.fig.colorbar(f1, orientation='horizontal') # SAVE FIGURE datestr = utils.string_from_time('output',pt,tupleformat=0) - self.fname = self.create_fname(fpath) # No da variable here - self.save(self.fig,self.p2p,self.fname) - self.fig.clf() + # self.fname = self.create_fname(fpath) # No da variable here + if save: + self.save(self.fig,p2p,fname) + + plt.close(self.fig) + return f1 + + # print("Plot saved to {0}.".format(os.path.join(p2p,fname))) #def plot2D(self,va,**kwargs): def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1,plottype='contourf'): diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index 79add12..633e5ad 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -16,7 +16,7 @@ from defaults import Defaults class Figure(object): - def __init__(self,config,wrfout): + def __init__(self,config,wrfout,ax=0): """ C : configuration settings W : data @@ -36,9 +36,11 @@ def __init__(self,config,wrfout): dpi = getattr(self.C,'DPI',self.D.dpi) # Create main figure - self.fig = plt.figure() + self.fig, self.ax = plt.subplots(1) + if ax: + self.ax = ax self.fig.set_dpi(dpi) - self.ax = self.fig.add_subplot(111) + # self.ax = self.fig.add_subplot(111) def create_fname(self,*naming): """Default naming should be: diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index 91372b9..d7417c4 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -27,6 +27,7 @@ import pdb import time + from wrfout import WRFOut from axes import Axes from figure import Figure @@ -492,45 +493,59 @@ def latlon(nlats,nlons): return DKE - def plot_diff_energy(self,ptype,energy,time,folder,fname,V): + def plot_diff_energy(self,ptype,energy,time,folder,fname,p2p,plotname,V, + no_title=0,ax=0): """ folder : directory holding computed data fname : naming scheme of required files + p2p : root directory for plots V : constant values to contour at """ sw = 0 DATA = self.load_data(folder,fname,format='pickle') - times = self.get_sequence(time) - - for n,t in enumerate(times): - for pn,perm in enumerate(DATA): - f1 = DATA[perm]['file1'] - f2 = DATA[perm]['file2'] - if sw==0: - # Get times and info about nc files - # First time to save power - W1 = WRFOut(f1) - permtimes = DATA[perm]['times'] - sw = 1 - - # Find array for required time - x = N.where(N.array(permtimes)==t)[0][0] - data = DATA[perm]['values'][x][0] - if not pn: - stack = data - else: - stack = N.dstack((data,stack)) - stack_average = N.average(stack,axis=2) - - #birdseye plot with basemap of DKE/DTE - F = BirdsEye(self.C,W1) # 2D figure class - #F.plot2D(va,t,en,lv,da,na) # Plot/save figure - fname_t = ''.join((fname,'_p{0:02d}'.format(n))) - F.plot_data(stack_average,'contourf',fname_t,t,V) - print("Plotting time {0} from {1}.".format(n,len(times))) - del data, stack + + if isinstance(time,collections.Sequence): + time = calendar.timegm(time) + + #for n,t in enumerate(times): + + for pn,perm in enumerate(DATA): + f1 = DATA[perm]['file1'] + f2 = DATA[perm]['file2'] + if sw==0: + # Get times and info about nc files + # First time to save power + W1 = WRFOut(f1) + permtimes = DATA[perm]['times'] + sw = 1 + + # Find array for required time + x = N.where(N.array(permtimes)==time)[0][0] + data = DATA[perm]['values'][x][0] + if not pn: + stack = data + else: + stack = N.dstack((data,stack)) + stack_average = N.average(stack,axis=2) + + if ax: + kwargs1 = {'ax':ax} + kwargs2 = {'save':0} + #birdseye plot with basemap of DKE/DTE + F = BirdsEye(self.C,W1,**kwargs1) # 2D figure class + #F.plot2D(va,t,en,lv,da,na) # Plot/save figure + tstr = utils.string_from_time('output',time) + fname_t = ''.join((plotname,'_{0}'.format(tstr))) + # fpath = os.path.join(p2p,fname_t) + fig_obj = F.plot_data(stack_average,'contourf',p2p,fname_t,time,V, + no_title=no_title,**kwargs2) + + if ax: + return fig_obj + + #print("Plotting time {0} from {1}.".format(n,len(times))) def plot_error_growth(self,ofname,folder,pfname,sensitivity=0,ylimits=0,**kwargs): """Plots line graphs of DKE/DTE error growth From 8daaea7f93bbc8c989289d029f5b094612852cc4 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 14 Aug 2014 17:05:37 -0500 Subject: [PATCH 097/111] Broken Clicker --- postWRF/postWRF/birdseye.py | 19 +++++++++++------- postWRF/postWRF/clicker.py | 36 ++++++++++++++++++++++++++++++++++ postWRF/postWRF/main.py | 39 +++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 postWRF/postWRF/clicker.py diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index f18be88..de74f5e 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -72,7 +72,8 @@ def plot_data(self,data,mplcommand,p2p,fname,pt,V=0,no_title=0,save=1): # print("Plot saved to {0}.".format(os.path.join(p2p,fname))) #def plot2D(self,va,**kwargs): - def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1,plottype='contourf'): + def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1, + plottype='contourf',save=1,return_data=0): """ Inputs: @@ -90,7 +91,7 @@ def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1,plottype='contourf'): Wlim : western limit smooth : smoothing. 1 is off. integer greater than one is the degree of smoothing, to be specified. - + save : whether to save to file """ # INITIALISE self.fig.set_size_inches(8,8) @@ -158,17 +159,17 @@ def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1,plottype='contourf'): if plottype == 'contourf': - self.bmap.contourf(*plotargs,cmap=cmap) + f1 = self.bmap.contourf(*plotargs,cmap=cmap) elif plottype == 'contour': - ctplt = self.bmap.contour(*plotargs,colors='k') + f1 = self.bmap.contour(*plotargs,colors='k') scaling_func = M.ticker.FuncFormatter(lambda x, pos:'{0:d}'.format(int(x*multiplier))) - plt.clabel(ctplt, inline=1, fmt=scaling_func, fontsize=9, colors='k') + plt.clabel(f1, inline=1, fmt=scaling_func, fontsize=9, colors='k') # LABELS, TITLES etc if self.C.plot_titles: plt.title(title) if plottype == 'contourf' and self.C.colorbar: - plt.colorbar(orientation='horizontal') + plt.colorbar(f1,orientation='horizontal') # SAVE FIGURE # pdb.set_trace() @@ -177,8 +178,12 @@ def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1,plottype='contourf'): if dom: naming.append(dom) self.fname = self.create_fname(*naming) - self.save(self.fig,outpath,self.fname) + if save: + self.save(self.fig,outpath,self.fname) plt.close() + if isinstance(data,N.ndarray): + return data.reshape((la_n,lo_n)) + def plot_streamlines(self,lv,pt,da=0): self.fig = plt.figure() diff --git a/postWRF/postWRF/clicker.py b/postWRF/postWRF/clicker.py new file mode 100644 index 0000000..2c6884a --- /dev/null +++ b/postWRF/postWRF/clicker.py @@ -0,0 +1,36 @@ +import matplotlib.pyplot as plt +import numpy as N + +# from figure import Figure + +class Clicker(object): + # def __init__(self,config,wrfout,ax=0): + def __init__(self,data=0,fig=0,ax=0): + # super(Clicker,self).__init__(config,wrfout,ax=ax) + if ax and fig: + self.ax = ax + self.fig = fig + elif ax or fig: + raise Exception + else: + self.fig, self.ax = plt.subplots(1,figsize=(5,5)) + + if isinstance(data,N.ndarray): + self.overlay_data(data) + + def click_x_y(self): + self.fig.canvas.mpl_connect('pick_event',self.onpick) + plt.show(self.fig) + + def onpick(self,event): + artist = event.artist + mouseevent = event.mouseevent + self.x = mouseevent.xdata + self.y = mouseevent.ydata + + def overlay_data(self,data,cmap='jet'): + xlen = data.shape[1] + ylen = data.shape[0] + self.ax.imshow(data,cmap=cmap,extent=(0,xlen,0,ylen), + picker=5) + return \ No newline at end of file diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index d7417c4..4e3ab06 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -39,6 +39,7 @@ from lookuptable import LookUpTable import WEM.utils as utils from xsection import CrossSection +from clicker import Clicker # TODO: Make this awesome @@ -836,3 +837,41 @@ def plot_xs(self,vrbl,times,latA=0,lonA=0,latB=0,lonB=0, t_list = utils.ensure_sequence_datenum(times) for t in t_list: XS.plot_xs(vrbl,t,outpath,clvs=clvs,ztop=ztop) + + def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,len_G=30,dom=1): + """ + Pick A, B points on sim ref overlay + This sets the angle between north and line AB + Also sets the length in along-line direction + For every gridpt along line AB: + Locate gust front via shear + Starting at front, do 3-grid-pt-average in line-normal + direction + + time : time (tuple or datenum) to plot + wrf_sd : string - subdirectory of wrfout file + wrf_nc : filename of wrf file requested. + If no wrfout file is explicitly specified, the + netCDF file in that folder is chosen if unambiguous. + out_sd : subdirectory of output .png. + len_G : length in km to compute behind front + dom : domain number + + """ + # Initialise + self.W = self.get_wrfout(wrf_sd,wrf_nc,dom=dom) + outpath = self.get_outpath(out_sd) + + # Plot sim ref, send basemap axis to clicker function + F = BirdsEye(self.C,self.W) + data = F.plot2D('cref',time,2000,dom,outpath,save=0,return_data=1) + C = Clicker(data=data) + + # Popup window and click two locations + C.click_x_y() + Ax, Ay = C.x, C.y + + C.click_x_y() + Bx, By = C.x, C.y + + # \ No newline at end of file From dec76d6ed8c979868ab59d324395ac76272e7b06 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 18 Aug 2014 17:06:01 -0500 Subject: [PATCH 098/111] Clicker popup fixed --- postWRF/bin/settings.py | 1 + postWRF/postWRF/birdseye.py | 146 ++++++++++++++++++++++-------------- postWRF/postWRF/clicker.py | 93 +++++++++++++++++++---- postWRF/postWRF/figure.py | 23 ++++++ postWRF/postWRF/main.py | 24 +++--- postWRF/postWRF/scales.py | 2 - postWRF/postWRF/wrfout.py | 46 +++++++++++- postWRF/postWRF/xsection.py | 40 ++++++++-- 8 files changed, 281 insertions(+), 94 deletions(-) diff --git a/postWRF/bin/settings.py b/postWRF/bin/settings.py index b0574e7..29832bb 100644 --- a/postWRF/bin/settings.py +++ b/postWRF/bin/settings.py @@ -9,5 +9,6 @@ def __init__(self): self.plot_titles = 1 self.terrain = 0 self.colorbar = 1 + self.matplotlibuse = 'gtkagg' #self.pickledir = '/home/jrlawson/data/sounding/WRFoutput/' diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index de74f5e..95c0e58 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -16,6 +16,46 @@ class BirdsEye(Figure): def __init__(self,config,wrfout,ax=0): super(BirdsEye,self).__init__(config,wrfout,ax=ax) + def get_contouring(self,vrbl,lv,V=0): + """ + Returns colourmap and contouring levels + V : manually override contour levels + """ + + # List of args and dictionary of kwargs + plotargs = [] + plotkwargs = {} + + # Scales object + S = Scales(vrbl,lv) + + if self.mplcommand == 'contour': + multiplier = S.get_multiplier(vrbl,lv) + + if isinstance(V,collections.Sequence): + clvs = V + else: + try: + clvs = S.clvs + except: + pass + + if S.cm: + plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),clvs] + plotkwargs['cmap'] = S.cm + elif isinstance(S.clvs,N.ndarray): + if self.mplcommand == 'contourf': + plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),clvs] + plotkwargs['cmap'] = plt.cm.jet + else: + plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),clvs] + else: + plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n))] + plotkwargs['cmap'] = plt.cm.jet + + # pdb.set_trace() + return plotargs, plotkwargs + def plot_data(self,data,mplcommand,p2p,fname,pt,V=0,no_title=0,save=1): """ Generic method that plots any matrix of data on a map @@ -35,19 +75,26 @@ def plot_data(self,data,mplcommand,p2p,fname,pt,V=0,no_title=0,save=1): # self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user self.fig.set_size_inches(5,5) self.bmap,x,y = self.basemap_setup()#ax=self.ax) + self.mplcommand = mplcommand + self.data = data - if mplcommand == 'contour': - if not V: - f1 = self.bmap.contour(x,y,data) - else: - f1 = self.bmap.contour(x,y,data,V) - elif mplcommand == 'contourf': - if not V: - f1 = self.bmap.contourf(x,y,data,alpha=0.5) - else: - f1 = self.bmap.contourf(x,y,data,V,alpha=0.5) - - + self.la_n = self.data.shape[-2] + self.lo_n = self.data.shape[-1] + + # if plottype == 'contourf': + # f1 = self.bmap.contourf(*plotargs,**plotkwargs) + # elif plottype == 'contour': + # plotkwargs['colors'] = 'k' + # f1 = self.bmap.contour(*plotargs,**plotkwargs) + # scaling_func = M.ticker.FuncFormatter(lambda x, pos:'{0:d}'.format(int(x*multiplier))) + # plt.clabel(f1, inline=1, fmt=scaling_func, fontsize=9, colors='k') + + plotargs, plotkwargs = self.get_contouring(vrbl,lv,V=V) + + if self.mplcommand == 'contour': + f1 = self.bmap.contour(*plotargs,**plotkwargs) + elif self.mplcommand == 'contourf': + f1 = self.bmap.contourf(*plotargs,**plotkwargs) # LABELS, TITLES etc """ @@ -96,6 +143,7 @@ def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1, # INITIALISE self.fig.set_size_inches(8,8) self.bmap,self.x,self.y = self.basemap_setup(smooth=smooth) + self.mplcommand = plottype # Make sure smooth=0 is corrected to 1 # They are both essentially 'off'. @@ -134,41 +182,45 @@ def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1, # FETCH DATA ncidx = {'t': tidx, 'lv': lvidx, 'la': latidx, 'lo': lonidx} - data = self.W.get(vrbl,ncidx)#,**vardict) + self.data = self.W.get(vrbl,ncidx)#,**vardict) - la_n = data.shape[-2] - lo_n = data.shape[-1] + self.la_n = self.data.shape[-2] + self.lo_n = self.data.shape[-1] # COLORBAR, CONTOURING - S = Scales(vrbl,lv) + plotargs, plotkwargs = self.get_contouring(vrbl,lv) - multiplier = S.get_multiplier(vrbl,lv) + # S = Scales(vrbl,lv) - if S.cm: - plotargs = (self.x,self.y,data.reshape((la_n,lo_n)),S.clvs) - cmap = S.cm - elif isinstance(S.clvs,N.ndarray): - if plottype == 'contourf': - plotargs = (self.x,self.y,data.reshape((la_n,lo_n)),S.clvs) - cmap = plt.cm.jet - else: - plotargs = (self.x,self.y,data.reshape((la_n,lo_n)),S.clvs) - else: - plotargs = (self.x,self.y,data.reshape((la_n,lo_n))) - cmap = plt.cm.jet + # multiplier = S.get_multiplier(vrbl,lv) + # if S.cm: + # plotargs = (self.x,self.y,data.reshape((la_n,lo_n)),S.clvs) + # cmap = S.cm + # elif isinstance(S.clvs,N.ndarray): + # if plottype == 'contourf': + # plotargs = (self.x,self.y,data.reshape((la_n,lo_n)),S.clvs) + # cmap = plt.cm.jet + # else: + # plotargs = (self.x,self.y,data.reshape((la_n,lo_n)),S.clvs) + # else: + # plotargs = (self.x,self.y,data.reshape((la_n,lo_n))) + # cmap = plt.cm.jet + # pdb.set_trace() - if plottype == 'contourf': - f1 = self.bmap.contourf(*plotargs,cmap=cmap) - elif plottype == 'contour': - f1 = self.bmap.contour(*plotargs,colors='k') + if self.mplcommand == 'contourf': + # f1 = self.bmap.contourf(*plotargs,cmap=cmap) + f1 = self.bmap.contourf(*plotargs,**plotkwargs) + elif self.mplcommand == 'contour': + plotkwargs['colors'] = 'k' + f1 = self.bmap.contour(*plotargs,**kwargs) scaling_func = M.ticker.FuncFormatter(lambda x, pos:'{0:d}'.format(int(x*multiplier))) plt.clabel(f1, inline=1, fmt=scaling_func, fontsize=9, colors='k') # LABELS, TITLES etc if self.C.plot_titles: plt.title(title) - if plottype == 'contourf' and self.C.colorbar: + if self.mplcommand == 'contourf' and self.C.colorbar: plt.colorbar(f1,orientation='horizontal') # SAVE FIGURE @@ -181,8 +233,8 @@ def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1, if save: self.save(self.fig,outpath,self.fname) plt.close() - if isinstance(data,N.ndarray): - return data.reshape((la_n,lo_n)) + if isinstance(self.data,N.ndarray): + return self.data.reshape((self.la_n,self.lo_n)) def plot_streamlines(self,lv,pt,da=0): @@ -231,27 +283,5 @@ def plot_streamlines(self,lv,pt,da=0): plt.clf() plt.close() - def basemap_setup(self,smooth=1): - # Fetch settings - basemap_res = getattr(self.C,'basemap_res',self.D.basemap_res) - - width_m = self.W.dx*(self.W.x_dim-1) - height_m = self.W.dy*(self.W.y_dim-1) - - m = Basemap( - projection='lcc',width=width_m,height=height_m, - lon_0=self.W.cen_lon,lat_0=self.W.cen_lat,lat_1=self.W.truelat1, - lat_2=self.W.truelat2,resolution=basemap_res,area_thresh=500, - ax=self.ax) - m.drawcoastlines() - m.drawstates() - m.drawcountries() - - # Draw meridians etc with wrff.lat/lon spacing - # Default should be a tenth of width of plot, rounded to sig fig - - s = slice(None,None,smooth) - x,y = m(self.W.lons[s,s],self.W.lats[s,s]) - return m, x, y diff --git a/postWRF/postWRF/clicker.py b/postWRF/postWRF/clicker.py index 2c6884a..2d7498c 100644 --- a/postWRF/postWRF/clicker.py +++ b/postWRF/postWRF/clicker.py @@ -1,36 +1,97 @@ +import matplotlib as M +M.use('gtkagg') + import matplotlib.pyplot as plt import numpy as N +import collections +import pdb # from figure import Figure +import colourtables as ct +from scales import Scales +from figure import Figure -class Clicker(object): +class Clicker(Figure): # def __init__(self,config,wrfout,ax=0): - def __init__(self,data=0,fig=0,ax=0): - # super(Clicker,self).__init__(config,wrfout,ax=ax) - if ax and fig: - self.ax = ax - self.fig = fig - elif ax or fig: - raise Exception - else: - self.fig, self.ax = plt.subplots(1,figsize=(5,5)) - + def __init__(self,config,wrfout,data=0,fig=0,ax=0): + super(Clicker,self).__init__(config,wrfout,ax=ax) + if isinstance(data,N.ndarray): - self.overlay_data(data) + self.bmap,self.x,self.y = self.basemap_setup() + S = Scales('cref',2000) + self.overlay_data(data,V=S.clvs,cmap=S.cm) def click_x_y(self): self.fig.canvas.mpl_connect('pick_event',self.onpick) plt.show(self.fig) + def draw_box(self): + self.rect = M.patches.Rectangle((0,0),1,1) + self.x0 = None + self.y0 = None + self.x1 = None + self.y1 = None + self.ax.add_patch(self.rect) + self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press) + self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release_box) + plt.show(self.fig) + + def draw_line(self): + self.line = M.lines.Line2D((0,0),(1,1)) + self.x0 = None + self.y0 = None + self.x1 = None + self.y1 = None + self.ax.add_line(self.line) + self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press) + self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release_line) + plt.show(self.fig) + + def on_press(self, event): + print 'press' + self.x0 = event.xdata + self.y0 = event.ydata + + def on_release_box(self, event): + print 'release' + self.x1 = event.xdata + self.y1 = event.ydata + self.rect.set_width(self.x1 - self.x0) + self.rect.set_height(self.y1 - self.y0) + self.rect.set_xy((self.x0, self.y0)) + self.ax.figure.canvas.draw() + + def on_release_line(self, event): + print 'release' + self.x1 = event.xdata + self.y1 = event.ydata + # self.rect.set_width(self.x1 - self.x0) + # self.rect.set_height(self.y1 - self.y0) + self.line.set_data((self.x0, self.x1),(self.y0,self.y1)) + self.ax.figure.canvas.draw() + def onpick(self,event): artist = event.artist mouseevent = event.mouseevent self.x = mouseevent.xdata self.y = mouseevent.ydata - def overlay_data(self,data,cmap='jet'): + def overlay_data(self,data,V=0,cmap=0): xlen = data.shape[1] ylen = data.shape[0] - self.ax.imshow(data,cmap=cmap,extent=(0,xlen,0,ylen), - picker=5) - return \ No newline at end of file + kwargs = {} + + if isinstance(V,N.ndarray): + kwargs['levels'] = V + + kwargs['cmap'] =cmap + kwargs['extent'] = (0,xlen,0,ylen) + kwargs['picker'] = 5 + + cf = self.bmap.contourf(self.x,self.y,data,**kwargs) + self.fig.colorbar(cf) + # pdb.set_trace() + + return + + \ No newline at end of file diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index 633e5ad..94fb29c 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -106,3 +106,26 @@ def create_colorbar(self,fpath,fname,cf,label=''): CB = plt.colorbar(cf,cax=CBax,orientation='horizontal') CB.set_label(label) self.save(fig,fpath,fname) + + def basemap_setup(self,smooth=1): + # Fetch settings + basemap_res = getattr(self.C,'basemap_res',self.D.basemap_res) + + width_m = self.W.dx*(self.W.x_dim-1) + height_m = self.W.dy*(self.W.y_dim-1) + + m = Basemap( + projection='lcc',width=width_m,height=height_m, + lon_0=self.W.cen_lon,lat_0=self.W.cen_lat,lat_1=self.W.truelat1, + lat_2=self.W.truelat2,resolution=basemap_res,area_thresh=500, + ax=self.ax) + m.drawcoastlines() + m.drawstates() + m.drawcountries() + + # Draw meridians etc with wrff.lat/lon spacing + # Default should be a tenth of width of plot, rounded to sig fig + + s = slice(None,None,smooth) + x,y = m(self.W.lons[s,s],self.W.lats[s,s]) + return m, x, y diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index 4e3ab06..aa03ab8 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -26,7 +26,8 @@ import os import pdb import time - +import matplotlib as M +M.use('gtkagg') from wrfout import WRFOut from axes import Axes @@ -53,6 +54,10 @@ def __init__(self,config): # This stuff should be elsewhere. + # matplotlibuse = getattr(self.C,'matplotlibuse','agg') + # M.use(matplotlibuse) + # print "Using {0} backend.".format(matplotlibuse) + #self.font_prop = getattr(self.C,'font_prop',self.D.font_prop) #self.usetex = getattr(self.C,'usetex',self.D.usetex) #self.plot_titles = getattr(self.C,'plot_titles',self.D.plot_titles) @@ -864,14 +869,15 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,len_G=30,dom=1): # Plot sim ref, send basemap axis to clicker function F = BirdsEye(self.C,self.W) - data = F.plot2D('cref',time,2000,dom,outpath,save=0,return_data=1) - C = Clicker(data=data) + self.data = F.plot2D('cref',time,2000,dom,outpath,save=0,return_data=1) + # pdb.set_trace() + C = Clicker(self.C,self.W,data=self.data) - # Popup window and click two locations - C.click_x_y() - Ax, Ay = C.x, C.y + C.draw_line() + + lon0, lat0 = C.bmap(C.x0,C.y0,inverse=True) + lon1, lat1 = C.bmap(C.x1,C.y1,inverse=True) - C.click_x_y() - Bx, By = C.x, C.y + X = CrossSection(self.C,self.W,lat0,lon0,lat1,lon1) - # \ No newline at end of file + self.W.cold_pool_strength(X,time) \ No newline at end of file diff --git a/postWRF/postWRF/scales.py b/postWRF/postWRF/scales.py index 3c1682c..901a3aa 100644 --- a/postWRF/postWRF/scales.py +++ b/postWRF/postWRF/scales.py @@ -52,14 +52,12 @@ def __init__(self,vrbl,lv,clvs=0): try: self.cm = self.A[vrbl]['cmap'](clvs) - #pdb.set_trace() except TypeError: #print("Using default colourtable.") #def_ct = plt.cm.get_cmap("jet") self.cm = 0 #cm = LinearSegmentedColormap('DEF_CT',def_ct) - def get_multiplier(self,vrbl,lv): m = self.A[vrbl].get('multiplier',1) return m diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 5fda6e3..e8beeb0 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -208,7 +208,7 @@ def create_slice(self,PS): if any('north' in p for p in PS['dim_names']): if 'la' not in PS: sl.append(slice(None,None)) - if isinstance(PS['la'],slice) or isinstance(PS['la'],N.ndarray): + elif isinstance(PS['la'],slice) or isinstance(PS['la'],N.ndarray): sl.append(PS['la']) else: sl.append(slice(PS['la'],PS['la']+1)) @@ -812,9 +812,47 @@ def get_limits(self): Wlim = self.lons[0] return Nlim, Elim, Slim, Wlim - def cold_pool_strength(self): - pass - + def cold_pool_strength(self,X,time): + """ + Returns array the same shape as WRF domain. + + X : cross-section object + """ + # Set up slices + time_idx = self.get_time_idx(time) + # lv_idx = 0 + + + # slices = {'t': time_idx, 'lv': lv_idx, 'la': lat_sl, 'lo': lon_sl} + slices = {'t':time_idx} + + # Get wind data + wind = self.get('wind10',slices) + + # This is the 2D plane for calculation data + coldpooldata = N.zeros(wind.shape[1:]) + + + hyp_pts, xx, yy = X.get_xs_slice() + + # pdb.set_trace() + + # xint = xx.astype(int) + # yint = yy.astype(int) + angle = N.arctan((yy[-1]-yy[0])/(xx[-1]-xx[0])) # In radians + + for x,y in zip(xx,yy): + # For every point along line AB: + # Create line-normal cross-section + + norm_xx, norm_yy = X.create_linenormal_xs(x,y) + + gf_x, gf_y = find_gust_front() + + pdb.set_trace() + + return coldpooldata + def cold_pool_height(self): pass diff --git a/postWRF/postWRF/xsection.py b/postWRF/postWRF/xsection.py index ca54e19..c892a85 100644 --- a/postWRF/postWRF/xsection.py +++ b/postWRF/postWRF/xsection.py @@ -40,6 +40,28 @@ def __init__(self,config,wrfout,latA=0,lonA=0,latB=0,lonB=0): print("Please click start and end points.") self.popup_transect() + def create_linenormal_xs(self,x,y,length_pts=3): + """ + Return a cross-section that runs normal to the + existing cross-section contained in self. + + x,y : coordinates of intersection + length_pts : length of normal line in grid points + """ + + hyp_pts, xx, yy = self.get_xs_slice() + + xint = xx.astype(int) + yint = yy.astype(int) + angle = N.arctan((yy[-1]-yy[0])/(xx[-1]-xx[0])) # In radians + + normal_angle = angle + N.radians(90) + pdb.set_trace() + + + #return norm_xx, norm_yy + return 1,2 + def popup_transect(self): """ Pops up window for user to select start and @@ -145,6 +167,16 @@ def interp(self,geopot, pres, p): return (1.0-w)*geopot[k] + w*geopot[k-1] + def get_xs_slice(self): + xA, yA = self.get_xy_from_latlon(self.latA,self.lonA) + xB, yB = self.get_xy_from_latlon(self.latB,self.lonB) + hyp_pts = int(N.hypot(xB-xA,yB-yA)) + xx = N.linspace(xA,xB,hyp_pts) + yy = N.linspace(yA,yB,hyp_pts) + + # pdb.set_trace() + return hyp_pts,xx,yy + def plot_xs(self,vrbl,ttime,outpath,clvs=0,ztop=0): """ @@ -157,11 +189,9 @@ def plot_xs(self,vrbl,ttime,outpath,clvs=0,ztop=0): """ # pdb.set_trace() tidx = self.W.get_time_idx(ttime) - xA, yA = self.get_xy_from_latlon(self.latA,self.lonA) - xB, yB = self.get_xy_from_latlon(self.latB,self.lonB) - hyp_pts = int(N.hypot(xB-xA,yB-yA)) - xx = N.linspace(xA,xB,hyp_pts) - yy = N.linspace(yA,yB,hyp_pts) + + hyp_pts, xx, yy = self.get_xs_slice() + xint = xx.astype(int) yint = yy.astype(int) angle = N.arctan((yy[-1]-yy[0])/(xx[-1]-xx[0])) # In radians From bcd29637cf902757b0c065add0908e306319d3cf Mon Sep 17 00:00:00 2001 From: John Lawson Date: Tue, 19 Aug 2014 16:40:56 -0500 Subject: [PATCH 099/111] Failure to make Clicker work --- postWRF/postWRF/clicker.py | 35 ++++++++++++++- postWRF/postWRF/main.py | 14 +++++- postWRF/postWRF/wrfout.py | 7 ++- postWRF/postWRF/xsection.py | 88 +++++++++++++++++++------------------ 4 files changed, 96 insertions(+), 48 deletions(-) diff --git a/postWRF/postWRF/clicker.py b/postWRF/postWRF/clicker.py index 2d7498c..598f121 100644 --- a/postWRF/postWRF/clicker.py +++ b/postWRF/postWRF/clicker.py @@ -84,7 +84,7 @@ def overlay_data(self,data,V=0,cmap=0): if isinstance(V,N.ndarray): kwargs['levels'] = V - kwargs['cmap'] =cmap + kwargs['cmap'] = cmap kwargs['extent'] = (0,xlen,0,ylen) kwargs['picker'] = 5 @@ -94,4 +94,35 @@ def overlay_data(self,data,V=0,cmap=0): return - \ No newline at end of file + def set_box_width(self,X): + """ + Ask user to specify a width that is normal to the + cross-section X. The plot will show with the box displayed. + If the user is not happy, they can try again. + """ + plt.show(self.fig) + user_is_happy = 0 + while not user_is_happy: + self.km = int(raw_input("Specify line-normal width (km): ")) + if not isinstance(self.km,int): + print("Value is not integer.") + raise Exception + + self.rect = M.patches.Rectangle((self.x0,self.y0),X.hyp_pts,X.angle) + self.ax.add_patch(self.rect) + self.ax.figure.canvas.draw() + + plt.show(self.fig) + + while True: + doesitwork = raw_input("Does this work? (y/n/x): ") + if doesitwork == 'y': + user_is_happy = 1 + break + elif doesitwork == 'n': + break + elif doesitwork == 'x': + raise Exception + else: + print("Try again.") + \ No newline at end of file diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index aa03ab8..d6238be 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -873,11 +873,21 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,len_G=30,dom=1): # pdb.set_trace() C = Clicker(self.C,self.W,data=self.data) + # Line from front to back of system C.draw_line() - + # C.draw_box() lon0, lat0 = C.bmap(C.x0,C.y0,inverse=True) lon1, lat1 = C.bmap(C.x1,C.y1,inverse=True) + # Create the cross-section object X = CrossSection(self.C,self.W,lat0,lon0,lat1,lon1) + + # Ask user the line-normal box width (self.km) + #C.set_box_width(X) + # pdb.set_trace() + + # Compute the grid (DX x DY) + cps = self.W.cold_pool_strength(X,time,width=C.km) - self.W.cold_pool_strength(X,time) \ No newline at end of file + # Plot this array + CPfig = BirdsEye(self.C,self.W) \ No newline at end of file diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index e8beeb0..134238b 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -812,12 +812,15 @@ def get_limits(self): Wlim = self.lons[0] return Nlim, Elim, Slim, Wlim - def cold_pool_strength(self,X,time): + def cold_pool_strength(self,X,time,km=100): """ Returns array the same shape as WRF domain. - X : cross-section object + X : cross-section object with given path + This path goes front-to-back through a bow + km : width in the line-normal direction """ + # Set up slices time_idx = self.get_time_idx(time) # lv_idx = 0 diff --git a/postWRF/postWRF/xsection.py b/postWRF/postWRF/xsection.py index c892a85..fc0541b 100644 --- a/postWRF/postWRF/xsection.py +++ b/postWRF/postWRF/xsection.py @@ -40,28 +40,18 @@ def __init__(self,config,wrfout,latA=0,lonA=0,latB=0,lonB=0): print("Please click start and end points.") self.popup_transect() - def create_linenormal_xs(self,x,y,length_pts=3): - """ - Return a cross-section that runs normal to the - existing cross-section contained in self. - - x,y : coordinates of intersection - length_pts : length of normal line in grid points - """ - - hyp_pts, xx, yy = self.get_xs_slice() + self.get_xs_slice() - xint = xx.astype(int) - yint = yy.astype(int) - angle = N.arctan((yy[-1]-yy[0])/(xx[-1]-xx[0])) # In radians - - normal_angle = angle + N.radians(90) + def get_xs_slice(self): + self.xA, self.yA = self.get_xy_from_latlon(self.latA,self.lonA) + self.xB, self.yB = self.get_xy_from_latlon(self.latB,self.lonB) + self.hyp_pts = int(N.hypot(self.xB-self.xA,self.yB-self.yA)) + self.xx = N.linspace(self.xA,self.xB,self.hyp_pts) + self.yy = N.linspace(self.yA,self.yB,self.hyp_pts) + self.angle = N.radians(90.0) + N.arctan((self.yy[0]-self.yy[-1])/(self.xx[-1]-self.xx[0])) pdb.set_trace() - - - #return norm_xx, norm_yy - return 1,2 - + return + def popup_transect(self): """ Pops up window for user to select start and @@ -167,17 +157,10 @@ def interp(self,geopot, pres, p): return (1.0-w)*geopot[k] + w*geopot[k-1] - def get_xs_slice(self): - xA, yA = self.get_xy_from_latlon(self.latA,self.lonA) - xB, yB = self.get_xy_from_latlon(self.latB,self.lonB) - hyp_pts = int(N.hypot(xB-xA,yB-yA)) - xx = N.linspace(xA,xB,hyp_pts) - yy = N.linspace(yA,yB,hyp_pts) - - # pdb.set_trace() - return hyp_pts,xx,yy + + def plot_xs(self,vrbl,ttime,outpath,clvs=0,ztop=0): """ Inputs: @@ -187,28 +170,27 @@ def plot_xs(self,vrbl,ttime,outpath,clvs=0,ztop=0): outpath : absolute path to directory to save output """ - # pdb.set_trace() tidx = self.W.get_time_idx(ttime) - - hyp_pts, xx, yy = self.get_xs_slice() - xint = xx.astype(int) - yint = yy.astype(int) - angle = N.arctan((yy[-1]-yy[0])/(xx[-1]-xx[0])) # In radians - # pdb.set_trace() + xint = self.xx.astype(int) + yint = self.yy.astype(int) # Get terrain heights - terrain_z, heighthalf = self.get_height(tidx,xint,yint,self.W.z_dim,hyp_pts) + terrain_z, heighthalf = self.get_height(self.tidx,xint,yint,self.W.z_dim,self.hyp_pts) # Set up plot # Length of x-section in km - xs_len = (1/1000.0) * N.sqrt((-1.0*hyp_pts*self.W.dy*N.cos(angle))**2 + - (hyp_pts*self.W.dy*N.sin(angle))**2) + xs_len = (1/1000.0) * N.sqrt((-1.0*self.hyp_pts*self.W.dy*N.cos(self.angle))**2 + + (self.hyp_pts*self.W.dy*N.sin(self.angle))**2) # Generate ticks along cross-section - xticks = N.arange(0,xs_len,xs_len/hyp_pts) + xticks = N.arange(0,xs_len,xs_len/self.hyp_pts) xlabels = [r"%3.0f" %t for t in xticks] - grid = N.swapaxes(N.repeat(N.array(xticks).reshape(hyp_pts,1),self.W.z_dim,axis=1),0,1) + grid = N.swapaxes(N.repeat(N.array(xticks).reshape(self.hyp_pts,1),self.W.z_dim,axis=1),0,1) + + ######### + #### ADD SELF BELOW HERE + ######### # Plotting if self.W.dx != self.W.dy: @@ -321,4 +303,26 @@ def draw_transect(self,outpath,fname): B = BirdsEye(self.C,self.W) m,x,y = B.basemap_setup() m.drawgreatcircle(self.lonA,self.latA,self.lonB,self.latB) - self.save(B.fig,outpath,fname) \ No newline at end of file + self.save(B.fig,outpath,fname) + + def create_linenormal_xs(self,x,y,length_pts=3): + """ + Return a cross-section that runs normal to the + existing cross-section contained in self. + + x,y : coordinates of intersection + length_pts : length of normal line in grid points + """ + + self.hyp_pts, self.xx, self.yy = self.get_xs_slice() + + xint = self.xx.astype(int) + yint = self.yy.astype(int) + self.angle = N.arctan((self.yy[-1]-self.yy[0])/(self.xx[-1]-self.xx[0])) # In radians + + normal_angle = self.angle + N.radians(90) + # pdb.set_trace() + + + #return norm_xx, norm_yy + return 1,2 \ No newline at end of file From 923a180f761d90180241bded4602ad3195e0c5c6 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Wed, 20 Aug 2014 18:41:51 -0500 Subject: [PATCH 100/111] Cold pool strength calculations --- postWRF/postWRF/birdseye.py | 58 ++++++++++++---------- postWRF/postWRF/main.py | 9 ++-- postWRF/postWRF/scales.py | 2 + postWRF/postWRF/wrfout.py | 96 ++++++++++++++++++++++++++++--------- postWRF/postWRF/xsection.py | 5 +- 5 files changed, 117 insertions(+), 53 deletions(-) diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 95c0e58..67a3870 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -16,7 +16,7 @@ class BirdsEye(Figure): def __init__(self,config,wrfout,ax=0): super(BirdsEye,self).__init__(config,wrfout,ax=ax) - def get_contouring(self,vrbl,lv,V=0): + def get_contouring(self,vrbl='user',lv='user',V=0,cmap=0): """ Returns colourmap and contouring levels V : manually override contour levels @@ -27,28 +27,32 @@ def get_contouring(self,vrbl,lv,V=0): plotkwargs = {} # Scales object - S = Scales(vrbl,lv) - - if self.mplcommand == 'contour': - multiplier = S.get_multiplier(vrbl,lv) - - if isinstance(V,collections.Sequence): - clvs = V - else: - try: - clvs = S.clvs - except: - pass - - if S.cm: - plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),clvs] - plotkwargs['cmap'] = S.cm - elif isinstance(S.clvs,N.ndarray): - if self.mplcommand == 'contourf': - plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),clvs] - plotkwargs['cmap'] = plt.cm.jet + if not vrbl == 'user': + S = Scales(vrbl,lv) + + if self.mplcommand == 'contour': + multiplier = S.get_multiplier(vrbl,lv) + + if isinstance(V,collections.Sequence): + clvs = V else: + try: + clvs = S.clvs + except: + pass + + if S.cm: plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),clvs] + plotkwargs['cmap'] = S.cm + elif isinstance(S.clvs,N.ndarray): + if self.mplcommand == 'contourf': + plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),clvs] + plotkwargs['cmap'] = plt.cm.jet + else: + plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),clvs] + else: + plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n))] + plotkwargs['cmap'] = plt.cm.jet else: plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n))] plotkwargs['cmap'] = plt.cm.jet @@ -56,12 +60,13 @@ def get_contouring(self,vrbl,lv,V=0): # pdb.set_trace() return plotargs, plotkwargs - def plot_data(self,data,mplcommand,p2p,fname,pt,V=0,no_title=0,save=1): + def plot_data(self,data,mplcommand,p2p,fname,pt,cmap='jet',V=0,no_title=1,save=1): """ Generic method that plots any matrix of data on a map Inputs: data : lat/lon matrix of data + vrbl : variable type for contouring convention m : basemap instance mplcommand : contour or contourf p2p : path to plots @@ -74,7 +79,7 @@ def plot_data(self,data,mplcommand,p2p,fname,pt,V=0,no_title=0,save=1): # self.fig = plt.figure() # self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user self.fig.set_size_inches(5,5) - self.bmap,x,y = self.basemap_setup()#ax=self.ax) + self.bmap,self.x,self.y = self.basemap_setup()#ax=self.ax) self.mplcommand = mplcommand self.data = data @@ -89,20 +94,23 @@ def plot_data(self,data,mplcommand,p2p,fname,pt,V=0,no_title=0,save=1): # scaling_func = M.ticker.FuncFormatter(lambda x, pos:'{0:d}'.format(int(x*multiplier))) # plt.clabel(f1, inline=1, fmt=scaling_func, fontsize=9, colors='k') - plotargs, plotkwargs = self.get_contouring(vrbl,lv,V=V) + plotargs, plotkwargs = self.get_contouring(V=V,cmap=cmap) if self.mplcommand == 'contour': f1 = self.bmap.contour(*plotargs,**plotkwargs) elif self.mplcommand == 'contourf': f1 = self.bmap.contourf(*plotargs,**plotkwargs) + else: + print("Specify plot type.") + raise Exception # LABELS, TITLES etc """ Change these to hasattr! """ #if self.C.plot_titles: - title = utils.string_from_time('title',pt,tupleformat=0) if not no_title: + title = utils.string_from_time('title',pt,tupleformat=0) plt.title(title) #if self.C.plot_colorbar: cb = self.fig.colorbar(f1, orientation='horizontal') diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index d6238be..9096f8b 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -870,7 +870,7 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,len_G=30,dom=1): # Plot sim ref, send basemap axis to clicker function F = BirdsEye(self.C,self.W) self.data = F.plot2D('cref',time,2000,dom,outpath,save=0,return_data=1) - # pdb.set_trace() + C = Clicker(self.C,self.W,data=self.data) # Line from front to back of system @@ -887,7 +887,10 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,len_G=30,dom=1): # pdb.set_trace() # Compute the grid (DX x DY) - cps = self.W.cold_pool_strength(X,time,width=C.km) + cps = self.W.cold_pool_strength(X,time,km=50) # Plot this array - CPfig = BirdsEye(self.C,self.W) \ No newline at end of file + CPfig = BirdsEye(self.C,self.W) + tstr = utils.string_from_time('output',time) + fname = 'ColdPoolStrength_' + tstr + CPfig.plot_data(cps,'contourf',outpath,fname,time) diff --git a/postWRF/postWRF/scales.py b/postWRF/postWRF/scales.py index 901a3aa..13f974a 100644 --- a/postWRF/postWRF/scales.py +++ b/postWRF/postWRF/scales.py @@ -136,4 +136,6 @@ def get_dict_of_levels(self): A['PMSL'] = {'cmap':0,'multiplier':0.01} A['PMSL'][2000] = (97000,103100,100) + #A['cps'] = {'cmap':0} + return A diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 134238b..0fffae6 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -16,6 +16,7 @@ import collections import WEM.utils as utils +import metconstants as mc class WRFOut(object): @@ -825,42 +826,91 @@ def cold_pool_strength(self,X,time,km=100): time_idx = self.get_time_idx(time) # lv_idx = 0 - # slices = {'t': time_idx, 'lv': lv_idx, 'la': lat_sl, 'lo': lon_sl} slices = {'t':time_idx} # Get wind data - wind = self.get('wind10',slices) + wind10 = self.get('wind10',slices)[0,...] # This is the 2D plane for calculation data - coldpooldata = N.zeros(wind.shape[1:]) + coldpooldata = N.zeros(wind10.shape) + # Compute required C2 fields to save time + dpt = self.get('dpt',slices)[0,...] + heights = self.get('Z',slices)[0,...] - hyp_pts, xx, yy = X.get_xs_slice() - - # pdb.set_trace() - - # xint = xx.astype(int) - # yint = yy.astype(int) - angle = N.arctan((yy[-1]-yy[0])/(xx[-1]-xx[0])) # In radians + # All cross-sections (parallel) + xxx = [X.xx,] + yyy = [X.yy,] - for x,y in zip(xx,yy): - # For every point along line AB: - # Create line-normal cross-section - - norm_xx, norm_yy = X.create_linenormal_xs(x,y) - - gf_x, gf_y = find_gust_front() - - pdb.set_trace() + + for xx, yy in zip(xxx, yyy): + xx = xx.astype(int) + yy = yy.astype(int) + wind_slice = wind10[yy,xx] + slice_loc = self.find_gust_front(wind_slice,X.angle) + gfx = xx[slice_loc] + gfy = yy[slice_loc] + xx_cut = xx[:N.where(xx==gfx)[0][0]] + yy_cut = yy[:N.where(yy==gfy)[0][0]] + for x,y in zip(xx_cut,yy_cut): + # pdb.set_trace() + coldpooldata[y,x] = self.compute_C2(x,y,dpt[:,y,x],heights[:,y,x]) + return coldpooldata - def cold_pool_height(self): - pass - - + def compute_C2(self,x,y,dpt,heights): + """ + C^2 as found in James et al. 2006 MWR + + x : x location in domain + y : y location in domain + dpt : density potential temperature slice + heights : geopotential height slice + """ + + dz,dptp = self.cold_pool_depth(dpt,heights) + C2 = -2*mc.g*(dptp/dpt[0])*dz + + return C2 + + def cold_pool_depth(self,dpt,heights): + dz = 0 + for d,z in zip(dpt,heights): + dptp = d - dpt[0] + # pdb.set_trace() + if dptp > -1.0: + break + dz = z + + return dz, dptp + def find_gust_front(self,wind_slice,angle): + """ + Find location of maximum shear in the horizontal wind along a + 1D slice. + wind_slice : 1D numpy array + angle : angle of slice cross-section + + """ + shp = wind_slice.shape + shear = N.zeros(shp) + for n in range(shp[0]): + if n == 0 or n == shp[0]-1: + shear[n] = 0 + else: + len1 = abs(self.dx / N.sin(angle)) + len2 = abs(self.dx / N.cos(angle)) + hyp = min((len1,len2)) + shear[n] = (wind_slice[n+1]-wind_slice[n-1]/(2*hyp)) + # N.W.DX + + maxshearloc = N.where(shear == shear.max()) + # pdb.set_trace() + return maxshearloc + # maxshearloc[0][0] returns the integer + diff --git a/postWRF/postWRF/xsection.py b/postWRF/postWRF/xsection.py index fc0541b..efc536e 100644 --- a/postWRF/postWRF/xsection.py +++ b/postWRF/postWRF/xsection.py @@ -48,8 +48,9 @@ def get_xs_slice(self): self.hyp_pts = int(N.hypot(self.xB-self.xA,self.yB-self.yA)) self.xx = N.linspace(self.xA,self.xB,self.hyp_pts) self.yy = N.linspace(self.yA,self.yB,self.hyp_pts) - self.angle = N.radians(90.0) + N.arctan((self.yy[0]-self.yy[-1])/(self.xx[-1]-self.xx[0])) - pdb.set_trace() + # self.angle = N.radians(90.0) + N.arctan((self.yy[0]-self.yy[-1])/(self.xx[-1]-self.xx[0])) + self.angle = N.math.atan2((self.yy[0]-self.yy[-1]),(self.xx[0]-self.xx[-1])) + N.pi + # pdb.set_trace() return def popup_transect(self): From f33dbf3f6e581c34a9980ed3d0b91289a0e2a1be Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 21 Aug 2014 17:32:17 -0500 Subject: [PATCH 101/111] Cold pool strength almost done --- postWRF/postWRF/birdseye.py | 45 +++++++------ postWRF/postWRF/clicker.py | 8 ++- postWRF/postWRF/figure.py | 12 ++-- postWRF/postWRF/main.py | 48 ++++++++++++-- postWRF/postWRF/wrfout.py | 124 +++++++++++++++++++++++++++--------- postWRF/postWRF/xsection.py | 10 ++- utils/GIS_tools.py | 3 + 7 files changed, 187 insertions(+), 63 deletions(-) diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 67a3870..b2ea9e5 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -13,8 +13,8 @@ from scales import Scales class BirdsEye(Figure): - def __init__(self,config,wrfout,ax=0): - super(BirdsEye,self).__init__(config,wrfout,ax=ax) + def __init__(self,config,wrfout,ax=0,fig=0): + super(BirdsEye,self).__init__(config,wrfout,ax=ax,fig=fig) def get_contouring(self,vrbl='user',lv='user',V=0,cmap=0): """ @@ -26,6 +26,8 @@ def get_contouring(self,vrbl='user',lv='user',V=0,cmap=0): plotargs = [] plotkwargs = {} + + # Scales object if not vrbl == 'user': S = Scales(vrbl,lv) @@ -33,23 +35,16 @@ def get_contouring(self,vrbl='user',lv='user',V=0,cmap=0): if self.mplcommand == 'contour': multiplier = S.get_multiplier(vrbl,lv) - if isinstance(V,collections.Sequence): - clvs = V - else: - try: - clvs = S.clvs - except: - pass - if S.cm: - plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),clvs] + plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),] plotkwargs['cmap'] = S.cm + elif isinstance(S.clvs,N.ndarray): if self.mplcommand == 'contourf': - plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),clvs] + plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),] plotkwargs['cmap'] = plt.cm.jet else: - plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),clvs] + plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),] else: plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n))] plotkwargs['cmap'] = plt.cm.jet @@ -57,6 +52,14 @@ def get_contouring(self,vrbl='user',lv='user',V=0,cmap=0): plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n))] plotkwargs['cmap'] = plt.cm.jet + if isinstance(V,N.ndarray): + plotkwargs['levels'] = V + else: + if vrbl == 'user': + pass + else: + plotkwargs['levels'] = S.clvs + # pdb.set_trace() return plotargs, plotkwargs @@ -78,7 +81,7 @@ def plot_data(self,data,mplcommand,p2p,fname,pt,cmap='jet',V=0,no_title=1,save=1 # INITIALISE # self.fig = plt.figure() # self.fig = self.figsize(8,8,self.fig) # Create a default figure size if not set by user - self.fig.set_size_inches(5,5) + # self.fig.set_size_inches(5,5) self.bmap,self.x,self.y = self.basemap_setup()#ax=self.ax) self.mplcommand = mplcommand self.data = data @@ -100,6 +103,12 @@ def plot_data(self,data,mplcommand,p2p,fname,pt,cmap='jet',V=0,no_title=1,save=1 f1 = self.bmap.contour(*plotargs,**plotkwargs) elif self.mplcommand == 'contourf': f1 = self.bmap.contourf(*plotargs,**plotkwargs) + elif self.mplcommand == 'pcolor': + f1 = self.bmap.pcolor(*plotargs,**plotkwargs) + elif self.mplcommand == 'pcolormesh': + f1 = self.bmap.pcolormesh(*plotargs,**plotkwargs) + elif self.mplcommand == 'scatter': + f1 = self.bmap.scatter(*plotargs,**plotkwargs) else: print("Specify plot type.") raise Exception @@ -113,13 +122,13 @@ def plot_data(self,data,mplcommand,p2p,fname,pt,cmap='jet',V=0,no_title=1,save=1 title = utils.string_from_time('title',pt,tupleformat=0) plt.title(title) #if self.C.plot_colorbar: - cb = self.fig.colorbar(f1, orientation='horizontal') + # self.fig.colorbar(f1,ax=self.ax,shrink=0.5,orientation='horizontal') # SAVE FIGURE datestr = utils.string_from_time('output',pt,tupleformat=0) # self.fname = self.create_fname(fpath) # No da variable here if save: - self.save(self.fig,p2p,fname) + self.save(p2p,fname) plt.close(self.fig) return f1 @@ -239,7 +248,7 @@ def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1, naming.append(dom) self.fname = self.create_fname(*naming) if save: - self.save(self.fig,outpath,self.fname) + self.save(elf.fname) plt.close() if isinstance(self.data,N.ndarray): return self.data.reshape((self.la_n,self.lo_n)) @@ -287,7 +296,7 @@ def plot_streamlines(self,lv,pt,da=0): datestr = utils.string_from_time('output',pt) na = ('streamlines',lv_na,datestr) self.fname = self.create_fname(*na) - self.save(self.fig,self.p2p,self.fname) + self.save(self.p2p,self.fname) plt.clf() plt.close() diff --git a/postWRF/postWRF/clicker.py b/postWRF/postWRF/clicker.py index 598f121..b10fd27 100644 --- a/postWRF/postWRF/clicker.py +++ b/postWRF/postWRF/clicker.py @@ -14,7 +14,7 @@ class Clicker(Figure): # def __init__(self,config,wrfout,ax=0): def __init__(self,config,wrfout,data=0,fig=0,ax=0): - super(Clicker,self).__init__(config,wrfout,ax=ax) + super(Clicker,self).__init__(config,wrfout,fig=fig,ax=ax) if isinstance(data,N.ndarray): self.bmap,self.x,self.y = self.basemap_setup() @@ -22,7 +22,8 @@ def __init__(self,config,wrfout,data=0,fig=0,ax=0): self.overlay_data(data,V=S.clvs,cmap=S.cm) def click_x_y(self): - self.fig.canvas.mpl_connect('pick_event',self.onpick) + # self.fig.canvas.mpl_connect('pick_event',self.onpick) + self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press) plt.show(self.fig) def draw_box(self): @@ -46,6 +47,7 @@ def draw_line(self): self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press) self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release_line) plt.show(self.fig) + return self.ax def on_press(self, event): print 'press' @@ -89,7 +91,7 @@ def overlay_data(self,data,V=0,cmap=0): kwargs['picker'] = 5 cf = self.bmap.contourf(self.x,self.y,data,**kwargs) - self.fig.colorbar(cf) + # self.fig.colorbar(cf,ax=self.ax,shrink=0.5,orientation='horizontal') # pdb.set_trace() return diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index 94fb29c..acb2439 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -16,7 +16,7 @@ from defaults import Defaults class Figure(object): - def __init__(self,config,wrfout,ax=0): + def __init__(self,config,wrfout,ax=0,fig=0,plotn=(1,1)): """ C : configuration settings W : data @@ -36,9 +36,11 @@ def __init__(self,config,wrfout,ax=0): dpi = getattr(self.C,'DPI',self.D.dpi) # Create main figure - self.fig, self.ax = plt.subplots(1) - if ax: + if ax and fig: self.ax = ax + self.fig = fig + else: + self.fig, self.ax = plt.subplots(nrows=plotn[0],ncols=plotn[1]) self.fig.set_dpi(dpi) # self.ax = self.fig.add_subplot(111) @@ -60,12 +62,12 @@ def figsize(self,defwidth,defheight,fig): fig.set_size_inches(width,height) return fig - def save(self,fig,p2p,fname): + def save(self,p2p,fname): # fig.tight_layout() utils.trycreate(p2p) fpath = os.path.join(p2p,fname) #self.fig.savefig(fpath) - fig.savefig(fpath,bbox_inches='tight') + self.fig.savefig(fpath,bbox_inches='tight') print("Saving figure {0}".format(fpath)) def get_limited_domain(self,da,smooth=1): diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index 9096f8b..c67048d 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -28,6 +28,7 @@ import time import matplotlib as M M.use('gtkagg') +import matplotlib.pyplot as plt from wrfout import WRFOut from axes import Axes @@ -843,7 +844,7 @@ def plot_xs(self,vrbl,times,latA=0,lonA=0,latB=0,lonB=0, for t in t_list: XS.plot_xs(vrbl,t,outpath,clvs=clvs,ztop=ztop) - def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,len_G=30,dom=1): + def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom=1,twoplot=0): """ Pick A, B points on sim ref overlay This sets the angle between north and line AB @@ -859,19 +860,35 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,len_G=30,dom=1): If no wrfout file is explicitly specified, the netCDF file in that folder is chosen if unambiguous. out_sd : subdirectory of output .png. - len_G : length in km to compute behind front + swath_width : length in gridpoints in cross-section-normal direction dom : domain number + return2 : return two figures. cold pool strength and cref/cross-section. """ # Initialise self.W = self.get_wrfout(wrf_sd,wrf_nc,dom=dom) outpath = self.get_outpath(out_sd) + # keyword arguments for plots + line_kwargs = {} + cps_kwargs = {} + # Create two-panel figure + if twoplot: + P2 = Figure(self.C,self.W,plotn=(1,2)) + line_kwargs['ax'] = P2.ax.flat[0] + line_kwargs['fig'] = P2.fig + P2.ax.flat[0].set_size_inches(3,3) + + cps_kwargs['ax'] = P2.ax.flat[1] + cps_kwargs['fig'] = P2.fig + P2.ax.flat[1].set_size_inches(6,6) + # Plot sim ref, send basemap axis to clicker function F = BirdsEye(self.C,self.W) self.data = F.plot2D('cref',time,2000,dom,outpath,save=0,return_data=1) - C = Clicker(self.C,self.W,data=self.data) + C = Clicker(self.C,self.W,data=self.data,**line_kwargs) + # C.fig.tight_layout() # Line from front to back of system C.draw_line() @@ -879,18 +896,35 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,len_G=30,dom=1): lon0, lat0 = C.bmap(C.x0,C.y0,inverse=True) lon1, lat1 = C.bmap(C.x1,C.y1,inverse=True) + # Pick location for environmental dpt + # C.click_x_y() + # Here, it is the end of the cross-section + lon_env, lat_env = C.bmap(C.x1, C.y1, inverse=True) + y_env,x_env,exactlat,exactlon = utils.getXY(self.W.lats1D,self.W.lons1D,lat_env,lon_env) # Create the cross-section object X = CrossSection(self.C,self.W,lat0,lon0,lat1,lon1) # Ask user the line-normal box width (self.km) #C.set_box_width(X) - # pdb.set_trace() # Compute the grid (DX x DY) - cps = self.W.cold_pool_strength(X,time,km=50) + cps = self.W.cold_pool_strength(X,time,swath_width=swath_width,env=(x_env,y_env)) # Plot this array - CPfig = BirdsEye(self.C,self.W) + CPfig = BirdsEye(self.C,self.W,**cps_kwargs) tstr = utils.string_from_time('output',time) fname = 'ColdPoolStrength_' + tstr - CPfig.plot_data(cps,'contourf',outpath,fname,time) + + # pdb.set_trace() + # imfig,imax = plt.subplots(1) + # imax.imshow(cps) + # plt.show(imfig) + # CPfig.plot_data(cps,'contourf',outpath,fname,time,V=N.arange(5,105,5)) + mplcommand = 'contour' + if mplcommand[:7] == 'contour': + cps_kwargs['V'] = N.arange(5,105,5) + CPfig.plot_data(cps,mplcommand,outpath,fname,time) + # CPfig.fig.tight_layout() + + if twoplot: + P2.save(outpath,fname+"_twopanel") \ No newline at end of file diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 0fffae6..2841c9e 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -813,13 +813,14 @@ def get_limits(self): Wlim = self.lons[0] return Nlim, Elim, Slim, Wlim - def cold_pool_strength(self,X,time,km=100): + def cold_pool_strength(self,X,time,swath_width=100,env=0): """ Returns array the same shape as WRF domain. X : cross-section object with given path This path goes front-to-back through a bow km : width in the line-normal direction + env : (x,y) for location to sample environmental dpt """ # Set up slices @@ -831,72 +832,112 @@ def cold_pool_strength(self,X,time,km=100): # Get wind data wind10 = self.get('wind10',slices)[0,...] + T2 = self.get('T2',slices)[0,...] # This is the 2D plane for calculation data coldpooldata = N.zeros(wind10.shape) # Compute required C2 fields to save time dpt = self.get('dpt',slices)[0,...] - heights = self.get('Z',slices)[0,...] + Z = self.get('Z',slices)[0,...] + HGT = self.get('HGT',slices)[0,...] + heights = Z-HGT + # pdb.set_trace() - # All cross-sections (parallel) - xxx = [X.xx,] - yyy = [X.yy,] + if isinstance(env,collections.Sequence): + xx_env = N.arange(env[0]-2,env[0]+3) + yy_env = N.arange(env[1]-2,env[1]+3) + dpt_env = N.mean(dpt[:,yy_env,xx_env],axis=1) + # All cross-sections (parallel) - for xx, yy in zip(xxx, yyy): + # xxx = [X.xx,] + # yyy = [X.yy,] + X.translate_xs(-swath_width/2) + for n in range(swath_width): + print("Cross section #{0}".format(n)) + # for xx, yy in zip(xxx, yyy): + X.translate_xs(1) + xx = X.xx + yy = X.yy xx = xx.astype(int) yy = yy.astype(int) wind_slice = wind10[yy,xx] - slice_loc = self.find_gust_front(wind_slice,X.angle) + T2_slice = T2[yy,xx] + slice_loc = self.find_gust_front(wind_slice,T2_slice,X.angle) gfx = xx[slice_loc] gfy = yy[slice_loc] - xx_cut = xx[:N.where(xx==gfx)[0][0]] - yy_cut = yy[:N.where(yy==gfy)[0][0]] - for x,y in zip(xx_cut,yy_cut): - # pdb.set_trace() - coldpooldata[y,x] = self.compute_C2(x,y,dpt[:,y,x],heights[:,y,x]) - + gf_pts = N.intersect1d(N.where(xx==gfx)[0],N.where(yy==gfy)[0]) + gf_pt = gf_pts[int(len(gf_pts)/2.0)] + xx_cp = xx[:gf_pt] + yy_cp = yy[:gf_pt] + # pdb.set_trace() + + # Compute enviromental dpt at each height + # Average all levels from the location of gust front + # forwards to the end of the cross-section. + if not env: + xx_env = xx[gf_pt+1:] + yy_env = yy[gf_pt+1:] + dpt_env = N.mean(dpt[:,yy_env,xx_env],axis=1) + # pdb.set_trace() + + for x,y in zip(xx_cp,yy_cp): + #for x,y in zip(xx,yy): + coldpooldata[y,x] = N.sqrt(self.compute_C2(x,y,dpt[:,y,x],heights[:,y,x],dpt_env)) + + # pdb.set_trace() return coldpooldata - def compute_C2(self,x,y,dpt,heights): + def compute_C2(self,x,y,dpt,heights,dpt_env): """ C^2 as found in James et al. 2006 MWR x : x location in domain y : y location in domain dpt : density potential temperature slice - heights : geopotential height slice + heights : height AGL slice + dpt_env : environmental dpt, column """ - dz,dptp = self.cold_pool_depth(dpt,heights) - C2 = -2*mc.g*(dptp/dpt[0])*dz - + dz, zidx = self.cold_pool_depth(dpt,heights,dpt_env) + C2 = -2*mc.g*((dpt[zidx]-dpt_env[zidx])/dpt_env[zidx])*dz + # print("dpt = {0} ... dpt_env = {1} ... C2 = {2}".format(dpt[0],dpt_env[0],C2)) return C2 - def cold_pool_depth(self,dpt,heights): + def cold_pool_depth(self,dpt,heights,dpt_env): dz = 0 - for d,z in zip(dpt,heights): - dptp = d - dpt[0] - # pdb.set_trace() + for d,z, de in zip(dpt[1:],heights[1:],dpt_env[1:]): + dptp = d - de if dptp > -1.0: break dz = z - return dz, dptp + if isinstance(dz,float): + zidx = N.where(heights==dz)[0] + else: + zidx = 0 + + return dz, zidx - def find_gust_front(self,wind_slice,angle): + def find_gust_front(self,wind_slice,T2_slice,angle,method=2): """ Find location of maximum shear in the horizontal wind along a 1D slice. wind_slice : 1D numpy array + T2_slice : temp 2m slice angle : angle of slice cross-section - + method : way to locate gust front """ + shp = wind_slice.shape + + # Compute gradient quantities shear = N.zeros(shp) + T2grad = N.zeros(shp) + for n in range(shp[0]): if n == 0 or n == shp[0]-1: shear[n] = 0 @@ -904,12 +945,37 @@ def find_gust_front(self,wind_slice,angle): len1 = abs(self.dx / N.sin(angle)) len2 = abs(self.dx / N.cos(angle)) hyp = min((len1,len2)) - shear[n] = (wind_slice[n+1]-wind_slice[n-1]/(2*hyp)) + # In kilometres: + shear[n] = ((wind_slice[n+1]-wind_slice[n-1])/(2*hyp))*1000.0 + T2grad[n] = ((T2_slice[n+1]-T2_slice[n-1])/(2*hyp))*1000.0 # N.W.DX - - maxshearloc = N.where(shear == shear.max()) + # pdb.set_trace() - return maxshearloc + + # Go from B to A + # Find first location where T2 drops and shear is ? + + if method==1: + ### METHOD 1: USING THRESHOLDS + # By default + gfidx = shp/2 + for n, s, t in zip(range(shp)[::-1],shear[::-1],T2grad[::-1]): + if (abs(s)>2.0) and (t<2.0): + gfidx = n + break + + elif method==2: + + ### METHOD 2: FINDING MAX GRADIENTS AND AVERAGING + shear = abs(shear) + T2grad = abs(T2grad) + + xsh_idx = N.where(shear == shear.max())[0][0] + xtg_idx = N.where(T2grad == T2grad.max())[0][0] + print("Max shear loc: {0} ... max tempgrad loc: {1}".format(xsh_idx,xtg_idx)) + gfidx = int((xsh_idx + xtg_idx)/2.0) + + return gfidx # maxshearloc[0][0] returns the integer diff --git a/postWRF/postWRF/xsection.py b/postWRF/postWRF/xsection.py index efc536e..45858dc 100644 --- a/postWRF/postWRF/xsection.py +++ b/postWRF/postWRF/xsection.py @@ -42,6 +42,14 @@ def __init__(self,config,wrfout,latA=0,lonA=0,latB=0,lonB=0): self.get_xs_slice() + def translate_xs(self,shiftpoints): + self.xA += shiftpoints + self.xB += shiftpoints + self.yA += shiftpoints + self.yB += shiftpoints + self.xx = N.linspace(self.xA,self.xB,self.hyp_pts) + self.yy = N.linspace(self.yA,self.yB,self.hyp_pts) + def get_xs_slice(self): self.xA, self.yA = self.get_xy_from_latlon(self.latA,self.lonA) self.xB, self.yB = self.get_xy_from_latlon(self.latB,self.lonB) @@ -96,7 +104,7 @@ def get_xy_from_latlon(self,lat,lon): exactlat, exactlon : exact coordinates of closest x/y """ - x,y,exactlat,exactlon = utils.getXY(self.W.lats1D,self.W.lons1D,lat,lon) + y,x,exactlat,exactlon = utils.getXY(self.W.lats1D,self.W.lons1D,lat,lon) return x,y def get_height(self,t,x,y,z,pts): diff --git a/utils/GIS_tools.py b/utils/GIS_tools.py index 576d756..b7db177 100644 --- a/utils/GIS_tools.py +++ b/utils/GIS_tools.py @@ -486,6 +486,9 @@ def wrfout_files_in(folders,dom=0,init_time='notset',descend=1,avoid=0, return wrfouts def getXY(lats,lons,ptlat,ptlon): + """ + Output is lat, lon so y,x + """ # Find closest lat/lon in array minlat = abs(lats-ptlat).min() minlon = abs(lons-ptlon).min() From 83f1adaf6cb0d06efdad54431265c48b6e529382 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Thu, 21 Aug 2014 16:33:35 -0600 Subject: [PATCH 102/111] Included DKEold.py in /bin --- postWRF/bin/.gitignore | 1 + postWRF/bin/DKEold.py | 149 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 postWRF/bin/DKEold.py diff --git a/postWRF/bin/.gitignore b/postWRF/bin/.gitignore index ec40646..33565ba 100644 --- a/postWRF/bin/.gitignore +++ b/postWRF/bin/.gitignore @@ -1,4 +1,5 @@ *.py +!DKEold.py !DKE_settings.py !DKE.py !20110419_plot.py diff --git a/postWRF/bin/DKEold.py b/postWRF/bin/DKEold.py new file mode 100644 index 0000000..a0cd06c --- /dev/null +++ b/postWRF/bin/DKEold.py @@ -0,0 +1,149 @@ +"""This script shows examples of using the package to create arrays +of data stored to disc. This is then plotted using the package. +""" +import sys +import os +import pdb +import calendar +import time +import matplotlib.pyplot as plt + +sys.path.append('/uufs/chpc.utah.edu/common/home/u0737349/gitprojects/') + +from DKE_settings import Settings +from WEM.postWRF import WRFEnviron +import WEM.utils as utils + + +# Time script +scriptstart = time.time() + +# Initialise settings and environment +config = Settings() +p = WRFEnviron(config) + +# User settings +init_time = utils.string_from_time('dir',(2009,9,10,0,0,0),strlen='hour') +rootdir = '/uufs/chpc.utah.edu/common/home/horel-group2/lawson2/' +outdir = '/uufs/chpc.utah.edu/common/home/u0737349/public_html/paper2/' + +# Create lists of all fig, ax, cb objects +figs = {} +axes = {} +cbs = {} + +#for rundate in ('25','27','29'): +plot_all = 1 +if plot_all: + for rundate in ['25','27','29']: + # print("Computing for {0} November".format(rundate)) + foldername = '201111' + rundate + '00' + runfolder = os.path.join(rootdir,foldername) + # path_to_wrfouts = utils.wrfout_files_in(runfolder,dom=1) + + itime = (2011,11,int(rundate),0,0,0) + ftime = (2011,12,2,12,0,0) + times = p.generate_times(itime,ftime,12*3600) + + path_to_plots = os.path.join(outdir,foldername) + for time in times: + #pdb.set_trace() + # Produce .npy data files with DKE data + # print("Compute_diff_energy...") + #p.compute_diff_energy('sum_z','kinetic',path_to_wrfouts,times,upper=500, + # d_save=runfolder, d_return=0,d_fname='DKE_500_2'+foldername) + # Contour fixed at these values + plotfname = 'DKE_500_' + V = range(200,2200,200) + p.plot_diff_energy('sum_z','kinetic',time,runfolder,'DKE_500_'+foldername, + path_to_plots,plotfname,V,no_title=1,ax=ax) + + +#print "Script took", time.time()-scriptstart, "seconds." +#pdb.set_trace() + +# Create publication figures +fig14, axes14 = plt.subplots(3,1,figsize=(5,7)) +initdate = '2011112500' +plotfname = 'DKE_500_' + +times = [(2011,m,d,12,0,0) for m,d in zip((11,11,12),(29,30,1))] +runfolder = os.path.join(rootdir,initdate) +path_to_plots = os.path.join(outdir,initdate) +V = range(200,2200,200) +labels = ['a)','b)','c)'] + +for ax,t,label in zip(axes14.flat, times,labels): + cf = p.plot_diff_energy('sum_z','kinetic',t,runfolder,'DKE_500_'+initdate, + path_to_plots,plotfname,V,no_title=1,ax=ax) + ax.text(0.05,0.85,label,transform=ax.transAxes, + bbox={'facecolor':'white'},fontsize=15,zorder=1000) + +fig14.tight_layout() +fig14.subplots_adjust(bottom=0.15) +cbar_ax = fig14.add_axes([0.15,0.075,0.7,0.025]) +cb = fig14.colorbar(cf,cax=cbar_ax,orientation='horizontal') +cb.set_label('Different Kinetic Energy ($m^{2}s^{-2}$)') + +output_path = os.path.join(path_to_plots,'fig14.png') +fig14.savefig(output_path) +plt.close(fig14) + +####################### +####################### + +fig15, axes15 = plt.subplots(3,1,figsize=(5,7)) +initdate = '2011112700' +plotfname = 'DKE_500_' + +times = [(2011,m,d,12,0,0) for m,d in zip((11,11,12),(29,30,1))] +runfolder = os.path.join(rootdir,initdate) +path_to_plots = os.path.join(outdir,initdate) +V = range(200,2200,200) +labels = ['a)','b)','c)'] + +for ax,t,label in zip(axes15.flat, times,labels): + cf = p.plot_diff_energy('sum_z','kinetic',t,runfolder,'DKE_500_'+initdate, + path_to_plots,plotfname,V,no_title=1,ax=ax) + ax.text(0.05,0.85,label,transform=ax.transAxes, + bbox={'facecolor':'white'},fontsize=15,zorder=1000) + +fig15.tight_layout() +fig15.subplots_adjust(bottom=0.15) +cbar_ax = fig15.add_axes([0.15,0.075,0.7,0.025]) +cb = fig15.colorbar(cf,cax=cbar_ax,orientation='horizontal') +cb.set_label('Different Kinetic Energy ($m^{2}s^{-2}$)') + +output_path = os.path.join(path_to_plots,'fig15.png') +fig15.savefig(output_path) +plt.close(fig15) + + +####################### +####################### + +fig16, axes16 = plt.subplots(3,1,figsize=(5,7)) +initdate = '2011112900' +plotfname = 'DKE_500_' + +times = [(2011,m,d,12,0,0) for m,d in zip((11,11,12),(29,30,1))] +runfolder = os.path.join(rootdir,initdate) +path_to_plots = os.path.join(outdir,initdate) +V = range(200,2200,200) +labels = ['a)','b)','c)'] + +for ax,t,label in zip(axes16.flat, times,labels): + cf = p.plot_diff_energy('sum_z','kinetic',t,runfolder,'DKE_500_'+initdate, + path_to_plots,plotfname,V,no_title=1,ax=ax) + ax.text(0.05,0.85,label,transform=ax.transAxes, + bbox={'facecolor':'white'},fontsize=15,zorder=1000) + +fig16.tight_layout() +fig16.subplots_adjust(bottom=0.15) +cbar_ax = fig16.add_axes([0.15,0.075,0.7,0.025]) +cb = fig16.colorbar(cf,cax=cbar_ax,orientation='horizontal') +cb.set_label('Different Kinetic Energy ($m^{2}s^{-2}$)') + +output_path = os.path.join(path_to_plots,'fig16.png') +fig16.savefig(output_path) +plt.close(fig16) From a67ce9378574e2ad8596b438adeaed05d8b5f40a Mon Sep 17 00:00:00 2001 From: John Lawson Date: Fri, 22 Aug 2014 19:54:54 -0500 Subject: [PATCH 103/111] Cold pool strength finished --- postWRF/postWRF/birdseye.py | 66 ++++++++++++++++--------------------- postWRF/postWRF/clicker.py | 3 +- postWRF/postWRF/main.py | 24 +++++++++++--- postWRF/postWRF/wrfout.py | 10 ++++-- 4 files changed, 57 insertions(+), 46 deletions(-) diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index b2ea9e5..ba0edaf 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -6,6 +6,7 @@ import numpy as N import collections import os +from mpl_toolkits.axes_grid1 import make_axes_locatable from defaults import Defaults from figure import Figure @@ -16,54 +17,40 @@ class BirdsEye(Figure): def __init__(self,config,wrfout,ax=0,fig=0): super(BirdsEye,self).__init__(config,wrfout,ax=ax,fig=fig) - def get_contouring(self,vrbl='user',lv='user',V=0,cmap=0): + def get_contouring(self,vrbl='user',lv='user',**kwargs): """ Returns colourmap and contouring levels + + Options keyword arguments: V : manually override contour levels """ - # List of args and dictionary of kwargs - plotargs = [] - plotkwargs = {} - + data = self.data.reshape((self.la_n,self.lo_n)) + # List of args and dictionary of kwargs + plotargs = [self.x,self.y,data] + plotkwargs = kwargs - # Scales object - if not vrbl == 'user': - S = Scales(vrbl,lv) + # cmap = getattr(kwargs,'cmap',plt.cm.jet) + + + if vrbl=='user': + pass - if self.mplcommand == 'contour': - multiplier = S.get_multiplier(vrbl,lv) - + else: + S = Scales(vrbl,lv) if S.cm: - plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),] plotkwargs['cmap'] = S.cm - - elif isinstance(S.clvs,N.ndarray): - if self.mplcommand == 'contourf': - plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),] - plotkwargs['cmap'] = plt.cm.jet - else: - plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n)),] - else: - plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n))] - plotkwargs['cmap'] = plt.cm.jet - else: - plotargs = plotargs + [self.x,self.y,self.data.reshape((self.la_n,self.lo_n))] - plotkwargs['cmap'] = plt.cm.jet - - if isinstance(V,N.ndarray): - plotkwargs['levels'] = V - else: - if vrbl == 'user': - pass - else: + if isinstance(S.clvs,N.ndarray): plotkwargs['levels'] = S.clvs + # if self.mplcommand == 'contour': + # multiplier = S.get_multiplier(vrbl,lv) + # pdb.set_trace() return plotargs, plotkwargs - def plot_data(self,data,mplcommand,p2p,fname,pt,cmap='jet',V=0,no_title=1,save=1): + def plot_data(self,data,mplcommand,p2p,fname,pt,no_title=1,save=1,**kwargs): """ Generic method that plots any matrix of data on a map @@ -71,7 +58,7 @@ def plot_data(self,data,mplcommand,p2p,fname,pt,cmap='jet',V=0,no_title=1,save=1 data : lat/lon matrix of data vrbl : variable type for contouring convention m : basemap instance - mplcommand : contour or contourf + mplcommand : contour or contourf etc p2p : path to plots fname : filename for plot V : scale for contours @@ -97,8 +84,8 @@ def plot_data(self,data,mplcommand,p2p,fname,pt,cmap='jet',V=0,no_title=1,save=1 # scaling_func = M.ticker.FuncFormatter(lambda x, pos:'{0:d}'.format(int(x*multiplier))) # plt.clabel(f1, inline=1, fmt=scaling_func, fontsize=9, colors='k') - plotargs, plotkwargs = self.get_contouring(V=V,cmap=cmap) - + plotargs, plotkwargs = self.get_contouring(**kwargs) + # pdb.set_trace() if self.mplcommand == 'contour': f1 = self.bmap.contour(*plotargs,**plotkwargs) elif self.mplcommand == 'contourf': @@ -122,7 +109,12 @@ def plot_data(self,data,mplcommand,p2p,fname,pt,cmap='jet',V=0,no_title=1,save=1 title = utils.string_from_time('title',pt,tupleformat=0) plt.title(title) #if self.C.plot_colorbar: - # self.fig.colorbar(f1,ax=self.ax,shrink=0.5,orientation='horizontal') + #self.bmap.colorbar(f1,location='bottom',orientation='horizontal') + # plt.show(self.fig) + # div0 = make_axes_locatable(self.ax) + # cax0 = div0.append_axes("bottom", size="20%", pad=0.05) + # cb0 = self.fig.colorbar(f1, cax=cax0) + # SAVE FIGURE datestr = utils.string_from_time('output',pt,tupleformat=0) diff --git a/postWRF/postWRF/clicker.py b/postWRF/postWRF/clicker.py index b10fd27..8ef736f 100644 --- a/postWRF/postWRF/clicker.py +++ b/postWRF/postWRF/clicker.py @@ -47,7 +47,6 @@ def draw_line(self): self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press) self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release_line) plt.show(self.fig) - return self.ax def on_press(self, event): print 'press' @@ -90,7 +89,7 @@ def overlay_data(self,data,V=0,cmap=0): kwargs['extent'] = (0,xlen,0,ylen) kwargs['picker'] = 5 - cf = self.bmap.contourf(self.x,self.y,data,**kwargs) + self.cf = self.bmap.contourf(self.x,self.y,data,**kwargs) # self.fig.colorbar(cf,ax=self.ax,shrink=0.5,orientation='horizontal') # pdb.set_trace() diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index c67048d..5b80554 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -844,7 +844,7 @@ def plot_xs(self,vrbl,times,latA=0,lonA=0,latB=0,lonB=0, for t in t_list: XS.plot_xs(vrbl,t,outpath,clvs=clvs,ztop=ztop) - def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom=1,twoplot=0): + def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom=1,twoplot=0,fig=0,axes=0): """ Pick A, B points on sim ref overlay This sets the angle between north and line AB @@ -863,6 +863,8 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom= swath_width : length in gridpoints in cross-section-normal direction dom : domain number return2 : return two figures. cold pool strength and cref/cross-section. + axes : if two-length tuple, this is the first and second axes for + cross-section/cref and cold pool strength, respectively """ # Initialise @@ -882,6 +884,15 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom= cps_kwargs['ax'] = P2.ax.flat[1] cps_kwargs['fig'] = P2.fig P2.ax.flat[1].set_size_inches(6,6) + + elif isinstance(axes,tuple) and len(axes)==2: + line_kwargs['ax'] = axes[0] + line_kwargs['fig'] = fig + + cps_kwargs['ax'] = axes[1] + cps_kwargs['fig'] = fig + + return_ax = 1 # Plot sim ref, send basemap axis to clicker function F = BirdsEye(self.C,self.W) @@ -921,10 +932,15 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom= # plt.show(imfig) # CPfig.plot_data(cps,'contourf',outpath,fname,time,V=N.arange(5,105,5)) mplcommand = 'contour' + plotkwargs = {} if mplcommand[:7] == 'contour': - cps_kwargs['V'] = N.arange(5,105,5) - CPfig.plot_data(cps,mplcommand,outpath,fname,time) + plotkwargs['levels'] = N.arange(10,85,2.5) + plotkwargs['cmap'] = plt.cm.ocean_r + cf2 = CPfig.plot_data(cps,mplcommand,outpath,fname,time,**plotkwargs) # CPfig.fig.tight_layout() if twoplot: - P2.save(outpath,fname+"_twopanel") \ No newline at end of file + P2.save(outpath,fname+"_twopanel") + + if return_ax: + return C.cf, cf2 \ No newline at end of file diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 2841c9e..feb3af0 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -921,7 +921,7 @@ def cold_pool_depth(self,dpt,heights,dpt_env): return dz, zidx - def find_gust_front(self,wind_slice,T2_slice,angle,method=2): + def find_gust_front(self,wind_slice,T2_slice,angle,method=3): """ Find location of maximum shear in the horizontal wind along a 1D slice. @@ -964,7 +964,7 @@ def find_gust_front(self,wind_slice,T2_slice,angle,method=2): gfidx = n break - elif method==2: + elif method==2 or method==3: ### METHOD 2: FINDING MAX GRADIENTS AND AVERAGING shear = abs(shear) @@ -973,7 +973,11 @@ def find_gust_front(self,wind_slice,T2_slice,angle,method=2): xsh_idx = N.where(shear == shear.max())[0][0] xtg_idx = N.where(T2grad == T2grad.max())[0][0] print("Max shear loc: {0} ... max tempgrad loc: {1}".format(xsh_idx,xtg_idx)) - gfidx = int((xsh_idx + xtg_idx)/2.0) + + if method==2: + gfidx = int((xsh_idx + xtg_idx)/2.0) + else: + gfidx = max([xsh_idx,xtg_idx]) return gfidx # maxshearloc[0][0] returns the integer From 5a0ff00077982a1982727b49cb93fbeefb421412 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Sun, 24 Aug 2014 18:44:12 -0500 Subject: [PATCH 104/111] Cool pool computations working --- postWRF/bin/bowecho_plot.py | 202 +++++++++++++++++++++--------------- postWRF/bin/settings.py | 1 + postWRF/postWRF/main.py | 10 +- postWRF/postWRF/wrfout.py | 23 +++- postWRF/postWRF/xsection.py | 42 +++++++- utils/GIS_tools.py | 1 + 6 files changed, 186 insertions(+), 93 deletions(-) diff --git a/postWRF/bin/bowecho_plot.py b/postWRF/bin/bowecho_plot.py index bbbdb89..00dc324 100644 --- a/postWRF/bin/bowecho_plot.py +++ b/postWRF/bin/bowecho_plot.py @@ -1,6 +1,10 @@ import os import pdb import sys +import matplotlib as M +M.use('gtkagg') +import matplotlib.pyplot as plt + sys.path.append('/home/jrlawson/gitprojects/') from WEM.postWRF import WRFEnviron @@ -11,44 +15,78 @@ config = Settings() p = WRFEnviron(config) +skewT = 0 +plot2D = 0 +streamlines = 0 +rucplot = 0 +coldpoolstrength = 1 + enstype = 'STCH' +# enstype = 'ICBC' +# enstype = 'MXMP' + #case = '20060526' #case = '20090910' -case = '20110419' -#case = '20130815' +# case = '20110419' +case = '20130815' IC = 'GEFSR2' #IC = 'NAM' #IC = 'RUC' -#ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] -#ensnames = ['p'+"%02d" %n for n in range(8,11)] -ensnames = ['p04',] -MP = 'ICBC' + + #ensnames = ['anl'] -#experiments = ['WSM6_Grau','WSM6_Hail','Kessler','Ferrier','WSM5','WDM5','Lin','WDM6_Grau','WDM6_Hail','Morrison_Grau','Morrison_Hail'] -#experiments = ['WDM5','Lin','WDM6_Grau','WDM6_Hail','Morrison_Grau','Morrison_Hail'] -#experiments = ['WSM5',] #experiment = 'VERIF' -experiments = ['s'+"%02d" %n for n in range(1,11)] -#itime = (2006,5,26,0,0,0) -#itime = (2009,9,10,23,0,0) -itime = (2011,4,19,18,0,0) -#itime = (2013,8,15,18,0,0) -#ftime = (2006,5,27,13,0,0) -#ftime = (2009,9,11,14,0,0) -ftime = (2011,4,20,10,30,0) -#ftime = (2013,8,16,12,0,0) +if enstype == 'STCH': + experiments = ['s'+"%02d" %n for n in range(1,11)] + ensnames = ['p09',] + MP = 'ICBC' +elif enstype == 'MXMP': + experiments = ['WSM6_Grau','WSM6_Hail','Kessler','Ferrier', + 'WSM5','WDM5','Lin','WDM6_Grau','WDM6_Hail', + 'Morrison_Grau','Morrison_Hail'] + # experiments = ['ICBC',] + ensnames = ['p09',] +elif enstype == 'ICBC': + ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] + +if case[:4] == '2006': + itime = (2006,5,26,0,0,0) + ftime = (2006,5,27,13,0,0) +elif case[:4] == '2009': + itime = (2009,9,10,23,0,0) + ftime = (2009,9,11,14,0,0) +elif case[:4] == '2011': + itime = (2011,4,19,18,0,0) + ftime = (2011,4,20,10,30,0) +elif case[:4] == '2013': + itime = (2013,8,15,21,0,0) + ftime = (2013,8,16,9,0,0) + times = [(2013,8,15,18,0,0),] +else: + raise Exception levels = 2000 -times = utils.generate_times(itime,ftime,60*60) +def get_folders(en,ex): + if enstype == 'STCH': + out_sd = os.path.join(case,IC,en,MP,ex) + wrf_sd = os.path.join(case,IC,en,MP,ex) + else: + out_sd = os.path.join(case,IC,en,ex) + wrf_sd = os.path.join(case,IC,en,ex) + return out_sd, wrf_sd + + +# times = utils.generate_times(itime,ftime,3*60*60) + #shear_times = utils.generate_times(itime,ftime,3*60*60) #sl_times = utils.generate_times(sl_itime,sl_ftime,1*60*60) -thresh = 10 -skewT_time = (2013,8,16,3,0,0) -skewT_latlon = (35.2435,-97.4708) +# skewT_time = (2013,8,16,3,0,0) +# skewT_latlon = (35.2435,-97.4708) +# thresh = 10 #variables = {'cref':{}} ; variables['cref'] = {'lv':2000,'pt':times} #variables = {'strongestwind':{}} ; variables['strongestwind'] = {'lv':2000, 'itime':itime, 'ftime':ftime, 'range':(thresh,27.5,1.25)} @@ -62,66 +100,66 @@ #variables = {'PMSL':{'lv':2000,'pt':times,'plottype':'contour','smooth':5}} #shear06 = {'shear':{'top':6,'bottom':0,'pt':shear_times}} - -""" -for en in ensnames: - for ex in experiments: +if skewT: + for en in ensnames: + for ex in experiments: + # Reload settings + p.C = Settings() + + # Change paths to new location + p.C.output_root = os.path.join(config.output_root,case,IC,en,MP,ex) + p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,MP,ex) + p.C.pickledir = os.path.join(config.wrfout_root,case,IC,en,MP,ex) + + p.plot_skewT(skewT_time,skewT_latlon,save_output=1) + + #p.plot_skewT(skewT_time,skewT_latlon,composite=1) + +if plot2D: + for en in ensnames: + for ex in experiments: + out_sd, wrf_sd = get_folders(en,ex) + + p.plot_strongest_wind(itime,ftime,2000,wrf_sd=wrf_sd,out_sd=out_sd) + #p.plot2D('cref',times,levels,wrf_sd=wrf_sd,out_sd=out_sd) + +if streamlines: + for en in ensnames: # Reload settings p.C = Settings() - - # Change paths to new location - p.C.output_root = os.path.join(config.output_root,case,IC,en,MP,ex) - p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,MP,ex) - p.C.pickledir = os.path.join(config.wrfout_root,case,IC,en,MP,ex) - - p.plot_skewT(skewT_time,skewT_latlon,save_output=1) - -#p.plot_skewT(skewT_time,skewT_latlon,composite=1) -""" - -for en in ensnames: - for ex in experiments: - # Reload settings - #p.C = Settings() - # Change paths to new location - if enstype == 'STCH': - out_sd = os.path.join(case,IC,en,MP,ex) - wrf_sd = os.path.join(case,IC,en,MP,ex) - else: - out_sd = os.path.join(case,IC,en,ex) - wrf_sd = os.path.join(case,IC,en,ex) - #print out_sd - #print wrf_sd - p.plot_strongest_wind(itime,ftime,2000,wrf_sd=wrf_sd,out_sd=out_sd) - #p.plot2D('cref',times,levels,wrf_sd=wrf_sd,out_sd=out_sd) - -""" - -for en in ensnames: - # Reload settings - p.C = Settings() - # Change paths to new location - p.C.output_root = os.path.join(config.output_root,case,IC,en,experiment) - p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,experiment) - #p.plot_2D(variables) - print p.C.wrfout_root - p.plot_streamlines(2000,sl_times) - - -# RUC file is one-per-time so .nc file is specified beforehand -en = ensnames[0] -RC = Settings() -RC.output_root = os.path.join(config.output_root,case,IC,en,experiment) -RC.path_to_RUC = os.path.join(config.RUC_root,case,IC,en,experiment) -WRF_dir = os.path.join(config.wrfout_root,case,'NAM',en,'ICBC') - -variables = ['streamlines',] -level = 2000 - -for t in sl_times: - RUC = RUCPlot(RC,t,wrfdir=WRF_dir) - #limits = RUC.colocate_WRF_map(WRF_dir) - RUC.plot(variables,level) -""" - + p.C.output_root = os.path.join(config.output_root,case,IC,en,experiment) + p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,experiment) + #p.plot_2D(variables) + print p.C.wrfout_root + p.plot_streamlines(2000,sl_times) + +if rucplot: + # RUC file is one-per-time so .nc file is specified beforehand + en = ensnames[0] + RC = Settings() + RC.output_root = os.path.join(config.output_root,case,IC,en,experiment) + RC.path_to_RUC = os.path.join(config.RUC_root,case,IC,en,experiment) + WRF_dir = os.path.join(config.wrfout_root,case,'NAM',en,'ICBC') + + variables = ['streamlines',] + level = 2000 + + for t in sl_times: + RUC = RUCPlot(RC,t,wrfdir=WRF_dir) + #limits = RUC.colocate_WRF_map(WRF_dir) + RUC.plot(variables,level) + +if coldpoolstrength: + for t in times: + for en in ensnames: + for ex in experiments: + fig = plt.figure(figsize=(8,6)) + gs = M.gridspec.GridSpec(1,2,width_ratios=[1,3]) + ax0 = plt.subplot(gs[0]) + ax1 = plt.subplot(gs[1]) + + out_sd, wrf_sd = get_folders(en,ex) + # print out_sd, wrf_sd + cf0, cf1 = p.cold_pool_strength(t,wrf_sd=wrf_sd,out_sd=out_sd,swath_width=130,fig=fig,axes=(ax0,ax1)) + plt.close(fig) \ No newline at end of file diff --git a/postWRF/bin/settings.py b/postWRF/bin/settings.py index 29832bb..4a4490c 100644 --- a/postWRF/bin/settings.py +++ b/postWRF/bin/settings.py @@ -10,5 +10,6 @@ def __init__(self): self.terrain = 0 self.colorbar = 1 self.matplotlibuse = 'gtkagg' + self.wrf_folders_descend = 0 #self.pickledir = '/home/jrlawson/data/sounding/WRFoutput/' diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index 5b80554..614f220 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -168,15 +168,19 @@ def get_wrfout(self,wrf_sd=0,wrf_nc=0,dom=0): wrf_nc : filename for wrf file dom : domain for wrf file """ + # Check configuration to see if wrfout files should be + # sought inside subdirectories. + descend = getattr(self.C,'wrf_folders_descend',1) if wrf_sd and wrf_nc: wrfpath = os.path.join(self.C.wrfout_root,wrf_sd,wrf_nc) elif wrf_sd: wrfdir = os.path.join(self.C.wrfout_root,wrf_sd) - wrfpath = utils.wrfout_files_in(wrfdir,dom=dom,unambiguous=1) + # print wrfdir + wrfpath = utils.wrfout_files_in(wrfdir,dom=dom,unambiguous=1,descend=descend) else: wrfdir = os.path.join(self.C.wrfout_root) - wrfpath = utils.wrfout_files_in(wrfdir,dom=dom,unambiguous=1) + wrfpath = utils.wrfout_files_in(wrfdir,dom=dom,unambiguous=1,descend=descend) return WRFOut(wrfpath) @@ -939,6 +943,8 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom= cf2 = CPfig.plot_data(cps,mplcommand,outpath,fname,time,**plotkwargs) # CPfig.fig.tight_layout() + plt.close(fig) + if twoplot: P2.save(outpath,fname+"_twopanel") diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index feb3af0..718196d 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -366,10 +366,25 @@ def compute_mixing_ratios(self,slices,**kwargs): qv = self.get('QVAPOR',slices) qc = self.get('QCLOUD',slices) qr = self.get('QRAIN',slices) - qi = self.get('QICE',slices) - qs = self.get('QSNOW',slices) - qg = self.get('QGRAUP',slices) - + + try: + qi = self.get('QICE',slices) + except KeyError: + print("MP scheme has no ice data.") + qi = 0 + + try: + qs = self.get('QSNOW',slices) + except KeyError: + print("MP scheme has no snow data.") + qs = 0 + + try: + qg = self.get('QGRAUP',slices) + except KeyError: + print("MP scheme has no graupel data.") + qg = 0 + rh = qc + qr + qi + qs + qg rv = qv diff --git a/postWRF/postWRF/xsection.py b/postWRF/postWRF/xsection.py index 45858dc..b714580 100644 --- a/postWRF/postWRF/xsection.py +++ b/postWRF/postWRF/xsection.py @@ -42,11 +42,43 @@ def __init__(self,config,wrfout,latA=0,lonA=0,latB=0,lonB=0): self.get_xs_slice() - def translate_xs(self,shiftpoints): - self.xA += shiftpoints - self.xB += shiftpoints - self.yA += shiftpoints - self.yB += shiftpoints + def translate_xs(self,sh): + """ + Translate the cross-section up or down a certain number of + points. + For simplicity with grid spacing, the logic allows for + 45 degree translation only. + + sh : number of points to shift + """ + + if (self.angle > 0.0) and (self.angle < 22.5): + shxa = -sh; shxb = -sh; shya = 0; shyb = 0 + elif (self.angle > 22.5) and (self.angle < 67.5): + shxa = -sh; shxb = -sh; shya = sh; shyb = sh + elif (self.angle > 67.5) and (self.angle < 112.5): + shxa = 0; shxb = 0; shya = sh; shyb = sh + elif (self.angle > 112.5) and (self.angle < 157.5): + shxa = sh; shxb = sh; shya = sh; shyb = sh + elif (self.angle > 157.5) and (self.angle < 202.5): + shxa = sh; shxb = sh; shya = 0; shyb = 0 + elif (self.angle > 202.5) and (self.angle < 247.5): + shxa = sh; shxb = sh; shya = -sh; shyb = -sh + elif (self.angle > 247.5) and (self.angle < 292.5): + shxa = 0; shxb = 0; shya = -sh; shyb = -sh + elif (self.angle > 292.5) and (self.angle < 337.5): + shxa = -sh; shxb = -sh; shya = -sh; shyb = -sh + elif (self.angle > 337.5) and (self.angle < 360.0): + shxa = -sh; shxb = -sh; shya = 0; shyb = 0 + else: + print("Angle {0} is weird.".format(self.angle)) + raise Exception + + + self.xA += shxa + self.xB += shxb + self.yA += shya + self.yB += shyb self.xx = N.linspace(self.xA,self.xB,self.hyp_pts) self.yy = N.linspace(self.yA,self.yB,self.hyp_pts) diff --git a/utils/GIS_tools.py b/utils/GIS_tools.py index b7db177..300e260 100644 --- a/utils/GIS_tools.py +++ b/utils/GIS_tools.py @@ -10,6 +10,7 @@ import pdb import sys import time +import glob import unix_tools as utils From e26d503b2ffb3fdae66fffa005a9c42f42554594 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Sun, 24 Aug 2014 21:49:27 -0500 Subject: [PATCH 105/111] Deleted files update --- lazyWRF/bin/organise.py | 18 - lazyWRF/lazyWRF/sensitivity_ensemble.py | 68 -- postWRF/bin/.DKE.py.swo | Bin 12288 -> 0 bytes postWRF/bin/20110419_plot.py | 54 -- postWRF/postWRF/.__init__.py.swp | Bin 36864 -> 0 bytes postWRF/postWRF/.birdseye.py.swp | Bin 20480 -> 0 bytes postWRF/postWRF/params.py | 0 postWRF/postWRF/protoinit.py | 2 - utils/PlotBasemap.py | 26 - utils/general_met.py | 44 - utils/generalmet.py | 44 - utils/gridded_data.py | 75 -- utils/mesowestscripts.py | 136 --- utils/metconstants.py | 10 - utils/more_utils.py | 213 ----- utils/shapefile.py | 1128 ----------------------- utils/topodata.py | 66 -- utils/utils.py | 213 ----- utils/wrf_tools.py | 242 ----- 19 files changed, 2339 deletions(-) delete mode 100644 lazyWRF/bin/organise.py delete mode 100644 lazyWRF/lazyWRF/sensitivity_ensemble.py delete mode 100644 postWRF/bin/.DKE.py.swo delete mode 100644 postWRF/bin/20110419_plot.py delete mode 100644 postWRF/postWRF/.__init__.py.swp delete mode 100644 postWRF/postWRF/.birdseye.py.swp delete mode 100644 postWRF/postWRF/params.py delete mode 100644 postWRF/postWRF/protoinit.py delete mode 100644 utils/PlotBasemap.py delete mode 100644 utils/general_met.py delete mode 100644 utils/generalmet.py delete mode 100644 utils/gridded_data.py delete mode 100644 utils/mesowestscripts.py delete mode 100644 utils/metconstants.py delete mode 100644 utils/more_utils.py delete mode 100644 utils/shapefile.py delete mode 100644 utils/topodata.py delete mode 100644 utils/utils.py delete mode 100644 utils/wrf_tools.py diff --git a/lazyWRF/bin/organise.py b/lazyWRF/bin/organise.py deleted file mode 100644 index 7551464..0000000 --- a/lazyWRF/bin/organise.py +++ /dev/null @@ -1,18 +0,0 @@ -class Organise: - def __init__(self): - self.path_to_WPS = '/ptmp/jrlawson/WPS' - self.path_to_WRF = '/ptmp/jrlawson/WRFV3/run' - self.path_to_GEFSR2 = os.join((path_to_WPS,'gefsfiles')) - self.path_to_soil = os.join((path_to_WPS,'gfsfiles/',casestr,'gfsanl*') - self.GEFSR2_Vtable = 'Vtable.GEFSR2' - self.soil_Vtable = 'Vtable.GFS_soilonly' - self.roughguesshr = 1 # Minimum time one WRF run would take - self.path_to_storage = '/chinook2/jrlawson/bowecho/' - - - # selected namelist.wps settings - self.maxdom = 1 - self.dx = - - # selected namelist.input settings - self.time_step = diff --git a/lazyWRF/lazyWRF/sensitivity_ensemble.py b/lazyWRF/lazyWRF/sensitivity_ensemble.py deleted file mode 100644 index b5ee93b..0000000 --- a/lazyWRF/lazyWRF/sensitivity_ensemble.py +++ /dev/null @@ -1,68 +0,0 @@ -# This is an example of creating a WRF ensemble using the stochastic kinetic energy backscatter scheme -# We assume met_em files have all been created, have been linked etc - -# Settings -datestring = '20090910' -ensembletype = 'STCH' # Change in future to expand to e.g. mixed microphysics -pathtoWRF = './' # Path to WRF folder where wrfout* files are generated - -def edit_namelist_input(old,new,pathtoWRF=pathtoWRF): - ninput = open(pathtoWRF+'namelist.input','r').readlines() - for idx, line in enumerate(ninput): - if old in line: - ninput[idx]= ninput[idx][:39] + new + " \n" - nameout = open(pathtoWRF+'namelist.input','w') - nameout.writelines(ninput) - nameout.close() - break - return - -if ensembletype == 'STCH': - ensnames = ['s' + '%02u' %n for n in range(1,11)] # Change the range to create n-member ensemble - -for n,ens in enumerate(ensnames): - # First, edit namelists - if ensembletype == 'STCH': - edit_namelist_input('nens',str(n)+',') - - # Run real.exe - p_real = subprocess.Popen('qsub -d '+pathtoWRF+' real_run.sh',cwd=pathtoWRF,shell=True,stdout=subprocess.PIPE) - p_real.wait() - jobid = p_real.stdout.read()[:5] # Assuming first five digits = job ID. - - # Run wrf.exe, but wait until real.exe has finished without errors - print 'Now submitting wrf.exe.' - # Again, change name of submission script if needed - p_wrf = subprocess.Popen('qsub -d '+pathtoWRF+' wrf_run.sh -W depend=afterok:'+jobid,cwd=pathtoWRF,shell=True) - p_wrf.wait() - print "real.exe and wrf.exe submitted." - - # Wait an hour to make sure real.exe is complete and wrf.exe is writing to file - time.sleep(60*60) - - # Check log file until wrf.exe has finished - finished = 0 - while not finished: - tailrsl = subprocess.Popen('tail '+pathtoWRF+'rsl.error.0000',shell=True,stdout=subprocess.PIPE) - tailoutput = tailrsl.stdout.read() - if "SUCCESS COMPLETE WRF" in tailoutput: - finished = 1 - print "WRF has finished; moving to next case." - else: - time.sleep(5*60) # Try again in 5 min - - # Copy namelist.input to directory - # Move wrfout file to directory - # Create directory if it doesn't exist - wrfoutdir = '/ptmp/jrlawson/predictability/'+datestring+'/wrfout/'+ensembletype+'/'+ens+'/' - - try: - os.stat(wrfoutdir) - except: - os.makedirs(wrfoutdir) - - os.system('cp ' + pathtoWRF + 'namelist.input ' + wrfoutdir) - os.system('mv' + pathtoWRF + 'wrfout* ' + wrfoutdir) - os.system('rm -f ' + pathtoWRF + 'rsl*') - - # Loop back to top diff --git a/postWRF/bin/.DKE.py.swo b/postWRF/bin/.DKE.py.swo deleted file mode 100644 index 423455066aa7e7d051b6a95f524e405159b44fc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2yKfvt9LFa>lY~G-=n&FySL_{{?CqX?7oQRf3CAH8M1t6PG_V@)&fcwayR*#9 z`hgJCDX5?Xi3%wB13(l=P(VkGXj9Qc#dr1|K5XMAqKGsre{OGQ<~NVe%$)Az*$(b* z+@e?fF2Qw?kUw5~YxCtx7tj9qEFn8;%=fe`Y}RR4Jjx0wANYKzWZ`FA8*YRzlIlp% zN3#k$!_1UQ?ugjv&b{?polNJ{r>;%c|tw}`ydA~ zxDRfEYv3w456*!{&k^!H_yT+m9)evEgLlB&;C0}G26z$teSwg_z;ECO@D2DHdI0MeW|74(4Sz%~nGget?sKJ5J2BRj+g>PS4rM*rs(!Q+gV4N>i zhS7!=u`CkpHwY>Hax6`hWN8|SLa5wQuA79hnua<0V+kwO|`-ZBiRVKxMMA_J`Ip z4*EuQax1epme#LJuP*fih$8hPB3j)bTw*IM?6a`T0*1W0{9;zmrrR9STnr1@GV2B` zth3>MG0x_NgA?O|0UNL|JTY#PSGRVoy`ax}tb1kN&BgX6dHs2NN33Hor9YX0D9y$~ zNAP(#(}~Pi!#0^Gp!iAVrxnzTaBdz89K~@$dcD}qaL9T(O|uC_-li}nD~1}A+@u=? zEb(!MC5hRu()}TqR*OxrGajsjJ&!aOlEPp)L_G^h!5|of!M~IGL9g3KU0GgA zWhhRF`;#?Szf!IzOOT{W=5_A${J1~8(^=qL*rX_qmPd0T(=s!05OOfx2)rysk+i(d zNamt*;-Ph~P3PUS>SkSy`52*G&7QFVXK$sRy+cE!(P$8v(u5mMwUG)RBSz9B)3Hyt zMwzDg!-q;i?dO_7Zly83Hi?!ze!z#qw{_G-Clf{WNbYGW_IX}n@ln)%EXKAGD}h~0 Jxl$bc_y?bn3HJa1 diff --git a/postWRF/bin/20110419_plot.py b/postWRF/bin/20110419_plot.py deleted file mode 100644 index 6456516..0000000 --- a/postWRF/bin/20110419_plot.py +++ /dev/null @@ -1,54 +0,0 @@ -import os -import pdb -import sys -sys.path.append('/home/jrlawson/gitprojects/') - -from WEM.postWRF.postWRF import WRFEnviron -from settings import Settings - -config = Settings() -p = WRFEnviron(config) - -case = '20130815' -IC = 'GEFSR2' -ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] -# ensnames = ['p'+"%02d" %n for n in range(8,9)] -# ensnames = ['c00'] -experiment = {'ICBC':'CTRL'} - -#itime = (2011,4,19,18,0,0) -itime = (2013,8,15,18,0,0) -#ftime = (2011,4,20,10,30,0) -ftime = (2013,8,16,11,30,0) -times = p.generate_times(itime,ftime,60*60) -shear_times = p.generate_times(itime,ftime,3*60*60) - -#variables = {'cref':{}, 'wind10':{}} -#variables = {'cref':{},'wind10':{},'CAPE':{}} -#variables['cref'] = {'lv':2000,'pt':times} -#variables['wind10'] = {'lv':2000,'pt':times} -variables = {'shear':{'pt':shear_times, 'top':3, 'bottom':0}} -# variables = {'thetae':{'lv':2000,'pt':times}, 'CAPE':{'pt':times}} -#variables = {'cref':{'lv':2000,'pt':times}} - -for en in ensnames: - # Reload settings - p.C = Settings() - # Change paths to new location - p.C.output_root = os.path.join(config.output_root,case,IC,en,experiment.keys()[0]) - p.C.wrfout_root = os.path.join(config.wrfout_root,case,IC,en,experiment.keys()[0]) - p.plot_2D(variables) - -""" -# Postage stamp plots -list_of_wrfouts = [] -for en in ensnames: - p.C = Settings() - list_of_wrfouts.append(os.path.join(config.output_root,case,IC,en,experiment.keys()[0])) - outdirectory = os.path.join(config.output_root,case,IC) - itime = (2011,4,19,21,0,0) - ftime = (2011,4,20,9,30,0) - times = p.generate_times(itime,ftime,3*60*60) - # CODE THIS UP v v v v v v v v v - #p.postage_stamps(variables,times,list_of_wrfouts,outdirectory) -""" diff --git a/postWRF/postWRF/.__init__.py.swp b/postWRF/postWRF/.__init__.py.swp deleted file mode 100644 index 9133e85aa1711adf97152862065930953ab1fe58..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36864 zcmeI53y@@2dEc*)kxY;Yi0k@5~@aVqt`LXJ%ogeGEIZyAjLM-08mE z(>p!g?Y_6?p_L#35)%Q&kI04XpafaijuR?+5KG3y!43%HvI$^^3bxF{5IYcJnIbp= z3Y_2fopbJ^yJvTcP$_Ww*8ZpO<2=6eo!9q$=R0Rtr%xR|>fchH^|+qxd8ePeYWdAq zKl>R!^E}Tx*J}k^QKv1RE_7Abn(axU$+y*r%xP8ek+w`yBSw1r=O0?Y&@jW?EuT z?76|e=D8=og@ndY#vB-PV9bFr2gV#2b70JYF$cyR7<1sel>>42O7FE0|1?{Gp8dUS z;P>hF`9}L24SfF|`+U&;{`|oAE9~=z{r$wi_bcslV1Ex;0vsLRYv0%H@2?DezudmR z#r|Gx3*_S4*W;ft2gV#2b70JYF$cyR7;|9EfiVZh92j$8%z-fn{=eivQ1iSWqU7Hc zMVa+~r~iNW`#kRt!27{@a0~dm;H%H_y!U`E=zw)_4!jy11}_HJgJ*!RUF~^a0G|RM z2JZzAfd|2xz`dXgUIBg#>;cz+uUzGM9|aGChrs>dwV(m&pa7l;o(A6gOum6Tz&HPn z=Y0cw0z3@f1YQNsf@{HFJ_91U6gy5B68O(!g!Lz_Ov2pw*_yBku+y{E#rQil2b^4798bJKa)s4-=etx>G zPVC3cjWF^uA^g^68l)9&hAlttXxLn+;?V=g77nZ&KXIpj==ceL`S{UA|I~pKhYuV) zvba2%3pzLQ$^~hMQ5@F%I~MQUcj~~AlZ*b+fx{=3b3t+sn}M&F6k{{!HG|bwIFd}V z&P61L{HWEe zhLK+j;=r%>Ivakx7W!MgdZ!=z^(M3iU0h?j5FUrEopKU)wcl*TezOh7@s#mvVLj;c zf@tLGcY2T))*`>!+2Fa^Zj}AQvEOgwi_>U?y|f-z)|!zYReMe9o4dHvo!Y?r#uwUZ zVYOLIOR(QR6t4QSKkCm;&)l5E*|TSl-oqn}P?=pQMq#U7qUt91dr7!n z825W^KZ<+FEAIU{9|pFfGWXmmmuj7jIZz^CD{Rl1pArS% zew}6$NF4;dMszmeS*_VKrEUisaKP|xuY?&vkEzhKefajW@5?mj zuw5(WNK~TyI?0EVDPQUayljJNZbLZ^y+w3mt9f^bHvi zI&~to8)4a>U+Z*;-0p9zhCP2ZjJLwD?ayexy_J$yTm7iH8J72C37{hjP5#`RKRxN+ zZ60p(Z_V(*WGy^RN?a>=^447w{!%ZD`Wp;Qt(Fv>jz~Z6g^U+bvlrIVZcBcApPMVR zJ8=}og-mCY)J&xb=k^L$o3ZEeaKDB_!_1NImO;mungWLQRq`%@*)akWY&r6 zMSjcx9Wc46!e0P=u9CBHfe2* za;@7eoOK-MLGzS+C2+A&@3iAWY42XG5P7J`w3xEpgo?v$LcvdN<}|3ePs2_4%VF$C z9hz?=Y=^y|MIGxL!V;EbCzr;QxrKo~W|V=fMu--jHs9f zGTfwPi16VAg&jJ5yFjmGs1JHF>Ox_!fS56w(etF85f7xDIfq}!g;SY0F@=B?>f?mfjj z-j9=#RhOF8b&&%jzpHnijekyDvF*JK1n!9ivEqFY;aw^FEf zE$F#tJIT7oYNyr0Et_F9iOj?%4ZYP|#T{Gjl(bgtQ3Fv}EF5Sw3X?8=8dyRdb+0>g zHWACTk!?%=n(uX@XxU`NW37p#+OCFEQ)^+fu||&-IT#$Gp%A;;E??lfZ8ASA!_r2)f=Of}5Jscu~I__DpkQ&NUSpd8hey+Q{aJ z1F4YiWxEGq-~*G<8WXa#e7Xa*&J^fJT_ox{mBUkeDvTnAb|xyOnsCFDZZNM>GB>8l z_j_gra~(%|kiWX4sh0dMoq>F`g=;=WjQ8fHmxO2@!s3-+J+!GM{ZEC2UVlh@j>?|s z|Nk02^_S3NMgMo}=Z~W6p93!gUq_dJ1XRHxPy!R++2Bg>JLv4c4qgY2f$PDu!DYY$ z??+Gn=ip~S8yp0p!#{=2{vPm8z&bbwUId;7{uBE8yTQ+byTD7qHQ>46Q|RmufI652 zUqna$WpFR}r{EO09hAWy@Hdq0N$@z3ay|gY*O&ui4vaZ4=D?T(-{Bl^a=A5E<#j-P zLVi#yL9tn?qlqCRp&q9~dtKxUEB2KoLSv&?#{ij8VntaQ4ARU8aaJ`U=djH~Wc?^a zcB!QXAQw)=dZTg0q1w+PBiw7UCCxvjb-++i!?Y|G(h_E~pt`P7qho_L11vd9pcKeI zKYfppi_Xv7Q{LmuG3^qvZc$k@IbdiyWRy3_q^NmRP)mE*_euqd6Lc`utx~-os0|4oin)wmPaXy@mzJd94SvMh zA;NYS^>FZ$gogx{BMzKFz~4A~ra;%|hf(3|neDS@rq2#%Cw8!7WrX=&SfsdU8#_gb zYMRm9Ts%oM{te|dfj)r|U1Jz?WccHKv5%=iD$iF(XC#)GnW3cQbCTx({Ypn+ID3h6 zdWOJ8pDbp}EcTn3!r9UFG~vG#Ekl!4MWNZQcl=;g%#ZD=+U3*EEt$v#%cHHix#|6R z{)zdv+b#*|GBkv!rgW+}RAMqlM+w(1$#O;}jA3wEho0udA48h8&0&|RL@+i)S=6qb z7D`G?`PCfK>IAil_HJvfG9A>|Y3B+?vqo)AGSQltP9EJTCN!LqKU~r}860Sf80bth zk_8YuUid+3&eY6o3}ZxR!D20Bpb~?wYZhu%^9ZJwq0{7o4I9n4g9UR9JD{w0m~|c6 zQmzL$DrKydPF)dGVMWYbjU6-EcH^CH$RHzcO2WuejvtI13~nQMZYLWC1<%r#)-*4j zS)be4g|r4-(Clv#Kl^gr-l;pZ}J8sYFW-m#x$a1WvsQu>GF^6Ef!Da6FYga#KIQc!>}CVYut&07MAeA z>_$CbobC|}_`jlC^#67A|F?=xjQ)Sn3PT@5@Bb+H2>6$v0fgSJ)a2vP`d;t4^*bJ7z_k;Jr z2eA`83X~_`;lkmVyfFvH92j$8%z?iR98j4+gt-l@_!U{k36KJ3jVR|lFeH#Cf1LCu z_vy7qg|b*Hq$OfM${}ka9Rt^Y_s`${HVq8+fE&ntn5WNW3wuBa|59`wVRmNbLd-|6%VtZp~(E@~%K8g^{p5v1##>a!%nOEH=(Sy#l2 zqhu~&&L8sa<=j7$7sr=Y4j(^u;7IZ!36Z=(WraHRe6!xCX_M0$&>;^x{3Hryj5!oC z7JkJUbb}w<$)c2#%5vp{Xvo|H8DvFh_5KXZl zKDF5lwgQS*-j$XfOar9jX82od=I5$*-0iTfP5Gjq+6SFv(wYlDN{8J zd>^t`=7VI$Tj=Lx$|Wr+ZJ3spkx5V^gDJR5G6`Iu?lh*_ zbuUjBGu{%<(#}xsSU3KZPFr86f)qjaH2MQ}q0Q3tj_`f#-owqSyat@cZC> z;4yG7H~?-1;_v^PK>Yhpg1z8*;4|p(e+2#wcrysVZQwfaVRZL*f>(huxE?%--u_>DYVZVl{m+57fV;su zcm=o<{1~_qTmhcKCh~{iV?ccS9|HdZ+z;*q;@kgB@HhDKKM5WMKLs|x?cgx@0q|-3 z_&*4q0C$6b0GT>b`9|sSB*MkPw3&f}Y5pXw{1E0T)a)Bqo<3KK{z_Fm1ZQB`9 z>04h*84HSIK{1`5CRDt3gLY2391DtLL2)c7js-=Thduct*LQ}XXlU8(@1Q|PG=G#l zG$*V#YK;()R*XlfApjv-d=#2dw-RTJ04@vjfno9@W}X2nk0_tI1U$GIW0aSJvi5X3 zG?TaI@^`;>^N4Qmz=3(_d$NI~J;gQ$_B(XLymi+-A;R*9Tg+zzXKY$h(Cuj+^Fy+e17qGm(sWG%c#Bjzw)CsR=ejh1GR! zumhEuODFdr)Y(R=Syp6myeKNsgpWlkR?WNG>GUu`VcE*X%{{0RihbkEW`IXYH)bQt zgnv{@B#ZXBplXsxg0?m_=(21_Nt8UhePT_Ver$UzhUG@NglZqw(^e!M27?5)LefjM z*}+DL{;aJJl8(38I4Q0Ww551{;gB{bzk`|XhJ;8P+|{hrzSSV)cKooNFsqbjonMk< zCX|d_j8`(GV2~N5BK!6lNN5*BnIR)(NfK+MnIicrc}z`B-DY>TVt$eBYa2~^T)R;c zMM^i*(&q}Y$F0jg$~a25Q%Uq*t0O<^1k%$T3a#b_olvcs)+pm?2>Yj%iO>q`46zpu zzuF2krg)jGvdfiidua@7>+x*b`V(g&u#NX3p^MJ6Hmv{OfIj&p(JRsaH$=JQ`U~`Z z+5i9Vfb0c$1L%NL;Dz8i@MU!V4}#wT_X9ZtpaPx)o({eTTn@gB4d9dDBjC5dzX$8! z6nGgBpMWBGKKO#z1i(ANgP;i(!1sgCVHd2KMj5xJP2M7R=_P_ z68tZ01AhWO2JQnt4bFgl;BxTi*aqGWeiC%S0q}hA6>I??2fqvM13O?F+zOr#CV2>Snn;1|K0!0W)BU>f{yYyj^AKM7XA4}edh`~N0*1jxSsw}C2n z1-KqOg)QLE!2_TH&VW7OYt)_8-(N``UhHx$DA$x~74Kg_;2Hr^g z7}bxC9V(7s^*fpwS+^nA6p6wmStU2>i|CsXL}e_KkL=x-$y-K}q}WNM z zI_7Bu$;4r-v>t0knQcT~G0*AXtfM{|N~Y#sW-#N)ru&>Cf69;hU2O7vVCPsuu33&S zF@se$!jXfVRiHbN%^aX*Obx7t?p2oZ%t;Mhkr-Jhi_LEeVHeQWA&}-7>q~9cY6CktOSUlIPBb#5uH@E}~*=Mc`bnIZu?8enoB{sr% zty6QZikxv_cQex;Er-&??pRFQNp^tnxlFU0n<=?gtGC+7=L#7QNA>S4WJVf^EMf@D zrWvnCExX#tqmowf+_6qOENLq-x1K~4YpV6K%(+o|`c*=>j+6C4wmQWTWGva5XBWmt z_@X=iQ$2=YGEGzy)^hDBDp7gmN9>#CKBYE2o{GxMVF~>4SzMGVdz zRgv|U&G@-%ZOXc{3mkP@EZ}d)W+Q;Oh)X6cQ$d@#J^T~q5ykWLEOKoWqx`)!P+!vW+Y{YW;nTNz2SHFljNd#%0zuCq^5c;ZXi6A(a_=t5L2X5x+msTdZ+i{NS zf``FF;GcmmI0CK$QXh{DhUIr&e4%Ehr*AG#%kZnmP2k3|#-OgfP|iSV(hy<> z8M|>Qv6dEZ_xB;7H2gdKz5aS59X%!C6QnzP>n)n>$u`1gFG3e+AWkI-T`>NN>_lr< zXUd6{^GsnSV@;4eR;D$~^lo7opoVtQcoI>rQzJ4smC4}bi0>ye-%pLmJQsh&_iTJz zhh(>(%wL|+BUg6(ln?UGDZh+)d8>jaU*+0evsaH_LTDOnG?UZNjL~GD(N)!Sq9+mJ zHMYjIBFFIbLX60=MNKqCPjZketFSt4j3GjICwUrzKXHYAcAcnQk_M1_yiL?1qJ>?v5BV`|eV+P{y1<1FgzYhOu}p z$^mK&sT_k9Rr^>NG@dqIjyRNq{gSK}Fj?s#i6Zb4O(tD4>#>;=4#*l+tS-4>s$VCu zm-`!$?42DpLY1t1AQN83-jk9(uV*#tR3~alhQTBC6N-ftvxDVSG(s#5jj(GFs(dQY7bRz#Oa{o zqF9YQsYS*mQwqkWt#UbQ?~y2;iL^-3kPAITi|pv&Bl;5Z|~k{Zer@{=2>qkP>=_zUSF*hEwzI0?_Wkh^)xXZBn; zTb7&>nOYLG0VgRXH#%ku4Z3V{S7E=39f&@6mC}`z#9W5Mmc2dg&l$2M)U|=aylebda_W8IpB7m5LlmW-syQ15prG zWzQC8DTcipRR7;9HPQd;R^R-u==z@n9|FG%9s@rEj)3QYr_l311pX1Y4O|Cg zeg7>$*7TnRofGVsUf`yUmZA3O$r9=rv-0m!-j&j&vYt^%LO4)6r{CGZb{obmrE@FMVo;LF$p zJ`DZ-+2AD0nfr30wi>T>m$KSAzqf z2xN`_*MQgs7I$@3wdvS=6n5rJj+#m#s0PIfq(kLo4WoS%al3j8_7s_$Y&?GV(r$EJL zGcolj6(Sug-xac}u9%@RB~z6Bk& zJtfC#<-$wYW;SdPo3K0iuwoIg3~uuqm29d>Vlw;7DcJZaDpLuxkBqI$TM{g}xr-X2 zv?r&_(OS?A&&-IYV=8k){Yn2u|5OrQYLxJ&T=*AA_!(L#UZ2=y+ERv-NoZ*V+0#-2MkMcuHh~VTWhI9+A`jKP#Nz z+!0S)ym>LnwyTna$jt1NMh{FPCO6K$=W}K4$j&`1wFD+nb~hsHOh7_nqg}&sm08kE z7$xz4OA^qT!v>T4(V;HQicB(c<*dlz_WvszvdKLC>$zJbUQxO1|CEva&vlL(&!sv> z40Zy`|7?UAn6vDa3nI{4d%JTO#Ca<*G9<|-H==ugSJYIX5}rHV&fH(((RSu|IHF9= zNCJAQoqLibMjUD!*0K?tL At^fc4 diff --git a/postWRF/postWRF/.birdseye.py.swp b/postWRF/postWRF/.birdseye.py.swp deleted file mode 100644 index 82843ceadd65b385b47e2c404b4719c8e86c6843..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 zcmeI2dyFJS9mo4bIAnPk{KJUR6g$zGF}=OBdzgFZME5a!OxWAy_K6}iH9a*u?e)v9 zn%SK*2WpIo2685b2#HZ+{KNPPG2$6TIV9x%&(O0Rt=dNO1UYQg3>9;v^4KZuLnl1Z%v;uEufCka95=6SEbiDy=Lj8kNTn&& zjI+Q7w}LtFZg3TN{u;yh99Rd7;3hBz{`xl91}=a{z{9`;cY_i*2wuI~FkS@bzz@O0 z;3TMl8^Oy5QAY4PZ~=S`+z*z(N5LzY^mq|G4ITjl@CooCa5XpxUb@mSeh$6^J_BrU z5}4p`m|Xca_!9Uym;O_%?*;Dx&*zgb zBt1%GKBn8mXR+IMX}}2+4%v1^mu$zKj^$A?>4gE|5p~>P%P_KdzSTxNC+r0xb{Vf! zM|`-T;x(L|kaFR;yuVZamBJMFbOq(&|q1O{`7}Q&oGoMB_ zoJ<-sYKtyzyY&y&YKBZ8J7BzB%Mx-}V8g+bsqc2ALOD)lA=50UOe0H3Ueg^aGXJvY zIAwF&ZHsPQoo(OjGIy&h>d6Jqd0d~=$!ub9rI0WS2hX&i?ZF2s>FvO7yZ*#fp-P&` zP~e?B@Xl!9Jm(>q5Oa&+t%x|uW6mrO`Ib$U@w_$UR%Bix3#b=e*eL|60axj_S%+ky z;n9m!y47Pv300CEcgu3T&QJ|e&YbpHVh92xnMc!6a?=T@&x+iWWTabGdIxV@Fk6MI z1hh&@WGN(VO8PW*Y0G1z%VL(RKybv=<%~3T92Ut6w97Aw-9S{v=RzNaBRmX)V}!O` z&lLkALXzZ0oE{gUPdcv0q%C~Buux`DB8bmOFG`vZce>1HmSyERF^{uj`IC$iP!y=r zr>568+p@igcr?v*7!9Fm&l3oq*r!s?6LLEw2&HF{cA6FvM`Q80g>+FcyAy}LEnJ^f z%3)7LJyA9zQDj&mt1}x7vd}!fzS1aOqBJz}gyn=@7`JF#3DMvKR3z05x_CMr;?XM? zDZwQ5&gQJh3x%DZT?86Dg-aGERn^T*~a3kN!FTc zi;Y#;(HSx;c^K-Po|Ne=bS@}zAi`eUsqB~o`GTk=`xqLrO2wl#`jOBMs#VkR`rLF7 zq3-xaG4{Uh%T0JFEmduc!bnTgTxFARsK>a4)@?g}Qk{yZA|4d+P)C;+mse(IR!rP2 ztu3#wG?$JS?^+fJ396#1g5EhXb(fLqf=+pl?{wiSBS~YDJ{!hJOKx8+EV+FZ-k&au z@-e(#pV@AE$0TL1k7uu+JyH3D?wNbiFO*N{!MS&)ct_}r8f$a6lKGjnncTMgCX1Xz z%M6m*c&_gX*0$TBPu-x>riE#;a;!}*E-&TDc!X$$&x--S|g7{FhnRuJJf%fM4e36 zYPI24zS!7kEE*~rPWest4l;>0RRzjiE4zD!wdRRN9uAU)3F-3BFj$WAVaH8fV0NOP&V)|^>vt|kJ=QBU@2)AMTdGEvDR zVKQ_EvJW>g7$w^8$>F~{2qr`38;paOGcK4I&^?Bi>r+v$6))v=Mv2QDAxa~?_1+;u zCaC)G(c$5ehlbZJ!ARbtBgOdmc>eO{x-{5gM06R(VSEa7DVJYRH|GI{1FIp13>|wL zx%AB2KsLVQ!%LyE442m0jiN2X#g9BE^nDt%Wm7MwBUHHvW5}rT$g_FVH#J)hElF#W zcV?l1sj9tffE%czN(4g{J}W;yDbai2yxVymj`PV^)$p&2l57)x`1ipV((X8OaEILJ zA(Xr92W;C7Tw&XZZHcFu#5Iv8401fmIhmE4*Bb*?Sx}vl{6uzrIW8pMXNLSR!*`|X zuu_jCWa5*4|8bJ~WtQXLTSkN&wg^M-lq-1l`JM%xP5wHv?9i5F-{rY2?4y@=gmU?D zm-$ESmLdQDKa79)2+03`{Hm(s9G-syF|7q||@D1=q&;SR(vslmn8F&;t0CvC~ z;AO1iUjV-Z_k%NF1vJ4?Fb>ARU$CbC40sR>Km_gvRY1TPcoOUT-vXZp9l*c>I0~)@ zuVQWgJopjV0&>lN28@Fju&)1G@B?rf41fbBz)j#ESl53RJPE!IJ_hRG-wE}NC1!O-b5540= zZ9~yJPLcsk?>NyrPV|lw>~qsQPV|nGWQ4?7?>NyrPV|nGq&esvC;RU>d5uktM&6O& TX4ixC$ajY9*??khRE_@tf!!M0 diff --git a/postWRF/postWRF/params.py b/postWRF/postWRF/params.py deleted file mode 100644 index e69de29..0000000 diff --git a/postWRF/postWRF/protoinit.py b/postWRF/postWRF/protoinit.py deleted file mode 100644 index e7dcb3f..0000000 --- a/postWRF/postWRF/protoinit.py +++ /dev/null @@ -1,2 +0,0 @@ -from macro import * - diff --git a/utils/PlotBasemap.py b/utils/PlotBasemap.py deleted file mode 100644 index f73ca91..0000000 --- a/utils/PlotBasemap.py +++ /dev/null @@ -1,26 +0,0 @@ -import numpy as N -import matplotlib as M -M.use('Agg') -import matplotlib.pyplot as plt -from mpl_toolkits.basemap import Basemap - -class PlotBasemap(Basemap): - def __init__(self): - self.Nlim = 50 - self.Elim = -100 - self.Slim = 30 - self.Wlim = -130 - self.initial() - - def initial(): - (projection....) - - def changeView(self,domain): - if domain == 'UK': - pass - - def outlines(self): - # Draw coastlines - def saveBasemap(self,outdir,fname): - self.fig = plt.gcf() - plt.savefig(outdir+fname) diff --git a/utils/general_met.py b/utils/general_met.py deleted file mode 100644 index f202440..0000000 --- a/utils/general_met.py +++ /dev/null @@ -1,44 +0,0 @@ -import numpy as N -import pdb - -def decompose_wind(wspd,wdir,convert=0): - # Split wind speed/wind direction into u,v - if (type(wspd) == N.array) & (type(wdir) == N.array): - uwind = N.array([-s * N.sin(N.radians(d)) if ((s>-1)&(d>-1)) else -9999 - for s,d in zip(wspd,wdir)]) - vwind = N.array([-s * N.cos(N.radians(d)) if ((s>-1)&(d>-1)) else -9999 - for s,d in zip(wspd,wdir)]) - else: - uwind = -wspd * N.sin(N.radians(wdir)) - vwind = -wspd * N.cos(N.radians(wdir)) - if convert == 'ms_kt': - uwind *= 1.94384449 - vwind *= 1.94384449 - elif convert == 'ms_mph': - uwind *= 2.23694 - vwind *= 2.23694 - elif convert == 'kt_ms': - uwind *= 0.51444444 - vwind *= 0.51444444 - else: - pass - return uwind, vwind - -def combine_wind_components(u,v): - wdir = N.degrees(N.arctan2(u,v)) + 180 - wspd = N.sqrt(u**2 + v**2) - return wspd, wdir - -def convert_kt2ms(wspd): - wspd_ms = wspd*0.51444 - return wspd_ms - -def dewpoint(T,RH): # Lawrence 2005 BAMS? - #T in C - #RH in 0-100 format - es = 6.11 * (10**((7.5*T)/(237.7+T))) - e = es * RH/100.0 - alog = 0.43429*N.log(e) - 0.43429*N.log(6.11) - Td = (237.7 * alog)/(7.5-alog) - #pdb.set_trace() - return Td diff --git a/utils/generalmet.py b/utils/generalmet.py deleted file mode 100644 index f202440..0000000 --- a/utils/generalmet.py +++ /dev/null @@ -1,44 +0,0 @@ -import numpy as N -import pdb - -def decompose_wind(wspd,wdir,convert=0): - # Split wind speed/wind direction into u,v - if (type(wspd) == N.array) & (type(wdir) == N.array): - uwind = N.array([-s * N.sin(N.radians(d)) if ((s>-1)&(d>-1)) else -9999 - for s,d in zip(wspd,wdir)]) - vwind = N.array([-s * N.cos(N.radians(d)) if ((s>-1)&(d>-1)) else -9999 - for s,d in zip(wspd,wdir)]) - else: - uwind = -wspd * N.sin(N.radians(wdir)) - vwind = -wspd * N.cos(N.radians(wdir)) - if convert == 'ms_kt': - uwind *= 1.94384449 - vwind *= 1.94384449 - elif convert == 'ms_mph': - uwind *= 2.23694 - vwind *= 2.23694 - elif convert == 'kt_ms': - uwind *= 0.51444444 - vwind *= 0.51444444 - else: - pass - return uwind, vwind - -def combine_wind_components(u,v): - wdir = N.degrees(N.arctan2(u,v)) + 180 - wspd = N.sqrt(u**2 + v**2) - return wspd, wdir - -def convert_kt2ms(wspd): - wspd_ms = wspd*0.51444 - return wspd_ms - -def dewpoint(T,RH): # Lawrence 2005 BAMS? - #T in C - #RH in 0-100 format - es = 6.11 * (10**((7.5*T)/(237.7+T))) - e = es * RH/100.0 - alog = 0.43429*N.log(e) - 0.43429*N.log(6.11) - Td = (237.7 * alog)/(7.5-alog) - #pdb.set_trace() - return Td diff --git a/utils/gridded_data.py b/utils/gridded_data.py deleted file mode 100644 index 29444de..0000000 --- a/utils/gridded_data.py +++ /dev/null @@ -1,75 +0,0 @@ -import numpy as N -import pdb - -# This returns the x and y grid point for a lat/lon. -# lats/lons are from the model output -# ptlat/ptlon are desired - -def getXY(lats,lons,ptlat,ptlon): - # Find closest lat/lon in array - minlat = abs(lats-ptlat).min() - minlon = abs(lons-ptlon).min() - # Find where these are in the grid - wherelat = N.where(abs(lats-ptlat) == minlat) - wherelon = N.where(abs(lons-ptlon) == minlon) - # pdb.set_trace() - lat_idx = N.where(lats==lats[wherelat])[0][0] - lon_idx = N.where(lons==lons[wherelon])[0][0] - exactlat = lats[wherelat] - exactlon = lons[wherelon] - return lat_idx,lon_idx, exactlat, exactlon - -def gettopo(): - fname = '/uufs/chpc.utah.edu/common/home/u0737349/dsws/topodata/globe30.bin' - f = open(fname,'r') - fdata = N.fromfile(f,dtype='int16') - # Transposes and reshapes to a lat-lon grid - # Changes negative values to 0 (sea level) - xnum = 43200.0 - ynum = 18000.0 - topodata = N.flipud(N.reshape(fdata,(ynum,xnum))).clip(0) - #topodata = ((N.reshape(fdata,(xnum,ynum))).clip(0)) - f.close(); del fdata - # Define size of pixels - xpixel = 360/xnum - ypixel = 150/ynum # Note only 150 degrees! - # Create lat/lon grid - lats = N.arange(-60,90,ypixel)#[::-1] - lons = N.arange(-180,180,xpixel)#[::-1] - print 'Topographic data has been loaded. Everest is but a mere pixel.' - return topodata, lats, lons - -def xs_distance(Alat, Alon, Blat, Blon): - phi1 = N.radians(90.0-Alat) - phi2 = N.radians(90.0-Blat) - theta1 = N.radians(Alon) - theta2 = N.radians(Blon) - arc = math.acos(math.sin(phi1)*math.sin(phi2)*math.cos(theta1-theta2) + - math.cos(phi1)*math.cos(phi2)) - xsdistance = rE * arc - return xsdistance - -# This dstacks arrays, unless it's the first time through, in which case it initialises the variable -def dstack_loop(data, Dict, Key): - # Try evaluating dict[key]. If it doesn't exist, then initialise it - # If it does exist, stack data - try: - Dict[Key] - except KeyError: - stack = data - #Dict[Key] = data - else: - stack = N.dstack((Dict[Key],data)) - return stack - pass - -# Create thinned pressure levels for skew T barb plotting -def thinned_barbs(pres): - levels = N.arange(20000.0,105000.0,5000.0) - plocs = [] - for l in levels: - ploc = N.where(abs(pres-l)==(abs(pres-l).min()))[0][0] - plocs.append(ploc) - thin_locs = N.array(plocs) - return thin_locs # Locations of data at thinned levels - diff --git a/utils/mesowestscripts.py b/utils/mesowestscripts.py deleted file mode 100644 index bd1383f..0000000 --- a/utils/mesowestscripts.py +++ /dev/null @@ -1,136 +0,0 @@ -# Functions useful for mesowest data - -import numpy as N -import calendar as cal -import pdb -from netCDF4 import Dataset - -def csvprocess(data,names,convert=0): - # Get stations - stationlist = N.unique(data['stid']) # Stations in the record - stnum = len(stationlist) # Number of them - - - # Create dictionary from data - D = {} # Initialise dictionary of data - for s in stationlist: - D[s] = {} # Initialise dictionary of station/s - print 'Loading station data for ' + s - those = N.where(data['stid']==s) # Indices of obs - obNum = len(those[0]) # Number of obs - - # Entries from MesoWest data - for n in names: - D[s][n] = data[n][those] - - # Sort times - year = [int(t[0:4]) for t in D[s]['tutc']] - month = [int(t[4:6]) for t in D[s]['tutc']] - day = [int(t[6:8]) for t in D[s]['tutc']] - hour = [int(t[9:11]) for t in D[s]['tutc']] - minute = [int(t[11:13]) for t in D[s]['tutc']] - - # Create python time - D[s]['pytime'] = N.empty(obNum) - for i in range(0,obNum-1): - D[s]['pytime'][i] = cal.timegm([year[i],month[i],day[i],hour[i],minute[i],0]) - - # Convert to S.I. units - if convert == 1: - D[s]['tmpc'] = (5/9.0) * (D[s]['tmpf']-32) - D[s]['dptc'] = (5/9.0) * (D[s]['dptf']-32) - D[s]['wsms'] = D[s]['wskn']*0.51444 - D[s]['wgms'] = D[s]['wgkn']*0.51444 - - ### DATA PROCESSING - # Find time of biggest wind increase (DSWS initiation) - - D[s]['dVdt'] = N.zeros(obNum) # This is our array of d(wind)/d(time) - for i in range(0,obNum-1): - if i == 0: - pass # First record is zero as there's no gradient yet - else: - D[s]['dVdt'][i] = ((D[s]['wsms'][i] - D[s]['wsms'][i-1])/ - (D[s]['pytime'][i] - D[s]['pytime'][i-1])) - if any(D[s]['dVdt']) == False: - # Data were absent (-9999) or rubbish (0) - D[s]['mwi'] = -9999 - D[s]['mwi_t'] = -9999 - else: - D[s]['mwi'] = D[s]['dVdt'].max() # max wind increase - loc = N.where(D[s]['dVdt']==D[s]['mwi']) - D[s]['mwi_t'] = D[s]['pytime'][loc][0] # time of max wind increase - - # Find time of maximum wind gust - if any(D[s]['wgms']) == False: - D[s]['mg'] = -9999 - D[s]['mg_t'] = -9999 - else: - D[s]['mg'] = D[s]['wgms'].max() # Maximum gust at the station - loc = N.where(D[s]['wgms']==D[s]['mg']) - D[s]['mg_t'] = D[s]['pytime'][loc][0] # time of max gust - - # Find time of maximum wind speed - if any(D[s]['wsms']) == False: - D[s]['ms'] = -9999 - D[s]['ms_t'] = -9999 - else: - D[s]['ms'] = D[s]['wsms'].max() # Maximum gust at the station - loc = N.where(D[s]['wsms']==D[s]['ms']) - D[s]['ms_t'] = D[s]['pytime'][loc][0] # time of max gust - - - # Frequency of observation in minutes - try: - D[s]['dt'] = (D[s]['pytime'][1] - D[s]['pytime'][0]) / 60 - except IndexError: - D[s]['dt'] = -9999 - - # Find lowest pressures - try: - D[s]['lowp'] = D[s]['alti'].min() - except IndexError: - D[s]['lowp'] = -9999 - D[s]['lowp_t'] = -9999 - finally: - if D[s]['lowp'] != -9999: - D[s]['lowp_t'] = N.where(D[s]['alti']==D[s]['lowp']) - else: - pass - - return D - -# WRF terrain in height above sea level -def WRFterrain(fileselection='control',dom=1): - if fileselection=='nouintah': - fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/wrfout/1/NAM/2011112906_nouintah/wrfout_d0'+str(dom)+'_2011-11-29_06:00:00' - elif fileselection=='withuintah': - fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/wrfout/1/NAM/2011112906_withuintah/wrfout_d0'+str(dom)+'_2011-11-29_06:00:00' - else: - fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/WRFV3/test/em_real/wrfout_1.3_wasatch/wrfout_d0'+str(dom)+'_2011-12-01_00:00:00' - nc = Dataset(fname,'r') - terrain = nc.variables['HGT'][0,:,:] - xlong = nc.variables['XLONG'][0] - xlat = nc.variables['XLAT'][0] - return terrain, xlong, xlat - -# WRF terrain in pressure coords...from first time stamp (won't vary THAT much for a tropospheric plot!) -# SLICE keyword, when true, gives 1-D vector through middle of square. - -def WRFterrain_P(fileselection='control',dom=1,slice=0): - if fileselection=='nouintah': - fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/wrfout/1/NAM/2011112906_nouintah/wrfout_d0'+str(dom)+'_2011-11-29_06:00:00' - elif fileselection=='withuintah': - fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/wrfout/1/NAM/2011112906_withuintah/wrfout_d0'+str(dom)+'_2011-11-29_06:00:00' - else: - fname = '/uufs/chpc.utah.edu/common/home/horel-group/lawson/WRFV3/test/em_real/wrfout_1.3_wasatch/wrfout_d0'+str(dom)+'_2011-12-01_00:00:00' - nc = Dataset(fname,'r') - terrain = nc.variables['PSFC'][0,:,:] - xlong = nc.variables['XLONG'][0] - xlat = nc.variables['XLAT'][0] - if slice==1: - Nx = nc.getncattr('WEST-EAST_GRID_DIMENSION')-1 - Ny = nc.getncattr('SOUTH-NORTH_GRID_DIMENSION')-1 - xlong = xlong[Ny/2,:] - xlat = xlat[:,Nx/2] - return terrain, xlong, xlat diff --git a/utils/metconstants.py b/utils/metconstants.py deleted file mode 100644 index ae6b121..0000000 --- a/utils/metconstants.py +++ /dev/null @@ -1,10 +0,0 @@ -Tb = 300.0 # Base temperature -Tz = 273.15 # 0 C in Kelvin -L = 2.501e6 # Latent heat of vaporisation -R = 287.04 # gas constant air -Rv = 461.5 # gas constant vapour -cp = 1005.0 -cv = 718.0 -kappa = (cp-cv)/cp -g = 9.81 -P0 = 10**5 # Reference pressure diff --git a/utils/more_utils.py b/utils/more_utils.py deleted file mode 100644 index 4cfaa72..0000000 --- a/utils/more_utils.py +++ /dev/null @@ -1,213 +0,0 @@ -import numpy as N -import os -import time -import calendar -import pdb - -""" A collection of useful utilities. -""" - -def trycreate(loc): - try: - os.stat(loc) - except: - os.makedirs(loc) - -def padded_times(timeseq): - padded = ['{0:04d}'.format(t) for t in timeseq] - return padded - -def string_from_time(usage,t,dom=0,strlen=0,conven=0,**kwargs): - """ - conven : convection of MM/DD versus DD/MM - """ - - - if isinstance(t,str): - if usage == 'output': - usage = 'skip' # Time is already a string - elif usage == 'title': - pass - # if kwargs['itime']: # For averages or maxima over time - # itime = kwargs['itime'] - # ftime = kwargs['ftime'] - # else: - # pass - else: - raise Exception - elif isinstance(t,int): - # In this case, time is in datenum. Get it into tuple format. - t = time.gmtime(t) - else: - pass - - if usage == 'title': - # Generates string for titles - if not 'itime' in kwargs: # i.e. for specific times - #if not hasattr(kwargs,'itime'): # i.e. for specific times - strg = '{3:02d}:{4:02d}Z on {2:02d}/{1:02d}/{0:04d}'.format(*t) - else: # i.e. for ranges (average over time) - s1 = '{3:02d}:{4:02d}Z to '.format(*kwargs['itime']) - s2 = '{3:02d}:{4:02d}Z'.format(*kwargs['ftime']) - strg = s1 + s2 - elif usage == 'wrfout': - # Generates string for wrfout file finding - # Needs dom - if not dom: - print("No domain specified; using domain #1.") - dom = 1 - strg = ('wrfout_d0' + str(dom) + - '{0:04d}-{1:02d}-{2:02d}_{3:02d}:{4:02d}:{5:02d}'.format(*t)) - elif usage == 'output': - if not conven: - # No convention set, assume DD/MM (I'm biased) - conven = 'full' - # Generates string for output file creation - if conven == 'DM': - strg = '{2:02d}{1:02d}_{3:02d}{4:02d}'.format(*t) - elif conven == 'MD': - strg = '{1:02d}{2:02d}_{3:02d}{4:02d}'.format(*t) - elif conven == 'full': - strg = '{0:04d}{1:02d}{2:02d}{3:02d}{4:02d}'.format(*t) - else: - print("Set convention for date format: DM or MD.") - elif usage == 'dir': - # Generates string for directory names - # Needs strlen which sets smallest scope of time for string - if not strlen: - print("No timescope strlen set; using hour as minimum.") - strlen = 'hour' - n = lookup_time(strlen) - strg = "{0:04d}".format(t[0]) + ''.join( - ["{0:02d}".format(a) for a in t[1:n+1]]) - elif usage == 'skip': - strg = t - else: - print("Usage for string not valid.") - raise Exception - return strg - -def lookup_time(str): - D = {'year':0, 'month':1, 'day':2, 'hour':3, 'minute':4, 'second':5} - return D[str] - -def get_level_naming(va,**kwargs): - lv = kwargs['lv'] - if lv < 1500: - return str(lv)+'hPa' - elif lv == 2000: - return 'sfc' - elif lv.endswith('K'): - return lv - elif lv.endswith('PVU'): - return lv - elif lv.endswith('km'): - return lv - elif lv == 'all': - if va == 'shear': - name = '{0}to{1}'.format(kwargs['bottom'],kwargs['top']) - return name - else: - return 'all_lev' - - -def level_type(lv): - """ Check to see what type of level is requested by user. - - """ - if lv < 1500: - return 'isobaric' - elif lv == 2000: - return 'surface' - elif lv.endswith('K'): - return 'isentropic' - elif lv.endswith('PVU'): - return 'PV-surface' - elif lv.endswith('km'): - return 'geometric' - elif lv == 'all': - return 'eta' - else: - print('Unknown vertical coordinate.') - raise Exception - -def closest(arr2D,val): - """ - Inputs: - val : required value - arr2D : 2D array of values - - Output: - - idx : index of closest value - - """ - idx = N.argmin(N.abs(arr2D - val)) - return idx - -def dstack_loop(data, obj): - """ - Tries to stack numpy array (data) into 'stack' object (obj). - If obj doesn't exist, then initialise it - If obj does exist, stack data. - """ - if isinstance(obj,N.ndarray): - stack = N.dstack((obj,data)) - else: - stack = data - - return stack - -def dstack_loop_v2(data, obj): - """ - Need to set obj = 0 at start of loop in master script - - Tries to stack numpy array (data) into 'stack' object (obj). - If obj doesn't exist, then initialise it - If obj does exist, stack data. - """ - try: - print obj - except NameError: - stack = data - else: - stack = N.dstack((obj,data)) - - return stack - -def vstack_loop(data, obj): - """ - Need to set obj = 0 at start of loop in master script - - Tries to stack numpy array (data) into 'stack' object (obj). - If obj doesn't exist, then initialise it - If obj does exist, stack data. - """ - - if isinstance(obj,N.ndarray): - stack = N.vstack((obj,data)) - else: - stack = data - - return stack - - -def generate_times(idate,fdate,interval): - """ - Interval in seconds - """ - i = calendar.timegm(idate) - f = calendar.timegm(fdate) - times = range(i,f,interval) - return times - -def generate_colours(M,n): - """ - M : Matplotlib instance - n : number of colours you want - - Returns - """ - - colourlist = [M.cm.spectral(i) for i in N.linspace(0.08,0.97,n)] - return colourlist diff --git a/utils/shapefile.py b/utils/shapefile.py deleted file mode 100644 index 11081cd..0000000 --- a/utils/shapefile.py +++ /dev/null @@ -1,1128 +0,0 @@ -""" -shapefile.py -Provides read and write support for ESRI Shapefiles. -author: jlawheadgeospatialpython.com -date: 20130622 -version: 1.1.7 -Compatible with Python versions 2.4-3.x -""" -__version__ = "1.1.7" - -from struct import pack, unpack, calcsize, error -import os -import sys -import time -import array -import tempfile -# -# Constants for shape types -NULL = 0 -POINT = 1 -POLYLINE = 3 -POLYGON = 5 -MULTIPOINT = 8 -POINTZ = 11 -POLYLINEZ = 13 -POLYGONZ = 15 -MULTIPOINTZ = 18 -POINTM = 21 -POLYLINEM = 23 -POLYGONM = 25 -MULTIPOINTM = 28 -MULTIPATCH = 31 - -PYTHON3 = sys.version_info[0] == 3 - -if PYTHON3: - xrange = range - -def b(v): - if PYTHON3: - if isinstance(v, str): - # For python 3 encode str to bytes. - return v.encode('utf-8') - elif isinstance(v, bytes): - # Already bytes. - return v - else: - # Error. - raise Exception('Unknown input type') - else: - # For python 2 assume str passed in and return str. - return v - -def u(v): - if PYTHON3: - if isinstance(v, bytes): - # For python 3 decode bytes to str. - return v.decode('utf-8') - elif isinstance(v, str): - # Already str. - return v - else: - # Error. - raise Exception('Unknown input type') - else: - # For python 2 assume str passed in and return str. - return v - -def is_string(v): - if PYTHON3: - return isinstance(v, str) - else: - return isinstance(v, basestring) - -class _Array(array.array): - """Converts python tuples to lits of the appropritate type. - Used to unpack different shapefile header parts.""" - def __repr__(self): - return str(self.tolist()) - -def signed_area(coords): - """Return the signed area enclosed by a ring using the linear time - algorithm at http://www.cgafaq.info/wiki/Polygon_Area. A value <= 0 - indicates a counter-clockwise oriented ring. - """ - xs, ys = map(list, zip(*coords)) - xs.append(xs[1]) - ys.append(ys[1]) - return sum(xs[i]*(ys[i+1]-ys[i-1]) for i in range(1, len(coords)))/2.0 - -class _Shape: - def __init__(self, shapeType=None): - """Stores the geometry of the different shape types - specified in the Shapefile spec. Shape types are - usually point, polyline, or polygons. Every shape type - except the "Null" type contains points at some level for - example verticies in a polygon. If a shape type has - multiple shapes containing points within a single - geometry record then those shapes are called parts. Parts - are designated by their starting index in geometry record's - list of shapes.""" - self.shapeType = shapeType - self.points = [] - - @property - def __geo_interface__(self): - if self.shapeType in [POINT, POINTM, POINTZ]: - return { - 'type': 'Point', - 'coordinates': tuple(self.points[0]) - } - elif self.shapeType in [MULTIPOINT, MULTIPOINTM, MULTIPOINTZ]: - return { - 'type': 'MultiPoint', - 'coordinates': tuple([tuple(p) for p in self.points]) - } - elif self.shapeType in [POLYLINE, POLYLINEM, POLYLINEZ]: - if len(self.parts) == 1: - return { - 'type': 'LineString', - 'coordinates': tuple([tuple(p) for p in self.points]) - } - else: - ps = None - coordinates = [] - for part in self.parts: - if ps == None: - ps = part - continue - else: - coordinates.append(tuple([tuple(p) for p in self.points[ps:part]])) - ps = part - else: - coordinates.append(tuple([tuple(p) for p in self.points[part:]])) - return { - 'type': 'MultiLineString', - 'coordinates': tuple(coordinates) - } - elif self.shapeType in [POLYGON, POLYGONM, POLYGONZ]: - if len(self.parts) == 1: - return { - 'type': 'Polygon', - 'coordinates': (tuple([tuple(p) for p in self.points]),) - } - else: - ps = None - coordinates = [] - for part in self.parts: - if ps == None: - ps = part - continue - else: - coordinates.append(tuple([tuple(p) for p in self.points[ps:part]])) - ps = part - else: - coordinates.append(tuple([tuple(p) for p in self.points[part:]])) - polys = [] - poly = [coordinates[0]] - for coord in coordinates[1:]: - if signed_area(coord) < 0: - polys.append(poly) - poly = [coord] - else: - poly.append(coord) - polys.append(poly) - if len(polys) == 1: - return { - 'type': 'Polygon', - 'coordinates': tuple(polys[0]) - } - elif len(polys) > 1: - return { - 'type': 'MultiPolygon', - 'coordinates': polys - } - -class _ShapeRecord: - """A shape object of any type.""" - def __init__(self, shape=None, record=None): - self.shape = shape - self.record = record - -class ShapefileException(Exception): - """An exception to handle shapefile specific problems.""" - pass - -class Reader: - """Reads the three files of a shapefile as a unit or - separately. If one of the three files (.shp, .shx, - .dbf) is missing no exception is thrown until you try - to call a method that depends on that particular file. - The .shx index file is used if available for efficiency - but is not required to read the geometry from the .shp - file. The "shapefile" argument in the constructor is the - name of the file you want to open. - - You can instantiate a Reader without specifying a shapefile - and then specify one later with the load() method. - - Only the shapefile headers are read upon loading. Content - within each file is only accessed when required and as - efficiently as possible. Shapefiles are usually not large - but they can be. - """ - def __init__(self, *args, **kwargs): - self.shp = None - self.shx = None - self.dbf = None - self.shapeName = "Not specified" - self._offsets = [] - self.shpLength = None - self.numRecords = None - self.fields = [] - self.__dbfHdrLength = 0 - # See if a shapefile name was passed as an argument - if len(args) > 0: - if is_string(args[0]): - self.load(args[0]) - return - if "shp" in kwargs.keys(): - if hasattr(kwargs["shp"], "read"): - self.shp = kwargs["shp"] - if hasattr(self.shp, "seek"): - self.shp.seek(0) - if "shx" in kwargs.keys(): - if hasattr(kwargs["shx"], "read"): - self.shx = kwargs["shx"] - if hasattr(self.shx, "seek"): - self.shx.seek(0) - if "dbf" in kwargs.keys(): - if hasattr(kwargs["dbf"], "read"): - self.dbf = kwargs["dbf"] - if hasattr(self.dbf, "seek"): - self.dbf.seek(0) - if self.shp or self.dbf: - self.load() - else: - raise ShapefileException("Shapefile Reader requires a shapefile or file-like object.") - - def load(self, shapefile=None): - """Opens a shapefile from a filename or file-like - object. Normally this method would be called by the - constructor with the file object or file name as an - argument.""" - if shapefile: - (shapeName, ext) = os.path.splitext(shapefile) - self.shapeName = shapeName - try: - self.shp = open("%s.shp" % shapeName, "rb") - except IOError: - raise ShapefileException("Unable to open %s.shp" % shapeName) - try: - self.shx = open("%s.shx" % shapeName, "rb") - except IOError: - raise ShapefileException("Unable to open %s.shx" % shapeName) - try: - self.dbf = open("%s.dbf" % shapeName, "rb") - except IOError: - raise ShapefileException("Unable to open %s.dbf" % shapeName) - if self.shp: - self.__shpHeader() - if self.dbf: - self.__dbfHeader() - - def __getFileObj(self, f): - """Checks to see if the requested shapefile file object is - available. If not a ShapefileException is raised.""" - if not f: - raise ShapefileException("Shapefile Reader requires a shapefile or file-like object.") - if self.shp and self.shpLength is None: - self.load() - if self.dbf and len(self.fields) == 0: - self.load() - return f - - def __restrictIndex(self, i): - """Provides list-like handling of a record index with a clearer - error message if the index is out of bounds.""" - if self.numRecords: - rmax = self.numRecords - 1 - if abs(i) > rmax: - raise IndexError("Shape or Record index out of range.") - if i < 0: i = range(self.numRecords)[i] - return i - - def __shpHeader(self): - """Reads the header information from a .shp or .shx file.""" - if not self.shp: - raise ShapefileException("Shapefile Reader requires a shapefile or file-like object. (no shp file found") - shp = self.shp - # File length (16-bit word * 2 = bytes) - shp.seek(24) - self.shpLength = unpack(">i", shp.read(4))[0] * 2 - # Shape type - shp.seek(32) - self.shapeType= unpack("2i", f.read(8)) - # Determine the start of the next record - next = f.tell() + (2 * recLength) - shapeType = unpack(" -10e38: - record.m.append(m) - else: - record.m.append(None) - # Read a single point - if shapeType in (1,11,21): - record.points = [_Array('d', unpack("<2d", f.read(16)))] - # Read a single Z value - if shapeType == 11: - record.z = unpack("i", shx.read(4))[0] * 2) - 100 - numRecords = shxRecordLength // 8 - # Jump to the first record. - shx.seek(100) - for r in range(numRecords): - # Offsets are 16-bit words just like the file length - self._offsets.append(unpack(">i", shx.read(4))[0] * 2) - shx.seek(shx.tell() + 4) - if not i == None: - return self._offsets[i] - - def shape(self, i=0): - """Returns a shape object for a shape in the the geometry - record file.""" - shp = self.__getFileObj(self.shp) - i = self.__restrictIndex(i) - offset = self.__shapeIndex(i) - if not offset: - # Shx index not available so iterate the full list. - for j,k in enumerate(self.iterShapes()): - if j == i: - return k - shp.seek(offset) - return self.__shape() - - def shapes(self): - """Returns all shapes in a shapefile.""" - shp = self.__getFileObj(self.shp) - shp.seek(100) - shapes = [] - while shp.tell() < self.shpLength: - shapes.append(self.__shape()) - return shapes - - def iterShapes(self): - """Serves up shapes in a shapefile as an iterator. Useful - for handling large shapefiles.""" - shp = self.__getFileObj(self.shp) - shp.seek(100) - while shp.tell() < self.shpLength: - yield self.__shape() - - def __dbfHeaderLength(self): - """Retrieves the header length of a dbf file header.""" - if not self.__dbfHdrLength: - if not self.dbf: - raise ShapefileException("Shapefile Reader requires a shapefile or file-like object. (no dbf file found)") - dbf = self.dbf - (self.numRecords, self.__dbfHdrLength) = \ - unpack("6i", 9994,0,0,0,0,0)) - # File length (Bytes / 2 = 16-bit words) - if headerType == 'shp': - f.write(pack(">i", self.__shpFileLength())) - elif headerType == 'shx': - f.write(pack('>i', ((100 + (len(self._shapes) * 8)) // 2))) - # Version, Shape type - f.write(pack("<2i", 1000, self.shapeType)) - # The shapefile's bounding box (lower left, upper right) - if self.shapeType != 0: - try: - f.write(pack("<4d", *self.bbox())) - except error: - raise ShapefileException("Failed to write shapefile bounding box. Floats required.") - else: - f.write(pack("<4d", 0,0,0,0)) - # Elevation - z = self.zbox() - # Measure - m = self.mbox() - try: - f.write(pack("<4d", z[0], z[1], m[0], m[1])) - except error: - raise ShapefileException("Failed to write shapefile elevation and measure values. Floats required.") - - def __dbfHeader(self): - """Writes the dbf header and field descriptors.""" - f = self.__getFileObj(self.dbf) - f.seek(0) - version = 3 - year, month, day = time.localtime()[:3] - year -= 1900 - # Remove deletion flag placeholder from fields - for field in self.fields: - if field[0].startswith("Deletion"): - self.fields.remove(field) - numRecs = len(self.records) - numFields = len(self.fields) - headerLength = numFields * 32 + 33 - recordLength = sum([int(field[2]) for field in self.fields]) + 1 - header = pack('2i", recNum, 0)) - recNum += 1 - start = f.tell() - # Shape Type - f.write(pack("i", length)) - f.seek(finish) - - def __shxRecords(self): - """Writes the shx records.""" - f = self.__getFileObj(self.shx) - f.seek(100) - for i in range(len(self._shapes)): - f.write(pack(">i", self._offsets[i] // 2)) - f.write(pack(">i", self._lengths[i])) - - def __dbfRecords(self): - """Writes the dbf records.""" - f = self.__getFileObj(self.dbf) - for record in self.records: - if not self.fields[0][0].startswith("Deletion"): - f.write(b(' ')) # deletion flag - for (fieldName, fieldType, size, dec), value in zip(self.fields, record): - fieldType = fieldType.upper() - size = int(size) - if fieldType.upper() == "N": - value = str(value).rjust(size) - elif fieldType == 'L': - value = str(value)[0].upper() - else: - value = str(value)[:size].ljust(size) - assert len(value) == size - value = b(value) - f.write(value) - - def null(self): - """Creates a null shape.""" - self._shapes.append(_Shape(NULL)) - - def point(self, x, y, z=0, m=0): - """Creates a point shape.""" - pointShape = _Shape(self.shapeType) - pointShape.points.append([x, y, z, m]) - self._shapes.append(pointShape) - - def line(self, parts=[], shapeType=POLYLINE): - """Creates a line shape. This method is just a convienience method - which wraps 'poly()'. - """ - self.poly(parts, shapeType, []) - - def poly(self, parts=[], shapeType=POLYGON, partTypes=[]): - """Creates a shape that has multiple collections of points (parts) - including lines, polygons, and even multipoint shapes. If no shape type - is specified it defaults to 'polygon'. If no part types are specified - (which they normally won't be) then all parts default to the shape type. - """ - polyShape = _Shape(shapeType) - polyShape.parts = [] - polyShape.points = [] - for part in parts: - # Make sure polygon is closed - if shapeType in (5,15,25,31) and part[0] != part[-1]: - part.append(part[0]) - for point in part: - # Ensure point is list - if not isinstance(point, list): - point = list(point) - # Make sure point has z and m values - while len(point) < 4: - point.append(0) - polyShape.points.append(point) - polyShape.parts.append(len(polyShape.points)) - if polyShape.shapeType == 31: - if not partTypes: - for part in parts: - partTypes.append(polyShape.shapeType) - polyShape.partTypes = partTypes - self._shapes.append(polyShape) - - def field(self, name, fieldType="C", size="50", decimal=0): - """Adds a dbf field descriptor to the shapefile.""" - self.fields.append((name, fieldType, size, decimal)) - - def record(self, *recordList, **recordDict): - """Creates a dbf attribute record. You can submit either a sequence of - field values or keyword arguments of field names and values. Before - adding records you must add fields for the record values using the - fields() method. If the record values exceed the number of fields the - extra ones won't be added. In the case of using keyword arguments to specify - field/value pairs only fields matching the already registered fields - will be added.""" - record = [] - fieldCount = len(self.fields) - # Compensate for deletion flag - if self.fields[0][0].startswith("Deletion"): fieldCount -= 1 - if recordList: - [record.append(recordList[i]) for i in range(fieldCount)] - elif recordDict: - for field in self.fields: - if field[0] in recordDict: - val = recordDict[field[0]] - if val is None: - record.append("") - else: - record.append(val) - if record: - self.records.append(record) - - def shape(self, i): - return self._shapes[i] - - def shapes(self): - """Return the current list of shapes.""" - return self._shapes - - def saveShp(self, target): - """Save an shp file.""" - if not hasattr(target, "write"): - target = os.path.splitext(target)[0] + '.shp' - if not self.shapeType: - self.shapeType = self._shapes[0].shapeType - self.shp = self.__getFileObj(target) - self.__shapefileHeader(self.shp, headerType='shp') - self.__shpRecords() - - def saveShx(self, target): - """Save an shx file.""" - if not hasattr(target, "write"): - target = os.path.splitext(target)[0] + '.shx' - if not self.shapeType: - self.shapeType = self._shapes[0].shapeType - self.shx = self.__getFileObj(target) - self.__shapefileHeader(self.shx, headerType='shx') - self.__shxRecords() - - def saveDbf(self, target): - """Save a dbf file.""" - if not hasattr(target, "write"): - target = os.path.splitext(target)[0] + '.dbf' - self.dbf = self.__getFileObj(target) - self.__dbfHeader() - self.__dbfRecords() - - def save(self, target=None, shp=None, shx=None, dbf=None): - """Save the shapefile data to three files or - three file-like objects. SHP and DBF files can also - be written exclusively using saveShp, saveShx, and saveDbf respectively. - If target is specified but not shp,shx, or dbf then the target path and - file name are used. If no options or specified, a unique base file name - is generated to save the files and the base file name is returned as a - string. - """ - # Create a unique file name if one is not defined - if shp: - self.saveShp(shp) - if shx: - self.saveShx(shx) - if dbf: - self.saveDbf(dbf) - elif not shp and not shx and not dbf: - generated = False - if not target: - temp = tempfile.NamedTemporaryFile(prefix="shapefile_",dir=os.getcwd()) - target = temp.name - generated = True - self.saveShp(target) - self.shp.close() - self.saveShx(target) - self.shx.close() - self.saveDbf(target) - self.dbf.close() - if generated: - return target - -class Editor(Writer): - def __init__(self, shapefile=None, shapeType=POINT, autoBalance=1): - self.autoBalance = autoBalance - if not shapefile: - Writer.__init__(self, shapeType) - elif is_string(shapefile): - base = os.path.splitext(shapefile)[0] - if os.path.isfile("%s.shp" % base): - r = Reader(base) - Writer.__init__(self, r.shapeType) - self._shapes = r.shapes() - self.fields = r.fields - self.records = r.records() - - def select(self, expr): - """Select one or more shapes (to be implemented)""" - # TODO: Implement expressions to select shapes. - pass - - def delete(self, shape=None, part=None, point=None): - """Deletes the specified part of any shape by specifying a shape - number, part number, or point number.""" - # shape, part, point - if shape and part and point: - del self._shapes[shape][part][point] - # shape, part - elif shape and part and not point: - del self._shapes[shape][part] - # shape - elif shape and not part and not point: - del self._shapes[shape] - # point - elif not shape and not part and point: - for s in self._shapes: - if s.shapeType == 1: - del self._shapes[point] - else: - for part in s.parts: - del s[part][point] - # part, point - elif not shape and part and point: - for s in self._shapes: - del s[part][point] - # part - elif not shape and part and not point: - for s in self._shapes: - del s[part] - - def point(self, x=None, y=None, z=None, m=None, shape=None, part=None, point=None, addr=None): - """Creates/updates a point shape. The arguments allows - you to update a specific point by shape, part, point of any - shape type.""" - # shape, part, point - if shape and part and point: - try: self._shapes[shape] - except IndexError: self._shapes.append([]) - try: self._shapes[shape][part] - except IndexError: self._shapes[shape].append([]) - try: self._shapes[shape][part][point] - except IndexError: self._shapes[shape][part].append([]) - p = self._shapes[shape][part][point] - if x: p[0] = x - if y: p[1] = y - if z: p[2] = z - if m: p[3] = m - self._shapes[shape][part][point] = p - # shape, part - elif shape and part and not point: - try: self._shapes[shape] - except IndexError: self._shapes.append([]) - try: self._shapes[shape][part] - except IndexError: self._shapes[shape].append([]) - points = self._shapes[shape][part] - for i in range(len(points)): - p = points[i] - if x: p[0] = x - if y: p[1] = y - if z: p[2] = z - if m: p[3] = m - self._shapes[shape][part][i] = p - # shape - elif shape and not part and not point: - try: self._shapes[shape] - except IndexError: self._shapes.append([]) - - # point - # part - if addr: - shape, part, point = addr - self._shapes[shape][part][point] = [x, y, z, m] - else: - Writer.point(self, x, y, z, m) - if self.autoBalance: - self.balance() - - def validate(self): - """An optional method to try and validate the shapefile - as much as possible before writing it (not implemented).""" - #TODO: Implement validation method - pass - - def balance(self): - """Adds a corresponding empty attribute or null geometry record depending - on which type of record was created to make sure all three files - are in synch.""" - if len(self.records) > len(self._shapes): - self.null() - elif len(self.records) < len(self._shapes): - self.record() - - def __fieldNorm(self, fieldName): - """Normalizes a dbf field name to fit within the spec and the - expectations of certain ESRI software.""" - if len(fieldName) > 11: fieldName = fieldName[:11] - fieldName = fieldName.upper() - fieldName.replace(' ', '_') - -# Begin Testing -def test(): - import doctest - doctest.NORMALIZE_WHITESPACE = 1 - doctest.testfile("README.txt", verbose=1) - -if __name__ == "__main__": - """ - Doctests are contained in the file 'README.txt'. This library was originally developed - using Python 2.3. Python 2.4 and above have some excellent improvements in the built-in - testing libraries but for now unit testing is done using what's available in - 2.3. - """ - test() diff --git a/utils/topodata.py b/utils/topodata.py deleted file mode 100644 index 42a10c3..0000000 --- a/utils/topodata.py +++ /dev/null @@ -1,66 +0,0 @@ -import numpy as N -import matplotlib as M -M.use('Agg') -import matplotlib.pyplot as plt -import pdb -import math -import sys - -sys.path.append('/uufs/chpc.utah.edu/common/home/u0737349/lawsonpython/') -import gridded_data - -# Constants -rE = 6378100 # radius of Earth in metres - -# Settings -plt.rc('text',usetex=True) -fonts = {'family':'Computer Modern','size':16} -plt.rc('font',**fonts) -height, width = (9,17) - -outdir = '/uufs/chpc.utah.edu/common/home/u0737349/public_html/thesis/topoxs/' - -# Functions -# First, if topography data is not in memory, load it -try: - dataloaded -except NameError: - topodata, lats, lons = gridded_data.gettopo() - dataloaded = 1 - -def get_map(Nlim,Elim,Slim,Wlim): - ymax,xmax = gridded_data.getXY(lats,lons,Nlim,Elim) #Here, x is lat, y is lon - ymin,xmin = gridded_data.getXY(lats,lons,Slim,Wlim) # Not sure why! - terrain = topodata[xmin:xmax,ymin:ymax] - xlat = lats[xmin:xmax] - xlon = lons[ymin:ymax] - #pdb.set_trace() - return terrain,xlat,xlon - -def get_cross_section(Alat, Alon, Blat, Blon): - # Now find cross-sections - Ax, Ay = gridded_data.getXY(lats,lons,Alat,Alon) - Bx, By = gridded_data.getXY(lats,lons,Blat,Blon) - - # Number of points along cross-section - xspt = int(N.hypot(Bx-Ax,By-Ay)) - xx = N.linspace(Ay,By,xspt).astype(int) - yy = N.linspace(Ax,Bx,xspt).astype(int) - - # Get terrain heights along this transect - heights = topodata[xx,yy] - - # Work out distance along this cross-section in m - xsdistance = gridded_data.xs_distance(Alat,Alon,Blat,Blon) - - xvals = N.linspace(0,xsdistance,xspt) - xlabels = ['%3.1f' %(x/1000) for x in xvals] - # Now plot cross-sections - fig = plt.figure(figsize=(width,height)) - plt.plot(xvals,heights) - delta = xspt/10 - plt.xticks(xvals[::delta],xlabels[::delta]) - plt.xlabel('Distance along cross-section (km)') - fname = 'test1.png' - plt.savefig(outdir+fname,bbox_inches='tight',pad_inches=0.3) - plt.clf() diff --git a/utils/utils.py b/utils/utils.py deleted file mode 100644 index 4cfaa72..0000000 --- a/utils/utils.py +++ /dev/null @@ -1,213 +0,0 @@ -import numpy as N -import os -import time -import calendar -import pdb - -""" A collection of useful utilities. -""" - -def trycreate(loc): - try: - os.stat(loc) - except: - os.makedirs(loc) - -def padded_times(timeseq): - padded = ['{0:04d}'.format(t) for t in timeseq] - return padded - -def string_from_time(usage,t,dom=0,strlen=0,conven=0,**kwargs): - """ - conven : convection of MM/DD versus DD/MM - """ - - - if isinstance(t,str): - if usage == 'output': - usage = 'skip' # Time is already a string - elif usage == 'title': - pass - # if kwargs['itime']: # For averages or maxima over time - # itime = kwargs['itime'] - # ftime = kwargs['ftime'] - # else: - # pass - else: - raise Exception - elif isinstance(t,int): - # In this case, time is in datenum. Get it into tuple format. - t = time.gmtime(t) - else: - pass - - if usage == 'title': - # Generates string for titles - if not 'itime' in kwargs: # i.e. for specific times - #if not hasattr(kwargs,'itime'): # i.e. for specific times - strg = '{3:02d}:{4:02d}Z on {2:02d}/{1:02d}/{0:04d}'.format(*t) - else: # i.e. for ranges (average over time) - s1 = '{3:02d}:{4:02d}Z to '.format(*kwargs['itime']) - s2 = '{3:02d}:{4:02d}Z'.format(*kwargs['ftime']) - strg = s1 + s2 - elif usage == 'wrfout': - # Generates string for wrfout file finding - # Needs dom - if not dom: - print("No domain specified; using domain #1.") - dom = 1 - strg = ('wrfout_d0' + str(dom) + - '{0:04d}-{1:02d}-{2:02d}_{3:02d}:{4:02d}:{5:02d}'.format(*t)) - elif usage == 'output': - if not conven: - # No convention set, assume DD/MM (I'm biased) - conven = 'full' - # Generates string for output file creation - if conven == 'DM': - strg = '{2:02d}{1:02d}_{3:02d}{4:02d}'.format(*t) - elif conven == 'MD': - strg = '{1:02d}{2:02d}_{3:02d}{4:02d}'.format(*t) - elif conven == 'full': - strg = '{0:04d}{1:02d}{2:02d}{3:02d}{4:02d}'.format(*t) - else: - print("Set convention for date format: DM or MD.") - elif usage == 'dir': - # Generates string for directory names - # Needs strlen which sets smallest scope of time for string - if not strlen: - print("No timescope strlen set; using hour as minimum.") - strlen = 'hour' - n = lookup_time(strlen) - strg = "{0:04d}".format(t[0]) + ''.join( - ["{0:02d}".format(a) for a in t[1:n+1]]) - elif usage == 'skip': - strg = t - else: - print("Usage for string not valid.") - raise Exception - return strg - -def lookup_time(str): - D = {'year':0, 'month':1, 'day':2, 'hour':3, 'minute':4, 'second':5} - return D[str] - -def get_level_naming(va,**kwargs): - lv = kwargs['lv'] - if lv < 1500: - return str(lv)+'hPa' - elif lv == 2000: - return 'sfc' - elif lv.endswith('K'): - return lv - elif lv.endswith('PVU'): - return lv - elif lv.endswith('km'): - return lv - elif lv == 'all': - if va == 'shear': - name = '{0}to{1}'.format(kwargs['bottom'],kwargs['top']) - return name - else: - return 'all_lev' - - -def level_type(lv): - """ Check to see what type of level is requested by user. - - """ - if lv < 1500: - return 'isobaric' - elif lv == 2000: - return 'surface' - elif lv.endswith('K'): - return 'isentropic' - elif lv.endswith('PVU'): - return 'PV-surface' - elif lv.endswith('km'): - return 'geometric' - elif lv == 'all': - return 'eta' - else: - print('Unknown vertical coordinate.') - raise Exception - -def closest(arr2D,val): - """ - Inputs: - val : required value - arr2D : 2D array of values - - Output: - - idx : index of closest value - - """ - idx = N.argmin(N.abs(arr2D - val)) - return idx - -def dstack_loop(data, obj): - """ - Tries to stack numpy array (data) into 'stack' object (obj). - If obj doesn't exist, then initialise it - If obj does exist, stack data. - """ - if isinstance(obj,N.ndarray): - stack = N.dstack((obj,data)) - else: - stack = data - - return stack - -def dstack_loop_v2(data, obj): - """ - Need to set obj = 0 at start of loop in master script - - Tries to stack numpy array (data) into 'stack' object (obj). - If obj doesn't exist, then initialise it - If obj does exist, stack data. - """ - try: - print obj - except NameError: - stack = data - else: - stack = N.dstack((obj,data)) - - return stack - -def vstack_loop(data, obj): - """ - Need to set obj = 0 at start of loop in master script - - Tries to stack numpy array (data) into 'stack' object (obj). - If obj doesn't exist, then initialise it - If obj does exist, stack data. - """ - - if isinstance(obj,N.ndarray): - stack = N.vstack((obj,data)) - else: - stack = data - - return stack - - -def generate_times(idate,fdate,interval): - """ - Interval in seconds - """ - i = calendar.timegm(idate) - f = calendar.timegm(fdate) - times = range(i,f,interval) - return times - -def generate_colours(M,n): - """ - M : Matplotlib instance - n : number of colours you want - - Returns - """ - - colourlist = [M.cm.spectral(i) for i in N.linspace(0.08,0.97,n)] - return colourlist diff --git a/utils/wrf_tools.py b/utils/wrf_tools.py deleted file mode 100644 index d0edd3b..0000000 --- a/utils/wrf_tools.py +++ /dev/null @@ -1,242 +0,0 @@ -""" -Utility scripts related to WRF files. -""" - -# Some functions to import WRF data etc -from netCDF4 import Dataset -import pdb -from mpl_toolkits.basemap import Basemap -import matplotlib.pyplot as plt -import numpy as N -#import scipy.ndimage as nd -import os -import calendar - -# datestring needs to be format yyyymmddhh -def wrf_nc_load(dom,var,ncfolder,datestr,thin,Nlim=0,Elim=0,Slim=0,Wlim=0): - datestr2 = datestr[0:4]+'-'+datestr[4:6]+'-'+datestr[6:8]+'_'+datestr[8:10]+':00:00' - fname = ncfolder+'wrfout_'+dom+'_'+datestr2 - try: - nc = Dataset(fname,'r') - except RuntimeError: - fname += '.nc' - finally: - nc = Dataset(fname,'r') - # Load in variables here - customise or automate perhaps? - u10 = nc.variables['U10'] - v10 = nc.variables['V10'] - terrain = nc.variables['HGT'][0,:,:] - times = nc.variables['Times'] - - ### PROCESSING - # x_dim and y_dim are the x and y dimensions of the model - # domain in gridpoints - x_dim = len(nc.dimensions['west_east']) - y_dim = len(nc.dimensions['south_north']) - - # Get the grid spacing - dx = float(nc.DX) - dy = float(nc.DY) - - width_meters = dx * (x_dim - 1) - height_meters = dy * (y_dim - 1) - - cen_lat = float(nc.CEN_LAT) - cen_lon = float(nc.CEN_LON) - truelat1 = float(nc.TRUELAT1) - truelat2 = float(nc.TRUELAT2) - standlon = float(nc.STAND_LON) - - # Draw the base map behind it with the lats and - # lons calculated earlier - - if Nlim: - m = Basemap(projection='lcc',lon_0=cen_lon,lat_0=cen_lat, - llcrnrlat=Slim,urcrnrlat=Nlim,llcrnrlon=Wlim, - urcrnrlon=Elim,rsphere=6371200.,resolution='h', - area_thresh=100) - else: - m = Basemap(resolution='i',projection='lcc', - width=width_meters,height=height_meters, - lat_0=cen_lat,lon_0=cen_lon,lat_1=truelat1, - lat_2=truelat2) - xlong = nc.variables['XLONG'][0] - xlat = nc.variables['XLAT'][0] - #xlong = xlongtot[N.where((xlongtot < Elim) & (xlongtot > Wlim))] - #xlat = xlattot[N.where((xlattot < Nlim) & (xlattot > Slim))] - #x,y = m(nc.variables['XLONG'][0],nc.variables['XLAT'][0]) - #px,py = m(xlongtot,xlattot) - #x,y = N.meshgrid(px,py) - x,y = m(xlong,xlat) - # This sets a thinned out grid point structure for plotting - # wind barbs at the interval specified in "thin" - #x_th,y_th = m(nc.variables['XLONG'][0,::thin,::thin],\ - # nc.variables['XLAT'][0,::thin,::thin]) - x_th, y_th = m(xlong[::thin, ::thin],xlat[::thin,::thin]) - data = (times,terrain,u10,v10) - return m, x, y, x_th, y_th, data, nc - -def mkloop(dom='d03',fpath='./'): - print "Creating a pretty loop..." - os.system('convert -delay 50 '+fpath+dom+'*.png > -loop '+fpath+dom+'_windloop.gif') - print "Finished!" - -def p_interpol(dom,var,ncfolder,datestr): - datestr2 = datestr[0:4]+'-'+datestr[4:6]+'-'+datestr[6:8]+'_'+datestr[8:10]+':00:00' - fname = ncfolder+'wrfout_'+dom+'_'+datestr2 - nc = Dataset(fname,'r') - var_data = nc.variables[var][:] - if var == 'T': # pert. theta - var_data += 300 # Add base state - pert_pressure = nc.variables['P'][:] #This is perturbation pressure - base_pressure = nc.variables['PB'][:] # This is base pressure - pressure = pert_pressure + base_pressure # Get into absolute pressure in Pa. - p_levels = N.arange(10000,100000,1000) - var_interp = N.zeros((pressure.shape[0],len(p_levels),pressure.shape[2],pressure.shape[3])) - #pdb.set_trace() - for (t,y,x),v in N.ndenumerate(var_interp[:,0,:,:]): - var_interp[t,:,y,x] = N.interp(p_levels,pressure[t,::-1,y,x],var_data[t,::-1,y,x]) - return var_interpi - -def hgt_from_sigma(nc): - vert_coord = (nc.variables['PH'][:] + nc.variables['PHB'][:]) / 9.81 - return vert_coord - -# This converts wrf's time to python and human time -def find_time_index(wrftime,reqtimetuple,tupleformat=1): - # wrftime = WRF Times in array - # reqtime = desired time in six-tuple - - # Convert required time to Python time if required - if tupleformat: - reqtime = calendar.timegm(reqtimetuple) - else: - reqtime = reqtimetuple - nt = wrftime.shape[0] - pytime = N.zeros([nt,1]) - t = wrftime - # Now convert WRF time to Python time - for i in range(nt): - yr = int(''.join(t[i,0:4])) - mth = int(''.join(t[i,5:7])) - day = int(''.join(t[i,8:10])) - hr = int(''.join(t[i,11:13])) - min = int(''.join(t[i,14:16])) - sec = int(''.join(t[i,17:19])) - pytime[i] = calendar.timegm([yr,mth,day,hr,min,sec]) - - # Now find closest WRF time - timeInd = N.where(abs(pytime-reqtime) == abs(pytime-reqtime).min())[0][0] - return timeInd - -# Find data for certain time, locations, level, variables -# For surface data -def TimeSfcLatLon(nc,varlist,times,latslons='all'): - # Time (DateTime in string) - if times == 'all': - timeInds = range(nc.variables['Times'].shape[0]) - elif len(times)==1: # If only one time is desired - # Time is in 6-tuple format - timeInds = find_time_index(nc.variables['Times'],times) # This function is from this module - elif len(times)==2: # Find all times between A and B - timeIndA = find_time_index(nc.variables['Times'],times[0]) - timeIndB = find_time_index(nc.variables['Times'],times[1]) - timeInds = range(timeIndA,timeIndB) - # Lat/lon of interest and their grid pointd - lats = nc.variables['XLAT'][:] - lons = nc.variables['XLONG'][:] - if latslons == 'all': - latInds = range(lats.shape[-2]) - lonInds = range(lons.shape[-1]) - else: - xmin,ymax = gridded_data.getXY(lats,lons,Nlim,Wlim) - xmax,ymin = gridded_data.getXY(lats,lons,Slim,Elim) - latInds = range(ymin,ymax) - lonInds = range(xmin,xmax) - - # Return sliced data - data = {} - for v in varlist: - data[v] = nc.variables[v][timeInds,latInds,lonInds] - # Reshape if only one time - if len(times)==1: - data[v] = N.reshape(data[v],(len(latInds),len(lonInds))) - return data - -# Get lat/lon as 1D arrays from WRF -def latlon_1D(nc): - Nx = nc.getncattr('WEST-EAST_GRID_DIMENSION')-1 - Ny = nc.getncattr('SOUTH-NORTH_GRID_DIMENSION')-1 - lats = nc.variables['XLAT'][0,:,Nx/2] - lons = nc.variables['XLONG'][0,Ny/2,:] - return lats, lons - -def wrfout_files_in(self,folders,dom='notset',init_time='notset',descend=1,**kwargs): - """ - Hunt through given folder(s) to find all occurrences of wrfout - files. - - Optional: - avoid : string. if folder name includes this - term, ignore the folder. - - Returns: - wrfouts : list of absolute paths to wrfout files - """ - - folders = self.get_sequence(folders) - avoids = [] - if 'avoid' in kwargs: - # Avoid folder names with this string - # or list of strings - avoid = self.get_sequence(kwargs['avoid']) - for a in avoid: - avoids.append('/{0}/'.format(a)) - - - w = 'wrfout' # Assume the prefix - if init_time=='notset': - suffix = '*0' - # We assume the user has wrfout files in different folders for different times - else: - try: - it = self.string_from_time('wrfout',init_time) - except: - print("Not a valid wrfout initialisation time; try again.") - raise Error - suffix = '*' + it - - if dom=='notset': - # Presume all domains are desired. - prefix = w + '_d' - elif (dom == 0) | (dom > 8): - print("Domain is out of range. Choose number between 1 and 8 inclusive.") - raise IndexError - else: - dom = 'd{0:02d}'.format(dom) - prefix = w + '_' + dom - - wrfouts = [] - if descend: - for folder in folders: - for root,dirs,files in os.walk(folder): - for fname in fnmatch.filter(files,prefix+suffix): - skip_me = 0 - fpath = os.path.join(root,fname) - if avoids: - for a in avoids: - if a in fpath: - skip_me = 1 - else: - pass - if not skip_me: - wrfouts.append(fpath) - - else: - for folder in folders: - findfile = os.path.join(folder,prefix+suffix) - files = glob.glob(findfile) - # pdb.set_trace() - for f in files: - wrfouts.append(os.path.join(folder,f)) - return wrfouts From 1adc932c2e9bd8340cf6c061ccb7e735bae059d0 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Sun, 24 Aug 2014 22:24:36 -0500 Subject: [PATCH 106/111] Added paramiko to utils --- utils/unix_tools.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/utils/unix_tools.py b/utils/unix_tools.py index 7bc08fe..9e522b9 100644 --- a/utils/unix_tools.py +++ b/utils/unix_tools.py @@ -4,8 +4,12 @@ import os import GIS_tools as utils +try: + import paramiko +except ImportError: + print("Module paramiko unavailable.") -def dir_from_naming(self,root,*args): +def dir_from_naming(root,*args): """ Generate file path from arguments @@ -17,3 +21,13 @@ def dir_from_naming(self,root,*args): path = os.path.join(root,*l) return path +def ssh_client(domain,username,password): + #key = paramiko.RSAKey(data=base64.decodestring('AAA...')) + client = paramiko.SSHClient() + #client.get_host_keys().add('ssh.example.com', 'ssh-rsa', key) + client.connect(domain, username=username, password=password) + return client + stdin, stdout, stderr = client.exec_command('ls') + for line in stdout: + print '... ' + line.strip('\n') + client.close() From c1cf0dcdf9b7a387b8996de8c807f7bd5b193505 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Fri, 29 Aug 2014 13:31:48 -0500 Subject: [PATCH 107/111] Spaghetti plots working --- postWRF/bin/bowecho_plot.py | 21 ++++++++++-- postWRF/postWRF/birdseye.py | 61 ++++++++++++++++++++++++++++++----- postWRF/postWRF/dke.py | 1 + postWRF/postWRF/figure.py | 1 + postWRF/postWRF/main.py | 64 +++++++++++++++++++++++++++++-------- postWRF/postWRF/scales.py | 8 +++++ postWRF/postWRF/wrfout.py | 7 ++++ utils/GIS_tools.py | 5 ++- 8 files changed, 143 insertions(+), 25 deletions(-) diff --git a/postWRF/bin/bowecho_plot.py b/postWRF/bin/bowecho_plot.py index 00dc324..f049b16 100644 --- a/postWRF/bin/bowecho_plot.py +++ b/postWRF/bin/bowecho_plot.py @@ -19,7 +19,8 @@ plot2D = 0 streamlines = 0 rucplot = 0 -coldpoolstrength = 1 +coldpoolstrength = 0 +spaghetti = 1 enstype = 'STCH' # enstype = 'ICBC' @@ -64,7 +65,7 @@ elif case[:4] == '2013': itime = (2013,8,15,21,0,0) ftime = (2013,8,16,9,0,0) - times = [(2013,8,15,18,0,0),] + times = [(2013,8,16,3,0,0),] else: raise Exception @@ -162,4 +163,18 @@ def get_folders(en,ex): out_sd, wrf_sd = get_folders(en,ex) # print out_sd, wrf_sd cf0, cf1 = p.cold_pool_strength(t,wrf_sd=wrf_sd,out_sd=out_sd,swath_width=130,fig=fig,axes=(ax0,ax1)) - plt.close(fig) \ No newline at end of file + plt.close(fig) + +if spaghetti: + wrf_sds = [] + for en in ensnames: + for ex in experiments: + out_sd, wrf_sd = get_folders(en,ex) + wrf_sds.append(wrf_sd) + + lv = 2000 + # Save to higher directory + out_d = os.path.dirname(out_sd) + for t in times: + p.spaghetti(t,lv,'cref',40,wrf_sds[:4],out_d) + diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index ba0edaf..3590145 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -8,6 +8,7 @@ import os from mpl_toolkits.axes_grid1 import make_axes_locatable +from wrfout import WRFOut from defaults import Defaults from figure import Figure import WEM.utils as utils @@ -240,14 +241,13 @@ def plot2D(self,vrbl,t,lv,dom,outpath,bounding=0,smooth=1, naming.append(dom) self.fname = self.create_fname(*naming) if save: - self.save(elf.fname) + self.save(outpath,self.fname) plt.close() if isinstance(self.data,N.ndarray): return self.data.reshape((self.la_n,self.lo_n)) - def plot_streamlines(self,lv,pt,da=0): - self.fig = plt.figure() + def plot_streamlines(self,lv,pt,outpath,da=0): m,x,y = self.basemap_setup() time_idx = self.W.get_time_idx(pt) @@ -284,13 +284,58 @@ def plot_streamlines(self,lv,pt,da=0): #plt.colorbar(divp,orientation='horizontal') if self.C.plot_titles: title = utils.string_from_time('title',pt) - plt.title(title) + m.title(title) datestr = utils.string_from_time('output',pt) na = ('streamlines',lv_na,datestr) - self.fname = self.create_fname(*na) - self.save(self.p2p,self.fname) - plt.clf() - plt.close() + fname = self.create_fname(*na) + self.save(outpath,fname) + + def spaghetti(self,t,lv,va,contour,wrfouts,outpath,da=0,dom=0): + """ + wrfouts : list of wrfout files + + Only change dom if there are multiple domains. + """ + m,x,y = self.basemap_setup() + + time_idx = self.W.get_time_idx(t) + + colours = utils.generate_colours(M,len(wrfouts)) + # import pdb; pdb.set_trace() + if lv==2000: + lv_idx = None + else: + print("Only support surface right now") + raise Exception + lat_sl, lon_sl = self.get_limited_domain(da) + slices = {'t': time_idx, 'lv': lv_idx, 'la': lat_sl, 'lo': lon_sl} + + # self.ax.set_color_cycle(colours) + ctlist = [] + for n,wrfout in enumerate(wrfouts): + self.W = WRFOut(wrfout) + data = self.W.get(va,slices)[0,...] + # m.contour(x,y,data,levels=[contour,]) + ct = m.contour(x,y,data,colors=[colours[n],],levels=[contour,],label=wrfout.split('/')[-2]) + print("Plotting contour level {0} for {1} from file \n {2}".format( + contour,va,wrfout)) + # ctlist.append(ct) + # self.ax.legend() + + # labels = [w.split('/')[-2] for w in wrfouts] + # print labels + # self.fig.legend(handles=ctlist) + # plt.legend(handles=ctlist,labels=labels) + #labels,ncol=3, loc=3, + # bbox_to_anchor=[0.5,1.5]) + + datestr = utils.string_from_time('output',t,tupleformat=0) + lv_na = utils.get_level_naming(va,lv) + naming = ['spaghetti',va,lv_na,datestr] + if dom: + naming.append(dom) + fname = self.create_fname(*naming) + self.save(outpath,fname) diff --git a/postWRF/postWRF/dke.py b/postWRF/postWRF/dke.py index d7c5473..087a1b6 100644 --- a/postWRF/postWRF/dke.py +++ b/postWRF/postWRF/dke.py @@ -1,5 +1,6 @@ # This script creates plots for pulsation data + ### IMPORTS import copy import pickle as P diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index acb2439..a0111dd 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -69,6 +69,7 @@ def save(self,p2p,fname): #self.fig.savefig(fpath) self.fig.savefig(fpath,bbox_inches='tight') print("Saving figure {0}".format(fpath)) + plt.close(self.fig) def get_limited_domain(self,da,smooth=1): if da: # Limited domain area diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index 614f220..50ae5f8 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -160,13 +160,14 @@ def plot2D(self,vrbl,times,levels,wrf_sd=0,wrf_nc=0,out_sd=0,f_prefix=0,f_suffix #rq[va]['vc'] = vc # Need this? # F.plot2D(va, t, lv, ) - def get_wrfout(self,wrf_sd=0,wrf_nc=0,dom=0): + def get_wrfout(self,wrf_sd=0,wrf_nc=0,dom=0,path_only=0): """Returns the WRFOut instance, given arguments: Optional inputs: wrf_sd : subdirectory for wrf file wrf_nc : filename for wrf file dom : domain for wrf file + path_only : only return absolute path """ # Check configuration to see if wrfout files should be # sought inside subdirectories. @@ -182,7 +183,10 @@ def get_wrfout(self,wrf_sd=0,wrf_nc=0,dom=0): wrfdir = os.path.join(self.C.wrfout_root) wrfpath = utils.wrfout_files_in(wrfdir,dom=dom,unambiguous=1,descend=descend) - return WRFOut(wrfpath) + if path_only: + return wrfpath + else: + return WRFOut(wrfpath) def generate_times(self,itime,ftime,x): """ @@ -689,7 +693,7 @@ def plot_error_growth(self,ofname,folder,pfname,sensitivity=0,ylimits=0,**kwargs plt.close() print("Saved {0}.".format(fpath)) - def composite_profile(self,va,skewT_time,skewT_latlon,enspaths,dom=1,mean=0,std=0,xlim=0,ylim=0): + def composite_profile(self,va,skewT_time,skewT_latlon,enspaths,dom=2,mean=0,std=0,xlim=0,ylim=0): P = Profile(self.C) P.composite_profile(va,skewT_time,skewT_latlon,enspaths,dom,mean,std,xlim,ylim) @@ -707,15 +711,15 @@ def plot_skewT(self,plot_time,plot_latlon,dom=1,save_output=0,composite=0): #ST = SkewT(self.C) pass - def plot_streamlines(self,lv,times): - wrfpath = self.wrfout_files_in(self.C.wrfout_root)[0] - self.W = WRFOut(wrfpath) + def plot_streamlines(self,lv,time,wrf_sd=0,wrf_nc=0,out_sd=0,dom=1): + self.W = self.get_wrfout(wrf_sd,wrf_nc,dom=dom) + outpath = self.get_outpath(out_sd) + self.F = BirdsEye(self.C,self.W) - for pt in times: - disp_t = utils.string_from_time('title',pt) - print("Plotting {0} at lv {1} for time {2}.".format( - 'streamlines',lv,disp_t)) - self.F.plot_streamlines(lv,pt) + disp_t = utils.string_from_time('title',time) + print("Plotting {0} at lv {1} for time {2}.".format( + 'streamlines',lv,disp_t)) + self.F.plot_streamlines(lv,time,outpath) def plot_strongest_wind(self,itime,ftime,levels,wrf_sd=0,wrf_nc=0,out_sd=0,f_prefix=0,f_suffix=0, bounding=0,dom=0): @@ -908,7 +912,7 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom= # Line from front to back of system C.draw_line() # C.draw_box() - lon0, lat0 = C.bmap(C.x0,C.y0,inverse=True) + # lon0, lat0 = C.bmap(C.x0,C.y0,inverse=True) lon1, lat1 = C.bmap(C.x1,C.y1,inverse=True) # Pick location for environmental dpt @@ -949,4 +953,38 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom= P2.save(outpath,fname+"_twopanel") if return_ax: - return C.cf, cf2 \ No newline at end of file + return C.cf, cf2 + + def spaghetti(self,t,lv,va,contour,wrf_sds,out_sd,dom=1): + """ + Do a multi-member spaghetti plot. + + t : time for plot + va : variable in question + contour : value to contour for each member + wrf_sds : list of wrf subdirs to loop over + out_sd : directory to save image + """ + # import pdb; pdb.set_trace() + outpath = self.get_outpath(out_sd) + + # Use first wrfout to initialise grid etc + self.W = self.get_wrfout(wrf_sds[0],dom=dom) + F = BirdsEye(self.C, self.W) + + ncfiles = [] + for wrf_sd in wrf_sds: + ncfile = self.get_wrfout(wrf_sd,dom=dom,path_only=1) + ncfiles.append(ncfile) + + F.spaghetti(t,lv,va,contour,ncfiles,outpath) + + + + + + + + + + diff --git a/postWRF/postWRF/scales.py b/postWRF/postWRF/scales.py index 13f974a..ebc2241 100644 --- a/postWRF/postWRF/scales.py +++ b/postWRF/postWRF/scales.py @@ -136,6 +136,14 @@ def get_dict_of_levels(self): A['PMSL'] = {'cmap':0,'multiplier':0.01} A['PMSL'][2000] = (97000,103100,100) + A['olr'] = {'cmap':ct.irsat} + A['olr'][2000] = [-100,-90,-85,-80,-75,-70,-65,-60,-55,-50, + -46,-42,-38,-36,-34,-32,-30,-28,-26,-24, + -22,-20,-18,-16,-14,-12,-10,-8,-6,-4,-2, + 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28, + 30,32,34,36,38,40,42,44,46,48,50,52] + + #A['cps'] = {'cmap':0} return A diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 718196d..7d38161 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -305,6 +305,7 @@ def return_tbl(self): tbl['RH'] = self.compute_RH tbl['dryairmass'] = self.compute_dryairmass tbl['QTOTAL'] = self.compute_qtotal + tbl['olr'] =self.compute_olr return tbl @@ -505,6 +506,12 @@ def compute_thetae(self,slices): thetae = (Drybulb + (Q * cc.Lv/cc.cp)) * (cc.P0/P) ** cc.kappa return thetae + def compute_olr(self,slices): + OLR = self.get('OLR',slices) + sbc = 0.000000056704 + ir = ((OLR/sbc)**0.25) - 273.15 + return ir + def compute_comp_ref(self,PS,**kwargs): """Amend this so variables obtain at start fetch only correct date, lats, lons All levels need to be fetched as this is composite reflectivity diff --git a/utils/GIS_tools.py b/utils/GIS_tools.py index 300e260..08a1817 100644 --- a/utils/GIS_tools.py +++ b/utils/GIS_tools.py @@ -760,6 +760,9 @@ def generate_colours(M,n): n : number of colours you want Returns + + Usage: when cycling over n plots, the colour should + be colourlist[n]. """ colourlist = [M.cm.spectral(i) for i in N.linspace(0.08,0.97,n)] @@ -827,4 +830,4 @@ def ensure_sequence_datenum(times): - \ No newline at end of file + From 84b81a5006acaf5b6bfe2bb6e5e8eccf0f423df9 Mon Sep 17 00:00:00 2001 From: John Lawson Date: Fri, 29 Aug 2014 16:09:37 -0500 Subject: [PATCH 108/111] Cold Pool Depth broken --- postWRF/bin/bowecho_plot.py | 6 +++--- postWRF/postWRF/main.py | 22 ++++++++++++++++------ postWRF/postWRF/wrfout.py | 30 +++++++++++++++++++++++++----- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/postWRF/bin/bowecho_plot.py b/postWRF/bin/bowecho_plot.py index f049b16..f68c1fc 100644 --- a/postWRF/bin/bowecho_plot.py +++ b/postWRF/bin/bowecho_plot.py @@ -19,8 +19,8 @@ plot2D = 0 streamlines = 0 rucplot = 0 -coldpoolstrength = 0 -spaghetti = 1 +coldpoolstrength = 1 +spaghetti = 0 enstype = 'STCH' # enstype = 'ICBC' @@ -162,7 +162,7 @@ def get_folders(en,ex): out_sd, wrf_sd = get_folders(en,ex) # print out_sd, wrf_sd - cf0, cf1 = p.cold_pool_strength(t,wrf_sd=wrf_sd,out_sd=out_sd,swath_width=130,fig=fig,axes=(ax0,ax1)) + cf0, cf1 = p.cold_pool_strength(t,wrf_sd=wrf_sd,out_sd=out_sd,swath_width=130,fig=fig,axes=(ax0,ax1),dz=1) plt.close(fig) if spaghetti: diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index 50ae5f8..5598a10 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -852,7 +852,7 @@ def plot_xs(self,vrbl,times,latA=0,lonA=0,latB=0,lonB=0, for t in t_list: XS.plot_xs(vrbl,t,outpath,clvs=clvs,ztop=ztop) - def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom=1,twoplot=0,fig=0,axes=0): + def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom=1,twoplot=0,fig=0,axes=0,dz=0): """ Pick A, B points on sim ref overlay This sets the angle between north and line AB @@ -873,6 +873,7 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom= return2 : return two figures. cold pool strength and cref/cross-section. axes : if two-length tuple, this is the first and second axes for cross-section/cref and cold pool strength, respectively + dz : plot height of cold pool only. """ # Initialise @@ -912,7 +913,7 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom= # Line from front to back of system C.draw_line() # C.draw_box() - # lon0, lat0 = C.bmap(C.x0,C.y0,inverse=True) + lon0, lat0 = C.bmap(C.x0,C.y0,inverse=True) lon1, lat1 = C.bmap(C.x1,C.y1,inverse=True) # Pick location for environmental dpt @@ -927,22 +928,31 @@ def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom= #C.set_box_width(X) # Compute the grid (DX x DY) - cps = self.W.cold_pool_strength(X,time,swath_width=swath_width,env=(x_env,y_env)) + cps = self.W.cold_pool_strength(X,time,swath_width=swath_width,env=(x_env,y_env),dz=dz) + # import pdb; pdb.set_trace() # Plot this array CPfig = BirdsEye(self.C,self.W,**cps_kwargs) tstr = utils.string_from_time('output',time) - fname = 'ColdPoolStrength_' + tstr + if dz: + fprefix = 'ColdPoolDepth_' + else: + fprefix = 'ColdPoolStrength_' + fname = fprefix + tstr - # pdb.set_trace() + pdb.set_trace() # imfig,imax = plt.subplots(1) # imax.imshow(cps) # plt.show(imfig) # CPfig.plot_data(cps,'contourf',outpath,fname,time,V=N.arange(5,105,5)) mplcommand = 'contour' plotkwargs = {} + if dz: + clvs = N.arange(100,5100,100) + else: + clvs = N.arange(10,85,2.5) if mplcommand[:7] == 'contour': - plotkwargs['levels'] = N.arange(10,85,2.5) + plotkwargs['levels'] = clvs plotkwargs['cmap'] = plt.cm.ocean_r cf2 = CPfig.plot_data(cps,mplcommand,outpath,fname,time,**plotkwargs) # CPfig.fig.tight_layout() diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 7d38161..a446bb7 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -835,7 +835,7 @@ def get_limits(self): Wlim = self.lons[0] return Nlim, Elim, Slim, Wlim - def cold_pool_strength(self,X,time,swath_width=100,env=0): + def cold_pool_strength(self,X,time,swath_width=100,env=0,dz=0): """ Returns array the same shape as WRF domain. @@ -907,10 +907,28 @@ def cold_pool_strength(self,X,time,swath_width=100,env=0): for x,y in zip(xx_cp,yy_cp): #for x,y in zip(xx,yy): - coldpooldata[y,x] = N.sqrt(self.compute_C2(x,y,dpt[:,y,x],heights[:,y,x],dpt_env)) + if dz: + coldpooldata[y,x] = self.compute_cpdz(x,y,dpt[:,y,x],heights[:,y,x],dpt_env) + # pdb.set_trace() + else: + coldpooldata[y,x] = N.sqrt(self.compute_C2(x,y,dpt[:,y,x],heights[:,y,x],dpt_env)) - # pdb.set_trace() return coldpooldata + + def compute_cpdz(self,x,y,dpt,heights,dpt_env): + """ + Cold pool depth + + x : x location in domain + y : y location in domain + dpt : density potential temperature slice + heights : height AGL slice + dpt_env : environmental dpt, column + """ + + dz, zidx = self.cold_pool_depth(dpt,heights,dpt_env) + # import pdb; pdb.set_trace() + return dz def compute_C2(self,x,y,dpt,heights,dpt_env): """ @@ -930,9 +948,11 @@ def compute_C2(self,x,y,dpt,heights,dpt_env): def cold_pool_depth(self,dpt,heights,dpt_env): dz = 0 + # thresh = -2.0 + thresh = -1.0 for d,z, de in zip(dpt[1:],heights[1:],dpt_env[1:]): dptp = d - de - if dptp > -1.0: + if dptp > thresh: break dz = z @@ -940,7 +960,7 @@ def cold_pool_depth(self,dpt,heights,dpt_env): zidx = N.where(heights==dz)[0] else: zidx = 0 - + # import pdb; pdb.set_trace() return dz, zidx def find_gust_front(self,wind_slice,T2_slice,angle,method=3): From 132eb311133ab590625a74add5ba8e20aebd323c Mon Sep 17 00:00:00 2001 From: John Lawson Date: Fri, 12 Sep 2014 14:36:18 -0500 Subject: [PATCH 109/111] Two-panel profile plots --- postWRF/bin/bowecho_plot.py | 43 ++++++++++-- postWRF/postWRF/birdseye.py | 5 +- postWRF/postWRF/clicker.py | 44 +++++++++---- postWRF/postWRF/figure.py | 23 +++++-- postWRF/postWRF/main.py | 127 +++++++++++++++++++++++++++++++++++- postWRF/postWRF/maps.py | 56 ++++++++++++++++ postWRF/postWRF/skewt.py | 73 ++++++++++++--------- postWRF/postWRF/stats.py | 23 +++++++ postWRF/postWRF/wrfout.py | 3 +- 9 files changed, 337 insertions(+), 60 deletions(-) create mode 100644 postWRF/postWRF/maps.py create mode 100644 postWRF/postWRF/stats.py diff --git a/postWRF/bin/bowecho_plot.py b/postWRF/bin/bowecho_plot.py index f68c1fc..eb2400a 100644 --- a/postWRF/bin/bowecho_plot.py +++ b/postWRF/bin/bowecho_plot.py @@ -19,8 +19,10 @@ plot2D = 0 streamlines = 0 rucplot = 0 -coldpoolstrength = 1 +coldpoolstrength = 0 spaghetti = 0 +std = 0 +profiles = 1 enstype = 'STCH' # enstype = 'ICBC' @@ -63,9 +65,9 @@ itime = (2011,4,19,18,0,0) ftime = (2011,4,20,10,30,0) elif case[:4] == '2013': - itime = (2013,8,15,21,0,0) - ftime = (2013,8,16,9,0,0) - times = [(2013,8,16,3,0,0),] + itime = (2013,8,15,12,0,0) + ftime = (2013,8,16,12,0,0) + # times = [(2013,8,16,3,0,0),] else: raise Exception @@ -81,7 +83,7 @@ def get_folders(en,ex): return out_sd, wrf_sd -# times = utils.generate_times(itime,ftime,3*60*60) +times = utils.generate_times(itime,ftime,6*60*60) #shear_times = utils.generate_times(itime,ftime,3*60*60) #sl_times = utils.generate_times(sl_itime,sl_ftime,1*60*60) @@ -162,7 +164,8 @@ def get_folders(en,ex): out_sd, wrf_sd = get_folders(en,ex) # print out_sd, wrf_sd - cf0, cf1 = p.cold_pool_strength(t,wrf_sd=wrf_sd,out_sd=out_sd,swath_width=130,fig=fig,axes=(ax0,ax1),dz=1) + cf0, cf1 = p.cold_pool_strength(t,wrf_sd=wrf_sd,out_sd=out_sd, + swath_width=130,fig=fig,axes=(ax0,ax1),dz=1) plt.close(fig) if spaghetti: @@ -178,3 +181,31 @@ def get_folders(en,ex): for t in times: p.spaghetti(t,lv,'cref',40,wrf_sds[:4],out_d) +if std: + wrf_sds = [] + for en in ensnames: + for ex in experiments: + out_sd, wrf_sd = get_folders(en,ex) + wrf_sds.append(wrf_sd) + + lv = 2000 + # Save to higher directory + out_d = os.path.dirname(out_sd) + for t in times: + p.std(t,lv,'RH',wrf_sds,out_d) + +if profiles: + wrf_sds = [] + for en in ensnames: + for ex in experiments: + out_sd, wrf_sd = get_folders(en,ex) + wrf_sds.append(wrf_sd) + + lv = 2000 + # Save to higher directory + out_d = os.path.dirname(out_sd) + for t in times: + p.twopanel_profile('RH',t,wrf_sds,out_d,two_panel=1, + xlim=[0,100,10],ylim=[500,1000,50]) + + diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index 3590145..fef8fe9 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -109,8 +109,9 @@ def plot_data(self,data,mplcommand,p2p,fname,pt,no_title=1,save=1,**kwargs): if not no_title: title = utils.string_from_time('title',pt,tupleformat=0) plt.title(title) - #if self.C.plot_colorbar: - #self.bmap.colorbar(f1,location='bottom',orientation='horizontal') + plot_colorbar = 1 + if plot_colorbar: + self.fig.colorbar(f1,orientation='horizontal') # plt.show(self.fig) # div0 = make_axes_locatable(self.ax) # cax0 = div0.append_axes("bottom", size="20%", pad=0.05) diff --git a/postWRF/postWRF/clicker.py b/postWRF/postWRF/clicker.py index 8ef736f..b003f80 100644 --- a/postWRF/postWRF/clicker.py +++ b/postWRF/postWRF/clicker.py @@ -10,28 +10,42 @@ import colourtables as ct from scales import Scales from figure import Figure +from defaults import Defaults class Clicker(Figure): # def __init__(self,config,wrfout,ax=0): def __init__(self,config,wrfout,data=0,fig=0,ax=0): - super(Clicker,self).__init__(config,wrfout,fig=fig,ax=ax) + # import pdb; pdb.set_trace() + self.C = config + self.D = Defaults() + self.W = wrfout + if isinstance(fig,M.figure.Figure): + self.fig = fig + self.ax = ax + else: + super(Clicker,self).__init__(config,wrfout,fig=fig,ax=ax) + + self.bmap,self.x,self.y = self.basemap_setup() if isinstance(data,N.ndarray): - self.bmap,self.x,self.y = self.basemap_setup() + # Lazily assuming it's reflectivity S = Scales('cref',2000) self.overlay_data(data,V=S.clvs,cmap=S.cm) - def click_x_y(self): + def click_x_y(self,plotpoint=0): + """ + plotpoint : boolean. If yes, plot point. + """ + # self.plotpoint = plotpoint # self.fig.canvas.mpl_connect('pick_event',self.onpick) + # self.point = M.patches.Circle((0,0),radius=1, color='g') + # self.ax.add_patch(self.point) self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press) + self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release_point) plt.show(self.fig) - + def draw_box(self): self.rect = M.patches.Rectangle((0,0),1,1) - self.x0 = None - self.y0 = None - self.x1 = None - self.y1 = None self.ax.add_patch(self.rect) self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press) self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release_box) @@ -39,10 +53,6 @@ def draw_box(self): def draw_line(self): self.line = M.lines.Line2D((0,0),(1,1)) - self.x0 = None - self.y0 = None - self.x1 = None - self.y1 = None self.ax.add_line(self.line) self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press) self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release_line) @@ -53,6 +63,14 @@ def on_press(self, event): self.x0 = event.xdata self.y0 = event.ydata + def on_release_point(self,event): + # self.point.set_xy((self.x0,self.y0)) + if hasattr(self,'scatt'): + if isinstance(self.scatt, M.collections.PathCollection): + self.scatt.remove() + self.scatt = self.ax.scatter(self.x0,self.y0,marker='x') + self.ax.figure.canvas.draw() + def on_release_box(self, event): print 'release' self.x1 = event.xdata @@ -126,4 +144,4 @@ def set_box_width(self,X): raise Exception else: print("Try again.") - \ No newline at end of file + diff --git a/postWRF/postWRF/figure.py b/postWRF/postWRF/figure.py index a0111dd..10e508d 100644 --- a/postWRF/postWRF/figure.py +++ b/postWRF/postWRF/figure.py @@ -16,7 +16,7 @@ from defaults import Defaults class Figure(object): - def __init__(self,config,wrfout,ax=0,fig=0,plotn=(1,1)): + def __init__(self,config,wrfout,ax=0,fig=0,plotn=(1,1),layout='normal'): """ C : configuration settings W : data @@ -39,6 +39,16 @@ def __init__(self,config,wrfout,ax=0,fig=0,plotn=(1,1)): if ax and fig: self.ax = ax self.fig = fig + elif layout == 'insetv': + self.fig = plt.figure(figsize=(8,6)) + self.gs = M.gridspec.GridSpec(1,2,width_ratios=[1,3]) + self.ax0 = plt.subplot(self.gs[0]) + self.ax1 = plt.subplot(self.gs[1]) + elif layout == 'inseth': + self.fig = plt.figure(figsize=(6,8)) + self.gs = M.gridspec.GridSpec(2,1,height_ratios=[1,3]) + self.ax0 = plt.subplot(self.gs[0]) + self.ax1 = plt.subplot(self.gs[1]) else: self.fig, self.ax = plt.subplots(nrows=plotn[0],ncols=plotn[1]) self.fig.set_dpi(dpi) @@ -62,10 +72,15 @@ def figsize(self,defwidth,defheight,fig): fig.set_size_inches(width,height) return fig - def save(self,p2p,fname): + def save(self,outpath,fname): # fig.tight_layout() - utils.trycreate(p2p) - fpath = os.path.join(p2p,fname) + if fname[-4:] == '.png': + pass + else: + fname = fname + '.png' + + utils.trycreate(outpath) + fpath = os.path.join(outpath,fname) #self.fig.savefig(fpath) self.fig.savefig(fpath,bbox_inches='tight') print("Saving figure {0}".format(fpath)) diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index 5598a10..ba29f6c 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -42,6 +42,8 @@ import WEM.utils as utils from xsection import CrossSection from clicker import Clicker +import maps +import stats # TODO: Make this awesome @@ -693,9 +695,78 @@ def plot_error_growth(self,ofname,folder,pfname,sensitivity=0,ylimits=0,**kwargs plt.close() print("Saved {0}.".format(fpath)) - def composite_profile(self,va,skewT_time,skewT_latlon,enspaths,dom=2,mean=0,std=0,xlim=0,ylim=0): + def composite_profile(self,va,time,latlon,enspaths,dom=2,mean=0,std=0,xlim=0,ylim=0): P = Profile(self.C) - P.composite_profile(va,skewT_time,skewT_latlon,enspaths,dom,mean,std,xlim,ylim) + P.composite_profile(va,time,latlon,enspaths,dom,mean,std,xlim,ylim) + + def twopanel_profile(self,va,time,wrf_sds,out_sd,two_panel=1,dom=1,mean=1,std=1, + xlim=0,ylim=0,latlon=0,overlay=0): + """ + Create two-panel figure with profile location on map, + with profile of all ensemble members in comparison. + + Inputs: + va : variable for profile + time : time of plot + wrf_sds : subdirs containing wrf file + out_d : out directory for plots + + Optional: + two_panel : add inset for plot location + dom : WRF domain to use + mean : overlay mean on profile + std : overlay +/- std dev on profile + xlim : three-item list/tuple with limits, spacing interval + for xaxis, in whatever default units + ylim : similarly for yaxis but in hPa + latlon : two-item list/tuple with lat/lon. + If not specified, use pop-ups to select. + overlay : data from the same time to overlay on inset + + """ + # Initialise with first wrfout file + self.W = self.get_wrfout(wrf_sds[0],dom=dom) + outpath = self.get_outpath(out_sd) + + # Get list of all wrfout files + enspaths = self.list_ncfiles(wrf_sds) + + self.data = 0 + if two_panel: + P2 = Figure(self.C,self.W,layout='inseth') + if overlay: + F = BirdsEye(self.C, self.W) + self.data = F.plot2D('cref',time,2000,dom,outpath,save=0,return_data=1) + + # Create basemap for clicker object + # F = BirdsEye(self.C,self.W) + # self.data = F.plot2D('cref',time,2000,dom,outpath,save=0,return_data=1) + + + + # TODO: Not sure basemap inset works for lat/lon specified + if isinstance(latlon,collections.Sequence): + if not len(latlon) == 2: + print("Latitude and longitude needs to be two-item list/tuple.") + raise Exception + lat0,lon0 = latlon + else: + t_long = utils.string_from_time('output',time) + print("Pick location for {0}".format(t_long)) + C = Clicker(self.C,self.W,fig=P2.fig,ax=P2.ax0,data=self.data) + # fig should be P2.fig. + # C.fig.tight_layout() + + # Pick location for profile + C.click_x_y(plotpoint=1) + lon0, lat0 = C.bmap(C.x0,C.y0,inverse=True) + + + # Compute profile + P = Profile(self.C) + P.composite_profile(va,time,(lat0,lon0),enspaths,outpath,dom=dom,mean=mean, + std=std,xlim=xlim,ylim=ylim,fig=P2.fig,ax=P2.ax1) + def plot_skewT(self,plot_time,plot_latlon,dom=1,save_output=0,composite=0): wrfouts = self.wrfout_files_in(self.C.wrfout_root) @@ -852,6 +923,7 @@ def plot_xs(self,vrbl,times,latA=0,lonA=0,latB=0,lonB=0, for t in t_list: XS.plot_xs(vrbl,t,outpath,clvs=clvs,ztop=ztop) + def cold_pool_strength(self,time,wrf_sd=0,wrf_nc=0,out_sd=0,swath_width=100,dom=1,twoplot=0,fig=0,axes=0,dz=0): """ Pick A, B points on sim ref overlay @@ -988,8 +1060,57 @@ def spaghetti(self,t,lv,va,contour,wrf_sds,out_sd,dom=1): ncfiles.append(ncfile) F.spaghetti(t,lv,va,contour,ncfiles,outpath) - + def std(self,t,lv,va,wrf_sds,out_sd,dom=1): + """Compute standard deviation of all members + for given variable. + + Inputs: + t : time + lv : level + va : variable + wrf_sds : list of wrf subdirs to loop over + out_sd : directory in which to save image + + """ + + outpath = self.get_outpath(out_sd) + + ncfiles = self.list_ncfiles(wrf_sds) + + # Use first wrfout to initialise grid, get indices + self.W = self.get_wrfout(wrf_sds[0],dom=dom) + + tidx = self.W.get_time_idx(t) + + if lv==2000: + # lvidx = None + lvidx = 0 + else: + print("Only support surface right now") + raise Exception + + std_data = stats.std(ncfiles,va,tidx,lvidx) + + F = BirdsEye(self.C, self.W) + t_name = utils.string_from_time('output',t) + fname_t = 'std_{0}_{1}'.format(va,t_name) + V = 0 + F.plot_data(std_data,'contourf',outpath,fname_t,t,V=V,no_title=1) + print("Plotting std dev for {0} at time {1}".format(va,t_name)) + + def list_ncfiles(self,wrf_sds,dom=1,path_only=1): + ncfiles = [] + for wrf_sd in wrf_sds: + ncfile = self.get_wrfout(wrf_sd,dom=dom,path_only=path_only) + ncfiles.append(ncfile) + return ncfiles + + def plot_domains(self,wrfouts,labels,latlons,out_sd=0,colour=0): + outpath = self.get_outpath(out_sd) + + maps.plot_domains(wrfouts,labels,latlons,outpath,colour) + return diff --git a/postWRF/postWRF/maps.py b/postWRF/postWRF/maps.py new file mode 100644 index 0000000..04fc1fc --- /dev/null +++ b/postWRF/postWRF/maps.py @@ -0,0 +1,56 @@ +# from figure import Figure +import matplotlib.pyplot as plt +from mpl_toolkits.basemap import Basemap +import collections +from wrfout import WRFOut +import os + +def plot_domains(wrfouts,labels,latlons,outpath,colour=0): + """ + wrfouts : list of wrfout file paths + latlons : dictionary of Nlim,Elim,Slim,Wlim + for plot + """ + + fig, ax = plt.subplots(1) + + # Create basemap first of all + #basemap_res = getattr(self.C,'basemap_res',self.D.basemap_res) + basemap_res = 'h' + + m = Basemap( + projection='merc', + llcrnrlon=latlons['Wlim'],llcrnrlat=latlons['Slim'], + urcrnrlon=latlons['Elim'],urcrnrlat=latlons['Nlim'], + lat_0=latlons['lat0'],lon_0=latlons['lon0'], + resolution=basemap_res,area_thresh=500, + ax=ax) + + m.drawcoastlines() + m.drawstates() + m.drawcountries() + + if not isinstance(colour,collections.Sequence): + colours = ['k',] * len(wrfouts) + else: + colours = colour + # Get corners of each domain + for gridlabel,fpath,colour in zip(labels,wrfouts,colours): + W = WRFOut(fpath) + print("Plotting domain {0} for {1}".format(gridlabel,fpath)) + #Nlim, Elim, Slim, Wlim = W.get_limits() + x,y = m(W.lons,W.lats) + xl = len(x[0,:]) + midpt = len(y[0,:])/2 + ax.annotate(gridlabel,color=colour,fontsize=10,xy=(x[0,-(0.12*xl)],y[0,midpt]), + bbox=dict(fc='white'),alpha=1,va='center',ha='left') + m.plot(x[0,:],y[0,:],colour,lw=2) + ax.plot(x[:,0],y[:,0],colour,lw=2) + ax.plot(x[len(y)-1,:],y[len(y)-1,:],colour,lw=2) + ax.plot(x[:,len(x)-1],y[:,len(x)-1],colour,lw=2) + + # fpath = os.path.join(self.C.output_root,'domains.png') + fname = 'domains.png' + fpath = os.path.join(outpath,fname) + fig.savefig(fpath) + print("Saved to "+fpath) diff --git a/postWRF/postWRF/skewt.py b/postWRF/postWRF/skewt.py index 4283140..651887b 100644 --- a/postWRF/postWRF/skewt.py +++ b/postWRF/postWRF/skewt.py @@ -29,7 +29,8 @@ def __init__(self,config,wrfout=0): if wrfout: self.W = wrfout - def composite_profile(self,va,plot_time,plot_latlon,wrfouts,dom,mean,std,xlim,ylim): + def composite_profile(self,va,plot_time,plot_latlon,wrfouts,outpath, + dom=1,mean=1,std=1,xlim=0,ylim=0,fig=0,ax=0): """ Loop over wrfout files. Get profile of variable @@ -37,30 +38,40 @@ def composite_profile(self,va,plot_time,plot_latlon,wrfouts,dom,mean,std,xlim,yl Optional standard deviation Optional mean + + If ax, save image to that axis """ # Set up figure - fig = plt.figure() - + if isinstance(fig,M.figure.Figure): + self.fig = fig + self.ax = ax + else: + self.fig, self.ax = plt.subplots() + # Plot settings if xlim: xmin, xmax, xint = xlim if ylim: - P_bot, P_top, dp = [y*100.0 for y in ylim] + if ylim[1] > ylim[0]: + # top to bottom + P_top, P_bot, dp = [y*100.0 for y in ylim] + else: + P_bot, P_top, dp = [y*100.0 for y in ylim] else: P_bot = 100000.0 P_top = 20000.0 dp = 10000.0 - plevs = N.arange(P_bot,P_top,dp) + # plevs = N.arange(P_bot,P_top,dp) # Get wrfout prototype for information W = WRFOut(wrfouts[0]) lat, lon = plot_latlon datestr = utils.string_from_time('output',plot_time) - t_idx = W.get_time_idx(plot_time,tuple_format=1) - y, x, exact_lat, exact_lon = gridded_data.getXY(W.lats1D,W.lons1D,lat,lon) + t_idx = W.get_time_idx(plot_time,) + y, x, exact_lat, exact_lon = utils.getXY(W.lats1D,W.lons1D,lat,lon) slices = {'t': t_idx, 'la': y, 'lo': x} #var_slices = {'t': t_idx, 'lv':0, 'la':y, 'lo':x} @@ -78,7 +89,7 @@ def composite_profile(self,va,plot_time,plot_latlon,wrfouts,dom,mean,std,xlim,yl # Set up legend labels = [] colourlist = utils.generate_colours(M,nens) - M.rcParams['axes.color_cycle'] = colourlist + # M.rcParams['axes.color_cycle'] = colourlist # Collect profiles for n,wrfout in enumerate(wrfouts): @@ -93,7 +104,7 @@ def composite_profile(self,va,plot_time,plot_latlon,wrfouts,dom,mean,std,xlim,yl profile_arr[:,n] = W.get(va,slices)[0,:,0,0] # Plot variable on graph - plt.plot(profile_arr[:,n],composite_P[:,n]) + self.ax.plot(profile_arr[:,n],composite_P[:,n],color=colourlist[n]) member = wrfout.split('/')[-2] labels.append(member) @@ -103,55 +114,55 @@ def composite_profile(self,va,plot_time,plot_latlon,wrfouts,dom,mean,std,xlim,yl if mean: profile_mean = N.mean(profile_arr,axis=1) profile_mean_P = N.mean(composite_P,axis=1) - plt.plot(profile_mean,profile_mean_P,color='black') + self.ax.plot(profile_mean,profile_mean_P,color='black') if std: # Assume mean P across ensemble is correct level # to plot standard deviation profile_std = N.std(profile_arr,axis=1) std_upper = profile_mean + profile_std std_lower = profile_mean - profile_std - plt.plot(std_upper,profile_mean_P,'k--') - plt.plot(std_lower,profile_mean_P,'k--') + self.ax.plot(std_upper,profile_mean_P,'k--') + self.ax.plot(std_lower,profile_mean_P,'k--') fname = '_'.join(('profile_comp',va,datestr,'{0:03d}'.format(x),'{0:03d}'.format(y))) + '.png' - utils.trycreate(self.path_to_output) - fpath = os.path.join(self.path_to_output,fname) - # Set semi-log graph - plt.gca().set_yscale('log') - + self.ax.set_yscale('log') # Plot limits, ticks - yticks = N.arange(P_bot,P_top,-100*100) - plt.yticks(yticks) - ytix = ["%4u" %(p/100.0) for p in yticks] - plt.yticks(yticks,ytix) + yticks = N.arange(P_bot,P_top+dp*100.0,-100*100.0) + # yticks = N.arange(P_bot,P_top+dp,-dp*100) + # self.ax.set_yticks(yticks) + ylabels = ["%4u" %(p/100.0) for p in yticks] + self.ax.set_yticks(yticks) + self.ax.set_yticklabels(ylabels) + # import pdb; pdb.set_trace() + # self.ax.yaxis.tick_right() #plt.axis([-20,50,105000.0,20000.0]) #plt.xlabel(r'Temperature ($^{\circ}$C) at 1000 hPa') #plt.xticks(xticks,['' if tick%10!=0 else str(tick) for tick in xticks]) - plt.ylabel('Pressure (hPa)') + self.ax.set_ylabel('Pressure (hPa)') #yticks = N.arange(self.P_bot,P_t-1,-10**4) #plt.yticks(yticks,yticks/100) if xlim: - plt.xlim([xmin,xmax]) + self.ax.set_xlim([xmin,xmax]) xticks = N.arange(xmin,xmax+xint,xint) - plt.xticks(xticks) + self.ax.set_xticks(xticks) # Flip y axis - plt.ylim([P_bot,P_top]) + # Limits are already set + self.ax.set_ylim([P_bot,P_top]) + + # plt.tight_layout(self.fig) #plt.autoscale(enable=1,axis='x') #ax = plt.gca() #ax.relim() #ax.autoscale_view() #plt.draw() - plt.legend(labels,loc=2,fontsize=6) - - plt.savefig(fpath) - plt.close() - - + self.ax.legend(labels,loc=2,fontsize=6) + self.save(outpath,fname) + plt.close(self.fig) class SkewT(Figure): def __init__(self,config,wrfout=0): diff --git a/postWRF/postWRF/stats.py b/postWRF/postWRF/stats.py new file mode 100644 index 0000000..5cf9d65 --- /dev/null +++ b/postWRF/postWRF/stats.py @@ -0,0 +1,23 @@ +import numpy as N +import pdb + +from wrfout import WRFOut + +def std(ncfiles,va,tidx,lvidx): + """ + Find standard deviation in along axis of ensemble + members. Returns matrix x-y for plotting + """ + for n, nc in enumerate(ncfiles): + W = WRFOut(nc) + slices = {'lv':lvidx, 't':tidx} + va_array = W.get(va,slices) + dims = va_array.shape + + if n==0: + all_members = N.zeros([len(ncfiles),1,1,dims[-2],dims[-1]]) + all_members[n,0,0,:,:] = va_array[...] + + std = N.std(all_members,axis=0).reshape([dims[-2],dims[-1]]) + # pdb.set_trace() + return std diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index a446bb7..08f7f67 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -176,7 +176,8 @@ def load(self,var,PS): # pdb.set_trace() if PS['destag_dim'] and isinstance(sl[PS['destag_dim']],N.ndarray): PS['destag_dim'] = None - + + # pdb.set_trace() data = self.destagger(d[sl],PS['destag_dim']) return data From f6141df0677716dd84e1fb3e06745a9ee71b11ee Mon Sep 17 00:00:00 2001 From: John Lawson Date: Mon, 22 Sep 2014 16:40:40 -0500 Subject: [PATCH 110/111] Frontogenesis maybe not working --- postWRF/bin/bowecho_plot.py | 73 ++++++++++---- postWRF/postWRF/birdseye.py | 5 +- postWRF/postWRF/colourtables.py | 18 ++-- postWRF/postWRF/main.py | 63 ++++++++++-- postWRF/postWRF/scales.py | 10 +- postWRF/postWRF/skewt.py | 17 +++- postWRF/postWRF/wrfout.py | 174 ++++++++++++++++++++++++++++++-- 7 files changed, 313 insertions(+), 47 deletions(-) diff --git a/postWRF/bin/bowecho_plot.py b/postWRF/bin/bowecho_plot.py index eb2400a..b1522db 100644 --- a/postWRF/bin/bowecho_plot.py +++ b/postWRF/bin/bowecho_plot.py @@ -4,6 +4,7 @@ import matplotlib as M M.use('gtkagg') import matplotlib.pyplot as plt +import numpy as N sys.path.append('/home/jrlawson/gitprojects/') @@ -22,20 +23,23 @@ coldpoolstrength = 0 spaghetti = 0 std = 0 -profiles = 1 +profiles = 0 +frontogenesis = 1 +upperlevel = 0 -enstype = 'STCH' +# enstype = 'STCH' # enstype = 'ICBC' -# enstype = 'MXMP' +enstype = 'MXMP' #case = '20060526' +case = '2006052612' #case = '20090910' # case = '20110419' -case = '20130815' +# case = '20130815' -IC = 'GEFSR2' -#IC = 'NAM' -#IC = 'RUC' +# IC = 'GEFSR2' +IC = 'NAM' +# IC = 'RUC' #ensnames = ['anl'] @@ -47,17 +51,20 @@ ensnames = ['p09',] MP = 'ICBC' elif enstype == 'MXMP': + # experiments = ['WSM6_Hail','Kessler','Ferrier', experiments = ['WSM6_Grau','WSM6_Hail','Kessler','Ferrier', 'WSM5','WDM5','Lin','WDM6_Grau','WDM6_Hail', 'Morrison_Grau','Morrison_Hail'] # experiments = ['ICBC',] - ensnames = ['p09',] + ensnames = ['anl',] elif enstype == 'ICBC': ensnames = ['c00'] + ['p'+"%02d" %n for n in range(1,11)] + experiments = ['ICBC',] if case[:4] == '2006': - itime = (2006,5,26,0,0,0) + itime = (2006,5,26,12,0,0) ftime = (2006,5,27,13,0,0) + times = [(2006,5,27,3,0,0),] elif case[:4] == '2009': itime = (2009,9,10,23,0,0) ftime = (2009,9,11,14,0,0) @@ -65,12 +72,13 @@ itime = (2011,4,19,18,0,0) ftime = (2011,4,20,10,30,0) elif case[:4] == '2013': - itime = (2013,8,15,12,0,0) + itime = (2013,8,15,3,0,0) ftime = (2013,8,16,12,0,0) # times = [(2013,8,16,3,0,0),] else: raise Exception +hourly = 6 levels = 2000 def get_folders(en,ex): @@ -83,7 +91,7 @@ def get_folders(en,ex): return out_sd, wrf_sd -times = utils.generate_times(itime,ftime,6*60*60) +# times = utils.generate_times(itime,ftime,hourly*60*60) #shear_times = utils.generate_times(itime,ftime,3*60*60) #sl_times = utils.generate_times(sl_itime,sl_ftime,1*60*60) @@ -123,8 +131,8 @@ def get_folders(en,ex): for ex in experiments: out_sd, wrf_sd = get_folders(en,ex) - p.plot_strongest_wind(itime,ftime,2000,wrf_sd=wrf_sd,out_sd=out_sd) - #p.plot2D('cref',times,levels,wrf_sd=wrf_sd,out_sd=out_sd) + # p.plot_strongest_wind(itime,ftime,2000,wrf_sd=wrf_sd,out_sd=out_sd) + p.plot2D('cref',times,levels,wrf_sd=wrf_sd,out_sd=out_sd) if streamlines: for en in ensnames: @@ -191,8 +199,10 @@ def get_folders(en,ex): lv = 2000 # Save to higher directory out_d = os.path.dirname(out_sd) + if enstype == 'ICBC': + out_d = os.path.dirname(out_d) for t in times: - p.std(t,lv,'RH',wrf_sds,out_d) + p.std(t,lv,'RH',wrf_sds,out_d,clvs=N.arange(0,26,1)) if profiles: wrf_sds = [] @@ -200,12 +210,41 @@ def get_folders(en,ex): for ex in experiments: out_sd, wrf_sd = get_folders(en,ex) wrf_sds.append(wrf_sd) - + + locs = {'KTOP':(39.073,-95.626),'KOAX':(41.320,-96.366), + 'KOUN':(35.244,-97.471)} lv = 2000 + vrbl = 'RH'; xlim=[0,110,10] + # vrbl = 'wind'; xlim=[0,50,5] # Save to higher directory + ml = -2 out_d = os.path.dirname(out_sd) + if enstype == 'ICBC': + out_d = os.path.dirname(out_d) + ml = -3 for t in times: - p.twopanel_profile('RH',t,wrf_sds,out_d,two_panel=1, - xlim=[0,100,10],ylim=[500,1000,50]) + for ln,ll in locs.iteritems(): + p.twopanel_profile(vrbl,t,wrf_sds,out_d,two_panel=1, + xlim=xlim,ylim=[500,1000,50], + latlon=ll,locname=ln,ml=ml) +if frontogenesis: + for en in ensnames: + for ex in experiments: + out_sd, wrf_sd = get_folders(en,ex) + for time in times: + p.frontogenesis(time,850,wrf_sd=wrf_sd,out_sd=out_sd, + clvs=N.arange(-5.0,5.25,0.25)*10**-11 + # clvs = N.arange(0,1.3,0.01)*10**-3 + ) + +if upperlevel: + for en in ensnames: + for ex in experiments: + out_sd, wrf_sd = get_folders(en,ex) + for time in times: + p.upperlevel_W(time,850,wrf_sd=wrf_sd,out_sd=out_sd, + clvs = N.arange(0,1.0,0.01) + ) + diff --git a/postWRF/postWRF/birdseye.py b/postWRF/postWRF/birdseye.py index fef8fe9..993d1cb 100644 --- a/postWRF/postWRF/birdseye.py +++ b/postWRF/postWRF/birdseye.py @@ -47,7 +47,10 @@ def get_contouring(self,vrbl='user',lv='user',**kwargs): # if self.mplcommand == 'contour': # multiplier = S.get_multiplier(vrbl,lv) - + if 'clvs' in kwargs: + if isinstance(kwargs['clvs'],N.ndarray): + plotkwargs['levels'] = kwargs['clvs'] + kwargs.pop('clvs') # pdb.set_trace() return plotargs, plotkwargs diff --git a/postWRF/postWRF/colourtables.py b/postWRF/postWRF/colourtables.py index b49e3a0..45f2cf7 100644 --- a/postWRF/postWRF/colourtables.py +++ b/postWRF/postWRF/colourtables.py @@ -63,7 +63,7 @@ def mixprecip1(*args): mix_coltbl = LinearSegmentedColormap('MIX_COLTBL',mix_cdict) return mix_coltbl -def grays(): +def grays(*args): grays_cdict ={'red': ((0.00, 1.00, 1.00), (1.00, 0.05, 0.05)), 'green': ((0.00, 1.00, 1.00), @@ -73,7 +73,7 @@ def grays(): grays_coltbl = LinearSegmentedColormap('GRAYS_COLTBL',grays_cdict) return grays_coltbl -def snow2(): +def snow2(*args): snowf_cdict ={'red': ((0.00, 0.91, 0.91), (0.06, 0.81, 0.81), (0.12, 0.51, 0.51), @@ -128,7 +128,7 @@ def snow2(): snowf_coltbl = LinearSegmentedColormap('SNOWF_COLTBL',snowf_cdict) return snowf_coltbl -def reflect(): +def reflect(*args): reflect_cdict ={'red': ((0.000, 0.40, 0.40), (0.067, 0.20, 0.20), (0.133, 0.00, 0.00), @@ -233,7 +233,7 @@ def reflect_ncdc(*args): reflect_ncdc_coltbl = LinearSegmentedColormap('REFLECT_NCDC_COLTBL',reflect_ncdc_cdict) return reflect_ncdc_coltbl -def irsat(): +def irsat(*args): irsat_cdict ={'red': ((0.000, 1.000, 0.294), (0.067, 1.000, 1.000), (0.133, 0.804, 0.804), @@ -279,7 +279,7 @@ def irsat(): irsat_coltbl = LinearSegmentedColormap('IRSAT_COLTBL',irsat_cdict) return irsat_coltbl -def bw_irsat(): +def bw_irsat(*args): bw_irsat_cdict ={'red': ((0.000, 1.000, 1.000), (1.000, 0.000, 0.000)), 'green': ((0.000, 1.000, 1.000), @@ -290,7 +290,7 @@ def bw_irsat(): return bw_irsat_coltbl -def precip1(): +def precip1(*args): precip_cdict ={'red': ((0.000, 1.000, 1.000), (0.004, 0.914, 0.914), (0.012, 0.812, 0.812), @@ -357,7 +357,7 @@ def precip1(): precip_coltbl = LinearSegmentedColormap('PRECIP_COLTBL',precip_cdict) return precip_coltbl -def dewpoint1(): +def dewpoint1(*args): dwp_cdict ={'red': ((0.00, 0.60, 0.60), (0.35, 0.70, 0.70), (0.40, 0.80, 0.80), @@ -395,7 +395,7 @@ def dewpoint1(): dwp_coltbl = LinearSegmentedColormap('DWP_COLTBL',dwp_cdict) return dwp_coltbl -def sftemp(): +def sftemp(*args): sfc_cdict ={'red': ((0.00, 0.20, 0.20), (0.08, 0.40, 0.40), (0.17, 0.27, 0.27), @@ -442,7 +442,7 @@ def sftemp(): sfc_coltbl = LinearSegmentedColormap('SFC_COLTBL',sfc_cdict) return sfc_coltbl -def thetae(): +def thetae(*args): thte_cdict ={'red': ((0.00, 0.20, 0.20), (0.08, 0.40, 0.40), (0.17, 0.27, 0.27), diff --git a/postWRF/postWRF/main.py b/postWRF/postWRF/main.py index ba29f6c..777f838 100644 --- a/postWRF/postWRF/main.py +++ b/postWRF/postWRF/main.py @@ -109,10 +109,10 @@ def plot2D(self,vrbl,times,levels,wrf_sd=0,wrf_nc=0,out_sd=0,f_prefix=0,f_suffix """ + # import pdb; pdb.set_trace() self.W = self.get_wrfout(wrf_sd,wrf_nc,dom=dom) outpath = self.get_outpath(out_sd) - # Make sure times are in datenum format and sequence. t_list = utils.ensure_sequence_datenum(times) @@ -700,7 +700,7 @@ def composite_profile(self,va,time,latlon,enspaths,dom=2,mean=0,std=0,xlim=0,yli P.composite_profile(va,time,latlon,enspaths,dom,mean,std,xlim,ylim) def twopanel_profile(self,va,time,wrf_sds,out_sd,two_panel=1,dom=1,mean=1,std=1, - xlim=0,ylim=0,latlon=0,overlay=0): + xlim=0,ylim=0,latlon=0,locname=0,overlay=0,ml=-2): """ Create two-panel figure with profile location on map, with profile of all ensemble members in comparison. @@ -719,9 +719,14 @@ def twopanel_profile(self,va,time,wrf_sds,out_sd,two_panel=1,dom=1,mean=1,std=1, xlim : three-item list/tuple with limits, spacing interval for xaxis, in whatever default units ylim : similarly for yaxis but in hPa + or dictionary with locations (METAR etc) and two-item tuple latlon : two-item list/tuple with lat/lon. If not specified, use pop-ups to select. + locname : pass this to the filename of output for saving overlay : data from the same time to overlay on inset + ml : member level. negative number that corresponds to the + folder in absolute string for naming purposes. + """ # Initialise with first wrfout file @@ -750,6 +755,9 @@ def twopanel_profile(self,va,time,wrf_sds,out_sd,two_panel=1,dom=1,mean=1,std=1, print("Latitude and longitude needs to be two-item list/tuple.") raise Exception lat0,lon0 = latlon + C = Clicker(self.C,self.W,fig=P2.fig,ax=P2.ax0,data=self.data) + x0, y0 = C.bmap(lon0,lat0) + C.ax.scatter(x0,y0,marker='x') else: t_long = utils.string_from_time('output',time) print("Pick location for {0}".format(t_long)) @@ -765,7 +773,8 @@ def twopanel_profile(self,va,time,wrf_sds,out_sd,two_panel=1,dom=1,mean=1,std=1, # Compute profile P = Profile(self.C) P.composite_profile(va,time,(lat0,lon0),enspaths,outpath,dom=dom,mean=mean, - std=std,xlim=xlim,ylim=ylim,fig=P2.fig,ax=P2.ax1) + std=std,xlim=xlim,ylim=ylim,fig=P2.fig,ax=P2.ax1, + locname=locname,ml=ml) def plot_skewT(self,plot_time,plot_latlon,dom=1,save_output=0,composite=0): @@ -1061,7 +1070,7 @@ def spaghetti(self,t,lv,va,contour,wrf_sds,out_sd,dom=1): F.spaghetti(t,lv,va,contour,ncfiles,outpath) - def std(self,t,lv,va,wrf_sds,out_sd,dom=1): + def std(self,t,lv,va,wrf_sds,out_sd,dom=1,clvs=0): """Compute standard deviation of all members for given variable. @@ -1070,8 +1079,10 @@ def std(self,t,lv,va,wrf_sds,out_sd,dom=1): lv : level va : variable wrf_sds : list of wrf subdirs to loop over + + Optional out_sd : directory in which to save image - + clvs : user-set contour levels """ outpath = self.get_outpath(out_sd) @@ -1095,8 +1106,13 @@ def std(self,t,lv,va,wrf_sds,out_sd,dom=1): F = BirdsEye(self.C, self.W) t_name = utils.string_from_time('output',t) fname_t = 'std_{0}_{1}'.format(va,t_name) - V = 0 - F.plot_data(std_data,'contourf',outpath,fname_t,t,V=V,no_title=1) + + # pdb.set_trace() + plotkwargs = {} + plotkwargs['no_title'] = 1 + if isinstance(clvs,N.ndarray): + plotkwargs['clvs'] = clvs + F.plot_data(std_data,'contourf',outpath,fname_t,t,**plotkwargs) print("Plotting std dev for {0} at time {1}".format(va,t_name)) def list_ncfiles(self,wrf_sds,dom=1,path_only=1): @@ -1112,10 +1128,41 @@ def plot_domains(self,wrfouts,labels,latlons,out_sd=0,colour=0): maps.plot_domains(wrfouts,labels,latlons,outpath,colour) return + def upperlevel_W(self,time,level,wrf_sd=0,out_sd=0,dom=1,clvs=0, + no_title=1): + # import pdb; pdb.set_trace() + outpath = self.get_outpath(out_sd) + self.W = self.get_wrfout(wrf_sd,dom=dom) + + data = self.W.isosurface_p('W',time,level) + F = BirdsEye(self.C,self.W) + tstr = utils.string_from_time('output',time) + fname = 'W_{0}_{1}.png'.format(level,tstr) + F.plot_data(data,'contourf',outpath,fname,time,clvs=clvs, + no_title=no_title) + def frontogenesis(self,time,level,wrf_sd=0,out_sd=0,dom=1, + clvs=0,no_title=1): + """ + Compute and plot (Miller?) frontogenesis as d/dt of theta gradient. + + Use a centred-in-time derivative; hence, if + time index is start or end of wrfout file, skip the plot. + """ + outpath = self.get_outpath(out_sd) + self.W = self.get_wrfout(wrf_sd,dom=dom) + tstr = utils.string_from_time('output',time) + + Front = self.W.compute_frontogenesis(time,level) - + if isinstance(Front,N.ndarray): + F = BirdsEye(self.C,self.W) + fname = 'frontogen_{0}.png'.format(tstr) + F.plot_data(Front,'contourf',outpath,fname,time,clvs=clvs, + no_title=no_title) + else: + print("Skipping this time; at start or end of run.") diff --git a/postWRF/postWRF/scales.py b/postWRF/postWRF/scales.py index ebc2241..3d122a8 100644 --- a/postWRF/postWRF/scales.py +++ b/postWRF/postWRF/scales.py @@ -16,6 +16,7 @@ These can be overwritten in user's config file... somehow """ import pdb +import matplotlib as M from matplotlib.colors import LinearSegmentedColormap import numpy as N import matplotlib.pyplot as plt @@ -51,12 +52,15 @@ def __init__(self,vrbl,lv,clvs=0): # raise Exception try: - self.cm = self.A[vrbl]['cmap'](clvs) + # Need to unify user cms and matplotlib cms. + # self.cm = self.A[vrbl]['cmap'] # This is for matplotlib + self.cm = self.A[vrbl]['cmap'](clvs) # This is for user except TypeError: #print("Using default colourtable.") #def_ct = plt.cm.get_cmap("jet") self.cm = 0 #cm = LinearSegmentedColormap('DEF_CT',def_ct) + # pdb.set_trace() def get_multiplier(self,vrbl,lv): m = self.A[vrbl].get('multiplier',1) @@ -135,6 +139,10 @@ def get_dict_of_levels(self): A['PMSL'] = {'cmap':0,'multiplier':0.01} A['PMSL'][2000] = (97000,103100,100) + + # A['RH'] = {'cmap':ct.irsat} + A['RH'] = {'cmap':M.cm.BrBG} + A['RH'][2000] = (0,110,5) A['olr'] = {'cmap':ct.irsat} A['olr'][2000] = [-100,-90,-85,-80,-75,-70,-65,-60,-55,-50, diff --git a/postWRF/postWRF/skewt.py b/postWRF/postWRF/skewt.py index 651887b..67bb802 100644 --- a/postWRF/postWRF/skewt.py +++ b/postWRF/postWRF/skewt.py @@ -30,7 +30,8 @@ def __init__(self,config,wrfout=0): self.W = wrfout def composite_profile(self,va,plot_time,plot_latlon,wrfouts,outpath, - dom=1,mean=1,std=1,xlim=0,ylim=0,fig=0,ax=0): + dom=1,mean=1,std=1,xlim=0,ylim=0,fig=0,ax=0, + locname=0,ml=-2): """ Loop over wrfout files. Get profile of variable @@ -40,6 +41,9 @@ def composite_profile(self,va,plot_time,plot_latlon,wrfouts,outpath, Optional mean If ax, save image to that axis + + ml : member level. negative number that corresponds to the + folder in absolute string for naming purposes. """ # Set up figure @@ -105,10 +109,10 @@ def composite_profile(self,va,plot_time,plot_latlon,wrfouts,outpath, # Plot variable on graph self.ax.plot(profile_arr[:,n],composite_P[:,n],color=colourlist[n]) - - member = wrfout.split('/')[-2] + + member = wrfout.split('/')[ml] labels.append(member) - # pdb.set_trace() + # if locname=='KOAX': pdb.set_trace() # Compute mean, std etc if mean: @@ -124,7 +128,10 @@ def composite_profile(self,va,plot_time,plot_latlon,wrfouts,outpath, self.ax.plot(std_upper,profile_mean_P,'k--') self.ax.plot(std_lower,profile_mean_P,'k--') - fname = '_'.join(('profile_comp',va,datestr,'{0:03d}'.format(x),'{0:03d}'.format(y))) + '.png' + if not locname: + fname = '_'.join(('profile_comp',va,datestr,'{0:03d}'.format(x),'{0:03d}'.format(y))) + '.png' + else: + fname = '_'.join(('profile_comp',va,datestr,locname)) + '.png' # Set semi-log graph self.ax.set_yscale('log') diff --git a/postWRF/postWRF/wrfout.py b/postWRF/postWRF/wrfout.py index 08f7f67..1cb7b99 100644 --- a/postWRF/postWRF/wrfout.py +++ b/postWRF/postWRF/wrfout.py @@ -14,6 +14,7 @@ import constants as cc import scipy.ndimage import collections +import scipy.interpolate import WEM.utils as utils import metconstants as mc @@ -57,13 +58,12 @@ def __init__(self,fpath,config=0): # from scipy import ndimage # from skimage.feature - def wrftime_to_datenum(self,times): + def wrftime_to_datenum(self): """ Convert wrf's weird Times variable to datenum time. - Input: - t : wrf's time """ + times = self.wrf_times wrf_times_epoch = N.zeros([times.shape[0]]) for n,t in enumerate(times): @@ -78,6 +78,7 @@ def wrftime_to_datenum(self,times): wrf_times_epoch[n] = calendar.timegm([yr,mth,day,hr,mins,sec]) + self.wrf_times_epoch = wrf_times_epoch return wrf_times_epoch @@ -102,7 +103,7 @@ def get_time_idx(self,t): raise Exception # Convert wrftimes to datenum times - wrf_times_epoch = self.wrftime_to_datenum(self.wrf_times) + self.wrftime_to_datenum() # Now find closest WRF time #self.time_idx = N.where( @@ -110,7 +111,7 @@ def get_time_idx(self,t): # abs(self.wrf_times_epoch-t_epoch).min() # )[0][0] # pdb.set_trace() - time_idx = utils.closest(wrf_times_epoch,t_epoch) + time_idx = utils.closest(self.wrf_times_epoch,t_epoch) return time_idx @@ -769,7 +770,44 @@ def get_lon_idx(self,lon): lon_idx = N.where(abs(self.lons-lon) == abs(self.lons-lon).min())[0][0] return lon_idx - def interp_to_p(self,config,nc_path,var,lv): + def isosurface_p(self,vrbl,time,level): + """ + Return an pressure level isosurface of given variable. + Interpolation is linear so watch out. + + """ + if isinstance(level,int): + hPa = level*100.0 + nlv = 1 + elif isinstance(level,collections.Sequence): + hPa = [l*100.0 for l in level] + nlv = len(hPa) + else: + print("Use integer or list of integers for level.") + raise Exception + + if time<1000: + tidx = time + else: + tidx = self.get_time_idx(time) + sl = {'t':tidx} + + # If this breaks, user is requesting non-4D data + datain = self.get(vrbl,sl)[0,:,:,:] + P = self.get('pressure',sl)[0,:,:,:] + # import pdb; pdb.set_trace() + dataout = N.zeros([nlv,P.shape[-2],P.shape[-1]]) + # pdb.set_trace() + for (i,j), p in N.ndenumerate(dataout[0,:,:]): + dataout[:,i,j] = N.interp(hPa,P[:,i,j][::-1],datain[:,i,j][::-1]) + # dataout = scipy.interpolate.griddata(P.flatten(),datain.flatten(),hPa) + if nlv == 1: + # Return 2D if only one level requested + return dataout[0,:,:] + else: + return dataout + + def interp_to_p_fortran(self,config,nc_path,var,lv): """ Uses p_interp fortran code to put data onto a pressure level specified. @@ -1025,5 +1063,129 @@ def find_gust_front(self,wind_slice,T2_slice,angle,method=3): return gfidx # maxshearloc[0][0] returns the integer + def compute_frontogenesis(self,time,level): + """ + Note that all variables fetched with self.get have been + destaggered and are at the same location. + + Output: + Front : Frontgenesis in Kelvin per second. + """ + # import pdb; pdb.set_trace() + tidx = self.get_time_idx(time) + if (tidx == 0) or (tidx == self.wrf_times.shape[0]-1): + return None + elif level == 2000: + tidxs = (tidx-1,tidx,tidx+1) + T = {} + TH2 = {} + Z = {} + dTdx = {} + dTdy = {} + dTdz = {} + U = {} + V = {} + W = {} + for n, tidx in enumerate(tidxs): + # Just assume surface + lvidx = 0 + U[n] = self.get('U10',{'t':tidx})[0,:,:] + V[n] = self.get('V10',{'t':tidx})[0,:,:] + W[n] = self.get('W',{'t':tidx, 'lv':0})[0,0,:,:] + T[n] = self.get('T',{'t':tidx, 'lv':[0,1]})[0,:,:,:] + TH2[n] = self.get('TH2',{'t':tidx})[0,:,:] + # Z[n] = self.get('Z',{'t':tidx, 'lv' :[0,1]})[0,:,:,:] + # We want gap between levels so not destaggered: + Z[n] = (self.nc.variables['PH'][tidx,0:2,:,:] + + self.nc.variables['PHB'][tidx,0:2,:,:])/mc.g + # Each grid point is DX km apart: + dTdx[n], dTdy[n] = N.gradient(TH2[n])/self.dx + + # import pdb; pdb.set_trace() + levelgap = Z[n][1,:,:] - Z[n][0,:,:] + _, __, dTdz[n] = N.gradient(T[n])/levelgap + # _, __, dTdz_2 = N.gradient(T[n])/levelgap + # N.gradient needs interpolating to one level here? + # dTdz[n] = (dTdz_2[1,...]-dTdz_2[0,...])/2.0 + + elif level == 850: + tidxs = (tidx-1,tidx,tidx+1) + Tupp = {} + Tlev = {} + Tlow = {} + dTdx = {} + dTdxLow = {} + dTdxUpp = {} + dTdy = {} + dTdyLow = {} + dTdyUpp = {} + dTdz = {} + U = {} + V = {} + W = {} + omega = {} + for n, t in enumerate(tidxs): + U[n] = self.isosurface_p('U',t,level) + V[n] = self.isosurface_p('V',t,level) + W[n] = self.isosurface_p('W',t,level) + + Tupp[n] = self.isosurface_p('T',t,level+25) + Tlev[n] = self.isosurface_p('T',t,level) + Tlow[n] = self.isosurface_p('T',t,level-25) + + # Compute omega + # P = rho* R* drybulb + # drybulb = T/((P0/P)^(R/cp) + drybulb = Tlev[n]/((100000.0/(level*100.0))**(mc.R/mc.cp)) + rho = (level*100.0)/(mc.R*drybulb) + omega[n] = -rho * mc.g * W[n] + + + # Height + # PH = self.isosurface_p('PH',t,(level-25,level+25)) + # PHB = self.isosurface_p('PHB',t,(level-25,level+25)) + # Z = (PH + PHB)/mc.g + + # Gradients in potential temperature + # Horizontal + # Each grid point is DX km apart: + dTdx[n], dTdy[n] = N.gradient(Tlev[n])/self.dx + dTdxUpp[n], dTdyUpp[n] = N.gradient(Tupp[n])/self.dx + dTdxLow[n], dTdyLow[n] = N.gradient(Tlow[n])/self.dx + + # Vertical + # levelgap = Z[0,:,:] - Z[1,:,:] + dp = 50*100.0 # pressure coordinates + dTdz[n] = (Tupp[n]-Tlow[n])/dp + + # Time difference in sec + dt = self.wrf_times_epoch[tidx+1]-self.wrf_times_epoch[tidx-1] + # Gradient part + grad = {} + for n in range(len(tidxs)): + # grad[n] = abs(dTdx[n] + dTdy[n] + dTdz[n])**2 + gradLev = abs(dTdx[n] + dTdy[n])**2 + gradUpp = abs(dTdxUpp[n] + dTdyUpp[n])**2 + gradLow = abs(dTdxLow[n] + dTdyLow[n])**2 + # grad[n] = N.dstack((gradLow,gradLev,gradUpp)) + grad[n] = N.dstack((gradUpp,gradLev,gradLow)) + + # Full derivative + dgraddx, dgraddy, dgraddz = N.gradient(grad[1]) + dgraddx = dgraddx[:,:,1]/self.dx # Middle level + dgraddy = dgraddy[:,:,1]/self.dy # Middle level + dgraddz = dgraddz[:,:,1]/dp # Middle level + + # Interpolate onto a single level + # dgraddx = (dgraddx[1,...]+dgraddx[0,...])/2.0 + # dgraddy = (dgraddy[1,...]+dgraddy[0,...])/2.0 + # dgraddz = (dgraddz[1,...]+dgraddz[0,...])/2.0 + + # Full equation + Front = ((grad[2][:,:,1]-grad[0][:,:,1])/dt) + U[1]*dgraddx + V[1]*dgraddy + omega[1]*dgraddz + # Front = ((grad[2][:,:,1]-grad[0][:,:,1])/dt) + U[1]*dgraddx + V[1]*dgraddy + import pdb; pdb.set_trace() + # Front = (Front_2[1]+Front_2[0])/2.0 + return Front From e027f98859cedef46f1e52538c500cd6041ddf78 Mon Sep 17 00:00:00 2001 From: Nishadh K A Date: Wed, 24 Sep 2014 20:33:48 +0530 Subject: [PATCH 111/111] minor edits to run lazywrf.py --- lazyWRF/lazyWRF/lazyWRF.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lazyWRF/lazyWRF/lazyWRF.py b/lazyWRF/lazyWRF/lazyWRF.py index a9f3665..ba1b48e 100644 --- a/lazyWRF/lazyWRF/lazyWRF.py +++ b/lazyWRF/lazyWRF/lazyWRF.py @@ -101,12 +101,15 @@ def edit_namelist(old,new,incolumn=1): break return -def edit_namelist_input(old,new,pathtoWRF=pathtoWRF): +def edit_namelist_input(old,new,incolumn=0,pathtoWRF=pathtoWRF): ninput = open(pathtoWRF+'namelist.input','r').readlines() for idx, line in enumerate(ninput): if old in line: - # Prefix for soil intermediate data filename - ninput[idx]= ninput[idx][:39] + new + " \n" + # Prefix for soil intermediate data filename + if incolumn==1: + ninput[idx] = ninput[idx][:43] + new + " \n" + else: + ninput[idx] = ' ' + old + ' = ' + new + "\n" nameout = open(pathtoWRF+'namelist.input','w') nameout.writelines(ninput) nameout.close() @@ -205,7 +208,7 @@ def intelligent_run(executable,email): ############################### # Open the WPS namelist; copy the old one in case of bugs -os.system('cp namelist.wps{,.python_backup}') +os.system('cp ' + pathtoWPS + 'namelist.wps' ' namelist.wps.python_backup') nwps = open('namelist.wps','r').readlines() # Sets values depending on initialisation data @@ -235,8 +238,8 @@ def intelligent_run(executable,email): edit_namelist("parent_grid_ratio",', '.join([str(p) for p in parent_grid_ratio])+',') edit_namelist("i_parent_start", ', '.join([str(i) for i in i_start])+',') edit_namelist("j_parent_start", ', '.join([str(j) for j in j_start])+',') - edit_namelist("dx",str(dx*1000),incolumn=0) - edit_namelist("dy",str(dy*1000),incolumn=0) + edit_namelist("dx",str(1000*int(dx))+',',incolumn=0) + edit_namelist("dy",str(1000*int(dy))+',',incolumn=0) edit_namelist("e_we",', '.join([str(w) for w in e_we])+',') edit_namelist("e_sn",', '.join([str(s) for s in e_sn])+',') edit_namelist("prefix",int_prefix+',',incolumn=0) @@ -259,12 +262,13 @@ def intelligent_run(executable,email): # Submit jobs (edit as needed) if WRF: + os.chdir(pathtoWRF) # Soft link data netCDFs files from WPS to WRF os.system('ln -sf ' + pathtoWPS + 'met_em* ' + pathtoWRF) ##### Sync namelist.input with namelist.wps # Copy original in case of bugs - os.system('cp ' + pathtoWRF + 'namelist.input{,.python_backup}') + os.system('cp ' + pathtoWRF +'namelist.input' ' namelist.input.python_backup') print 'met_em* linked. Now amending namelist.input.' @@ -307,7 +311,7 @@ def intelligent_run(executable,email): edit_namelist_input("end_minute", (min2+', ')*domains) edit_namelist_input("end_second", (s2+', ')*domains) edit_namelist_input("interval_seconds", str(int(interval*3600))+',') - edit_namelist_input("max_dom",str(domains)+',') + edit_namelist_input("max_dom",str(domains)) edit_namelist_input("e_we", ', '.join([str(w) for w in e_we])+',') edit_namelist_input("e_sn", ', '.join([str(s) for s in e_sn])+',') edit_namelist_input("num_metgrid_levels", str(atmos_levs)+',') @@ -322,13 +326,13 @@ def intelligent_run(executable,email): print 'Namelist edited. Now submitting real.exe.' # Run real, get ID number of job # Change name of submission script if needed - p_real = subprocess.Popen('qsub -d '+pathtoWRF+' real_run.sh',cwd=pathtoWRF,shell=True,stdout=subprocess.PIPE) + p_real = subprocess.Popen('./real.exe',cwd=pathtoWRF,shell=True,stdout=subprocess.PIPE) p_real.wait() jobid = p_real.stdout.read()[:5] # Assuming first five digits = job ID. # Run WRF but wait until Real has finished without errors print 'Now submitting wrf.exe.' # Again, change name of submission script if needed - p_wrf = subprocess.Popen('qsub -d '+pathtoWRF+' wrf_run.sh -W depend=afterok:'+jobid,cwd=pathtoWRF,shell=True) + p_wrf = subprocess.Popen('./wrf.exe',cwd=pathtoWRF,shell=True,stdout=subprocess.PIPE) p_wrf.wait() print "real.exe and wrf.exe submitted. Exiting Python script."