def convert_specific_humidity_to_mixing_ratio(Q):
    from metpy.calc import mixing_ratio_from_specific_humidity
    from metpy.units import units
    try:
        q_unit = Q.attrs['units']
    except KeyError:
        q_unit = 'g/kg'
    q_values = Q.values * units(q_unit)
    MR = mixing_ratio_from_specific_humidity(q_values)
    da = Q.copy(data=MR.magnitude)
    da.attrs['units'] = q_unit
    da.attrs['long_name'] = 'Mixing Ratio'
    return da
Beispiel #2
0
def virt_temp(t, q):
    """
    Compute virtual temperature in [K] from temperature and
    specific humidity.

    Arguments:
    t -- temperature in [K]
    q -- specific humidity in [kg/kg]

    t and q can be scalars of NumPy arrays. They just have to either all
    scalars, or all arrays.

    Returns: Virtual temperature in [K]. Same dimension as input fields.
    """
    t = units.Quantity(t, "K")
    mix_rat = mpcalc.mixing_ratio_from_specific_humidity(q)
    v_temp = mpcalc.virtual_temperature(t, mix_rat)
    return v_temp
Beispiel #3
0
def test_mixing_ratio_from_specific_humidity():
    """Tests mixing ratio from specific humidity."""
    q = 0.012
    w = mixing_ratio_from_specific_humidity(q)
    assert_almost_equal(w, 0.01215, 3)
Beispiel #4
0
def test_mixing_ratio_from_specific_humidity():
    """Test mixing ratio from specific humidity."""
    q = 0.012 * units.dimensionless
    w = mixing_ratio_from_specific_humidity(q)
    assert_almost_equal(w, 0.01215, 3)
Beispiel #5
0
def get_density_vertical_velocity_and_omega(circle):

    den_m = [None] * len(circle.sounding)

    for n in range(len(circle.sounding)):
        if len(circle.isel(sounding=n).sonde_id.values) > 1:
            mr = mpcalc.mixing_ratio_from_specific_humidity(
                circle.isel(sounding=n).q_sounding.values
            )
            den_m[n] = mpcalc.density(
                circle.isel(sounding=n).p_sounding.values * units.Pa,
                circle.isel(sounding=n).ta_sounding.values * units.kelvin,
                mr,
            ).magnitude
        else:
            den_m[n] = np.nan

    circle["density"] = (["sounding", "circle", "alt"], den_m)
    circle["mean_density"] = (["circle", "alt"], np.nanmean(den_m, axis=0))

    D = circle.D.values
    mean_den = circle.mean_density

    nan_ids = np.where(np.isnan(D) == True)  # [0]

    w_vel = np.full([len(circle["circle"]), len(circle.alt)], np.nan)
    p_vel = np.full([len(circle["circle"]), len(circle.alt)], np.nan)

    w_vel[:, 0] = 0
    # last = 0

    for cir in range(len(circle["circle"])):
        last = 0
        for m in range(1, len(circle.alt)):

            if (
                len(
                    np.intersect1d(
                        np.where(nan_ids[1] == m)[0], np.where(nan_ids[0] == cir)[0]
                    )
                )
                > 0
            ):

                ids_for_nan_ids = np.intersect1d(
                    np.where(nan_ids[1] == m)[0], np.where(nan_ids[0] == cir)[0]
                )
                w_vel[nan_ids[0][ids_for_nan_ids], nan_ids[1][ids_for_nan_ids]] = np.nan

            else:
                w_vel[cir, m] = w_vel[cir, last] - circle.D.isel(circle=cir).isel(
                    alt=m
                ).values * 10 * (m - last)
                last = m

        for n in range(1, len(circle.alt)):

            p_vel[cir, n] = (
                -circle.mean_density.isel(circle=cir).isel(alt=n)
                * 9.81
                * w_vel[cir, n]
                # * 60
                # * 60
                # / 100
            )

    circle["W"] = (["circle", "alt"], w_vel)
    circle["omega"] = (["circle", "alt"], p_vel)

    return print("Finished estimating density, W and omega ...")
Beispiel #6
0
def test_mixing_ratio_from_specific_humidity():
    """Test mixing ratio from specific humidity."""
    q = 0.012 * units.dimensionless
    w = mixing_ratio_from_specific_humidity(q)
    assert_almost_equal(w, 0.01215, 3)
