Exemplo n.º 1
0
def add_entropy(ax,
                pressure,
                temperature,
                mixing_ratio,
                ds=100,
                linewidth=1.0):
    "add entropy curves and rescale values to fit in by 0.5*entropy + ds"
    p = pressure * units('mbar')
    T = temperature * units('degC')
    q = mixing_ratio * units('kilogram/kilogram')
    qs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(T), p)
    Td = mpcalc.dewpoint(mpcalc.vapor_pressure(p, q))  # dewpoint
    Tp = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC')  # parcel profile

    # specific entropy [joule/(kg*K)]
    # sd : specific entropy of dry air
    # sm1 : specific entropy of airborne mositure in state 1 (water vapor)
    # sm2 : specific entropy of airborne mositure in state 2 (saturated water vapor)

    sd = entropy(T, q * 0, p)
    sm1 = entropy(T, q, p)
    sm2 = entropy(T, qs, p)

    ax.plot(sd.magnitude * 0.5 + ds, p, '--k')
    ax.plot(sm1.magnitude * 0.5 + ds, p, '--b')
    ax.plot(sm2.magnitude * 0.5 + ds, p, '--r')
Exemplo n.º 2
0
def calc_q_from_rh(ds):
    """
    Input :

        ds : Dataset 

    Output :

        q : Specific humidity values
    
    Function to estimate specific humidity from the relative humidity,
    temperature and pressure in the given dataset. This function uses MetPy's
    functions to get q:

    (i) mpcalc.dewpoint_from_relative_humidity()
    (ii) mpcalc.specific_humidity_from_dewpoint()
                        
    """
    # dp = mpcalc.dewpoint_from_relative_humidity(
    #     dataset.T.values * units.degC, dataset.rh.values / 100,
    # ).magnitude

    # q = mpcalc.specific_humidity_from_dewpoint(
    #     dp * units.degC, dataset.p.values * units.hPa
    # ).magnitude

    e_s = pp.calc_saturation_pressure(ds.ta.values)
    w_s = mpcalc.mixing_ratio(e_s * units.Pa, ds.p.values * units.Pa).magnitude
    w = ds.rh.values * w_s
    q = w / (1 + w)

    return q
Exemplo n.º 3
0
def add_curves_Wyoming(ax, datetime, station, linewidth=1.0, LH_Tdepend=False):
    """
    overlaying new curves of multiple soundings from Wyoming datasets
    date: using datetime module. ex. datetime(2018,06,06) 
    station: station name. ex. 'MFL' Miami, Florida
    """
    from siphon.simplewebservice.wyoming import WyomingUpperAir

    date = datetime
    station = station
    df = WyomingUpperAir.request_data(date, station)
    pressure = df['pressure'].values
    Temp = df['temperature'].values
    Temp_dew = df['dewpoint'].values
    altitude = df['height'].values
    q = mpcalc.mixing_ratio(
        mpcalc.saturation_vapor_pressure(Temp_dew * units('degC')),
        pressure * units('mbar'))
    q = mpcalc.specific_humidity_from_mixing_ratio(q)
    qs = mpcalc.mixing_ratio(
        mpcalc.saturation_vapor_pressure(Temp * units('degC')),
        pressure * units('mbar'))

    # specific energies
    if LH_Tdepend == False:
        mse = mpcalc.moist_static_energy(altitude * units('meter'),
                                         Temp * units('degC'), q)
        mse_s = mpcalc.moist_static_energy(altitude * units('meter'),
                                           Temp * units('degC'), qs)
        dse = mpcalc.dry_static_energy(altitude * units('meter'),
                                       Temp * units('degC'))
    else:
        # A short course in cloud physics, Roger and Yau (1989)
        Lvt = (2500.8 - 2.36 * T.magnitude +
               0.0016 * T.magnitude**2 - 0.00006 * T.magnitude**3) * units(
                   'joule/gram')  # latent heat of evaporation
        #Lf = 2834.1 - 0.29*T - 0.004*T**2                  # latent heat of fusion

        mse = Cp_d * T + g * altitude + Lvt * q
        mse_s = Cp_d * T + g * altitude + Lvt * qs
        dse = mpcalc.dry_static_energy(altitude, T)

    # adding curves on the main axes
    ax.plot(dse.magnitude, pressure, 'k', linewidth=linewidth)
    ax.plot(mse.magnitude, pressure, 'b', linewidth=linewidth)
    ax.plot(mse_s.magnitude, pressure, 'r', linewidth=linewidth)
Exemplo n.º 4
0
def add_curves(ax,
               pressure,
               temperature,
               mixing_ratio,
               altitude,
               linewidth=1.0,
               LH_Tdepend=False):
    """
    overlaying new curves of multiple soundings from profiles
    """
    p = pressure * units('mbar')
    T = temperature * units('degC')
    q = mixing_ratio * units('kilogram/kilogram')
    qs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(T), p)
    Td = mpcalc.dewpoint(mpcalc.vapor_pressure(p, q))  # dewpoint
    Tp = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC')  # parcel profile

    # Altitude based on the hydrostatic eq.
    if len(altitude) == len(pressure):  # (1) altitudes for whole levels
        altitude = altitude * units('meter')
    elif len(altitude
             ) == 1:  # (2) known altitude where the soundings was launched
        z_surf = altitude.copy() * units('meter')
        # given altitude
        altitude = np.zeros((np.size(T))) * units('meter')
        for i in range(np.size(T)):
            altitude[i] = mpcalc.thickness_hydrostatic(
                p[:i + 1], T[:i + 1]) + z_surf  # Hypsometric Eq. for height
    else:
        print(
            '***NOTE***: the altitude at the surface is assumed 0 meter, and altitudes are derived based on the hypsometric equation'
        )
        altitude = np.zeros(
            (np.size(T))) * units('meter')  # surface is 0 meter
        for i in range(np.size(T)):
            altitude[i] = mpcalc.thickness_hydrostatic(
                p[:i + 1], T[:i + 1])  # Hypsometric Eq. for height

    # specific energies
    if LH_Tdepend == False:
        mse = mpcalc.moist_static_energy(altitude, T, q)
        mse_s = mpcalc.moist_static_energy(altitude, T, qs)
        dse = mpcalc.dry_static_energy(altitude, T)
    else:
        # A short course in cloud physics, Roger and Yau (1989)
        Lvt = (2500.8 - 2.36 * T.magnitude +
               0.0016 * T.magnitude**2 - 0.00006 * T.magnitude**3) * units(
                   'joule/gram')  # latent heat of evaporation
        #Lf = 2834.1 - 0.29*T - 0.004*T**2                  # latent heat of fusion

        mse = Cp_d * T + g * altitude + Lvt * q
        mse_s = Cp_d * T + g * altitude + Lvt * qs
        dse = mpcalc.dry_static_energy(altitude, T)

    ax.plot(dse, p, '--k', linewidth=linewidth)
    ax.plot(mse, p, '--b', linewidth=linewidth)
    ax.plot(mse_s, p, '--r', linewidth=linewidth)
Exemplo n.º 5
0
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)
Exemplo n.º 6
0
def calc_rh_from_q(dataset, T=None):
    """
    Input :

        dataset : Dataset

        T : Temperature values estimated from interpolated potential_temperature;
            if not specified, function will calculate this from given dataset using
            calc_T_from_theta()

    Output :

        rh : Relative humidity values 

    Function to estimate relative humidity from specific humidity, temperature
    and pressure in the given dataset. This function uses MetPy's
    functions to get rh:

    (i) mpcalc.relative_humidity_from_specific_humidity()
    
    """
    if T is None:
        T = calc_T_from_theta(dataset)

    # rh = mpcalc.relative_humidity_from_specific_humidity(
    #     dataset.q.values, T * units.degC, dataset.p.values * units.hPa,
    # ).magnitude

    w = dataset.q / (1 - dataset.q)
    e_s = pp.calc_saturation_pressure(dataset.ta.values)
    w_s = mpcalc.mixing_ratio(e_s * units.Pa,
                              dataset.p.values * units.Pa).magnitude

    rh = w / w_s

    return rh
