예제 #1
0
def test_precipitable_water_descriptive_bound_error():
    """Test that error is raised when bound is outside profile after nan have been removed."""
    pressure = np.array([
        1001, 1000, 997, 977.9, 977, 957, 937.8, 925, 906, 899.3, 887, 862.5,
        854, 850, 800, 793.9, 785, 777, 771, 762, 731.8, 726, 703, 700, 655,
        630, 621.2, 602, 570.7, 548, 546.8, 539, 513, 511, 485, 481, 468, 448,
        439, 424, 420, 412
    ]) * units.hPa
    dewpoint = np.array([
        np.nan, np.nan, -26.8, np.nan, -27.3, -28.2, np.nan, -27.2, -26.6,
        np.nan, -27.4, np.nan, -23.5, -23.5, -25.1, np.nan, -22.9, -17.8,
        -16.6, np.nan, np.nan, -16.4, np.nan, -18.5, -21., -23.7, np.nan,
        -28.3, np.nan, -32.6, np.nan, -33.8, -35., -35.1, -38.1, -40., -43.3,
        -44.6, -46.4, np.nan, np.nan, np.nan
    ]) * units.degC

    # Top bound is above highest pressure in profile
    with pytest.raises(ValueError,
                       match='The pressure and dewpoint profile ranges from'):
        precipitable_water(pressure, dewpoint, top=units.Quantity(415, 'hPa'))

    # Bottom bound is below lowest pressure in profile
    with pytest.raises(ValueError,
                       match='The pressure and dewpoint profile ranges from'):
        precipitable_water(pressure,
                           dewpoint,
                           bottom=units.Quantity(999, 'hPa'))
예제 #2
0
def wrap_xr_metpy_pw(dewpt,
                     pressure,
                     bottom=None,
                     top=None,
                     verbose=False,
                     cumulative=False):
    from metpy.calc import precipitable_water
    from metpy.units import units
    import numpy as np
    # try:
    #     T_unit = dewpt.attrs['units']
    #     assert T_unit == 'degC'
    # except KeyError:
    #     T_unit = 'degC'
    #     if verbose:
    #         print('assuming dewpoint units are degC...')
    dew_values = dewpt.values * units('K')
    try:
        P_unit = pressure.attrs['units']
        assert P_unit == 'hPa'
    except KeyError:
        P_unit = 'hPa'
        if verbose:
            print('assuming pressure units are hPa...')
    if top is not None:
        top_with_units = top * units(P_unit)
    else:
        top_with_units = None
    if bottom is not None:
        bottom_with_units = bottom * units(P_unit)
    else:
        bottom_with_units = None
    pressure_values = pressure.values * units(P_unit)
    if cumulative:
        pw_list = []
        # first value is nan:
        pw_list.append(np.nan)
        for pre_val in pressure_values[1:]:
            if np.isnan(pre_val):
                pw_list.append(np.nan)
                continue
            pw = precipitable_water(pressure_values,
                                    dew_values,
                                    bottom=None,
                                    top=pre_val)
            pw_units = pw.units.format_babel('~P')
            pw_list.append(pw.magnitude)
        pw = np.array(pw_list)
        return pw, pw_units
    else:
        pw = precipitable_water(pressure_values,
                                dew_values,
                                bottom=bottom_with_units,
                                top=top_with_units)
        pw_units = pw.units.format_babel('~P')
        return pw.magnitude, pw_units
예제 #3
0
    def calculate_basic_thermo(self):

        #Enclose in try, except because not every sounding will have a converging parcel path or CAPE.
        try:

            #Precipitable Water
            self.pw = mc.precipitable_water(self.sounding["pres"],
                                            self.sounding["dewp"])

            #Lifting condensation level
            self.lcl_pres, self.lcl_temp = mc.lcl(self.sounding["pres"][0],
                                                  self.sounding["temp"][0],
                                                  self.sounding["dewp"][0])

            #Surface-based CAPE and CIN
            self.parcel_path = mc.parcel_profile(self.sounding["pres"],
                                                 self.sounding["temp"][0],
                                                 self.sounding["dewp"][0])
            self.sfc_cape, self.sfc_cin = mc.cape_cin(self.sounding["pres"],
                                                      self.sounding["temp"],
                                                      self.sounding["dewp"],
                                                      self.parcel_path)

        #Do this when parcel path fails to converge
        except Exception as e:
            print("WARNING: No LCL, CAPE, or PW stats because:\n{}.".format(e))
            self.parcel_path = numpy.nan
            self.pw = numpy.nan
            self.lcl_pres = numpy.nan
            self.lcl_temp = numpy.nan
            self.sfc_cape = numpy.nan
            self.sfc_cin = numpy.nan

        #Returning
        return
예제 #4
0
def test_precipitable_water():
    """Test precipitable water with observed sounding."""
    data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC')
    pw = precipitable_water(data['dewpoint'], data['pressure'],
                            top=400 * units.hPa)
    truth = (0.8899441949243486 * units('inches')).to('millimeters')
    assert_array_equal(pw, truth)
