def polar_coords_to_latlon(radar, sweep_number): """Function under construction, not currently used""" # Radar location needed to get lat/lon of every gate klat, klon, klatr, klonr = get_radar_latlon_plus_radians(radar) # Initialize information on sweep geometry sweep_sw = get_sweep_data(radar, DEFAULT_SW, sweep_number) sweep_az_sw = get_sweep_azimuths(radar, sweep_number) sweep_elev_sw = get_sweep_elevations(radar, sweep_number) sw_lonr = 0.0 * sweep_sw sw_azr_1d = np.deg2rad(sweep_az_sw) sw_sr_1d = radar.range['data'][:] / RNG_MULT result = np.meshgrid(sw_sr_1d, sweep_az_sw) sw_azr_2d = np.deg2rad(result[1]) sw_sr_2d = result[0] result = np.meshgrid(sw_sr_1d, sweep_elev_sw) sw_el = result[1] sweep_size = radar.sweep_end_ray_index['data'][sweep_number] + 1 - \ radar.sweep_start_ray_index['data'][sweep_number] sw_gr, sw_ht = rsl_get_groundr_and_h(sw_sr_2d, sw_el) sw_latr = np.arcsin((np.sin(klatr) * np.cos(sw_gr/re)) + (np.cos(klatr) * np.sin(sw_gr/re) * np.cos(sw_azr_2d))) # Get longitude at every gate as well as precompute eps_sw for j in np.arange(sweep_size): for i in np.arange(radar.ngates): sw_lonr[j, i] = klonr + \ atan2c_longitude(sw_azr_1d[j], sw_gr[j, i], klatr, sw_latr[j, i]) sw_lat = np.rad2deg(sw_latr) sw_lon = np.rad2deg(sw_lonr) return sw_lat, sw_lon
def calc_turb_sweep(radar, sweep_number, radius=DEFAULT_RADIUS, split_cut=False, xran=MAX_INT, yran=MAX_INT, verbose=False, name_dz=DEFAULT_DZ, name_sw=DEFAULT_SW, use_ntda=True, beamwidth=DEFAULT_BEAMWIDTH, gate_spacing=DEFAULT_GATE_SPACING): """ Provide a Py-ART radar object containing reflectivity and spectrum width variables as an argument, along with the sweep number and any necessary changes to the keywords, and receive back turbulence for the same sweep as spectrum width (along with longitude and latitude on the same coordinate system). radar = Py-ART radar object sweep_number = Can be as low as 0, as high as # of sweeps minus 1 radius = radius of influence (km) split_cut = Set to True if using NEXRAD or similar radar that has two separate low-level sweeps at the same tilt angle for DZ & SW verbose = Set to True to get more information about calculation progress. xran = [Min X from radar, Max X from radar], subsectioning improves performance yran = [Min Y from radar, Max Y from radar], subsectioning improves performance name_dz = Name of reflectivity field, used by Py-ART to access field name_sw = Name of spectrum width field, used by Py-ART to access field use_ntda = Flag to use the spatial averaging and weighting employed by NTDA beamwidth = Beamwidth of radar in degrees gate_spacing = Gate spacing of radar in km """ if verbose: overall_time = time.time() begin_time = time.time() sweep_dz = get_sweep_data(radar, name_dz, sweep_number) try: fill_val_sw = radar.fields[name_sw]['_FillValue'] except KeyError: fill_val_sw = BAD_DATA_VAL try: fill_val_dz = radar.fields[name_dz]['_FillValue'] except KeyError: fill_val_dz = BAD_DATA_VAL sweep_sw, sweep_az_sw, sweep_elev_sw, dz_sw = \ _retrieve_sweep_fields(radar, name_sw, name_dz, sweep_number, sweep_dz, split_cut) # Radar location needed to get lat/lon # of every gate for distance calculations klat, klon, klatr, klonr = get_radar_latlon_plus_radians(radar) # Initialize information on sweep geomtery sw_lonr = 0.0 * sweep_sw sw_azr_1d = np.deg2rad(sweep_az_sw) sw_sr_1d = radar.range['data'][:] / RNG_MULT result = np.meshgrid(sw_sr_1d, sweep_az_sw) sw_azr_2d = np.deg2rad(result[1]) sw_sr_2d = result[0] result = np.meshgrid(sw_sr_1d, sweep_elev_sw) sw_el = result[1] sweep_size = radar.sweep_end_ray_index['data'][sweep_number] + 1 - \ radar.sweep_start_ray_index['data'][sweep_number] sw_gr, sw_ht = rsl_get_groundr_and_h(sw_sr_2d, sw_el) sw_latr = np.arcsin((np.sin(klatr) * np.cos(sw_gr/re)) + (np.cos(klatr) * np.sin(sw_gr/re) * np.cos(sw_azr_2d))) if verbose: print(time.time() - begin_time, 'seconds to complete all preliminary processing') begin_time = time.time() # Get longitude at every gate for j in np.arange(sweep_size): for i in np.arange(radar.ngates): sw_lonr[j, i] = klonr +\ atan2c_longitude(sw_azr_1d[j], sw_gr[j, i], klatr, sw_latr[j, i]) sw_lat = np.rad2deg(sw_latr) sw_lon = np.rad2deg(sw_lonr) # Determine NTDA interest fields csnr_sw = _calc_csnr_for_every_gate(dz_sw, sw_sr_2d) crng_sw = _calc_crng_for_every_gate(sw_sr_2d) czh_sw = _calc_czh_for_every_gate(dz_sw, sw_ht) cpr = 1.0 # Not calculating Cpr, user must remove second trip manually. if verbose: print(time.time() - begin_time, 'seconds to compute longitudes and precompute Csnr, Crng, Czh') begin_time = time.time() xx, yy = calc_cartesian_coords_radians(sw_lonr, sw_latr, klonr, klatr) # Flatten all arrays to avoid performance buzzkill of nested loops. # Then reduce the data using masks to make the job manageable. sweep_sw = sweep_sw.ravel() dz_sw = dz_sw.ravel() condition = np.logical_and(dz_sw != fill_val_dz, sweep_sw != fill_val_sw) sweep_sw = sweep_sw[condition] dz_sw = dz_sw[condition] # Brief detour to get eps and check to see if that's all user wants. # Placing this code here with reduced sweep_sw improves performance. sr_1d = flatten_and_reduce_data_array(sw_sr_2d, condition) cond = gate_spacing >= sr_1d * np.deg2rad(beamwidth) eps_sw = edr_long_range(sweep_sw, sr_1d, beamwidth, gate_spacing) eps_sw[cond] = edr_short_range(sweep_sw[cond], sr_1d[cond], beamwidth, gate_spacing) if not use_ntda: turb_radar_f = 1.0 * eps_sw turb_radar = sw_lat.flatten() * 0.0 + fill_val_sw turb_radar[condition] = turb_radar_f eps_sw = np.reshape(turb_radar, (len(sweep_az_sw), radar.ngates)) if verbose: print(time.time() - overall_time, 'seconds to process radar sweep') return eps_sw, sw_lat, sw_lon # Provided NTDA is wanted, continue flattening/reducing arrays xx = flatten_and_reduce_data_array(xx, condition) yy = flatten_and_reduce_data_array(yy, condition) csnr_sw = flatten_and_reduce_data_array(csnr_sw, condition) crng_sw = flatten_and_reduce_data_array(crng_sw, condition) czh_sw = flatten_and_reduce_data_array(czh_sw, condition) turb_radar_f = 0.0 * sweep_sw + fill_val_sw # Find the distance to every other good gate ind, ind_sw = _calc_tree(xx, yy, radius) cswv_sw = _calc_cswv_for_every_gate(xx, sweep_sw, ind_sw) if verbose: print(time.time() - begin_time, 'seconds to get eps, reduce data,', 'compute BallTree, and get Cswv') begin_time = time.time() # Loop thru data and do NTDA filtering for i in np.arange(len(xx)): if verbose: if i % 50000 == 0: print('i =', i, 'of', len(xx) - 1, time.time() - begin_time, 'seconds elapsed during loop') if xran[0] < xx[i] < xran[1] and yran[0] < yy[i] < yran[1]: # Broadcating employed to minimize the amount of looping eps = eps_sw[ind[i]]**2 csnr = csnr_sw[ind[i]]**0.6667 crng = crng_sw[ind[i]] czh = czh_sw[ind[i]] cswv = cswv_sw[ind[i]] # Begin NTDA-specific calculation tot = csnr * cpr * cswv * czh * crng num = tot * eps tot = np.sum(tot) num = np.sum(num) if tot > 0: turb_radar_f[i] = np.sqrt(num/tot) # Restore turbulence to a full 2-D sweep array and return along w/ lat/lon. turb_radar = sw_lat.flatten() * 0.0 + fill_val_sw turb_radar[condition] = turb_radar_f turb_radar = np.reshape(turb_radar, (len(sweep_az_sw), radar.ngates)) if verbose: print(time.time() - overall_time, 'seconds to process radar sweep') return turb_radar, sw_lat, sw_lon