Example #1
0
def test_virtemp():
    input_p = 925
    input_t = 7
    input_td = 3
    correct_t = 7.873652724440433
    returned_t = thermo.virtemp(input_p, input_t, input_td)
    npt.assert_almost_equal(returned_t, correct_t)

    input_p = 950
    input_t = 20
    input_td = 14
    correct_t = 21.883780613639033
    returned_t = thermo.virtemp(input_p, input_t, input_td)
    npt.assert_almost_equal(returned_t, correct_t)
Example #2
0
def test_virtemp():
    input_p = 925
    input_t = 7
    input_td = 3
    correct_t = 7.873652724440433
    returned_t = thermo.virtemp(input_p, input_t, input_td)
    npt.assert_almost_equal(returned_t, correct_t)

    input_p = 950
    input_t = 20
    input_td = 14
    correct_t = 21.883780613639033
    returned_t = thermo.virtemp(input_p, input_t, input_td)
    npt.assert_almost_equal(returned_t, correct_t)
Example #3
0
def vtmp(prof, p):
    '''
    Interpolates the given data to calculate a virtual temperature
    at a given pressure

    Parameters
    ----------
    prof : profile object
        Profile object
    p : number, numpy array
        Pressure (hPa) of the level for which virtual temperature is desired

    Returns
    -------
    Virtual tmperature (C) at the given pressure

    '''
    t = temp(prof, p)
    td = dwpt(prof, p)
    try:
        vt = [thermo.virtemp(pp, tt, tdtd) for pp, tt, tdtd in zip(p, t, td)]
        return ma.asarray(vt)
    except TypeError:
        return thermo.virtemp(p, t, td)
Example #4
0
def vtmp(prof, p):
    '''
    Interpolates the given data to calculate a virtual temperature
    at a given pressure

    Parameters
    ----------
    prof : profile object
        Profile object
    p : number, numpy array
        Pressure (hPa) of the level for which virtual temperature is desired

    Returns
    -------
    Virtual tmperature (C) at the given pressure

    '''
    t = temp(prof, p)
    td = dwpt(prof, p)
    try:
        vt = [thermo.virtemp(pp, tt, tdtd) for pp,tt,tdtd in zip(p, t, td)]
        return ma.asarray(vt)
    except TypeError:
        return thermo.virtemp(p, t, td)
Example #5
0
def vtmp(p, prof):
    '''
    Interpolates the given data to calculate a virtual temperature at a
    given pressure

    Inputs
    ------
        p           (float)                 Pressure (hPa) of a level
        prof        (profile object)        Profile object

    Returns
    -------
        Virtual temperature (C) at the given pressure
    '''
    t = temp(p, prof)
    td = dwpt(p, prof)
    return thermo.virtemp(p, t, td)
Example #6
0
def vtmp(p, prof):
    '''
    Interpolates the given data to calculate a virtual temperature at a
    given pressure

    Inputs
    ------
        p           (float)                 Pressure (hPa) of a level
        prof        (profile object)        Profile object

    Returns
    -------
        Virtual temperature (C) at the given pressure
    '''
    t = temp(p, prof)
    td = dwpt(p, prof)
    return thermo.virtemp(p, t, td)
Example #7
0
    def __init__(self, **kwargs):
        '''
        Create the sounding data object
        
        Parameters
        ----------
        Mandatory Keywords
        pres : array_like
        The pressure values (Hectopaschals)
        hght : array_like
        The corresponding height values (Meters)
        tmpc : array_like
        The corresponding temperature values (Celsius)
        dwpc : array_like
        The corresponding dewpoint temperature values (Celsius)
            
        Optional Keyword Pairs (must use one or the other)
        wdir : array_like
        The direction from which the wind is blowing in
        meteorological degrees
        wspd : array_like
        The speed of the wind
            
        OR
            
        u : array_like
        The U-component of the direction from which the wind
        is blowing
            
        v : array_like
        The V-component of the direction from which the wind
        is blowing.
            
        Optional Keywords
        missing : number (default: sharppy.sharptab.constants.MISSING)
        The value of the missing flag

        location : string (default: None)
        The 3 character station identifier or 4 character
        WMO station ID for radiosonde locations. Used for
        the PWV database.
        
        strictQC : boolean
        A flag that indicates whether or not the strict quality control
        routines should be run on the profile upon construction.

        Returns
        -------
        prof: Profile object
            
        '''
        super(BasicProfile, self).__init__(**kwargs)

        strictQC = kwargs.get('strictQC', True)

        assert len(self.pres) == len(self.hght) == len(self.tmpc) == len(self.dwpc),\
                "Length of pres, hght, tmpc, or dwpc arrays passed to constructor are not the same."

        ## did the user provide the wind in vector form?
        if self.wdir is not None:
            assert len(self.wdir) == len(self.wspd) == len(
                self.pres
            ), "Length of wdir and wspd arrays passed to constructor are not the same length as the pres array."
            self.wdir[self.wdir == self.missing] = ma.masked
            self.wspd[self.wspd == self.missing] = ma.masked
            self.wdir[self.wspd.mask] = ma.masked
            self.wspd[self.wdir.mask] = ma.masked
            self.u, self.v = utils.vec2comp(self.wdir, self.wspd)

        ## did the user provide the wind in u,v form?
        elif self.u is not None:
            assert len(self.u) == len(self.v) == len(
                self.pres
            ), "Length of u and v arrays passed to constructor are not the same length as the pres array."
            self.u[self.u == self.missing] = ma.masked
            self.v[self.v == self.missing] = ma.masked
            self.u[self.v.mask] = ma.masked
            self.v[self.u.mask] = ma.masked
            self.wdir, self.wspd = utils.comp2vec(self.u, self.v)

        ## check if any standard deviation data was supplied
        if self.tmp_stdev is not None:
            self.dew_stdev[self.dew_stdev == self.missing] = ma.masked
            self.tmp_stdev[self.tmp_stdev == self.missing] = ma.masked
            self.dew_stdev.set_fill_value(self.missing)
            self.tmp_stdev.set_fill_value(self.missing)

        if self.omeg is not None:
            ## get the omega data and turn into arrays
            assert len(self.omeg) == len(
                self.pres
            ), "Length of omeg array passed to constructor is not the same length as the pres array."
            self.omeg[self.omeg == self.missing] = ma.masked
        else:
            self.omeg = ma.masked_all(len(self.hght))

        # QC Checks on the arrays passed to the constructor.
        qc_tools.areProfileArrayLengthEqual(self)

        ## mask the missing values
        self.pres[self.pres == self.missing] = ma.masked
        self.hght[self.hght == self.missing] = ma.masked
        self.tmpc[self.tmpc == self.missing] = ma.masked
        self.dwpc[self.dwpc == self.missing] = ma.masked

        #if not qc_tools.isPRESValid(self.pres):
        ##    qc_tools.raiseError("Incorrect order of pressure array (or repeat values) or pressure array is of length <= 1.", ValueError)
        if not qc_tools.isHGHTValid(self.hght) and strictQC:
            qc_tools.raiseError(
                "Incorrect order of height (or repeat values) array or height array is of length <= 1.",
                ValueError)
        if not qc_tools.isTMPCValid(self.tmpc):
            qc_tools.raiseError(
                "Invalid temperature array. Array contains a value < -273.15 Celsius.",
                ValueError)
        if not qc_tools.isDWPCValid(self.dwpc):
            qc_tools.raiseError(
                "Invalid dewpoint array. Array contains a value < -273.15 Celsius.",
                ValueError)
        if not qc_tools.isWSPDValid(self.wspd) and strictQC:
            qc_tools.raiseError(
                "Invalid wind speed array. Array contains a value < 0 knots.",
                ValueError)
        if not qc_tools.isWDIRValid(self.wdir) and strictQC:
            qc_tools.raiseError(
                "Invalid wind direction array. Array contains a value < 0 degrees or value > 360 degrees.",
                ValueError)

        self.logp = np.log10(self.pres.copy())
        self.vtmp = thermo.virtemp(self.pres, self.tmpc, self.dwpc)
        idx = np.ma.where(self.pres > 0)[0]
        self.vtmp[self.dwpc.mask[idx]] = self.tmpc[
            self.dwpc.mask[idx]]  # Masking any virtual temperature

        ## get the index of the top and bottom of the profile
        self.sfc = self.get_sfc()
        self.top = self.get_top()
        ## generate the wetbulb profile
        self.wetbulb = self.get_wetbulb_profile()
        ## generate theta-e profile
        self.thetae = self.get_thetae_profile()