Beispiel #7
0
     ws = df['speed'].to_numpy()
     ws = ws * units.knot
     u = df['u_wind'].to_numpy()
     u = u * units.knot
     v = df['v_wind'].to_numpy()
     v = v * units.knot
     rh = mpcalc.relative_humidity_from_dewpoint(t,td)
     rh = rh * units.percent
     rh = rh * 100.
     
     #compute mixing ratio, pot. temp, spec. humidity, virtual pot. temp. 
     pt = mpcalc.potential_temperature(p,t) 
     # this is written for metpy 1.0, older versions of metpy want the arguments reversed in the below q to dp function
     # check your version with metpy.__version__
     q = mpcalc.specific_humidity_from_dewpoint(p,td) 
     mr = mpcalc.mixing_ratio_from_specific_humidity(q)
     vpt = mpcalc.virtual_potential_temperature(p, t, mr)
     mr =  mr * 1000 * (units.gram/units.kilogram) #adjust units for viz
 
 # if using NCAR research sondes
 if grade == 1:
     file_path = '/Users/elizabeth.smith/Documents/CHEESEHEAD/sondes/'
     snd_name = 'CHEESEHEAD_ISS1_RS41_v1_'+f"{date:%Y%m%d_%H}"
     snd_file = glob.glob(file_path+snd_name+'*.nc')
     if len(snd_file)<1: #glob found none
         #print("Missing obs on "+f"{date:%Y%m%d_%H}")
         continue
     else:
         snd_file = np.sort(glob.glob(file_path+snd_name+'*.nc'))[0]
         print("Found obs on "+f"{date:%Y%m%d_%H}")
         ### JTC 4/8/21  Adding full date out to seconds and 
