From 93f8c53d95da98a77ccd78fd977f57c0232e2057 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Fri, 10 Nov 2023 15:21:15 -0700 Subject: [PATCH 01/30] started wavelet_stat wrapper --- metplus/wrappers/wavelet_stat_wrapper.py | 164 +++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100755 metplus/wrappers/wavelet_stat_wrapper.py diff --git a/metplus/wrappers/wavelet_stat_wrapper.py b/metplus/wrappers/wavelet_stat_wrapper.py new file mode 100755 index 0000000000..9ac25bc943 --- /dev/null +++ b/metplus/wrappers/wavelet_stat_wrapper.py @@ -0,0 +1,164 @@ +''' +Program Name: wavelet_stat_wrapper.py +Contact(s): George McCabe +Abstract: +History Log: Initial version +Usage: +Parameters: None +Input Files: +Output Files: +Condition codes: 0 for success, 1 for failure +''' + +import os + +from . import CompareGriddedWrapper + +# pylint:disable=pointless-string-statement +"""!@namespace WaveletStatWrapper +@brief Wraps the MET tool wavelet_stat to compare gridded datasets +@endcode +""" + + +class WaveletStatWrapper(CompareGriddedWrapper): + """!Wraps the MET tool wavelet_stat to compare gridded datasets""" + + RUNTIME_FREQ_DEFAULT = 'RUN_ONCE_FOR_EACH' + RUNTIME_FREQ_SUPPORTED = ['RUN_ONCE_FOR_EACH'] + + WRAPPER_ENV_VAR_KEYS = [ + 'METPLUS_MODEL', + 'METPLUS_DESC', + 'METPLUS_OBTYPE', + 'METPLUS_REGRID_DICT', + 'METPLUS_FCST_FILE_TYPE', + 'METPLUS_FCST_FIELD', + 'METPLUS_OBS_FILE_TYPE', + 'METPLUS_OBS_FIELD', + 'METPLUS_CENSOR_THRESH', + 'METPLUS_CENSOR_VAL', + 'METPLUS_MASK_MISSING_FLAG', + 'METPLUS_GRID_DECOMP_FLAG', + 'METPLUS_TITLE_DICT', + 'METPLUS_WAVELET_DICT', + 'METPLUS_OUTPUT_FLAG_DICT', + 'METPLUS_NC_PAIRS_FLAG_DICT', + 'METPLUS_PS_PLOT_FLAG', + 'METPLUS_FCST_RAW_PLOT_DICT', + 'METPLUS_OBS_RAW_PLOT_DICT', + 'METPLUS_WVLT_PLOT_DICT', + 'METPLUS_OUTPUT_PREFIX', + ] + + OUTPUT_FLAGS = [ + 'isc', + ] + + NC_PAIRS_FLAGS = [ + 'raw', + 'diff', + ] + + def __init__(self, config, instance=None): + self.app_name = 'wavelet_stat' + self.app_path = os.path.join(config.getdir('MET_BIN_DIR', ''), + self.app_name) + super().__init__(config, instance=instance) + + def create_c_dict(self): + c_dict = super().create_c_dict() + app = self.app_name.upper() + c_dict['VERBOSITY'] = self.config.getstr('config', + f'LOG_{app}_VERBOSITY', + c_dict['VERBOSITY']) + + # get the MET config file path or use default + c_dict['CONFIG_FILE'] = self.get_config_file('WaveletStatConfig_wrapped') + + c_dict['OBS_INPUT_DIR'] = self.config.getdir(f'OBS_{app}_INPUT_DIR', '') + c_dict['OBS_INPUT_TEMPLATE'] = ( + self.config.getraw('config', f'OBS_{app}_INPUT_TEMPLATE') + ) + if not c_dict['OBS_INPUT_TEMPLATE']: + self.log_error(f"OBS_{app}_INPUT_TEMPLATE required to run") + + c_dict['OBS_INPUT_DATATYPE'] = ( + self.config.getstr('config', f'OBS_{app}_INPUT_DATATYPE', '') + ) + + c_dict['FCST_INPUT_DIR'] = self.config.getdir(f'FCST_{app}_INPUT_DIR', '') + c_dict['FCST_INPUT_TEMPLATE'] = ( + self.config.getraw('config', f'FCST_{app}_INPUT_TEMPLATE') + ) + if not c_dict['FCST_INPUT_TEMPLATE']: + self.log_error(f"FCST_{app}_INPUT_TEMPLATE required to run") + + c_dict['FCST_INPUT_DATATYPE'] = ( + self.config.getstr('config', f'FCST_{app}_INPUT_DATATYPE', '') + ) + + c_dict['OUTPUT_DIR'] = self.config.getdir(f'{app}_OUTPUT_DIR', '') + if not c_dict['OUTPUT_DIR']: + self.log_error(f"Must set {app}_OUTPUT_DIR") + + c_dict['OUTPUT_TEMPLATE'] = ( + self.config.getraw('config', f'{app}_OUTPUT_TEMPLATE') + ) + c_dict['ONCE_PER_FIELD'] = ( + self.config.getbool('config', + f'{app}_ONCE_PER_FIELD', + False) + ) + + c_dict['FCST_PROB_THRESH'] = ( + self.config.getstr('config', + f'FCST_{app}_PROB_THRESH', '==0.1') + ) + c_dict['OBS_PROB_THRESH'] = ( + self.config.getstr('config', + f'OBS_{app}_PROB_THRESH', '==0.1') + ) + + c_dict['ALLOW_MULTIPLE_FILES'] = False + + # MET config variables + self.add_met_config(name='censor_thresh', data_type='list', + extra_args={'remove_quotes': True}) + + self.add_met_config(name='censor_val', data_type='list', + extra_args={'remove_quotes': True}) + + self.add_met_config(name='mask_missing_flag', data_type='string', + extra_args={'remove_quotes': True, + 'uppercase': True}) + + self.add_met_config(name='grid_decomp_flag', data_type='string', + extra_args={'remove_quotes': True, + 'uppercase': True}) + + self.handle_flags('output') + self.handle_flags('nc_pairs') + + self.add_met_config(name='file_type', data_type='string', + env_var_name='FCST_FILE_TYPE', + metplus_configs=['{app}_FCST_FILE_TYPE', + 'FCST_{app}_FILE_TYPE', + '{app}_FILE_TYPE'], + extra_args={'remove_quotes': True, + 'uppercase': True}) + + self.add_met_config(name='file_type', data_type='string', + env_var_name='OBS_FILE_TYPE', + metplus_configs=['{app}_OBS_FILE_TYPE', + 'OBS_{app}_FILE_TYPE', + '{app}_FILE_TYPE'], + extra_args={'remove_quotes': True, + 'uppercase': True}) + + self.add_met_config_dict('title', { + 'width': 'int', + 'location': 'float', # TODO: create dict list for location + }) + + return c_dict From 1a0dc1a89a2413611df8f36bba28cadce59e0c86 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Mon, 13 Nov 2023 12:43:52 -0700 Subject: [PATCH 02/30] per #2420, prevent crash if increment is set to an empty string --- .../tests/pytests/util/time_looping/test_time_looping.py | 5 +++++ metplus/util/time_looping.py | 9 ++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/internal/tests/pytests/util/time_looping/test_time_looping.py b/internal/tests/pytests/util/time_looping/test_time_looping.py index de4c835779..53689acce0 100644 --- a/internal/tests/pytests/util/time_looping/test_time_looping.py +++ b/internal/tests/pytests/util/time_looping/test_time_looping.py @@ -366,6 +366,11 @@ def test_time_generator_error_check_beg_end(metplus_config, prefix): # _INCREMENT is less than 60 seconds config.set('config', f'{prefix}_INCREMENT', '10S') assert next(tl.time_generator(config)) is None + + # _INCREMENT is empty string + config.set('config', f'{prefix}_INCREMENT', '') + assert next(tl.time_generator(config)) is not None + config.set('config', f'{prefix}_INCREMENT', '1d') # _END time comes before _BEG time diff --git a/metplus/util/time_looping.py b/metplus/util/time_looping.py index de7def9dc3..045c6e6ced 100644 --- a/metplus/util/time_looping.py +++ b/metplus/util/time_looping.py @@ -58,9 +58,12 @@ def time_generator(config): # if list is not provided, use _BEG, _END, and _INCREMENT start_string = config.getraw('config', f'{prefix}_BEG') end_string = config.getraw('config', f'{prefix}_END', start_string) - time_interval = get_relativedelta( - config.getstr('config', f'{prefix}_INCREMENT', '60') - ) + + time_interval = config.getstr('config', f'{prefix}_INCREMENT', '60') + # if [INIT/VALID]_INCREMENT is an empty string, set it to prevent crash + if not time_interval: + time_interval = '60' + time_interval = get_relativedelta(time_interval) start_dt = _get_current_dt(start_string, time_format, From 788b96ec13043a7f275bce0760e7aa29ebffa6ef Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Tue, 14 Nov 2023 17:23:56 -0700 Subject: [PATCH 03/30] remove sys.exit call inside METplus config setup function so that it does not cause a calling python script or shell to exit. instead pass None instead of a METplusConfig object and sys.exit only in the run_metplus.py script that is called --- .../pytests/util/run_util/test_run_util.py | 35 ++++++++++--------- metplus/util/run_util.py | 4 +-- ush/run_metplus.py | 8 +++-- 3 files changed, 26 insertions(+), 21 deletions(-) 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 45d27fa46a..9db4183816 100644 --- a/internal/tests/pytests/util/run_util/test_run_util.py +++ b/internal/tests/pytests/util/run_util/test_run_util.py @@ -2,6 +2,7 @@ from unittest import mock import os +import re import produtil import metplus.util.run_util as ru @@ -141,23 +142,27 @@ def test_pre_run_setup_env_vars(): @pytest.mark.util def test_pre_run_setup_sed_file(capfd): - with mock.patch.object(ru.sys, 'exit') as mock_sys: - with mock.patch.object( - ru, - 'validate_config_variables', - return_value=(False, ['sed command 1', 'sed command 2']), - ): - actual = get_config_from_file('sed_run_util.conf') - mock_sys.assert_called_with(1) + with mock.patch.object( + ru, + 'validate_config_variables', + return_value=(False, ['sed command 1', 'sed command 2']), + ): + actual = get_config_from_file('sed_run_util.conf') + assert actual is None # check sed file is written correctly - sed_file = os.path.join(actual.getdir('OUTPUT_BASE'), 'sed_commands.txt') + out, err = capfd.readouterr() + sed_err_regex = r'.*Find/Replace commands have been generated in (.*)\n' + sed_file = None + match = re.match(sed_err_regex, err) + if match: + sed_file = match.group(1) + assert os.path.exists(sed_file) with open(sed_file, 'r') as f: assert f.read() == 'sed command 1\nsed command 2\n' # check correct errors logged - out, err = capfd.readouterr() expected_error_msgs = [ f'Find/Replace commands have been generated in {sed_file}', 'ERROR: Correct configuration variables and rerun. Exiting.', @@ -168,9 +173,8 @@ def test_pre_run_setup_sed_file(capfd): @pytest.mark.util def test_pre_run_setup_deprecated(capfd): - with mock.patch.object(ru.sys, 'exit') as mock_sys: - actual = get_config_from_file('sed_run_util.conf') - mock_sys.assert_called_with(1) + actual = get_config_from_file('sed_run_util.conf') + assert actual is None out, err = capfd.readouterr() @@ -185,9 +189,8 @@ def test_pre_run_setup_deprecated(capfd): @pytest.mark.util def test_pre_run_setup_no_install(capfd): - with mock.patch.object(ru.sys, 'exit') as mock_sys: - actual = get_config_from_file('no_install_run_util.conf') - mock_sys.assert_called_with(1) + actual = get_config_from_file('no_install_run_util.conf') + assert actual is None out, err = capfd.readouterr() assert 'MET_INSTALL_DIR must be set correctly to run METplus' in err diff --git a/metplus/util/run_util.py b/metplus/util/run_util.py index 994c68d7b5..cef74cc930 100644 --- a/metplus/util/run_util.py +++ b/metplus/util/run_util.py @@ -163,11 +163,11 @@ def pre_run_setup(config_inputs): logger.error("Correct configuration variables and rerun. Exiting.") logger.info(f"Check the log file for more information: {log_file}") - sys.exit(1) + return None if not config.getdir('MET_INSTALL_DIR', must_exist=True): logger.error('MET_INSTALL_DIR must be set correctly to run METplus') - sys.exit(1) + return None # handle dir to write temporary files handle_tmp_dir(config) diff --git a/ush/run_metplus.py b/ush/run_metplus.py index 66dbb038a2..108e5b1711 100755 --- a/ush/run_metplus.py +++ b/ush/run_metplus.py @@ -26,9 +26,7 @@ import produtil.setup -from metplus.util import metplus_check from metplus.util import pre_run_setup, run_metplus, post_run_cleanup -from metplus import __version__ as metplus_version '''!@namespace run_metplus Main script the processes all the tasks in the PROCESS_LIST @@ -42,6 +40,8 @@ def main(): config_inputs = get_config_inputs_from_command_line() config = pre_run_setup(config_inputs) + if not config: + return False # warn if calling master_metplus.py script_name = os.path.basename(__file__) @@ -53,6 +53,7 @@ def main(): total_errors = run_metplus(config) post_run_cleanup(config, 'METplus', total_errors) + return True def usage(): @@ -121,7 +122,8 @@ def get_config_inputs_from_command_line(): if __name__ == "__main__": try: produtil.setup.setup(send_dbn=False, jobname='run-METplus') - main() + if not main(): + sys.exit(1) except Exception as exc: print(traceback.format_exc()) print('ERROR: run_metplus failed: %s' % exc) From 1a41a7377849343880158879732eec5bf74283d8 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Tue, 14 Nov 2023 17:24:27 -0700 Subject: [PATCH 04/30] remove unused imports --- metplus/wrappers/tc_diag_wrapper.py | 2 +- metplus/wrappers/tc_pairs_wrapper.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/metplus/wrappers/tc_diag_wrapper.py b/metplus/wrappers/tc_diag_wrapper.py index 52797d9824..5a45202a45 100755 --- a/metplus/wrappers/tc_diag_wrapper.py +++ b/metplus/wrappers/tc_diag_wrapper.py @@ -14,7 +14,7 @@ from ..util import time_util from . import RuntimeFreqWrapper -from ..util import do_string_sub, skip_time, get_lead_sequence +from ..util import do_string_sub, get_lead_sequence from ..util import parse_var_list, sub_var_list, getlist from ..util import find_indices_in_config_section from ..util.met_config import add_met_config_dict_list diff --git a/metplus/wrappers/tc_pairs_wrapper.py b/metplus/wrappers/tc_pairs_wrapper.py index c7b5a7cf9c..6df53af8f0 100755 --- a/metplus/wrappers/tc_pairs_wrapper.py +++ b/metplus/wrappers/tc_pairs_wrapper.py @@ -20,12 +20,11 @@ import datetime import glob -from ..util import getlist, get_lead_sequence, skip_time, mkdir_p +from ..util import getlist, mkdir_p from ..util import ti_calculate from ..util import do_string_sub from ..util import get_tags, find_indices_in_config_section from ..util.met_config import add_met_config_dict_list -from ..util import time_generator, log_runtime_banner, add_to_time_input from . import RuntimeFreqWrapper '''!@namespace TCPairsWrapper From c9351d2b652778eecde64f901ac66b4cd55ebf55 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Wed, 15 Nov 2023 15:25:03 -0700 Subject: [PATCH 05/30] also support variables that do not have a number index, e.g. WAVELET_STAT_TITLE_LOCATION_X_LL and WAVELET_STAT_TITLE_LOCATION1_X_LL --- metplus/util/met_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metplus/util/met_config.py b/metplus/util/met_config.py index 7cf33564e1..40a40039e1 100644 --- a/metplus/util/met_config.py +++ b/metplus/util/met_config.py @@ -307,7 +307,7 @@ def add_met_config_dict_list(config, app_name, output_dict, dict_name, function for more information) """ search_string = f'{app_name}_{dict_name}'.upper() - regex = r'^' + search_string + r'(\d+)_(\w+)$' + regex = r'^' + search_string + r'(\d*)_(\w+)$' indices = find_indices_in_config_section(regex, config, index_index=1, id_index=2) From 8ae12acdcda241520c2dda6e005e4bdcd5108448 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Wed, 15 Nov 2023 15:37:50 -0700 Subject: [PATCH 06/30] improve add_met_config_dict logic to properly support dictionaries that contain a member that is a list of dictionaries -- previously only a single item in the list of dictionaries could be set. now any number of those variables can be defined --- metplus/util/met_config.py | 77 ++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/metplus/util/met_config.py b/metplus/util/met_config.py index 40a40039e1..6fab11d27b 100644 --- a/metplus/util/met_config.py +++ b/metplus/util/met_config.py @@ -13,6 +13,7 @@ from .config_metplus import parse_var_list from .field_util import format_all_field_info + class METConfig: """! Stores information for a member of a MET config variables that can be used to set the value, the data type of the item, @@ -195,6 +196,11 @@ def add_met_config_dict(config, app_name, output_dict, dict_name, items): for nickname in nicknames: metplus_configs.append(nickname) + # if list of dictionaries (dictlist) + elif 'list' in data_type: + children = get_met_config_dict_list(config, app_name, f'{dict_name}_{name}', kids) + if not children: + continue # if dictionary, read get children from MET config else: children = [] @@ -263,15 +269,22 @@ def add_met_config_item(config, item, output_dict, depth=0): # handle dictionary or dictionary list item if 'dict' in item.data_type: tmp_dict = {} - for child in item.children: - if not add_met_config_item(config, child, tmp_dict, - depth=depth+1): + # handle list of dictionaries (dictlist) + if isinstance(item.children, dict): + dict_string = format_met_config_dict_list(config, item.name, item.children) + if dict_string is None: return False + # handle dictionary with list of children + else: + for child in item.children: + if not add_met_config_item(config, child, tmp_dict, + depth=depth+1): + return False - dict_string = format_met_config(item.data_type, - tmp_dict, - item.name, - keys=None) + dict_string = format_met_config(item.data_type, + tmp_dict, + item.name, + keys=None) # if handling dict MET config that is not nested inside another if not depth and item.data_type == 'dict': @@ -293,8 +306,7 @@ def add_met_config_item(config, item, output_dict, depth=0): **item.extra_args) -def add_met_config_dict_list(config, app_name, output_dict, dict_name, - dict_items): +def get_met_config_dict_list(config, app_name, dict_name, dict_items): """! Read METplusConfig and format MET config variables that are a list of dictionaries. Sets value in output dict with key starting with METPLUS_. @@ -313,10 +325,9 @@ def add_met_config_dict_list(config, app_name, output_dict, dict_name, id_index=2) all_met_config_items = {} - is_ok = True for index, items in indices.items(): # read all variables for each index - met_config_items = {} + met_config_items = [] # check if any variable found doesn't match valid variables not_in_dict = [item for item in items @@ -325,8 +336,7 @@ def add_met_config_dict_list(config, app_name, output_dict, dict_name, for item in not_in_dict: config.logger.error("Invalid variable: " f"{search_string}{index}_{item}") - is_ok = False - continue + return None for name, item_info in dict_items.items(): data_type, extra, kids, nicknames = _parse_item_info(item_info) @@ -338,22 +348,43 @@ def add_met_config_dict_list(config, app_name, output_dict, dict_name, extra_args=extra_args, ) - if not add_met_config_item(config, item, met_config_items): - is_ok = False + met_config_items.append(item) - dict_string = format_met_config('dict', - met_config_items, - name='') - all_met_config_items[index] = dict_string + all_met_config_items[index] = met_config_items + + return all_met_config_items + + +def add_met_config_dict_list(config, app_name, output_dict, dict_name, + dict_items): + is_ok = True + all_met_configs = get_met_config_dict_list(config, app_name, dict_name, dict_items) + if all_met_configs is None: + return False + output_string = format_met_config_dict_list(config, dict_name, all_met_configs) + if output_string is None: + is_ok = False - # format list of dictionaries - output_string = format_met_config('list', - all_met_config_items, - dict_name) output_dict[f'METPLUS_{dict_name.upper()}_LIST'] = output_string return is_ok +def format_met_config_dict_list(config, dict_name, all_met_configs): + all_met_config_items = {} + for index, met_configs in all_met_configs.items(): + met_config_items = {} + for met_config in met_configs: + if not add_met_config_item(config, met_config, met_config_items): + return None + + dict_string = format_met_config('dict', met_config_items, name='') + all_met_config_items[index] = dict_string + + # format list of dictionaries + output_string = format_met_config('list', all_met_config_items, dict_name) + return output_string + + def format_met_config(data_type, c_dict, name, keys=None): """! Return formatted variable named with any if they are set to a value. If none of the items are set, return empty string From 1d852aca87d13a0a397921fa34b670e0cc3b6463 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Wed, 15 Nov 2023 15:38:24 -0700 Subject: [PATCH 07/30] add support for more MET config variables for WaveletStat wrapper --- metplus/wrappers/wavelet_stat_wrapper.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/metplus/wrappers/wavelet_stat_wrapper.py b/metplus/wrappers/wavelet_stat_wrapper.py index 9ac25bc943..fa19e586e2 100755 --- a/metplus/wrappers/wavelet_stat_wrapper.py +++ b/metplus/wrappers/wavelet_stat_wrapper.py @@ -158,7 +158,23 @@ def create_c_dict(self): self.add_met_config_dict('title', { 'width': 'int', - 'location': 'float', # TODO: create dict list for location + 'location': ('dictlist', '', {'x_ll': 'int', 'y_ll': 'int'}) }) + self.add_met_config_dict('wavelet', { + 'type': ('string', 'remove_quotes,uppercase'), + 'member': 'int' + }) + + self.add_met_config(name='ps_plot_flag', data_type='bool') + + for config_name in ('fcst_raw_plot', 'obs_raw_plot', 'wvlt_plot'): + self.add_met_config_dict(config_name, { + 'color_table': 'string', + 'plot_min': 'float', + 'plot_max': 'float', + }) + + self.add_met_config(name='output_prefix', data_type='string') + return c_dict From 126cb2c21bfe0183316b5c65927232aa80d91f4c Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 08:43:20 -0700 Subject: [PATCH 08/30] change post_run_cleanup function to return boolean instead of calling sys.exit when there are errors. this prevents scripts that may call this function from exiting when there is an error in a use case --- internal/tests/pytests/util/run_util/test_run_util.py | 8 +++----- metplus/util/run_util.py | 4 ++-- ush/run_metplus.py | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) 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 9db4183816..661d106c56 100644 --- a/internal/tests/pytests/util/run_util/test_run_util.py +++ b/internal/tests/pytests/util/run_util/test_run_util.py @@ -436,7 +436,7 @@ def test_post_run_cleanup_no_errors(post_run_config): with mock.patch.object(ru, 'get_logfile_info', return_value='/log/file.log'): actual = ru.post_run_cleanup(post_run_config, 'fake_app', 0) - assert actual == None + assert actual is True for msg in expected_msgs: _check_log_info(post_run_config, [msg]) @@ -454,11 +454,9 @@ def test_post_run_cleanup_errors(post_run_config): ru, 'get_user_info', return_value='Allan H. Murphy' ) as mock_user: with mock.patch.object(ru, 'get_logfile_info', return_value='/log/file.log'): - with mock.patch.object(ru.sys, 'exit') as mock_sys: - actual = ru.post_run_cleanup(post_run_config, 'fake_app', 5) + actual = ru.post_run_cleanup(post_run_config, 'fake_app', 5) - mock_sys.assert_called_once_with(1) - assert actual == None + assert actual is False _check_log_info( post_run_config, ['Check the log file for more information: /log/file.log'] ) diff --git a/metplus/util/run_util.py b/metplus/util/run_util.py index cef74cc930..be0d5fd867 100644 --- a/metplus/util/run_util.py +++ b/metplus/util/run_util.py @@ -335,7 +335,7 @@ def post_run_cleanup(config, app_name, total_errors): # print success log message if terminal does not include INFO if not log_terminal_includes_info(config): print(success_log) - return + return True error_msg = (f'{app_name} has finished running{user_string} ' f'but had {total_errors} error') @@ -344,4 +344,4 @@ def post_run_cleanup(config, app_name, total_errors): error_msg += '.' logger.error(error_msg) logger.info(log_message) - sys.exit(1) + return False diff --git a/ush/run_metplus.py b/ush/run_metplus.py index 108e5b1711..fe9152cb14 100755 --- a/ush/run_metplus.py +++ b/ush/run_metplus.py @@ -52,8 +52,7 @@ def main(): total_errors = run_metplus(config) - post_run_cleanup(config, 'METplus', total_errors) - return True + return post_run_cleanup(config, 'METplus', total_errors) def usage(): From f4f57603eb0bcc0cb79ade169e878ad37c29c5de Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 08:43:36 -0700 Subject: [PATCH 09/30] remove redundant exit because usage function exits --- ush/run_metplus.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ush/run_metplus.py b/ush/run_metplus.py index fe9152cb14..482c67e81f 100755 --- a/ush/run_metplus.py +++ b/ush/run_metplus.py @@ -90,7 +90,6 @@ def get_config_inputs_from_command_line(): for help_arg in help_args: if help_arg in sys.argv: usage() - sys.exit(0) # pull out command line arguments config_inputs = [] From 18a6b182f0b1543d9623bebb20c1702db0a55fea Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:54:36 -0700 Subject: [PATCH 10/30] added default config for wavelet_stat --- parm/met_config/WaveletStatConfig_wrapped | 143 ++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 parm/met_config/WaveletStatConfig_wrapped diff --git a/parm/met_config/WaveletStatConfig_wrapped b/parm/met_config/WaveletStatConfig_wrapped new file mode 100644 index 0000000000..4ad968af7e --- /dev/null +++ b/parm/met_config/WaveletStatConfig_wrapped @@ -0,0 +1,143 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Wavelet-Stat configuration file. +// +// For additional information, please see the MET User's Guide. +// +//////////////////////////////////////////////////////////////////////////////// + +// +// Output model name to be written +// +model = "WRF"; + +// +// Output description to be written +// May be set separately in each "obs.field" entry +// +desc = "NA"; + +// +// Output observation type to be written +// +obtype = "ANALYS"; + +//////////////////////////////////////////////////////////////////////////////// + +// +// Verification grid +// May be set separately in each "field" entry +// +regrid = { + to_grid = NONE; + method = NEAREST; + width = 1; + vld_thresh = 0.5; + shape = SQUARE; +} + +//////////////////////////////////////////////////////////////////////////////// + +// +// May be set separately in each "field" entry +// +censor_thresh = []; +censor_val = []; + +// +// Forecast and observation fields to be verified +// +fcst = { + field = [ + { + name = "APCP"; + level = [ "A03" ]; + cat_thresh = [ >0.0, >=5.0 ]; + } + ]; +} +obs = fcst; + +//////////////////////////////////////////////////////////////////////////////// + +// +// Handle missing data +// +mask_missing_flag = NONE; + +//////////////////////////////////////////////////////////////////////////////// + +// +// Decompose the field into dyadic tiles +// +grid_decomp_flag = AUTO; + +tile = { + width = 0; + location = [ + { + x_ll = 0; + y_ll = 0; + } + ]; +} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Wavelet to be used for the decomposition +// +wavelet = { + type = HAAR; + member = 2; +} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Statistical output types +// +output_flag = { + isc = NONE; +} + +// +// NetCDF matched pairs and PostScript output files +// +nc_pairs_flag = { + raw = TRUE; + diff = TRUE; +} +ps_plot_flag = TRUE; + +//////////////////////////////////////////////////////////////////////////////// + +// +// Plotting information +// +met_data_dir = "MET_BASE"; + +fcst_raw_plot = { + color_table = "MET_BASE/colortables/met_default.ctable"; + plot_min = 0.0; + plot_max = 0.0; +} + +obs_raw_plot = { + color_table = "MET_BASE/colortables/met_default.ctable"; + plot_min = 0.0; + plot_max = 0.0; +} + +wvlt_plot = { + color_table = "MET_BASE/colortables/NCL_colortables/BlWhRe.ctable"; + plot_min = 0.0; + plot_max = 0.0; +} + +//////////////////////////////////////////////////////////////////////////////// + +output_prefix = ""; +version = "V11.1.0"; + +//////////////////////////////////////////////////////////////////////////////// From 786d8836fd543498628c478ddc0413194e31deea Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 10:47:08 -0700 Subject: [PATCH 11/30] added documentation and wrapped MET config for WaveletStat wrapper --- docs/Users_Guide/glossary.rst | 378 +++++++++++++++++- docs/Users_Guide/wrappers.rst | 354 ++++++++++++++++ metplus/util/constants.py | 1 + parm/met_config/WaveletStatConfig_wrapped | 105 +++-- .../WaveletStat/WaveletStat.conf | 133 ++++++ 5 files changed, 910 insertions(+), 61 deletions(-) create mode 100644 parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf diff --git a/docs/Users_Guide/glossary.rst b/docs/Users_Guide/glossary.rst index ef14ea48bc..e4dad390ca 100644 --- a/docs/Users_Guide/glossary.rst +++ b/docs/Users_Guide/glossary.rst @@ -2057,7 +2057,7 @@ METplus Configuration Glossary | *Used by:* GridStat GRID_STAT_OUTPUT_TEMPLATE - Sets the subdirectories below :term:`GRID_STAT_OUTPUT_DIR` using a template to allow run time information. If LOOP_BY = VALID, default value is valid time YYYYMMDDHHMM/grid_stat. If LOOP_BY = INIT, default value is init time YYYYMMDDHHMM/grid_stat. + Sets the subdirectories below :term:`GRID_STAT_OUTPUT_DIR` using a template to allow run time information. | *Used by:* GridStat @@ -11128,3 +11128,379 @@ METplus Configuration Glossary Specify the value for 'cat_thresh' in the MET configuration file for GridStat. | *Used by:* GridStat + + WAVELET_STAT_MODEL + Specify the value for 'model' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_DESC + Specify the value for 'desc' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_OBTYPE + Specify the value for 'obtype' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_REGRID_TO_GRID + Specify the value for 'regrid.to_grid' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_REGRID_METHOD + Specify the value for 'regrid.method' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_REGRID_WIDTH + Specify the value for 'regrid.width' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_REGRID_VLD_THRESH + Specify the value for 'regrid.vld_thresh' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_REGRID_SHAPE + Specify the value for 'regrid.shape' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_CENSOR_THRESH + Specify the value for 'censor_thresh' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_CENSOR_VAL + Specify the value for 'censor_val' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_MASK_MISSING_FLAG + Specify the value for 'mask_missing_flag' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_GRID_DECOMP_FLAG + Specify the value for 'grid_decomp_flag' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_TITLE_WIDTH + Specify the value for 'title.width' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_TITLE_LOCATION_X_LL + Specify the value for the nth 'title.location.x_ll' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_TITLE_LOCATION_Y_LL + Specify the value for the nth 'title.location.y_ll' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_WAVELET_TYPE + Specify the value for 'wavelet.type' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_WAVELET_MEMBER + Specify the value for 'wavelet.member' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_OUTPUT_FLAG_ISC + Specify the value for 'output_flag.isc' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_NC_PAIRS_FLAG_RAW + Specify the value for 'nc_pairs_flag.raw' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_NC_PAIRS_FLAG_DIFF + Specify the value for 'nc_pairs_flag.diff' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_PS_PLOT_FLAG + Specify the value for 'ps_plot_flag' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_FCST_RAW_PLOT_COLOR_TABLE + Specify the value for 'fcst_raw_plot.color_table' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_FCST_RAW_PLOT_PLOT_MIN + Specify the value for 'fcst_raw_plot.plot_min' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_FCST_RAW_PLOT_PLOT_MAX + Specify the value for 'fcst_raw_plot.plot_max' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_OBS_RAW_PLOT_COLOR_TABLE + Specify the value for 'obs_raw_plot.color_table' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_OBS_RAW_PLOT_PLOT_MIN + Specify the value for 'obs_raw_plot.plot_min' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_OBS_RAW_PLOT_PLOT_MAX + Specify the value for 'obs_raw_plot.plot_max' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_WVLT_PLOT_COLOR_TABLE + Specify the value for 'wvlt_plot.color_table' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_WVLT_PLOT_PLOT_MIN + Specify the value for 'wvlt_plot.plot_min' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_WVLT_PLOT_PLOT_MAX + Specify the value for 'wvlt_plot.plot_max' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_OUTPUT_PREFIX + Specify the value for 'output_prefix' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_FILE_WINDOW_BEGIN + See :term:`OBS_WAVELET_STAT_FILE_WINDOW_BEGIN` + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_FILE_WINDOW_END + See :term:`OBS_WAVELET_STAT_FILE_WINDOW_END` + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_INPUT_DATATYPE + Specify the data type of the input directory for forecast files used with the MET WAVELET_STAT tool. Currently valid options are NETCDF, GRIB, and GEMPAK. If set to GEMPAK, data will automatically be converted to NetCDF via GempakToCF. A corresponding variable exists for observation data called :term:`OBS_WAVELET_STAT_INPUT_DATATYPE`. + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_INPUT_DIR + Input directory for forecast files to use with the MET tool wavelet_stat. A corresponding variable exists for observation data called :term:`OBS_WAVELET_STAT_INPUT_DIR`. + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_INPUT_TEMPLATE + Template used to specify forecast input filenames for the MET tool wavelet_stat. A corresponding variable exists for observation data called :term:`OBS_WAVELET_STAT_INPUT_TEMPLATE`. To utilize Python Embedding as input to the MET tools, set this value to PYTHON_NUMPY or PYTHON_XARRAY. + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_PROB_THRESH + Threshold values to be used for probabilistic data in wavelet_stat. The value can be a single item or a comma separated list of items that must start with a comparison operator (>,>=,==,!=,<,<=,gt,ge,eq,ne,lt,le). A corresponding variable exists for observation data called :term:`OBS_WAVELET_STAT_PROB_THRESH`. + + | *Used by:* WaveletStat + + OBS_WAVELET_STAT_FILE_WINDOW_BEGIN + Used to control the lower bound of the window around the valid time to determine if a file should be used for processing by WaveletStat. See :ref:`Directory_and_Filename_Template_Info` subsection called 'Using Windows to Find Valid Files.' Units are seconds. If :term:`OBS_WAVELET_STAT_FILE_WINDOW_BEGIN` is not set in the config file, the value of :term:`OBS_FILE_WINDOW_BEGIN` will be used instead. If both file window begin and window end values are set to 0, then METplus will require an input file with an exact time match to process. + + | *Used by:* WaveletStat + + OBS_WAVELET_STAT_FILE_WINDOW_END + Used to control the upper bound of the window around the valid time to determine if a file should be used for processing by WaveletStat. See :ref:`Directory_and_Filename_Template_Info` subsection called 'Using Windows to Find Valid Files.' Units are seconds. If :term:`OBS_WAVELET_STAT_FILE_WINDOW_END` is not set in the config file, the value of :term:`OBS_FILE_WINDOW_END` will be used instead. If both file window begin and window end values are set to 0, then METplus will require an input file with an exact time match to process. + + | *Used by:* WaveletStat + + OBS_WAVELET_STAT_INPUT_DATATYPE + See :term:`FCST_WAVELET_STAT_INPUT_DATATYPE` + + | *Used by:* WaveletStat + + OBS_WAVELET_STAT_INPUT_DIR + See :term:`FCST_WAVELET_STAT_INPUT_DIR` + + | *Used by:* WaveletStat + + OBS_WAVELET_STAT_INPUT_TEMPLATE + See :term:`FCST_WAVELET_STAT_INPUT_TEMPLATE` + + | *Used by:* WaveletStat + + OBS_WAVELET_STAT_PROB_THRESH + See :term:`FCST_WAVELET_STAT_PROB_THRESH` + + | *Used by:* WaveletStat + + WAVELET_STAT_OUTPUT_DIR + Specify the output directory where files from the MET wavelet_stat tool are written. + + | *Used by:* WaveletStat + + WAVELET_STAT_OUTPUT_TEMPLATE + Sets the subdirectories below :term:`WAVELET_STAT_OUTPUT_DIR` using a template to allow run time information. + + | *Used by:* WaveletStat + + LOG_WAVELET_STAT_VERBOSITY + Overrides the log verbosity for WaveletStat only. + If not set, the verbosity level is controlled by :term:`LOG_MET_VERBOSITY`. + + | *Used by:* WaveletStat + + WAVELET_STAT_CONFIG_FILE + Path to configuration file read by wavelet_stat. + If unset, parm/met_config/WaveletStatConfig_wrapped will be used. + + | *Used by:* WaveletStat + + WAVELET_STAT_ONCE_PER_FIELD + True/False. If True, wavelet_stat will run once to process all name/level/threshold combinations specified. + If False, it will run once for each name/level. Some cases require this to be set to False, + for example processing probablistic forecasts or precipitation accumulations. + + | *Used by:* WaveletStat + + WAVELET_STAT_CUSTOM_LOOP_LIST + Sets custom string loop list for a specific wrapper. See :term:`CUSTOM_LOOP_LIST`. + + | *Used by:* WaveletStat + + WAVELET_STAT_SKIP_IF_OUTPUT_EXISTS + If True, do not run app if output file already exists. Set to False to overwrite files. + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_FILE_TYPE + Specify the value for 'fcst.file_type' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + OBS_WAVELET_STAT_FILE_TYPE + Specify the value for 'obs.file_type' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_IS_PROB + Wrapper-specific version of :term:`FCST_IS_PROB`. + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_PROB_IN_GRIB_PDS + Wrapper-specific version of :term:`FCST_PROB_IN_GRIB_PDS`. + + | *Used by:* WaveletStat + + WAVELET_STAT_MET_CONFIG_OVERRIDES + Override any variables in the MET configuration file that are not + supported by the wrapper. This should be set to the full variable name + and value that you want to override, including the equal sign and the + ending semi-colon. The value is directly appended to the end of the + wrapped MET config file. + + Example: + WAVELET_STAT_MET_CONFIG_OVERRIDES = desc = "override_desc"; model = "override_model"; + + See :ref:`Overriding Unsupported MET config file settings` for more information + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_WINDOW_BEGIN + Passed to the WaveletStat MET config file to determine the range of data within a file that should be used for processing. Units are seconds. If the variable is not set, WaveletStat will use :term:`FCST_WINDOW_BEGIN`. + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_WINDOW_END + Passed to the WaveletStat MET config file to determine the range of data within a file that should be used for processing. Units are seconds. If the variable is not set, WaveletStat will use :term:`FCST_WINDOW_END`. + + | *Used by:* WaveletStat + + OBS_WAVELET_STAT_WINDOW_BEGIN + Passed to the WaveletStat MET config file to determine the range of data within a file that should be used for processing. Units are seconds. If the variable is not set, WaveletStat will use :term:`OBS_WINDOW_BEGIN`. + + | *Used by:* WaveletStat + + OBS_WAVELET_STAT_WINDOW_END + Passed to the WaveletStat MET config file to determine the range of data within a file that should be used for processing. Units are seconds. If the variable is not set, WaveletStat will use :term:`OBS_WINDOW_END`. + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_VAR_NAME + Wrapper specific field info variable. See :term:`FCST_VAR_NAME`. + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_VAR_LEVELS + Wrapper specific field info variable. See :term:`FCST_VAR_LEVELS`. + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_VAR_THRESH + Wrapper specific field info variable. See :term:`FCST_VAR_THRESH`. + + | *Used by:* WaveletStat + + FCST_WAVELET_STAT_VAR_OPTIONS + Wrapper specific field info variable. See :term:`FCST_VAR_OPTIONS`. + + | *Used by:* WaveletStat + + OBS_WAVELET_STAT_VAR_NAME + Wrapper specific field info variable. See :term:`OBS_VAR_NAME`. + + | *Used by:* WaveletStat + + OBS_WAVELET_STAT_VAR_LEVELS + Wrapper specific field info variable. See :term:`OBS_VAR_LEVELS`. + + | *Used by:* WaveletStat + + OBS_WAVELET_STAT_VAR_THRESH + Wrapper specific field info variable. See :term:`OBS_VAR_THRESH`. + + | *Used by:* WaveletStat + + OBS_WAVELET_STAT_VAR_OPTIONS + Wrapper specific field info variable. See :term:`OBS_VAR_OPTIONS`. + + | *Used by:* WaveletStat + + WAVELET_STAT_SKIP_VALID_TIMES + List of valid times to skip for WaveletStat only. + If set, values set in :term:`SKIP_VALID_TIMES` are ignored for WaveletStat. + See :term:`SKIP_VALID_TIMES` for formatting information. + + | *Used by:* WaveletStat + + WAVELET_STAT_INC_VALID_TIMES + List of valid times to include for WaveletStat only. + If set, values set in :term:`INC_VALID_TIMES` are ignored for WaveletStat. + See :term:`SKIP_VALID_TIMES` for formatting information. + + | *Used by:* WaveletStat + + WAVELET_STAT_SKIP_INIT_TIMES + List of initialization times to skip for WaveletStat only. + If set, values set in :term:`SKIP_INIT_TIMES` are ignored for WaveletStat. + See :term:`SKIP_VALID_TIMES` for formatting information. + + | *Used by:* WaveletStat + + WAVELET_STAT_INC_INIT_TIMES + List of initialization times to include for WaveletStat only. + If set, values set in :term:`INC_INIT_TIMES` are ignored for WaveletStat. + See :term:`SKIP_VALID_TIMES` for formatting information. + + | *Used by:* WaveletStat diff --git a/docs/Users_Guide/wrappers.rst b/docs/Users_Guide/wrappers.rst index b29a86b7b9..3830523601 100644 --- a/docs/Users_Guide/wrappers.rst +++ b/docs/Users_Guide/wrappers.rst @@ -10914,3 +10914,357 @@ METplus Configuration | :term:`USER_SCRIPT_SKIP_INIT_TIMES` | :term:`USER_SCRIPT_INC_INIT_TIMES` | + +.. _wavelet_stat_wrapper: + +WaveletStat +=========== + +Description +----------- + +Used to configure the MET tool wavelet_stat. + +METplus Configuration +--------------------- + +| :term:`FCST_WAVELET_STAT_INPUT_DIR` +| :term:`OBS_WAVELET_STAT_INPUT_DIR` +| :term:`WAVELET_STAT_OUTPUT_DIR` +| :term:`FCST_WAVELET_STAT_INPUT_TEMPLATE` +| :term:`OBS_WAVELET_STAT_INPUT_TEMPLATE` +| :term:`WAVELET_STAT_OUTPUT_TEMPLATE` +| :term:`LOG_WAVELET_STAT_VERBOSITY` +| :term:`WAVELET_STAT_CONFIG_FILE` +| :term:`FCST_WAVELET_STAT_INPUT_DATATYPE` +| :term:`OBS_WAVELET_STAT_INPUT_DATATYPE` +| :term:`WAVELET_STAT_ONCE_PER_FIELD` +| :term:`WAVELET_STAT_CUSTOM_LOOP_LIST` +| :term:`WAVELET_STAT_SKIP_IF_OUTPUT_EXISTS` +| :term:`FCST_WAVELET_STAT_FILE_TYPE` +| :term:`OBS_WAVELET_STAT_FILE_TYPE` +| :term:`FCST_WAVELET_STAT_IS_PROB` +| :term:`FCST_WAVELET_STAT_PROB_IN_GRIB_PDS` +| :term:`WAVELET_STAT_MET_CONFIG_OVERRIDES` +| :term:`FCST_WAVELET_STAT_PROB_THRESH` +| :term:`OBS_WAVELET_STAT_PROB_THRESH` +| :term:`FCST_WAVELET_STAT_WINDOW_BEGIN` +| :term:`FCST_WAVELET_STAT_WINDOW_END` +| :term:`OBS_WAVELET_STAT_WINDOW_BEGIN` +| :term:`OBS_WAVELET_STAT_WINDOW_END` +| :term:`FCST_WAVELET_STAT_FILE_WINDOW_BEGIN` +| :term:`FCST_WAVELET_STAT_FILE_WINDOW_END` +| :term:`OBS_WAVELET_STAT_FILE_WINDOW_BEGIN` +| :term:`OBS_WAVELET_STAT_FILE_WINDOW_END` +| :term:`FCST_WAVELET_STAT_VAR_NAME` +| :term:`FCST_WAVELET_STAT_VAR_LEVELS` +| :term:`FCST_WAVELET_STAT_VAR_THRESH` +| :term:`FCST_WAVELET_STAT_VAR_OPTIONS` +| :term:`OBS_WAVELET_STAT_VAR_NAME` +| :term:`OBS_WAVELET_STAT_VAR_LEVELS` +| :term:`OBS_WAVELET_STAT_VAR_THRESH` +| :term:`OBS_WAVELET_STAT_VAR_OPTIONS` +| :term:`WAVELET_STAT_SKIP_VALID_TIMES` +| :term:`WAVELET_STAT_INC_VALID_TIMES` +| :term:`WAVELET_STAT_SKIP_INIT_TIMES` +| :term:`WAVELET_STAT_INC_INIT_TIMES` +| :term:`WAVELET_STAT_MODEL` +| :term:`WAVELET_STAT_DESC` +| :term:`WAVELET_STAT_OBTYPE` +| :term:`WAVELET_STAT_REGRID_TO_GRID` +| :term:`WAVELET_STAT_REGRID_METHOD` +| :term:`WAVELET_STAT_REGRID_WIDTH` +| :term:`WAVELET_STAT_REGRID_VLD_THRESH` +| :term:`WAVELET_STAT_REGRID_SHAPE` +| :term:`WAVELET_STAT_CENSOR_THRESH` +| :term:`WAVELET_STAT_CENSOR_VAL` +| :term:`WAVELET_STAT_MASK_MISSING_FLAG` +| :term:`WAVELET_STAT_GRID_DECOMP_FLAG` +| :term:`WAVELET_STAT_TITLE_WIDTH` +| :term:`WAVELET_STAT_TITLE_LOCATION_X_LL` +| :term:`WAVELET_STAT_TITLE_LOCATION_Y_LL` +| :term:`WAVELET_STAT_WAVELET_TYPE` +| :term:`WAVELET_STAT_WAVELET_MEMBER` +| :term:`WAVELET_STAT_OUTPUT_FLAG_ISC` +| :term:`WAVELET_STAT_NC_PAIRS_FLAG_RAW` +| :term:`WAVELET_STAT_NC_PAIRS_FLAG_DIFF` +| :term:`WAVELET_STAT_PS_PLOT_FLAG` +| :term:`WAVELET_STAT_FCST_RAW_PLOT_COLOR_TABLE` +| :term:`WAVELET_STAT_FCST_RAW_PLOT_PLOT_MIN` +| :term:`WAVELET_STAT_FCST_RAW_PLOT_PLOT_MAX` +| :term:`WAVELET_STAT_OBS_RAW_PLOT_COLOR_TABLE` +| :term:`WAVELET_STAT_OBS_RAW_PLOT_PLOT_MIN` +| :term:`WAVELET_STAT_OBS_RAW_PLOT_PLOT_MAX` +| :term:`WAVELET_STAT_WVLT_PLOT_COLOR_TABLE` +| :term:`WAVELET_STAT_WVLT_PLOT_PLOT_MIN` +| :term:`WAVELET_STAT_WVLT_PLOT_PLOT_MAX` +| :term:`WAVELET_STAT_OUTPUT_PREFIX` + +.. _wavelet-stat-met-conf: + +MET Configuration +----------------- + +Below is the wrapped MET configuration file used for this wrapper. +Environment variables are used to control entries in this configuration file. +The default value for each environment variable is obtained from +(except where noted below): + +`MET_INSTALL_DIR/share/met/config/WaveletStatConfig_default `_ + +Below the file contents are descriptions of each environment variable +referenced in this file and the corresponding METplus configuration item used +to set the value of the environment variable. For detailed examples showing +how METplus sets the values of these environment variables, +see :ref:`How METplus controls MET config file settings`. + +.. dropdown:: Click to view parm/met_config/WaveletStatConfig_wrapped + + .. literalinclude:: ../../parm/met_config/WaveletStatConfig_wrapped + +Environment variables in wrapped MET config +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +${METPLUS_MODEL} +^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_MODEL` + - model + +${METPLUS_DESC} +^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_DESC` + - desc + +${METPLUS_OBTYPE} +^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_OBTYPE` + - obtype + +${METPLUS_REGRID_DICT} +^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_REGRID_TO_GRID` + - regrid.to_grid + * - :term:`WAVELET_STAT_REGRID_METHOD` + - regrid.method + * - :term:`WAVELET_STAT_REGRID_WIDTH` + - regrid.width + * - :term:`WAVELET_STAT_REGRID_VLD_THRESH` + - regrid.vld_thresh + * - :term:`WAVELET_STAT_REGRID_SHAPE` + - regrid.shape + +${METPLUS_CENSOR_THRESH} +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_CENSOR_THRESH` + - censor_thresh + +${METPLUS_CENSOR_VAL} +^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_CENSOR_VAL` + - censor_val + +${METPLUS_MASK_MISSING_FLAG} +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_MASK_MISSING_FLAG` + - mask_missing_flag + +${METPLUS_GRID_DECOMP_FLAG} +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_GRID_DECOMP_FLAG` + - grid_decomp_flag + +${METPLUS_TITLE_DICT} +^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_TITLE_WIDTH` + - title.width + * - :term:`WAVELET_STAT_TITLE_LOCATION_X_LL` + - title.location.x_ll + * - :term:`WAVELET_STAT_TITLE_LOCATION_Y_LL` + - title.location.y_ll + +${METPLUS_WAVELET_DICT} +^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_WAVELET_TYPE` + - wavelet.type + * - :term:`WAVELET_STAT_WAVELET_MEMBER` + - wavelet.member + +${METPLUS_OUTPUT_FLAG_DICT} +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_OUTPUT_FLAG_ISC` + - output_flag.isc + +${METPLUS_NC_PAIRS_FLAG_DICT} +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_NC_PAIRS_FLAG_RAW` + - nc_pairs_flag.raw + * - :term:`WAVELET_STAT_NC_PAIRS_FLAG_DIFF` + - nc_pairs_flag.diff + +${METPLUS_PS_PLOT_FLAG} +^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_PS_PLOT_FLAG` + - ps_plot_flag + +${METPLUS_FCST_RAW_PLOT_DICT} +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_FCST_RAW_PLOT_COLOR_TABLE` + - fcst_raw_plot.color_table + * - :term:`WAVELET_STAT_FCST_RAW_PLOT_PLOT_MIN` + - fcst_raw_plot.plot_min + * - :term:`WAVELET_STAT_FCST_RAW_PLOT_PLOT_MAX` + - fcst_raw_plot.plot_max + +${METPLUS_OBS_RAW_PLOT_DICT} +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_OBS_RAW_PLOT_COLOR_TABLE` + - obs_raw_plot.color_table + * - :term:`WAVELET_STAT_OBS_RAW_PLOT_PLOT_MIN` + - obs_raw_plot.plot_min + * - :term:`WAVELET_STAT_OBS_RAW_PLOT_PLOT_MAX` + - obs_raw_plot.plot_max + +${METPLUS_WVLT_PLOT_DICT} +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_WVLT_PLOT_COLOR_TABLE` + - wvlt_plot.color_table + * - :term:`WAVELET_STAT_WVLT_PLOT_PLOT_MIN` + - wvlt_plot.plot_min + * - :term:`WAVELET_STAT_WVLT_PLOT_PLOT_MAX` + - wvlt_plot.plot_max + +${METPLUS_OUTPUT_PREFIX} +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_OUTPUT_PREFIX` + - output_prefix + +${METPLUS_MET_CONFIG_OVERRIDES} +""""""""""""""""""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`WAVELET_STAT_MET_CONFIG_OVERRIDES` + - n/a diff --git a/metplus/util/constants.py b/metplus/util/constants.py index 9a330a42e7..38f133a0e0 100644 --- a/metplus/util/constants.py +++ b/metplus/util/constants.py @@ -43,6 +43,7 @@ 'tcstat': 'TCStat', 'usage': 'Usage', 'userscript': 'UserScript', + 'waveletstat': 'WaveletStat', } # supported file extensions that will automatically be uncompressed diff --git a/parm/met_config/WaveletStatConfig_wrapped b/parm/met_config/WaveletStatConfig_wrapped index 4ad968af7e..843fcbe302 100644 --- a/parm/met_config/WaveletStatConfig_wrapped +++ b/parm/met_config/WaveletStatConfig_wrapped @@ -9,18 +9,21 @@ // // Output model name to be written // -model = "WRF"; +//model = +${METPLUS_MODEL} // // Output description to be written // May be set separately in each "obs.field" entry // -desc = "NA"; +//desc = +${METPLUS_DESC} // // Output observation type to be written // -obtype = "ANALYS"; +//obtype = +${METPLUS_OBTYPE} //////////////////////////////////////////////////////////////////////////////// @@ -28,87 +31,74 @@ obtype = "ANALYS"; // Verification grid // May be set separately in each "field" entry // -regrid = { - to_grid = NONE; - method = NEAREST; - width = 1; - vld_thresh = 0.5; - shape = SQUARE; -} +//regrid = { +${METPLUS_REGRID_DICT} //////////////////////////////////////////////////////////////////////////////// // // May be set separately in each "field" entry // -censor_thresh = []; -censor_val = []; +//censor_thresh = +${METPLUS_CENSOR_THRESH} +//censor_val = +${METPLUS_CENSOR_VAL} // // Forecast and observation fields to be verified // fcst = { - field = [ - { - name = "APCP"; - level = [ "A03" ]; - cat_thresh = [ >0.0, >=5.0 ]; - } - ]; + ${METPLUS_FCST_FILE_TYPE} + ${METPLUS_FCST_FIELD} +} +obs = { + ${METPLUS_OBS_FILE_TYPE} + ${METPLUS_OBS_FIELD} } -obs = fcst; //////////////////////////////////////////////////////////////////////////////// // // Handle missing data // -mask_missing_flag = NONE; +//mask_missing_flag = +${METPLUS_MASK_MISSING_FLAG} //////////////////////////////////////////////////////////////////////////////// // // Decompose the field into dyadic tiles // -grid_decomp_flag = AUTO; +//grid_decomp_flag = +${METPLUS_GRID_DECOMP_FLAG} -tile = { - width = 0; - location = [ - { - x_ll = 0; - y_ll = 0; - } - ]; -} +//title = { +${METPLUS_TITLE_DICT} //////////////////////////////////////////////////////////////////////////////// // // Wavelet to be used for the decomposition // -wavelet = { - type = HAAR; - member = 2; -} +//wavelet = { +${METPLUS_WAVELET_DICT} //////////////////////////////////////////////////////////////////////////////// // // Statistical output types // -output_flag = { - isc = NONE; -} +//output_flag = { +${METPLUS_OUTPUT_FLAG_DICT} // // NetCDF matched pairs and PostScript output files // -nc_pairs_flag = { - raw = TRUE; - diff = TRUE; -} -ps_plot_flag = TRUE; +//nc_pairs_flag = { +${METPLUS_NC_PAIRS_FLAG_DICT} + +//ps_plot_flag = +${METPLUS_PS_PLOT_FLAG} //////////////////////////////////////////////////////////////////////////////// @@ -117,27 +107,22 @@ ps_plot_flag = TRUE; // met_data_dir = "MET_BASE"; -fcst_raw_plot = { - color_table = "MET_BASE/colortables/met_default.ctable"; - plot_min = 0.0; - plot_max = 0.0; -} +//fcst_raw_plot = { +${METPLUS_FCST_RAW_PLOT_DICT} -obs_raw_plot = { - color_table = "MET_BASE/colortables/met_default.ctable"; - plot_min = 0.0; - plot_max = 0.0; -} +//obs_raw_plot = { +${METPLUS_OBS_RAW_PLOT_DICT} -wvlt_plot = { - color_table = "MET_BASE/colortables/NCL_colortables/BlWhRe.ctable"; - plot_min = 0.0; - plot_max = 0.0; -} +//wvlt_plot = { +${METPLUS_WVLT_PLOT_DICT} //////////////////////////////////////////////////////////////////////////////// -output_prefix = ""; -version = "V11.1.0"; +//output_prefix = +${METPLUS_OUTPUT_PREFIX} + +//version = "V11.1.0"; //////////////////////////////////////////////////////////////////////////////// + +${METPLUS_MET_CONFIG_OVERRIDES} diff --git a/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf b/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf new file mode 100644 index 0000000000..0a7ca1bdf3 --- /dev/null +++ b/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf @@ -0,0 +1,133 @@ +[config] + +# Documentation for this use case can be found at +# https://metplus.readthedocs.io/en/latest/generated/met_tool_wrapper/WaveletStat/WaveletStat.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 = WaveletStat + + +### +# Time Info +# LOOP_BY options are INIT, VALID, RETRO, and REALTIME +# If set to INIT or RETRO: +# INIT_TIME_FMT, INIT_BEG, INIT_END, and INIT_INCREMENT must also be set +# If set to VALID or REALTIME: +# VALID_TIME_FMT, VALID_BEG, VALID_END, and VALID_INCREMENT must also be set +# LEAD_SEQ is the list of forecast leads to process +# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#timing-control +### + +LOOP_BY = INIT +INIT_TIME_FMT = %Y%m%d%H +INIT_BEG=2005080700 +INIT_END=2005080700 +INIT_INCREMENT = 12H + +LEAD_SEQ = 12 + + +### +# File I/O +# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#directory-and-filename-template-info +### + +FCST_WAVELET_STAT_INPUT_DIR = {INPUT_BASE}/met_test/data/sample_fcst +FCST_WAVELET_STAT_INPUT_TEMPLATE = {init?fmt=%Y%m%d%H}/wrfprs_ruc13_{lead?fmt=%HH}.tm00_G212 + +OBS_WAVELET_STAT_INPUT_DIR = {INPUT_BASE}/met_test/new +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} + + +### +# Field Info +# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#field-info +### + +MODEL = WRF +OBTYPE = MC_PCP + +WAVELET_STAT_ONCE_PER_FIELD = False + +#FCST_IS_PROB = false + +FCST_VAR1_NAME = APCP +FCST_VAR1_LEVELS = A03 +FCST_VAR1_THRESH = gt12.7, gt25.4, gt50.8, gt76.2 + +OBS_VAR1_NAME = APCP_03 +OBS_VAR1_LEVELS = "(*,*)" +OBS_VAR1_THRESH = gt12.7, gt25.4, gt50.8, gt76.2 + + +### +# GridStat Settings (optional) +# https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#gridstat +### + +#LOG_WAVELET_STAT_VERBOSITY = 2 + +WAVELET_STAT_CONFIG_FILE = {PARM_BASE}/met_config/WaveletStatConfig_wrapped + +#FCST_WAVELET_STAT_FILE_TYPE = +#OBS_WAVELET_STAT_FILE_TYPE = + +#FCST_WAVELET_STAT_FILE_WINDOW_BEGIN = 0 +#FCST_WAVELET_STAT_FILE_WINDOW_END = 0 +#OBS_WAVELET_STAT_FILE_WINDOW_BEGIN = 0 +#OBS_WAVELET_STAT_FILE_WINDOW_END = 0 + +#WAVELET_STAT_MODEL = +#WAVELET_STAT_DESC = +#WAVELET_STAT_OBTYPE = + +#WAVELET_STAT_REGRID_TO_GRID = +#WAVELET_STAT_REGRID_METHOD = +#WAVELET_STAT_REGRID_WIDTH = +#WAVELET_STAT_REGRID_VLD_THRESH = +#WAVELET_STAT_REGRID_SHAPE = + +#WAVELET_STAT_CENSOR_THRESH = +#WAVELET_STAT_CENSOR_VAL = + +#WAVELET_STAT_MASK_MISSING_FLAG = + +#WAVELET_STAT_GRID_DECOMP_FLAG = + +#WAVELET_STAT_TITLE_WIDTH = +#WAVELET_STAT_TITLE_LOCATION1_X_LL = +#WAVELET_STAT_TITLE_LOCATION1_Y_LL = + +#WAVELET_STAT_WAVELET_TYPE = +#WAVELET_STAT_WAVELET_MEMBER = + +#WAVELET_STAT_OUTPUT_FLAG_ISC = + +#WAVELET_STAT_NC_PAIRS_FLAG_RAW = +#WAVELET_STAT_NC_PAIRS_FLAG_DIFF = + +#WAVELET_STAT_PS_PLOT_FLAG = + +#WAVELET_STAT_FCST_RAW_PLOT_COLOR_TABLE = +#WAVELET_STAT_FCST_RAW_PLOT_PLOT_MIN = +#WAVELET_STAT_FCST_RAW_PLOT_PLOT_MAX = + +#WAVELET_STAT_OBS_RAW_PLOT_COLOR_TABLE = +#WAVELET_STAT_OBS_RAW_PLOT_PLOT_MIN = +#WAVELET_STAT_OBS_RAW_PLOT_PLOT_MAX = + +#WAVELET_STAT_WVLT_PLOT_COLOR_TABLE = +#WAVELET_STAT_WVLT_PLOT_PLOT_MIN = +#WAVELET_STAT_WVLT_PLOT_PLOT_MAX = + +#WAVELET_STAT_OUTPUT_PREFIX = From 7206a9378550057787b1d813c8d5f0db3c285b79 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 11:38:09 -0700 Subject: [PATCH 12/30] set output_flag so use case succeeds --- parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf b/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf index 0a7ca1bdf3..de26ad8ddf 100644 --- a/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf +++ b/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf @@ -111,7 +111,7 @@ WAVELET_STAT_CONFIG_FILE = {PARM_BASE}/met_config/WaveletStatConfig_wrapped #WAVELET_STAT_WAVELET_TYPE = #WAVELET_STAT_WAVELET_MEMBER = -#WAVELET_STAT_OUTPUT_FLAG_ISC = +WAVELET_STAT_OUTPUT_FLAG_ISC = STAT #WAVELET_STAT_NC_PAIRS_FLAG_RAW = #WAVELET_STAT_NC_PAIRS_FLAG_DIFF = From dd088f87049abae69722c4808e43051d5ccd47ec Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:21:59 -0700 Subject: [PATCH 13/30] added missing variables --- docs/Users_Guide/glossary.rst | 25 +++++++++++++++---------- docs/Users_Guide/wrappers.rst | 11 +++++++---- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/docs/Users_Guide/glossary.rst b/docs/Users_Guide/glossary.rst index e4dad390ca..5b3b800f65 100644 --- a/docs/Users_Guide/glossary.rst +++ b/docs/Users_Guide/glossary.rst @@ -11129,21 +11129,11 @@ METplus Configuration Glossary | *Used by:* GridStat - WAVELET_STAT_MODEL - Specify the value for 'model' in the MET configuration file for WaveletStat. - - | *Used by:* WaveletStat - WAVELET_STAT_DESC Specify the value for 'desc' in the MET configuration file for WaveletStat. | *Used by:* WaveletStat - WAVELET_STAT_OBTYPE - Specify the value for 'obtype' in the MET configuration file for WaveletStat. - - | *Used by:* WaveletStat - WAVELET_STAT_REGRID_TO_GRID Specify the value for 'regrid.to_grid' in the MET configuration file for WaveletStat. @@ -11504,3 +11494,18 @@ METplus Configuration Glossary See :term:`SKIP_VALID_TIMES` for formatting information. | *Used by:* WaveletStat + + WAVELET_STAT_REGRID_CONVERT + Specify the value for 'regrid.convert' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_REGRID_CENSOR_THRESH + Specify the value for 'regrid.censor_thresh' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat + + WAVELET_STAT_REGRID_CENSOR_VAL + Specify the value for 'regrid.censor_val' in the MET configuration file for WaveletStat. + + | *Used by:* WaveletStat diff --git a/docs/Users_Guide/wrappers.rst b/docs/Users_Guide/wrappers.rst index 3830523601..5ade57fea6 100644 --- a/docs/Users_Guide/wrappers.rst +++ b/docs/Users_Guide/wrappers.rst @@ -10968,14 +10968,17 @@ METplus Configuration | :term:`WAVELET_STAT_INC_VALID_TIMES` | :term:`WAVELET_STAT_SKIP_INIT_TIMES` | :term:`WAVELET_STAT_INC_INIT_TIMES` -| :term:`WAVELET_STAT_MODEL` +| :term:`MODEL` | :term:`WAVELET_STAT_DESC` -| :term:`WAVELET_STAT_OBTYPE` +| :term:`OBTYPE` | :term:`WAVELET_STAT_REGRID_TO_GRID` | :term:`WAVELET_STAT_REGRID_METHOD` | :term:`WAVELET_STAT_REGRID_WIDTH` | :term:`WAVELET_STAT_REGRID_VLD_THRESH` | :term:`WAVELET_STAT_REGRID_SHAPE` +| :term:`WAVELET_STAT_REGRID_CONVERT` +| :term:`WAVELET_STAT_REGRID_CENSOR_THRESH` +| :term:`WAVELET_STAT_REGRID_CENSOR_VAL` | :term:`WAVELET_STAT_CENSOR_THRESH` | :term:`WAVELET_STAT_CENSOR_VAL` | :term:`WAVELET_STAT_MASK_MISSING_FLAG` @@ -11034,7 +11037,7 @@ ${METPLUS_MODEL} * - METplus Config(s) - MET Config File - * - :term:`WAVELET_STAT_MODEL` + * - :term:`MODEL` - model ${METPLUS_DESC} @@ -11058,7 +11061,7 @@ ${METPLUS_OBTYPE} * - METplus Config(s) - MET Config File - * - :term:`WAVELET_STAT_OBTYPE` + * - :term:`OBTYPE` - obtype ${METPLUS_REGRID_DICT} From 1b39063f45753b5a51f5c1e00571179add85b0ec Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:22:08 -0700 Subject: [PATCH 14/30] fix missing f-string --- metplus/wrappers/wavelet_stat_wrapper.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/metplus/wrappers/wavelet_stat_wrapper.py b/metplus/wrappers/wavelet_stat_wrapper.py index fa19e586e2..301ba7a2c8 100755 --- a/metplus/wrappers/wavelet_stat_wrapper.py +++ b/metplus/wrappers/wavelet_stat_wrapper.py @@ -142,17 +142,17 @@ def create_c_dict(self): self.add_met_config(name='file_type', data_type='string', env_var_name='FCST_FILE_TYPE', - metplus_configs=['{app}_FCST_FILE_TYPE', - 'FCST_{app}_FILE_TYPE', - '{app}_FILE_TYPE'], + metplus_configs=[f'{app}_FCST_FILE_TYPE', + f'FCST_{app}_FILE_TYPE', + f'{app}_FILE_TYPE'], extra_args={'remove_quotes': True, 'uppercase': True}) self.add_met_config(name='file_type', data_type='string', env_var_name='OBS_FILE_TYPE', - metplus_configs=['{app}_OBS_FILE_TYPE', - 'OBS_{app}_FILE_TYPE', - '{app}_FILE_TYPE'], + metplus_configs=[f'{app}_OBS_FILE_TYPE', + f'OBS_{app}_FILE_TYPE', + f'{app}_FILE_TYPE'], extra_args={'remove_quotes': True, 'uppercase': True}) From 5c0fb807ae5dd34a0bb52737f2228d4668a810be Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:22:33 -0700 Subject: [PATCH 15/30] added unit tests for new wrapper --- .../wavelet_stat/test_wavelet_stat.py | 337 ++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 internal/tests/pytests/wrappers/wavelet_stat/test_wavelet_stat.py diff --git a/internal/tests/pytests/wrappers/wavelet_stat/test_wavelet_stat.py b/internal/tests/pytests/wrappers/wavelet_stat/test_wavelet_stat.py new file mode 100644 index 0000000000..977bcbb4b3 --- /dev/null +++ b/internal/tests/pytests/wrappers/wavelet_stat/test_wavelet_stat.py @@ -0,0 +1,337 @@ +#!/usr/bin/env python3 + +import pytest + +import os + +from metplus.wrappers.wavelet_stat_wrapper import WaveletStatWrapper + +fcst_dir = '/some/path/fcst' +obs_dir = '/some/path/obs' +fcst_name = 'APCP' +fcst_level = 'A03' +obs_name = 'APCP_03' +obs_level_no_quotes = '(*,*)' +obs_level = f'"{obs_level_no_quotes}"' +both_thresh = ' lt-0.5,gt-0.5 && lt0.5,gt0.5 ' +fcst_fmt = f'field = [{{ name="{fcst_name}"; level="{fcst_level}"; cat_thresh=[{both_thresh}]; }}];' +obs_fmt = (f'field = [{{ name="{obs_name}"; ' + f'level="{obs_level_no_quotes}"; cat_thresh=[{both_thresh}]; }}];') +time_fmt = '%Y%m%d%H' +run_times = ['2005080700', '2005080712'] + + +def set_minimum_config_settings(config): + # set config variables to prevent command from running and bypass check + # if input files actually exist + config.set('config', 'DO_NOT_RUN_EXE', True) + config.set('config', 'INPUT_MUST_EXIST', False) + + # set process and time config variables + config.set('config', 'PROCESS_LIST', 'WaveletStat') + config.set('config', 'LOOP_BY', 'INIT') + config.set('config', 'INIT_TIME_FMT', time_fmt) + config.set('config', 'INIT_BEG', run_times[0]) + config.set('config', 'INIT_END', run_times[-1]) + config.set('config', 'INIT_INCREMENT', '12H') + config.set('config', 'LEAD_SEQ', '12H') + config.set('config', 'LOOP_ORDER', 'times') + config.set('config', 'WAVELET_STAT_CONFIG_FILE', + '{PARM_BASE}/met_config/WaveletStatConfig_wrapped') + config.set('config', 'FCST_WAVELET_STAT_INPUT_DIR', fcst_dir) + config.set('config', 'OBS_WAVELET_STAT_INPUT_DIR', obs_dir) + config.set('config', 'FCST_WAVELET_STAT_INPUT_TEMPLATE', + '{init?fmt=%Y%m%d%H}/fcst_file_F{lead?fmt=%3H}') + config.set('config', 'OBS_WAVELET_STAT_INPUT_TEMPLATE', + '{valid?fmt=%Y%m%d%H}/obs_file') + config.set('config', 'WAVELET_STAT_OUTPUT_DIR', + '{OUTPUT_BASE}/WaveletStat/output') + config.set('config', 'WAVELET_STAT_OUTPUT_TEMPLATE', '{valid?fmt=%Y%m%d%H}') + + config.set('config', 'FCST_VAR1_NAME', fcst_name) + config.set('config', 'FCST_VAR1_LEVELS', fcst_level) + config.set('config', 'OBS_VAR1_NAME', obs_name) + config.set('config', 'OBS_VAR1_LEVELS', obs_level) + config.set('config', 'BOTH_VAR1_THRESH', both_thresh) + + +@pytest.mark.parametrize( + 'config_overrides, expected_values', [ + # 0 generic FCST is prob + ({'FCST_IS_PROB': True}, + {'FCST_IS_PROB': True, 'OBS_IS_PROB': False}), + # 1 generic OBS is prob + ({'OBS_IS_PROB': True}, + {'FCST_IS_PROB': False, 'OBS_IS_PROB': True}), + # 2 generic FCST and OBS is prob + ({'FCST_IS_PROB': True, 'OBS_IS_PROB': True}, + {'FCST_IS_PROB': True, 'OBS_IS_PROB': True}), + # 3 generic FCST true, wrapper FCST false + ({'FCST_IS_PROB': True, 'FCST_WAVELET_STAT_IS_PROB': False}, + {'FCST_IS_PROB': False, 'OBS_IS_PROB': False}), + # 4 generic OBS true, wrapper OBS false + ({'OBS_IS_PROB': True, 'OBS_WAVELET_STAT_IS_PROB': False}, + {'FCST_IS_PROB': False, 'OBS_IS_PROB': False}), + # 5 generic FCST unset, wrapper FCST true + ({'FCST_WAVELET_STAT_IS_PROB': True}, + {'FCST_IS_PROB': True, 'OBS_IS_PROB': False}), + # 6 generic OBS unset, wrapper OBS true + ({'OBS_WAVELET_STAT_IS_PROB': True}, + {'FCST_IS_PROB': False, 'OBS_IS_PROB': True}), + # 7 generic FCST false, wrapper FCST true + ({'FCST_IS_PROB': False, 'FCST_WAVELET_STAT_IS_PROB': True}, + {'FCST_IS_PROB': True, 'OBS_IS_PROB': False}), + # 8 generic FCST true, wrapper FCST false + ({'FCST_IS_PROB': True, 'FCST_WAVELET_STAT_IS_PROB': False}, + {'FCST_IS_PROB': False, 'OBS_IS_PROB': False}), + ] +) +@pytest.mark.wrapper_b +def test_wavelet_stat_is_prob(metplus_config, config_overrides, expected_values): + config = metplus_config + + set_minimum_config_settings(config) + + # set config variable overrides + for key, value in config_overrides.items(): + config.set('config', key, value) + + wrapper = WaveletStatWrapper(config) + assert wrapper.isOK + for key, expected_value in expected_values.items(): + assert expected_value == wrapper.c_dict[key] + + +@pytest.mark.parametrize( + 'config_overrides, env_var_values', [ + ({'MODEL': 'my_model'}, + {'METPLUS_MODEL': 'model = "my_model";'}), + + ({'WAVELET_STAT_DESC': 'my_desc'}, + {'METPLUS_DESC': 'desc = "my_desc";'}), + + ({'WAVELET_STAT_DESC': 'my_desc'}, + {'METPLUS_DESC': 'desc = "my_desc";'}), + + ({'OBTYPE': 'my_obtype'}, + {'METPLUS_OBTYPE': 'obtype = "my_obtype";'}), + + ({'WAVELET_STAT_REGRID_TO_GRID': 'FCST',}, + {'METPLUS_REGRID_DICT': 'regrid = {to_grid = FCST;}'}), + + ({'WAVELET_STAT_REGRID_METHOD': 'NEAREST',}, + {'METPLUS_REGRID_DICT': 'regrid = {method = NEAREST;}'}), + + ({'WAVELET_STAT_REGRID_WIDTH': '1',}, + {'METPLUS_REGRID_DICT': 'regrid = {width = 1;}'}), + + ({'WAVELET_STAT_REGRID_VLD_THRESH': '0.5',}, + {'METPLUS_REGRID_DICT': 'regrid = {vld_thresh = 0.5;}'}), + + ({'WAVELET_STAT_REGRID_SHAPE': 'SQUARE',}, + {'METPLUS_REGRID_DICT': 'regrid = {shape = SQUARE;}'}), + + ({'WAVELET_STAT_REGRID_CONVERT': '2*x', }, + {'METPLUS_REGRID_DICT': 'regrid = {convert(x) = 2*x;}'}), + + ({'WAVELET_STAT_REGRID_CENSOR_THRESH': '>12000,<5000', }, + {'METPLUS_REGRID_DICT': 'regrid = {censor_thresh = [>12000, <5000];}'}), + + ({'WAVELET_STAT_REGRID_CENSOR_VAL': '12000,5000', }, + {'METPLUS_REGRID_DICT': 'regrid = {censor_val = [12000, 5000];}'}), + + ({'WAVELET_STAT_REGRID_TO_GRID': 'FCST', + 'WAVELET_STAT_REGRID_METHOD': 'NEAREST', + 'WAVELET_STAT_REGRID_WIDTH': '1', + 'WAVELET_STAT_REGRID_VLD_THRESH': '0.5', + 'WAVELET_STAT_REGRID_SHAPE': 'SQUARE', + 'WAVELET_STAT_REGRID_CONVERT': '2*x', + 'WAVELET_STAT_REGRID_CENSOR_THRESH': '>12000,<5000', + 'WAVELET_STAT_REGRID_CENSOR_VAL': '12000,5000', + }, + {'METPLUS_REGRID_DICT': ('regrid = {to_grid = FCST;method = NEAREST;' + 'width = 1;vld_thresh = 0.5;shape = SQUARE;' + 'convert(x) = 2*x;' + 'censor_thresh = [>12000, <5000];' + 'censor_val = [12000, 5000];}' + )}), + + ({'WAVELET_STAT_CENSOR_THRESH': '>12000,<5000', }, + {'METPLUS_CENSOR_THRESH': 'censor_thresh = [>12000, <5000];'}), + ({'WAVELET_STAT_CENSOR_VAL': '12000, 5000', }, + {'METPLUS_CENSOR_VAL': 'censor_val = [12000, 5000];'}), + + ({'WAVELET_STAT_MASK_MISSING_FLAG': 'NONE', }, + {'METPLUS_MASK_MISSING_FLAG': 'mask_missing_flag = NONE;'}), + + ({'WAVELET_STAT_GRID_DECOMP_FLAG': 'AUTO', }, + {'METPLUS_GRID_DECOMP_FLAG': 'grid_decomp_flag = AUTO;'}), + + ({'WAVELET_STAT_TITLE_WIDTH': '0', }, + {'METPLUS_TITLE_DICT': 'title = {width = 0;}'}), + + ({'WAVELET_STAT_TITLE_LOCATION_X_LL': '1', }, + {'METPLUS_TITLE_DICT': 'title = {location = [{x_ll = 1;}];}'}), + + ({'WAVELET_STAT_TITLE_LOCATION_Y_LL': '1', }, + {'METPLUS_TITLE_DICT': 'title = {location = [{y_ll = 1;}];}'}), + + ({ + 'WAVELET_STAT_TITLE_WIDTH': '1', + 'WAVELET_STAT_TITLE_LOCATION1_X_LL': '1', + 'WAVELET_STAT_TITLE_LOCATION1_Y_LL': '2', + 'WAVELET_STAT_TITLE_LOCATION2_X_LL': '3', + 'WAVELET_STAT_TITLE_LOCATION2_Y_LL': '4', + }, + {'METPLUS_TITLE_DICT': 'title = {width = 1;location = [{x_ll = 1;y_ll = 2;},{x_ll = 3;y_ll = 4;}];}'}), + + ({'WAVELET_STAT_WAVELET_TYPE': 'HAAR', }, + {'METPLUS_WAVELET_DICT': 'wavelet = {type = HAAR;}'}), + + ({'WAVELET_STAT_WAVELET_MEMBER': '2', }, + {'METPLUS_WAVELET_DICT': 'wavelet = {member = 2;}'}), + + ({ + 'WAVELET_STAT_WAVELET_TYPE': 'HAAR', + 'WAVELET_STAT_WAVELET_MEMBER': '2', + }, + {'METPLUS_WAVELET_DICT': 'wavelet = {type = HAAR;member = 2;}'}), + + ({'WAVELET_STAT_OUTPUT_FLAG_ISC': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {isc = STAT;}'}), + + ({'WAVELET_STAT_NC_PAIRS_FLAG_RAW': 'TRUE', }, + {'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {raw = TRUE;}'}), + + ({'WAVELET_STAT_NC_PAIRS_FLAG_DIFF': 'TRUE', }, + {'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {diff = TRUE;}'}), + + ({'WAVELET_STAT_NC_PAIRS_FLAG_RAW': 'TRUE', + 'WAVELET_STAT_NC_PAIRS_FLAG_DIFF': 'TRUE', + }, + {'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {raw = TRUE;diff = TRUE;}' + }), + + ({'WAVELET_STAT_FCST_RAW_PLOT_COLOR_TABLE': 'MET_BASE/colortables/met_default.ctable', }, + {'METPLUS_FCST_RAW_PLOT_DICT': 'fcst_raw_plot = {color_table = \"MET_BASE/colortables/met_default.ctable\";}'}), + + ({'WAVELET_STAT_FCST_RAW_PLOT_PLOT_MIN': '0.0', }, + {'METPLUS_FCST_RAW_PLOT_DICT': 'fcst_raw_plot = {plot_min = 0.0;}'}), + + ({'WAVELET_STAT_FCST_RAW_PLOT_PLOT_MAX': '1.0', }, + {'METPLUS_FCST_RAW_PLOT_DICT': 'fcst_raw_plot = {plot_max = 1.0;}'}), + + ({'WAVELET_STAT_FCST_RAW_PLOT_COLOR_TABLE': 'MET_BASE/colortables/met_default.ctable', + 'WAVELET_STAT_FCST_RAW_PLOT_PLOT_MIN': '0.0', + 'WAVELET_STAT_FCST_RAW_PLOT_PLOT_MAX': '1.0', + }, + {'METPLUS_FCST_RAW_PLOT_DICT': 'fcst_raw_plot = {color_table = \"MET_BASE/colortables/met_default.ctable\";plot_min = 0.0;plot_max = 1.0;}'}), + + ({'WAVELET_STAT_OBS_RAW_PLOT_PLOT_MIN': '0.0', }, + {'METPLUS_OBS_RAW_PLOT_DICT': 'obs_raw_plot = {plot_min = 0.0;}'}), + + ({'WAVELET_STAT_OBS_RAW_PLOT_PLOT_MAX': '1.0', }, + {'METPLUS_OBS_RAW_PLOT_DICT': 'obs_raw_plot = {plot_max = 1.0;}'}), + + ({'WAVELET_STAT_OBS_RAW_PLOT_COLOR_TABLE': 'MET_BASE/colortables/met_default.ctable', + 'WAVELET_STAT_OBS_RAW_PLOT_PLOT_MIN': '0.0', + 'WAVELET_STAT_OBS_RAW_PLOT_PLOT_MAX': '1.0', + }, + {'METPLUS_OBS_RAW_PLOT_DICT': 'obs_raw_plot = {color_table = \"MET_BASE/colortables/met_default.ctable\";plot_min = 0.0;plot_max = 1.0;}'}), + + ({'WAVELET_STAT_WVLT_PLOT_COLOR_TABLE': 'MET_BASE/colortables/met_default.ctable', }, + { + 'METPLUS_WVLT_PLOT_DICT': 'wvlt_plot = {color_table = \"MET_BASE/colortables/met_default.ctable\";}'}), + + ({'WAVELET_STAT_WVLT_PLOT_PLOT_MIN': '0.0', }, + {'METPLUS_WVLT_PLOT_DICT': 'wvlt_plot = {plot_min = 0.0;}'}), + + ({'WAVELET_STAT_WVLT_PLOT_PLOT_MAX': '1.0', }, + {'METPLUS_WVLT_PLOT_DICT': 'wvlt_plot = {plot_max = 1.0;}'}), + + ({'WAVELET_STAT_WVLT_PLOT_COLOR_TABLE': 'MET_BASE/colortables/NCL_colortables/BlWhRe.ctable', + 'WAVELET_STAT_WVLT_PLOT_PLOT_MIN': '0.0', + 'WAVELET_STAT_WVLT_PLOT_PLOT_MAX': '1.0', + }, + {'METPLUS_WVLT_PLOT_DICT': 'wvlt_plot = {color_table = \"MET_BASE/colortables/NCL_colortables/BlWhRe.ctable\";plot_min = 0.0;plot_max = 1.0;}'}), + + ({'WAVELET_STAT_OUTPUT_PREFIX': 'my_output_prefix'}, + {'METPLUS_OUTPUT_PREFIX': 'output_prefix = "my_output_prefix";'}), + + ({'FCST_WAVELET_STAT_FILE_TYPE': 'NETCDF_NCCF', }, + {'METPLUS_FCST_FILE_TYPE': 'file_type = NETCDF_NCCF;'}), + ({'OBS_WAVELET_STAT_FILE_TYPE': 'NETCDF_NCCF', }, + {'METPLUS_OBS_FILE_TYPE': 'file_type = NETCDF_NCCF;'}), + + ] +) +@pytest.mark.wrapper_b +def test_wavelet_stat_single_field(metplus_config, config_overrides, env_var_values): + + config = metplus_config + set_minimum_config_settings(config) + + # set config variable overrides + for key, value in config_overrides.items(): + config.set('config', key, value) + + wrapper = WaveletStatWrapper(config) + assert wrapper.isOK + + app_path = os.path.join(config.getdir('MET_BIN_DIR'), wrapper.app_name) + verbosity = f"-v {wrapper.c_dict['VERBOSITY']}" + config_file = wrapper.c_dict.get('CONFIG_FILE') + out_dir = wrapper.c_dict.get('OUTPUT_DIR') + expected_cmds = [(f"{app_path} {verbosity} " + f"{fcst_dir}/2005080700/fcst_file_F012 " + f"{obs_dir}/2005080712/obs_file " + f"{config_file} -outdir {out_dir}/2005080712"), + (f"{app_path} {verbosity} " + f"{fcst_dir}/2005080712/fcst_file_F012 " + f"{obs_dir}/2005080800/obs_file " + f"{config_file} -outdir {out_dir}/2005080800"), + ] + + all_cmds = wrapper.run_all_times() + print(f"ALL COMMANDS: {all_cmds}") + + missing_env = [item for item in env_var_values + if item not in wrapper.WRAPPER_ENV_VAR_KEYS] + env_var_keys = wrapper.WRAPPER_ENV_VAR_KEYS + missing_env + + assert len(all_cmds) == len(expected_cmds) + for (cmd, env_vars), expected_cmd in zip(all_cmds, expected_cmds): + # ensure commands are generated as expected + assert cmd == expected_cmd + + # check that environment variables were set properly + # including deprecated env vars (not in wrapper env var keys) + for env_var_key in env_var_keys: + print(f"ENV VAR: {env_var_key}") + match = next((item for item in env_vars if + item.startswith(env_var_key)), None) + assert match is not None + actual_value = match.split('=', 1)[1] + if env_var_key == 'METPLUS_FCST_FIELD': + assert actual_value == fcst_fmt + elif env_var_key == 'METPLUS_OBS_FIELD': + assert actual_value == obs_fmt + else: + assert env_var_values.get(env_var_key, '') == actual_value + + +@pytest.mark.wrapper_b +def test_get_config_file(metplus_config): + fake_config_name = '/my/config/file' + + config = metplus_config + default_config_file = os.path.join(config.getdir('PARM_BASE'), + 'met_config', + 'WaveletStatConfig_wrapped') + + wrapper = WaveletStatWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == default_config_file + + config.set('config', 'WAVELET_STAT_CONFIG_FILE', fake_config_name) + wrapper = WaveletStatWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name From 5793ded5910d07a0d474c0d94391c2badd13fca6 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:23:15 -0700 Subject: [PATCH 16/30] set values in use case conf to test that the appropriate env vars are set --- .../WaveletStat/WaveletStat.conf | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf b/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf index de26ad8ddf..59c3015cfd 100644 --- a/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf +++ b/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf @@ -75,59 +75,59 @@ OBS_VAR1_THRESH = gt12.7, gt25.4, gt50.8, gt76.2 # https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#gridstat ### -#LOG_WAVELET_STAT_VERBOSITY = 2 +LOG_WAVELET_STAT_VERBOSITY = 4 WAVELET_STAT_CONFIG_FILE = {PARM_BASE}/met_config/WaveletStatConfig_wrapped -#FCST_WAVELET_STAT_FILE_TYPE = -#OBS_WAVELET_STAT_FILE_TYPE = +FCST_WAVELET_STAT_FILE_TYPE = NETCDF_NCCF +OBS_WAVELET_STAT_FILE_TYPE = NETCDF_NCCF -#FCST_WAVELET_STAT_FILE_WINDOW_BEGIN = 0 -#FCST_WAVELET_STAT_FILE_WINDOW_END = 0 -#OBS_WAVELET_STAT_FILE_WINDOW_BEGIN = 0 -#OBS_WAVELET_STAT_FILE_WINDOW_END = 0 +FCST_WAVELET_STAT_FILE_WINDOW_BEGIN = -10 +FCST_WAVELET_STAT_FILE_WINDOW_END = 10 +OBS_WAVELET_STAT_FILE_WINDOW_BEGIN = -20 +OBS_WAVELET_STAT_FILE_WINDOW_END = 20 -#WAVELET_STAT_MODEL = -#WAVELET_STAT_DESC = -#WAVELET_STAT_OBTYPE = +MODEL = MY_MOD +WAVELET_STAT_DESC = MY_DESC +OBTYPE = MY_OBTYPE -#WAVELET_STAT_REGRID_TO_GRID = -#WAVELET_STAT_REGRID_METHOD = -#WAVELET_STAT_REGRID_WIDTH = -#WAVELET_STAT_REGRID_VLD_THRESH = -#WAVELET_STAT_REGRID_SHAPE = +WAVELET_STAT_REGRID_TO_GRID = NONE +WAVELET_STAT_REGRID_METHOD = NEAREST +WAVELET_STAT_REGRID_WIDTH = 1 +WAVELET_STAT_REGRID_VLD_THRESH = 0.5 +WAVELET_STAT_REGRID_SHAPE = SQUARE -#WAVELET_STAT_CENSOR_THRESH = -#WAVELET_STAT_CENSOR_VAL = +WAVELET_STAT_CENSOR_THRESH = >1 +WAVELET_STAT_CENSOR_VAL = 1 -#WAVELET_STAT_MASK_MISSING_FLAG = +WAVELET_STAT_MASK_MISSING_FLAG = NONE -#WAVELET_STAT_GRID_DECOMP_FLAG = +WAVELET_STAT_GRID_DECOMP_FLAG = AUTO -#WAVELET_STAT_TITLE_WIDTH = -#WAVELET_STAT_TITLE_LOCATION1_X_LL = -#WAVELET_STAT_TITLE_LOCATION1_Y_LL = +WAVELET_STAT_TITLE_WIDTH = 0 +WAVELET_STAT_TITLE_LOCATION1_X_LL = 0 +WAVELET_STAT_TITLE_LOCATION1_Y_LL = 0 -#WAVELET_STAT_WAVELET_TYPE = -#WAVELET_STAT_WAVELET_MEMBER = +WAVELET_STAT_WAVELET_TYPE = HAAR +WAVELET_STAT_WAVELET_MEMBER = 2 WAVELET_STAT_OUTPUT_FLAG_ISC = STAT -#WAVELET_STAT_NC_PAIRS_FLAG_RAW = -#WAVELET_STAT_NC_PAIRS_FLAG_DIFF = +WAVELET_STAT_NC_PAIRS_FLAG_RAW = TRUE +WAVELET_STAT_NC_PAIRS_FLAG_DIFF = TRUE -#WAVELET_STAT_PS_PLOT_FLAG = +WAVELET_STAT_PS_PLOT_FLAG = TRUE -#WAVELET_STAT_FCST_RAW_PLOT_COLOR_TABLE = -#WAVELET_STAT_FCST_RAW_PLOT_PLOT_MIN = -#WAVELET_STAT_FCST_RAW_PLOT_PLOT_MAX = +WAVELET_STAT_FCST_RAW_PLOT_COLOR_TABLE = MET_BASE/colortables/met_default.ctable +WAVELET_STAT_FCST_RAW_PLOT_PLOT_MIN = 0.0 +WAVELET_STAT_FCST_RAW_PLOT_PLOT_MAX = 0.0 -#WAVELET_STAT_OBS_RAW_PLOT_COLOR_TABLE = -#WAVELET_STAT_OBS_RAW_PLOT_PLOT_MIN = -#WAVELET_STAT_OBS_RAW_PLOT_PLOT_MAX = +WAVELET_STAT_OBS_RAW_PLOT_COLOR_TABLE = MET_BASE/colortables/met_default.ctable +WAVELET_STAT_OBS_RAW_PLOT_PLOT_MIN = 0.0 +WAVELET_STAT_OBS_RAW_PLOT_PLOT_MAX = 0.0 -#WAVELET_STAT_WVLT_PLOT_COLOR_TABLE = -#WAVELET_STAT_WVLT_PLOT_PLOT_MIN = -#WAVELET_STAT_WVLT_PLOT_PLOT_MAX = +WAVELET_STAT_WVLT_PLOT_COLOR_TABLE = MET_BASE/colortables/NCL_colortables/BlWhRe.ctable +WAVELET_STAT_WVLT_PLOT_PLOT_MIN = 0.0 +WAVELET_STAT_WVLT_PLOT_PLOT_MAX = 0.0 -#WAVELET_STAT_OUTPUT_PREFIX = +WAVELET_STAT_OUTPUT_PREFIX = my_prefix From 019be3ee1db72ab7933586b37c105722bd92eafc Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:24:36 -0700 Subject: [PATCH 17/30] Revert "set values in use case conf to test that the appropriate env vars are set" This reverts commit 5793ded5910d07a0d474c0d94391c2badd13fca6. --- .../WaveletStat/WaveletStat.conf | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf b/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf index 59c3015cfd..de26ad8ddf 100644 --- a/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf +++ b/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf @@ -75,59 +75,59 @@ OBS_VAR1_THRESH = gt12.7, gt25.4, gt50.8, gt76.2 # https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#gridstat ### -LOG_WAVELET_STAT_VERBOSITY = 4 +#LOG_WAVELET_STAT_VERBOSITY = 2 WAVELET_STAT_CONFIG_FILE = {PARM_BASE}/met_config/WaveletStatConfig_wrapped -FCST_WAVELET_STAT_FILE_TYPE = NETCDF_NCCF -OBS_WAVELET_STAT_FILE_TYPE = NETCDF_NCCF +#FCST_WAVELET_STAT_FILE_TYPE = +#OBS_WAVELET_STAT_FILE_TYPE = -FCST_WAVELET_STAT_FILE_WINDOW_BEGIN = -10 -FCST_WAVELET_STAT_FILE_WINDOW_END = 10 -OBS_WAVELET_STAT_FILE_WINDOW_BEGIN = -20 -OBS_WAVELET_STAT_FILE_WINDOW_END = 20 +#FCST_WAVELET_STAT_FILE_WINDOW_BEGIN = 0 +#FCST_WAVELET_STAT_FILE_WINDOW_END = 0 +#OBS_WAVELET_STAT_FILE_WINDOW_BEGIN = 0 +#OBS_WAVELET_STAT_FILE_WINDOW_END = 0 -MODEL = MY_MOD -WAVELET_STAT_DESC = MY_DESC -OBTYPE = MY_OBTYPE +#WAVELET_STAT_MODEL = +#WAVELET_STAT_DESC = +#WAVELET_STAT_OBTYPE = -WAVELET_STAT_REGRID_TO_GRID = NONE -WAVELET_STAT_REGRID_METHOD = NEAREST -WAVELET_STAT_REGRID_WIDTH = 1 -WAVELET_STAT_REGRID_VLD_THRESH = 0.5 -WAVELET_STAT_REGRID_SHAPE = SQUARE +#WAVELET_STAT_REGRID_TO_GRID = +#WAVELET_STAT_REGRID_METHOD = +#WAVELET_STAT_REGRID_WIDTH = +#WAVELET_STAT_REGRID_VLD_THRESH = +#WAVELET_STAT_REGRID_SHAPE = -WAVELET_STAT_CENSOR_THRESH = >1 -WAVELET_STAT_CENSOR_VAL = 1 +#WAVELET_STAT_CENSOR_THRESH = +#WAVELET_STAT_CENSOR_VAL = -WAVELET_STAT_MASK_MISSING_FLAG = NONE +#WAVELET_STAT_MASK_MISSING_FLAG = -WAVELET_STAT_GRID_DECOMP_FLAG = AUTO +#WAVELET_STAT_GRID_DECOMP_FLAG = -WAVELET_STAT_TITLE_WIDTH = 0 -WAVELET_STAT_TITLE_LOCATION1_X_LL = 0 -WAVELET_STAT_TITLE_LOCATION1_Y_LL = 0 +#WAVELET_STAT_TITLE_WIDTH = +#WAVELET_STAT_TITLE_LOCATION1_X_LL = +#WAVELET_STAT_TITLE_LOCATION1_Y_LL = -WAVELET_STAT_WAVELET_TYPE = HAAR -WAVELET_STAT_WAVELET_MEMBER = 2 +#WAVELET_STAT_WAVELET_TYPE = +#WAVELET_STAT_WAVELET_MEMBER = WAVELET_STAT_OUTPUT_FLAG_ISC = STAT -WAVELET_STAT_NC_PAIRS_FLAG_RAW = TRUE -WAVELET_STAT_NC_PAIRS_FLAG_DIFF = TRUE +#WAVELET_STAT_NC_PAIRS_FLAG_RAW = +#WAVELET_STAT_NC_PAIRS_FLAG_DIFF = -WAVELET_STAT_PS_PLOT_FLAG = TRUE +#WAVELET_STAT_PS_PLOT_FLAG = -WAVELET_STAT_FCST_RAW_PLOT_COLOR_TABLE = MET_BASE/colortables/met_default.ctable -WAVELET_STAT_FCST_RAW_PLOT_PLOT_MIN = 0.0 -WAVELET_STAT_FCST_RAW_PLOT_PLOT_MAX = 0.0 +#WAVELET_STAT_FCST_RAW_PLOT_COLOR_TABLE = +#WAVELET_STAT_FCST_RAW_PLOT_PLOT_MIN = +#WAVELET_STAT_FCST_RAW_PLOT_PLOT_MAX = -WAVELET_STAT_OBS_RAW_PLOT_COLOR_TABLE = MET_BASE/colortables/met_default.ctable -WAVELET_STAT_OBS_RAW_PLOT_PLOT_MIN = 0.0 -WAVELET_STAT_OBS_RAW_PLOT_PLOT_MAX = 0.0 +#WAVELET_STAT_OBS_RAW_PLOT_COLOR_TABLE = +#WAVELET_STAT_OBS_RAW_PLOT_PLOT_MIN = +#WAVELET_STAT_OBS_RAW_PLOT_PLOT_MAX = -WAVELET_STAT_WVLT_PLOT_COLOR_TABLE = MET_BASE/colortables/NCL_colortables/BlWhRe.ctable -WAVELET_STAT_WVLT_PLOT_PLOT_MIN = 0.0 -WAVELET_STAT_WVLT_PLOT_PLOT_MAX = 0.0 +#WAVELET_STAT_WVLT_PLOT_COLOR_TABLE = +#WAVELET_STAT_WVLT_PLOT_PLOT_MIN = +#WAVELET_STAT_WVLT_PLOT_PLOT_MAX = -WAVELET_STAT_OUTPUT_PREFIX = my_prefix +#WAVELET_STAT_OUTPUT_PREFIX = From 15771af4f172a12b706e4d35ea315b2b06d11c70 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:29:16 -0700 Subject: [PATCH 18/30] do not set deprecated env var OUTPUT_PREFIX --- metplus/wrappers/command_builder.py | 3 --- metplus/wrappers/ensemble_stat_wrapper.py | 1 + metplus/wrappers/grid_stat_wrapper.py | 1 + metplus/wrappers/mode_wrapper.py | 1 + metplus/wrappers/mtd_wrapper.py | 1 + metplus/wrappers/point_stat_wrapper.py | 1 + metplus/wrappers/tc_diag_wrapper.py | 5 +++++ 7 files changed, 10 insertions(+), 3 deletions(-) diff --git a/metplus/wrappers/command_builder.py b/metplus/wrappers/command_builder.py index 6d018b7916..eaeb81205c 100755 --- a/metplus/wrappers/command_builder.py +++ b/metplus/wrappers/command_builder.py @@ -1399,9 +1399,6 @@ def get_output_prefix(self, time_info=None, set_env_vars=True): output_prefix_fmt = f'output_prefix = "{output_prefix}";' self.env_var_dict['METPLUS_OUTPUT_PREFIX'] = output_prefix_fmt - # set old method of setting OUTPUT_PREFIX - self.add_env_var('OUTPUT_PREFIX', output_prefix) - return output_prefix def handle_climo_dict(self): diff --git a/metplus/wrappers/ensemble_stat_wrapper.py b/metplus/wrappers/ensemble_stat_wrapper.py index 661f32cc3e..1de97bf155 100755 --- a/metplus/wrappers/ensemble_stat_wrapper.py +++ b/metplus/wrappers/ensemble_stat_wrapper.py @@ -78,6 +78,7 @@ class EnsembleStatWrapper(CompareGriddedWrapper): 'MODEL', 'OBTYPE', 'REGRID_TO_GRID', + 'OUTPUT_PREFIX', ] OUTPUT_FLAGS = [ diff --git a/metplus/wrappers/grid_stat_wrapper.py b/metplus/wrappers/grid_stat_wrapper.py index 95245dd1f8..bc60e2b7e4 100755 --- a/metplus/wrappers/grid_stat_wrapper.py +++ b/metplus/wrappers/grid_stat_wrapper.py @@ -73,6 +73,7 @@ class GridStatWrapper(CompareGriddedWrapper): 'NEIGHBORHOOD_WIDTH', 'NEIGHBORHOOD_SHAPE', 'NEIGHBORHOOD_COV_THRESH', + 'OUTPUT_PREFIX', ] OUTPUT_FLAGS = [ diff --git a/metplus/wrappers/mode_wrapper.py b/metplus/wrappers/mode_wrapper.py index 49431c4712..ab856a1432 100755 --- a/metplus/wrappers/mode_wrapper.py +++ b/metplus/wrappers/mode_wrapper.py @@ -89,6 +89,7 @@ class MODEWrapper(CompareGriddedWrapper): 'OBS_MERGE_THRESH', 'FCST_MERGE_FLAG', 'OBS_MERGE_FLAG', + 'OUTPUT_PREFIX', ] WEIGHTS = { diff --git a/metplus/wrappers/mtd_wrapper.py b/metplus/wrappers/mtd_wrapper.py index 34bcc647e0..aa3a46dab5 100755 --- a/metplus/wrappers/mtd_wrapper.py +++ b/metplus/wrappers/mtd_wrapper.py @@ -55,6 +55,7 @@ class MTDWrapper(CompareGriddedWrapper): 'MIN_VOLUME', 'FCST_FILE_TYPE', 'OBS_FILE_TYPE', + 'OUTPUT_PREFIX', ] def __init__(self, config, instance=None): diff --git a/metplus/wrappers/point_stat_wrapper.py b/metplus/wrappers/point_stat_wrapper.py index 673fbb6d9c..e150ab4a25 100755 --- a/metplus/wrappers/point_stat_wrapper.py +++ b/metplus/wrappers/point_stat_wrapper.py @@ -63,6 +63,7 @@ class PointStatWrapper(CompareGriddedWrapper): 'POINT_STAT_GRID', 'POINT_STAT_STATION_ID', 'POINT_STAT_MESSAGE_TYPE', + 'OUTPUT_PREFIX', 'METPLUS_MASK_GRID', # deprecated in v5.1.0 'METPLUS_MASK_POLY', # deprecated in v5.1.0 'METPLUS_MASK_SID', # deprecated in v5.1.0 diff --git a/metplus/wrappers/tc_diag_wrapper.py b/metplus/wrappers/tc_diag_wrapper.py index 5a45202a45..e72f0efffa 100755 --- a/metplus/wrappers/tc_diag_wrapper.py +++ b/metplus/wrappers/tc_diag_wrapper.py @@ -67,6 +67,11 @@ class TCDiagWrapper(RuntimeFreqWrapper): 'METPLUS_ONE_TIME_PER_FILE_FLAG', ] + # deprecated env vars that are no longer supported in the wrapped MET conf + DEPRECATED_WRAPPER_ENV_VAR_KEYS = [ + 'OUTPUT_PREFIX', + ] + def __init__(self, config, instance=None): self.app_name = "tc_diag" self.app_path = os.path.join(config.getdir('MET_BIN_DIR'), From 13e2f6ce6d5f1bcf7b8369bf9a6196e7645af99f Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:36:56 -0700 Subject: [PATCH 19/30] added use case documentation --- .../met_tool_wrapper/WaveletStat/README.rst | 2 + .../WaveletStat/WaveletStat.py | 115 ++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 docs/use_cases/met_tool_wrapper/WaveletStat/README.rst create mode 100644 docs/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.py diff --git a/docs/use_cases/met_tool_wrapper/WaveletStat/README.rst b/docs/use_cases/met_tool_wrapper/WaveletStat/README.rst new file mode 100644 index 0000000000..f6849160e8 --- /dev/null +++ b/docs/use_cases/met_tool_wrapper/WaveletStat/README.rst @@ -0,0 +1,2 @@ +WaveletStat +----------- diff --git a/docs/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.py b/docs/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.py new file mode 100644 index 0000000000..9159d58575 --- /dev/null +++ b/docs/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.py @@ -0,0 +1,115 @@ +""" +WaveletStat: Basic Use Case +=========================== + +met_tool_wrapper/WaveletStat/WaveletStat.conf + +""" +############################################################################## +# Scientific Objective +# -------------------- +# +# Compare 3 hour forecast precipitation accumulations to observations +# of 3 hour precipitation accumulation. Generate statistics of the results. + +############################################################################## +# Datasets +# -------- +# +# | **Forecast:** WRF 3 hour precipitation accumulation +# | **Observation:** MU 3 hour precipitation accumulation +# +# | **Location:** All of the input data required for this use case can be found in the met_test sample data tarball. Click here for the METplus releases page and download sample data for the appropriate release: https://github.com/dtcenter/METplus/releases +# | This tarball should be unpacked into the directory that you will set the value of INPUT_BASE. See the `Running METplus`_ section for more information. +# | + +############################################################################## +# METplus Components +# ------------------ +# +# This use case utilizes the METplus WaveletStat wrapper to search for +# files that are valid at a given run time and generate a command to run +# the MET tool wavelet_stat if all required files are found. + +############################################################################## +# METplus Workflow +# ---------------- +# +# WaveletStat is the only tool called in this example. It processes the following +# run times: +# +# | **Init:** 2005-08-07_0Z +# | **Forecast lead:** 12 hour +# | + +############################################################################## +# METplus Configuration +# --------------------- +# +# METplus first loads all of the configuration files found in parm/metplus_config, +# then it loads any configuration files passed to METplus via the command line: +# parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf +# +# .. highlight:: bash +# .. literalinclude:: ../../../../parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf + +############################################################################## +# MET Configuration +# ----------------- +# +# METplus sets environment variables based on user settings in the METplus configuration file. +# See :ref:`How METplus controls MET config file settings` for more details. +# +# **YOU SHOULD NOT SET ANY OF THESE ENVIRONMENT VARIABLES YOURSELF! THEY WILL BE OVERWRITTEN BY METPLUS WHEN IT CALLS THE MET TOOLS!** +# +# If there is a setting in the MET configuration file that is currently not supported by METplus you'd like to control, please refer to: +# :ref:`Overriding Unsupported MET config file settings` +# +# .. note:: See the :ref:`WaveletStat MET Configuration` section of the User's Guide for more information on the environment variables used in the file below: +# +# .. highlight:: bash +# .. literalinclude:: ../../../../parm/met_config/WaveletStatConfig_wrapped + +############################################################################## +# Running METplus +# --------------- +# +# Pass the use case configuration file to the run_metplus.py script +# along with any user-specific system configuration files if desired:: +# +# run_metplus.py /path/to/METplus/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf /path/to/user_system.conf +# +# See :ref:`running-metplus` for more information. +# + +############################################################################## +# Expected Output +# --------------- +# +# A successful run will output the following both to the screen and to the logfile:: +# +# INFO: METplus has successfully finished running. +# +# Refer to the value set for **OUTPUT_BASE** to find where the output data was generated. +# Output for this use case will be found in wavelet_stat/2005080700 (relative to **OUTPUT_BASE**) +# and will contain the following files: +# +# * wavelet_stat_120000L_20050807_120000V_isc.txt +# * wavelet_stat_120000L_20050807_120000V.nc +# * wavelet_stat_120000L_20050807_120000V.ps +# * wavelet_stat_120000L_20050807_120000V.stat + +############################################################################## +# Keywords +# -------- +# +# .. note:: +# +# * WaveletStatToolUseCase +# +# Navigate to the :ref:`quick-search` page to discover other similar use cases. +# +# +# +# sphinx_gallery_thumbnail_path = '_static/met_tool_wrapper-WaveletStat.png' +# From 8c1d74e1d5bb928f25df76ec40750f3248d40a19 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:37:03 -0700 Subject: [PATCH 20/30] add use case to run in automated tests --- .github/parm/use_case_groups.json | 5 +++++ internal/tests/use_cases/all_use_cases.txt | 1 + 2 files changed, 6 insertions(+) diff --git a/.github/parm/use_case_groups.json b/.github/parm/use_case_groups.json index 1752f76df0..a2cf6e83a6 100644 --- a/.github/parm/use_case_groups.json +++ b/.github/parm/use_case_groups.json @@ -9,6 +9,11 @@ "index_list": "30-58", "run": false }, + { + "category": "met_tool_wrapper", + "index_list": "63", + "run": true + }, { "category": "air_quality_and_comp", "index_list": "0", diff --git a/internal/tests/use_cases/all_use_cases.txt b/internal/tests/use_cases/all_use_cases.txt index 875caec30d..235c910c71 100644 --- a/internal/tests/use_cases/all_use_cases.txt +++ b/internal/tests/use_cases/all_use_cases.txt @@ -62,6 +62,7 @@ Category: met_tool_wrapper 60::PointStat_python_embedding_obs:: met_tool_wrapper/PointStat/PointStat_python_embedding_obs.conf 61::PlotPointObs:: met_tool_wrapper/PlotPointObs/PlotPointObs.conf 62::TCDiag:: met_tool_wrapper/TCDiag/TCDiag.conf +63::WaveletStat:: met_tool_wrapper/WaveletStat/WaveletStat.conf Category: air_quality_and_comp 0::EnsembleStat_fcstICAP_obsMODIS_aod::model_applications/air_quality_and_comp/EnsembleStat_fcstICAP_obsMODIS_aod.conf From cf9b9616eff1caf0602d1e8b927c91bf279b82a4 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:47:40 -0700 Subject: [PATCH 21/30] fix bash interpretation error when rending docs --- parm/met_config/WaveletStatConfig_wrapped | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parm/met_config/WaveletStatConfig_wrapped b/parm/met_config/WaveletStatConfig_wrapped index 843fcbe302..02bfcb2738 100644 --- a/parm/met_config/WaveletStatConfig_wrapped +++ b/parm/met_config/WaveletStatConfig_wrapped @@ -2,7 +2,7 @@ // // Wavelet-Stat configuration file. // -// For additional information, please see the MET User's Guide. +// For additional information, please see the MET Users Guide. // //////////////////////////////////////////////////////////////////////////////// From 1946730f9aa8e0aceab3e0cc2da710d10499ae8f Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 14:05:28 -0700 Subject: [PATCH 22/30] replace image with smaller one --- docs/_static/met_tool_wrapper-WaveletStat.png | Bin 0 -> 21686 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/_static/met_tool_wrapper-WaveletStat.png diff --git a/docs/_static/met_tool_wrapper-WaveletStat.png b/docs/_static/met_tool_wrapper-WaveletStat.png new file mode 100644 index 0000000000000000000000000000000000000000..6c661c8452ddf2f847ecac1daed451a9584e36bc GIT binary patch literal 21686 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 literal 0 HcmV?d00001 From b7918e0b504123333d7825b38cc626e8a1587246 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 14:05:54 -0700 Subject: [PATCH 23/30] turn off use case after confirming it runs successfully in GHA --- .github/parm/use_case_groups.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/parm/use_case_groups.json b/.github/parm/use_case_groups.json index a2cf6e83a6..75e5f5dd08 100644 --- a/.github/parm/use_case_groups.json +++ b/.github/parm/use_case_groups.json @@ -12,7 +12,7 @@ { "category": "met_tool_wrapper", "index_list": "63", - "run": true + "run": false }, { "category": "air_quality_and_comp", From 3e7467a1a150981220f09f5718922e1c399eba27 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 14:36:53 -0700 Subject: [PATCH 24/30] replace with smaller image --- docs/_static/met_tool_wrapper-WaveletStat.png | Bin 21686 -> 7246 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/_static/met_tool_wrapper-WaveletStat.png b/docs/_static/met_tool_wrapper-WaveletStat.png index 6c661c8452ddf2f847ecac1daed451a9584e36bc..202de6b72187e2da7fe184978ff5acc1e2e0413a 100644 GIT binary patch delta 7239 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 VQ8IOp6vY4l002ovPDHLkV1jAI?9KoH literal 21686 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 From 2a9f8663656b4cbdb34c447cbd9ccda59d4ce48e Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 16 Nov 2023 15:18:07 -0700 Subject: [PATCH 25/30] fixed incorrect variable name for GenEnsProd nmep_smooth.type.method/width --- docs/Users_Guide/glossary.rst | 10 +++++----- docs/Users_Guide/wrappers.rst | 8 ++++---- .../met_tool_wrapper/GenEnsProd/GenEnsProd.conf | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/Users_Guide/glossary.rst b/docs/Users_Guide/glossary.rst index 5b3b800f65..0222fbd7e2 100644 --- a/docs/Users_Guide/glossary.rst +++ b/docs/Users_Guide/glossary.rst @@ -5449,10 +5449,10 @@ METplus Configuration Glossary .. warning:: **DEPRECATED:** Please use :term:`GEN_ENS_PROD_NMEP_SMOOTH_SHAPE` in :ref:`gen_ens_prod_wrapper` instead. ENSEMBLE_STAT_NMEP_SMOOTH_METHOD - .. warning:: **DEPRECATED:** Please use :term:`GEN_ENS_PROD_NMEP_SMOOTH_METHOD` in :ref:`gen_ens_prod_wrapper` instead. + .. warning:: **DEPRECATED:** Please use :term:`GEN_ENS_PROD_NMEP_SMOOTH_TYPE_METHOD` in :ref:`gen_ens_prod_wrapper` instead. ENSEMBLE_STAT_NMEP_SMOOTH_WIDTH - .. warning:: **DEPRECATED:** Please use :term:`GEN_ENS_PROD_NMEP_SMOOTH_WIDTH` in :ref:`gen_ens_prod_wrapper` instead. + .. warning:: **DEPRECATED:** Please use :term:`GEN_ENS_PROD_NMEP_SMOOTH_TYPE_WIDTH` in :ref:`gen_ens_prod_wrapper` instead. ENSEMBLE_STAT_CENSOR_THRESH Specify the value for 'censor_thresh' in the MET configuration file for EnsembleStat. @@ -8271,12 +8271,12 @@ METplus Configuration Glossary | *Used by:* GenEnsProd - GEN_ENS_PROD_NMEP_SMOOTH_METHOD + GEN_ENS_PROD_NMEP_SMOOTH_TYPE_METHOD Specify the value for 'nmep_smooth.type.method' in the MET configuration file for GenEnsProd. | *Used by:* GenEnsProd - GEN_ENS_PROD_NMEP_SMOOTH_WIDTH + GEN_ENS_PROD_NMEP_SMOOTH_TYPE_WIDTH Specify the value for 'nmep_smooth.type.width' in the MET configuration file for GenEnsProd. | *Used by:* GenEnsProd @@ -11285,7 +11285,7 @@ METplus Configuration Glossary | *Used by:* WaveletStat FCST_WAVELET_STAT_INPUT_DATATYPE - Specify the data type of the input directory for forecast files used with the MET WAVELET_STAT tool. Currently valid options are NETCDF, GRIB, and GEMPAK. If set to GEMPAK, data will automatically be converted to NetCDF via GempakToCF. A corresponding variable exists for observation data called :term:`OBS_WAVELET_STAT_INPUT_DATATYPE`. + Specify the data type of the input directory for forecast files used with the MET wavelet_stat tool. Currently valid options are NETCDF, GRIB, and GEMPAK. If set to GEMPAK, data will automatically be converted to NetCDF via GempakToCF. A corresponding variable exists for observation data called :term:`OBS_WAVELET_STAT_INPUT_DATATYPE`. | *Used by:* WaveletStat diff --git a/docs/Users_Guide/wrappers.rst b/docs/Users_Guide/wrappers.rst index 5ade57fea6..81d9794618 100644 --- a/docs/Users_Guide/wrappers.rst +++ b/docs/Users_Guide/wrappers.rst @@ -1112,8 +1112,8 @@ METplus Configuration | :term:`GEN_ENS_PROD_NMEP_SMOOTH_SHAPE` | :term:`GEN_ENS_PROD_NMEP_SMOOTH_GAUSSIAN_DX` | :term:`GEN_ENS_PROD_NMEP_SMOOTH_GAUSSIAN_RADIUS` -| :term:`GEN_ENS_PROD_NMEP_SMOOTH_METHOD` -| :term:`GEN_ENS_PROD_NMEP_SMOOTH_WIDTH` +| :term:`GEN_ENS_PROD_NMEP_SMOOTH_TYPE_METHOD` +| :term:`GEN_ENS_PROD_NMEP_SMOOTH_TYPE_WIDTH` | :term:`GEN_ENS_PROD_CLIMO_MEAN_FILE_NAME` | :term:`GEN_ENS_PROD_CLIMO_MEAN_VAR_NAME` | :term:`GEN_ENS_PROD_CLIMO_MEAN_VAR_LEVELS` @@ -1414,9 +1414,9 @@ ${METPLUS_NMEP_SMOOTH_DICT} - nmep_smooth.gaussian_dx * - :term:`GEN_ENS_PROD_NMEP_SMOOTH_GAUSSIAN_RADIUS` - nmep_smooth.gaussian_radius - * - :term:`GEN_ENS_PROD_NMEP_SMOOTH_METHOD` + * - :term:`GEN_ENS_PROD_NMEP_SMOOTH_TYPE_METHOD` - nmep_smooth.type.method - * - :term:`GEN_ENS_PROD_NMEP_SMOOTH_WIDTH` + * - :term:`GEN_ENS_PROD_NMEP_SMOOTH_TYPE_WIDTH` - nmep_smooth.type.width ${METPLUS_CLIMO_MEAN_DICT} diff --git a/parm/use_cases/met_tool_wrapper/GenEnsProd/GenEnsProd.conf b/parm/use_cases/met_tool_wrapper/GenEnsProd/GenEnsProd.conf index 3c4a300a40..55ff515571 100644 --- a/parm/use_cases/met_tool_wrapper/GenEnsProd/GenEnsProd.conf +++ b/parm/use_cases/met_tool_wrapper/GenEnsProd/GenEnsProd.conf @@ -123,8 +123,8 @@ GEN_ENS_PROD_ENS_THRESH = 0.8 #GEN_ENS_PROD_NMEP_SMOOTH_SHAPE = CIRCLE #GEN_ENS_PROD_NMEP_SMOOTH_GAUSSIAN_DX = 81.27 #GEN_ENS_PROD_NMEP_SMOOTH_GAUSSIAN_RADIUS = 120 -#GEN_ENS_PROD_NMEP_SMOOTH_METHOD = GAUSSIAN -#GEN_ENS_PROD_NMEP_SMOOTH_WIDTH = 1 +#GEN_ENS_PROD_NMEP_SMOOTH_TYPE_METHOD = GAUSSIAN +#GEN_ENS_PROD_NMEP_SMOOTH_TYPE_WIDTH = 1 #GEN_ENS_PROD_CLIMO_MEAN_FILE_NAME = #GEN_ENS_PROD_CLIMO_MEAN_FIELD = From 298e8a1adf82605c1c929d4d192732ae5bd3152d Mon Sep 17 00:00:00 2001 From: j-opatz <59586397+j-opatz@users.noreply.github.com> Date: Fri, 17 Nov 2023 09:38:07 -0700 Subject: [PATCH 26/30] added observation dataset --- docs/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.py b/docs/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.py index 9159d58575..e7d746b6f3 100644 --- a/docs/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.py +++ b/docs/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.py @@ -17,7 +17,7 @@ # -------- # # | **Forecast:** WRF 3 hour precipitation accumulation -# | **Observation:** MU 3 hour precipitation accumulation +# | **Observation:** NCEP Stage 2 National Precipitation Analysis - NPA (multi-sensor) 3 hour precipitation accumulation # # | **Location:** All of the input data required for this use case can be found in the met_test sample data tarball. Click here for the METplus releases page and download sample data for the appropriate release: https://github.com/dtcenter/METplus/releases # | This tarball should be unpacked into the directory that you will set the value of INPUT_BASE. See the `Running METplus`_ section for more information. From c559affbd0e96605ae3fbb864cb30d0f3702d35f Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Fri, 17 Nov 2023 13:00:26 -0700 Subject: [PATCH 27/30] fix incorrect MET config variable name --- docs/Users_Guide/glossary.rst | 12 +++++----- docs/Users_Guide/wrappers.rst | 22 ++++++++--------- .../wavelet_stat/test_wavelet_stat.py | 24 +++++++++---------- metplus/wrappers/wavelet_stat_wrapper.py | 4 ++-- parm/met_config/WaveletStatConfig_wrapped | 4 ++-- .../WaveletStat/WaveletStat.conf | 6 ++--- 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/docs/Users_Guide/glossary.rst b/docs/Users_Guide/glossary.rst index 0222fbd7e2..1ab1e903bf 100644 --- a/docs/Users_Guide/glossary.rst +++ b/docs/Users_Guide/glossary.rst @@ -11179,18 +11179,18 @@ METplus Configuration Glossary | *Used by:* WaveletStat - WAVELET_STAT_TITLE_WIDTH - Specify the value for 'title.width' in the MET configuration file for WaveletStat. + WAVELET_STAT_TILE_WIDTH + Specify the value for 'tile.width' in the MET configuration file for WaveletStat. | *Used by:* WaveletStat - WAVELET_STAT_TITLE_LOCATION_X_LL - Specify the value for the nth 'title.location.x_ll' in the MET configuration file for WaveletStat. + WAVELET_STAT_TILE_LOCATION_X_LL + Specify the value for the nth 'tile.location.x_ll' in the MET configuration file for WaveletStat. | *Used by:* WaveletStat - WAVELET_STAT_TITLE_LOCATION_Y_LL - Specify the value for the nth 'title.location.y_ll' in the MET configuration file for WaveletStat. + WAVELET_STAT_TILE_LOCATION_Y_LL + Specify the value for the nth 'tile.location.y_ll' in the MET configuration file for WaveletStat. | *Used by:* WaveletStat diff --git a/docs/Users_Guide/wrappers.rst b/docs/Users_Guide/wrappers.rst index 81d9794618..1078f6466c 100644 --- a/docs/Users_Guide/wrappers.rst +++ b/docs/Users_Guide/wrappers.rst @@ -10983,9 +10983,9 @@ METplus Configuration | :term:`WAVELET_STAT_CENSOR_VAL` | :term:`WAVELET_STAT_MASK_MISSING_FLAG` | :term:`WAVELET_STAT_GRID_DECOMP_FLAG` -| :term:`WAVELET_STAT_TITLE_WIDTH` -| :term:`WAVELET_STAT_TITLE_LOCATION_X_LL` -| :term:`WAVELET_STAT_TITLE_LOCATION_Y_LL` +| :term:`WAVELET_STAT_TILE_WIDTH` +| :term:`WAVELET_STAT_TILE_LOCATION_X_LL` +| :term:`WAVELET_STAT_TILE_LOCATION_Y_LL` | :term:`WAVELET_STAT_WAVELET_TYPE` | :term:`WAVELET_STAT_WAVELET_MEMBER` | :term:`WAVELET_STAT_OUTPUT_FLAG_ISC` @@ -11132,8 +11132,8 @@ ${METPLUS_GRID_DECOMP_FLAG} * - :term:`WAVELET_STAT_GRID_DECOMP_FLAG` - grid_decomp_flag -${METPLUS_TITLE_DICT} -^^^^^^^^^^^^^^^^^^^^^ +${METPLUS_TILE_DICT} +^^^^^^^^^^^^^^^^^^^^ .. list-table:: :widths: 5 5 @@ -11141,12 +11141,12 @@ ${METPLUS_TITLE_DICT} * - METplus Config(s) - MET Config File - * - :term:`WAVELET_STAT_TITLE_WIDTH` - - title.width - * - :term:`WAVELET_STAT_TITLE_LOCATION_X_LL` - - title.location.x_ll - * - :term:`WAVELET_STAT_TITLE_LOCATION_Y_LL` - - title.location.y_ll + * - :term:`WAVELET_STAT_TILE_WIDTH` + - tile.width + * - :term:`WAVELET_STAT_TILE_LOCATION_X_LL` + - tile.location.x_ll + * - :term:`WAVELET_STAT_TILE_LOCATION_Y_LL` + - tile.location.y_ll ${METPLUS_WAVELET_DICT} ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/internal/tests/pytests/wrappers/wavelet_stat/test_wavelet_stat.py b/internal/tests/pytests/wrappers/wavelet_stat/test_wavelet_stat.py index 977bcbb4b3..d944846bc6 100644 --- a/internal/tests/pytests/wrappers/wavelet_stat/test_wavelet_stat.py +++ b/internal/tests/pytests/wrappers/wavelet_stat/test_wavelet_stat.py @@ -167,23 +167,23 @@ def test_wavelet_stat_is_prob(metplus_config, config_overrides, expected_values) ({'WAVELET_STAT_GRID_DECOMP_FLAG': 'AUTO', }, {'METPLUS_GRID_DECOMP_FLAG': 'grid_decomp_flag = AUTO;'}), - ({'WAVELET_STAT_TITLE_WIDTH': '0', }, - {'METPLUS_TITLE_DICT': 'title = {width = 0;}'}), + ({'WAVELET_STAT_TILE_WIDTH': '0', }, + {'METPLUS_TILE_DICT': 'tile = {width = 0;}'}), - ({'WAVELET_STAT_TITLE_LOCATION_X_LL': '1', }, - {'METPLUS_TITLE_DICT': 'title = {location = [{x_ll = 1;}];}'}), + ({'WAVELET_STAT_TILE_LOCATION_X_LL': '1', }, + {'METPLUS_TILE_DICT': 'tile = {location = [{x_ll = 1;}];}'}), - ({'WAVELET_STAT_TITLE_LOCATION_Y_LL': '1', }, - {'METPLUS_TITLE_DICT': 'title = {location = [{y_ll = 1;}];}'}), + ({'WAVELET_STAT_TILE_LOCATION_Y_LL': '1', }, + {'METPLUS_TILE_DICT': 'tile = {location = [{y_ll = 1;}];}'}), ({ - 'WAVELET_STAT_TITLE_WIDTH': '1', - 'WAVELET_STAT_TITLE_LOCATION1_X_LL': '1', - 'WAVELET_STAT_TITLE_LOCATION1_Y_LL': '2', - 'WAVELET_STAT_TITLE_LOCATION2_X_LL': '3', - 'WAVELET_STAT_TITLE_LOCATION2_Y_LL': '4', + 'WAVELET_STAT_TILE_WIDTH': '1', + 'WAVELET_STAT_TILE_LOCATION1_X_LL': '1', + 'WAVELET_STAT_TILE_LOCATION1_Y_LL': '2', + 'WAVELET_STAT_TILE_LOCATION2_X_LL': '3', + 'WAVELET_STAT_TILE_LOCATION2_Y_LL': '4', }, - {'METPLUS_TITLE_DICT': 'title = {width = 1;location = [{x_ll = 1;y_ll = 2;},{x_ll = 3;y_ll = 4;}];}'}), + {'METPLUS_TILE_DICT': 'tile = {width = 1;location = [{x_ll = 1;y_ll = 2;},{x_ll = 3;y_ll = 4;}];}'}), ({'WAVELET_STAT_WAVELET_TYPE': 'HAAR', }, {'METPLUS_WAVELET_DICT': 'wavelet = {type = HAAR;}'}), diff --git a/metplus/wrappers/wavelet_stat_wrapper.py b/metplus/wrappers/wavelet_stat_wrapper.py index 301ba7a2c8..2e824b4de9 100755 --- a/metplus/wrappers/wavelet_stat_wrapper.py +++ b/metplus/wrappers/wavelet_stat_wrapper.py @@ -40,7 +40,7 @@ class WaveletStatWrapper(CompareGriddedWrapper): 'METPLUS_CENSOR_VAL', 'METPLUS_MASK_MISSING_FLAG', 'METPLUS_GRID_DECOMP_FLAG', - 'METPLUS_TITLE_DICT', + 'METPLUS_TILE_DICT', 'METPLUS_WAVELET_DICT', 'METPLUS_OUTPUT_FLAG_DICT', 'METPLUS_NC_PAIRS_FLAG_DICT', @@ -156,7 +156,7 @@ def create_c_dict(self): extra_args={'remove_quotes': True, 'uppercase': True}) - self.add_met_config_dict('title', { + self.add_met_config_dict('tile', { 'width': 'int', 'location': ('dictlist', '', {'x_ll': 'int', 'y_ll': 'int'}) }) diff --git a/parm/met_config/WaveletStatConfig_wrapped b/parm/met_config/WaveletStatConfig_wrapped index 02bfcb2738..9f3f0e17f8 100644 --- a/parm/met_config/WaveletStatConfig_wrapped +++ b/parm/met_config/WaveletStatConfig_wrapped @@ -72,8 +72,8 @@ ${METPLUS_MASK_MISSING_FLAG} //grid_decomp_flag = ${METPLUS_GRID_DECOMP_FLAG} -//title = { -${METPLUS_TITLE_DICT} +//tile = { +${METPLUS_TILE_DICT} //////////////////////////////////////////////////////////////////////////////// diff --git a/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf b/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf index de26ad8ddf..40bbd5cac0 100644 --- a/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf +++ b/parm/use_cases/met_tool_wrapper/WaveletStat/WaveletStat.conf @@ -104,9 +104,9 @@ WAVELET_STAT_CONFIG_FILE = {PARM_BASE}/met_config/WaveletStatConfig_wrapped #WAVELET_STAT_GRID_DECOMP_FLAG = -#WAVELET_STAT_TITLE_WIDTH = -#WAVELET_STAT_TITLE_LOCATION1_X_LL = -#WAVELET_STAT_TITLE_LOCATION1_Y_LL = +#WAVELET_STAT_TILE_WIDTH = +#WAVELET_STAT_TILE_LOCATION1_X_LL = +#WAVELET_STAT_TILE_LOCATION1_Y_LL = #WAVELET_STAT_WAVELET_TYPE = #WAVELET_STAT_WAVELET_MEMBER = From 8c47c244f88cb7d6477df89430b875766651cc43 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Fri, 17 Nov 2023 14:08:03 -0700 Subject: [PATCH 28/30] added tests to ensure WOFS use case configuration behaves correctly --- .../gen_ens_prod/test_gen_ens_prod_wrapper.py | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/internal/tests/pytests/wrappers/gen_ens_prod/test_gen_ens_prod_wrapper.py b/internal/tests/pytests/wrappers/gen_ens_prod/test_gen_ens_prod_wrapper.py index 572130433f..678753d59c 100644 --- a/internal/tests/pytests/wrappers/gen_ens_prod/test_gen_ens_prod_wrapper.py +++ b/internal/tests/pytests/wrappers/gen_ens_prod/test_gen_ens_prod_wrapper.py @@ -340,13 +340,21 @@ def handle_input_dir(config): ({'GEN_ENS_PROD_NMEP_SMOOTH_GAUSSIAN_RADIUS': '120', }, { 'METPLUS_NMEP_SMOOTH_DICT': 'nmep_smooth = {gaussian_radius = 120;}'}), - # 59 - ({'GEN_ENS_PROD_NMEP_SMOOTH_TYPE_METHOD': 'GAUSSIAN', }, - {'METPLUS_NMEP_SMOOTH_DICT': 'nmep_smooth = {type = [{method = GAUSSIAN;}];}'}), # 60 ({'GEN_ENS_PROD_NMEP_SMOOTH_TYPE_WIDTH': '1', }, {'METPLUS_NMEP_SMOOTH_DICT': 'nmep_smooth = {type = [{width = 1;}];}'}), # 61 + ({'GEN_ENS_PROD_NMEP_SMOOTH_TYPE_METHOD': 'GAUSSIAN', }, + {'METPLUS_NMEP_SMOOTH_DICT': 'nmep_smooth = {type = [{method = GAUSSIAN;}];}'}), + # 62 old name without sub directory name + ({'GEN_ENS_PROD_NMEP_SMOOTH_METHOD': 'GAUSSIAN', }, + {'METPLUS_NMEP_SMOOTH_DICT': 'nmep_smooth = {type = [{method = GAUSSIAN;}];}'}), + # 63 both old and new name - should use value from new name + ({'GEN_ENS_PROD_NMEP_SMOOTH_METHOD': 'GAUSSIAN', + 'GEN_ENS_PROD_NMEP_SMOOTH_TYPE_METHOD': 'NEAREST' + }, + {'METPLUS_NMEP_SMOOTH_DICT': 'nmep_smooth = {type = [{method = NEAREST;}];}'}), + # 64 ({ 'GEN_ENS_PROD_NMEP_SMOOTH_VLD_THRESH': '0.0', 'GEN_ENS_PROD_NMEP_SMOOTH_SHAPE': 'circle', @@ -362,13 +370,26 @@ def handle_input_dir(config): 'type = [{method = GAUSSIAN;width = 1;}];}' ) }), - # 62 + # 65 test from WOFS use case + ({ + 'GEN_ENS_PROD_NMEP_SMOOTH_VLD_THRESH': '1.0', + 'GEN_ENS_PROD_NMEP_SMOOTH_SHAPE': 'SQUARE', + 'GEN_ENS_PROD_NMEP_SMOOTH_METHOD': 'NEAREST', + 'GEN_ENS_PROD_NMEP_SMOOTH_WIDTH': '1', + }, + { + 'METPLUS_NMEP_SMOOTH_DICT': ( + 'nmep_smooth = {vld_thresh = 1.0;shape = SQUARE;' + 'type = [{method = NEAREST;width = 1;}];}' + ) + }), + # 66 ({'GEN_ENS_PROD_ENS_MEMBER_IDS': '1,2,3,4', }, {'METPLUS_ENS_MEMBER_IDS': 'ens_member_ids = ["1", "2", "3", "4"];'}), - # 63 + # 67 ({'GEN_ENS_PROD_CONTROL_ID': '0', }, {'METPLUS_CONTROL_ID': 'control_id = "0";'}), - # 64 + # 68 ({'GEN_ENS_PROD_NORMALIZE': 'CLIMO_STD_ANOM', }, {'METPLUS_NORMALIZE': 'normalize = CLIMO_STD_ANOM;'}), From 38809a9aa4c520b7d7cd1cd92808e4793f428e93 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Fri, 17 Nov 2023 14:12:31 -0700 Subject: [PATCH 29/30] remove check for invalid variables because it is not needed and can cause failures for valid variables --- metplus/util/met_config.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/metplus/util/met_config.py b/metplus/util/met_config.py index 6fab11d27b..c83cd0e00f 100644 --- a/metplus/util/met_config.py +++ b/metplus/util/met_config.py @@ -325,19 +325,9 @@ def get_met_config_dict_list(config, app_name, dict_name, dict_items): id_index=2) all_met_config_items = {} - for index, items in indices.items(): + for index, _ in indices.items(): # read all variables for each index met_config_items = [] - - # check if any variable found doesn't match valid variables - not_in_dict = [item for item in items - if item.lower() not in dict_items] - if any(not_in_dict): - for item in not_in_dict: - config.logger.error("Invalid variable: " - f"{search_string}{index}_{item}") - return None - for name, item_info in dict_items.items(): data_type, extra, kids, nicknames = _parse_item_info(item_info) metplus_configs = [f'{search_string}{index}_{name.upper()}'] From 3cda66571c02e77ad9a3beda1b6ea35cac67e630 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Fri, 17 Nov 2023 14:18:16 -0700 Subject: [PATCH 30/30] properly handle list of dictionaries that are found inside another dictionary to support previous variable names used for setting gen_ens_prod's nmep_smooth.type.method and .width --- metplus/util/met_config.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/metplus/util/met_config.py b/metplus/util/met_config.py index c83cd0e00f..da8268dbe5 100644 --- a/metplus/util/met_config.py +++ b/metplus/util/met_config.py @@ -198,7 +198,7 @@ def add_met_config_dict(config, app_name, output_dict, dict_name, items): # if list of dictionaries (dictlist) elif 'list' in data_type: - children = get_met_config_dict_list(config, app_name, f'{dict_name}_{name}', kids) + children = get_met_config_dict_list(config, app_name, name, kids, parent=dict_name) if not children: continue # if dictionary, read get children from MET config @@ -306,7 +306,7 @@ def add_met_config_item(config, item, output_dict, depth=0): **item.extra_args) -def get_met_config_dict_list(config, app_name, dict_name, dict_items): +def get_met_config_dict_list(config, app_name, dict_name, dict_items, parent=None): """! Read METplusConfig and format MET config variables that are a list of dictionaries. Sets value in output dict with key starting with METPLUS_. @@ -317,12 +317,29 @@ def get_met_config_dict_list(config, app_name, dict_name, dict_items): @param dict_items dictionary where the key is name of variable inside MET dictionary and the value is info about the item (see parse_item_info function for more information) + @param parent optional name of dictionary that contains the item (nested + dictionaries) """ - search_string = f'{app_name}_{dict_name}'.upper() - regex = r'^' + search_string + r'(\d*)_(\w+)$' - indices = find_indices_in_config_section(regex, config, - index_index=1, - id_index=2) + regex_end = r'(\d*)_(\w+)$' + if not parent: + search_string = f'{app_name}_{dict_name}'.upper() + regex = r'^' + search_string + regex_end + indices = find_indices_in_config_section(regex, config, + index_index=1, + id_index=2) + else: + search_string = f'{app_name}_{parent}_{dict_name}'.upper() + regex = r'^' + search_string + regex_end + indices = find_indices_in_config_section(regex, config, + index_index=1, + id_index=2) + # if no indices were found, try again excluding sub dict name + if not indices: + search_string = f'{app_name}_{parent}'.upper() + regex = r'^' + search_string + regex_end + indices = find_indices_in_config_section(regex, config, + index_index=1, + id_index=2) all_met_config_items = {} for index, _ in indices.items():