Exemplo n.º 7
0
def main(args={}):
    """
    Main function
    """
    try:
        args = get_args()
    except:
        sys.exit()

    setup_logging(args['verbose'])

    logging.debug('Gathering version information')
    try:
        __version__ = eurec4a_snd.__version__
        package_version_set = True
    except (ModuleNotFoundError, AttributeError):
        logging.debug('No eurec4a_snd package version found')
        __version__ = 'see git_version'
        package_version_set = False

    try:
        git_module_version = sp.check_output(
            ["git", "describe", "--always", "--dirty"], stderr=sp.STDOUT).strip().decode()
        git_version_set = True
    except (sp.CalledProcessError, FileNotFoundError):
        logging.debug('No git-version could be found.')
        git_module_version = "--"
        git_version_set = False

    if (not git_version_set and not package_version_set):
        logging.warning('No version of the converter could be found!'
                        ' Please consider the installation via conda'
                        ' or if this is not working clone the git re'
                        'pository')

    logging.info('Version of script: {} (conda package), {} (git version)'.format(__version__, git_module_version))

    logging.debug('Create filelist')
    if args['inputfile'] is None:
        filelist = glob.glob(args['inputpath']+'*.nc')
    else:
        filelist = glob.glob(args['inputfile'])
    filelist = sorted(filelist)

    # Create outputfolder if necessary
    if not os.path.exists(args['outputfolder']):
        os.makedirs(args['outputfolder'])

    for f, file in enumerate(tqdm.tqdm(filelist)):
        logging.info(f'Process file {file}')
        ds = xr.open_dataset(file, use_cftime=False)
        ds = ds.isel({'sounding': 0})
        ds_input = ds.copy()

        # Check monotonic ascent/descent
        if np.all(np.diff(ds.isel(level=slice(20,-1)).altitude.values) > 0) or np.all(np.diff(ds.isel(level=slice(20,-1)).altitude.values) < 0):
            logging.debug('Sounding is monotonic ascending/descending')
        else:
            logging.warning('Sounding is not monotonic ascending/descending. The ascent rate will be artificial')

        # Remove standard pressure levels (extendedVerticalSoundingSignificance)
        # extendedVerticalSoundingSignificance == 65536
        non_std_level_mask = ~np.isin(ds.pressure, std_pressures)
        ds = ds.isel({'level': non_std_level_mask})
        # extendedVerticalSoundingSignificance == 18432
        non_nan_altitude = ~np.isnan(ds.altitude.values)
        ds = ds.isel({'level': non_nan_altitude})
        # Geopotential height issue
        # the geopotential height is not a measured coordinate and
        # the same height can occur at different pressure levels
        # here the first occurrence is used
        _, uniq_altitude_idx = np.unique(ds.altitude.values, return_index=True)
        ds = ds.isel({'level': uniq_altitude_idx})

        # Check if platform is known
        # if ds.platform not in platform_rename_dict.keys():
        #     logging.error('The platform {} is not known. Please choose one of {}'.format(ds.platform, platform_rename_dict.keys()))
        #     sys.exit()

        # Consistent platform test
        if f == 0:
            platform = ds.platform
        else:
            assert ds.platform == platform, 'The platform seems to change from {} to {}'.format(platform, ds.platform)

        # Unique levels test
        if len(ds.altitude) != len(np.unique(ds.altitude)):
            logging.warning('Altitude levels are not unique of {}'.format(file))
            break

        # Prepare some data that cannot be linearly interpolated
        logging.debug('Calculate wind components')
        u, v = get_wind_components(ds.windDirection.values, ds.windSpeed.values)
        ds['wind_u'] = xr.DataArray(u, dims=['level'])
        ds['wind_v'] = xr.DataArray(v, dims=['level'])

        if 'altitude_WGS84' in ds.keys():
            logging.debug('Calculate WGS84 altitude')
            logging.warning('Caution: This feature is under development. Especially, the correct averaging of the '
                            'position at the 0 meridian and close to the poles is not yet implemented.')
            # Convert lat, lon, alt to cartesian coordinates
            ecef = pyproj.Proj(proj='geocent', ellps='WGS84', datum='WGS84')
            lla = pyproj.Proj(proj='latlong', ellps='WGS84', datum='WGS84')
            x, y, z = pyproj.transform(lla, ecef,
                                                           ds.longitude.values,
                                                           ds.latitude.values,
                                                           ds.altitude_WGS84.values,
                                                           radians=False)
            variables_dict.update({'x':'x', 'y':'y', 'z':'z'})
            for var, val in {'x':x, 'y':y, 'z':z}.items():
                ds[var] = xr.DataArray(val, dims=['level'])
        else:
            logging.warning('No WGS84 altitude could be found.')

        logging.debug('Humidity calculations to get q')
        theta = calc_theta_from_T(ds['temperature'].values, ds['pressure'].values)
        e_s = calc_saturation_pressure(ds['temperature'].values + 273.15)
        w_s = mpcalc.mixing_ratio(e_s * units.Pa, ds['pressure'].values*units.hPa).magnitude
        w = ds['humidity'].values/100.*w_s
        q = w/(1+w)

        da_w = xr.DataArray([w*1000],
                            dims=['sounding', 'altitude'],
                            coords={'altitude': ds.altitude.values})
        da_theta = xr.DataArray([theta],
                                dims=['sounding', 'altitude'],
                                coords={'altitude': ds.altitude.values})
        da_q = xr.DataArray([q*1000],
                            dims=['sounding', 'altitude'],
                            coords={'altitude': ds.altitude.values})

        ds_new = xr.Dataset()
        for variable_name_in, variable_name_out in variables_dict.items():
            try:
                ds_new[variable_name_out] = xr.DataArray([ds[variable_name_in].values],
                                                         dims=['sounding', 'altitude'],
                                                         coords={'altitude': ds.altitude.values}
                                                         )
            except (KeyError, ValueError):
                ds_new[variable_name_out] = xr.DataArray([ds[variable_name_in].values],
                                                         dims=['sounding']
                                                         )
            ds_new[variable_name_out].attrs = ds[variable_name_in].attrs  # Copy attributes from input
        ds_new['mixing_ratio'] = da_w
        ds_new['theta'] = da_theta
        ds_new['specific_humidity'] = da_q

        flight_time_unix = ds_new.flight_time.astype(float)/1e9
        ds_new['flight_time'].values = flight_time_unix

        # Interpolation
        logging.debug("Starting interpolation")
        if args['method'] == 'linear':
            ds_new = ds_new.dropna(dim='altitude',
                                   subset=output_variables,
                                   how='any')
            ds_interp = ds_new.interp(altitude=interpolation_grid)
        elif args['method'] == 'bin':
            ds_interp = ds_new.groupby_bins('altitude', interpolation_bins,
                                            labels=interpolation_grid,
                                            restore_coord_dims=True).mean()
            ds_interp = ds_interp.transpose()
            ds_interp = ds_interp.rename({'altitude_bins':'altitude'})
            # Create bounds variable
            ds_interp['alt_bnds'] = xr.DataArray(np.array([interpolation_bins[:-1],interpolation_bins[1:]]).T,
                                                 dims=['altitude','nv'],
                                                 coords={'altitude': ds_interp.altitude.values}
                                                 )

            ds_interp['launch_time'] = ds_new['launch_time']

        ## Interpolation NaN
        logging.debug('Interpolate NaN')
        ds_interp = ds_interp.interpolate_na('altitude', max_gap=max_gap_fill, use_coordinate=True)

        dims_2d = ['sounding', 'altitude']
        coords_1d = {'altitude': ds_interp.altitude.values}

        wind_u = ds_interp.isel({'sounding': 0})['wind_u']
        wind_v = ds_interp.isel({'sounding': 0})['wind_v']
        dir, wsp = get_directional_wind(wind_u, wind_v)
        ds_interp['wind_direction'] = xr.DataArray([np.array(dir)],
                                                   dims=dims_2d,
                                                   coords=coords_1d)
        ds_interp['wind_speed'] = xr.DataArray([np.array(wsp)],
                                                   dims=dims_2d,
                                                   coords=coords_1d)
        if 'altitude_WGS84' in ds.keys():
            lon, lat, alt = pyproj.transform(ecef, lla,
                                             ds_interp['x'].values[0],
                                             ds_interp['y'].values[0],
                                             ds_interp['z'].values[0],
                                             radians=False)
            for var, val in {'latitude':lat, 'longitude':lon, 'altitude_WGS84': alt}.items():
                ds_interp[var] = xr.DataArray([val], dims=dims_2d, coords=coords_1d)

            del ds_interp['x']
            del ds_interp['y']
            del ds_interp['z']
            del ds_interp['altitude_WGS84']

        ds_input = ds_input.sortby('altitude')
        ds_input.altitude.load()
        ds_input.pressure.load()
        logging.debug("Pressure interpolation")
        interp_pres = pressure_interpolation(ds.pressure.values,
                                             ds.altitude.values,
                                             ds_interp.altitude.values)
        ds_interp['pressure'] = xr.DataArray([np.array(interp_pres)],
                                             dims=dims_2d,
                                             coords=coords_1d)

        ds_interp['launch_time'] = xr.DataArray([ds_interp.isel({'sounding': 0}).launch_time.item()/1e9],
                                                dims=['sounding'])
        ds_interp['platform'] = xr.DataArray([platform_number_dict[platform]],
                                             dims=['sounding'])

        # Calculations after interpolation
        logging.debug("Starting calculations after interpolation")
        # Recalculate temperature and relative humidity from theta and q
        temperature = calc_T_from_theta(ds_interp.isel(sounding=0)['theta'].values, ds_interp.isel(sounding=0)['pressure'].values)
        ds_interp['temperature'] = xr.DataArray([np.array(temperature)],
                                                   dims=dims_2d,
                                                   coords=coords_1d)

        w = (ds_interp.isel(sounding=0)['specific_humidity'].values/1000)/(1-ds_interp.isel(sounding=0)['specific_humidity'].values/1000.)
        e_s = calc_saturation_pressure(ds_interp.isel(sounding=0)['temperature'].values+273.15)
        w_s = mpcalc.mixing_ratio(e_s*units.Pa, ds_interp.isel(sounding=0)['pressure'].values*units.hPa).magnitude
        relative_humidity = w/w_s*100
 
        ds_interp['relative_humidity'] = xr.DataArray([np.array(relative_humidity)],
                                                   dims=dims_2d,
                                                   coords=coords_1d)

        # Count number of measurements within each bin
        ds_interp['N_ptu'] = xr.DataArray(
            ds_new.pressure.groupby_bins('altitude', interpolation_bins, labels=interpolation_grid,
                                         restore_coord_dims=True).count().values,
            dims=dims_2d,
            coords=coords_1d)
        ds_interp['N_gps'] = xr.DataArray(
            ds_new.latitude.groupby_bins('altitude', interpolation_bins, labels=interpolation_grid,
                                         restore_coord_dims=True).count().values,
            dims=dims_2d,
            coords=coords_1d)

        # Cell method used
        data_exists = np.where(np.isnan(ds_interp.pressure), False, True)
        data_mean = np.where(np.isnan(ds_interp.N_ptu), False, True)  # no data or interp: nan; mean > 0
        data_method = np.zeros_like(data_exists, dtype='uint')
        data_method[np.logical_and(data_exists, data_mean)] = 2
        data_method[np.logical_and(data_exists, ~data_mean)] = 1
        ds_interp['m_ptu'] = xr.DataArray(data_method, dims=dims_2d, coords=coords_1d)
        ds_interp['N_ptu'].values[np.logical_and(~data_mean, data_method > 0)] = 0

        data_exists = np.where(np.isnan(ds_interp.latitude), False, True)
        data_mean = np.where(np.isnan(ds_interp.N_gps), False, True)  # no data or interp: nan; mean > 0
        data_method = np.zeros_like(data_exists, dtype='uint')
        data_method[np.logical_and(data_exists, data_mean)] = 2
        data_method[np.logical_and(data_exists, ~data_mean)] = 1
        ds_interp['m_gps'] = xr.DataArray(data_method, dims=dims_2d, coords=coords_1d)
        ds_interp['N_gps'].values[np.logical_and(~data_mean, data_method > 0)] = 0

        direction = get_direction(ds_interp, ds)
        if direction == 'ascending':
            ds_interp['ascent_flag'] = xr.DataArray([1], dims=['sounding'])
        else:
            ds_interp['ascent_flag'] = xr.DataArray([0], dims=['sounding'])

        # Copy trajectory id from level1 dataset
        ds_interp['sounding'] = xr.DataArray([ds['sounding'].values], dims=['sounding'])
        ds_interp.sounding.attrs = ds['sounding'].attrs

        script_basename = os.path.basename(__file__)
        script_modification_time = time.ctime(os.path.getmtime(os.path.realpath(__file__)))
        glob_attrs_dict = {'title': 'EUREC4A interpolated sounding data',
                             'platform': platform,
                             'surface_altitude': ds.attrs['surface_altitude'],
                             'instrument': ds.instrument,
                             'doi': 'pending',
                             'created_with': '{file} with its last modifications on {time}'.
                             format(time=script_modification_time,
                                    file=script_basename),
                             'git_version': git_module_version,
                             'python_version': "{} (with numpy:{}, netCDF4:{}, eurec4a_snd:{})".
                             format(sys.version, np.__version__, netCDF4.__version__, __version__),
                             'created_on': str(time.ctime(time.time())),
                             'featureType': 'trajectory',
                             'Conventions': 'CF-1.7'
                             }

        # Overwrite standard attrs with those defined in config file
        # Get global meta data from mwx_config.json
        level='L2'
        glob_attrs_dict2 = get_global_attrs(json_config_fn, f'{ds.campaign_id}_{ds.platform}_{ds.instrument_id}_{level}')

        for attrs, value in glob_attrs_dict2.items():
            glob_attrs_dict[attrs] = value

        import pandas as pd

        def convert_num2_date_with_nan(num, format):
            if not np.isnan(num):
                return num2date(num, format, only_use_python_datetimes=True, only_use_cftime_datetimes=False)
            else:
                return pd.NaT

        convert_nums2date = np.vectorize(convert_num2_date_with_nan)

        ds_interp['flight_time'].data = convert_nums2date(ds_interp.flight_time.data, "seconds since 1970-01-01")
        ds_interp['launch_time'].data = convert_nums2date(ds_interp.launch_time.data, "seconds since 1970-01-01")

        for variable in ['temperature', 'dew_point', 'wind_speed', 'pressure',
                         'wind_u', 'wind_v', 'latitude', 'longitude',
                         'mixing_ratio', 'wind_direction',
                         'specific_humidity', 'relative_humidity',
                         'ascent_rate'
                         ]:
    #             ds_interp[variable].values = np.round(ds_interp[variable].values, 2)
            ds_interp[variable].encoding['dtype'] = 'f4'
        ds_interp['ascent_flag'].encoding['dtype'] = 'int16'
        ds_interp['platform'].encoding['dtype'] = 'int16'
        ds_interp['m_gps'].encoding['dtype'] = 'int16'
        ds_interp['m_ptu'].encoding['dtype'] = 'int16'
        ds_interp['N_gps'].encoding['dtype'] = 'f4'
        ds_interp['N_ptu'].encoding['dtype'] = 'f4'
        ds_interp['alt_bnds'].encoding['dtype'] = 'int16'
        ds_interp['altitude'].encoding['dtype'] = 'int16'
        ds_interp.sounding.encoding = {'dtype': 'str'}

        ds_interp = set_global_attributes(ds_interp, glob_attrs_dict)
        ds_interp = set_additional_var_attributes(ds_interp, meta_data_dict)

        # Transpose dataset if necessary
        for variable in ds_interp.data_vars:
             if variable == 'alt_bnds': continue
             dims = ds_interp[variable].dims
             if (len(dims) == 2) and (dims[0] != 'sounding'):
                 ds_interp[variable] = ds_interp[variable].T

        time_dt = pd.Timestamp(np.datetime64(ds_interp.isel({'sounding': 0}).launch_time.data.astype("<M8[ns]")))

        time_fmt = time_dt.strftime('%Y%m%dT%H%M')
        platform_filename = platform  # platform_rename_dict[platform]
        outfile = args['outputfolder']+'{campaign}_{platform}_{instrument_id}_{level}_{date}_{version}.nc'.format(campaign=ds.campaign_id,
                                                                                                                  platform=platform_filename,
                                                                                                                  instrument_id=ds.instrument_id,
                                                                                                                  level=level,
                                                                                                                  date=time_fmt,
                                                                                                                  version=git_module_version
                                                                                                                  )
        logging.info('Write output to {}'.format(outfile))
        write_dataset(ds_interp, outfile)
