GridStat: Use Python embedding to evaluate GeoTIFF imagery across multiple fields

model_applications/marine_and_cryosphere/GridStat_fcstGSWR_obsMRMS_GeoTIFF_multiField.conf

Scientific Objective

This use case provides an example for utilizing Python Embedding to read an unsupported file format into METplus and leverage the verification capabilities. GeoTIFF format forecasts are read in via Python and verified against the MRMS dataset, providing useful evaulation statistics for a numerical forecast that previously would not have been able to utilize METplus.

Version Added

METplus version 6.1

Datasets

Forecast: MIT-LL Global Synthetic Weather Radar (GSWR), ~0.045 degree global resolution

Observation: MultiRadar/MultiSensor (MRMS) ~0.01 degree CONUS resolution

Climatology: None

Location: All of the input data required for this use case can be found in a sample data tarball. Each use case category will have one or more sample data tarballs. It is only necessary to download the tarball with the use case’s dataset and not the entire collection of sample data. Click here to access the METplus releases page and download sample data for the appropriate release: https://github.com/dtcenter/METplus/releases This tarball should be unpacked into the directory that you will set the value of INPUT_BASE. See Running METplus section for more information.

METplus Components

The only tool this use case calls is GridStat, with six separate instances of GridStat used. Within GridStat a Python script is used for ingesting forecast data.

METplus Workflow

Beginning time (INIT_BEG): 2024-10-15

End time (INIT_END): 2024-10-15

Increment between beginning and end times (INIT_INCREMENT): 15 minutes

Sequence of forecast leads to process (LEAD_SEQ): 60, 75, 90, 105, 120, 240, 300, 360, 420, and 480 minutes

The use case runs six instances of GridStat; one for each of the three variable fields to be verified across the two different types of forecast files. In the first GridStat call, the configuration options that will not change across the other instances are set, along with the initial five lead times separated by 15 minute intervals. Due to the observation dataset having irregular seconds attached to the valid time, the OBS_VAR1_OPTIONS sets the valid time equal to the valid time being verified. This will ensure that the forecast being evaluated is always the same valid time as the observation. The irregular valid time extends to the name of the observation files as well, so OBS_GRID_STAT_FILE_WINDOW has a +/- 120 seconds setting that will capture the file’s slight irregular offset in seconds. Once the forecast python embedding is complete, the verification is completed with a regrid to the coarser forecast grid and the requested line type is created. The two GridStat instances containing “adv” keep the same timing information, changing only the observation file naming template and the corresponding forecast file passed to the Python script, as well as slight changes to the thresholding and output location. The remaining three GridStat instances with “blend” need their own LEAD_SEQ, with five different lead times incremented by one hour. These instances also update the observation input file templates, thresholding, and output location.

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, i.e. parm/use_cases/model_applications/short_range/GridStat_fcstGSWR_obsMRMS_GeoTIFF_multiField.conf

[config]

# Documentation for this use case can be found at
# https://metplus.readthedocs.io/en/latest/generated/model_applications/short_range/GridStat_fcstGSWR_obsMRMS_GeoTIFF_multiField.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 = GridStat, GridStat(adv_compRef), GridStat(adv_vil), GridStat(blend_Et), GridStat(blend_compRef), GridStat(blend_vil)


###
# 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%M
INIT_BEG=2410150100
INIT_END=2410150100
INIT_INCREMENT = 15M

LEAD_SEQ = 60M, 75M, 90M, 105M, 120M

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

FCST_GRID_STAT_INPUT_DIR = {INPUT_BASE}/model_applications/short_range/GridStat_fcstGSWR_obsMRMS_GeoTIFF_multiField
FCST_GRID_STAT_INPUT_TEMPLATE = PYTHON_NUMPY 

OBS_GRID_STAT_INPUT_DIR = {INPUT_BASE}/model_applications/short_range/GridStat_fcstGSWR_obsMRMS_GeoTIFF_multiField
OBS_GRID_STAT_INPUT_TEMPLATE = EchoTop_30_00.50_{valid?fmt=%Y%m%d-%H%M%S}.grib2