Example #8
0
    def __init__(self, **kwargs):
        '''
        Create the sounding data object
        
        Parameters
        ----------
        Mandatory Keywords
        pres : array_like
            The pressure values (Hectopaschals)
        hght : array_like
            The corresponding height values (Meters)
        tmpc : array_like
            The corresponding temperature values (Celsius)
        dwpc : array_like
        The corresponding dewpoint temperature values (Celsius)
            
        Optional Keyword Pairs (must use one or the other)
        wdir : array_like
            The direction from which the wind is blowing in
            meteorological degrees
        wspd : array_like
            The speed of the wind (kts)
            
        OR
            
        u : array_like
            The U-component of the direction from which the wind
            is blowing (kts)
            
        v : array_like
            The V-component of the direction from which the wind
            is blowing. (kts)
            
        Optional Keywords
        missing : number (default: sharppy.sharptab.constants.MISSING)
            The value of the missing flag

        location : string (default: None)
            The 3 character station identifier or 4 character
            WMO station ID for radiosonde locations. Used for
            the PWV database.
        
        strictQC : boolean
            A flag that indicates whether or not the strict quality control
            routines should be run on the profile upon construction.

        Returns
        -------
        prof: Profile object
            
        '''
        super(BasicProfile, self).__init__(**kwargs)

        self.strictQC = kwargs.get('strictQC', True)

        ## did the user provide the wind in vector form?
        if self.wdir is not None:
            self.wdir[self.wdir == self.missing] = ma.masked
            self.wspd[self.wspd == self.missing] = ma.masked
            self.wdir[self.wspd.mask] = ma.masked
            self.wspd[self.wdir.mask] = ma.masked
            self.u, self.v = utils.vec2comp(self.wdir, self.wspd)

        ## did the user provide the wind in u,v form?
        elif self.u is not None:
            self.u[self.u == self.missing] = ma.masked
            self.v[self.v == self.missing] = ma.masked
            self.u[self.v.mask] = ma.masked
            self.v[self.u.mask] = ma.masked
            self.wdir, self.wspd = utils.comp2vec(self.u, self.v)

        ## check if any standard deviation data was supplied
        if self.tmp_stdev is not None:
            self.dew_stdev[self.dew_stdev == self.missing] = ma.masked
            self.tmp_stdev[self.tmp_stdev == self.missing] = ma.masked
            self.dew_stdev.set_fill_value(self.missing)
            self.tmp_stdev.set_fill_value(self.missing)

        if self.omeg is not None:
            ## get the omega data and turn into arrays
            self.omeg[self.omeg == self.missing] = ma.masked
        else:
            self.omeg = ma.masked_all(len(self.hght))

        # QC Checks on the arrays passed to the constructor.
        qc_tools.areProfileArrayLengthEqual(self)
       
        ## mask the missing values
        self.pres[self.pres == self.missing] = ma.masked
        self.hght[self.hght == self.missing] = ma.masked
        self.tmpc[self.tmpc == self.missing] = ma.masked
        self.dwpc[self.dwpc == self.missing] = ma.masked

        self.logp = np.log10(self.pres.copy())
        self.vtmp = thermo.virtemp( self.pres, self.tmpc, self.dwpc )
        idx = np.ma.where(self.pres > 0)[0]
        self.vtmp[self.dwpc.mask[idx]] = self.tmpc[self.dwpc.mask[idx]] # Masking any virtual temperature 

        ## get the index of the top and bottom of the profile
        self.sfc = self.get_sfc()
        self.top = self.get_top()

        if self.strictQC is True:
            self.checkDataIntegrity()

        ## generate the wetbulb profile
        self.wetbulb = self.get_wetbulb_profile()
        ## generate theta-e profile
        self.thetae = self.get_thetae_profile()
        ## generate theta profile
        self.theta = self.get_theta_profile()
        ## generate water vapor mixing ratio profile
        self.wvmr = self.get_wvmr_profile()
        ## generate rh profile
        self.relh = self.get_rh_profile()
