def parse_acft_sounding(flight_level_file, req_ini, req_end, return_interp): raw = pd.read_table(flight_level_file, engine='python', delimiter='\s*') ini_raw = raw['TIME'].iloc[0] end_raw = raw['TIME'].iloc[-1] timestamp = pd.date_range( '2001-01-23 '+ini_raw, '2001-01-24 '+end_raw, freq='s') raw.index = timestamp raw.drop(['TIME'], axis=1, inplace=True) ''' req_ini and req_end are python datatime variables ''' st = raw.index.searchsorted(req_ini) en = raw.index.searchsorted(req_end) data = raw[['PRES_ALT', 'AIR_PRESS', 'AIR_TEMP', 'DEW_POINT', 'WIND_SPD', 'WIND_DIR', 'LAT', 'LON']].ix[st:en] x = data['PRES_ALT'].values xnew = np.linspace(min(x), max(x), x.size) if x[0] > x[-1]: descening = True data = data.iloc[::-1] if return_interp: ''' flight sounding might include constant height levels or descening trayectories, so we interpolate data to a common vertical grid ''' data2 = data.drop_duplicates(subset='PRES_ALT') x2 = data2['PRES_ALT'].values ''' interpolate each field ''' for n in ['AIR_PRESS', 'AIR_TEMP', 'DEW_POINT', 'WIND_SPD', 'WIND_DIR']: y = data2[n].values # spl = UnivariateSpline(x2,y,k=4) # data[n]= spl(xnew) rbf = Rbf(x2, y, smooth=1.0) ynew = rbf(xnew) data[n] = ynew '''' update values ''' data['PRES_ALT'] = xnew ''' set index ''' data = data.set_index('PRES_ALT') ''' compute thermo ''' hgt = data.index.values Tc = data['AIR_TEMP'] Tk = Tc+273.15 press = data['AIR_PRESS'] dewp = data['DEW_POINT'] Rh = tm.relative_humidity(C=Tc, Dewp=dewp) Sat_mixr = tm.sat_mix_ratio(C=Tc, hPa=press) Mr = Rh*Sat_mixr/100. theta = tm.theta2(K=Tk, hPa=press, mixing_ratio=Mr) thetaeq = tm.theta_equiv2(K=Tk, hPa=press, relh=Rh, mixing_ratio=Mr) data['theta'] = theta.values data['thetaeq'] = thetaeq.values bvf_dry = tm.bv_freq_dry( theta=theta, agl_m=hgt, depth_m=100, centered=True) bvf_moist = tm.bv_freq_moist(K=Tk, hPa=press, mixing_ratio=Mr, agl_m=hgt, depth_m=100, centered=True) data = pd.merge( data, bvf_dry, left_index=True, right_index=True, how='outer') data = pd.merge( data, bvf_moist, left_index=True, right_index=True, how='outer') data.bvf_dry.interpolate(method='linear', inplace=True) data.bvf_moist.interpolate(method='linear', inplace=True) ''' drop last incorrect decreasing (increasing) altitude (pressure) value ''' data2 = data.ix[:xnew[-2]] ''' return dataframe ''' return data2 else: hgt = data['PRES_ALT'] Tc = data['AIR_TEMP'] Tk = Tc+273.15 press = data['AIR_PRESS'] dewp = data['DEW_POINT'] Rh = tm.relative_humidity(C=Tc, Dewp=dewp) Sat_mixr = tm.sat_mix_ratio(C=Tc, hPa=press) Mr = Rh*Sat_mixr/100. theta = tm.theta2(K=Tk, hPa=press, mixing_ratio=Mr) thetaeq = tm.theta_equiv2(K=Tk, hPa=press, relh=Rh, mixing_ratio=Mr) data['theta'] = theta.values data['thetaeq'] = thetaeq.values data['bvf_dry'] = np.nan data['bvf_moist'] = np.nan return data
def parse_sounding(file_sound): col_names = get_var_names(file_sound) # col_units = get_var_units(file_sound) ''' read tabular file ''' raw_sounding = pd.read_table(file_sound, skiprows=36, header=None) raw_sounding.drop(19, axis=1, inplace=True) raw_sounding.columns = col_names sounding = raw_sounding[ ['Height', 'TE', 'TD', 'RH', 'u', 'v', 'P', 'MR', 'DD']] sounding.units = {'Height': 'm', 'TE': 'K', 'TD': 'K', 'RH': '%', 'u': 'm s-1', 'v': 'm s-1', 'P': 'hPa', 'MR': 'g kg-1'} ''' replace nan values ''' nan_value = -32768.00 sounding = sounding.applymap(lambda x: np.nan if x == nan_value else x) ''' QC soundings that include descening trayectories; criteria is 3 consecutive values descending ''' sign = np.sign(np.diff(sounding['Height'])) rep = find_repeats(sign.tolist(), -1, 3) try: lastgood = np.where(rep)[0][0]-1 sounding = sounding.ix[0:lastgood] except IndexError: ''' all good''' pass ''' set index ''' sounding = sounding.set_index('Height') ''' QC ''' sounding = sounding.groupby(sounding.index).first() sounding.dropna(how='all', inplace=True) sounding.RH = sounding.RH.apply(lambda x: 100 if x > 100 else x) u_nans = nan_fraction(sounding.u) v_nans = nan_fraction(sounding.v) if u_nans > 0. or v_nans > 0.: sounding.u.interpolate(method='linear', inplace=True) sounding.v.interpolate(method='linear', inplace=True) rh_nans = nan_fraction(sounding.RH) td_nans = nan_fraction(sounding.TD) mr_nans = nan_fraction(sounding.MR) if rh_nans < 5. and td_nans > 50. and mr_nans > 50.: sat_mixr = tm.sat_mix_ratio(K=sounding.TE, hPa=sounding.P) mixr = (sounding.RH/100)*sat_mixr*1000 sounding.loc[:, 'MR'] = mixr # [g kg-1] ''' add potential temperature ''' theta = tm.theta2( K=sounding.TE, hPa=sounding.P, mixing_ratio=sounding.MR/1000) thetaeq = tm.theta_equiv2(K=sounding.TE, hPa=sounding.P, relh=sounding.RH, mixing_ratio=sounding.MR/1000) sounding.loc[:, 'theta'] = pd.Series(theta, index=sounding.index) sounding.loc[:, 'thetaeq'] = pd.Series(thetaeq, index=sounding.index) ''' add Brunt-Vaisala frequency ''' hgt = sounding.index.values bvf_dry = tm.bv_freq_dry(theta=sounding.theta, agl_m=hgt, depth_m=100, centered=True) bvf_moist = tm.bv_freq_moist(K=sounding.TE, hPa=sounding.P, mixing_ratio=sounding.MR/1000, agl_m=hgt, depth_m=100, centered=True) sounding = pd.merge( sounding, bvf_dry, left_index=True, right_index=True, how='outer') sounding = pd.merge( sounding, bvf_moist, left_index=True, right_index=True, how='outer') ''' interpolate between layer-averaged values ''' sounding.bvf_dry.interpolate(method='linear', inplace=True) sounding.bvf_moist.interpolate(method='linear', inplace=True) sounding.loc[sounding.MR.isnull(), 'bvf_dry'] = np.nan sounding.loc[sounding.MR.isnull(), 'bvf_moist'] = np.nan ''' NOTE: if sounding hgt jumps from 12 to 53m then 12m bvf values are NaN since there are no data between 12 and 53m to calculate a layer-based value. ''' return sounding
def parse_sounding2(file_sound): ''' This version make a resample of vertical gates and uses make_layer2 in bvf calculations (Thermodyn module) ''' col_names = get_var_names(file_sound) # col_units = get_var_units(file_sound) ''' read tabular file ''' raw_sounding = pd.read_table(file_sound, skiprows=36, header=None) raw_sounding.drop(19, axis=1, inplace=True) raw_sounding.columns = col_names sounding = raw_sounding[ ['Height', 'TE', 'TD', 'RH', 'u', 'v', 'P', 'MR', 'DD']] sounding.units = {'Height': 'm', 'TE': 'K', 'TD': 'K', 'RH': '%', 'u': 'm s-1', 'v': 'm s-1', 'P': 'hPa', 'MR': 'g kg-1'} ''' replace nan values ''' nan_value = -32768.00 sounding = sounding.applymap(lambda x: np.nan if x == nan_value else x) ''' QC soundings that include descening trayectories; criteria is 3 consecutive values descending ''' sign = np.sign(np.diff(sounding['Height'])) rep = find_repeats(sign.tolist(), -1, 3) try: lastgood = np.where(rep)[0][0]-1 sounding = sounding.ix[0:lastgood] except IndexError: ''' all good''' pass # print sounding ''' resample to constant height values ''' resamp = 2 # [m] sounding = sounding.set_index('Height') hgt = sounding.index.values[-1] resample = np.arange(10, hgt, resamp) soundres = sounding.loc[resample] soundres.iloc[0] = sounding.iloc[0] # copy first value soundres.iloc[-1] = sounding.iloc[-1] # copy last value soundres.interpolate( method='linear', inplace=True, limit=80, limit_direction='backward') soundres = soundres.reset_index().drop_duplicates( subset='Height', keep='first') soundres = soundres.set_index('Height') ''' QC ''' soundres.RH = soundres.RH.apply(lambda x: 100 if x > 100 else x) rh_nans = nan_fraction(soundres.RH) # [%] td_nans = nan_fraction(soundres.TD) # [%] mr_nans = nan_fraction(soundres.MR) # [%] if rh_nans < 5. and td_nans > 50. and mr_nans > 50.: sat_mixr = tm.sat_mix_ratio(K=soundres.TE, hPa=soundres.P) mixr = (soundres.RH/100.)*sat_mixr*1000 soundres.loc[:, 'MR'] = mixr # [g kg-1] ''' add potential temperature ''' theta = tm.theta2(K=soundres.TE, hPa=soundres.P, mixing_ratio=soundres.MR/1000) thetaeq = tm.theta_equiv2(K=soundres.TE, hPa=soundres.P, relh=soundres.RH, mixing_ratio=soundres.MR/1000) soundres.loc[:, 'theta'] = pd.Series(theta, index=soundres.index) soundres.loc[:, 'thetaeq'] = pd.Series(thetaeq, index=soundres.index) ''' add Brunt-Vaisala frequency ''' hgt = soundres.index.values depth = 100 # [m] bvf_dry = tm.bv_freq_dry(theta=soundres.theta, agl_m=hgt, depth_m=depth) bvf_moist = tm.bv_freq_moist(K=soundres.TE, hPa=soundres.P, mixing_ratio=soundres.MR/1000, agl_m=hgt, depth_m=depth) soundres = pd.merge( soundres, bvf_dry, left_index=True, right_index=True, how='outer') soundres = pd.merge( soundres, bvf_moist, left_index=True, right_index=True, how='outer') ''' interpolate between layer-averaged values ''' soundres.bvf_dry.interpolate(method='cubic', inplace=True) soundres.bvf_moist.interpolate(method='cubic', inplace=True) return soundres
def parse_acft_sounding(flight_level_file, req_ini, req_end, return_interp): raw = pd.read_table(flight_level_file, engine='python', delimiter='\s*') ini_raw = raw['TIME'].iloc[0] end_raw = raw['TIME'].iloc[-1] timestamp = pd.date_range('2001-01-23 ' + ini_raw, '2001-01-24 ' + end_raw, freq='s') raw.index = timestamp raw.drop(['TIME'], axis=1, inplace=True) ''' req_ini and req_end are python datatime variables ''' st = raw.index.searchsorted(req_ini) en = raw.index.searchsorted(req_end) data = raw[[ 'PRES_ALT', 'AIR_PRESS', 'AIR_TEMP', 'DEW_POINT', 'WIND_SPD', 'WIND_DIR', 'LAT', 'LON' ]].ix[st:en] x = data['PRES_ALT'].values xnew = np.linspace(min(x), max(x), x.size) if x[0] > x[-1]: descening = True data = data.iloc[::-1] if return_interp: ''' flight sounding might include constant height levels or descening trayectories, so we interpolate data to a common vertical grid ''' data2 = data.drop_duplicates(subset='PRES_ALT') x2 = data2['PRES_ALT'].values ''' interpolate each field ''' for n in [ 'AIR_PRESS', 'AIR_TEMP', 'DEW_POINT', 'WIND_SPD', 'WIND_DIR' ]: y = data2[n].values # spl = UnivariateSpline(x2,y,k=4) # data[n]= spl(xnew) rbf = Rbf(x2, y, smooth=1.0) ynew = rbf(xnew) data[n] = ynew '''' update values ''' data['PRES_ALT'] = xnew ''' set index ''' data = data.set_index('PRES_ALT') ''' compute thermo ''' hgt = data.index.values Tc = data['AIR_TEMP'] Tk = Tc + 273.15 press = data['AIR_PRESS'] dewp = data['DEW_POINT'] Rh = tm.relative_humidity(C=Tc, Dewp=dewp) Sat_mixr = tm.sat_mix_ratio(C=Tc, hPa=press) Mr = Rh * Sat_mixr / 100. theta = tm.theta2(K=Tk, hPa=press, mixing_ratio=Mr) thetaeq = tm.theta_equiv2(K=Tk, hPa=press, relh=Rh, mixing_ratio=Mr) data['theta'] = theta.values data['thetaeq'] = thetaeq.values bvf_dry = tm.bv_freq_dry(theta=theta, agl_m=hgt, depth_m=100, centered=True) bvf_moist = tm.bv_freq_moist(K=Tk, hPa=press, mixing_ratio=Mr, agl_m=hgt, depth_m=100, centered=True) data = pd.merge(data, bvf_dry, left_index=True, right_index=True, how='outer') data = pd.merge(data, bvf_moist, left_index=True, right_index=True, how='outer') data.bvf_dry.interpolate(method='linear', inplace=True) data.bvf_moist.interpolate(method='linear', inplace=True) ''' drop last incorrect decreasing (increasing) altitude (pressure) value ''' data2 = data.ix[:xnew[-2]] ''' return dataframe ''' return data2 else: hgt = data['PRES_ALT'] Tc = data['AIR_TEMP'] Tk = Tc + 273.15 press = data['AIR_PRESS'] dewp = data['DEW_POINT'] Rh = tm.relative_humidity(C=Tc, Dewp=dewp) Sat_mixr = tm.sat_mix_ratio(C=Tc, hPa=press) Mr = Rh * Sat_mixr / 100. theta = tm.theta2(K=Tk, hPa=press, mixing_ratio=Mr) thetaeq = tm.theta_equiv2(K=Tk, hPa=press, relh=Rh, mixing_ratio=Mr) data['theta'] = theta.values data['thetaeq'] = thetaeq.values data['bvf_dry'] = np.nan data['bvf_moist'] = np.nan return data
def parse_sounding(file_sound): col_names = get_var_names(file_sound) # col_units = get_var_units(file_sound) ''' read tabular file ''' raw_sounding = pd.read_table(file_sound, skiprows=36, header=None) raw_sounding.drop(19, axis=1, inplace=True) raw_sounding.columns = col_names sounding = raw_sounding[[ 'Height', 'TE', 'TD', 'RH', 'u', 'v', 'P', 'MR', 'DD' ]] sounding.units = { 'Height': 'm', 'TE': 'K', 'TD': 'K', 'RH': '%', 'u': 'm s-1', 'v': 'm s-1', 'P': 'hPa', 'MR': 'g kg-1' } ''' replace nan values ''' nan_value = -32768.00 sounding = sounding.applymap(lambda x: np.nan if x == nan_value else x) ''' QC soundings that include descening trayectories; criteria is 3 consecutive values descending ''' sign = np.sign(np.diff(sounding['Height'])) rep = find_repeats(sign.tolist(), -1, 3) try: lastgood = np.where(rep)[0][0] - 1 sounding = sounding.ix[0:lastgood] except IndexError: ''' all good''' pass ''' set index ''' sounding = sounding.set_index('Height') ''' QC ''' sounding = sounding.groupby(sounding.index).first() sounding.dropna(how='all', inplace=True) sounding.RH = sounding.RH.apply(lambda x: 100 if x > 100 else x) u_nans = nan_fraction(sounding.u) v_nans = nan_fraction(sounding.v) if u_nans > 0. or v_nans > 0.: sounding.u.interpolate(method='linear', inplace=True) sounding.v.interpolate(method='linear', inplace=True) rh_nans = nan_fraction(sounding.RH) td_nans = nan_fraction(sounding.TD) mr_nans = nan_fraction(sounding.MR) if rh_nans < 5. and td_nans > 50. and mr_nans > 50.: sat_mixr = tm.sat_mix_ratio(K=sounding.TE, hPa=sounding.P) mixr = (sounding.RH / 100) * sat_mixr * 1000 sounding.loc[:, 'MR'] = mixr # [g kg-1] ''' add potential temperature ''' theta = tm.theta2(K=sounding.TE, hPa=sounding.P, mixing_ratio=sounding.MR / 1000) thetaeq = tm.theta_equiv2(K=sounding.TE, hPa=sounding.P, relh=sounding.RH, mixing_ratio=sounding.MR / 1000) sounding.loc[:, 'theta'] = pd.Series(theta, index=sounding.index) sounding.loc[:, 'thetaeq'] = pd.Series(thetaeq, index=sounding.index) ''' add Brunt-Vaisala frequency ''' hgt = sounding.index.values bvf_dry = tm.bv_freq_dry(theta=sounding.theta, agl_m=hgt, depth_m=100, centered=True) bvf_moist = tm.bv_freq_moist(K=sounding.TE, hPa=sounding.P, mixing_ratio=sounding.MR / 1000, agl_m=hgt, depth_m=100, centered=True) sounding = pd.merge(sounding, bvf_dry, left_index=True, right_index=True, how='outer') sounding = pd.merge(sounding, bvf_moist, left_index=True, right_index=True, how='outer') ''' interpolate between layer-averaged values ''' sounding.bvf_dry.interpolate(method='linear', inplace=True) sounding.bvf_moist.interpolate(method='linear', inplace=True) sounding.loc[sounding.MR.isnull(), 'bvf_dry'] = np.nan sounding.loc[sounding.MR.isnull(), 'bvf_moist'] = np.nan ''' NOTE: if sounding hgt jumps from 12 to 53m then 12m bvf values are NaN since there are no data between 12 and 53m to calculate a layer-based value. ''' return sounding
def parse_sounding2(file_sound): ''' This version make a resample of vertical gates and uses make_layer2 in bvf calculations (Thermodyn module) ''' col_names = get_var_names(file_sound) # col_units = get_var_units(file_sound) ''' read tabular file ''' raw_sounding = pd.read_table(file_sound, skiprows=36, header=None) raw_sounding.drop(19, axis=1, inplace=True) raw_sounding.columns = col_names sounding = raw_sounding[[ 'Height', 'TE', 'TD', 'RH', 'u', 'v', 'P', 'MR', 'DD' ]] sounding.units = { 'Height': 'm', 'TE': 'K', 'TD': 'K', 'RH': '%', 'u': 'm s-1', 'v': 'm s-1', 'P': 'hPa', 'MR': 'g kg-1' } ''' replace nan values ''' nan_value = -32768.00 sounding = sounding.applymap(lambda x: np.nan if x == nan_value else x) ''' QC soundings that include descening trayectories; criteria is 3 consecutive values descending ''' sign = np.sign(np.diff(sounding['Height'])) rep = find_repeats(sign.tolist(), -1, 3) try: lastgood = np.where(rep)[0][0] - 1 sounding = sounding.ix[0:lastgood] except IndexError: ''' all good''' pass # print sounding ''' resample to constant height values ''' resamp = 2 # [m] sounding = sounding.set_index('Height') hgt = sounding.index.values[-1] resample = np.arange(10, hgt, resamp) soundres = sounding.loc[resample] soundres.iloc[0] = sounding.iloc[0] # copy first value soundres.iloc[-1] = sounding.iloc[-1] # copy last value soundres.interpolate(method='linear', inplace=True, limit=80, limit_direction='backward') soundres = soundres.reset_index().drop_duplicates(subset='Height', keep='first') soundres = soundres.set_index('Height') ''' QC ''' soundres.RH = soundres.RH.apply(lambda x: 100 if x > 100 else x) rh_nans = nan_fraction(soundres.RH) # [%] td_nans = nan_fraction(soundres.TD) # [%] mr_nans = nan_fraction(soundres.MR) # [%] if rh_nans < 5. and td_nans > 50. and mr_nans > 50.: sat_mixr = tm.sat_mix_ratio(K=soundres.TE, hPa=soundres.P) mixr = (soundres.RH / 100.) * sat_mixr * 1000 soundres.loc[:, 'MR'] = mixr # [g kg-1] ''' add potential temperature ''' theta = tm.theta2(K=soundres.TE, hPa=soundres.P, mixing_ratio=soundres.MR / 1000) thetaeq = tm.theta_equiv2(K=soundres.TE, hPa=soundres.P, relh=soundres.RH, mixing_ratio=soundres.MR / 1000) soundres.loc[:, 'theta'] = pd.Series(theta, index=soundres.index) soundres.loc[:, 'thetaeq'] = pd.Series(thetaeq, index=soundres.index) ''' add Brunt-Vaisala frequency ''' hgt = soundres.index.values depth = 100 # [m] bvf_dry = tm.bv_freq_dry(theta=soundres.theta, agl_m=hgt, depth_m=depth) bvf_moist = tm.bv_freq_moist(K=soundres.TE, hPa=soundres.P, mixing_ratio=soundres.MR / 1000, agl_m=hgt, depth_m=depth) soundres = pd.merge(soundres, bvf_dry, left_index=True, right_index=True, how='outer') soundres = pd.merge(soundres, bvf_moist, left_index=True, right_index=True, how='outer') ''' interpolate between layer-averaged values ''' soundres.bvf_dry.interpolate(method='cubic', inplace=True) soundres.bvf_moist.interpolate(method='cubic', inplace=True) return soundres