GenVxMask and Point-Stat: Computing PBLH from AMDAR data using “Theta-increase” method

model_applications/pbl/PointStat_fcstHRRR_obsAMDAR_PBLH_PyEmbed.conf

Scientific Objective

The Planetary Boundary Layer Height (PBLH) arises from a complex interaction of lower atmosphere and surface processes, and is therefore a useful metric to evaluate models. This PointStat use case computes PBLH from AMDAR aircraft data using the “Theta-increase” method (Nielsen-Gammon et al., 2008, J. App. Met. Clim.), which computes PBLH by finding the lowest altitude in an aircraft profile that exceeds a specified increase in potential temperature from a base value. Generally this theta-increase (pt_delta) ranges from 1.0-2.5 K. The pt_delta, list of airports to process, and sounding are specified in the configuration script.

Datasets

Forecast: HRRR, RRFS (reads the “HPBL” grib2 field) Observation: AMDAR hourly 1-d netcdf files

METplus Components

This use case utilizes GenVxMask and the METplus PointStat tool to compare PBLH from AMDAR data to model output. The python embedding script “calc_amdar_pblh.py” computes PBLH and sends data MET via python embedding. The configuration file also filters output through static geographic masks generated by GenVxMask.

METplus Workflow

GenVxMask and PointStat are called in this example. The following run times are processed:

Valid: 2022-07-01_20Z
Forecast lead: 12 hour

GenVxMask input file is a two-row text file (met_mask_AIRPORT.txt): row 1: AIRPORT row 2: lat lon GenVxMask output file is a netcdf file w/ geographic radius part of the file name (met_mask_AIRPORT_100km.nc) Input file provided in this example: (met_mask_DENVER.txt): row 1: DENVER row2: 39.856 -104.6764

# PointStat is run with Python embedding (calc_amdar_pblh.py).

METplus Configuration

METplus first loads all of the configuration files found in parm/metplus_config, then it loads any configuration files passed to METplus via the command line with the -c option, i.e. -c parm/use_cases/model_applications/pbl/PointStat_fcstHRRR_obsAMDAR_PBLH_PyEmbed.conf

[config]

# Documentation for this use case can be found at
# https://metplus.readthedocs.io/en/latest/generated/model_applications/pbl/PointStat_fcstHRRR_obsAMDAR_PBLH_PyEmbed.html

# For additional information, please see the METplus Users Guide.
# https://metplus.readthedocs.io/en/latest/Users_Guide

###
# Processes to run
# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#process-list
###

PROCESS_LIST = GenVxMask, PointStat

###
# Time Info
# LOOP_BY options are INIT, VALID, RETRO, and REALTIME
# If set to INIT or RETRO:
#   INIT_TIME_FMT, INIT_BEG, INIT_END, and INIT_INCREMENT must also be set
# If set to VALID or REALTIME:
#   VALID_TIME_FMT, VALID_BEG, VALID_END, and VALID_INCREMENT must also be set
# LEAD_SEQ is the list of forecast leads to process
# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#timing-control
###

LOOP_BY = INIT
INIT_TIME_FMT = %Y%m%d%H
INIT_BEG = 2022070108
INIT_END = 2022070108
INIT_INCREMENT = 1H

LEAD_SEQ = 12

# USER SETTINGS HERE
# CUSTOM_LOOP_LIST = DENVER, DALLAS, BOSTON, MINNEAPOLIS   [list of airports sepearated by commas; each needs a mask file]
# PY_SOUNDING_FLAG = ALL, ASC, DESC  [only one value here]
# PY_PT_DELTA =  Potential Temperature delta setting (K) [usually 1.0-2.5]
# Valid_Time(YYYYMMDD_HHMMSS)
GEN_VX_MASK_AIRPORT_RADIUS_KM = 100
CUSTOM_LOOP_LIST = DENVER
PY_SOUNDING_FLAG = ALL  
PY_PT_DELTA = 1.25
PY_VAL_TIME = {valid?fmt=%Y%m%d_%H0000}

