diff --git a/ci/actions/run_tests/entrypoint.sh b/ci/actions/run_tests/entrypoint.sh index 4c6fb23be0..a854f82dfe 100644 --- a/ci/actions/run_tests/entrypoint.sh +++ b/ci/actions/run_tests/entrypoint.sh @@ -51,7 +51,7 @@ fi pip_command="pip3 install Pillow" # build command to run -command="./ci/jobs/run_use_cases_docker.py ${CATEGORIES} ${SUBSETLIST}" +command="./ci/jobs/run_use_cases.py ${CATEGORIES} ${SUBSETLIST}" # add input volumes to run command # keep track of --volumes-from arguments to docker run command diff --git a/ci/jobs/create_output_data_volumes.sh b/ci/jobs/create_output_data_volumes.sh index 7a65e68fc8..ca41ca8291 100755 --- a/ci/jobs/create_output_data_volumes.sh +++ b/ci/jobs/create_output_data_volumes.sh @@ -1,5 +1,9 @@ #! /bin/bash +# Run by GitHub Actions (in .github/workflows/main.yml) to create +# Docker data volumes from output data to create a "truth" +# data set to use in difference tests. + if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then echo This is a pull request, so skip this setp exit 0 diff --git a/ci/jobs/diff_output.py b/ci/jobs/diff_output.py index 353523a37f..969e474497 100755 --- a/ci/jobs/diff_output.py +++ b/ci/jobs/diff_output.py @@ -1,5 +1,8 @@ #! /usr/bin/env python3 +# Run by GitHub Actions (in ci/jobs/run_use_cases.py) to +# trigger difference tests. + import sys import os diff --git a/ci/jobs/docker_setup.sh b/ci/jobs/docker_setup.sh index 9d890b23d7..93719ce1e2 100755 --- a/ci/jobs/docker_setup.sh +++ b/ci/jobs/docker_setup.sh @@ -1,5 +1,13 @@ #! /bin/bash +# Run by GitHub Actions (in .github/workflows/main.yml) to build +# METplus Docker image and put it up to DockerHub so it can be +# used by the use case tests. +# If GitHub Actions run is triggered by a fork that does not have +# permissions to push Docker images to DockerHub, the script is +# is also called (in ci/actions/run_tests/entrypoint.sh) to +# build the Docker image to use for each use case test group + branch_name=`${GITHUB_WORKSPACE}/ci/jobs/print_branch_name.py` if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then branch_name=${branch_name}-pull_request diff --git a/ci/jobs/docker_update_data_volumes.py b/ci/jobs/docker_update_data_volumes.py index 5d7e6748c2..9098f56684 100755 --- a/ci/jobs/docker_update_data_volumes.py +++ b/ci/jobs/docker_update_data_volumes.py @@ -1,5 +1,10 @@ #! /usr/bin/env python3 +# Run by GitHub Actions (in .github/workflows/main.yml) check DTCenter web +# server for any input data tarfiles that have been updated and need to be +# regenerated as Docker data volumes to be used in use case tests. +# Push new/updated data volumes up to DockerHub + import sys import os import shlex diff --git a/ci/jobs/docker_utils.py b/ci/jobs/docker_utils.py index b094bc8c70..1bc5d6552c 100644 --- a/ci/jobs/docker_utils.py +++ b/ci/jobs/docker_utils.py @@ -1,6 +1,11 @@ import os import re +# Utilities used by various CI jobs. Functionality includes: +# - Check if Docker data volumes need to be updated. +# - Get appropriate branch name to use to obtain/create Docker +# images. This is needed for pull request runs. + # repository used for storing input data for development branches DOCKERHUB_DATA_REPO = 'dtcenter/metplus-data-dev' diff --git a/ci/jobs/download_gempaktocf.py b/ci/jobs/download_gempaktocf.py deleted file mode 100755 index 3201f3cf93..0000000000 --- a/ci/jobs/download_gempaktocf.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 - -"""! Downlaods GempakToCF.jar that is used to convert GEMPAK data to NetCDF""" - -import os -import shlex -import subprocess - -GEMPAK_TO_CF_URL = ('https://dtcenter.org/sites/default/files/community-code/' - 'metplus/utilities/GempakToCF.jar') - -def run(input_data_directory): - """! Downloads GempakToCF.jar from website into input_data_directory - - @param input_data_directory destination to download file - @returns True if successful, False if an error occurred - """ - if not os.path.exists(input_data_directory): - print(f"Creating directory: {input_data_directory}") - os.makedirs(input_data_directory) - - print(f"Downloading {GEMPAK_TO_CF_URL} into {input_data_directory}") - - cmd = f"curl -L -O {GEMPAK_TO_CF_URL}" - try: - subprocess.run(shlex.split(cmd), - check=True, - cwd=input_data_directory) - except subprocess.CalledProcessError as err: - print(f"ERROR: Download failed: {os.path.basename(GEMPAK_TO_CF_URL)}") - return False - - return True - -if __name__ == "__main__": - input_data_directory = os.path.join(os.environ['OWNER_BUILD_DIR'], - 'input') - run(input_data_directory) diff --git a/ci/jobs/get_artifact_name.sh b/ci/jobs/get_artifact_name.sh index b56a8b5c4d..e2446b4d91 100755 --- a/ci/jobs/get_artifact_name.sh +++ b/ci/jobs/get_artifact_name.sh @@ -1,5 +1,9 @@ #! /bin/bash +# Run by GitHub Actions (in .github/workflows/main.yml and +# ci/actions/run_tests/entrypoint.sh) to get properly +# formatted artifact name for use case output + artifact_name=$1 # strip of :NEW if found at end of name if [ ${artifact_name: -4} == ":NEW" ]; then diff --git a/ci/jobs/get_artifacts.py b/ci/jobs/get_artifacts.py deleted file mode 100755 index 895590429e..0000000000 --- a/ci/jobs/get_artifacts.py +++ /dev/null @@ -1,46 +0,0 @@ -#! /usr/bin/env python3 - -import os -import sys -import subprocess -import shlex - -names = sys.argv[1].split(',') - -DEVELOP_REF_RUN = 552853822 - -ARTIFACT_IDS_DEVELOP = { - 'air_quality_and_comp': 39369666, - 'climate': 39369667, - 'convection_allowing_models_a': 39369668, - 'convection_allowing_models_b': 39369669, - 'cryosphere': 39369670, - 'data_assimilation': 39369671, - 'medium_range_a': 39369672, - 'medium_range_b': 39369673, - 'medium_range_c': 39369674, - 'met_tool_wrapper': 39369675, - 'precipitation': 39369676, -} - -for name in names: - artifact_id = ARTIFACT_IDS_DEVELOP[name] - my_secret_api_token = os.environ.get('MY_SECRET_API_TOKEN') - artifact_url = f"https://{my_secret_api_token}@api.github.com/repos/dtcenter/metplus/actions/artifacts/{artifact_id}/zip" - - truth_dir = os.path.abspath(os.path.join(os.environ.get('GITHUB_WORKSPACE'), - os.pardir, - 'truth')) - - output_file = os.path.join(truth_dir, f'{name}.zip') - - if not os.path.exists(truth_dir): - print(f"Creating directory: {truth_dir}") - os.makedirs(truth_dir) - - cmd = f'curl -L -o {output_file} {artifact_url}' - print(cmd) - ret = subprocess.run(shlex.split(cmd)) - - cmd = f'unzip {output_file} -d {truth_dir}/' - ret = subprocess.run(shlex.split(cmd)) diff --git a/ci/jobs/get_data_volumes.py b/ci/jobs/get_data_volumes.py index d1e96601ef..86ea3c6ec7 100755 --- a/ci/jobs/get_data_volumes.py +++ b/ci/jobs/get_data_volumes.py @@ -1,5 +1,10 @@ #! /usr/bin/env python3 +# Run by GitHub Actions (in ci/actions/run_tests/entrypoint.sh) +# to obtain Docker data volumes for input and output data, create +# an alias name for the volumes, and generate --volumes-from arguments +# that are added to the Docker run command to make data available + import sys import os import subprocess diff --git a/ci/jobs/get_use_case_commands.py b/ci/jobs/get_use_case_commands.py index 0eceaca127..597e8e80a2 100755 --- a/ci/jobs/get_use_case_commands.py +++ b/ci/jobs/get_use_case_commands.py @@ -1,5 +1,9 @@ #! /usr/bin/env python3 +# Script to obtain commands needed to run use case groups including +# scripts or pip commands to obtain external Python dependencies +# Run by GitHub Actions (in ci/jobs/run_use_cases.py) to run use case tests + import sys import os @@ -19,6 +23,7 @@ def handle_requirements(requirements, work_dir): script_path = os.path.join(work_dir, 'ci', 'jobs', + 'python_requirements', f'get_{requirement.lower()}.sh') print(f"Looking for script: {script_path}") if os.path.exists(script_path): diff --git a/ci/jobs/gha_get_current_branch.sh b/ci/jobs/gha_get_current_branch.sh deleted file mode 100755 index e2af4e54a3..0000000000 --- a/ci/jobs/gha_get_current_branch.sh +++ /dev/null @@ -1 +0,0 @@ -echo ${GITHUB_REF#refs/heads/} diff --git a/ci/jobs/gha_get_dockerhub_tag.sh b/ci/jobs/gha_get_dockerhub_tag.sh deleted file mode 100755 index d1a78a4bc2..0000000000 --- a/ci/jobs/gha_get_dockerhub_tag.sh +++ /dev/null @@ -1,2 +0,0 @@ -current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -echo dtcenter/metplus-dev:`$current_dir/gha_get_current_branch.sh` diff --git a/ci/jobs/print_branch_name.py b/ci/jobs/print_branch_name.py index b92aacbe9f..b3bfcd299a 100755 --- a/ci/jobs/print_branch_name.py +++ b/ci/jobs/print_branch_name.py @@ -1,5 +1,9 @@ #! /usr/bin/env python3 +# Script to easily get branch name from docker_utils function +# Run by GitHub Actions (in ci/actions/run_tests/entrypoint.sh, +# ci/jobs/create_output_data_volumes.sh, and ci/jobs/docker_setup.sh) + from docker_utils import get_branch_name print(get_branch_name()) diff --git a/ci/jobs/print_log_errors.py b/ci/jobs/print_log_errors.py deleted file mode 100755 index aa1ed68559..0000000000 --- a/ci/jobs/print_log_errors.py +++ /dev/null @@ -1,98 +0,0 @@ -#! /usr/bin/env python3 - -import sys -import os -import glob - -# strings to search for to find errors -error_str = 'ERROR' -command_start_str = 'COPYABLE ENVIRONMENT FOR NEXT COMMAND' -check_logfile_string = "Check the logfile for more information on why it failed" - -def run(output_dir, replacement_dir=None): - """! Search for logs under output_dir that have MET errors and print the - commands that have errors to the screen. Assumes logs are either in - {output_dir}/logs or {output_dir}//logs where is - any subdirectory under the output directory - @param output_dir directory to search for logs - @param replacement_dir directory to replace for output_dir to find - files. This is needed when running through Travis because the - directory in Travis differs from the directory listed in the log - files because the are run in the Docker container. - """ - # pytest logs directory is in top level of output_dir - log_topdir_glob = os.path.join(output_dir, - 'logs', - '*') - - # use case logs directory are in each subdirectory in output_dir - log_subdir_glob = os.path.join(output_dir, - '*', - 'logs', - '*') - logs_with_errors = set() - - # get list of log files that have MET errors in them using check_logfile_string - all_log_files = glob.glob(log_topdir_glob) + glob.glob(log_subdir_glob) - for log_file in all_log_files: - - with open(log_file, 'r') as file_handle: - lines = file_handle.readlines() - - for line in lines: - if check_logfile_string in line: - error_log = line.split(':')[-1].strip() - logs_with_errors.add(error_log) - - # find error lines and get text for command that contains the error - for log_file in logs_with_errors: - try: - if replacement_dir: - local_log_file = log_file.replace(replacement_dir, output_dir) - - with open(local_log_file, 'r') as file_handle: - lines = file_handle.readlines() - except OSError: - print(f"ERROR: Could not open {local_log_file}") - continue - - error_indices = [] - command_start_indices = [] - for index, line in enumerate(lines): - if line.startswith(error_str): - error_indices.append(index) - if command_start_str in line: - command_start_indices.append(index) - - if not error_indices: - continue - - lines_to_print = set() - for index, command_start_2 in enumerate(command_start_indices): - if index == 0: - continue - - command_start_1 = command_start_indices[index-1] - # check if any error lines are found between command start lines - for error_index in error_indices: - if error_index > command_start_1 and error_index < command_start_2: - lines_to_print.add((command_start_1, command_start_2)) - - for start_index, end_index in lines_to_print: - print(f"\nPrinting lines {start_index} to {end_index} " - f"from {local_log_file}") - for line in lines[start_index:end_index]: - print(line.strip()) - -if __name__ == "__main__": - if len(sys.argv) < 2: - print("ERROR: Must supply directory to search as argument") - sys.exit(1) - elif len(sys.argv) > 2: - replacement_dir = sys.argv[2] - else: - replacement_dir = None - - output_dir = sys.argv[1] - run(output_dir, replacement_dir) - diff --git a/ci/jobs/print_python_version.py b/ci/jobs/print_python_version.py index 7de32fe4f0..5b6e382354 100755 --- a/ci/jobs/print_python_version.py +++ b/ci/jobs/print_python_version.py @@ -1,5 +1,8 @@ #! /usr/bin/env python3 +# Script to easily obtain minimum python version requirement +# Used in GitHub Actions (in ci/jobs/python_requirements/get_miniconda.sh) + import sys import os diff --git a/ci/jobs/get_cartopy.sh b/ci/jobs/python_requirements/get_cartopy.sh similarity index 100% rename from ci/jobs/get_cartopy.sh rename to ci/jobs/python_requirements/get_cartopy.sh diff --git a/ci/jobs/get_metcalcpy.sh b/ci/jobs/python_requirements/get_metcalcpy.sh similarity index 91% rename from ci/jobs/get_metcalcpy.sh rename to ci/jobs/python_requirements/get_metcalcpy.sh index 192a3b638e..86837d0aef 100755 --- a/ci/jobs/get_metcalcpy.sh +++ b/ci/jobs/python_requirements/get_metcalcpy.sh @@ -5,7 +5,7 @@ pip3 install scipy pip3 install pingouin basedir=$(dirname "$0") -work_dir=$basedir/../.. +work_dir=$basedir/../../.. # run manage externals to obtain METcalcpy ${work_dir}/manage_externals/checkout_externals -e ${work_dir}/ci/parm/Externals_metcalcpy.cfg diff --git a/ci/jobs/get_metplotpy.sh b/ci/jobs/python_requirements/get_metplotpy.sh similarity index 91% rename from ci/jobs/get_metplotpy.sh rename to ci/jobs/python_requirements/get_metplotpy.sh index cb79c9faea..1fc1f4b64c 100755 --- a/ci/jobs/get_metplotpy.sh +++ b/ci/jobs/python_requirements/get_metplotpy.sh @@ -5,7 +5,7 @@ pip3 install scipy pip3 install cmocean basedir=$(dirname "$0") -work_dir=$basedir/../.. +work_dir=$basedir/../../.. # run manage externals to obtain METcalcpy ${work_dir}/manage_externals/checkout_externals -e ${work_dir}/ci/parm/Externals_metplotpy.cfg diff --git a/ci/jobs/get_miniconda.sh b/ci/jobs/python_requirements/get_miniconda.sh similarity index 93% rename from ci/jobs/get_miniconda.sh rename to ci/jobs/python_requirements/get_miniconda.sh index 0f6dc4a10e..ea644774fd 100755 --- a/ci/jobs/get_miniconda.sh +++ b/ci/jobs/python_requirements/get_miniconda.sh @@ -2,7 +2,7 @@ script_dir=$(dirname "$0") -python_version=`${script_dir}/print_python_version.py` +python_version=`${script_dir}/../print_python_version.py` # these are used to obtain version of MiniConda3 # the version determines the default version of Python diff --git a/ci/jobs/get_pygrib.sh b/ci/jobs/python_requirements/get_pygrib.sh similarity index 83% rename from ci/jobs/get_pygrib.sh rename to ci/jobs/python_requirements/get_pygrib.sh index 18d28318f0..6f1b63aa05 100755 --- a/ci/jobs/get_pygrib.sh +++ b/ci/jobs/python_requirements/get_pygrib.sh @@ -1,7 +1,6 @@ #!/bin/bash #shell script to install pygrib with dependencies -# todd arbetter: arbetter@ucar.edu yum -y install eccodes-devel pip3 install numpy diff --git a/ci/jobs/get_xesmf.sh b/ci/jobs/python_requirements/get_xesmf.sh similarity index 100% rename from ci/jobs/get_xesmf.sh rename to ci/jobs/python_requirements/get_xesmf.sh diff --git a/ci/jobs/run_use_cases.py b/ci/jobs/run_use_cases.py index c4d4acb69d..1ca795c1a3 100755 --- a/ci/jobs/run_use_cases.py +++ b/ci/jobs/run_use_cases.py @@ -1,85 +1,158 @@ -#!/usr/bin/env python3 +#! /usr/bin/env python3 + +# Used in GitHub Actions (in ci/actions/run_tests/entrypoint.sh) +# to obtain and run commands to run use cases from group, +# execute difference tests if requested, copy error logs and/or +# files that reported differences into directory to make +# them available in GitHub Actions artifacts for easy review -import sys import os -import re -import shlex +import sys import subprocess -from os.path import dirname - -import get_data_volumes -import download_gempaktocf -import print_log_errors +import shlex +import shutil import get_use_case_commands -from metplus.util.met_util import expand_int_string_to_list -def main(categories, subset_list): +# add ci/util to sys path to get diff utility +diff_util_dir = os.path.join(os.environ.get('GITHUB_WORKSPACE'), + 'ci', + 'util') +sys.path.insert(0, diff_util_dir) +from diff_util import compare_dir + +TRUTH_DIR = '/data/truth' +OUTPUT_DIR = '/data/output' +DIFF_DIR = '/data/diff' +ERROR_LOG_DIR = '/data/error_logs' + +def copy_error_logs(): + """! Copy log output to error log directory if any use case failed """ + use_case_dirs = os.listdir(OUTPUT_DIR) + for use_case_dir in use_case_dirs: + log_dir = os.path.join(OUTPUT_DIR, + use_case_dir, + 'logs') + if not os.path.isdir(log_dir): + continue + + # check if there are errors in the metplus.log file and + # only copy directory if there are any errors + metplus_log = os.path.join(log_dir, 'metplus.log') + found_errors = False + with open(metplus_log, 'r') as file_handle: + if 'ERROR:' in file_handle.read(): + found_errors = True + if not found_errors: + continue + + output_dir = os.path.join(ERROR_LOG_DIR, + use_case_dir) + log_files = os.listdir(log_dir) + for log_file in log_files: + log_path = os.path.join(log_dir, log_file) + output_path = os.path.join(output_dir, log_file) + print(f"Copying {log_path} to {output_path}") + # create output directory if it doesn't exist + output_dir = os.path.dirname(output_path) + if not os.path.exists(output_dir): + os.makedirs(output_dir) + shutil.copyfile(log_path, output_path) + +def copy_diff_output(diff_files): + """! Loop through difference output and copy files + to directory so it can be made available for comparison. + Files will be put into the same directory with _truth or + _output added before their file extension. + + @param diff_files list of tuples containing truth file path + and file path of output that was just generated. Either tuple + value may be an empty string if the file was not found. + """ + for truth_file, out_file, _ in diff_files: + if truth_file: + copy_to_diff_dir(truth_file, + 'truth') + if out_file: + copy_to_diff_dir(out_file, + 'output') + +def copy_to_diff_dir(file_path, data_type): + """! Generate output path based on input file path, + adding text based on data_type to the filename, then + copy input file to that output path. + + @param file_path full path of file to copy + @param data_type data identifier, should be 'truth' + or 'output' + @returns True if success, False if there was a problem + copying the file + """ + if data_type == 'truth': + data_dir = TRUTH_DIR + else: + data_dir = OUTPUT_DIR - categories_list = categories.split(',') + # replace data dir with diff directory + diff_out = file_path.replace(data_dir, DIFF_DIR) - OWNER_BUILD_DIR = os.path.dirname(os.environ['GITHUB_WORKSPACE']) + # add data type identifier to filename before extension + output_path, extension = os.path.splitext(diff_out) + output_path = f'{output_path}_{data_type}{extension}' - # get data volumes - print(f"calling get_data_volumes.main({categories_list})") - volumes_from = get_data_volumes.main(categories_list) + # create output directory if it doesn't exist + output_dir = os.path.dirname(output_path) + if not os.path.exists(output_dir): + os.makedirs(output_dir) - # obtain GempakToCF.jar for cases that read GEMPAK data - input_data_directory = os.path.join(OWNER_BUILD_DIR, - 'input') - download_gempaktocf.run(input_data_directory) + print(f'Copying {file_path} to\n{output_path}') + try: + shutil.copyfile(file_path, output_path) + except OSError as err: + print(f'Could not copy file. {err}') + return False - # becomes False if any use case fails - isOK = True + return True + +def main(): + categories, subset_list, compare = ( + get_use_case_commands.handle_command_line_args() + ) + categories_list = categories.split(',') + all_commands = ( + get_use_case_commands.main(categories_list, + subset_list, + work_dir=os.environ.get('GITHUB_WORKSPACE')) + ) - # run use cases - work_dir = os.path.join(os.environ.get('DOCKER_WORK_DIR'), - 'METplus') - all_commands = get_use_case_commands.main(categories_list, - subset_list, - work_dir=work_dir) - for command, requirements in all_commands: - travis_build_dir = os.environ['GITHUB_WORKSPACE'] - if requirements: - reqs = f"{';'.join(requirements)};" + isOK = True + for cmd, reqs in all_commands: + if reqs: + reqs_fmt = f"{';'.join(reqs)};" else: - reqs = '' - cmd = (f'{travis_build_dir}/ci/jobs/docker_run_metplus.sh' - f' "{reqs}{command}" "{volumes_from}"') - print(cmd) + reqs_fmt = '' + print(f'{reqs}\n{cmd}') + full_cmd = f"{reqs_fmt}{cmd}" try: - subprocess.run(shlex.split(cmd), check=True) + subprocess.run(full_cmd, check=True, shell=True) except subprocess.CalledProcessError as err: - print(f"ERROR: Command failed: {cmd} -- {err}") + print(f"ERROR: Command failed: {full_cmd} -- {err}") isOK = False - output_dir = os.path.join(OWNER_BUILD_DIR, - 'output') - replacement_dir = os.path.join(os.environ['DOCKER_DATA_DIR'], - 'output') - print_log_errors.run(output_dir, - replacement_dir) - - # if any tests failed, exit 1, otherwise exit 0 - if not isOK: - sys.exit(1) - -def handle_command_line_args(): - # read command line arguments to determine which use cases to run - if len(sys.argv) < 2: - print("No use cases specified") - sys.exit(1) + copy_error_logs() - # split up categories by & or , - categories = sys.argv[1] + if compare and isOK: + print('******************************') + print("Comparing output to truth data") + diff_files = compare_dir(TRUTH_DIR, OUTPUT_DIR, debug=True) + if diff_files: + isOK = False - # get subset values if specified - if len(sys.argv) > 2: - subset_list = expand_int_string_to_list(sys.argv[2]) - else: - subset_list = None + # copy difference files into directory + # so it can be easily downloaded and compared + copy_diff_output(diff_files) - return categories, subset_list + if not isOK: + sys.exit(1) -if __name__ == "__main__": - categories, subset_list = handle_command_line_args() - main(categories, subset_list) +if __name__ == '__main__': + main() diff --git a/ci/jobs/run_use_cases_docker.py b/ci/jobs/run_use_cases_docker.py deleted file mode 100755 index 1cd29ce9be..0000000000 --- a/ci/jobs/run_use_cases_docker.py +++ /dev/null @@ -1,141 +0,0 @@ -#! /usr/bin/env python3 - -import os -import sys -import subprocess -import shlex -import shutil - -import get_use_case_commands - -# add ci/util to sys path to get diff utility -diff_util_dir = os.path.join(os.environ.get('GITHUB_WORKSPACE'), - 'ci', - 'util') -sys.path.insert(0, diff_util_dir) -from diff_util import compare_dir - -TRUTH_DIR = '/data/truth' -OUTPUT_DIR = '/data/output' -DIFF_DIR = '/data/diff' -ERROR_LOG_DIR = '/data/error_logs' - -def copy_error_logs(): - """! Copy log output to error log directory if any use case failed """ - use_case_dirs = os.listdir(OUTPUT_DIR) - for use_case_dir in use_case_dirs: - log_dir = os.path.join(OUTPUT_DIR, - use_case_dir, - 'logs') - if not os.path.isdir(log_dir): - continue - output_dir = os.path.join(ERROR_LOG_DIR, - use_case_dir) - log_files = os.listdir(log_dir) - for log_file in log_files: - log_path = os.path.join(log_dir, log_file) - output_path = os.path.join(output_dir, log_file) - print(f"Copying {log_path} to {output_path}") - # create output directory if it doesn't exist - output_dir = os.path.dirname(output_path) - if not os.path.exists(output_dir): - os.makedirs(output_dir) - shutil.copyfile(log_path, output_path) - -def copy_diff_output(diff_files): - """! Loop through difference output and copy files - to directory so it can be made available for comparison. - Files will be put into the same directory with _truth or - _output added before their file extension. - - @param diff_files list of tuples containing truth file path - and file path of output that was just generated. Either tuple - value may be an empty string if the file was not found. - """ - for truth_file, out_file, _ in diff_files: - if truth_file: - copy_to_diff_dir(truth_file, - 'truth') - if out_file: - copy_to_diff_dir(out_file, - 'output') - -def copy_to_diff_dir(file_path, data_type): - """! Generate output path based on input file path, - adding text based on data_type to the filename, then - copy input file to that output path. - - @param file_path full path of file to copy - @param data_type data identifier, should be 'truth' - or 'output' - @returns True if success, False if there was a problem - copying the file - """ - if data_type == 'truth': - data_dir = TRUTH_DIR - else: - data_dir = OUTPUT_DIR - - # replace data dir with diff directory - diff_out = file_path.replace(data_dir, DIFF_DIR) - - # add data type identifier to filename before extension - output_path, extension = os.path.splitext(diff_out) - output_path = f'{output_path}_{data_type}{extension}' - - # create output directory if it doesn't exist - output_dir = os.path.dirname(output_path) - if not os.path.exists(output_dir): - os.makedirs(output_dir) - - print(f'Copying {file_path} to\n{output_path}') - try: - shutil.copyfile(file_path, output_path) - except OSError as err: - print(f'Could not copy file. {err}') - return False - - return True - -def main(): - categories, subset_list, compare = ( - get_use_case_commands.handle_command_line_args() - ) - categories_list = categories.split(',') - all_commands = ( - get_use_case_commands.main(categories_list, - subset_list, - work_dir=os.environ.get('GITHUB_WORKSPACE')) - ) - - isOK = True - for cmd, reqs in all_commands: - if reqs: - reqs_fmt = f"{';'.join(reqs)};" - else: - reqs_fmt = '' - print(f'{reqs}\n{cmd}') - full_cmd = f"{reqs_fmt}{cmd}" - try: - subprocess.run(full_cmd, check=True, shell=True) - except subprocess.CalledProcessError as err: - print(f"ERROR: Command failed: {full_cmd} -- {err}") - isOK = False - copy_error_logs() - - if compare and isOK: - print('******************************') - print("Comparing output to truth data") - diff_files = compare_dir(TRUTH_DIR, OUTPUT_DIR, debug=True) - if diff_files: - isOK = False - - # copy difference files into directory - # so it can be easily downloaded and compared - copy_diff_output(diff_files) - - if not isOK: - sys.exit(1) - -if __name__ == '__main__': - main() diff --git a/ci/jobs/set_job_controls.sh b/ci/jobs/set_job_controls.sh index 9dd6e14ae9..cea137273a 100755 --- a/ci/jobs/set_job_controls.sh +++ b/ci/jobs/set_job_controls.sh @@ -1,5 +1,9 @@ #! /bin/bash +# Run by GitHub Actions (in .github/workflows/main.yml) to parse +# info from GitHub event and commit message from last commit before +# a push to determine which jobs to run and which to skip. + # set default status for jobs run_docs=true run_get_image=true diff --git a/ci/util/diff_util.py b/ci/util/diff_util.py index c969500a0a..9fd79c283a 100644 --- a/ci/util/diff_util.py +++ b/ci/util/diff_util.py @@ -19,6 +19,10 @@ '.zip', ] +UNSUPPORTED_EXTENSIONS = [ + '.pdf', +] + def get_file_type(filepath): _, file_extension = os.path.splitext(filepath) if file_extension in IMAGE_EXTENSIONS: @@ -39,6 +43,9 @@ def get_file_type(filepath): if file_extension in SKIP_EXTENSIONS: return 'skip' + if file_extension in UNSUPPORTED_EXTENSIONS: + return f'unsupported{file_extension}' + return 'unknown' def compare_dir(dir_a, dir_b, debug=False): @@ -127,6 +134,10 @@ def compare_files(filepath_a, filepath_b, debug=False, dir_a=None, dir_b=None): print(f'Skipping') return None + if file_type.startswith('unsupported'): + print(f"Unsupported file type encountered: {file_type.split('.')[1]}") + return (filepath_a, filepath_b, file_type) + if file_type == 'netcdf': print("Comparing NetCDF") if not nc_is_equal(filepath_a, filepath_b): diff --git a/docs/Users_Guide/glossary.rst b/docs/Users_Guide/glossary.rst index 18e2a79cf3..890e51f973 100644 --- a/docs/Users_Guide/glossary.rst +++ b/docs/Users_Guide/glossary.rst @@ -5388,6 +5388,9 @@ METplus Configuration Glossary | *Used by:* EnsembleStat + ENSEMBLE_STAT_CLIMO_CDF_CDF_BINS + See :term:`ENSEMBLE_STAT_CLIMO_CDF_BINS` + ENSEMBLE_STAT_CLIMO_CDF_BINS Specify the value for 'climo_cdf.cdf_bins' in the MET configuration file for EnsembleStat. @@ -5993,3 +5996,198 @@ METplus Configuration Glossary | *Used by:* CyclonePlotter + GRID_STAT_CLIMO_CDF_CDF_BINS + See :term:`GRID_STAT_CLIMO_CDF_BINS` + + GRID_STAT_CLIMO_CDF_BINS + Specify the value for 'climo_cdf.cdf_bins' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_CLIMO_CDF_CENTER_BINS + Specify the value for 'climo_cdf.center_bins' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_CLIMO_CDF_WRITE_BINS + Specify the value for 'climo_cdf.write_bins' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + POINT_STAT_CLIMO_CDF_CDF_BINS + See :term:`POINT_STAT_CLIMO_CDF_BINS` + + POINT_STAT_CLIMO_CDF_BINS + Specify the value for 'climo_cdf.cdf_bins' in the MET configuration file for PointStat. + + | *Used by:* PointStat + + POINT_STAT_CLIMO_CDF_CENTER_BINS + Specify the value for 'climo_cdf.center_bins' in the MET configuration file for PointStat. + + | *Used by:* PointStat + + POINT_STAT_CLIMO_CDF_WRITE_BINS + Specify the value for 'climo_cdf.write_bins' in the MET configuration file for PointStat. + + | *Used by:* PointStat + + GRID_STAT_OUTPUT_FLAG_FHO + Specify the value for 'output_flag.fho' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_CTC + Specify the value for 'output_flag.ctc' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_CTS + Specify the value for 'output_flag.cts' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_MCTC + Specify the value for 'output_flag.mctc' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_MCTS + Specify the value for 'output_flag.mcts' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_CNT + Specify the value for 'output_flag.cnt' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_SL1L2 + Specify the value for 'output_flag.sl1l2' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_SAL1L2 + Specify the value for 'output_flag.sal1l2' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_VL1L2 + Specify the value for 'output_flag.vl1l2' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_VAL1L2 + Specify the value for 'output_flag.val1l2' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_VCNT + Specify the value for 'output_flag.vcnt' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_PCT + Specify the value for 'output_flag.pct' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_PSTD + Specify the value for 'output_flag.pstd' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_PJC + Specify the value for 'output_flag.pjc' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_PRC + Specify the value for 'output_flag.prc' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_ECLV + Specify the value for 'output_flag.eclv' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_NBRCTC + Specify the value for 'output_flag.nbrctc' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_NBRCTS + Specify the value for 'output_flag.nbrcts' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_NBRCNT + Specify the value for 'output_flag.nbrcnt' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_GRAD + Specify the value for 'output_flag.grad' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_OUTPUT_FLAG_DMAP + Specify the value for 'output_flag.dmap' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_NC_PAIRS_FLAG_LATLON + Specify the value for 'nc_pairs_flag.latlon' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_NC_PAIRS_FLAG_RAW + Specify the value for 'nc_pairs_flag.raw' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_NC_PAIRS_FLAG_DIFF + Specify the value for 'nc_pairs_flag.diff' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_NC_PAIRS_FLAG_CLIMO + Specify the value for 'nc_pairs_flag.climo' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_NC_PAIRS_FLAG_CLIMO_CDP + Specify the value for 'nc_pairs_flag.climo_cdp' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_NC_PAIRS_FLAG_WEIGHT + Specify the value for 'nc_pairs_flag.weight' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_NC_PAIRS_FLAG_NBRHD + Specify the value for 'nc_pairs_flag.nbrhd' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_NC_PAIRS_FLAG_FOURIER + Specify the value for 'nc_pairs_flag.fourier' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_NC_PAIRS_FLAG_GRADIENT + Specify the value for 'nc_pairs_flag.gradient' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_NC_PAIRS_FLAG_DISTANCE_MAP + Specify the value for 'nc_pairs_flag.distance_map' in the MET configuration file for GridStat. + + | *Used by:* GridStat + + GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK + Specify the value for 'nc_pairs_flag.apply_mask' in the MET configuration file for GridStat. + + | *Used by:* GridStat diff --git a/docs/Users_Guide/wrappers.rst b/docs/Users_Guide/wrappers.rst index 16fc09561a..29a704ce35 100755 --- a/docs/Users_Guide/wrappers.rst +++ b/docs/Users_Guide/wrappers.rst @@ -1047,7 +1047,7 @@ Below the file contents are descriptions of each environment variable referenced * - :term:`GRID_DIAG_MASK_POLY` - mask.poly -.. note:: Since the default value in the MET config file for 'grid' is grid = [ "FULL" ];, setting GRID_STAT_MASK_GRID to an empty string will result in a value of grid = []; in the MET config file. +.. note:: Since the default value in the MET config file for 'grid' is grid = [ "FULL" ];, setting GRID_DIAG_MASK_GRID to an empty string will result in a value of grid = []; in the MET config file. **${METPLUS_MET_CONFIG_OVERRIDES}** @@ -1098,6 +1098,41 @@ METplus Configuration | :term:`GRID_STAT_REGRID_WIDTH` | :term:`GRID_STAT_REGRID_VLD_THRESH` | :term:`GRID_STAT_REGRID_SHAPE` +| :term:`GRID_STAT_CLIMO_CDF_BINS` +| :term:`GRID_STAT_CLIMO_CDF_CENTER_BINS` +| :term:`GRID_STAT_CLIMO_CDF_WRITE_BINS` +| :term:`GRID_STAT_OUTPUT_FLAG_FHO` +| :term:`GRID_STAT_OUTPUT_FLAG_CTC` +| :term:`GRID_STAT_OUTPUT_FLAG_CTS` +| :term:`GRID_STAT_OUTPUT_FLAG_MCTC` +| :term:`GRID_STAT_OUTPUT_FLAG_MCTS` +| :term:`GRID_STAT_OUTPUT_FLAG_CNT` +| :term:`GRID_STAT_OUTPUT_FLAG_SL1L2` +| :term:`GRID_STAT_OUTPUT_FLAG_SAL1L2` +| :term:`GRID_STAT_OUTPUT_FLAG_VL1L2` +| :term:`GRID_STAT_OUTPUT_FLAG_VAL1L2` +| :term:`GRID_STAT_OUTPUT_FLAG_VCNT` +| :term:`GRID_STAT_OUTPUT_FLAG_PCT` +| :term:`GRID_STAT_OUTPUT_FLAG_PSTD` +| :term:`GRID_STAT_OUTPUT_FLAG_PJC` +| :term:`GRID_STAT_OUTPUT_FLAG_PRC` +| :term:`GRID_STAT_OUTPUT_FLAG_ECLV` +| :term:`GRID_STAT_OUTPUT_FLAG_NBRCTC` +| :term:`GRID_STAT_OUTPUT_FLAG_NBRCTS` +| :term:`GRID_STAT_OUTPUT_FLAG_NBRCNT` +| :term:`GRID_STAT_OUTPUT_FLAG_GRAD` +| :term:`GRID_STAT_OUTPUT_FLAG_DMAP` +| :term:`GRID_STAT_NC_PAIRS_FLAG_LATLON` +| :term:`GRID_STAT_NC_PAIRS_FLAG_RAW` +| :term:`GRID_STAT_NC_PAIRS_FLAG_DIFF` +| :term:`GRID_STAT_NC_PAIRS_FLAG_CLIMO` +| :term:`GRID_STAT_NC_PAIRS_FLAG_CLIMO_CDP` +| :term:`GRID_STAT_NC_PAIRS_FLAG_WEIGHT` +| :term:`GRID_STAT_NC_PAIRS_FLAG_NBRHD` +| :term:`GRID_STAT_NC_PAIRS_FLAG_FOURIER` +| :term:`GRID_STAT_NC_PAIRS_FLAG_GRADIENT` +| :term:`GRID_STAT_NC_PAIRS_FLAG_DISTANCE_MAP` +| :term:`GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK` | :term:`GRID_STAT_MASK_GRID` (optional) | :term:`GRID_STAT_MASK_POLY` (optional) | :term:`GRID_STAT_MET_CONFIG_OVERRIDES` @@ -1327,6 +1362,104 @@ Below the file contents are descriptions of each environment variable referenced * - :term:`GRID_STAT_MET_CONFIG_OVERRIDES` - n/a +**${METPLUS_CLIMO_CDF_DICT}** + +.. list-table:: + :widths: 5 5 + :header-rows: 0 + + * - METplus Config(s) + - MET Config File + * - :term:`GRID_STAT_CLIMO_CDF_BINS` + - climo_cdf.cdf_bins + * - :term:`GRID_STAT_CLIMO_CDF_CENTER_BINS` + - climo_cdf.center_bins + * - :term:`GRID_STAT_CLIMO_CDF_WRITE_BINS` + - climo_cdf.write_bins + +**${METPLUS_OUTPUT_FLAG_DICT}** + +.. list-table:: + :widths: 5 5 + :header-rows: 0 + + * - METplus Config(s) + - MET Config File + * - :term:`GRID_STAT_OUTPUT_FLAG_FHO` + - output_flag.fho + * - :term:`GRID_STAT_OUTPUT_FLAG_CTC` + - output_flag.ctc + * - :term:`GRID_STAT_OUTPUT_FLAG_CTS` + - output_flag.cts + * - :term:`GRID_STAT_OUTPUT_FLAG_MCTC` + - output_flag.mctc + * - :term:`GRID_STAT_OUTPUT_FLAG_MCTS` + - output_flag.mcts + * - :term:`GRID_STAT_OUTPUT_FLAG_CNT` + - output_flag.cnt + * - :term:`GRID_STAT_OUTPUT_FLAG_SL1L2` + - output_flag.sl1l2 + * - :term:`GRID_STAT_OUTPUT_FLAG_SAL1L2` + - output_flag.sal1l2 + * - :term:`GRID_STAT_OUTPUT_FLAG_VL1L2` + - output_flag.vl1l2 + * - :term:`GRID_STAT_OUTPUT_FLAG_VAL1L2` + - output_flag.val1l2 + * - :term:`GRID_STAT_OUTPUT_FLAG_VCNT` + - output_flag.vcnt + * - :term:`GRID_STAT_OUTPUT_FLAG_PCT` + - output_flag.pct + * - :term:`GRID_STAT_OUTPUT_FLAG_PSTD` + - output_flag.pstd + * - :term:`GRID_STAT_OUTPUT_FLAG_PJC` + - output_flag.pjc + * - :term:`GRID_STAT_OUTPUT_FLAG_PRC` + - output_flag.prc + * - :term:`GRID_STAT_OUTPUT_FLAG_ECLV` + - output_flag.eclv + * - :term:`GRID_STAT_OUTPUT_FLAG_NBRCTC` + - output_flag.nbrctc + * - :term:`GRID_STAT_OUTPUT_FLAG_NBRCTS` + - output_flag.nbrcts + * - :term:`GRID_STAT_OUTPUT_FLAG_NBRCNT` + - output_flag.nbrcnt + * - :term:`GRID_STAT_OUTPUT_FLAG_GRAD` + - output_flag.grad + * - :term:`GRID_STAT_OUTPUT_FLAG_DMAP` + - output_flag.dmap + +**${METPLUS_NC_PAIRS_FLAG_DICT}** + +.. list-table:: + :widths: 5 5 + :header-rows: 0 + + * - METplus Config(s) + - MET Config File + * - :term:`GRID_STAT_NC_PAIRS_FLAG_LATLON` + - nc_pairs_flag.latlon + * - :term:`GRID_STAT_NC_PAIRS_FLAG_RAW` + - nc_pairs_flag.raw + * - :term:`GRID_STAT_NC_PAIRS_FLAG_DIFF` + - nc_pairs_flag.diff + * - :term:`GRID_STAT_NC_PAIRS_FLAG_CLIMO` + - nc_pairs_flag.climo + * - :term:`GRID_STAT_NC_PAIRS_FLAG_CLIMO_CDP` + - nc_pairs_flag.climo_cdp + * - :term:`GRID_STAT_NC_PAIRS_FLAG_WEIGHT` + - nc_pairs_flag.weight + * - :term:`GRID_STAT_NC_PAIRS_FLAG_NBRHD` + - nc_pairs_flag.nbrhd + * - :term:`GRID_STAT_NC_PAIRS_FLAG_FOURIER` + - nc_pairs_flag.fourier + * - :term:`GRID_STAT_NC_PAIRS_FLAG_GRADIENT` + - nc_pairs_flag.gradient + * - :term:`GRID_STAT_NC_PAIRS_FLAG_DISTANCE_MAP` + - nc_pairs_flag.distance_map + * - :term:`GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK` + - nc_pairs_flag.apply_mask + + .. _make_plots_wrapper: MakePlots @@ -2369,6 +2502,9 @@ Configuration | :term:`POINT_STAT_SKIP_IF_OUTPUT_EXISTS` | :term:`POINT_STAT_DESC` | :term:`POINT_STAT_MET_CONFIG_OVERRIDES` +| :term:`POINT_STAT_CLIMO_CDF_BINS` +| :term:`POINT_STAT_CLIMO_CDF_CENTER_BINS` +| :term:`POINT_STAT_CLIMO_CDF_WRITE_BINS` | :term:`FCST_POINT_STAT_WINDOW_BEGIN` (optional) | :term:`FCST_POINT_STAT_WINDOW_END` (optional) | :term:`OBS_POINT_STAT_WINDOW_BEGIN` (optional) @@ -2601,6 +2737,21 @@ Below the file contents are descriptions of each environment variable referenced * - :term:`POINT_STAT_MET_CONFIG_OVERRIDES` - n/a +**${METPLUS_CLIMO_CDF_DICT}** + +.. list-table:: + :widths: 5 5 + :header-rows: 0 + + * - METplus Config(s) + - MET Config File + * - :term:`POINT_STAT_CLIMO_CDF_BINS` + - climo_cdf.cdf_bins + * - :term:`POINT_STAT_CLIMO_CDF_CENTER_BINS` + - climo_cdf.center_bins + * - :term:`POINT_STAT_CLIMO_CDF_WRITE_BINS` + - climo_cdf.write_bins + .. _py_embed_ingest_wrapper: PyEmbedIngest diff --git a/internal_tests/pytests/grid_stat/test_grid_stat_wrapper.py b/internal_tests/pytests/grid_stat/test_grid_stat_wrapper.py index d49a2fc858..9fd939fdcd 100644 --- a/internal_tests/pytests/grid_stat/test_grid_stat_wrapper.py +++ b/internal_tests/pytests/grid_stat/test_grid_stat_wrapper.py @@ -120,6 +120,172 @@ ({'GRID_STAT_OUTPUT_PREFIX': 'my_output_prefix'}, {'METPLUS_OUTPUT_PREFIX': 'output_prefix = "my_output_prefix";'}), + + ({'GRID_STAT_OUTPUT_FLAG_FHO': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {fho = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_CTC': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {ctc = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_CTS': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {cts = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_MCTC': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {mctc = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_MCTS': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {mcts = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_CNT': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {cnt = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_SL1L2': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {sl1l2 = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_SAL1L2': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {sal1l2 = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_VL1L2': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {vl1l2 = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_VAL1L2': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {val1l2 = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_VCNT': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {vcnt = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_PCT': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {pct = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_PSTD': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {pstd = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_PJC': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {pjc = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_PRC': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {prc = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_ECLV': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {eclv = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_NBRCTC': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {nbrctc = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_NBRCTS': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {nbrcts = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_NBRCNT': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {nbrcnt = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_GRAD': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {grad = STAT;}'}), + + ({'GRID_STAT_OUTPUT_FLAG_DMAP': 'STAT', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {dmap = STAT;}'}), + + ({ + 'GRID_STAT_OUTPUT_FLAG_FHO': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_CTC': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_CTS': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_MCTC': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_MCTS': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_CNT': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_SL1L2': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_SAL1L2': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_VL1L2': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_VAL1L2': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_VCNT': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_PCT': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_PSTD': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_PJC': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_PRC': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_ECLV': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_NBRCTC': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_NBRCTS': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_NBRCNT': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_GRAD': 'STAT', + 'GRID_STAT_OUTPUT_FLAG_DMAP': 'STAT', + }, + { + 'METPLUS_OUTPUT_FLAG_DICT': ( + 'output_flag = {fho = STAT;ctc = STAT;cts = STAT;' + 'mctc = STAT;mcts = STAT;cnt = STAT;sl1l2 = STAT;' + 'sal1l2 = STAT;vl1l2 = STAT;val1l2 = STAT;' + 'vcnt = STAT;pct = STAT;pstd = STAT;pjc = STAT;' + 'prc = STAT;eclv = STAT;nbrctc = STAT;nbrcts = STAT;' + 'nbrcnt = STAT;grad = STAT;dmap = STAT;}')}), + + ({'GRID_STAT_NC_PAIRS_FLAG_LATLON': 'TRUE', }, + {'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {latlon = TRUE;}'}), + + ({'GRID_STAT_NC_PAIRS_FLAG_RAW': 'TRUE', }, + {'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {raw = TRUE;}'}), + + ({'GRID_STAT_NC_PAIRS_FLAG_DIFF': 'TRUE', }, + {'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {diff = TRUE;}'}), + + ({'GRID_STAT_NC_PAIRS_FLAG_CLIMO': 'TRUE', }, + {'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {climo = TRUE;}'}), + + ({'GRID_STAT_NC_PAIRS_FLAG_CLIMO_CDP': 'TRUE', }, + { + 'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {climo_cdp = TRUE;}'}), + + ({'GRID_STAT_NC_PAIRS_FLAG_WEIGHT': 'TRUE', }, + {'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {weight = TRUE;}'}), + + ({'GRID_STAT_NC_PAIRS_FLAG_NBRHD': 'TRUE', }, + {'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {nbrhd = TRUE;}'}), + + ({'GRID_STAT_NC_PAIRS_FLAG_FOURIER': 'TRUE', }, + {'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {fourier = TRUE;}'}), + + ({'GRID_STAT_NC_PAIRS_FLAG_GRADIENT': 'TRUE', }, + { + 'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {gradient = TRUE;}'}), + + ({'GRID_STAT_NC_PAIRS_FLAG_DISTANCE_MAP': 'TRUE', }, + { + 'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {distance_map = TRUE;}'}), + + ({'GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK': 'TRUE', }, + { + 'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {apply_mask = TRUE;}'}), + + ({ + 'GRID_STAT_NC_PAIRS_FLAG_LATLON': 'TRUE', + 'GRID_STAT_NC_PAIRS_FLAG_RAW': 'TRUE', + 'GRID_STAT_NC_PAIRS_FLAG_DIFF': 'TRUE', + 'GRID_STAT_NC_PAIRS_FLAG_CLIMO': 'TRUE', + 'GRID_STAT_NC_PAIRS_FLAG_CLIMO_CDP': 'TRUE', + 'GRID_STAT_NC_PAIRS_FLAG_WEIGHT': 'TRUE', + 'GRID_STAT_NC_PAIRS_FLAG_NBRHD': 'TRUE', + 'GRID_STAT_NC_PAIRS_FLAG_FOURIER': 'TRUE', + 'GRID_STAT_NC_PAIRS_FLAG_GRADIENT': 'TRUE', + 'GRID_STAT_NC_PAIRS_FLAG_DISTANCE_MAP': 'TRUE', + 'GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK': 'TRUE', + }, + { + 'METPLUS_NC_PAIRS_FLAG_DICT': 'nc_pairs_flag = {latlon = TRUE;raw = TRUE;diff = TRUE;climo = TRUE;climo_cdp = TRUE;weight = TRUE;nbrhd = TRUE;fourier = TRUE;gradient = TRUE;distance_map = TRUE;apply_mask = TRUE;}'}), + + ({'GRID_STAT_CLIMO_CDF_CDF_BINS': '1', }, + {'METPLUS_CLIMO_CDF_DICT': 'climo_cdf = {cdf_bins = 1.0;}'}), + + ({'GRID_STAT_CLIMO_CDF_CENTER_BINS': 'True', }, + {'METPLUS_CLIMO_CDF_DICT': 'climo_cdf = {center_bins = TRUE;}'}), + + ({'GRID_STAT_CLIMO_CDF_WRITE_BINS': 'False', }, + {'METPLUS_CLIMO_CDF_DICT': 'climo_cdf = {write_bins = FALSE;}'}), + + ({ + 'GRID_STAT_CLIMO_CDF_CDF_BINS': '1', + 'GRID_STAT_CLIMO_CDF_CENTER_BINS': 'True', + 'GRID_STAT_CLIMO_CDF_WRITE_BINS': 'False', + }, + { + 'METPLUS_CLIMO_CDF_DICT': 'climo_cdf = {cdf_bins = 1.0;center_bins = TRUE;write_bins = FALSE;}'}), + ] ) def test_grid_stat_single_field(metplus_config, config_overrides, diff --git a/internal_tests/pytests/point_stat/test_point_stat_wrapper.py b/internal_tests/pytests/point_stat/test_point_stat_wrapper.py index 3e60fe832b..38f8200904 100755 --- a/internal_tests/pytests/point_stat/test_point_stat_wrapper.py +++ b/internal_tests/pytests/point_stat/test_point_stat_wrapper.py @@ -135,6 +135,22 @@ def point_stat_wrapper(metplus_config): 'obs_window = {beg = -2700;end = 2700;}', }), + ({'POINT_STAT_CLIMO_CDF_CDF_BINS': '1', }, + {'METPLUS_CLIMO_CDF_DICT': 'climo_cdf = {cdf_bins = 1.0;}'}), + + ({'POINT_STAT_CLIMO_CDF_CENTER_BINS': 'True', }, + {'METPLUS_CLIMO_CDF_DICT': 'climo_cdf = {center_bins = TRUE;}'}), + + ({'POINT_STAT_CLIMO_CDF_WRITE_BINS': 'False', }, + {'METPLUS_CLIMO_CDF_DICT': 'climo_cdf = {write_bins = FALSE;}'}), + + ({ + 'POINT_STAT_CLIMO_CDF_CDF_BINS': '1', + 'POINT_STAT_CLIMO_CDF_CENTER_BINS': 'True', + 'POINT_STAT_CLIMO_CDF_WRITE_BINS': 'False', + }, + { + 'METPLUS_CLIMO_CDF_DICT': 'climo_cdf = {cdf_bins = 1.0;center_bins = TRUE;write_bins = FALSE;}'}), ] ) diff --git a/metplus/util/__init__.py b/metplus/util/__init__.py index f5c2b7edf6..570ee45208 100644 --- a/metplus/util/__init__.py +++ b/metplus/util/__init__.py @@ -1,4 +1,5 @@ from .metplus_check import * +from .doc_util import * from .config_metplus import * from .time_util import * from .met_util import * diff --git a/metplus/util/doc_util.py b/metplus/util/doc_util.py new file mode 100755 index 0000000000..67ad9dbeef --- /dev/null +++ b/metplus/util/doc_util.py @@ -0,0 +1,240 @@ +#! /usr/bin/env python3 + +import sys +import os + +# dictionary used by get_wrapper_name function to easily convert wrapper +# name in many formats to the correct name of the wrapper class +LOWER_TO_WRAPPER_NAME = {'ascii2nc': 'ASCII2NC', + 'cycloneplotter': 'CyclonePlotter', + 'ensemblestat': 'EnsembleStat', + 'example': 'Example', + 'extracttiles': 'ExtractTiles', + 'gempaktocf': 'GempakToCF', + 'genvxmask': 'GenVxMask', + 'griddiag': 'GridDiag', + 'gridstat': 'GridStat', + 'makeplots': 'MakePlots', + 'mode': 'MODE', + 'mtd': 'MTD', + 'modetimedomain': 'MTD', + 'pb2nc': 'PB2NC', + 'pcpcombine': 'PCPCombine', + 'plotdataplane': 'PlotDataPlane', + 'point2grid': 'Point2Grid', + 'pointtogrid': 'Point2Grid', + 'pointstat': 'PointStat', + 'pyembedingest': 'PyEmbedIngest', + 'regriddataplane': 'RegridDataPlane', + 'seriesanalysis': 'SeriesAnalysis', + 'statanalysis': 'StatAnalysis', + 'tcgen': 'TCGen', + 'tcpairs': 'TCPairs', + 'tcrmw': 'TCRMW', + 'tcstat': 'TCStat', + 'tcmprplotter': 'TCMPRPlotter', + 'usage': 'Usage', + 'userscript': 'UserScript', + } + +def get_wrapper_name(process_name): + """! Determine name of wrapper from string that may not contain the correct + capitalization, i.e. Pcp-Combine translates to PCPCombine + + @param process_name string that was listed in the PROCESS_LIST + @returns name of wrapper (without 'Wrapper' at the end) and None if + name cannot be determined + """ + lower_process = (process_name.replace('-', '') + .replace('_', '') + .replace(' ', '') + .lower()) + if lower_process in LOWER_TO_WRAPPER_NAME.keys(): + return LOWER_TO_WRAPPER_NAME[lower_process] + + return None + + +def print_doc_text(tool_name, met_var, dict_items): + """! Format documentation for adding support for a new MET config variable + through METplus wrappers. + + @param tool_name MET tool name, i.e. grid_stat + @param met_var MET variable name, i.e. output_flag + @param dict_items (optional) list of MET dictionary var items if met_var + is a dictionary + """ + wrapper_caps = tool_name.upper() + met_var_caps = met_var.upper() + env_var_name = f'METPLUS_{met_var_caps}' + + wrapper_camel = get_wrapper_name(wrapper_caps) + + metplus_var = f'{wrapper_caps}_{met_var_caps}' + + metplus_config_names = [] + met_config_values = [] + if not dict_items: + metplus_config_names.append(metplus_var) + met_config_values.append(met_var) + else: + env_var_name = f'{env_var_name}_DICT' + for item_name in dict_items: + item_name_caps = item_name.upper() + metplus_config_name = f'{metplus_var}_{item_name_caps}' + + metplus_config_names.append(metplus_config_name) + met_config_values.append(f"{met_var}.{item_name}") + + print('WARNING: Guidance output from this script may differ slightly ' + 'from the actual steps to take. It is intended to assist the process.' + ' The text that is generated should be reviewed for accuracy before ' + 'adding to codebase.') + + print(f"\nWrapper: {wrapper_camel}") + print(f"MET Variable: {met_var}") + if dict_items: + print(f"Dictionary Items:") + for item in dict_items: + print(f' {item}') + + print('\n==================================================\n') + print(f'\n\nIn the {tool_name}_wrapper.py file, in the {wrapper_camel}Wrapper ' + f'class, add the following to the WRAPPER_ENV_VAR_KEYS class ' + f"variable list:\n\n\n '{env_var_name}',\n\n") + + print('\n==================================================\n') + print(f'In the create_c_dict function for {wrapper_camel}Wrapper, add a ' + 'function call to read the new METplus config variables and save ' + 'the value to be added to the wrapped MET config file.\n\n') + if not dict_items: + print(f" self.add_met_config(name='{met_var}',\n" + " data_type='',\n" + f" metplus_configs=['{metplus_var}'])" + "\n\n\n" + "where can be string, list, int, float, bool, " + "or thresh.\n\n") + else: + print("Typically a function is written to handle MET config dictionary" + " items. Search for functions that start with handle_ in " + "CommandBuilder or other parent class wrappers to see if a " + "function already exists for the item you are adding or to use " + "as an example to write a new one.\n\n") + + print('\n==================================================\n') + print('Add the new variables to the basic use case example for the tool,\n' + f'i.e. parm/use_cases/met_tool_wrapper/{wrapper_camel}/' + f'{wrapper_camel}.conf:\n\n') + for mp_config in metplus_config_names: + print(f'#{mp_config} =') + + print('\n\n==================================================\n') + print(f"In the parm/met_config/{wrapper_camel}Config_wrapped file, " + f"compare the default values set for {met_var} to the version" + f" in share/met/config/{wrapper_camel}Config_default. If " + "they do differ, make sure to add variables to the use case " + "config files so that they produce the same output.\n\n") + print(f"In the parm/met_config/{wrapper_camel}Config_wrapped file, " + "replace:\n\n") + print(f"{met_var} = ...\n\n with:\n\n//{met_var} =" + f"{' {' if dict_items else ''}\n${{{env_var_name}}}\n\n") + + print('\n==================================================\n') + print(f"\n\nIn docs/Users_Guide/wrappers.rst under {wrapper_camel} => " + "METplus Configuration section, add:\n\n") + for metplus_config_name in metplus_config_names: + print(f'| :term:`{metplus_config_name}`') + + print('\n==================================================\n') + print(f"\n\nIn docs/Users_Guide/wrappers.rst under {wrapper_camel} => " + "MET Configuration section, add:\n\n") + var_header = (f"**${{{env_var_name}}}**") + + list_table_text = (f"{var_header}\n\n" + ".. list-table::\n" + " :widths: 5 5\n" + " :header-rows: 0\n\n" + " * - METplus Config(s)\n" + " - MET Config File\n" + ) + + for metplus_config_name, met_config_name in zip(metplus_config_names, met_config_values): + list_table_text += (f" * - :term:`{metplus_config_name}`\n" + f" - {met_config_name}\n" + ) + print(list_table_text) + + print('\n==================================================\n') + print(f"In docs/Users_Guide/glossary.rst, add:\n\n") + for metplus_config_name, met_config_name in zip(metplus_config_names, met_config_values): + glossary_entry = (f" {metplus_config_name}\n" + f" Specify the value for '{met_config_name}' " + f"in the MET configuration file for {wrapper_camel}.\n\n" + f" | *Used by:* {wrapper_camel}") + print(f'{glossary_entry}\n') + + print('\n==================================================\n') + print(f"In internal_tests/pytests/{tool_name}/" + f"test_{tool_name}_wrapper.py, add the following items to " + "the tests to ensure the new items are set properly. Note: " + "if the tool does not have unit tests to check the handling of " + "MET config variables, you will need to add those tests. See " + "grid_stat/test_grid_stat_wrapper.py for an example. Change " + "VALUE to an appropriate value for the variable.\n\n") + + input_dict_items = [] + output_items = [] + for metplus_config_name, met_config_name in zip(metplus_config_names, met_config_values): + if dict_items: + item_name = met_config_name.split('.')[1] + output_item = f"{item_name} = VALUE;" + else: + output_item = 'VALUE;' + mp_config_dict_item = f"'{metplus_config_name}': 'VALUE'," + input_dict_items.append(mp_config_dict_item) + output_items.append(output_item) + if dict_items: + output_fmt = f"{{{output_item}}}" + else: + output_fmt = output_item + + + test_text = (f" ({{{mp_config_dict_item} }},\n" + f" {{'{env_var_name}': '{met_var} = " + f"{output_fmt}'}}),\n") + print(test_text) + + if dict_items: + all_items_text = " ({\n" + for input_dict_item in input_dict_items: + all_items_text += f" {input_dict_item}\n" + all_items_text += (" },\n" + f" {{'{env_var_name}': '{met_var} = {{") + all_items_text += ''.join(output_items) + all_items_text += "}'})," + print(all_items_text) + + +def doc_util_usage(): + """! Print usage statement for script """ + print(f"{__file__} [ 3: + items = ','.join(sys.argv[3:]).split(',') + dict_items = [item.strip() for item in items] + + print_doc_text(tool_name, met_var, dict_items) diff --git a/metplus/util/met_util.py b/metplus/util/met_util.py index 5cb8e2a429..2d8dd27341 100644 --- a/metplus/util/met_util.py +++ b/metplus/util/met_util.py @@ -27,6 +27,7 @@ from .string_template_substitution import parse_template from .string_template_substitution import get_tags from . import time_util as time_util +from .doc_util import get_wrapper_name from .. import get_metplus_version @@ -39,39 +40,6 @@ PYTHON_EMBEDDING_TYPES = ['PYTHON_NUMPY', 'PYTHON_XARRAY', 'PYTHON_PANDAS'] -LOWER_TO_WRAPPER_NAME = {'ascii2nc': 'ASCII2NC', - 'cycloneplotter': 'CyclonePlotter', - 'ensemblestat': 'EnsembleStat', - 'example': 'Example', - 'extracttiles': 'ExtractTiles', - 'gempaktocf': 'GempakToCF', - 'genvxmask': 'GenVxMask', - 'griddiag': 'GridDiag', - 'gridstat': 'GridStat', - 'makeplots': 'MakePlots', - 'mode': 'MODE', - 'mtd': 'MTD', - 'modetimedomain': 'MTD', - 'pb2nc': 'PB2NC', - 'pcpcombine': 'PCPCombine', - 'plotdataplane': 'PlotDataPlane', - 'point2grid': 'Point2Grid', - 'pointtogrid': 'Point2Grid', - 'Point_2_Grid': 'Point2Grid', - 'pointstat': 'PointStat', - 'pyembedingest': 'PyEmbedIngest', - 'regriddataplane': 'RegridDataPlane', - 'seriesanalysis': 'SeriesAnalysis', - 'statanalysis': 'StatAnalysis', - 'tcgen': 'TCGen', - 'tcpairs': 'TCPairs', - 'tcrmw': 'TCRMW', - 'tcstat': 'TCStat', - 'tcmprplotter': 'TCMPRPlotter', - 'usage': 'Usage', - 'userscript': 'UserScript', - } - valid_comparisons = {">=": "ge", ">": "gt", "==": "eq", @@ -1780,23 +1748,6 @@ def get_process_list(config): return out_process_list -def get_wrapper_name(process_name): - """! Determine name of wrapper from string that may not contain the correct - capitalization, i.e. Pcp-Combine translates to PCPCombine - - @param process_name string that was listed in the PROCESS_LIST - @returns name of wrapper (without 'Wrapper' at the end) and None if - name cannot be determined - """ - lower_process = (process_name.replace('-', '') - .replace('_', '') - .replace(' ', '') - .lower()) - if lower_process in LOWER_TO_WRAPPER_NAME.keys(): - return LOWER_TO_WRAPPER_NAME[lower_process] - - return None - # minutes def shift_time(time_str, shift): """ Adjust time by shift hours. Format is %Y%m%d%H%M%S @@ -2659,7 +2610,7 @@ def expand_int_string_to_list(int_string): """! Expand string into a list of integer values. Items are separated by commas. Items that are formatted X-Y will be expanded into each number from X to Y inclusive. If the string ends with +, then add a str '+' - to the end of the list. Used in ci/jobs/run_use_cases.py + to the end of the list. Used in ci/jobs/get_use_case_commands.py @param int_string String containing a comma-separated list of integers @returns List of integers and potentially '+' as the last item diff --git a/metplus/wrappers/command_builder.py b/metplus/wrappers/command_builder.py index 0297121174..ee5348e10d 100755 --- a/metplus/wrappers/command_builder.py +++ b/metplus/wrappers/command_builder.py @@ -2064,7 +2064,13 @@ def handle_met_config_dict(self, dict_name, dict_items, def add_met_config(self, **kwargs): """! Create METConfigInfo object from arguments and process @param kwargs key arguments that should match METConfigInfo - arguments + arguments, which includes the following: + @param name MET config variable name to set + @param data_type type of variable to set, i.e. string, list, bool + @param metplus_configs variables from METplus config that should + be read to get the value. This can be a list of variable names + in order of precedence (first variable is used if it is set, + otherwise 2nd variable is used if set, etc.) """ item = met_config(**kwargs) self.handle_met_config_item(item) diff --git a/metplus/wrappers/compare_gridded_wrapper.py b/metplus/wrappers/compare_gridded_wrapper.py index b0300216a7..aabaa0938a 100755 --- a/metplus/wrappers/compare_gridded_wrapper.py +++ b/metplus/wrappers/compare_gridded_wrapper.py @@ -405,3 +405,28 @@ def get_command(self): cmd += '-outdir {}'.format(self.outdir) return cmd + + def handle_climo_cdf_dict(self): + app_name_upper = self.app_name.upper() + tmp_dict = {} + self.set_met_config_float(tmp_dict, + [f'{app_name_upper}_CLIMO_CDF_BINS', + f'{app_name_upper}_CLIMO_CDF_CDF_BINS'], + 'cdf_bins', + 'CLIMO_CDF_BINS') + self.set_met_config_bool(tmp_dict, + f'{app_name_upper}_CLIMO_CDF_CENTER_BINS', + 'center_bins', + 'CLIMO_CDF_CENTER_BINS') + self.set_met_config_bool(tmp_dict, + f'{app_name_upper}_CLIMO_CDF_WRITE_BINS', + 'write_bins', + 'CLIMO_CDF_WRITE_BINS') + climo_cdf = ( + self.format_met_config_dict(tmp_dict, + 'climo_cdf', + ['CLIMO_CDF_BINS', + 'CLIMO_CDF_CENTER_BINS', + 'CLIMO_CDF_WRITE_BINS']) + ) + self.env_var_dict['METPLUS_CLIMO_CDF_DICT'] = climo_cdf diff --git a/metplus/wrappers/ensemble_stat_wrapper.py b/metplus/wrappers/ensemble_stat_wrapper.py index 485c46b879..69fa693f12 100755 --- a/metplus/wrappers/ensemble_stat_wrapper.py +++ b/metplus/wrappers/ensemble_stat_wrapper.py @@ -387,29 +387,6 @@ def handle_nbrhd_prob_dict(self): ) self.env_var_dict['METPLUS_NBRHD_PROB_DICT'] = nbrhd_prob - def handle_climo_cdf_dict(self): - tmp_dict = {} - self.set_met_config_float(tmp_dict, - 'ENSEMBLE_STAT_CLIMO_CDF_BINS', - 'cdf_bins', - 'CLIMO_CDF_BINS') - self.set_met_config_bool(tmp_dict, - 'ENSEMBLE_STAT_CLIMO_CDF_CENTER_BINS', - 'center_bins', - 'CLIMO_CDF_CENTER_BINS') - self.set_met_config_bool(tmp_dict, - 'ENSEMBLE_STAT_CLIMO_CDF_WRITE_BINS', - 'write_bins', - 'CLIMO_CDF_WRITE_BINS') - climo_cdf = ( - self.format_met_config_dict(tmp_dict, - 'climo_cdf', - ['CLIMO_CDF_BINS', - 'CLIMO_CDF_CENTER_BINS', - 'CLIMO_CDF_WRITE_BINS']) - ) - self.env_var_dict['METPLUS_CLIMO_CDF_DICT'] = climo_cdf - def handle_interp_dict(self): tmp_dict = {} self.set_met_config_string(tmp_dict, diff --git a/metplus/wrappers/grid_stat_wrapper.py b/metplus/wrappers/grid_stat_wrapper.py index f1354d11f8..ff6b64bac9 100755 --- a/metplus/wrappers/grid_stat_wrapper.py +++ b/metplus/wrappers/grid_stat_wrapper.py @@ -39,8 +39,47 @@ class GridStatWrapper(CompareGriddedWrapper): 'METPLUS_NBRHD_WIDTH', 'METPLUS_NBRHD_COV_THRESH', 'METPLUS_OUTPUT_PREFIX', + 'METPLUS_CLIMO_CDF_DICT', + 'METPLUS_OUTPUT_FLAG_DICT', + 'METPLUS_NC_PAIRS_FLAG_DICT', ] + OUTPUT_FLAGS = ['fho', + 'ctc', + 'cts', + 'mctc', + 'mcts', + 'cnt', + 'sl1l2', + 'sal1l2', + 'vl1l2', + 'val1l2', + 'vcnt', + 'pct', + 'pstd', + 'pjc', + 'prc', + 'eclv', + 'nbrctc', + 'nbrcts', + 'nbrcnt', + 'grad', + 'dmap', + ] + + NC_PAIRS_FLAGS = ['latlon', + 'raw', + 'diff', + 'climo', + 'climo_cdp', + 'weight', + 'nbrhd', + 'fourier', + 'gradient', + 'distance_map', + 'apply_mask', + ] + def __init__(self, config, instance=None, config_overrides={}): self.app_name = 'grid_stat' self.app_path = os.path.join(config.getdir('MET_BIN_DIR', ''), @@ -137,6 +176,11 @@ def create_c_dict(self): self.handle_mask(single_value=False, c_dict=c_dict) + self.handle_climo_cdf_dict() + + self.handle_flags('output') + self.handle_flags('nc_pairs') + return c_dict def set_environment_variables(self, time_info): diff --git a/metplus/wrappers/point_stat_wrapper.py b/metplus/wrappers/point_stat_wrapper.py index 80a629b371..5e8ab2f347 100755 --- a/metplus/wrappers/point_stat_wrapper.py +++ b/metplus/wrappers/point_stat_wrapper.py @@ -34,6 +34,7 @@ class PointStatWrapper(CompareGriddedWrapper): 'METPLUS_MASK_POLY', 'METPLUS_MASK_SID', 'METPLUS_OUTPUT_PREFIX', + 'METPLUS_CLIMO_CDF_DICT', ] def __init__(self, config, instance=None, config_overrides={}): @@ -137,6 +138,8 @@ def create_c_dict(self): 'message_type', 'METPLUS_MESSAGE_TYPE',) + self.handle_climo_cdf_dict() + c_dict['OBS_VALID_BEG'] = ( self.config.getraw('config', 'POINT_STAT_OBS_VALID_BEG', '') ) diff --git a/parm/met_config/GridStatConfig_wrapped b/parm/met_config/GridStatConfig_wrapped index 45f9174891..3333a8f332 100644 --- a/parm/met_config/GridStatConfig_wrapped +++ b/parm/met_config/GridStatConfig_wrapped @@ -97,11 +97,8 @@ climo_stdev = { // // May be set separately in each "obs.field" entry // -climo_cdf = { - cdf_bins = 1; - center_bins = FALSE; - write_bins = TRUE; -} +//climo_cdf = { +${METPLUS_CLIMO_CDF_DICT} //////////////////////////////////////////////////////////////////////////////// @@ -196,47 +193,15 @@ distance_map = { // // Statistical output types // -output_flag = { - fho = NONE; - ctc = STAT; - cts = STAT; - mctc = NONE; - mcts = NONE; - cnt = NONE; - sl1l2 = NONE; - sal1l2 = NONE; - vl1l2 = NONE; - val1l2 = NONE; - vcnt = NONE; - pct = NONE; - pstd = NONE; - pjc = NONE; - prc = NONE; - eclv = BOTH; - nbrctc = NONE; - nbrcts = NONE; - nbrcnt = NONE; - grad = BOTH; - dmap = NONE; -} +//output_flag = { +${METPLUS_OUTPUT_FLAG_DICT} // // NetCDF matched pairs output file // May be set separately in each "obs.field" entry // -nc_pairs_flag = { - latlon = FALSE; - raw = FALSE; - diff = FALSE; - climo = FALSE; - climo_cdp = FALSE; - weight = FALSE; - nbrhd = FALSE; - fourier = FALSE; - gradient = FALSE; - distance_map = FALSE; - apply_mask = FALSE; -} +// nc_pairs_flag = { +${METPLUS_NC_PAIRS_FLAG_DICT} //////////////////////////////////////////////////////////////////////////////// diff --git a/parm/met_config/PointStatConfig_wrapped b/parm/met_config/PointStatConfig_wrapped index 2042f7b982..a1b943f0e7 100644 --- a/parm/met_config/PointStatConfig_wrapped +++ b/parm/met_config/PointStatConfig_wrapped @@ -102,11 +102,8 @@ ${METPLUS_CLIMO_STDEV_FILE} // // May be set separately in each "obs.field" entry // -climo_cdf = { - cdf_bins = 1; - center_bins = FALSE; - write_bins = TRUE; -} +//climo_cdf = { +${METPLUS_CLIMO_CDF_DICT} //////////////////////////////////////////////////////////////////////////////// diff --git a/parm/use_cases/met_tool_wrapper/GridStat/GridStat.conf b/parm/use_cases/met_tool_wrapper/GridStat/GridStat.conf index 0d19cc6f68..3d41b6b99b 100644 --- a/parm/use_cases/met_tool_wrapper/GridStat/GridStat.conf +++ b/parm/use_cases/met_tool_wrapper/GridStat/GridStat.conf @@ -132,6 +132,45 @@ OBS_GRID_STAT_PROB_THRESH = ==0.1 GRID_STAT_OUTPUT_PREFIX = {MODEL}_{CURRENT_FCST_NAME}_vs_{OBTYPE}_{CURRENT_OBS_NAME} +#GRID_STAT_CLIMO_CDF_BINS = 1 +#GRID_STAT_CLIMO_CDF_CENTER_BINS = False +#GRID_STAT_CLIMO_CDF_WRITE_BINS = True + +#GRID_STAT_OUTPUT_FLAG_FHO = NONE +GRID_STAT_OUTPUT_FLAG_CTC = STAT +GRID_STAT_OUTPUT_FLAG_CTS = STAT +#GRID_STAT_OUTPUT_FLAG_MCTC = NONE +#GRID_STAT_OUTPUT_FLAG_MCTS = NONE +#GRID_STAT_OUTPUT_FLAG_CNT = NONE +#GRID_STAT_OUTPUT_FLAG_SL1L2 = NONE +#GRID_STAT_OUTPUT_FLAG_SAL1L2 = NONE +#GRID_STAT_OUTPUT_FLAG_VL1L2 = NONE +#GRID_STAT_OUTPUT_FLAG_VAL1L2 = NONE +#GRID_STAT_OUTPUT_FLAG_VCNT = NONE +#GRID_STAT_OUTPUT_FLAG_PCT = NONE +#GRID_STAT_OUTPUT_FLAG_PSTD = NONE +#GRID_STAT_OUTPUT_FLAG_PJC = NONE +#GRID_STAT_OUTPUT_FLAG_PRC = NONE +GRID_STAT_OUTPUT_FLAG_ECLV = BOTH +#GRID_STAT_OUTPUT_FLAG_NBRCTC = NONE +#GRID_STAT_OUTPUT_FLAG_NBRCTS = NONE +#GRID_STAT_OUTPUT_FLAG_NBRCNT = NONE +GRID_STAT_OUTPUT_FLAG_GRAD = BOTH +#GRID_STAT_OUTPUT_FLAG_DMAP = NONE + +GRID_STAT_NC_PAIRS_FLAG_LATLON = FALSE +GRID_STAT_NC_PAIRS_FLAG_RAW = FALSE +GRID_STAT_NC_PAIRS_FLAG_DIFF = FALSE +GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE +#GRID_STAT_NC_PAIRS_FLAG_CLIMO_CDP = FALSE +#GRID_STAT_NC_PAIRS_FLAG_WEIGHT = FALSE +#GRID_STAT_NC_PAIRS_FLAG_NBRHD = FALSE +#GRID_STAT_NC_PAIRS_FLAG_FOURIER = FALSE +#GRID_STAT_NC_PAIRS_FLAG_GRADIENT = FALSE +#GRID_STAT_NC_PAIRS_FLAG_DISTANCE_MAP = FALSE +GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE + + # End of [config] section and start of [dir] section [dir] diff --git a/parm/use_cases/met_tool_wrapper/GridStat/GridStat_python_embedding.conf b/parm/use_cases/met_tool_wrapper/GridStat/GridStat_python_embedding.conf index 8440b6ef78..156d0509f7 100644 --- a/parm/use_cases/met_tool_wrapper/GridStat/GridStat_python_embedding.conf +++ b/parm/use_cases/met_tool_wrapper/GridStat/GridStat_python_embedding.conf @@ -115,6 +115,18 @@ OBS_GRID_STAT_PROB_THRESH = ==0.1 GRID_STAT_OUTPUT_PREFIX = {MODEL}_vs_{OBTYPE} +GRID_STAT_OUTPUT_FLAG_CTC = STAT +GRID_STAT_OUTPUT_FLAG_CTS = STAT +GRID_STAT_OUTPUT_FLAG_ECLV = BOTH +GRID_STAT_OUTPUT_FLAG_GRAD = BOTH + +GRID_STAT_NC_PAIRS_FLAG_LATLON = FALSE +GRID_STAT_NC_PAIRS_FLAG_RAW = FALSE +GRID_STAT_NC_PAIRS_FLAG_DIFF = FALSE +GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE +GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE + + # End of [config] section and start of [dir] section [dir] diff --git a/parm/use_cases/met_tool_wrapper/PointStat/PointStat.conf b/parm/use_cases/met_tool_wrapper/PointStat/PointStat.conf index 25b193aee1..fe60e962b6 100644 --- a/parm/use_cases/met_tool_wrapper/PointStat/PointStat.conf +++ b/parm/use_cases/met_tool_wrapper/PointStat/PointStat.conf @@ -50,6 +50,10 @@ LOOP_ORDER = processes # or the value of the environment variable METPLUS_PARM_BASE if set POINT_STAT_CONFIG_FILE ={PARM_BASE}/met_config/PointStatConfig_wrapped +#POINT_STAT_CLIMO_CDF_BINS = 1 +#POINT_STAT_CLIMO_CDF_CENTER_BINS = False +#POINT_STAT_CLIMO_CDF_WRITE_BINS = True + # Time relative to each input file's valid time (in seconds if no units are specified) for data within the file to be # considered valid. Values are set in the 'obs_window' dictionary in the PointStat config file OBS_POINT_STAT_WINDOW_BEGIN = -5400 diff --git a/parm/use_cases/model_applications/climate/GridStat_fcstCESM_obsGFS_ConusTemp.conf b/parm/use_cases/model_applications/climate/GridStat_fcstCESM_obsGFS_ConusTemp.conf index 60731e7dda..f4997cd44d 100644 --- a/parm/use_cases/model_applications/climate/GridStat_fcstCESM_obsGFS_ConusTemp.conf +++ b/parm/use_cases/model_applications/climate/GridStat_fcstCESM_obsGFS_ConusTemp.conf @@ -80,7 +80,19 @@ OBS_IS_PROB = false # Output prefix set in grid_stat config file GRID_STAT_OUTPUT_PREFIX={MODEL}_{CURRENT_OBS_NAME}_vs_{OBTYPE} -GRID_STAT_MET_CONFIG_OVERRIDES = interp = { field = NONE;} output_flag = {cnt = STAT; sl1l2 = STAT;eclv = NONE;grad = NONE; } +GRID_STAT_OUTPUT_FLAG_CTC = STAT +GRID_STAT_OUTPUT_FLAG_CTS = STAT +GRID_STAT_OUTPUT_FLAG_CNT = STAT +GRID_STAT_OUTPUT_FLAG_SL1L2 = STAT + + +GRID_STAT_NC_PAIRS_FLAG_LATLON = FALSE +GRID_STAT_NC_PAIRS_FLAG_RAW = FALSE +GRID_STAT_NC_PAIRS_FLAG_DIFF = FALSE +GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE +GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE + +GRID_STAT_MET_CONFIG_OVERRIDES = interp = { field = NONE;} # End of [config] section and start of [dir] section [dir] diff --git a/parm/use_cases/model_applications/convection_allowing_models/GridStat_fcstFV3_obsGOES_BrightnessTempDmap.conf b/parm/use_cases/model_applications/convection_allowing_models/GridStat_fcstFV3_obsGOES_BrightnessTempDmap.conf index b7c0b936e0..aab5494da8 100644 --- a/parm/use_cases/model_applications/convection_allowing_models/GridStat_fcstFV3_obsGOES_BrightnessTempDmap.conf +++ b/parm/use_cases/model_applications/convection_allowing_models/GridStat_fcstFV3_obsGOES_BrightnessTempDmap.conf @@ -40,7 +40,11 @@ GRID_STAT_CONFIG_FILE = {PARM_BASE}/met_config/GridStatConfig_wrapped GRID_STAT_OUTPUT_PREFIX = FV3_core_{instance} -GRID_STAT_MET_CONFIG_OVERRIDES = output_flag = {ctc = NONE;cts = NONE; eclv = NONE;grad = NONE;dmap = BOTH;} nc_pairs_flag = {latlon = TRUE;raw = TRUE;diff = TRUE; distance_map = TRUE; apply_mask = TRUE;} +GRID_STAT_OUTPUT_FLAG_DMAP = BOTH + +GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE +GRID_STAT_NC_PAIRS_FLAG_DISTANCE_MAP = TRUE + [dir] # Input and Output Diretory of the object data diff --git a/parm/use_cases/model_applications/convection_allowing_models/GridStat_fcstHRRR_obsPracPerfect_SurrogateSevere.conf b/parm/use_cases/model_applications/convection_allowing_models/GridStat_fcstHRRR_obsPracPerfect_SurrogateSevere.conf index a9b98a8377..27aff82488 100644 --- a/parm/use_cases/model_applications/convection_allowing_models/GridStat_fcstHRRR_obsPracPerfect_SurrogateSevere.conf +++ b/parm/use_cases/model_applications/convection_allowing_models/GridStat_fcstHRRR_obsPracPerfect_SurrogateSevere.conf @@ -80,7 +80,14 @@ FCST_IS_PROB = false GRID_STAT_CONFIG_FILE = {PARM_BASE}/met_config/GridStatConfig_wrapped -GRID_STAT_MET_CONFIG_OVERRIDES = output_flag = {ctc = BOTH; cts = BOTH; eclv = NONE; grad = NONE; } +GRID_STAT_OUTPUT_FLAG_CTC = BOTH +GRID_STAT_OUTPUT_FLAG_CTS = BOTH + +GRID_STAT_NC_PAIRS_FLAG_LATLON = FALSE +GRID_STAT_NC_PAIRS_FLAG_RAW = FALSE +GRID_STAT_NC_PAIRS_FLAG_DIFF = FALSE +GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE +GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE [dir] diff --git a/parm/use_cases/model_applications/convection_allowing_models/GridStat_fcstHRRR_obsPracPerfect_SurrogateSevereProb.conf b/parm/use_cases/model_applications/convection_allowing_models/GridStat_fcstHRRR_obsPracPerfect_SurrogateSevereProb.conf index 8337c3952b..c066d23743 100644 --- a/parm/use_cases/model_applications/convection_allowing_models/GridStat_fcstHRRR_obsPracPerfect_SurrogateSevereProb.conf +++ b/parm/use_cases/model_applications/convection_allowing_models/GridStat_fcstHRRR_obsPracPerfect_SurrogateSevereProb.conf @@ -83,7 +83,17 @@ FCST_GRID_STAT_INPUT_DATATYPE = NETCDF GRID_STAT_CONFIG_FILE = {PARM_BASE}/met_config/GridStatConfig_wrapped -GRID_STAT_MET_CONFIG_OVERRIDES = output_flag = {ctc = NONE; cts = NONE; pct = BOTH; pstd = BOTH; pjc = BOTH; prc = BOTH; eclv = NONE; grad = NONE; } +GRID_STAT_OUTPUT_FLAG_PCT = BOTH +GRID_STAT_OUTPUT_FLAG_PSTD = BOTH +GRID_STAT_OUTPUT_FLAG_PJC = BOTH +GRID_STAT_OUTPUT_FLAG_PRC = BOTH + +GRID_STAT_NC_PAIRS_FLAG_LATLON = FALSE +GRID_STAT_NC_PAIRS_FLAG_RAW = FALSE +GRID_STAT_NC_PAIRS_FLAG_DIFF = FALSE +GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE +GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE + [dir] # input and output data directories for each application in PROCESS_LIST diff --git a/parm/use_cases/model_applications/convection_allowing_models/MODE_fcstFV3_obsGOES_BrightnessTempObjs.conf b/parm/use_cases/model_applications/convection_allowing_models/MODE_fcstFV3_obsGOES_BrightnessTempObjs.conf index 60d9efb9d0..0196ca8227 100644 --- a/parm/use_cases/model_applications/convection_allowing_models/MODE_fcstFV3_obsGOES_BrightnessTempObjs.conf +++ b/parm/use_cases/model_applications/convection_allowing_models/MODE_fcstFV3_obsGOES_BrightnessTempObjs.conf @@ -75,7 +75,10 @@ GRID_STAT_NEIGHBORHOOD_SHAPE = SQUARE GRID_STAT_CONFIG_FILE = {PARM_BASE}/met_config/GridStatConfig_wrapped -GRID_STAT_MET_CONFIG_OVERRIDES = output_flag = {ctc = NONE;cts = NONE; eclv = NONE; nbrctc = BOTH; grad = NONE; dmap = BOTH; } nc_pairs_flag = {latlon = TRUE; raw = TRUE; diff = TRUE; distance_map = TRUE; apply_mask = TRUE;} +GRID_STAT_OUTPUT_FLAG_NBRCTC = BOTH +GRID_STAT_OUTPUT_FLAG_DMAP = BOTH + +GRID_STAT_NC_PAIRS_FLAG_DISTANCE_MAP = TRUE GRID_STAT_OUTPUT_PREFIX = FV3_core_{instance} diff --git a/parm/use_cases/model_applications/cryosphere/GridStat_MODE_fcstIMS_obsNCEP_sea_ice.conf b/parm/use_cases/model_applications/cryosphere/GridStat_MODE_fcstIMS_obsNCEP_sea_ice.conf index bff936218e..d604f85b40 100644 --- a/parm/use_cases/model_applications/cryosphere/GridStat_MODE_fcstIMS_obsNCEP_sea_ice.conf +++ b/parm/use_cases/model_applications/cryosphere/GridStat_MODE_fcstIMS_obsNCEP_sea_ice.conf @@ -68,8 +68,16 @@ GRID_STAT_MASK_GRID = #GRID_STAT_MASK_POLY = -GRID_STAT_MET_CONFIG_OVERRIDES = output_flag = {fho = STAT; cnt = STAT;sl1l2 = STAT;pct = STAT;pstd = STAT;eclv = NONE; nbrcnt = STAT; grad = NONE; } nc_pairs_flag = {latlon = TRUE; raw = TRUE; diff = TRUE; climo = TRUE; weight = FALSE; nbrhd = TRUE; fourier = FALSE; gradient = FALSE; apply_mask = TRUE;} - +GRID_STAT_OUTPUT_FLAG_CTC = STAT +GRID_STAT_OUTPUT_FLAG_CTS = STAT +GRID_STAT_OUTPUT_FLAG_FHO = STAT +GRID_STAT_OUTPUT_FLAG_CNT = STAT +GRID_STAT_OUTPUT_FLAG_SL1L2 = STAT +GRID_STAT_OUTPUT_FLAG_PCT = STAT +GRID_STAT_OUTPUT_FLAG_PSTD = STAT +GRID_STAT_OUTPUT_FLAG_NBRCNT = STAT + +GRID_STAT_NC_PAIRS_FLAG_NBRHD = TRUE #################################################################################################### # MODE Configurations diff --git a/parm/use_cases/model_applications/medium_range/GridStat_fcstGFS_obsGFS_Sfc_MultiField.conf b/parm/use_cases/model_applications/medium_range/GridStat_fcstGFS_obsGFS_Sfc_MultiField.conf index f2190d83a8..2fffb058c0 100644 --- a/parm/use_cases/model_applications/medium_range/GridStat_fcstGFS_obsGFS_Sfc_MultiField.conf +++ b/parm/use_cases/model_applications/medium_range/GridStat_fcstGFS_obsGFS_Sfc_MultiField.conf @@ -129,7 +129,17 @@ GRID_STAT_MASK_POLY = {INPUT_BASE}/model_applications/medium_range/poly/NHX.nc, {INPUT_BASE}/model_applications/medium_range/poly/CAM.nc, {INPUT_BASE}/model_applications/medium_range/poly/NSA.nc -GRID_STAT_MET_CONFIG_OVERRIDES = climo_mean = { regrid = { method = BILIN;width = 2;} day_interval = 1; time_interp_method = NEAREST;} output_flag = { ctc = NONE; cts = NONE; sl1l2 = STAT; eclv = NONE; grad = NONE;} grid_weight_flag = COS_LAT; climo_cdf = { write_bins=FALSE;} +GRID_STAT_CLIMO_CDF_WRITE_BINS = False + +GRID_STAT_OUTPUT_FLAG_SL1L2 = STAT + +GRID_STAT_NC_PAIRS_FLAG_LATLON = FALSE +GRID_STAT_NC_PAIRS_FLAG_RAW = FALSE +GRID_STAT_NC_PAIRS_FLAG_DIFF = FALSE +GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE +GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE + +GRID_STAT_MET_CONFIG_OVERRIDES = climo_mean = { regrid = { method = BILIN;width = 2;} day_interval = 1; time_interp_method = NEAREST;} grid_weight_flag = COS_LAT; # variables to describe format of forecast data FCST_IS_PROB = false diff --git a/parm/use_cases/model_applications/medium_range/GridStat_fcstGFS_obsGFS_climoNCEP_MultiField.conf b/parm/use_cases/model_applications/medium_range/GridStat_fcstGFS_obsGFS_climoNCEP_MultiField.conf index ebb6ff6091..4ee8ccafc4 100644 --- a/parm/use_cases/model_applications/medium_range/GridStat_fcstGFS_obsGFS_climoNCEP_MultiField.conf +++ b/parm/use_cases/model_applications/medium_range/GridStat_fcstGFS_obsGFS_climoNCEP_MultiField.conf @@ -59,7 +59,18 @@ GRID_STAT_REGRID_WIDTH = 2 GRID_STAT_MASK_GRID = FULL GRID_STAT_MASK_POLY = {INPUT_BASE}/model_applications/medium_range/poly/NHX.nc, {INPUT_BASE}/model_applications/medium_range/poly/SHX.nc, {INPUT_BASE}/model_applications/medium_range/poly/TRO.nc, {INPUT_BASE}/model_applications/medium_range/poly/PNA.nc -GRID_STAT_MET_CONFIG_OVERRIDES = climo_mean = { regrid = { method = BILIN;width = 2;} day_interval = 1; time_interp_method = NEAREST;} climo_cdf = { write_bins=FALSE;} output_flag = { ctc = NONE; cts = NONE; sal1l2 = STAT; val1l2 = STAT; eclv = NONE; grad = NONE;} grid_weight_flag = COS_LAT; climo_mean = fcst; +GRID_STAT_CLIMO_CDF_WRITE_BINS = False + +GRID_STAT_OUTPUT_FLAG_SAL1L2 = STAT +GRID_STAT_OUTPUT_FLAG_VAL1L2 = STAT + +GRID_STAT_NC_PAIRS_FLAG_LATLON = FALSE +GRID_STAT_NC_PAIRS_FLAG_RAW = FALSE +GRID_STAT_NC_PAIRS_FLAG_DIFF = FALSE +GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE +GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE + +GRID_STAT_MET_CONFIG_OVERRIDES = climo_mean = { regrid = { method = BILIN;width = 2;} day_interval = 1; time_interp_method = NEAREST;} grid_weight_flag = COS_LAT; climo_mean = fcst; # variables to describe format of forecast data FCST_IS_PROB = false diff --git a/parm/use_cases/model_applications/precipitation/GridStat_fcstGFS_obsCCPA_GRIB.conf b/parm/use_cases/model_applications/precipitation/GridStat_fcstGFS_obsCCPA_GRIB.conf index af0a4dc5b7..b4a7d9a045 100644 --- a/parm/use_cases/model_applications/precipitation/GridStat_fcstGFS_obsCCPA_GRIB.conf +++ b/parm/use_cases/model_applications/precipitation/GridStat_fcstGFS_obsCCPA_GRIB.conf @@ -56,7 +56,17 @@ GRID_STAT_OUTPUT_PREFIX = {MODEL}_{CURRENT_FCST_NAME}_vs_{OBTYPE}_{CURRENT_OBS_N GRID_STAT_MASK_POLY = {INPUT_BASE}/model_applications/precipitation/poly/CONUS.nc, {INPUT_BASE}/model_applications/precipitation/poly/EAST.nc, {INPUT_BASE}/model_applications/precipitation/poly/WEST.nc -GRID_STAT_MET_CONFIG_OVERRIDES = climo_mean = { regrid = { method = BILIN;width = 2;} time_interp_method = NEAREST;} climo_cdf = { write_bins=FALSE;} output_flag = { cts = NONE; eclv = NONE; grad = NONE;} +GRID_STAT_CLIMO_CDF_WRITE_BINS = False + +GRID_STAT_OUTPUT_FLAG_CTC = STAT + +GRID_STAT_NC_PAIRS_FLAG_LATLON = FALSE +GRID_STAT_NC_PAIRS_FLAG_RAW = FALSE +GRID_STAT_NC_PAIRS_FLAG_DIFF = FALSE +GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE +GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE + +GRID_STAT_MET_CONFIG_OVERRIDES = climo_mean = { regrid = { method = BILIN;width = 2;} time_interp_method = NEAREST;} # Forecast data description variables FCST_PCP_COMBINE_INPUT_DATATYPE = GRIB diff --git a/parm/use_cases/model_applications/precipitation/GridStat_fcstHREFmean_obsStgIV_Gempak.conf b/parm/use_cases/model_applications/precipitation/GridStat_fcstHREFmean_obsStgIV_Gempak.conf index 250a7e5134..eaec762c96 100644 --- a/parm/use_cases/model_applications/precipitation/GridStat_fcstHREFmean_obsStgIV_Gempak.conf +++ b/parm/use_cases/model_applications/precipitation/GridStat_fcstHREFmean_obsStgIV_Gempak.conf @@ -71,7 +71,16 @@ GRID_STAT_NEIGHBORHOOD_WIDTH = 3, 7, 15 GRID_STAT_NEIGHBORHOOD_SHAPE = SQUARE GRID_STAT_NEIGHBORHOOD_COV_THRESH = >=0.5 -GRID_STAT_MET_CONFIG_OVERRIDES = output_flag = { eclv = NONE; grad = NONE;dmap = STAT; } nc_pairs_flag = {distance_map = TRUE;} +GRID_STAT_OUTPUT_FLAG_CTC = STAT +GRID_STAT_OUTPUT_FLAG_CTS = STAT +GRID_STAT_OUTPUT_FLAG_DMAP = STAT + +GRID_STAT_NC_PAIRS_FLAG_LATLON = FALSE +GRID_STAT_NC_PAIRS_FLAG_RAW = FALSE +GRID_STAT_NC_PAIRS_FLAG_DIFF = FALSE +GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE +GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE +GRID_STAT_NC_PAIRS_FLAG_DISTANCE_MAP = TRUE # HREF Mean Model Options: diff --git a/parm/use_cases/model_applications/precipitation/GridStat_fcstHREFmean_obsStgIV_NetCDF.conf b/parm/use_cases/model_applications/precipitation/GridStat_fcstHREFmean_obsStgIV_NetCDF.conf index db6ccd9f22..42dc08c726 100644 --- a/parm/use_cases/model_applications/precipitation/GridStat_fcstHREFmean_obsStgIV_NetCDF.conf +++ b/parm/use_cases/model_applications/precipitation/GridStat_fcstHREFmean_obsStgIV_NetCDF.conf @@ -78,7 +78,16 @@ GRID_STAT_NEIGHBORHOOD_WIDTH = 3, 7, 15 GRID_STAT_NEIGHBORHOOD_SHAPE = SQUARE GRID_STAT_NEIGHBORHOOD_COV_THRESH = >=0.5 -GRID_STAT_MET_CONFIG_OVERRIDES = output_flag = { eclv = NONE; grad = NONE;dmap = STAT; } nc_pairs_flag = {distance_map = TRUE;} +GRID_STAT_OUTPUT_FLAG_CTC = STAT +GRID_STAT_OUTPUT_FLAG_CTS = STAT +GRID_STAT_OUTPUT_FLAG_DMAP = STAT + +GRID_STAT_NC_PAIRS_FLAG_LATLON = FALSE +GRID_STAT_NC_PAIRS_FLAG_RAW = FALSE +GRID_STAT_NC_PAIRS_FLAG_DIFF = FALSE +GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE +GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE +GRID_STAT_NC_PAIRS_FLAG_DISTANCE_MAP = TRUE # HREF Mean Model Options: diff --git a/parm/use_cases/model_applications/precipitation/GridStat_fcstHRRR-TLE_obsStgIV_GRIB.conf b/parm/use_cases/model_applications/precipitation/GridStat_fcstHRRR-TLE_obsStgIV_GRIB.conf index 16f55cde37..8a78237763 100644 --- a/parm/use_cases/model_applications/precipitation/GridStat_fcstHRRR-TLE_obsStgIV_GRIB.conf +++ b/parm/use_cases/model_applications/precipitation/GridStat_fcstHRRR-TLE_obsStgIV_GRIB.conf @@ -66,8 +66,17 @@ GRID_STAT_OUTPUT_PREFIX = PROB_{MODEL}_{CURRENT_FCST_NAME}_vs_{OBTYPE}_{CURRENT_ GRID_STAT_MASK_GRID = -GRID_STAT_MET_CONFIG_OVERRIDES = output_flag = {ctc = NONE; cts = NONE; pct = BOTH; pstd = BOTH; pjc = BOTH; prc = BOTH; eclv = STAT; grad = NONE;} - +GRID_STAT_OUTPUT_FLAG_PCT = BOTH +GRID_STAT_OUTPUT_FLAG_PSTD = BOTH +GRID_STAT_OUTPUT_FLAG_PJC = BOTH +GRID_STAT_OUTPUT_FLAG_PRC = BOTH +GRID_STAT_OUTPUT_FLAG_ECLV = STAT + +GRID_STAT_NC_PAIRS_FLAG_LATLON = FALSE +GRID_STAT_NC_PAIRS_FLAG_RAW = FALSE +GRID_STAT_NC_PAIRS_FLAG_DIFF = FALSE +GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE +GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE # PHPT Model Options: diff --git a/parm/use_cases/model_applications/s2s/GridStat_SeriesAnalysis_fcstNMME_obsCPC_seasonal_forecast.conf b/parm/use_cases/model_applications/s2s/GridStat_SeriesAnalysis_fcstNMME_obsCPC_seasonal_forecast.conf index 418a1f9b80..c351b7efe7 100644 --- a/parm/use_cases/model_applications/s2s/GridStat_SeriesAnalysis_fcstNMME_obsCPC_seasonal_forecast.conf +++ b/parm/use_cases/model_applications/s2s/GridStat_SeriesAnalysis_fcstNMME_obsCPC_seasonal_forecast.conf @@ -53,8 +53,14 @@ OBTYPE = CPC # location of grid_stat MET config file GRID_STAT_CONFIG_FILE = {PARM_BASE}/met_config/GridStatConfig_wrapped +GRID_STAT_OUTPUT_FLAG_CTC = STAT +GRID_STAT_OUTPUT_FLAG_CNT = STAT +GRID_STAT_OUTPUT_FLAG_SL1L2 = STAT + +GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE + # Override MET config file settings -GRID_STAT_MET_CONFIG_OVERRIDES = nc_pairs_flag = {latlon=TRUE; raw=TRUE; diff=TRUE; climo=TRUE;} output_flag = {cts=NONE; cnt=STAT; sl1l2=STAT; eclv=NONE; grad=NONE;} nc_pairs_var_name = "precip"; +GRID_STAT_MET_CONFIG_OVERRIDES = nc_pairs_var_name = "precip"; # variables to describe format of forecast data FCST_IS_PROB = false diff --git a/parm/use_cases/model_applications/space_weather/GridStat_fcstGloTEC_obsGloTEC_vx7.conf b/parm/use_cases/model_applications/space_weather/GridStat_fcstGloTEC_obsGloTEC_vx7.conf index c368e4dbb5..c0b106b956 100644 --- a/parm/use_cases/model_applications/space_weather/GridStat_fcstGloTEC_obsGloTEC_vx7.conf +++ b/parm/use_cases/model_applications/space_weather/GridStat_fcstGloTEC_obsGloTEC_vx7.conf @@ -88,7 +88,17 @@ LOOP_ORDER = times GRID_STAT_CONFIG_FILE = {PARM_BASE}/met_config/GridStatConfig_wrapped # Override MET config file settings for this use case -GRID_STAT_MET_CONFIG_OVERRIDES = nc_pairs_flag = {latlon=TRUE; raw=TRUE; diff=TRUE;climo = FALSE;weight = FALSE; nbrhd = FALSE; fourier = FALSE;gradient = FALSE; apply_mask = FALSE;} output_flag = {mctc=STAT; mcts=STAT; cnt=STAT; sl1l2=STAT; eclv=NONE; grad=NONE;} file_type = NETCDF_NCCF; +GRID_STAT_MET_CONFIG_OVERRIDES = file_type = NETCDF_NCCF; + +GRID_STAT_OUTPUT_FLAG_CTC = STAT +GRID_STAT_OUTPUT_FLAG_CTS = STAT +GRID_STAT_OUTPUT_FLAG_MCTC = STAT +GRID_STAT_OUTPUT_FLAG_MCTS = STAT +GRID_STAT_OUTPUT_FLAG_CNT = STAT +GRID_STAT_OUTPUT_FLAG_SL1L2 = STAT + +GRID_STAT_NC_PAIRS_FLAG_CLIMO = FALSE +GRID_STAT_NC_PAIRS_FLAG_APPLY_MASK = FALSE # Name to identify model (forecast) data in output MODEL = GloTEC_without_cosmic