Beispiel #8
0
    def __init__(self, datea, fhr, atcf, config):
        # Forecast fields to compute

        wnd_lev_1 = [250, 500]
        wnd_lev_2 = [350, 500]
        n_wnd_lev = len(wnd_lev_1)

        # Read steering flow parameters, or use defaults
        steerp1 = float(config['fields'].get('steer_level1', '300'))
        steerp2 = float(config['fields'].get('steer_level2', '850'))
        tcradius = float(config['fields'].get('steer_radius', '333'))

        # lat_lon info
        lat1 = float(config['fields'].get('min_lat', '0.'))
        lat2 = float(config['fields'].get('max_lat', '65.'))
        lon1 = float(config['fields'].get('min_lon', '-180.'))
        lon2 = float(config['fields'].get('max_lon', '-10.'))

        if not 'min_lat' in config:
            config.update({'min_lat': lat1})
            config.update({'max_lat': lat2})
            config.update({'min_lon': lon1})
            config.update({'max_lon': lon2})

        self.fhr = fhr
        self.atcf_files = atcf.atcf_files
        self.config = config
        self.nens = int(len(self.atcf_files))
        df_files = {}
        self.datea_str = datea
        self.datea = dt.datetime.strptime(datea, '%Y%m%d%H')
        self.datea_s = self.datea.strftime("%m%d%H%M")
        self.fff = str(self.fhr + 1000)[1:]
        datea_1 = self.datea + dt.timedelta(hours=self.fhr)
        datea_1 = datea_1.strftime("%m%d%H%M")

        self.dpp = importlib.import_module(config['io_module'])

        logging.warning("Computing hour {0} ensemble fields".format(self.fff))

        #  Obtain the ensemble lat/lon information, replace missing values with mean
        self.ens_lat, self.ens_lon = atcf.ens_lat_lon_time(self.fhr)

        e_cnt = 0
        m_lat = 0.0
        m_lon = 0.0
        for n in range(self.nens):
            if self.ens_lat[n] != atcf.missing and self.ens_lon[
                    n] != atcf.missing:
                e_cnt = e_cnt + 1
                m_lat = m_lat + self.ens_lat[n]
                m_lon = m_lon + self.ens_lon[n]

        if e_cnt > 0:
            m_lon = m_lon / e_cnt
            m_lat = m_lat / e_cnt

            for n in range(self.nens):
                if self.ens_lat[n] == atcf.missing or self.ens_lon[
                        n] == atcf.missing:
                    self.ens_lat[n] = m_lat
                    self.ens_lon[n] = m_lon

        #  Read grib file information for this forecast hour
        g1 = self.dpp.ReadGribFiles(self.datea_str, self.fhr, self.config)

        dencode = {
            'ensemble_data': {
                'dtype': 'float32'
            },
            'latitude': {
                'dtype': 'float32'
            },
            'longitude': {
                'dtype': 'float32'
            },
            'ensemble': {
                'dtype': 'int32'
            }
        }

        #  Compute steering wind components
        uoutfile = '{0}/{1}_f{2}_usteer_ens.nc'.format(config['work_dir'],
                                                       str(self.datea_str),
                                                       self.fff)
        voutfile = '{0}/{1}_f{2}_vsteer_ens.nc'.format(config['work_dir'],
                                                       str(self.datea_str),
                                                       self.fff)
        if (not os.path.isfile(uoutfile)
                or not os.path.isfile(voutfile)) and config['fields'].get(
                    'calc_uvsteer', 'True') == 'True':

            logging.warning("  Computing steering wind information")

            inpDict = {'isobaricInhPa': (steerp1, steerp2)}
            inpDict = g1.set_var_bounds('zonal_wind', inpDict)

            #  Create output arrays
            outDict = {
                'latitude': (lat1, lat2),
                'longitude': (lon1, lon2),
                'description': 'zonal steering wind',
                'units': 'm/s',
                '_FillValue': -9999.
            }
            outDict = g1.set_var_bounds('zonal_wind', outDict)
            uensmat = g1.create_ens_array('zonal_wind', self.nens, outDict)

            outDict = {
                'latitude': (lat1, lat2),
                'longitude': (lon1, lon2),
                'description': 'meridional steering wind',
                'units': 'm/s',
                '_FillValue': -9999.
            }
            outDict = g1.set_var_bounds('meridional_wind', outDict)
            vensmat = g1.create_ens_array('meridional_wind', self.nens,
                                          outDict)

            outDict = {
                'latitude': (lat1, lat2),
                'longitude': (lon1, lon2),
                'description': 'steering wind vorticity',
                'units': '1/s',
                '_FillValue': -9999.
            }
            outDict = g1.set_var_bounds('zonal_wind', outDict)
            vortmat = g1.create_ens_array('zonal_wind', self.nens, outDict)

            wencode = {
                'latitude': {
                    'dtype': 'float32'
                },
                'longitude': {
                    'dtype': 'float32'
                }
            }

            for n in range(self.nens):

                #  Read global zonal and meridional wind, write to file
                uwnd = g1.read_grib_field('zonal_wind', n, inpDict).rename('u')
                vwnd = g1.read_grib_field('meridional_wind', n,
                                          inpDict).rename('v')

                #             print(uwnd[:,0,0])
                #             print(vwnd[:,0,0])
                #             sys.exit(2)

                uwnd.to_netcdf('wind_info.nc',
                               mode='w',
                               encoding=wencode,
                               format='NETCDF3_CLASSIC')
                vwnd.to_netcdf('wind_info.nc',
                               mode='a',
                               encoding=wencode,
                               format='NETCDF3_CLASSIC')

                latvec = uwnd.latitude.values
                lonvec = uwnd.longitude.values

                if e_cnt > 0:

                    latcen = latvec[np.abs(latvec - self.ens_lat[n]).argmin()]
                    loncen = lonvec[np.abs(lonvec - self.ens_lon[n]).argmin()]

                    #  Call NCL to remove TC winds, read result from file
                    os.system('ncl -Q {0}/tc_steer.ncl tclat={1} tclon={2} tcradius={3}'.format(config['script_dir'],\
                                          str(latcen), str(loncen), str(tcradius)))

                    wfile = nc.Dataset('wind_info.nc')
                    uwnd[:, :, :] = wfile.variables['u'][:, :, :]
                    vwnd[:, :, :] = wfile.variables['v'][:, :, :]

                    os.remove('wind_info.nc')

                #  Integrate the winds over the layer to obtain the steering wind
                pres, lat, lon = uwnd.indexes.values()
                nlev = len(pres)

                uint = uwnd[0, :, :]
                uint[:, :] = 0.0
                vint = vwnd[0, :, :]
                vint[:, :] = 0.0

                for k in range(nlev - 1):

                    uint[:, :] = uint[:, :] + 0.5 * (uwnd[k, :, :] + uwnd[
                        k + 1, :, :]) * abs(pres[k + 1] - pres[k])
                    vint[:, :] = vint[:, :] + 0.5 * (vwnd[k, :, :] + vwnd[
                        k + 1, :, :]) * abs(pres[k + 1] - pres[k])