###
# File I/O
# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#directory-and-filename-template-info
###

GEN_VX_MASK_INPUT_DIR = {INPUT_BASE}/model_applications/pbl/PointStat_fcstHRRR_obsAMDAR_PBLH_PyEmbed
GEN_VX_MASK_INPUT_TEMPLATE = {init?fmt=%Y%m%d%H}/pblh_22{init?fmt=%j}{init?fmt=%H}0000{lead?fmt=%HH}
GEN_VX_MASK_INPUT_MASK_DIR = {INPUT_BASE}/model_applications/pbl/PointStat_fcstHRRR_obsAMDAR_PBLH_PyEmbed
GEN_VX_MASK_INPUT_MASK_TEMPLATE = met_mask_{custom?fmt=%s}.txt

GEN_VX_MASK_OUTPUT_DIR = {INPUT_BASE}/model_applications/pbl/PointStat_fcstHRRR_obsAMDAR_PBLH_PyEmbed
GEN_VX_MASK_OUTPUT_TEMPLATE = {OUTPUT_BASE}/gen_vx_mask_pblh/met_mask_{custom?fmt=%s}_{GEN_VX_MASK_AIRPORT_RADIUS_KM}km.nc

FCST_POINT_STAT_INPUT_DIR = {INPUT_BASE}/model_applications/pbl/PointStat_fcstHRRR_obsAMDAR_PBLH_PyEmbed
FCST_POINT_STAT_INPUT_TEMPLATE = {init?fmt=%Y%m%d%H}/pblh_22{init?fmt=%j}{init?fmt=%H}0000{lead?fmt=%HH}

OBS_POINT_STAT_INPUT_DIR =
OBS_POINT_STAT_INPUT_TEMPLATE = PYTHON_NUMPY= {PARM_BASE}/use_cases/model_applications/pbl/PointStat_fcstHRRR_obsAMDAR_PBLH_PyEmbed/calc_amdar_pblh.py {INPUT_BASE}/model_applications/pbl/PointStat_fcstHRRR_obsAMDAR_PBLH_PyEmbed/22{valid?fmt=%j}{valid?fmt=%H}00q.cdf

POINT_STAT_OUTPUT_DIR = {OUTPUT_BASE}/point_stat_pblh
POINT_STAT_OUTPUT_TEMPLATE = {valid?fmt=%Y%m%d}

POINT_STAT_CLIMO_MEAN_INPUT_DIR =
POINT_STAT_CLIMO_MEAN_INPUT_TEMPLATE =

POINT_STAT_CLIMO_STDEV_INPUT_DIR =
POINT_STAT_CLIMO_STDEV_INPUT_TEMPLATE =


###
# Field Info
# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#field-info
###

POINT_STAT_ONCE_PER_FIELD = False

FCST_VAR1_NAME = HPBL
FCST_VAR1_LEVELS = L0
FCST_VAR1_THRESH = <=0, >10000
OBS_VAR1_NAME = HPBL
OBS_VAR1_LEVELS = L0
OBS_VAR1_THRESH = <=0, >10000
OBS_VAR1_OPTIONS = set_attr_units = "m"


###
# GenVxMask Settings
# https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#genvxmask
###

GEN_VX_MASK_SKIP_IF_OUTPUT_EXISTS = True
GEN_VX_MASK_OPTIONS = -type "circle" -thresh le{GEN_VX_MASK_AIRPORT_RADIUS_KM}

# PointStat Settings
# https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#pointstat
###

LOG_POINT_STAT_VERBOSITY = 4

POINT_STAT_CONFIG_FILE ={PARM_BASE}/met_config/PointStatConfig_wrapped

POINT_STAT_CLIMO_MEAN_TIME_INTERP_METHOD = NEAREST
POINT_STAT_INTERP_TYPE_METHOD = BILIN
POINT_STAT_INTERP_TYPE_WIDTH = 2