Example #9
0
''' Create the Sounding (Profile) Object '''
Example #10
0
    def __init__(self, **kwargs):
        '''
        Create the sounding data object
        
        Parameters
        ----------
        Mandatory Keywords
        pres : array_like
        The pressure values (Hectopaschals)
        hght : array_like
        The corresponding height values (Meters)
        tmpc : array_like
        The corresponding temperature values (Celsius)
        dwpc : array_like
        The corresponding dewpoint temperature values (Celsius)
            
        Optional Keyword Pairs (must use one or the other)
        wdir : array_like
        The direction from which the wind is blowing in
        meteorological degrees
        wspd : array_like
        The speed of the wind
            
        OR
            
        u : array_like
        The U-component of the direction from which the wind
        is blowing
            
        v : array_like
        The V-component of the direction from which the wind
        is blowing.
            
        Optional Keywords
        missing : number (default: sharppy.sharptab.constants.MISSING)
        The value of the missing flag
        location : string (default: None)
        The 3 character station identifier or 4 character
        WMO station ID for radiosonde locations. Used for
        the PWV database.
        
        strictQC : boolean
        A flag that indicates whether or not the strict quality control
        routines should be run on the profile upon construction.
        Returns
        -------
        prof: Profile object
            
        '''
        super(BasicProfile, self).__init__(**kwargs)

        strictQC = kwargs.get('strictQC', True)

        assert len(self.pres) == len(self.hght) == len(self.tmpc) == len(self.dwpc),\
                "Length of pres, hght, tmpc, or dwpc arrays passed to constructor are not the same."

        ## did the user provide the wind in vector form?
        if self.wdir is not None:
            assert len(self.wdir) == len(self.wspd) == len(self.pres), "Length of wdir and wspd arrays passed to constructor are not the same length as the pres array."
            self.wdir[self.wdir == self.missing] = ma.masked
            self.wspd[self.wspd == self.missing] = ma.masked
            self.wdir[self.wspd.mask] = ma.masked
            self.wspd[self.wdir.mask] = ma.masked
            self.u, self.v = utils.vec2comp(self.wdir, self.wspd)

        ## did the user provide the wind in u,v form?
        elif self.u is not None:
            assert len(self.u) == len(self.v) == len(self.pres), "Length of u and v arrays passed to constructor are not the same length as the pres array."
            self.u[self.u == self.missing] = ma.masked
            self.v[self.v == self.missing] = ma.masked
            self.u[self.v.mask] = ma.masked
            self.v[self.u.mask] = ma.masked
            self.wdir, self.wspd = utils.comp2vec(self.u, self.v)

        ## check if any standard deviation data was supplied
        if self.tmp_stdev is not None:
            self.dew_stdev[self.dew_stdev == self.missing] = ma.masked
            self.tmp_stdev[self.tmp_stdev == self.missing] = ma.masked
            self.dew_stdev.set_fill_value(self.missing)
            self.tmp_stdev.set_fill_value(self.missing)

        if self.omeg is not None:
            ## get the omega data and turn into arrays
            assert len(self.omeg) == len(self.pres), "Length of omeg array passed to constructor is not the same length as the pres array."
            self.omeg[self.omeg == self.missing] = ma.masked
        else:
            self.omeg = ma.masked_all(len(self.hght))

        # QC Checks on the arrays passed to the constructor.
        qc_tools.areProfileArrayLengthEqual(self)
       
        ## mask the missing values
        self.pres[self.pres == self.missing] = ma.masked
        self.hght[self.hght == self.missing] = ma.masked
        self.tmpc[self.tmpc == self.missing] = ma.masked
        self.dwpc[self.dwpc == self.missing] = ma.masked

        #if not qc_tools.isPRESValid(self.pres):
        ##    qc_tools.raiseError("Incorrect order of pressure array (or repeat values) or pressure array is of length <= 1.", ValueError)
        if not qc_tools.isHGHTValid(self.hght) and strictQC:
            qc_tools.raiseError("Incorrect order of height (or repeat values) array or height array is of length <= 1.", ValueError)
        if not qc_tools.isTMPCValid(self.tmpc):
            qc_tools.raiseError("Invalid temperature array. Array contains a value < 273.15 Celsius.", ValueError)
        if not qc_tools.isDWPCValid(self.dwpc):
            qc_tools.raiseError("Invalid dewpoint array. Array contains a value < 273.15 Celsius.", ValueError)
        if not qc_tools.isWSPDValid(self.wspd) and strictQC:
            qc_tools.raiseError("Invalid wind speed array. Array contains a value < 0 knots.", ValueError)
        if not qc_tools.isWDIRValid(self.wdir) and strictQC:
            qc_tools.raiseError("Invalid wind direction array. Array contains a value < 0 degrees or value >= 360 degrees.", ValueError)     


        self.logp = np.log10(self.pres.copy())
        self.vtmp = thermo.virtemp( self.pres, self.tmpc, self.dwpc )
        idx = np.ma.where(self.pres > 0)[0]
        self.vtmp[self.dwpc.mask[idx]] = self.tmpc[self.dwpc.mask[idx]] # Masking any virtual temperature 

        ## get the index of the top and bottom of the profile
        self.sfc = self.get_sfc()
        self.top = self.get_top()
        ## generate the wetbulb profile
        self.wetbulb = self.get_wetbulb_profile()
        ## generate theta-e profile
        self.thetae = self.get_thetae_profile()
Example #11
0
    def __init__(self, **kwargs):
        '''
        Create the sounding data object
        
        Parameters
        ----------
        Mandatory Keywords
        hght : array_like
        The corresponding height values (Meters)
            
        Optional Keyword Pairs (must use one or the other)
        wdir : array_like
        The direction from which the wind is blowing in
        meteorological degrees
        wspd : array_like
        The speed of the wind
        rms : array_like
        The RMS from the VAD algorithm.
        OR
            
        u : array_like
        The U-component of the direction from which the wind
        is blowing
            
        v : array_like
        The V-component of the direction from which the wind
        is blowing.
            
        Optional Keywords
        missing : number (default: sharppy.sharptab.constants.MISSING)
        The value of the missing flag
        location : string (default: None)
        The 3 character station identifier or 4 character
        WMO station ID for radiosonde locations. Used for
        the PWV database.
        Returns
        -------
        prof: Profile object
            
        '''

        #Dummy variables so Profile doesn't crash
        wspd = kwargs.get('wspd')
        kwargs['tmpc'] = np.ones(len(wspd))
        kwargs['dwpc'] = np.ones(len(wspd))
        kwargs['pres'] = np.arange(1,len(wspd)+1,1)[::-1]

        super(VADProfile, self).__init__(**kwargs)
        
        self.rms = kwargs.get('rms')
        
        assert len(self.hght) == len(self.tmpc) == len(self.dwpc),\
                "Length of pres, hght, tmpc, or dwpc arrays passed to constructor are not the same."

        ## did the user provide the wind in vector form?
        if self.wdir is not None:
            assert len(self.wdir) == len(self.wspd) == len(self.hght), "Length of wdir and wspd arrays passed to constructor are not the same length as the pres array."
            self.wdir[self.wdir == self.missing] = ma.masked
            self.wspd[self.wspd == self.missing] = ma.masked
            self.wdir[self.wspd.mask] = ma.masked
            self.wspd[self.wdir.mask] = ma.masked
            self.u, self.v = utils.vec2comp(self.wdir, self.wspd)

        ## did the user provide the wind in u,v form?
        elif self.u is not None:
            assert len(self.u) == len(self.v) == len(self.hght), "Length of u and v arrays passed to constructor are not the same length as the pres array."
            self.u[self.u == self.missing] = ma.masked
            self.v[self.v == self.missing] = ma.masked
            self.u[self.v.mask] = ma.masked
            self.v[self.u.mask] = ma.masked
            self.wdir, self.wspd = utils.comp2vec(self.u, self.v)

                ## mask the missing values
        self.hght[self.hght == self.missing] = ma.masked
        strictQC = False
        #if not qc_tools.isHGHTValid(self.hght):
        #    qc_tools.raiseError("Incorrect order of height (or repeat values) array or height array is of length <= 1.", ValueError)
        if not qc_tools.isWSPDValid(self.wspd) and strictQC:
            qc_tools.raiseError("Invalid wind speed array. Array contains a value < 0 knots.", ValueError)
        if not qc_tools.isWDIRValid(self.wdir) and strictQC:
            qc_tools.raiseError("Invalid wind direction array. Array contains a value < 0 degrees or value >= 360 degrees.", ValueError)     

        print """Using the VADDecoder...something to keep in mind is that all interp.py routines
               use the prof.logp variable to perform the pressure based interpolation.  Because VAD 
               and radar wind profiler data does not contain pressure data, this will break
               any interpolation routines and any routines that require pressure. 
               Will have to find a way around this."""
        self.logp = np.log10(self.pres.copy())
        self.vtmp = thermo.virtemp( self.pres, self.tmpc, self.dwpc )
        idx = np.ma.where(self.pres > 0)[0]
        #self.vtmp[slf.dwpc.mask[idx]] = self.tmpc[self.dwpc.mask[idx]] # Masking any virtual temperature 

        ## get the index of the top and bottom of the profile
        self.sfc = self.get_sfc()
        self.top = self.get_top()
