GridStat: WRF and MMA Fire Perimeter

model_applications/fire/GridStat_fcstWRF_obsMMA_fire_perimeter.conf

Scientific Objective

This use case demonstrates the use of GridStat to evaluate the performance of the fire spread forecast from the WRF-Fire model for the 416 fire in Colorado in 2018. Using available fire perimeter observations and WRF-Fire forecasts, contingency statistics are produced to evaluate the forecast performance relative to the observed fire.

Version Added

METplus version 6.0

Datasets

Forecast: WRF Fire

Observations: Multimission Aircraft (MMA)

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

This use case uses the UserScript wrapper to run a Python script to that converts KML fire perimeter files to the poly line format that can be read by MET. Then it runs GenVxMask to create gridded MET NetCDF files from the poly files. Then it runs GridStat to process the WRF fire forecast files and the observation mask files.

METplus Workflow

Beginning time (INIT_BEG): 2018-06-01 at 16Z

End time (INIT_END): 2018-06-01 at 16Z

Increment between beginning and end times (INIT_INCREMENT): None

Sequence of forecast leads to process (LEAD_SEQ): 4 hour, 23 hour, 32 hour

This use case processes 3 forecast leads initialized at 16Z on June 1, 2018, running 3 times. First, the UserScript tool is called. This tool calls a Python script to convert the kml shapefiles to polylines. Then, GenVxMask is run to convert the shapefile produced by UserScript to a netCDF mask, providing the observation input for GridStat. Finally, GridStat is called to compare the WRF-Fire forecast of fire area to the observation mask created using the GenVxMask tool.

METplus Configuration

METplus first loads the default configuration file, then it loads any configuration files passed to METplus via the command line e.g. parm/use_cases/model_applications/fire/GridStat_fcstWRF_obsMMA_fire_perimeter.conf

[config]

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

PROCESS_LIST = UserScript, GenVxMask, GridStat


###
# 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
###

USER_SCRIPT_RUNTIME_FREQ = RUN_ONCE_FOR_EACH

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

LEAD_SEQ = 4H, 23H, 32H


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

SCRIPT_DIR = {PARM_BASE}/use_cases/model_applications/fire/GridStat_fcstWRF_obsMMA_fire_perimeter

USER_SCRIPT_INPUT_DIR = {INPUT_BASE}/model_applications/fire/GridStat_fcstWRF_obsMMA_fire_perimeter/mma
USER_SCRIPT_OUTPUT_DIR = {OUTPUT_BASE}/poly


GEN_VX_MASK_INPUT_DIR =
GEN_VX_MASK_INPUT_TEMPLATE = "lambert 472 472 37.402645 -107.88144 -107.808 0.02754 6371.229 37.461 N"

GEN_VX_MASK_INPUT_MASK_DIR =
GEN_VX_MASK_INPUT_MASK_TEMPLATE = {USER_SCRIPT_OUTPUT_DIR}/fire_perim_{valid?fmt=%Y%m%d_%H}.poly

GEN_VX_MASK_OUTPUT_DIR =
GEN_VX_MASK_OUTPUT_TEMPLATE = {OUTPUT_BASE}/mask/fire_perim_{valid?fmt=%Y%m%d_%H}_mask.nc

FCST_GRID_STAT_INPUT_DIR = {INPUT_BASE}/model_applications/fire/GridStat_fcstWRF_obsMMA_fire_perimeter/wrf
FCST_GRID_STAT_INPUT_TEMPLATE = {init?fmt=%Y%m%d_%H}/wrfout_fire_d02_{valid?fmt=%Y-%m-%d_%H %M %S}

OBS_GRID_STAT_INPUT_TEMPLATE = {GEN_VX_MASK_OUTPUT_TEMPLATE}

GRID_STAT_OUTPUT_DIR = {OUTPUT_BASE}/grid_stat
GRID_STAT_OUTPUT_TEMPLATE = {valid?fmt=%Y%m%d%H}


###
# UserScript Settings
# https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#userscript
###

USER_SCRIPT_COMMAND = python3 {SCRIPT_DIR}/find_and_read_fire_perim_poly.py {USER_SCRIPT_INPUT_DIR} {valid?fmt=%Y%m%d%H} {USER_SCRIPT_OUTPUT_DIR}


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

GEN_VX_MASK_OPTIONS = -type poly


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

MODEL = WRF_FIRE
OBTYPE = FIRE_PERIM_KML

FCST_VAR1_NAME = FIRE_AREA
FCST_VAR1_LEVELS = "0,*,*"
FCST_VAR1_THRESH = >0.5
FCST_VAR1_OPTIONS = set_attr_units = "%"; set_attr_level = "Z0";

OBS_VAR1_NAME = FIRE_PERIM
OBS_VAR1_LEVELS = "*,*"
OBS_VAR1_THRESH = ==1
OBS_VAR1_OPTIONS = set_attr_valid = "{valid?fmt=%Y%m%d_%H%M%S}"


GRID_STAT_REGRID_TO_GRID = FCST

GRID_STAT_OUTPUT_FLAG_CTC = STAT
GRID_STAT_OUTPUT_FLAG_CTS = STAT

GRID_STAT_NC_PAIRS_FLAG_LATLON = TRUE

[user_env_vars]
MET_USE_WRF_SUBGRID = yes

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 use case does not use Python Embedding.