#             if pres[0] > pres[-1]:
#               uint = -np.trapz(uwnd[:,:,:], pres, axis=0) / abs(pres[-1]-pres[0])
#               vint = -np.trapz(vwnd[:,:,:], pres, axis=0) / abs(pres[-1]-pres[0])
#             else:
#               uint = np.trapz(uwnd[:,:,:], pres, axis=0) / abs(pres[-1]-pres[0])
#               vint = np.trapz(vwnd[:,:,:], pres, axis=0) / abs(pres[-1]-pres[0])

                if lat[0] > lat[-1]:
                    slat1 = lat2
                    slat2 = lat1
                else:
                    slat1 = lat1
                    slat2 = lat2

                #  Write steering flow to ensemble arrays
                uensmat[n, :, :] = np.squeeze(
                    uint.sel(latitude=slice(slat1, slat2),
                             longitude=slice(lon1,
                                             lon2))) / abs(pres[-1] - pres[0])
                vensmat[n, :, :] = np.squeeze(
                    vint.sel(latitude=slice(slat1, slat2),
                             longitude=slice(lon1,
                                             lon2))) / abs(pres[-1] - pres[0])

                #  Compute the vorticity associated with the steering wind

#             circ = VectorWind(unew, vnew).vorticity() * 1.0e5

#             vortmat[n,:,:] = np.squeeze(circ.sel(latitude=slice(lat2, lat1), longitude=slice(lon1, lon2)))

            uensmat.to_netcdf(uoutfile, encoding=dencode)
            vensmat.to_netcdf(voutfile, encoding=dencode)