POINT_STAT_OUTPUT_FLAG_SL1L2 = STAT
POINT_STAT_OUTPUT_FLAG_VL1L2 = STAT
POINT_STAT_OUTPUT_FLAG_MPR = STAT

OBS_POINT_STAT_WINDOW_BEGIN = -3600
OBS_POINT_STAT_WINDOW_END = 3600

POINT_STAT_OFFSETS = 0

MODEL = HRRR

POINT_STAT_DESC = {PY_SOUNDING_FLAG}_{PY_PT_DELTA}
OBTYPE =

POINT_STAT_REGRID_TO_GRID = NONE
POINT_STAT_REGRID_METHOD = BILIN
POINT_STAT_REGRID_WIDTH = 2

POINT_STAT_OUTPUT_PREFIX = {custom?fmt=%s}_{PY_SOUNDING_FLAG}_{PY_PT_DELTA}

POINT_STAT_MASK_GRID =  
POINT_STAT_MASK_POLY = {OUTPUT_BASE}/gen_vx_mask_pblh/met_mask_{custom?fmt=%s}_{GEN_VX_MASK_AIRPORT_RADIUS_KM}km.nc
POINT_STAT_MASK_SID =

POINT_STAT_MESSAGE_TYPE = ADPSFC

# INFO TO PASS TO PYTHON SCRIPT
[user_env_vars]

AIRPORT = {custom?fmt=%s}    
SOUNDING_FLAG = {PY_SOUNDING_FLAG}  
PT_DELTA = {PY_PT_DELTA}
VAL_TIME = {PY_VAL_TIME}

MET Configuration

METplus sets environment variables based on user settings in the METplus configuration file. See How METplus controls MET config file settings for more details.

YOU SHOULD NOT SET ANY OF THESE ENVIRONMENT VARIABLES YOURSELF! THEY WILL BE OVERWRITTEN BY METPLUS WHEN IT CALLS THE MET TOOLS!

If there is a setting in the MET configuration file that is currently not supported by METplus you’d like to control, please refer to: Overriding Unsupported MET config file settings

Note

See the PointStat MET Configuration section of the User’s Guide for more information on the environment variables used in the file below:

////////////////////////////////////////////////////////////////////////////////
//
// Point-Stat configuration file.
//
// For additional information, see the MET_BASE/config/README file.
//
////////////////////////////////////////////////////////////////////////////////

//
// Output model name to be written
//
// model =
${METPLUS_MODEL}

//
// Output description to be written
// May be set separately in each "obs.field" entry
//
// desc =
${METPLUS_DESC}

////////////////////////////////////////////////////////////////////////////////

//
// Verification grid
//
// regrid = {
${METPLUS_REGRID_DICT}

////////////////////////////////////////////////////////////////////////////////

//
// May be set separately in each "field" entry
//
censor_thresh = [];
censor_val    = [];
cat_thresh    = [ NA ];
cnt_thresh    = [ NA ];
cnt_logic     = UNION;
wind_thresh   = [ NA ];
wind_logic    = UNION;
eclv_points   = 0.05;
//hss_ec_value =
${METPLUS_HSS_EC_VALUE}
rank_corr_flag = FALSE;

//
// Forecast and observation fields to be verified
//
fcst = {
  ${METPLUS_FCST_FILE_TYPE}
  ${METPLUS_FCST_FIELD}
}

obs = {
  ${METPLUS_OBS_FILE_TYPE}
  ${METPLUS_OBS_FIELD}
}
////////////////////////////////////////////////////////////////////////////////

//
// Point observation filtering options
// May be set separately in each "obs.field" entry
//
// message_type =
${METPLUS_MESSAGE_TYPE}
sid_exc        = [];

//obs_quality_inc =
${METPLUS_OBS_QUALITY_INC}

//obs_quality_exc =
${METPLUS_OBS_QUALITY_EXC}

