Note
Go to the end to download the full example code.
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.