Note
Go to the end to download the full example code.
GridStat: Verifying Soil moisture of SFS-GSL output against ERA5-Land, continuous and categorical statistics
model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture.conf
Scientific Objective
This use case verifies 30 years of Soil Moisture data from an ensemble for a given year and month (here June). The purpose is to evaluate the Soil Moisture ensemble mean at a 1 degree resolution against ERA5-Land data. The use case demonstrates how to compute categorical, continuous, and anomaly statistics over the globe and CONUS with Grid-Stat, and also select continuous statistics at each grid point over the globe statistics with Series-Analysis. It also illustrates how to read in NMME data and compute an emsemble mean from NMME data as input to Grid-Stat and Series-Analysis, and how to plot example statistics from the command line.
Version Added
METplus version 6.1
Datasets
Forecast: 30 SFS-GSL Ensemble files, 0-1m Soil Moisture fields in mm
Observation: ERA5-Land, Monthly 0-1m Soil Moisture field in mm
Climatology Forecast: SFS-GSL 30 year Enemble Mean Soil Moisture mean and standard deviation
Climatology Observation: ERA5-Land, 30 year Monthly 0-1m Soil Moisture mean and standard deviation
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 calls GridStat 30 times, once for each year of data of the SFS-GSL ensemble. It also calls Series-Analysis once and UserScript twice. METcalcpy, METplotpy, and METdataio are required to run the two UserScripts. These steps could be turned off if graphics are not desired. The METcalcpy scripts accessed include the following:
metcalcpy/util/read_env_vars_in_config.py
The METplopty scrips accessed include the following:
metplotpy/plots/line/line.py
The METdataio scripts accessed include the following:
METdbLoad/ush/read_data_files.py
METdbLoad/ush/read_load_xml.py
METreformat/write_stat_ascii.py
METplus Workflow
Beginning time (VALID_BEG): 1991-06-00
End time (VALID_END): 2020-06-00
Increment between beginning and end times (VALID_INCREMENT): 12 months
Sequence of forecast leads to process (LEAD_SEQ): None
This use case initially computes statistics with Grid-Stat using ensemble means from the SFS-GSL 5 member ensemble monthly forecast data. With an increment of 12 months, all Junes from 1991 to 2020 are processed for a total of 30 years over the globe and CONUS.
Then, Series-Analysis is run once to compute 2D statistics over the entire 30 year time period. Finally, the two UserScripts are each run once; one to reformat data and another to create a plot of ME and RMSE for June over the 30 year time period.
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/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture.conf
[config]
# Documentation for this use case can be found at
# https://metplus.readthedocs.io/en/latest/generated/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture.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, SeriesAnalysis(sm_2d), UserScript(reformat_CNT), UserScript(plot_stats)
###
# 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
###
LOG_LEVEL = INFO
LOOP_BY = VALID
VALID_TIME_FMT = %Y%m%d%H
VALID_BEG = 1991060100
VALID_END = 2020060100
VALID_INCREMENT = 12m
LEAD_SEQ = 0
###
# GridStat File I/O
# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#directory-and-filename-template-info
###
FCST_GRID_STAT_INPUT_TEMPLATE = PYTHON_NUMPY
OBS_GRID_STAT_INPUT_DIR = {INPUT_BASE}/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/ERA5
OBS_GRID_STAT_INPUT_TEMPLATE = ERA5.soilm1m.1x1.1991-2020.mon.nc
GRID_STAT_FCST_CLIMO_MEAN_FILE_NAME = {INPUT_BASE}/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/clim/SFS_GSL.soilm1m.30year_1991_2020.{valid?fmt=%m}_mean_stdev.nc
GRID_STAT_FCST_CLIMO_STDEV_FILE_NAME = {GRID_STAT_FCST_CLIMO_MEAN_FILE_NAME}
GRID_STAT_OBS_CLIMO_MEAN_FILE_NAME = {INPUT_BASE}/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/clim/ERA5_soilm1m_1x1_30year_1991_2020_{valid?fmt=%m}_mean_stdev.nc
GRID_STAT_OBS_CLIMO_STDEV_FILE_NAME = {GRID_STAT_OBS_CLIMO_MEAN_FILE_NAME}
GRID_STAT_OUTPUT_DIR = {OUTPUT_BASE}/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/grid_stat
#GRID_STAT_OUTPUT_TEMPLATE = {valid?fmt=%Y%m%d%H}
###
# GridStat Field Info
# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#field-info
###
MODEL = SFS-GSL
OBTYPE = ERA5
FCST_GRID_STAT_VAR1_NAME = {PARM_BASE}/use_cases/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/sfs_gsl_model_wrapper.py {INPUT_BASE}/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/SFS_GSL/SFS_GSL.soilm1m.{valid?fmt=%Y%m}.fcst.nc {valid?fmt=%2m} {valid?fmt=%Y}
FCST_GRID_STAT_VAR1_THRESH = gt1.0, gt2.0, >SFP25, >SFP50, >SFP75
OBS_GRID_STAT_VAR1_NAME = soilm1m
OBS_GRID_STAT_VAR1_LEVELS = "({valid?fmt=%Y%m%d_%H%M%S},*,*)"
OBS_GRID_STAT_FILE_TYPE = NETCDF_NCCF
OBS_VAR1_THRESH = gt1.0, gt2.0, >SOP25, >SOP50, >SOP75
GRID_STAT_FCST_CLIMO_MEAN_VAR1_NAME = soilm1m_mean
GRID_STAT_FCST_CLIMO_MEAN_VAR1_LEVELS = "(*,*)"
GRID_STAT_FCST_CLIMO_MEAN_HOUR_INTERVAL = NA
GRID_STAT_FCST_CLIMO_STDEV_VAR1_NAME = soilm1m_stdev
GRID_STAT_FCST_CLIMO_STDEV_VAR1_LEVELS = "(*,*)"
GRID_STAT_FCST_CLIMO_STDEV_HOUR_INTERVAL = NA
GRID_STAT_OBS_CLIMO_MEAN_VAR1_NAME = soilm1m_mean
GRID_STAT_OBS_CLIMO_MEAN_VAR1_LEVELS = "(*,*)"
GRID_STAT_OBS_CLIMO_MEAN_HOUR_INTERVAL = NA
GRID_STAT_OBS_CLIMO_STDEV_VAR1_NAME = soilm1m_stdev
GRID_STAT_OBS_CLIMO_STDEV_VAR1_LEVELS = "(*,*)"
GRID_STAT_OBS_CLIMO_STDEV_HOUR_INTERVAL = NA
###
# GridStat Settings
# https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#gridstat
###
GRID_STAT_VERIFICATION_MASK_TEMPLATE =
{INPUT_BASE}/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/masks/CONUS.nc
GRID_STAT_OUTPUT_FLAG_FHO = BOTH
GRID_STAT_OUTPUT_FLAG_CTC = STAT
GRID_STAT_OUTPUT_FLAG_CTS = STAT
GRID_STAT_OUTPUT_FLAG_CNT = STAT
GRID_STAT_OUTPUT_FLAG_SL1L2 = STAT
GRID_STAT_OUTPUT_FLAG_SAL1L2 = STAT
GRID_STAT_REGRID_TO_GRID = OBS
GRID_STAT_REGRID_METHOD = BILIN
GRID_STAT_REGRID_WIDTH = 2
GRID_STAT_REGRID_SHAPE = SQUARE
GRID_STAT_ONCE_PER_FIELD = False
GRID_STAT_OUTPUT_PREFIX = {MODEL}_vs_{OBTYPE}
[sm_2d]
###
# SeriesAnalysis File I/O
# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#directory-and-filename-template-info
###
FCST_SERIES_ANALYSIS_INPUT_DIR = {INPUT_BASE}/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/SFS_GSL
FCST_SERIES_ANALYSIS_INPUT_TEMPLATE = SFS_GSL.soilm1m.{init?fmt=%Y%m}.fcst.nc
FCST_SERIES_ANALYSIS_INPUT_DATATYPE = PYTHON_NUMPY
OBS_SERIES_ANALYSIS_INPUT_DIR = {INPUT_BASE}/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/ERA5
OBS_SERIES_ANALYSIS_INPUT_TEMPLATE = ERA5.soilm1m.1x1.1991-2020.mon.nc
OBS_SERIES_ANALYSIS_INPUT_DATATYPE = NETCDF_NCCF
SERIES_ANALYSIS_FCST_CLIMO_MEAN_FILE_NAME = {INPUT_BASE}/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/clim/SFS_GSL.soilm1m.30year_1991_2020.06_mean_stdev.nc
SERIES_ANALYSIS_FCST_CLIMO_STDEV_FILE_NAME = {SERIES_ANALYSIS_FCST_CLIMO_MEAN_FILE_NAME}
SERIES_ANALYSIS_OBS_CLIMO_MEAN_FILE_NAME = {INPUT_BASE}/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/clim/ERA5_soilm1m_1x1_30year_1991_2020_06_mean_stdev.nc
SERIES_ANALYSIS_OBS_CLIMO_STDEV_FILE_NAME = {SERIES_ANALYSIS_OBS_CLIMO_MEAN_FILE_NAME}
SERIES_ANALYSIS_OUTPUT_DIR = {OUTPUT_BASE}/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/series_analysis
SERIES_ANALYSIS_OUTPUT_TEMPLATE = {MODEL}_vs_{OBTYPE}_June.nc
###
# SeriesAnalysis Field Info
# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#field-info
###
FCST_SERIES_ANALYSIS_VAR1_NAME = {PARM_BASE}/use_cases/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/sfs_gsl_model_wrapper.py MET_PYTHON_INPUT_ARG {valid?fmt=%m} {valid?fmt=%Y}
OBS_SERIES_ANALYSIS_VAR1_NAME = soilm1m
OBS_SERIES_ANALYSIS_VAR1_LEVELS = "({valid?fmt=%Y%m%d_%H%M%S},*,*)"
SERIES_ANALYSIS_FCST_CLIMO_MEAN_VAR1_NAME = soilm1m_mean
SERIES_ANALYSIS_FCST_CLIMO_MEAN_VAR1_LEVELS = "(*,*)"
SERIES_ANALYSIS_FCST_CLIMO_MEAN_HOUR_INTERVAL = NA
SERIES_ANALYSIS_FCST_CLIMO_STDEV_VAR1_NAME = soilm1m_stdev
SERIES_ANALYSIS_FCST_CLIMO_STDEV_VAR1_LEVELS = "(*,*)"
SERIES_ANALYSIS_FCST_CLIMO_STDEV_HOUR_INTERVAL = NA
SERIES_ANALYSIS_OBS_CLIMO_MEAN_VAR1_NAME = soilm1m_mean
SERIES_ANALYSIS_OBS_CLIMO_MEAN_VAR1_LEVELS = "(*,*)"
SERIES_ANALYSIS_OBS_CLIMO_MEAN_HOUR_INTERVAL = NA
SERIES_ANALYSIS_OBS_CLIMO_STDEV_VAR1_NAME = soilm1m_stdev
SERIES_ANALYSIS_OBS_CLIMO_STDEV_VAR1_LEVELS = "(*,*)"
SERIES_ANALYSIS_OBS_CLIMO_STDEV_HOUR_INTERVAL = NA
###
# Series Analysis Settings
# https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#seriesanalysis
###
SERIES_ANALYSIS_RUNTIME_FREQ = RUN_ONCE
SERIES_ANALYSIS_DESC =
SERIES_ANALYSIS_CAT_THRESH =
SERIES_ANALYSIS_VLD_THRESH =
SERIES_ANALYSIS_BLOCK_SIZE = 0
SERIES_ANALYSIS_CTS_LIST =
SERIES_ANALYSIS_REGRID_TO_GRID = OBS
SERIES_ANALYSIS_REGRID_METHOD = BILIN
SERIES_ANALYSIS_REGRID_WIDTH = 2
SERIES_ANALYSIS_REGRID_VLD_THRESH = 0.0
SERIES_ANALYSIS_REGRID_SHAPE = SQUARE
SERIES_ANALYSIS_RUN_ONCE_PER_STORM_ID = False
SERIES_ANALYSIS_IS_PAIRED = False
SERIES_ANALYSIS_CONFIG_FILE = {PARM_BASE}/met_config/SeriesAnalysisConfig_wrapped
SERIES_ANALYSIS_OUTPUT_STATS_CNT = TOTAL, ME, RMSE, FBAR, OBAR, ANOM_CORR
MODEL = SFS-GSL-SA
OBTYPE = ERA5
[user_env_vars]
# This section contains some needed variables for the reformatting and plotting
# Paths to METdataio, METcalcpy, and METplotpy as needed for the use case
METDATAIO_BASE = {METPLUS_BASE}/../METdataio
METCALCPY_BASE = {METPLUS_BASE}/../METcalcpy
METPLOTPY_BASE = {METPLUS_BASE}/../METplotpy
PYTHONPATH = {METDATAIO_BASE}:{METDATAIO_BASE}/METdbLoad:{METDATAIO_BASE}/METdbLoad/ush:{METDATAIO_BASE}/METreformat:{METCALCPY_BASE}:{METCALCPY_BASE}/metcalcpy:{METPLOTPY_BASE}:{METPLOTPY_BASE}/metplotpy/plots
###
# Settings for the reformatting of the CTS linetype, to later be iused for plotting
###
REFORMAT_YAML_CONFIG_NAME = {PARM_BASE}/use_cases/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/reformat_CNT.yaml
# Input directory where the .stat files you need to reformat are located
REFORMAT_INPUT_DIR = {OUTPUT_BASE}/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/grid_stat
# Output directory to store the reformatted data
REFORMAT_OUTPUT_DIR = {OUTPUT_BASE}/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/reformatted
# Name of the output file containing reformatted data
REFORMAT_OUTPUT_FILENAME = reformat_CNT.data
# Line type to reformat
# Currently support FHO, CTC, CTS, CNT, SL1L2, VL1L2, PCT, MCTC, VCNT, ECNT, RHIST, TCDiag, and MPR line types
REFORMAT_LINETYPE = CNT
###
# Settings for creating the plots of ME and RMSE
###
# Directory where the YAML configurations for plotting are located
PLOTTING_YAML_CONFIG_DIR = {PARM_BASE}/use_cases/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture
# YAML Configuration file list
PLOTTING_YAML_CONFIG_FILE_LIST = custom_line_ME.yaml, custom_line_RMSE.yaml
#Input for plotting (this is the Reformatted data above
PLOTTING_STAT_INPUT = {REFORMAT_OUTPUT_DIR}/{REFORMAT_OUTPUT_FILENAME}
# Output directory for plots
PLOTTING_OUTPUT_DIR = {OUTPUT_BASE}/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/plots
# List of the output filenames for plotting. This should have the same number of files as
# PLOTTING_YAML_CONFIG_FILE_LIST and they should be in the same order
PLOTTING_OUTPUT_FILENAME_LIST = SoilMoisture_ME.png, SoilMoisture_RMSE.png
# Log file for the plotting
PLOTTING_LOG_FILENAME = {LOG_DIR}/plotting.log
[reformat_CNT]
###
# UserScript Settings to reformat the CNT linetype
# https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#userscript
###
USER_SCRIPT_RUNTIME_FREQ = RUN_ONCE
USER_SCRIPT_COMMAND = {PARM_BASE}/use_cases/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/reformat_CNT_linetype.py
[plot_stats]
###
# UserScript Settings to create the plots
# https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#userscript
###
USER_SCRIPT_RUNTIME_FREQ = RUN_ONCE
USER_SCRIPT_COMMAND = {PARM_BASE}/use_cases/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture/plot_line_stats.py
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}
SeriesAnalysisConfig_wrapped
////////////////////////////////////////////////////////////////////////////////
//
// Series-Analysis 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
//
//desc =
${METPLUS_DESC}
//
// Output observation type to be written
//
//obtype =
${METPLUS_OBTYPE}
////////////////////////////////////////////////////////////////////////////////
//
// Verification grid
// May be set separately in each "field" entry
//
//regrid = {
${METPLUS_REGRID_DICT}
////////////////////////////////////////////////////////////////////////////////
censor_thresh = [];
censor_val = [];
//cat_thresh =
${METPLUS_CAT_THRESH}
cnt_thresh = [ NA ];
cnt_logic = UNION;
//
// Forecast and observation fields to be verified
//
fcst = {
${METPLUS_FCST_FILE_TYPE}
${METPLUS_FCST_CAT_THRESH}
//field = [
${METPLUS_FCST_FIELD}
${METPLUS_FCST_CLIMO_MEAN_DICT}
${METPLUS_FCST_CLIMO_STDEV_DICT}
}
obs = {
${METPLUS_OBS_FILE_TYPE}
${METPLUS_OBS_CAT_THRESH}
//field = [
${METPLUS_OBS_FIELD}
${METPLUS_OBS_CLIMO_MEAN_DICT}
${METPLUS_OBS_CLIMO_STDEV_DICT}
}
////////////////////////////////////////////////////////////////////////////////
//
// Climatology data
//
//climo_mean = {
${METPLUS_CLIMO_MEAN_DICT}
//climo_stdev = {
${METPLUS_CLIMO_STDEV_DICT}
//climo_cdf = {
${METPLUS_CLIMO_CDF_DICT}
////////////////////////////////////////////////////////////////////////////////
//
// Confidence interval settings
//
ci_alpha = [ 0.05 ];
boot = {
interval = PCTILE;
rep_prop = 1.0;
n_rep = 0;
rng = "mt19937";
seed = "";
}
////////////////////////////////////////////////////////////////////////////////
//
// Verification masking regions
//
//mask = {
${METPLUS_MASK_DICT}
////////////////////////////////////////////////////////////////////////////////
//
// Gradient statistics
// May be set separately in each "obs.field" entry
//
//gradient = {
${METPLUS_GRADIENT_DICT}
//
// Number of grid points to be processed concurrently. Set smaller to use
// less memory but increase the number of passes through the data.
//
//block_size =
${METPLUS_BLOCK_SIZE}
//
// Ratio of valid matched pairs to compute statistics for a grid point
//
//vld_thresh =
${METPLUS_VLD_THRESH}
////////////////////////////////////////////////////////////////////////////////
//
// Statistical output types
//
//output_stats = {
${METPLUS_OUTPUT_STATS_DICT}
////////////////////////////////////////////////////////////////////////////////
//hss_ec_value =
${METPLUS_HSS_EC_VALUE}
rank_corr_flag = FALSE;
tmp_dir = "${MET_TMP_DIR}";
//version = "V10.0";
////////////////////////////////////////////////////////////////////////////////
${METPLUS_TIME_OFFSET_WARNING}
${METPLUS_MET_CONFIG_OVERRIDES}
Python Embedding
This script reads output from the SFS-GSL model, which provides soil moisture forecasts in separate monthly NetCDF files. It accepts command-line arguments specifying the file path, a valid forecast month, and year. The script loads forecast data (fcst) along with associated latitude, longitude, and target time variables. It converts the model’s target time values—representing months since January 1960—into actual calendar dates, then filters the data to retain only forecasts matching the specified valid month. It computes the ensemble mean over the 5-member ensemble and prepares the resulting data in a format suitable for input into the MET (Model Evaluation Tools) verification system. The code is located in the following directory:
parm/use_cases/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture
sfs_gsl_model_wrapper.py
# Script to read SFS-GSL model output.
# Separate monthly files
# Data description
# 5 member ensemble, target: forecast target month
# Variable: fcst(ensmem, target, lat, lon)
import sys
import re
import numpy as np
import datetime as dt
from datetime import datetime
from netCDF4 import Dataset, chartostring
import pandas as pd
print('Usage')
print('/location/of/model/data valid_month valid_year')
# Define inputs
#print('arguments: '+str(arguments))
path = sys.argv[0]
print('path: ' + path)
mod_path = sys.argv[1]
print('mod_path: ' + mod_path)
valid_month = sys.argv[2]
print('Valid Month: ' + valid_month)
year = sys.argv[3]
print('Year: ' + str(year))
valid_time = year+valid_month
valid_time = dt.datetime.strptime(valid_time,"%Y%m")
print('Reading input file')
# Setup data to be read into MET
f = Dataset(mod_path, 'r')
lat = f.variables['lat'][::-1]
lon = f.variables['lon'][:]
fcst = f.variables['fcst'][:]
target = f.variables['target'][:]
mon_since = datetime(1960, 1, 1)
target_dates = [mon_since + pd.DateOffset(months=int(months)) for months in target]
#Get valid month from arguments above
val_month = int(valid_month)
desired_dates = [i for i, date in enumerate(target_dates) if date.month == val_month]
if not desired_dates:
print(f"No data available for the specified month: {desired_month}")
else:
# Extract the forecast data for the specified month
fcst_for_month = fcst[:, desired_dates, :, :]
# Calculate the mean over the ensemble dimension (ensmem)
var = np.mean(fcst_for_month, axis=0)
val_time = valid_time
print('Valid Time: ' + str(val_time))
print('Shape of variable to read into MET: ' + str(var.shape))
#squeeze out all 1d arrays, add fill value, convert to float64
var = np.float64(var)
var[var < 0] = np.nan
met_data = np.squeeze(var).copy()
#create a metadata dictionary
attrs = {
'valid': str(val_time.strftime("%Y%m%d"))+'_'+str(val_time.strftime("%H%M%S")),
'init': str(val_time.strftime("%Y%m%d"))+'_'+str(val_time.strftime("%H%M%S")),
'name': 'Soil_moisture',
'long_name': 'SFS_GSL 0-1m soilm1m ensemble mean',
'lead': str(int(valid_month)),
'accum': '00',
'level': '0-1m',
'units': 'mm',
'grid': {
'name': 'Global 1 degree',
'type': 'LatLon',
'lat_ll': -90.0,
'lon_ll': 0.0,
'delta_lat': 1.0,
'delta_lon': 1.0,
'Nlon': f.dimensions['lon'].size,
'Nlat': f.dimensions['lat'].size,
}
}
print("valid time: " + repr(val_time.strftime("%Y%m%d%H%M")))
print("Attributes:\t" + repr(attrs))
f.close()
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 are two Python scripts used in this use case, called using the “UserScript” keyword in the METplus wrappers PROCESS_LIST configuration item. These scripts provide an interface to the functions in the METdataio, METcalcpy, and METplotpy Python modules of METplus. The functions used in these scripts demonstrate reformatting of the GridStat output to meet the format required by METcalcpy and METplotpy, and then plotting that reformatted output using functions from METcalcpy and METplotpy.
The first Python script is called reformat_CNT_linetype.py. This script takes the output CNT linetype from Grid Stat and reformats it so that the data can be plotted. The script takes an input .yaml file, reformat_CNT.yaml. Environment variables in the yaml file are specified in the [user_env_vars] section of the GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture.conf METplus configuration file.
The second Python script is plot_line_stats.py. This script creates line plots for ME and RMSE over time, using the YAML files custom_line_ME.yaml, and custom_line_RMSE.yaml Input variables to both scripts are set in the [user_env_vars] section of the GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture.conf file.
For more information about YAML configuration options for the line plots shown here, see the METplotpy line plot documentation.
Both Python scripts are located in the following directory:
parm/use_cases/model_applications/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture
reformat_CNT_linetype.py
#!/usr/bin/env python3
import os
import time
import logging
from METdbLoad.ush.read_data_files import ReadDataFiles
from METdbLoad.ush.read_load_xml import XmlLoadFile
from METreformat.write_stat_ascii import WriteStatAscii
from metcalcpy.util import read_env_vars_in_config as readconfig
logger = logging.getLogger(__name__)
def main():
# Read in the YAML configuration file. Environment variables in the
# configuration file are supported.
input_cnt_config_file = os.getenv("REFORMAT_YAML_CONFIG_NAME", "reformat_CNT.yaml")
settings = readconfig.parse_config(input_cnt_config_file)
logging.info(settings)
# Replacing the need for an XML specification file, pass in the XMLLoadFile and
# ReadDataFile parameters
rdf_obj: ReadDataFiles = ReadDataFiles()
xml_loadfile_obj: XmlLoadFile = XmlLoadFile(None)
# Retrieve all the filenames in the data_dir specified in the YAML config file
load_files = xml_loadfile_obj.filenames_from_template(settings['input_data_dir'],
{})
flags = xml_loadfile_obj.flags
print(flags)
line_types = xml_loadfile_obj.line_types
print(line_types)
beg_read_data = time.perf_counter()
rdf_obj.read_data(flags, load_files, line_types)
end_read_data = time.perf_counter()
time_to_read = end_read_data - beg_read_data
logger.info("Time to read input .stat data files using METdbLoad: %f", time_to_read)
file_df = rdf_obj.stat_data
# Check if the output file already exists, if so, delete it to avoid
# appending output from subsequent runs into the same file.
existing_output_file = os.path.join(settings['output_dir'], settings['output_filename'])
logger.info("Checking if {existing_output_file} already exists")
if os.path.exists(existing_output_file):
logger.info("Removing existing output file {existing_output_file}")
os.remove(existing_output_file)
# Write stat file in ASCII format
stat_lines_obj: WriteStatAscii = WriteStatAscii(settings, logger)
stat_lines_obj.write_stat_ascii(file_df, settings)
if __name__ == "__main__":
main()
plot_line_stats.py
#!/usr/bin/env python3
import os
from time import perf_counter
import logging
import yaml
import metcalcpy.util.read_env_vars_in_config as readconfig
from metplotpy.plots.line import line
def main():
# Read the input files
yaml_files_str = os.environ['PLOTTING_YAML_CONFIG_FILE_LIST'].split(',')
yaml_files = [yf.lstrip() for yf in yaml_files_str]
yaml_file_dir = os.environ['PLOTTING_YAML_CONFIG_DIR']
plot_output_file_list_str = os.environ['PLOTTING_OUTPUT_FILENAME_LIST'].split(',')
plot_output_files = [po.lstrip() for po in plot_output_file_list_str]
plot_output_dir = os.environ['PLOTTING_OUTPUT_DIR']
# Check to see that the two lists have the same number of elements
# If they dont', error out
if len(yaml_files) != len(plot_output_files):
raise RuntimeError('The number of files in PLOTTING_YAML_CONFIG_FILE_LIST must be equal to the number of files in PLOTTING_OUTPUT_FILENAME_LIST')
# Loop through data
for i,j in zip (yaml_files,plot_output_files):
os.environ['PLOTTING_YAML_CONFIG_NAME'] = os.path.join(yaml_file_dir,i)
os.environ['PLOTTING_OUTPUT_FILENAME'] = os.path.join(plot_output_dir,j)
# Read in the YAML configuration file. Environment variables in
# the configuration file are supported.
try:
input_config_file = os.getenv("PLOTTING_YAML_CONFIG_NAME", "custom_line.yaml")
settings = readconfig.parse_config(input_config_file)
logging.info(settings)
except yaml.YAMLError as exc:
logging.error(exc)
try:
start = perf_counter()
plot = line.Line(settings)
plot.save_to_file()
plot.write_html()
plot.write_output_file()
end = perf_counter()
execution_time = end - start
plot.logger.info(f"Finished creating line plot, execution time: {execution_time} seconds")
except ValueError as val_er:
print(val_er)
if __name__ == "__main__":
main()
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/s2s_soil_moisture/GridStat_fcstSFSGSL_obsERA5Land_SoilMoisture.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 the GridStat run will be found in the grid_stat directory (relative to OUTPUT_BASE) and will have the following files:
* grid_stat_SFS-GSL_vs_ERA5_060000L_YYYY0601_000000V_fho.txt
* grid_stat_SFS-GSL_vs_ERA5_060000L_YYYY0601_000000V_pairs.nc
* grid_stat_SFS-GSL_vs_ERA5_060000L_YYYY0601_000000V.stat
Each file should contain corresponding statistics for the line type(s) requested. For the netCDF file output from Grid Stat, 16 variable fields are present (not including the lat/lon fields). Those variables are:
* FCST_Soil_moisture_0-1m_FULL(lat, lon)
* FCST_Soil_moisture_0-1m_CONUS(lat, lon)
* OBS_soilm1m_20200601_000000_all_all_FULL(lat, lon)
* OBS_soilm1m_20200601_000000_all_all_CONUS(lat, lon)
* DIFF_Soil_moisture_0-1m_soilm1m_20200601_000000_all_all_FULL(lat, lon)
* DIFF_Soil_moisture_0-1m_soilm1m_20200601_000000_all_all_CONUS(lat, lon)
* FCST_CLIMO_MEAN_soilm1m_20190601_000000_all_all_FULL(lat, lon)
* FCST_CLIMO_MEAN_soilm1m_20190601_000000_all_all_CONUS(lat, lon)
* FCST_CLIMO_STDEV_soilm1m_20190601_000000_all_all_FULL(lat, lon)
* FCST_CLIMO_STDEV_soilm1m_20190601_000000_all_all_CONUS(lat, lon)
* OBS_CLIMO_MEAN_soilm1m_20190601_000000_all_all_FULL(lat, lon)
* OBS_CLIMO_MEAN_soilm1m_20190601_000000_all_all_CONUS(lat, lon)
* OBS_CLIMO_STDEV_soilm1m_20190601_000000_all_all_FULL(lat, lon)
* OBS_CLIMO_STDEV_soilm1m_20190601_000000_all_all_CONUS(lat, lon)
* OBS_CLIMO_CDF_soilm1m_20190601_000000_all_all_FULL(lat, lon)
* OBS_CLIMO_CDF_soilm1m_20190601_000000_all_all_CONUS(lat, lon)
The output from SeriesAnalysis will be in the series_analysis directory (relative to OUTPUT_BASE) and will contain 3 files:
* series_analysis_files_fcst_init_ALL_valid_ALL_lead_ALL.txt
* series_analysis_files_obs_init_ALL_valid_ALL_lead_ALL.txt
* SFS-GSL-SA_vs_ERA5_June.nc
The netCDF file from SeriesAnalysis contains 5 variable fields (not including the lat/lon fields). Those variables are:
* series_cnt_TOTAL(lat, lon)
* series_cnt_ME(lat, lon)
* series_cnt_RMSE(lat, lon)
* series_cnt_FBAR(lat, lon)
* series_cnt_OBAR(lat, lon)
The output from the first UserScript can be found in the reformatted directory (relative to OUTPUT_BASE) and will contain 1 file:
* reformat_CNT.data
The output from the second UserScript will be 2 plots found in the plots directory (relative to OUTPUT_BASE):
* SoilMoisture_ME.png
* SoilMoisture_RMSE.png
Keywords
Note
GridStatToolUseCase
SeriesAnalysisUseCase
UserScriptUseCase
PythonEmbeddingFileUseCase
S2SAppUseCase
S2SSoilMoistureAppUseCase
NetCDFFileUseCase
METdataioUseCase
METcalcpyUseCase
METplotpyUseCase
Navigate to the METplus Quick Search for Use Cases page to discover other similar use cases.