def lapse_rate(lower, upper, prof, pres=1): ''' Calculates the lapse rate (C/km) from a profile object Inputs ------ lower (float) Lower Bound of lapse rate upper (float) Upper Bound of lapse rate prof (profile object) Profile Object pres (int 0/1) Flag to know to convert pres to height Returns ------- lapse rate (float [C/km]) ''' if pres: p1 = lower p2 = upper z1 = interp.hght(lower, prof) z2 = interp.hght(upper, prof) else: z1 = interp.msl(lower, prof) z2 = interp.msl(upper, prof) p1 = interp.pres(z1, prof) p2 = interp.pres(z2, prof) tv1 = interp.vtmp(p1, prof) tv2 = interp.vtmp(p2, prof) if not QC(tv1) or not QC(tv2): return RMISSD return (tv2 - tv1) / (z2 - z1) * -1000.
def non_parcel_bunkers_motion(prof): ''' Compute the Bunkers Storm Motion for a Right Moving Supercell Inputs ------ prof (profile object) Profile Object Returns ------- rstu (float) Right Storm Motion U-component rstv (float) Right Storm Motion V-component lstu (float) Left Storm Motion U-component lstv (float) Left Storm Motion V-component ''' d = MS2KTS(7.5) # Deviation value emperically derived as 7.5 m/s msl6km = interp.msl(6000., prof) p6km = interp.pres(msl6km, prof) # SFC-6km Mean Wind mnu6, mnv6 = mean_wind_npw(prof.gSndg[prof.sfc][prof.pind], p6km, prof, 20) # SFC-6km Shear Vector shru6, shrv6 = wind_shear(prof.gSndg[prof.sfc][prof.pind], p6km, prof) # Bunkers Right Motion tmp = d / vector.comp2vec(shru6, shrv6)[1] rstu = mnu6 + (tmp * shrv6) rstv = mnv6 - (tmp * shru6) lstu = mnu6 - (tmp * shrv6) lstv = mnv6 + (tmp * shru6) return rstu, rstv, lstu, lstv
def corfidi_mcs_motion(prof): ''' Calculated the Meso-beta Elements (Corfidi) Vectors Inputs ------ prof (profile object) Profile Object Returns ------- upu (float) U-component of the upshear vector upv (float) V-component of the upshear vector dnu (float) U-component of the downshear vector dnv (float) V-component of the downshear vector ''' # Compute the tropospheric (850hPa-300hPa) mean wind mnu1, mnv1 = mean_wind_npw(850., 300., prof) # Compute the low-level (SFC-1500m) mean wind p_1p5km = interp.pres(interp.msl(1500., prof), prof) mnu2, mnv2 = mean_wind_npw(prof.gSndg[prof.sfc][prof.pind], p_1p5km, prof) # Compute the upshear vector upu = mnu1 - mnu2 upv = mnv1 - mnv2 # Compute the downshear vector dnu = mnu1 + upu dnv = mnv1 + upv return upu, upv, dnu, dnv
def bulk_rich(pcl, prof): ''' Calculates the Bulk Richardson Number for a given parcel. Inputs ------ pcl (parcel object) Parcel Object prof (profile object) Profile Object Returns ------- Bulk Richardson Number ''' # Make sure parcel is initialized if pcl.lplvals.flag == RMISSD: pbot = RMISSD elif pcl.lplvals.flag > 0 and pcl.lplvals.flag < 4: ptop = interp.pres(interp.msl(6000., prof), prof) pbot = prof.gSndg[prof.sfc][prof.pind] else: h0 = interp.hght(pcl.pres, prof) try: pbot = interp.pres(h0 - 500., prof) except: pbot = RMISSD if not QC(pbot): pbot = prof.gSndg[prof.sfc][prof.pind] h1 = interp.hght(pbot, prof) ptop = interp.pres(h1 + 6000., prof) if not QC(pbot) or not QC(ptop): pcl.brnshear = RMISSD pcl.brn = RMISSD return pcl # Calculate lowest 500m mean wind p = interp.pres(interp.hght(pbot, prof) + 500., prof) mnlu, mnlv = winds.mean_wind(pbot, p, prof) # Calculate the 6000m mean wind mnuu, mnuv = winds.mean_wind(pbot, ptop, prof) # Make sure CAPE and Shear are available if not QC(pcl.bplus) or not QC(mnlu) or not QC(mnuu): pcl.brnshear = RMISSD pcl.brn = RMISSD return pcl # Calculate shear between levels dx = mnuu - mnlu dy = mnuv - mnlv pcl.brnshear = KTS2MS(vector.comp2vec(dx, dy)[1]) pcl.brnshear = pcl.brnshear**2 / 2. pcl.brn = pcl.bplus / pcl.brnshear return pcl
def bunkers_storm_motion(prof, pbot=None, **kwargs): ''' Compute the Bunkers Storm Motion for a Right Moving Supercell using a parcel based approach. Inputs ------ prof (profile object) Profile Object pbot (float) Base of effective-inflow layer (hPa) Returns ------- rstu (float) Right Storm Motion U-component rstv (float) Right Storm Motion V-component lstu (float) Left Storm Motion U-component lstv (float) Left Storm Motion V-component ''' d = MS2KTS(7.5) # Deviation value emperically derived as 7.5 m/s # If MUPCL provided, use it, otherwise create MUPCL if 'mupcl' in kwargs: mupcl = kwargs.get('mupcl') else: mulplvals = params.DefineParcel(3, prof, pres=400) mupcl = params.parcelx(-1, -1, mulplvals.pres, mulplvals.temp, mulplvals.dwpt, prof, lplvals=mulplvals) mucape = mupcl.bplus mucinh = mupcl.bminus muel = mupcl.elhght if not pbot: pbot, ptop = effective_inflow_layer(100, -250, prof) base = interp.agl(interp.hght(pbot, prof), prof) if mucape > 100. and QC(muel) and base >= 750: depth = muel - base htop = base + depth / 2. ptop = interp.pres(interp.msl(base + htop, prof), prof) mnu, mnv = winds.mean_wind_npw(pbot, ptop, prof) sru, srv = winds.wind_shear(pbot, ptop, prof) srmag = vector.mag(sru, srv) uchg = d / srmag * srv vchg = d / srmag * sru rstu = mnu + uchg rstv = mnv - vchg lstu = mnu - uchg lstv = mnv + vchg else: rstu, rstv, lstu, lstv = winds.non_parcel_bunkers_motion(prof) return rstu, rstv, lstu, lstv
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
def helicity(lower, upper, prof, stu=0, stv=0): ''' Calculates the relative helicity (m2/s2) of a layer from lower to upper. If storm-motion vector is supplied, storm-relative helicity, both positve and negative, is returned. Inputs ------ lower (float) Bottom level of layer (m, AGL) upper (float) Top level of layer (m, AGL) prof (profile object) Profile Object stu (float; optional) U-component of storm-motion stv (float; optional) V-component of storm-motion Returns ------- phel+nhel (float) Combined Helicity (m2/s2) phel (float) Positive Helicity (m2/s2) nhel (float) Negative Helicity (m2/s2) ''' lower = interp.msl(lower, prof) upper = interp.msl(upper, prof) plower = interp.pres(lower, prof) pupper = interp.pres(upper, prof) phel = 0 nhel = 0 # Find lower and upper ind bounds for looping i = 0 while interp.msl(prof.gSndg[i][prof.zind], prof) < lower: i += 1 lptr = i if interp.msl(prof.gSndg[i][prof.zind], prof) == lower: lptr += 1 while interp.msl(prof.gSndg[i][prof.zind], prof) <= upper: i += 1 uptr = i if interp.msl(prof.gSndg[i][prof.zind], prof) == upper: uptr -= 1 # Integrate from interpolated bottom level to iptr level sru1, srv1 = interp.components(plower, prof) sru1 = KTS2MS(sru1 - stu) srv1 = KTS2MS(srv1 - stv) # Loop through levels for i in range(lptr, uptr + 1): if QC(prof.gSndg[i][prof.uind]) and QC(prof.gSndg[i][prof.vind]): sru2, srv2 = interp.components(prof.gSndg[i][prof.pind], prof) sru2 = KTS2MS(sru2 - stu) srv2 = KTS2MS(srv2 - stv) lyrh = (sru2 * srv1) - (sru1 * srv2) if lyrh > 0: phel += lyrh else: nhel += lyrh sru1 = sru2 srv1 = srv2 # Integrate from tptr level to interpolated top level sru2, srv2 = interp.components(pupper, prof) sru2 = KTS2MS(sru2 - stu) srv2 = KTS2MS(srv2 - stv) lyrh = (sru2 * srv1) - (sru1 * srv2) if lyrh > 0: phel += lyrh else: nhel += lyrh return phel + nhel, phel, nhel
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
def helicity(lower, upper, prof, stu=0, stv=0): ''' Calculates the relative helicity (m2/s2) of a layer from lower to upper. If storm-motion vector is supplied, storm-relative helicity, both positve and negative, is returned. Inputs ------ lower (float) Bottom level of layer (m, AGL) upper (float) Top level of layer (m, AGL) prof (profile object) Profile Object stu (float; optional) U-component of storm-motion stv (float; optional) V-component of storm-motion Returns ------- phel+nhel (float) Combined Helicity (m2/s2) phel (float) Positive Helicity (m2/s2) nhel (float) Negative Helicity (m2/s2) ''' lower = interp.msl(lower, prof) upper = interp.msl(upper, prof) plower = interp.pres(lower, prof) pupper = interp.pres(upper, prof) phel = 0 nhel = 0 # Find lower and upper ind bounds for looping i = 0 while interp.msl(prof.gSndg[i][prof.zind], prof) < lower: i+=1 lptr = i if interp.msl(prof.gSndg[i][prof.zind], prof) == lower: lptr+=1 while interp.msl(prof.gSndg[i][prof.zind], prof) <= upper: i+=1 uptr = i if interp.msl(prof.gSndg[i][prof.zind], prof) == upper: uptr-=1 # Integrate from interpolated bottom level to iptr level sru1, srv1 = interp.components(plower, prof) sru1 = KTS2MS(sru1 - stu) srv1 = KTS2MS(srv1 - stv) # Loop through levels for i in range(lptr, uptr+1): if QC(prof.gSndg[i][prof.uind]) and QC(prof.gSndg[i][prof.vind]): sru2, srv2 = interp.components(prof.gSndg[i][prof.pind], prof) sru2 = KTS2MS(sru2 - stu) srv2 = KTS2MS(srv2 - stv) lyrh = (sru2 * srv1) - (sru1 * srv2) if lyrh > 0: phel += lyrh else: nhel += lyrh sru1 = sru2 srv1 = srv2 # Integrate from tptr level to interpolated top level sru2, srv2 = interp.components(pupper, prof) sru2 = KTS2MS(sru2 - stu) srv2 = KTS2MS(srv2 - stv) lyrh = (sru2 * srv1) - (sru1 * srv2) if lyrh > 0: phel += lyrh else: nhel += lyrh return phel+nhel, phel, nhel