duplicate_flag = NONE;
obs_summary    = NONE;
obs_perc_value = 50;

//
// Mapping of message type group name to comma-separated list of values.
//
//message_type_group_map =
${METPLUS_MESSAGE_TYPE_GROUP_MAP}

////////////////////////////////////////////////////////////////////////////////

//
// Climatology data
//
//climo_mean = {
${METPLUS_CLIMO_MEAN_DICT}


//climo_stdev = {
${METPLUS_CLIMO_STDEV_DICT}

//
// May be set separately in each "obs.field" entry
//
//climo_cdf = {
${METPLUS_CLIMO_CDF_DICT}

////////////////////////////////////////////////////////////////////////////////

//
// Point observation time window
//
// obs_window = {
${METPLUS_OBS_WINDOW_DICT}

////////////////////////////////////////////////////////////////////////////////

//
// Verification masking regions
//
//mask = {
${METPLUS_MASK_DICT}

////////////////////////////////////////////////////////////////////////////////

//
// Confidence interval settings
//
ci_alpha  = [ 0.05 ];

boot = {
   interval = PCTILE;
   rep_prop = 1.0;
   n_rep    = 0;
   rng      = "mt19937";
   seed     = "";
}

////////////////////////////////////////////////////////////////////////////////

//
// Interpolation methods
//
//interp = {
${METPLUS_INTERP_DICT}

////////////////////////////////////////////////////////////////////////////////

//
// HiRA verification method
//
//hira = {
${METPLUS_HIRA_DICT}

////////////////////////////////////////////////////////////////////////////////

//
// Statistical output types
//
//output_flag = {
${METPLUS_OUTPUT_FLAG_DICT}

////////////////////////////////////////////////////////////////////////////////
// Threshold for SEEPS p1 (Probability of being dry)

//seeps_p1_thresh =
${METPLUS_SEEPS_P1_THRESH}

////////////////////////////////////////////////////////////////////////////////

tmp_dir = "${MET_TMP_DIR}";

// output_prefix =
${METPLUS_OUTPUT_PREFIX}
//version        = "V10.0.0";

////////////////////////////////////////////////////////////////////////////////

${METPLUS_MET_CONFIG_OVERRIDES}

Python Embedding

This use case uses a Python embedding script to read input data

parm/use_cases/model_applications/pbl/PointStat_fcstHRRR_obsAMDAR_PBLH_PyEmbed/calc_amdar_pblh.py

'''
This script reads AMDAR hourly netcdf files, computes PBLH, and sends 11-column ascii table to MET for point-stat
See accompanying PointStat_python_embedding_obs_amdar_pblh.conf for settings and passing in env variables here 
Jason M. English, May 2023
'''

import os
import sys
import pandas as pd
import numpy as np
import netCDF4 as nc

# silence this annoying warning about numpy bool being deprecated in favor of python bool
from warnings import filterwarnings
filterwarnings(action='ignore', category=DeprecationWarning, message='`np.bool` is a deprecated alias')

########################################################################

print("Python Script:\t" + repr(sys.argv[0]))

##
##  input file specified on the command line
##  load the data into the numpy array
##

loc_name = os.environ.get('AIRPORT') # "DENVER", "DALLAS", "BOSTON", "MINNEAPOLIS" # airport, not city
sf_include = os.environ.get('SOUNDING_FLAG')  # what sounding flags to include: ASC or DESC
pt_delta = float(os.environ.get('PT_DELTA')) #1.25  # potential temperature delta that triggers PBLH calculation (K)
val_time = os.environ.get('VAL_TIME')  # valid time for this call (nearest hour)
rbox = 2.0   #  +/- deg;  set bigger than you need so MET mask can cut from that later
alt_base = 200.  # highest altitude where the alt min is looked for to get base potential temperature
gap_max = 400.  # maximum allowable altitude gap (m) between the computed PBLH and the altitude of the data point below it (gap_max + pblh/20)
alt_dp = 4    # minimum number of data points for the flight to be considered
alt_adj = "yes"  # adjust minimum altitude to be >= 0 yes or no
out_rej = 2  # number of sigmas to trigger outlier reject