#          vortmat.to_netcdf(vortfile, encoding=dencode)

        else:

            logging.warning("  Obtaining steering wind information from file")

        #  Read geopotential height from file, if ensemble file is not present
        if config['fields'].get('calc_height', 'True') == 'True':

            if 'height_levels' in config['fields']:
                height_list = json.loads(config['fields'].get('height_levels'))
            else:
                height_list = [500]

            for level in height_list:

                levstr = '%0.3i' % int(level)
                outfile = '{0}/{1}_f{2}_h{3}hPa_ens.nc'.format(
                    config['work_dir'], str(self.datea_str), self.fff, levstr)

                if not os.path.isfile(outfile):

                    logging.warning(
                        '  Computing {0} hPa height'.format(levstr))

                    vDict = {
                        'latitude': (lat1, lat2),
                        'longitude': (lon1, lon2),
                        'isobaricInhPa': (level, level),
                        'description': '{0} hPa height'.format(levstr),
                        'units': 'm',
                        '_FillValue': -9999.
                    }
                    vDict = g1.set_var_bounds('geopotential_height', vDict)
                    ensmat = g1.create_ens_array('geopotential_height',
                                                 g1.nens, vDict)

                    for n in range(g1.nens):
                        ensmat[n, :, :] = np.squeeze(
                            g1.read_grib_field('geopotential_height', n,
                                               vDict))

                    ensmat.to_netcdf(outfile, encoding=dencode)

                elif os.path.isfile(outfile):

                    logging.warning(
                        "  Obtaining {0} hPa height data from {1}".format(
                            levstr, outfile))

        #  Compute 250 hPa PV if the file does not exist
        outfile = '{0}/{1}_f{2}_pv250_ens.nc'.format(config['work_dir'],
                                                     str(self.datea_str),
                                                     self.fff)
        if (not os.path.isfile(outfile)
                and config['fields'].get('calc_pv250hPa', 'True') == 'True'):

            logging.warning("  Computing 250 hPa PV")

            vDict = {
                'latitude': (lat1, lat2),
                'longitude': (lon1, lon2),
                'isobaricInhPa': (200, 300),
                'description': '250 hPa Potential Vorticity',
                'units': 'PVU',
                '_FillValue': -9999.
            }
            vDict = g1.set_var_bounds('zonal_wind', vDict)

            ensmat = g1.create_ens_array('zonal_wind', self.nens, vDict)

            for n in range(self.nens):

                #  Read all the necessary files from file, smooth fields, so sensitivities are useful
                tmpk = g1.read_grib_field('temperature', n, vDict) * units('K')

                lats = tmpk.latitude.values * units('degrees')
                lons = tmpk.longitude.values * units('degrees')
                pres = tmpk.isobaricInhPa.values * units('hPa')

                tmpk = mpcalc.smooth_n_point(tmpk, 9, 4)

                thta = mpcalc.potential_temperature(pres[:, None, None], tmpk)

                uwnd = mpcalc.smooth_n_point(
                    g1.read_grib_field('zonal_wind', n, vDict) * units('m/s'),
                    9, 4)
                vwnd = mpcalc.smooth_n_point(
                    g1.read_grib_field('meridional_wind', n, vDict) *
                    units('m/s'), 9, 4)

                dx, dy = mpcalc.lat_lon_grid_deltas(lons,
                                                    lats,
                                                    x_dim=-1,
                                                    y_dim=-2,
                                                    geod=None)

                #  Compute PV and place in ensemble array
                pvout = mpcalc.potential_vorticity_baroclinic(
                    thta, pres[:, None, None], uwnd, vwnd, dx[None, :, :],
                    dy[None, :, :], lats[None, :, None])

                ensmat[n, :, :] = np.squeeze(pvout[np.where(
                    pres == 250 * units('hPa'))[0], :, :]) * 1.0e6

            ensmat.to_netcdf(outfile, encoding=dencode)

        elif os.path.isfile(outfile):

            logging.warning(
                "  Obtaining 250 hPa PV data from {0}".format(outfile))

        #  Compute the equivalent potential temperature (if desired and file is missing)
        if config['fields'].get('calc_theta-e', 'False') == 'True':

            if 'theta-e_levels' in config['fields']:
                thetae_list = json.loads(
                    config['fields'].get('theta-e_levels'))
            else:
                thetae_list = [700, 850]

            for level in thetae_list:

                levstr = '%0.3i' % int(level)
                outfile = '{0}/{1}_f{2}_e{3}hPa_ens.nc'.format(
                    config['work_dir'], str(self.datea_str), self.fff, levstr)

                if not os.path.isfile(outfile):

                    logging.warning(
                        '  Computing {0} hPa Theta-E'.format(levstr))

                    vDict = {
                        'latitude': (lat1, lat2),
                        'longitude': (lon1, lon2),
                        'isobaricInhPa': (level, level),
                        'description':
                        '{0} hPa Equivalent Potential Temperature'.format(
                            levstr),
                        'units':
                        'K',
                        '_FillValue':
                        -9999.
                    }
                    vDict = g1.set_var_bounds('temperature', vDict)

                    ensmat = g1.create_ens_array('temperature', g1.nens, vDict)

                    for n in range(g1.nens):

                        tmpk = g1.read_grib_field('temperature', n,
                                                  vDict) * units.K
                        pres = tmpk.isobaricInhPa.values * units.hPa

                        if g1.has_specific_humidity:
                            qvap = np.squeeze(
                                g1.read_grib_field('specific_humidity', n,
                                                   vDict))
                            tdew = mpcalc.dewpoint_from_specific_humidity(
                                pres[None, None], tmpk, qvap)
                        else:
                            relh = g1.read_grib_field('relative_humidity', n,
                                                      vDict)
                            relh = np.minimum(np.maximum(relh, 0.01),
                                              100.0) * units.percent
                            tdew = mpcalc.dewpoint_from_relative_humidity(
                                tmpk, relh)

                        ensmat[n, :, :] = np.squeeze(
                            mpcalc.equivalent_potential_temperature(
                                pres[None, None], tmpk, tdew))

                    ensmat.to_netcdf(outfile, encoding=dencode)

                elif os.path.isfile(outfile):

                    logging.warning(
                        "  Obtaining {0} hPa Theta-e data from {1}".format(
                            levstr, outfile))

        #  Compute the 500-850 hPa water vapor mixing ratio (if desired and file is missing)
        outfile = '{0}/{1}_f{2}_q500-850hPa_ens.nc'.format(
            config['work_dir'], str(self.datea_str), self.fff)
        if (not os.path.isfile(outfile) and config['fields'].get(
                'calc_q500-850hPa', 'False') == 'True'):

            logging.warning("  Computing 500-850 hPa Water Vapor")

            vDict = {
                'latitude': (lat1, lat2),
                'longitude': (lon1, lon2),
                'description': '500-850 hPa Integrated Water Vapor',
                'units': 'hPa',
                '_FillValue': -9999.
            }
            vDict = g1.set_var_bounds('temperature', vDict)

            ensmat = g1.create_ens_array('temperature', len(self.atcf_files),
                                         vDict)

            vDict = {
                'latitude': (lat1, lat2),
                'longitude': (lon1, lon2),
                'isobaricInhPa': (500, 850),
                'description': '500-850 hPa Integrated Water Vapor',
                'units': 'hPa',
                '_FillValue': -9999.
            }
            vDict = g1.set_var_bounds('temperature', vDict)

            for n in range(self.nens):

                tmpk = np.squeeze(g1.read_grib_field('temperature', n,
                                                     vDict)) * units('K')
                pres = (tmpk.isobaricInhPa.values * units.hPa).to(units.Pa)

                if g1.has_specific_humidity:
                    qvap = mpcalc.mixing_ratio_from_specific_humidity(
                        g1.read_grib_field('specific_humidity', n, vDict))
                else:
                    relh = np.minimum(
                        np.maximum(
                            g1.read_grib_field('relative_humidity', n, vDict),
                            0.01), 100.0) * units('percent')
                    qvap = mpcalc.mixing_ratio_from_relative_humidity(
                        pres[:, None, None], tmpk, relh)

                #  Integrate water vapor over the pressure levels
                ensmat[n, :, :] = np.abs(np.trapz(
                    qvap, pres, axis=0)) / mpcon.earth_gravity

            ensmat.to_netcdf(outfile, encoding=dencode)

        elif os.path.isfile(outfile):

            logging.warning(
                "  Obtaining 500-850 hPa water vapor data from {0}".format(
                    outfile))

        #  Compute wind-related forecast fields (if desired and file is missing)
        if config['fields'].get('calc_winds', 'False') == 'True':

            if 'wind_levels' in config['fields']:
                wind_list = json.loads(config['fields'].get('wind_levels'))
            else:
                wind_list = [850]

            for level in wind_list:

                levstr = '%0.3i' % int(level)
                ufile = '{0}/{1}_f{2}_u{3}hPa_ens.nc'.format(
                    config['work_dir'], str(self.datea_str), self.fff, levstr)
                vfile = '{0}/{1}_f{2}_v{3}hPa_ens.nc'.format(
                    config['work_dir'], str(self.datea_str), self.fff, levstr)

                if (not os.path.isfile(ufile)) or (not os.path.isfile(vfile)):

                    logging.warning(
                        '  Computing {0} hPa wind information'.format(levstr))

                    uDict = {
                        'latitude': (lat1, lat2),
                        'longitude': (lon1, lon2),
                        'isobaricInhPa': (level, level),
                        'description': '{0} hPa zonal wind'.format(levstr),
                        'units': 'm/s',
                        '_FillValue': -9999.
                    }
                    uDict = g1.set_var_bounds('zonal_wind', uDict)

                    uensmat = g1.create_ens_array('zonal_wind', g1.nens, uDict)

                    vDict = {
                        'latitude': (lat1, lat2),
                        'longitude': (lon1, lon2),
                        'isobaricInhPa': (level, level),
                        'description':
                        '{0} hPa meridional wind'.format(levstr),
                        'units': 'm/s',
                        '_FillValue': -9999.
                    }
                    vDict = g1.set_var_bounds('meridional_wind', vDict)

                    vensmat = g1.create_ens_array('meridional_wind', g1.nens,
                                                  vDict)

                    for n in range(g1.nens):

                        uwnd = g1.read_grib_field('zonal_wind', n,
                                                  uDict).squeeze()
                        vwnd = g1.read_grib_field('meridional_wind', n,
                                                  vDict).squeeze()

                        uensmat[n, :, :] = uwnd[:, :]
                        vensmat[n, :, :] = vwnd[:, :]

                    uensmat.to_netcdf(ufile, encoding=dencode)
                    vensmat.to_netcdf(vfile, encoding=dencode)

                elif os.path.isfile(outfile):

                    logging.warning(
                        "  Obtaining {0} hPa wind information from file".
                        format(levstr))