예제 #5
0
def test_precipitable_water():
    """Test precipitable water with observed sounding."""
    with UseSampleData():
        data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC', source='wyoming')
    pw = precipitable_water(data.variables['dewpoint'][:], data.variables['pressure'][:])
    truth = (0.8899441949243486 * units('inches')).to('millimeters')
    assert_array_equal(pw, truth)
예제 #6
0
def do_profile(cursor, fid, gdf):
    """Process this profile."""
    profile = gdf[pd.notnull(gdf['tmpc']) & pd.notnull(gdf['dwpc'])]
    if profile.empty:
        LOG.info("profile %s is empty, skipping", fid)
        return
    if profile['pressure'].min() > 400:
        raise ValueError("Profile only up to %s mb" %
                         (profile['pressure'].min(), ))
    pwater = precipitable_water(profile['dwpc'].values * units.degC,
                                profile['pressure'].values * units.hPa)
    (sbcape, sbcin) = surface_based_cape_cin(
        profile['pressure'].values * units.hPa,
        profile['tmpc'].values * units.degC,
        profile['dwpc'].values * units.degC,
    )
    (mucape, mucin) = most_unstable_cape_cin(
        profile['pressure'].values * units.hPa,
        profile['tmpc'].values * units.degC,
        profile['dwpc'].values * units.degC,
    )
    cursor.execute(
        """
        UPDATE raob_flights SET sbcape_jkg = %s, sbcin_jkg = %s,
        mucape_jkg = %s, mucin_jkg = %s, pwater_mm = %s,
        computed = 't' WHERE fid = %s
    """, (nonull(sbcape.to(units('joules / kilogram')).m),
          nonull(sbcin.to(units('joules / kilogram')).m),
          nonull(mucape.to(units('joules / kilogram')).m),
          nonull(mucin.to(units('joules / kilogram')).m),
          nonull(pwater.to(units('mm')).m), fid))
예제 #7
0
def test_precipitable_water_xarray():
    """Test precipitable water with xarray input."""
    data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC')
    press = xr.DataArray(data['pressure'], attrs={'units': str(data['pressure'].units)})
    dewp = xr.DataArray(data['dewpoint'], dims=('press',), coords=(press,))
    pw = precipitable_water(press, dewp, top=400 * units.hPa)
    truth = 22.60430651 * units.millimeters
    assert_almost_equal(pw, truth)
예제 #8
0
def test_precipitable_water():
    """Test precipitable water with observed sounding."""
    data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC')
    pw = precipitable_water(data['pressure'],
                            data['dewpoint'],
                            top=400 * units.hPa)
    truth = 22.60430651 * units.millimeters
    assert_array_almost_equal(pw, truth, 4)
예제 #9
0
def test_precipitable_water():
    """Test precipitable water with observed sounding."""
    data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC')
    pw = precipitable_water(data['pressure'],
                            data['dewpoint'],
                            top=400 * units.hPa)
    truth = (0.8899441949243486 * units('inches')).to('millimeters')
    assert_array_equal(pw, truth)
예제 #10
0
def test_precipitable_water_bound_error():
    """Test with no top bound given and data that produced floating point issue #596."""
    pressure = np.array([993., 978., 960.5, 927.6, 925., 895.8, 892., 876., 45.9, 39.9, 36.,
                         36., 34.3]) * units.hPa
    dewpoint = np.array([25.5, 24.1, 23.1, 21.2, 21.1, 19.4, 19.2, 19.2, -87.1, -86.5, -86.5,
                         -86.5, -88.1]) * units.degC
    pw = precipitable_water(pressure, dewpoint)
    truth = 89.86846252697836 * units('millimeters')
    assert_almost_equal(pw, truth, 5)
예제 #11
0
def test_precipitable_water_no_bounds():
    """Test precipitable water with observed sounding and no bounds given."""
    data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC')
    dewpoint = data['dewpoint']
    pressure = data['pressure']
    inds = pressure >= 400 * units.hPa
    pw = precipitable_water(pressure[inds], dewpoint[inds])
    truth = (0.8899441949243486 * units('inches')).to('millimeters')
    assert_array_equal(pw, truth)
예제 #12
0
def test_precipitable_water_bound_error():
    """Test with no top bound given and data that produced floating point issue #596."""
    pressure = np.array([993., 978., 960.5, 927.6, 925., 895.8, 892., 876., 45.9, 39.9, 36.,
                         36., 34.3]) * units.hPa
    dewpoint = np.array([25.5, 24.1, 23.1, 21.2, 21.1, 19.4, 19.2, 19.2, -87.1, -86.5, -86.5,
                         -86.5, -88.1]) * units.degC
    pw = precipitable_water(dewpoint, pressure)
    truth = 89.86955998646951 * units('millimeters')
    assert_almost_equal(pw, truth, 8)