if loc_name == "DENVER":    
   gnd0 = 5300.*0.3048   # surface elevation at location (msl);  DIA is at 5430ft but up to 300 feet lower than that within 500m of the airport
   lat0 = 39.856
   lon0 = -104.6764
elif loc_name == "DALLAS":
   gnd0 = 550.*0.3048
   lat0 = 32.8998
   lon0 = -97.0403
elif loc_name == "MINNEAPOLIS":
   gnd0 = 840.*0.3048
   lat0 = 44.8848
   lon0 = -93.2223
elif loc_name == "BOSTON":
   gnd0 = 20.*0.3048  
   lat0 = 42.3656
   lon0 = -71.0096

# convert tail number array to readable character string
def get_tn(tn):  
  tnc = tn.astype(str)   # convert to character string
  tnc = np.char.array(tnc)   # removes whitespace and allows vectorized string operations
  tnc_splice = tnc[:,0]+tnc[:,1]+tnc[:,2]+tnc[:,3]+tnc[:,4]+tnc[:,5]+tnc[:,6]+tnc[:,7]+tnc[:,8]   # tail number spliced into a single string
  return tnc_splice
 
if len(sys.argv) != 2:
   print("ERROR: calc_amdar_pblh.py -> Must specify exactly one input file.")
   sys.exit(1)

