diff --git a/.github/jobs/get_use_case_commands.py b/.github/jobs/get_use_case_commands.py index 2324e8cd62..70381d135d 100755 --- a/.github/jobs/get_use_case_commands.py +++ b/.github/jobs/get_use_case_commands.py @@ -78,6 +78,7 @@ def handle_automation_env(host_name, reqs, work_dir): f'cd {METPLUS_DOCKER_LOC};' f'{work_dir}/manage_externals/checkout_externals' f' -e {work_dir}/.github/parm/Externals_metdatadb.cfg;' + f'{python_path} -m pip install {METPLUS_DOCKER_LOC}/../METdatadb;' 'cd -;' ) diff --git a/.github/jobs/run_diff_docker.py b/.github/jobs/run_diff_docker.py index fee43a9d48..c6c3937434 100755 --- a/.github/jobs/run_diff_docker.py +++ b/.github/jobs/run_diff_docker.py @@ -80,7 +80,6 @@ def copy_to_diff_dir(file_path, data_type): 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: diff --git a/.github/parm/use_case_groups.json b/.github/parm/use_case_groups.json index 125d2b9f44..ef1ae2ffec 100644 --- a/.github/parm/use_case_groups.json +++ b/.github/parm/use_case_groups.json @@ -121,7 +121,12 @@ }, { "category": "s2s", - "index_list": "6", + "index_list": "6-7", + "new": false + }, + { + "category": "s2s", + "index_list": "8-9", "new": false }, { diff --git a/ci/util/diff_util.py b/ci/util/diff_util.py index c4fa7aa63d..e894bbda04 100644 --- a/ci/util/diff_util.py +++ b/ci/util/diff_util.py @@ -117,13 +117,14 @@ def compare_dir(dir_a, dir_b, debug=False, save_diff=False): print(f"ERROR: File does not exist: {filepath_a}") diff_files.append(('', filepath_b, 'file not found (new output)', '')) - print("\nSummary:\n") + print("\n\n**************************************************\nSummary:\n") if diff_files: print("\nERROR: Some differences were found") for filepath_a, filepath_b, reason, diff_file in diff_files: print(f"{reason}\n A:{filepath_a}\n B:{filepath_b}") if diff_file: print(f"Difference file: {diff_file}") + print() else: print("\nNo differences found in any files") diff --git a/docs/Contributors_Guide/coding_standards.rst b/docs/Contributors_Guide/coding_standards.rst index 06cdfe4326..675880c0fe 100644 --- a/docs/Contributors_Guide/coding_standards.rst +++ b/docs/Contributors_Guide/coding_standards.rst @@ -13,3 +13,82 @@ Coding Standards docstrings `_, and `Sphinx `_ for documentation * **NOTE: Please do not use f-strings in the run_metplus.py file so that the Python version check can notify the user of the incorrect version. Using Python 3.5 or earlier will output the SyntaxError from the f-string instead of the useful error message.** + +Python code analysis tools +-------------------------- + +Static: + +pylint +^^^^^^ + +:: + pip install pylint or conda install pylint + + pylint.org + + checks for code errors + + pylint pep8 code-to-analyze will check for errors as well as PEP-8 style code + +pyflakes +^^^^^^^^ + +:: + + pip install pyflakes or conda install pyflakes + + parses code rather than importing code, therefore OK to use on modules with side-effects + + checks for code errors + + faster than pylint + + https://pypi.python.org/pypi/pyflakes + + flake8 is wrapper to pyflakes, performs PEP-8 style checking in addition to error checking + http://flake8.pycqa.org/en/latest/index.html#quickstart + +vulture +^^^^^^^ + +:: + + checks for unused imports, variables, methods, classed ie "dead code" + + pip install vulture or conda install vulture + + https://pypi.python.org/pypi/vulture + +Dynamic (run-time): + + +cpde-coverage analysis +^^^^^^^^^^^^^^^^^^^^^^ + +Useful when running unit tests to determine whether tests are executing all possible branches, loops, etc. + +figleaf +^^^^^^^ + +http://darcs.idyll.org/~t/projects/figleaf/doc/ +Checking for God objects and God methods: + +(from Chapter 4 of "How to Make Mistakes in Python", Mike Pirnat) + +:: + + find . -name "*.py" -exec wc -l {} \; | sort -g -r + for all Python source files, order by size + anything over 1000 lines is worth investigating (as general rule of thumb) + + grep "^class " mybigmodule.py |wc -l + counts the number of classes defined in mybigmodule.py + + grep "\sdef " mybigmodule.py |wc -l + + counts the number of methods defined within a class or other function (ie at some level of indentation) in mybigmodule.py + + try this if the above doesn't work: grep "def " mybigmodule.py |wc -l + + A high ratio of methods to classes warrants investigation (what constitutes a high ratio- 10:1, 5:1???) diff --git a/docs/Contributors_Guide/github_workflow.rst b/docs/Contributors_Guide/github_workflow.rst index d9f3fb839e..a2c632aab2 100644 --- a/docs/Contributors_Guide/github_workflow.rst +++ b/docs/Contributors_Guide/github_workflow.rst @@ -12,6 +12,9 @@ that described in where new or updated code is created on a 'feature' branch that is based on the `NCAR/METplus GitHub 'develop' branch `_. +From James McCreight (WRF-Hydro team) this is a good write-up on +`best practices for collaboration on GitHub `_ + The feature branch is named after the corresponding GitHub issue: *feature__* diff --git a/docs/Release_Guide/metplus_development.rst b/docs/Release_Guide/metplus_development.rst index ef345fbc18..2bd5843439 100644 --- a/docs/Release_Guide/metplus_development.rst +++ b/docs/Release_Guide/metplus_development.rst @@ -14,6 +14,6 @@ Create a new vX.Y.Z-betaN or vX.Y.Z-rcN development release from the develop bra .. include:: release_steps/merge_release_issue.rst .. include:: release_steps/create_release_on_github.rst .. include:: release_steps/metplus/create_release_extra.rst +.. include:: release_steps/metplus/update_version_on_develop.rst .. include:: release_steps/metplus/update_dtc_website.rst .. include:: release_steps/finalize_release_on_github_development.rst -.. include:: release_steps/metplus/update_version_on_develop.rst diff --git a/docs/Release_Guide/metplus_official.rst b/docs/Release_Guide/metplus_official.rst index 6032298019..bfdc4a4d53 100644 --- a/docs/Release_Guide/metplus_official.rst +++ b/docs/Release_Guide/metplus_official.rst @@ -9,11 +9,11 @@ Create a new vX.Y.Z official release from the develop branch. .. include:: release_steps/clone_project_repository.rst .. include:: release_steps/checkout_develop_branch.rst .. include:: release_steps/create_release_feature_branch.rst -.. include:: release_steps/metplus/update_version_official.rst +.. include:: release_steps/metplus/update_release_date.rst .. include:: release_steps/update_release_notes_official.rst .. include:: release_steps/rotate_authorship.rst .. include:: release_steps/merge_release_issue.rst -.. include:: release_steps/create_release_branch.rst +.. include:: release_steps/metplus/create_release_reference_branch.rst .. include:: release_steps/metplus/update_release_content.rst .. include:: release_steps/push_release_branch.rst .. include:: release_steps/create_release_on_github.rst @@ -22,3 +22,4 @@ Create a new vX.Y.Z official release from the develop branch. .. include:: release_steps/finalize_release_on_github_official.rst .. include:: release_steps/metplus/update_version_on_develop.rst .. include:: release_steps/update_docs_official.rst +.. include:: release_steps/metplus/update_web_server_data.rst diff --git a/docs/Release_Guide/release_steps/merge_release_issue.rst b/docs/Release_Guide/release_steps/merge_release_issue.rst index 2b30dcb36a..da554f4da3 100644 --- a/docs/Release_Guide/release_steps/merge_release_issue.rst +++ b/docs/Release_Guide/release_steps/merge_release_issue.rst @@ -1,4 +1,5 @@ Merge Release Issue ------------------- -* After updating the version number and release notes on a release feature branch, submit a pull request to merge those changes back into the source branch. +* After updating the release-specific content on a release feature branch, + submit a pull request to merge those changes back into the source branch. diff --git a/docs/Release_Guide/release_steps/metplus/create_release_reference_branch.rst b/docs/Release_Guide/release_steps/metplus/create_release_reference_branch.rst new file mode 100644 index 0000000000..94845f5361 --- /dev/null +++ b/docs/Release_Guide/release_steps/metplus/create_release_reference_branch.rst @@ -0,0 +1,37 @@ +Create Release Reference Branch +------------------------------- + +* Create a branch from the develop branch for the reference branch for the + new official release and push it to GitHub. The branch name should match + the format main_vX.Y-ref where X.Y is the major/minor release number. + +.. parsed-literal:: + + cd |projectRepo| + git checkout develop + git pull + git checkout -b main_vX.Y-ref + git push -u origin main_vX.Y-ref + +Pushing this branch to GitHub should trigger the GitHub Actions automation +that runs all of the use cases and creates Docker data volumes with the output +data. These data will be used to verify that any bugfixes applied to the +main_vX.Y branch does not break any of existing logic. + +Navigate to https://github.com/dtcenter/METplus/actions and verify that a +*Testing* workflow was triggered on the *main_vX.Y-ref* branch. + +.. figure:: /Release_Guide/release_steps/metplus/metplus-automation-reference-data.png + +* Wait until the entire workflow has run successfully. The final job entitled + "Create Output Docker Data Volumes" should create Docker data volumes for + each use case category on DockerHub (dtcenter/metplus-data-dev). The names + of these volumes start with *output-*. + +* After the truth data volumes have been generated, create the main_vX.Y + branch off of the -ref branch. + +:: + + git checkout -b main_vX.Y + git push -u origin main_vX.Y diff --git a/docs/Release_Guide/release_steps/metplus/metplus-automation-reference-data.png b/docs/Release_Guide/release_steps/metplus/metplus-automation-reference-data.png new file mode 100644 index 0000000000..374ba02ced Binary files /dev/null and b/docs/Release_Guide/release_steps/metplus/metplus-automation-reference-data.png differ diff --git a/docs/Release_Guide/release_steps/metplus/update_release_content.rst b/docs/Release_Guide/release_steps/metplus/update_release_content.rst index 6b732193a2..92ba48afda 100644 --- a/docs/Release_Guide/release_steps/metplus/update_release_content.rst +++ b/docs/Release_Guide/release_steps/metplus/update_release_content.rst @@ -4,15 +4,22 @@ Update Release Content Update content that should go into the release version but remain unchanged in the develop branch. -**Update the version number in the quick search links:** +Update the version number +^^^^^^^^^^^^^^^^^^^^^^^^^ -* Open the docs/Users_Guide/quicksearch.rst file for editing. -* Replace the word "develop" in all of the links with "vX.Y.Z", - replacing the X.Y.Z with the version number. - For example, replace "develop" with "v4.0.0". -* Save and close the file. +Remove **-dev** from the version number: -**Update the version numbers in the manage externals files:** +* As of METplus 4.0.0, we are naming releases with X.Y.Z format even if Z is 0. +* As of METplus v4.0.0, the file containing the version number is located at + **metplus/VERSION** (in earlier releases, the file was located at + docs/version or doc/version). +* In the develop branch, the version should match the upcoming release + with -dev added to the end like X.Y.Z-betaN-dev, i.e. 4.0.0-beta1-dev +* Remove **-dev** from the version number so that it matches the release + you are creating. + +Update the version numbers in the manage externals files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ build_components/Externals_stable.cfg diff --git a/docs/Release_Guide/release_steps/metplus/update_release_date.rst b/docs/Release_Guide/release_steps/metplus/update_release_date.rst new file mode 100644 index 0000000000..f2a1444fcd --- /dev/null +++ b/docs/Release_Guide/release_steps/metplus/update_release_date.rst @@ -0,0 +1,5 @@ +Update Release Date +^^^^^^^^^^^^^^^^^^^ + +* The release date is stored in *metplus/RELEASE_DATE* in YYYYMMDD format. +* Change the value stored in this file to match the current date. \ No newline at end of file diff --git a/docs/Release_Guide/release_steps/metplus/update_version_official.rst b/docs/Release_Guide/release_steps/metplus/update_version_official.rst deleted file mode 100644 index b0f9d0e62a..0000000000 --- a/docs/Release_Guide/release_steps/metplus/update_version_official.rst +++ /dev/null @@ -1,9 +0,0 @@ -Update Version Number for Release ---------------------------------- - -Remove **-dev** from the version number: - -* As of METplus 4.0.0, we are naming releases with X.Y.Z format even if Z is 0. -* As of METplus v4.0.0, the file containing the version number is located at **metplus/VERSION** (in earlier releases, the file was located at docs/version or doc/version). -* In the develop branch, the version should match the upcoming release with -dev added to the end like X.Y.Z-betaN-dev, i.e. 4.0.0-beta1-dev -* Remove **-dev** from the version number so that it matches the release you are creating. diff --git a/docs/Release_Guide/release_steps/metplus/update_version_on_develop.rst b/docs/Release_Guide/release_steps/metplus/update_version_on_develop.rst index 8a71254a13..cbad49ae5c 100644 --- a/docs/Release_Guide/release_steps/metplus/update_version_on_develop.rst +++ b/docs/Release_Guide/release_steps/metplus/update_version_on_develop.rst @@ -1,9 +1,9 @@ Update Version on Develop Branch -------------------------------- -Change metplus/VERSION value to the next release after this one with -dev added -to the end. Releases will loosely following these names, but are subject to -change: +Switch to the develop branch and change metplus/VERSION value to the +next release after this one with -dev added to the end. +Releases will loosely following these names, but are subject to change: +-------------------+-----------------------+ | Release Version | New Develop Version | diff --git a/docs/Release_Guide/release_steps/metplus/update_web_server_data.rst b/docs/Release_Guide/release_steps/metplus/update_web_server_data.rst new file mode 100644 index 0000000000..2e8311eb44 --- /dev/null +++ b/docs/Release_Guide/release_steps/metplus/update_web_server_data.rst @@ -0,0 +1,11 @@ +Update DTC Web Server Data +-------------------------- + +Create Directory for Next Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +On the DTC web server where the sample input data for use cases is hosted, +run the setup_next_release_data.py script for the next upcoming release +to set up the data directory for the next major/minor version development. +The script can be found in the METplus repository in internal_tests/use_cases. +See the comments in the script for more details. diff --git a/docs/Users_Guide/glossary.rst b/docs/Users_Guide/glossary.rst index e69a9242ea..612d99e2c0 100644 --- a/docs/Users_Guide/glossary.rst +++ b/docs/Users_Guide/glossary.rst @@ -495,7 +495,10 @@ METplus Configuration Glossary .. warning:: **DEPRECATED:** Please use :term:`PY_EMBED_INGEST__OUTPUT_DIR`. ASCII2NC_CONFIG_FILE - Path to optional configuration file read by ASCII2NC. + Path to optional configuration file read by ascii2nc. + To utilize a configuration file, set this to + {PARM_BASE}/parm/met_config/Ascii2NcConfig_wrapped. + If unset, no config file will be used. | *Used by:* ASCII2NC @@ -505,12 +508,14 @@ METplus Configuration Glossary | *Used by:* ASCII2NC TC_STAT_CONFIG_FILE - Path to optional configuration file read by TCStat. + Path to configuration file read by tc_stat. + If unset, parm/met_config/TCStatConfig_wrapped will be used. | *Used by:* TCStat TC_RMW_CONFIG_FILE - Path to optional configuration file read by TCRMW. + Path to configuration file read by tc_rmw. + If unset, parm/met_config/TCRMWConfig_wrapped will be used. | *Used by:* TCRMW @@ -788,7 +793,7 @@ METplus Configuration Glossary | *Used by:* EnsembleStat, GridStat, MODE, StatAnalysis CONFIG_FILE - Specific configuration file name to use for MET tools. + .. warning:: **DEPRECATED:** Please use :term:`TCMPR_PLOTTER_CONFIG_FILE`. | *Used by:* TCMPRPlotter @@ -968,7 +973,8 @@ METplus Configuration Glossary .. warning:: **DEPRECATED:** Please use :term:`ENSEMBLE_STAT_CONFIG_FILE` instead. ENSEMBLE_STAT_CONFIG_FILE - Specify the absolute path to the configuration file for the MET ensemble_stat tool. + Path to configuration file read by ensemble_stat. + If unset, parm/met_config/EnsembleStatConfig_wrapped will be used. | *Used by:* EnsembleStat @@ -1961,7 +1967,8 @@ METplus Configuration Glossary .. warning:: **DEPRECATED:** Please use :term:`GRID_STAT_CONFIG_FILE` instead. GRID_STAT_CONFIG_FILE - Specify the absolute path to the configuration file used by the MET grid_stat tool. + Path to configuration file read by grid_stat. + If unset, parm/met_config/GridStatConfig_wrapped will be used. | *Used by:* GridStat @@ -2407,7 +2414,8 @@ METplus Configuration Glossary .. warning:: **DEPRECATED:** Please use :term:`MODE_CONFIG_FILE` instead. Path to mode configuration file. MODE_CONFIG_FILE - Path to mode configuration file. + Path to configuration file read by mode. + If unset, parm/met_config/MODEConfig_wrapped will be used. | *Used by:* MODE @@ -2495,7 +2503,8 @@ METplus Configuration Glossary .. warning:: **DEPRECATED:** Please use :term:`MTD_CONFIG_FILE` instead. MTD_CONFIG_FILE - Path to mode-TD configuration file. + Path to configuration file read by mtd. + If unset, parm/met_config/MTDConfig_wrapped will be used. | *Used by:* MTD @@ -3198,7 +3207,8 @@ METplus Configuration Glossary | *Used by:* All PB2NC_CONFIG_FILE - Specify the absolute path to the configuration file for the MET pb2nc tool. + Path to configuration file read by pb2nc. + If unset, parm/met_config/PB2NCConfig_wrapped will be used. | *Used by:* PB2NC @@ -3367,7 +3377,8 @@ METplus Configuration Glossary | *Used by:* TCMPRPlotter POINT_STAT_CONFIG_FILE - Specify the absolute path to the configuration file to be used with the MET point_stat tool. + Path to configuration file read by point_stat. + If unset, parm/met_config/PointStatConfig_wrapped will be used. | *Used by:* PointStat @@ -3549,7 +3560,8 @@ METplus Configuration Glossary | *Used by:* TCMPRPlotter SERIES_ANALYSIS_CONFIG_FILE - Specify the absolute path for the configuration file to use with the MET series_analysis tool by initialization time. + Path to configuration file read by series_analysis. + If unset, parm/met_config/SeriesAnalysisConfig_wrapped will be used. | *Used by:* SeriesAnalysis @@ -3670,7 +3682,10 @@ METplus Configuration Glossary .. warning:: **DEPRECATED:** Please use :term:`STAT_ANALYSIS_CONFIG_FILE` instead. STAT_ANALYSIS_CONFIG_FILE - Specify the absolute path for the configuration file used with the MET stat_analysis tool. It is recommended to set this to {PARM_BASE}/use_cases/plotting/met_config/STATAnalysisConfig. + Path to optional configuration file read by stat_analysis. + To utilize a configuration file, set this to + {PARM_BASE}/parm/met_config/STATAnalysisConfig_wrapped. + If unset, no config file will be used. | *Used by:* StatAnalysis @@ -3787,7 +3802,8 @@ METplus Configuration Glossary | *Used by:* TCPairs TC_PAIRS_CONFIG_FILE - Provide the absolute path to the configuration file for the MET tc_pairs tool. + Path to configuration file read by tc_pairs. + If unset, parm/met_config/TCPairsConfig_wrapped will be used. | *Used by:* TCPairs @@ -4652,9 +4668,10 @@ METplus Configuration Glossary | *Used by:* GridDiag GRID_DIAG_CONFIG_FILE - Specify the absolute path to the configuration file used by the MET grid_stat tool. + Path to configuration file read by grid_diag. + If unset, parm/met_config/GridDiagConfig_wrapped will be used. - | *Used by:* GridStat + | *Used by:* GridDiag GRID_DIAG_CUSTOM_LOOP_LIST Sets custom string loop list for a specific wrapper. See :term:`CUSTOM_LOOP_LIST`. @@ -4758,7 +4775,8 @@ METplus Configuration Glossary | *Used by:* TCGen TC_GEN_CONFIG_FILE - Provide the absolute path to the configuration file for the MET TCGen tool. + Path to configuration file read by tc_gen. + If unset, parm/met_config/TCGenConfig_wrapped will be used. | *Used by:* TCGen diff --git a/internal_tests/pytests/ascii2nc/test_ascii2nc_wrapper.py b/internal_tests/pytests/ascii2nc/test_ascii2nc_wrapper.py index e4e6fd00b5..641c4d3599 100644 --- a/internal_tests/pytests/ascii2nc/test_ascii2nc_wrapper.py +++ b/internal_tests/pytests/ascii2nc/test_ascii2nc_wrapper.py @@ -138,7 +138,7 @@ def test_ascii2nc_wrapper(metplus_config, config_overrides, 'use_cases/met_tool_wrapper/ASCII2NC/ASCII2NC.conf', config_overrides) ) - assert(wrapper.isOK) + assert wrapper.isOK input_path = wrapper.config.getraw('config', 'ASCII2NC_INPUT_TEMPLATE') input_dir = os.path.dirname(input_path) @@ -157,10 +157,10 @@ def test_ascii2nc_wrapper(metplus_config, config_overrides, config_file = wrapper.c_dict.get('CONFIG_FILE') expected_cmd = (f"{app_path} " - f"{input_dir}/{input_file} " - f"{output_dir}/{output_file} " - f"-config {config_file} " - f"{verbosity}") + f"{input_dir}/{input_file} " + f"{output_dir}/{output_file} " + f"-config {config_file} " + f"{verbosity}") assert(all_commands[0][0] == expected_cmd) @@ -172,3 +172,15 @@ def test_ascii2nc_wrapper(metplus_config, config_overrides, value = match.split('=', 1)[1] assert (env_var_values.get(env_var_key, '') == value) + +def test_get_config_file(metplus_config): + fake_config_name = '/my/config/file' + config = metplus_config() + config.set('config', 'INPUT_MUST_EXIST', False) + + wrapper = ASCII2NCWrapper(config) + assert not wrapper.c_dict['CONFIG_FILE'] + + config.set('config', 'ASCII2NC_CONFIG_FILE', fake_config_name) + wrapper = ASCII2NCWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name diff --git a/internal_tests/pytests/ensemble_stat/test_ensemble_stat_wrapper.py b/internal_tests/pytests/ensemble_stat/test_ensemble_stat_wrapper.py index b1bfa40b25..85517687f1 100644 --- a/internal_tests/pytests/ensemble_stat/test_ensemble_stat_wrapper.py +++ b/internal_tests/pytests/ensemble_stat/test_ensemble_stat_wrapper.py @@ -104,7 +104,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, config.set('config', key, value) wrapper = EnsembleStatWrapper(config) - assert(wrapper.isOK) + assert wrapper.isOK all_cmds = wrapper.run_all_times() for (_, actual_env_vars), run_time in zip(all_cmds, run_times): @@ -376,8 +376,8 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, '["/some/climo_mean/file.txt"];}'), 'CLIMO_MEAN_FILE': '"/some/climo_mean/file.txt"'}), - ({'ENSEMBLE_STAT_CLIMO_MEAN_FIELD': 'CLM_NAME', }, - {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {field = ["CLM_NAME"];}'}), + ({'ENSEMBLE_STAT_CLIMO_MEAN_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', }, + {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {field = [{name="CLM_NAME"; level="(0,0,*,*)";}];}'}), ({'ENSEMBLE_STAT_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', }, {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {method = NEAREST;}}'}), @@ -407,7 +407,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, ({ 'ENSEMBLE_STAT_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', - 'ENSEMBLE_STAT_CLIMO_MEAN_FIELD': 'CLM_NAME', + 'ENSEMBLE_STAT_CLIMO_MEAN_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', 'ENSEMBLE_STAT_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', 'ENSEMBLE_STAT_CLIMO_MEAN_REGRID_WIDTH': '1', 'ENSEMBLE_STAT_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', @@ -419,7 +419,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, }, {'METPLUS_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' '["/some/climo_mean/file.txt"];' - 'field = ["CLM_NAME"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' @@ -433,8 +433,8 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, '["/some/climo_stdev/file.txt"];}'), 'CLIMO_STDEV_FILE': '"/some/climo_stdev/file.txt"'}), - ({'ENSEMBLE_STAT_CLIMO_STDEV_FIELD': 'CLM_NAME', }, - {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {field = ["CLM_NAME"];}'}), + ({'ENSEMBLE_STAT_CLIMO_STDEV_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', }, + {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {field = [{name="CLM_NAME"; level="(0,0,*,*)";}];}'}), ({'ENSEMBLE_STAT_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', }, { @@ -466,7 +466,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, ({ 'ENSEMBLE_STAT_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', - 'ENSEMBLE_STAT_CLIMO_STDEV_FIELD': 'CLM_NAME', + 'ENSEMBLE_STAT_CLIMO_STDEV_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', 'ENSEMBLE_STAT_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', 'ENSEMBLE_STAT_CLIMO_STDEV_REGRID_WIDTH': '1', 'ENSEMBLE_STAT_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', @@ -478,7 +478,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, }, {'METPLUS_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' '["/some/climo_stdev/file.txt"];' - 'field = ["CLM_NAME"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' @@ -499,7 +499,7 @@ def test_ensemble_stat_single_field(metplus_config, config_overrides, config.set('config', key, value) wrapper = EnsembleStatWrapper(config) - assert(wrapper.isOK) + assert wrapper.isOK app_path = os.path.join(config.getdir('MET_BIN_DIR'), wrapper.app_name) verbosity = f"-v {wrapper.c_dict['VERBOSITY']}" @@ -537,3 +537,18 @@ def test_ensemble_stat_single_field(metplus_config, config_overrides, assert (actual_value == ens_fmt) else: assert(env_var_values.get(env_var_key, '') == actual_value) + +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', + 'EnsembleStatConfig_wrapped') + + wrapper = EnsembleStatWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == default_config_file + + config.set('config', 'ENSEMBLE_STAT_CONFIG_FILE', fake_config_name) + wrapper = EnsembleStatWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name diff --git a/internal_tests/pytests/grid_diag/test_grid_diag.py b/internal_tests/pytests/grid_diag/test_grid_diag.py index 05202292aa..21e48d27f4 100644 --- a/internal_tests/pytests/grid_diag/test_grid_diag.py +++ b/internal_tests/pytests/grid_diag/test_grid_diag.py @@ -170,3 +170,18 @@ def test_get_all_files_and_subset(metplus_config, time_info, expected_subset): def test_get_list_file_name(metplus_config, time_info, expected_filename): wrapper = GridDiagWrapper(metplus_config()) assert(wrapper.get_list_file_name(time_info, 'input0') == expected_filename) + +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', + 'GridDiagConfig_wrapped') + + wrapper = GridDiagWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == default_config_file + + config.set('config', 'GRID_DIAG_CONFIG_FILE', fake_config_name) + wrapper = GridDiagWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name 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 00420bdd55..6b220af88d 100644 --- a/internal_tests/pytests/grid_stat/test_grid_stat_wrapper.py +++ b/internal_tests/pytests/grid_stat/test_grid_stat_wrapper.py @@ -97,7 +97,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, config.set('config', key, value) wrapper = GridStatWrapper(config) - assert(wrapper.isOK) + assert wrapper.isOK all_cmds = wrapper.run_all_times() for (_, actual_env_vars), run_time in zip(all_cmds, run_times): @@ -421,8 +421,8 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, '["/some/climo_mean/file.txt"];}'), 'CLIMO_MEAN_FILE': '"/some/climo_mean/file.txt"'}), - ({'GRID_STAT_CLIMO_MEAN_FIELD': 'CLM_NAME', }, - {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {field = ["CLM_NAME"];}'}), + ({'GRID_STAT_CLIMO_MEAN_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', }, + {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {field = [{name="CLM_NAME"; level="(0,0,*,*)";}];}'}), ({'GRID_STAT_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', }, {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {method = NEAREST;}}'}), @@ -452,7 +452,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, ({ 'GRID_STAT_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', - 'GRID_STAT_CLIMO_MEAN_FIELD': 'CLM_NAME', + 'GRID_STAT_CLIMO_MEAN_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', 'GRID_STAT_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', 'GRID_STAT_CLIMO_MEAN_REGRID_WIDTH': '1', 'GRID_STAT_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', @@ -464,7 +464,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, }, {'METPLUS_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' '["/some/climo_mean/file.txt"];' - 'field = ["CLM_NAME"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' @@ -478,8 +478,8 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, '["/some/climo_stdev/file.txt"];}'), 'CLIMO_STDEV_FILE': '"/some/climo_stdev/file.txt"'}), - ({'GRID_STAT_CLIMO_STDEV_FIELD': 'CLM_NAME', }, - {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {field = ["CLM_NAME"];}'}), + ({'GRID_STAT_CLIMO_STDEV_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', }, + {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {field = [{name="CLM_NAME"; level="(0,0,*,*)";}];}'}), ({'GRID_STAT_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', }, { @@ -511,7 +511,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, ({ 'GRID_STAT_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', - 'GRID_STAT_CLIMO_STDEV_FIELD': 'CLM_NAME', + 'GRID_STAT_CLIMO_STDEV_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', 'GRID_STAT_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', 'GRID_STAT_CLIMO_STDEV_REGRID_WIDTH': '1', 'GRID_STAT_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', @@ -523,7 +523,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, }, {'METPLUS_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' '["/some/climo_stdev/file.txt"];' - 'field = ["CLM_NAME"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' @@ -553,7 +553,7 @@ def test_grid_stat_single_field(metplus_config, config_overrides, config.set('config', key, value) wrapper = GridStatWrapper(config) - assert(wrapper.isOK) + assert wrapper.isOK app_path = os.path.join(config.getdir('MET_BIN_DIR'), wrapper.app_name) verbosity = f"-v {wrapper.c_dict['VERBOSITY']}" @@ -589,3 +589,18 @@ def test_grid_stat_single_field(metplus_config, config_overrides, assert (actual_value == obs_fmt) else: assert(env_var_values.get(env_var_key, '') == actual_value) + +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', + 'GridStatConfig_wrapped') + + wrapper = GridStatWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == default_config_file + + config.set('config', 'GRID_STAT_CONFIG_FILE', fake_config_name) + wrapper = GridStatWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name diff --git a/internal_tests/pytests/mode/test_mode_wrapper.py b/internal_tests/pytests/mode/test_mode_wrapper.py index 787f427738..69a17f9146 100644 --- a/internal_tests/pytests/mode/test_mode_wrapper.py +++ b/internal_tests/pytests/mode/test_mode_wrapper.py @@ -315,7 +315,7 @@ def test_mode_single_field(metplus_config, config_overrides, config.set('config', key, value) wrapper = MODEWrapper(config) - assert(wrapper.isOK) + assert wrapper.isOK app_path = os.path.join(config.getdir('MET_BIN_DIR'), wrapper.app_name) verbosity = f"-v {wrapper.c_dict['VERBOSITY']}" @@ -433,8 +433,23 @@ def test_config_synonyms(metplus_config, config_name, env_var_name, set_minimum_config_settings(config) config.set('config', config_name, in_value) wrapper = MODEWrapper(config) - assert(wrapper.isOK) + assert wrapper.isOK expected_output = f'{met_name} = {out_value};' assert(wrapper.env_var_dict[env_var_name] == expected_output) + +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', + 'MODEConfig_wrapped') + + wrapper = MODEWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == default_config_file + + config.set('config', 'MODE_CONFIG_FILE', fake_config_name) + wrapper = MODEWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name diff --git a/internal_tests/pytests/mtd/test_mtd_wrapper.py b/internal_tests/pytests/mtd/test_mtd_wrapper.py index 065124f6ed..27cf14b611 100644 --- a/internal_tests/pytests/mtd/test_mtd_wrapper.py +++ b/internal_tests/pytests/mtd/test_mtd_wrapper.py @@ -188,7 +188,7 @@ def test_mtd_single(metplus_config): mw.c_dict['SINGLE_DATA_SRC'] = 'FCST' mw.c_dict['FCST_INPUT_DIR'] = fcst_dir mw.c_dict['FCST_INPUT_TEMPLATE'] = "{init?fmt=%Y%m%d}/{init?fmt=%Y%m%d}_i{init?fmt=%H}_f{lead?fmt=%.3H}_HRRRTLE_PHPT.grb2" - input_dict = {'init' : datetime.datetime.strptime("201705100300", '%Y%m%d%H%M') } + input_dict = {'init': datetime.datetime.strptime("201705100300", '%Y%m%d%H%M') } mw.run_at_time(input_dict) single_list_file = os.path.join(mw.config.getdir('STAGING_DIR'), 'file_lists', '20170510040000_mtd_single_APCP.txt') @@ -203,3 +203,18 @@ def test_mtd_single(metplus_config): single_list[1] == os.path.join(fcst_dir,'20170510', '20170510_i03_f002_HRRRTLE_PHPT.grb2') and single_list[2] == os.path.join(fcst_dir,'20170510', '20170510_i03_f003_HRRRTLE_PHPT.grb2') ) + +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', + 'MTDConfig_wrapped') + + wrapper = MTDWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == default_config_file + + config.set('config', 'MTD_CONFIG_FILE', fake_config_name) + wrapper = MTDWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name diff --git a/internal_tests/pytests/pb2nc/test_pb2nc_wrapper.py b/internal_tests/pytests/pb2nc/test_pb2nc_wrapper.py index 923a63d290..f9c3ab8cc0 100644 --- a/internal_tests/pytests/pb2nc/test_pb2nc_wrapper.py +++ b/internal_tests/pytests/pb2nc/test_pb2nc_wrapper.py @@ -317,7 +317,7 @@ def test_pb2nc_all_fields(metplus_config, config_overrides, config.set('config', key, value) wrapper = PB2NCWrapper(config) - assert(wrapper.isOK) + assert wrapper.isOK app_path = os.path.join(config.getdir('MET_BIN_DIR'), wrapper.app_name) verbosity = f"-v {wrapper.c_dict['VERBOSITY']}" @@ -347,3 +347,18 @@ def test_pb2nc_all_fields(metplus_config, config_overrides, assert(match is not None) value = match.split('=', 1)[1] assert(env_var_values.get(env_var_key, '') == value) + +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', + 'PB2NCConfig_wrapped') + + wrapper = PB2NCWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == default_config_file + + config.set('config', 'PB2NC_CONFIG_FILE', fake_config_name) + wrapper = PB2NCWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name 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 f783182362..d169410db0 100755 --- a/internal_tests/pytests/point_stat/test_point_stat_wrapper.py +++ b/internal_tests/pytests/point_stat/test_point_stat_wrapper.py @@ -46,7 +46,7 @@ def test_met_dictionary_in_var_options(metplus_config): 'interp = { type = [ { method = NEAREST; width = 1; } ] };') wrapper = PointStatWrapper(config) - assert(wrapper.isOK) + assert wrapper.isOK all_cmds = wrapper.run_all_times() @@ -290,8 +290,8 @@ def test_met_dictionary_in_var_options(metplus_config): '["/some/climo_mean/file.txt"];}'), 'CLIMO_MEAN_FILE': '"/some/climo_mean/file.txt"'}), - ({'POINT_STAT_CLIMO_MEAN_FIELD': 'CLM_NAME', }, - {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {field = ["CLM_NAME"];}'}), + ({'POINT_STAT_CLIMO_MEAN_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', }, + {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {field = [{name="CLM_NAME"; level="(0,0,*,*)";}];}'}), ({'POINT_STAT_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', }, { @@ -323,7 +323,7 @@ def test_met_dictionary_in_var_options(metplus_config): ({ 'POINT_STAT_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', - 'POINT_STAT_CLIMO_MEAN_FIELD': 'CLM_NAME', + 'POINT_STAT_CLIMO_MEAN_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', 'POINT_STAT_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', 'POINT_STAT_CLIMO_MEAN_REGRID_WIDTH': '1', 'POINT_STAT_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', @@ -335,7 +335,7 @@ def test_met_dictionary_in_var_options(metplus_config): }, {'METPLUS_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' '["/some/climo_mean/file.txt"];' - 'field = ["CLM_NAME"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' @@ -349,9 +349,9 @@ def test_met_dictionary_in_var_options(metplus_config): '["/some/climo_stdev/file.txt"];}'), 'CLIMO_STDEV_FILE': '"/some/climo_stdev/file.txt"'}), - ({'POINT_STAT_CLIMO_STDEV_FIELD': 'CLM_NAME', }, + ({'POINT_STAT_CLIMO_STDEV_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', }, { - 'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {field = ["CLM_NAME"];}'}), + 'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {field = [{name="CLM_NAME"; level="(0,0,*,*)";}];}'}), ({'POINT_STAT_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', }, { @@ -384,7 +384,7 @@ def test_met_dictionary_in_var_options(metplus_config): ({ 'POINT_STAT_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', - 'POINT_STAT_CLIMO_STDEV_FIELD': 'CLM_NAME', + 'POINT_STAT_CLIMO_STDEV_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', 'POINT_STAT_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', 'POINT_STAT_CLIMO_STDEV_REGRID_WIDTH': '1', 'POINT_STAT_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', @@ -396,7 +396,7 @@ def test_met_dictionary_in_var_options(metplus_config): }, {'METPLUS_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' '["/some/climo_stdev/file.txt"];' - 'field = ["CLM_NAME"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' @@ -469,7 +469,7 @@ def test_point_stat_all_fields(metplus_config, config_overrides, config.set('config', key, value) wrapper = PointStatWrapper(config) - assert(wrapper.isOK) + assert wrapper.isOK app_path = os.path.join(config.getdir('MET_BIN_DIR'), wrapper.app_name) verbosity = f"-v {wrapper.c_dict['VERBOSITY']}" @@ -507,3 +507,18 @@ def test_point_stat_all_fields(metplus_config, config_overrides, assert (value == obs_fmt) else: assert(env_var_values.get(env_var_key, '') == value) + +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', + 'PointStatConfig_wrapped') + + wrapper = PointStatWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == default_config_file + + config.set('config', 'POINT_STAT_CONFIG_FILE', fake_config_name) + wrapper = PointStatWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name diff --git a/internal_tests/pytests/series_analysis/test_series_analysis.py b/internal_tests/pytests/series_analysis/test_series_analysis.py index bcdf600670..aead971ac1 100644 --- a/internal_tests/pytests/series_analysis/test_series_analysis.py +++ b/internal_tests/pytests/series_analysis/test_series_analysis.py @@ -95,8 +95,8 @@ def set_minimum_config_settings(config): '["/some/climo_mean/file.txt"];}'), 'CLIMO_MEAN_FILE': '"/some/climo_mean/file.txt"'}), - ({'SERIES_ANALYSIS_CLIMO_MEAN_FIELD': 'CLM_NAME', }, - {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {field = ["CLM_NAME"];}'}), + ({'SERIES_ANALYSIS_CLIMO_MEAN_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', }, + {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {field = [{name="CLM_NAME"; level="(0,0,*,*)";}];}'}), ({'SERIES_ANALYSIS_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', }, {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {method = NEAREST;}}'}), @@ -126,7 +126,7 @@ def set_minimum_config_settings(config): ({ 'SERIES_ANALYSIS_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', - 'SERIES_ANALYSIS_CLIMO_MEAN_FIELD': 'CLM_NAME', + 'SERIES_ANALYSIS_CLIMO_MEAN_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', 'SERIES_ANALYSIS_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', 'SERIES_ANALYSIS_CLIMO_MEAN_REGRID_WIDTH': '1', 'SERIES_ANALYSIS_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', @@ -138,7 +138,7 @@ def set_minimum_config_settings(config): }, {'METPLUS_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' '["/some/climo_mean/file.txt"];' - 'field = ["CLM_NAME"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' @@ -152,8 +152,8 @@ def set_minimum_config_settings(config): '["/some/climo_stdev/file.txt"];}'), 'CLIMO_STDEV_FILE': '"/some/climo_stdev/file.txt"'}), - ({'SERIES_ANALYSIS_CLIMO_STDEV_FIELD': 'CLM_NAME', }, - {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {field = ["CLM_NAME"];}'}), + ({'SERIES_ANALYSIS_CLIMO_STDEV_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', }, + {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {field = [{name="CLM_NAME"; level="(0,0,*,*)";}];}'}), ({'SERIES_ANALYSIS_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', }, { @@ -185,7 +185,7 @@ def set_minimum_config_settings(config): ({ 'SERIES_ANALYSIS_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', - 'SERIES_ANALYSIS_CLIMO_STDEV_FIELD': 'CLM_NAME', + 'SERIES_ANALYSIS_CLIMO_STDEV_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', 'SERIES_ANALYSIS_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', 'SERIES_ANALYSIS_CLIMO_STDEV_REGRID_WIDTH': '1', 'SERIES_ANALYSIS_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', @@ -197,7 +197,7 @@ def set_minimum_config_settings(config): }, {'METPLUS_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' '["/some/climo_stdev/file.txt"];' - 'field = ["CLM_NAME"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' @@ -219,7 +219,7 @@ def test_series_analysis_single_field(metplus_config, config_overrides, config.set('config', key, value) wrapper = SeriesAnalysisWrapper(config) - assert(wrapper.isOK) + assert wrapper.isOK app_path = os.path.join(config.getdir('MET_BIN_DIR'), wrapper.app_name) verbosity = f"-v {wrapper.c_dict['VERBOSITY']}" @@ -801,3 +801,18 @@ def test_get_netcdf_min_max(metplus_config): min, max = wrapper.get_netcdf_min_max(filepath, variable_name) assert(min == expected_min) assert(max == expected_max) + +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', + 'SeriesAnalysisConfig_wrapped') + + wrapper = SeriesAnalysisWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == default_config_file + + config.set('config', 'SERIES_ANALYSIS_CONFIG_FILE', fake_config_name) + wrapper = SeriesAnalysisWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name diff --git a/internal_tests/pytests/stat_analysis/test_stat_analysis.py b/internal_tests/pytests/stat_analysis/test_stat_analysis.py index 46137e6e3c..2c567aeaa7 100644 --- a/internal_tests/pytests/stat_analysis/test_stat_analysis.py +++ b/internal_tests/pytests/stat_analysis/test_stat_analysis.py @@ -795,3 +795,15 @@ def test_get_level_list(metplus_config, data_type, config_list, expected_list): saw = StatAnalysisWrapper(config) assert(saw.get_level_list(data_type) == expected_list) + +def test_get_config_file(metplus_config): + fake_config_name = '/my/config/file' + config = metplus_config() + config.set('config', 'INPUT_MUST_EXIST', False) + + wrapper = StatAnalysisWrapper(config) + assert not wrapper.c_dict['CONFIG_FILE'] + + config.set('config', 'STAT_ANALYSIS_CONFIG_FILE', fake_config_name) + wrapper = StatAnalysisWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name diff --git a/internal_tests/pytests/tc_gen/test_tc_gen_wrapper.py b/internal_tests/pytests/tc_gen/test_tc_gen_wrapper.py index 2be49ab1e8..9486c629b6 100644 --- a/internal_tests/pytests/tc_gen/test_tc_gen_wrapper.py +++ b/internal_tests/pytests/tc_gen/test_tc_gen_wrapper.py @@ -310,7 +310,7 @@ def test_tc_gen(metplus_config, config_overrides, env_var_values): config.set('config', key, value) wrapper = TCGenWrapper(config) - assert(wrapper.isOK) + assert wrapper.isOK app_path = os.path.join(config.getdir('MET_BIN_DIR'), wrapper.app_name) verbosity = f"-v {wrapper.c_dict['VERBOSITY']}" @@ -361,3 +361,18 @@ def test_tc_gen(metplus_config, config_overrides, env_var_values): with open(track_path, 'r') as file_handle: lines = file_handle.read().splitlines() assert(len(lines) == expected_track_count) + +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', + 'TCGenConfig_wrapped') + + wrapper = TCGenWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == default_config_file + + config.set('config', 'TC_GEN_CONFIG_FILE', fake_config_name) + wrapper = TCGenWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name diff --git a/internal_tests/pytests/tc_pairs/test_tc_pairs_wrapper.py b/internal_tests/pytests/tc_pairs/test_tc_pairs_wrapper.py index e662b1ee17..21c00850ed 100644 --- a/internal_tests/pytests/tc_pairs/test_tc_pairs_wrapper.py +++ b/internal_tests/pytests/tc_pairs/test_tc_pairs_wrapper.py @@ -68,7 +68,7 @@ def test_read_storm_info(metplus_config, config_overrides, isOK): config.set('config', key, value) wrapper = TCPairsWrapper(config) - assert(wrapper.isOK == isOK) + assert wrapper.isOK == isOK @pytest.mark.parametrize( 'storm_id,basin,cyclone', [ @@ -511,3 +511,20 @@ def test_tc_pairs_read_all_files(metplus_config, config_overrides, # unset begin and end for next loop del env_var_values[f'METPLUS_{loop_by}_BEG'] del env_var_values[f'METPLUS_{loop_by}_END'] + +def test_get_config_file(metplus_config): + fake_config_name = '/my/config/file' + + config = metplus_config() + config.set('config', 'INIT_TIME_FMT', time_fmt) + config.set('config', 'INIT_BEG', run_times[0]) + default_config_file = os.path.join(config.getdir('PARM_BASE'), + 'met_config', + 'TCPairsConfig_wrapped') + + wrapper = TCPairsWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == default_config_file + + config.set('config', 'TC_PAIRS_CONFIG_FILE', fake_config_name) + wrapper = TCPairsWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name diff --git a/internal_tests/pytests/tc_stat/test_tc_stat_wrapper.py b/internal_tests/pytests/tc_stat/test_tc_stat_wrapper.py index 34b88fb999..b464c2aa85 100644 --- a/internal_tests/pytests/tc_stat/test_tc_stat_wrapper.py +++ b/internal_tests/pytests/tc_stat/test_tc_stat_wrapper.py @@ -247,3 +247,20 @@ def test_handle_jobs_create_parent_dir(metplus_config, jobs, init_dt, # remove parent dirs to clean up for next run cleanup_test_dirs(parent_dirs, output_dir) + + +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', + 'TCStatConfig_wrapped') + + wrapper = TCStatWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == default_config_file + + config.set('config', 'TC_STAT_CONFIG_FILE', fake_config_name) + wrapper = TCStatWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name diff --git a/internal_tests/use_cases/all_use_cases.txt b/internal_tests/use_cases/all_use_cases.txt index 5c5c8df264..e8242d6623 100644 --- a/internal_tests/use_cases/all_use_cases.txt +++ b/internal_tests/use_cases/all_use_cases.txt @@ -121,7 +121,9 @@ Category: s2s 4::TCGen_fcstGFSO_obsBDECKS_GDF_TDF:: model_applications/s2s/TCGen_fcstGFSO_obsBDECKS_GDF_TDF.conf:: metplotpy_env,metplus 5::UserScript_obsPrecip_obsOnly_Hovmoeller:: model_applications/s2s/UserScript_obsPrecip_obsOnly_Hovmoeller.conf:: metplotpy_env 6:: UserScript_obsPrecip_obsOnly_CrossSpectraPlot:: model_applications/s2s/UserScript_obsPrecip_obsOnly_CrossSpectraPlot.conf:: spacetime_env -7:: UserScript_fcstGFS_obsERA_OMI:: model_applications/s2s/UserScript_fcstGFS_obsERA_OMI.conf:: spacetime_env +7:: UserScript_fcstGFS_obsERA_PhaseDiagram:: model_applications/s2s/UserScript_fcstGFS_obsERA_PhaseDiagram.conf:: spacetime_env +8:: UserScript_fcstGFS_obsERA_OMI:: model_applications/s2s/UserScript_fcstGFS_obsERA_OMI.conf:: spacetime_env, metdatadb +9:: UserScript_fcstGFS_obsERA_RMM:: model_applications/s2s/UserScript_fcstGFS_obsERA_RMM.conf:: spacetime_env, metdatadb Category: space_weather diff --git a/internal_tests/use_cases/setup_next_release_data.py b/internal_tests/use_cases/setup_next_release_data.py new file mode 100755 index 0000000000..f6f42e1a82 --- /dev/null +++ b/internal_tests/use_cases/setup_next_release_data.py @@ -0,0 +1,114 @@ +#! /usr/bin/env python3 + +################################################################################ +# Name: setup_next_release_data.py +# Author: George McCabe (mccabe@ucar.edu) +# Description: Run this script from the DTC web server after cutting an +# official release to set up the data directory for the next upcoming +# release. For example, if 4.0.0 was just created, then run this script +# with 4.1 as the argument. If 4.1.0 was just created, then run this +# script with 5.0 as the argument unless it has been decided that we will +# create a 4.2 release. The script will create a directory for the new +# version and create symbolic links for each data set to point to the +# version of that data set that was last updated. For example, if the +# climate data set has not been updated since v3.1, then the symbolic link +# will point into the v3.1 directory to that appropriate tar file. +################################################################################ + +import sys +import os +import re + +data_dir = '/home/met_test/METplus_Data' + +def get_files_from_dir(version_dir, check_dir): + files = os.listdir(check_dir) + if not files: + return False + + print(f"Using {check_dir}") + + found_files = False + for filename in files: + if not filename.startswith('sample_data'): + continue + + filepath = os.path.join(check_dir, filename) + if os.path.islink(filepath): + print(f"\nFound symbolic link: {filepath}") + filepath = os.path.realpath(filepath) + filename = os.path.basename(filepath) + print(f"Real path: {filepath}") + elif os.path.isfile(filepath): + print(f"\nFound file: {filepath}") + else: + print(f"ERROR: Unexpected non-file found: {filepath}") + return False + + linkname = f"{'-'.join(filename.split('-')[0:-1])}.tgz" + linkpath = os.path.join(version_dir, linkname) + print(f"Creating symbolic link:\n" + f" TO: {filepath}\n AT: {linkpath}") + os.symlink(filepath, linkpath) + found_files = True + + return found_files + +def main(new_version): + match = re.match(r'^v([0-9]+)\.([0-9]+)', new_version) + if not match: + print(f"ERROR: Version does not match vX.Y format: {new_version}") + sys.exit(1) + + major = int(match.group(1)) + minor = int(match.group(2)) + + version = f'v{major}.{minor}' + print(f"Handling {version}") + + version_dir = os.path.join(data_dir, version) + + if not os.path.exists(version_dir): + print(f"Creating directory: {version_dir}") + os.makedirs(version_dir) + + if os.listdir(version_dir): + print(f"ERROR: Directory is not empty: {version_dir}") + sys.exit(1) + + # loop down versions, i.e. 4.0, 3.1, 3.0, 2.X, etc. + for cur_major in range(major, -1, -1): + # if looking at the major version + if cur_major == major: + # if minor is 0, skip to next major + if minor == 0: + continue + # otherwise start with 1 below minor version + minor_start = minor - 1 + # if looking at lower than major, start minor + # with 5 in case minor > 1 is created + else: + minor_start = 5 + + for cur_minor in range(minor_start, -1, -1): + cur_version = f'v{cur_major}.{cur_minor}' + print(f"Looking for {cur_version}") + check_dir = os.path.join(data_dir, cur_version) + if os.path.exists(check_dir): + if get_files_from_dir(version_dir, check_dir): + print("\nSymbolic links created " + f"successfully in {version_dir}") + return True + + return False + +if __name__ == '__main__': + if len(sys.argv) < 2: + print("ERROR: Must supply new major/minor version " + "as argument, i.e. v4.0") + sys.exit(1) + + new_version = sys.argv[1] + if not main(new_version): + print("Something went wrong") + sys.exit(1) diff --git a/metplus/wrappers/ascii2nc_wrapper.py b/metplus/wrappers/ascii2nc_wrapper.py index fdb2a50b09..308a84ee5a 100755 --- a/metplus/wrappers/ascii2nc_wrapper.py +++ b/metplus/wrappers/ascii2nc_wrapper.py @@ -43,9 +43,11 @@ def create_c_dict(self): 'LOG_ASCII2NC_VERBOSITY', c_dict['VERBOSITY']) c_dict['ALLOW_MULTIPLE_FILES'] = True - c_dict['CONFIG_FILE'] = self.config.getraw('config', - 'ASCII2NC_CONFIG_FILE', - '') + + # ASCII2NC config file is optional, so + # don't provide wrapped config file name as default value + c_dict['CONFIG_FILE'] = self.get_config_file() + c_dict['ASCII_FORMAT'] = self.config.getstr('config', 'ASCII2NC_INPUT_FORMAT', '') diff --git a/metplus/wrappers/command_builder.py b/metplus/wrappers/command_builder.py index b4827e9091..343333010c 100755 --- a/metplus/wrappers/command_builder.py +++ b/metplus/wrappers/command_builder.py @@ -145,6 +145,11 @@ def check_for_unused_env_vars(self): if not hasattr(self, 'WRAPPER_ENV_VAR_KEYS'): return + if not os.path.exists(config_file): + if self.c_dict.get('INPUT_MUST_EXIST', True): + self.log_error(f'Config file does not exist: {config_file}') + return + # read config file content with open(config_file, 'r') as file_handle: content = file_handle.read() @@ -1425,24 +1430,28 @@ def set_met_config_list(self, c_dict, mp_config, met_config_name, return # convert value from config to a list - conf_value = util.getlist(conf_value) - if conf_value or kwargs.get('allow_empty', False): - conf_value = str(conf_value) - # if not removing quotes, escape any quotes found in list items - if not kwargs.get('remove_quotes', False): - conf_value = conf_value.replace('"', '\\"') - - conf_value = conf_value.replace("'", '"') - - if kwargs.get('remove_quotes', False): - conf_value = conf_value.replace('"', '') + conf_values = util.getlist(conf_value) + if conf_values or kwargs.get('allow_empty', False): + out_values = [] + for conf_value in conf_values: + remove_quotes = kwargs.get('remove_quotes', False) + # if not removing quotes, escape any quotes found in list items + if not remove_quotes: + conf_value = conf_value.replace('"', '\\"') + + conf_value = util.remove_quotes(conf_value) + if not remove_quotes: + conf_value = f'"{conf_value}"' + + out_values.append(conf_value) + out_value = f"[{', '.join(out_values)}]" if not c_dict_key: c_key = met_config_name.upper() else: c_key = c_dict_key - conf_value = f'{met_config_name} = {conf_value};' + conf_value = f'{met_config_name} = {out_value};' c_dict[c_key] = conf_value def set_met_config_string(self, c_dict, mp_config, met_config_name, @@ -1794,7 +1803,7 @@ def handle_climo_dict(self): # define layout of climo_mean and climo_stdev dictionaries climo_items = { 'file_name': ('list', '', None), - 'field': ('list', '', None), + 'field': ('list', 'remove_quotes', None), 'regrid': ('dict', '', [ ('method', 'string', 'uppercase,remove_quotes'), @@ -2114,8 +2123,9 @@ def handle_time_summary_dict(self, c_dict, remove_bracket_list=None): if not remove_bracket_list: return - for list_values in remove_bracket_list: - c_dict[list_values] = c_dict[list_values].strip('[]') + for list_value in remove_bracket_list: + if c_dict.get(list_value): + c_dict[list_value] = c_dict[list_value].strip('[]') def handle_mask(self, single_value=False, get_flags=False): """! Read mask dictionary values and set them into env_var_list @@ -2278,6 +2288,29 @@ def get_met_config(self, **kwargs): """ return met_config(**kwargs) + def get_config_file(self, default_config_file=None): + """! Get the MET config file path for the wrapper from the + METplusConfig object. If unset, use the default value if provided. + + @param default_config_file (optional) filename of wrapped MET config + file found in parm/met_config to use if config file is not set + @returns path to wrapped config file or None if no default is provided + """ + config_name = f'{self.app_name.upper()}_CONFIG_FILE' + config_file = self.config.getraw('config', config_name, '') + if not config_file: + if not default_config_file: + return None + + default_config_path = os.path.join(self.config.getdir('PARM_BASE'), + 'met_config', + default_config_file) + self.logger.debug(f"{config_name} is not set. " + f"Using {default_config_path}") + config_file = default_config_path + + return config_file + def get_start_time_input_dict(self): """! Get the first run time specified in config. Used if only running the wrapper once (LOOP_ORDER = processes). diff --git a/metplus/wrappers/ensemble_stat_wrapper.py b/metplus/wrappers/ensemble_stat_wrapper.py index 61a1964caa..5a4677fab1 100755 --- a/metplus/wrappers/ensemble_stat_wrapper.py +++ b/metplus/wrappers/ensemble_stat_wrapper.py @@ -204,13 +204,10 @@ def create_c_dict(self): c_dict['OBS_GRID_FILE_WINDOW_BEGIN'] = c_dict['OBS_FILE_WINDOW_BEGIN'] c_dict['OBS_GRID_FILE_WINDOW_END'] = c_dict['OBS_FILE_WINDOW_END'] - # set the MET config file path and variables set - # in th config file via environment variables - c_dict['CONFIG_FILE'] = \ - self.config.getraw('config', 'ENSEMBLE_STAT_CONFIG_FILE', '') - - if not c_dict['CONFIG_FILE']: - self.log_error("Must set ENSEMBLE_STAT_CONFIG_FILE.") + # get the MET config file path or use default + c_dict['CONFIG_FILE'] = ( + self.get_config_file('EnsembleStatConfig_wrapped') + ) # read by MET through environment variable, not set in MET config file c_dict['MET_OBS_ERR_TABLE'] = \ diff --git a/metplus/wrappers/grid_diag_wrapper.py b/metplus/wrappers/grid_diag_wrapper.py index a1fa89ac45..f68b0182d1 100755 --- a/metplus/wrappers/grid_diag_wrapper.py +++ b/metplus/wrappers/grid_diag_wrapper.py @@ -48,10 +48,9 @@ def create_c_dict(self): 'LOG_GRID_DIAG_VERBOSITY', c_dict['VERBOSITY']) c_dict['ALLOW_MULTIPLE_FILES'] = True - c_dict['CONFIG_FILE'] = self.config.getraw('config', - 'GRID_DIAG_CONFIG_FILE') - if not c_dict['CONFIG_FILE']: - self.log_error('GRID_DIAG_CONFIG_FILE required to run.') + + # get the MET config file path or use default + c_dict['CONFIG_FILE'] = self.get_config_file('GridDiagConfig_wrapped') c_dict['INPUT_DIR'] = self.config.getdir('GRID_DIAG_INPUT_DIR', '') self.get_input_templates(c_dict) diff --git a/metplus/wrappers/grid_stat_wrapper.py b/metplus/wrappers/grid_stat_wrapper.py index 10995682cf..33ca143376 100755 --- a/metplus/wrappers/grid_stat_wrapper.py +++ b/metplus/wrappers/grid_stat_wrapper.py @@ -104,8 +104,10 @@ def create_c_dict(self): c_dict['VERBOSITY'] = self.config.getstr('config', 'LOG_GRID_STAT_VERBOSITY', c_dict['VERBOSITY']) - c_dict['CONFIG_FILE'] = self.config.getraw('config', - 'GRID_STAT_CONFIG_FILE', '') + + # get the MET config file path or use default + c_dict['CONFIG_FILE'] = self.get_config_file('GridStatConfig_wrapped') + c_dict['OBS_INPUT_DIR'] = \ self.config.getdir('OBS_GRID_STAT_INPUT_DIR', '') c_dict['OBS_INPUT_TEMPLATE'] = \ diff --git a/metplus/wrappers/mode_wrapper.py b/metplus/wrappers/mode_wrapper.py index 346434abf0..5b126d7455 100755 --- a/metplus/wrappers/mode_wrapper.py +++ b/metplus/wrappers/mode_wrapper.py @@ -116,11 +116,9 @@ def create_c_dict(self): c_dict['VERBOSITY'] = self.config.getstr('config', 'LOG_MODE_VERBOSITY', c_dict['VERBOSITY']) - c_dict['CONFIG_FILE'] = self.config.getraw('config', - 'MODE_CONFIG_FILE', - '') - if not c_dict['CONFIG_FILE']: - self.log_error('MODE_CONFIG_FILE must be set') + + # get the MET config file path or use default + c_dict['CONFIG_FILE'] = self.get_config_file('MODEConfig_wrapped') c_dict['OBS_INPUT_DIR'] = \ self.config.getdir('OBS_MODE_INPUT_DIR', '') diff --git a/metplus/wrappers/mtd_wrapper.py b/metplus/wrappers/mtd_wrapper.py index be8adea1ee..30807f592a 100755 --- a/metplus/wrappers/mtd_wrapper.py +++ b/metplus/wrappers/mtd_wrapper.py @@ -62,9 +62,10 @@ def create_c_dict(self): self.config.getraw('config', 'MTD_OUTPUT_TEMPLATE') ) - c_dict['CONFIG_FILE'] = self.config.getraw('config', - 'MTD_CONFIG_FILE', - '') + + # get the MET config file path or use default + c_dict['CONFIG_FILE'] = self.get_config_file('MTDConfig_wrapped') + # new method of reading/setting MET config values self.set_met_config_int(self.env_var_dict, 'MTD_MIN_VOLUME', 'min_volume', 'METPLUS_MIN_VOLUME') diff --git a/metplus/wrappers/pb2nc_wrapper.py b/metplus/wrappers/pb2nc_wrapper.py index c3c9f52853..8d656df648 100755 --- a/metplus/wrappers/pb2nc_wrapper.py +++ b/metplus/wrappers/pb2nc_wrapper.py @@ -88,12 +88,8 @@ def create_c_dict(self): 'PB2NC_INPUT_DATATYPE', '') ) - # Configuration - c_dict['CONFIG_FILE'] = self.config.getraw('config', - 'PB2NC_CONFIG_FILE', - '') - if c_dict['CONFIG_FILE'] == '': - self.log_error('PB2NC_CONFIG_FILE is required') + # get the MET config file path or use default + c_dict['CONFIG_FILE'] = self.get_config_file('PB2NCConfig_wrapped') self.set_met_config_list(self.env_var_dict, 'PB2NC_MESSAGE_TYPE', diff --git a/metplus/wrappers/point_stat_wrapper.py b/metplus/wrappers/point_stat_wrapper.py index 0c26a268eb..1183bdcec1 100755 --- a/metplus/wrappers/point_stat_wrapper.py +++ b/metplus/wrappers/point_stat_wrapper.py @@ -134,10 +134,8 @@ def create_c_dict(self): # get climatology config variables self.handle_climo_dict() - # Configuration - c_dict['CONFIG_FILE'] = ( - self.config.getraw('config', 'POINT_STAT_CONFIG_FILE', '') - ) + # get the MET config file path or use default + c_dict['CONFIG_FILE'] = self.get_config_file('PointStatConfig_wrapped') self.handle_obs_window_variables(c_dict) @@ -232,9 +230,6 @@ def create_c_dict(self): if not c_dict['OUTPUT_DIR']: self.log_error('Must set POINT_STAT_OUTPUT_DIR in config file') - if not c_dict['CONFIG_FILE']: - self.log_error("POINT_STAT_CONFIG_FILE must be set.") - return c_dict def add_obs_valid_args(self, time_info): diff --git a/metplus/wrappers/series_analysis_wrapper.py b/metplus/wrappers/series_analysis_wrapper.py index b05f055426..0367b7ad83 100755 --- a/metplus/wrappers/series_analysis_wrapper.py +++ b/metplus/wrappers/series_analysis_wrapper.py @@ -221,12 +221,10 @@ def create_c_dict(self): if not c_dict['OUTPUT_DIR']: self.log_error("Must set SERIES_ANALYSIS_OUTPUT_DIR to run.") + # get the MET config file path or use default c_dict['CONFIG_FILE'] = ( - self.config.getraw('config', - 'SERIES_ANALYSIS_CONFIG_FILE') + self.get_config_file('SeriesAnalysisConfig_wrapped') ) - if not c_dict['CONFIG_FILE']: - self.log_error("SERIES_ANALYSIS_CONFIG_FILE must be set") c_dict['BACKGROUND_MAP'] = ( self.config.getbool('config', diff --git a/metplus/wrappers/stat_analysis_wrapper.py b/metplus/wrappers/stat_analysis_wrapper.py index e78d441bc6..0e6ad4f1e4 100755 --- a/metplus/wrappers/stat_analysis_wrapper.py +++ b/metplus/wrappers/stat_analysis_wrapper.py @@ -154,9 +154,10 @@ def create_c_dict(self): c_dict['VERBOSITY']) ) c_dict['LOOP_ORDER'] = self.config.getstr('config', 'LOOP_ORDER') - c_dict['CONFIG_FILE'] = self.config.getstr('config', - 'STAT_ANALYSIS_CONFIG_FILE', - '') + + # STATAnalysis config file is optional, so + # don't provide wrapped config file name as default value + c_dict['CONFIG_FILE'] = self.get_config_file() c_dict['OUTPUT_DIR'] = self.config.getdir('STAT_ANALYSIS_OUTPUT_DIR', '') diff --git a/metplus/wrappers/tc_gen_wrapper.py b/metplus/wrappers/tc_gen_wrapper.py index 3a8b61cbf8..6876731bfd 100755 --- a/metplus/wrappers/tc_gen_wrapper.py +++ b/metplus/wrappers/tc_gen_wrapper.py @@ -105,10 +105,9 @@ def create_c_dict(self): c_dict['VERBOSITY']) ) c_dict['ALLOW_MULTIPLE_FILES'] = True - c_dict['CONFIG_FILE'] = ( - self.config.getraw('config', - f'{app_name_upper}_CONFIG_FILE', '') - ) + + # get the MET config file path or use default + c_dict['CONFIG_FILE'] = self.get_config_file('TCGenConfig_wrapped') c_dict['GENESIS_INPUT_DIR'] = ( self.config.getdir(f'{app_name_upper}_GENESIS_INPUT_DIR', '') diff --git a/metplus/wrappers/tc_pairs_wrapper.py b/metplus/wrappers/tc_pairs_wrapper.py index 91824b11c5..59a6fa1a13 100755 --- a/metplus/wrappers/tc_pairs_wrapper.py +++ b/metplus/wrappers/tc_pairs_wrapper.py @@ -96,12 +96,9 @@ def create_c_dict(self): c_dict['MISSING_VAL'] = ( self.config.getstr('config', 'TC_PAIRS_MISSING_VAL', '-9999') ) - c_dict['CONFIG_FILE'] = self.config.getraw('config', - 'TC_PAIRS_CONFIG_FILE', - '') - if not c_dict['CONFIG_FILE']: - self.log_error("TC_PAIRS_CONFIG_FILE is required to " - "run TCPairs wrapper") + + # get the MET config file path or use default + c_dict['CONFIG_FILE'] = self.get_config_file('TCPairsConfig_wrapped') self.add_met_config(name='init_beg', data_type='string', @@ -167,10 +164,14 @@ def create_c_dict(self): self.config.getdir('TC_PAIRS_BDECK_INPUT_DIR', '') c_dict['EDECK_DIR'] = \ self.config.getdir('TC_PAIRS_EDECK_INPUT_DIR', '') - c_dict['OUTPUT_DIR'] = self.config.getdir('TC_PAIRS_OUTPUT_DIR') + c_dict['OUTPUT_DIR'] = self.config.getdir('TC_PAIRS_OUTPUT_DIR', '') + if not c_dict['OUTPUT_DIR']: + self.log_error('TC_PAIRS_OUTPUT_DIR must be set') + c_dict['READ_ALL_FILES'] = ( self.config.getbool('config', - 'TC_PAIRS_READ_ALL_FILES') + 'TC_PAIRS_READ_ALL_FILES', + False) ) # get list of models to process diff --git a/metplus/wrappers/tc_stat_wrapper.py b/metplus/wrappers/tc_stat_wrapper.py index f90574b761..513911c4b5 100755 --- a/metplus/wrappers/tc_stat_wrapper.py +++ b/metplus/wrappers/tc_stat_wrapper.py @@ -115,7 +115,9 @@ def create_c_dict(self): if not c_dict['LOOKIN_DIR']: self.log_error("TC_STAT_LOOKIN_DIR must be set") - c_dict['OUTPUT_DIR'] = self.config.getdir('TC_STAT_OUTPUT_DIR') + c_dict['OUTPUT_DIR'] = self.config.getdir('TC_STAT_OUTPUT_DIR', '') + if not c_dict['OUTPUT_DIR']: + self.log_error("TC_STAT_OUTPUT_DIR must be set") c_dict['JOBS'] = getlist(self.config.getraw('config', 'TC_STAT_JOB_ARGS', @@ -124,16 +126,8 @@ def create_c_dict(self): self.log_error('No job arguments defined. ' 'Please set TC_STAT_JOB_ARGS') - c_dict['CONFIG_FILE'] = self.config.getstr('config', - 'TC_STAT_CONFIG_FILE', - '') - if not c_dict['CONFIG_FILE']: - default_config = os.path.join(self.config.getdir('PARM_BASE'), - 'met_config', - 'TCStatConfig_wrapped') - self.logger.debug("TC_STAT_CONFIG_FILE not set. Using " - f"{default_config}") - c_dict['CONFIG_FILE'] = default_config + # get the MET config file path or use default + c_dict['CONFIG_FILE'] = self.get_config_file('TCStatConfig_wrapped') self.handle_description() diff --git a/metplus/wrappers/tcrmw_wrapper.py b/metplus/wrappers/tcrmw_wrapper.py index 5fcca91368..0db3f864c8 100755 --- a/metplus/wrappers/tcrmw_wrapper.py +++ b/metplus/wrappers/tcrmw_wrapper.py @@ -60,8 +60,9 @@ def create_c_dict(self): 'LOG_TC_RMW_VERBOSITY', c_dict['VERBOSITY']) c_dict['ALLOW_MULTIPLE_FILES'] = True - c_dict['CONFIG_FILE'] = self.config.getraw('config', - 'TC_RMW_CONFIG_FILE', '') + + # get the MET config file path or use default + c_dict['CONFIG_FILE'] = self.get_config_file('TCRMWConfig_wrapped') c_dict['INPUT_DIR'] = self.config.getdir('TC_RMW_INPUT_DIR', '') c_dict['INPUT_TEMPLATE'] = self.config.getraw('filename_templates', diff --git a/parm/use_cases/model_applications/s2s/UserScript_fcstGFS_obsERA_OMI/OMI_driver.py b/parm/use_cases/model_applications/s2s/UserScript_fcstGFS_obsERA_OMI/OMI_driver.py index 0c60d95834..1c63b88104 100755 --- a/parm/use_cases/model_applications/s2s/UserScript_fcstGFS_obsERA_OMI/OMI_driver.py +++ b/parm/use_cases/model_applications/s2s/UserScript_fcstGFS_obsERA_OMI/OMI_driver.py @@ -53,6 +53,7 @@ def run_omi_steps(inlabel, olr_filetxt, spd, EOF1, EOF2, oplot_dir): olr_input_files = olr_input_files[1:] # Read in the netCDF data from a list of files + netcdf_reader = read_netcdf.ReadNetCDF() ds_orig = netcdf_reader.read_into_xarray(olr_input_files)