예제 #13
0
def get_skewt_vars(p, tc, tdc, pro):
    """This function processes the dataset values and returns a string element
    which can be used as a subtitle to replicate the styles of NCL Skew-T
    Diagrams.

    Args:

        p (:class: `pint.quantity.build_quantity_class.<locals>.Quantity`):
            Pressure level input from dataset

        tc (:class: `pint.quantity.build_quantity_class.<locals>.Quantity`):
            Temperature for parcel from dataset

        tdc (:class: `pint.quantity.build_quantity_class.<locals>.Quantity`):
            Dew point temperature for parcel from dataset

        pro (:class: `pint.quantity.build_quantity_class.<locals>.Quantity`):
            Parcel profile temperature converted to degC


    Returns:
        :class: 'str'
    """

    # CAPE
    cape = mpcalc.cape_cin(p, tc, tdc, pro)
    cape = cape[0].magnitude

    # Precipitable Water
    pwat = mpcalc.precipitable_water(p, tdc)
    pwat = (pwat.magnitude / 10) * units.cm  # Convert mm to cm
    pwat = pwat.magnitude

    # Pressure and temperature of lcl
    lcl = mpcalc.lcl(p[0], tc[0], tdc[0])
    plcl = lcl[0].magnitude
    tlcl = lcl[1].magnitude

    # Showalter index
    shox = showalter_index(p, tc, tdc)
    shox = shox[0].magnitude

    # Place calculated values in iterable list
    vals = [plcl, tlcl, shox, pwat, cape]
    vals = [round(num) for num in vals]

    # Define variable names for calculated values
    names = ['Plcl=', 'Tlcl[C]=', 'Shox=', 'Pwat[cm]=', 'Cape[J]=']

    # Combine the list of values with their corresponding labels
    lst = list(chain.from_iterable(zip(names, vals)))
    lst = map(str, lst)

    # Create one large string for later plotting use
    joined = ' '.join(lst)

    return joined
예제 #14
0
def test_precipitable_water_no_bounds():
    """Test precipitable water with observed sounding and no bounds given."""
    data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC')
    dewpoint = data['dewpoint']
    pressure = data['pressure']
    inds = pressure >= 400 * units.hPa
    pw = precipitable_water(dewpoint[inds], pressure[inds])
    truth = (0.8899441949243486 * units('inches')).to('millimeters')
    assert_array_equal(pw, truth)
예제 #15
0
def test_precipitable_water_no_bounds():
    """Test precipitable water with observed sounding and no bounds given."""
    data = get_upper_air_data(datetime(2016, 5, 22, 0), 'DDC')
    dewpoint = data['dewpoint']
    pressure = data['pressure']
    inds = pressure >= 400 * units.hPa
    pw = precipitable_water(pressure[inds], dewpoint[inds])
    truth = 22.60430651 * units.millimeters
    assert_array_almost_equal(pw, truth, 4)
예제 #16
0
 def tpw(self, top=None):
     """ Calculates total precipitable water taking only water vapors into the account"""
     
     if top is None:
         top = np.min(self.p)*units.hectopascal + .01 * units.hPa
         
     self.total_pw = mcalc.precipitable_water(dewpt=self.dew *units.K, pressure=self.p *units.hectopascal, top=top) 
     # has a padding factor to avoid error due to float point comparision
     
     return self.total_pw
예제 #17
0
def test_precipitable_water_nans():
    """Test that PW returns appropriate number if NaNs are present."""
    pressure = np.array([1001, 1000, 997, 977.9, 977, 957, 937.8, 925, 906, 899.3, 887, 862.5,
                         854, 850, 800, 793.9, 785, 777, 771, 762, 731.8, 726, 703, 700, 655,
                         630, 621.2, 602, 570.7, 548, 546.8, 539, 513, 511, 485, 481, 468,
                         448, 439, 424, 420, 412]) * units.hPa
    dewpoint = np.array([-25.1, -26.1, -26.8, np.nan, -27.3, -28.2, np.nan, -27.2, -26.6,
                        np.nan, -27.4, np.nan, -23.5, -23.5, -25.1, np.nan, -22.9, -17.8,
                        -16.6, np.nan, np.nan, -16.4, np.nan, -18.5, -21., -23.7, np.nan,
                        -28.3, np.nan, -32.6, np.nan, -33.8, -35., -35.1, -38.1, -40.,
                        -43.3, -44.6, -46.4, -47., -49.2, -50.7]) * units.degC
    pw = precipitable_water(pressure, dewpoint)
    truth = 4.003660322395436 * units.mm
    assert_almost_equal(pw, truth, 5)
예제 #18
0
def calculate_pwv_from_kit_rs(path=des_path):
    """ for kit_rs that go up to 12kms"""
    from metpy.calc import precipitable_water
    import xarray as xr
    ds = read_kit_rs(path)
    dew = ds['DewPoint']
    P = ds['Pressure']
    times = []
    for dt in P.time:
        d = dew.sel(time=dt)
        p = P.sel(time=dt)
        pwv = precipitable_water(p, d).magnitude
        times.append(pwv)
    pwv_rs = xr.DataArray(times, dims=['time'])
    pwv_rs['time'] = P.time
    pwv_rs.name = 'pwv'
    pwv_rs.attrs['long_name'] = 'precipitable water'
    pwv_rs.attrs['units'] = 'mm'
    pwv_rs.attrs['action'] = 'proccesed by metpy.calc on KIT-RS data'
    return pwv_rs