GRID_STAT_CLIMO_MEAN_INPUT_DIR =
GRID_STAT_CLIMO_MEAN_INPUT_TEMPLATE =

GRID_STAT_CLIMO_STDEV_INPUT_DIR =
GRID_STAT_CLIMO_STDEV_INPUT_TEMPLATE =

GRID_STAT_OUTPUT_DIR = {OUTPUT_BASE}
GRID_STAT_OUTPUT_TEMPLATE = advected/echotop/{valid?fmt=%Y%m%d%H%M}


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

MODEL = GSWR
OBTYPE = MRMS

GRID_STAT_ONCE_PER_FIELD = False

FCST_IS_PROB = false
#FCST_GRID_STAT_PROB_THRESH = ==0.1

FCST_VAR1_NAME = {PARM_BASE}/use_cases/model_applications/short_range/GridStat_fcstGSWR_obsMRMS_GeoTIFF_multiField/TIFF_readin.py {FCST_GRID_STAT_INPUT_DIR}/AdvectedEtForecastMosaic_H={lead?fmt=%3M}.i{init?fmt=%y%m%d%H%M%S}.v{valid?fmt=%y%m%d%H%M%S}.f{lead?fmt=%4M}.tif
FCST_VAR1_THRESH = ge15.0, ge25.0, ge35.0, >=SFP98.0, >=SFP99.0


OBS_VAR1_NAME = EchoTop30
OBS_VAR1_LEVELS = L500
OBS_VAR1_THRESH = ge15.0, ge25.0, ge35.0, >=SOP98.0, >=SOP99.0
OBS_VAR1_OPTIONS = set_attr_valid = "{valid?fmt=%Y%m%d_%H%M%S}"


###
# GridStat Settings (optional)
# https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#gridstat
###

#LOG_GRID_STAT_VERBOSITY = 2

GRID_STAT_CONFIG_FILE = {PARM_BASE}/met_config/GridStatConfig_wrapped

GRID_STAT_REGRID_TO_GRID = FCST
GRID_STAT_REGRID_METHOD = BILIN
GRID_STAT_REGRID_WIDTH = 2
GRID_STAT_REGRID_VLD_THRESH = 0.6
GRID_STAT_REGRID_SHAPE = SQUARE

GRID_STAT_DESC = NA

FCST_GRID_STAT_FILE_WINDOW_BEGIN = 0
FCST_GRID_STAT_FILE_WINDOW_END = 0
OBS_GRID_STAT_FILE_WINDOW_BEGIN = -120
OBS_GRID_STAT_FILE_WINDOW_END = 120

GRID_STAT_NEIGHBORHOOD_WIDTH = 1
GRID_STAT_NEIGHBORHOOD_SHAPE = SQUARE

GRID_STAT_NEIGHBORHOOD_COV_THRESH = >=0.5

GRID_STAT_OUTPUT_PREFIX = 

GRID_STAT_OUTPUT_FLAG_CTC = NONE
GRID_STAT_OUTPUT_FLAG_CTS = STAT
GRID_STAT_OUTPUT_FLAG_ECLV = NONE
GRID_STAT_OUTPUT_FLAG_GRAD = NONE


GRID_STAT_NC_PAIRS_FLAG_LATLON = TRUE
GRID_STAT_NC_PAIRS_FLAG_RAW = TRUE
GRID_STAT_NC_PAIRS_FLAG_DIFF = FALSE
GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE
GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE


GRID_STAT_CENSOR_THRESH = < 0.0
GRID_STAT_CENSOR_VAL = -9999

[adv_compRef]

OBS_GRID_STAT_INPUT_TEMPLATE = MergedReflectivityQC_19.00_{valid?fmt=%Y%m%d-%H%M%S}.grib2


GRID_STAT_OUTPUT_TEMPLATE = advected/comp_ref/{valid?fmt=%Y%m%d%H%M}