# Read the input file as the first argument
input_file = os.path.expandvars(sys.argv[1])
try:
   print("Input File:\t" + repr(input_file))
   ncf = nc.Dataset(input_file)
    
   tn = ncf['tailNumber'][:]  
   sf = ncf['sounding_flag'][:]  # -1=descent, 0=cruising, 1=ascent 
   t = ncf['temperature'][:]
   alt = ncf['altitude'][:] - gnd0   # subtract surface height to get AGL
   lat = ncf['latitude'][:]
   lon = ncf['longitude'][:]
   ncf.close()

   # set to NaN if cruising flight (not ascent or descent)
   t = np.where(sf == 0, np.nan, t)
   if sf_include == "ASC":
     t = np.where(sf == -1, np.nan, t)  # discard descents
   if sf_include == "DESC":
     t = np.where(sf == 1, np.nan, t)  # discard ascents
 
   # set to NaN if outside alt, lat/lon  bounds
   t = np.where((lat > lat0-rbox) & (lat < lat0+rbox) & (lon > lon0-rbox ) & (lon < lon0+rbox), t, np.nan)
 
   # convert tail number array to readable character string
   tns = get_tn(tn)

   # set tail number array to NaN wherever temperature array is NaN
   tns = np.where(np.isnan(t), np.nan, tns)
   
   # get unique tail numbers within the specified lat/lon range
   tn_list = np.unique(tns)
   nflight = tn_list.size
   
   # Create arrays for saving PBLH and other fields for each flight
   pblh = np.full([nflight],np.nan)
   pblh_o = np.full([nflight],np.nan)   #pblh interpolated with outliers excluded
   pt_min = np.full([nflight],np.nan)  # potential temperature minimum 
   lat_avg = np.full([nflight],np.nan)  
   lon_avg = np.full([nflight],np.nan) 

   for i,tn_name in enumerate(tn_list):  #loop through tail numbers

      if tn_name != "nan":
        tn_arr = np.where(tns == tn_name, tns, 'null')   # set array to null if it doesn't  this tail number
        tn_ind = np.where(tns == tn_arr)   # get list of indices 
      
        # take the elements from each array ing only this flight (via the specified indices)
        tn_i  = np.squeeze(np.take(tn_arr, tn_ind)) 
        sf_i  = np.squeeze(np.take(sf, tn_ind)) 
        t_i   = np.squeeze(np.take(t, tn_ind))
        alt_i = np.squeeze(np.take(alt, tn_ind))
        lat_i = np.squeeze(np.take(lat, tn_ind))
        lon_i = np.squeeze(np.take(lon, tn_ind))
      
        # only include ascents/descents that have enough altitude/temperature data
        if (np.amin(alt_i) < alt_base) & (np.amax(alt_i) > alt_base) & (alt_i.size >= alt_dp):
  
          # sort altitude and temperature arrays to be ascending
          sort_inds = np.argsort(alt_i)
          t_d = np.copy(t_i[sort_inds])
          alt_d = np.copy(alt_i[sort_inds])
          lat_d = np.copy(lat_i[sort_inds])
          lon_d = np.copy(lon_i[sort_inds])

          # adjust altitude minimum to zero if it's negative
          if alt_adj == "yes":
            if np.nanmin(alt_d) < 0:
              alt_d[:] = alt_d[:] - np.nanmin(alt_d)

          # convert altitude to pressure 
          slp = 101325.  # Sea level pressure (Pa)
          expon = (-9.80665 *0.0289644) / (8.31432 * -0.0065)
          p_d = slp * (1. - (alt_d + gnd0)/44307.694)**expon   # needs to be pressure altitude (add ground ht) 
    
          # convert temperature to potential temperature
          pt_d = np.copy(t_d)
          pt_d[:] = t_d[:] * (slp/p_d[:])**0.286
  
          # Find minimum potential temperature that occurs below the specified altitude alt_base
          pt_min[i] = np.nanmin(np.where(alt_d < alt_base, pt_d, np.nan))
          pt_min_ind = np.where(pt_d == pt_min[i])[0][0]  # find array indexing that value

          # Only move forward if minimum PT is within a reasonable range
          if (pt_min[i] > 0) & (pt_min[i] < 3040): 

          # consider only potential temperature values above where pt_min was found when searching for pblh
            alt_d[:pt_min_ind] = np.nan
            pt_d[:pt_min_ind] = np.nan
            pt_dif = np.copy(pt_d)
            pt_dif[:] = pt_d[:] - pt_min[i] 
  
            # determine lowest height that exceeds the specified pt_delta (K)
            if np.nanmax(pt_d) >= (pt_min[i]+pt_delta):    # make sure it exists in this profile
              pblh_alt = np.nanmin(np.where(pt_d >= (pt_min[i]+pt_delta),alt_d, np.nan))
              pblh_ind = np.where(alt_d == pblh_alt)[0][0]   # altitude index where pblh is found
  
              # only include pblh if the altitude below it isn't too big of a gap
              if pblh_ind.size == 1:   # make sure only 1 index was found
                alt_gap = alt_d[pblh_ind]-alt_d[pblh_ind-1]
  
                if alt_gap < (gap_max + alt_d[pblh_ind]/20.):
                  pblh[i] = alt_d[pblh_ind]
       
                  # linear interpolate PBLH between this data point and the one below it
                  pblh[i] = np.interp((pt_min[i]+pt_delta), pt_d[pblh_ind-1:pblh_ind+1], alt_d[pblh_ind-1:pblh_ind+1]) 
  
                  # compute lat/lon for this flight by taking the avg lat/lon coordiantes over the flight
                  lat_avg[i] = np.average(lat_i)
                  lon_avg[i] = np.average(lon_i)

          print("tn=", tn_i[0], ", sf=", sf_include, ", n=", pt_d.size, ", pt_min (K)=", np.array2string(pt_min[i]), 
                ", pblh interp (m)=", np.array2string(pblh[i]), ", pblh closest (m)=", np.array2string(pblh[i])) 