Exemplo n.º 8
0
time = sorted(time)
print(time)

temp = data.variables['air_temperature'][:] * units('degC')
dewp = data.variables['dew_point_temperature'][:] * units('degC')
slp = data.variables['inches_ALTIM'][:] * units('inHg')
wspd = data.variables['wind_speed'][:] * units('m/s')
wdir = data.variables['wind_from_direction'][:] * units('degree')

########################################
# Use MetPy Calculations to calculate RH
# --------------------------------------

# Get ambient partial pressure, use to calculate mixing ratio
es = mpcalc.saturation_vapor_pressure(dewp)
mixr = mpcalc.mixing_ratio(es, slp)

# Calculate vapor pressure
vp = mpcalc.vapor_pressure(slp, mixr)

# Calculate saturation vapor pressure
svp = mpcalc.saturation_vapor_pressure(temp)

# Calculate relative humidity as a percentage
rh = (vp / svp) * 100


########################################
# Make Meteogram Plot
# -------------------
Exemplo n.º 9
0
                        'potential_T'] = mpcalc.potential_temperature(p, T)
                    df_selected_time['potential_T_C'] = df_selected_time[
                        'potential_T'].values - 273.15

                    df_selected_time[
                        'saturation_vaper_pressure'] = mpcalc.saturation_vapor_pressure(
                            T)
                    df_selected_time[
                        'vaper_pressure'] = mpcalc.saturation_vapor_pressure(
                            Td)
                    VPS = df_selected_time[
                        'saturation_vaper_pressure'].values * units.hPa
                    VP = df_selected_time['vaper_pressure'].values * units.hPa

                    df_selected_time[
                        'saturation_mixing_ratio'] = mpcalc.mixing_ratio(
                            VPS, p)
                    df_selected_time['mixing_ratio'] = mpcalc.mixing_ratio(
                        VP, p)
                    MRS = df_selected_time[
                        'saturation_mixing_ratio'].values * units('g/kg')
                    MR = df_selected_time['mixing_ratio'].values * units(
                        'g/kg')

                    # Calculate RH
                    #RH = mpcalc.relative_humidity_from_dewpoint(T[0], Td[0])
                    df_selected_time[
                        'RH'] = mpcalc.relative_humidity_from_dewpoint(T, Td)
                    df_selected_time[
                        'RH_MR'] = mpcalc.relative_humidity_from_mixing_ratio(
                            MR, T, p)
Exemplo n.º 10
0
def test_mixing_ratio():
    """Test mixing ratio calculation."""
    p = 998. * units.mbar
    e = 73.75 * units.mbar
    assert_almost_equal(mixing_ratio(e, p), 0.04963, 2)