예제 #19
0
def do_profile(cursor, fid, gdf, nt):
    """Process this profile."""
    # The inbound profile may contain mandatory level data that is below
    # the surface.  It seems the best we can do here is to ensure both
    # temperature and dewpoint are valid and call that the bottom.
    td_profile = gdf[pd.notnull(gdf["tmpc"]) & pd.notnull(gdf["dwpc"])]
    wind_profile = gdf[pd.notnull(gdf["u"])]
    # Presently we are all or nothing here.  The length is arb
    if len(td_profile.index) < 5 or len(wind_profile.index) < 5:
        msg = ("quorum fail td: %s wind: %s, skipping") % (
            len(td_profile.index),
            len(wind_profile.index),
        )
        raise ValueError(msg)
    if gdf["pressure"].min() > 500:
        raise ValueError("Profile only up to %s mb" %
                         (gdf["pressure"].min(), ))
    # Does a crude check that our metadata station elevation is within 50m
    # of the profile bottom, otherwise we ABORT
    station_elevation_m = get_station_elevation(td_profile, nt)
    # get surface wind
    u_sfc, v_sfc = get_surface_winds(wind_profile)
    u_1km, v_1km = get_aloft_winds(wind_profile, station_elevation_m + 1000.0)
    u_3km, v_3km = get_aloft_winds(wind_profile, station_elevation_m + 3000.0)
    u_6km, v_6km = get_aloft_winds(wind_profile, station_elevation_m + 6000.0)
    shear_sfc_1km_smps = np.sqrt((u_1km - u_sfc)**2 + (v_1km - v_sfc)**2)
    shear_sfc_3km_smps = np.sqrt((u_3km - u_sfc)**2 + (v_3km - v_sfc)**2)
    shear_sfc_6km_smps = np.sqrt((u_6km - u_sfc)**2 + (v_6km - v_sfc)**2)

    total_totals = compute_total_totals(td_profile)
    sweat_index = compute_sweat_index(td_profile, total_totals)
    try:
        bunkers_rm, bunkers_lm, mean0_6_wind = bunkers_storm_motion(
            wind_profile["pressure"].values * units.hPa,
            wind_profile["u"].values * units("m/s"),
            wind_profile["v"].values * units("m/s"),
            wind_profile["height"].values * units("m"),
        )
    except ValueError:
        # Profile may not go up high enough
        bunkers_rm = [np.nan * units("m/s"), np.nan * units("m/s")]
        bunkers_lm = [np.nan * units("m/s"), np.nan * units("m/s")]
        mean0_6_wind = [np.nan * units("m/s"), np.nan * units("m/s")]
    bunkers_rm_smps = wind_speed(bunkers_rm[0], bunkers_rm[1])
    bunkers_rm_drct = wind_direction(bunkers_rm[0], bunkers_rm[1])
    bunkers_lm_smps = wind_speed(bunkers_lm[0], bunkers_lm[1])
    bunkers_lm_drct = wind_direction(bunkers_lm[0], bunkers_lm[1])
    mean0_6_wind_smps = wind_speed(mean0_6_wind[0], mean0_6_wind[1])
    mean0_6_wind_drct = wind_direction(mean0_6_wind[0], mean0_6_wind[1])
    try:
        (
            srh_sfc_1km_pos,
            srh_sfc_1km_neg,
            srh_sfc_1km_total,
        ) = storm_relative_helicity(
            wind_profile["u"].values * units("m/s"),
            wind_profile["v"].values * units("m/s"),
            wind_profile["height"].values * units("m"),
            1000.0 * units("m"),
        )
    except ValueError:
        srh_sfc_1km_pos = np.nan * units("m")  # blah
        srh_sfc_1km_neg = np.nan * units("m")  # blah
        srh_sfc_1km_total = np.nan * units("m")  # blah
    try:
        (
            srh_sfc_3km_pos,
            srh_sfc_3km_neg,
            srh_sfc_3km_total,
        ) = storm_relative_helicity(
            wind_profile["u"].values * units("m/s"),
            wind_profile["v"].values * units("m/s"),
            wind_profile["height"].values * units("m"),
            3000.0 * units("m"),
        )
    except ValueError:
        srh_sfc_3km_pos = np.nan * units("m")  # blah
        srh_sfc_3km_neg = np.nan * units("m")  # blah
        srh_sfc_3km_total = np.nan * units("m")  # blah
    pwater = precipitable_water(
        td_profile["dwpc"].values * units.degC,
        td_profile["pressure"].values * units.hPa,
    )
    (sbcape, sbcin) = surface_based_cape_cin(
        td_profile["pressure"].values * units.hPa,
        td_profile["tmpc"].values * units.degC,
        td_profile["dwpc"].values * units.degC,
    )
    (mucape, mucin) = most_unstable_cape_cin(
        td_profile["pressure"].values * units.hPa,
        td_profile["tmpc"].values * units.degC,
        td_profile["dwpc"].values * units.degC,
    )
    el_p, el_t = el(
        td_profile["pressure"].values * units.hPa,
        td_profile["tmpc"].values * units.degC,
        td_profile["dwpc"].values * units.degC,
    )
    lfc_p, lfc_t = lfc(
        td_profile["pressure"].values * units.hPa,
        td_profile["tmpc"].values * units.degC,
        td_profile["dwpc"].values * units.degC,
    )
    (lcl_p, lcl_t) = lcl(
        td_profile["pressure"].values[0] * units.hPa,
        td_profile["tmpc"].values[0] * units.degC,
        td_profile["dwpc"].values[0] * units.degC,
    )
    vals = [
        el_p.to(units("hPa")).m,
        lfc_p.to(units("hPa")).m,
        lcl_p.to(units("hPa")).m,
    ]
    [el_hght, lfc_hght, lcl_hght] = log_interp(
        np.array(vals, dtype="f"),
        td_profile["pressure"].values[::-1],
        td_profile["height"].values[::-1],
    )
    el_agl = gt1(el_hght - station_elevation_m)
    lcl_agl = gt1(lcl_hght - station_elevation_m)
    lfc_agl = gt1(lfc_hght - station_elevation_m)
    args = (
        nonull(sbcape.to(units("joules / kilogram")).m),
        nonull(sbcin.to(units("joules / kilogram")).m),
        nonull(mucape.to(units("joules / kilogram")).m),
        nonull(mucin.to(units("joules / kilogram")).m),
        nonull(pwater.to(units("mm")).m),
        nonull(el_agl),
        nonull(el_p.to(units("hPa")).m),
        nonull(el_t.to(units.degC).m),
        nonull(lfc_agl),
        nonull(lfc_p.to(units("hPa")).m),
        nonull(lfc_t.to(units.degC).m),
        nonull(lcl_agl),
        nonull(lcl_p.to(units("hPa")).m),
        nonull(lcl_t.to(units.degC).m),
        nonull(total_totals),
        nonull(sweat_index),
        nonull(bunkers_rm_smps.m),
        nonull(bunkers_rm_drct.m),
        nonull(bunkers_lm_smps.m),
        nonull(bunkers_lm_drct.m),
        nonull(mean0_6_wind_smps.m),
        nonull(mean0_6_wind_drct.m),
        nonull(srh_sfc_1km_pos.m),
        nonull(srh_sfc_1km_neg.m),
        nonull(srh_sfc_1km_total.m),
        nonull(srh_sfc_3km_pos.m),
        nonull(srh_sfc_3km_neg.m),
        nonull(srh_sfc_3km_total.m),
        nonull(shear_sfc_1km_smps),
        nonull(shear_sfc_3km_smps),
        nonull(shear_sfc_6km_smps),
        fid,
    )
    cursor.execute(
        """
        UPDATE raob_flights SET sbcape_jkg = %s, sbcin_jkg = %s,
        mucape_jkg = %s, mucin_jkg = %s, pwater_mm = %s,
        el_agl_m = %s, el_pressure_hpa = %s, el_tmpc = %s,
        lfc_agl_m = %s, lfc_pressure_hpa = %s, lfc_tmpc = %s,
        lcl_agl_m = %s, lcl_pressure_hpa = %s, lcl_tmpc = %s,
        total_totals = %s, sweat_index = %s,
        bunkers_rm_smps = %s, bunkers_rm_drct = %s,
        bunkers_lm_smps = %s, bunkers_lm_drct = %s,
        mean_sfc_6km_smps = %s, mean_sfc_6km_drct = %s,
        srh_sfc_1km_pos = %s, srh_sfc_1km_neg = %s, srh_sfc_1km_total = %s,
        srh_sfc_3km_pos = %s, srh_sfc_3km_neg = %s, srh_sfc_3km_total = %s,
        shear_sfc_1km_smps = %s, shear_sfc_3km_smps = %s,
        shear_sfc_6km_smps = %s,
        computed = 't' WHERE fid = %s
    """,
        args,
    )