FCST_VAR1_NAME = {PARM_BASE}/use_cases/model_applications/short_range/GridStat_fcstGSWR_obsMRMS_GeoTIFF_multiField/TIFF_readin.py {FCST_GRID_STAT_INPUT_DIR}/AdvectedCompReflForecastMosaic_H={lead?fmt=%3M}.i{init?fmt=%y%m%d%H%M%S}.v{valid?fmt=%y%m%d%H%M%S}.f{lead?fmt=%4M}.tif
FCST_VAR1_THRESH = ge20.0, ge30.0, ge40.0, ge50.0, >=SFP98.0, >=SFP99.0


OBS_VAR1_NAME = ConusMergedReflectivityQC
OBS_VAR1_LEVELS = L19000
OBS_VAR1_THRESH = ge20.0, ge30.0, ge40.0, ge50.0, >=SFP98.0, >=SFP99.0

[adv_vil]

OBS_GRID_STAT_INPUT_TEMPLATE = VIL_00.50_{valid?fmt=%Y%m%d-%H%M%S}.grib2


GRID_STAT_OUTPUT_TEMPLATE = advected/VIL/{valid?fmt=%Y%m%d%H%M}


FCST_VAR1_NAME = {PARM_BASE}/use_cases/model_applications/short_range/GridStat_fcstGSWR_obsMRMS_GeoTIFF_multiField/TIFF_readin.py {FCST_GRID_STAT_INPUT_DIR}/AdvectedVilForecastMosaic_H={lead?fmt=%3M}.i{init?fmt=%y%m%d%H%M%S}.v{valid?fmt=%y%m%d%H%M%S}.f{lead?fmt=%4M}.tif
FCST_VAR1_THRESH = ge5.0, ge20.0, ge35.0, >=SFP99.0, >=SFP99.5


OBS_VAR1_NAME = VIL
OBS_VAR1_LEVELS = L500
OBS_VAR1_THRESH = ge5.0, ge20.0, ge35.0, >=SFP99.0, >=SFP99.5


[blend_Et]

LEAD_SEQ = 240M, 300M, 360M, 420M, 480M

OBS_GRID_STAT_INPUT_TEMPLATE = EchoTop_30_00.50_{valid?fmt=%Y%m%d-%H%M%S}.grib2


GRID_STAT_OUTPUT_TEMPLATE = blended/echotop/{valid?fmt=%Y%m%d%H%M}


FCST_VAR1_NAME = {PARM_BASE}/use_cases/model_applications/short_range/GridStat_fcstGSWR_obsMRMS_GeoTIFF_multiField/TIFF_readin.py {FCST_GRID_STAT_INPUT_DIR}/BlendedEtForecastMosaic_H={lead?fmt=%3M}.i{init?fmt=%y%m%d%H%M%S}.v{valid?fmt=%y%m%d%H%M%S}.f{lead?fmt=%4M}.tif
FCST_VAR1_THRESH = ge15.0, ge25.0, ge35.0, >=SFP98.0, >=SFP99.0


OBS_VAR1_NAME = EchoTop30
OBS_VAR1_LEVELS = L500
OBS_VAR1_THRESH = ge15.0, ge25.0, ge35.0, >=SFP98.0, >=SFP99.0

[blend_compRef]

LEAD_SEQ = 240M, 300M, 360M, 420M, 480M

OBS_GRID_STAT_INPUT_TEMPLATE = MergedReflectivityQC_19.00_{valid?fmt=%Y%m%d-%H%M%S}.grib2


GRID_STAT_OUTPUT_TEMPLATE = blended/comp_ref/{valid?fmt=%Y%m%d%H%M}


FCST_VAR1_NAME = {PARM_BASE}/use_cases/model_applications/short_range/GridStat_fcstGSWR_obsMRMS_GeoTIFF_multiField/TIFF_readin.py {FCST_GRID_STAT_INPUT_DIR}/BlendedCompReflForecastMosaic_H={lead?fmt=%3M}.i{init?fmt=%y%m%d%H%M%S}.v{valid?fmt=%y%m%d%H%M%S}.f{lead?fmt=%4M}.tif
FCST_VAR1_THRESH = ge20.0, ge30.0, ge40.0, ge50.0, >=SFP98.0, >=SFP99.0


