def integrate_GH_pts(list_GH_pts): num_beams = len(list_GH_pts) list_variables = list_GH_pts[0].values.keys() integrated_variables = {} for k in list_variables: integrated_variables[k] = [float('nan')] for i in list_GH_pts: integrated_variables[k] = utilities.nansum_arr( integrated_variables[k], i.values[k] * i.GH_weight) # Get index of central beam idx_0 = int(num_beams / 2) # Sum the mask of all beams to get overall mask mask = np.zeros( num_beams, ) # This mask serves to tell if the measured point is ok, or below topo or above COSMO domain for i, p in enumerate(list_GH_pts): mask = utilities.sum_arr(mask, p.mask) # Get mask of every Beam mask /= float( num_beams ) # Larger than 1 means that every Beam is below TOPO, smaller than 0 that at least one Beam is above COSMO domain mask[np.logical_and(mask >= 0, mask < 1)] = 0 heights_radar = list_GH_pts[idx_0].heights_profile distances_radar = list_GH_pts[idx_0].dist_profile lats = list_GH_pts[idx_0].lats_profile lons = list_GH_pts[idx_0].lons_profile integrated_beam = Beam(integrated_variables, mask, lats, lons, distances_radar, heights_radar) return integrated_beam
def get_doppler_velocity(list_beams, lut_sz=0): ########################################################################### # Get setup global doppler_scheme global microphysics_scheme doppler_scheme = cfg.CONFIG['doppler']['scheme'] microphysics_scheme = cfg.CONFIG['microphysics']['scheme'] # Check if necessary variables for turbulence scheme are present if doppler_scheme == 4 and 'EDR' in list_beams.keys(): add_turb = True else: add_turb = False if doppler_scheme == 4: print( 'No eddy dissipitation rate variable found in COSMO file, could not use doppler_scheme == 4, using doppler_scheme == 3 instead' ) # Get dimensions num_beams = len(list_beams) # Number of beams idx_0 = int(num_beams / 2) # Index of central beam len_beams = max([len(l.dist_profile) for l in list_beams]) # Beam length if microphysics_scheme == 1: hydrom_types = ['R', 'S', 'G'] # Rain, snow and graupel elif microphysics_scheme == 2: hydrom_types = ['R', 'S', 'G', 'H'] # Add hail # Create dic of hydrometeors list_hydrom = {} for h in hydrom_types: list_hydrom[h] = hydrometeors.create_hydrometeor( h, microphysics_scheme) ########################################################################### # Get radial wind and doppler spectrum (if scheme == 1 or 2) if doppler_scheme == 1 or doppler_scheme == 2: rvel_avg = np.zeros(len_beams, ) * float( 'nan') # average radial velocity sum_weights = np.zeros(len_beams, ) # mask of GH weights for beam in list_beams: if doppler_scheme == 1: # Weighting by PSD only v_hydro = get_v_hydro_unweighted(beam, list_hydrom) elif doppler_scheme == 2: # Weighting by RCS and PSD v_hydro = get_v_hydro_weighted(beam, list_hydrom, lut_sz) # Get radial velocity knowing hydrometeor fall speed and U,V,W from model theta = beam.elev_profile * DEG2RAD phi = beam.GH_pt[0] * DEG2RAD proj_wind = proj_vel(beam.values['U'], beam.values['V'], beam.values['W'], v_hydro, theta, phi) # Get mask of valid values sum_weights = utilities.sum_arr( sum_weights, ~np.isnan(proj_wind) * beam.GH_weight) # Average radial velocity for all sub-beams rvel_avg = utilities.nansum_arr(rvel_avg, (proj_wind) * beam.GH_weight) # We need to divide by the total weights of valid beams at every bin rvel_avg /= sum_weights elif doppler_scheme == 3: rvel_avg = np.zeros(len_beams, ) doppler_spectrum = np.zeros((len_beams, len(constants.VARRAY))) for beam in list_beams: beam_spectrum = get_doppler_spectrum( beam, list_hydrom, lut_sz) * beam.GH_weight # Multiply by GH weight if add_turb: # Spectrum spread caused by turbulence turb_std = get_turb_std(constants.RANGE_RADAR, beam.values['EDR']) beam_spectrum = turb_spectrum_spread(beam_spectrum, turb_std) doppler_spectrum += beam_spectrum try: rvel_avg = np.sum(np.tile(constants.VARRAY, (len_beams, 1)) * doppler_spectrum, axis=1) / np.sum(doppler_spectrum, axis=1) except: rvel_avg *= float('nan') ########################################################################### # Get mask # This mask serves to tell if the measured point is ok, or below topo or above COSMO domain mask = np.zeros(len_beams, ) for i, beam in enumerate(list_beams): mask = utilities.sum_arr(mask, beam.mask) # Get mask of every Beam mask /= num_beams # Larger than 1 means that every Beam is below TOPO, smaller than 0 that at least one Beam is above COSMO domain mask[np.logical_and(mask >= 0, mask < 1)] = 0 # Finally get vectors of distances, height and lat/lon at the central beam idx_0 = int(len(list_beams) / 2) heights_radar = list_beams[idx_0].heights_profile distances_radar = list_beams[idx_0].dist_profile lats = list_beams[idx_0].lats_profile lons = list_beams[idx_0].lons_profile if doppler_scheme == 3: dic_vars = {'RVEL': rvel_avg, 'DSPECTRUM': doppler_spectrum} else: # No doppler spectrum is computed dic_vars = {'RVEL': rvel_avg} beam_doppler = Beam(dic_vars, mask, lats, lons, distances_radar, heights_radar) return beam_doppler
def get_profiles_GH(dic_variables, azimuth, elevation, radar_range=0, N=0, list_refraction=0): list_variables = dic_variables.values() keys = dic_variables.keys() # Get options bandwidth_3dB = cfg.CONFIG['radar']['3dB_beamwidth'] integration_scheme = cfg.CONFIG['integration']['scheme'] refraction_method = cfg.CONFIG['refraction']['scheme'] list_variables = dic_variables.values() keys = dic_variables.keys() # Calculate quadrature weights if integration_scheme == 1: # Classical single gaussian scheme nh_GH = int(cfg.CONFIG['integration']['nh_GH']) nv_GH = int(cfg.CONFIG['integration']['nv_GH']) # Get GH points and weights sigma = bandwidth_3dB / (2 * np.sqrt(2 * np.log(2))) pts_hor, weights_hor = np.polynomial.hermite.hermgauss(nh_GH) pts_hor = pts_hor * sigma pts_ver, weights_ver = np.polynomial.hermite.hermgauss(nv_GH) pts_ver = pts_ver * sigma weights = np.outer(weights_hor * sigma, weights_ver * sigma) weights *= np.abs(np.cos(pts_ver)) sum_weights = np.sum(weights.ravel()) beam_broadening = nh_GH > 1 or nv_GH > 1 # Boolean for beam-broadening (if only one GH point : No beam-broadening) elif integration_scheme == 2: # Improved multi-gaussian scheme nr_GH = int(cfg.CONFIG['integration']['nr_GH']) na_GL = int(cfg.CONFIG['integration']['na_GL']) antenna_params = cfg.CONFIG['integration']['antenna_params'] pts_ang, w_ang = np.polynomial.legendre.leggauss(na_GL) pts_rad, w_rad = np.polynomial.hermite.hermgauss(nr_GH) a_dB = antenna_params[:, 0] mu = antenna_params[:, 1] sigma = antenna_params[:, 2] list_pts = [] weights = [] sum_weights = 0 for i in range(nr_GH): for j in range(len(sigma)): for k in range(na_GL): r = mu[j] + np.sqrt(2) * sigma[j] * pts_rad[i] theta = np.pi * pts_ang[k] + np.pi weight = np.pi * w_ang[k] * w_rad[i] * 10**( 0.1 * a_dB[j]) * np.sqrt(2) * sigma[j] * abs( r) # Laplacian weight *= np.cos(r * np.sin(theta)) weights.append(weight) sum_weights += weight list_pts.append([ r * np.cos(theta) + azimuth, r * np.sin(theta) + elevation ]) elif integration_scheme == 3: # Gauss-Legendre with real-antenna weighting nh_GH = int(cfg.CONFIG['integration']['nh_GH']) nv_GH = int(cfg.CONFIG['integration']['nv_GH']) antenna = np.genfromtxt(cfg.CONFIG['integration']['antenna_diagram'], delimiter=',') angles = antenna[:, 0] power_sq = (10**(0.1 * antenna[:, 1]))**2 bounds = np.max(angles) pts_hor, weights_hor = np.polynomial.legendre.leggauss(nh_GH) pts_hor = pts_hor * bounds pts_ver, weights_ver = np.polynomial.legendre.leggauss(nv_GH) pts_ver = pts_ver * bounds power_sq_pts = (utilities.vector_1d_to_polar(angles, power_sq, pts_hor, pts_ver).T) weights = power_sq_pts * np.outer(weights_hor, weights_ver) weights *= np.abs(np.cos(pts_ver)) weights *= 2 * bounds sum_weights = np.sum(weights.ravel()) beam_broadening = nh_GH > 1 or nv_GH > 1 # Boolean for beam-broadening (if only one GH point : No beam-broadening) # # elif integration_scheme == 3.5: # Gauss-Legendre with real-antenna weighting # nh_GH = int(cfg.CONFIG['integration']['nh_GH']) # nv_GH = int(cfg.CONFIG['integration']['nv_GH']) # # antenna = np.genfromtxt(cfg.CONFIG['integration']['antenna_diagram'],delimiter=',') # # angles = antenna[:,0] # power_sq = (10**(0.1*antenna[:,1]))**2 # bounds = np.max(angles) # # pts_hor=np.linspace(-bounds,bounds,nh_GH) # # pts_ver=np.linspace(-bounds,bounds,nv_GH) # # power_sq_pts = (utilities.vector_1d_to_polar(angles,power_sq,pts_hor,pts_ver).T) # weights = power_sq_pts # weights *= np.abs(np.cos(pts_ver)) # weights *= 2*bounds # # sum_weights = np.sum(weights.ravel()) # beam_broadening=nh_GH>1 or nv_GH>1 # Boolean for beam-broadening (if only one GH point : No beam-broadening) elif integration_scheme == 4: # Real antenna, for testing only data = pickle.load( open('/storage/cosmo_pol/tests/real_antenna_ss.p', 'rb')) angles = data['angles'] power_squ = (data['data'].T)**2 pts_hor = angles pts_ver = angles # threshold = -np.Inf beam_broadening = True # In this scheme we always consider several beams weights = power_squ * np.abs(np.cos(pts_ver)) sum_weights = np.sum(weights) elif integration_scheme == 5: # Discrete Gautschi quadrature nh_GA = int(cfg.CONFIG['integration']['nh_GH']) nv_GA = int(cfg.CONFIG['integration']['nv_GH']) antenna = np.genfromtxt(cfg.CONFIG['integration']['antenna_diagram'], delimiter=',') angles = antenna[:, 0] power_sq = (10**(0.1 * antenna[:, 1]))**2 bounds = np.max(angles) antenna_fit = interp.interp1d(angles, power_sq, fill_value=0) antenna_fit_weighted = interp.interp1d( angles, power_sq * np.cos(np.abs(np.deg2rad(angles))), fill_value=0) pts_hor, weights_hor = quadrature.get_points_and_weights( antenna_fit, -bounds, bounds, nh_GA) pts_ver, weights_ver = quadrature.get_points_and_weights( antenna_fit_weighted, -bounds, bounds, nv_GA) weights = np.outer(weights_hor, weights_ver) sum_weights = np.sum(weights.ravel()) beam_broadening = nh_GA > 1 or nv_GA > 1 # Boolean for beam-broadening (if only one GH point : No beam-broadening) elif integration_scheme == 6: # Sparse Gauss-Hermite nh_GH = int(cfg.CONFIG['integration']['nh_GH']) nv_GH = int(cfg.CONFIG['integration']['nv_GH']) # Get GH points and weights sigma = bandwidth_3dB / (2 * np.sqrt(2 * np.log(2))) grid = SparseGrids.SparseGrid(dim=2, qrule=SparseGrids.GQN, k=int( cfg.CONFIG['integration']['nh_GH']), sym=True) XF, W = grid.sparseGrid() W = np.squeeze(W) XF *= sigma weights = W weights *= np.abs(np.cos(XF[:, 1])) * sigma pts_hor = XF[:, 0] + azimuth pts_ver = XF[:, 1] + elevation list_pts = [pt for pt in zip(pts_hor, pts_ver)] sum_weights = np.sum(weights) beam_broadening = nh_GH > 1 or nv_GH > 1 # Boolean for beam-broadening (if only one GH point : No beam-broadening) # Keep only weights above threshold if integration_scheme != 6: weights_sort = np.sort(np.array(weights).ravel())[::-1] # Desc. order weights_cumsum = np.cumsum(weights_sort / sum_weights) weights_cumsum[-1] = 1. # Avoid floating precision issues idx_above = np.where( weights_cumsum >= cfg.CONFIG['integration']['weight_threshold'] )[0][0] threshold = weights_sort[idx_above] sum_weights = np.sum(weights_sort[weights_sort >= threshold]) else: threshold = -np.inf # Get beams list_beams = [] # create vector of bin positions rranges = np.arange(cfg.CONFIG['radar']['radial_resolution'] / 2, cfg.CONFIG['radar']['range'], cfg.CONFIG['radar']['radial_resolution']) if integration_scheme not in [2, 6]: # Only regular grids! if list_refraction == 0: # Calculate refraction for vertical GH points list_refraction = [] # Get coordinates of virtual radar radar_pos = cfg.CONFIG['radar']['coords'] for pt in pts_ver: if cfg.CONFIG['radar']['type'] == 'GPM': S, H, E = atm_refraction.get_GPM_refraction(pt + elevation) else: S, H, E = atm_refraction.get_radar_refraction( rranges, pt + elevation, radar_pos, refraction_method, N) list_refraction.append((S, H, E)) for i in range(len(pts_hor)): for j in range(len(pts_ver)): if weights[i, j] >= threshold or not beam_broadening: # GH coordinates pt = [pts_hor[i] + azimuth, pts_ver[j] + elevation] weight = weights[i, j] / sum_weights # Interpolate beam lats, lons, b = get_radar_beam_trilin( list_variables, pts_hor[i] + azimuth, list_refraction[j][0], list_refraction[j][1]) # Create dictionary of beams dic_beams = {} for k, bi in enumerate( b): # Loop on interpolated variables if k == 0: # Do this only for the first variable (same mask for all variables) mask_beam = np.zeros((len(bi))) mask_beam[ bi == -9999] = -1 # Means that the interpolated point is above COSMO domain mask_beam[np.isnan( bi )] = 1 # Means that the interpolated point is below COSMO terrain bi[mask_beam != 0] = float( 'nan') # Assign NaN to all missing data dic_beams[keys[k]] = bi # Create dictionary list_beams.append( Beam(dic_beams, mask_beam, lats, lons, list_refraction[j][0], list_refraction[j][1], list_refraction[j][2], pt, weight)) else: # create vector of bin positions # Get coordinates of virtual radar radar_pos = cfg.CONFIG['radar']['coords'] for i in range(len(list_pts)): if weights[i] >= threshold: if cfg.CONFIG['radar']['type'] == 'GPM': S, H, E = atm_refraction.get_GPM_refraction(list_pts[i][1]) else: S, H, E = atm_refraction.get_radar_refraction( rranges, list_pts[i][1], radar_pos, refraction_method, N) lats, lons, b = get_radar_beam_trilin(list_variables, list_pts[i][0], S, H) # Create dictionary of beams dic_beams = {} for k, bi in enumerate(b): # Loop on interpolated variables if k == 0: # Do this only for the first variable (same mask for all variables) mask_beam = np.zeros((len(bi))) mask_beam[ bi == -9999] = -1 # Means that the interpolated point is above COSMO domain mask_beam[np.isnan( bi )] = 1 # NaN means that the interpolated point is below COSMO terrain bi[mask_beam != 0] = float( 'nan') # Assign NaN to all missing data dic_beams[keys[k]] = bi # Create dictionary list_beams.append( Beam(dic_beams, mask_beam, lats, lons, S, H, E, list_pts[i], weights[i] / sum_weights)) return list_beams
def get_radar_observables(list_beams, lut_sz): ########################################################################### # Get setup att_corr = cfg.CONFIG['attenuation']['correction'] hydrom_scheme = cfg.CONFIG['microphysics']['scheme'] # Get dimensions num_beams = len(list_beams) # Number of beams idx_0 = int(num_beams / 2) # Index of central beam len_beams = len(list_beams[idx_0].dist_profile) # Beam length # Initialize radial_res = cfg.CONFIG['radar']['radial_resolution'] if hydrom_scheme == 1: hydrom_types = ['R', 'S', 'G'] # Rain, snow and graupel elif hydrom_scheme == 2: hydrom_types = ['R', 'S', 'G', 'H'] # Add hail # Initialize matrices sz_integ = np.zeros((len_beams, len(hydrom_types), 12), dtype='float32') sz_integ.fill(np.nan) ########################################################################### for j, h in enumerate(hydrom_types): # Loop on hydrometeors # Create a hydrometeor instance scheme = '2mom' if hydrom_scheme == 2 else '1mom' hydrom = create_hydrometeor(h, scheme) # Get list of diameters for this hydrometeor list_D = lut_sz[h].axes[lut_sz[h].axes_names['d']] # Diameter bin size dD = list_D[1] - list_D[0] for i, beam in enumerate(list_beams): # Loop on subbeams # For GPM some sub-beams are longer than the main beam, so we discard # the "excess" part for k in beam.values.keys(): beam.values[k] = beam.values[k][0:len_beams] valid_data = beam.values['Q' + h + '_v'] > 0 elev = beam.elev_profile # Since lookup tables are defined for angles >0, we have to check # if angles are larger than 90°, in that case we take 180-elevation # by symmetricity elev[elev > 90] = 180 - elev[elev > 90] # Also check if angles are smaller than 0, in that case, flip sign elev[elev < 0] = -elev[elev < 0] T = beam.values['T'] ''' Part 1: Query of the SZ Lookup table ''' # Get SZ matrix sz = lut_sz[h].lookup_line(e=elev[valid_data], t=T[valid_data]) ''' Part 2 : Get the PSD of the particles ''' QM = beam.values['Q' + h + '_v'] # Get mass densities # 1 Moment case if hydrom_scheme == 1: if h != 'S': hydrom.set_psd(QM[valid_data]) else: # For snow N0 is Q and temperature dependent hydrom.set_psd(T[valid_data], QM[valid_data]) # 2 Moment case elif hydrom_scheme == 2: QN = beam.values['QN' + h + '_v'] # Get concentrations as well hydrom.set_psd(QN[valid_data], QM[valid_data]) # Compute particle numbers for all diameters N = hydrom.get_N(list_D) if len(N.shape) == 1: N = np.reshape( N, [len(N), 1]) # To be consistent with the einsum dimensions ''' Part 3 : Integrate the SZ coefficients ''' sz_psd_integ = np.einsum('ijk,ji->ik', sz, N) * dD sz_integ[valid_data, j, :] = nansum_arr(sz_integ[valid_data, j, :], sz_psd_integ * beam.GH_weight) # Finally we integrate for all hydrometeors sz_integ = np.nansum(sz_integ, axis=1) sz_integ[sz_integ == 0] = np.nan # Get radar observables ZH, ZV, ZDR, RHOHV, KDP, AH, AV, DELTA_HV = get_pol_from_sz(sz_integ) # print 10*np.log10(ZH)[0] # print(sz_integ[0]) KDP_m = KDP + DELTA_HV # Account for differential phase on prop. PHIDP = nan_cumsum(2 * KDP_m) * radial_res / 1000. if att_corr: # AH and AV are in dB so we need to convert them to linear ZV *= nan_cumprod( 10**(-0.1 * AV * (radial_res / 1000.))) # divide to get dist in km ZH *= nan_cumprod(10**(-0.1 * AH * (radial_res / 1000.))) # print(nan_cumprod(10**(-0.1*AH*(radial_res/1000.)))) ZDR = ZH / ZV ########################################################################### # Create outputs rad_obs = {} rad_obs['ZH'] = ZH rad_obs['ZDR'] = ZDR rad_obs['ZV'] = ZV rad_obs['KDP'] = KDP_m rad_obs['DELTA_HV'] = DELTA_HV rad_obs['PHIDP'] = PHIDP rad_obs['RHOHV'] = RHOHV rad_obs['AH'] = AH rad_obs['AV'] = AV # This mask serves to tell if the measured point is ok, or below topo or above COSMO domain mask = np.zeros(len_beams, ) for i, beam in enumerate(list_beams): mask = sum_arr(mask, beam.mask[0:len_beams], cst=1) # Get mask of every Beam # Larger than 0 means that at least one Beam is below TOPO, smaller than 0 that at least one Beam is above COSMO domain mask /= num_beams mask[np.logical_and( mask >= 0, mask < 1 )] = 0 # If at least one beam is above topo, we still consider this gate # Finally get vectors of distances, height and lat/lon at the central beam heights_radar = list_beams[idx_0].heights_profile distances_radar = list_beams[idx_0].dist_profile lats = list_beams[idx_0].lats_profile lons = list_beams[idx_0].lons_profile beam_pol = Beam(rad_obs, mask, lats, lons, distances_radar, heights_radar) return beam_pol
def get_profiles(interpolation_mode, dic_variables, azimuth, elevation, radar_range=0, N=0, list_refraction=0): list_variables = dic_variables.values() keys = dic_variables.keys() # Get options bandwidth_3dB = config['radar_3dB_beamwidth'] ########################################################################### if interpolation_mode == 'GH': nh_GH = int(config['nh_GH']) nv_GH = int(config['nv_GH']) # Get GH points and weights sigma = bandwidth_3dB / (2 * np.sqrt(2 * np.log(2))) pts_hor, weights_hor = np.polynomial.hermite.hermgauss(nh_GH) pts_hor = pts_hor * np.sqrt(2) * sigma pts_ver, weights_ver = np.polynomial.hermite.hermgauss(nv_GH) pts_ver = pts_ver * np.sqrt(2) * sigma weights = np.outer(weights_hor, weights_ver) threshold = np.mean([ (weights_hor[0] * weights_hor[int(nh_GH / 2)]) / (nv_GH * nh_GH), (weights_ver[0] * weights_ver[int(nv_GH / 2)]) / (nv_GH * nh_GH) ]) sum_weights = np.pi beam_broadening = nh_GH > 1 and nv_GH > 1 # Boolean for beam-broadening (if only one GH point : No beam-broadening) ########################################################################### if interpolation_mode == 'GH_improved': n_rad = 9 n_ang = 9 x_leg, w_leg = np.polynomial.legendre.leggauss(n_ang) x_her, w_her = np.polynomial.hermite.hermgauss(n_rad) # bw = [5,1.5,1.5,1.8,1.5,1.5,5] sigma = [2, 2, 0.68574, 2, 2, 0.39374, 1.21239] mu = [-12.86, -7.0931, 0, 0.2948, 8.37059, 10.05448, 13.20] a_dB = [-44.219, -37.7577, 0, -24.0645, -42.1912, -41.4037, -44.7814] list_pts = [] sum_weights = 0 for i in range(n_rad): for j in range(len(sigma)): for k in range(n_ang): r = mu[j] + np.sqrt(2) * sigma[j] * x_her[i] theta = np.pi * x_leg[k] + np.pi weight = np.pi * w_her[i] * w_leg[k] * 10**( 0.1 * a_dB[j]) * np.sqrt(2) * sigma[j] * abs( r) # Laplacian sum_weights += weight list_pts.append([ weight, [ r * np.cos(theta) + azimuth, r * np.sin(theta) + elevation ] ]) beam_broadening = n_rad > 1 or n_ang > 1 # Boolean for beam-broadening (if only one GH point : No beam-broadening) ########################################################################### elif interpolation_mode == 'Gauss': # Get points and weights sigma = bandwidth_3dB / (2 * np.sqrt(2 * np.log(2))) data = pickle.load(open('real_antenna_s.p', 'rb')) angles = data['angles'] pts_hor = angles pts_ver = angles threshold = -np.Inf beam_broadening = True ax, ay = np.meshgrid(angles, angles) d = (ax**2 + ay**2) weights = 1 / (2 * np.pi * sigma) * np.exp( -0.5 * d / sigma**2) * (angles[1] - angles[0])**2 sum_weights = np.sum(weights) ########################################################################### elif interpolation_mode == 'Real': data = pickle.load(open('real_antenna.p', 'rb')) angles = data['angles'] pts_hor = angles pts_ver = angles threshold = -np.Inf beam_broadening = True weights = data['data'] sum_weights = np.sum(weights) ########################################################################### elif interpolation_mode == 'Real_s': data = pickle.load(open('real_antenna_s.p', 'rb')) angles = data['angles'] pts_hor = angles pts_ver = angles threshold = -np.Inf beam_broadening = True weights = data['data'] sum_weights = np.sum(weights) list_beams = [] if interpolation_mode == 'GH_improved': # create vector of bin positions bins_ranges = np.arange(config['radar_rres'] / 2, config['radar_range'], config['radar_rres']) # Get coordinates of virtual radar radar_pos = config['radar_coords'] refraction_method = '4/3' # Other methods take too much time if no quadrature scheme is used for i in range(len(list_pts)): S, H, E = atm_refraction.get_radar_refraction( bins_ranges, list_pts[i][1][1], radar_pos, refraction_method, N) lats, lons, b = get_radar_beam_trilin(list_variables, list_pts[i][0], S, H) # Create dictionary of beams dic_beams = {} for k, bi in enumerate(b): # Loop on interpolated variables if k == 0: # Do this only for the first variable (same mask for all variables) mask_beam = np.zeros((len(bi))) mask_beam[ bi == -9999] = -1 # Means that the interpolated point is above COSMO domain mask_beam[np.isnan( bi )] = 1 # NaN means that the interpolated point is below COSMO terrain bi[mask_beam != 0] = float( 'nan') # Assign NaN to all missing data dic_beams[keys[k]] = bi # Create dictionary list_beams.append( Beam(dic_beams, mask_beam, lats, lons, S, H, E, list_pts[i][1], list_pts[i][0] / sum_weights)) else: print interpolation_mode if list_refraction == 0: # Calculate refraction for vertical GH points list_refraction = [] # create vector of bin positions bins_ranges = np.arange(config['radar_rres'] / 2, config['radar_range'], config['radar_rres']) # Get coordinates of virtual radar radar_pos = config['radar_coords'] refraction_method = '4/3' # Other methods take too much time if no quadrature scheme is used for pt in pts_ver: S, H, E = atm_refraction.get_radar_refraction( bins_ranges, pt + elevation, radar_pos, refraction_method, N) list_refraction.append((S, H, E)) for i in range(len(pts_hor)): print i for j in range(len(pts_ver)): if weights[i, j] > threshold or not beam_broadening: # GH coordinates pt = [pts_hor[i] + azimuth, pts_ver[j] + elevation] weight = weights[i, j] / sum_weights # Interpolate beam lats, lons, b = get_radar_beam_trilin( list_variables, pts_hor[i] + azimuth, list_refraction[j][0], list_refraction[j][1]) # Create dictionary of beams dic_beams = {} for k, bi in enumerate( b): # Loop on interpolated variables if k == 0: # Do this only for the first variable (same mask for all variables) mask_beam = np.zeros((len(bi))) mask_beam[ bi == -9999] = -1 # Means that the interpolated point is above COSMO domain mask_beam[np.isnan( bi )] = 1 # NaN means that the interpolated point is below COSMO terrain bi[mask_beam != 0] = float( 'nan') # Assign NaN to all missing data dic_beams[keys[k]] = bi # Create dictionary list_beams.append( Beam(dic_beams, mask_beam, lats, lons, list_refraction[j][0], list_refraction[j][1], list_refraction[j][2], pt, weight)) return list_beams
def get_radar_observables(list_beams, lut): ########################################################################### # Get setup att_corr = cfg.CONFIG['attenuation']['correction'] hydrom_scheme = cfg.CONFIG['microphysics']['scheme'] # Get dimensions num_beams = len(list_beams) idx_0 = int(num_beams / 2) len_beams = len(list_beams[idx_0].dist_profile) # Initialize if att_corr: # Compute radar bins range radial_res = cfg.CONFIG['radar']['radial_resolution'] if hydrom_scheme == 1: hydrom_types = ['R', 'S', 'G'] # Rain, snow and graupel elif hydrom_scheme == 2: hydrom_types = ['R', 'S', 'G', 'H'] # Add hail rad_obs_integrated = {} rad_obs = {} for o in LIST_OBSERVABLES: rad_obs_integrated[o] = np.zeros( (len_beams, len(hydrom_types))) * float('nan') rad_obs[o] = np.zeros( (len_beams, len(hydrom_types), num_beams)) * float('nan') ########################################################################### for j, h in enumerate(hydrom_types): # Loop on hydrometeors sum_weights = np.zeros((len_beams, )) for i, beam in enumerate(list_beams[0:]): # Loop on subbeams elev = beam.elev_profile # Since lookup tables are defined for angles >0, we have to check # if angles are larger than 90°, in that case we take 180-elevation # by symmetricity elev[elev > 90] = 180 - elev[elev > 90] # Also check if angles are smaller than 0, in that case, flip sign elev[elev < 0] = -elev[elev < 0] T = beam.values['T'] QM = np.log10(beam.values['Q' + h + '_v']) # Get log mass densities if hydrom_scheme == 2: # Get log number densities as well QN = np.log10(beam.values['QN' + h + '_v']) lut_pts = np.column_stack((elev, T)).T elif hydrom_scheme == 1: lut_pts = np.column_stack((elev, T, QM)).T # Get polarimetric variables from lookup-table ZH_prof = lut[h]['ZH'].lookup_pts(lut_pts) ZDR_prof = lut[h]['ZDR'].lookup_pts(lut_pts) KDP_prof = lut[h]['KDP'].lookup_pts( lut_pts) + lut[h]['DELTAHV'].lookup_pts(lut_pts) RHOHV_prof = lut[h]['RHOHV'].lookup_pts(lut_pts) # DELTAHV_prof=lut[h]['DELTAHV'].lookup_pts(lut_pts) # PHIDP_prof=nan_cumsum(KDP_prof+DELTAHV_prof)*cfg.CONFIG['radar']['radial_resolution']/1000 ZV_prof = ZH_prof / ZDR_prof # Use ZDR and ZH to get ZV # Note that Z2=Z1-a*r in dB gives Z2_l = Z1_l * (1/a_l)**r in linear if att_corr: # AH and AV are in dB so we need to convert them to linear Av_prof = lut[h]['AV'].lookup_pts(lut_pts) ZV_prof = ZH_prof / ZDR_prof ZV_prof *= nan_cumprod( 10**(-Av_prof / 10. * (radial_res / 1000.))) # divide to get dist in km Ah_prof = lut[h]['AH'].lookup_pts(lut_pts) # convert to linear ZH_prof *= nan_cumprod(10**(-Ah_prof / 10. * (radial_res / 1000.))) ZDR_prof = ZH_prof / ZV_prof # Add contributions from this subbeam rad_obs_integrated['ZH'][:, j] = nansum_arr( rad_obs_integrated['ZH'][:, j], ZH_prof * beam.GH_weight) rad_obs_integrated['ZV'][:, j] = nansum_arr( rad_obs_integrated['ZV'][:, j], ZV_prof * beam.GH_weight) rad_obs_integrated['KDP'][:, j] = nansum_arr( rad_obs_integrated['KDP'][:, j], KDP_prof * np.sqrt(beam.GH_weight)) rad_obs_integrated['RHOHV'][:, j] = nansum_arr( rad_obs_integrated['RHOHV'][:, j], RHOHV_prof**np.sqrt(beam.GH_weight)) rad_obs['ZH'][:, j, i] = ZH_prof rad_obs['ZV'][:, j, i] = ZV_prof rad_obs['KDP'][:, j, i] = KDP_prof rad_obs['RHOHV'][:, j, i] = RHOHV_prof sum_weights = sum_arr(sum_weights, ~np.isnan(QM) * np.sqrt(beam.GH_weight)) sum_weights_sqrt = sum_arr(np.sqrt(sum_weights), ~np.isnan(QM) * np.sqrt(beam.GH_weight)) # Rhohv and Kdp are divided by the total received power rad_obs_integrated['KDP'][:, j] = divide_by_power( rad_obs_integrated['KDP'][:, j], sum_weights_sqrt) rad_obs_integrated['RHOHV'][:, j] = divide_by_power( rad_obs_integrated['RHOHV'][:, j], sum_weights_sqrt) # If weight = 0, this will get infinite ########################################################################### # This mask serves to tell if the measured point is ok, or below topo or above COSMO domain mask = np.zeros(len_beams, ) for i, beam in enumerate(list_beams): mask = sum_arr(mask, beam.mask) # Get mask of every Beam # Larger than 1 means that every Beam is below TOPO, smaller than 0 that at least one Beam is above COSMO domain mask /= num_beams mask[np.logical_and(mask >= 0, mask < 1)] = 0 rad_obs_integrated = combine(rad_obs_integrated) rad_obs = combine(rad_obs) # Add standard deviation to output for var in rad_obs.keys(): rad_obs_integrated['std_' + var] = np.nanstd(rad_obs[var], axis=1) # Finally get vectors of distances, height and lat/lon at the central beam idx_0 = int(len(list_beams) / 2) heights_radar = list_beams[idx_0].heights_profile distances_radar = list_beams[idx_0].dist_profile lats = list_beams[idx_0].lats_profile lons = list_beams[idx_0].lons_profile beam_pol = Beam(rad_obs_integrated, mask, lats, lons, distances_radar, heights_radar) return beam_pol