Exemplo n.º 11
0
def entropy_plots(pressure,
                  temperature,
                  mixing_ratio,
                  altitude,
                  h0_std=2000,
                  ensemble_size=20,
                  ent_rate=np.arange(0, 2, 0.05),
                  entrain=False):
    """
    plotting the summarized entropy diagram with annotations and thermodynamic parameters
    """
    p = pressure * units('mbar')
    T = temperature * units('degC')
    q = mixing_ratio * units('kilogram/kilogram')
    qs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(T), p)
    Td = mpcalc.dewpoint(mpcalc.vapor_pressure(p, q))  # dewpoint
    Tp = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC')  # parcel profile

    # Altitude based on the hydrostatic eq.
    if len(altitude) == len(pressure):  # (1) altitudes for whole levels
        altitude = altitude * units('meter')
    elif len(altitude
             ) == 1:  # (2) known altitude where the soundings was launched
        z_surf = altitude.copy() * units('meter')
        # given altitude
        altitude = np.zeros((np.size(T))) * units('meter')
        for i in range(np.size(T)):
            altitude[i] = mpcalc.thickness_hydrostatic(
                p[:i + 1], T[:i + 1]) + z_surf  # Hypsometric Eq. for height
    else:
        print(
            '***NOTE***: the altitude at the surface is assumed 0 meter, and altitudes are derived based on the hypsometric equation'
        )
        altitude = np.zeros(
            (np.size(T))) * units('meter')  # surface is 0 meter
        for i in range(np.size(T)):
            altitude[i] = mpcalc.thickness_hydrostatic(
                p[:i + 1], T[:i + 1])  # Hypsometric Eq. for height

    # specific entropy [joule/(kg*K)]
    # sd : specific entropy of dry air
    # sm1 : specific entropy of airborne mositure in state 1 (water vapor)
    # sm2 : specific entropy of airborne mositure in state 2 (saturated water vapor)

    sd = entropy(T.magnitude, q.magnitude * 1e-6, p.magnitude)
    sm1 = entropy(T.magnitude, q.magnitude, p.magnitude)
    sm2 = entropy(T.magnitude, qs.magnitude, p.magnitude)
    ###############################

    # Water vapor calculations
    p_PWtop = min(p)
    #p_PWtop = max(200*units.mbar, min(p) + 1*units.mbar) # integrating until 200mb
    cwv = mpcalc.precipitable_water(Td, p,
                                    top=p_PWtop)  # column water vapor [mm]
    cwvs = mpcalc.precipitable_water(
        T, p, top=p_PWtop)  # saturated column water vapor [mm]
    crh = (cwv / cwvs) * 100.  # column relative humidity [%]

    #================================================
    # plotting MSE vertical profiles
    fig = plt.figure(figsize=[12, 8])
    ax = fig.add_axes([0.1, 0.1, 0.6, 0.8])
    ax.plot(sd, p, '-k', linewidth=2)
    ax.plot(sm1, p, '-b', linewidth=2)
    ax.plot(sm2, p, '-r', linewidth=2)

    # mse based on different percentages of relative humidity
    qr = np.zeros((9, np.size(qs))) * units('kilogram/kilogram')
    sm1_r = qr  # container
    for i in range(9):
        qr[i, :] = qs * 0.1 * (i + 1)
        sm1_r[i, :] = entropy(T.magnitude, qr[i, :].magnitude, p.magnitude)

    for i in range(9):
        ax.plot(sm1_r[i, :], p[:], '-', color='grey', linewidth=0.7)
        ax.text(sm1_r[i, 3].magnitude - 2, p[3].magnitude, str((i + 1) * 10))

    # drawing LCL and LFC levels
    [lcl_pressure, lcl_temperature] = mpcalc.lcl(p[0], T[0], Td[0])
    lcl_idx = np.argmin(np.abs(p.magnitude - lcl_pressure.magnitude))

    [lfc_pressure, lfc_temperature] = mpcalc.lfc(p, T, Td)
    lfc_idx = np.argmin(np.abs(p.magnitude - lfc_pressure.magnitude))

    # conserved mse of air parcel arising from 1000 hpa
    sm1_p = np.squeeze(np.ones((1, np.size(T))) * sm1[0])

    # illustration of CAPE
    el_pressure, el_temperature = mpcalc.el(p, T, Td)  # equilibrium level
    el_idx = np.argmin(np.abs(p.magnitude - el_pressure.magnitude))
    ELps = [el_pressure.magnitude
            ]  # Initialize an array of EL pressures for detrainment profile

    [CAPE, CIN] = mpcalc.cape_cin(p[:el_idx], T[:el_idx], Td[:el_idx],
                                  Tp[:el_idx])

    plt.plot(sm1_p, p, color='green', linewidth=2)
    #ax.fill_betweenx(p[lcl_idx:el_idx+1],sm1_p[lcl_idx:el_idx+1],sm2[lcl_idx:el_idx+1],interpolate=True
    #                ,color='green',alpha='0.3')

    ax.fill_betweenx(p, sd, sm1, color='deepskyblue', alpha='0.5')
    ax.set_xlabel('Specific entropies: sd, sm, sm_sat [J K$^{-1}$ kg$^{-1}$]',
                  fontsize=14)
    ax.set_ylabel('Pressure [hPa]', fontsize=14)
    ax.set_xticks([0, 50, 100, 150, 200, 250, 300, 350])
    ax.set_xlim([0, 440])
    ax.set_ylim(1030, 120)

    if entrain is True:
        # Depict Entraining parcels
        # Parcel mass solves dM/dz = eps*M, solution is M = exp(eps*Z)
        # M=1 at ground without loss of generality

        # Distribution of surface parcel h offsets
        h0offsets = np.sort(np.random.normal(
            0, h0_std, ensemble_size)) * units('joule/kilogram')
        # Distribution of entrainment rates
        entrainment_rates = ent_rate / (units('km'))

        for h0offset in h0offsets:

            h4ent = sm1.copy()
            h4ent[0] += h0offset

            for eps in entrainment_rates:

                M = np.exp(eps * (altitude - altitude[0])).to('dimensionless')
                # dM is the mass contribution at each level, with 1 at the origin level.
                M[0] = 0
                dM = np.gradient(M)
                # parcel mass is a sum of all the dM's at each level
                # conserved linearly-mixed variables like h are weighted averages
                if eps.magnitude == 0.0:
                    hent = np.ones(len(h4ent)) * h4ent[0]  # no mixing
                else:
                    hent = np.cumsum(dM * h4ent) / np.cumsum(dM)
                # Boolean for positive buoyancy, and its topmost altitude (index) where curve is clippes
                posboy = (hent > sm2)
                posboy[0] = True  # so there is always a detrainment level

                # defining the first EL by posboy as the detrainment layer, swiching from positive buoyancy to
                # negative buoyancy (0 to 1) and skipping the surface
                ELindex_ent = 0
                for idx in range(len(posboy) - 1):
                    if posboy[idx + 1] == 0 and posboy[idx] == 1 and idx > 0:
                        ELindex_ent = idx
                        break

                # Plot the curve
                plt.plot(hent[0:ELindex_ent + 2],
                         p[0:ELindex_ent + 2],
                         linewidth=0.6,
                         color='g')
                #plt.plot( hent[0:], p[0:], linewidth=0.6, color='g')
                # Keep a list for a histogram plot (detrainment profile)
                if p[ELindex_ent].magnitude < lfc_pressure.magnitude:  # buoyant parcels only
                    ELps.append(p[ELindex_ent].magnitude)

        # Plot a crude histogram of parcel detrainment levels
        NBINS = 20
        pbins = np.linspace(1000, 150,
                            num=NBINS)  # pbins for detrainment levels
        hist = np.zeros((len(pbins) - 1))
        for x in ELps:
            for i in range(len(pbins) - 1):
                if (x < pbins[i]) & (x >= pbins[i + 1]):
                    hist[i] += 1
                    break

        det_per = hist / sum(hist) * 100
        # percentages of detrainment ensumbles at levels

        ax2 = fig.add_axes([0.705, 0.1, 0.1, 0.8], facecolor=None)
        ax2.barh(pbins[1:],
                 det_per,
                 color='lightgrey',
                 edgecolor='k',
                 height=15 * (20 / NBINS))
        ax2.set_xlim([0, 100])
        ax2.set_xticks([0, 20, 40, 60, 80, 100])
        ax2.set_ylim([1030, 120])
        ax2.set_xlabel('Detrainment [%]')
        ax2.grid()
        ax2.set_zorder(2)

        ax.plot([400, 400], [1100, 0])
        ax.annotate('Detrainment', xy=(362, 320), color='dimgrey')
        ax.annotate('ensemble: ' + str(ensemble_size * len(entrainment_rates)),
                    xy=(364, 340),
                    color='dimgrey')
        ax.annotate('Detrainment', xy=(362, 380), color='dimgrey')
        ax.annotate(' scale: 0 - 2 km', xy=(365, 400), color='dimgrey')

        # Overplots on the mess: undilute parcel and CAPE, etc.
        ax.plot((1, 1) * sm1[0], (1, 0) * (p[0]), color='g', linewidth=2)

        # Replot the sounding on top of all that mess
        ax.plot(sm2, p, color='r', linewidth=1.5)
        ax.plot(sm1, p, color='b', linewidth=1.5)

        # label LCL and LCF
        ax.plot((sm2[lcl_idx] + (-2000, 2000) * units('joule/kilogram')),
                lcl_pressure + (0, 0) * units('mbar'),
                color='orange',
                linewidth=3)
        ax.plot((sm2[lfc_idx] + (-2000, 2000) * units('joule/kilogram')),
                lfc_pressure + (0, 0) * units('mbar'),
                color='magenta',
                linewidth=3)

    # Plot a crude histogram of parcel detrainment levels
    # Text parts
    ax.text(30, pressure[3], 'RH (%)', fontsize=11, color='k')
    ax.text(20,
            200,
            'CAPE = ' + str(np.around(CAPE.magnitude, decimals=2)) + ' [J/kg]',
            fontsize=12,
            color='green')
    ax.text(20,
            250,
            'CIN = ' + str(np.around(CIN.magnitude, decimals=2)) + ' [J/kg]',
            fontsize=12,
            color='green')
    ax.text(20,
            300,
            'LCL = ' + str(np.around(lcl_pressure.magnitude, decimals=2)) +
            ' [hpa]',
            fontsize=12,
            color='darkorange')
    ax.text(20,
            350,
            'LFC = ' + str(np.around(lfc_pressure.magnitude, decimals=2)) +
            ' [hpa]',
            fontsize=12,
            color='magenta')
    ax.text(20,
            400,
            'CWV = ' + str(np.around(cwv.magnitude, decimals=2)) + ' [mm]',
            fontsize=12,
            color='deepskyblue')
    ax.text(20,
            450,
            'CRH = ' + str(np.around(crh.magnitude, decimals=2)) + ' [%]',
            fontsize=12,
            color='blue')
    ax.legend(['DEnt', 'MEnt', 'SMEnt'], fontsize=12, loc=1)

    ax.set_zorder(3)

    return (ax)