OBS_VAR1_NAME = ConusMergedReflectivityQC
OBS_VAR1_LEVELS = L19000
OBS_VAR1_THRESH = ge20.0, ge30.0, ge40.0, ge50.0, >=SFP98.0, >=SFP99.0

[blend_vil]

LEAD_SEQ = 240M, 300M, 360M, 420M, 480M

OBS_GRID_STAT_INPUT_TEMPLATE = VIL_00.50_{valid?fmt=%Y%m%d-%H%M%S}.grib2


GRID_STAT_OUTPUT_TEMPLATE = blended/VIL/{valid?fmt=%Y%m%d%H%M}


FCST_VAR1_NAME = {PARM_BASE}/use_cases/model_applications/short_range/GridStat_fcstGSWR_obsMRMS_GeoTIFF_multiField/TIFF_readin.py {FCST_GRID_STAT_INPUT_DIR}/BlendedVilForecastMosaic_H={lead?fmt=%3M}.i{init?fmt=%y%m%d%H%M%S}.v{valid?fmt=%y%m%d%H%M%S}.f{lead?fmt=%4M}.tif
FCST_VAR1_THRESH = ge5.0, ge20.0, ge35.0, >=SFP99.0, >=SFP99.5


OBS_VAR1_NAME = VIL
OBS_VAR1_LEVELS = L500
OBS_VAR1_THRESH = ge5.0, ge20.0, ge35.0, >=SFP99.0, >=SFP99.5

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

GridStatConfig_wrapped
////////////////////////////////////////////////////////////////////////////////
//
// Grid-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}

//
// Output observation type to be written
//
// obtype =
${METPLUS_OBTYPE}

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

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

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

//censor_thresh =
${METPLUS_CENSOR_THRESH}
//censor_val =
${METPLUS_CENSOR_VAL}
//cat_thresh =
${METPLUS_CAT_THRESH}
cnt_thresh  	 = [ NA ];
cnt_logic   	 = UNION;
wind_thresh 	 = [ NA ];
wind_logic  	 = UNION;
eclv_points      = 0.05;
//nc_pairs_var_name =
${METPLUS_NC_PAIRS_VAR_NAME}
nc_pairs_var_suffix = "";
//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}
  ${METPLUS_FCST_CLIMO_MEAN_DICT}
  ${METPLUS_FCST_CLIMO_STDEV_DICT}
}
obs = {
  ${METPLUS_OBS_FILE_TYPE}
  ${METPLUS_OBS_FIELD}
  ${METPLUS_OBS_CLIMO_MEAN_DICT}
  ${METPLUS_OBS_CLIMO_STDEV_DICT}
}

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

//
// Climatology mean 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}

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

//
// 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     = "";
}

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

//
// Data smoothing methods
//
//interp = {
${METPLUS_INTERP_DICT}

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

//
// Neighborhood methods
//
nbrhd = {
   field      = BOTH;
   // shape =
   ${METPLUS_NBRHD_SHAPE}
   // width =
   ${METPLUS_NBRHD_WIDTH}
   // cov_thresh =
   ${METPLUS_NBRHD_COV_THRESH}
   vld_thresh = 1.0;
}

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

//
// Fourier decomposition
// May be set separately in each "obs.field" entry
//
//fourier = {
${METPLUS_FOURIER_DICT}

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

//
// Gradient statistics
// May be set separately in each "obs.field" entry
//
//gradient = {
${METPLUS_GRADIENT_DICT}

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

//
// Distance Map statistics
// May be set separately in each "obs.field" entry
//
//distance_map = {
${METPLUS_DISTANCE_MAP_DICT}


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

//seeps_p1_thresh =
${METPLUS_SEEPS_P1_THRESH}

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

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

//
// NetCDF matched pairs output file
// May be set separately in each "obs.field" entry
//
// nc_pairs_flag = {
${METPLUS_NC_PAIRS_FLAG_DICT}

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

//ugrid_dataset =
${METPLUS_UGRID_DATASET}

//ugrid_max_distance_km =
${METPLUS_UGRID_MAX_DISTANCE_KM}

//ugrid_coordinates_file =
${METPLUS_UGRID_COORDINATES_FILE}

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