예제 #20
0
def fmi2skewt(station, time, img_name):

    apikey = 'e72a2917-1e71-4d6f-8f29-ff4abfb8f290'

    url = 'http://data.fmi.fi/fmi-apikey/' + str(
        apikey
    ) + '/wfs?request=getFeature&storedquery_id=fmi::observations::weather::sounding::multipointcoverage&fmisid=' + str(
        station) + '&starttime=' + str(time) + '&endtime=' + str(time) + '&'

    req = requests.get(url)
    xmlstring = req.content
    tree = ET.ElementTree(ET.fromstring(xmlstring))
    root = tree.getroot()

    #reading location and time data to "positions" from XML
    positions = ""
    for elem in root.getiterator(
            tag='{http://www.opengis.net/gmlcov/1.0}positions'):
        positions = elem.text

    #'positions' is string type variable
    #--> split positions into a list by " "
    #then remove empty chars and "\n"
    # from pos_split --> data into positions_data

    try:
        pos_split = positions.split(' ')
    except NameError:
        return "Sounding data not found: stationid " + station + " time " + time

    pos_split = positions.split(' ')

    positions_data = []
    for i in range(0, len(pos_split)):
        if not (pos_split[i] == "" or pos_split[i] == "\n"):
            positions_data.append(pos_split[i])

    #index for height: 2,6,10 etc in positions_data
    height = []
    myList = range(2, len(positions_data))
    for i in myList[::4]:
        height.append(positions_data[i])

    p = []
    for i in range(0, len(height)):
        p.append(height2pressure(float(height[i])))

    #reading wind speed, wind direction, air temperature and dew point data to 'values'
    values = ""
    for elem in root.getiterator(
            tag='{http://www.opengis.net/gml/3.2}doubleOrNilReasonTupleList'):
        values = elem.text

    #split 'values' into a list by " "
    #then remove empty chars and "\n"

    val_split = values.split(' ')
    values_data = []
    for i in range(0, len(val_split)):
        if not (val_split[i] == "" or val_split[i] == "\n"):
            values_data.append(val_split[i])

    #data in values_data: w_speed, w_dir, t_air, t_dew
    wind_speed = []
    wind_dir = []
    T = []
    Td = []
    myList = range(0, len(values_data))
    for i in myList[::4]:
        wind_speed.append(float(values_data[i]))
        wind_dir.append(float(values_data[i + 1]))
        T.append(float(values_data[i + 2]))
        Td.append(float(values_data[i + 3]))

    if stationid == "101104":
        loc_time = "Jokioinen Ilmala " + time
    elif stationid == "101932":
        loc_time = "Sodankyla Tahtela " + time
    else:
        return None

    #calculate wind components u,v:
    u = []
    v = []
    for i in range(0, len(wind_speed)):
        u1, v1 = getWindComponent(wind_speed[i], wind_dir[i])
        u.append(u1)
        v.append(v1)

    #find index for pressure < 100hPa (for number of wind bars)
    if min(p) > 100:
        wthin = len(p) / 20
        u_plot = u
        v_plot = v
        p_plot = p
    else:
        for i in range(0, len(p)):
            if p[i] - 100 <= 0:
                wthin = i / 20
                u_plot = u[0:i]
                v_plot = v[0:i]
                p_plot = p[0:i]
                break

    #units
    wind_speed = wind_speed * units("m/s")
    wind_dir = wind_dir * units.deg
    T = T * units.degC
    Td = Td * units.degC
    p = p * units("hPa")

    #calculate pwat, lcl, cape, cin and plot cape
    pwat = mpcalc.precipitable_water(Td, p, bottom=None, top=None)
    lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0])
    prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC')

    try:
        cape, cin = mpcalc.cape_cin(p, T, Td, prof)
    except IndexError:
        cape = 0 * units("J/kg")
        cin = 0 * units("J/kg")

    #__________________plotting__________________
    fig = plt.figure(figsize=(9, 9))
    skew = SkewT(fig, rotation=45)
    font_par = {
        'family': 'monospace',
        'color': 'darkred',
        'weight': 'normal',
        'size': 10,
    }
    font_title = {
        'family': 'monospace',
        'color': 'black',
        'weight': 'normal',
        'size': 20,
    }
    font_axis = {
        'family': 'monospace',
        'color': 'black',
        'weight': 'normal',
        'size': 10,
    }
    # Plot the data using normal plotting functions, in this case using
    # log scaling in Y, as dictated by the typical meteorological plot
    skew.plot(p, T, 'k')
    skew.plot(p, Td, 'b')
    skew.ax.set_ylim(1000, 100)
    skew.ax.set_xlim(-50, 30)
    skew.plot_barbs(p_plot[0::wthin], u_plot[0::wthin], v_plot[0::wthin])
    skew.plot_dry_adiabats(alpha=0.4)
    skew.plot_moist_adiabats(alpha=0.4)
    skew.plot_mixing_lines(alpha=0.4)
    #skew.shade_cape(p, T, prof,color="orangered")
    plt.title(loc_time, fontdict=font_title)
    plt.xlabel("T (C)", fontdict=font_axis)
    plt.ylabel("P (hPa)", fontdict=font_axis)

    #round and remove units from cape,cin,plcl,tlcl,pwat
    if cape.magnitude > 0:
        capestr = str(np.round(cape.magnitude))
    else:
        capestr = "NaN"

    if cin.magnitude > 0:
        cinstr = str(np.round(cin.magnitude))
    else:
        cinstr = "NaN"

    lclpstr = str(np.round(lcl_pressure.magnitude))
    lclTstr = str(np.round(lcl_temperature.magnitude))
    pwatstr = str(np.round(pwat.magnitude))

    #    str_par = "CAPE[J/kg]=" + capestr + " CIN[J/kg]=" + cinstr + " Plcl[hPa]=" + lclpstr + " Tlcl[C]=" + lclTstr + " pwat[mm]=" + pwatstr
    #    font = {'family': 'monospace',
    #        'color':  'darkred',
    #        'weight': 'normal',
    #        'size': 10,
    #        }
    #    plt.text(-20,1250,str_par,fontdict=font_par)
    save_file = img_name
    plt.savefig(save_file)
