def ms_sums(diameter, beta, depol, multiple_scatter_parameters, ms_obj): ''' ms_sums(diameter,beta,depol,multiple_scatter_parameters,ms_obj): diameter = particle diameter profile or profiles beta = extinction or backscatter cross section profile or profiles depol = depolarization profile or profiles returns: ms_obj = summed profiles " .diameter_ice " .diameter_water " .beta_ice " .beta_water " .n_samples_ice " .n_samples_water ''' is_ice = np.zeros_like(beta) is_water = np.zeros_like(beta) is_ice[depol >= multiple_scatter_parameters['h2o_depol_threshold']] = 1.0 is_water[depol < multiple_scatter_parameters['h2o_depol_threshold']] = 1.0 #diameter_ice = diameter * is_ice #diameter_water = diameter * is_water beta_ice = beta * is_ice beta_water = beta * is_water #compute diameter * beta for ice and water components if not diameter == None: diameter_ice = diameter * beta * is_ice diameter_water = diameter * beta * is_water #if diameter is not supplied from lidar-radar retrieval get it from constants in multiple_scatter_parameters else: diameter_ice = hau.Z_Array( np.ones_like(beta) * multiple_scatter_parameters['mode_diameter_ice'] * beta * is_ice) diameter_water = hau.Z_Array( np.ones_like(beta) * multiple_scatter_parameters['mode_diameter_water'] * beta * is_water) if ms_obj == None: ms_obj = hau.Time_Z_Group() setattr(ms_obj, 'beta_ice', hau.Z_Array(nansum(beta_ice, 0))) setattr(ms_obj, 'beta_water', hau.Z_Array(nansum(beta_water, 0))) setattr(ms_obj, 'diameter_ice', hau.Z_Array(nansum(diameter_ice, 0))) setattr(ms_obj, 'diameter_water', hau.Z_Array(nansum(diameter_water, 0))) setattr(ms_obj, 'n_samples_ice', hau.Z_Array(sum(~np.isnan(beta_ice) * is_ice, 0))) setattr(ms_obj, 'n_samples_water', hau.Z_Array(sum(~np.isnan(beta_water) * is_water, 0))) else: ms_obj.beta_ice += nansum(beta_ice, 0) ms_obj.beta_water += nansum(beta_water, 0) ms_obj.diameter_ice += nansum(diameter_ice, 0) ms_obj.diameter_water += nansum(diameter_water, 0) ms_obj.n_samples_ice += sum(~np.isnan(beta) * is_ice, 0) ms_obj.n_samples_water += sum(~np.isnan(beta) * is_water, 0) return ms_obj
def process(self): isMmcr = (self.framestream.radarType == 'MMCR' ) #FIXME better way to discover the division isKazr = (self.framestream.radarType.startswith('KAZR') ) #FIXME better way to discover the division isMwacr = ('WACR' in self.framestream.radarType ) #FIXME better way to discover the division for f in self.framestream: f = copy.copy(f) if hasattr(f, 'heights'): # f._altitudevarname='heights' f.heights = hau.Z_Array(f.heights, dtype=numpy.float64) if hasattr(f, 'Reflectivity'): f.Reflectivity = hau.TZ_Array(f.Reflectivity, dtype=numpy.float64) if isMmcr: f.Reflectivity /= 100.0 setattr( f, 'Backscatter', hau.TZ_Array( self.reflectivityToBackscatterCrossSection( f.Reflectivity))) if hasattr(f, 'SpectralWidth'): f.SpectralWidth = hau.TZ_Array(f.SpectralWidth, dtype=numpy.float64) if isMmcr: f.SpectralWidth /= 1000.0 if hasattr(f, 'MeanDopplerVelocity'): f.MeanDopplerVelocity = hau.TZ_Array(f.MeanDopplerVelocity, dtype=numpy.float64) if isMmcr: f.MeanDopplerVelocity /= 1000.0 if isKazr or isMwacr: f.MeanDopplerVelocity *= -1.0 yield f
def preYield(self, x, attrs, found): if not hasattr(x, 'altitudes') or not hasattr( x, 'times') or x.times.size == 0: return False altmask = x.altitudes < 90000 for n, v in vars(x).items(): #print n,v if n.startswith('_'): continue v = v[altmask] if n == 'altitudes': setattr(x, '_altitudevarname', n) setattr(x, n, hau.Z_Array(v).copy()) continue if n in ('latitude', 'longitude', 'times'): if v.size > 0: setattr(x, n, v[0]) continue setattr(x, n, hau.Z_Array( v.astype('double'))) #.reshape([1]+list(v.shape)))) x.temps += 273.15 if not hasattr(x, 'dew_points') and hasattr(x, 'relative_humidity'): setattr( x, 'dew_points', hau.Z_Array(self.su.cal_dew_point(x.relative_humidity, x.temps))) else: x.dew_points += 273.15 if not hasattr(x, 'frost_points') and hasattr(x, 'dew_points'): setattr(x, 'frost_points', hau.Z_Array(self.su.cal_frost_point(x.dew_points))) else: x.frost_points += 273.15 x.sounding_type = 'arm' x.sounding_id = attrs['zeb_platform'] x.station_id = attrs['site_id'] x.top = max(x.altitudes) x.bot = min(x.altitudes) #print vars(x) #if x.times.size<=0: # return False x.sample_latitude = x.latitude x.sample_longitude = x.longitude x.sample_time = x.times return True
def Rayleigh_cross_section(wavelength, pressures, temps, altitudes): """ Rayleigh_lidar_return(wavelength,,pressures,temperatures,altitudes): compute Rayleigh scattering cross section see R Holz thesis for this equation giving the Rayleigh scattering cross section. beta=3.78e-6*press/temp at a wavelength of 532 nm then rescale to actual wavelength """ nalts = altitudes.shape[0] beta_r = hau.Z_Array(np.zeros(nalts)) #Rayleigh scatteromg cross section at 532 nm beta_r[:nalts] = 3.78e-6 * pressures[:nalts] / temps[:nalts] #Rayleigh scattering cross section beta_r = hau.Z_Array(beta_r * (532.0 / wavelength)**4) return beta_r
def process(self): fr = None flags = None olda = None qasource = tf.TimeTrickle(self.qasource, 'time') altitudes = hau.Z_Array(self.qaparser.altitudeAxis) for f in self.timealtsource: #FIXME include angles if not isinstance(f, dict): f = vars(f) t = f[self.timename] a = self.constantAltitude if fr is None or ((not qasource.atEnd) and t >= qasource.nextTime ): #if need an update to the qa record #print 'Getting qa source for time',t fr = qasource(t) flags = None if 'range_flags' in fr and fr[ 'range_flags'] is not None: #if there is a range dependence, and a potentially non-constant altitude if self.altname is not None and self.altname in f: a = f[self.altname] if a is None: raise RuntimeError( 'Need platform altitude to merge in range-dependant qa Flags' ) if olda is None or a != olda: flags = None olda = a if flags is None: #was cleared either because new flags from the qa file, or new altitude from the stream if 'range_flags' in fr and fr['range_flags'] is not None: flags = self.qaparser.mergeVectors(fr['flags'], fr['range_flags'], a) else: flags = fr['flags'] flags = self.qaparser.translateToEnumeration(flags) flags = hau.TZ_Array(flags.reshape([1] + list(flags.shape)), dtype='int32', summode='and') ret = hau.Time_Z_Group(timevarname='times', altname='altitudes') setattr(ret, 'times', hau.T_Array([t])) setattr(ret, 'delta_t', hau.T_Array([f['width'].total_seconds()])) setattr(ret, 'altitudes', copy.copy(altitudes)) setattr(ret, 'start', f['start']) setattr(ret, 'width', f['width']) if self.splitFields: for f, idx in self.qaparser.flagbits.items(): setattr( ret, 'qa_' + f, hau.TZ_Array((flags / (10**idx)) % 10, dtype='int32', summode='and')) else: setattr(ret, 'qaflags', flags) yield ret
def convert(self, profile, posframe): sounding = hau.Time_Z_Group() if 'cache' in self.name: sounding.sounding_type = 'virtual' sounding.sounding_id = 'Cached Forecast' sounding.station_id = 'Cached Forecast' elif 'virtual' in self.name: sounding.sounding_type = 'virtual' sounding.sounding_id = 'NWP Virt' sounding.station_id = 'NWP Virt' else: sounding.sounding_type = 'model' sounding.sounding_id = 'Static GRIB' sounding.station_id = 'Static GRIB' sounding.latitude = profile['lat'][0] sounding.longitude = profile['lon'][0] sounding.times = datetime(1970, 1, 1, 0, 0, 0) + timedelta( seconds=profile['base_time']) + timedelta( seconds=profile['time_offset'][0]) if posframe is None: sounding.sample_latitude = sounding.latitude sounding.sample_longitude = sounding.longitude sounding.sample_time = sounding.times else: sounding.sample_latitude = posframe['latitude'] sounding.sample_longitude = posframe['longitude'] sounding.sample_time = posframe['start'] minlen = profile['alt'].size for k, v in profile.items(): if hasattr(v, 'shape'): if profile['alt'].size != v.size: print 'WARNING SOUNDING ATTRIBUTE ' + k + ' has a length', v.size, 'while alts are', profile[ 'alt'].size profile[k] = v[:profile['alt'].size] if minlen > v.size: minlen = v.size if minlen < profile['alt'].size: print "TRIMMING TO SHORTENED SOUNDING ATTRIBUTE of length", minlen for k, v in profile.items(): if hasattr(v, 'shape'): profile[k] = v[:minlen] sounding.top = numpy.max(profile['alt']) sounding.bot = numpy.min(profile['alt']) sounding.altitudes = hau.Z_Array(profile['alt']) if 'tkel' in profile: sounding.temps = hau.Z_Array(profile['tkel']) else: sounding.temps = hau.Z_Array(profile['tdry']) + 273.15 sounding.pressures = hau.Z_Array(profile['pres']) sounding.dew_points = hau.Z_Array( self.su.cal_dew_point(hau.Z_Array(profile['rh']), sounding.temps)) sounding.frost_points = hau.Z_Array( self.su.cal_frost_point(sounding.dew_points)) return sounding
def lidar_return(beta_r, beta_e_trans, beta_e_rec, altitudes): """ lidar_return(beta_r,beta_e_trans,beta_e_rec,altitudes beta_r = backscatter cross section profile(1/(m sr) beta_e_trans = extinction cross section at transmit wavelength (1/m) beta_e_rec = extinction cross section at receive wavelength (1/m) altitudes = vector of altitudes at which to make calculations (m) atten_bs = attenuated backscatter cross_section_profile (1/m) """ delta_r = np.zeros_like(altitudes) delta_r[:-1] = altitudes[1:] - altitudes[:-1] #optical depths on tranmit and receive paths od_trans = np.cumsum(beta_e_trans * delta_r) od_rec = np.cumsum(beta_e_rec * delta_r) atten_bs = hau.Z_Array(beta_r * exp(-od_trans - od_rec)) return atten_bs
def compute_i2a_mol_ratio(profiles,Cxx,corr_adjusts,process_defaults): """compute_i2a_mol_ratio(i2a,mol,process_defaults): compute ratio of argon broadened i2 filtered profile to normal i2 filtered profile i2a = corrected argon broadened profile mol = normal i2 filtered profile process_defaults = processing instructions from process_control.json""" i2a = profiles.molecular_i2a_counts[0,:].copy() i2a = i2a * corr_adjusts['i2a_ratio'] mol = profiles.molecular_counts[0,:].copy() if process_defaults.enabled('i2a_mol_ratio'): #compute filter length in bins window = np.int(np.float(process_defaults.get_value('i2a_mol_ratio','filter_window')\ /(profiles.msl_altitudes[2]-profiles.msl_altitudes[1]))) print 'applying 3-order savitzky_golay filter before computing i2a/i2 ratio', print ' window = ',window * ( profiles.msl_altitudes[2]-profiles.msl_altitudes[1]), ' meters' #window must be odd number if window == 2*(window/2): window = window +1 if window >=5: i2a=sg.savitzky_golay(i2a,window,3) mol=sg.savitzky_golay(mol,window,3) else: print 'Window too short---no filtering applied to i2a_mol_ratio' i2a_mol_ratio = (i2a - Cxx.Cam_i2a*corr_adjusts['Cam_corr'] * profiles.combined_hi_counts[0,:])\ /(mol - Cxx.Cam*corr_adjusts['Cam_corr'] * profiles.combined_hi_counts[0,:]) i2a_mol_ratio = hau.Z_Array(i2a_mol_ratio[np.newaxis,:]) return i2a_mol_ratio
def updateProfiles(self,profiles,mean,hsrl_constants,calframe={},qc_mask=None): try: hsrl_Cxx = calframe.pop('rs_Cxx',None) hsrl_cal = calframe.pop('rs_cal',None) hsrl_sounding = calframe.pop('sounding',None) pc=self.hsrl_process_control if hsrl_Cxx is not None else None if not hasattr(mean,'msl_altitudes') and hasattr(mean,'molecular_counts'): mean=copy.copy(mean) setattr(mean,'msl_altitudes',hau.Z_Array(np.arange(0,mean.molecular_counts.shape[1])*hsrl_constants['binwidth']*3e8/2)) if pc is not None: sel_telescope_dir = pc.get_value('averaged_profiles','telescope_pointing') else: sel_telescope_dir='all' profiles=self.mr.generate_ave_profiles( mean, qc_mask, hsrl_Cxx,hsrl_constants, pc,sel_telescope_dir,self.hsrl_corr_adjusts,old_profiles=profiles) #compute temperatures if data is available if hasattr(profiles,'molecular_i2a_counts') and hasattr(hsrl_Cxx,'Cam_i2a'): profiles.i2a_mol_ratio = self.iat.compute_i2a_mol_ratio( profiles ,hsrl_Cxx ,self.hsrl_corr_adjusts ,pc) profiles.i2a_temperatures=self.iat.compute_temperature_from_i2a( self.hsrl_instrument,profiles.i2a_mol_ratio ,hsrl_cal.i2a_temp_table,hsrl_sounding.pressures,self.hsrl_corr_adjusts) profiles.i2a_temperatures[profiles.msl_altitudes <= hsrl_constants['lidar_altitude']+200] = np.nan if self.multistream:#this is safe only if we are top level self.setProvidesUsing(profiles) except Exception as e: print 'Exception occurred in profiles ',e traceback.print_exc() raise #do nothing return profiles
def __call__(self, _mean, t_filter_order, correct_below_range, min_fit_alt, z_norm_interval, enable_z_fit): """ def func(x,a): #fourth order polynomial with last value = 1 and df/dx of last value = 0 #return a[0] + a[1] * x + a[2] * x**2 + a[3] * x**3 + a[4] * x**4 #val = a[0] + a[1] * x**2 + a[2] * x**3 + a[3] * x**4 #val = val -(2*a[1]*x[-1] + 3*a[2]*x[-1]**2 + 4*a[3]*x[-1]**3) * x val = a[0] * x**2 + a[1] * x**3 + a[2] * x**4 # linear term from df/dx = 0 at x[-1] b = -(2*a[0]*x[-1] + 3*a[1]*x[-1]**2 + 4*a[2]*x[-1]**3) # constant term from x[-1] = 1 a = 1.0 - (b*x[-1] + a[0]*x[-1]**2 + a[1]*x[-1]**3 + a[2]*x[-1]**4) #full quadratic val = a + b*x + val return val def func2(x,a): #fourth order polynomial with last value = 1 and df/dx of first and last value = 0 #return a[0] + a[1] * x + a[2] * x**2 + a[3] * x**3 + a[4] * x**4 val = a[0] * x**3 + a[1] * x**4 #second order term from df/dx = 0 at first and last points c = -(3 * a[0] * (x[-1]**2 - x[0]**2) + 4 * a[1] * (x[-1]**3 - x[0]**3))\ /(2 * (x[-1] - x[0])) # linear term from df/dx = 0 at x[-1] b = -(2* c *x[-1] + 3*a[0]*x[-1]**2 + 4*a[1]*x[-1]**3) # constant term from x[-1] = 1 a = 1.0 - (b*x[-1] + c * x[-1]**2 + a[0]*x[-1]**3 + a[1]*x[-1]**4) val = a + b*x + c*x**2 + val return val """ def func3(x, a): #fifth order polynomial with last value = 1 and df/dx of first and last value = 0 #return a[0] + a[1] * x + a[2] * x**2 + a[3] * x**3 + a[4] * x**4 + a[5] * x**5 val = a[0] * x**3 + a[1] * x**4 + a[2] * x**5 #second order term from df/dx = 0 at first and last points c = -(3*a[0]*(x[-1]**2 - x[0]**2) + 4*a[1]*(x[-1]**3 - x[0]**3) +5*a[2]*(x[-1]**4-x[0]**4))\ /(2*(x[-1] - x[0])) # linear term from df/dx = 0 at x[-1] b = -(2 * c * x[-1] + 3 * a[0] * x[-1]**2 + 4 * a[1] * x[-1]**3 + 5 * a[2] * x[-1]**4) # constant term from x[-1] = 1 a = 1.0 - (b * x[-1] + c * x[-1]**2 + a[0] * x[-1]**3 + a[1] * x[-1]**4 + a[2] * x[-1]**5) val = a + b * x + c * x**2 + val return val def residuals(p, x, y): res = y - func3(x, p) return res s_time = datetime.utcnow() mean = listMerge(_mean) ntimes = mean.times.size useindex = 0 if self.priorTime is None: self.priorTime = mean.times[0] - timedelta(seconds=10) while useindex < (ntimes - 1) and self.priorTime >= mean.times[useindex]: useindex = useindex + 1 self.priorTime = mean.times[useindex] r_mean = copy.deepcopy(_mean[useindex]) print 'wfov window ', r_mean.times[0], r_mean.times[-1], ntimes if not hasattr(mean, 'raw_molecular_counts') or not hasattr( mean, 'raw_molecular_wfov_counts') or not hasattr( mean, 'msl_altitudes'): print print 'WARNING: Missing value for wfov calculation. skipped' print "hasattr(r_inv,'raw_molecular_wfov_counts') = ", hasattr( mean, 'raw_molecular_wfov_counts') print "hasattr(r_inv,'msl_altitudes') = ", hasattr( mean, 'msl_altitudes') return r_mean if not hasattr(r_mean, 'raw_molecular_wfov_counts') or not hasattr( r_mean, 'msl_altitudes'): return r_mean if not hasattr(r_mean, 'calibration_wfov_mol_ratio'): print 'CALIBRATION WFOV mol ratio missing!' return r_mean if r_mean.msl_altitudes[-1] < (correct_below_range + z_norm_interval + r_mean.lidar_altitude): print 'WARNING--max altitude two low for wfov correction--no corr applied' print ' wfov cor requires max alt >', ( correct_below_range + z_norm_interval + r_mean.lidar_altitude) return r_mean data_len = len(mean.raw_molecular_counts[0, :]) msl_altitudes = mean.msl_altitudes #distance between altitude bins adz = hau.Z_Array(np.zeros(msl_altitudes.shape)) adz[1:] = msl_altitudes[1:] - msl_altitudes[:-1] adz[0] = adz[1] t_window_pts = ntimes ones_array = np.ones_like(mean.raw_molecular_counts) molecular_wfov_counts = (mean.raw_molecular_wfov_counts - mean.m_wfov_dark_counts * ones_array) molecular_counts = (mean.raw_molecular_counts - mean.mol_dark_counts * ones_array) if 0: import matplotlib.pylab as plt plt.figure(1111) plt.plot(msl_altitudes, np.nanmean(molecular_counts, 0), 'c', msl_altitudes, 46 * (np.nanmean(molecular_wfov_counts, 0)), 'r') ax = plt.gca() ax.set_yscale('log') #filter in time t_pts = np.arange(len(molecular_counts[:, 0])) fit_time = datetime.utcnow() top_norm_bin = len(msl_altitudes[msl_altitudes <= correct_below_range + z_norm_interval]) top_bin = len(msl_altitudes[msl_altitudes <= correct_below_range]) bot_bin = len(msl_altitudes[msl_altitudes <= min_fit_alt]) if t_filter_order <> 1: pc = np.polyfit(t_pts, molecular_counts, t_filter_order) filtered_molecular_counts = np.polyval(pc, useindex) pcw = np.polyfit(t_pts, molecular_wfov_counts, t_filter_order) filtered_molecular_wfov_counts = np.polyval(pcw, useindex) pcw = np.polyfit(t_pts, mean.raw_molecular_wfov_counts, t_filter_order) filtered_raw_molecular_wfov_counts = np.polyval(pcw, useindex) else: # simplified computation when fit is linear filtered_molecular_counts = np.nanmean( molecular_counts[:, :top_norm_bin], 0) filtered_molecular_wfov_counts = np.nanmean( molecular_wfov_counts[:, :top_norm_bin], 0) filtered_raw_molecular_wfov_counts = np.nanmean( mean.raw_molecular_wfov_counts[:, :top_norm_bin], 0) wfov_mol_ratio = filtered_molecular_wfov_counts / filtered_molecular_counts #wfov_mol_ratio is now ratioed to the value measured with the geometric correction wfov_mol_ratio /= mean.calibration_wfov_mol_ratio[:top_norm_bin] if 0: import matplotlib.pylab as plt plt.figure(6666) plt.plot(np.arange(top_norm_bin),wfov_mol_ratio,'b'\ ,np.arange(top_norm_bin),wfov_mol_ratio[:top_norm_bin],'r') # add functional code here. # 'inv' is a normal 2-D frame of data (read only) # 'r_inv' is the 1-D frame to be returned. # 'useindex' is the time index of the 'r_inv' frame within 'inv' #normalize wfov_correction by mean value between top_bin and 1.5 km above top_bin wfov_mol_ratio /= np.nanmean(wfov_mol_ratio[top_bin:top_norm_bin]) #wfov_mol_ratio /= wfov_mol_ratio[top_bin] if 0: import matplotlib.pylab as plt plt.figure(6667) plt.plot(msl_altitudes[:top_norm_bin],wfov_mol_ratio,'g'\ ,msl_altitudes[:top_norm_bin],wfov_mol_ratio[:top_norm_bin],'r') plt.grid(True) #bin_vec = np.arange(len(wfov_mol_ratio)) #smoothed_wfov_mol_ratio = np.ones_like(wfov_mol_ratio) bin_vec = np.arange(top_norm_bin) smoothed_wfov_mol_ratio = np.ones(len(msl_altitudes)) if enable_z_fit == "spline": #wfov fit with spline #forces correction factor to 1.0 and slope to zero at top of layer #weights = np.sqrt(filtered_molecular_wfov_counts[bot_bin:top_bin]) weights = filtered_molecular_wfov_counts[bot_bin:top_bin] \ / np.sqrt(filtered_raw_molecular_wfov_counts[bot_bin:top_bin]) temp = wfov_mol_ratio[bot_bin:top_bin].copy() weights = weights[np.isnan(temp) == 0] bins = bin_vec[bot_bin:top_bin].copy() bins = bins[np.isnan(temp) == 0] temp = temp[np.isnan(temp) == 0] #force correction factor to 1.0 and slope = 0.0 at top of corrected layer temp[-2:] = 1.0 weights[-2:] = 1000.0 npts = bins.shape[0] - 1 - 2 s = 0.05 * (npts - np.sqrt(2 * npts)) tck = splrep(bins, temp, w=weights, s=s) smoothed_wfov_mol_ratio[bot_bin:top_bin] = splev( bin_vec[bot_bin:top_bin], tck) elif enable_z_fit == "polynomial": #smooth over ranges bot_bin to top_bin if enabled # 'wfov correction using polynomial fit' temp = wfov_mol_ratio[bot_bin:top_bin].copy() temp[np.isnan(temp)] = 0 p = leastsq(residuals ,x0=np.array([-1.0e-5,0.0000,0.0]) ,args=(bin_vec[bot_bin:top_bin] \ ,temp)) #use fitted values between min_fit_alt and fit_below_alt smoothed_wfov_mol_ratio[bot_bin:top_bin] = func3( bin_vec[bot_bin:top_bin], p[0]) else: #no smoothing # 'wfov correction with no range smoothing' smoothed_wfov_mol_ratio[:top_bin] = wfov_mol_ratio[:top_bin] #make value constant below bot_bin smoothed_wfov_mol_ratio[:bot_bin] = smoothed_wfov_mol_ratio[bot_bin] #smallestval=nanmin(smoothed_wfov_mol_ratio[smoothed_wfov_mol_ratio>0.0]) #smoothed_wfov_mol_ratio[np.logical_not(smoothed_wfov_mol_ratio>0.0)]=smallestval if 0: #plot fit as function of altitude import matplotlib.pylab as plt plt.figure(6668) plt.plot(msl_altitudes[:top_bin], wfov_mol_ratio[:top_bin], 'c', msl_altitudes[bot_bin:top_bin], smoothed_wfov_mol_ratio[bot_bin:top_bin], 'r', msl_altitudes[top_bin:], smoothed_wfov_mol_ratio[top_bin:], 'g') plt.grid(True) plt.xlabel('Range (m)') plt.ylabel('Correction') ax = plt.gca() ax.set_ylim(0.7, 1.1) ax.set_xlim(0, 8000) #plt.show() r_mean.filtered_wfov_mol_ratio = hau.TZ_Array( np.ones(r_mean.raw_molecular_counts.shape)) #no correction for NaN points smoothed_wfov_mol_ratio[np.isnan(smoothed_wfov_mol_ratio)] = 1.0 #limit size of correction smoothed_wfov_mol_ratio[smoothed_wfov_mol_ratio < 0.5] = 0.5 r_mean.filtered_wfov_mol_ratio[0, :] = smoothed_wfov_mol_ratio r_mean.wfov_extinction_corr = hau.TZ_Array( np.zeros(r_mean.raw_molecular_counts.shape)) r_mean.wfov_extinction_corr[ 0, 1:top_bin - 1] = smoothed_wfov_mol_ratio[ 2:top_bin] - smoothed_wfov_mol_ratio[:top_bin - 2] r_mean.wfov_extinction_corr[0,1:top_bin-1] = -0.25 * r_mean.wfov_extinction_corr[0,1:top_bin-1] \ /(smoothed_wfov_mol_ratio[1:top_bin-1] * adz[1:top_bin-1]) # make wfov geo correction to mean counts. for chan in vars(r_mean).keys(): if not chan.endswith('_counts'): continue applyFilter = True for badPart in ['dark', 'raw_', 'var_', 'wfov']: if badPart in chan: applyFilter = False break if not applyFilter: continue if hasattr(r_mean, chan): setattr( r_mean, chan, (getattr(r_mean, chan) * r_mean.filtered_wfov_mol_ratio)) return r_mean
def __call__(self,rs_inv,rs_particle,calvals):#update to process,and return a completed frame rs_multiple_scattering=hau.Time_Z_Group(rs_inv.times.copy(),timevarname='times',altname='msl_altitudes') N1 = 2 N2 = self.multiple_scatter_parameters['highest_order'] start_alt = self.multiple_scatter_parameters['lowest_altitude'] p180_water = self.multiple_scatter_parameters['p180_water'] p180_ice = self.multiple_scatter_parameters['p180_ice'] h2o_depol_threshold = self.multiple_scatter_parameters['h2o_depol_threshold'] p180_ice = self.multiple_scatter_parameters['p180_ice'] p180_water = self.multiple_scatter_parameters['p180_water'] second_wavelength =self.multiple_scatter_parameters['second_wavelength'] wavelength = calvals['wavelength']*1e-9 #assert(rs_particle!=None or self.multiple_scatter_parameters['particle_info_source'] == 'constant') if 1: #self.multiple_scatter_parameters['processing_mode'] == '1d': if self.multiple_scatter_parameters['particle_info_source'] == 'constant': #in this case the mode diameter will be reset to ice or water values from multiple_scattering.json file #depending on h2o_depol_threshold mode_diameter = None else: #use lidar-radar retrieved particle sizes #print 'particle' #print dir(rs_particle) mode_diameter = rs_particle.mode_diameter.copy() #accumulates sums for 1-d average multiple scatter profile self.ms_obj = msu.ms_sums(mode_diameter,rs_inv.beta_a_backscat ,rs_inv.linear_depol,self.multiple_scatter_parameters,self.ms_obj) self.ms_obj.beta_water[self.ms_obj.beta_water < 0] = 0.0 self.ms_obj.beta_ice[self.ms_obj.beta_ice < 0] = 0.0 beta_total = self.ms_obj.beta_water +self.ms_obj.beta_ice total_samples = self.ms_obj.n_samples_water +self.ms_obj.n_samples_ice #when no ice or water data points are present averages must be zero #compute weighted averages of beta, diameter when ice and water are present #ms_obj.beta_water and ms_obj.beta_ice have the sum of beta_backscatter for ice and water #self.ms_obj.n_samples_water[self.ms_obj.n_samples_water == 0] = np.infty #self.ms_obj.n_samples_ice[self.ms_obj.n_samples_ice == 0] = np.infty #compute ave beta_extinction profile from sum beta_backscat profiles extinction_profile = (self.ms_obj.beta_water/p180_water + self.ms_obj.beta_ice/p180_ice)/total_samples diameter = (self.ms_obj.diameter_ice + self.ms_obj.diameter_water)/beta_total #convert altitudes into range ranges = rs_inv.msl_altitudes.copy() zenith_angle = np.abs(calvals['telescope_roll_angle_offset'])*np.pi/180.0 ranges = ranges/np.cos(zenith_angle) start_range = start_alt/np.cos(zenith_angle) end_range = rs_inv.msl_altitudes[-1]/np.cos(zenith_angle) if start_range >= end_range: raise RuntimeError(' start altitude'+str(np.int(start_range))+ ' is above highest data point') ms_ratios_profile = msu.msinteg(N1,N2,start_range \ ,end_range,self.multiple_scatter_parameters['step_size'], extinction_profile, diameter ,ranges,wavelength,self.multiple_scatter_parameters,calvals) rs_multiple_scattering.ranges = ms_ratios_profile[:,0] rs_multiple_scattering.ms_ratios_profile = ms_ratios_profile rs_multiple_scattering.extinction_profile = hau.Z_Array(extinction_profile[np.newaxis,:]) rs_multiple_scattering.weighted_diameter = hau.Z_Array(diameter[np.newaxis,:]) rs_multiple_scattering.msl_altitudes = rs_inv.msl_altitudes.copy() rs_multiple_scattering.wavelength = wavelength #compute multiple scattering for a second wavelength if it is provided #assume no change is extinction cross section if second_wavelength: ms_ratios_profile_2 = msu.msinteg(N1,N2,start_range \ ,end_range,self.multiple_scatter_parameters['step_size'], extinction_profile, diameter ,ranges,second_wavelength,self.multiple_scatter_parameters,calvals) rs_multiple_scattering.ms_ratios_profile_2 = ms_ratios_profile_2 rs_multiple_scattering.second_wavelength = second_wavelength if self.multiple_scatter_parameters['processing_mode'] == '2d': #do multiple scatter calculation for all profiles in frame print 'begining 2d multiple scatter processing' #estimate extinction based on backscatter phase function beta = rs_inv.beta_a_backscat.copy() beta = beta/p180_water beta[rs_inv.linear_depol>self.multiple_scatter_parameters['h2o_depol_threshold']] \ = beta[rs_inv.linear_depol>self.multiple_scatter_parameters['h2o_depol_threshold']]*p180_water/p180_ice beta[beta < 0]=0.0 if self.multiple_scatter_parameters['particle_info_source'] == 'constant': mode_diameter = np.ones_like(rs_inv.beta_a_backscat) \ * self.multiple_scatter_parameters['mode_diameter_water'] mode_diameter[rs_inv.linear_depol > self.multiple_scatter_parameters['h2o_depol_threshold']] \ = self.multiple_scatter_parameters['mode_diameter_ice'] #convert altitudes into ranges ranges = rs_inv.msl_altitudes.copy() zenith_angle = np.abs(calvals['telescope_roll_angle_offset'])*np.pi/180.0 ranges = ranges/np.cos(zenith_angle) start_range = start_alt/np.cos(zenith_angle) end_range = rs_inv.msl_altitudes[-1]/np.cos(zenith_angle) for i in range(rs_inv.beta_a_backscat.shape[0]): print 'Computing multiple scattering for ' ,rs_inv.times[i] if self.multiple_scatter_parameters['particle_info_source'] == 'constant': ratios = msu.msinteg(N1,N2,start_range \ ,end_range,self.multiple_scatter_parameters['step_size'],beta[i,:],mode_diameter[i,:] ,ranges,wavelength,self.multiple_scatter_parameters,calvals) else: #get mode diameter from lidar_radar measured values ratios = msu.msinteg(N1,N2,start_range \ ,end_range,wavelength,self.multiple_scatter_parameters['step_size'] ,beta[i,:],rs_particle.mode_diameter[i,:] ,ranges,self.multiple_scatter_parameters,calvals) #load values into output array if not hasattr(rs_multiple_scattering,'ms_ratio_total'): rs_multiple_scattering.ms_ratio_total = np.zeros((beta.shape[0],ratios.shape[0])) rs_multiple_scattering.ms_ratio_total[i,:] =nansum(ratios[:,2:],1) rs_multiple_scattering.msl_altitudes = rs_inv.msl_altitudes.copy() rs_multiple_scattering.ms_ratio_total =hau.TZ_Array(rs_multiple_scattering.ms_ratio_total) #rs_multiple_scattering.start_altitude = start_alt return rs_multiple_scattering
def select_raqms_profile(soundings, request_time, requested_altitudes, offset=0): """selects sounding prior to request_time from soundings -- the sounding is returned in a Time_Z_Group as Z_arrays""" if soundings is None or soundings.times.size == 0: raise RuntimeError, "select_faqms_profile: No soundings for %s " %\ request_time import atmospheric_profiles.soundings.sounding_utilities as su sounding = hau.Time_Z_Group() sounding.altitudes = hau.Z_Array(requested_altitudes) max_alt = requested_altitudes[-1] max_bin = len(requested_altitudes) index = sum(soundings.times <= request_time) - 1 + offset if index < 0 or index >= len(soundings.times): return None #initialize variables for inclusion in T_Z_Group sounding.temps = hau.Z_Array(np.zeros((max_bin))) sounding.dew_points = hau.Z_Array(np.zeros(max_bin)) sounding.frost_points = hau.Z_Array(np.zeros(max_bin)) sounding.pressures = hau.TZ_Array(np.zeros(max_bin)) sounding.ext_total = hau.TZ_Array(np.zeros(max_bin)) sounding.ext_salt = hau.TZ_Array(np.zeros(max_bin)) sounding.wind_spd = hau.TZ_Array(np.zeros(max_bin)) sounding.wind_dir = hau.TZ_Array(np.zeros(max_bin)) #sounding.times is a single time at this point, however it will later be included #in a list of all the soundings used in this processing request. In order that it #be treated properly it must be defined as a T_Array sounding.times = hau.T_Array([soundings.times[index]]) sounding.latitude = hau.T_Array([soundings.latitude[index]]) sounding.longitude = hau.T_Array([soundings.longitude[index]]) #sounding.times=hau.T_Array([soundings.times[index]]) #interpolate model levels to lidar bin altitudes #temp=interpolate.splrep(soundings.model_level_alts[index,-1::-1] \ # ,soundings.temperatures[index,-1::-1]) #sounding.temps=interpolate.splev(sounding.altitudes,temp,der=0) temp=interpolate.splrep(soundings.model_level_alts[index,-1::-1] \ ,soundings.pressures[index,-1::-1]) sounding.pressures = interpolate.splev(sounding.altitudes, temp, der=0) sounding.temps=np.interp(sounding.altitudes \ ,soundings.model_level_alts[index,-1::-1] \ ,soundings.temperatures[index,-1::-1]) #calculate dew point at model levels for selected profile dew_pts=su.cal_dew_point(soundings.relative_humidity[index,:] \ ,soundings.temperatures[index,:]) frost_pts = su.cal_frost_point(dew_pts) #calculate wind speed and direction from u and v u_vel = soundings.u_vel[index, -1::-1] v_vel = soundings.v_vel[index, -1::-1] wind_spd = np.sqrt(u_vel**2 + v_vel**2) wind_dir = np.arctan(v_vel / u_vel) * 180.0 / np.pi for i in range(len(u_vel)): if (u_vel[i] < 0 and v_vel[i]) < 0: wind_dir[i] = 180.0 - wind_dir[i] elif (u_vel[i] > 0 and v_vel[i]) > 0: wind_dir[i] = 180.0 + wind_dir[i] elif u_vel[i] < 0: wind_dir[i] = 270.0 - wind_dir[i] else: wind_dir[i] = np.nan #interpolate to lidar bin altitudes sounding.frost_points=np.interp(sounding.altitudes \ ,soundings.model_level_alts[index,-1::-1],frost_pts[-1::-1]) sounding.dew_points=np.interp(sounding.altitudes \ ,soundings.model_level_alts[index,-1::-1],dew_pts[-1::-1]) sounding.ext_total=np.interp(sounding.altitudes\ ,soundings.model_level_alts[index,-1::-1]\ ,soundings.ext_total[index,-1::-1]) sounding.ext_salt=np.interp(sounding.altitudes\ ,soundings.model_level_alts[index,-1::-1]\ ,soundings.ext_salt[index,-1::-1]) sounding.ext_dust=np.interp(sounding.altitudes\ ,soundings.model_level_alts[index,-1::-1]\ ,soundings.ext_dust[index,-1::-1]) sounding.wind_dir = np.interp(sounding.altitudes \ ,soundings.model_level_alts[index,-1::-1],wind_dir) sounding.wind_spd = np.interp(sounding.altitudes \ ,soundings.model_level_alts[index,-1::-1],wind_spd) sounding.top = sounding.altitudes[-1] sounding.bot = sounding.altitudes[0] #plt.figure(1) #plt.plot(temperatures,altitudes,dew_points,altitudes) #plt.figure(2) #plt.plot(ext_total,altitudes) #plt.show() return sounding
def hsrl_inversion(r_msl, rs_Cxx, rs_constants,corr_adjusts,process_defaults): """hsrl_inversion(range_processed_returns,calibration_structure ,system_constants) Invert hsrl raw count data into separate molecular and particulate profiles and compute backcatter cross-section and depolarization from the profiles returned structure rs always returns: times = times of records delta_t = time seperation between records msl_altitudes = bin altitudes in meters seeded shots = total number of seeded laser shots in profile beta_r_backscat = Rayleigh scattering cross section from sounding Na = number of aerosol photon counts Nm = total number of molecular counts(including i2a if present) Nm_i2 = number of molecular counts in i2 channel Na = number of particulate counts Ncp = Ncp photon counts linear_depol = fractional particulate depolarization beta_a_backscat = particulate aerosol backscatter cross-section beta_a_backscat_par = par polarization component of backscat cross-section beta_a_backscat_perp= perp polarization component of backscat cross-section integrated_backscat = cumsum of backscatter cross section in altitude if i2a channel exists the following are added to rs: Nm_i2a = number of molecular photons derived from i2a channel Nm = rs.Nm_2i + rs.Nm_i2a Ncp_i2a beta_a_backscat_par_i2a/Nm beta_a_backscat_perp_i2a if these exist in input file they are added to rs: telescope_pointing GPS_MSL_Alt circular_depol """ rs = hau.Time_Z_Group(like=r_msl) # r_msl.molecular_counts is hau.TZ_Array (2D) nalts = r_msl.molecular_counts.shape[1] if rs_Cxx.beta_r.shape[0] < nalts: print 'hsrl_inversion(): size too small on calibration arrays : rs_Cxx.beta_r = %d vs nalts = %d. padding cal with last value' % \ (rs_Cxx.beta_r.shape[0], nalts) #assert(rs_Cxx.beta_r.shape[0] != nalts) os=rs_Cxx.beta_r.shape[0] for k,v in vars(rs_Cxx).items(): if hasattr(v,'size') and v.size==os: ns=list(v.shape) ns[0]=nalts tmp=np.zeros(ns,dtype=v.dtype) tmp[:]=v[-1] tmp[:os]=v setattr(rs_Cxx,k,tmp) elif rs_Cxx.beta_r.shape[0] > nalts: print 'WARNING hsrl_inversion(): size larger on calibration arrays. may be an error : rs_Cxx.beta_r = %d vs nalts = %d' % \ (rs_Cxx.beta_r.shape[0], nalts) rs.times = r_msl.times.copy() rs.delta_t = r_msl.delta_t.copy() rs.msl_altitudes = r_msl.msl_altitudes.copy() rs.seeded_shots=r_msl.seeded_shots.copy() if hasattr(r_msl,'telescope_pointing'): rs.telescope_pointing=r_msl.telescope_pointing.copy() # Rayleigh backscatter cross section profile rs.beta_r_backscat = hau.Z_Array(np.zeros(nalts)) rs.beta_r_backscat[:nalts] = rs_Cxx.beta_r[:nalts] * 3.0 / (8.0 * np.pi) #for normal i2 channel kk = 1.0 / (rs_Cxx.Cmm[:nalts] - rs_Cxx.Cmc[:nalts] * rs_Cxx.Cam) rs.Na = hau.TZ_Array( kk[:nalts] * (rs_Cxx.Cmm[:nalts] * r_msl.combined_counts\ - rs_Cxx.Cmc[ : nalts] * r_msl.molecular_counts) ) rs.Nm = hau.TZ_Array( kk[:nalts] * (r_msl.molecular_counts \ - rs_Cxx.Cam * r_msl.combined_counts) ) rs.Nm_i2 = rs.Nm.copy() #if data includes an i2a channel we generation a seperate inversion--bagohsrl only #systems with i2a channel record linear depolarization if hasattr(r_msl,'molecular_i2a_counts') and hasattr(rs_Cxx,'Cmm_i2a'): kk = 1.0 / (rs_Cxx.Cmm_i2a[:nalts] - rs_Cxx.Cmc[:nalts] * rs_Cxx.Cam_i2a) rs.Na_i2a = hau.TZ_Array( kk[:nalts] * (rs_Cxx.Cmm_i2a[:nalts] * r_msl.combined_counts - rs_Cxx.Cmc[ : nalts] * r_msl.molecular_i2a_counts) ) rs.Nm_i2a = hau.TZ_Array( kk[:nalts] * (r_msl.molecular_i2a_counts \ - rs_Cxx.Cam_i2a * r_msl.combined_counts) ) rs.Nm = (rs.Nm_i2 + rs.Nm_i2a)/2.0 #bagohsrl is only hsrl with i2a channel--it measures linear depolarization rs.Ncp = rs_constants['combined_to_cross_pol_gain_ratio'] \ * (r_msl.cross_pol_counts - rs_constants['polarization_cross_talk']*corr_adjusts['pol_x_talk'] * r_msl.combined_counts) - rs.Nm_i2 * 0.0035 / (1.0 - 0.0035) rs.Ncp_i2a = rs_constants['combined_to_cross_pol_gain_ratio'] \ * (r_msl.cross_pol_counts - rs_constants['polarization_cross_talk']*corr_adjusts['pol_x_talk'] * r_msl.combined_counts) - rs.Nm_i2a * 0.0035 / (1.0 - 0.0035) if not rs_constants.has_key('no_depol_channel') or rs_constants['no_depol_channel']==0 : rs.linear_depol_i2a = rs.Ncp_i2a / rs.Na_i2a #compute the linear depolarization as the average of normal and i2a values #note these two componets should be almost identical rs.linear_depol = (rs.Ncp + rs.Ncp_i2a)/(rs.Na_i2a + rs.Na) #when backscatter is small linear_depol can become indeterminate--bound values rs.linear_depol[rs.linear_depol < 0.0] = 0.0 rs.linear_depol[rs.linear_depol > 0.6] = 0.6 else: rs.linear_depol = np.zeros_like(rs.Nm) rs.linear_depol_i2a = np.zeros_like(rs.Nm) rs.beta_a_backscat_perp_i2a=rs.Na_i2a/rs.Nm_i2a \ *rs.linear_depol_i2a*rs.beta_r_backscat rs.beta_a_backscat_perp = rs.Na/rs.Nm_i2 \ *rs.linear_depol * rs.beta_r_backscat rs.beta_a_backscat_par_i2a = rs.Na_i2a / rs.Nm_i2a * rs.beta_r_backscat rs.beta_a_backscat_par = rs.Na / rs.Nm_i2 * rs.beta_r_backscat rs.beta_a_backscat = 0.5 *(rs.beta_a_backscat_par +rs.beta_a_backscat_perp \ + rs.beta_a_backscat_par_i2a + rs.beta_a_backscat_perp_i2a) if rs_constants.has_key('no_i2_channel') and rs_constants['no_i2_channel']==1: print print 'WARNING--I2 channel is not being used in calculations' print "calvals has 'no_i2_channel'== 1" print rs.linear_depol = rs.Ncp_i2a / rs.Na_i2a rs.beta_a_backscat = rs.beta_a_backscat_par_i2a + rs.beta_a_backscat_perp_i2a if not process_defaults.enabled('depolarization_is_aerosol_only'): #user wants bulk depolarization aerosol combined with molecular #recompute depolarization print 'computing bulk depolarization--aerosol and molecular combined' rs.linear_depol = rs_constants['combined_to_cross_pol_gain_ratio'] \ * (r_msl.cross_pol_counts\ - rs_constants['polarization_cross_talk']*corr_adjusts['pol_x_talk']\ * r_msl.combined_counts) / r_msl.combined_counts #compute circular polarization from linear--good only for vertical pointing systems. rs.circular_depol = 2 * rs.linear_depol / (1 - rs.linear_depol) elif hasattr(rs_Cxx,'Cmm_i2a') or rs_constants.has_key('polarization_is_linear') \ and rs_constants['polarization_is_linear']==1: if hasattr(rs_Cxx,'Cmm_i2a'): print print 'hsrl_inversion(): WARNING i2a counts found, but no calibration' print 'computing without i2a channel' print rs.Ncp = rs_constants['combined_to_cross_pol_gain_ratio'] \ * (r_msl.cross_pol_counts - rs_constants['polarization_cross_talk']*corr_adjusts['pol_x_talk'] * r_msl.combined_counts) - rs.Nm_i2 * 0.0035 / (1.0 - 0.0035) rs.linear_depol = rs.Ncp/rs.Na #when backscatter is small linear_depol can become indeterminate--bound values rs.linear_depol[rs.linear_depol < 0.0] = 0.0 rs.linear_depol[rs.linear_depol > 0.6] = 0.6 if not process_defaults.enabled('depolarization_is_aerosol_only'): #user wants bulk depolarization aerosol combined with molecular #recompute depolarization rs.linear_depol = rs_constants['combined_to_cross_pol_gain_ratio'] \ * (r_msl.cross_pol_counts\ - rs_constants['polarization_cross_talk']*corr_adjusts['pol_x_talk']\ * r_msl.combined_counts) / r_msl.combined_counts #compute circular from linear--good only for vertical pointing system rs.circular_depol = 2*rs.linear_depol /(1 - rs.linear_depol) rs.beta_a_backscat_perp = rs.Na/rs.Nm_i2 \ *rs.linear_depol * rs.beta_r_backscat rs.beta_a_backscat_par = rs.Na / rs.Nm * rs.beta_r_backscat rs.beta_a_backscat = rs.beta_a_backscat_par +rs.beta_a_backscat_perp else: #instrument with no i2a channel and measures circular polarization rs.Ncp = rs_constants['combined_to_cross_pol_gain_ratio'] \ * (r_msl.cross_pol_counts\ - rs_constants['polarization_cross_talk']*corr_adjusts['pol_x_talk']\ * r_msl.combined_counts) - rs.Nm_i2 * 0.0074 / (1.0 - 0.0074) rs.circular_depol = rs.Ncp / rs.Na #when Na becomes small, circular_depol may become indeterminate # (and don't fault on Nans) #rs.circular_depol = np.nan_to_num(rs.circular_depol) rs.circular_depol[rs.circular_depol < 0.0] = 0.0 rs.circular_depol[rs.circular_depol > 3.0] = 3.0 rs.beta_a_backscat_perp=rs.Na/rs.Nm_i2*rs.circular_depol*rs.beta_r_backscat rs.beta_a_backscat_par = rs.Na / rs.Nm_i2 * rs.beta_r_backscat rs.beta_a_backscat = rs.beta_a_backscat_par + rs.beta_a_backscat_perp if not process_defaults.enabled('depolarization_is_aerosol_only'): #user wants bulk depolarization containing both aerosol and molecular print 'computing bulk depolarization--air and aerosol together' rs.circular_depol = rs_constants['combined_to_cross_pol_gain_ratio'] \ * (r_msl.cross_pol_counts\ - rs_constants['polarization_cross_talk']*corr_adjusts['pol_x_talk']\ * r_msl.combined_counts) / r_msl.combined_counts rs.linear_depol = rs.circular_depol / (2 + rs.circular_depol) #compute integrated backscatter cross section rs.integrated_backscat = rs.beta_a_backscat.copy() rs.integrated_backscat[np.isnan(rs.integrated_backscat)] = 0.0 da=rs.msl_altitudes.copy() da[:-1]=da[1:]-da[:-1] da[-1]=da[-2] tda=hau.TZ_Array(np.transpose(da[:,np.newaxis]*np.ones((1,rs.times.size)))) rs.integrated_backscat = np.cumsum(rs.integrated_backscat,1) \ *tda if hasattr(r_msl,'GPS_MSL_Alt'): rs.GPS_MSL_Alt = r_msl.GPS_MSL_Alt if hasattr(r_msl,'combined_1064_counts'): rs.combined_1064_counts = r_msl.combined_1064_counts\ * rs_constants['IR_combined_hi_gain_ratio'] if hasattr(r_msl,'wfov_extinction_corr'): #transf wfov_extinction correction to output structure rs.wfov_extinction_corr = r_msl.wfov_extinction_corr return rs
def search(self, start_time_datetime=None, end_time_datetime=None,reverse_padding=None,timeres_timedelta=None,min_alt_m=None,max_alt_m=None,\ altres_m=None,window_width_timedelta=None,forimage=None,inclusive=False,timesource=None,allow_nans=False,*args, **kwargs): """ :param start_time_datetime: start time (optional) :type start_time_datetime: datetime.datetime :param end_time_datetime: end time (optional) if unspecified, will continue to return frames thru now, unending :type end_time_datetime: datetime.datetime :param reverse_padding: (optional)in the case of reading up to the current time, this timedelta is always subtracted from the current time to get the most recent moment to read :type reverse_padding: datetime.timedelta :param timeres_timedelta: (optional) time resolution, or None to optimized for images :type timeres_timedelta: datetime.timedelta :param min_alt_m: minimum altitude in meters :param max_alt_m: maximum altitude in meters :param altres_m: (optional) altitude resolution :param window_width_timedelta: used with start or end time (not both) to determine preferred return width. if unspecified, will imply the other unspecified, or from now if neither :type window_width_timedelta: datetime.timedelta :param corr_adjusts: correction adjustments. if unspecified, will use default :param block_when_out_of_data: (optional) if unspecified or True, will block for 'timeres_timedelta' until trying for more frames. if False, yield None until more data is available :param forimage: (optional) if provided, is a dict(x=##,y=##) containing preferred x and y pixel count for an image. if no resolutions are provided, these are used to create an optimal one. if not provided, lacking resolutions are native :param inclusive: if true, will expand window to ensure including any data that intersects the requested times """ if len(args): print 'Unused dpl_radar.search args = ',args if len(kwargs): print "Unused dpl_radar.search kwargs = ",kwargs # altitude configuration notes: min and max alt are required. resolution is optional, in which case the process_control will determine pixel count, and thus resolution # time configuration notes: there are many modes to this operation, and two layers # calibration layer: possible parameters are start, end, and window width. (implemented by dpl_calibration.parse_timewindow(), which will return in all cases start and width, and end if will terminate) # specification groups possible: start+end, start+window,start,end+window,window (in order of priority) # start and end specify the range of calibrations to stream # if only one is specified, window is used to roll forward or back the one specified to make the other # if window is not specified, it goes from start to now (defining the window) # if neither are specified, start is set to now-window # if end is not specified, it keeps rolling forward without termination. if it is, it WILL go to that point (even in future) and terminate # if start and window are specified, and start+window is past now, start will be ignored # processing layer: # extra parameter of maxtimeslice specifies the largest volume of actual data to be returned (may be violated if no data is available, and filler piles up) # if it is not specified, natural flow is not interrupted for the sake of data volume # here, it only cares if timeres is not specified. If it is not specified, it will follows the same steps as calibration to find the preferred window size, and use process_control to determine pixel count, and resolution prms={} if reverse_padding!=None: prms['now']=datetime.utcnow()-reverse_padding d=parse_timewindow(start_time_datetime,end_time_datetime,window_width_timedelta,**prms) if forimage!=None: if not isinstance(forimage,dict): import lg_base.core.canvas_info as ci forimage=ci.load_canvas_info()['canvas_pixels'] if altres_m==None: if min_alt_m==None: min_alt_m=0 if max_alt_m==None: max_alt_m=30 altres_m=(max_alt_m-min_alt_m)/forimage['y'] if timeres_timedelta==None: timeres_timedelta=timedelta(seconds=d['windowwidth'].total_seconds()/forimage['x']) from lg_dpl_toolbox.filters import time_frame,resample_altitude def extralibparms():#scope abuse if 'merge' in self.instrument: return dict() return dict(firstaltitude=min_alt_m-altpad,lastaltitude=max_alt_m+altpad) altpad=(2*altres_m) if altres_m!=None else 0 ramannar=self.lib(start=d['starttime'],end=d['endtime'],**extralibparms()) #FIXME too constant, and hardcoded if timeres_timedelta!=None and ramannar.ramanNativeTimeResolution>timeres_timedelta: #dropping time resolution to 'pure native' timeres_timedelta=None if inclusive: #remake with padding padAmount=(2*timeres_timedelta) if timeres_timedelta!=None else None if padAmount is None or padAmount<(2*ramannar.ramanNativeTimeResolution): padAmount=2*ramannar.ramanNativeTimeResolution if d['starttime']: d['starttime']-=padAmount if d['endtime']: d['endtime']+=padAmount altpad=(2*altres_m) if altres_m!=None else 50 ramannar=self.lib(start=d['starttime'],end=d['endtime'],**extralibparms()) elif timeres_timedelta:#just pad the input data. don't mess with the mean narrator below ramannar=self.lib(start=d['starttime']-timeres_timedelta,end=d['endtime']+timeres_timedelta,**extralibparms()) #ramannar=self.rf.RadarPrefilter(ramannar) if timesource is not None: ramannar=time_frame.MeanNarrator(ramannar,timesource=timesource,allow_nans=allow_nans) elif timeres_timedelta is not None: ramannar=time_frame.MeanNarrator(ramannar,basetime=d['starttime'],timeres=timeres_timedelta,endtime=d['endtime'],allow_nans=allow_nans) if 'merge' not in self.instrument and altres_m is not None: requested_altitudes=hau.Z_Array(np.arange(min_alt_m,max_alt_m+altres_m*0.1,altres_m)) ramannar=resample_altitude.ResampleXd(ramannar,'altitudes',requested_altitudes,left=np.NaN,right=np.NaN) return ramannar
def preYield(self,x,attrs,found): if not hasattr(x,'heights') or not hasattr(x,'times') or x.times.size==0: return False if 'merge' in self.ramanType: basealt=0 #don't correct yet for merge elif hasattr(x,'alt'):#platform height from sealevel basealt=x.alt elif "altitude [m AMSL]" in attrs: basealt=float(attrs["altitude [m AMSL]"]) setattr(x,'alt',basealt) print 'Got an altitude from attributes', basealt,x.alt,self.ramanType else: raise RuntimeError("Can't find MSL altitude") dualmerge=hasattr(x,'heights') and hasattr(x,'heights_low') if dualmerge: print 'merging low into high' #print 'high =',x.heights #print 'low =',x.heights_low mergemap=[] merged=[0,0] hlowcount=x.heights_low.size for lidx,alt in enumerate(x.heights_low): idxs=np.where(x.heights==alt)[0] if idxs.size==0: #print 'height_low',alt,'at offset',lidx,'not in high' #raise RuntimeError('missing alt') mergemap.append(None) merged[1]=merged[1]+1 else: if idxs.size>1: print 'heigh_low',alt,'has',len(idxs),'matches' #print 'merging in alt',alt,'from low',lidx,'to high',idxs,'(len',idxs.size,')' mergemap.append(int(idxs.ravel()[0])) merged[0]=merged[0]+1 if merged[1]>0: print 'Successfully merged =',merged[0],' unmerged and dropped =',merged[1] delattr(x,'heights_low') for n,v in vars(x).items(): #print n,v if n.startswith('_'): continue #v=v[altmask] fd = found[n] if n in found else {} self.fixValue(v,fd,attrs,'missing_value',np.NAN) self.fixValue(v,fd,attrs,'missing-data',np.NAN) self.fixValue(v,fd,attrs,'nan_value',np.NAN) units = '' if 'units' not in fd else fd['units'] if '/' in units: sp=units.split('/') numer=sp[0] denom=sp[1] else: numer=units denom='1' if 'km' in denom:# THIS DOES ASSUME ANY KM/M conversions don't stick, and have to be redone here v/=1000.0 if 'km' in numer: v*=1000.0 if 'depol' in n and 'counts' not in n and 'uncert' not in n and (len(units)<2 or units=="unitless") : v*=100.0 if n=='heights' and 'merge' not in self.ramanType: setattr(x,'_altitudevarname','altitudes') setattr(x,"altitudes",hau.Z_Array(v+basealt)) continue if (n.endswith('_low') or '_low_' in n) and len(v.shape)>1 and v.shape[1]==hlowcount and dualmerge: tmp=v hname=n.replace('_low','_high') if not hasattr(x,hname): hname=n.replace('_low','') if not hasattr(x,hname): print "Can't find high equivalent to "+n v=getattr(x,hname).copy() def subslice(chunk=None): ret=[slice(None),slice(chunk)] for x in range(2,len(v.shape)): ret.append(slice(None)) return tuple(ret) v[subslice()]=np.NAN for si,i in enumerate(mergemap): if i is not None: v[subslice(i)]=tmp[subslice(si)] setattr(x,n,v) dt=x.times.copy() dt.summode="sum" setattr(x,'width',dt) setattr(x,'start',x.times.copy()) if dt.size>1: for i in range(dt.size-1): dt[i]=(x.times[i+1]-x.times[i]).total_seconds() dt[-1]=dt[-2] for i,dtv in enumerate(dt): x.start[i]-=timedelta(seconds=dtv/2.0) return True
def msinteg(N1, N2, firstR, lastR, inc, beta, mode_dia, ranges, wavelength, multiple_scattering_parameters, calvals): """ msinteg(N1,N2,firstR,lastR,inc,beta,mode_dia ,ranges,multiple_scattering_parameters,calvals,wavelength) N1 = highest order of scattering to compute. N2 = lowest order of scattering to compute. firstR = begining of the range interval to compute (meters), optical depth below firstR does not contribute to computed result. lastR = end of the range interval to compute (meters). inc = number of range indices between ms computations (normally inc=1). beta = column vector with scattering cross sections (1/m), mode_dia = column vector with mode diameter at each range (m) if supplied as single value it will be expanded to a column vector. ranges = column vector containing the ranges at which beta_c, beta_R, and mode_dia are specified. Answers will be returned at these points. ranges must be equally spaced. Ranges must be supplied in meters, wavelength = lidar wavelength (m) multiple_scatter_parameters['particle_info_source'] == 'measured' | 'constant' ['p180_water'] ['p180_ice'] ['h2o_depol_threshold'] water when depol < threshold ['alpha_water'] ['alpha_ice'] ['g_water'] ['g_ice'] where: N(D) ~ D^alpha * exp(-alpha/g *(D/mode_dia)^g) calvals = dictionary of calibration constants for particular hsrl #old doc header function ms=msinteg(id,N1,N2,firstR,lastR,inc,beta_c,r_particle... ,ranges,wavelength,FOVT,d_beam,FOVR,d_rec,P180,P18_2,P18_n); Python only version of mutliple scatter code-uses ms_int.py a recursive integration routine. The mode diameter of the gamma distribution is used to compute the square of the Gaussian diffraction peak width. See 'http://lidar.ssec.wisc.edu/mscat/derivations' for details. id = integer used in output file name,usually the starting shot number. N1 = highest order of scattering to compute. N2 = lowest order of scattering to compute. firstR = begining of the range interval to compute (meters), optical depth below firstR does not contribute to computed result. lastR = end of the range interval to compute (meters). inc = number of range indices between ms computations (normally inc=1). ranges = column vector containing the ranges at which beta_c, beta_R, and mode_dia are specified. Answers will be returned at these points. ranges must be equally spaced. Ranges must be supplied in meters, beta = column vector with scattering cross sections (1/m), mode_dia = column vector with mode diameter at each range (m) if supplied as single value it will be expanded to a column vector. alpha,gam = gamma size parameters, N(D)=D^alpha *exp(-b*D^gam) lambda = wavelength (m) FOVT = laser divergence in radians. d_beam = 1/e full with of the laser beam at telecope exit FOVR = reciever fov in radians. d_rec = diameter of receiving mirror (m) P180 = backscatter phase function for single scattering. P18_2 = backscatter phase function for second order scattering P18_n = backscatter phase function for all higher orders of scattering. beta_source is a string which will be appended to field name of results. it is expected to indicate the algorithm used to compute the extinction cross section profile used in the calculations. """ #convert angles to half angles--inputs in form of full angle divergence #and program in terms of half angles. FOVR = calvals['telescope_fov'] / 2.0 FOVT = calvals['laser_beam_divergence'] / 2.0 r_beam = calvals['1/e_laser_beam_width'] / 2.0 r_rec = calvals['telescope_diameter'] / 2.0 #wavelength = calvals['wavelength']*1e-9 alpha = multiple_scattering_parameters['alpha_water'] gam = multiple_scattering_parameters['g_water'] nranges = ranges.shape[0] #find index of first and last range involved in this computation first_pt = 1 for i in range(nranges): if ranges[i] <= firstR: first_pt = i if ranges[i] <= lastR: last_pt = i #convert mode diameter to diffraction peak width squared #area weighted diameter-squared from mode diameter of gamma distribution ave_D_squared = mode_dia**2 * (gam / alpha)**2 * ss.gamma( (alpha + 3) / gam) / ss.gamma((alpha + 1) / gam) #theta_s = (4*wavelength^2/(pi^2 *<D^2>) theta_sq = (2 * wavelength / np.pi)**2 / ave_D_squared #if mode_dia is provided as a single value expand into array #if not isinstance(mode_dia,hau.Z_Array): # if not isinstance(mode_dia,np.ndarray): # theta_sq = theta_sq * np.ones_like(ranges) nranges = len(ranges) beta_c = beta.copy() dr = ranges[1] - ranges[0] # cloud optical depth tau_c = np.zeros_like(ranges) beta_c[np.isnan(beta_c)] = 0.0 tau_c[first_pt:last_pt] = np.cumsum(beta_c[first_pt:last_pt])-beta_c[first_pt]/2.0 \ -beta_c[first_pt:last_pt]/2.0 tau_c = tau_c * dr #sum of Rayleigh and cloud optical depth tau_t = np.cumsum(beta_c[first_pt:last_pt])-beta_c[first_pt]/2.0 \ -beta_c[first_pt:last_pt]/2.0 tau_t = tau_t * dr tbeta = np.transpose(beta_c) #time0=cputime; #ms = np.zeros((ranges.shape[0],N2-N1+3)) #compute for subset of ranges if inc > 1 #inc =3 indices = range(first_pt, last_pt, inc) ms = np.zeros((len(indices), N2 + 1)) ms[:, 0] = ranges[indices] s_time = datetime.utcnow() for n_th in range(N1, N2 + 1): j = 0 for i in indices: divlR2 = (FOVT * ranges[i] + r_beam)**2 fovR2 = (FOVR * ranges[i] + r_rec)**2 ms[j,n_th] = ms_int(n_th,ranges \ ,first_pt,i, tbeta,theta_sq,divlR2,fovR2) ms[j, n_th] = tau_c[i]**(n_th - 1) / ss.gamma(n_th) - ms[j, n_th] #print 'Range=%g N=%g tau=%g,ms=%g' %(ranges[i],n_th,tau_c[i]**(n_th-1)/ss.gamma(n_th),ms[j,n_th]) j = j + 1 #interpolate back to input resolution #ms_ratios_profile[number_of_range_bins,number_of_orders_of_scattering + 1] ms_ratios_profile = np.zeros((beta.shape[0], N2 + 1)) ms_ratios_profile[first_pt:last_pt, 0] = ranges[first_pt:last_pt] for i in range(2, N2 + 1): ms_ratios_profile[first_pt:last_pt, i] = np.interp(ranges[first_pt:last_pt], ranges[indices], ms[:, i]) ms_ratios_profile = hau.Z_Array(ms_ratios_profile) print 'time for multiple scatter cal = ', datetime.utcnow() - s_time return ms_ratios_profile
def process(self): #calsource=time_frame.TimeTrickle(self.cals,timename='chunk_start_time',endtimename='chunk_end_time') rif = self.rif pickle = self.pickle lasttime = None calsource = time_frame.TimeTrickle(self.cals, timename='chunk_start_time', endtimename='chunk_end_time') for f in self.datasource: orig = f if self.sourcename is not None: if isinstance(f, dict) and self.sourcename in f: f = f[self.sourcename] elif hasattr(f, self.sourcename): f = getattr(f, self.sourcename) else: print "No Raman Ranged to process. can't find subframe " + self.sourcename continue if f is None or f.times.size == 0: print "No Raman Ranged to process. Raman Merge is empty" continue print "Raman Merge to Ranged running" lasttime = f.times[0] caldictionary = calsource(lasttime) pname = ('frame', 'cal frame') p1 = [] p1.append(pickle.dumps(f)) p1.append(pickle.dumps(caldictionary)) ret = rif.process_ranged_raman(caldictionary['rs_constants'], f, self.raman_process_control, caldictionary['rs_cal'], self.raman_corr_adjusts) p2 = [] p2.append(pickle.dumps(f)) p2.append(pickle.dumps(caldictionary)) for i in range(len(p1)): if p1[i] != p2[i]: print 'Detected modification of ' + pname[i] + '. FAIL' raise RuntimeError('Modification of Raman Ranged ' + pname[i]) hau.verifyAllNew(ramancal=caldictionary, ramanmerge=f, ramanrange=ret) if ret is None: print "Raman Range is none" import time time.sleep(5) elif hasattr(f, 'alt'): #platform height from sealevel setattr(ret, '_altitudevarname', 'altitudes') setattr(ret, "altitudes", hau.Z_Array(f.heights + f.alt)) if self.destination is not None: if isinstance(orig, dict): orig[self.destination] = ret else: setattr(orig, self.destination, ret) yield orig else: yield ret
def profile(self, request_time, request_lat, request_long, offset=0): """returns a profile of temperature,pressure, dew_point, frost point at time, lat, and long extracted from the sounding_archive. If request_lat and request_long are empty they are ignored request_time = python datetime for reqested sounding request_lat = requested latitude for sounding--ignored if [] request_lon = requested longitude for sounding--ignored if []""" if self.soundings == None: return None print 'sounding_type= ', self.sounding_type, request_time if self.sounding_type == 'NOAA raob': temp_sounding = hau.Time_Z_Group() #find correct sounding profile out of archive file sounding = hau.selectByTime(self.soundings, request_time, offset=offset) if sounding is None: return None sounding.sounding_type = self.sounding_type sounding.sounding_id = sounding.station_id sounding.latitude = sounding.stalat sounding.longitude = sounding.stalong sounding.frost_points = cal_frost_point(sounding.dew_points) temp_sounding.type = self.sounding_type temp_sounding.times = sounding.times temp_sounding.altitudes = hau.Z_Array(sounding.altitudes) temp_sounding.temps = hau.TZ_Array(sounding.temps[np.newaxis, :]) temp_sounding.pressures = hau.TZ_Array( sounding.pressures[np.newaxis, :]) temp_sounding.dew_pts = hau.TZ_Array( sounding.dew_points[np.newaxis, :]) temp_sounding.frost_pts = hau.TZ_Array( sounding.frost_points[np.newaxis, :]) temp_sounding.wind_dir = hau.TZ_Array( sounding.wind_dir[np.newaxis, :]) temp_sounding.wind_spd = hau.TZ_Array( sounding.wind_spd[np.newaxis, :]) temp_sounding.wind_spd = hau.TZ_Array( sounding.wind_spd[np.newaxis, :]) temp_sounding.station_id = sounding.station_id temp_sounding.top = sounding.top sounding.times = sounding.times[0] #sounding.expire_time=sounding.expire_time[0] elif self.sounding_type == "time curtain" \ and self.sounding_id == "raqms": temp_sounding = hau.Time_Z_Group() sounding = raqms.select_raqms_profile(self.soundings,request_time \ ,self.requested_altitudes,offset=offset) # ,self.max_alt,self.alt_res) if sounding == None: return None sounding.station_id = self.sounding_id temp_sounding.type = self.sounding_type temp_sounding.times = sounding.times temp_sounding.latitude = hau.T_Array(sounding.latitude) temp_sounding.longitude = hau.T_Array(sounding.longitude) temp_sounding.altitudes = hau.Z_Array(sounding.altitudes) temp_sounding.temps = hau.TZ_Array(sounding.temps[np.newaxis, :]) temp_sounding.pressures = hau.TZ_Array( sounding.pressures[np.newaxis, :]) temp_sounding.dew_pts = hau.TZ_Array( sounding.dew_points[np.newaxis, :]) temp_sounding.frost_pts = hau.TZ_Array( sounding.frost_points[np.newaxis, :]) temp_sounding.wind_dir = hau.TZ_Array( sounding.wind_dir[np.newaxis, :]) temp_sounding.wind_spd = hau.TZ_Array( sounding.wind_spd[np.newaxis, :]) temp_sounding.ext_total = hau.TZ_Array( sounding.ext_total[np.newaxis, :]) temp_sounding.ext_salt = hau.TZ_Array( sounding.ext_salt[np.newaxis, :]) temp_sounding.ext_dust = hau.TZ_Array( sounding.ext_dust[np.newaxis, :]) temp_sounding.wind_spd = hau.TZ_Array( sounding.wind_spd[np.newaxis, :]) temp_sounding.station_id = sounding.station_id temp_sounding.top = sounding.top sounding.times = sounding.times[0] #set up time to read in new sounding (in datetime) #sounding.expire_time = sounding.times+timedelta(seconds=5*60) #expire time can not be less then request time--raqms file must be missing soundings #set expire_time 5 minutes after request time #if sounding.expire_time <= request_time: # print "****Warning----missing raqms sounding at ",request_time # print " using sounding from ",sounding.times # sounding.expire_time = request_time + timedelta(seconds=5*60) #remove any negative pressure values temp_sounding.pressures[temp_sounding.pressures < 0] = 0.0 #remove NaN's from pressure and temperature values temp_sounding.pressures[np.isnan(temp_sounding.pressures)] = 0.0 temp_sounding.temps[np.isnan(temp_sounding.temps)] = 1.0 return sounding
def quick_cal( i2_scan, Cam, Cam_i2a,sounding, wavelength, method_string , i2_scan_corr, i2a_scan_corr): """quick_cal_stream(i2_scan, Cam, sounding, wavelength, method_string, i2_scan_corr) A function which computes hsrl calibration coefficients. at the altitudes specified (in meters) within 'alt_vector' using a precomputed iodine scan file(i2_scan_file). i2_scan(:,:) = input containing i2 scan info i2_scan(:,0) = freq (GHz) -------(:,1) = combined channel scan -------(:,2) = molecular channel scan -------(:,3) = theoretical i2 transmission -------(:,4) = measured i2/combined if bagohsrl with argon buffered i2 cell -------(:,5) = molecular i2a/combined -------(:,6) = molecular i2a channel scan Cam = aerosol in molecular coefficent Cam_i2a = aerosol in argon-buffered molecular channel (will = None when not present) sounding = return structure from read_sounding_file.py contains temp profile wavelength = laser wavelength (nm) method_string = molecular line shape ''maxwellian','tenti_s6','wirtschas' i2_scan_corr = adjustment factor for i2 scan that adjusts i2 molecular channel gain relative to combined channel gain i2a_scan_corr = adjustment factor for i2a scan that adjusts i2a molecular channel gain relative to combined channel gain rs = return structure containing calibration coefficents calibration values are returned at altitudes rs_sounding.alititudes[:] 1 = particulate in combined channel rs.Cmc[i] = molecular in combined channel rs.Cmm[i] = molecular in molecular channel rs.Cam = particulate in molecular channel rs.beta_r[i] = Rayleigh scattering cross section (1/m)""" rs = hau.Time_Z_Group() # calibration structure # i2_scan=cal_vec.i2_scan i2_scan=i2_scan.copy() # if selected use theory*combined as synthetic mol scan print method_string if method_string.find('i2 theory') >= 0: i2_scan[:, 2] = i2_scan[:, 4] * i2_scan[:, 1] # trim i2 scan to +-4 GHz about line center #i2_scan = i2_scan[abs(i2_scan[:, 0]) <= 4, :] # rescale i2 molecular component of i2 scan if required if i2_scan_corr != 1.0: i2_scan[:, 2] = i2_scan[:, 2] * i2_scan_corr # rescale i2a molecular component of i2 scan if required if i2a_scan_corr != 1.0 and i2_scan.shape[1]>6: i2_scan[:, 6] = i2_scan[:, 6] * i2a_scan_corr if 0: import matplotlib.pylab as plt plt.figure(444443) plt.plot(i2_scan[:,0],i2_scan[:,1:3],i2_scan[:,0],i2_scan[:,2]/i2_scan[:,1],'k') plt.xlabel('freq GHz') plt.grid(True) # trim i2 scan to +-4 GHz about line center i2_scan = i2_scan[abs(i2_scan[:, 0]) <= 4, :] if 0: import matplotlib.pylab as plt plt.figure(444444) plt.plot(i2_scan[:,0],i2_scan[:,1:3]) plt.xlabel('freq GHz') plt.grid(True) # compute Rayleigh scattering cross section # see R Holz thesis for this equation giving the Rayleigh scatter cross section. # beta=3.78e-6*press/temp at a wavelength of 532 nm # then rescale to actual wavelength nalts = sounding.altitudes.shape[0] if not (nalts==sounding.pressures.shape[0] and nalts==sounding.temps.shape[0]): print "ERROR: SOMETHIGN BAD ABOUT SOUNDING AT TIME ",sounding.times return None rs.beta_r = hau.Z_Array(np.zeros( nalts )) rs.beta_r[:nalts] = 3.78e-6 * sounding.pressures[:nalts] / sounding.temps[:nalts] rs.beta_r = rs.beta_r * (532.0 / wavelength) ** 4 # spectral width of molecular scattering m_bar = 28.97 * 1.65978e-27 # average mass of an air molecule sigma_0 = 1 / (wavelength * 1e-9) # number in 1/meters kb = 1.38044e-23 # Boltzmans constant J/(K deg) c = 3e8 # speed of light in m/s sigma = i2_scan[:, 0] * 1e9 / c # wavenumber vector rs.Cmm = hau.Z_Array(np.zeros( nalts )) rs.Cmc = hau.Z_Array(np.zeros( nalts )) sample_altitudes = np.zeros(nalts) #for bagohsrl with argon buffered i2 cell if len(i2_scan[0,:]) > 6: rs.Cmm_i2a = hau.Z_Array(np.zeros( nalts )) rs.Cam_i2a = Cam_i2a rs.Cam = Cam print 'RBS computed with '+method_string+ ' spectrum' spectrum_time = datetime.utcnow() dz = sounding.altitudes[2]-sounding.altitudes[1] delta_i = np.int(np.ceil(300.0/dz)) nk=int(nalts/delta_i) if delta_i>1 and nk<2:# if interpolation is to happen, but not enough to interpolate, force it to the edge nk=2 delta_i=nalts-1 sample_altitudes = np.zeros(nk) rs.msl_altitudes = sounding.altitudes.copy() i=0 k=0 while k < len(sample_altitudes): if not np.isfinite(sounding.temps[i]) or not np.isfinite(sounding.pressures[i]): i=i+delta_i k=k+1 continue if method_string.find('maxwellian') >= 0: norm = m_bar * c ** 2 / (8 * sigma_0 ** 2 * kb * sounding.temps[ i]) spectrum = np.exp(-norm * sigma ** 2) elif method_string.find('tenti_s6') >= 0: from tenti_s6 import tenti_s6 spectrum = tenti_s6(wavelength * 1e-9,sounding.temps[i], sounding.pressures[ i], i2_scan[:, 0] * 1e9) elif method_string.find('witschas') >= 0: spectrum = witschas_spectrum(sounding.temps[i], sounding.pressures[ i], wavelength * 1e-9, i2_scan[:, 0] * 1e9) spectrum = spectrum / sum(spectrum) sample_altitudes[k] = sounding.altitudes[i] rs.Cmc[ k] = sum(spectrum * i2_scan[:, 1]) rs.Cmm[ k] = sum(spectrum * i2_scan[:, 2]) if i2_scan.shape[1]>6: rs.Cmm_i2a[ k] = sum(spectrum * i2_scan[:, 6]) i = i + delta_i k = k + 1 # if Cxx computed at less than full altitude resolution if delta_i >1: rs.Cmc = np.interp(sounding.altitudes,sample_altitudes[0:k-1] ,rs.Cmc[0:k-1]) rs.Cmm = np.interp(sounding.altitudes,sample_altitudes[0:k-1] ,rs.Cmm[0:k-1]) if hasattr(rs,'Cmm_i2a'): rs.Cmm_i2a = np.interp(sounding.altitudes,sample_altitudes[0:k-1] ,rs.Cmm_i2a[0:k-1]) print method_string, 'computed for ',k-1,' altitudes in '\ , (datetime.utcnow() - spectrum_time).total_seconds(),' seconds' plots = 0 if plots: import matplotlib.pyplot as plt plt.figure(600) plt.plot(i2_scan[:, 0], spectrum[:]) fig = plt.grid(True) plt.xlabel('Frequency (GHz)') plt.ylabel('Intensity') # ax=gca() # ax.set_yscale('log') plt.figure(601) plt.plot(rs.Cmm,sounding.altitudes/1000.0,'b' ,rs.Cmm_i2a,sounding.altitudes/1000.0,'r') plt.grid(True) plt.xlabel('Cmm, Cmm_i2a') plt.ylabel('Altitude') plt.show() # add sounding identification to return structure rs.sounding_id = sounding.station_id rs.sounding_time = sounding.times return rs
def geometry_correction(apply_to, rs, rs_cal, nbins, s_bin, apply_geo_corr=None): """geometry_correction(selected_vars,rs,rs_cal,nalts,s_bin,wfov_mol_ratio=Noneapply_geo_corr=None) apply_to = apply geo correction to these variables rs = structure containing channel counts rs_cal = structure containing geo_corr data nbins = number of altitude bins to process s_bin = range bin containing laser pulse wfov_mol_ratio = ratio of molecular_wfov_counts to molecular counts (optional correction) apply_geo_corr = """ for field in apply_to: if hasattr(rs, field): ones_array = np.ones(getattr(rs, field).shape) break if apply_geo_corr is None or apply_geo_corr > 0: geocorr = rs_cal.geo.geo_correction[:nbins - s_bin] * ones_array[:, s_bin:nbins] elif apply_geo_corr == 0: #r-squared correction geocorr = (rs_cal.geo.data[:(nbins - s_bin), 0]**2 * ones_array[:, s_bin:nbins]) / 1e6 else: #no correction applied return rs if hasattr(rs,'telescope_pointing') and hasattr(rs_cal,'ngeo') \ and (rs.telescope_pointing<0.1).any(): mask = (rs.telescope_pointing < 0.1) indices = arange(ones_array.shape[0]) indices = indices[mask] geocorr[indices,:] = rs_cal.ngeo.data[:nalts - s_bin, 1]\ * ones_array[indices, s_bin:nalts] for field in apply_to: if hasattr(rs, field): temp = getattr(rs, field) #print 'field_________________________',field if 0: #field == 'nitrogen_counts_low': import matplotlib.pylab as plt bin_vec = np.arange(temp.shape[1]) plt.figure(77777) plt.plot(bin_vec, temp[0, :], 'b', s_bin + np.arange(nbins - s_bin), geocorr[0, :], 'g') temp[:, s_bin:nbins] *= geocorr if 0: #field == 'nitrogen_counts_low': plt.plot(bin_vec, np.nanmean(temp, 0), 'r') setattr(rs, field, temp) #add geo_corr to rs and offset by s_bin to align with range scale of data buffers geo_corr = np.zeros(ones_array.shape[1]) geo_corr[s_bin:nbins] = rs_cal.geo.data[:nbins - s_bin, 1].copy() rs.geo_corr = hau.Z_Array(geo_corr) #make full resolution geo_corr array with sbin offset to match data arrays rs.geo_corr_array = hau.TZ_Array(np.zeros(ones_array.shape)) rs.geo_corr_array[:, s_bin:nbins] = geocorr return rs