########################################################################

   # Now that all flights at this hour have been computed, conduct statistics and averaging on them
   if np.count_nonzero(~np.isnan(pblh)) > 0:
     pblh_o[:] = np.where((pblh >= np.nanmean(pblh)-float(out_rej)*np.nanstd(pblh)) & 
                          (pblh <= np.nanmean(pblh)+float(out_rej)*np.nanstd(pblh)), pblh, np.nan)

     # only include flights with a computed PBLH value
     lat_avg[np.isnan(pblh_o)] = np.nan
     lon_avg[np.isnan(pblh_o)] = np.nan
     pblh_o = pblh_o[~np.isnan(pblh_o)] 
     lat_avg= lat_avg[~np.isnan(lat_avg)] 
     lon_avg= lon_avg[~np.isnan(lon_avg)] 

   # Read and format the input 11-column observations:
   #   (1)  string:  Message_Type ('ADPSFC')
   #   (2)  string:  Station_ID (AIRPORT)
   #   (3)  string:  Valid_Time(YYYYMMDD_HHMMSS)
   #   (4)  numeric: Lat(Deg North)
   #   (5)  numeric: Lon(Deg East)
   #   (6)  numeric: Elevation(msl) 
   #   (7)  string:  Var_Name(or GRIB_Code)
   #   (8)  numeric: Level
   #   (9)  numeric: Height(msl or agl)
   #   (10) string:  QC_String
   #   (11) numeric: Observation_Value

   point_data = pd.DataFrame({'typ':'ADPSFC', 'sid':loc_name, 'vld':val_time,
                               'lat':lat_avg, 'lon':lon_avg, 'elv':gnd0, 'var':'HPBL',
                               'lvl':0, 'hgt':0, 'qc':'AMDAR', 'obs':pblh_o}).values.tolist()
     
   print(point_data)
   print("     point_data: Data Length:\t" + repr(len(point_data)))
   print("     point_data: Data Type:\t" + repr(type(point_data)))
   
except NameError:
    print("Can't find the input file")
    sys.exit(1)

Running METplus

It is recommended to run this use case by:

Passing in PointStat_fcstHRRR_obsAMDAR_PBLH_PyEmbed.conf then a user-specific system configuration file:

run_metplus.py -c /path/to/PointStat_fcstHRRR_obsAMDAR_PBLH_PyEmbed.conf -c /path/to/user_system.conf

The following METplus configuration variables must be set correctly to run this example.:

  • INPUT_BASE - Path to directory where sample data tarballs are unpacked (See Datasets section to obtain tarballs).

  • OUTPUT_BASE - Path where METplus output will be written. This must be in a location where you have write permissions

  • MET_INSTALL_DIR - Path to location where MET is installed locally

Example User Configuration File:

[dir]
INPUT_BASE = /path/to/sample/input/data
OUTPUT_BASE = /path/to/output/dir
MET_INSTALL_DIR = /path/to/met-X.Y

NOTE: All of these items must be found under the [dir] section.

Expected Output

A successful run will output the following both to the screen and to the logfile:

INFO: METplus has successfully finished running.

Refer to the value set for OUTPUT_BASE to find where the output data was generated. Output for this use case will be found in point_stat_pblh (relative to OUTPUT_BASE) with subdirectories for valid time (YYYYMMDD) and will contain .stat files with the following naming convention:

convention: point_stat_{AIRPORT}_{SOUNDING_FLAG}_{PT_DELTA}_{LEADTIME}L_{VALIDTIME}.stat

example: point_stat_DENVER_ALL_1.25_120000L_20220701_200000V.stat

Keywords

Note

  • GenVxMaskToolUseCase

  • PointStatToolUseCase

  • PythonEmbeddingFileUseCase

  • PBLAppUseCase

Navigate to the METplus Quick Search for Use Cases page to discover other similar use cases.

sphinx_gallery_thumbnail_path = ‘_static/pbl-PointStat_fcstHRRR_obsAMDAR_PBLH_PyEmbed.png’

Total running time of the script: (0 minutes 0.000 seconds)

Gallery generated by Sphinx-Gallery