예제 #21
0
# Plot the data using normal plotting functions, in this case using
# log scaling in Y, as dictated by the typical meteorological plot
skew.plot(p, T, 'r')
skew.ax.set_xlabel('Temperature (Celsius)')
skew.plot(p, Td, 'g')

#plots the barbs
my_interval = np.arange(100, 1000, 20) * units('mbar')
ix = resample_nn_1d(p, my_interval)

skew.plot_barbs(p[ix], u[ix], v[ix])
skew.ax.set_ylim(1075, 100)
skew.ax.set_ylabel('Pressure (hPa)')

lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0]) #LCL
pwat = mpcalc.precipitable_water(Td, p, 500 * units.hectopascal).to('in') #PWAT
cape, cin = mpcalc.most_unstable_cape_cin(p[:], T[:], Td[:]) #MUCAPE
cape_sfc, cin_sfc = mpcalc.surface_based_cape_cin(p, T, Td) #SBCAPE
prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') #parcel profile
equiv_pot_temp = mpcalc.equivalent_potential_temperature(p, T, Td) #equivalent potential temperature
el_pressure, el_temperature = mpcalc.el(p, T, Td) #elevated level
lfc_pressure, lfc_temperature = mpcalc.lfc(p, T, Td) #LFC

#calculates shear
u_threekm_bulk_shear, v_threekm_bulk_shear = mpcalc.bulk_shear(p, u, v, hgt, bottom = min(hgt), depth = 3000 * units.meter)
threekm_bulk_shear = mpcalc.get_wind_speed(u_threekm_bulk_shear, v_threekm_bulk_shear)
u_onekm_bulk_shear, v_onekm_bulk_shear = mpcalc.bulk_shear(p, u, v, hgt, bottom = min(hgt), depth = 1000 * units.meter)
onekm_bulk_shear = mpcalc.get_wind_speed(u_onekm_bulk_shear, v_onekm_bulk_shear)

