From a61bb93406dc4ee44b2fbc6d68955699879ee0df Mon Sep 17 00:00:00 2001 From: bikegeek <3753118+bikegeek@users.noreply.github.com> Date: Wed, 7 Feb 2024 14:46:51 -0700 Subject: [PATCH] Feature 2406 redo usecase rrfs (#2488) * issue #2406 RRFS use case files * issue #2406 added usecase to tests * Issue #2406 added metplotpy and metcalcpy as dependencies --- .github/parm/use_case_groups.json | 9 +- ...tRRFS_fcstOnly_Reformat_Aggregate_Plot.png | Bin 0 -> 70128 bytes ...format_Aggregate_Plot_ecnt_spread_skill.py | 255 ++++++++++++++++ internal/tests/use_cases/all_use_cases.txt | 1 + ...RRFS_fcstOnly_Reformat_Aggregate_Plot.conf | 67 +++++ .../aggregate_ecnt.py | 66 ++++ .../aggregate_ecnt.yaml | 41 +++ .../plot_spread_skill.py | 35 +++ .../plot_spread_skill.yaml | 281 ++++++++++++++++++ .../reformat_ecnt.yaml | 23 ++ .../reformat_ecnt_linetype.py | 58 ++++ 11 files changed, 834 insertions(+), 2 deletions(-) create mode 100644 docs/_static/short-range_UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.png create mode 100755 docs/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot_ecnt_spread_skill.py create mode 100755 parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.conf create mode 100755 parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/aggregate_ecnt.py create mode 100644 parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/aggregate_ecnt.yaml create mode 100755 parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/plot_spread_skill.py create mode 100644 parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/plot_spread_skill.yaml create mode 100755 parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/reformat_ecnt.yaml create mode 100755 parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/reformat_ecnt_linetype.py diff --git a/.github/parm/use_case_groups.json b/.github/parm/use_case_groups.json index db716d52c5..5684f4f2cd 100644 --- a/.github/parm/use_case_groups.json +++ b/.github/parm/use_case_groups.json @@ -61,12 +61,12 @@ }, { "category": "marine_and_cryosphere", - "index_list": "0-1", + "index_list": "0-2", "run": false }, { "category": "marine_and_cryosphere", - "index_list": "2-5", + "index_list": "3-5", "run": false }, { @@ -264,6 +264,11 @@ "index_list": "13", "run": false }, + { + "category": "short_range", + "index_list": "14", + "run": false + }, { "category": "space_weather", "index_list": "0-1", diff --git a/docs/_static/short-range_UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.png b/docs/_static/short-range_UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.png new file mode 100644 index 0000000000000000000000000000000000000000..3dfa948d3c0ec82a3363f3d835e1a9ae995f3089 GIT binary patch literal 70128 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 literal 0 HcmV?d00001 diff --git a/docs/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot_ecnt_spread_skill.py b/docs/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot_ecnt_spread_skill.py new file mode 100755 index 0000000000..c48e6c6cec --- /dev/null +++ b/docs/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot_ecnt_spread_skill.py @@ -0,0 +1,255 @@ +""" +UserScript: Reformat MET .stat ECNT data, calculate aggregation statistics, and generate a spread skill plot +====================================================================================================================== + +model_applications/ +short_range/ +UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot_ecnt_spread_skill.py + +""" + +################################################################################# +# Scientific Objective +# -------------------- +# +# This use case illustrates how to use MET .stat output (using the ECNT linetype data) to generate a +# spread skill plot using a subset of the METplus Analysis Tools (METdataio, METcalcpy, +# and METplotpy). The METdataio METreformat module extracts the ECNT linetype data and +# performs reformatting, the METcalcpy agg-stat module performs aggregation, and the +# METplotpy line plot is used to generate the spread skill plot. +# + +################################################################################# +# Datasets +# -------- +# +# * Forecast dataset: RRFS GEFS (Rapid Refresh Forecast System Global Ensemble Forecast System) +# * Observation dataset: None +# +# **Input**: MET .stat files from MET ensemble-stat tool for RRFS for 20220506 +# +# **Location**: All the input data required for this use case can be found in the met_test sample data tarball +# (**sample_data-short_range.tgz**). +# +# Click here to see the METplus releases page and download sample data for the appropriate +# release: https://github.com/dtcenter/METplus/releases +# +# See `Running METplus `_ +# section for more information. +# +# **This tarball should be unpacked into the directory corresponding to the value of INPUT_BASE** in the +# `User Configuration File `_ +# section. +# + +############################################################################# +# External Dependencies +# --------------------- +# You will need to use the version of Python that is required for the METplus version +# in use. Refer to the Installation section of the User's Guide for basic Python requirements: +# https://metplus.readthedocs.io/en/latest/Users_Guide/installation.html +# +# The METplus Analysis tools: METdataio, METcalcpy, and METplotpy have the additional third-party +# Python package requirements. The version numbers are found in the requirements.txt file found at the +# top-level directory of each repository. +# +# * lxml +# * pandas +# * pyyaml +# * numpy +# * netcdf4 +# * xarray +# * scipy +# * metpy +# * pint +# * python-dateutil +# * kaleido (python-kaleido) +# * plotly +# * matplotlib + + + +############################################################################## +# METplus Components +# ------------------ +# +# This use case runs the UserScript wrapper tool to run a user provided script, +# in this case, reformat_ecnt_linetype.py, agg_stat_ecnt.py and plot_spread_skill.py. +# It also requires the METdataio, METcalcpy and METplotpy source code to reformat the MET .stat output, +# perform aggregation, and generate the plot. Clone the METdataio repository +# (https://github.com/dtcenter/METdataio), +# METcalcpy repository (https://github.com/dtcenter/METcalcpy, and the METplotpy +# repository (https://github.com/dtcenter/METplotpy) under the same base directory as the +# METPLUS_BASE directory so that the METdataio, METcalcpy, and METplotpy directories are under the +# same base directory (i.e. if the METPLUS_BASE directory is /home/username/working/METplus, +# then clone the METdataio, METcalcpy and METplotpy source code into the /home/username/working directory) +# +# Clone the METdataio, METcalcpy, and METplotpy source code from their repositories under a base directory. The +# repositories are located: +# +# * https://github.com/dtcenter/METdataio +# * https://github.com/dtcenter/METcalcpy +# * https://github.com/dtcenter/METplotpy +# +# +# +# Define the OUTPUT_BASE, INPUT_BASE, and MET_INSTALL_DIR settings in the user +# configuration file. For instructions on how to set up the user configuration file, refer to the `User ConfigurationFile +# `_ section. +# + + +############################################################################## +# METplus Workflow +# ---------------- +# +# This use case reads in the MET .stat output that contains the ECNT linetype (from +# the MET ensemble-stat tool). The .stat output *MUST* reside under one directory. +# If .stat files are spread among multiple directories, these must be consolidated under a +# single directory. +# The use case loops over three processes: reformatting, aggregating, and plotting. +# + + +############################################################################## +# METplus Configuration +# --------------------- +# +# METplus first loads all the configuration files found in parm/metplus_config, +# then it loads any configuration files passed to METplus via the command line +# with the -c option, i.e. -c parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.conf +# +# .. highlight:: bash +# .. literalinclude:: ../../../../parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.conf +# + +############################################################################# +# MET Configuration +# --------------------- +# +# There are no MET tools used in this use case. The use case uses MET .stat +# output as input for the reformatting step. +# + +############################################################################## +# Python Embedding +# ---------------- +# +# There is no python embedding in this use case +# + +############################################################################## +# Python Scripts +# ---------------- +# This use case uses Python scripts to invoke the METdataio reformatter, the METcalcpy aggregator, and the METplotpy +# line plot. +# +# The following Python script (from METdataio) is used to reformat the MET .stat ECNT linetype data +# into a format that can be used by the aggregating script. +# +# .. highlight:: python +# .. literalinclude:: ../../../../parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/reformat_ecnt_linetype.py +# +# This Python script (from METcalcpy) is used to calculate aggregation statistics for the ECNT linetype. +# +# .. highlight:: python +# .. literalinclude:: ../../../../parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/aggregate_ecnt.py +# +# Finally,this Python script (from METplotpy) is used to generate a spread-skill plot using the METplotypy line plot code. +# +# .. highlight:: python +# .. literalinclude:: ../../../../parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/plot_spread_skill.py +# + +############################################################################## +# Running METplus +# --------------- +# +# This use case can be run two ways: +# +# 1) Passing in UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.conf, +# then a user-specific system configuration file:: +# +# run_metplus.py -c /path/to/METplus/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.conf -c /path/to/user_system.conf +# +# 2) Modifying the configurations in parm/metplus_config, then passing in UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.conf:: +# +# run_metplus.py -c /path/to/METplus/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.conf +# +# The former method is recommended. Whether you add them to a user-specific configuration file or modify the metplus_config files, the following variables must be set correctly: +# +# * **INPUT_BASE** - Path to directory where sample data tarballs are unpacked (See Datasets section to obtain tarballs). This is not required to run METplus, but it is required to run the examples in parm/use_cases +# * **OUTPUT_BASE** - Path where METplus output will be written. This must be in a location where you have write permissions +# * **MET_INSTALL_DIR** - Path to location where MET is installed locally +# +# and for the [exe] section, you will need to define the location of NON-MET executables. +# If the executable is in the user's path, METplus will find it from the name. +# If the executable is not in the path, specify the full path to the executable here (i.e. RM = /bin/rm) +# The following executables are required for performing series analysis use cases: +# +# Example User Configuration File:: +# +# [config] +# INPUT_BASE = /path/to/sample/input/data +# OUTPUT_BASE = /path/to/output/dir +# MET_INSTALL_DIR = /path/to/met-X.Y +# +# +# [exe] +# RM = /path/to/rm +# CUT = /path/to/cut +# TR = /path/to/tr +# NCAP2 = /path/to/ncap2 +# CONVERT = /path/to/convert +# NCDUMP = /path/to/ncdump +# + +############################################################################## +# Expected Output +# ---------------- +# +# A successful run will output the following both to the screen and to the logfile, one for the +# reformat, aggregate, and plot steps of the use case:: +# +# INFO: METplus has successfully finished running. +# +# +# **Reformat Output** +# +# The reformatted ensemble-stat ECNT linetype data should exist in the location specified in the user +# configuration file (OUTPUT_BASE). Verify that the ensemble_stat_ecnt.data file exists. The file now has all +# the statistics under the stat_name and stat_value columns, all ECNT statistic columns labelled with their +# corresponding names (e.g. crps, crpss, rmse, etc.) and confidence level values under the +# following columns: stat_btcl and stat_btcu +# +# +# **Aggregation Output** +# +# The METcalcpy agg_stat module is used to calculate aggregated statistics and confidence intervals for each +# series (line) point. +# +# **Plot Output** +# +# A spread-skill plot of temperature for the RMSE, SPREAD_PLUS_OERR, and a ratio line of SPREAD_PLUS_OERR/RMSE is +# created and found in the output location specified in the user configuration file (OUTPUT_BASE). The plot is named +# short-range_UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.png +# +# + +############################################################################## +# Keywords +# -------- +# +# .. note:: +# +# * UserScriptUseCase +# * ShortRangeAppUseCase +# * METdataioUseCase +# * METcalcpyUseCase +# * METplotpyUseCase +# +# Navigate to the :ref:`quick-search` page to discover other similar use cases. +# +# +# +# sphinx_gallery_thumbnail_path = '_static/short-range_UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.png' diff --git a/internal/tests/use_cases/all_use_cases.txt b/internal/tests/use_cases/all_use_cases.txt index 6bb6af4eff..658f73d3dd 100644 --- a/internal/tests/use_cases/all_use_cases.txt +++ b/internal/tests/use_cases/all_use_cases.txt @@ -175,6 +175,7 @@ Category: short_range 11::UserScript_fcstFV3_fcstOnly_PhysicsTendency_VerticalProfile::model_applications/short_range/UserScript_fcstFV3_fcstOnly_PhysicsTendency_VerticalProfile.conf:: metplotpy_env 12::UserScript_fcstFV3_fcstOnly_PhysicsTendency_VerticalCrossSection::model_applications/short_range/UserScript_fcstFV3_fcstOnly_PhysicsTendency_VerticalCrossSection.conf:: metplotpy_env 13::MODEMultivar_fcstHRRR_obsMRMS_HRRRanl::model_applications/short_range/MODEMultivar_fcstHRRR_obsMRMS_HRRRanl.conf +14::UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot::model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.conf:: metdataio, metcalcpy, metplotpy,mp_analysis_env Category: space_weather diff --git a/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.conf b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.conf new file mode 100755 index 0000000000..6ee8473174 --- /dev/null +++ b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.conf @@ -0,0 +1,67 @@ +[config] + +# Documentation for this use case can be found at +# https://metplus.readthedocs.io/en/latest/generated/model_applications/short-range/UserScript_fcstRRFS_obsOnly_Reformat_Aggregate_Plot.html + +# For additional information, please see the METplus Users Guide. +# https://metplus.readthedocs.io/en/latest/Users_Guide + +### +# Processes to run +# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#process-list +### + +PROCESS_LIST = UserScript(reformatter), UserScript(aggregate), UserScript(plotting) + +### +# Time Info +# LOOP_BY options are INIT, VALID, RETRO, and REALTIME +# If set to INIT or RETRO: +# INIT_TIME_FMT, INIT_BEG, INIT_END, and INIT_INCREMENT must also be set +# If set to VALID or REALTIME: +# VALID_TIME_FMT, VALID_BEG, VALID_END, and VALID_INCREMENT must also be set +# LEAD_SEQ is the list of forecast leads to process +# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#timing-control +### + +LOOP_BY = VALID +VALID_TIME_FMT = %Y%m%d_%H%M%S +VALID_BEG = 20220506_000000 + +USER_SCRIPT_RUNTIME_FREQ = RUN_ONCE +LOOP_ORDER = processes + +### +# UserScript Settings +# https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#userscript +### + + +[user_env_vars] +REFORMAT_YAML_CONFIG_NAME = {PARM_BASE}/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/reformat_ecnt.yaml +AGGREGATE_YAML_CONFIG_NAME = {PARM_BASE}/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/aggregate_ecnt.yaml +PLOTTING_YAML_CONFIG_NAME = {PARM_BASE}/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/plot_spread_skill.yaml +REFORMAT_INPUT_BASE = {INPUT_BASE}/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot +REFORMAT_OUTPUT_BASE = {OUTPUT_BASE}/reformatted +AGGREGATE_INPUT_BASE = {REFORMAT_OUTPUT_BASE} +AGGREGATE_OUTPUT_BASE = {OUTPUT_BASE}/aggregated +PLOT_INPUT_BASE = {AGGREGATE_OUTPUT_BASE} +PLOT_OUTPUT_BASE = {OUTPUT_BASE}/plot +METDATAIO_BASE = {METPLUS_BASE}/../METdataio +METCALCPY_BASE = {METPLUS_BASE}/../METcalcpy +METPLOTPY_BASE = {METPLUS_BASE}/../METplotpy +PYTHONPATH = {METDATAIO_BASE}:{METDATAIO_BASE}/METdbLoad:{METDATAIO_BASE}/METdbLoad/ush:{METDATAIO_BASE}/METreformat:{METCALCPY_BASE}:{METCALCPY_BASE}/metcalcpy:{METPLOTPY_BASE}:{METPLOTPY_BASE}/metplotpy/plots + + +[reformatter] +USER_SCRIPT_COMMAND = {PARM_BASE}/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/reformat_ecnt_linetype.py + +[aggregate] +USER_SCRIPT_COMMAND = {PARM_BASE}/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/aggregate_ecnt.py + +[plotting] +USER_SCRIPT_COMMAND = {PARM_BASE}/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/plot_spread_skill.py + + + + diff --git a/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/aggregate_ecnt.py b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/aggregate_ecnt.py new file mode 100755 index 0000000000..4766dd500e --- /dev/null +++ b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/aggregate_ecnt.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + + +import os +import time +import logging +import pandas as pd +import yaml +from metcalcpy.util import read_env_vars_in_config as readconfig +from metcalcpy.agg_stat import AggStat + +logger = logging.getLogger(__name__) + +def main(): + ''' + Read in the config file (with ENVIRONMENT variables defined in the + UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.conf). Invoke METcalcpy agg_stat module to + calculate the aggregation statistics, clean up the data so it is compatible for the METplotpy line plot + and write a tab-separated ASCII file. + + ''' + + start_agg_step = time.time() + + # Read in the YAML configuration file. Environment variables in + # the configuration file are supported. + try: + input_config_file = os.getenv("AGGREGATE_YAML_CONFIG_NAME", "aggregate_ecnt.yaml") + settings = readconfig.parse_config(input_config_file) + logger.info(settings) + except yaml.YAMLError as exc: + logger.error(exc) + + # Calculate the aggregation statistics using METcalcpy agg_stat + agg_begin = time.time() + try: + os.mkdir(os.getenv("AGGREGATE_OUTPUT_BASE")) + except OSError: + # Directory already exists, ignore error. + pass + + AGG_STAT = AggStat(settings) + AGG_STAT.calculate_stats_and_ci() + agg_finish = time.time() + time_for_aggregation = agg_finish - agg_begin + logger.info("Total time for calculating aggregation statistics (in seconds): {time_for_aggregation}") + + # Add a 'dummy' column (fcst_valid with the same values as fcst_lead) + # to the output data. The aggregation was based + # on the fcst_lead BUT the line plot requires a *second* time-related + # column (i.e. fcst_init_beg, fcst_valid, etc.) to identify unique + # points. In this case, the aggregated data already consists of + # unique points. If any other time column was used, this + # step would not be required. + output_file = settings['agg_stat_output'] + df = pd.read_csv(output_file, sep='\t') + df['fcst_valid'] = df['fcst_lead'] + df.to_csv(output_file, sep='\t') + + finish_agg = time.time() + total_agg_step = finish_agg - start_agg_step + logger.info("Total time for performing the aggregation step (in sec): {total_agg_step} ") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/aggregate_ecnt.yaml b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/aggregate_ecnt.yaml new file mode 100644 index 0000000000..4d94d41809 --- /dev/null +++ b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/aggregate_ecnt.yaml @@ -0,0 +1,41 @@ +agg_stat_input: !ENV '${AGGREGATE_INPUT_BASE}/ensemble_stat_ecnt.data' +agg_stat_output: !ENV '${AGGREGATE_OUTPUT_BASE}/ecnt_aggregated.data' +alpha: 0.05 +append_to_file: null +circular_block_bootstrap: True +derived_series_1: [] +derived_series_2: [] +event_equal: False +fcst_var_val_1: + TMP: + - ECNT_RMSE + - ECNT_SPREAD_PLUS_OERR +fcst_var_val_2: {} +indy_vals: +- '30000' +- '40000' +- '60000' +- '90000' +- '120000' +- '150000' +- '160000' +- '170000' +- '180000' +- '200000' +- '240000' +- '270000' + +indy_var: fcst_lead +line_type: ecnt +list_stat_1: + - ECNT_RMSE + - ECNT_SPREAD_PLUS_OERR +list_stat_2: [] +method: perc +num_iterations: 1 +num_threads: -1 +random_seed: null +series_val_1: + model: + - RRFS_GEFS_GF.SPP.SPPT +series_val_2: {} diff --git a/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/plot_spread_skill.py b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/plot_spread_skill.py new file mode 100755 index 0000000000..d482ba5d4e --- /dev/null +++ b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/plot_spread_skill.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + + +import os +from time import perf_counter +import logging +import yaml +import metcalcpy.util.read_env_vars_in_config as readconfig +from metplotpy.plots.line import line + +def main(): + + # Read in the YAML configuration file. Environment variables in + # the configuration file are supported. + try: + input_config_file = os.getenv("PLOTTING_YAML_CONFIG_NAME", "plot_spread_skill.yaml") + settings = readconfig.parse_config(input_config_file) + logging.info(settings) + except yaml.YAMLError as exc: + logging.error(exc) + + try: + start = perf_counter() + plot = line.Line(settings) + plot.save_to_file() + plot.write_html() + plot.write_output_file() + end = perf_counter() + execution_time = end - start + plot.line_logger.info(f"Finished creating line plot, execution time: {execution_time} seconds") + except ValueError as val_er: + print(val_er) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/plot_spread_skill.yaml b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/plot_spread_skill.yaml new file mode 100644 index 0000000000..2b1951bd82 --- /dev/null +++ b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/plot_spread_skill.yaml @@ -0,0 +1,281 @@ +alpha: 0.05 +box_avg: 'False' +box_boxwex: 0.2 +box_notch: 'False' +box_outline: 'True' +box_pts: 'False' +caption_align: 0.0 +caption_col: '#333333' +caption_offset: 3.0 +caption_size: 0.8 +caption_weight: 1 +cex: 1 + +colors: +# Colors for each line that will be generated. +# Accessible colors https://davidmathlogic.com/colorblind/#%23D81B60-%231E88E5-%23FFC107-%23004D40 +- '#D41159' # reddish +- '#4B0092' # purple-ish +- '#1Aff1A' # lime green-ish + + +con_series: +- 1 +- 1 +- 1 + +create_html: 'False' + +# Two derived series (lines) are supported (i.e. difference or ratio lines: DIFF or RATIO). +derived_series_1: + # Generate the ratio line of the ECNT spread_plus_oerr/rmse for temperature and the + # The first line is the top portion (left side) of the ratio, the second is the bottom (right side), and the + # third line indicates the type of derived line (i.e. RATIO or DIFF). This is a list of lists, so remember + # to have two '-' at the start of the first line ( - - 'model var stat') + # + # RRFS_GEFS_GF.SPP.SPPT model for temperature and the SPREAD_PLUS_OERR and RMSE statistics. + # +- - RRFS_GEFS_GF.SPP.SPPT TMP ECNT_SPREAD_PLUS_OERR + - RRFS_GEFS_GF.SPP.SPPT TMP ECNT_RMSE + - RATIO + +derived_series_2: [] + +dump_points_1: 'False' +dump_points_2: 'False' +event_equal: 'False' +fcst_var_val_1: + # Specify the variable name and the statistic(s) of interest. + # This will generate the line for the spread_plus_oerr values for TMP and a + # line for rmse values for TMP. + TMP: + - ECNT_SPREAD_PLUS_OERR + - ECNT_RMSE + +fcst_var_val_2: {} + +fixed_vars_vals_input: {} +grid_col: '#cccccc' +grid_lty: 3 +grid_lwd: 1 +grid_on: 'True' +grid_x: listX + +# Labels for the x-axis +indy_label: +- '3' +- '4' +- '6' +- '9' +- '12' +- '15' +- '16' +- '17' +- '18' +- '20' +- '24' +- '27' +- '30' +- '32' +- '33' +- '34' +- '36' + +# Stagger the points so overlapping points don't obscure one another +indy_stagger_1: 'True' +indy_stagger_2: 'True' + +# Values for the x-axis +indy_vals: +- '30000' +- '40000' +- '60000' +- '90000' +- '120000' +- '150000' +- '160000' +- '170000' +- '180000' +- '200000' +- '240000' +- '270000' +- '300000' +- '320000' +- '330000' +- '340000' +- '360000' + +# Specify the variable corresponding to the x-axis +indy_var: fcst_lead + +legend_box: o +legend_inset: + x: 0.0 + y: -0.25 +legend_ncol: -1 +legend_size: 0.8 +line_type: None + +# Explicitly set only the statistics for the derived series line +# (without the accompanying variable name) +list_stat_1: + - ECNT_SPREAD_PLUS_OERR + - ECNT_RMSE +list_stat_2: [] + +mar: +- 8 +- 4 +- 5 +- 4 +method: bca +mgp: +- 1 +- 1 +- 0 +num_iterations: 1 +num_threads: -1 +plot_caption: Caption +plot_ci: +- std +- std +- std + +# Turn on/off displaying the lines +plot_disp: +- 'True' +- 'True' +- 'True' + + +plot_filename: !ENV '${PLOT_OUTPUT_BASE}/short-range_UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.png' +plot_height: 8.5 +plot_res: 72 +plot_stat: median +plot_type: png16m +plot_units: in +plot_width: 11.0 + +# Optional, uncomment and set to directory to store the .points1 file +# that is used by METviewer (created when dump_points_1 is set to True) +# if dump_points_1 is True and this is uncommented, the points1 file +# will be saved in the default location (i.e. where the input data file is stored). +#points_path: /path/to + +random_seed: null + +# Define the style of the line +series_line_style: +- '-' # solid +- '--' # dashes +- ':' # dots + + + +series_line_width: +- 1 +- 1 +- 1 + +series_order: +# The order in which to apply settings for line width, line style, colors, symbols, etc. +# A convenience for experimenting with which color/line style/symbol combination works best for +# a particular line. +- 1 +- 2 +- 3 + + +series_symbols: +- . # circle +- "s" # square +- ^ # triangle up + + +series_type: +# b=lines + markers, l=lines only p=markers only +- b +- b +- b + +series_val_1: + # value of interest, in this case, the model, RRFS_GEFS_GF.SPP.SPPT + model: + - 'RRFS_GEFS_GF.SPP.SPPT' +series_val_2: {} + +show_nstats: 'False' +show_signif: +- 'False' +- 'False' +- 'False' + + +stat_input: !ENV '${PLOT_INPUT_BASE}/ecnt_aggregated.data' +sync_yaxes: 'False' +title: Spread skill plot with ratio line from MET ensemble-stat data (aggregation statistics) +title_align: 0.5 +title_offset: -2 +title_size: 1.4 +title_weight: 2.0 +# Use empty list to use automatically generated legend labels +user_legend: [] +# comment above and uncomment below to use user-specified legend labels +#user_legend: ['ECNT SPREAD_PLUS_OERR for RRFS_GEFS_GF.SPP.SPPT TMP ', 'ECNT RMSE for for RRFS_GEFS_GF.SPP.SPPT TMP', 'RATIO(RRFS_GEFS_GF.SPP.SPPT TMP SPREAD_PLUS_OERR/RMSE)'] +variance_inflation_factor: 'False' +vert_plot: 'False' +x2lab_align: 0.5 +x2lab_offset: -0.5 +x2lab_size: 0.8 +x2lab_weight: 1 +x2tlab_horiz: 0.5 +x2tlab_orient: 1 +x2tlab_perp: 1 +x2tlab_size: 0.8 +xaxis: fcst lead (hr) +xaxis_reverse: 'False' +xlab_align: 0.5 +xlab_offset: 2 +xlab_size: 1 +xlab_weight: 1 +xlim: [] +xtlab_decim: 0 +xtlab_horiz: 0.5 +xtlab_orient: 1 +xtlab_perp: -0.75 +xtlab_size: 1 +y2lab_align: 0.5 +y2lab_offset: 1 +y2lab_size: 1 +y2lab_weight: 1 +y2lim: [] +y2tlab_horiz: 0.5 +y2tlab_orient: 1 +y2tlab_perp: 1 +y2tlab_size: 1.0 +yaxis_1: Statistic value (or ratio of statistic values) +yaxis_2: '' +ylab_align: 0.5 +ylab_offset: -2 +ylab_size: 1 +ylab_weight: 1 +ylim: [] +ytlab_horiz: 0.5 +ytlab_orient: 1 +ytlab_perp: 0.5 +ytlab_size: 1 + +show_legend: +# 0 for don't show, 1 to show + - 1 + - 1 + - 1 + + +# Default logging is ERROR and to stdout. Omit the log_filename and log_level setting name and value if +# to use default settings. +log_filename: !ENV '${PLOT_OUTPUT_BASE}/spread_skill_ecnt.log' +log_level: INFO + +# 0-value on the x-axis is to the far left. +start_from_zero: False diff --git a/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/reformat_ecnt.yaml b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/reformat_ecnt.yaml new file mode 100755 index 0000000000..a60bbdac21 --- /dev/null +++ b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/reformat_ecnt.yaml @@ -0,0 +1,23 @@ +# The REFORMAT_OUTPUT_BASE and REFORMAT_INPUT_BASE are defined in the +# UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot.conf file +# + +# Indicates whether the .stat file input contains aggregated statistics (i.e. output from MET stat-analysis +# has been applied to MET .stat output from point-stat, grid-stat, or ensemble-stat, or previously aggregated +# via METcalcpy agg_stat). In this example, the input data is not aggregated. +# Set this to False, ensuring the appropriate format for METcalcpy agg_stat. +input_stats_aggregated: False +output_dir: !ENV '${REFORMAT_OUTPUT_BASE}' +output_filename: ensemble_stat_ecnt.data +line_type: ECNT +input_data_dir: !ENV '${REFORMAT_INPUT_BASE}' + +# !!! DO NOT modify the settings for the log_directory and log_filename.!!! +# The METdataio METreformat code is expecting values for these settings even though the log +# messages are in the METplus logfile. +log_directory: !ENV '${REFORMAT_OUTPUT_BASE}' +log_filename: 'ecnt_reformat.log' + +# Log levels are: INFO, DEBUG, WARNING, ERROR. +# Set to INFO for most verbose, ERROR for least verbose. +log_level: DEBUG diff --git a/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/reformat_ecnt_linetype.py b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/reformat_ecnt_linetype.py new file mode 100755 index 0000000000..239458c1f2 --- /dev/null +++ b/parm/use_cases/model_applications/short_range/UserScript_fcstRRFS_fcstOnly_Reformat_Aggregate_Plot/reformat_ecnt_linetype.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + + +import os +import time +import logging + +from METdbLoad.ush.read_data_files import ReadDataFiles +from METdbLoad.ush.read_load_xml import XmlLoadFile +from METreformat.write_stat_ascii import WriteStatAscii +from metcalcpy.util import read_env_vars_in_config as readconfig + + +logger = logging.getLogger(__name__) + +def main(): + + # Read in the YAML configuration file. Environment variables in + # the configuration file are supported. + input_config_file = os.getenv("REFORMAT_YAML_CONFIG_NAME", "reformat_ecnt.yaml") + settings = readconfig.parse_config(input_config_file) + logging.info(settings) + + + # Replacing the need for an XML specification file, pass in the XMLLoadFile and + # ReadDataFile parameters + rdf_obj: ReadDataFiles = ReadDataFiles() + xml_loadfile_obj: XmlLoadFile = XmlLoadFile(None) + + # Retrieve all the filenames in the data_dir specified in the YAML config file + load_files = xml_loadfile_obj.filenames_from_template(settings['input_data_dir'], + {}) + + flags = xml_loadfile_obj.flags + line_types = xml_loadfile_obj.line_types + beg_read_data = time.perf_counter() + rdf_obj.read_data(flags, load_files, line_types) + end_read_data = time.perf_counter() + time_to_read = end_read_data - beg_read_data + logger.info("Time to read input .stat data files using METdbLoad: {time_to_read}") + file_df = rdf_obj.stat_data + + # Check if the output file already exists, if so, delete it to avoid + # appending output from subsequent runs into the same file. + existing_output_file = os.path.join(settings['output_dir'], settings['output_filename']) + logger.info("Checking if {existing_output_file} already exists") + if os.path.exists(existing_output_file): + logger.info("Removing existing output file {existing_output_file}") + os.remove(existing_output_file) + + # Write stat file in ASCII format + stat_lines_obj: WriteStatAscii = WriteStatAscii(settings) + # stat_lines_obj.write_stat_ascii(file_df, parms, logger) + stat_lines_obj.write_stat_ascii(file_df, settings) + + +if __name__ == "__main__": + main()