class temperature(PseudoNetCDFFile): """ temperature provides a PseudoNetCDF interface for CAMx temperature files. Where possible, the inteface follows IOAPI conventions (see www.baronams.com). ex: >>> temperature_path = 'camx_temperature.bin' >>> rows,cols = 65,83 >>> temperaturefile = temperature(temperature_path,rows,cols) >>> temperaturefile.variables.keys() ['TFLAG', 'AIRTEMP', 'SURFTEMP'] >>> tflag = temperaturefile.variables['TFLAG'] >>> tflag.dimensions ('TSTEP', 'VAR', 'DATE-TIME') >>> tflag[0,0,:] array([2005185, 0]) >>> tflag[-1,0,:] array([2005185, 240000]) >>> v = temperaturefile.variables['SURFTEMP'] >>> v.dimensions ('TSTEP', 'ROW', 'COL') >>> v.shape (25, 65, 83) >>> v = temperaturefile.variables['AIRTEMP'] >>> v.dimensions ('TSTEP', 'LAY', 'ROW', 'COL') >>> v.shape (25, 28, 65, 83) >>> temperaturefile.dimensions {'TSTEP': 25, 'LAY': 28, 'ROW': 65, 'COL': 83} """ id_fmt = 'fi' data_fmt = 'f' def __init__(self, rf, rows=None, cols=None): self.rffile = OpenRecordFile(rf) self.id_size = struct.calcsize(self.id_fmt) self.__readheader() self.__gettimestep() if rows is None and cols is None: rows = self.cell_count cols = 1 elif rows is None: rows = self.cell_count / cols elif cols is None: cols = self.cell_count / rows else: if cols * rows != self.cell_count: raise ValueError( ("The product of cols (%d) and rows (%d) " + "must equal cells (%d)") % (cols, rows, self.cell_count)) self.createDimension('TSTEP', self.time_step_count) self.createDimension('COL', cols) self.createDimension('ROW', rows) self.createDimension('LAY', self.nlayers) self.createDimension('SURF', 1) self.variables = PseudoNetCDFVariables(self.__var_get, ['AIRTEMP', 'SURFTEMP']) def __var_get(self, key): def decor(k): return dict(units='K', var_desc=k.ljust(16), long_name=k.ljust(16)) def constr(k): return self.__variables(k) values = constr(key) dims = { 'AIRTEMP': ('TSTEP', 'LAY', 'ROW', 'COL'), 'SURFTEMP': ('TSTEP', 'SURF', 'ROW', 'COL') }[key] var = self.createVariable(key, 'f', dims) var[:] = values for k, v in decor(key).items(): setattr(var, k, v) return var def __readheader(self): self.data_start_byte = 0 self.rffile._newrecord(0) self.area_size = self.rffile.record_size self.area_count = (self.area_size - self.id_size) // struct.calcsize( self.data_fmt) self.area_padded_size = self.area_size + 8 self.area_fmt = self.id_fmt + self.data_fmt * (self.area_count) self.start_time, self.start_date = self.rffile.read(self.id_fmt) self.record_size = self.rffile.record_size self.padded_size = self.record_size + 8 self.cell_count = (self.record_size - self.id_size) // struct.calcsize( self.data_fmt) self.record_fmt = self.id_fmt + self.data_fmt * (self.cell_count) def __gettimestep(self): d, t = date, time = self.start_date, self.start_time self.nlayers = -1 while (d, t) == (date, time): self.nlayers += 1 t, d = self.rffile.read(self.id_fmt) self.time_step = timediff((self.start_date, self.start_time), (d, t)) self.rffile.infile.seek(0, 2) self.rffile.previous() self.end_time, self.end_date = self.rffile.read(self.id_fmt) self.time_step_count = int( timediff((self.start_date, self.start_time), (self.end_date, self.end_time)) // self.time_step) + 1 def __variables(self, k): if k == 'SURFTEMP': out = zeros( (len(self.dimensions['TSTEP']), 1, len( self.dimensions['ROW']), len(self.dimensions['COL'])), 'f') vars = self.__surfmaps() elif k == 'AIRTEMP': out = zeros( (len(self.dimensions['TSTEP']), len(self.dimensions['LAY']), len(self.dimensions['ROW']), len(self.dimensions['COL'])), 'f') vars = self.__airmaps() for i, v in enumerate(vars): out[i, ...] = v return out def __surfpos(self): pos = self.data_start_byte + 12 inc = self.area_padded_size + self.padded_size * self.nlayers self.rffile.infile.seek(0, 2) rflen = self.rffile.tell() while pos < rflen: yield pos pos += inc raise StopIteration def __surfmaps(self): for pos in self.__surfpos(): tmpmm = memmap(self.rffile.infile.name, '>f', 'r', pos, (self.area_count, )) newshape = [ len(self.dimensions['ROW']), len(self.dimensions['COL']) ] yield tmpmm.reshape(*newshape) def __airpos(self): pos = self.area_padded_size + self.data_start_byte inc = self.area_padded_size + self.padded_size * self.nlayers self.rffile.infile.seek(0, 2) rflen = self.rffile.tell() while pos < rflen: yield pos pos += inc raise StopIteration def __airmaps(self): for pos in self.__airpos(): firstshape = ((self.cell_count + 4) * self.nlayers, ) tmpmm = memmap(self.rffile.infile.name, '>f', 'r', pos, firstshape) newshape1 = [self.nlayers, self.cell_count + 4] tmpmm = tmpmm.reshape(*newshape1)[:, 3:-1] newshape2 = [ len(self.dimensions['LAY']), len(self.dimensions['ROW']), len(self.dimensions['COL']) ] yield tmpmm.reshape(*newshape2) def timerange(self): return timerange( (self.start_date, self.start_time), timeadd((self.end_date, self.end_time), (0, self.time_step), (2400, 24)[int(self.time_step % 2)]), self.time_step, (2400, 24)[int(self.time_step % 2)])
class ipr(PseudoNetCDFFile): """ ipr provides a PseudoNetCDF interface for CAMx ipr files. Where possible, the inteface follows IOAPI conventions (see www.baronams.com). ex: >>> ipr_path = 'camx_ipr.bin' >>> iprfile = ipr(ipr_path) >>> iprfile.variables.keys() ['TFLAG', 'SPAD_O3', 'DATE_O3', 'TIME_O3', 'SPC_O3', 'PAGRID_O3', 'NEST_O3', 'I_O3', 'J_O3', 'K_O3', 'INIT_O3', 'CHEM_O3', 'EMIS_O3', 'PTEMIS_O3', 'PIG_O3', 'WADV_O3', 'EADV_O3', 'SADV_O3', 'NADV_O3', 'BADV_O3', 'TADV_O3', 'DIL_O3', 'WDIF_O3', 'EDIF_O3', 'SDIF_O3', 'NDIF_O3', 'BDIF_O3', 'TDIF_O3', 'DDEP_O3', 'WDEP_O3', 'INORGACHEM_O3', 'ORGACHEM_O3', 'AQACHEM_O3', 'FCONC_O3', 'UCNV_O3', 'AVOL_O3', 'EPAD_O3'] >>> v = iprfile.variables['CHEM_O3'] >>> tflag = iprfile.variables['TFLAG'] >>> tflag.dimensions ('TSTEP', 'VAR', 'DATE-TIME') >>> tflag[0,0,:] array([2005185, 0]) >>> tflag[-1,0,:] array([2005185, 240000]) >>> v.dimensions ('TSTEP', 'LAY', 'ROW', 'COL') >>> v.shape (25, 28, 65, 83) >>> iprfile.dimensions {'TSTEP': 25, 'LAY': 28, 'ROW': 65, 'COL': 83} """ id_fmt="if10s5i" dt_fmt="if" data_fmt="f" def __init__(self,rf,multi=False, **props): """ Initialization included reading the header and learning about the format. see __readheader and __gettimestep() for more info Keywords (i.e., props) for projection: P_ALP, P_BET, P_GAM, XCENT, YCENT, XORIG, YORIG, XCELL, YCELL """ self.__rffile=OpenRecordFile(rf) self.__readheader() self.__ipr_record_type={ 24: dtype( dict( names=['SPAD', 'DATE', 'TIME', 'SPC', 'PAGRID', 'NEST', 'I', 'J', 'K', 'INIT', 'CHEM', 'EMIS', 'PTEMIS', 'PIG', 'WADV', 'EADV', 'SADV', 'NADV', 'BADV', 'TADV', 'DIL', 'WDIF', 'EDIF', 'SDIF', 'NDIF', 'BDIF', 'TDIF', 'DDEP', 'WDEP', 'AERCHEM', 'FCONC', 'UCNV', 'AVOL', 'EPAD'], formats=['>i', '>i', '>f', '>S10', '>i', '>i', '>i', '>i', '>i', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>i'])), 26: dtype( dict( names=['SPAD', 'DATE', 'TIME', 'SPC', 'PAGRID', 'NEST', 'I', 'J', 'K', 'INIT', 'CHEM', 'EMIS', 'PTEMIS', 'PIG', 'WADV', 'EADV', 'SADV', 'NADV', 'BADV', 'TADV', 'DIL', 'WDIF', 'EDIF', 'SDIF', 'NDIF', 'BDIF', 'TDIF', 'DDEP', 'WDEP', 'INORGACHEM', 'ORGACHEM', 'AQACHEM', 'FCONC', 'UCNV', 'AVOL', 'EPAD'], formats=['>i', '>i', '>f', '>S10', '>i', '>i', '>i', '>i', '>i', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>i'])) }[len(self.prcnames)] prcs=['SPAD', 'DATE', 'TIME', 'PAGRID', 'NEST', 'I', 'J', 'K', 'INIT', 'CHEM', 'EMIS', 'PTEMIS', 'PIG', 'WADV', 'EADV', 'SADV', 'NADV', 'BADV', 'TADV', 'DIL', 'WDIF', 'EDIF', 'SDIF', 'NDIF', 'BDIF', 'TDIF', 'DDEP', 'WDEP']+{24: ['AERCHEM'], 26: ['INORGACHEM', 'ORGACHEM', 'AQACHEM']}[len(self.prcnames)]+['FCONC', 'UCNV', 'AVOL', 'EPAD'] varkeys=['_'.join(i) for i in cartesian(prcs,self.spcnames)] varkeys+=['SPAD','DATE','TIME','PAGRID','NEST','I','J','K','TFLAG'] self.groups = {} NSTEPS = len([i_ for i_ in self.timerange()]) NVARS = len(varkeys) self.createDimension('VAR', NVARS) self.createDimension('DATE-TIME', 2) self.createDimension('TSTEP', NSTEPS) padatatype = [] pavarkeys = [] for di, domain in enumerate(self.padomains): dk = 'PA%02d' % di prefix = dk + '_' grp = self.groups[dk] = PseudoNetCDFFile() pavarkeys.extend([prefix + k for k in varkeys]) grp.createDimension('VAR', NVARS) grp.createDimension('DATE-TIME', 2) grp.createDimension('TSTEP', NSTEPS) grp.createDimension('COL', domain['iend'] - domain['istart'] + 1) grp.createDimension('ROW', domain['jend'] - domain['jstart'] + 1) grp.createDimension('LAY', domain['tlay'] - domain['blay'] + 1) padatatype.append((dk, self.__ipr_record_type, (len(grp.dimensions['ROW']), len(grp.dimensions['COL']), len(grp.dimensions['LAY'])))) if len(self.padomains) == 1: self.createDimension('COL', domain['iend']-domain['istart']+1) self.createDimension('ROW', domain['jend']-domain['jstart']+1) self.createDimension('LAY', domain['tlay']-domain['blay']+1) exec("""def varget(k): return self._ipr__variables('%s', k)""" % dk, dict(self = self), locals()) if len(self.padomains) == 1: self.variables = PseudoNetCDFVariables(varget,varkeys) else: grp.variables = PseudoNetCDFVariables(varget,varkeys) self.__memmaps=memmap(self.__rffile.infile.name,dtype(padatatype),'r',self.data_start_byte).reshape(NSTEPS, len(self.spcnames)) for k, v in props.items(): setattr(self, k, v) try: add_cf_from_ioapi(self) except: pass def __del__(self): try: self.__memmaps.close() del self.__memmaps except: pass def __decorator(self,name,pncfv): spc = name.split('_')[-1] prc = name.split('_')[0] # IPR units are consistent with 'IPR' if prc == 'UCNV': units = 'm**3/mol' elif prc == 'AVOL': units = 'm**3' else: units = get_uamiv_units('IPR', spc) decor=lambda k: dict(units=units, var_desc=k.ljust(16), long_name=k.ljust(16)) for k,v in decor(name).items(): setattr(pncfv,k,v) return pncfv def __variables(self,pk, proc_spc): if proc_spc in self.__ipr_record_type.names: proc=proc_spc proc_spc=proc_spc+'_'+self.spcnames[0] return PseudoNetCDFVariable(self,proc_spc,'f',('TSTEP','LAY','ROW','COL'),values=self.__memmaps[pk][:,0,:,:,:][proc].swapaxes(1, 3).swapaxes(2, 3)) if proc_spc=='TFLAG': thisdate = self.__memmaps[pk][:,0,:,:,:]['DATE'].swapaxes(1, 3).swapaxes(2, 3)[..., 0, 0, 0] thistime = self.__memmaps[pk][:,0,:,:,:]['TIME'].swapaxes(1, 3).swapaxes(2, 3)[..., 0, 0, 0] return ConvertCAMxTime(thisdate, thistime, len(self.groups[pk].dimensions['VAR'])) for k in self.__ipr_record_type.names: proc=proc_spc[:len(k)] spc=proc_spc[len(k)+1:] if proc==k and spc in self.spcnames: spc=self.spcnames.index(spc) dvals = self.__memmaps[pk][:,spc][proc].swapaxes(1, 3).swapaxes(2, 3) return self.__decorator(proc_spc,PseudoNetCDFVariable(self,proc_spc,'f',('TSTEP','LAY','ROW','COL'),values=dvals)) raise KeyError("Bad!") def __readheader(self): """ __readheader reads the header section of the ipr file it initializes each header field (see CAMx Users Manual for a list) as properties of the ipr class """ self.runmessage=self.__rffile.read("80s") self.start_date,self.start_time,self.end_date,self.end_time=self.__rffile.read("ifif") self.grids=[] for grid in range(self.__rffile.read("i")[-1]): self.grids.append( dict( zip( ['orgx','orgy','ncol','nrow','xsize','ysize'], self.__rffile.read("iiiiii") ) ) ) self.spcnames = [] for spc in range(self.__rffile.read("i")[-1]): self.spcnames.append(self.__rffile.read("10s")[-1].strip()) self.nspec=len(self.spcnames) self.padomains=[] for padomain in range(self.__rffile.read("i")[-1]): self.padomains.append( dict( zip( ['grid','istart','iend','jstart','jend','blay','tlay'], self.__rffile.read("iiiiiii") ) ) ) self.activedomain=self.padomains[0] self.prcnames=[] for i in range(self.__rffile.read('i')[-1]): self.prcnames.append(self.__rffile.read('25s')[-1].strip()) self.data_start_byte=self.__rffile.record_start self.record_fmt=self.id_fmt + str(len(self.prcnames)) + self.data_fmt self.record_size=self.__rffile.record_size self.SDATE,self.STIME,dummy,dummy,dummy,dummy,dummy,dummy=self.__rffile.read(self.id_fmt) self.__rffile.previous() self.TSTEP=100. self.padded_size=self.record_size+8 domain=self.padomains[0] self.records_per_time=self.nspec*(domain['iend']-domain['istart']+1)*(domain['jend']-domain['jstart']+1)*(domain['tlay']-domain['blay']+1) self.time_data_block=self.padded_size*self.records_per_time self.time_step=100. def timerange(self): return timerange((self.start_date,self.start_time+self.time_step),timeadd((self.end_date,self.end_time),(0,self.time_step)),self.time_step)
class ipr(PseudoNetCDFFile): """ ipr provides a PseudoNetCDF interface for CAMx ipr files. Where possible, the inteface follows IOAPI conventions (see www.baronams.com). ex: >>> ipr_path = 'camx_ipr.bin' >>> iprfile = ipr(ipr_path) >>> iprfile.variables.keys() ['TFLAG', 'SPAD_O3', 'DATE_O3', 'TIME_O3', 'SPC_O3', 'PAGRID_O3', 'NEST_O3', 'I_O3', 'J_O3', 'K_O3', 'INIT_O3', 'CHEM_O3', 'EMIS_O3', 'PTEMIS_O3', 'PIG_O3', 'WADV_O3', 'EADV_O3', 'SADV_O3', 'NADV_O3', 'BADV_O3', 'TADV_O3', 'DIL_O3', 'WDIF_O3', 'EDIF_O3', 'SDIF_O3', 'NDIF_O3', 'BDIF_O3', 'TDIF_O3', 'DDEP_O3', 'WDEP_O3', 'INORGACHEM_O3', 'ORGACHEM_O3', 'AQACHEM_O3', 'FCONC_O3', 'UCNV_O3', 'AVOL_O3', 'EPAD_O3'] >>> v = iprfile.variables['CHEM_O3'] >>> tflag = iprfile.variables['TFLAG'] >>> tflag.dimensions ('TSTEP', 'VAR', 'DATE-TIME') >>> tflag[0,0,:] array([2005185, 0]) >>> tflag[-1,0,:] array([2005185, 240000]) >>> v.dimensions ('TSTEP', 'LAY', 'ROW', 'COL') >>> v.shape (25, 28, 65, 83) >>> iprfile.dimensions {'TSTEP': 25, 'LAY': 28, 'ROW': 65, 'COL': 83} """ id_fmt = "if10s5i" dt_fmt = "if" data_fmt = "f" def __init__(self, rf, multi=False, **props): """ Initialization included reading the header and learning about the format. see __readheader and __gettimestep() for more info Keywords (i.e., props) for projection: P_ALP, P_BET, P_GAM, XCENT, YCENT, XORIG, YORIG, XCELL, YCELL """ self.__rffile = OpenRecordFile(rf) self.__readheader() self.__ipr_record_type = { 24: dtype( dict(names=[ 'SPAD', 'DATE', 'TIME', 'SPC', 'PAGRID', 'NEST', 'I', 'J', 'K', 'INIT', 'CHEM', 'EMIS', 'PTEMIS', 'PIG', 'WADV', 'EADV', 'SADV', 'NADV', 'BADV', 'TADV', 'DIL', 'WDIF', 'EDIF', 'SDIF', 'NDIF', 'BDIF', 'TDIF', 'DDEP', 'WDEP', 'AERCHEM', 'FCONC', 'UCNV', 'AVOL', 'EPAD' ], formats=[ '>i', '>i', '>f', '>S10', '>i', '>i', '>i', '>i', '>i', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>i' ])), 26: dtype( dict(names=[ 'SPAD', 'DATE', 'TIME', 'SPC', 'PAGRID', 'NEST', 'I', 'J', 'K', 'INIT', 'CHEM', 'EMIS', 'PTEMIS', 'PIG', 'WADV', 'EADV', 'SADV', 'NADV', 'BADV', 'TADV', 'DIL', 'WDIF', 'EDIF', 'SDIF', 'NDIF', 'BDIF', 'TDIF', 'DDEP', 'WDEP', 'INORGACHEM', 'ORGACHEM', 'AQACHEM', 'FCONC', 'UCNV', 'AVOL', 'EPAD' ], formats=[ '>i', '>i', '>f', '>S10', '>i', '>i', '>i', '>i', '>i', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>f', '>i' ])) }[len(self.prcnames)] prcs = [ 'SPAD', 'DATE', 'TIME', 'PAGRID', 'NEST', 'I', 'J', 'K', 'INIT', 'CHEM', 'EMIS', 'PTEMIS', 'PIG', 'WADV', 'EADV', 'SADV', 'NADV', 'BADV', 'TADV', 'DIL', 'WDIF', 'EDIF', 'SDIF', 'NDIF', 'BDIF', 'TDIF', 'DDEP', 'WDEP' ] + { 24: ['AERCHEM'], 26: ['INORGACHEM', 'ORGACHEM', 'AQACHEM'] }[len(self.prcnames)] + ['FCONC', 'UCNV', 'AVOL', 'EPAD'] varkeys = ['_'.join(i) for i in cartesian(prcs, self.spcnames)] varkeys += [ 'SPAD', 'DATE', 'TIME', 'PAGRID', 'NEST', 'I', 'J', 'K', 'TFLAG' ] self.groups = {} NSTEPS = len([i_ for i_ in self.timerange()]) NVARS = len(varkeys) self.createDimension('VAR', NVARS) self.createDimension('DATE-TIME', 2) self.createDimension('TSTEP', NSTEPS) padatatype = [] pavarkeys = [] for di, domain in enumerate(self.padomains): dk = 'PA%02d' % di prefix = dk + '_' grp = self.groups[dk] = PseudoNetCDFFile() pavarkeys.extend([prefix + k for k in varkeys]) grp.createDimension('VAR', NVARS) grp.createDimension('DATE-TIME', 2) grp.createDimension('TSTEP', NSTEPS) grp.createDimension('COL', domain['iend'] - domain['istart'] + 1) grp.createDimension('ROW', domain['jend'] - domain['jstart'] + 1) grp.createDimension('LAY', domain['tlay'] - domain['blay'] + 1) padatatype.append( (dk, self.__ipr_record_type, (len(grp.dimensions['ROW']), len(grp.dimensions['COL']), len(grp.dimensions['LAY'])))) if len(self.padomains) == 1: self.createDimension('COL', domain['iend'] - domain['istart'] + 1) self.createDimension('ROW', domain['jend'] - domain['jstart'] + 1) self.createDimension('LAY', domain['tlay'] - domain['blay'] + 1) exec( """def varget(k): return self._ipr__variables('%s', k)""" % dk, dict(self=self), locals()) if len(self.padomains) == 1: self.variables = PseudoNetCDFVariables(varget, varkeys) else: grp.variables = PseudoNetCDFVariables(varget, varkeys) self.__memmaps = memmap(self.__rffile.infile.name, dtype(padatatype), 'r', self.data_start_byte).reshape( NSTEPS, len(self.spcnames)) for k, v in props.items(): setattr(self, k, v) try: add_cf_from_ioapi(self) except: pass def __del__(self): try: self.__memmaps.close() del self.__memmaps except: pass def __decorator(self, name, pncfv): spc = name.split('_')[-1] prc = name.split('_')[0] # IPR units are consistent with 'IPR' if prc == 'UCNV': units = 'm**3/mol' elif prc == 'AVOL': units = 'm**3' else: units = get_uamiv_units('IPR', spc) decor = lambda k: dict( units=units, var_desc=k.ljust(16), long_name=k.ljust(16)) for k, v in decor(name).items(): setattr(pncfv, k, v) return pncfv def __variables(self, pk, proc_spc): if proc_spc in self.__ipr_record_type.names: proc = proc_spc proc_spc = proc_spc + '_' + self.spcnames[0] return PseudoNetCDFVariable( self, proc_spc, 'f', ('TSTEP', 'LAY', 'ROW', 'COL'), values=self.__memmaps[pk][:, 0, :, :, :][proc].swapaxes( 1, 3).swapaxes(2, 3)) if proc_spc == 'TFLAG': thisdate = self.__memmaps[pk][:, 0, :, :, :]['DATE'].swapaxes( 1, 3).swapaxes(2, 3)[..., 0, 0, 0] thistime = self.__memmaps[pk][:, 0, :, :, :]['TIME'].swapaxes( 1, 3).swapaxes(2, 3)[..., 0, 0, 0] return ConvertCAMxTime(thisdate, thistime, len(self.groups[pk].dimensions['VAR'])) for k in self.__ipr_record_type.names: proc = proc_spc[:len(k)] spc = proc_spc[len(k) + 1:] if proc == k and spc in self.spcnames: spc = self.spcnames.index(spc) dvals = self.__memmaps[pk][:, spc][proc].swapaxes(1, 3).swapaxes( 2, 3) return self.__decorator( proc_spc, PseudoNetCDFVariable(self, proc_spc, 'f', ('TSTEP', 'LAY', 'ROW', 'COL'), values=dvals)) raise KeyError("Bad!") def __readheader(self): """ __readheader reads the header section of the ipr file it initializes each header field (see CAMx Users Manual for a list) as properties of the ipr class """ self.runmessage = self.__rffile.read("80s") self.start_date, self.start_time, self.end_date, self.end_time = self.__rffile.read( "ifif") self.grids = [] for grid in range(self.__rffile.read("i")[-1]): self.grids.append( dict( zip(['orgx', 'orgy', 'ncol', 'nrow', 'xsize', 'ysize'], self.__rffile.read("iiiiii")))) self.spcnames = [] for spc in range(self.__rffile.read("i")[-1]): self.spcnames.append(self.__rffile.read("10s")[-1].strip()) self.nspec = len(self.spcnames) self.padomains = [] for padomain in range(self.__rffile.read("i")[-1]): self.padomains.append( dict( zip([ 'grid', 'istart', 'iend', 'jstart', 'jend', 'blay', 'tlay' ], self.__rffile.read("iiiiiii")))) self.activedomain = self.padomains[0] self.prcnames = [] for i in range(self.__rffile.read('i')[-1]): self.prcnames.append(self.__rffile.read('25s')[-1].strip()) self.data_start_byte = self.__rffile.record_start self.record_fmt = self.id_fmt + str(len(self.prcnames)) + self.data_fmt self.record_size = self.__rffile.record_size self.SDATE, self.STIME, dummy, dummy, dummy, dummy, dummy, dummy = self.__rffile.read( self.id_fmt) self.__rffile.previous() self.TSTEP = 100. self.padded_size = self.record_size + 8 domain = self.padomains[0] self.records_per_time = self.nspec * ( domain['iend'] - domain['istart'] + 1) * (domain['jend'] - domain['jstart'] + 1) * (domain['tlay'] - domain['blay'] + 1) self.time_data_block = self.padded_size * self.records_per_time self.time_step = 100. def timerange(self): return timerange((self.start_date, self.start_time + self.time_step), timeadd((self.end_date, self.end_time), (0, self.time_step)), self.time_step)
class temperature(PseudoNetCDFFile): """ temperature provides a PseudoNetCDF interface for CAMx temperature files. Where possible, the inteface follows IOAPI conventions (see www.baronams.com). ex: >>> temperature_path = 'camx_temperature.bin' >>> rows,cols = 65,83 >>> temperaturefile = temperature(temperature_path,rows,cols) >>> temperaturefile.variables.keys() ['TFLAG', 'AIRTEMP', 'SURFTEMP'] >>> tflag = temperaturefile.variables['TFLAG'] >>> tflag.dimensions ('TSTEP', 'VAR', 'DATE-TIME') >>> tflag[0,0,:] array([2005185, 0]) >>> tflag[-1,0,:] array([2005185, 240000]) >>> v = temperaturefile.variables['SURFTEMP'] >>> v.dimensions ('TSTEP', 'ROW', 'COL') >>> v.shape (25, 65, 83) >>> v = temperaturefile.variables['AIRTEMP'] >>> v.dimensions ('TSTEP', 'LAY', 'ROW', 'COL') >>> v.shape (25, 28, 65, 83) >>> temperaturefile.dimensions {'TSTEP': 25, 'LAY': 28, 'ROW': 65, 'COL': 83} """ id_fmt='fi' data_fmt='f' def __init__(self,rf,rows=None,cols=None): self.rffile=OpenRecordFile(rf) self.id_size=struct.calcsize(self.id_fmt) self.__readheader() self.__gettimestep() if rows==None and cols==None: rows=self.cell_count cols=1 elif rows==None: rows=self.cell_count/cols elif cols==None: cols=self.cell_count/rows else: if cols*rows!=self.cell_count: raise ValueError("The product of cols (%d) and rows (%d) must equal cells (%d)" % (cols,rows,self.cell_count)) self.createDimension('TSTEP', self.time_step_count) self.createDimension('COL', cols) self.createDimension('ROW', rows) self.createDimension('LAY', self.nlayers) self.createDimension('SURF', 1) self.variables=PseudoNetCDFVariables(self.__var_get,['AIRTEMP','SURFTEMP']) def __var_get(self,key): decor=lambda k: dict(units='K',var_desc=k.ljust(16),long_name=k.ljust(16)) constr=lambda k: self.__variables(k) values=constr(key) dims={'AIRTEMP':('TSTEP','LAY','ROW','COL'),'SURFTEMP':('TSTEP','SURF','ROW','COL')}[key] var=self.createVariable(key,'f',dims) var[:] = values for k,v in decor(key).items(): setattr(var,k,v) return var def __readheader(self): self.data_start_byte=0 self.rffile._newrecord(0) self.area_size=self.rffile.record_size self.area_count=(self.area_size-self.id_size)/struct.calcsize(self.data_fmt) self.area_padded_size=self.area_size+8 self.area_fmt=self.id_fmt+self.data_fmt*(self.area_count) self.start_time,self.start_date=self.rffile.read(self.id_fmt) self.record_size=self.rffile.record_size self.padded_size=self.record_size+8 self.cell_count=(self.record_size-self.id_size)/struct.calcsize(self.data_fmt) self.record_fmt=self.id_fmt+self.data_fmt*(self.cell_count) def __gettimestep(self): d,t=date,time=self.start_date,self.start_time self.nlayers=-1 while (d,t)==(date,time): self.nlayers+=1 t,d=self.rffile.read(self.id_fmt) self.time_step=timediff((self.start_date,self.start_time),(d,t)) self.rffile.infile.seek(0,2) self.rffile.previous() self.end_time,self.end_date=self.rffile.read(self.id_fmt) self.time_step_count=int(timediff((self.start_date,self.start_time),(self.end_date,self.end_time))/self.time_step)+1 def __variables(self,k): if k=='SURFTEMP': out=zeros((len(self.dimensions['TSTEP']),1,len(self.dimensions['ROW']),len(self.dimensions['COL'])),'f') vars=self.__surfmaps() elif k=='AIRTEMP': out=zeros((len(self.dimensions['TSTEP']),len(self.dimensions['LAY']),len(self.dimensions['ROW']),len(self.dimensions['COL'])),'f') vars=self.__airmaps() for i,(d,t) in enumerate(self.timerange()): out[i,...]=vars.next() return out def __surfpos(self): pos=self.data_start_byte+12 inc=self.area_padded_size+self.padded_size*self.nlayers self.rffile.infile.seek(0,2) rflen=self.rffile.tell() while pos<rflen: yield pos pos+=inc raise StopIteration def __surfmaps(self): for pos in self.__surfpos(): yield memmap(self.rffile.infile.name,'>f','r',pos,(self.area_count,)).reshape(len(self.dimensions['ROW']),len(self.dimensions['COL'])) def __airpos(self): pos=self.area_padded_size+self.data_start_byte inc=self.area_padded_size+self.padded_size*self.nlayers self.rffile.infile.seek(0,2) rflen=self.rffile.tell() while pos<rflen: yield pos pos+=inc raise StopIteration def __airmaps(self): for pos in self.__airpos(): yield memmap(self.rffile.infile.name,'>f','r',pos,((self.cell_count+4)*self.nlayers,)).reshape(self.nlayers,self.cell_count+4)[:,3:-1].reshape(len(self.dimensions['LAY']),len(self.dimensions['ROW']),len(self.dimensions['COL'])) def timerange(self): return timerange((self.start_date,self.start_time),timeadd((self.end_date,self.end_time),(0,self.time_step),(2400,24)[int(self.time_step % 2)]),self.time_step,(2400,24)[int(self.time_step % 2)])