# L. Fita, LMD-Jussieu. February 2015 ## e.g. # validation_sim.py -d X@west_east@None,Y@south_north@None,T@Time@time -D X@XLONG@longitude,Y@XLAT@latitude,T@time@time -k single-station -l 4.878773,43.915876,12. -o /home/lluis/DATA/obs/HyMeX/IOP15/sfcEnergyBalance_Avignon/OBSnetcdf.nc -s /home/lluis/PY/wrfout_d01_2012-10-18_00:00:00.tests -v HFX@H import numpy as np import os import re from optparse import OptionParser from netCDF4 import Dataset as NetCDFFile main = 'validarion_sim.py' errormsg = 'ERROR -- errror -- ERROR -- error' warnmsg = 'WARNING -- warning -- WARNING -- warning' # version version=1.0 # Filling values for floats, integer and string fillValueF = 1.e20 fillValueI = -99999 fillValueS = '---' def index_3mat(matA,matB,matC,val): """ Function to provide the coordinates of a given value inside three matrix simultaneously index_mat(matA,matB,matC,val) matA= matrix with one set of values matB= matrix with the other set of values matB= matrix with the third set of values val= triplet of values to search >>> index_mat(np.arange(27).reshape(3,3,3),np.arange(100,127).reshape(3,3,3),np.arange(200,227).reshape(3,3,3),[22,122,222]) [2 1 1] """ fname = 'index_3mat' matAshape = matA.shape matBshape = matB.shape matCshape = matC.shape for idv in range(len(matAshape)): if matAshape[idv] != matBshape[idv]: print errormsg print ' ' + fname + ': Dimension',idv,'of matrices A:',matAshape[idv], \ 'and B:',matBshape[idv],'does not coincide!!' quit(-1) if matAshape[idv] != matCshape[idv]: print errormsg print ' ' + fname + ': Dimension',idv,'of matrices A:',matAshape[idv], \ 'and C:',matCshape[idv],'does not coincide!!' quit(-1) minA = np.min(matA) maxA = np.max(matA) minB = np.min(matB) maxB = np.max(matB) minC = np.min(matC) maxC = np.max(matC) if val[0] < minA or val[0] > maxA: print warnmsg print ' ' + fname + ': first value:',val[0],'outside matA range',minA,',', \ maxA,'!!' if val[1] < minB or val[1] > maxB: print warnmsg print ' ' + fname + ': second value:',val[1],'outside matB range',minB,',', \ maxB,'!!' if val[2] < minC or val[2] > maxC: print warnmsg print ' ' + fname + ': second value:',val[2],'outside matC range',minC,',', \ maxC,'!!' dist = np.zeros(tuple(matAshape), dtype=np.float) dist = np.sqrt((matA - np.float(val[0]))**2 + (matB - np.float(val[1]))**2 + \ (matC - np.float(val[2]))**2) mindist = np.min(dist) matlist = list(dist.flatten()) ifound = matlist.index(mindist) Ndims = len(matAshape) valpos = np.zeros((Ndims), dtype=int) baseprevdims = np.zeros((Ndims), dtype=int) for dimid in range(Ndims): baseprevdims[dimid] = np.product(matAshape[dimid+1:Ndims]) if dimid == 0: alreadyplaced = 0 else: alreadyplaced = np.sum(baseprevdims[0:dimid]*valpos[0:dimid]) valpos[dimid] = int((ifound - alreadyplaced )/ baseprevdims[dimid]) return valpos def index_2mat(matA,matB,val): """ Function to provide the coordinates of a given value inside two matrix simultaneously index_mat(matA,matB,val) matA= matrix with one set of values matB= matrix with the pother set of values val= couple of values to search >>> index_mat(np.arange(27).reshape(3,3,3),np.arange(100,127).reshape(3,3,3),[22,111]) [2 1 1] """ fname = 'index_2mat' matAshape = matA.shape matBshape = matB.shape for idv in range(len(matAshape)): if matAshape[idv] != matBshape[idv]: print errormsg print ' ' + fname + ': Dimension',idv,'of matrices A:',matAshape[idv], \ 'and B:',matBshape[idv],'does not coincide!!' quit(-1) minA = np.min(matA) maxA = np.max(matA) minB = np.min(matB) maxB = np.max(matB) if val[0] < minA or val[0] > maxA: print warnmsg print ' ' + fname + ': first value:',val[0],'outside matA range',minA,',', \ maxA,'!!' if val[1] < minB or val[1] > maxB: print warnmsg print ' ' + fname + ': second value:',val[1],'outside matB range',minB,',', \ maxB,'!!' dist = np.zeros(tuple(matAshape), dtype=np.float) dist = np.sqrt((matA - np.float(val[0]))**2 + (matB - np.float(val[1]))**2) mindist = np.min(dist) matlist = list(dist.flatten()) ifound = matlist.index(mindist) Ndims = len(matAshape) valpos = np.zeros((Ndims), dtype=int) baseprevdims = np.zeros((Ndims), dtype=int) for dimid in range(Ndims): baseprevdims[dimid] = np.product(matAshape[dimid+1:Ndims]) if dimid == 0: alreadyplaced = 0 else: alreadyplaced = np.sum(baseprevdims[0:dimid]*valpos[0:dimid]) valpos[dimid] = int((ifound - alreadyplaced )/ baseprevdims[dimid]) return valpos def index_mat(mat,val): """ Function to provide the coordinates of a given value inside a matrix index_mat(mat,val) mat= matrix with values val= value to search >>> index_mat(np.arange(27).reshape(3,3,3),22) [2 1 1] """ fname = 'index_mat' matshape = mat.shape matlist = list(mat.flatten()) ifound = matlist.index(val) Ndims = len(matshape) valpos = np.zeros((Ndims), dtype=int) baseprevdims = np.zeros((Ndims), dtype=int) for dimid in range(Ndims): baseprevdims[dimid] = np.product(matshape[dimid+1:Ndims]) if dimid == 0: alreadyplaced = 0 else: alreadyplaced = np.sum(baseprevdims[0:dimid]*valpos[0:dimid]) valpos[dimid] = int((ifound - alreadyplaced )/ baseprevdims[dimid]) return valpos def coincident_CFtimes(tvalB, tunitA, tunitB): """ Function to make coincident times for two different sets of CFtimes tvalB= time values B tunitA= time units times A to which we want to make coincidence tunitB= time units times B >>> coincident_CFtimes(np.arange(10),'seconds since 1949-12-01 00:00:00', 'hours since 1949-12-01 00:00:00') [ 0. 3600. 7200. 10800. 14400. 18000. 21600. 25200. 28800. 32400.] >>> coincident_CFtimes(np.arange(10),'seconds since 1949-12-01 00:00:00', 'hours since 1979-12-01 00:00:00') [ 9.46684800e+08 9.46688400e+08 9.46692000e+08 9.46695600e+08 9.46699200e+08 9.46702800e+08 9.46706400e+08 9.46710000e+08 9.46713600e+08 9.46717200e+08] """ import datetime as dt fname = 'coincident_CFtimes' trefA = tunitA.split(' ')[2] + ' ' + tunitA.split(' ')[3] trefB = tunitB.split(' ')[2] + ' ' + tunitB.split(' ')[3] tuA = tunitA.split(' ')[0] tuB = tunitB.split(' ')[0] if tuA != tuB: if tuA == 'microseconds': if tuB == 'microseconds': tB = tvalB*1. elif tuB == 'seconds': tB = tvalB*10.e6 elif tuB == 'minutes': tB = tvalB*60.*10.e6 elif tuB == 'hours': tB = tvalB*3600.*10.e6 elif tuB == 'days': tB = tvalB*3600.*24.*10.e6 else: print errormsg print ' ' + fname + ": combination of time untis: '" + tuA + \ "' & '" + tuB + "' not ready !!" quit(-1) elif tuA == 'seconds': if tuB == 'microseconds': tB = tvalB/10.e6 elif tuB == 'seconds': tB = tvalB*1. elif tuB == 'minutes': tB = tvalB*60. elif tuB == 'hours': tB = tvalB*3600. elif tuB == 'days': tB = tvalB*3600.*24. else: print errormsg print ' ' + fname + ": combination of time untis: '" + tuA + \ "' & '" + tuB + "' not ready !!" quit(-1) elif tuA == 'minutes': if tuB == 'microseconds': tB = tvalB/(60.*10.e6) elif tuB == 'seconds': tB = tvalB/60. elif tuB == 'minutes': tB = tvalB*1. elif tuB == 'hours': tB = tvalB*60. elif tuB == 'days': tB = tvalB*60.*24. else: print errormsg print ' ' + fname + ": combination of time untis: '" + tuA + \ "' & '" + tuB + "' not ready !!" quit(-1) elif tuA == 'hours': if tuB == 'microseconds': tB = tvalB/(3600.*10.e6) elif tuB == 'seconds': tB = tvalB/3600. elif tuB == 'minutes': tB = tvalB/60. elif tuB == 'hours': tB = tvalB*1. elif tuB == 'days': tB = tvalB*24. else: print errormsg print ' ' + fname + ": combination of time untis: '" + tuA + \ "' & '" + tuB + "' not ready !!" quit(-1) elif tuA == 'days': if tuB == 'microseconds': tB = tvalB/(24.*3600.*10.e6) elif tuB == 'seconds': tB = tvalB/(24.*3600.) elif tuB == 'minutes': tB = tvalB/(24.*60.) elif tuB == 'hours': tB = tvalB/24. elif tuB == 'days': tB = tvalB*1. else: print errormsg print ' ' + fname + ": combination of time untis: '" + tuA + \ "' & '" + tuB + "' not ready !!" quit(-1) else: print errormsg print ' ' + fname + ": time untis: '" + tuA + "' not ready !!" quit(-1) else: tB = tvalB*1. if trefA != trefB: trefTA = dt.datetime.strptime(trefA, '%Y-%m-%d %H:%M:%S') trefTB = dt.datetime.strptime(trefB, '%Y-%m-%d %H:%M:%S') difft = trefTB - trefTA diffv = difft.days*24.*3600.*10.e6 + difft.seconds*10.e6 + difft.microseconds print ' ' + fname + ': different reference refA:',trefTA,'refB',trefTB print ' difference:',difft,':',diffv,'microseconds' if tuA == 'microseconds': tB = tB + diffv elif tuA == 'seconds': tB = tB + diffv/10.e6 elif tuA == 'minutes': tB = tB + diffv/(60.*10.e6) elif tuA == 'hours': tB = tB + diffv/(3600.*10.e6) elif tuA == 'dayss': tB = tB + diffv/(24.*3600.*10.e6) else: print errormsg print ' ' + fname + ": time untis: '" + tuA + "' not ready !!" quit(-1) return tB ####### ###### ##### #### ### ## # strCFt="Refdate,tunits (CF reference date [YYYY][MM][DD][HH][MI][SS] format and " + \ " and time units: 'weeks', 'days', 'hours', 'miuntes', 'seconds')" kindobs=['multi-points', 'single-station', 'trajectory'] strkObs="kind of observations; 'multi-points': multiple individual punctual obs " + \ "(e.g., lightning strikes), 'single-station': single station on a fixed position,"+\ "'trajectory': following a trajectory" #sumc,[constant]: add [constant] to variables values #subc,[constant]: substract [constant] to variables values #mulc,[constant]: multipy by [constant] to variables values #divc,[constant]: divide by [constant] to variables values parser = OptionParser() parser.add_option("-d", "--dimensions", dest="dims", help="[DIM]@[simdim]@[obsdim] ',' list of couples of dimensions names from each source ([DIM]='X','Y','Z','T'; None, no value)", metavar="VALUES") parser.add_option("-D", "--vardimensions", dest="vardims", help="[DIM]@[simvardim]@[obsvardim] ',' list of couples of variables names with dimensions values from each source ([DIM]='X','Y','Z','T'; None, no value)", metavar="VALUES") parser.add_option("-k", "--kindObs", dest="obskind", type='choice', choices=kindobs, help=strkObs, metavar="FILE") parser.add_option("-l", "--stationLocation", dest="stloc", help="longitude, latitude and height of the station (only for 'single-station')", metavar="FILE") parser.add_option("-o", "--observation", dest="fobs", help="observations file to validate", metavar="FILE") parser.add_option("-s", "--simulation", dest="fsim", help="simulation file to validate", metavar="FILE") parser.add_option("-v", "--variables", dest="vars", help="[simvar]@[obsvar]@[[oper]@[val]] ',' list of couples of variables to validate and if necessary operation and value", metavar="VALUES") (opts, args) = parser.parse_args() ####### ####### ## MAIN ####### ofile='validation_sim.nc' if opts.dims is None: print errormsg print ' ' + main + ': No list of dimensions are provided!!' print ' a ',' list of values X@[dimxsim]@[dimxobs],...,T@[dimtsim]@[dimtobs]'+\ ' is needed' quit(-1) else: print main +': couple of dimensions _______' dims = {} ds = opts.dims.split(',') for d in ds: dsecs = d.split('@') if len(dsecs) != 3: print errormsg print ' ' + main + ': wrong number of values in:',dsecs,' 3 are needed !!' print ' [DIM]@[dimnsim]@[dimnobs]' quit(-1) dims[dsecs[0]] = [dsecs[1], dsecs[2]] print dsecs[0],':',dsecs[1],',',dsecs[2] if opts.vardims is None: print errormsg print ' ' + main + ': No list of variables with dimension values are provided!!' print ' a ',' list of values X@[vardimxsim]@[vardimxobs],...,T@' + \ '[vardimtsim]@[vardimtobs] is needed' quit(-1) else: print main +': couple of variable dimensions _______' vardims = {} ds = opts.vardims.split(',') for d in ds: dsecs = d.split('@') if len(dsecs) != 3: print errormsg print ' ' + main + ': wrong number of values in:',dsecs,' 3 are needed !!' print ' [DIM]@[vardimnsim]@[vardimnobs]' quit(-1) vardims[dsecs[0]] = [dsecs[1], dsecs[2]] print dsecs[0],':',dsecs[1],',',dsecs[2] if opts.obskind is None: print errormsg print ' ' + main + ': No kind of observations provided !!' quit(-1) else: obskind = opts.obskind if obskind == 'single-station': if opts.stloc is None: print errormsg print ' ' + main + ': No station location provided !!' quit(-1) else: stationdesc = [np.float(opts.stloc.split(',')[0]), \ np.float(opts.stloc.split(',')[1]), np.float(opts.stloc.split(',')[2])] if opts.fobs is None: print errormsg print ' ' + main + ': No observations file is provided!!' quit(-1) else: if not os.path.isfile(opts.fobs): print errormsg print ' ' + main + ": observations file '" + opts.fobs + "' does not exist !!" quit(-1) if opts.fsim is None: print errormsg print ' ' + main + ': No simulation file is provided!!' quit(-1) else: if not os.path.isfile(opts.fsim): print errormsg print ' ' + main + ": simulation file '" + opts.fsim + "' does not exist !!" quit(-1) if opts.vars is None: print errormsg print ' ' + main + ': No list of couples of variables is provided!!' print ' a ',' list of values [varsim]@[varobs],... is needed' quit(-1) else: valvars = [] vs = opts.dims.split(',') for v in vs: vsecs = v.split('@') if len(dsecs) < 2: print errormsg print ' ' + main + ': wrong number of values in:',vsecs, \ ' at least 2 are needed !!' print ' [varsim]@[varobs]@[[oper][val]]' quit(-1) valvars.append(vsecs) # Openning observations trajectory ## oobs = NetCDFFile(opts.fobs, 'r') valdimobs = {} for dn in dims: print dn,':',dims[dn] if dims[dn][1] != 'None': if not oobs.dimensions.has_key(dims[dn][1]): print errormsg print ' ' + main + ": observations file does not have dimension '" + \ dims[dn][1] + "' !!" quit(-1) if vardims[dn][1] != 'None': if not oobs.variables.has_key(vardims[dn][1]): print errormsg print ' ' + main + ": observations file does not have varibale " + \ "dimension '" + vardims[dn][1] + "' !!" quit(-1) valdimobs[dn] = oobs.variables[vardims[dn][1]][:] else: if dn == 'X': valdimobs[dn] = stationdesc[0] elif dn == 'Y': valdimobs[dn] = stationdesc[1] elif dn == 'Z': valdimobs[dn] = stationdesc[2] osim = NetCDFFile(opts.fsim, 'r') valdimsim = {} for dn in dims: if not osim.dimensions.has_key(dims[dn][0]): print errormsg print ' ' + main + ": simulation file does not have dimension '" + \ dims[dn][0] + "' !!" quit(-1) if not osim.variables.has_key(vardims[dn][0]): print errormsg print ' ' + main + ": simulation file does not have varibale dimension '" + \ vardims[dn][0] + "' !!" quit(-1) valdimsim[dn] = osim.variables[vardims[dn][0]][:] # General characteristics dimtobs = len(valdimobs['T']) dimtsim = len(valdimsim['T']) print main +': observational time-steps:',dimtobs,'simulation:',dimtsim if obskind == 'multi-points': trajpos = np.zeros((2,dimt),dtype=int) for it in dimtobs: trajpos[:,it] = index_2mat(valdimsim['X'],valdimsim['Y'], \ [valdimobs['X'][it],valdimobss['Y'][it]]) elif obskind == 'single-station': stsimpos = index_2mat(valdimsim['Y'],valdimsim['X'],[valdimobs['Y'], \ valdimobs['X']]) print main + ': station point in simulation:', stsimpos print ' station position:',valdimobs['X'],',',valdimobs['Y'] print ' simulation coord.:',valdimsim['X'][tuple(stsimpos)],',', \ valdimsim['Y'][tuple(stsimpos)] elif obskind == 'trajectory': if dims.has_key('Z'): trajpos = np.zeros((3,dimt),dtype=int) for it in dimtobs: trajpos[0:1,it] = index_2mat(valdimsim['X'],valdimsim['Y'], \ [valdimobs['X'][it],valdimobss['Y'][it]]) trajpos[2,it] = index_mat(valdimsim['Z'],valdimobs['Z'][it]) else: trajpos = np.zeros((2,dimt),dtype=int) for it in dimtobs: trajpos[:,it] = index_2mat(valdimsim['X'],valdimsim['Y'], \ [valdimobs['X'][it],valdimobss['Y'][it]]) # Getting times tobj = oobs.variables[vardims['T'][1]] obstunits = tobj.getncattr('units') tobj = osim.variables[vardims['T'][0]] simtunits = tobj.getncattr('units') simobstimes = coincident_CFtimes(valdimsim['T'], obstunits, simtunits) print 'Lluis:',simobstimes