#shows the level of the LCL, LFC, and EL.
skew.ax.text(T[0].magnitude, p[0].magnitude + 5, str(int(np.round(T[0].to('degF').magnitude))), fontsize = 'medium', horizontalalignment = 'left', verticalalignment = 'top', color = 'red')
예제 #22
0
                              EL_temperature,
                              'bo',
                              markersize=8,
                              fillstyle='none',
                              label='EL')

                    # Calculate most unstable parcel
                    mup_pressure, mup_temperature, mup_dewTemperature, mup_index \
                        = mpcalc.most_unstable_parcel(p, T, Td, heights=prof_0, bottom=1050 * units.hPa)
                    skew.plot(p[mup_index - 1:mup_index + 1],
                              T[mup_index - 1:mup_index + 1],
                              'b',
                              label='the most unstable level')

                    # Calculate precipitable water
                    precipitable_water = mpcalc.precipitable_water(Td, p)

                    # An example of a slanted line at constant T -- in this case the 0
                    # isotherm
                    skew.ax.axvline(0,
                                    color='brown',
                                    linestyle='-',
                                    linewidth=1,
                                    label='isothermal')
                    for i in range(46):
                        for j in range(1, 10):
                            skew.ax.axvline(i * 5 - 160 + j,
                                            color='brown',
                                            linestyle='-',
                                            linewidth=0.3)
                        skew.ax.axvline(i * 5 - 160,
예제 #23
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)
예제 #24
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)
예제 #25
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
예제 #26
0
 def process_skewt(self):
     # Calculation
     index_p100 = get_pressure_level_index(self.p_i, 100)
     lcl_p, lcl_t = mpcalc.lcl(self.p_i[0], self.t_i[0], self.td_i[0])
     lfc_p, lfc_t = mpcalc.lfc(self.p_i, self.t_i, self.td_i)
     el_p, el_t = mpcalc.el(self.p_i, self.t_i, self.td_i)
     prof = mpcalc.parcel_profile(self.p_i, self.t_i[0], self.td_i[0]).to('degC')
     cape, cin = mpcalc.cape_cin(self.p_i, self.t_i, self.td_i, prof)
     mucape, mucin = mpcalc.most_unstable_cape_cin(self.p_i, self.t_i, self.td_i)
     pwat = mpcalc.precipitable_water(self.td_i, self.p_i)
     i8 = get_pressure_level_index(self.p_i, 850)
     i7 = get_pressure_level_index(self.p_i, 700)
     i5 = get_pressure_level_index(self.p_i, 500)
     theta850 = mpcalc.equivalent_potential_temperature(850 * units('hPa'), self.t_i[i8], self.td_i[i5])
     theta500 = mpcalc.equivalent_potential_temperature(500 * units('hPa'), self.t_i[i5], self.td_i[i5])
     thetadiff = theta850 - theta500
     k = self.t_i[i8] - self.t_i[i5] + self.td_i[i8] - (self.t_i[i7] - self.td_i[i7])
     a = ((self.t_i[i8] - self.t_i[i5]) - (self.t_i[i8] - self.td_i[i5]) -
         (self.t_i[i7] - self.td_i[i7]) - (self.t_i[i5] - self.td_i[i5]))
     sw = c_sweat(np.array(self.t_i[i8].magnitude), np.array(self.td_i[i8].magnitude),
                  np.array(self.t_i[i5].magnitude), np.array(self.u_i[i8].magnitude),
                  np.array(self.v_i[i8].magnitude), np.array(self.u_i[i5].magnitude),
                  np.array(self.v_i[i5].magnitude))
     si = showalter_index(self.t_i[i8], self.td_i[i8], self.t_i[i5])
     li = lifted_index(self.t_i[0], self.td_i[0], self.p_i[0], self.t_i[i5])
     srh_pos, srh_neg, srh_tot = mpcalc.storm_relative_helicity(self.u_i, self.v_i, self.alt, 1000 * units('m'))
     sbcape, sbcin = mpcalc.surface_based_cape_cin(self.p_i, self.t_i, self.td_i)
     shr6km = mpcalc.bulk_shear(self.p_i, self.u_i, self.v_i, heights=self.alt, depth=6000 * units('m'))
     wshr6km = mpcalc.wind_speed(*shr6km)
     sigtor = mpcalc.significant_tornado(sbcape, delta_height(self.p_i[0], lcl_p), srh_tot, wshr6km)[0]
     # Plotting
     self.ax.set_ylim(1050, 100)
     self.ax.set_xlim(-40, 50)
     self.plot(self.p_i, self.t_i, 'r', linewidth=1)
     self.plot(self.p_i[:self.dp_idx], self.td_i[:self.dp_idx], 'g', linewidth=1)
     self.plot_barbs(self.p_i[:index_p100], self.u_i[:index_p100] * 1.94, self.v_i[:index_p100] * 1.94)
     self.plot(lcl_p, lcl_t, 'ko', markerfacecolor='black')
     self.plot(self.p_i, prof, 'k', linewidth=2)
     if cin.magnitude < 0:
         chi = -1 * cin.magnitude
         self.shade_cin(self.p_i, self.t_i, prof)
     elif cin.magnitude > 0:
         chi = cin.magnitude
         self.shade_cin(self.p_i, self.t_i, prof)
     else:
         chi = 0.
     self.shade_cape(self.p_i, self.t_i, prof)
     self.plot_dry_adiabats(linewidth=0.5)
     self.plot_moist_adiabats(linewidth=0.5)
     self.plot_mixing_lines(linewidth=0.5)
     plt.title('Skew-T Plot \nStation: {} Time: {}'.format(self.st, self.time.strftime('%Y.%m.%d %H:%M')), fontsize=14, loc='left')
     # Add hodograph
     ax = self._fig.add_axes([0.95, 0.71, 0.17, 0.17])
     h = Hodograph(ax, component_range=50)
     h.add_grid(increment=20)
     h.plot_colormapped(self.u_i[:index_p100], self.v_i[:index_p100], self.alt[:index_p100], linewidth=1.2)
     # Annotate parameters
     # Annotate names
     namelist = ['CAPE', 'CIN', 'MUCAPE', 'PWAT', 'K', 'A', 'SWEAT', 'LCL', 'LFC', 'EL', 'SI', 'LI', 'T850-500',
                 'θse850-500', 'SRH', 'STP']
     xcor = -50
     ycor = -90
     spacing = -9
     for nm in namelist:
         ax.text(xcor, ycor, '{}: '.format(nm), fontsize=10)
         ycor += spacing
     # Annotate values
     varlist = [cape, chi, mucape, pwat, k, a, sw, lcl_p, lfc_p, el_p, si, li, self.t_i[i8] - self.t_i[i5], thetadiff,
                srh_tot, sigtor]
     xcor = 10
     ycor = -90
     for v in varlist:
         if hasattr(v, 'magnitude'):
             v = v.magnitude
         ax.text(xcor, ycor, str(np.round_(v, 2)), fontsize=10)
         ycor += spacing
     # Annotate units
     unitlist = ['J/kg', 'J/kg', 'J/kg', 'mm', '°C', '°C', '', 'hPa', 'hPa', 'hPa', '°C', '°C', '°C', '°C']
     xcor = 45
     ycor = -90
     for u in unitlist:
         ax.text(xcor, ycor, ' {}'.format(u), fontsize=10)
         ycor += spacing