Exemplo n.º 12
0
def msed_plots(pressure,
               temperature,
               mixing_ratio,
               h0_std=2000,
               ensemble_size=20,
               ent_rate=np.arange(0, 2, 0.05),
               entrain=False):
    """
    plotting the summarized static energy diagram with annotations and thermodynamic parameters
    """
    p = pressure * units('mbar')
    T = temperature * units('degC')
    q = mixing_ratio * units('kilogram/kilogram')
    qs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(T), p)
    Td = mpcalc.dewpoint(mpcalc.vapor_pressure(p, q))  # dewpoint
    Tp = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC')  # parcel profile

    # Altitude based on the hydrostatic eq.
    altitude = np.zeros((np.size(T))) * units('meter')  # surface is 0 meter
    for i in range(np.size(T)):
        altitude[i] = mpcalc.thickness_hydrostatic(
            p[:i + 1], T[:i + 1])  # Hypsometric Eq. for height

    # Static energy calculations
    mse = mpcalc.moist_static_energy(altitude, T, q)
    mse_s = mpcalc.moist_static_energy(altitude, T, qs)
    dse = mpcalc.dry_static_energy(altitude, T)

    # Water vapor calculations
    p_PWtop = max(200 * units.mbar,
                  min(p) + 1 * units.mbar)  # integrating until 200mb
    cwv = mpcalc.precipitable_water(Td, p,
                                    top=p_PWtop)  # column water vapor [mm]
    cwvs = mpcalc.precipitable_water(
        T, p, top=p_PWtop)  # saturated column water vapor [mm]
    crh = (cwv / cwvs) * 100.  # column relative humidity [%]

    #================================================
    # plotting MSE vertical profiles
    fig = plt.figure(figsize=[12, 8])
    ax = fig.add_axes([0.1, 0.1, 0.6, 0.8])
    ax.plot(dse, p, '-k', linewidth=2)
    ax.plot(mse, p, '-b', linewidth=2)
    ax.plot(mse_s, p, '-r', linewidth=2)

    # mse based on different percentages of relative humidity
    qr = np.zeros((9, np.size(qs))) * units('kilogram/kilogram')
    mse_r = qr * units('joule/kilogram')  # container
    for i in range(9):
        qr[i, :] = qs * 0.1 * (i + 1)
        mse_r[i, :] = mpcalc.moist_static_energy(altitude, T, qr[i, :])

    for i in range(9):
        ax.plot(mse_r[i, :], p[:], '-', color='grey', linewidth=0.7)
        ax.text(mse_r[i, 3].magnitude / 1000 - 1, p[3].magnitude,
                str((i + 1) * 10))

    # drawing LCL and LFC levels
    [lcl_pressure, lcl_temperature] = mpcalc.lcl(p[0], T[0], Td[0])
    lcl_idx = np.argmin(np.abs(p.magnitude - lcl_pressure.magnitude))

    [lfc_pressure, lfc_temperature] = mpcalc.lfc(p, T, Td)
    lfc_idx = np.argmin(np.abs(p.magnitude - lfc_pressure.magnitude))

    # conserved mse of air parcel arising from 1000 hpa
    mse_p = np.squeeze(np.ones((1, np.size(T))) * mse[0].magnitude)

    # illustration of CAPE
    el_pressure, el_temperature = mpcalc.el(p, T, Td)  # equilibrium level
    el_idx = np.argmin(np.abs(p.magnitude - el_pressure.magnitude))
    ELps = [el_pressure.magnitude
            ]  # Initialize an array of EL pressures for detrainment profile

    [CAPE, CIN] = mpcalc.cape_cin(p[:el_idx], T[:el_idx], Td[:el_idx],
                                  Tp[:el_idx])

    plt.plot(mse_p, p, color='green', linewidth=2)
    ax.fill_betweenx(p[lcl_idx:el_idx + 1],
                     mse_p[lcl_idx:el_idx + 1],
                     mse_s[lcl_idx:el_idx + 1],
                     interpolate=True,
                     color='green',
                     alpha='0.3')

    ax.fill_betweenx(p, dse, mse, color='deepskyblue', alpha='0.5')
    ax.set_xlabel('Specific static energies: s, h, hs [kJ kg$^{-1}$]',
                  fontsize=14)
    ax.set_ylabel('Pressure [hpa]', fontsize=14)
    ax.set_xticks([280, 300, 320, 340, 360, 380])
    ax.set_xlim([280, 390])
    ax.set_ylim(1030, 120)

    if entrain is True:
        # Depict Entraining parcels
        # Parcel mass solves dM/dz = eps*M, solution is M = exp(eps*Z)
        # M=1 at ground without loss of generality

        # Distribution of surface parcel h offsets
        H0STDEV = h0_std  # J/kg
        h0offsets = np.sort(np.random.normal(
            0, H0STDEV, ensemble_size)) * units('joule/kilogram')
        # Distribution of entrainment rates
        entrainment_rates = ent_rate / (units('km'))

        for h0offset in h0offsets:

            h4ent = mse.copy()
            h4ent[0] += h0offset

            for eps in entrainment_rates:

                M = np.exp(eps * (altitude - altitude[0])).to('dimensionless')
                # dM is the mass contribution at each level, with 1 at the origin level.
                M[0] = 0
                dM = np.gradient(M)

                # parcel mass is a  sum of all the dM's at each level
                # conserved linearly-mixed variables like h are weighted averages
                hent = np.cumsum(dM * h4ent) / np.cumsum(dM)

                # Boolean for positive buoyancy, and its topmost altitude (index) where curve is clippes
                posboy = (hent > mse_s)
                posboy[0] = True  # so there is always a detrainment level

                ELindex_ent = np.max(np.where(posboy))
                # Plot the curve
                plt.plot(hent[0:ELindex_ent + 2],
                         p[0:ELindex_ent + 2],
                         linewidth=0.25,
                         color='g')
                # Keep a list for a histogram plot (detrainment profile)
                if p[ELindex_ent].magnitude < lfc_pressure.magnitude:  # buoyant parcels only
                    ELps.append(p[ELindex_ent].magnitude)

        # Plot a crude histogram of parcel detrainment levels
        NBINS = 20
        pbins = np.linspace(1000, 150,
                            num=NBINS)  # pbins for detrainment levels
        hist = np.zeros((len(pbins) - 1))
        for x in ELps:
            for i in range(len(pbins) - 1):
                if (x < pbins[i]) & (x >= pbins[i + 1]):
                    hist[i] += 1
                    break

        det_per = hist / sum(hist) * 100
        # percentages of detrainment ensumbles at levels

        ax2 = fig.add_axes([0.705, 0.1, 0.1, 0.8], facecolor=None)
        ax2.barh(pbins[1:],
                 det_per,
                 color='lightgrey',
                 edgecolor='k',
                 height=15 * (20 / NBINS))
        ax2.set_xlim([0, max(det_per)])
        ax2.set_ylim([1030, 120])
        ax2.set_xlabel('Detrainment [%]')
        ax2.grid()
        ax2.set_zorder(2)

        ax.plot([400, 400], [1100, 0])
        ax.annotate('Detrainment', xy=(362, 320), color='dimgrey')
        ax.annotate('ensemble: ' + str(ensemble_size * len(entrainment_rates)),
                    xy=(364, 340),
                    color='dimgrey')
        ax.annotate('Detrainment', xy=(362, 380), color='dimgrey')
        ax.annotate(' scale: 0 - 2 km', xy=(365, 400), color='dimgrey')

        # Overplots on the mess: undilute parcel and CAPE, etc.
        ax.plot((1, 1) * mse[0], (1, 0) * (p[0]), color='g', linewidth=2)

        # Replot the sounding on top of all that mess
        ax.plot(mse_s, p, color='r', linewidth=1.5)
        ax.plot(mse, p, color='b', linewidth=1.5)

        # label LCL and LCF
        ax.plot((mse_s[lcl_idx] + (-2000, 2000) * units('joule/kilogram')),
                lcl_pressure + (0, 0) * units('mbar'),
                color='orange',
                linewidth=3)
        ax.plot((mse_s[lfc_idx] + (-2000, 2000) * units('joule/kilogram')),
                lfc_pressure + (0, 0) * units('mbar'),
                color='magenta',
                linewidth=3)

    ### Internal waves (100m adiabatic displacements, assumed adiabatic: conserves s, sv, h).
    #dZ = 100 *mpunits.units.meter
    dp = 1000 * units.pascal

    # depict displacements at sounding levels nearest these target levels
    targetlevels = [900, 800, 700, 600, 500, 400, 300, 200] * units.hPa
    for ilev in targetlevels:
        idx = np.argmin(np.abs(p - ilev))

        # dp: hydrostatic
        rho = (p[idx]) / Rd / (T[idx])
        dZ = -dp / rho / g

        # dT: Dry lapse rate dT/dz_dry is -g/Cp
        dT = (-g / Cp_d * dZ).to('kelvin')
        Tdisp = T[idx].to('kelvin') + dT

        # dhsat
        dqs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(Tdisp),
                                  p[idx] + dp) - qs[idx]
        dhs = g * dZ + Cp_d * dT + Lv * dqs

        # Whiskers on the data plots
        ax.plot((mse_s[idx] + dhs * (-1, 1)),
                p[idx] + dp * (-1, 1),
                linewidth=3,
                color='r')
        ax.plot((dse[idx] * (1, 1)),
                p[idx] + dp * (-1, 1),
                linewidth=3,
                color='k')
        ax.plot((mse[idx] * (1, 1)),
                p[idx] + dp * (-1, 1),
                linewidth=3,
                color='b')

        # annotation to explain it
        if ilev == 400 * ilev.units:
            ax.plot(360 * mse_s.units + dhs * (-1, 1) / 1000,
                    440 * units('mbar') + dp * (-1, 1),
                    linewidth=3,
                    color='r')
            ax.annotate('+/- 10mb', xy=(362, 440), fontsize=8)
            ax.annotate(' adiabatic displacement', xy=(362, 460), fontsize=8)

    # Plot a crude histogram of parcel detrainment levels
    # Text parts
    ax.text(290, pressure[3], 'RH (%)', fontsize=11, color='k')
    ax.text(285,
            200,
            'CAPE = ' + str(np.around(CAPE.magnitude, decimals=2)) + ' [J/kg]',
            fontsize=12,
            color='green')
    ax.text(285,
            250,
            'CIN = ' + str(np.around(CIN.magnitude, decimals=2)) + ' [J/kg]',
            fontsize=12,
            color='green')
    ax.text(285,
            300,
            'LCL = ' + str(np.around(lcl_pressure.magnitude, decimals=2)) +
            ' [hpa]',
            fontsize=12,
            color='darkorange')
    ax.text(285,
            350,
            'LFC = ' + str(np.around(lfc_pressure.magnitude, decimals=2)) +
            ' [hpa]',
            fontsize=12,
            color='magenta')
    ax.text(285,
            400,
            'CWV = ' + str(np.around(cwv.magnitude, decimals=2)) + ' [mm]',
            fontsize=12,
            color='deepskyblue')
    ax.text(285,
            450,
            'CRH = ' + str(np.around(crh.magnitude, decimals=2)) + ' [%]',
            fontsize=12,
            color='blue')
    ax.legend(['DSE', 'MSE', 'SMSE'], fontsize=12, loc=1)

    ax.set_zorder(3)

    return (ax)
