diff --git a/.github/parm/test_settings.conf b/.github/parm/test_settings.conf index 6bd022758..f8014615c 100644 --- a/.github/parm/test_settings.conf +++ b/.github/parm/test_settings.conf @@ -14,6 +14,8 @@ FILE_LISTS_DIR = {STAGING_DIR}/file_lists SCRUB_STAGING_DIR=False +EXIT_ON_WARN = True + # also set path to GempakToCF.jar for GEMPAK use cases GEMPAKTOCF_JAR = /data/input/GempakToCF.jar diff --git a/docs/Users_Guide/glossary.rst b/docs/Users_Guide/glossary.rst index e2f1d8991..4d2d8c3bf 100644 --- a/docs/Users_Guide/glossary.rst +++ b/docs/Users_Guide/glossary.rst @@ -15167,3 +15167,441 @@ METplus Configuration Glossary Specify the value for 'ensemble_flag.eas_width' in the MET configuration file for GenEnsProd. | *Used by:* GenEnsProd + + WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run. + Set to False to supress it. + + | *Used by:* All + + ASCII2NC_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for ASCII2NC. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* ASCII2NC + + DATA_INGEST_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for DataIngest. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* DataIngest + + ENSEMBLE_STAT_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for EnsembleStat. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* EnsembleStat + + EXTRACT_TILES_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for ExtractTiles. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* ExtractTiles + + GEN_VX_MASK_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for GenVxMask. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* GenVxMask + + GEN_ENS_PROD_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for GenEnsProd. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* GenEnsProd + + GRID_DIAG_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for GridDiag. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* GridDiag + + GRID_STAT_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for GridStat. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* GridStat + + IODA2NC_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for IODA2NC. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* IODA2NC + + MADIS2NC_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for MADIS2NC. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* MADIS2NC + + MODE_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for MODE. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* MODE + + MTD_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for MTD. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* MTD + + PAIR_STAT_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for PairStat. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* PairStat + + PB2NC_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for PB2NC. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* PB2NC + + PCP_COMBINE_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for PCPCombine. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* PCPCombine + + PLOT_DATA_PLANE_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for PlotDataPlane. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* PlotDataPlane + + PLOT_POINT_OBS_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for PlotPointObs. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* PlotPointObs + + POINT2GRID_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for Point2Grid. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* Point2Grid + + POINT_STAT_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for PointStat. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* PointStat + + PY_EMBED_INGEST_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for PyEmbedIngest. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* PyEmbedIngest + + REGRID_DATA_PLANE_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for RegridDataPlane. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* RegridDataPlane + + RMW_ANALYSIS_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for RMWAnalysis. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* RMWAnalysis + + SERIES_ANALYSIS_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for SeriesAnalysis. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* SeriesAnalysis + + STAT_ANALYSIS_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for StatAnalysis. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* StatAnalysis + + TC_DIAG_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for TCDiag. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* TCDiag + + TC_GEN_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for TCGen. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* TCGen + + TC_PAIRS_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for TCPairs. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* TCPairs + + TCRMW_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for TCRMW. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* TCRMW + + TC_STAT_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for TCStat. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* TCStat + + WAVELET_STAT_WARN_IF_DUPLICATE_OUTPUT + Log a warning message when an output file is written + more than once during a single METplus run for WaveletStat. + Set to False to supress it. + See also :term:`WARN_IF_DUPLICATE_OUTPUT`. + + | *Used by:* WaveletStat + + WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* All + + ASCII2NC_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for ASCII2NC + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* ASCII2NC + + DATA_INGEST_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for DataIngest. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* DataIngest + + ENSEMBLE_STAT_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for EnsembleStat. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* EnsembleStat + + EXTRACT_TILES_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for ExtractTiles. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* ExtractTiles + + GEN_VX_MASK_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for GenVxMask. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* GenVxMask + + GEN_ENS_PROD_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for GenEnsProd. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* GenEnsProd + + GRID_DIAG_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for GridDiag. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* GridDiag + + GRID_STAT_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for GridStat. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* GridStat + + IODA2NC_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for IODA2NC. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* IODA2NC + + MADIS2NC_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for MADIS2NC. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* MADIS2NC + + MODE_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for MODE. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* MODE + + MTD_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for MTD. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* MTD + + PAIR_STAT_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for PairStat. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* PairStat + + PB2NC_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for PB2NC. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* PB2NC + + PCP_COMBINE_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for PCPCombine. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* PCPCombine + + PLOT_DATA_PLANE_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for PlotDataPlane. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* PlotDataPlane + + PLOT_POINT_OBS_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for PlotPointObs. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* PlotPointObs + + POINT2GRID_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for Point2Grid. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* Point2Grid + + POINT_STAT_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for PointStat. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* PointStat + + PY_EMBED_INGEST_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for PyEmbedIngest. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* PyEmbedIngest + + REGRID_DATA_PLANE_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for RegridDataPlane. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* RegridDataPlane + + RMW_ANALYSIS_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for RMWAnalysis. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* RMWAnalysis + + SERIES_ANALYSIS_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for SeriesAnalysis. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* SeriesAnalysis + + STAT_ANALYSIS_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for StatAnalysis. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* StatAnalysis + + TC_DIAG_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for TCDiag. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* TCDiag + + TC_GEN_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for TCGen. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* TCGen + + TC_PAIRS_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for TCPairs. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* TCPairs + + TCRMW_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for TCRMW. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* TCRMW + + TC_STAT_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for TCStat. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* TCStat + + WAVELET_STAT_WARN_IF_OUTPUT_EXISTS + Log a warning message when an output file already exists for WaveletStat. + Set to True to output warning. See also :term:`WARN_IF_OUTPUT_EXISTS`. + + | *Used by:* WaveletStat + + EXIT_ON_WARN + Immediately exit METplus run when a warning message is encountered. + + | *Used by:* All diff --git a/docs/Users_Guide/systemconfiguration.rst b/docs/Users_Guide/systemconfiguration.rst index 596f3e7d4..592ce189c 100644 --- a/docs/Users_Guide/systemconfiguration.rst +++ b/docs/Users_Guide/systemconfiguration.rst @@ -1481,14 +1481,14 @@ This is the behavior that was previously executed when LOOP_ORDER = processes. VALID_END = 20190203 VALID_INCREMENT = 1d -will run in the following order:: +will run in the following order: - * PCPCombine at 2019-02-01 - * PCPCombine at 2019-02-02 - * PCPCombine at 2019-02-03 - * GridStat at 2019-02-01 - * GridStat at 2019-02-02 - * GridStat at 2019-02-03 +* PCPCombine at 2019-02-01 +* PCPCombine at 2019-02-02 +* PCPCombine at 2019-02-03 +* GridStat at 2019-02-01 +* GridStat at 2019-02-02 +* GridStat at 2019-02-03 .. _Custom_Looping: @@ -2232,6 +2232,10 @@ how these variables affect how the data is processed. FCST_SERIES_ANALYSIS_INPUT_TEMPLATE = I{init?fmt=%Y%m%d%H}_F{lead?fmt=%3H}_V{valid?fmt=%H} + SERIES_ANALYSIS_OUTPUT_DIR = /my/output/dir + + SERIES_ANALYSIS_OUTPUT_TEMPLATE = I{init?fmt=%Y%m%d%H}_F{lead?fmt=%3H}_V{valid?fmt=%H}.nc + In this example, the wrapper will go through all initialization and forecast lead times and find any files that match the template under /my/fcst/dir: @@ -2251,8 +2255,11 @@ Example 1: Run Once:: For this configuration, a single command will be built to call SeriesAnalysis. The wildcard character '\*' will replace init, valid, and lead in the template when attempting to find data to process. +The output files will use 'all' in place of the filename template tags that +are ambiguous. Template Used: I\*_F\*_V\* + Files Processed:: I2020101712_F003_V15 @@ -2262,6 +2269,10 @@ Files Processed:: I2020101912_F003_V15 I2020101912_F006_V18 +Output Created:: + + Iall_Fall_Vall.nc + Example 2 Run Once Per Initialization Time:: [config] @@ -2273,26 +2284,45 @@ The wildcard character '\*' will replace valid and lead in the template when attempting to find data to process. Runtime: Init: 2020-10-17 12Z + Template Used: I2020101712_F\*_V\* + Files Processed:: I2020101712_F003_V15 I2020101712_F006_V18 +Output Created:: + + I2020101712_Fall_Vall.nc + Runtime: Init: 2020-10-18 12Z + Template Used: I2020101812_F\*_V\* + Files Processed:: I2020101812_F003_V15 I2020101812_F006_V18 +Output Created:: + + I2020101812_Fall_Vall.nc + + Runtime: Init: 2020-10-19 12Z + Template Used: I2020101912_F\*_V\* + Files Processed:: I2020101912_F003_V15 I2020101912_F006_V18 +Output Created:: + + I2020101912_Fall_Vall.nc + .. note:: If LOOP_BY was set to VALID, then the values defined by VALID_BEG, VALID_END, and VALID_INCREMENT would be substituted for the valid time @@ -2309,21 +2339,33 @@ The wildcard character '\*' will replace valid and init in the template when attempting to find data to process. Runtime: Lead: 3 hour + Template Used: I\*_F003*_V\* + Files Processed:: I2020101712_F003_V15 I2020101812_F003_V15 I2020101912_F003_V15 +Output Created:: + + Iall_F003_Vall.nc + Runtime: Lead: 6 hour + Template Used: I\*_F006*_V\* + Files Processed:: I2020101712_F006_V18 I2020101812_F006_V18 I2020101912_F006_V18 +Output Created:: + + Iall_F006_Vall.nc + Example 4 Run Once For Each Time:: [config] @@ -2335,41 +2377,77 @@ The wildcard character '\*' will replace valid only in the template when attempting to find data to process. Runtime: Init: 2020-10-17 12Z, Forecast: 3 hour + Template Used: I2020101712_F003_V\* + Files Processed:: I2020101712_F003_V15 +Output Created:: + + I2020101712_F003_V15.nc + Runtime: Init: 2020-10-17 12Z, Forecast: 6 hour + Template Used: I2020101712_F006_V\* + Files Processed:: I2020101712_F006_V18 +Output Created:: + + I2020101712_F006_V18.nc + Runtime: Init: 2020-10-18 12Z, Forecast: 3 hour + Template Used: I2020101812_F003_V\* + Files Processed:: I2020101812_F003_V15 +Output Created:: + + I2020101812_F003_V15.nc + Runtime: Init: 2020-10-18 12Z, Forecast: 6 hour + Template Used: I2020101812_F006_V\* + Files Processed:: I2020101812_F006_V18 +Output Created:: + + I2020101812_F006_V18.nc + Runtime: Init: 2020-10-19 12Z, Forecast: 3 hour + Template Used: I2020101912_F003_V\* + Files Processed:: I2020101912_F003_V15 +Output Created:: + + I2020101912_F003_V15.nc + Runtime: Init: 2020-10-19 12Z, Forecast: 6 hour + Template Used: I2020101912_F006_V\* + Files Processed:: I2020101912_F006_V18 +Output Created:: + + I2020101912_F006_V18.nc + .. _config-utilities: diff --git a/docs/Users_Guide/wrappers.rst b/docs/Users_Guide/wrappers.rst index 49effc671..93f12af7d 100644 --- a/docs/Users_Guide/wrappers.rst +++ b/docs/Users_Guide/wrappers.rst @@ -67,7 +67,8 @@ METplus Configuration | :term:`ASCII2NC_INC_INIT_TIMES` | :term:`ASCII2NC_ALLOW_MISSING_INPUTS` | :term:`ASCII2NC_INPUT_THRESH` - +| :term:`ASCII2NC_WARN_IF_DUPLICATE_OUTPUT` +| :term:`ASCII2NC_WARN_IF_OUTPUT_EXISTS` | .. _ascii2nc-met-conf: @@ -214,6 +215,8 @@ Optional: | :term:`DATA_INGEST__AUTO_DECOMPRESS` | :term:`DATA_INGEST__USERNAME` | :term:`DATA_INGEST__PASSWORD` +| :term:`DATA_INGEST_WARN_IF_DUPLICATE_OUTPUT` +| :term:`DATA_INGEST_WARN_IF_OUTPUT_EXISTS` .. _ensemble_stat_wrapper: @@ -470,6 +473,8 @@ METplus Configuration | :term:`ENSEMBLE_STAT_INC_INIT_TIMES` | :term:`ENSEMBLE_STAT_ALLOW_MISSING_INPUTS` | :term:`ENSEMBLE_STAT_INPUT_THRESH` +| :term:`ENSEMBLE_STAT_WARN_IF_DUPLICATE_OUTPUT` +| :term:`ENSEMBLE_STAT_WARN_IF_OUTPUT_EXISTS` | .. warning:: **DEPRECATED:** @@ -1450,6 +1455,8 @@ the dimensions and density of the tiles comprising the subregion: | :term:`EXTRACT_TILES_VAR_LIST` | :term:`EXTRACT_TILES_SKIP_IF_OUTPUT_EXISTS` | :term:`EXTRACT_TILES_CUSTOM_LOOP_LIST` +| :term:`EXTRACT_TILES_WARN_IF_DUPLICATE_OUTPUT` +| :term:`EXTRACT_TILES_WARN_IF_OUTPUT_EXISTS` | .. warning:: **DEPRECATED:** @@ -1606,7 +1613,8 @@ METplus Configuration | :term:`GEN_ENS_PROD_INPUT_THRESH` | :term:`GEN_ENS_PROD_N_MEMBERS` | :term:`GEN_ENS_PROD_VLD_THRESH` - +| :term:`GEN_ENS_PROD_WARN_IF_DUPLICATE_OUTPUT` +| :term:`GEN_ENS_PROD_WARN_IF_OUTPUT_EXISTS` .. _gen-ens-prod-met-conf: @@ -2033,6 +2041,8 @@ Configuration | :term:`GEN_VX_MASK_INC_INIT_TIMES` | :term:`GEN_VX_MASK_ALLOW_MISSING_INPUTS` | :term:`GEN_VX_MASK_INPUT_THRESH` +| :term:`GEN_VX_MASK_WARN_IF_DUPLICATE_OUTPUT` +| :term:`GEN_VX_MASK_WARN_IF_OUTPUT_EXISTS` | .. _gfdl_tracker_wrapper: @@ -3346,6 +3356,8 @@ METplus Configuration | :term:`GRID_DIAG_OUTPUT_FLAG_HISTOGRAM_1D` | :term:`GRID_DIAG_OUTPUT_FLAG_HISTOGRAM_2D` | :term:`GRID_DIAG_OUTPUT_FLAG_INFO_THEORY` +| :term:`GRID_DIAG_WARN_IF_DUPLICATE_OUTPUT` +| :term:`GRID_DIAG_WARN_IF_OUTPUT_EXISTS` .. _grid-diag-met-conf: @@ -3715,7 +3727,8 @@ METplus Configuration | :term:`GRID_STAT_INPUT_THRESH` | :term:`GRID_STAT_GRADIENT_DX` | :term:`GRID_STAT_GRADIENT_DY` -| +| :term:`GRID_STAT_WARN_IF_DUPLICATE_OUTPUT` +| :term:`GRID_STAT_WARN_IF_OUTPUT_EXISTS` .. warning:: **DEPRECATED** @@ -4509,6 +4522,9 @@ METplus Configuration | :term:`IODA2NC_INC_INIT_TIMES` | :term:`IODA2NC_ALLOW_MISSING_INPUTS` | :term:`IODA2NC_INPUT_THRESH` +| :term:`IODA2NC_WARN_IF_DUPLICATE_OUTPUT` +| :term:`IODA2NC_WARN_IF_OUTPUT_EXISTS` + .. _ioda2nc-met-conf: @@ -4793,7 +4809,9 @@ METplus Configuration | :term:`MADIS2NC_INC_INIT_TIMES` | :term:`MADIS2NC_ALLOW_MISSING_INPUTS` | :term:`MADIS2NC_INPUT_THRESH` -| +| :term:`MADIS2NC_WARN_IF_DUPLICATE_OUTPUT` +| :term:`MADIS2NC_WARN_IF_OUTPUT_EXISTS` + .. _madis2nc-met-conf: @@ -5222,7 +5240,9 @@ METplus Configuration | :term:`MODE_TIME_OFFSET_WARNING` | :term:`MODE_ALLOW_MISSING_INPUTS` | :term:`MODE_INPUT_THRESH` -| +| :term:`MODE_WARN_IF_DUPLICATE_OUTPUT` +| :term:`MODE_WARN_IF_OUTPUT_EXISTS` + .. warning:: **DEPRECATED:** @@ -5984,7 +6004,9 @@ METplus Configuration | :term:`MTD_INC_INIT_TIMES` | :term:`MTD_ALLOW_MISSING_INPUTS` | :term:`MTD_INPUT_THRESH` -| +| :term:`MTD_WARN_IF_DUPLICATE_OUTPUT` +| :term:`MTD_WARN_IF_OUTPUT_EXISTS` + .. warning:: **DEPRECATED:** @@ -6406,6 +6428,9 @@ Configuration | :term:`PAIR_STAT_INC_INIT_TIMES` | :term:`PAIR_STAT_ALLOW_MISSING_INPUTS` | :term:`PAIR_STAT_INPUT_THRESH` +| :term:`PAIR_STAT_WARN_IF_DUPLICATE_OUTPUT` +| :term:`PAIR_STAT_WARN_IF_OUTPUT_EXISTS` + .. _pair-stat-met-conf: @@ -7338,6 +7363,9 @@ METplus Configuration | :term:`PB2NC_INC_INIT_TIMES` | :term:`PB2NC_ALLOW_MISSING_INPUTS` | :term:`PB2NC_INPUT_THRESH` +| :term:`PB2NC_WARN_IF_DUPLICATE_OUTPUT` +| :term:`PB2NC_WARN_IF_OUTPUT_EXISTS` + .. warning:: **DEPRECATED:** @@ -7697,7 +7725,8 @@ METplus Configuration | :term:`OBS_PCP_COMBINE_VLD_THRESH` | :term:`PCP_COMBINE_ALLOW_MISSING_INPUTS` | :term:`PCP_COMBINE_INPUT_THRESH` -| +| :term:`PCP_COMBINE_WARN_IF_DUPLICATE_OUTPUT` +| :term:`PCP_COMBINE_WARN_IF_OUTPUT_EXISTS` .. warning:: **DEPRECATED:** @@ -7763,6 +7792,8 @@ Configuration | :term:`PLOT_DATA_PLANE_INC_INIT_TIMES` | :term:`PLOT_DATA_PLANE_ALLOW_MISSING_INPUTS` | :term:`PLOT_DATA_PLANE_INPUT_THRESH` +| :term:`PLOT_DATA_PLANE_WARN_IF_DUPLICATE_OUTPUT` +| :term:`PLOT_DATA_PLANE_WARN_IF_OUTPUT_EXISTS` .. _plot_point_obs_wrapper: @@ -7832,6 +7863,8 @@ Configuration | :term:`PLOT_POINT_OBS_INC_INIT_TIMES` | :term:`PLOT_POINT_OBS_ALLOW_MISSING_INPUTS` | :term:`PLOT_POINT_OBS_INPUT_THRESH` +| :term:`PLOT_POINT_OBS_WARN_IF_DUPLICATE_OUTPUT` +| :term:`PLOT_POINT_OBS_WARN_IF_OUTPUT_EXISTS` .. _plot-point-obs-met-conf: @@ -8221,7 +8254,9 @@ METplus Configuration | :term:`POINT2GRID_MET_CONFIG_OVERRIDES` | :term:`POINT2GRID_ALLOW_MISSING_INPUTS` | :term:`POINT2GRID_INPUT_THRESH` -| +| :term:`POINT2GRID_WARN_IF_DUPLICATE_OUTPUT` +| :term:`POINT2GRID_WARN_IF_OUTPUT_EXISTS` + .. warning:: **DEPRECATED:** @@ -8595,7 +8630,9 @@ Configuration | :term:`POINT_STAT_POINT_WEIGHT_FLAG` | :term:`POINT_STAT_ALLOW_MISSING_INPUTS` | :term:`POINT_STAT_INPUT_THRESH` -| +| :term:`POINT_STAT_WARN_IF_DUPLICATE_OUTPUT` +| :term:`POINT_STAT_WARN_IF_OUTPUT_EXISTS` + .. warning:: **DEPRECATED:** @@ -9416,7 +9453,9 @@ METplus Configuration | :term:`PY_EMBED_INGEST_CUSTOM_LOOP_LIST` | :term:`PY_EMBED_INGEST__OUTPUT_FIELD_NAME` | :term:`PY_EMBED_INGEST_SKIP_IF_OUTPUT_EXISTS` -| +| :term:`PY_EMBED_INGEST_WARN_IF_DUPLICATE_OUTPUT` +| :term:`PY_EMBED_INGEST_WARN_IF_OUTPUT_EXISTS` + .. warning:: **DEPRECATED:** @@ -9472,7 +9511,9 @@ METplus Configuration | :term:`REGRID_DATA_PLANE_INC_INIT_TIMES` | :term:`REGRID_DATA_PLANE_ALLOW_MISSING_INPUTS` | :term:`REGRID_DATA_PLANE_INPUT_THRESH` -| +| :term:`REGRID_DATA_PLANE_WARN_IF_DUPLICATE_OUTPUT` +| :term:`REGRID_DATA_PLANE_WARN_IF_OUTPUT_EXISTS` + .. warning:: **DEPRECATED:** @@ -9528,7 +9569,9 @@ METplus Configuration | :term:`RMW_ANALYSIS_INC_VALID_TIMES` | :term:`RMW_ANALYSIS_SKIP_INIT_TIMES` | :term:`RMW_ANALYSIS_INC_INIT_TIMES` -| +| :term:`RMW_ANALYSIS_WARN_IF_DUPLICATE_OUTPUT` +| :term:`RMW_ANALYSIS_WARN_IF_OUTPUT_EXISTS` + .. _rmw-analysis-met-conf: @@ -10043,7 +10086,9 @@ METplus Configuration | :term:`SERIES_ANALYSIS_INPUT_THRESH` | :term:`SERIES_ANALYSIS_GRADIENT_DX` | :term:`SERIES_ANALYSIS_GRADIENT_DY` -| +| :term:`SERIES_ANALYSIS_WARN_IF_DUPLICATE_OUTPUT` +| :term:`SERIES_ANALYSIS_WARN_IF_OUTPUT_EXISTS` + .. warning:: **DEPRECATED:** @@ -10860,6 +10905,9 @@ The following values are optional in the METplus configuration file: | :term:`STAT_ANALYSIS_INC_VALID_TIMES` | :term:`STAT_ANALYSIS_SKIP_INIT_TIMES` | :term:`STAT_ANALYSIS_INC_INIT_TIMES` +| :term:`STAT_ANALYSIS_WARN_IF_DUPLICATE_OUTPUT` +| :term:`STAT_ANALYSIS_WARN_IF_OUTPUT_EXISTS` + .. warning:: **DEPRECATED:** @@ -11452,7 +11500,9 @@ METplus Configuration | :term:`TC_DIAG_INC_INIT_TIMES` | :term:`TC_DIAG_ALLOW_MISSING_INPUTS` | :term:`TC_DIAG_INPUT_THRESH` -| +| :term:`TC_DIAG_WARN_IF_DUPLICATE_OUTPUT` +| :term:`TC_DIAG_WARN_IF_OUTPUT_EXISTS` + .. _tc-diag-met-conf: @@ -11986,6 +12036,9 @@ METplus Configuration | :term:`TC_GEN_NC_PAIRS_GRID` | :term:`TC_GEN_ALLOW_MISSING_INPUTS` | :term:`TC_GEN_INPUT_THRESH` +| :term:`TC_GEN_WARN_IF_DUPLICATE_OUTPUT` +| :term:`TC_GEN_WARN_IF_OUTPUT_EXISTS` + .. warning:: **DEPRECATED:** @@ -12647,7 +12700,9 @@ METplus Configuration | :term:`TC_PAIRS_INC_VALID_TIMES` | :term:`TC_PAIRS_SKIP_INIT_TIMES` | :term:`TC_PAIRS_INC_INIT_TIMES` -| +| :term:`TC_PAIRS_WARN_IF_DUPLICATE_OUTPUT` +| :term:`TC_PAIRS_WARN_IF_OUTPUT_EXISTS` + .. warning:: **DEPRECATED:** @@ -13057,7 +13112,9 @@ METplus Configuration | :term:`TC_RMW_INC_VALID_TIMES` | :term:`TC_RMW_SKIP_INIT_TIMES` | :term:`TC_RMW_INC_INIT_TIMES` -| +| :term:`TCRMW_WARN_IF_DUPLICATE_OUTPUT` +| :term:`TCRMW_WARN_IF_OUTPUT_EXISTS` + .. _tc-rmw-met-conf: @@ -13486,7 +13543,9 @@ METplus Configuration | :term:`TC_STAT_INC_VALID_TIMES` | :term:`TC_STAT_SKIP_INIT_TIMES` | :term:`TC_STAT_INC_INIT_TIMES` -| +| :term:`TC_STAT_WARN_IF_DUPLICATE_OUTPUT` +| :term:`TC_STAT_WARN_IF_OUTPUT_EXISTS` + .. warning:: **DEPRECATED:** @@ -14252,6 +14311,9 @@ METplus Configuration | :term:`WAVELET_STAT_TIME_OFFSET_WARNING` | :term:`WAVELET_STAT_ALLOW_MISSING_INPUTS` | :term:`WAVELET_STAT_INPUT_THRESH` +| :term:`WAVELET_STAT_WARN_IF_DUPLICATE_OUTPUT` +| :term:`WAVELET_STAT_WARN_IF_OUTPUT_EXISTS` + .. _wavelet-stat-met-conf: diff --git a/docs/requirements.txt b/docs/requirements.txt index f47052913..29a78e3c1 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -pillow==12.2.0 +pillow==12.3.0 sphinx==8.2.3 sphinx-copybutton==0.5.2 sphinx-design==0.6.1 diff --git a/internal/scripts/dev_tools/add_met_config_helper.py b/internal/scripts/dev_tools/add_met_config_helper.py index a6a4d9518..91afa7bcf 100755 --- a/internal/scripts/dev_tools/add_met_config_helper.py +++ b/internal/scripts/dev_tools/add_met_config_helper.py @@ -9,15 +9,18 @@ import sys import os +from typing import Any try: - from metplus.util.string_manip import get_wrapper_name + from metplus.util.string_manip import get_wrapper_name, camel_to_underscore + from metplus.util.constants import LOWER_TO_WRAPPER_NAME except ImportError: # if metplus package is not installed, find util relative to this script metplus_home = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir) sys.path.insert(0, os.path.abspath(metplus_home)) - from metplus.util.string_manip import get_wrapper_name + from metplus.util.string_manip import get_wrapper_name, camel_to_underscore + from metplus.util.constants import LOWER_TO_WRAPPER_NAME SCRIPT_INFO_TEXT = ( 'This script is intended to help developers add support for setting ' @@ -29,8 +32,16 @@ 'added or replaced in the files. Do not include the dash lines.' ) +WRAPPERS_TO_SKIP = ( + 'CyclonePlotter', + 'Example', + 'GempakToCF', + 'GFDLTracker', + 'Usage', +) + -def print_doc_text(tool_name, input_dict): +def print_doc_text(tool_name, input_dict, skip_met_config=False): """! Format documentation for adding support for a new MET config variable through METplus wrappers. @@ -39,27 +50,38 @@ def print_doc_text(tool_name, input_dict): @param dict_items (optional) list of MET dictionary var items if met_var is a dictionary """ + tool_names = [] + if tool_name == "all": + for wrapper_name in LOWER_TO_WRAPPER_NAME.values(): + if wrapper_name not in WRAPPERS_TO_SKIP and camel_to_underscore(wrapper_name) not in tool_names: + tool_names.append(camel_to_underscore(wrapper_name)) + else: + tool_names = tool_name.split() + _print_script_info_text() - wrapper_camel = get_wrapper_name(tool_name) - if wrapper_camel is None: - print(f'ERROR: Invalid tool name: {tool_name}') - sys.exit(1) + wrapper_camel_list = [] + for tool in tool_names: + wrapper_camel = get_wrapper_name(tool) + if wrapper_camel is None: + print(f'ERROR: Invalid tool name: {tool}') + sys.exit(1) + wrapper_camel_list.append(wrapper_camel) # get info for each variable and store it in a dictionary - met_vars = _get_met_vars(tool_name, input_dict) + met_vars_dict = _get_met_vars(tool_names, wrapper_camel_list, input_dict) - _step_add_wrapper_env_var_keys(met_vars, tool_name) - _step_add_wrapper_config_dict(met_vars, tool_name) + _step_add_wrapper_content(met_vars_dict, skip_met_config) - _step_add_parm_metplus_config(met_vars, wrapper_camel) - _step_add_parm_met_config(met_vars, wrapper_camel) + _step_add_parm_metplus_config(met_vars_dict) + if not skip_met_config: + _step_add_parm_met_config(met_vars_dict) - _step_add_doc_metplus_config(met_vars, wrapper_camel) - _step_add_doc_met_config(met_vars, wrapper_camel) - _step_add_doc_glossary(met_vars, wrapper_camel) - _step_add_unit_tests(tool_name, met_vars) - _step_test_met_tool(wrapper_camel) + _step_add_unit_tests(met_vars_dict, skip_met_config) + _step_test_met_tool(met_vars_dict.keys()) + + _step_add_doc_wrappers(met_vars_dict, skip_met_config) + _step_add_doc_glossary(met_vars_dict, skip_met_config) _print_script_end_text() @@ -77,35 +99,41 @@ def _print_script_end_text(): _print_divider_line(before=False) -def _step_add_wrapper_env_var_keys(met_vars, tool_name): +def _step_add_wrapper_content(met_vars_dict, skip_met_config): _print_divider_line() - print(f'In metplus/wrappers/{tool_name}_wrapper.py\n\n' - f'In the {get_wrapper_name(tool_name)}Wrapper ' - f'class, add the following to the WRAPPER_ENV_VAR_KEYS class ' - f"variable list:\n") - _print_divider_line(char='-', after=False) - for var in met_vars: - print(f" '{var['env_var_name']}',") - _print_divider_line(char='-', before=False) + if skip_met_config: + print("Add handling of the METplus config variable as needed\n") + for _, (tool_name, met_vars) in met_vars_dict.items(): + print(f'In metplus/wrappers/{tool_name}_wrapper.py') + if skip_met_config: + continue -def _step_add_wrapper_config_dict(met_vars, tool_name): - _print_divider_line() - print(f'In metplus/wrappers/{tool_name}_wrapper.py\n') - print(f'In the create_c_dict function for ' - f'{get_wrapper_name(tool_name)}Wrapper, add a ' - 'function call to read the new METplus config variables and save ' - 'the value to be added to the wrapped MET config file.\n') - - _print_divider_line(char='-', after=False) - for var in met_vars: - _print_add_met_config(var) - _print_divider_line(char='-', before=False) + print(f'\nIn the {get_wrapper_name(tool_name)}Wrapper ' + f'class, add the following to the WRAPPER_ENV_VAR_KEYS class ' + f"variable list:") + _print_divider_line(char='-', after=False) + for var in met_vars: + print(f" '{var['env_var_name']}',") + _print_divider_line(char='-', before=False) + + print(f'In the create_c_dict function for ' + f'{get_wrapper_name(tool_name)}Wrapper, add a ' + 'function call to read the new METplus config variables and save ' + 'the value to be added to the wrapped MET config file.') + + _print_divider_line(char='-', after=False) + for var in met_vars: + _print_add_met_config(var) + _print_divider_line(char='-', before=False) + + if skip_met_config: + return - print("where DATA_TYPE can be string, list, int, float, bool, " + print("DATA_TYPE can be string, list, int, float, bool, " "or thresh. Refer to the METplus Contributor's Guide " "Basic Components section to see how to add additional info.\n") - print("Sometimes a function is written to handle MET config dictionary" + print("NOTE: Sometimes a function is written to handle MET config dictionary" " items that are complex and common to many wrappers." " Search for functions that start with handle_ in " "CommandBuilder or other parent class wrappers to see if a " @@ -113,99 +141,110 @@ def _step_add_wrapper_config_dict(met_vars, tool_name): "as an example to write a new one.") -def _step_add_parm_metplus_config(met_vars, wrapper_camel): +def _step_add_parm_metplus_config(met_vars_dict): _print_divider_line() - print(f'In parm/use_cases/met_tool_wrapper/{wrapper_camel}/{wrapper_camel}.conf') - print('\nAdd the new variables commented out in the basic use case') + for wrapper_camel, (tool_name, met_vars) in met_vars_dict.items(): + print(f'In parm/use_cases/met_tool_wrapper/{wrapper_camel}/{wrapper_camel}.conf') + print('\nAdd the new variables commented out in the basic use case') - _print_divider_line(char='-', after=False) + _print_divider_line(char='-', after=False) - for var in met_vars: - for mp_config in var['metplus_config_names']: - print(f'#{mp_config} =') + for var in met_vars: + for mp_config in var['metplus_config_names']: + print(f'#{mp_config} =') - _print_divider_line(char='-', before=False) + _print_divider_line(char='-', before=False) -def _step_add_parm_met_config(met_vars, wrapper_camel): +def _step_add_parm_met_config(met_vars_dict): + _print_divider_line() + for wrapper_camel, (tool_name, met_vars) in met_vars_dict.items(): + var_names = '/'.join([var['name'] for var in met_vars]) + print(f"In parm/met_config/{wrapper_camel}Config_wrapped\n\n" + "IMPORTANT: Compare the default values set for " + f"{var_names} " + "to the version" + f" in share/met/config/{wrapper_camel}Config_default. If " + "they do differ, make sure to add variables to the use case " + "config files so that they produce the same output.\n\n") + + for var in met_vars: + print("REPLACE:") + _print_divider_line(char='-', after=False) + print(f"{var['name']} = ...") + _print_divider_line(char='-', before=False) + print('with:') + _print_divider_line(char='-', after=False) + print(f"//{var['name']} ={' {' if var['dict_items'] else ''}") + print(f"${{{var['env_var_name']}}}") + _print_divider_line(char='-', before=False) + + +def _step_add_doc_wrappers(met_vars_dict, skip_met_config): _print_divider_line() + print("In docs/Users_Guide/wrappers.rst\n") + + for wrapper_camel, (tool_name, met_vars) in met_vars_dict.items(): + print(f"Under {wrapper_camel} => METplus Configuration section, add:") - var_names = '/'.join([var['name'] for var in met_vars]) - print(f"In parm/met_config/{wrapper_camel}Config_wrapped\n\n" - "IMPORTANT: Compare the default values set for " - f"{var_names} " - "to the version" - f" in share/met/config/{wrapper_camel}Config_default. If " - "they do differ, make sure to add variables to the use case " - "config files so that they produce the same output.\n\n") - - for var in met_vars: - print("REPLACE:") - _print_divider_line(char='-', after=False) - print(f"{var['name']} = ...") - _print_divider_line(char='-', before=False) - print('with:') _print_divider_line(char='-', after=False) - print(f"//{var['name']} ={' {' if var['dict_items'] else ''}") - print(f"${{{var['env_var_name']}}}") + for var in met_vars: + for metplus_config_name in var['metplus_config_names']: + print(f'| :term:`{metplus_config_name}`') _print_divider_line(char='-', before=False) + if skip_met_config: + continue -def _step_add_doc_metplus_config(met_vars, wrapper_camel): - _print_divider_line() - print(f"In docs/Users_Guide/wrappers.rst\n\n" - f"Under {wrapper_camel} => METplus Configuration section, add:") - - _print_divider_line(char='-', after=False) - for var in met_vars: - for metplus_config_name in var['metplus_config_names']: - print(f'| :term:`{metplus_config_name}`') - _print_divider_line(char='-', before=False) - - -def _step_add_doc_met_config(met_vars, wrapper_camel): - _print_divider_line() - print(f"In docs/Users_Guide/wrappers.rst\n\n" - f"Under {wrapper_camel} => MET Configuration section, add:") - _print_divider_line(char='-') - for var in met_vars: - _print_met_config_table(var) - _print_divider_line(char='-', before=False) + print(f"Under {wrapper_camel} => MET Configuration section, add:") + _print_divider_line(char='-') + for var in met_vars: + _print_met_config_table(var) + _print_divider_line(char='-', before=False) -def _step_add_doc_glossary(met_vars, wrapper_camel): +def _step_add_doc_glossary(met_vars_dict, skip_met_config): _print_divider_line() print("In docs/Users_Guide/glossary.rst\n\n" "Add the following anywhere in the file:") _print_divider_line(char='-') - for var in met_vars: - _print_glossary_entry(var, wrapper_camel) + for wrapper_camel, (_, met_vars) in met_vars_dict.items(): + for var in met_vars: + _print_glossary_entry(var, wrapper_camel, skip_met_config) _print_divider_line(char='-', before=False) -def _step_add_unit_tests(tool_name, met_vars): +def _step_add_unit_tests(met_vars_dict, skip_met_config): _print_divider_line() - print(f"In internal/tests/pytests/wrappers/{tool_name}/" - f"test_{tool_name}_wrapper.py" - "\n\nAdd the following items to " - "the tests to ensure the new items are set properly. Note: " - "if the tool does not have unit tests to check the handling of " - "MET config variables, you will need to add those tests. See " - "grid_stat/test_grid_stat_wrapper.py for an example. Change " - "VALUE to an appropriate value for the variable.\n") - - _print_divider_line(char='-', after=False) - for var in met_vars: - _print_unit_test(var) - _print_divider_line(char='-', before=False) + if skip_met_config: + print("Add any unit tests as needed") + for _, (tool_name, met_vars) in met_vars_dict.items(): + print(f"In internal/tests/pytests/wrappers/{tool_name}/test_{tool_name}_wrapper.py") + if skip_met_config: + continue -def _step_test_met_tool(wrapper_camel): + print("\nAdd the following items to " + "the tests to ensure the new items are set properly. Note: " + "if the tool does not have unit tests to check the handling of " + "MET config variables, you will need to add those tests. See " + "grid_stat/test_grid_stat_wrapper.py for an example. Change " + "VALUE to an appropriate value for the variable.\n") + + _print_divider_line(char='-', after=False) + for var in met_vars: + _print_unit_test(var) + _print_divider_line(char='-', before=False) + + +def _step_test_met_tool(wrapper_camel_list): # add note to test setting a valid value in the basic use case config file # to ensure that it is formatted properly when read by the MET tool _print_divider_line() - print(f"In parm/use_cases/met_tool_wrapper/{wrapper_camel}" - "\n\nVerify that the new METplus configuration variable(s) " + for wrapper_camel in wrapper_camel_list: + print(f"In parm/use_cases/met_tool_wrapper/{wrapper_camel}") + + print("\nVerify that the new METplus configuration variable(s) " "will be formatted properly when read by the MET tool by " "setting the variable(s) in the basic use case config files " "to a valid value " @@ -214,38 +253,47 @@ def _step_test_met_tool(wrapper_camel): "after you have confirmed this step.") -def _get_met_vars(tool_name, input_dict): - met_vars = [] - for var_name, dict_list in input_dict.items(): - metplus_var = f"{tool_name.upper()}_{var_name.upper().replace('.', '_')}" - env_var_name = f'METPLUS_{var_name.upper()}' - met_var = {'name': var_name, 'dict_items': dict_list, - 'metplus_config_names': [], 'met_config_names': []} - if not dict_list: - met_var['env_var_name'] = env_var_name - met_var['metplus_config_names'].append(metplus_var) - met_var['met_config_names'].append(var_name) - else: - met_var['env_var_name'] = f'{env_var_name}_DICT' - for item_name in dict_list: - metplus_config = f"{metplus_var}_{item_name.upper().replace('.', '_')}" - met_config = f"{var_name}.{item_name}" - met_var['metplus_config_names'].append(metplus_config) - met_var['met_config_names'].append(met_config) - - met_vars.append(met_var) - +def _get_met_vars(tool_names, wrapper_camel_list, input_dict): _print_divider_line() print('Generating instructions for adding support for:\n') - print(f"Wrapper: {get_wrapper_name(tool_name)}\n") + + met_vars_dict = {} + for tool_name, wrapper_camel in zip(tool_names, wrapper_camel_list): + met_vars = [] + for var_name, dict_list in input_dict.items(): + metplus_var = f"{tool_name.upper()}_{var_name.upper().replace('.', '_')}" + env_var_name = f'METPLUS_{var_name.upper()}' + met_var = {'name': var_name, 'dict_items': dict_list, + 'metplus_config_names': [], 'met_config_names': []} + if not dict_list: + met_var['env_var_name'] = env_var_name + met_var['metplus_config_names'].append(metplus_var) + met_var['met_config_names'].append(var_name) + else: + met_var['env_var_name'] = f'{env_var_name}_DICT' + for item_name in dict_list: + metplus_config = f"{metplus_var}_{item_name.upper().replace('.', '_')}" + met_config = f"{var_name}.{item_name}" + met_var['metplus_config_names'].append(metplus_config) + met_var['met_config_names'].append(met_config) + + met_vars.append(met_var) + + met_vars_dict[wrapper_camel] = (tool_name, met_vars) + + _print_met_vars(met_vars, tool_name) + + return met_vars_dict + + +def _print_met_vars(met_vars: list[Any], tool_name): + print(f"Wrapper: {get_wrapper_name(tool_name)}") for index, var in enumerate(met_vars, 1): - print(f"MET Variable {index}: {var['name']}") + print(f" MET Variable {index}: {var['name']}") if var['dict_items']: - print(f" Dictionary Items: {', '.join(var['dict_items'])}") + print(f" Dictionary Items: {', '.join(var['dict_items'])}") print() - return met_vars - def _print_divider_line(char='=', count=80, before=True, after=True): value = char * count @@ -290,14 +338,19 @@ def _print_met_config_table(var): print(list_table_text) -def _print_glossary_entry(var, wrapper_camel): +def _print_glossary_entry(var, wrapper_camel, skip_met_config): metplus_names = var['metplus_config_names'] met_names = var['met_config_names'] for metplus_config_name, met_config_name in zip(metplus_names, met_names): + variable_info = " REPLACE ME" + if not skip_met_config: + variable_info = ( + f" Specify the value for '{met_config_name}' " + f"in the MET configuration file" + ) + glossary_entry = ( - f" {metplus_config_name}\n" - f" Specify the value for '{met_config_name}' " - f"in the MET configuration file for {wrapper_camel}.\n\n" + f" {metplus_config_name}\n{variable_info} for {wrapper_camel}.\n\n" f" | *Used by:* {wrapper_camel}" ) print(f'{glossary_entry}\n') @@ -364,34 +417,54 @@ def _get_output_item(dict_items, met_config_name): def doc_util_usage(): """! Print usage statement for script """ + file = os.path.basename(__file__) print( 'Usage:\n' - f'{__file__} " []" ' - '" []"\n' - f"\nExample: {__file__} grid_stat output_prefix " + f'{file} " []" ' + '" []" [--skip_met_config]\n' + f"\nExample: {file} grid_stat output_prefix " "\n (simple variable named output_prefix)\n" - f'\nExample: {__file__} grid_stat "output_flag fho ctc mctc" ' + f'\nExample: {file} grid_stat "output_flag fho ctc mctc" ' '\n (dictionary named output_flag containing fho, ctc, and mctc)\n' - f'\nExample: {__file__} grid_stat "output_flag fho ctc mctc" ' + f'\nExample: {file} grid_stat "output_flag fho ctc mctc" ' 'output_prefix \n (both of the variables from the previous ' 'examples)\n' - f'\nExample: {__file__} point_stat "topo_mask interp.method interp.width" ' + f'\nExample: {file} point_stat "topo_mask interp.method interp.width" ' '\n (dictionary named topo_mask containing dictionary interp containing method and width)\n' + f"\nExample: {file} \"grid_stat point_stat\" output_prefix " + "\n (variable named output_prefix for multiple wrappers)\n" + f"\nExample: {file} all skip_warn_output_overwrite --skip_met_config" + "\n (variable named skip_warn_output_overwrite for all wrappers, skipping MET config steps)\n" ) if __name__ == "__main__": - # sys.argv[1] is MET tool name, i.e. grid_stat - # sys.argv[2+] is MET variable name, i.e. output_flag or a MET variable - # name followed by a list of MET dictionary var items separated by spaces - if len(sys.argv) < 3: - doc_util_usage() - sys.exit(1) - - TOOL_NAME = sys.argv[1] - INPUT_DICT = {} - for arg in sys.argv[2:]: + import argparse + + + # custom ArgumentParser for custom usage statement function + class CustomParser(argparse.ArgumentParser): + def error(self, message): + # Override standard error handling to print usage statement and exit + doc_util_usage() + sys.exit(1) + + parser = CustomParser(add_help=False) + + # Add optional boolean flag + parser.add_argument( + "--skip_met_config", + action="store_true", + help="Skip steps specific to MET config variables", + ) + + parser.add_argument("TOOL_NAME") + parser.add_argument("raw_vars", nargs="+") + args = parser.parse_args() + + variable_info = {} + for arg in args.raw_vars: variable_name, *dictionary_items = arg.split() - INPUT_DICT[variable_name] = dictionary_items + variable_info[variable_name] = dictionary_items - print_doc_text(TOOL_NAME, INPUT_DICT) + print_doc_text(args.TOOL_NAME, variable_info, skip_met_config=args.skip_met_config) diff --git a/internal/tests/pytests/conftest.py b/internal/tests/pytests/conftest.py index 480bbfb7e..bb69c0014 100644 --- a/internal/tests/pytests/conftest.py +++ b/internal/tests/pytests/conftest.py @@ -341,3 +341,21 @@ def run_all_and_check_missing_run_error(wrapper, missing, run, errors): assert wrapper.errors == errors return run_all_and_check_missing_run_error + +@pytest.fixture(scope="module") +def check_warn_output_overwrite(): + def check_warn_output_overwrite_wrapper(wrapper, expected_warning, expected_path): + if expected_warning: + # Verify that a warning was logged + assert wrapper.logger.warning.called + + # Extract and verify the warning message content + # Using the same pattern as existing tests in the file + last_msg = wrapper.logger.warning.call_args_list[-1][0][0] + assert "Output has already been written" in last_msg + assert expected_path in last_msg + else: + # Verify that no warning was logged for new files + assert not wrapper.logger.warning.called + + return check_warn_output_overwrite_wrapper diff --git a/internal/tests/pytests/pytest.ini b/internal/tests/pytests/pytest.ini deleted file mode 100644 index 2851d2060..000000000 --- a/internal/tests/pytests/pytest.ini +++ /dev/null @@ -1,12 +0,0 @@ -[pytest] -markers = - run_metplus: custom marker for testing run_metplus.py script - util: custom marker for testing metplus/util logic - wrapper_a: custom marker for testing metplus/wrapper logic - A group - wrapper_b: custom marker for testing metplus/wrapper logic - B group - wrapper_c: custom marker for testing metplus/wrapper logic - C group - wrapper_d: custom marker for testing metplus/wrapper logic - D group - wrapper: custom marker for testing metplus/wrapper logic - all others - long: custom marker for tests that take a long time to run - plotting: custom marker for tests that involve plotting - diff: custom marker for diff util tests that require additional packages diff --git a/internal/tests/pytests/requirements_3.12.txt b/internal/tests/pytests/requirements_3.12.txt index 4c5a871df..76f895081 100644 --- a/internal/tests/pytests/requirements_3.12.txt +++ b/internal/tests/pytests/requirements_3.12.txt @@ -1,7 +1,7 @@ netCDF4>=1.7.2 pandas>=2.2.3 pdf2image>=1.17.0 -pillow>=12.2.0 +pillow>=12.3.0 pytest>=9.0.3 pytest-cov>=6.0.0 python-dateutil>=2.9.0.post0 diff --git a/internal/tests/pytests/requirements_3.14.txt b/internal/tests/pytests/requirements_3.14.txt index 9b7f850d6..0d75e79cf 100644 --- a/internal/tests/pytests/requirements_3.14.txt +++ b/internal/tests/pytests/requirements_3.14.txt @@ -1,7 +1,7 @@ netCDF4>=1.7.4 pandas>=3.0.2 pdf2image>=1.17.0 -pillow>=12.2.0 +pillow>=12.3.0 pytest>=9.0.3 pytest-cov>=7.1.0 python-dateutil>=2.9.0.post0 diff --git a/internal/tests/pytests/util/run_util/test_run_util.py b/internal/tests/pytests/util/run_util/test_run_util.py index f409a82fb..b8a0cb60f 100644 --- a/internal/tests/pytests/util/run_util/test_run_util.py +++ b/internal/tests/pytests/util/run_util/test_run_util.py @@ -50,6 +50,7 @@ 'METPLUS_VERSION', 'ALLOW_MISSING_INPUTS', 'INPUT_THRESH', + 'EXIT_ON_WARN', ] def remove_output_base(config): @@ -473,3 +474,92 @@ def test_post_run_cleanup_errors(post_run_config): post_run_config, ['Check the log file for more information: /log/file.log'] ) assert mock.call(err_msg) in post_run_config.logger.error.call_args_list + +success_settings = { + 'PROCESS_LIST': 'GridStat, PCPCombine', + 'LOOP_BY': 'INIT', + 'INIT_TIME_FMT': '%Y%m%d%H', + 'INIT_BEG': '2005080700', + 'INIT_END': '2005080700', + 'INIT_INCREMENT': '12H', + 'LEAD_SEQ': '12H', + 'FCST_GRID_STAT_INPUT_DIR': '{INPUT_BASE}/met_test/data/sample_fcst', + 'FCST_GRID_STAT_INPUT_TEMPLATE': '{init?fmt=%Y%m%d%H}/wrfprs_ruc13_{lead?fmt=%HH}.tm00_G212', + 'OBS_GRID_STAT_INPUT_DIR': '{INPUT_BASE}/met_test/new', + 'OBS_GRID_STAT_INPUT_TEMPLATE': 'ST2ml{valid?fmt=%Y%m%d%H}_A03h.nc', + 'GRID_STAT_OUTPUT_TEMPLATE': '{init?fmt=%Y%m%d%H}', + 'FCST_PCP_COMBINE_RUN': True, + 'FCST_PCP_COMBINE_METHOD': 'SUM', + 'FCST_PCP_COMBINE_INPUT_DIR': '/some/dir', + 'FCST_PCP_COMBINE_INPUT_TEMPLATE': '{init?fmt=%Y%m%d}/file.{init?fmt=%Y%m%d%H}f{lead?fmt=%HHH}.nc', + 'FCST_PCP_COMBINE_OUTPUT_TEMPLATE': 'file.{init?fmt=%Y%m%d%H}.f{lead?fmt=%HHH}.nc', + 'BOTH_VAR1_NAME': 'APCP', + 'BOTH_VAR1_LEVELS': 'A03', + 'BOTH_VAR1_THRESH': 'gt1, gt5', + 'INPUT_MUST_EXIST': 'False', + 'DO_NOT_RUN_EXE': 'True', +} + +@pytest.mark.parametrize( + 'exit_on_warn, config_overrides, expected_init_errors, expected_run_errors', + [ + # successful run without warnings or errors + (False, {}, 0, 0), + (True, {}, 0, 0), + # warning in init - GridStat (1st process) + (False, {'GRID_STAT_FCST_INPUT_TEMPLATE': 'template.nc'}, 0, 0), + (True, {'GRID_STAT_FCST_INPUT_TEMPLATE': 'template.nc'}, 1, 0), + # warning in init - PCPCombine (2nd process) + (False, {'PCP_COMBINE_FCST_INPUT_TEMPLATE': 'template.nc'}, 0, 0), + (True, {'PCP_COMBINE_FCST_INPUT_TEMPLATE': 'template.nc'}, 1, 0), + # warning in init - both processes + (False, {'GRID_STAT_FCST_INPUT_TEMPLATE': 'template.nc','PCP_COMBINE_FCST_INPUT_TEMPLATE': 'template.nc'}, 0, 0), + (True, {'GRID_STAT_FCST_INPUT_TEMPLATE': 'template.nc', 'PCP_COMBINE_FCST_INPUT_TEMPLATE': 'template.nc'}, 1, 0), + # warning in run - GridStat (1st process) + (False, {'GRID_STAT_WARN_IF_OUTPUT_EXISTS': True}, 0, 0), + (True, {'GRID_STAT_WARN_IF_OUTPUT_EXISTS': True}, 0, 1), + # warning in run - PCPCombine (2nd process) + (False, {'PCP_COMBINE_WARN_IF_OUTPUT_EXISTS': True}, 0, 0), + (True, {'PCP_COMBINE_WARN_IF_OUTPUT_EXISTS': True}, 0, 1), + # warning in run - both processes + (False, {'GRID_STAT_WARN_IF_OUTPUT_EXISTS': True, 'PCP_COMBINE_WARN_IF_OUTPUT_EXISTS': True}, 0, 0), + (True, {'GRID_STAT_WARN_IF_OUTPUT_EXISTS': True, 'PCP_COMBINE_WARN_IF_OUTPUT_EXISTS': True}, 0, 1), + ], +) +@pytest.mark.util +def test_exit_on_warn(metplus_config_files, tmp_path_factory, make_dummy_empty, exit_on_warn, config_overrides, expected_init_errors, expected_run_errors): + # create fake output directories to test warnings when output already exists + fake_output_dir = tmp_path_factory.mktemp("output_dir") + make_dummy_empty(fake_output_dir, 'grid_stat/2005080700/grid_stat_120000L_20050807_120000V.stat') + make_dummy_empty(fake_output_dir, 'pcp_combine/file.2005080700.f012.nc') + + initial_overrides = [ + f'config.GRID_STAT_OUTPUT_DIR={fake_output_dir}/grid_stat', + f'config.FCST_PCP_COMBINE_OUTPUT_DIR={fake_output_dir}/pcp_combine', + ] + + # set EXIT_ON_WARN before config init because it is currently read in config_metplus.launch + # after refactor to support wrapper-specific EXIT_ON_WARN, this can be set in config_overrides instead + + if exit_on_warn: + initial_overrides.append('config.EXIT_ON_WARN=True') + config = metplus_config_files(initial_overrides) + + for key, value in success_settings.items(): + config.set('config', key, value) + + for key, value in config_overrides.items(): + config.set('config', key, value) + + process_list = ru.get_process_list(config) + processes = ru._load_all_wrappers(config, process_list) + + # initialization warning when EXIT_ON_WARN=True cause processes to be None + if expected_init_errors: + assert processes is None + else: + assert processes + + all_commands = ru._run_processes(processes) + total_errors = ru._get_total_errors_and_log_counts(processes, config.logger) + assert total_errors == expected_run_errors diff --git a/internal/tests/pytests/wrappers/command_builder/test_command_builder.py b/internal/tests/pytests/wrappers/command_builder/test_command_builder.py index 13613b880..9f366a78c 100644 --- a/internal/tests/pytests/wrappers/command_builder/test_command_builder.py +++ b/internal/tests/pytests/wrappers/command_builder/test_command_builder.py @@ -1255,3 +1255,51 @@ def test_errors_and_defaults(metplus_config): assert actual is None assert _in_last_err('bar', cb.logger) + +TEST_SEARCH_PATH = ( + '/output/path/precip24_2026070112.nc', + '/output/path/precip24_2026070118.nc', + '/output/path/grid_stat_120000L_20050807_120000V*', + '/output/path/grid_stat_WRF_APCP_vs_MC_PCP_APCP_03_120000L_20050807_120000V*', +) +@pytest.mark.parametrize( + 'output_paths, expected_warnings', [ + # Case 1: First time writing a file (no warning) + ([TEST_SEARCH_PATH[0]], [False]), + + # Case 2: Writing the same file twice (warning on second call) + ([TEST_SEARCH_PATH[0], TEST_SEARCH_PATH[0]], [False, True]), + + # Case 3: Multiple different files (no warnings) + ([TEST_SEARCH_PATH[0], TEST_SEARCH_PATH[1]], [False, False]), + + # Case 4: Search format/wildcard path (warning on repeat) + ([TEST_SEARCH_PATH[2], TEST_SEARCH_PATH[2]], [False, True]), + + # Case 5: Search different wildcard paths + ([TEST_SEARCH_PATH[2], TEST_SEARCH_PATH[3]], [False, False]), + ] +) +@pytest.mark.wrapper +def test_check_if_output_has_been_written(metplus_config, check_warn_output_overwrite, + output_paths, expected_warnings): + """!Test that CommandBuilder tracks written files and warns on overwrites. + """ + # Create a CommandBuilder instance using the existing metplus_config fixture + cb = CommandBuilder(metplus_config) + + # Iterate through the sequence of file write attempts + for path, should_warn in zip(output_paths, expected_warnings): + # Reset the mock logger for each path to isolate the check + cb.logger.warning.reset_mock() + + # Call the new logic + cb._check_if_output_has_been_written(path) + + check_warn_output_overwrite(cb, should_warn, path) + + # Final check: verify all unique paths were added to the tracking list + unique_paths = list(dict.fromkeys(output_paths)) + assert len(cb.output_written) == len(unique_paths) + for path in unique_paths: + assert path in cb.output_written diff --git a/internal/tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py b/internal/tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py index 3eb4200a2..dcd007f2c 100644 --- a/internal/tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py +++ b/internal/tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py @@ -951,3 +951,48 @@ def test_get_config_file(metplus_config): config.set('config', 'GRID_STAT_CONFIG_FILE', fake_config_name) wrapper = GridStatWrapper(config) assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name + +@pytest.mark.parametrize( + 'config_overrides, expected_warning, expected_path', [ + ({}, False, 'grid_stat_120000L_20050808_000000V*'), + # run once per field without unique prefix + ({'GRID_STAT_ONCE_PER_FIELD': True}, + True, 'grid_stat_120000L_20050808_000000V*'), + # run once per field with unique prefix + ({'GRID_STAT_ONCE_PER_FIELD': True, 'GRID_STAT_OUTPUT_PREFIX': '{fcst_name}_vs_{obs_name}'}, + False, 'grid_stat_ABCD_vs_OBCD_120000L_20050808_000000V*'), + ] +) +@pytest.mark.wrapper_b +def test_grid_stat_warn_on_overwrite(metplus_config, check_warn_output_overwrite, + config_overrides, expected_warning, expected_path): + config = metplus_config + set_minimum_config_settings(config) + + # set additional settings + config.set('config', 'FCST_VAR2_NAME', 'FBCD') + config.set('config', 'FCST_VAR2_LEVELS', fcst_level) + config.set('config', 'OBS_VAR2_NAME', 'OBCD') + config.set('config', 'OBS_VAR2_LEVELS', obs_level) + config.set('config', 'BOTH_VAR2_THRESH', both_thresh) + config.set('config', 'GRID_STAT_OUTPUT_TEMPLATE', '') + + # set config variable overrides + for key, value in config_overrides.items(): + config.set('config', key, value) + + wrapper = GridStatWrapper(config) + + # create one of the expected output files to ensure that the existence + # of this file does not affect the logging of the warning message + output_dir = wrapper.config.getdir('GRID_STAT_OUTPUT_DIR') + os.makedirs(output_dir, exist_ok=True) + for output_file in ('grid_stat_120000L_20050808_000000V.stat', 'grid_stat_FBCD_vs_OBCD_120000L_20050808_000000V.stat'): + output_path = os.path.join(output_dir, output_file) + with open(output_path, 'w') as file_handle: + file_handle.write('') + assert os.path.exists(output_path) + + wrapper.run_all_times() + + check_warn_output_overwrite(wrapper, expected_warning, expected_path) diff --git a/internal/tests/pytests/wrappers/series_analysis/test_series_analysis.py b/internal/tests/pytests/wrappers/series_analysis/test_series_analysis.py index 87ccbc3f7..f3f4a8d0d 100644 --- a/internal/tests/pytests/wrappers/series_analysis/test_series_analysis.py +++ b/internal/tests/pytests/wrappers/series_analysis/test_series_analysis.py @@ -6,6 +6,7 @@ from metplus.wrappers.series_analysis_wrapper import SeriesAnalysisWrapper from metplus.wrappers import series_analysis_wrapper as saw +from metplus.util.run_util import get_process_list, _load_all_wrappers, _run_processes fcst_dir = '/some/fcst/dir' obs_dir = '/some/obs/dir' @@ -95,7 +96,7 @@ def set_minimum_config_settings(config): config.set('config', 'SERIES_ANALYSIS_OUTPUT_DIR', '{OUTPUT_BASE}/SeriesAnalysis/output') config.set('config', 'SERIES_ANALYSIS_OUTPUT_TEMPLATE', - '{init?fmt=%Y%m%d%H}') + 'sa_{init?fmt=%Y%m%d%H}.nc') config.set('config', 'FCST_VAR1_NAME', fcst_name) config.set('config', 'FCST_VAR1_LEVELS', fcst_level) @@ -647,7 +648,7 @@ def test_series_analysis_single_field(metplus_config, config_overrides, expected_cmds = [] for run_time in run_times: - cmd = (f"{app_path} {file_args} -out {out_dir}/{extra_args}" + cmd = (f"{app_path} {file_args} -out {out_dir}/sa_.nc{extra_args}" f"-config {config_file} {verbosity}") expected_cmds.append(cmd.replace('', run_time)) @@ -1304,3 +1305,75 @@ def test_get_fcst_and_obs_path(metplus_config, actual = wrapper._get_fcst_and_obs_path(time_info, '*', lead_group) assert actual == expected +@pytest.mark.parametrize( + 'config_overrides, expected_warning, expected_path', [ + # no warning + ({}, False, None), + # 2 fields/vars, so output is overwritten + ({'FCST_VAR2_NAME': 'FBCD', 'FCST_VAR2_LEVELS': fcst_level, + 'OBS_VAR2_NAME': 'OBCD', 'OBS_VAR2_LEVELS': obs_level,}, + True, '2005080712.nc'), + # 2 fields/vars, output template includes field, so output is not overwritten + ({'FCST_VAR2_NAME': 'FBCD', 'FCST_VAR2_LEVELS': fcst_level, + 'OBS_VAR2_NAME': 'OBCD', 'OBS_VAR2_LEVELS': obs_level, + 'SERIES_ANALYSIS_OUTPUT_TEMPLATE': 'sa_{init?fmt=%Y%m%d%H}_{fcst_name}.nc',}, + False, None), + # output template does not include init + ({'SERIES_ANALYSIS_OUTPUT_TEMPLATE': 'sa_F{lead?fmt=%H}.nc'}, True, 'sa_Fall.nc'), + # run once per lead, template does not include lead + ({'SERIES_ANALYSIS_RUNTIME_FREQ': 'RUN_ONCE_PER_LEAD', 'LEAD_SEQ': '12H, 18H'}, True, 'sa_all.nc'), + # run once per lead, template includes lead + ({'SERIES_ANALYSIS_OUTPUT_TEMPLATE': 'sa_F{lead?fmt=%H}.nc', + 'SERIES_ANALYSIS_RUNTIME_FREQ': 'RUN_ONCE_PER_LEAD', 'LEAD_SEQ': '12H, 18H'}, + False, None), + + # custom loop list causes multiple duplicate runs + ({'SERIES_ANALYSIS_CUSTOM_LOOP_LIST': 'A,B'}, True, '2005080712.nc'), + # custom loop list with custom string in output template + ({'SERIES_ANALYSIS_CUSTOM_LOOP_LIST': 'A,B', + 'SERIES_ANALYSIS_OUTPUT_TEMPLATE': 'sa_{init?fmt=%Y%m%d%H}_{custom}.nc'}, False, None), + # custom loop list with custom string in output template but no init in template + ({'SERIES_ANALYSIS_CUSTOM_LOOP_LIST': 'A,B', + 'SERIES_ANALYSIS_OUTPUT_TEMPLATE': 'sa_{custom}.nc'}, True, 'sa_B.nc'), + + # multiple instances cause multiple duplicate runs + ({'PROCESS_LIST': 'SeriesAnalysis(A), SeriesAnalysis(B)'}, True, '2005080712.nc'), + # multiple instances with instance string in output template + ({'PROCESS_LIST': 'SeriesAnalysis(A), SeriesAnalysis(B)', + 'SERIES_ANALYSIS_OUTPUT_TEMPLATE': 'sa_{init?fmt=%Y%m%d%H}_{instance}.nc'}, False, None), + # multiple instances with instance string in output template but no init in template + ({'PROCESS_LIST': 'SeriesAnalysis(A), SeriesAnalysis(B)', + 'SERIES_ANALYSIS_OUTPUT_TEMPLATE': 'sa_{instance}.nc'}, True, 'sa_B.nc'), + + ] +) +@pytest.mark.wrapper_b +def test_series_analysis_warn_on_overwrite(metplus_config, check_warn_output_overwrite, + config_overrides, expected_warning, expected_path): + config = metplus_config + set_minimum_config_settings(config) + + # set config variable overrides + for key, value in config_overrides.items(): + config.set('config', key, value) + + process_list = get_process_list(config) + processes = _load_all_wrappers(config, process_list) + assert processes + + for wrapper in processes: + + # create one of the expected output files to ensure that the existence + # of this file does not affect the logging of the warning message + output_dir = wrapper.config.getdir('SERIES_ANALYSIS_OUTPUT_DIR') + os.makedirs(output_dir, exist_ok=True) + for output_file in ('2005080700.nc', '2005080712.nc'): + output_path = os.path.join(output_dir, output_file) + with open(output_path, 'w') as file_handle: + file_handle.write('') + assert os.path.exists(output_path) + + _run_processes(processes) + + for wrapper in processes: + check_warn_output_overwrite(wrapper, expected_warning, expected_path) diff --git a/metplus/util/__init__.py b/metplus/util/__init__.py index edd8a2a0d..5ec9dbcd8 100644 --- a/metplus/util/__init__.py +++ b/metplus/util/__init__.py @@ -1,5 +1,6 @@ from .metplus_check import * from .constants import * +from .exceptions import * from .string_manip import * from .system_util import * from .time_util import * diff --git a/metplus/util/config_metplus.py b/metplus/util/config_metplus.py index 679b81d30..884e4c3da 100644 --- a/metplus/util/config_metplus.py +++ b/metplus/util/config_metplus.py @@ -22,6 +22,7 @@ from metplus.produtil.config import ProdConfig from .constants import RUNTIME_CONFS, MISSING_DATA_VALUE +from .exceptions import MPWarningError from .string_template_substitution import do_string_sub from .string_manip import getlist, remove_quotes from .string_manip import validate_thresholds, find_indices_in_config_section @@ -83,6 +84,16 @@ # set all loggers to use UTC logging.Formatter.converter = time.gmtime +# logging handler for exiting on warnings if EXIT_ON_WARN is True +class ExitOnWarningHandler(logging.Handler): + def __init__(self): + super().__init__(level=logging.WARNING) + + def emit(self, record): + if record.levelno == logging.WARNING: + self.format(record) + raise MPWarningError() + def setup(args, base_confs=None): """!Setup the METplusConfig by reading in default configurations and any @@ -268,6 +279,11 @@ def launch(config_list): with open(final_conf, 'wt') as file_handle: config.write(file_handle) + # add warning handler to exit when a warning occurs if EXIT_ON_WARN is True + if config.getbool('config', 'EXIT_ON_WARN', False): + warning_handler = ExitOnWarningHandler() + config.logger.addHandler(warning_handler) + return config diff --git a/metplus/util/constants.py b/metplus/util/constants.py index cd1c00396..0624d4f32 100644 --- a/metplus/util/constants.py +++ b/metplus/util/constants.py @@ -192,3 +192,10 @@ def debug(self, message): pass # skip logging def info(self, message): pass # skip logging def warning(self, message): pass # skip logging def error(self, message): pass # skip logging + +# config variable names that involve warnings and their default values +# generic and wrapper-specific versions are supported +WARNING_CONFIGS = { + 'WARN_IF_DUPLICATE_OUTPUT': True, + 'WARN_IF_OUTPUT_EXISTS': False, +} diff --git a/metplus/util/exceptions.py b/metplus/util/exceptions.py new file mode 100644 index 000000000..145d0dfa5 --- /dev/null +++ b/metplus/util/exceptions.py @@ -0,0 +1,9 @@ +class MPWarningError(Exception): + """Raised when a warning is encountered and EXIT_ON_WARN is True""" + + def __init__(self): + # Pass a clear error message to the base Exception class + super().__init__( + "Encountered a warning when EXIT_ON_WARN is set." + " Ending METplus run now." + ) diff --git a/metplus/util/run_util.py b/metplus/util/run_util.py index 17a1a1629..81542e029 100644 --- a/metplus/util/run_util.py +++ b/metplus/util/run_util.py @@ -16,6 +16,7 @@ from .. import get_metplus_version from .config_metplus import setup from . import get_wrapper_instance +from .exceptions import MPWarningError class RunArgs(NamedTuple): @@ -201,6 +202,7 @@ def run_metplus(config): # Use config object to get the list of processes to call process_list = get_process_list(config) + # catch any unexpected exceptions in the code and error/exit try: # if Usage is in process list, run it and exit if 'Usage' in [p[0] for p in process_list]: @@ -218,19 +220,15 @@ def run_metplus(config): if init_errors: return init_errors - all_commands = [] - for process in processes: - new_commands = process.run_all_times() - if new_commands: - all_commands.extend(new_commands) + all_commands = _run_processes(processes) # write out all commands and environment variables to file write_all_commands(all_commands, config) # compute total number of errors that occurred and output results return _get_total_errors_and_log_counts(processes, config.logger) - except Exception: - config.logger.exception("Fatal error occurred") + except Exception as err: + config.logger.exception(f"Fatal error occurred: {err}") config.logger.info("Check the log file for more information: " f"{get_logfile_info(config)}") return 1 @@ -285,6 +283,38 @@ def _check_wrapper_init_errors(processes, logger=None): return errors +def _run_processes(processes): + """!Loop over the list of processes and call run_all_times on each. + Track all commands that were run and output file regexes that were written. + Pass output file regexes to next process in the list so it can check if + duplicate paths are being overwritten. + + @param processes list of processes to run + @returns list of tuples with all commands that were run and + the env vars that were set for them + """ + # track output written by each process and add to next wrapper + # to ensure the same output is not being written by multiple processes + output_written = [] + + all_commands = [] + for process in processes: + process.output_written.extend(output_written) + try: + new_commands = process.run_all_times() + + # log error count if warning-on-error occurs, add any commands run + # before warning, then exit without running any more processes + except MPWarningError as err: + process.log_error(err) + all_commands.extend(process.all_commands) + return all_commands + + output_written.extend(process.output_written) + if new_commands: + all_commands.extend(new_commands) + + return all_commands def _get_total_errors_and_log_counts(processes, logger=None): total_errors = 0 diff --git a/metplus/util/wrapper_init.py b/metplus/util/wrapper_init.py index df87279e8..4a74cbb6f 100644 --- a/metplus/util/wrapper_init.py +++ b/metplus/util/wrapper_init.py @@ -1,6 +1,7 @@ from importlib import import_module from . import camel_to_underscore +from .exceptions import MPWarningError def get_wrapper_class(config, process): @@ -17,11 +18,14 @@ def get_wrapper_class(config, process): module = import_module(package_name) wrapper_class = getattr(module, f"{process}Wrapper") except AttributeError as err: - config.logger.error(f"There was a problem loading {process} wrapper: {err}") + config.logger.error( + f"There was a problem loading {process} wrapper: {err}" + ) return None except ModuleNotFoundError: - config.logger.error(f"Could not load {process} wrapper. " - "Wrapper may have been disabled.") + config.logger.error( + f"Could not load {process} wrapper. Wrapper may have been disabled." + ) return None return wrapper_class @@ -40,4 +44,11 @@ def get_wrapper_instance(config, process, instance=None): wrapper_class = get_wrapper_class(config, process) if wrapper_class is None: return None - return wrapper_class(config, instance=instance) + + try: + wrapper_instance = wrapper_class(config, instance=instance) + except MPWarningError as err: + config.logger.error(f"{err} Could not initialize {process} wrapper.") + wrapper_instance = None + + return wrapper_instance diff --git a/metplus/wrappers/command_builder.py b/metplus/wrappers/command_builder.py index 6994997af..981008b8a 100755 --- a/metplus/wrappers/command_builder.py +++ b/metplus/wrappers/command_builder.py @@ -14,9 +14,12 @@ from datetime import datetime from abc import ABCMeta from inspect import getframeinfo, stack +from typing import Any from ..util.constants import PYTHON_EMBEDDING_TYPES, COMPRESSION_EXTENSIONS from ..util.constants import MULTIPLE_INPUT_WRAPPERS, TIME_OFFSET_WARNING_WRAPPERS +from ..util.constants import WARNING_CONFIGS +from ..util.exceptions import MPWarningError from ..util import getlist, preprocess_file from ..util import do_string_sub, ti_calculate, get_seconds_from_string from ..util import shift_time_seconds, seconds_to_met_time @@ -64,6 +67,7 @@ def __init__(self, config, instance=None): self.outfile = "" self.param = "" self.all_commands = [] + self.output_written = [] # set app name to empty string if not set by wrapper # needed to create instance of parent wrapper for unit tests @@ -169,9 +173,10 @@ def check_for_unused_env_vars(self): for env_var_key in self.WRAPPER_ENV_VAR_KEYS: env_var_string = f"${{{env_var_key}}}" if env_var_string not in content: - self.logger.warning(f"Environment variable {env_var_string} " - "is not utilized in MET config file: " - f"{config_file}") + self.logger.warning( + f"Environment variable {env_var_string} is not utilized in " + f"MET config file: {config_file}" + ) def create_c_dict(self): c_dict = dict() @@ -218,6 +223,13 @@ def create_c_dict(self): 'DO_NOT_RUN_EXE', False) + # read generic or wrapper-specific config for + # warning skips and exit on warning (all default to False) + for name, default in WARNING_CONFIGS.items(): + c_dict[name] = self.get_wrapper_or_generic_config( + name, var_type='bool', default=default + ) + return c_dict def clear(self, clear_input_files=True): @@ -893,8 +905,9 @@ def find_input_files_ensemble(self, time_info, fill_missing=True): # check if control file is found in ensemble list if ctrl_file in input_files: # warn and remove control file if found - self.logger.warning(f"Control file found in ensemble list: " - f"{ctrl_file}. Removing from list.") + self.logger.warning( + f"Control file found in ensemble list: {ctrl_file}. Removing from list." + ) input_files.remove(ctrl_file) # compare number of files found to expected number of members @@ -950,8 +963,9 @@ def _check_expected_ensembles(self, input_files): ) # add fake files to list for ens_thresh checking diff = num_expected - num_found - self.logger.warning(f'Adding {diff} fake files to ' - 'ensure ens_thresh check is accurate') + self.logger.warning( + f'Adding {diff} fake files to ensure ens_thresh check is accurate' + ) for _ in range(0, diff, 1): input_files.append('MISSING') @@ -1065,15 +1079,59 @@ def find_and_check_output_file(self, time_info=None, self.logger.debug(f"Creating output directory: {parent_dir}") mkdir_p(parent_dir) - if not output_exists or not skip_if_output_exists: + self._check_if_output_has_been_written(output_path, skip_if_output_exists) + + if not output_exists: return True - # if the output file exists and we are supposed to skip, don't run tool - self.logger.debug(f'Skip writing output {output_path} because it already ' - 'exists. Remove file or change ' - f'{self.app_name.upper()}_SKIP_IF_OUTPUT_EXISTS to False ' - 'to process') - return False + warn_if_exists = self.c_dict.get('WARN_IF_OUTPUT_EXISTS', False) + + if skip_if_output_exists: + # if the output file exists and we are supposed to skip, don't run tool + skip_config_name = f"{self.app_name.upper()}_SKIP_IF_OUTPUT_EXISTS" + msg = ( + f"Skip writing output {output_path} because it already exists. " + f"Remove file or change {skip_config_name} to False to process" + ) + if self.c_dict.get('WARN_IF_OUTPUT_EXISTS', False): + self.logger.warning(msg) + else: + self.logger.debug(msg) + + return False + + if warn_if_exists: + self.logger.warning(f"Output {output_path} already exists and will be overwritten. " + f"Set {self.app_name.upper()}_WARN_IF_OUTPUT_EXISTS=False to turn " + "off this warning.") + + return True + + def _check_if_output_has_been_written(self, output_path, skip_if_output_exists=False): + """!Check if output file has already been written during this METplus run. + Log a warning if it has already been written. Otherwise add output path + to the list of output files that have been written. + + @param output_path path to output file or search string for apps that + write multiple output files + @param skip_if_output_exists boolean to skip writing output files if + they already exist + """ + if (not self.c_dict.get('WARN_IF_DUPLICATE_OUTPUT', True) + or skip_if_output_exists): + return + + if output_path in self.output_written: + self.logger.warning( + "Output has already been written during this METplus run and " + f"will be overwritten: {output_path}. Disable this warning by " + "setting WARN_IF_DUPLICATE_OUTPUT=False " + f"or {self.app_name.upper()}_WARN_IF_DUPLICATE_OUTPUT=False. " + "Check that the *_OUTPUT_TEMPLATE and/or *_OUTPUT_PREFIX config" + " options are set to produce unique output for each run" + ) + else: + self.output_written.append(output_path) @staticmethod def _get_valid_and_lead_from_time_info(time_info): @@ -1491,20 +1549,22 @@ def handle_climo_dict(self, sub_groups=('fcst', 'obs')): sub_groups=sub_groups): self.errors += 1 - def get_wrapper_or_generic_config(self, generic_name, var_type='str'): + def get_wrapper_or_generic_config(self, generic_name, var_type='str', + default: Any=''): """! Check for config variable with _ prepended first. If set use that value. If not, check for config without prefix. @param generic_name name of variable to read from config @param var_type type of variable to read, e.g. str, bool, int, or float. Default is str. + @param default value to return if variables are not set @returns value if set or empty string if not """ name = self.config.get_mp_config_name( [f'{self.app_name}_{generic_name}'.upper(), generic_name.upper()] ) if not name: - return '' + return default if var_type == 'bool': return self.config.getbool('config', name) if var_type == 'float': diff --git a/metplus/wrappers/example_wrapper.py b/metplus/wrappers/example_wrapper.py index 7d39f8103..803008962 100755 --- a/metplus/wrappers/example_wrapper.py +++ b/metplus/wrappers/example_wrapper.py @@ -36,10 +36,11 @@ def create_c_dict(self): self.logger.debug('EXAMPLE_INPUT_DIR was not set') if not c_dict['INPUT_TEMPLATE']: - self.logger.warning('EXAMPLE_INPUT_TEMPLATE was not set. ' - 'You should set this variable to see how the ' - 'runtime is substituted. ' - 'For example: {valid?fmt=%Y%m%d%H}.ext') + self.logger.warning( + 'EXAMPLE_INPUT_TEMPLATE was not set. ' + 'You should set this variable to see how the ' + 'runtime is substituted. For example: {valid?fmt=%Y%m%d%H}.ext' + ) full_path = os.path.join(c_dict['INPUT_DIR'], c_dict['INPUT_TEMPLATE']) self.logger.info(f"Input directory is {c_dict['INPUT_DIR']}") diff --git a/metplus/wrappers/extract_tiles_wrapper.py b/metplus/wrappers/extract_tiles_wrapper.py index bc7b06c9f..cecdf7dbc 100755 --- a/metplus/wrappers/extract_tiles_wrapper.py +++ b/metplus/wrappers/extract_tiles_wrapper.py @@ -23,8 +23,8 @@ class ExtractTilesWrapper(LoopTimesWrapper): """! Takes tc-pairs data and regrids paired data to an n x m grid as specified in the config file. """ - RUNTIME_FREQ_DEFAULT = 'RUN_ONCE_FOR_EACH' - RUNTIME_FREQ_SUPPORTED = ['RUN_ONCE_FOR_EACH'] + RUNTIME_FREQ_DEFAULT = 'RUN_ONCE_PER_INIT_OR_VALID' + RUNTIME_FREQ_SUPPORTED = 'ALL' COLUMNS_OF_INTEREST = { 'TC_STAT': [ @@ -154,6 +154,7 @@ def regrid_data_plane_init(self): overrides[f'{data_type}_{rdp}_OUTPUT_DIR'] = self.c_dict['OUTPUT_DIR'] overrides[f'{rdp}_SKIP_IF_OUTPUT_EXISTS'] = self.c_dict['SKIP_IF_OUTPUT_EXISTS'] + overrides[f'{rdp}_WARN_IF_DUPLICATE_OUTPUT'] = self.c_dict['WARN_IF_DUPLICATE_OUTPUT'] overrides[f'{rdp}_ONCE_PER_FIELD'] = False overrides[f'{rdp}_MANDATORY'] = False diff --git a/metplus/wrappers/pcp_combine_wrapper.py b/metplus/wrappers/pcp_combine_wrapper.py index a001ef3d9..30997c5ab 100755 --- a/metplus/wrappers/pcp_combine_wrapper.py +++ b/metplus/wrappers/pcp_combine_wrapper.py @@ -394,12 +394,13 @@ def _get_lookback_seconds(self, time_info, var_info, data_src): lookback = self.c_dict[f"{data_src}_LOOKBACK"] elif var_info: lookback = var_info[f'{data_src.lower()}_level'] - self.logger.warning( + self.logger.debug( f'{data_src}_PCP_COMBINE_LOOKBACK is ' f'not set. Using {lookback} from ' f'{data_src}_VAR{var_info.get("index")}_LEVELS' '. It is recommended that you explicitly set ' - 'the output accumulation.') + 'the output accumulation.' + ) else: lookback = '0' @@ -760,7 +761,7 @@ def _handle_name_argument(self, var_info, data_src): output_name = self.c_dict.get(f'{data_src}_OUTPUT_NAME') if not output_name and var_info: output_name = var_info.get(f"{data_src.lower()}_name") - self.logger.warning( + self.logger.debug( f'{data_src}_PCP_COMBINE_OUTPUT_NAME is ' f'not set. Using {output_name} from ' f'{data_src}_VAR{var_info.get("index")}_NAME.' diff --git a/metplus/wrappers/point_stat_wrapper.py b/metplus/wrappers/point_stat_wrapper.py index f934a66ef..81d4ac395 100755 --- a/metplus/wrappers/point_stat_wrapper.py +++ b/metplus/wrappers/point_stat_wrapper.py @@ -326,8 +326,9 @@ def find_input_files(self, time_info): # if there is more than 1 file, create file list file if len(model_path) > 1: - self.logger.warning('Multiple forecast files found.' - 'Using the first one') + self.logger.warning( + 'Multiple forecast files found. Using the first one' + ) self.infiles.append(model_path[0]) diff --git a/metplus/wrappers/py_embed_ingest_wrapper.py b/metplus/wrappers/py_embed_ingest_wrapper.py index c135e4b9f..a4bc08244 100755 --- a/metplus/wrappers/py_embed_ingest_wrapper.py +++ b/metplus/wrappers/py_embed_ingest_wrapper.py @@ -90,6 +90,8 @@ def create_c_dict(self): self.config.set(instance, 'REGRID_DATA_PLANE_SKIP_IF_OUTPUT_EXISTS', c_dict['SKIP_IF_OUTPUT_EXISTS']) + self.config.set(instance, 'REGRID_DATA_PLANE_WARN_IF_DUPLICATE_OUTPUT', + c_dict['WARN_IF_DUPLICATE_OUTPUT']) # set config variable to prevent incorrect error in RegridDataPlane # RDP requires either FCST_ or OBS_ options when run directly, but diff --git a/metplus/wrappers/regrid_data_plane_wrapper.py b/metplus/wrappers/regrid_data_plane_wrapper.py index c084cf2c9..5f11a30b8 100755 --- a/metplus/wrappers/regrid_data_plane_wrapper.py +++ b/metplus/wrappers/regrid_data_plane_wrapper.py @@ -212,9 +212,10 @@ def get_output_names(self, var_list, data_type): # if there are any duplicates, clear list and build names using # input name and input level if len(output_names) != len(set(output_names)): - self.logger.warning("Duplicate output names found. " - "Replacing output names to use " - "{input_name}_{input_level} instead.") + self.logger.warning( + "Duplicate output names found. Replacing output names to use " + "{input_name}_{input_level} instead." + ) output_names.clear() for field_info in var_list: input_name = field_info[f'{data_type.lower()}_name'] diff --git a/metplus/wrappers/runtime_freq_wrapper.py b/metplus/wrappers/runtime_freq_wrapper.py index 7d73d748f..fd346533a 100755 --- a/metplus/wrappers/runtime_freq_wrapper.py +++ b/metplus/wrappers/runtime_freq_wrapper.py @@ -106,9 +106,7 @@ def validate_runtime_freq(self, c_dict): err_msg = (f"{self.app_name.upper()}_RUNTIME_FREQ=" f"{c_dict['RUNTIME_FREQ']} not supported.") if hasattr(self, 'RUNTIME_FREQ_DEFAULT'): - self.logger.warning( - f"{err_msg} Using {self.RUNTIME_FREQ_DEFAULT}" - ) + self.logger.warning(f"{err_msg} Using {self.RUNTIME_FREQ_DEFAULT}") c_dict['RUNTIME_FREQ'] = self.RUNTIME_FREQ_DEFAULT else: self.log_error(err_msg) @@ -171,8 +169,10 @@ def _get_templates_from_prefixes(self, prefix_list, c_dict, label): # If templates were found with this prefix, use it if templates: if found_prefix: - self.logger.warning(f'{prefix_list[0]}_INPUT_TEMPLATE and ' - f'{prefix}_INPUT_TEMPLATE are set. Using the former. ') + self.logger.warning( + f'{prefix_list[0]}_INPUT_TEMPLATE and ' + f'{prefix}_INPUT_TEMPLATE are set. Using the former.' + ) continue return_templates = templates.copy() @@ -194,8 +194,10 @@ def _handle_input_file_list(self, prefix_list, c_dict, template_dict, label): if template: # log a warning if multiple formats of config are set if template_found: - self.logger.warning(f'{prefix_list[0]}_INPUT_FILE_LIST and ' - f'{prefix}_INPUT_TEMPLATE are set. Using the former. ') + self.logger.warning( + f'{prefix_list[0]}_INPUT_FILE_LIST and ' + f'{prefix}_INPUT_TEMPLATE are set. Using the former.' + ) c_dict['EXPLICIT_FILE_LIST'] = True template_dict[label.rstrip('_')] = (template, True, False) template_found = True @@ -363,6 +365,9 @@ def run_once_per_init_or_valid(self, custom): def _get_leads_as_group(self, time_input): """!""" + if self.c_dict.get('SKIP_LEAD_SEQ', False): + return {'': [0]} + lead_groups = get_lead_sequence_groups(self.config) if lead_groups: return lead_groups @@ -906,8 +911,9 @@ def _handle_fcst_inputs_for_ensemble(self, all_input_files, fcst_files): ctrl_file = all_input_files.get('CTRL') if ctrl_file in fcst_files: # warn and remove control file if found - self.logger.warning(f"Control file found in ensemble list: " - f"{ctrl_file}. Removing from list.") + self.logger.warning( + f"Control file found in ensemble list: {ctrl_file}. Removing from list." + ) fcst_files.remove(ctrl_file) # check EnsembleStat number of files diff --git a/metplus/wrappers/stat_analysis_wrapper.py b/metplus/wrappers/stat_analysis_wrapper.py index e9e8f5305..734a0568d 100755 --- a/metplus/wrappers/stat_analysis_wrapper.py +++ b/metplus/wrappers/stat_analysis_wrapper.py @@ -202,8 +202,9 @@ def create_c_dict(self): # if MODEL_LIST was not set, populate it from the model info list if not c_dict['MODEL_LIST'] and c_dict['MODEL_INFO_LIST']: - self.logger.warning("MODEL_LIST was left blank, " - + "creating with MODELn information.") + self.logger.debug( + "MODEL_LIST was left blank, creating with MODELn information." + ) for model_info in c_dict['MODEL_INFO_LIST']: c_dict['MODEL_LIST'].append(model_info['name']) @@ -579,8 +580,10 @@ def _set_lists_loop_or_group(self, c_dict): if c_dict[list_name]: continue - self.logger.warning(f'{list_name} was found in LOOP_LIST_ITEMS' - ' but is empty. Moving to group list') + self.logger.debug( + f"{list_name} was found in LOOP_LIST_ITEMS but is empty. " + "Moving to group list" + ) c_dict['GROUP_LIST_ITEMS'].append(list_name) c_dict['LOOP_LIST_ITEMS'].remove(list_name) diff --git a/metplus/wrappers/tc_pairs_wrapper.py b/metplus/wrappers/tc_pairs_wrapper.py index 10d58b00f..1677f1edc 100755 --- a/metplus/wrappers/tc_pairs_wrapper.py +++ b/metplus/wrappers/tc_pairs_wrapper.py @@ -321,20 +321,15 @@ def validate_runtime_freq(self, c_dict): # check deprecated TC_PAIRS_RUN_ONCE, warn and handle if set elif self.config.has_option('config', 'TC_PAIRS_RUN_ONCE'): - self.logger.warning('TC_PAIRS_RUN_ONCE is deprecated.') run_once = self.config.getbool('config', 'TC_PAIRS_RUN_ONCE', True) - if run_once: - self.logger.warning('Setting TC_PAIRS_RUNTIME_FREQ=RUN_ONCE.' - 'Please remove TC_PAIRS_RUN_ONCE and ' - 'set TC_PAIRS_RUNTIME_FREQ=RUN_ONCE ' - 'to remove this warning') - c_dict['RUNTIME_FREQ'] = 'RUN_ONCE' - else: - self.logger.warning('Setting TC_PAIRS_RUNTIME_FREQ=RUN_ONCE_FOR_EACH.' - 'Please remove TC_PAIRS_RUN_ONCE and ' - 'set TC_PAIRS_RUNTIME_FREQ=RUN_ONCE_FOR_EACH ' - 'to remove this warning') - c_dict['RUNTIME_FREQ'] = 'RUN_ONCE_FOR_EACH' + freq_override = 'RUN_ONCE' if run_once else 'RUN_ONCE_FOR_EACH' + c_dict['RUNTIME_FREQ'] = freq_override + msg = ( + f"TC_PAIRS_RUN_ONCE is deprecated. Setting TC_PAIRS_RUNTIME_FREQ={freq_override}. " + f"Please remove TC_PAIRS_RUN_ONCE and set TC_PAIRS_RUNTIME_FREQ={freq_override} " + "to remove this warning" + ) + self.logger.warning(msg) # if runtime frequency set to run once for each time, check skip lead if c_dict['RUNTIME_FREQ'] == 'RUN_ONCE_FOR_EACH': diff --git a/metplus/wrappers/tc_stat_wrapper.py b/metplus/wrappers/tc_stat_wrapper.py index 503ce7696..8884402ed 100755 --- a/metplus/wrappers/tc_stat_wrapper.py +++ b/metplus/wrappers/tc_stat_wrapper.py @@ -185,6 +185,9 @@ def create_c_dict(self): c_dict['FIND_FILES'] = False # force error if inputs are missing c_dict['ALLOW_MISSING_INPUTS'] = False + # ignore forecast leads + c_dict['SKIP_LEAD_SEQ'] = True + return c_dict def set_met_config_for_environment_variables(self): diff --git a/parm/use_cases/met_tool_wrapper/ASCII2NC/ASCII2NC.conf b/parm/use_cases/met_tool_wrapper/ASCII2NC/ASCII2NC.conf index 39e06016d..7d250bea9 100644 --- a/parm/use_cases/met_tool_wrapper/ASCII2NC/ASCII2NC.conf +++ b/parm/use_cases/met_tool_wrapper/ASCII2NC/ASCII2NC.conf @@ -48,10 +48,13 @@ ASCII2NC_OUTPUT_DIR = ASCII2NC_OUTPUT_TEMPLATE = {OUTPUT_BASE}/ascii2nc/precip24_{valid?fmt=%Y%m%d%H}.nc ASCII2NC_SKIP_IF_OUTPUT_EXISTS = False +#ASCII2NC_WARN_ON_DUPLICATE_OUTPUT = +#ASCII2NC_WARN_ON_OUTPUT_EXISTS = #ASCII2NC_FILE_WINDOW_BEGIN = 0 #ASCII2NC_FILE_WINDOW_END = 0 + ### # ASCII2NC Settings # https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#ascii2nc diff --git a/parm/use_cases/met_tool_wrapper/CyclonePlotter/CyclonePlotter.conf b/parm/use_cases/met_tool_wrapper/CyclonePlotter/CyclonePlotter.conf index 931f906fb..719d2f9bb 100644 --- a/parm/use_cases/met_tool_wrapper/CyclonePlotter/CyclonePlotter.conf +++ b/parm/use_cases/met_tool_wrapper/CyclonePlotter/CyclonePlotter.conf @@ -13,6 +13,9 @@ PROCESS_LIST = CyclonePlotter +LOOP_BY = INIT +INIT_TIME_FMT = %Y%m%d%H +INIT_BEG = 2015030112 ### # File I/O @@ -29,9 +32,9 @@ CYCLONE_PLOTTER_OUTPUT_DIR = {OUTPUT_BASE}/cyclone # https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#cycloneplotter ### -CYCLONE_PLOTTER_INIT_DATE = 20150301 +CYCLONE_PLOTTER_INIT_DATE = {init?fmt=%Y%m%d} -CYCLONE_PLOTTER_INIT_HR = 12 +CYCLONE_PLOTTER_INIT_HR = {init?fmt=%H} CYCLONE_PLOTTER_MODEL = GFSO CYCLONE_PLOTTER_PLOT_TITLE = Model Forecast Storm Tracks diff --git a/parm/use_cases/met_tool_wrapper/DataIngest/DataIngest.conf b/parm/use_cases/met_tool_wrapper/DataIngest/DataIngest.conf index 6ef9afc72..9da71574e 100644 --- a/parm/use_cases/met_tool_wrapper/DataIngest/DataIngest.conf +++ b/parm/use_cases/met_tool_wrapper/DataIngest/DataIngest.conf @@ -40,6 +40,8 @@ LEAD_SEQ = 0 ### DATA_INGEST_SKIP_IF_OUTPUT_EXISTS = False +#DATA_INGEST_WARN_ON_DUPLICATE_OUTPUT = +#DATA_INGEST_WARN_ON_OUTPUT_EXISTS = DATA_INGEST_1_INPUT_TEMPLATE = https://dtcenter.ucar.edu/dfiles/code/METplus/DataIngest_input/{valid?fmt=%Y/%m/%d}/point/metar/netcdf/{valid?fmt=%Y%m%d_%H%M}.gz DATA_INGEST_1_OUTPUT_TEMPLATE = {OUTPUT_BASE}/data_ingest/madis_metar/{valid?fmt=%Y%m%d_%H%M}.nc diff --git a/parm/use_cases/met_tool_wrapper/EnsembleStat/EnsembleStat.conf b/parm/use_cases/met_tool_wrapper/EnsembleStat/EnsembleStat.conf index ca351155b..91aea7585 100644 --- a/parm/use_cases/met_tool_wrapper/EnsembleStat/EnsembleStat.conf +++ b/parm/use_cases/met_tool_wrapper/EnsembleStat/EnsembleStat.conf @@ -64,6 +64,8 @@ ENSEMBLE_STAT_CLIMO_STDEV_INPUT_TEMPLATE = ENSEMBLE_STAT_OUTPUT_DIR = {OUTPUT_BASE}/ensemble ENSEMBLE_STAT_OUTPUT_TEMPLATE = {init?fmt=%Y%m%d%H%M}/ensemble_stat +#ENSEMBLE_STAT_WARN_ON_DUPLICATE_OUTPUT = +#ENSEMBLE_STAT_WARN_ON_OUTPUT_EXISTS = ### # Field Info diff --git a/parm/use_cases/met_tool_wrapper/ExtractTiles/ExtractTiles.conf b/parm/use_cases/met_tool_wrapper/ExtractTiles/ExtractTiles.conf index 019209b8a..c824d3ff8 100644 --- a/parm/use_cases/met_tool_wrapper/ExtractTiles/ExtractTiles.conf +++ b/parm/use_cases/met_tool_wrapper/ExtractTiles/ExtractTiles.conf @@ -52,7 +52,8 @@ FCST_EXTRACT_TILES_OUTPUT_TEMPLATE = {init?fmt=%Y%m%d_%H}/{storm_id}/FCST_TILE_F OBS_EXTRACT_TILES_OUTPUT_TEMPLATE = {init?fmt=%Y%m%d_%H}/{storm_id}/OBS_TILE_F{lead?fmt=%3H}_gfs_4_{valid?fmt=%Y%m%d}_{valid?fmt=%H}00_000.nc EXTRACT_TILES_SKIP_IF_OUTPUT_EXISTS = yes - +#EXTRACT_TILES_WARN_ON_DUPLICATE_OUTPUT = +#EXTRACT_TILES_WARN_ON_OUTPUT_EXISTS = ### # Field Info diff --git a/parm/use_cases/met_tool_wrapper/GenEnsProd/GenEnsProd.conf b/parm/use_cases/met_tool_wrapper/GenEnsProd/GenEnsProd.conf index 5e6505342..15246e46c 100644 --- a/parm/use_cases/met_tool_wrapper/GenEnsProd/GenEnsProd.conf +++ b/parm/use_cases/met_tool_wrapper/GenEnsProd/GenEnsProd.conf @@ -41,10 +41,8 @@ LEAD_SEQ = 24H GEN_ENS_PROD_INPUT_DIR = {INPUT_BASE}/met_test/data/sample_fcst GEN_ENS_PROD_INPUT_TEMPLATE = - {init?fmt=%Y%m%d%H}/arw-fer-gep1/d01_{init?fmt=%Y%m%d%H}_{lead?fmt=%3H}00.grib, {init?fmt=%Y%m%d%H}/arw-sch-gep2/d01_{init?fmt=%Y%m%d%H}_{lead?fmt=%3H}00.grib, {init?fmt=%Y%m%d%H}/arw-tom-gep3/d01_{init?fmt=%Y%m%d%H}_{lead?fmt=%3H}00.grib, - {init?fmt=%Y%m%d%H}/arw-tom-gep4/d01_{init?fmt=%Y%m%d%H}_{lead?fmt=%3H}00.grib, {init?fmt=%Y%m%d%H}/arw-fer-gep5/d01_{init?fmt=%Y%m%d%H}_{lead?fmt=%3H}00.grib, {init?fmt=%Y%m%d%H}/arw-sch-gep6/d01_{init?fmt=%Y%m%d%H}_{lead?fmt=%3H}00.grib, {init?fmt=%Y%m%d%H}/arw-tom-gep7/d01_{init?fmt=%Y%m%d%H}_{lead?fmt=%3H}00.grib @@ -56,6 +54,8 @@ GEN_ENS_PROD_CTRL_INPUT_TEMPLATE = GEN_ENS_PROD_OUTPUT_DIR = {OUTPUT_BASE}/gen_ens_prod GEN_ENS_PROD_OUTPUT_TEMPLATE = gen_ens_prod_{valid?fmt=%Y%m%d_%H%M%S}V_ens.nc +#GEN_VX_MASK_WARN_ON_DUPLICATE_OUTPUT = +#GEN_VX_MASK_WARN_ON_OUTPUT_EXISTS = ### # Field Info @@ -90,7 +90,7 @@ ENS_VAR5_THRESH = >=5.0 # https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#genensprod ### -GEN_ENS_PROD_N_MEMBERS = 6 +GEN_ENS_PROD_N_MEMBERS = 5 #LOG_GEN_ENS_PROD_VERBOSITY = 2 diff --git a/parm/use_cases/met_tool_wrapper/GenVxMask/GenVxMask.conf b/parm/use_cases/met_tool_wrapper/GenVxMask/GenVxMask.conf index 9b4958136..236bfac59 100644 --- a/parm/use_cases/met_tool_wrapper/GenVxMask/GenVxMask.conf +++ b/parm/use_cases/met_tool_wrapper/GenVxMask/GenVxMask.conf @@ -50,6 +50,8 @@ GEN_VX_MASK_OUTPUT_TEMPLATE = {OUTPUT_BASE}/met_tool_wrapper/GenVxMask/POLY_GFS_ GEN_VX_MASK_SKIP_IF_OUTPUT_EXISTS = False +#GEN_ENS_PROD_WARN_ON_DUPLICATE_OUTPUT = +#GEN_ENS_PROD_WARN_ON_OUTPUT_EXISTS = GEN_VX_MASK_FILE_WINDOW_BEGIN = 0 GEN_VX_MASK_FILE_WINDOW_END = 0 diff --git a/parm/use_cases/met_tool_wrapper/GridDiag/GridDiag.conf b/parm/use_cases/met_tool_wrapper/GridDiag/GridDiag.conf index 141f819f2..a7b417ef6 100644 --- a/parm/use_cases/met_tool_wrapper/GridDiag/GridDiag.conf +++ b/parm/use_cases/met_tool_wrapper/GridDiag/GridDiag.conf @@ -47,6 +47,8 @@ GRID_DIAG_INPUT_TEMPLATE = gfs.subset.t00z.pgrb2.0p25.f{lead?fmt=%H}, gfs.subset GRID_DIAG_OUTPUT_DIR = {OUTPUT_BASE}/met_tool_wrapper/GridDiag GRID_DIAG_OUTPUT_TEMPLATE = grid_diag_out.nc +#GRID_DIAG_WARN_ON_DUPLICATE_OUTPUT = +#GRID_DIAG_WARN_ON_OUTPUT_EXISTS = ### # Field Info diff --git a/parm/use_cases/met_tool_wrapper/GridStat/GridStat.conf b/parm/use_cases/met_tool_wrapper/GridStat/GridStat.conf index f3871a281..47c81ef14 100644 --- a/parm/use_cases/met_tool_wrapper/GridStat/GridStat.conf +++ b/parm/use_cases/met_tool_wrapper/GridStat/GridStat.conf @@ -54,6 +54,9 @@ GRID_STAT_CLIMO_STDEV_INPUT_TEMPLATE = GRID_STAT_OUTPUT_DIR = {OUTPUT_BASE}/met_tool_wrapper/GridStat/GridStat GRID_STAT_OUTPUT_TEMPLATE = {init?fmt=%Y%m%d%H} +#GRID_STAT_WARN_ON_DUPLICATE_OUTPUT = +#GRID_STAT_WARN_ON_OUTPUT_EXISTS = + ### # Field Info diff --git a/parm/use_cases/met_tool_wrapper/IODA2NC/IODA2NC.conf b/parm/use_cases/met_tool_wrapper/IODA2NC/IODA2NC.conf index e0ab14a4d..21d77b4a1 100644 --- a/parm/use_cases/met_tool_wrapper/IODA2NC/IODA2NC.conf +++ b/parm/use_cases/met_tool_wrapper/IODA2NC/IODA2NC.conf @@ -43,6 +43,8 @@ IODA2NC_INPUT_TEMPLATE = ioda.NC001007.{valid?fmt=%Y%m%d%H}.nc IODA2NC_OUTPUT_DIR = {OUTPUT_BASE}/ioda2nc IODA2NC_OUTPUT_TEMPLATE = ioda.NC001007.{valid?fmt=%Y%m%d%H}.summary.nc +#IODA2NC_WARN_ON_DUPLICATE_OUTPUT = +#IODA2NC_WARN_ON_OUTPUT_EXISTS = ### # IODA2NC Settings diff --git a/parm/use_cases/met_tool_wrapper/MADIS2NC/MADIS2NC.conf b/parm/use_cases/met_tool_wrapper/MADIS2NC/MADIS2NC.conf index 41b800907..2eda7f012 100644 --- a/parm/use_cases/met_tool_wrapper/MADIS2NC/MADIS2NC.conf +++ b/parm/use_cases/met_tool_wrapper/MADIS2NC/MADIS2NC.conf @@ -48,6 +48,8 @@ MADIS2NC_OUTPUT_DIR = MADIS2NC_OUTPUT_TEMPLATE = {OUTPUT_BASE}/madis2nc/met_metar_{init?fmt=%Y%m%d%H}_F{lead?fmt=%3H}.nc #MADIS2NC_SKIP_IF_OUTPUT_EXISTS = False +#MADIS2NC_WARN_ON_DUPLICATE_OUTPUT = +#MADIS2NC_WARN_ON_OUTPUT_EXISTS = #MADIS2NC_FILE_WINDOW_BEGIN = 0 #MADIS2NC_FILE_WINDOW_END = 0 diff --git a/parm/use_cases/met_tool_wrapper/MODE/MODE.conf b/parm/use_cases/met_tool_wrapper/MODE/MODE.conf index 429b71f6e..3ce0b7bb2 100644 --- a/parm/use_cases/met_tool_wrapper/MODE/MODE.conf +++ b/parm/use_cases/met_tool_wrapper/MODE/MODE.conf @@ -48,6 +48,9 @@ OBS_MODE_INPUT_TEMPLATE = {valid?fmt=%Y%m%d%H}/wrfprs_ruc13_00.tm00_G212 MODE_OUTPUT_DIR = {OUTPUT_BASE}/mode MODE_OUTPUT_TEMPLATE = {valid?fmt=%Y%m%d%H} +#MODE_WARN_ON_DUPLICATE_OUTPUT = +#MODE_WARN_ON_OUTPUT_EXISTS = + FCST_MODE_FILE_WINDOW_BEGIN = 0 FCST_MODE_FILE_WINDOW_END = 0 diff --git a/parm/use_cases/met_tool_wrapper/MTD/MTD.conf b/parm/use_cases/met_tool_wrapper/MTD/MTD.conf index d68d7a63a..d81637e29 100644 --- a/parm/use_cases/met_tool_wrapper/MTD/MTD.conf +++ b/parm/use_cases/met_tool_wrapper/MTD/MTD.conf @@ -48,6 +48,8 @@ OBS_MTD_INPUT_TEMPLATE = ST2ml{valid?fmt=%Y%m%d%H}_A03h.nc MTD_OUTPUT_DIR = {OUTPUT_BASE}/mtd MTD_OUTPUT_TEMPLATE = {valid?fmt=%Y%m%d%H} +#MTD_WARN_ON_DUPLICATE_OUTPUT = +#MTD_WARN_ON_OUTPUT_EXISTS = ### # Field Info diff --git a/parm/use_cases/met_tool_wrapper/PB2NC/PB2NC.conf b/parm/use_cases/met_tool_wrapper/PB2NC/PB2NC.conf index b07193bac..7c8f53c33 100644 --- a/parm/use_cases/met_tool_wrapper/PB2NC/PB2NC.conf +++ b/parm/use_cases/met_tool_wrapper/PB2NC/PB2NC.conf @@ -45,6 +45,8 @@ PB2NC_INPUT_TEMPLATE = ndas.t{da_init?fmt=%H}z.prepbufr.tm{offset?fmt=%2H}.{da_i PB2NC_OFFSETS = 12 PB2NC_SKIP_IF_OUTPUT_EXISTS = True +#PB2NC_WARN_ON_DUPLICATE_OUTPUT = +#PB2NC_WARN_ON_OUTPUT_EXISTS = PB2NC_OUTPUT_DIR = {OUTPUT_BASE}/pb2nc PB2NC_OUTPUT_TEMPLATE = sample_pb.nc diff --git a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_add.conf b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_add.conf index 5c41b2218..f9163bd80 100644 --- a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_add.conf +++ b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_add.conf @@ -47,6 +47,8 @@ FCST_PCP_COMBINE_INPUT_TEMPLATE = NEWSe_{init?fmt=%Y%m%d}_i{init?fmt=%H%M}_m0_f{ FCST_PCP_COMBINE_OUTPUT_DIR = {OUTPUT_BASE}/met_tool_wrapper/PCPCombine/PCPCombine_add FCST_PCP_COMBINE_OUTPUT_TEMPLATE = NEWSe5min_mem00_lag00.nc +#PCP_COMBINE_WARN_ON_DUPLICATE_OUTPUT = +#PCP_COMBINE_WARN_ON_OUTPUT_EXISTS = ### # PCPCombine Settings diff --git a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_bucket.conf b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_bucket.conf index 6325df895..3076d0113 100644 --- a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_bucket.conf +++ b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_bucket.conf @@ -47,6 +47,8 @@ FCST_PCP_COMBINE_INPUT_TEMPLATE = gfs_{init?fmt=%Y%m%d%H}_F{lead?fmt=%3H}.grib FCST_PCP_COMBINE_OUTPUT_DIR = {OUTPUT_BASE}/met_tool_wrapper/PCPCombine/PCPCombine_bucket FCST_PCP_COMBINE_OUTPUT_TEMPLATE = gfs_{valid?fmt=%Y%m%d%H}_A{level?fmt=%3H}.nc +#PCP_COMBINE_WARN_ON_DUPLICATE_OUTPUT = +#PCP_COMBINE_WARN_ON_OUTPUT_EXISTS = ### # PCPCombine Settings diff --git a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_derive.conf b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_derive.conf index 831f636ba..6f607e3ec 100644 --- a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_derive.conf +++ b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_derive.conf @@ -47,6 +47,8 @@ FCST_PCP_COMBINE_INPUT_TEMPLATE = {init?fmt=%Y%m%d%H}/wrfprs_ruc13_{lead?fmt=%HH FCST_PCP_COMBINE_OUTPUT_DIR = {OUTPUT_BASE}/met_tool_wrapper/PCPCombine/PCPCombine_derive FCST_PCP_COMBINE_OUTPUT_TEMPLATE = wrfprs_ruc13_{init?fmt=%Y%m%d%H}_f{lead?fmt=%HH}_A{level?fmt=%HH}.nc +#PCP_COMBINE_WARN_ON_DUPLICATE_OUTPUT = +#PCP_COMBINE_WARN_ON_OUTPUT_EXISTS = ### # PCPCombine Settings diff --git a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_loop_custom.conf b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_loop_custom.conf index 720d6ccb4..0e29c450e 100644 --- a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_loop_custom.conf +++ b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_loop_custom.conf @@ -49,6 +49,8 @@ FCST_PCP_COMBINE_INPUT_TEMPLATE = {init?fmt=%Y%m%d%H}/{custom?fmt=%s}/d01_{init? FCST_PCP_COMBINE_OUTPUT_DIR = {OUTPUT_BASE}/met_tool_wrapper/PCPCombine/PCPCombine_loop_custom FCST_PCP_COMBINE_OUTPUT_TEMPLATE = {custom?fmt=%s}/d01_{init?fmt=%Y%m%d%H}_0{lead?fmt=%HH}00.nc +#PCP_COMBINE_WARN_ON_DUPLICATE_OUTPUT = +#PCP_COMBINE_WARN_ON_OUTPUT_EXISTS = ### # PCPCombine Settings diff --git a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_python_embedding.conf b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_python_embedding.conf index ebb7a0127..87857a4fc 100644 --- a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_python_embedding.conf +++ b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_python_embedding.conf @@ -47,6 +47,8 @@ OBS_PCP_COMBINE_INPUT_TEMPLATE = PYTHON_NUMPY OBS_PCP_COMBINE_OUTPUT_DIR = {OUTPUT_BASE}/met_tool_wrapper/PCPCombine/PCPCombine_combine_py_embed OBS_PCP_COMBINE_OUTPUT_TEMPLATE = IMERG.{valid?fmt=%Y%m%d_%H%M}_A{level?fmt=%2H}h +#PCP_COMBINE_WARN_ON_DUPLICATE_OUTPUT = +#PCP_COMBINE_WARN_ON_OUTPUT_EXISTS = ### # PCPCombine Settings diff --git a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_subtract.conf b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_subtract.conf index 59174513d..c58a53b75 100644 --- a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_subtract.conf +++ b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_subtract.conf @@ -48,6 +48,8 @@ FCST_PCP_COMBINE_INPUT_TEMPLATE = {init?fmt=%Y%m%d%H}/wrfprs_ruc13_{lead?fmt=%HH FCST_PCP_COMBINE_OUTPUT_DIR = {OUTPUT_BASE}/met_tool_wrapper/PCPCombine/PCPCombine_subtract FCST_PCP_COMBINE_OUTPUT_TEMPLATE = wrfprs_ruc13_{init?fmt=%Y%m%d%H}_f{lead?fmt=%HH}_A03.nc +#PCP_COMBINE_WARN_ON_DUPLICATE_OUTPUT = +#PCP_COMBINE_WARN_ON_OUTPUT_EXISTS = ### # PCPCombine Settings diff --git a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_sum.conf b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_sum.conf index 9012e7fb3..54727c2e9 100644 --- a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_sum.conf +++ b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_sum.conf @@ -47,6 +47,8 @@ FCST_PCP_COMBINE_INPUT_TEMPLATE = NEWSe_{init?fmt=%Y%m%d}_i{init?fmt=%H%M}_m0_f* FCST_PCP_COMBINE_OUTPUT_DIR = {OUTPUT_BASE}/met_tool_wrapper/PCPCombine/PCPCombine_sum FCST_PCP_COMBINE_OUTPUT_TEMPLATE = NEWSe5min_mem00_lag00.nc +#PCP_COMBINE_WARN_ON_DUPLICATE_OUTPUT = +#PCP_COMBINE_WARN_ON_OUTPUT_EXISTS = ### # PCPCombine Settings diff --git a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_user_defined.conf b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_user_defined.conf index a30bc6bd3..71b796b49 100644 --- a/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_user_defined.conf +++ b/parm/use_cases/met_tool_wrapper/PCPCombine/PCPCombine_user_defined.conf @@ -48,6 +48,8 @@ FCST_PCP_COMBINE_INPUT_TEMPLATE = {init?fmt=%Y%m%d%H}/wrfprs_ruc13_{lead?fmt=%HH FCST_PCP_COMBINE_OUTPUT_DIR = {OUTPUT_BASE}/met_tool_wrapper/PCPCombine/PCPCombine_user_defined FCST_PCP_COMBINE_OUTPUT_TEMPLATE = wrfprs_ruc13_{init?fmt=%Y%m%d%H}_f{lead?fmt=%HH}_A{level?fmt=%HH}.nc +#PCP_COMBINE_WARN_ON_DUPLICATE_OUTPUT = +#PCP_COMBINE_WARN_ON_OUTPUT_EXISTS = ### # PCPCombine Settings diff --git a/parm/use_cases/met_tool_wrapper/PairStat/PairStat.conf b/parm/use_cases/met_tool_wrapper/PairStat/PairStat.conf index cecfe8a8b..2f79e2623 100644 --- a/parm/use_cases/met_tool_wrapper/PairStat/PairStat.conf +++ b/parm/use_cases/met_tool_wrapper/PairStat/PairStat.conf @@ -47,6 +47,9 @@ PAIR_STAT_PAIRS_INPUT_TEMPLATE = point_stat_{lead?fmt=%H%M%S}L_{valid?fmt=%Y%m%d PAIR_STAT_OUTPUT_DIR = {OUTPUT_BASE}/pair_stat PAIR_STAT_OUTPUT_TEMPLATE = {valid?fmt=%Y%m%d_%H}_out +#PAIR_STAT_WARN_ON_DUPLICATE_OUTPUT = +#PAIR_STAT_WARN_ON_OUTPUT_EXISTS = + #PAIR_STAT_CLIMO_MEAN_INPUT_DIR = #PAIR_STAT_CLIMO_MEAN_INPUT_TEMPLATE = diff --git a/parm/use_cases/met_tool_wrapper/PlotDataPlane/PlotDataPlane_grib1.conf b/parm/use_cases/met_tool_wrapper/PlotDataPlane/PlotDataPlane_grib1.conf index f67abc606..ae754d781 100644 --- a/parm/use_cases/met_tool_wrapper/PlotDataPlane/PlotDataPlane_grib1.conf +++ b/parm/use_cases/met_tool_wrapper/PlotDataPlane/PlotDataPlane_grib1.conf @@ -45,6 +45,8 @@ PLOT_DATA_PLANE_INPUT_TEMPLATE = {INPUT_BASE}/met_test/data/sample_fcst/{valid?f PLOT_DATA_PLANE_OUTPUT_DIR = PLOT_DATA_PLANE_OUTPUT_TEMPLATE = {OUTPUT_BASE}/met_tool_wrapper/plot_data_plane/nam.t00z.awip1236.tm{valid?fmt=%H}.{valid?fmt=%Y%m%d}_TMPZ2.ps +#PLOT_DATA_PLANE_WARN_ON_DUPLICATE_OUTPUT = +#PLOT_DATA_PLANE_WARN_ON_OUTPUT_EXISTS = ### # Field Info diff --git a/parm/use_cases/met_tool_wrapper/PlotDataPlane/PlotDataPlane_netcdf.conf b/parm/use_cases/met_tool_wrapper/PlotDataPlane/PlotDataPlane_netcdf.conf index b98422004..65ba3eb62 100644 --- a/parm/use_cases/met_tool_wrapper/PlotDataPlane/PlotDataPlane_netcdf.conf +++ b/parm/use_cases/met_tool_wrapper/PlotDataPlane/PlotDataPlane_netcdf.conf @@ -45,6 +45,8 @@ PLOT_DATA_PLANE_INPUT_TEMPLATE = {INPUT_BASE}/met_test/out/pcp_combine/sample_fc PLOT_DATA_PLANE_OUTPUT_DIR = PLOT_DATA_PLANE_OUTPUT_TEMPLATE = {OUTPUT_BASE}/met_tool_wrapper/plot_data_plane/sample_fcst_12L_{valid?fmt=%Y%m%d%H}V_12A_APCP12_NC_MET.ps +#PLOT_DATA_PLANE_WARN_ON_DUPLICATE_OUTPUT = +#PLOT_DATA_PLANE_WARN_ON_OUTPUT_EXISTS = ### # Field Info diff --git a/parm/use_cases/met_tool_wrapper/PlotDataPlane/PlotDataPlane_python_embedding.conf b/parm/use_cases/met_tool_wrapper/PlotDataPlane/PlotDataPlane_python_embedding.conf index 5240c11cd..a7376e9de 100644 --- a/parm/use_cases/met_tool_wrapper/PlotDataPlane/PlotDataPlane_python_embedding.conf +++ b/parm/use_cases/met_tool_wrapper/PlotDataPlane/PlotDataPlane_python_embedding.conf @@ -45,6 +45,8 @@ PLOT_DATA_PLANE_INPUT_TEMPLATE = PYTHON_NUMPY PLOT_DATA_PLANE_OUTPUT_DIR = PLOT_DATA_PLANE_OUTPUT_TEMPLATE = {OUTPUT_BASE}/met_tool_wrapper/plot_data_plane/py_embed_fcst.ps +#PLOT_DATA_PLANE_WARN_ON_DUPLICATE_OUTPUT = +#PLOT_DATA_PLANE_WARN_ON_OUTPUT_EXISTS = ### # Field Info diff --git a/parm/use_cases/met_tool_wrapper/PlotPointObs/PlotPointObs.conf b/parm/use_cases/met_tool_wrapper/PlotPointObs/PlotPointObs.conf index 304e1c525..e8b0f9ca9 100644 --- a/parm/use_cases/met_tool_wrapper/PlotPointObs/PlotPointObs.conf +++ b/parm/use_cases/met_tool_wrapper/PlotPointObs/PlotPointObs.conf @@ -54,6 +54,8 @@ PLOT_POINT_OBS_OUTPUT_DIR = {OUTPUT_BASE}/plot_point_obs PLOT_POINT_OBS_OUTPUT_TEMPLATE = nam_and_ndas.{valid?fmt=%Y%m%d}.t{valid?fmt=%H}z.prepbufr_CONFIG.ps #PLOT_POINT_OBS_SKIP_IF_OUTPUT_EXISTS = False +#PLOT_POINT_OBS_WARN_ON_DUPLICATE_OUTPUT = +#PLOT_POINT_OBS_WARN_ON_OUTPUT_EXISTS = ### diff --git a/parm/use_cases/met_tool_wrapper/Point2Grid/Point2Grid.conf b/parm/use_cases/met_tool_wrapper/Point2Grid/Point2Grid.conf index 03695fcc6..eb4104340 100644 --- a/parm/use_cases/met_tool_wrapper/Point2Grid/Point2Grid.conf +++ b/parm/use_cases/met_tool_wrapper/Point2Grid/Point2Grid.conf @@ -45,6 +45,9 @@ POINT2GRID_INPUT_TEMPLATE = {INPUT_BASE}/met_test/data/sample_obs/prepbufr/sampl POINT2GRID_OUTPUT_DIR = POINT2GRID_OUTPUT_TEMPLATE = {OUTPUT_BASE}/met_tool_wrapper/Point2Grid/grid.{init?fmt=%Y%d%H}.nc +#POINT2GRID_WARN_ON_DUPLICATE_OUTPUT = +#POINT2GRID_WARN_ON_OUTPUT_EXISTS = + POINT2GRID_FILE_WINDOW_BEGIN = 0 POINT2GRID_FILE_WINDOW_END = 0 diff --git a/parm/use_cases/met_tool_wrapper/PointStat/PointStat.conf b/parm/use_cases/met_tool_wrapper/PointStat/PointStat.conf index 3b7fe7079..25f2e6a8d 100644 --- a/parm/use_cases/met_tool_wrapper/PointStat/PointStat.conf +++ b/parm/use_cases/met_tool_wrapper/PointStat/PointStat.conf @@ -47,6 +47,9 @@ OBS_POINT_STAT_INPUT_TEMPLATE = sample_pb.nc POINT_STAT_OUTPUT_DIR = {OUTPUT_BASE}/point_stat +#POINT_STAT_WARN_ON_DUPLICATE_OUTPUT = +#POINT_STAT_WARN_ON_OUTPUT_EXISTS = + POINT_STAT_CLIMO_MEAN_INPUT_DIR = POINT_STAT_CLIMO_MEAN_INPUT_TEMPLATE = diff --git a/parm/use_cases/met_tool_wrapper/PyEmbedIngest/PyEmbedIngest.conf b/parm/use_cases/met_tool_wrapper/PyEmbedIngest/PyEmbedIngest.conf index 8064c39b7..4298a2ef4 100644 --- a/parm/use_cases/met_tool_wrapper/PyEmbedIngest/PyEmbedIngest.conf +++ b/parm/use_cases/met_tool_wrapper/PyEmbedIngest/PyEmbedIngest.conf @@ -53,3 +53,6 @@ PY_EMBED_INGEST_2_OUTPUT_TEMPLATE = {OUTPUT_BASE}/met_tool_wrapper/PyEmbedIngest PY_EMBED_INGEST_2_SCRIPT = {MET_INSTALL_DIR}/share/met/python/examples/read_ascii_numpy.py {INPUT_BASE}/met_test/data/python/fcst.txt FCST PY_EMBED_INGEST_2_TYPE = NUMPY PY_EMBED_INGEST_2_OUTPUT_GRID = G130 + +#PY_EMBED_INGEST_WARN_ON_DUPLICATE_OUTPUT = +#PY_EMBED_INGEST_WARN_ON_OUTPUT_EXISTS = diff --git a/parm/use_cases/met_tool_wrapper/RMWAnalysis/RMWAnalysis.conf b/parm/use_cases/met_tool_wrapper/RMWAnalysis/RMWAnalysis.conf index bb8de345d..5d62c6115 100644 --- a/parm/use_cases/met_tool_wrapper/RMWAnalysis/RMWAnalysis.conf +++ b/parm/use_cases/met_tool_wrapper/RMWAnalysis/RMWAnalysis.conf @@ -45,6 +45,8 @@ RMW_ANALYSIS_INPUT_TEMPLATE = tc_rmw_aal14{init?fmt=%Y}.nc RMW_ANALYSIS_OUTPUT_DIR = {OUTPUT_BASE}/rmw_analysis RMW_ANALYSIS_OUTPUT_TEMPLATE = rmw_analysis_aal14{init?fmt=%Y}.nc +#RMW_ANALYSIS_WARN_ON_DUPLICATE_OUTPUT = +#RMW_ANALYSIS_WARN_ON_OUTPUT_EXISTS = ### # Field Info diff --git a/parm/use_cases/met_tool_wrapper/RegridDataPlane/RegridDataPlane.conf b/parm/use_cases/met_tool_wrapper/RegridDataPlane/RegridDataPlane.conf index 1a9940e23..a52c482ed 100644 --- a/parm/use_cases/met_tool_wrapper/RegridDataPlane/RegridDataPlane.conf +++ b/parm/use_cases/met_tool_wrapper/RegridDataPlane/RegridDataPlane.conf @@ -47,6 +47,8 @@ OBS_REGRID_DATA_PLANE_OUTPUT_DIR = {OUTPUT_BASE}/regrid_data_plane OBS_REGRID_DATA_PLANE_TEMPLATE = ST2ml{valid?fmt=%Y%m%d%H}.Grb_G212 +#REGRID_DATA_PLANE_WARN_ON_DUPLICATE_OUTPUT = +#REGRID_DATA_PLANE_WARN_ON_OUTPUT_EXISTS = ### # Field Info diff --git a/parm/use_cases/met_tool_wrapper/SeriesAnalysis/SeriesAnalysis.conf b/parm/use_cases/met_tool_wrapper/SeriesAnalysis/SeriesAnalysis.conf index 91072f8a8..205352c20 100644 --- a/parm/use_cases/met_tool_wrapper/SeriesAnalysis/SeriesAnalysis.conf +++ b/parm/use_cases/met_tool_wrapper/SeriesAnalysis/SeriesAnalysis.conf @@ -60,6 +60,9 @@ SERIES_ANALYSIS_AGGR_INPUT_TEMPLATE = SERIES_ANALYSIS_OUTPUT_DIR = {OUTPUT_BASE}/met_tool_wrapper/SeriesAnalysis SERIES_ANALYSIS_OUTPUT_TEMPLATE = {init?fmt=%Y%m%d%H}_sa.nc +#SERIES_ANALYSIS_WARN_ON_DUPLICATE_OUTPUT = +#SERIES_ANALYSIS_WARN_ON_OUTPUT_EXISTS = + SERIES_ANALYSIS_CLIMO_MEAN_INPUT_DIR = SERIES_ANALYSIS_CLIMO_MEAN_INPUT_TEMPLATE = diff --git a/parm/use_cases/met_tool_wrapper/StatAnalysis/StatAnalysis.conf b/parm/use_cases/met_tool_wrapper/StatAnalysis/StatAnalysis.conf index 89f4167b6..384009979 100644 --- a/parm/use_cases/met_tool_wrapper/StatAnalysis/StatAnalysis.conf +++ b/parm/use_cases/met_tool_wrapper/StatAnalysis/StatAnalysis.conf @@ -45,6 +45,9 @@ MODEL1_STAT_ANALYSIS_DUMP_ROW_TEMPLATE = {fcst_valid_hour?fmt=%H}Z/{MODEL1}/{MOD STAT_ANALYSIS_OUTPUT_DIR = {OUTPUT_BASE}/stat_analysis STAT_ANALYSIS_OUTPUT_TEMPLATE = job.out +#STAT_ANALYSIS_WARN_ON_DUPLICATE_OUTPUT = +#STAT_ANALYSIS_WARN_ON_OUTPUT_EXISTS = + ### # StatAnalysis Settings diff --git a/parm/use_cases/met_tool_wrapper/TCDiag/TCDiag.conf b/parm/use_cases/met_tool_wrapper/TCDiag/TCDiag.conf index b10919341..806f9807e 100644 --- a/parm/use_cases/met_tool_wrapper/TCDiag/TCDiag.conf +++ b/parm/use_cases/met_tool_wrapper/TCDiag/TCDiag.conf @@ -48,6 +48,9 @@ TC_DIAG_INPUT1_TECH_ID_LIST = AVNO TC_DIAG_OUTPUT_DIR = {OUTPUT_BASE}/tc_diag TC_DIAG_OUTPUT_TEMPLATE = {date?fmt=%Y} +#TC_DIAG_WARN_ON_DUPLICATE_OUTPUT = +#TC_DIAG_WARN_ON_OUTPUT_EXISTS = + ### # Field Info diff --git a/parm/use_cases/met_tool_wrapper/TCGen/TCGen.conf b/parm/use_cases/met_tool_wrapper/TCGen/TCGen.conf index ace36034e..a0510db59 100644 --- a/parm/use_cases/met_tool_wrapper/TCGen/TCGen.conf +++ b/parm/use_cases/met_tool_wrapper/TCGen/TCGen.conf @@ -55,6 +55,9 @@ TC_GEN_GENESIS_INPUT_TEMPLATE = {init?fmt=%Y}*/genesis*{init?fmt=%Y}* TC_GEN_OUTPUT_DIR = {OUTPUT_BASE}/met_tool_wrapper/TCGen TC_GEN_OUTPUT_TEMPLATE = tc_gen_{init?fmt=%Y} +#TC_GEN_WARN_ON_DUPLICATE_OUTPUT = +#TC_GEN_WARN_ON_OUTPUT_EXISTS = + ### # Field Info diff --git a/parm/use_cases/met_tool_wrapper/TCPairs/TCPairs_extra_tropical.conf b/parm/use_cases/met_tool_wrapper/TCPairs/TCPairs_extra_tropical.conf index e5a0bcf1e..fd6889c41 100644 --- a/parm/use_cases/met_tool_wrapper/TCPairs/TCPairs_extra_tropical.conf +++ b/parm/use_cases/met_tool_wrapper/TCPairs/TCPairs_extra_tropical.conf @@ -57,6 +57,8 @@ TC_PAIRS_OUTPUT_TEMPLATE = {date?fmt=%Y%m}/{basin?fmt=%s}q{date?fmt=%Y%m%d%H}.gf TC_PAIRS_SKIP_IF_OUTPUT_EXISTS = yes TC_PAIRS_SKIP_IF_REFORMAT_EXISTS = yes +#TC_PAIRS_WARN_ON_DUPLICATE_OUTPUT = +#TC_PAIRS_WARN_ON_OUTPUT_EXISTS = TC_PAIRS_READ_ALL_FILES = no diff --git a/parm/use_cases/met_tool_wrapper/TCPairs/TCPairs_tropical.conf b/parm/use_cases/met_tool_wrapper/TCPairs/TCPairs_tropical.conf index 9c3415b5e..5251e418e 100644 --- a/parm/use_cases/met_tool_wrapper/TCPairs/TCPairs_tropical.conf +++ b/parm/use_cases/met_tool_wrapper/TCPairs/TCPairs_tropical.conf @@ -56,6 +56,9 @@ TC_PAIRS_OUTPUT_DIR = {OUTPUT_BASE}/tc_pairs TC_PAIRS_OUTPUT_TEMPLATE = tc_pairs_{basin?fmt=%s}{date?fmt=%Y%m%d%H}.dat TC_PAIRS_SKIP_IF_OUTPUT_EXISTS = no +#TC_PAIRS_WARN_ON_DUPLICATE_OUTPUT = +#TC_PAIRS_WARN_ON_OUTPUT_EXISTS = + TC_PAIRS_READ_ALL_FILES = no TC_PAIRS_REFORMAT_DECK = no diff --git a/parm/use_cases/met_tool_wrapper/TCRMW/TCRMW.conf b/parm/use_cases/met_tool_wrapper/TCRMW/TCRMW.conf index 3e118c961..d097c2406 100644 --- a/parm/use_cases/met_tool_wrapper/TCRMW/TCRMW.conf +++ b/parm/use_cases/met_tool_wrapper/TCRMW/TCRMW.conf @@ -46,6 +46,8 @@ TC_RMW_INPUT_TEMPLATE = gfs.subset.t00z.pgrb2.0p25.f* TC_RMW_OUTPUT_DIR = {OUTPUT_BASE}/met_tool_wrapper/TCRMW TC_RMW_OUTPUT_TEMPLATE = tc_rmw_aal14{date?fmt=%Y}.nc +#TCRMW_WARN_ON_DUPLICATE_OUTPUT = +#TCRMW_WARN_ON_OUTPUT_EXISTS = ### # Field Info diff --git a/parm/use_cases/met_tool_wrapper/TCStat/TCStat.conf b/parm/use_cases/met_tool_wrapper/TCStat/TCStat.conf index ec1705915..de3a03006 100644 --- a/parm/use_cases/met_tool_wrapper/TCStat/TCStat.conf +++ b/parm/use_cases/met_tool_wrapper/TCStat/TCStat.conf @@ -42,6 +42,8 @@ TC_STAT_LOOKIN_DIR = {INPUT_BASE}/met_test/tc_pairs TC_STAT_OUTPUT_DIR = {OUTPUT_BASE}/tc_stat TC_STAT_OUTPUT_TEMPLATE = job.out +#TC_STAT_WARN_ON_DUPLICATE_OUTPUT = +#TC_STAT_WARN_ON_OUTPUT_EXISTS = ### # TCStat Settings diff --git a/parm/use_cases/met_tool_wrapper/UserScript/UserScript_run_once.conf b/parm/use_cases/met_tool_wrapper/UserScript/UserScript_run_once.conf index 704b434f4..2945b37af 100644 --- a/parm/use_cases/met_tool_wrapper/UserScript/UserScript_run_once.conf +++ b/parm/use_cases/met_tool_wrapper/UserScript/UserScript_run_once.conf @@ -37,6 +37,8 @@ LEAD_SEQ = 0H, 12H, 15H, 24H, 120H USER_SCRIPT_CUSTOM_LOOP_LIST = nc +USER_SCRIPT_SKIP_INIT_TIMES = "%Y%m%d%H%M%S: 20141030183015,20141031063015,20141031183015" + ### # File I/O diff --git a/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf b/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf index 1cf908fd5..809600f67 100644 --- a/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf +++ b/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf @@ -48,6 +48,9 @@ OBS_WAVELET_STAT_INPUT_TEMPLATE = ST2ml{valid?fmt=%Y%m%d%H}_A03h.nc WAVELET_STAT_OUTPUT_DIR = {OUTPUT_BASE}/wavelet_stat WAVELET_STAT_OUTPUT_TEMPLATE = {init?fmt=%Y%m%d%H} +#WAVELET_STAT_WARN_ON_DUPLICATE_OUTPUT = +#WAVELET_STAT_WARN_ON_OUTPUT_EXISTS = + ### # Field Info diff --git a/parm/use_cases/model_applications/medium_range/GridStat_fcstGFS_obsGFS_climoNCEP_MultiField.conf b/parm/use_cases/model_applications/medium_range/GridStat_fcstGFS_obsGFS_climoNCEP_MultiField.conf index 8e8eb73c0..6fb9b131f 100644 --- a/parm/use_cases/model_applications/medium_range/GridStat_fcstGFS_obsGFS_climoNCEP_MultiField.conf +++ b/parm/use_cases/model_applications/medium_range/GridStat_fcstGFS_obsGFS_climoNCEP_MultiField.conf @@ -54,7 +54,7 @@ GRID_STAT_OUTPUT_TEMPLATE = {valid?fmt=%Y%m%d%H%M}/grid_stat MODEL1_STAT_ANALYSIS_LOOKIN_DIR = {OUTPUT_BASE}/met_out/{MODEL1}/anom/*/grid_stat STAT_ANALYSIS_OUTPUT_DIR = {OUTPUT_BASE}/gather_by_date/stat_analysis/grid2grid/anom -MODEL1_STAT_ANALYSIS_DUMP_ROW_TEMPLATE = {fcst_valid_hour?fmt=%H}Z/{MODEL1}/{MODEL1}_{valid?fmt=%Y%m%d}.stat +MODEL1_STAT_ANALYSIS_DUMP_ROW_TEMPLATE = {fcst_valid_hour?fmt=%H}Z/{MODEL1}/{MODEL1}_{fcst_var}_{fcst_level}_{valid?fmt=%Y%m%d}.stat ### diff --git a/parm/use_cases/model_applications/medium_range/TCStat_SeriesAnalysis_fcstGFS_obsGFS_FeatureRelative_SeriesByLead_PyEmbed_Multiple_Diagnostics.conf b/parm/use_cases/model_applications/medium_range/TCStat_SeriesAnalysis_fcstGFS_obsGFS_FeatureRelative_SeriesByLead_PyEmbed_Multiple_Diagnostics.conf index ea00e6493..00fb18117 100644 --- a/parm/use_cases/model_applications/medium_range/TCStat_SeriesAnalysis_fcstGFS_obsGFS_FeatureRelative_SeriesByLead_PyEmbed_Multiple_Diagnostics.conf +++ b/parm/use_cases/model_applications/medium_range/TCStat_SeriesAnalysis_fcstGFS_obsGFS_FeatureRelative_SeriesByLead_PyEmbed_Multiple_Diagnostics.conf @@ -38,6 +38,8 @@ SERIES_ANALYSIS_RUNTIME_FREQ = RUN_ONCE_PER_LEAD SERIES_ANALYSIS_RUN_ONCE_PER_STORM_ID = False +PY_EMBED_INGEST_SKIP_IF_OUTPUT_EXISTS = True + ### # File I/O diff --git a/parm/use_cases/model_applications/s2s_mid_lat/UserScript_fcstGFS_obsERA_Blocking.conf b/parm/use_cases/model_applications/s2s_mid_lat/UserScript_fcstGFS_obsERA_Blocking.conf index cc69d67d2..c9dc1283f 100644 --- a/parm/use_cases/model_applications/s2s_mid_lat/UserScript_fcstGFS_obsERA_Blocking.conf +++ b/parm/use_cases/model_applications/s2s_mid_lat/UserScript_fcstGFS_obsERA_Blocking.conf @@ -37,7 +37,7 @@ VALID_INCREMENT = 86400 LEAD_SEQ = 0 # Only Process DJF -SKIP_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:0229" +SKIP_VALID_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:0229" ### @@ -192,7 +192,7 @@ VALID_END = 2017022800 VALID_INCREMENT = 86400 # Add the first/last 2 days to the skip times to compute the running mean -SKIP_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,1202,0227,0228,0229" +SKIP_VALID_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,1202,0227,0228,0229" OBS_PCP_COMBINE_RUN = TRUE OBS_PCP_COMBINE_METHOD = DERIVE @@ -235,7 +235,7 @@ VALID_INCREMENT = 86400 LEAD_SEQ = 0 # Add the first/last 2 days to the skip times to compute the running mean -SKIP_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,1202,0227,0228,0229" +SKIP_VALID_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,1202,0227,0228,0229" OBS_PCP_COMBINE_RUN = True @@ -259,7 +259,7 @@ OBS_PCP_COMBINE_OUTPUT_TEMPLATE = {OBS_ANOM_INPUT_TEMPLATE} [create_cbl_filelist] # Skip the days on the edges that are not available due to the running mean -SKIP_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,0229" +SKIP_VALID_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,0229" # Find the files for each lead time USER_SCRIPT_RUNTIME_FREQ = RUN_ONCE_PER_LEAD @@ -373,7 +373,7 @@ IBL_PLOT_FCST_LABEL = GEFS LEAD_SEQ = 24 # Skip the days on the edges that are not available due to the running mean -SKIP_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,0229" +SKIP_VALID_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,0229" # Run the user script once for each lead USER_SCRIPT_RUNTIME_FREQ = RUN_ONCE_PER_LEAD diff --git a/parm/use_cases/model_applications/s2s_mid_lat/UserScript_fcstGFS_obsERA_WeatherRegime.conf b/parm/use_cases/model_applications/s2s_mid_lat/UserScript_fcstGFS_obsERA_WeatherRegime.conf index 07ec4711c..d8d0a1dfb 100644 --- a/parm/use_cases/model_applications/s2s_mid_lat/UserScript_fcstGFS_obsERA_WeatherRegime.conf +++ b/parm/use_cases/model_applications/s2s_mid_lat/UserScript_fcstGFS_obsERA_WeatherRegime.conf @@ -39,7 +39,7 @@ VALID_INCREMENT = 86400 LEAD_SEQ = 0 # Only Process DJF -SKIP_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,0229" +SKIP_VALID_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,0229" ### diff --git a/parm/use_cases/model_applications/s2s_mid_lat/UserScript_obsERA_obsOnly_Blocking.conf b/parm/use_cases/model_applications/s2s_mid_lat/UserScript_obsERA_obsOnly_Blocking.conf index c949f8c77..18b42a686 100644 --- a/parm/use_cases/model_applications/s2s_mid_lat/UserScript_obsERA_obsOnly_Blocking.conf +++ b/parm/use_cases/model_applications/s2s_mid_lat/UserScript_obsERA_obsOnly_Blocking.conf @@ -38,7 +38,7 @@ VALID_INCREMENT = 86400 LEAD_SEQ = 0 # Only Process DJF -SKIP_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:0229" +SKIP_VALID_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:0229" # Run the obs data # A variable set to be used in the pre-processing steps @@ -121,7 +121,7 @@ OBS_PCP_COMBINE_OUTPUT_TEMPLATE = Z500_daily_{valid?fmt=%Y%m%d?shift=-64800}_NH. [running_mean_obs] # Add the first/last 2 days to the skip times to compute the running mean -SKIP_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,1202,1203,1204,0229" +SKIP_VALID_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,1202,1203,1204,0229" OBS_PCP_COMBINE_RUN = {OBS_RUN} @@ -156,7 +156,7 @@ OBS_PCP_COMBINE_OUTPUT_TEMPLATE = Z500_5daymean_{valid?fmt=%Y%m%d?shift=-172800} [anomaly_obs] # Add the first/last 2 days to the skip times to compute the running mean -SKIP_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,1202,0227,0228,0229" +SKIP_VALID_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,1202,0227,0228,0229" OBS_PCP_COMBINE_RUN = {OBS_RUN} @@ -247,7 +247,7 @@ OBS_BLOCKING_PLOT_OUTPUT_NAME = ERA_Block_Freq_DJF [script_blocking] # Skip the days on the edges that are not available due to the running mean -SKIP_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,1202,0227,0228,0229" +SKIP_VALID_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:1201,1202,0227,0228,0229" # Run the user script once per lead USER_SCRIPT_RUNTIME_FREQ = RUN_ONCE_PER_LEAD diff --git a/parm/use_cases/model_applications/s2s_mid_lat/UserScript_obsERA_obsOnly_WeatherRegime.conf b/parm/use_cases/model_applications/s2s_mid_lat/UserScript_obsERA_obsOnly_WeatherRegime.conf index b228043c9..a461347e2 100644 --- a/parm/use_cases/model_applications/s2s_mid_lat/UserScript_obsERA_obsOnly_WeatherRegime.conf +++ b/parm/use_cases/model_applications/s2s_mid_lat/UserScript_obsERA_obsOnly_WeatherRegime.conf @@ -37,7 +37,7 @@ VALID_INCREMENT = 86400 LEAD_SEQ = 0 # Only Process DJF -SKIP_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:0229" +SKIP_VALID_TIMES = "%m:begin_end_incr(3,11,1)", "%m%d:0229" USER_SCRIPT_RUNTIME_FREQ = RUN_ONCE_PER_LEAD diff --git a/parm/use_cases/model_applications/tc_and_extra_tc/PointStat_fcstWRF_obsMADIS_hurricane_matthew.conf b/parm/use_cases/model_applications/tc_and_extra_tc/PointStat_fcstWRF_obsMADIS_hurricane_matthew.conf index d9347f8eb..39faf37e3 100644 --- a/parm/use_cases/model_applications/tc_and_extra_tc/PointStat_fcstWRF_obsMADIS_hurricane_matthew.conf +++ b/parm/use_cases/model_applications/tc_and_extra_tc/PointStat_fcstWRF_obsMADIS_hurricane_matthew.conf @@ -39,6 +39,9 @@ LEAD_SEQ = begin_end_incr(0,48,1) MADIS2NC_ALLOW_MISSING_INPUTS = True MADIS2NC_MISSING_INPUT_THRESH = 0.875 +# skip known missing times to prevent warnings in example use case +MADIS2NC_SKIP_VALID_TIMES = "%Y%m%d%H:2016100603,2016100621,2016100715,2016100719,2016100720,2016100721" + [config] ### diff --git a/pyproject.toml b/pyproject.toml index 30e5629b7..061e94759 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,3 +45,18 @@ dependencies = {file = ["requirements.txt"]} [tool.setuptools-git-versioning] enabled = true version_file = "metplus/VERSION" + +[tool.pytest.ini_options] +markers = [ + "run_metplus: custom marker for testing run_metplus.py script", + "util: custom marker for testing metplus/util logic", + "wrapper_a: custom marker for testing metplus/wrapper logic - A group", + "wrapper_b: custom marker for testing metplus/wrapper logic - B group", + "wrapper_c: custom marker for testing metplus/wrapper logic - C group", + "wrapper_d: custom marker for testing metplus/wrapper logic - D group", + "wrapper: custom marker for testing metplus/wrapper logic - all others", + "long: custom marker for tests that take a long time to run", + "plotting: custom marker for tests that involve plotting", + "diff: custom marker for diff util tests that require additional packages", + +] \ No newline at end of file