Example #12
0
def parcelx(lower, upper, pres, temp, dwpt, prof, **kwargs):
    '''
    Lifts the specified parcel, calculated various levels and parameters from
    the profile object. B+/B- are calculated based on the specified layer.

    !! All calculations use the virtual temperature correction unless noted. !!

    Inputs
    ------
        lower       (float)                 Lower-bound lifting level (hPa)
        upper       (float)                 Upper-bound lifting level
        pres        (float)                 Pressure of parcel to lift (hPa)
        temp        (float)                 Temperature of parcel to lift (C)
        dwpt        (float)                 Dew Point of parcel to lift (C)
        prof        (profile object)        Profile Object

    Returns
    -------
        pcl         (parcel object)         Parcel Object
    '''
    pcl = Parcel(-1, -1, pres, temp, dwpt)
    if 'lplvals' in kwargs: pcl.lplvals = kwargs.get('lplvals')
    else:
        lplvals = DefineParcel(prof, 5, pres=pres, temp=temp, dwpt=dwpt)
        pcl.lplvals = lplvals

    if prof.gNumLevels < 1: return pcl

    lyre = -1
    cap_strength = RMISSD
    cap_strengthpres = RMISSD
    li_max = RMISSD
    li_maxpres = RMISSD
    totp = 0.
    totn = 0.
    tote = 0.
    cinh_old = 0.

    # See if default layer is specified
    if lower == -1:
        lower = prof.gSndg[prof.sfc][prof.pind]
        pcl.blayer = lower
    if upper == -1:
        upper = prof.gSndg[prof.gNumLevels - 1][prof.pind]
        pcl.tlayer = upper

    # Make sure that this is a valid layer
    if lower > pres:
        lower = pres
        pcl.blayer = lower
    if not QC(interp.vtmp(lower, prof)) or \
       not QC(interp.vtmp(upper, prof)):
        return RMISSD

    # Begin with the Mixing Layer
    te1 = interp.vtmp(pres, prof)
    pe1 = lower
    h1 = interp.hght(pe1, prof)
    tp1 = thermo.virtemp(pres, temp, dwpt)
    # te1 = tp1

    # Lift parcel and return LCL pres (hPa) and LCL temp (c)
    pe2, tp2 = thermo.drylift(pres, temp, dwpt)
    blupper = pe2  # Define top of layer as LCL pres
    h2 = interp.hght(pe2, prof)
    te2 = interp.vtmp(pe2, prof)
    pcl.lclpres = pe2
    pcl.lclhght = interp.agl(h2, prof)

    # Calculate lifted parcel theta for use in iterative CINH loop below
    # RECALL: lifted parcel theta is CONSTANT from LPL to LCL
    theta_parcel = thermo.theta(pe2, tp2, 1000.)

    # Environmental theta and mixing ratio at LPL
    bltheta = thermo.theta(pres, interp.temp(pres, prof), 1000.)
    blmr = thermo.mixratio(pres, dwpt)

    # ACCUMULATED CINH IN MIXING LAYER BELOW THE LCL
    # This will be done in 10mb increments, and will use the virtual
    # temperature correction where possible
    pinc = -10
    a = int(lower)
    b = int(blupper)
    for pp in range(a, b, int(pinc)):
        pp1 = pp
        pp2 = pp + pinc
        if pp2 < blupper: pp2 = blupper
        dz = interp.hght(pp2, prof) - interp.hght(pp1, prof)

        # Calculate difference between Tv_parcel and Tv_environment at top
        # and bottom of 10mb layers. Make use of constant lifted parcel
        # theta and mixing ratio from LPL to LCL
        tv_env_bot = thermo.virtemp(
            pp1, thermo.theta(pp1, interp.temp(pp1, prof), 1000.),
            interp.dwpt(pp1, prof))
        tdef1 = (thermo.virtemp(pp1, theta_parcel,
            thermo.temp_at_mixrat(blmr, pp1)) - tv_env_bot) / \
            (thermo.ctok(tv_env_bot))

        tv_env_top = thermo.virtemp(
            pp2, thermo.theta(pp2, interp.temp(pp2, prof), 1000.),
            interp.dwpt(pp2, prof))
        tdef2 = (thermo.virtemp(pp2, theta_parcel,
            thermo.temp_at_mixrat(blmr, pp2)) - tv_env_top) / \
            (thermo.ctok(tv_env_bot))

        lyre = G * (tdef1 + tdef2) / 2. * dz
        if lyre < 0: totn += lyre

    # Move the bottom layer to the top of the boundary layer
    if lower > pe2:
        lower = pe2
        pcl.blayer = lower

    # Calculate height of various temperature levels
    p0c = temp_lvl(0., prof)
    pm10c = temp_lvl(-10., prof)
    pm20c = temp_lvl(-20., prof)
    pm30c = temp_lvl(-30., prof)
    hgt0c = interp.hght(p0c, prof)
    hgtm10c = interp.hght(pm10c, prof)
    hgtm20c = interp.hght(pm20c, prof)
    hgtm30c = interp.hght(pm30c, prof)
    pcl.p0c = p0c
    pcl.pm10c = pm10c
    pcl.pm20c = pm20c
    pcl.pm30c = pm30c
    pcl.hght0c = hgt0c
    pcl.hghtm10c = hgtm10c
    pcl.hghtm20c = hgtm20c
    pcl.hghtm30c = hgtm30c

    # Find lowest observation in layer
    i = 0
    while prof.gSndg[i][prof.pind] > lower:
        if i == prof.gNumLevels - 1: break
        i += 1
    while not QC(prof.gSndg[i][prof.tdind]):
        if i == prof.gNumLevels - 1: break
        i += 1
    lptr = i
    if prof.gSndg[i][prof.pind] == lower:
        if i != prof.gNumLevels - 1: lptr += 1

    # Find highest observation in layer
    i = prof.gNumLevels - 1
    while prof.gSndg[i][prof.pind] < upper:
        if i < lptr: break
        i -= 1
    uptr = i
    if prof.gSndg[i][prof.pind] == upper:
        if i > lptr: uptr -= 1

    # START WITH INTERPOLATED BOTTOM LAYER
    # Begin moist ascent from lifted parcel LCL (pe2, tp2)
    pe1 = lower
    h1 = interp.hght(pe1, prof)
    te1 = interp.vtmp(pe1, prof)
    tp1 = thermo.wetlift(pe2, tp2, pe1)
    lyre = 0
    lyrlast = 0
    for i in range(lptr, prof.gNumLevels):
        if not QC(prof.gSndg[i][prof.tind]): continue
        pe2 = prof.gSndg[i][prof.pind]
        h2 = prof.gSndg[i][prof.zind]
        te2 = interp.vtmp(pe2, prof)
        tp2 = thermo.wetlift(pe1, tp1, pe2)
        tdef1 = (thermo.virtemp(pe1, tp1, tp1) - te1) / thermo.ctok(te1)
        tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / thermo.ctok(te2)
        lyrlast = lyre
        lyre = G * (tdef1 + tdef2) / 2. * (h2 - h1)

        # Add layer energy to total positive if lyre > 0
        if lyre > 0:
            totp += lyre
            # Add layer energy to total negative if lyre < 0, only up to EL
        else:
            if pe2 > 500.: totn += lyre

        # Check for Max LI
        mli = thermo.virtemp(pe2, tp2, tp2) - te2
        if mli > li_max:
            li_max = mli
            li_maxpres = pe2

        # Check for Max Cap Strength
        mcap = te2 - mli
        if mcap > cap_strength:
            cap_strength = mcap
            cap_strengthpres = pe2

        tote += lyre
        pelast = pe1
        pe1 = pe2
        h1 = h2
        te1 = te2
        tp1 = tp2

        # Is this the top of the specified layer
        if i >= uptr and not QC(pcl.bplus):
            pe3 = pe1
            h3 = h1
            te3 = te1
            tp3 = tp1
            lyrf = lyre
            if lyrf > 0:
                pcl.bplus = totp - lyrf
                pcl.bminus = totn
            else:
                pcl.bplus = totp
                if pe2 > 500.: pcl.bminus = totn + lyrf
                else: pcl.bminus = totn
            pe2 = upper
            h2 = interp.hght(pe2, prof)
            te2 = interp.vtmp(pe2, prof)
            tp2 = thermo.wetlift(pe3, tp3, pe2)
            tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / thermo.ctok(te3)
            tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / thermo.ctok(te2)
            lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3)
            if lyrf > 0: pcl.bplus += lyrf
            else:
                if pe2 > 500.: pcl.bminus += lyrf
            if pcl.bplus == 0: pcl.bminus = 0.

        # Is this the freezing level
        if te2 < 0. and not QC(pcl.bfzl):
            pe3 = pelast
            h3 = interp.hght(pe3, prof)
            te3 = interp.vtmp(pe3, prof)
            tp3 = thermo.wetlift(pe1, tp1, pe3)
            lyrf = lyre
            if lyrf > 0.: pcl.bfzl = totp - lyrf
            else: pcl.bfzl = totp
            if not QC(p0c) or p0c > pe3:
                pcl.bfzl = 0
            elif QC(pe2):
                te2 = interp.vtmp(pe2, prof)
                tp2 = thermo.wetlift(pe3, tp3, pe2)
                tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                    thermo.ctok(te3)
                tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                    thermo.ctok(te2)
                lyrf = G * (tdef3 + tdef2) / 2. * (hgt0c - h3)
                if lyrf > 0: pcl.bfzl += lyrf

        # Is this the -10C level
        if te2 < -10. and not QC(pcl.wm10c):
            pe3 = pelast
            h3 = interp.hght(pe3, prof)
            te3 = interp.vtmp(pe3, prof)
            tp3 = thermo.wetlift(pe1, tp1, pe3)
            lyrf = lyre
            if lyrf > 0.: pcl.wm10c = totp - lyrf
            else: pcl.wm10c = totp
            if not QC(pm10c) or pm10c > pcl.lclpres:
                pcl.wm10c = 0
            elif QC(pe2):
                te2 = interp.vtmp(pe2, prof)
                tp2 = thermo.wetlift(pe3, tp3, pe2)
                tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                    thermo.ctok(te3)
                tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                    thermo.ctok(te2)
                lyrf = G * (tdef3 + tdef2) / 2. * (hgtm10c - h3)
                if lyrf > 0: pcl.wm10c += lyrf

        # Is this the -20C level
        if te2 < -20. and not QC(pcl.wm20c):
            pe3 = pelast
            h3 = interp.hght(pe3, prof)
            te3 = interp.vtmp(pe3, prof)
            tp3 = thermo.wetlift(pe1, tp1, pe3)
            lyrf = lyre
            if lyrf > 0.: pcl.wm20c = totp - lyrf
            else: pcl.wm20c = totp
            if not QC(pm20c) or pm20c > pcl.lclpres:
                pcl.wm20c = 0
            elif QC(pe2):
                te2 = interp.vtmp(pe2, prof)
                tp2 = thermo.wetlift(pe3, tp3, pe2)
                tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                    thermo.ctok(te3)
                tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                    thermo.ctok(te2)
                lyrf = G * (tdef3 + tdef2) / 2. * (hgtm20c - h3)
                if lyrf > 0: pcl.wm20c += lyrf

        # Is this the -30C level
        if te2 < -30. and not QC(pcl.wm30c):
            pe3 = pelast
            h3 = interp.hght(pe3, prof)
            te3 = interp.vtmp(pe3, prof)
            tp3 = thermo.wetlift(pe1, tp1, pe3)
            lyrf = lyre
            if lyrf > 0.: pcl.wm30c = totp - lyrf
            else: pcl.wm30c = totp
            if not QC(pm30c) or pm30c > pcl.lclpres:
                pcl.wm30c = 0
            elif QC(pe2):
                te2 = interp.vtmp(pe2, prof)
                tp2 = thermo.wetlift(pe3, tp3, pe2)
                tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                    thermo.ctok(te3)
                tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                    thermo.ctok(te2)
                lyrf = G * (tdef3 + tdef2) / 2. * (hgtm30c - h3)
                if lyrf > 0: pcl.wm30c += lyrf

        # Is this the 3km level
        if pcl.lclhght < 3000.:
            h = interp.agl(interp.hght(pe2, prof), prof)
            if h >= 3000. and not QC(pcl.b3km):
                pe3 = pelast
                h3 = interp.hght(pe3, prof)
                te3 = interp.vtmp(pe3, prof)
                tp3 = thermo.wetlift(pe1, tp1, pe3)
                lyrf = lyre
                if lyrf > 0: pcl.b3km = totp - lyrf
                else: pcl.b3km = totp
                h2 = interp.msl(3000., prof)
                pe2 = interp.pres(h2, prof)
                if QC(pe2):
                    te2 = interp.vtmp(pe2, prof)
                    tp2 = thermo.wetlift(pe3, tp3, pe2)
                    tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                        thermo.ctok(te3)
                    tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                        thermo.ctok(te2)
                    lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3)
                    if lyrf > 0: pcl.b3km += lyrf
        else: pcl.b3km = 0.

        # Is this the 6km level
        if pcl.lclhght < 6000.:
            h = interp.agl(interp.hght(pe2, prof), prof)
            if h >= 6000. and not QC(pcl.b6km):
                pe3 = pelast
                h3 = interp.hght(pe3, prof)
                te3 = interp.vtmp(pe3, prof)
                tp3 = thermo.wetlift(pe1, tp1, pe3)
                lyrf = lyre
                if lyrf > 0: pcl.b6km = totp - lyrf
                else: pcl.b6km = totp
                h2 = interp.msl(6000., prof)
                pe2 = interp.pres(h2, prof)
                if QC(pe2):
                    te2 = interp.vtmp(pe2, prof)
                    tp2 = thermo.wetlift(pe3, tp3, pe2)
                    tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                        thermo.ctok(te3)
                    tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                        thermo.ctok(te2)
                    lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3)
                    if lyrf > 0: pcl.b6km += lyrf
        else: pcl.b6km = 0.

        # LFC Possibility
        if lyre >= 0. and lyrlast <= 0.:
            tp3 = tp1
            te3 = te1
            pe2 = pe1
            pe3 = pelast
            while interp.vtmp(pe3, prof) > thermo.virtemp(
                    pe3, thermo.wetlift(pe2, tp3, pe3),
                    thermo.wetlift(pe2, tp3, pe3)):
                pe3 -= 5
            pcl.lfcpres = pe3
            pcl.lfchght = interp.agl(interp.hght(pe3, prof), prof)
            cinh_old = totn
            tote = 0.
            pcl.elpres = RMISSD
            li_max = RMISSD

            if cap_strength < 0.: cap_strength = 0.
            pcl.cap = cap_strength
            pcl.cappres = cap_strengthpres
            # Hack to force LFC to be at least at the LCL
            if pcl.lfcpres > pcl.lclpres:
                pcl.lfcpres = pcl.lclpres
                pcl.lfchght = pcl.lclhght

        # EL Possibility
        if lyre <= 0. and lyrlast >= 0.:
            tp3 = tp1
            te3 = te1
            pe2 = pe1
            pe3 = pelast
            while interp.vtmp(pe3, prof) < thermo.virtemp(
                    pe3, thermo.wetlift(pe2, tp3, pe3),
                    thermo.wetlift(pe2, tp3, pe3)):
                pe3 -= 5
            pcl.elpres = pe3
            pcl.elhght = interp.agl(interp.hght(pe3, prof), prof)
            pcl.mplpres = RMISSD
            pcl.limax = -li_max
            pcl.limaxpress = li_maxpres

        # MPL Possibility
        if tote < 0. and not QC(pcl.mplpres) and QC(pcl.elpres):
            pe3 = pelast
            h3 = interp.hght(pe3, prof)
            te3 = interp.vtmp(pe3, prof)
            tp3 = thermo.wetlift(pe1, tp1, pe3)
            totx = tote - lyre
            pe2 = pelast
            while totx > 0:
                pe2 -= 1
                te2 = interp.vtmp(pe2, prof)
                tp2 = thermo.wetlift(pe3, tp3, pe2)
                h2 = interp.hght(pe2, prof)
                tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                    thermo.ctok(te3)
                tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                    thermo.ctok(te2)
                lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3)
                totx += lyrf
                tp3 = tp2
                te3 = te2
                pe3 = pe2
            pcl.mplpres = pe2
            pcl.mplhght = interp.agl(interp.hght(pe2, prof), prof)

        # 500 hPa Lifted Index
        if prof.gSndg[i][prof.pind] <= 500. and pcl.li5 == RMISSD:
            a = interp.vtmp(500., prof)
            b = thermo.wetlift(pe1, tp1, 500.)
            pcl.li5 = a - thermo.virtemp(500, b, b)

        # 300 hPa Lifted Index
        if prof.gSndg[i][prof.pind] <= 300. and pcl.li3 == RMISSD:
            a = interp.vtmp(300., prof)
            b = thermo.wetlift(pe1, tp1, 300.)
            pcl.li3 = a - thermo.virtemp(300, b, b)

    # Calculate BRN if available
    pcl = bulk_rich(pcl, prof)

    pcl.bminus = cinh_old
    if pcl.bplus == 0: pcl.bminus = 0.
    return pcl