Exemplo n.º 13
0
def EnergyMassPlot(pressure, temperature, dewpoint,
                  height, uwind, vwind, sphum=None, rh=None,
                  label='', size=(12,10), return_fig=False): 
    p=pressure
    Z=height
    T=temperature
    Td=dewpoint
    if isinstance(sphum,np.ndarray) and isinstance(rh,np.ndarray):
        q=sphum
        qs=q/rh
    else:
        q = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(Td),p)
        qs= mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(T),p)
    
    s = g*Z + Cp_d*T 
    sv= g*Z + Cp_d*mpcalc.virtual_temperature(T,q)
    h = s            + Lv*q
    hs= s            + Lv*qs
    
    parcel_Tprofile = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC')
    CAPE,CIN = mpcalc.cape_cin(p,T,Td,parcel_Tprofile)
    ELp,ELT = mpcalc.el(p,T,Td)
    ELindex = np.argmin(np.abs(p - ELp))
    lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0])
    
    p_PWtop = max(200*units.mbar, min(p) +1*units.mbar)
    PW = mpcalc.precipitable_water(Td,p, top=p_PWtop)
    PWs = mpcalc.precipitable_water(T,p, top=p_PWtop)
    CRH = (PW/PWs).magnitude *100. 
    
    fig,ax=setup_fig(size=size,label=label)
    
    ax.plot(s  /1000, p, color='r', linewidth=1.5)  ### /1000 for kJ/kg
    ax.plot(sv /1000, p, color='r', linestyle='-.') 
    ax.plot(h  /1000, p, color='b', linewidth=1.5) 
    ax.plot(hs /1000, p, color='r', linewidth=1.5) 
    ### RH rulings between s and h lines: annotate near 800 hPa level
    annot_level = 800 #hPa
    idx = np.argmin(np.abs(p - annot_level *units.hPa))
    right_annot_loc = 380
    for iRH in np.arange(10,100,10):
        ax.plot( (s+ Lv*qs*iRH/100.)/1000, p, linewidth=0.5, linestyle=':', color='k')
        ax.annotate(str(iRH), xy=( (s[idx]+Lv*qs[idx]*iRH/100.)/1000, annot_level),                    
                    horizontalalignment='center',fontsize=6)
    ax.annotate('RH (%)', xy=(right_annot_loc, annot_level), fontsize=10)
    
    if  not np.isnan(CAPE.magnitude) and CAPE.magnitude >10:  
        parcelh  = h [0]        # for a layer mean: np.mean(h[idx1:idx2])
        parcelsv = sv[0]
        parcelp0 = p[0]

        # Undilute parcel
        ax.plot( (1,1)*parcelh/1000., (1,0)*parcelp0, linewidth=0.5, color='g')
        maxbindex = np.argmax(parcel_Tprofile - T)
        ax.annotate('CAPE='+str(int(CAPE.magnitude)), 
                    xy=(parcelh/1000., p[maxbindex]), color='g')

        # Plot LCL at saturation point, above the lifted sv of the surface parcel 
        lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0])
        ax.annotate('LCL', xy=(sv[0]/1000., lcl_pressure), fontsize=10, color='g', horizontalalignment='right')
        # Purple fill for negative buoyancy below LCL:
        ax.fill_betweenx(p, sv/1000., parcelsv/1000., where=p>lcl_pressure, facecolor='purple', alpha=0.4)

        # Positive moist convective buoyancy in green 
        # Above LCL:
        ax.fill_betweenx(p, hs/1000., parcelh/1000., where= parcelh>hs, facecolor='g', alpha=0.4)


        # Depict Entraining parcels
        # Parcel mass solves dM/dz = eps*M, solution is M = exp(eps*Z)
        # M=1 at ground without loss of generality
        entrainment_distance = 10000., 5000., 2000. 
        ax.annotate('entrain: 10,5,2 km',  xy=(parcelh/1000, 140), color='g', 
                    fontsize=8, horizontalalignment='right')
        ax.annotate('parcel h',            xy=(parcelh/1000, 120), color='g', 
                    fontsize=10, horizontalalignment='right')

        for ED in entrainment_distance: 
            eps = 1.0 / (ED*units.meter)
            M = np.exp(eps * (Z-Z[0]).to('m')).to('dimensionless')

            # dM is the mass contribution at each level, with 1 at the origin level. 
            M[0] = 0
            dM = np.gradient(M)

            # parcel mass is a  sum of all the dM's at each level
            # conserved linearly-mixed variables like h are weighted averages 
            hent = np.cumsum(dM*h) / np.cumsum(dM)

            ax.plot( hent[0:ELindex+3]/1000., p[0:ELindex+3], linewidth=0.5, color='g')
        ### Internal waves (100m adiabatic displacements, assumed adiabatic: conserves s, sv, h). 
    dZ = 100 *units.meter

    # depict displacements at sounding levels nearest these target levels
    targetlevels = [900,800,700,600,500,400,300,200]*units.hPa
    for ilev in targetlevels:
        idx = np.argmin(np.abs(p - ilev))

        # dT: Dry lapse rate dT/dz_dry is -g/Cp
        dT = (-g/Cp_d *dZ).to('kelvin')    
        Tdisp = T[idx].to('kelvin') + dT

        # dp: hydrostatic
        rho = (p[idx]/Rd/T[idx])
        dp = -rho*g*dZ

        # dhsat
        #qs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(T)     ,p)
        dqs = mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(Tdisp) ,p[idx]+dp) -qs[idx]
        dhs = g*dZ + Cp_d*dT + Lv*dqs

        # Whiskers on the data plots
        ax.plot( (hs[idx]+dhs*(-1,1))/1000, p[idx]+dp*(-1,1), linewidth=3, color='r')  
        ax.plot( (s [idx]    *( 1,1))/1000, p[idx]+dp*(-1,1), linewidth=3, color='r')  
        ax.plot( (h [idx]    *( 1,1))/1000, p[idx]+dp*(-1,1), linewidth=3, color='b')  

        # annotation to explain it 
        if ilev == 600*ilev.units:
            ax.plot(right_annot_loc*hs.units +dhs*(-1,1)/1000, p[idx]+dp*(-1,1), linewidth=3, color='r')  
            ax.annotate('+/- 100m', xy=(right_annot_loc,600), fontsize=8)
            ax.annotate('  internal', xy=(right_annot_loc,630), fontsize=8)
            ax.annotate('  waves', xy=(right_annot_loc,660), fontsize=8)


    ### Blue fill proportional to precipitable water, and blue annotation
    ax.fill_betweenx(p, s/1000., h/1000., where=h > s, facecolor='b', alpha=0.4)

    # Have to specify the top of the PW integral. 
    # I want whole atmosphere of course, but 200 hPa captures it all really. 
    #import metpy.calc as mpcalc
    p_PWtop = max(200*units.mbar, min(p) +1*units.mbar)
    PW = mpcalc.precipitable_water(Td,p, top=p_PWtop)
    PWs = mpcalc.precipitable_water(T,p, top=p_PWtop)
    CRH = (PW/PWs).magnitude *100. 

    # PW annotation arrow tip at 700 mb
    idx = np.argmin(np.abs(p - 700*p.units))
    centerblue = (s[idx]+h[idx])/2.0 /1000.

    ax.annotate('CWV='+str(round(PW.to('mm').magnitude, 1))+'mm',
                xy=(centerblue, 700), xytext=(285, 200), 
                color='blue', fontsize=15,
                arrowprops=dict(width = 1, edgecolor='blue', shrink=0.02),
                )
    ax.annotate('(' + str(round(CRH,1)) +'% of sat)',
                xy=(285, 230), color='blue', fontsize=12)



    ### Surface water values at 1C intervals, for eyeballing surface fluxes
    sT = np.trunc(T[0].to('degC'))
    sTint = int(sT.magnitude)

    for idT in [-2,0,2,4]:
        ssTint = sTint + idT # UNITLESS degC integers, for labels

        # Kelvin values for computations
        ssTC = ssTint * units.degC
        ssTK = ssTC.to('kelvin')
        ss = g*Z[0] + Cp_d*ssTK 
        hs = ss     + Lv*mpcalc.mixing_ratio(mpcalc.saturation_vapor_pressure(ssTK) ,p[0])

        ax.annotate(str(ssTint), xy=(ss/1000., p[0]+0*p.units), 
                    verticalalignment='top', horizontalalignment='center',
                    color='red', fontsize=7)
        ax.annotate(str(ssTint), xy=(hs/1000., p[0]+0*p.units), 
                    verticalalignment='top', horizontalalignment='center',
                    color='red', fontsize=9)
        ax.annotate('\u00b0C water', xy=(right_annot_loc, p[0]), verticalalignment='top', 
                    fontsize=10, color='r')
    if return_fig:
        return ax,fig