//grid_weight_flag =
${METPLUS_GRID_WEIGHT_FLAG}

tmp_dir = "${MET_TMP_DIR}";

// output_prefix =
${METPLUS_OUTPUT_PREFIX}

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

${METPLUS_TIME_OFFSET_WARNING}
${METPLUS_MET_CONFIG_OVERRIDES}

Python Embedding

This script is necessary to read in the forecast file, which is in the unsupported GeoTIFF format. The only input required is a full path to the file to be evaluated, as each file only contains one field for analysis. After checking for exactly one input, two separate routines are used to extract the variable name from the filename, as well as the timing information from the file name. The data from the file is extracted using another routine, followed by two final routines that extract the latitude and longitude information from the file contents and preparing the data to be passed back to GridStat. That final data routine also adjusts the data if the variable field in the file is VIL, which requires additional steps to change the units to the expected kg/m^2. Any negative values are set to bad data (the negative values are the result of pixels with no data but bad pixel data could be seen as an actual value in MET if not thresholded) and finally the MET-required dictionary is constructed. The location of the code is

parm/use_cases/model_applications/short_range/GridStat_fcstGSWR_obsMRMS_GeoTIFF_multiField/TIFF_readin.py
import sys
import os
import numpy as np
import datetime
import rioxarray


def get_variable_attributes(filename: str):
    """
    returns variable type, long name, and units based on filename
    """
    if 'CompRefl' in filename:
        return 'CompRefl', 'Composite Reflectivty', 'dBZ'
    if 'Vil' in filename:
        return 'VIL', 'Vertically Integrated Liquid', 'kg/m^2'
    if 'Et' in filename:
        return 'EchoTops', 'Echo Tops', 'kft'

    print(f"ERROR: Unsupported file: {filename}")
    sys.exit(1)

def get_variable_name(filename: str):
    var_name, *_ = get_variable_attributes(filename)
    return var_name

def open_file(filename: str):
    """
    Opens GSWR geotiff file using xarray and adds in some 
    additional metadata.
    """
    ds = rioxarray.open_rasterio(filename)
    ds = ds.to_dataset(name='band_data')
    ds['band'] = [get_variable_name(filename),]
    #rewrote this to also grab the lead (since its being calculated anyway)
    #and add it as a coord
    issue_time, valid_time, lead_time=parse_time(filename)
    ds = ds.assign_coords(issue_time=('issue_time', [issue_time,]))
    ds = ds.assign_coords(valid_time=('valid_time', [valid_time,]))
    ds = ds.assign_coords(lead_time=('lead_time', [lead_time,]))
    ds['band_data']=ds.band_data.expand_dims('valid_time')
    return ds

def parse_time(filename: str):
    """
    Returns issue time, valid time, and forecast lead in minutes from filename
    Example filename: /path/to/BlendedVilForecastMosaic_H=600.i241015010000.v241015110000.f0600.tif
    """
    try:
        _, issue_time, valid_time, *_ = os.path.basename(filename).split('.')
        issue_time = datetime.datetime.strptime(issue_time,'i%y%m%d%H%M%S')
        valid_time = datetime.datetime.strptime(valid_time,'v%y%m%d%H%M%S')
    except (TypeError, ValueError):
        print(f"ERROR: Could not parse time from filename: {filename}")
        sys.exit(1)

    lead = (valid_time-issue_time).total_seconds() / 60
    return issue_time, valid_time, lead

def decode_vil(vil8bitval: np.ndarray):
    vilval = np.zeros_like(vil8bitval)
    vilval[vil8bitval<=18] = ( vil8bitval[vil8bitval<=18] - 2 ) / 90.6591
    exparg = np.zeros_like(vil8bitval)
    exparg[vil8bitval > 18] = (vil8bitval[vil8bitval > 18]-83.9028)/38.8763
    vilval[vil8bitval > 18]= np.exp(exparg[vil8bitval > 18])
    vilval[vil8bitval<=5] = 0
    vilval[np.isnan(vil8bitval)]=np.nan
    return vilval