Example #13
0
def parcelx(lower, upper, pres, temp, dwpt, prof, **kwargs):
    '''
    Lifts the specified parcel, calculated various levels and parameters from
    the profile object. B+/B- are calculated based on the specified layer.

    !! All calculations use the virtual temperature correction unless noted. !!

    Inputs
    ------
        lower       (float)                 Lower-bound lifting level (hPa)
        upper       (float)                 Upper-bound lifting level
        pres        (float)                 Pressure of parcel to lift (hPa)
        temp        (float)                 Temperature of parcel to lift (C)
        dwpt        (float)                 Dew Point of parcel to lift (C)
        prof        (profile object)        Profile Object

    Returns
    -------
        pcl         (parcel object)         Parcel Object
    '''
    pcl = Parcel(-1, -1, pres, temp, dwpt)
    if 'lplvals' in kwargs: pcl.lplvals = kwargs.get('lplvals')
    else:
        lplvals = DefineParcel(prof, 5, pres=pres, temp=temp, dwpt=dwpt)
        pcl.lplvals = lplvals

    if prof.gNumLevels < 1: return pcl

    lyre = -1
    cap_strength = RMISSD
    cap_strengthpres = RMISSD
    li_max = RMISSD
    li_maxpres = RMISSD
    totp = 0.
    totn = 0.
    tote = 0.
    cinh_old = 0.

    # See if default layer is specified
    if lower == -1:
        lower = prof.gSndg[prof.sfc][prof.pind]
        pcl.blayer = lower
    if upper == -1:
        upper = prof.gSndg[prof.gNumLevels-1][prof.pind]
        pcl.tlayer = upper

    # Make sure that this is a valid layer
    if lower > pres:
        lower = pres
        pcl.blayer = lower
    if not QC(interp.vtmp(lower, prof)) or \
       not QC(interp.vtmp(upper, prof)):
        return RMISSD

    # Begin with the Mixing Layer
    te1 = interp.vtmp(pres, prof)
    pe1 = lower
    h1 = interp.hght(pe1, prof)
    tp1 = thermo.virtemp(pres, temp, dwpt)
    # te1 = tp1

    # Lift parcel and return LCL pres (hPa) and LCL temp (c)
    pe2, tp2 = thermo.drylift(pres, temp, dwpt)
    blupper = pe2       # Define top of layer as LCL pres
    h2 = interp.hght(pe2, prof)
    te2 = interp.vtmp(pe2, prof)
    pcl.lclpres = pe2
    pcl.lclhght = interp.agl(h2, prof)

    # Calculate lifted parcel theta for use in iterative CINH loop below
    # RECALL: lifted parcel theta is CONSTANT from LPL to LCL
    theta_parcel = thermo.theta(pe2, tp2, 1000.)

    # Environmental theta and mixing ratio at LPL
    bltheta = thermo.theta(pres, interp.temp(pres, prof), 1000.)
    blmr = thermo.mixratio(pres, dwpt)

    # ACCUMULATED CINH IN MIXING LAYER BELOW THE LCL
    # This will be done in 10mb increments, and will use the virtual
    # temperature correction where possible
    pinc = -10
    a = int(lower)
    b = int(blupper)
    for pp in range(a, b, int(pinc)):
        pp1 = pp
        pp2 = pp + pinc
        if pp2 < blupper: pp2 = blupper
        dz = interp.hght(pp2, prof) - interp.hght(pp1, prof)

        # Calculate difference between Tv_parcel and Tv_environment at top
        # and bottom of 10mb layers. Make use of constant lifted parcel
        # theta and mixing ratio from LPL to LCL
        tv_env_bot = thermo.virtemp(pp1, thermo.theta(pp1,
            interp.temp(pp1, prof), 1000.), interp.dwpt(pp1, prof))
        tdef1 = (thermo.virtemp(pp1, theta_parcel,
            thermo.temp_at_mixrat(blmr, pp1)) - tv_env_bot) / \
            (thermo.ctok(tv_env_bot))

        tv_env_top = thermo.virtemp(pp2, thermo.theta(pp2,
            interp.temp(pp2, prof), 1000.), interp.dwpt(pp2, prof))
        tdef2 = (thermo.virtemp(pp2, theta_parcel,
            thermo.temp_at_mixrat(blmr, pp2)) - tv_env_top) / \
            (thermo.ctok(tv_env_bot))

        lyre = G * (tdef1 + tdef2) / 2. * dz
        if lyre < 0: totn += lyre

    # Move the bottom layer to the top of the boundary layer
    if lower > pe2:
        lower = pe2
        pcl.blayer = lower

    # Calculate height of various temperature levels
    p0c = temp_lvl(0., prof)
    pm10c = temp_lvl(-10., prof)
    pm20c = temp_lvl(-20., prof)
    pm30c = temp_lvl(-30., prof)
    hgt0c = interp.hght(p0c, prof)
    hgtm10c = interp.hght(pm10c, prof)
    hgtm20c = interp.hght(pm20c, prof)
    hgtm30c = interp.hght(pm30c, prof)
    pcl.p0c = p0c
    pcl.pm10c = pm10c
    pcl.pm20c = pm20c
    pcl.pm30c = pm30c
    pcl.hght0c = hgt0c
    pcl.hghtm10c = hgtm10c
    pcl.hghtm20c = hgtm20c
    pcl.hghtm30c = hgtm30c

    # Find lowest observation in layer
    i = 0
    while prof.gSndg[i][prof.pind] > lower:
        if i == prof.gNumLevels-1: break
        i += 1
    while not QC(prof.gSndg[i][prof.tdind]):
        if i == prof.gNumLevels-1: break
        i += 1
    lptr = i
    if prof.gSndg[i][prof.pind] == lower:
        if i != prof.gNumLevels-1: lptr += 1

    # Find highest observation in layer
    i = prof.gNumLevels-1
    while prof.gSndg[i][prof.pind] < upper:
        if i < lptr: break
        i -= 1
    uptr = i
    if prof.gSndg[i][prof.pind] == upper:
        if i > lptr: uptr -= 1

    # START WITH INTERPOLATED BOTTOM LAYER
    # Begin moist ascent from lifted parcel LCL (pe2, tp2)
    pe1 = lower
    h1 = interp.hght(pe1, prof)
    te1 = interp.vtmp(pe1, prof)
    tp1 = thermo.wetlift(pe2, tp2, pe1)
    lyre = 0
    lyrlast = 0
    for i in range(lptr, prof.gNumLevels):
        if not QC(prof.gSndg[i][prof.tind]): continue
        pe2 = prof.gSndg[i][prof.pind]
        h2 = prof.gSndg[i][prof.zind]
        te2 = interp.vtmp(pe2, prof)
        tp2 = thermo.wetlift(pe1, tp1, pe2)
        tdef1 = (thermo.virtemp(pe1, tp1, tp1) - te1) / thermo.ctok(te1)
        tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / thermo.ctok(te2)
        lyrlast = lyre
        lyre = G * (tdef1 + tdef2) / 2. * (h2 - h1)

        # Add layer energy to total positive if lyre > 0
        if lyre > 0: totp += lyre
        # Add layer energy to total negative if lyre < 0, only up to EL
        else:
            if pe2 > 500.: totn += lyre

        # Check for Max LI
        mli = thermo.virtemp(pe2, tp2, tp2) - te2
        if  mli > li_max:
            li_max = mli
            li_maxpres = pe2

        # Check for Max Cap Strength
        mcap = te2 - mli
        if mcap > cap_strength:
            cap_strength = mcap
            cap_strengthpres = pe2

        tote += lyre
        pelast = pe1
        pe1 = pe2
        h1 = h2
        te1 = te2
        tp1 = tp2

        # Is this the top of the specified layer
        if i >= uptr and not QC(pcl.bplus):
            pe3 = pe1
            h3 = h1
            te3 = te1
            tp3 = tp1
            lyrf = lyre
            if lyrf > 0:
                pcl.bplus = totp - lyrf
                pcl.bminus = totn
            else:
                pcl.bplus = totp
                if pe2 > 500.: pcl.bminus = totn + lyrf
                else: pcl.bminus = totn
            pe2 = upper
            h2 = interp.hght(pe2, prof)
            te2 = interp.vtmp(pe2, prof)
            tp2 = thermo.wetlift(pe3, tp3, pe2)
            tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / thermo.ctok(te3)
            tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / thermo.ctok(te2)
            lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3)
            if lyrf > 0: pcl.bplus += lyrf
            else:
                if pe2 > 500.: pcl.bminus += lyrf
            if pcl.bplus == 0: pcl.bminus = 0.

        # Is this the freezing level
        if te2 < 0. and not QC(pcl.bfzl):
            pe3 = pelast
            h3 = interp.hght(pe3, prof)
            te3 = interp.vtmp(pe3, prof)
            tp3 = thermo.wetlift(pe1, tp1, pe3)
            lyrf = lyre
            if lyrf > 0.: pcl.bfzl = totp - lyrf
            else: pcl.bfzl = totp
            if not QC(p0c) or p0c > pe3:
                pcl.bfzl = 0
            elif QC(pe2):
                te2 = interp.vtmp(pe2, prof)
                tp2 = thermo.wetlift(pe3, tp3, pe2)
                tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                    thermo.ctok(te3)
                tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                    thermo.ctok(te2)
                lyrf = G * (tdef3 + tdef2) / 2. * (hgt0c - h3)
                if lyrf > 0: pcl.bfzl += lyrf

        # Is this the -10C level
        if te2 < -10. and not QC(pcl.wm10c):
            pe3 = pelast
            h3 = interp.hght(pe3, prof)
            te3 = interp.vtmp(pe3, prof)
            tp3 = thermo.wetlift(pe1, tp1, pe3)
            lyrf = lyre
            if lyrf > 0.: pcl.wm10c = totp - lyrf
            else: pcl.wm10c = totp
            if not QC(pm10c) or pm10c > pcl.lclpres:
                pcl.wm10c = 0
            elif QC(pe2):
                te2 = interp.vtmp(pe2, prof)
                tp2 = thermo.wetlift(pe3, tp3, pe2)
                tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                    thermo.ctok(te3)
                tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                    thermo.ctok(te2)
                lyrf = G * (tdef3 + tdef2) / 2. * (hgtm10c - h3)
                if lyrf > 0: pcl.wm10c += lyrf

        # Is this the -20C level
        if te2 < -20. and not QC(pcl.wm20c):
            pe3 = pelast
            h3 = interp.hght(pe3, prof)
            te3 = interp.vtmp(pe3, prof)
            tp3 = thermo.wetlift(pe1, tp1, pe3)
            lyrf = lyre
            if lyrf > 0.: pcl.wm20c = totp - lyrf
            else: pcl.wm20c = totp
            if not QC(pm20c) or pm20c > pcl.lclpres:
                pcl.wm20c = 0
            elif QC(pe2):
                te2 = interp.vtmp(pe2, prof)
                tp2 = thermo.wetlift(pe3, tp3, pe2)
                tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                    thermo.ctok(te3)
                tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                    thermo.ctok(te2)
                lyrf = G * (tdef3 + tdef2) / 2. * (hgtm20c - h3)
                if lyrf > 0: pcl.wm20c += lyrf

        # Is this the -30C level
        if te2 < -30. and not QC(pcl.wm30c):
            pe3 = pelast
            h3 = interp.hght(pe3, prof)
            te3 = interp.vtmp(pe3, prof)
            tp3 = thermo.wetlift(pe1, tp1, pe3)
            lyrf = lyre
            if lyrf > 0.: pcl.wm30c = totp - lyrf
            else: pcl.wm30c = totp
            if not QC(pm30c) or pm30c > pcl.lclpres:
                pcl.wm30c = 0
            elif QC(pe2):
                te2 = interp.vtmp(pe2, prof)
                tp2 = thermo.wetlift(pe3, tp3, pe2)
                tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                    thermo.ctok(te3)
                tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                    thermo.ctok(te2)
                lyrf = G * (tdef3 + tdef2) / 2. * (hgtm30c - h3)
                if lyrf > 0: pcl.wm30c += lyrf

        # Is this the 3km level
        if pcl.lclhght < 3000.:
            h = interp.agl(interp.hght(pe2, prof), prof)
            if h >= 3000. and not QC(pcl.b3km):
                pe3 = pelast
                h3 = interp.hght(pe3, prof)
                te3 = interp.vtmp(pe3, prof)
                tp3 = thermo.wetlift(pe1, tp1, pe3)
                lyrf = lyre
                if lyrf > 0: pcl.b3km = totp - lyrf
                else: pcl.b3km = totp
                h2 = interp.msl(3000., prof)
                pe2 = interp.pres(h2, prof)
                if QC(pe2):
                    te2 = interp.vtmp(pe2, prof)
                    tp2 = thermo.wetlift(pe3, tp3, pe2)
                    tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                        thermo.ctok(te3)
                    tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                        thermo.ctok(te2)
                    lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3)
                    if lyrf > 0: pcl.b3km += lyrf
        else: pcl.b3km = 0.

        # Is this the 6km level
        if pcl.lclhght < 6000.:
            h = interp.agl(interp.hght(pe2, prof), prof)
            if h >= 6000. and not QC(pcl.b6km):
                pe3 = pelast
                h3 = interp.hght(pe3, prof)
                te3 = interp.vtmp(pe3, prof)
                tp3 = thermo.wetlift(pe1, tp1, pe3)
                lyrf = lyre
                if lyrf > 0: pcl.b6km = totp - lyrf
                else: pcl.b6km = totp
                h2 = interp.msl(6000., prof)
                pe2 = interp.pres(h2, prof)
                if QC(pe2):
                    te2 = interp.vtmp(pe2, prof)
                    tp2 = thermo.wetlift(pe3, tp3, pe2)
                    tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                        thermo.ctok(te3)
                    tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                        thermo.ctok(te2)
                    lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3)
                    if lyrf > 0: pcl.b6km += lyrf
        else: pcl.b6km = 0.

        # LFC Possibility
        if lyre >= 0. and lyrlast <= 0.:
            tp3 = tp1
            te3 = te1
            pe2 = pe1
            pe3 = pelast
            while interp.vtmp(pe3, prof) > thermo.virtemp(pe3,
                thermo.wetlift(pe2, tp3, pe3), thermo.wetlift(pe2, tp3, pe3)):
                    pe3 -= 5
            pcl.lfcpres = pe3
            pcl.lfchght = interp.agl(interp.hght(pe3, prof), prof)
            cinh_old = totn
            tote = 0.
            pcl.elpres = RMISSD
            li_max = RMISSD

            if cap_strength < 0.: cap_strength = 0.
            pcl.cap = cap_strength
            pcl.cappres = cap_strengthpres
            # Hack to force LFC to be at least at the LCL
            if pcl.lfcpres > pcl.lclpres:
                pcl.lfcpres = pcl.lclpres
                pcl.lfchght = pcl.lclhght

        # EL Possibility
        if lyre <= 0. and lyrlast >= 0.:
            tp3 = tp1
            te3 = te1
            pe2 = pe1
            pe3 = pelast
            while interp.vtmp(pe3, prof) < thermo.virtemp(pe3,
                thermo.wetlift(pe2, tp3, pe3), thermo.wetlift(pe2, tp3, pe3)):
                    pe3 -= 5
            pcl.elpres = pe3
            pcl.elhght = interp.agl(interp.hght(pe3, prof), prof)
            pcl.mplpres = RMISSD
            pcl.limax = -li_max
            pcl.limaxpress = li_maxpres

        # MPL Possibility
        if tote < 0. and not QC(pcl.mplpres) and QC(pcl.elpres):
            pe3 = pelast
            h3 = interp.hght(pe3, prof)
            te3 = interp.vtmp(pe3, prof)
            tp3 = thermo.wetlift(pe1, tp1, pe3)
            totx = tote - lyre
            pe2 = pelast
            while totx > 0:
                pe2 -= 1
                te2 = interp.vtmp(pe2, prof)
                tp2 = thermo.wetlift(pe3, tp3, pe2)
                h2 = interp.hght(pe2, prof)
                tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \
                    thermo.ctok(te3)
                tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \
                    thermo.ctok(te2)
                lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3)
                totx += lyrf
                tp3 = tp2
                te3 = te2
                pe3 = pe2
            pcl.mplpres = pe2
            pcl.mplhght = interp.agl(interp.hght(pe2, prof), prof)

        # 500 hPa Lifted Index
        if prof.gSndg[i][prof.pind] <= 500. and pcl.li5 == RMISSD:
            a = interp.vtmp(500., prof)
            b = thermo.wetlift(pe1, tp1, 500.)
            pcl.li5 = a - thermo.virtemp(500, b, b)

        # 300 hPa Lifted Index
        if prof.gSndg[i][prof.pind] <= 300. and pcl.li3 == RMISSD:
            a = interp.vtmp(300., prof)
            b = thermo.wetlift(pe1, tp1, 300.)
            pcl.li3 = a - thermo.virtemp(300, b, b)

    # Calculate BRN if available
    pcl = bulk_rich(pcl, prof)

    pcl.bminus = cinh_old
    if pcl.bplus == 0: pcl.bminus = 0.
    return pcl