Exemplo n.º 14
0
def test_mixing_ratio_dimensions():
    """Verify mixing ratio returns a dimensionless number."""
    p = 998. * units.mbar
    e = 73.75 * units.hPa
    assert str(mixing_ratio(e, p).units) == 'dimensionless'
Exemplo n.º 15
0
def test_mixing_ratio_dimensions():
    """Verify mixing ratio returns a dimensionless number."""
    p = 998. * units.mbar
    e = 73.75 * units.hPa
    assert str(mixing_ratio(e, p).units) == 'dimensionless'
Exemplo n.º 16
0
def test_mixing_ratio():
    """Test mixing ratio calculation."""
    p = 998. * units.mbar
    e = 73.75 * units.mbar
    assert_almost_equal(mixing_ratio(e, p), 0.04963, 2)
Exemplo n.º 17
0
def plot_skewt(snd: Sounding, save_to: Optional[str] = None, p_top: int = 100):
    """
    Plots a skew-T from the given Sounding.
    :param snd: The Sounding.
    :param save_to: Where to save the figure.  Default None, which does not save the figure, and instead shows it.
    :param p_top: Pressure at the top of the skew-T.  If you change this, Metpy might change the rotation of the
    isotherms.  No known fix yet.
    :return: None.
    """
    ####################################################################################################################
    # Data extraction and masking

    # Extract data from sounding.
    p = snd.p
    T = snd.T
    Td = snd.Td
    Tw = snd.Tw
    Te = snd.thetaE
    z = snd.z
    cf = snd["CFRL"]
    omega = snd.omega
    u, v = snd.wind_components()

    e = mpcalc.saturation_vapor_pressure(T)
    rv = mpcalc.mixing_ratio(e, p)
    w = mpcalc.vertical_velocity(omega, p, T, rv).to("cm/s")

    # Create masks to filter what data is plotted.
    mask_dewpoint = Td > -9000. * units.degC  # Plot only non-missing dewpoints.
    mask_wetbulb = Tw > -9000. * units.degC  # Plot only non-missing dewpoints.
    mask_thetae = Te > 0. * units.K  # Plot only non-missing theta-es.
    mask_barbs = p > p_top * units.hPa  # Plot only winds below the top of the sounding.

    ####################################################################################################################
    # Define intervals of height for coloring barbs and hodograph.
    z_interval_levels = [1500, 3000, 6000, 9000, 12000, 99999]
    z_interval_colors = ["red", "orange", "green", "blue", "purple", "grey"]

    z_colors = []

    for item in z:
        for color, interval in zip(z_interval_colors, z_interval_levels):
            if item <= interval * units.meter:
                z_colors.append(color)
                break

    ####################################################################################################################
    # Plotting skew-T

    fig = plt.figure(figsize=(11, 11))
    ax_hodo = fig.add_axes([0.70, 0.675, 0.25, 0.25])
    ax_thte = fig.add_axes([0.70, 0.375, 0.25, 0.25])
    skew = SkewT(fig, rotation=45, rect=[0.05, 0.05, 0.60, 0.9])

    # Plot temperature, dewpoint, and wet-bulb.
    skew.plot(p, T, 'r')
    skew.plot(p[mask_dewpoint], Td[mask_dewpoint], 'g')
    skew.plot(p[mask_wetbulb], Tw[mask_wetbulb], color='#009999', linewidth=1)

    # Calculate and plot surface parcel trace.
    sfc_trace = snd.parcel_trace(0).to('degC')
    sfc_trace_plot = skew.plot(p,
                               sfc_trace,
                               c='orange',
                               linewidth=2,
                               zorder=-10)

    # Calculate and plot MU parcel trace.
    mu_level_index = np.argmax(Te[p > 750. * units.hPa])
    mu_trace = snd.parcel_trace(mu_level_index).to('degC')
    mu_trace_plot = skew.plot(p[mu_level_index:],
                              mu_trace,
                              c='gray',
                              linewidth=2,
                              zorder=-9)

    # Plot each barb individually for control over color.  Unfortunately, the c arg of plot_barbs doesn't work for this
    # purpose.
    for p_, u_, v_, c_ in zip(p[mask_barbs][::BARB_DENSITY],
                              u[mask_barbs][::BARB_DENSITY],
                              v[mask_barbs][::BARB_DENSITY],
                              np.array(z_colors)[mask_barbs][::BARB_DENSITY]):
        skew.plot_barbs(p_, u_, v_, y_clip_radius=0.03, barbcolor=c_)

    ####################################################################################################################
    # Cloud fraction and omega
    zero_line = 1 / 15
    cf_plot = (cf * zero_line) / 100
    w_plot = (w.magnitude / 20) + zero_line
    skew.ax.plot(np.zeros(cf_plot.shape) + 1 / 15,
                 snd.p,
                 transform=skew.ax.get_yaxis_transform(),
                 color="grey")
    skew.ax.plot(cf_plot,
                 snd.p,
                 transform=skew.ax.get_yaxis_transform(),
                 color="black")
    skew.ax.plot(w_plot,
                 snd.p,
                 transform=skew.ax.get_yaxis_transform(),
                 color="purple")

    skew.ax.text(np.max(w_plot),
                 snd.p[np.argmax(w_plot)],
                 " {:.1f}".format(np.max(w.magnitude)),
                 color="purple",
                 ha="left",
                 va="center",
                 transform=skew.ax.get_yaxis_transform())
    skew.ax.text(max(np.min(w_plot), 0),
                 snd.p[np.argmin(w_plot)],
                 " {:.1f}".format(np.min(w.magnitude)),
                 color="purple",
                 ha="left",
                 va="center",
                 transform=skew.ax.get_yaxis_transform())
    # skew.ax.fill_betweenx(snd.p, cloud_fractions, np.zeros(cloud_fractions.shape))

    ####################################################################################################################
    # Tweaking

    skew.ax.set_xlim(-30, 40)
    skew.ax.set_ylim(1020, p_top)
    skew.ax.set_xlabel("")
    skew.ax.set_ylabel("")

    # Add legend for the parcel traces.
    skew.ax.legend(handles=[
        mlines.Line2D([], [], color='orange', label='Surface parcel'),
        mlines.Line2D([], [],
                      color='gray',
                      label=r"Max $\theta_e$ below 750mb"),
        mlines.Line2D([], [], color='black', label=r"Cloud fraction"),
        mlines.Line2D([], [],
                      color='purple',
                      label=r"Vertical velocity (cm/s)"),
    ],
                   loc="upper center")

    # Add adiabats and isohumes.
    skew.plot_dry_adiabats(t0=np.arange(233, 533, 10) * units.K,
                           alpha=0.25,
                           color='orangered')
    skew.plot_moist_adiabats(t0=np.arange(233, 400, 5) * units.K,
                             alpha=0.25,
                             color='tab:green')
    # Reshape required as a quirk of metpy.
    skew.plot_mixing_lines(w=np.array(
        [1, 2, 3, 4, 6, 8, 10, 12, 16, 20, 24, 28, 36]).reshape(-1, 1) / 1000.,
                           p=np.arange(1000, 99, -100) * units.hPa,
                           linestyle='dotted',
                           color='tab:blue')

    plt.title('RAP sounding at {}'.format(snd.params["STID"]), loc='left')
    plt.title('{:.0f}-hour forecast valid at {}'.format(
        snd.params["STIM"], snd.params["TIME"]),
              loc='right')

    ####################################################################################################################
    # Theta-E plot

    # Set up axis for theta-e plot.
    ax_thte.plot(Te[mask_thetae], p[mask_thetae])

    ax_thte.set_xlim(300, 360)
    ax_thte.set_ylim(1020, p_top)
    ax_thte.set_yscale("log")
    ax_thte.set_yticks(np.arange(p_top, 1001, 100))
    ax_thte.set_yticklabels(np.arange(p_top, 1001, 100))
    ax_thte.set_xlabel("")
    ax_thte.grid(axis="both")
    plt.text(0.5,
             0.9,
             "Theta-E (Kelvins)",
             ha="center",
             va="center",
             transform=ax_thte.transAxes)

    ####################################################################################################################
    # Hodograph

    # Set up axis for hodograph.
    h = Hodograph(ax_hodo, component_range=100)
    h.add_grid(20)

    # Plot each segment individually for control over color, reversed so that the full hodograph is plotted first,
    # followed by all but the last segment, etc.  Unfortunately, the plot_colormapped() function doesn't work for this
    # purpose.
    for color, interval in zip(reversed(z_interval_colors),
                               reversed(z_interval_levels)):
        mask = z < interval * units.meter
        h.plot(u.magnitude[mask], v.magnitude[mask], c=color)

    for vector in snd.bunkers_storm_motion():
        h.plot(vector[0], vector[1], c="black", markersize=3, marker="o")

    ax_hodo.set_xticks([])
    ax_hodo.set_yticks([])
    ax_hodo.set_xlim(-60, 100)
    ax_hodo.set_ylim(-60, 100)
    plt.text(0.1,
             0.9,
             "Velocity (knots)",
             ha="left",
             va="center",
             transform=ax_hodo.transAxes)
    for a in range(20, 61, 20):
        ax_hodo.text(-a * 0.71, -a * 0.71, a, ha="center", va="center")

