def _plot_profile(self, skew): profiles = self.atmo_profiles # dictionary pres = profiles.get('pres').get('data') temp = profiles.get('temp').get('data') sphum = profiles.get('sphum').get('data') dewpt = mpcalc.dewpoint_from_specific_humidity(sphum, temp, pres).to('degF') # Pressure vs temperature skew.plot(pres, temp, 'r', linewidth=1.5) # Pressure vs dew point temperature skew.plot(pres, dewpt, 'blue', linewidth=1.5) # Compute parcel profile and plot it parcel_profile = mpcalc.parcel_profile(pres, temp[0], dewpt[0]).to('degC') skew.plot( pres, parcel_profile, 'orange', linestyle='dashed', linewidth=1.2, )
def test_dewpoint_specific_humidity(): """Test relative humidity from specific humidity.""" p = 1013.25 * units.mbar temperature = 20. * units.degC q = 0.012 * units.dimensionless td = dewpoint_from_specific_humidity(q, temperature, p) assert_almost_equal(td, 16.973 * units.degC, 3)
def test_dewpoint_specific_humidity(): """Test relative humidity from specific humidity.""" p = 1013.25 * units.mbar temperature = 20. * units.degC q = 0.012 * units.dimensionless td = dewpoint_from_specific_humidity(q, temperature, p) assert_almost_equal(td, 16.973 * units.degC, 3)
def _write_profile(self, csv_path): profiles = self.atmo_profiles # dictionary pres = profiles.get('pres').get('data') u = profiles.get('u').get('data') v = profiles.get('v').get('data') temp = profiles.get('temp').get('data').to('degC') sphum = profiles.get('sphum').get('data') dewpt = np.array( mpcalc.dewpoint_from_specific_humidity(sphum, temp, pres).to('degC')) wspd = np.array(mpcalc.wind_speed(u, v)) wdir = np.array(mpcalc.wind_direction(u, v)) pres = np.array(pres) temp = np.array(temp) profile = pd.DataFrame({ 'LEVEL': pres, 'TEMP': temp, 'DWPT': dewpt, 'WDIR': wdir, 'WSPD': wspd, }) profile.to_csv(csv_path, index=False, float_format="%10.2f")
def adjust_dew_point_Belcher(tmy, df_c0, df_cf, T_fmy): Tdew_fmy = np.array([]) # Grab monthly means temp1 = df_cf.groupby(['month']).mean().reset_index() tempt = df_c0.groupby(['month']).mean().reset_index() # Scaling ahuss = months2hrs((temp1.huss_c / tempt.huss)) #ahuss = months2hrs( 1 + (temp1.huss - tempt.huss) ) # FMY Specific Humidity huss_fmy = ahuss * tmy.huss #Force high values down by finding specific humidity for 100% relative humidity # saturation_huss = mpcalc.specific_humidity_from_mixing_ratio( # mpcalc.mixing_ratio_from_relative_humidity(1.00, T_fmy * units.degC, tmy.press * units.mbar)).magnitude; # huss_fmy[huss_fmy > saturation_huss] = saturation_huss[huss_fmy > saturation_huss]; # # #Force low values up by finding specific humidity for 0% relative humidity # dry_huss = mpcalc.specific_humidity_from_mixing_ratio( # mpcalc.mixing_ratio_from_relative_humidity(0.00, T_fmy * units.degC, tmy.press * units.mbar)).magnitude; # huss_fmy[huss_fmy < dry_huss] = dry_huss[huss_fmy < dry_huss]; # FMY dew point Tdew_fmy = mpcalc.dewpoint_from_specific_humidity( huss_fmy, T_fmy * units.degC, tmy.press * units.mbar).magnitude #Force high values down Tdew_fmy[Tdew_fmy > T_fmy] = T_fmy[Tdew_fmy > T_fmy] return (Tdew_fmy)
def thermo_plots(pressure, temperature, mixing_ratio): """" plots for vertical profiles of temperature, dewpoint, mixing ratio and relative humidity. Parameters ---------- pressure : array-like Atmospheric pressure profile (surface to TOA) temperature: array-like Atmospheric temperature profile (surface to TOA) dewpoint: array-like Atmospheric dewpoint profile (surface to TOA) Returns ------- """ p = pressure * units('mbar') q = mixing_ratio * units('kilogram/kilogram') T = temperature * units('degC') Td = mpcalc.dewpoint_from_specific_humidity(q, T, p) # dewpoint Tp = mpcalc.parcel_profile(p, T[0], Td[0]) # parcel plt.figure(figsize=(12, 5)) lev = find_nearest(p.magnitude, 100) plt.subplot(1, 3, 1) plt.plot(T[:lev], p[:lev], '-ob') plt.plot(Td[:lev], p[:lev], '-og') plt.plot(Tp[:lev], p[:lev], '-or') plt.xlabel('Temperature [C]', fontsize=12) plt.ylabel('Pressure [hpa]', fontsize=12) plt.gca().invert_yaxis() plt.legend(['Temp', 'Temp_Dew', 'Temp_Parcel'], loc=1) plt.grid() qs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(T), p) # Relative humidity RH = q / qs * 100 # Relative humidity plt.subplot(1, 3, 2) plt.plot(q[:lev], p[:lev], '-og') plt.xlabel('Mixing ratio [kg/kg]', fontsize=12) plt.gca().invert_yaxis() plt.grid() plt.subplot(1, 3, 3) plt.plot(RH[:lev], p[:lev], '-og') plt.xlabel('Relative humiduty [%]', fontsize=12) plt.gca().invert_yaxis() plt.grid() plt.tight_layout() return (plt)
def calc_fronts(u, v, q, t, lon, lat, date_list): ''' Parse era5 variables to kinematics(), to calculate various thermal and kinematic front parameters. 12 are computed, but for brevity, only four are returned for now. ''' #Using MetPy, derive equivalent potential temperature ta_unit = units.units.K * t dp_unit = mpcalc.dewpoint_from_specific_humidity( q * units.units.dimensionless, ta_unit, 850 * units.units.hectopascals) thetae = np.array( mpcalc.equivalent_potential_temperature(850 * units.units.hectopascals, ta_unit, dp_unit)) #From the lat-lon information accompanying the ERA5 data, resconstruct 2d coordinates and grid spacing (dx, dy) x, y = np.meshgrid(lon, lat) dx, dy = mpcalc.lat_lon_grid_deltas(x, y) #Derive various kinematic/thermal diagnostics for each time step kinemats = [ kinematics(u[i], v[i], thetae[i], dx, dy, y, smooth=True, sigma=2) for i in np.arange(len(date_list)) ] F = [kinemats[i][0] for i in np.arange(len(date_list)) ] #Frontogenesis function (degC / 100 km / 3 hr) Fn = [kinemats[i][1] for i in np.arange(len(date_list)) ] #Frontogenetical function (deg C / 100 km / 3 hr) Fs = [kinemats[i][2] for i in np.arange(len(date_list)) ] #Rotational component of frontogensis (deg C/ 100 km / 3 hr) icon = [kinemats[i][3] for i in np.arange(len(date_list)) ] #Instantaneous contraction rate (s^-1 * 1e5) vgt = [kinemats[i][4] for i in np.arange(len(date_list)) ] #Horizontal velovity gradient tensor magnitude (s^-1 * 1e5) conv = [kinemats[i][5] for i in np.arange(len(date_list))] #Convergence (s^-1 * 1e5) vo = [kinemats[i][6] for i in np.arange(len(date_list))] #Relative vorticity (s^-1 * 1e5) tfp = [kinemats[i][7] for i in np.arange(len(date_list)) ] #Thermal front parameter (km^-2) mag_te = [kinemats[i][8] for i in np.arange(len(date_list)) ] #Magnitude of theta-e gradient (100 km ^-1) v_f = [kinemats[i][9] for i in np.arange(len(date_list))] #Advection of TFP (m/s) thetae = [kinemats[i][10] for i in np.arange(len(date_list)) ] #Theta-e (K), may be smoothed depending on arguments cond = [kinemats[i][11] for i in np.arange(len(date_list))] #Extra condition return [thetae, mag_te, tfp, v_f]
def eqpt_approx(p, t, q): """ Computes equivalent potential temperature in [K] from pressure, temperature and specific humidity. Arguments: p -- pressure in [Pa] t -- temperature in [K] q -- specific humidity in [kg/kg] p, t and q can be scalars or NumPy arrays. Returns: equivalent potential temperature in [K]. """ return mpcalc.equivalent_potential_temperature( units.Pa * p, units.K * t, mpcalc.dewpoint_from_specific_humidity(units.Pa * p, units.K * t, q)).to("K").m
def eqpt_approx(p, t, q): """ Computes equivalent potential temperature in [K] from pressure, temperature and specific humidity. Arguments: p -- pressure in [Pa] t -- temperature in [K] q -- specific humidity in [kg/kg] p, t and q can be scalars or NumPy arrays. Returns: equivalent potential temperature in [K]. Same dimensions as the inputs. """ p = units.Quantity(p, "Pa") t = units.Quantity(t, "K") dew_temp = mpcalc.dewpoint_from_specific_humidity(p, t, q) eqpt_temp = mpcalc.equivalent_potential_temperature(p, t, dew_temp) return eqpt_temp.to('K').magnitude
def read_merra2(domain,times,pres=True,delta_t=1): #Read 3-hourly MERRA2 pressure level/surface data if len(times) > 1: date_list = date_seq(times,"hours",delta_t) else: date_list = times files_3d = []; files_2d = [] for d in date_list: files_3d.append(glob.glob("/g/data/rr7/MERRA2/raw/M2I3NPASM.5.12.4/"+d.strftime("%Y")+"/"+d.strftime("%m")+"/MERRA2*"+d.strftime("%Y%m%d")+"*.nc4")[0]) files_2d.append(glob.glob("/g/data/ua8/MERRA2/1hr/M2I1NXASM.5.12.4/"+d.strftime("%Y")+"/"+d.strftime("%m")+"/MERRA2*"+d.strftime("%Y%m%d")+"*.nc4")[0]) files_3d = np.unique(files_3d) files_2d = np.unique(files_2d) f3d = xr.open_mfdataset(files_3d, combine="by_coords").sel({"time":date_list, "lev":slice(1000,100), "lon":slice(domain[2], domain[3]), "lat":slice(domain[0], domain[1])}) f2d = xr.open_mfdataset(files_2d, combine="by_coords").sel({"time":date_list, "lon":slice(domain[2], domain[3]), "lat":slice(domain[0], domain[1])}) ta_file = f3d["T"]; z_file = f3d["H"]; ua_file = f3d["U"]; va_file = f3d["V"]; hur_file = f3d["RH"] uas_file = f2d["U10M"]; vas_file = f2d["V10M"]; hus_file = f2d["QV2M"]; tas_file = f2d["T2M"]; ps_file = f2d["PS"] ta = ta_file.values - 273.15 ua = ua_file.values va = va_file.values hgt = z_file.values hur = hur_file.values * 100 hur[hur<0] = 0 hur[hur>100] = 100 dp = get_dp(ta,hur) uas = uas_file.values vas = vas_file.values tas = tas_file.values - 273.15 ps = ps_file.values / 100 ta2d = np.array(mpcalc.dewpoint_from_specific_humidity(hus_file.values, tas*units.units.degC, \ ps*units.units.hectopascal)) terrain = f3d["PHIS"].isel({"time":0}).values / 9.8 lon = f2d["lon"].values lat = f2d["lat"].values p = f3d["lev"].values return [ta,dp,hur,hgt,terrain,p,ps,ua,va,uas,vas,tas,ta2d,lon,lat,date_list]
def load_weather_model_sounding(latitude, longitude, valid_time): with tracer.span(name="latest_run"): model = "icon-eu" run = latest_run(model, valid_time) http_session = session() with ThreadPoolExecutor(max_workers=config.parameter_all_levels_workers) as executor: p_future = executor.submit(parameter_all_levels, model, run, "p", latitude, longitude, session=http_session) T_future = executor.submit(parameter_all_levels, model, run, "T", latitude, longitude, session=http_session) QV_future = executor.submit(parameter_all_levels, model, run, "QV", latitude, longitude, session=http_session) U_future = executor.submit(parameter_all_levels, model, run, "U", latitude, longitude, session=http_session) V_future = executor.submit(parameter_all_levels, model, run, "V", latitude, longitude, session=http_session) HHL_future = executor.submit(parameter_all_levels, model, run, "HHL", latitude, longitude, "time_invariant", session=http_session) # Pressure Pa p_raw = p_future.result() p = p_raw.data # Temperature K T = T_future.result().data # Specific Humidty kg/kg QV = QV_future.result().data # Dewpoint K Td = mpcalc.dewpoint_from_specific_humidity(QV * units("kg/kg"), T * units.K, p * units.Pa) # Wind m/s U = U_future.result().data V = V_future.result().data # Height above MSL for model level HHL = HHL_future.result().data meta_data = WeatherModelSoundingMetaData(p_raw.model_time, p_raw.valid_time) return WeatherModelSounding(latitude, longitude, p, T, QV, Td, U, V, HHL, meta_data)
model_time = data['hhl']['model_time'] valid_time = data['hhl']['valid_time'] print( f"model={model}\nmodel_time={model_time}\nvalid_time={valid_time}\nlat={lat} lon={lon}\n") print(f"hhl\tHoehe\t\tTemp\t\tTau\t\t\trel Hum\t\t\tDruck\t\t\t\t\tWind\t\t\tWind aus") for i in range(top_level - 1, base_level - 1): t = data['t']['data'][i] * units.K p = (data['p']['data'][i]) * units.Pa qv = data['qv']['data'][i] * units("kg/kg") h = data['hhl']['data'][i] * units.meter u = data['u']['data'][i] * units('m/s') v = data['v']['data'][i] * units('m/s') # Dewpoint K dewpt = mpcalc.dewpoint_from_specific_humidity(qv, t, p) # relative humidity relhum = mpcalc.relative_humidity_from_dewpoint(t, dewpt) celsius = t.to('degC') ms = mpcalc.wind_speed(u, v) wdir = mpcalc.wind_direction(u, v, convention='from') dp = p.to('hPa') print(f"{i:-2d}\t{h:~5.1f}\t{celsius:~5.1f}\t{dewpt:~5.1f}\t{relhum:~%}\t{dp:6.1f}\t{ms:~3.1f}\t{wdir:3.0f}")
specific_humidity_RA = RA_data['specific_humidity_gkg-1'] # relative humidity, differentiate between water and ice rhw, rhi = mr2rh(temp_degC, specific_humidity_RA, p_1) RH_RA = np.zeros(len(rhw)) ind_tresh = RA_data.index[RA_data.altitude_m > 5000][0] RH_RA[0:ind_tresh] = rhw[0:ind_tresh] RH_RA[ind_tresh:-1] = rhi[ind_tresh:-1] RH_RA = RH_RA[0:len(RH_RA)] p_1 = p_1.values * units.hPa temp_degC = temp_degC.values * units.degC # calculate dew point temperature specific_humidity_RA = specific_humidity_RA.values * units('g/kg') temp_d_degC = cc.dewpoint_from_specific_humidity(specific_humidity_RA, temp_degC, p_1) RA_data.dew_point_degC = temp_d_degC RA_data.temperature_degC = RA_data.temperature_K - 273.15 ########################################## ## E) RM: Radiometer ########################################## RM_data = xr.open_dataset( RM_archive + '/radiometer_06610_concat_filtered_0.nc').to_dataframe() RM_data = RM_data[RM_data.time_YMDHMS_1.dt.month == RS_time.month] RM_data = RM_data[RM_data.time_YMDHMS_1.dt.day == RS_time.day] date = nearest(RM_data.time_YMDHMS_1, RS_time) RM_data = RM_data[RM_data.time_YMDHMS_1 == date] RM_data["time_YMDHMS"] = RM_data.time_YMDHMS_1.dt.round('30min').values #RM_data['pressure_hPa'] = '0'
def read_cmip(model, experiment, ensemble, year, domain, cmip_ver=5, group="", al33=False, ver6hr="", ver3hr="", project="CMIP"): if cmip_ver == 5: #Get CMIP5 file paths if al33: #NOTE unknown behaviour for al33 directories with multiple versions if group == "": raise ValueError("Group required") if ver6hr == "": ver6hr = "v*" if ver3hr == "": ver3hr = "v*" hus_files = np.sort(glob.glob("/g/data/al33/replicas/CMIP5/combined/"+\ group+"/"+model+"/"+experiment+\ "/6hr/atmos/6hrLev/"+ensemble+"/"+ver6hr+"/hus/*6hrLev*")) ta_files = np.sort(glob.glob("/g/data/al33/replicas/CMIP5/combined/"+\ group+"/"+model+"/"+experiment+\ "/6hr/atmos/6hrLev/"+ensemble+"/"+ver6hr+"/ta/*6hrLev*")) ua_files = np.sort(glob.glob("/g/data/al33/replicas/CMIP5/combined/"+\ group+"/"+model+"/"+experiment+\ "/6hr/atmos/6hrLev/"+ensemble+"/"+ver6hr+"/ua/*6hrLev*")) va_files = np.sort(glob.glob("/g/data/al33/replicas/CMIP5/combined/"+\ group+"/"+model+"/"+experiment+\ "/6hr/atmos/6hrLev/"+ensemble+"/"+ver6hr+"/va/*6hrLev*")) huss_files = np.sort(glob.glob("/g/data/al33/replicas/CMIP5/combined/"+\ group+"/"+model+"/"+experiment+\ "/3hr/atmos/3hr/"+ensemble+"/"+ver3hr+"/huss/*")) tas_files = np.sort(glob.glob("/g/data/al33/replicas/CMIP5/combined/"+\ group+"/"+model+"/"+experiment+\ "/3hr/atmos/3hr/"+ensemble+"/"+ver3hr+"/tas/*")) uas_files = np.sort(glob.glob("/g/data/al33/replicas/CMIP5/combined/"+\ group+"/"+model+"/"+experiment+\ "/3hr/atmos/3hr/"+ensemble+"/"+ver3hr+"/uas/*")) vas_files = np.sort(glob.glob("/g/data/al33/replicas/CMIP5/combined/"+\ group+"/"+model+"/"+experiment+\ "/3hr/atmos/3hr/"+ensemble+"/"+ver3hr+"/vas/*")) ps_files = np.sort(glob.glob("/g/data/al33/replicas/CMIP5/combined/"+\ group+"/"+model+"/"+experiment+\ "/3hr/atmos/3hr/"+ensemble+"/"+ver3hr+"/ps/*")) else: hus_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP5/"+\ model+"/"+experiment+\ "/6hr/atmos/"+ensemble+"/hus/latest/*6hrLev*")) ta_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP5/"+\ model+"/"+experiment+\ "/6hr/atmos/"+ensemble+"/ta/latest/*6hrLev*")) ua_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP5/"+\ model+"/"+experiment+\ "/6hr/atmos/"+ensemble+"/ua/latest/*6hrLev*")) va_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP5/"+\ model+"/"+experiment+\ "/6hr/atmos/"+ensemble+"/va/latest/*6hrLev*")) huss_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP5/"+\ model+"/"+experiment+\ "/3hr/atmos/"+ensemble+"/huss/latest/*")) tas_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP5/"+\ model+"/"+experiment+\ "/3hr/atmos/"+ensemble+"/tas/latest/*")) uas_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP5/"+\ model+"/"+experiment+\ "/3hr/atmos/"+ensemble+"/uas/latest/*")) vas_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP5/"+\ model+"/"+experiment+\ "/3hr/atmos/"+ensemble+"/vas/latest/*")) ps_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP5/"+\ model+"/"+experiment+\ "/3hr/atmos/"+ensemble+"/ps/latest/*")) elif cmip_ver == 6: #Get CMIP6 file paths hus_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP6/"+project+"/"+group+"/"+model+\ "/"+experiment+"/"+ensemble+"/6hrLev/hus/gn/latest/*")) ta_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP6/"+project+"/"+group+"/"+model+\ "/"+experiment+"/"+ensemble+"/6hrLev/ta/gn/latest/*")) ua_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP6/"+project+"/"+group+"/"+model+\ "/"+experiment+"/"+ensemble+"/6hrLev/ua/gn/latest/*")) va_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP6/"+project+"/"+group+"/"+model+\ "/"+experiment+"/"+ensemble+"/6hrLev/va/gn/latest/*")) huss_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP6/"+project+"/"+group+"/"+model+\ "/"+experiment+"/"+ensemble+"/3hr/huss/gn/latest/*")) tas_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP6/"+project+"/"+group+"/"+model+\ "/"+experiment+"/"+ensemble+"/3hr/tas/gn/latest/*")) uas_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP6/"+project+"/"+group+"/"+model+\ "/"+experiment+"/"+ensemble+"/3hr/uas/gn/latest/*")) vas_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP6/"+project+"/"+group+"/"+model+\ "/"+experiment+"/"+ensemble+"/3hr/vas/gn/latest/*")) ps_files = np.sort(glob.glob("/g/data/r87/DRSv3/CMIP6/"+project+"/"+group+"/"+model+\ "/"+experiment+"/"+ensemble+"/3hr/ps/gn/latest/*")) #Isolate the files relevant for the current "year" #NOTE will have to change to incorperate months if there is more than one file per year hus_fid = get_fid(hus_files, year) ta_fid = get_fid(ta_files, year) ua_fid = get_fid(ua_files, year) va_fid = get_fid(va_files, year) huss_fid = get_fid(huss_files, year) tas_fid = get_fid(tas_files, year) uas_fid = get_fid(uas_files, year) vas_fid = get_fid(vas_files, year) ps_fid = get_fid(ps_files, year) #Load the data hus = xr.open_mfdataset([hus_files[i] for i in hus_fid], use_cftime=True) ta = xr.open_mfdataset([ta_files[i] for i in ta_fid], use_cftime=True) ua = xr.open_mfdataset([ua_files[i] for i in ua_fid], use_cftime=True) va = xr.open_mfdataset([va_files[i] for i in va_fid], use_cftime=True) #Load surface data and match to 6 hourly huss = xr.open_mfdataset([huss_files[i] for i in huss_fid], use_cftime=True) tas = xr.open_mfdataset([tas_files[i] for i in tas_fid], use_cftime=True) uas = xr.open_mfdataset([uas_files[i] for i in uas_fid], use_cftime=True) vas = xr.open_mfdataset([vas_files[i] for i in vas_fid], use_cftime=True) ps = xr.open_mfdataset([ps_files[i] for i in ps_fid], use_cftime=True) #Trim to the domain given by "domain", as well as the year given by "year". Expand domain # for later compairsons with ERA5, and for interpolation of U/V domain[0] = domain[0] - 5 domain[1] = domain[1] + 5 domain[2] = domain[2] - 5 domain[3] = domain[3] + 5 hus = trim_cmip5(hus, domain, year) ta = trim_cmip5(ta, domain, year) huss = trim_cmip5(huss, domain, year) tas = trim_cmip5(tas, domain, year) ps = trim_cmip5(ps, domain, year) #Interpolate u, v, uas and vas, using a slightly bigger domain, then trim to "domain" ua = trim_cmip5(ua, [domain[0], domain[1], domain[2], domain[3]], year) va = trim_cmip5(va, [domain[0], domain[1], domain[2], domain[3]], year) uas = trim_cmip5(uas, [domain[0], domain[1], domain[2], domain[3]], year) vas = trim_cmip5(vas, [domain[0], domain[1], domain[2], domain[3]], year) ua = ua.ua.interp({"lon": hus.lon}, method="linear", assume_sorted=True) va = va.va.interp({"lat": hus.lat}, method="linear", assume_sorted=True) uas = uas.uas.interp({ "lat": hus.lat, "lon": hus.lon }, method="linear", assume_sorted=True) vas = vas.vas.interp({ "lat": hus.lat, "lon": hus.lon }, method="linear", assume_sorted=True) ua = trim_cmip5(ua, domain, year) va = trim_cmip5(va, domain, year) uas = trim_cmip5(uas, domain, year) vas = trim_cmip5(vas, domain, year) #Get common times for all datasets common_times = np.array(list(set(hus.time.values) & set(ta.time.values) & set(ua.time.values)\ & set(va.time.values) & set(huss.time.values) & set(tas.time.values)\ & set(uas.time.values) & set(vas.time.values) & set(ps.time.values))) #Restrict all data to common times hus = hus.sel({"time": np.in1d(hus.time, common_times)}) ta = ta.sel({"time": np.in1d(ta.time, common_times)}) ua = ua.sel({"time": np.in1d(ua.time, common_times)}) va = va.sel({"time": np.in1d(va.time, common_times)}) huss = huss.sel({"time": np.in1d(huss.time, common_times)}) tas = tas.sel({"time": np.in1d(tas.time, common_times)}) uas = uas.sel({"time": np.in1d(uas.time, common_times)}) vas = vas.sel({"time": np.in1d(vas.time, common_times)}) ps = ps.sel({"time": np.in1d(ps.time, common_times)}) #Either convert vertical coordinate to height or pressure, depending on the model names = [] for name, da in hus.data_vars.items(): names.append(name) if "orog" in names: #If the model has been stored on a hybrid height coordinate, it should have the # variable "orog". Convert height coordinate to height ASL, and calculate # pressure via the hydrostatic equation z = hus.lev + (hus.b * hus.orog) orog = hus.orog.values q = hus.hus / (1 - hus.hus) tv = ta.ta * ((q + 0.622) / (0.622 * (1 + q))) p = np.swapaxes( np.swapaxes(ps.ps * np.exp(-9.8 * z / (287 * tv)), 3, 2), 2, 1) if ((model in ["ACCESS1-3", "ACCESS1-0", "ACCESS-CM2", "ACCESS-ESM1-5"])): z = np.swapaxes(z, 0, 1).values orog = orog[0] else: z = np.tile( z.values.astype("float32"), [ta.ta.shape[0], 1, 1, 1], ) elif np.any(np.in1d(["p0", "ap"], names)): #If the model has been stored on a hybrid pressure coordinate, it should have the # variable "p0". Convert hybrid pressure coordinate to pressure, and calculate # height via the hydrostatic equation if "p0" in names: p = (hus.a * hus.p0 + hus.b * hus.ps).transpose( "time", "lev", "lat", "lon") elif "ap" in names: p = (hus.ap + hus.b * hus.ps).transpose("time", "lev", "lat", "lon") else: raise ValueError( "Check the hybrid-pressure coordinate of this model") q = hus.hus / (1 - hus.hus) tv = ta.ta * ((q + 0.622) / (0.622 * (1 + q))) z = (-287 * tv * (np.log(p / ps.ps)).transpose("time", "lev", "lat", "lon")) / 9.8 orog = trim_cmip5( xr.open_dataset(glob.glob("/g/data/r87/DRSv3/CMIP5/"+\ model+"/historical/fx/atmos/r0i0p0/orog/latest/orog*.nc")[0]).orog,\ domain, year).values orog[orog < 0] = 0 z = (z + orog).values else: raise ValueError("Check the vertical coordinate of this model") #Sanity checks on pressure and height, one of which is calculated via hydrostatic approx. Note ACCESS-CM2 is # missing a temperature level, and so that level will have zero pressure. Ignore sanity check for this model. if (z.min() < -1000) | (z.max() > 100000): raise ValueError( "Potentially erroneous Z values (less than -1000 or greater than 100,000 km" ) if (p.max().values > 200000) | (p.min().values < 0): if model != "ACCESS-CM2": raise ValueError( "Potentially erroneous pressure (less than 0 or greater than 200,000 Pa" ) #Convert quantities into those expected by wrf_(non)_parallel.py ta = ta.ta.values - 273.15 hur = mpcalc.relative_humidity_from_specific_humidity(hus.hus.values, \ ta*units.units.degC, p.values*units.units.pascal) * 100 pres = p.values / 100. sfc_pres = ps.ps.values / 100. tas = tas.tas.values - 273.15 ta2d = mpcalc.dewpoint_from_specific_humidity(huss.huss.values, tas*units.units.degC, \ ps.ps.values*units.units.pascal) lon = p.lon.values lat = p.lat.values ua = ua.values va = va.values uas = uas.values vas = vas.values #Mask all data above 100 hPa. For ACCESS-CM2, mask data below 20 m if model == "ACCESS-CM2": ta[(pres < 100) | (p == 0) | (p == np.inf)] = np.nan hur[(pres < 100) | (p == 0) | (p == np.inf)] = np.nan z[(pres < 100) | (p == 0) | (p == np.inf)] = np.nan ua[(pres < 100) | (p == 0) | (p == np.inf)] = np.nan va[(pres < 100) | (p == 0) | (p == np.inf)] = np.nan else: ta[pres < 100] = np.nan hur[pres < 100] = np.nan z[pres < 100] = np.nan ua[pres < 100] = np.nan va[pres < 100] = np.nan date_list = p.time.values date_list = np.array([ dt.datetime.strptime(date_list[t].strftime(), "%Y-%m-%d %H:%M:%S") for t in np.arange(len(date_list)) ]) return [ta, hur, z, orog, pres, sfc_pres, ua, va, uas, vas, tas, ta2d, lon,\ lat, date_list]
T_ADV850 = mpcalc.advection(T850 * units.kelvin, [U850, V850], (DX, DY), dim_order='yx') * units('K/sec') PT850 = mpcalc.potential_temperature(850 * units.mbar, T850) FRONT_850 = mpcalc.frontogenesis(PT850, U850, V850, DX, DY, dim_order='YX') # ============================================================================= # FIG #5: 850: GEOPOTENTIAL HEIGHT, EQUIV. POT. TEMP, WINDS, LAPSE RATES # ============================================================================= H500 = HGT_DATA.variables['hgt'][TIME_INDEX, 5, :, :] H700 = HGT_DATA.variables['hgt'][TIME_INDEX, 3, :, :] T500 = AIR_DATA.variables['air'][TIME_INDEX, 5, :, :] * units('degC') T700 = AIR_DATA.variables['air'][TIME_INDEX, 3, :, :] * units('degC') LR = -1000 * (T500 - T700) / (H500 - H700) H850 = HGT_DATA.variables['hgt'][TIME_INDEX, 2, :, :] T850 = AIR_DATA.variables['air'][TIME_INDEX, 2, :, :] * units('kelvin') SH850 = SHUM_DATA.variables['shum'][TIME_INDEX, 2, :, :] DP850 = mpcalc.dewpoint_from_specific_humidity(SH850, T850, 850 * units.mbar) EPT850 = mpcalc.equivalent_potential_temperature(850 * units.mbar, T850, DP850) # ============================================================================= # FIG #6: 925: MOISTURE FLUX, MOISTURE FLUX CONVERGENCE, # ============================================================================= RH925 = HUM_DATA.variables['rhum'][TIME_INDEX, 1, :, :] SH925 = SHUM_DATA.variables['shum'][TIME_INDEX, 1, :, :] U925 = UWND_DATA.variables['uwnd'][TIME_INDEX, 1, :, :] * units('m/s') V925 = VWND_DATA.variables['vwnd'][TIME_INDEX, 1, :, :] * units('m/s') H925 = HGT_DATA.variables['hgt'][TIME_INDEX, 1, :, :] SH_ADV925 = mpcalc.advection(SH925, [U925, V925], (DX, DY), dim_order='yx') SH_DIV925 = SH925 * (mpcalc.divergence(U925, V925, DX, DY, dim_order='YX')) MFLUXX = SH925 * U925 MFLUXY = SH925 * V925 MFC_925 = SH_ADV925 + SH_DIV925 # =============================================================================
def read_cmip6(group, model, experiment, ensemble, year, domain): #DEPRECIATED - USE READ_CMIP INSTEAD, SPECIFYING CMIP_VER=6 #Read CMIP6 data from the r87 project (from oi10 and fs38) #For the given model, institute, experiment, get the relevant file paths. hus_files = np.sort( glob.glob("/g/data/r87/DRSv3/CMIP6/CMIP/" + group + "/" + model + "/" + experiment + "/" + ensemble + "/6hrLev/hus/gn/latest/*")) ta_files = np.sort( glob.glob("/g/data/r87/DRSv3/CMIP6/CMIP/" + group + "/" + model + "/" + experiment + "/" + ensemble + "/6hrLev/ta/gn/latest/*")) ua_files = np.sort( glob.glob("/g/data/r87/DRSv3/CMIP6/CMIP/" + group + "/" + model + "/" + experiment + "/" + ensemble + "/6hrLev/ua/gn/latest/*")) va_files = np.sort( glob.glob("/g/data/r87/DRSv3/CMIP6/CMIP/" + group + "/" + model + "/" + experiment + "/" + ensemble + "/6hrLev/va/gn/latest/*")) huss_files = np.sort( glob.glob("/g/data/r87/DRSv3/CMIP6/CMIP/" + group + "/" + model + "/" + experiment + "/" + ensemble + "/3hr/huss/gn/latest/*")) tas_files = np.sort( glob.glob("/g/data/r87/DRSv3/CMIP6/CMIP/" + group + "/" + model + "/" + experiment + "/" + ensemble + "/3hr/tas/gn/latest/*")) uas_files = np.sort( glob.glob("/g/data/r87/DRSv3/CMIP6/CMIP/" + group + "/" + model + "/" + experiment + "/" + ensemble + "/3hr/uas/gn/latest/*")) vas_files = np.sort( glob.glob("/g/data/r87/DRSv3/CMIP6/CMIP/" + group + "/" + model + "/" + experiment + "/" + ensemble + "/3hr/vas/gn/latest/*")) ps_files = np.sort( glob.glob("/g/data/r87/DRSv3/CMIP6/CMIP/" + group + "/" + model + "/" + experiment + "/" + ensemble + "/3hr/ps/gn/latest/*")) #Isolate the files relevant for the current "year" #NOTE will have to change to incorperate months if there is more than one file per year hus_fid = get_fid(hus_files, year) ta_fid = get_fid(ta_files, year) ua_fid = get_fid(ua_files, year) va_fid = get_fid(va_files, year) huss_fid = get_fid(huss_files, year) tas_fid = get_fid(tas_files, year) uas_fid = get_fid(uas_files, year) vas_fid = get_fid(vas_files, year) ps_fid = get_fid(ps_files, year) #Load the data, match 3 hourly and 6 hourly data hus = xr.open_mfdataset([hus_files[i] for i in hus_fid]) ta = xr.open_mfdataset([ta_files[i] for i in ta_fid]) ua = xr.open_mfdataset([ua_files[i] for i in ua_fid]) va = xr.open_mfdataset([va_files[i] for i in va_fid]) huss = xr.open_mfdataset([huss_files[i] for i in huss_fid]) huss = huss.sel({"time": np.in1d(huss.time, hus.time)}) tas = xr.open_mfdataset([tas_files[i] for i in tas_fid]) tas = tas.sel({"time": np.in1d(tas.time, ta.time)}) uas = xr.open_mfdataset([uas_files[i] for i in uas_fid]) uas = uas.sel({"time": np.in1d(uas.time, ua.time)}) vas = xr.open_mfdataset([vas_files[i] for i in vas_fid]) vas = vas.sel({"time": np.in1d(vas.time, va.time)}) ps = xr.open_mfdataset([ps_files[i] for i in ps_fid]) ps = ps.sel({"time": np.in1d(ps.time, hus.time)}) #and trim to the domain given by "domain", as well as the year given by "year" hus = trim_cmip5(hus, domain, year) ta = trim_cmip5(ta, domain, year) huss = trim_cmip5(huss, domain, year) tas = trim_cmip5(tas, domain, year) ps = trim_cmip5(ps, domain, year) #Interpolate u, v, uas and vas, using a slightly bigger domain, then trim to "domain" ua = trim_cmip5( ua, [domain[0] - 5, domain[1] + 5, domain[2] - 5, domain[3] + 5], year) va = trim_cmip5( va, [domain[0] - 5, domain[1] + 5, domain[2] - 5, domain[3] + 5], year) uas = trim_cmip5( uas, [domain[0] - 5, domain[1] + 5, domain[2] - 5, domain[3] + 5], year) vas = trim_cmip5( vas, [domain[0] - 5, domain[1] + 5, domain[2] - 5, domain[3] + 5], year) ua = ua.ua.interp({"lon": hus.lon}, method="linear", assume_sorted=True) va = va.va.interp({"lat": hus.lat}, method="linear", assume_sorted=True) uas = uas.uas.interp({ "lat": hus.lat, "lon": hus.lon }, method="linear", assume_sorted=True) vas = vas.vas.interp({ "lat": hus.lat, "lon": hus.lon }, method="linear", assume_sorted=True) ua = trim_cmip5(ua, domain, year).values va = trim_cmip5(va, domain, year).values uas = trim_cmip5(uas, domain, year).values vas = trim_cmip5(vas, domain, year).values #Convert vertical coordinate to height z = hus.lev + (hus.b * hus.orog) orog = hus.orog.values #Calculate pressure via hydrostatic equation q = hus.hus / (1 - hus.hus) tv = ta.ta * ((q + 0.622) / (0.622 * (1 + q))) p = np.swapaxes(np.swapaxes(ps.ps * np.exp(-9.8 * z / (287 * tv)), 3, 2), 2, 1) #Convert quantities into those expected by wrf_parallel.py ta = ta.ta.values - 273.15 hur = mpcalc.relative_humidity_from_specific_humidity(hus.hus.values, \ ta*units.units.degC, p.values*units.units.pascal) * 100 z = np.tile(z.values, [ta.shape[0], 1, 1, 1]) pres = p.values / 100. sfc_pres = ps.ps.values / 100. tas = tas.tas.values - 273.15 ta2d = mpcalc.dewpoint_from_specific_humidity(hus.hus.values, ta*units.units.degC, \ p.values*units.units.pascal) lon = p.lon.values lat = p.lat.values date_list = p.time.values #Mask all data above 100 hPa ta[pres < 100] = np.nan hur[pres < 100] = np.nan z[pres < 100] = np.nan ua[pres < 100] = np.nan va[pres < 100] = np.nan return [ta, hur, z, orog, pres, sfc_pres, ua, va, uas, vas, tas, ta2d, lon,\ lat, date_list]
def __init__(self, datea, fhr, atcf, config): # Forecast fields to compute wnd_lev_1 = [250, 500] wnd_lev_2 = [350, 500] n_wnd_lev = len(wnd_lev_1) # Read steering flow parameters, or use defaults steerp1 = float(config['fields'].get('steer_level1', '300')) steerp2 = float(config['fields'].get('steer_level2', '850')) tcradius = float(config['fields'].get('steer_radius', '333')) # lat_lon info lat1 = float(config['fields'].get('min_lat', '0.')) lat2 = float(config['fields'].get('max_lat', '65.')) lon1 = float(config['fields'].get('min_lon', '-180.')) lon2 = float(config['fields'].get('max_lon', '-10.')) if not 'min_lat' in config: config.update({'min_lat': lat1}) config.update({'max_lat': lat2}) config.update({'min_lon': lon1}) config.update({'max_lon': lon2}) self.fhr = fhr self.atcf_files = atcf.atcf_files self.config = config self.nens = int(len(self.atcf_files)) df_files = {} self.datea_str = datea self.datea = dt.datetime.strptime(datea, '%Y%m%d%H') self.datea_s = self.datea.strftime("%m%d%H%M") self.fff = str(self.fhr + 1000)[1:] datea_1 = self.datea + dt.timedelta(hours=self.fhr) datea_1 = datea_1.strftime("%m%d%H%M") self.dpp = importlib.import_module(config['io_module']) logging.warning("Computing hour {0} ensemble fields".format(self.fff)) # Obtain the ensemble lat/lon information, replace missing values with mean self.ens_lat, self.ens_lon = atcf.ens_lat_lon_time(self.fhr) e_cnt = 0 m_lat = 0.0 m_lon = 0.0 for n in range(self.nens): if self.ens_lat[n] != atcf.missing and self.ens_lon[ n] != atcf.missing: e_cnt = e_cnt + 1 m_lat = m_lat + self.ens_lat[n] m_lon = m_lon + self.ens_lon[n] if e_cnt > 0: m_lon = m_lon / e_cnt m_lat = m_lat / e_cnt for n in range(self.nens): if self.ens_lat[n] == atcf.missing or self.ens_lon[ n] == atcf.missing: self.ens_lat[n] = m_lat self.ens_lon[n] = m_lon # Read grib file information for this forecast hour g1 = self.dpp.ReadGribFiles(self.datea_str, self.fhr, self.config) dencode = { 'ensemble_data': { 'dtype': 'float32' }, 'latitude': { 'dtype': 'float32' }, 'longitude': { 'dtype': 'float32' }, 'ensemble': { 'dtype': 'int32' } } # Compute steering wind components uoutfile = '{0}/{1}_f{2}_usteer_ens.nc'.format(config['work_dir'], str(self.datea_str), self.fff) voutfile = '{0}/{1}_f{2}_vsteer_ens.nc'.format(config['work_dir'], str(self.datea_str), self.fff) if (not os.path.isfile(uoutfile) or not os.path.isfile(voutfile)) and config['fields'].get( 'calc_uvsteer', 'True') == 'True': logging.warning(" Computing steering wind information") inpDict = {'isobaricInhPa': (steerp1, steerp2)} inpDict = g1.set_var_bounds('zonal_wind', inpDict) # Create output arrays outDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'description': 'zonal steering wind', 'units': 'm/s', '_FillValue': -9999. } outDict = g1.set_var_bounds('zonal_wind', outDict) uensmat = g1.create_ens_array('zonal_wind', self.nens, outDict) outDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'description': 'meridional steering wind', 'units': 'm/s', '_FillValue': -9999. } outDict = g1.set_var_bounds('meridional_wind', outDict) vensmat = g1.create_ens_array('meridional_wind', self.nens, outDict) outDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'description': 'steering wind vorticity', 'units': '1/s', '_FillValue': -9999. } outDict = g1.set_var_bounds('zonal_wind', outDict) vortmat = g1.create_ens_array('zonal_wind', self.nens, outDict) wencode = { 'latitude': { 'dtype': 'float32' }, 'longitude': { 'dtype': 'float32' } } for n in range(self.nens): # Read global zonal and meridional wind, write to file uwnd = g1.read_grib_field('zonal_wind', n, inpDict).rename('u') vwnd = g1.read_grib_field('meridional_wind', n, inpDict).rename('v') # print(uwnd[:,0,0]) # print(vwnd[:,0,0]) # sys.exit(2) uwnd.to_netcdf('wind_info.nc', mode='w', encoding=wencode, format='NETCDF3_CLASSIC') vwnd.to_netcdf('wind_info.nc', mode='a', encoding=wencode, format='NETCDF3_CLASSIC') latvec = uwnd.latitude.values lonvec = uwnd.longitude.values if e_cnt > 0: latcen = latvec[np.abs(latvec - self.ens_lat[n]).argmin()] loncen = lonvec[np.abs(lonvec - self.ens_lon[n]).argmin()] # Call NCL to remove TC winds, read result from file os.system('ncl -Q {0}/tc_steer.ncl tclat={1} tclon={2} tcradius={3}'.format(config['script_dir'],\ str(latcen), str(loncen), str(tcradius))) wfile = nc.Dataset('wind_info.nc') uwnd[:, :, :] = wfile.variables['u'][:, :, :] vwnd[:, :, :] = wfile.variables['v'][:, :, :] os.remove('wind_info.nc') # Integrate the winds over the layer to obtain the steering wind pres, lat, lon = uwnd.indexes.values() nlev = len(pres) uint = uwnd[0, :, :] uint[:, :] = 0.0 vint = vwnd[0, :, :] vint[:, :] = 0.0 for k in range(nlev - 1): uint[:, :] = uint[:, :] + 0.5 * (uwnd[k, :, :] + uwnd[ k + 1, :, :]) * abs(pres[k + 1] - pres[k]) vint[:, :] = vint[:, :] + 0.5 * (vwnd[k, :, :] + vwnd[ k + 1, :, :]) * abs(pres[k + 1] - pres[k]) # if pres[0] > pres[-1]: # uint = -np.trapz(uwnd[:,:,:], pres, axis=0) / abs(pres[-1]-pres[0]) # vint = -np.trapz(vwnd[:,:,:], pres, axis=0) / abs(pres[-1]-pres[0]) # else: # uint = np.trapz(uwnd[:,:,:], pres, axis=0) / abs(pres[-1]-pres[0]) # vint = np.trapz(vwnd[:,:,:], pres, axis=0) / abs(pres[-1]-pres[0]) if lat[0] > lat[-1]: slat1 = lat2 slat2 = lat1 else: slat1 = lat1 slat2 = lat2 # Write steering flow to ensemble arrays uensmat[n, :, :] = np.squeeze( uint.sel(latitude=slice(slat1, slat2), longitude=slice(lon1, lon2))) / abs(pres[-1] - pres[0]) vensmat[n, :, :] = np.squeeze( vint.sel(latitude=slice(slat1, slat2), longitude=slice(lon1, lon2))) / abs(pres[-1] - pres[0]) # Compute the vorticity associated with the steering wind # circ = VectorWind(unew, vnew).vorticity() * 1.0e5 # vortmat[n,:,:] = np.squeeze(circ.sel(latitude=slice(lat2, lat1), longitude=slice(lon1, lon2))) uensmat.to_netcdf(uoutfile, encoding=dencode) vensmat.to_netcdf(voutfile, encoding=dencode) # vortmat.to_netcdf(vortfile, encoding=dencode) else: logging.warning(" Obtaining steering wind information from file") # Read geopotential height from file, if ensemble file is not present if config['fields'].get('calc_height', 'True') == 'True': if 'height_levels' in config['fields']: height_list = json.loads(config['fields'].get('height_levels')) else: height_list = [500] for level in height_list: levstr = '%0.3i' % int(level) outfile = '{0}/{1}_f{2}_h{3}hPa_ens.nc'.format( config['work_dir'], str(self.datea_str), self.fff, levstr) if not os.path.isfile(outfile): logging.warning( ' Computing {0} hPa height'.format(levstr)) vDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'isobaricInhPa': (level, level), 'description': '{0} hPa height'.format(levstr), 'units': 'm', '_FillValue': -9999. } vDict = g1.set_var_bounds('geopotential_height', vDict) ensmat = g1.create_ens_array('geopotential_height', g1.nens, vDict) for n in range(g1.nens): ensmat[n, :, :] = np.squeeze( g1.read_grib_field('geopotential_height', n, vDict)) ensmat.to_netcdf(outfile, encoding=dencode) elif os.path.isfile(outfile): logging.warning( " Obtaining {0} hPa height data from {1}".format( levstr, outfile)) # Compute 250 hPa PV if the file does not exist outfile = '{0}/{1}_f{2}_pv250_ens.nc'.format(config['work_dir'], str(self.datea_str), self.fff) if (not os.path.isfile(outfile) and config['fields'].get('calc_pv250hPa', 'True') == 'True'): logging.warning(" Computing 250 hPa PV") vDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'isobaricInhPa': (200, 300), 'description': '250 hPa Potential Vorticity', 'units': 'PVU', '_FillValue': -9999. } vDict = g1.set_var_bounds('zonal_wind', vDict) ensmat = g1.create_ens_array('zonal_wind', self.nens, vDict) for n in range(self.nens): # Read all the necessary files from file, smooth fields, so sensitivities are useful tmpk = g1.read_grib_field('temperature', n, vDict) * units('K') lats = tmpk.latitude.values * units('degrees') lons = tmpk.longitude.values * units('degrees') pres = tmpk.isobaricInhPa.values * units('hPa') tmpk = mpcalc.smooth_n_point(tmpk, 9, 4) thta = mpcalc.potential_temperature(pres[:, None, None], tmpk) uwnd = mpcalc.smooth_n_point( g1.read_grib_field('zonal_wind', n, vDict) * units('m/s'), 9, 4) vwnd = mpcalc.smooth_n_point( g1.read_grib_field('meridional_wind', n, vDict) * units('m/s'), 9, 4) dx, dy = mpcalc.lat_lon_grid_deltas(lons, lats, x_dim=-1, y_dim=-2, geod=None) # Compute PV and place in ensemble array pvout = mpcalc.potential_vorticity_baroclinic( thta, pres[:, None, None], uwnd, vwnd, dx[None, :, :], dy[None, :, :], lats[None, :, None]) ensmat[n, :, :] = np.squeeze(pvout[np.where( pres == 250 * units('hPa'))[0], :, :]) * 1.0e6 ensmat.to_netcdf(outfile, encoding=dencode) elif os.path.isfile(outfile): logging.warning( " Obtaining 250 hPa PV data from {0}".format(outfile)) # Compute the equivalent potential temperature (if desired and file is missing) if config['fields'].get('calc_theta-e', 'False') == 'True': if 'theta-e_levels' in config['fields']: thetae_list = json.loads( config['fields'].get('theta-e_levels')) else: thetae_list = [700, 850] for level in thetae_list: levstr = '%0.3i' % int(level) outfile = '{0}/{1}_f{2}_e{3}hPa_ens.nc'.format( config['work_dir'], str(self.datea_str), self.fff, levstr) if not os.path.isfile(outfile): logging.warning( ' Computing {0} hPa Theta-E'.format(levstr)) vDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'isobaricInhPa': (level, level), 'description': '{0} hPa Equivalent Potential Temperature'.format( levstr), 'units': 'K', '_FillValue': -9999. } vDict = g1.set_var_bounds('temperature', vDict) ensmat = g1.create_ens_array('temperature', g1.nens, vDict) for n in range(g1.nens): tmpk = g1.read_grib_field('temperature', n, vDict) * units.K pres = tmpk.isobaricInhPa.values * units.hPa if g1.has_specific_humidity: qvap = np.squeeze( g1.read_grib_field('specific_humidity', n, vDict)) tdew = mpcalc.dewpoint_from_specific_humidity( pres[None, None], tmpk, qvap) else: relh = g1.read_grib_field('relative_humidity', n, vDict) relh = np.minimum(np.maximum(relh, 0.01), 100.0) * units.percent tdew = mpcalc.dewpoint_from_relative_humidity( tmpk, relh) ensmat[n, :, :] = np.squeeze( mpcalc.equivalent_potential_temperature( pres[None, None], tmpk, tdew)) ensmat.to_netcdf(outfile, encoding=dencode) elif os.path.isfile(outfile): logging.warning( " Obtaining {0} hPa Theta-e data from {1}".format( levstr, outfile)) # Compute the 500-850 hPa water vapor mixing ratio (if desired and file is missing) outfile = '{0}/{1}_f{2}_q500-850hPa_ens.nc'.format( config['work_dir'], str(self.datea_str), self.fff) if (not os.path.isfile(outfile) and config['fields'].get( 'calc_q500-850hPa', 'False') == 'True'): logging.warning(" Computing 500-850 hPa Water Vapor") vDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'description': '500-850 hPa Integrated Water Vapor', 'units': 'hPa', '_FillValue': -9999. } vDict = g1.set_var_bounds('temperature', vDict) ensmat = g1.create_ens_array('temperature', len(self.atcf_files), vDict) vDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'isobaricInhPa': (500, 850), 'description': '500-850 hPa Integrated Water Vapor', 'units': 'hPa', '_FillValue': -9999. } vDict = g1.set_var_bounds('temperature', vDict) for n in range(self.nens): tmpk = np.squeeze(g1.read_grib_field('temperature', n, vDict)) * units('K') pres = (tmpk.isobaricInhPa.values * units.hPa).to(units.Pa) if g1.has_specific_humidity: qvap = mpcalc.mixing_ratio_from_specific_humidity( g1.read_grib_field('specific_humidity', n, vDict)) else: relh = np.minimum( np.maximum( g1.read_grib_field('relative_humidity', n, vDict), 0.01), 100.0) * units('percent') qvap = mpcalc.mixing_ratio_from_relative_humidity( pres[:, None, None], tmpk, relh) # Integrate water vapor over the pressure levels ensmat[n, :, :] = np.abs(np.trapz( qvap, pres, axis=0)) / mpcon.earth_gravity ensmat.to_netcdf(outfile, encoding=dencode) elif os.path.isfile(outfile): logging.warning( " Obtaining 500-850 hPa water vapor data from {0}".format( outfile)) # Compute wind-related forecast fields (if desired and file is missing) if config['fields'].get('calc_winds', 'False') == 'True': if 'wind_levels' in config['fields']: wind_list = json.loads(config['fields'].get('wind_levels')) else: wind_list = [850] for level in wind_list: levstr = '%0.3i' % int(level) ufile = '{0}/{1}_f{2}_u{3}hPa_ens.nc'.format( config['work_dir'], str(self.datea_str), self.fff, levstr) vfile = '{0}/{1}_f{2}_v{3}hPa_ens.nc'.format( config['work_dir'], str(self.datea_str), self.fff, levstr) if (not os.path.isfile(ufile)) or (not os.path.isfile(vfile)): logging.warning( ' Computing {0} hPa wind information'.format(levstr)) uDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'isobaricInhPa': (level, level), 'description': '{0} hPa zonal wind'.format(levstr), 'units': 'm/s', '_FillValue': -9999. } uDict = g1.set_var_bounds('zonal_wind', uDict) uensmat = g1.create_ens_array('zonal_wind', g1.nens, uDict) vDict = { 'latitude': (lat1, lat2), 'longitude': (lon1, lon2), 'isobaricInhPa': (level, level), 'description': '{0} hPa meridional wind'.format(levstr), 'units': 'm/s', '_FillValue': -9999. } vDict = g1.set_var_bounds('meridional_wind', vDict) vensmat = g1.create_ens_array('meridional_wind', g1.nens, vDict) for n in range(g1.nens): uwnd = g1.read_grib_field('zonal_wind', n, uDict).squeeze() vwnd = g1.read_grib_field('meridional_wind', n, vDict).squeeze() uensmat[n, :, :] = uwnd[:, :] vensmat[n, :, :] = vwnd[:, :] uensmat.to_netcdf(ufile, encoding=dencode) vensmat.to_netcdf(vfile, encoding=dencode) elif os.path.isfile(outfile): logging.warning( " Obtaining {0} hPa wind information from file". format(levstr))