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 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 process(self): for f in self.framestream: if hasattr(f, 'Backscatter'): f = copy.copy(f) setattr( f, 'Reflectivity', hau.TZ_Array( self.backscatterCrossSectionToReflectivity( f.Backscatter))) yield f
def radar_masking(rs_radar, processing_defaults, instrument): """radar_masking(rs_radar,processing_defaults) rs_radar = structure generated by radar processing stream processing_defaults = config info from 'radar_processing_defaults.json' qc_radar_mask, bit[0] = longical and of all other bits qc_radar_mask, bit[1] = cleared if radar refectivity fall below SNR threshhold """ assert (rs_radar != None) if not hasattr(rs_radar, 'Backscatter'): return rs_radar threshhold = processing_defaults.get_value('radar_SNR_mask', 'threshhold') #radar mask ok when sig_to_noise greater than threshhold in dB rs_radar = copy.copy(rs_radar) #fix me this has been changed to a single bit because the interpolator is #not aware of summode rs_radar.qc_radar_mask = \ hau.TZ_Array(np.ones(rs_radar.Backscatter.shape),summode='or',dtype='uint16') #rs_radar.qc_radar_mask[:,:]=65535 #mask = np.ones_like(rs_radar.Backscatter) #mask[rs_radar.SignalToNoiseRatio < threshhold] = 0 #mask = ~(3*mask.astype('uint16')) #rs_radar.qc_radar_mask &= mask rs_radar.qc_radar_mask[rs_radar.SignalToNoiseRatio < threshhold] = 0 rs_radar.qc_radar_mask[np.isnan(rs_radar.SignalToNoiseRatio)] = 0 print print 'instrument', instrument if (instrument == 'magkazrge' or 'magmwacr') \ and processing_defaults.enabled('ship_motion_correction'): temp = rs_radar.MeanDopplerVelocity.copy() temp[rs_radar.qc_radar_mask == 0] = np.NaN rs_radar.vertically_averaged_doppler = hau.T_Array(nanmean(temp, 1)) return rs_radar
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 process_mass_dimension_particle(rs_inv, rs_radar, particle_parameters, lambda_radar, entire_frame): """ generate and return the particle measurements based on a given hsrl inverted data, radar (and its lambda), and particle parameters dictionary """ ParticleParameters = namedtuple('ParticleParameters', ','.join(particle_parameters.keys())) pparm = ParticleParameters( **particle_parameters ) #pparm is a structure of the particle parameters, instead of a dictionary 'particle_parameters' #create timez group and add heights rs_particle = hau.Time_Z_Group(rs_inv.times.copy(), timevarname='times', altname='heights') setattr(rs_particle, 'heights', rs_inv.msl_altitudes.copy()) #remove points where lidar signal is noise dominated by setting to #very small value. clipped_beta_a_back = rs_inv.beta_a_backscat.copy() if hasattr(rs_inv, 'std_beta_a_backscat'): clipped_beta_a_back[clipped_beta_a_back < ( 2 * rs_inv.std_beta_a_backscat)] = -np.inf else: print 'No std_beta_a_backscat statistics to filter particle measurements' clipped_beta_a_back[np.logical_not(np.isfinite( rs_inv.beta_a_backscat))] = -np.inf used_beta_a = None if hasattr(rs_inv, 'beta_a'): used_beta_a = rs_inv.beta_a.copy() #mask beta_a? rs_particle.effective_diameter_prime,used_beta_a = \ lred.lidar_radar_eff_diameter_prime( beta_a_backscat=clipped_beta_a_back ,radar_backscat=rs_radar.Backscatter ,depol=rs_inv.linear_depol ,h2o_depol_threshold=particle_parameters['h2o_depol_threshold'] ,beta_a=used_beta_a ,p180_ice=particle_parameters['p180_ice'] ,lambda_radar=lambda_radar) rs_particle.effective_diameter,rs_particle.num_particles,rs_particle.LWC,rs_particle.mean_diameter=\ lred.d_eff_from_d_eff_prime( rs_particle.effective_diameter_prime ,clipped_beta_a_back ,rs_inv.linear_depol ,used_beta_a ,pparm) #rs_particle.hsrl_radar_rain_rate = 3600 * 10* .0001 * rs_particle.LWC * rs_radar.MeanDopplerVelocity #convert to mks units LWC kg/m^3, Doppler m/s, rain_rate m/s rs_particle.hsrl_radar_rain_rate = 0.001 * rs_particle.LWC * rs_radar.MeanDopplerVelocity #retype all these fields to a proper TZ_Array for f in [ 'hsrl_radar_rain_rate', 'effective_diameter_prime', 'effective_diameter', 'num_particles', 'LWC', 'mean_diameter' ]: if hasattr(rs_particle, f): setattr(rs_particle, f, hau.TZ_Array(getattr(rs_particle, f))) return rs_particle
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 read_raqms_file(instrument, start_time): """read raqms file between start and end time instrument - e.g. 'gvhsrl' start_time - datetime object """ raqms = hau.Time_Z_Group(altname='model_level_alts') filename = find_raqms_filename(instrument, start_time) if not filename: return None nc = Dataset(filename, 'r') times = getAll(nc, 'time') aircraft_alts = getAll(nc, 'alt') pressures = getAll(nc, 'pressure') temperatures = getAll(nc, 'temperature') model_level_alts = getAll(nc, 'altitude') relative_humidity = getAll(nc, 'rh') latitude = getAll(nc, 'lat') longitude = getAll(nc, 'lon') u_vel = getAll(nc, 'uvel') v_vel = getAll(nc, 'vvel') ext_total = getAll(nc, 'ext_tot') ext_dust = getAll(nc, 'ext_dust') ext_salt = getAll(nc, 'ext_salt') base_time = datetime(start_time.year, start_time.month, start_time.day, 0, 0, 0) #np.fix(start_time) #time=times.astype('float64') #convert raqms seconds from start of day to python datetimes #times=base_time + time/(3600.0*24.0) times = hau.T_Array( [base_time + timedelta(seconds=float(x)) for x in times]) assert (times.size > 0) selectedMask = (times > start_time) for i, x in enumerate(selectedMask): fi = i if x: if i > 0: selectedMask[i - 1] = True break selectedMask[-1] = True selectedTimes = np.arange(times.size)[selectedMask] raqms.latitude = hau.T_Array(latitude[selectedTimes]) raqms.longitude = hau.T_Array(longitude[selectedTimes]) raqms.pressures = hau.TZ_Array(pressures[selectedTimes, :]) raqms.temperatures = hau.TZ_Array(temperatures[selectedTimes, :]) raqms.ext_total = hau.TZ_Array(ext_total[selectedTimes, :]) raqms.ext_dust = hau.TZ_Array(ext_dust[selectedTimes, :]) raqms.ext_salt = hau.TZ_Array(ext_salt[selectedTimes, :]) raqms.relative_humidity = hau.TZ_Array(relative_humidity[selectedTimes, :]) raqms.u_vel = hau.TZ_Array(u_vel[selectedTimes, :]) raqms.v_vel = hau.TZ_Array(v_vel[selectedTimes, :]) raqms.model_level_alts = hau.TZ_Array(model_level_alts[selectedTimes, :] * 1000.0) raqms.times = times[selectedTimes] return raqms
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 compute_optical_depth(Nm, beta_r_backscat, msl_altitudes, processing_defaults, constants, telescope_pointing=None): """uses Nm and beta_r_backscat to compute the optical depth profile with the optical depth profile set to zero at an alititude given by 'mol_norm_alt' which must provided in meters. returns: od = total optical depth od_aerosol = aerosol optical depth mol_norm_index = bin number at which optical depth is normalized to zero mol_ref_aod = estimated optical depth at altitudes[mol_norm_index]""" #mol_od_between_lidar_and_norm_alt = 0.0 mol_norm_alt = processing_defaults.get_value('mol_norm_alt', 'meters') mol_norm_index = len(msl_altitudes[msl_altitudes <= mol_norm_alt]) if processing_defaults.enabled('molecular_smooth'): window_offset = np.float( processing_defaults.get_value('molecular_smooth', 'window_length')) / 2.0 else: window_offset = 0.0 if ('installation' not in constants or constants['installation'] == 'ground' or constants['installation'] == 'shipborne')\ and mol_norm_alt < (constants['lidar_altitude']+150.0 + window_offset): mol_norm_alt = constants['lidar_altitude'] + 150. + window_offset mol_norm_index = len(msl_altitudes[msl_altitudes <= mol_norm_alt]) lidar_alt_index = len( msl_altitudes[msl_altitudes <= constants['lidar_altitude']]) print 'requested molecular norm altitude too low-- reset at ', mol_norm_alt, ' m' processing_defaults.set_value('mol_norm_alt', 'meters', mol_norm_alt) od = hau.TZ_Array(np.NaN * np.zeros_like(Nm)) indices = np.arange(len(msl_altitudes)) if processing_defaults.enabled('molecular_smooth'): window = processing_defaults.get_value('molecular_smooth', 'window_length') if mol_norm_alt < constants['lidar_altitude'] + 150 + window / 2.0: mol_norm_alt = constants['lidar_altitude'] + 150 + window / 2.0 mol_norm_index = len(msl_altitudes[msl_altitudes <= mol_norm_alt]) print ' ' print 'Warning:******requested mol_normalization_altitude not within acquired data' print ' setting normalization altitude = ', msl_altitudes[ mol_norm_index] print ' ' if mol_norm_index >= len(msl_altitudes): mol_norm_index = len(msl_altitudes) - 1 print ' ' print 'Warning:******requested normalization altitude higher than requested range' print ' setting normalization altitude =', msl_altitudes[ mol_norm_index] if mol_norm_index <= Nm.shape[1]: if 0: print print print 'alts', msl_altitudes.shape print 'beta_r', beta_r_backscat.shape print 'Nm', Nm.shape print 'norm_alt', msl_altitudes[mol_norm_index] print import matplotlib.pylab as plt plt.figure(2000) plt.plot( msl_altitudes, np.nanmean(Nm, 0) * beta_r_backscat[mol_norm_index] / np.nanmean(Nm[:, mol_norm_index], 0), msl_altitudes, beta_r_backscat) plt.ylabel('altitude') ax = plt.gca() ax.set_yscale('log') plt.grid(True) time_vec = np.ones_like(Nm[:, 0]) bin_vec = np.ones_like(beta_r_backscat) beta_r_array = (time_vec[:, np.newaxis] * beta_r_backscat[np.newaxis, :]) Nm_norm_array = Nm[:, mol_norm_index] Nm_norm_array = Nm_norm_array[:, np.newaxis] * bin_vec[np.newaxis, :] prelog_od = Nm[:, :] / ( beta_r_array * Nm_norm_array) * beta_r_backscat[mol_norm_index] prelog_od[np.isnan(prelog_od)] = 0 prelog_od[prelog_od <= 0.0] = np.NaN od = -0.5 * np.log(prelog_od) if telescope_pointing is not None: tmpod = od od = od.copy() od[:, :] = np.NAN od[telescope_pointing < 0.1, :] = -1.0 * tmpod[ telescope_pointing < 0.1, :] od[telescope_pointing > 0.9, :] = tmpod[ telescope_pointing > 0.9, :] dz = msl_altitudes[1] - msl_altitudes[0] mol_od = 8.0 * np.pi * np.cumsum(beta_r_backscat) * dz / 3.0 mol_od = mol_od - mol_od[mol_norm_index] mol_od_array = time_vec[:, np.newaxis] * mol_od[np.newaxis, :] od_aerosol = od - mol_od_array else: od[:, :] = np.NaN od_aerosol[:, :] = od.copy() print ' ' print '*******requested od_normalization altitude not within acquired data' print ' ' #compute optical depth below mol_norm_index #extrapolate from just above mol_norm_index to estimate unmeasured optical depth if dz >= 100: n_bins = 1 else: n_bins = int(100.0 / dz + 1) norm_alt_range = msl_altitudes[mol_norm_index] - constants['lidar_altitude'] mol_ref_aod = (od[:, mol_norm_index + n_bins] - od[:, mol_norm_index]) * (norm_alt_range / (dz * n_bins)) mol_ref_aod -= (mol_od_array[:,mol_norm_index + n_bins] - mol_od_array[:,mol_norm_index])\ *norm_alt_range/(dz*n_bins) mol_ref_aod = hau.T_Array(mol_ref_aod) od = hau.TZ_Array(od) od_aerosol = hau.TZ_Array(od_aerosol) return (od, od_aerosol, mol_norm_index, mol_ref_aod)
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
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 read_sounding_file(instrument, sounding_type, id, start_time, requested_altitudes): """ read_sounding_file([instrument,sounding_type,id,start_time,alt_res,max_alt) returns arrays rs.temps(sounding#, rs.dew_points(sounding#,alt_index), rs.wdir(sounding#,alt_index), rs.wspd(sounding#,alt_index) along with several scalers with sounding info instrument (e.g. 'ahsrl','gvhsrl','mf2hsrl','nshsrl') sounding_type may be radiosonde station id, model identifier, or other instrument sounding_type (e.g. 'NOAA','ARM',....... sounding id (for sounding_type=NOAA, this a 3-letter e.g. 'MSN') start_time first time for which the sounding is needed, provided as matplot time requested_altitudes is a vector of altitudes at which sounding values are requested (m) returns temp_sounding(object) with all soundings from the current file after the starting time returns the last time at which this sounding can be used as rs.expire_time""" import lg_dpl_toolbox.core.archival as hru if sounding_type[:].find('NOAA raob') >= 0: rs = hau.Time_Z_Group() time_struct = start_time dir_path = hru.get_path_to_data(instrument, start_time) filename = dir_path + '/' + '%4i' % time_struct.year + '/' \ + '%02i' % time_struct.month + '/sondes.' + id[:] + '.nc' print 'sounding file--', filename if not os.path.exists(filename): return None #raise RuntimeError, 'sounding file %s does not exist' \ # % filename nc = Dataset(filename, 'r') times = getAll(nc, 'synTime') # combine mandatory and sig height measurements heights = np.hstack((getAll(nc, 'htMan'), getAll(nc, 'htSigT'))) epoch = datetime(1970, 1, 1, 0, 0, 0) t_mask = times < 1e36 for i in range(len(times)): t_mask[i] = times[i] < 1e36 and any(heights[i, :] < 1e36) times = times[t_mask] times = [epoch + timedelta(seconds=soff) for soff in times[:]] # select times, one prior to start time --> last profile in file #indices = np.arange(len(times)) #start_index = max(indices[times <= start_time]) #rs.times = zeros(len(times) - start_index) rs.times = np.zeros(len(times)) #rs.times = times[start_index:] # rs.times = hau.T_Array( rs.times ) rs.times = times[:] wmosta = getAll(nc, 'wmoStat') # wmo station number stalong = getAll(nc, 'staLon') stalat = getAll(nc, 'staLat') # combine mandatory and sig height measurements temps = np.hstack((getAll(nc, 'tpMan'), getAll(nc, 'tpSigT'))) pressures = np.hstack((getAll(nc, 'prMan'), getAll(nc, 'prSigT'))) dew_points = np.hstack((getAll(nc, 'tdMan'), getAll(nc, 'tdSigT'))) wind_dir = np.hstack((getAll(nc, 'wdMan'), getAll(nc, 'wdSigT'))) wind_spd = np.hstack((getAll(nc, 'wsMan'), getAll(nc, 'wsSigT'))) heights = heights[t_mask, :] temps = temps[t_mask, :] pressures = pressures[t_mask, :] dew_points = dew_points[t_mask, :] wind_dir = wind_dir[t_mask, :] wind_spd = wind_spd[t_mask, :] [n_soundings, n_heights] = temps.shape # defined standard atmosphere climatology for use above highest reported level # climate=temp_sounding() climate = hau.Time_Z_Group() climate.altitudes = np.zeros((n_soundings, 9)) climate.temps = np.zeros((n_soundings, 9)) climate.pressures = np.zeros((n_soundings, 9)) climate.dew_pt = np.zeros((n_soundings, 9)) climate.wind_spd = np.zeros((n_soundings, 9)) climate.wind_dir = np.zeros((n_soundings, 9)) # find the highest valid point in each sounding rs.top = np.zeros((n_soundings, )) rs.bot = np.zeros((n_soundings, )) # climate.altitudes[0,:]=array([10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000, 50000]) for i in range(n_soundings): mask = heights[i, :] <= 50000 if np.any(mask == True): rs.top[i] = max(heights[i, mask]) rs.bot[i] = min(heights[i, temps[i, :] != 99999]) else: rs.top[i] = 0.0 rs.bot[i] = 0.0 rs.top = hau.T_Array(rs.top) rs.bot = hau.T_Array(rs.bot) climate.altitudes[i, :] = np.array([ 10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, ]) climate.temps[i, :] = np.array([ 223.1, 216, 216, 221, 226, 237, 251, 265, 270, ]) climate.pressures[i, :] = np.array([ 264.3, 120.45, 54.75, 25.11, 11.71, 5.58, 2.77, 1.43, 0.759, ]) climate.dew_pt[i, :] = np.NaN # don't use climatology lower than 2km above highest valid measurement climate.altitudes[climate.altitudes <= rs.top[i] + 2000] = \ 9e36 climate.temps[climate.altitudes <= rs.top[i] + 2000] = 9e36 climate.pressures[climate.altitudes <= rs.top[i] + 2000] = \ 9e36 # stack the climatology on top of the observations heights = np.hstack((heights, climate.altitudes)) temps = np.hstack((temps, climate.temps)) pressures = np.hstack((pressures, climate.pressures)) dew_points = np.hstack((dew_points, climate.dew_pt)) wind_dir = np.hstack((wind_dir, climate.wind_dir)) wind_spd = np.hstack((wind_spd, climate.wind_spd)) #print heights.shape heights_unsorted = heights.copy() temps_unsorted = temps.copy() pressures_unsorted = pressures.copy() dew_points_unsorted = dew_points.copy() wind_dir_unsorted = wind_dir.copy() wind_spd_unsorted = wind_spd.copy() for i in range(heights_unsorted.shape[0]): indices = np.argsort(heights_unsorted[i, :]) heights[i, :] = heights_unsorted[i, indices] temps[i, :] = temps_unsorted[i, indices] pressures[i, :] = pressures_unsorted[i, indices] dew_points[i, :] = dew_points_unsorted[i, indices] wind_dir[i, :] = wind_dir_unsorted[i, indices] wind_spd[i, :] = wind_spd_unsorted[i, indices] # sort combined file by height and select times of interest if 0: indices = heights.argsort(axis=1) index_a = np.transpose( np.transpose(np.ones(heights.shape, dtype=int)) * np.arange(heights.shape[0])) heights = heights[index_a, indices] temps = temps[index_a, indices] pressures = pressures[index_a, indices] dew_points = dew_points[index_a, indices] wind_dir = wind_dir[index_a, indices] wind_spd = wind_spd[index_a, indices] pressures[heights > 1e5] = np.NaN temps[heights > 1e5] = np.NaN dew_points[heights > 1e5] = np.NaN # interpolate to altitude resolution requested # and remove missing data points max_alt = requested_altitudes[-1] max_bin = requested_altitudes.shape[0] alts = requested_altitudes n_soundings = len(rs.times) #max_bin = round(max_alt / float(alt_res)) + 1 # create sounding arrays as hau class items rs.altitudes = hau.TZ_Array(np.zeros((n_soundings, max_bin))) rs.temps = hau.TZ_Array(np.zeros((n_soundings, max_bin))) rs.dew_points = hau.TZ_Array(np.zeros((n_soundings, max_bin))) rs.pressures = hau.TZ_Array(np.zeros((n_soundings, max_bin))) rs.wind_spd = hau.TZ_Array(np.zeros((n_soundings, max_bin))) rs.wind_dir = hau.TZ_Array(np.zeros((n_soundings, max_bin))) #rs.times =hau.T_Array(np.zeros(n_soundings)) rs.times = hau.T_Array(times) #rs.expire_time =hau.T_Array(np.zeros(n_soundings)) rs.stalat = hau.T_Array(np.zeros(n_soundings)) rs.stalong = hau.T_Array(np.zeros(n_soundings)) rs.wmosta = hau.T_Array(np.zeros(n_soundings)) rs.stalat[:] = stalat[0] rs.stalong[:] = stalong[0] rs.wmosta[:] = wmosta[0] rs.station_id = id # interpolate to lidar altitude scale for i in range(n_soundings): rs.altitudes[i, :] = alts k = i rs.temps[i, :] = np.interp(alts, heights[k, temps[k, :] != 99999], temps[k, temps[k, :] != 99999]) rs.dew_points[i, :] = np.interp( alts, heights[k, dew_points[k, :] != 99999], dew_points[k, dew_points[k, :] != 99999]) #now using spline fit to pressures #should use hydrostatic equation to interpolate press1 = pressures[k, pressures[k, :] != 99999] h1 = heights[k, pressures[k, :] != 99999] temp = interpolate.splrep(h1[~np.isnan(press1)], press1[~np.isnan(press1)]) rs.pressures[i, :] = interpolate.splev(alts, temp, der=0) rs.wind_spd[i, :] = np.interp(alts, heights[k, wind_spd[k, :] != 99999], wind_spd[k, wind_spd[k, :] != 99999]) rs.wind_dir[i, :] = np.interp(alts, heights[k, wind_dir[k, :] != 99999], wind_dir[k, wind_dir[k, :] != 99999]) #if i < n_soundings - 1: # #rs.expire_time[i] = (rs.times[i] + rs.times[i + 1]) # rs.expire_time = rs.times[i] + timedelta(seconds=(rs.times[i+1] - rs.times[i]).total_seconds() / 2.0) + timedelta(seconds=60*30) # add 1/2 hour to point to next sounding #else: # #rs.expire_time[i] = rs.times[i] + 0.25 + 1 / 48.0 # new sonde profile expected in 6 1/2 hrs # rs.expire_time = rs.times[i] + timedelta(days=0.25, seconds= 30*60) # new sonde profile expected in 6 1/2 hrs # convert dew point depression to dew point temp rs.dew_points = rs.temps - rs.dew_points plots = 0 # FIXME if plots == 1: import matplotlib.pylab as plt plt.figure(801) plt.plot(temps[0, :], heights[0, :] / 1000, 'r', dew_points[0, :], heights[0, :] / 1000) fig = plt.grid(True) plt.xlabel('deg-K ') plt.ylabel('Altitude MSL (km)') plt.title('Temperature, dew point') plt.figure(802) #set_printoptions(threshold=np.NaN) plt.plot(rs.temps[0, :], rs.altitudes[0, :] / 1000, 'r', rs.dew_points[0, :], rs.altitudes[0, :] / 1000) fig = plt.grid(True) plt.xlabel('deg-K ') plt.ylabel('Altitude MSL (km)') plt.title('Temperature, dew point') plt.figure(803) plt.plot(pressures[0, :], heights[0, :] / 1000, 'r') fig = plt.grid(True) ax = plt.gca() #ax.set_xscale('log') plt.xlabel('deg-K ') plt.ylabel('Altitude MSL (km)') plt.title('Pressures') plt.figure(804) bin_vec = range(len(heights[0, :])) heights[heights > 1000] = np.NaN plt.plot(heights[0, :] / 1000, bin_vec, 'r') fig = plt.grid(True) ax = plt.gca() #ax.set_xscale('log') plt.xlabel('altitudes') plt.ylabel('bins') plt.title('Heights') plt.show() raise RuntimeError('deliberate abort') else: print ' ' print 'ERROR**************unknown sounding source************' print ' ' rs = [] return rs
def applyCorrection(self, rs_mean): rs_mean.cross_pol_counts = hau.TZ_Array(self.virtual_cross_counts) rs_mean.combined_counts = hau.TZ_Array(self.virtual_total_counts) rs_mean.molecular_counts = hau.TZ_Array(self.virtual_mol_counts) # insert other computed variables here rs_mean.f11 = hau.TZ_Array(self.f11) rs_mean.f12 = hau.TZ_Array(self.f12) rs_mean.f13 = hau.TZ_Array(self.f13) rs_mean.f14 = hau.TZ_Array(self.f14) rs_mean.f22 = hau.TZ_Array(self.f22) rs_mean.f23 = hau.TZ_Array(self.f23) rs_mean.f24 = hau.TZ_Array(self.f24) rs_mean.f33 = hau.TZ_Array(self.f33) rs_mean.f34 = hau.TZ_Array(self.f34) rs_mean.f44 = hau.TZ_Array(self.f44)
def process_spheroid_particle(rs_inv, rs_radar, particle_parameters, lambda_radar, entire_frame, sounding=None, size_dist=None): """ process_spheroid_particle(rs_inv,rs_radar,particle_parameters,lambda_radar,entire_frame, sounding=None,p180_water=None,size_dist=None): generate and return the particle measurements based on a given hsrl inverted data, radar (and its lambda), and particle parameters dictionary """ #create timez group and add heights rs_particle = hau.Time_Z_Group(rs_inv.times.copy(), timevarname='times', altname='heights') setattr(rs_particle, 'heights', rs_inv.msl_altitudes.copy()) setattr(rs_particle, 'delta_t', rs_inv.delta_t.copy()) #remove points where lidar signal is noise dominated by setting to #very small value. #clipped_beta_a_back=rs_inv.beta_a_backscat.copy() #if 0: #if hasattr(rs_inv,'std_beta_a_backscat'): # clipped_beta_a_back[clipped_beta_a_back<(2*rs_inv.std_beta_a_backscat)]=-np.inf #else: # print 'No std_beta_a_backscat statistics to filter particle measurements' #clipped_beta_a_back[np.logical_not(np.isfinite(rs_inv.beta_a_backscat))]=-np.inf; rs_particle.q_backscatter = np.NaN * np.zeros_like(rs_inv.beta_a_backscat) rs_particle.phase = np.zeros_like(rs_inv.beta_a_backscat) rs_particle.phase[ rs_inv.linear_depol > particle_parameters['h2o_depol_threshold']] = 1 rs_particle.phase[np.isnan(rs_inv.beta_a_backscat)] = np.NaN #set aspect ratio parameter for ice filled bins rs_particle.zeta = np.ones(rs_inv.beta_a_backscat.shape) rs_particle.zeta[rs_inv.linear_depol > particle_parameters['h2o_depol_threshold']] \ = particle_parameters['zeta'] print 'Extinction cross section for particle size calculations derived from ' \ ,particle_parameters['ext_source'] print 'Extinction due nonprecipitating aerosols = '\ ,particle_parameters['background_aerosol_bs'],'1/(m sr)' #store the mask field with the particle info rs_particle.qc_mask = rs_inv.qc_mask.copy() clipped_beta_a_backscat = rs_inv.beta_a_backscat.copy() copy_radar_backscatter = rs_radar.Backscatter.copy() #clipped_beta_a_backscat = copy_beta_a.copy() clipped_beta_a_backscat = clipped_beta_a_backscat \ - particle_parameters['background_aerosol_bs'] clipped_beta_a_backscat[clipped_beta_a_backscat < 0] = np.NaN #create an empty mode_diameter array rs_particle.mode_diameter = np.zeros_like(rs_inv.beta_a_backscat) #create an empty array for extinction--used only for particle calculations #bs_ratio_to_dmode will return extinction cross section in clipped_beta_a clipped_beta_a = np.NaN * np.zeros_like(rs_inv.beta_a_backscat) #water #compute mode diameter, extinction cross section, and backscatter efficeincy #from radar and lidar backscatter cross sections using mie theory and assumed #size distribution to predict mode diameter and q_backscatter for points #identified as water. if particle_parameters['radar_model'] == "Mie": rs_particle.mode_diameter, clipped_beta_a, rs_particle.q_backscatter\ ,rs_particle.dstar \ = size_dist.dmode_from_radar_lidar_mie(copy_radar_backscatter\ ,clipped_beta_a_backscat) else: #use only Rayliegh approx solution--particle_parameter['radar_model']=="Rayleigh" #mode diameter is computed for all points assuming everything is water #subsequent calculation will replace ice phase points. if particle_parameters['ext_source'] == 'ext': clipped_beta_a = rs_inv.extinction_aerosol.copy() elif particle_parameters['ext_source'] == 'bs/p180': clipped_beta_a = clipped_beta_a_backscat / particle_parameters[ 'p180_water'] else: print 'particle_parameters=', particle_parameters[ 'ext_source'], ' not supported' print 'in spheroid_particle_processing' print j clipped_beta_a[np.isnan(clipped_beta_a_backscat)] = np.NaN phase = np.zeros_like(rs_inv.beta_a_backscat) zeta = np.ones_like(rs_inv.beta_a_backscat) rs_particle.mode_diameter = size_dist.dmode_from_lidar_radar_rayleigh( rs_particle.mode_diameter, clipped_beta_a, copy_radar_backscatter, zeta, phase) #ice #compute extinction cross section for ice points using backscatter phase function clipped_beta_a[rs_particle.phase==1] = \ clipped_beta_a_backscat[rs_particle.phase==1]/particle_parameters['p180_ice'] zeta = np.zeros_like(clipped_beta_a) zeta[rs_particle.phase == 1] = particle_parameters['zeta'] #derive mode_diameter directly from radar backscatter and lidar extinction #cross sections for parts of image populated by ice rs_particle.mode_diameter[rs_particle.phase==1] = size_dist.dmode_from_lidar_radar_rayleigh(\ rs_particle.mode_diameter[rs_particle.phase==1] \ ,clipped_beta_a[rs_particle.phase==1],copy_radar_backscatter[rs_particle.phase==1]\ ,zeta[rs_particle.phase==1],rs_particle.phase[rs_particle.phase==1]) #creates effective_diameter_prime array from mode diameter rs_particle.effective_diameter_prime = \ size_dist.deff_prime(rs_particle.mode_diameter,rs_particle.phase,zeta) rs_particle.effective_diameter = size_dist.eff_diameter(\ rs_particle.mode_diameter,rs_particle.phase) rs_particle.mean_diameter = size_dist.mean_diameter(\ rs_particle.mode_diameter,rs_particle.phase) #compute liquid water content for bins with phase == 0 #bins with phase > 0 will return with NaN's if particle_parameters['radar_model'] == "Mie": rs_particle.LWC = su.liquid_water_content_mie( rs_particle.effective_diameter, clipped_beta_a, rs_particle.q_backscatter) rs_particle.p180_extinction = rs_inv.beta_a_backscat / rs_particle.q_backscatter else: if particle_parameters['ext_source'] == 'bs/p180': rs_particle.extinction_aerosol = rs_inv.beta_a_backscat / particle_parameters[ 'p180_water'] clipped_beta_a = rs_particle.extinction_aerosol.copy() else: clipped_beta_a = rs_inv.extinction_aerosol.copy() clipped_beta_a[np.isnan(clipped_beta_a_backscat)] = np.NaN rs_particle.LWC = np.NaN * np.zeros_like( rs_particle.effective_diameter) su.liquid_water_content_ext_approx(rs_particle.LWC, rs_particle.effective_diameter, clipped_beta_a, rs_particle.phase) rs_particle.p180_extinction = rs_inv.beta_a_backscat / particle_parameters[ 'p180_water'] rs_particle.extinction_aerosol = rs_inv.extinction_aerosol.copy() #compute ice water water content for bins with phase > 0 (kg/m^3) #return in LWC array bins with phase > 0 su.ice_water_content(rs_particle.LWC, rs_particle.effective_diameter, clipped_beta_a, rs_particle.phase) if hasattr(rs_radar, 'vertically_averaged_doppler'): rs_radar.raw_MeanDopplerVelocity = rs_radar.MeanDopplerVelocity.copy() motion_correction = np.transpose(rs_radar.vertically_averaged_doppler\ *np.transpose(np.ones_like(rs_radar.MeanDopplerVelocity))) rs_radar.MeanDopplerVelocity -= motion_correction if sounding != None: s_time = datetime.utcnow() rs_particle.rw_fall_velocity,rs_particle.mw_fall_velocity \ ,rs_particle.model_spectral_width,rs_particle.nw_fall_velocity\ = su.weighted_fall_velocity( rs_particle.mode_diameter ,particle_parameters ,rs_particle.zeta ,sounding.temps ,sounding.pressures ,rs_particle.phase,size_dist) print 'time for fall_velocity = ', datetime.utcnow() - s_time # compute precip rate (m/s) #rain_rate = 1/density # (m^3/kg) * LWC (kg/m^3) * fall_velocity (m/s) #using # Doppler velocity rs_particle.hsrl_radar_dv_precip_rate = # 0.001 * rs_particle.LWC * rs_radar.MeanDopplerVelocity #using raw Doppler to give precip rate in m/s rs_particle.hsrl_radar_dv_precip_rate = 0.001 * rs_particle.LWC * rs_radar.MeanDopplerVelocity #remove points with Doppler folding rs_particle.hsrl_radar_dv_precip_rate[ rs_radar.MeanDopplerVelocity < -2.0] = np.NaN if sounding != None: #using modeled mass weighted velocity and dividing by the density of water, # 1000 kg/m^3, to give precip_rate in m/s rs_particle.hsrl_radar_precip_rate = 0.001 * rs_particle.LWC * rs_particle.mw_fall_velocity #retype all these fields to a proper TZ_Array #for f in ['effective_diameter_prime']: for f in vars(rs_particle).keys(): v = getattr(rs_particle, f) if isinstance(v, hau.Z_Array): continue #properly typed. continue elif isinstance(v, np.ndarray): if len(v.shape) == 2: setattr(rs_particle, f, hau.TZ_Array(v)) elif len(v.shape) == 1: print '1 Dimensional Variable ' + f + ' will be changed to a T_Array. FIXME!!!!' setattr(rs_particle, f, hau.T_Array(v)) else: raise RuntimeError( "I don't know what to type particle array " + f + " with dimensions " + repr(v.shape)) else: pass #not an array type. should be safe to ignore """ #compute fitting error of computed radar weighted fall velocity #to measured Doppler veleocity. temp = rs_radar.Backscatter.copy() temp[np.isnan(rs_radar.Backscatter)]=0.0 temp[rs_inv.msl_altitudes>400]=0.0 fitting_error = np.sqrt(nanmean((rs_particle.rw_fall_velocity[temp >1e-9] \ - rs_radar.MeanDopplerVelocity[temp >1e-9])**2)) print print rs_radar.times[0],' ---> ' ,rs_radar.times[-1] print 'fitting_error (m/s)= ',fitting_error print """ 'rs_particle--spher' print dir(rs_particle) return rs_particle
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