Previously, this use case used Python Embedding to read the WRF-Fire forecast into GridStat. Enhancements to MET in v12.1.0 allow the WRF Fire fields that are on a subgrid to be read into the MET tools natively, removing the need to use Python Embedding to accomplish the read. The Python Embedding script is still available in the repository for reference: parm/use_cases/model_applications/fire/GridStat_fcstWRF_obsMMA_fire_perimeter/read_wrfout_fire.py

User Scripting

This use case calls a Python script to read MMA fire perimeter .kml files and convert them into a poly line file that can be read by GenVxMask. The script hard codes the filename template for the .kml files and a valid time format. This valid time format is provided by the METplus configuration file. The script the sets up a variable for the previous valid time and an output file path. If a .kml file is not found for the current valid time, the script searches for a .kml file from the previous hours. Once a file is found, the Python script parses the input file to find the set of coordinates that define the fire perimeter. These coordinates are then written to a text file (.poly file) in the order longitude, latitude, elevation for each point. This .poly file is then ready to be read in by the GenVxMask tool.

parm/use_cases/model_applications/fire/GridStat_fcstWRF_obsMMA_fire_perimeter/find_and_read_fire_perim_poly.py
import sys
import os
from datetime import datetime, timedelta
from glob import glob
import xml.etree.ElementTree as ET

KML_TEMPLATE = 'N328SF_%m%d%Y%H*.kml'
VALID_FORMAT = '%Y%m%d%H'

if len(sys.argv) != 4:
    print("ERROR: Must supply input directory, valid time (YYYYMMDDHH), and output directory to script")
    sys.exit(1)

# read input directory
input_dir = sys.argv[1]

# parse valid time
try:
    valid_time = datetime.strptime(sys.argv[2], VALID_FORMAT)
except ValueError:
    print(f"ERROR: Invalid format for valid time: {sys.argv[2]} (Should match {VALID_FORMAT})")
    sys.exit(1)

# get previous hour valid time
valid_one_hour_ago = valid_time - timedelta(hours=1)

# build output path
output_dir = sys.argv[3]
output_file = f"fire_perim_{valid_time.strftime('%Y%m%d_%H')}.poly"
output_path = os.path.join(output_dir, output_file)

# create output directory if it does not exist
os.makedirs(output_dir, exist_ok=True)

# find input file
input_file = None
input_glob = os.path.join(input_dir, valid_time.strftime(KML_TEMPLATE))
found_files =  glob(input_glob)

# if no files were found, search one hour ago
if not found_files:
    input_glob = os.path.join(input_dir, valid_one_hour_ago.strftime(KML_TEMPLATE))
    found_files =  glob(input_glob)

    if not found_files:
        print(f"ERROR: Could not find any files for {valid_time} in {input_dir}")
        sys.exit(1)

    # if multiple files are found for previous hour, use last file
    if len(found_files) > 1:
        print(f"WARNING: Found multiple files: {found_files}")
        print("Processing the LAST file")
        input_file = found_files[-1]

elif len(found_files) > 1:
    print(f"WARNING: Found multiple files: {found_files}")
    print("Processing the FIRST file")

if not input_file:
    input_file = found_files[0]

print(f"Parsing file: {input_file}")

tree = ET.parse(input_file)
root = tree.getroot()

search_path = './/{*}coordinates'
coordinates = root.find(search_path).text.split()

with open(output_path, 'w') as file_handle:
    file_handle.write('FIRE_PERIM\n')
    for coord in coordinates:
        lon, lat, elev = coord.split(',')
        file_handle.write(f'{lat} {lon}\n')

print(f"Wrote output to {output_path}")

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/fire/GridStat_fcstWRF_obsMMA_fire_perimeter.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. There are three groups of outputs. First, three polyline files resulting from the UserScript tool

  • poly/fire_perim_20180601_20.poly

  • poly/fire_perim_20180602_15.poly

  • poly/fire_perim_20180603_00.poly

Second, three netCDF files resulting from the GenVxMask tool containing fire perimeter observations

  • mask/fire_perim_20180601_20_mask.nc

  • mask/fire_perim_20180602_15_mask.nc

  • mask/fire_perim_20180603_00_mask.nc

Finally, six files from the GridStat tool. The .stat files contain the CTC and CTS line types for the FIRE_AREA variable at the given lead time (two lines total per stat file). The netCDF files contain the following five fields: lat (latitude), lon (longitude), FCST_FIRE_AREA_Z0_FULL (fire spread area from the WRF-Fire forecast), OBS_FIRE_PERIM_all_all_FULL (fire spread area from the .kml observations), and DIFF_FIRE_AREA_Z0_FIRE_PERIM_all_all_FULL (both the forecast and observed fire spread areas, including overlaps and differences).

  • grid_stat/2018060120/grid_stat_040000L_20180601_200000V.stat

  • grid_stat/2018060120/grid_stat_040000L_20180601_200000V_pairs.nc

  • grid_stat/2018060215/grid_stat_230000L_20180602_150000V.stat

  • grid_stat/2018060215/grid_stat_230000L_20180602_150000V_pairs.nc

  • grid_stat/2018060300/grid_stat_320000L_20180603_000000V.stat

  • grid_stat/2018060300/grid_stat_320000L_20180603_000000V_pairs.nc

Keywords

Note

  • UserScriptUseCase

  • GenVxMaskToolUseCase

  • GridStatToolUseCase

  • WRFFileUseCase

  • GRIB2FileUseCase

  • FireAppUseCase

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

Gallery generated by Sphinx-Gallery