########################################################################################################################
    parameter_names = ["SB STP", "0-1 SRH", "SB CAPE", "SB CIN"]
    parameters = [
        snd.significant_tornado()[0],
        snd.storm_relative_helicity()[2],
        snd.cape_cin(0)[0],
        snd.cape_cin(0)[1]
    ]

    for name, value, i in zip(parameter_names, parameters,
                              range(len(parameters))):
        s = "{:15} {:10.3f}".format(name, value.magnitude)
        fig.text(0.70,
                 0.32 - (0.02 * i),
                 s,
                 ha="left",
                 va="top",
                 family="monospace",
                 transform=fig.transFigure)

########################################################################################################################
    if save_to is None:
        plt.show()
    else:
        plt.savefig(save_to)
        plt.close()
def f(fullname, selected_time):
    fullname_el = fullname.split('/')
    #fullname_el = fullname.split('\\')
    filename = fullname_el[-1]
    filename_el = filename.split('_')
    save_base_dir_name = '../1data/'
    save_dir_name = '{0}{1}/'.format(save_base_dir_name + dir_name, obs_year)
    print('site : {0}'.format(dir_name))

    if os.path.isfile('{0}{1}_{2}_student.csv'\
                      .format(save_dir_name, filename_el[-5], selected_time[:13]))\
        and os.path.isfile('{0}{1}_{2}_solution.csv'\
                      .format(save_dir_name, filename_el[-5], selected_time[:13])):
        write_log(log_file, '{3} ::: {0}{1}_{2} files are already exist'\
                  .format(save_dir_name, filename_el[-5], selected_time[:13], datetime.now()))
    else:
        try:
            f = lambda s: selected_time in s
            ids = df['time'].apply(f)
            df_selected_time = df[ids]
            df_selected_time = df_selected_time.sort_values('pressure',
                                                            ascending=False)

            print('filename : {0}'.format(fullname))
            print('df_selected_time.\n{0}'.format(df_selected_time))

            df_selected_time.to_csv(r'{0}{1}_{2}_student.csv'\
                      .format(save_dir_name, filename_el[-5], selected_time[:13]))

            #################################################################################
            ### We will pull the data out of the example dataset into individual variables and
            ### assign units.
            #################################################################################

            p = df_selected_time['pressure'].values * units.hPa
            T = df_selected_time['temperature'].values * units.degC
            Td = df_selected_time['dewpoint'].values * units.degC
            wind_speed = df_selected_time['speed'].values * units.knots
            wind_dir = df_selected_time['direction'].values * units.degrees
            u, v = mpcalc.wind_components(wind_speed, wind_dir)

            # Calculate web bulb temperature
            df_selected_time['wet_bulb_T'] = mpcalc.wet_bulb_temperature(
                p, T, Td)

            # Calculate potential temperature
            df_selected_time['potential_T'] = mpcalc.potential_temperature(
                p, T)
            df_selected_time['potential_T_C'] = df_selected_time[
                'potential_T'].values - 273.15

            # Calculate saturation vaper pressure
            df_selected_time[
                'saturation_vaper_pressure'] = mpcalc.saturation_vapor_pressure(
                    T)
            df_selected_time[
                'vaper_pressure'] = mpcalc.saturation_vapor_pressure(Td)
            SVP = df_selected_time[
                'saturation_vaper_pressure'].values * units.hPa
            VP = df_selected_time['vaper_pressure'].values * units.hPa

            # Calculate mixing ratio
            df_selected_time['saturation_mixing_ratio'] = mpcalc.mixing_ratio(
                SVP, p)
            df_selected_time['mixing_ratio'] = mpcalc.mixing_ratio(VP, p)
            SMR = df_selected_time['saturation_mixing_ratio'].values * units(
                'g/kg')
            MR = df_selected_time['mixing_ratio'].values * units('g/kg')

            # Calculate relative humidity
            df_selected_time['relative_humidity_from_dewpoint'] \
                = mpcalc.relative_humidity_from_dewpoint(T, Td)
            df_selected_time['relative_humidity_from_mixing_ratio'] \
                = mpcalc.relative_humidity_from_mixing_ratio(MR, T, p)

            # Calculate virtual temperature
            df_selected_time['virtual_temperature'] \
                = mpcalc.virtual_temperature(T, MR)

            # Calculate virtual potential temperature
            df_selected_time['virtual_potential_temperature'] \
                = mpcalc.virtual_potential_temperature(p, T, MR)

            print('df_selected_time after drop nan.\n{0}'.format(
                df_selected_time))

            df_selected_time.to_csv(r'{0}{1}_{2}_solution.csv'\
                      .format(save_dir_name, filename_el[-5], selected_time[:13]))

        except Exception as err:
            write_log(err_log_file, '{4} ::: {0} with {1}{2} on {3}'\
                      .format(err, dir_name, filename, selected_time[:13], datetime.now()))
    print('Thread {0} is finished'.format(selected_time))
    return 0  # Return a dummy value
Exemplo n.º 19
0
def convert_one(f_in, f_out=False, dir_out='./', save_csv=True, debug=False):
    skiprows = 3  # make sure this is correct

    with open(f_in) as f:
        for i, line in enumerate(f):
            pass
    N_lines = i

    with open(f_in) as f:
        N_lines = N_lines - skiprows
        data = np.full((N_lines, 8), np.nan)
        j = 0

        for i, line in enumerate(f):
            if i == skiprows:
                header = line
            if i > skiprows:
                values = line.split()
                values = [float(v) for v in values]
                #print(values)
                data[j, :] = np.array(values)
                j += 1

    if debug: print('input header:', header)

    p = data[:, 0]
    z = data[:, 1]
    T = data[:, 2]
    ff = data[:, 6]
    dd = data[:, 7]

    if True:
        rh = data[:, 4] / 100
        svp = mpcalc.saturation_vapor_pressure(T * units.K)
        vp = rh * svp
        r = mpcalc.mixing_ratio(vp, p * units.millibar)
    else:
        r = data[:, 5] / 1000.

    #### convert to WRF variables

    # constants (amsglossary)
    Rd = 287.05
    Rv = 461.51
    cpd = 1005.7  # specific heat dry air
    cpv = 1847.  # specific heat vapor
    kappa_moist = Rd / cpd * (1 + r * Rv / Rd) / (1 + r * cpv / cpd)
    print('kappa_moist mean, std:', kappa_moist.mean(), kappa_moist.std())

    ####################################
    # Wind modifications
    ff = np.concatenate(
        [np.linspace(2, 30, 36),
         np.linspace(30, 0,
                     len(p) - 36)])
    dd = np.concatenate([
        np.linspace(90, 210, 10),
        np.linspace(213, 270, 20),
        np.linspace(273, 360,
                    len(p) - 30)
    ])

    ####################################
    # Temperature to potential temperature
    pot_tmp = T * (1000 / p)**kappa_moist

    u = -ff * np.cos(dd / 180 * np.pi - np.pi / 2)
    v = -ff * np.sin(dd / 180 * np.pi + np.pi / 2)

    ####################################
    # modifications
    #vp = mpcalc.vapor_pressure(p*units.millibar, r)
    if False:
        svp = mpcalc.saturation_vapor_pressure(T * units.K)
        #Td = mpcalc.dewpoint_from_relative_humidity(T*units.K, vp/svp)
        rh = 0.9
        vp = rh * svp
        r1 = mpcalc.mixing_ratio(vp, p * units.millibar)

        fig, ax = plt.subplots()
        ax.plot(r, p, 'r-')
        r[(r < r1)
          & (T > 263)] = r1[(r < r1) &
                            (T > 263)]  # allow r to be greater than 90%

        ax.plot(r, p, 'g-')
        ax.invert_yaxis()
        fig.savefig('test.png')
    # vp = mpcalc.vapor_pressure(p*units.millibar, r)
    # Td = mpcalc.dewpoint_from_relative_humidity(T*units.K, vp/svp)

    ###############
    def plot(T, p, r, f_in, dir_out):
        T = T * units.K
        p = p * units.millibar

        vp = mpcalc.vapor_pressure(p, r)
        svp = mpcalc.saturation_vapor_pressure(T)
        Td = mpcalc.dewpoint_from_relative_humidity(T, vp / svp)
        Td[np.isnan(
            Td)] = -99. * units.degree_Celsius  # fill nan with very low temp

        f_in_basename = os.path.basename(f_in)
        prof.core(p,
                  T,
                  Td,
                  u,
                  v,
                  saveto=dir_out + 'prof_' + f_in_basename + '.png',
                  title=f_in_basename)

    plot(T, p, r, f_in, dir_out)

    ####################################
    # surface measurements
    sp = p[0]  # surface pressure
    t_2m = pot_tmp[0]  # surface potential Temperature
    r_2m = r[0].magnitude * 1000.  # surface vapor mixing ratio
    n_levels = z.shape[0]

    line1 = '{:9.2f} {:9.2f} {:10.2f}'.format(sp, t_2m, r_2m)

    if save_csv and f_out:
        os.makedirs(dir_out + '/csv/', exist_ok=True)
        csvname = dir_out + '/' + '.'.join(f_out.split('.')[:-1]) + '.csv'

        df = pd.DataFrame(data={'p': p, 'T': T, 'Qv': r})
        df.to_csv(csvname)
        print(csvname, 'saved.')

    wrfformat = '{:9.2f} {:9.2f} {:10.2f} {:10.2f} {:10.2f}'
    r *= 1000.

    if f_out:
        r = r.magnitude

        f_out = dir_out + f_out
        with open(f_out, 'w') as f:
            f.write(line1 + ' \n')
            for i in range(n_levels):
                d = wrfformat.format(z[i], pot_tmp[i], r[i], u[i], v[i])
                if debug: print(d)
                f.write(d + '\n')
        print(f_out, 'saved.')