def get_time_attributes(filename):
    init, valid, lead = parse_time(filename)
    init = init.strftime('%Y%m%d_%H%M%S')
    valid = valid.strftime('%Y%m%d_%H%M%S')

    #if the lead is divisible by 60, then it has hours
    #we need to pull out that division and retain any minutes that are left
    #and format it all as a string
    if int(lead) / 60 > 0.9:
        lead = str(int(lead/60)).zfill(2) + str(int(lead) % 60).zfill(2) + '00'
    else:
        lead = '00'+ str(int(lead)) + '00'

    return init, valid, lead

def get_grid_attributes(ds):
    #we need to capture the deltas for lat and lon
    #as well as the ll's for each
    #while the current input has equal deltas for lat and lon,
    #a dynamic setting allows future flexibility

    lon_delta = float(abs(ds['x'][0] - ds['x'][1]))
    lat_delta = float(abs(ds['y'][0] - ds['y'][1]))
    lat_ll = float(min(ds['y'].values))
    lon_ll = float(min(ds['x'].values))
    return lon_delta, lat_delta, lat_ll, lon_ll

def get_met_data(ds):
    #change data values over to numpy array and open them to 
    #changes by creating a copy of the data

    data = np.array(ds.band_data.values.copy(),dtype='float32')

    if 'VIL' in name:
        data = decode_vil(data)

    met_data = data[0,0]

    #Need to change bad adata values; due to VIL needing adjustment to values,
    #bad data is anything over 81. All others are 255
    if 'VIL' in name:
        met_data[met_data>81.0]=-9999
    else:
        met_data[met_data==255]=-9999

    return met_data


# error if filepath was not provided as argument

if len(sys.argv) < 2:
    print("ERROR: Must supply filepath argument")
    sys.exit(1)

filepath = sys.argv[1]

# parse time and variable info from the filename

name, long_name, units = get_variable_attributes(filepath)
init, valid, lead = get_time_attributes(filepath)

# open file and read data and grid information

ds = open_file(filepath)

# get lat/lon info

lon_delta, lat_delta, lat_ll, lon_ll = get_grid_attributes(ds)

# get gridded data

met_data = get_met_data(ds)

attrs = {

        'valid': valid,
        'init': init,
        'name': name,
        'long_name': long_name,
        'lead': lead,
        'accum': '000000',
        'level': 'SURFACE',
        'units': units,

        'grid': {
            'name': 'LatitudeLongitude',
            'type': "LatLon",
            'lat_ll': lat_ll,
            'lon_ll': lon_ll,
            'delta_lat': lat_delta,
            'delta_lon': lon_delta,
            'Nlat': 4004,
            'Nlon': 8008,
            }
        }

print("Attributes:\t" + repr(attrs))

For more information on the basic requirements to utilize Python Embedding in METplus, please refer to the MET User’s Guide section on Python Embedding.

User Scripting

There is no User Scripting in this use case.

Running METplus

Pass the use case configuration file to the run_metplus.py script along with any user-specific system configuration files if desired:

run_metplus.py /path/to/METplus/parm/use_cases/model_applications/short_range/GridStat_fcstGSWR_obsMRMS_GeoTIFF_multiField.conf /path/to/user_system.conf

See Running METplus for more information.

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 one of two directories:

  • advected

  • blended

Under these two directories will be three subdirectories, corresponding to the variable fields of interest:

  • echo_tops

  • vil

  • reflectivity

Finally, each variable will have subdirectories corresponding to each of the leads that were evaluated. This should look like the following for advected:

  • 202410150200

  • 202410150215

  • 202410150230

  • 202410150245

  • 202410150300

And for blended, the following subdirectories are present:

  • 202410150500

  • 202410150600

  • 202410150700

  • 202410150800

  • 202410150900

Regardless of which directory is chosen, the lowest directory will contain a netCDF file with the raw forecast and observation fields, and a stat file with CTS output.

Keywords

Note

  • GridStatToolUseCase

  • PythonEmbeddingFileUseCase

  • GRIB2FileUseCase

  • ShortRangeAppUseCase

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

Gallery generated by Sphinx-Gallery