From 6ab1d99744abcb67a381bc6a3ce58fdd701b1342 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 14:41:05 -0700 Subject: [PATCH] Update develop-ref after #2872 (#2879) * add argument to workflow dispatch event so that MET DockerHub repo used for tests can be easily overridden to test changes in a MET PR before merging * Feature dtcenter/MET#2796 GHA Node20 deprecation warnings (#2473) * per dtcenter/MET#2796, update versions of actions to prevent deprecated node warnings in GHA runs * change arguments to workflow dispatch so they are no longer required -- these are not needed to be set when triggering by hand through the web interface * Feature dtcenter/MET#2796 develop - Fix error log artifact creation (#2475) * updated version of pillow to fix security vulnerability alerted by dependabot in PR #2477 * remove docker image after runtime image is created from metplus image and conda env image * turn on use case to test image removal * prune images if image tag doesn't exist -- it appears that if the image is built on the fly (when PR is coming from fork) then the tag exists, but if not, the image tag is set to * support commands that must run in the shell to see if || will work in docker image pruning step * try to fix image removal * Feature 2383 use case sat alt (#2480) * new docs, files for use case * new files * updating to run use case * updated python libraries, changed test env * trying new point logic * added to script for nan removal * redid Python script to take adv of new MET ability for nans * Update run status * removed unused settings * run image prune commands separately * changed shell back to false * split up use case groups so the same envs are used by a group to see if that resolves the disk space issues * turn off use cases * feature 2253 fix empty pytest logs (#2485) * added more commands to free up disk space as suggested in https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh, ci-run-all-cases * Feature 2406 redo usecase rrfs (#2488) * issue #2406 RRFS use case files * issue #2406 added usecase to tests * Issue #2406 added metplotpy and metcalcpy as dependencies * Feature #2460 allow missing input (#2493) * changed template to use datetime format that works on MacOS * update logic to only write a file list file if there are more than 1 files, updated unit tests to match new behavior, added exception handling to series analysis to prevent crash if file does not exist * use getraw instead of getstr to prevent crash if providing a filename template tag to override a config variable on the command line * Add optional argument to subset file function to always write a file list text file even if there is only 1 file found. Use this argument in UserScript wrapper so that the environment variables that contain paths to file list files are consistent in format for use in user scripts * enhanced function to support different output variable types * removed the need for overriding clear function in specific wrappers and added optional argument to skip clearing input file list * clean up formatting * per #2460, start to implement logic to prevent errors when some input files are not found * isolate logic to find input files into find_input_files functions. clean up those functions to return boolean instead of sometimes returning None or a list of files to be consistent * remove python embedding checks because MET is now smart enough to determine if a python script is used with always setting file_type * turn on use cases to test error handling * merge artifacts * run only failed cases * always run merge step * run on a case that will succeed to test error log merge step * only run error log merge step if there were 'Save error logs' jobs that succeeded * run cases that will fail * fix condition to merge error logs * run group that will succeed but have diffs - check error logs doesn't fail * testing - add use case group that will succeed but will cause diffs becaus there is no truth data - to confirm that the error log merge step behaves properly in this case * run 3 jobs, 2 should error, to confirm that error_logs is created properly * repeat diff no error test but with * per dtcenter/MET#2796, fix error log artifact creation by merging error logs if any of the 'Save error logs' steps ran successfully * run test to confirm diff does not cause merge error logs to fail * Revert "run test to confirm diff does not cause merge error logs to fail" This reverts commit ff2d1cac57c431a047ee250e9dae9b0a813a78ba. * run test to confirm error logs are merged properly when 2 use case groups have errors * try checking output variable as string instead of boolean * Revert "run test to confirm error logs are merged properly when 2 use case groups have errors" This reverts commit 8106666a73685e654e0146d4fed56f2382f1bfc7. * run test again * test again * move check for error logs for shell script and use github env vars * Revert "run test again" This reverts commit 7a0a99c6e7031c5dafb1177d4b4ca3f32a999dac. * break 2 use cases to test that error logs are still created properly * checkout repo to get script used to merge error logs * Revert "break 2 use cases to test that error logs are still created properly" This reverts commit cb6d0b46db353b4b4709183be2fe7e5ce64ff5ff. * test merge error log again on no error diff run * fix script * move merge error logic back to workflow * break 2 use cases to test that error logs are still created properly * Revert "break 2 use cases to test that error logs are still created properly" This reverts commit 82aa0e11096aace3ccc2c79cd631533fc6426900. * remove testing use case group * Revert "remove python embedding checks because MET is now smart enough to determine if a python script is used with always setting file_type" This reverts commit de3b4b03a45bb871c71e770ff9e602739d6b63d5. * clean up lines * update logic to check that python embedding is set up properly to only try to set file_type automatically if it is not already set and if the wrapper is a tool that supports multiple input files via python embedding (which require file_type to be set). also changed error if not set properly to warning and use PYTHON_NUMPY as a default * remove run_count increment before run_at_time_once - set closer to find_input_files so run count and missing input count are consistent * return boolean from find_input_files function to be consistent with other functions * per #2460, warn instead of error if missing inputs are allowed, track counters for number of runs and missing inputs * per #2460, added check to report error if allowed missing input threshold is met * run clear before running plot_data_plane * removed test group * report warning instead of error if ALLOW_MISSING_INPUTS is True * cleanup * change function to pytest fixture so it can be used by other test scripts * update ascii2nc test to process more than 1 time to ensure commands are built properly for each run * add unit tests to ensure missing input file logic works properly for ascii2nc and grid_stat * set variable to skip RuntimeFreq logic to find input files to prevent duplicate increment of run_count -- these will be removed when the wrapper has been updated to find files using RuntimeFreq logic * remove unneccesary error checking * cleanup * call function to handle input templates that need to be handled separately for each item in the comma-separated list (for UserScript and GridDiag only) * add time_info to ALL_FILES dictionaries to be consistent with other wrappers * clean up logging for reporting error when missing inputs exceeds threshold * added function to get files for a single run time to be consistent with other functions * skip increment of run_count when FIND_FILES=True and RuntimeFreq input file logic is skipped to prevent duplicate increments * added empty test files * remove redundant variables * view warnings on a failed test run * add more empty test files * added unit tests for missing input logic * remove MANDATORY setting for EnsembleStat and GenEnsProd and instead pass mandatory argument to call to find model files so warnings/errors are properly displayed for other inputs * cleanup * remove allow missing input logic from ExtractTiles wrapper * added functions to parse template/dir variables from config, removed explicit calls to read those variables from GridStat * remove error if more labels than inputs are provided (for UserScript and GridDiag only) -- extra labels will just be ignored * added required boolean for input templates * per #2460, change warning messages to debug when checking a list of DA offsets since it is common that a given offset will not always be found in the files * added tests for missing input logic for many wrappers * cleanup * fix increment of number of runs * skip missing input logic * change how required is handled for input templates * warn instead of error if missing input is allowed * remove increment of missing input counters because it is handled in RuntimeFreq * check status of input files and increment counters in overridden run_once_per_lead. remove increment of missing input counters because it is handled in run_once_per_lead * added unit tests for missing input logic * skip missing input logic * cleanup * cleanup, use fixture for tests, add unit tests for missing input, bypass missing input logic on wrappers that don't need it * removed file that is not needed * added unit tests for pb2nc to test -valid_beg/end arguments and changes to properly support any runtime frequencies * warn instead of error if allowing missing inputs * cleanup * implement changes to properly support all runtime frequencies for pb2nc. previously all files that match a wildcard will be used instead of selecting only files that fall within the specified time range. some functions moved into pb2nc wrapper will eventually be moved up so that they are used by all wrappers to be consistent * added unit tests that will fail until wrapper is updated * replace functions in RuntimeFreq wrapper used to find input files so they can be used by all wrappers, updated ioda2nc wrapper to find input files properly to fix tests * cleanup * removed mtd version of get_input_templates and added logic to RuntimeFreq's version to get the same behavior * added unit tests for MTD missing input checks * per #2491, add release notes for beta3 * Feature #2491 v6.0.0 beta3 (#2495) * update version for beta3 release * fixed typos in release notes * update version to note development towards beta4 release * Per suggestion from @JohnHalleyGotway, create intermediate branch for updating truth data to avoid branch protection rules. I added a step to delete the intermediate branch locally if it exists to prevent conflicts with the update * added quotes to prevent error in echo caused by parenthesis * fix incorrect command * Revert "fix incorrect command" This reverts commit e7dffb6b0b351ab1b4bca5b563c1f5beef7737a9. * Revert "added quotes to prevent error in echo caused by parenthesis" This reverts commit c1cb3c4f0d7851bea720a50fac6011cd381017dc. * Revert "Per suggestion from @JohnHalleyGotway, create intermediate branch for updating truth data to avoid branch protection rules. I added a step to delete the intermediate branch locally if it exists to prevent conflicts with the update" This reverts commit 525809dc3bd73ace969b046062967796035f4d86. * Hotfix: Allow symbolic link to run_metplus.py to run (#2500) * Adding use case tests * Changing test environment * Testing environment changes * Documentation update * Updating Documentation * Updating documentation for disk space failure * Added new use case category * Fixing use case test * Fixing bug in use case file * Testing s2s after data removal * add back use cases that were accidentally removed * fix incorrect use case added * Setting tests to false for merge * Removes extraneous imports. * Switches to function call for the coupling index. * Correct number of args in comment. * Testing for old use cases * Setting tests to false for merge * update tests to update develop data -- modified commands to create new use case category directory if it does not already exist, move step to remove old data to be completed just after new data is copied to vX.Y * Summation has to have a dimension supplied for the gridded data, but for pandas the only dimension is time (but it is un-named). Therefore the numerator for the covariance term had to be split out between the fcst and obs case. * Feature 2463 modify table (#2508) * creating test dropdown menus * fixing warnings * fixing warnings * fixing warnings 3 * fixing warnings 4 * Attempt to fix documentation errors * adding 2 more test dropdowns please note. There is still a message about WARNING: Duplicate explicit target name: "gridstat: cloud fractions with neighborhood and probabilities (pygrib)". John O will fix this. I should not touch it. * fixing spacing * trying to fix link * take 2 * Removing double underscores added earlier * moving dropdown menus * Adding version to dropdown menu title * fixing spacing * dropdowns date util, eofs, h5py * fixing formatting * fixing formatting * Per #2463, adding template for future entries * adding imageio, lxml & matplotlib * dropdown up to nc-time-axis * fixing spacing problems * Fixing broken s2s links and other incorrect links * Fixing spelling and capitalization * Removing the dash in front of 1.4 for nc-time-axis * Modifying formatting * adding dropdowns thru pylab * fixing problems * dropdowns thru scikit-learn * fixing spacing * final dropdowns thru yaml * fixing spacing * fixing loose ends * Per #2463, moving information to an Appendix and adding text and links in an overview * Per #2463, adding to index.rst * Per #2463, reworded language and updated Python 3.8 reference * Per #2463, fixing errors * Per #2463, made updates based on feedback at the METplus Engineering meeting. * removing tables, changing most METplus wrappers, version numbers. * trying to fix met_version * Per #2463, adding necessary code for substitutions * Per #2463, fixing syntax error * adding period * removing section 1.5 * Per #2463, replace old label reference with new label reference and updated text to reflect the move to drop down menus * read python version from file to replace in docs * fixed typo in variable name * adding python_version to the overview. --------- Co-authored-by: Julie Prestopnik Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> * per #2509, automate MET version used in documentation to be X+6.Y.0 of METplus version * Bugfix #2520 ASCII2NC file window issue and redundant wrapper initialization (#2522) * per PyCharm documentation, only ignore workspace.xml idea file and commit the rest of the .idea files to version control * per #2520, create function to get METplus wrapper class without initializing it and use that function to read list of deprecated env vars to prevent redundant initialization of wrappers that can cause unintended side effects * handle file_window variables consistently by using CommandBuilder function * comment out optional config variables that previously caused a failure when unset * Update update_truth.md Fix typo in the update_truth issue template in the develop branch. * Update update_truth.md Update wording in the update_truth issue template. * Feature #2530 dev timeline (#2532) * Per #2530, add a development timeline to the METplus Release Information section of the User's Guide. Also update the Release Guide instructions. * Per #2530, tweak the wording. * Update docs/Release_Guide/release_steps/update_release_notes_development.rst Co-authored-by: Julie Prestopnik --------- Co-authored-by: Julie Prestopnik * Adds static station lookup file for use with Python embedding for FLUXNET observations. * Major overhaul to forecast Python embedding script for the TCI use case. * Major overhaul to observation Python embedding script for the TCI use case, to compute TCI from raw observations rather than read pre-computed TCI. * Updates documentation file for TCI use case. * Adds METcalcpy version number. * Refactors wording and fixes typo. * Fixes RST formatting. * Finally fixed RST error. * Adds support to remove leap days if requested. * Updates command line args for Python embedding scripts. * Feature #2537 develop sonarqube_gha (#2541) * Per #2537, add SonarQube workflow for METplus * Per #2537, update nightly build email list. * Per #2537, fix cut/paste error configure_sonarqube.sh * Per #2537, exclude test code from code coverage statistics. * Updated conf file for use case. * Removes new TCI function because it is in METcalcpy now. * Removes old code, somsome reorganization and clarification and setting of params, and also switches the fluxnet metadata file to a command line argument instead of an environment variable. * Update the 6.0.0 Coordinated Release development timeline in release-notes.rst * Support for environment variables or default options for filtering and filename patterns, DEBUG mode added and set to False by default, adjustment of print statements for logging, and refactoring filtering of stations to ensure we don't process a file that we shouldn't by better coupling of filenames and stations. * Makes DEBUG an env var for config via metplus wrappers. * Reorganization of config file, adds environment variables, and updates comments for use case changes. * Updates to documentation. * Fixes tables. * Adds table of contents to the top for users to click on. * Updates use case documentation file. * Updated config file with obs and fcst subirectories in the path. * Added optional key/value to use_case_groups.json to prevent a use case group from running to easily disable it temporarily. Disable short_range:14 use case until it can be fixed with #2551 * update pillow version based on recommendation from dependabot: https://github.com/dtcenter/METplus/security/dependabot/5 * Switches to using metplotpy_env to get metcalcpy dependency. * Adds filtering based on missing data values. * Finishing touches to debug statements for testing. * Fixing a few minor code smells from last week. * update link to METplus Components Python Requirements table in PR template * Update docs/use_cases/model_applications/land_surface/PointStat_fcstCESM_obsFLUXNET2015_TCI.py Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> * Feature #2555 v6.0.0 beta4 (#2556) * update version for beta4 release * added release notes for beta4 release * update version for development towards beta5 release * update location of METviewer docker-compose.yml file that moved from PR dtcenter/METviewer#525 * Feature #2537 develop single_sq_project (#2558) * Update the beta4 release date wording * Feature #2433 Ugrid config variables in GridStat/PointStat (#2517) * update version for release * added new use case that was missing * Bugfix #2279 main_v5.1 - buoy station file from 2022 (#2281) * Fix typo in pull_request_template.md * added notes to make it clear that upgrade instructions are not needed if upgrading from 5.0 to 5.1 * New issue template: Update Truth (#2332) Co-authored-by: John Halley Gotway (cherry picked from commit 44335f33ab152a0b254041961a41895dde614ae0) * add GitHub Actions workflow that is used to create Docker images that hold the conda environment used for the automated tests -- adding this to the default main_v5.1 branch so that it will become available to run for other branches as a workflow_dispatch workflow * Per #2433, added support for setting the ugrid MET config variables for GridStat and PointStat wrappers. Also moved the seeps variable up so that it matches the order of the default config files in the MET repo * add argument to workflow dispatch event so that MET DockerHub repo used for tests can be easily overridden to test changes in a MET PR before merging * Feature dtcenter/MET#2796 main_v5.1 GHA Node20 deprecation warnings (#2474) * per dtcenter/MET#2796, update versions of actions to prevent deprecated node warnings in GHA runs - main_v5.1 * fix ReadTheDocs requirements to include pillow which is a dependency of sphinx-gallery: see https://blog.readthedocs.com/defaulting-latest-build-tools/ for more info on why this was necessary * install python packages via apk instead of pip to prevent GHA failures that were fixed in develop but not in main_v5.1 * per dtcenter/MET#2796, fix error log merging for main_v5.1 same as develop * Bump pillow from 10.0.1 to 10.2.0 in /docs (#2477) Bumps [pillow](https://github.com/python-pillow/Pillow) from 10.0.1 to 10.2.0. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](https://github.com/python-pillow/Pillow/compare/10.0.1...10.2.0) --- updated-dependencies: - dependency-name: pillow dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Create 5.1.0_casper * per #2433, add support for setting the optional -config argument for a ugrid config file for PointStat and GridStat. Also moved the optional arguments to be added to the command after all of the required arguments so the command is easier to read * per #2433 and discussion on meeting 3/21/2024, change command line argument from -config to -ugrid_config * update unit tests to check for new command line argument name -ugrid_config * Updates information about GDAS surface winds having a QC value that is above the default settings in the PB2NC config file. --------- Signed-off-by: dependabot[bot] Co-authored-by: John Halley Gotway Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Julie Prestopnik Co-authored-by: Daniel Adriaansen * SonarQube add python code coverage report (#2565) * run code coverage before SonarQube scan * generate xml report and configure SQ to read coverage.xml * exclude more files from code coverage report * exclude more files that should not be included in the code coverage report * more changes to code coverage exclude list * removed bad characters accidentally added * exclude cyclone plotter wrapper because it is excluded from code coverage report * ignore SonarQube lint files generated by PyCharm * Updating MTD conv radius/thresh description (#2566) * Updating MTD conv radius/thresh description * Update glossary.rst * Update docs/Users_Guide/glossary.rst Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> * Update docs/Users_Guide/glossary.rst Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> --------- Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> * Feature update modulefiles (#2564) * Adding 6.0.0 file for derecho and removing 5.1.0 file for cheyenne * Updating modulefiles * Updating orion file * Adding file for hercules * Adding file for casper * Update internal/scripts/installation/modulefiles/6.0.0_casper Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> --------- Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> * Feature #1989: Add OMI to Verification Datasets Guide (#2585) Refs: #1989 * Feature #2479 time_offset_warning (#2587) * Per #2479, add support for setting time_offset_warning in all wrapped MET config files * Per #2479, add documentation and examples to basic use case param files for time_offset_warning * Feature 2346 develop update templates (#2589) * Per #2346, modifying issue templates * Per #2346, modifying pull request template * Per #2346, modifying entries based on suggestions by @georgemccabe * Bugfix #2567 PointStat multiple point observation files (#2582) * per #2567, properly pass multiple point observation files to point_stat using the -point_obs argument * per #2567, fix bug that caused file window logic to fail * Revert "per #2567, fix bug that caused file window logic to fail" This reverts commit 27fe8226c58e9a028a7979664bbf224296fdd6ea. * per #2567, fix bug that caused file window logic to fail * Feature #1514 MADIS2NC wrapper (#2591) * Per #1514, implement MADIS2NC wrapper and added example use case. Also updated the function to handle the time_summary dictionary in MET config files to support names that exactly match the name found in the dictionary, e.g. ASCII2NC_TIME_SUMMARY_OBS_VAR sets time_summary.obs_var (previously only ASCII2NC_TIME_SUMMARY_VAR_NAMES was supported and is still supported) * remove execute permissions from image files * Per #1514, add image for basic use case * removed large image files that are no longer being used in documentation * add support for time_offset_warning for MADIS2NC wrapper after the PR to add that support for other wrappers has been merged into develop * report error if output template is not defined * update contributor's guide with more up-to-date info on how to create a new wrapper and basic components of wrappers * fix warnings in documentation * fix formatting issues * Per #1514, add new basic use case to automated test suite * add step to comment out version number in wrapped MET config file * turn off use case to prepare for PR * added a pytest fixture to handle comparison of use case commands and environment variable values to remove a lot of redundant logic in each wrapper test. Added fake madis data * removed commented code * properly substitute template tags in all command line arguments * properly handle unset rec_beg and rec_end to prevent missing value from being added to command lien arg * added new madis2nc use case to existing met_tool_wrapper and temporarily disabled land_surface:0 until we can resolve the differences * Feature 2346 develop update templates (#2594) * Per #2346, modifying issue templates * Per #2346, modifying pull request template * Per #2346, modifying entries based on suggestions by @georgemccabe * Per 2346, making updates based on feedback at last MET Eng. Meeting * Enhance update truth data workflow to create a uniquely named branch to update *-ref branches and commit/append to a log file that tracks the reasons for updating the truth data. This is done to ensure that the *-ref branch testing workflow run that actually updates the truth data is always run even if there are no other changes to the METplus branch since the last update, e.g. when a change to another component like MET warrants the truth data update * git add change log file in case it doesn't already exist * changed logic to no longer push changes to develop/main_vX.Y, but instead merge changes from -ref into the update branch * retain update truth history file from *-ref * dtcenter/MET#2899 fixes a bug reading point observations using Python Embedding in PointStat and EnsembleStat, which should fix the PBL use case -- dtcenter/METplus#2246 -- so turned on diff test for PBL use case to ensure that results are consistent going forward * Feature #2429 MvMODE multivar intensity (#2603) * Feature #2547 ASCII2NC -valid_beg and -valid_end arguments (#2606) * prevent divide by zero if run_count is 0 * Per #2547, add support for setting -valid_beg and -valid_end command line arguments. Added changes to make ASCII2NC wrapper able to run for all runtime frequencies * Refactored system_util preprocess_file function to reduce cognitive complexity and add quotation marks around 'filenames' that contain spaces (typically python embedding commands) so explicit handling of these cases don't need to be done downstream. Added unit tests to test more python embedding cases * remove logic to add quotes around input file since it is handled already in preprocess_file * changed find_input_files function to return None or a time_info dictionary to be consistent across wrappers * added ReformatPointWrapper to use as parent class for ASCII2NC, MADIS2NC, PB2NC, and Point2Grid wrappers to consistently handle tools that reformat point observation data. Moved verbosity to the end of commands * clean up pb2nc wrapper to be more consistent with other ReformatPoint wrappers * per #2547, added glossary entries for new config variables to set -valid_beg/end and added commented example to basic use case config file * added glossary entries for *_RUNTIME_FREQ variables * Per #2513, remove TC_RMW_MAX_RANGE_KM * Feature 2494 update fv3 data (#2610) * Updated for new data * Updated to match new data * Updates due to new data/updates to data * Updates due to new data/data variables * Changes due to changes to data variables, date * Updates due to changes to data * Update the date to reflect the new data (with updates to variables) * fixed error with formatting * Remove redundant instructions. * For testing * Update use_case_groups.json * Revert to original location of input data to use the same data for all three FV3 Physics Tendency use cases. * Revert to original location of data from tarball * Remove typo in file directory name * Update use_case_groups.json Returned 10-12 to follow 11 * Update use_case_groups.json revert to false for testing the short range use cases for FV3 physics tendency * Update use_case_groups.json fix alignment of opening curly brace * Update use_case_groups.json revert to original formatting * removed config variable that should be set by the user because it is specific to the user's environment --------- Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> * Feature #2578 PCPCombine -input_thresh for missing inputs (#2609) * refactoring to reduce duplicate/redundant code, reduce cognitive complexity to satisfy SonarQube, etc * change wording of error log to satisfy test * set -input_thresh argument if set for add, derive, and sum methods. refactor setting of method arguments, e.g. -add, -sum, etc., cleanup * refactor how level is handled in find_data function so that if the level has already been set, it will use that value, otherwise try to get it from {data_type}_level, e.g. fcst_level, otherwise set it to 0 to prevent errors * refactor duplicate code into function to satisfy SonarQube * removed unused variable * use find_data function to find input files to be consistent with other wrappers, only allow multiple input files to be found for a given call to find_data if using the -derive method * fix typo in key * suppress warnings when files aren't found because it is expected * formatting to be consistent in doc string * per #2578, add MISSING before file path that is not found if input_thresh is set and less than 1.0, added unit test to ensure correct behavior occurs * add documentation blocks for new functions, ci-run-all-diff * use pytest fixture instead of local function * add support for setting -vld_thresh argument * per #2578, added documentation and tests for setting -input_thresh and -vld_thresh * moved verbosity argument to end of command to more easily change it when debugging * refactor to reduce cognitive complexity to satisfy SonarQube * update usage statement to include METplus version number * Add copy button for code blocks to easily copy/paste commands (#2611) * add copy button for code blocks to easily copy/paste commands * turn off copy button always visible but leave code block so it can be turned on easily if desired * Update release-notes.rst Update METplus-6.0.0 development schedule. * Fix numbering. * fix broken link * Feature 2557 stratosphere qbo (#2621) * Initial commit for QBO driver * Adding initial documentation for Stratosphere * Updating documentation typo * Updates to Polar plot * Updates to Stratosphere use cases and documentation * Updates to polar use case * Updating documentation * Updates to Polar and QBO * Updated documentation for use cases that cannot be run in actions * Removed some unnecessary imports * Fixed staging dir settings * documentation update * More documentation edits * More updates * Fixed link in documentation * Updating documentation * Updated bullet list * More updates * Added some additional comments to the .conf file * Fixed typo in output file name in documentation * Updated output section of documentation * Updated documentation * Fixed list that was not working * Updated broken link * Fixed broken link again * Fixed another typo * Trying to fix documentation error * Trying to fix indent error * fixed capital typo * Added clarification * use v2 of metplus-action-release-checksum to prevent failure in cUrl command to upload asset * update version of certifi to fix dependabot alert -- see #2632 (#2633) * Feature #2626 v6.0.0-beta5 release (#2630) * update version and added release notes * Per #2626, updating dates --------- Co-authored-by: Julie Prestopnik * Updated release dates * Updating for changes on wcoss2 * Feature #2656 update_truth (#2657) * Per #2656, update the read_iodav2_mpr.py scripts to write 3 additional values of NA in each MPR line corresponding to the OBS_CLIMO_CDF, FCST_CLIMO_MEAN, and FCST_CLIMO_STDEV columns. * Per #2656, update the ugrid_lfric_mpr.py to write 3 additional values of NA in each MPR line corresponding to the OBS_CLIMO_CDF, FCST_CLIMO_MEAN, and FCST_CLIMO_STDEV columns. * Feature 2679 user support (#2681) * Per 2679, updating user support documentation * Per #2679, fixing formatting error * Update user_support.rst * Update user_support.rst --------- Co-authored-by: John Halley Gotway * Feature 2452 update release guide (#2692) * Per #2452, updating MET official release instructions * Update to indicate the "DTC" web server machine Co-authored-by: John Halley Gotway * Updated language for tar file Co-authored-by: John Halley Gotway --------- Co-authored-by: John Halley Gotway * Updating release schedule * Feature #2612 forecast lead groups (#2698) * Refactor to prevent calling sys.exit from functions that could cause the calling script to exit unintentionally. Instead return None or False and only sys.exit a non-zero value from a script that is called explicitly. Refactored validate config logic to reduce cognitive complexity to satisfy SonarQube * per #2596, ensure the same RUN_ID is set for a given METplus run even if instances are used in the PROCESS_LIST * fixed linter complaints * remove unused imports * cleanup for SonarQube and linter * remove unused imports * import parent wrappers directly so that PyCharm can follow class hierarchy * per #2612, add support for processing groups of forecast leads * remove call to prune_empty function and ci-run-all-diff * fixed check on DataFrame to properly check for an empty one * remove prune_empty function that is no longer used * update conda environment requirements for metdataio * set label to Group for LEAD_SEQ_ if LEAD_SEQ__LABEL is not set (instead of erroring), use forecast lead label for lead if set, log lead group label and formatted list of leads for each run * increase timeout for request from 60 to 90 * handle exception and output an error message with a suggestion to rerun the failed GHA jobs when a hiccup with DockerHub prevents the list of available Docker data volumes from being obtained. FYI @jprestop * cleanup to address SonarQube complaints, move call to exit out of main function so a script calling the function does not exit when the function errors * cleanup to appease SonarQube * per #2612, add support for creating groups of forecast leads by specifying a list of forecast leads and a time interval to create groups of leads * per #2612, add unit tests for function that gets groups of forecast leads and added tests for new groups capabilities * various refactoring to satisfy SonarQube complaints * renamed config LEAD_SEQ_DIVISIONS to LEAD_SEQ_GROUP_SIZE and LEAD_SEQ_DIVISIONS_LABEL to LEAD_SEQ_GROUP_LABEL * per #2612, add documentation for new config variables including examples * fix empty check for DataFrame * fix rst typo * minor cleanup to satisfy linter * Per #2612, if runtime freq is RUN_ONCE_PER_LEAD and groups of forecast leads are specified, then the wrapper will be run once per lead GROUP instead of once for each unique lead in the lead groups. Update unit test to reflect new behavior * update versions packages to match METplotpy requirements and update call to cartopy feature download script -- see https://github.com/SciTools/cartopy/pull/2263 * Feature #2622 forecast climatology (#2696) * remove support for old environment variables CLIMO_MEAN_FILE and CLIMO_STDEV_FILE that were used in METplus versions earlier than v4.0.0 * fixed SonarQube bugs * add better error checking for DockerHub queries that occasionally fail in GHA * update pycharm files * per #2622, support setting climo_mean and climo_stdev dictionary values inside fcst and obs dictionaries for grid_stat, ensemble_stat, point_stat, and series_analysis. Quietly add support for setting climo dictionaries inside the ens dictionary for gen_ens_prod -- this support is not needed because there is only 1 dictionary that may contain the climo dicts, but adding support for completeness so it can be assumed that {APP_NAME}_{DICT}_{SUBDICT}_{VAR} is supported, e.g. GEN_ENS_PROD_ENS_CLIMO_MEAN_REGRID_METHOD * turn on use cases that started failing from MET changes * turn off all use cases from push events * reorder assert so that expected vs. actual in PyCharm diff are accurate * remove commented code * added unit tests for climo VAR variables that were missing * add VAR variables used to specify climo_mean/stdev field values * bugfix to allow empty list for diag_info_map.diag_name * another hotfix to allow empty list for diag_info_map.match_to_track * Feature #2651 SeriesAnalysis -aggr argument and new use case (#2701) * exclude build directory from PyCharm project * refactor to satisfy SonarQube complaints * Per #2651, add support for setting -aggr argument with file path. Refactor functions to resolve SonarQube complaints. Enhance unit tests to test multiple init times and -aggr argument * add new config variables to basic use case * change settings to match MET unit test * added new config variables to set -aggr argument in SeriesAnalysis wrapper to documentation * Per #2651, add new use case to demonstrate using the -aggr argument to SeriesAnalysis * turn on new use case to test it in GHA * remove blank line * turn off use case to prepare for PR * Bugfix #2705 develop - SeriesAnalysis fix time substitution into field info (#2709) * fix SonarQube complaints * Per #2705, set correct time information in field info by using the time info used to find the input files (self.c_dict['ALL_FILES']) instead of relying on parsing the time info from the other input files (fcst vs. obs) * clean up unused code after bugfix for #2705 * Feature 2647 use case mv mode for rrfs (#2706) * establish documentation, need to fill out with provided info * updated documentation, needs to have img path and output path fixed * updated output strings, still need to update pic link * removing pound sign to render correctly * Revert "removing pound sign to render correctly" This reverts commit ffe607a96fd33dc5ba15a479f76778dcba7e9656. * targeted removal of pound sign with bullet points section. * Per #2647, attempt to fix syntax error * Per #2647, adding back contents with formatting changes to test if it works * Per #2647, adding back contents in a different location to try to resolve syntax error * Per #2647, testing pipe for horizontal scroll bar * Per #2647, removed the pipe symbol and are attempting to fix syntax error again * Per #2647, reverting to original failed attempt of a table of contents * Per #2647, trying new formatting * Per #2647, fixing new formatting * Per #2647, attempt formatting changes * Per #2647, reverting back to original state * added to automated testing, updated docs, config file * added docs back in * added image, new quick search term, disabled auto tests * testing use_case_groups.json to see if/how this fails * Updated to disable use_case_groups.json run * added use case to list of exceeding disk space --------- Co-authored-by: lisagoodrich <33230218+lisagoodrich@users.noreply.github.com> Co-authored-by: Lisa Goodrich Co-authored-by: Julie Prestopnik * Bugfix 2675 move to pyproject.toml (#2699) * 2675: move to pyproject.toml * 2675: update toml, remove setup.py ref * bugfix 2675: reinstate VERSION file * bugfix 2675: hardcode version in pyproject.toml * use relative versions * slightly modify import to make it easier to move produtil into metplus * read version from version file * disable install of run_metplus script since it is broken * only install metplus and sub-packages, add version files to package data (parm cannot be added because it is non-package data) * add newline back * install produtil package so metplus can be imported in python * remove unused import * move produtil under metplus to differentiate it from the full NCEP produtil package and isolate it to metplus when installing metplus as a package -- previously produtil would be installed as its own package when metplus is installed * fixed more imports * Create links for parm directory and scripts/run_metplus.py under metplus so the parm files and run script will be installed as part of the metplus python package. Modified config_metplus to find the parm directory from under metplus to be consistent between running via python package and via scripts * Exclude parm from sonar sources because it will be found under metplus. Add metplus/parm/** to list of coverage exclusions * fixed more imports * add parm back to list of sonar sources --------- Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> * Removing stated location of the version selector menu * Feature 2489 update docs (#2717) * adding new Literal Include section * under bold section adding "Environment" to Variables * Adding Substitution References section * removing blank space * fixing code typos * another typo * Update docs/Contributors_Guide/documentation.rst Co-authored-by: Julie Prestopnik * Update docs/Contributors_Guide/documentation.rst Co-authored-by: Julie Prestopnik * Update docs/Contributors_Guide/documentation.rst Co-authored-by: Julie Prestopnik * Update docs/Contributors_Guide/documentation.rst Co-authored-by: Julie Prestopnik * Update docs/Contributors_Guide/documentation.rst Co-authored-by: Julie Prestopnik --------- Co-authored-by: Julie Prestopnik * Feature #2540 Point2Grid config file (#2580) * per #2540, add support for setting values in a config file for Point2Grid wrapper -- removed -qc flag argument in favor of setting obs_quality_inc in the config file * Added support for setting time_offset_warning now that the changes for other wrappers was merged into develop. Also added support for MET_CONFIG_OVERRIDES for Point2Grid * per #2540, set -goes_qc command line argument from POINT2GRID_GOES_QC_FLAGS and continue to support legacy POINT2GRID_QC_FLAGS. Other fixes to resolve incorrectly resolved conflicts from previous commit merging develop into branch * per #2540, remove quality_mark_thresh from config file because it has been removed from MET point2grid * Per discussion in PR #2580, remove time_offset_warning support from tools that don't actually use the value -- to add support for a new wrapper, update metplus/util/constants.py TIME_OFFSET_WARNING_WRAPPERS to include the wrapper's name * removed env vars for time_offset_warning for wrappers that no longer set it * install ImageMagick convert because it appears to no longer be available in the image that runs the unit tests (ubuntu 24.04?) * Bugfix metdbload config (#2728) * Update METdbLoadConfig.xml A schema is now used to validate all XML specification files and elements must adhere to a particular order. The element follows the headers and load elements and preceeds the folder tmpl element. * Update use_case_groups.json temporarily run tests for the short range use cases * Update use_case_groups.json Return all the short_range use case test "run" values back to false * install convert because some tests fail if it is not available * Feature #1882 docs: new use case that uses METplus Analysis (#2726) * Fixing mistake * Fixing reference * Fixing link * Updating sections * clean up new content --------- Co-authored-by: Christina Kalb * Feature 918 add use case template (#2690) * updated template, 1st part * added more to template, updated internal references. Needs final two cats * finished updating template * updated syntax, length of lines * moved example to separate file, testing indents for discussion * added new collapse menu for MET configs, documentation on adding keywords, testing 2 separate options to stop paragraph jumping during bullet lists * updated spacing used for 2nd line indentation * Update docs/Contributors_Guide/add_use_case.rst Co-authored-by: lisagoodrich <33230218+lisagoodrich@users.noreply.github.com> * Minor changes, updates hyperlink to template to be directly to the file and not to the directoy in GitHub. * Update add_use_case.rst * Update docs/Contributors_Guide/add_use_case.rst Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> * Update add_use_case.rst * Missing space in hyperlink. * Changes to use a keyword to assist users in checking off each section they should review and also PR reviewers in ensuring each section has been visited and reviewed by the contributor. * updated Python section to User Scripting --------- Co-authored-by: lisagoodrich <33230218+lisagoodrich@users.noreply.github.com> Co-authored-by: Daniel Adriaansen Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> * Feature #2727 point_weight_flag and obtype_as_group_val_flag (#2730) * Per #2727, add support for setting point_weight_flag * Per #2727, add support for obtype_as_group_val_flag * per #2714, deprecate master_metplus.py * Update VERSION * Feature #2710 - clean up use cases (#2711) * per #2710, remove line that has no effect because next line overwrites value * per #2710, fix bug in if/else block where both were doing the same thing. Removed commented code and create variable for constant to satisfy SonarQube * updated config file commands * updated level of winds to capture change in script --------- Co-authored-by: j-opatz * Feature #2682 Component version lookup (#2733) * per #2714, deprecate master_metplus.py * exclude metplus/scripts from SonarQube scan to prevent incorrect duplicate code error since script lives in ush * added Dockerfile to build image with MET/METplus and METplus Analysis Python packages * start of script to get versions of METplus components * per #2682, created METplus component version lookup table and functions to get formatted version of requested components * add descriptions for tests * add update information and made script callable * made script executable * add workflow to trigger on release and create dtcenter/metplus and dtcenter/metplus-analysis Docker images * add scripts used by new workflow * removed build hook scripts since we will be building images via GHA * change logic to get MET version using component version script instead of build hook file that just adds 6 to major version * add missing shebang * make script callable directly and make output component a required input * pass LATEST_TAG to push script * ensure only 1st line of version file is read to avoid newline * turn on use case to test * turn off use case * Feature #2735 v6.0.0 beta6 (#2736) * change version for beta6 release * added script to help generate release notes for development releases * add default title for issues and added documentation issue template * sort issues by number, improve formatting for release notes title, add logging to alert users what is happening when timely github queries are running * added release notes for beta6 release * Add option to component version script to return 'develop' if the input version is a beta or rc version to preserve previous behavior of GHA scripts. Update GHA scripts to use new option * applied suggestions from feedback in PR #2736 * bold some release notes * update version for development towards rc1 release * fix logic to determine offical X.Y.Z release * Feature #2739 labels (#2740) * Per #2739, add NRL reporting label and add METplus-Internal to the list of repositories to be updated. * Per #2739, add info about labels to the Contributor's Guide. * Per #2739, update the list of METplus repos in process_labels.sh but remove them from the other shell scripts to avoid keeping the same list updated in multiple spots. * Per #2739, move label details from the README to the Contributor's Guide * Updated release notes adding this issue: Develop an RST template for use cases * Feature 2716 existing builds (#2748) * adding the first 4 paragraphs * fixing web addresses * Highest level of dropdown menus added * NCAR machines dropdown menus * CASPER dropdown menu * web addresses can't be bolded. Fixing dropdown problems * blank line problem * italics date adding Derechio parts * DERECHO dropdown * fixing problems * fixing mor problems * fixing bolding * trying to fix web address * adding NCAR RAL MACHINES and trying to fix website issue * NCAR/RAL Internal Development dropdown * adding remaining dropdown structure * WCOSS2 dropdown * HERA dropdown * fixing last updated to be consistent * HERCULES dropdown * fixing pip * orion dropdown * JET dropdown * GAEA dropdown * fixing typo * Community machines, Docker Hub & AWS * fixing typo * Changing to "Users should create a file like" * to set a personalized input... * adding last updated and Docker Hub web links * Updating Jet information after recent install and changing some formatting for consistency * Removing bolding for consistency * creating a separate file for existing builds to add to 2 areas make it section 2.1 where METplus Components Release Note Links and Release Notes * adding existing_builds as an include * Rename existing_builds_rst to existing_builds.rst I had an underline instead of a period * changing to a section, not a chapter * trying a different section * changing existing builds to an include file * Update docs/Users_Guide/existing_builds.rst Co-authored-by: Julie Prestopnik * Update docs/Users_Guide/existing_builds.rst Co-authored-by: Julie Prestopnik * removing existing builds completely from the Release Guide --------- Co-authored-by: Julie Prestopnik * Update release-notes.rst * add custom string to time info dictionary for substituting values into the field info string * Feature #2742 develop fix StatAnalysis to set fcst_lev/obs_lev (#2760) * Feature 2763 existing builds (#2765) * Per #2763, updated build for Casper * Per #2763, updating Orion * Bugfix #2762 PCPCombine derive field info (#2764) * per #2762, change derive mode to add field info for each file instead of adding the first field info only * Per #2762, improve how the accumulation amounts are handling using relativedelta to ensure that the correct number of seconds are used for each search window when using inconsistent time intervals like months or years * Bugfix #2762 part 2 -- fix PCPCombine leap year issue (#2766) * remove check if script is master_metplus.py because it has been deprecated * per #2762, fix bug that caused failure depending on valid time relating to leap years * Feature #2754 rc1_main_vX.Y (#2755) * Per #2754, update instructions to create the main_vX.Y and main_vX.Y-ref branches (when applicable) for the first release candidate rather than waiting until the official release. Note that I deleted the push_release_branch.rst instructions since it's not needed. The preceeding instructions execute the push and these are redundant - even for METplus which has custom instructions. * Per #2754, add a note to make the detail stand out better * Per #2754, make the GitHub release instructions more explicit to match the names of the actual fields on GitHub. * Per #2754, not changing comment, just using more consisent formatting options. * Per #2754, change example from 'betaN' to 'rc1' since this step is only run for the rc1 release. * Per #2754, use consistent single quotes instead of italics for formatting. * Updates to METplus instructions to properly handle version numbers for rc1 release. Update METplus Official release instructions to use main branch and update version number for official release since main branch will contain -rcN version numbers * update instructions to checkout correct branch * clarify next deveopment version can be X+1.0 OR X.Y+1 * update version table for other components -- this could likely be refactored to make the version file path a variable and include the same update_version_on_develop.rst file for METplus, METcalcpy, METdataio, METplotpy, and METviewer --------- Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> * Resolving use case template formatting problems and adding dropdowns * Resolving use case template formatting problem * Fixing formatting * Updating to add language if no Python embedding or User Scripting is used. * Feature #2562 version lookup documentation and automated tests (#2773) * set default input version to the latest official release, which is the 2nd highest coordinated release version number in the version lookup table. This assumes that the highest version is what is being developed towards * per #2562, update Release Guide to include instructions for updating the component versions lookup table in the METplus repository * Per #2562 and #2597, replace automated test logic that used manage_externals to use component version lookup to get versions of METplotpy/METcalcpy/METdataio * clean up SonarQube complaints * turn on some use cases to test changes * fix clone * do not get python dependencies for METplotpy/METcalcpy/METdataio installs * use full path to package to import * clone METplus Analysis repos on the same level as METplus to match previous location using manage_externals, remove manage_externals config files that are no longer used by the automated tests * clean up linter complaints * turn off use cases after confirming that they run successfully * clean up usage statement formatting * remove info about updating manage_externals config files used in automated tests that have been removed * added description of component versions script to Contributor's Guide and added stubs for other utility descriptions * change format of example results and add text to describe what is being demonstrated from each example * fix formatting * fixed bad variable replacement * Update docs/Contributors_Guide/utilities.rst --------- Co-authored-by: Julie Prestopnik * Feature 2737 release instruct (#2775) * Per #2737 updating version instructions * Per #2737, update version instructions for METcalcpy * Per #2737, renaming development file * Per #2737, update version instructions for METplotpy * Per #2737, updating version instructions for METdataio * Per #2737, updating version instructions for METviewer * Per #2737, adding pyproject.toml to the list of files to update * Per #2751, this work was actually completed already, adding text to make the directions more clear. (#2776) * Made minor modifications for consistency * Feature 2339 dependencies (#2777) * Per #2339, update documention to include info about dependencies and coordinated release announcement * Per #2339, renaming file and header * Per #2339, correcting typo in formatting * Per #2339, updated formatting * Per #2339, updating additional dependencies * Per #2300, add Python package disclaimer for the METplus components (#2782) * Feature #2666 sonarqube (#2784) * Unrelated to #2666 but found while working on it, update the expected output file names for the TCDiag basic met_tool use case. * Per #2666, add details about SonarQube. Still need to proof-read and wordsmith * Per #2666, more SonarQube details * Per #2666, fix up anchor links, switch to *'s for bullets like we use everywhere else, and massage the wording * Apply suggestions from code review Co-authored-by: Julie Prestopnik --------- Co-authored-by: Julie Prestopnik * Update the instructions for official MET relases with a reminder to create a new version of the MET flowchart. * Feature #2772 Apptainer documentation (#2783) * update version of action to latest * per #2772, added instructions to run a METplus use case using Apptainer * Apply suggestions from code review Co-authored-by: Dan Adriaansen * bugfix dtcenter/MET#2899 prevents differences in output from use case, so removing the 'disabled' item to run use case in GHA * hotfix: remove quotes around input files because logic that obtains python embedding commands as input file arguments handles quotes * fix tests to match new expected commands --------- Co-authored-by: Dan Adriaansen * Feature #2560 Fire GridStat use case (#2769) * added config file and python scripts for fire weather use case * Improved logic if multiple files were found to use the closest file to the valid time. Assuming that the files are in order, it will use the first file if multiple files match the valid hour or the last file if multiple files match 1 hour prior to the valid hour. This does not factor in if files before and after the valid time were found. Modifications are needed to obtain the correct file if, for example, the valid time is 12Z and there are 1258 and 1159 files available -- in this case the 1258 file will be used even though 1159 is closer. * rename use case and update configuration to read data from appropriate directories * per #2560, add new use case to automated tests * turn of use case to prepare for PR * removed comments * added in-progress use case documentation - still needs updates * fix formatting * more formatting * more doc formatting and cleanup * remove notes from sections that were updated * doc cleanup * add thumbnail for new use case * add description of fire use case category * turn off use case * fix SonarQube complaints * update use case doc to match use case template * minor formatting change to match template * update path for kml schema file * use wildcard to avoid having to reference namespace that includes insecure protocol * Update documentation to resemble template --------- Co-authored-by: j-opatz <59586397+j-opatz@users.noreply.github.com> * Feature #2597 remove manage_externals and its config files (#2785) * Feature #2757 allow missing docs (#2786) * per #2757, add documentation for allow missing input logic * fix incorrect link to glossary item * fix typo * Per #2757, add info and examples for allow missing input logic * Feature 2741 add version (#2793) * adding contents and version * adding contents and version * pbl and precip directory updates * s2s directory updates * short_range directory updates * adding contents and version numbers to last 3 directories * Removing "UPDATE_SECTION_CONTENT" text --------- Co-authored-by: Julie Prestopnik * Update component version script to NOT return develop for an rcN version unless an option is set to override that behavior (in case we need to change how rc is handled). Update automated scripts to get the correct MET Docker images for RC versions (get met-dev/main_vX.Y for RC, get met-dev/develop for betas or dev version, get met/X.Y-latest for official releases). Updated unit tests for component version script to ensure correct behavior occurs * Feature #2789 v6.0.0-rc1 release (#2792) * Per #2779, assigned a number to all use cases that are not run in GHA and added a reason to describe why the use cases have been disabled * adding location information * updating location information * Revert "adding location information" This reverts commit cc6d18506b12d30b931276163f6da52dfaaebb4f. * Revert "updating location information" This reverts commit 5920779fca3a1e5fd1b82538a9b206ef97f76320. * added location of input data that is not found with rest of input data for use case category * added information on how to run generate_release_notes.py script and updated script to clean up some formatting issues * Per #2789, added release notes for rc1 release * update version for rc1 release * fixed broken commands in use case scripts --------- Co-authored-by: lisagoodrich <33230218+lisagoodrich@users.noreply.github.com> * update version for development toward next release first beta * Updating RC1 release instructions for MET to create the vX.Y input test data directory prior to creating the main_vX.Y-ref branch. * prevent error when input version requested does not exist in lookup table * handle if input version is develop * Feature 2741 update use cases (#2795) * updating template first pass * removing backslash * removing extra # symbol * removing spaces. Keep as is * removing spaces for consistency * adding changes to match template * fixing spacing * fixing spacing take 2 * Per #2741, correcting typo in OUPUT_BASE in template file * Per #2741, correcting typo in OUPUT_BASE * Per #2741, updating sections based on template and template description * Updated language in several sections, needs additional work on output and config file * updating for consistency * Updating file based on suggestions from George * Updating with suggestions from George * Updating file based on suggestions from George * reviewing with Julie * Per #2741 attempting to resolve errors * Per #2741, updating template to fix rendering problems * Per #2741, fixing formatting * Per #2741 attempting to resolve warnings * Per #2741, resolving error * Resolving formatting problems with template * Fixing formatting problems * updating for consistency * fixing formatting * removing extra spaces * Updating formatting * updating for consistency * adding METplus Workflow times * modifications * Update GridStat_fcstCESM_obsGFS_ConusTemp.py Added some information to the use case * Update MODE_fcstCESM_obsGPCP_AsianMonsoonPrecip.py Updates * Added an extra blank line * updating for consistency * fixing spacing * adding space * removing Update content * updating for consistency * adding a period * updating for consistency * updating for consistency * updating for consistency * updating for consistency * adding return * replacing underlines * replacing return? * fixing blank line * more attempts to fix warnings * updating for consistency * adding double colons and stuff * trying stuff * trying extra spacing * Changing Datasets Location info to match template * Per #2741, updating formatting and wording * Per #2741, updating after reviewing * Per #2741, attempting to resolve errors * updating for consistency * fixing brackets and section breaks * adding return * Per #2741, updating after reviewing * updating for consistency * fixing spacing * adding a pound * Updating wording for User Scripting * Updating the text for User Scripting * Updating User Script section * Updating text for User Scripting * Updating text * adding location information * adding location information * updated first cloud file, will use for remaining cloud use cases * updated language in numerous use case doc files * updated final marine use case --------- Co-authored-by: Julie Prestopnik Co-authored-by: j-opatz <59586397+j-opatz@users.noreply.github.com> Co-authored-by: Christina Kalb Co-authored-by: j-opatz * fix typo * update timeline to include actual date of rc1 release * added missing script to create mp_analysis conda environment for use case tests * add mp_analysis conda env to workflow to create conda environments for DockerHub * Feature 2741 running metplus (#2801) * marine_and_cryosphere directories * fixing underlining * trying to fix warning msg. * trying to fix warning msg. again * Removing space before i.e. * updating medium_range and pbl directories * deleting empty file * removing lots of old text * precipitation files * removing junk file * removing extra # * add return * updating 3 dirs s2s, space_weather and unstructured_grids * updating short_range files * adding tc_and_extra_tc files * removing defunct -c option * Updating Datasets section * Added needed return character before "#Pass" * Added /path/to/user_system.conf to end of run_metplus.py line * Added a comma after "command line" * Updating file path * Added fields for empty Satasets section --------- Co-authored-by: Julie Prestopnik * Updated HERA information * Resolving error * Updated jet and hera * prevent error in the command to pull the changelog file when it does not exist yet * Feature 2751 develop release updates (#2798) * Adding period to end of sentence. * Release Guide (develop) - updating existing builds (#2806) * update existing builds for upcoming coordinated 6.1 (beta1) release * added template for existing builds for next dev cycle and added it to ignore list * Update release guide instructions to set the new location of the Existing Builds page on the Downloads page. Update instructions for creating a new existing builds page to instead copy the template and replace values in the develop branch * added a step to update the development timeline info for an official release * Updating existing_builds files * changed Docker Hub to DockerHub * added step to update the DockerHub info on the Existing Builds page * change version to official release because RAL-IT doesn't install development releases on RAL machines --------- Co-authored-by: Julie Prestopnik * prevent error when merge commit messages are too long * prevent error when change summary text contains backticks * Update METplus-6.0.0 release date from 12/11/24 to 12/18/24. * Feature 2741 dropdowns (#2808) * marine_and_cryosphere directories * fixing underlining * trying to fix warning msg. * trying to fix warning msg. again * Removing space before i.e. * updating medium_range and pbl directories * deleting empty file * removing lots of old text * precipitation files * removing junk file * removing extra # * add return * updating 3 dirs s2s, space_weather and unstructured_grids * updating short_range files * adding tc_and_extra_tc files * removing defunct -c option * added dropdown menus for marine_and_cryosphere * fixing problems and dropdowns for medium_range * fixing new problems * dropdowns for pbl and precip * adding double colons * Attempting to resolve errors * dropdowns for s2s and s2s_mjo * adding dropdowns for short_range files * final dropdown directories * Update docs/use_cases/model_applications/marine_and_cryosphere/GridStat_fcstRTOFS_obsGHRSST_climWOA_sst.py removing note Co-authored-by: Julie Prestopnik * Apply suggestions from code review adding all of the suggestions Co-authored-by: Julie Prestopnik * Update GridStat_fcstRTOFS_obsGHRSST_climWOA_sst.py ensuring literal include is in dropdown menu * Update GridStat_fcstRTOFS_obsOSTIA_iceCover.py ensuring literal include is in dropdown * ensuring literal include is in dropdown menu * marine_and_cryosphere MET Configuration dropdown fixes * remaining dropdown directories for MET Configuration * Julie changing dropdowns Co-authored-by: Julie Prestopnik * HEAD needed to be removed * There are not MET tools used in this use case * There are no MET tools used in this use case * Apply suggestions from code review Co-authored-by: Julie Prestopnik * Per #2741, few small changes * updates from pull request discussion * removing space Co-authored-by: Julie Prestopnik * fixing dropdown Co-authored-by: Julie Prestopnik * removing bash Co-authored-by: Julie Prestopnik * update from Julie Co-authored-by: Julie Prestopnik * Fixing error * lots of typos Co-authored-by: Julie Prestopnik * Adding period * Adding colon * Added period * Added period * Added period * typos and more Co-authored-by: Julie Prestopnik --------- Co-authored-by: Julie Prestopnik * Feature #511 lists in command line single config overrides (#2815) * exclude metplus/scripts dir from PyCharm project to prevent incorrect duplicate code warnings with files that are sym linked * to expand on #2772, updating instructions to include note to set tmp directory for apptainer to prevent issues pulling large images that require a lot of temp space * per #511, add unit test for expected behavior to support comma-separated lists in a command line single config override that should fail until fix is made * add a test to ensure that the -c argument is properly ignored since it is can be used in old use cases that were created when the argument was required * per #511, add support for command line single config overrides to include values that are lists. Simplify logic to parse arguments to strip out -c/--config/-config arguments and skip check/error if argument is invalid because it is already handled in the metplus_config setup step that parses the arguments * added unit test to ensure that an invalid command line argument causes the appropriate failure from run_metplus.py * Feature metplus 2780 dedication (#2819) * adding In Memoriam section * Changing to "This Coordinated Release is dedicated to" removing version * adding links * fixing Randy's link * Per #2780, change case of words and add comma after year --------- Co-authored-by: Julie Prestopnik * Feature #2816 enhance workflow dispatch testing (#2818) * per #2816, update title of workflow displayed in Actions tab to display the repo that triggered the workflow on workflow_dispatch events. Preserve the behavior of pull_request and push events by displaying the PR title or head commit message respectively * fix logic for run-name * Update event info to note that the workflow was triggered manually if the repo/title was not provided. Update description of workflow_dispatch argument to describe what it can be used for when triggering manually. Only run step to build URL to commit that triggered workflow if it was triggered from an external repo * add optional argument for workflow_dispatch to specify the title of the workflow run * revert change to repository argument description since title argument was added to handle custom workflow run titles * Feature #2814 develop - doc single config overrides (#2825) * per #2814, add documentation to describe how to set a single config variable on the command line * fixed typos and formatting issues * move new content to top of chapter and reword intro sentence * change label and add link to user env var section * per #2814, added info and examples for single config overrides with a list of values * Sorts the list of FLUXNET csv files returned from glob. (#2836) * Bugfix #2830 develop fix missing log output (#2841) * Per #2830, skip closing of log handlers for METplusConfig objects that are created for copying values for process list instances so they are not closed before the end of the run. * remove some output directories after tests are run * Updated METexpress versions in component_versions.py (#2842) * added script to regroup release notes for official release by parsing dev release notes. improve naming of drop downs for generating dev release notes to be consistent * improve script to handle different formatting for categories with no issues * per suggestion from @bikegeek, output 'None' if there were no items under a category * reorder instructions to follow more natural progression * update instructions for updating ReadTheDocs based on the changes to the RTD web interface * add next coordinated release to version lookup table * move instructions to create directory for data for new dev cycle on DTC web server to be done for the rc1 release instead of after the official release * update versions of packages that have vulnerabilities * Feature #2586 GenVxMask improvements (#2833) * resolve some SonarQube complaints * per #2586, added function with tests to properly parse list of command line arguments that can now contain comma-separated lists that should not be split up into separate items * add support for {app}_{data_type}_FILE_WINDOW_BEGIN/END, e.g. GEN_VX_MASK_OBS_FILE_WINDOW_BEGIN. This just adds support for an additional variation of the config variable names * add support for an empty label for input templates * update wrapper to be consistent with other wrappers wrt finding input files, progress towards #2492. Allow file window range to be specified separately for mask and input files. Other cleanup to move towards consistent wrappers with fewer wrapper-specific overrides of functions like get_command * update unit tests to align with changes for #2492 * add documentation for config variables that are newly supported to allow file window range to be specified separately for mask and input files * renamed GEN_VX_MASK_OBS variables to be GEN_VX_MASK_INPUT as suggested by @JohnHalleyGotway in PR review * fix logic to properly read input files by handling inputs that support multiple inputs with labels (used by GridDiag and UserScript wrappers) and typical inputs (all other wrappers). Prior to this change only input templates that have the FCST or OBS identifier were read properly via get_input_templates * Feature 2628 documentation updates (#2817) * Feature #2844 release_guide_dropdowns (#2847) * Work in progress adding instructions dropdowns to the release guide * Per #2844, more dropdowns * Per #2844, add instruction dropdowns for all MET instructions * Per #2844, more dropdowns. * Per #2844, use instruction dropdowns throughout * Per #2844, remove METexpress AWS instruction since its no longer relevant. * Remove accidentally committed .DS_Store file. * Update the MET Release Guide instructions about the dependent library tar files. * Minor formatting change * update URL for posting sample data * Doc-only change to rename the 'Code support' section as 'User support' based on 1/9/2025 decision at the METplus-Analysis Tools project meeting * fixed typo * Add 2 new 'alert' labels to flag changes that modify config options and/or output formats. * Update the common labels by adding the 'pull request:' prefix, one new reporting label and update scripts to run /bin/bash instead of /bin/sh, which now points to dash on seneca. * Feature #2827 SeriesAnalysis gradient (#2835) * Add 'requestor: NOAA/NWS' label for use in METplus Discussions. * Feature #2758 SonarQube (1) (#2866) * test that this file is not needed anymore because RTD controls the version selector * various changes to appease SonarQube complaints * remove deprecated docker command * resolve more SonarQube code smells * update URL for posting sample data * Add note to the Release and Contributor's Guide pointing readers to the develop branch content. * Updating common_labels.txt in the METplus develop branch * Update the details of the 'pull request' labels. * Feature #2781 PairStat wrapper (#2872) * update release guide for METplus to create data location for next release on mohawk when creating rc1 release instead of after official release * cleanup * per #2781, start first implementation of PairStat wrapper * appease SonarQube by increasing code coverage for component versions script * add new wrapper to lookup dictionary * per #2781 add wrapped MET config file * use runtime freq logic to find input files * start unit tests (broken) * reorder assert to align expected and actual values with PyCharm notation * update unit tests * rename field info variable 'field' to 'pairs' to match Pair-Stat naming convention. Add command line arguments to pair_stat command * reorder assert to align actual and expected in PyCharm * refactor function to get start/end times to prevent error when using INIT/VALID_LIST and use time_generator to be more consistent and reduce duplicate code * change many wrappers to be consistent with finding input files. Errors are now thrown when any input file is not found, checking other input types even when another input was not found. This increased errors reported in unit tests, so updated tests to reflect this. * remove line that is not needed * fix bug introduced with recent changes and run use cases that failed to test that the fix is correct * fix multi-variate MODE run to pass all fields to the call instead of just the first one * update SeriesAnalysis test to use logic that is actually used when running * turn off use cases that now succeed and turn on use case to test fix to multi-variate mode * turn off use case after confirming that it now runs successfully * refactor logic to satisfy SonarQube to reduce cognitive complexity and remove unused imports * remove functions that are not used * handle -pairs flag like other command line args for input files. Add documentation to new functions * updates to new wrapper, add documentation, basic use case stubs. create function for duplicate code to handle land_mask and topo_mask in PointStat and PairStat wrappers * remove unused import * remove config variable that is no longer used in MET * remove match_month support from climo_mean/stdev * remove obs_valid_beg/end from command line args because it is not supported by pair_stat * remove test config files that are not being used * configure basic use case to run command that is being used to test/develop the app * error if invalid tool name is provided * add missing tests * remove support for config variables that did not make it into the final implementation of the MET pair_stat tool * fix formatting on a few config values * correct input file info * turn on use case to test * fix command line argument for output directory that changed since the last test * refactor and clean up to reduce SonarQube issues * update use case to prevent redundant runs of SeriesAnalysis(run_two) by only setting custom loop list for the first instance of SeriesAnalysis * Fix bug that prevents correct field information from being set when multiple fields are read from the same file. Removed unused function * per feedback in PR #2872, remove support for setting output_prefix * add PairStat to list of wrappers that use the MODEL config variable * per feedback in PR #2872, modify wrapper behavior to change output path from a directory to a filename base that will be used to write output files with various extensions, e.g. _mpr.txt or .stat * remove output_prefix from tables * regroup met_tool_wrapper use cases * added entry to update truth change log: develop #2872 --------- Signed-off-by: dependabot[bot] Co-authored-by: George McCabe <23407799+georgemccabe@users.noreply.github.com> Co-authored-by: j-opatz <59586397+j-opatz@users.noreply.github.com> Co-authored-by: John Sharples <41682323+John-Sharples@users.noreply.github.com> Co-authored-by: bikegeek <3753118+bikegeek@users.noreply.github.com> Co-authored-by: metplus-bot <97135045+metplus-bot@users.noreply.github.com> Co-authored-by: Christina Kalb Co-authored-by: Daniel Adriaansen Co-authored-by: Hank Fisher Co-authored-by: lisagoodrich <33230218+lisagoodrich@users.noreply.github.com> Co-authored-by: Julie Prestopnik Co-authored-by: John Halley Gotway Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mallory Row Co-authored-by: Lisa Goodrich Co-authored-by: j-opatz Co-authored-by: Molly Smith --- .github/parm/use_case_groups.json | 7 +- .github/update_truth_change_log.txt | 1 + docs/Users_Guide/glossary.rst | 818 ++++++++++++++++-- docs/Users_Guide/quicksearch.rst | 2 + docs/Users_Guide/wrappers.rst | 801 ++++++++++++++++- .../met_tool_wrapper/PairStat/PairStat.py | 107 +++ .../met_tool_wrapper/PairStat/README.rst | 2 + .../dev_tools/add_met_config_helper.py | 3 + .../tests/config/GridStatConfig_prob_precip | 222 ----- internal/tests/config/STATAnalysisConfig | 94 -- internal/tests/config/ascii2nc_mask_grid.conf | 55 -- internal/tests/config/ascii2nc_mask_poly.conf | 57 -- internal/tests/config/ascii2nc_mask_sid.conf | 55 -- .../tests/config/ascii2nc_multi_file.conf | 58 -- .../tests/config/ascii2nc_single_file.conf | 55 -- internal/tests/config/grid_gefs_prob.conf | 75 -- .../test_component_versions.py | 19 + internal/tests/pytests/conftest.py | 55 +- .../util/met_config/test_met_config.py | 6 +- .../util/time_looping/test_time_looping.py | 70 +- .../test_ensemble_stat_wrapper.py | 79 +- .../gen_ens_prod/test_gen_ens_prod_wrapper.py | 22 +- .../wrappers/grid_diag/test_grid_diag.py | 161 ++-- .../grid_stat/test_grid_stat_wrapper.py | 43 +- .../wrappers/ioda2nc/test_ioda2nc_wrapper.py | 15 +- .../wrappers/mode/test_mode_wrapper.py | 2 +- .../pytests/wrappers/mtd/test_mtd_wrapper.py | 6 +- .../pair_stat/test_pair_stat_wrapper.py | 753 ++++++++++++++++ .../wrappers/pb2nc/test_pb2nc_wrapper.py | 15 +- .../point_stat/test_point_stat_wrapper.py | 134 +-- .../series_analysis/test_series_analysis.py | 86 +- .../wavelet_stat/test_wavelet_stat.py | 10 +- internal/tests/use_cases/all_use_cases.txt | 1 + metplus/component_versions.py | 12 +- metplus/util/constants.py | 1 + metplus/util/met_config.py | 1 - metplus/util/time_looping.py | 37 +- metplus/wrappers/command_builder.py | 4 +- metplus/wrappers/compare_gridded_wrapper.py | 155 ++-- metplus/wrappers/ensemble_stat_wrapper.py | 74 +- metplus/wrappers/grid_diag_wrapper.py | 41 +- metplus/wrappers/grid_stat_wrapper.py | 2 - metplus/wrappers/mode_wrapper.py | 34 +- metplus/wrappers/mtd_wrapper.py | 314 ++++--- metplus/wrappers/pair_stat_wrapper.py | 248 ++++++ metplus/wrappers/point_stat_wrapper.py | 35 +- metplus/wrappers/runtime_freq_wrapper.py | 311 +++++-- metplus/wrappers/series_analysis_wrapper.py | 55 +- metplus/wrappers/wavelet_stat_wrapper.py | 19 +- parm/met_config/PairStatConfig_wrapped | 168 ++++ .../met_tool_wrapper/PairStat/PairStat.conf | 212 +++++ ..._climoStandardized_MultiStatisticTool.conf | 4 +- 52 files changed, 3903 insertions(+), 1713 deletions(-) create mode 100644 docs/use_cases/met_tool_wrapper/PairStat/PairStat.py create mode 100644 docs/use_cases/met_tool_wrapper/PairStat/README.rst delete mode 100755 internal/tests/config/GridStatConfig_prob_precip delete mode 100644 internal/tests/config/STATAnalysisConfig delete mode 100644 internal/tests/config/ascii2nc_mask_grid.conf delete mode 100644 internal/tests/config/ascii2nc_mask_poly.conf delete mode 100644 internal/tests/config/ascii2nc_mask_sid.conf delete mode 100644 internal/tests/config/ascii2nc_multi_file.conf delete mode 100644 internal/tests/config/ascii2nc_single_file.conf delete mode 100644 internal/tests/config/grid_gefs_prob.conf create mode 100755 internal/tests/pytests/wrappers/pair_stat/test_pair_stat_wrapper.py create mode 100755 metplus/wrappers/pair_stat_wrapper.py create mode 100644 parm/met_config/PairStatConfig_wrapped create mode 100644 parm/use_cases/met_tool_wrapper/PairStat/PairStat.conf diff --git a/.github/parm/use_case_groups.json b/.github/parm/use_case_groups.json index c4a3b5b7c8..1f026922cd 100644 --- a/.github/parm/use_case_groups.json +++ b/.github/parm/use_case_groups.json @@ -1,7 +1,7 @@ [ { "category": "met_tool_wrapper", - "index_list": "0-29,59-64", + "index_list": "0-29,59-66", "run": false }, { @@ -9,11 +9,6 @@ "index_list": "30-58", "run": false }, - { - "category": "met_tool_wrapper", - "index_list": "65", - "run": false - }, { "category": "air_quality_and_comp", "index_list": "0", diff --git a/.github/update_truth_change_log.txt b/.github/update_truth_change_log.txt index 071df7665d..05e8c8c1f9 100644 --- a/.github/update_truth_change_log.txt +++ b/.github/update_truth_change_log.txt @@ -17,3 +17,4 @@ [$(date +%Y%m%d_%H:%M:%S) ${branch_name}] #2817 - #2817 changed the output file name for the s2s_mid_lat/UserScript_fcstGFS_obsERA_Blocking use case [$(date +%Y%m%d_%H:%M:%S) ${branch_name}] dtcenter/MET#3036 - dtcenter/MET#3036 adds new columns [$(date +%Y%m%d_%H:%M:%S) ${branch_name}] dtcenter/MET#3057 - Small change to climo-based statistics from Point-Stat after correcting the timestamps for "monthly" NetCDF climo input data. +[$(date +%Y%m%d_%H:%M:%S) ${branch_name}] #2872 - #2872 adds PairStat basic use case output and changes the name of the file_list files for a few use cases (see PR description for complete list) diff --git a/docs/Users_Guide/glossary.rst b/docs/Users_Guide/glossary.rst index b044cc499d..9b55ba1d6d 100644 --- a/docs/Users_Guide/glossary.rst +++ b/docs/Users_Guide/glossary.rst @@ -1510,7 +1510,7 @@ METplus Configuration Glossary FCST_POINT_STAT_INPUT_TEMPLATE Template used to specify forecast input filenames for the MET tool point_stat. A corresponding variable exists for observation data called :term:`OBS_POINT_STAT_INPUT_TEMPLATE`. To utilize Python Embedding as input to the MET tools, set this value to PYTHON_NUMPY or PYTHON_XARRAY. - | *Used by:* GriPointStat + | *Used by:* PointStat FCST_REGRID_DATA_PLANE_RUN If True, process forecast data with RegridDataPlane. @@ -2408,7 +2408,7 @@ METplus Configuration Glossary MODEL Specify the model name. This is the model name listed in the MET .stat files. - | *Used by:* EnsembleStat, GridStat, PointStat, PCPCombine, TCPairs, GridDiag, TCRMW + | *Used by:* EnsembleStat, GridStat, PointStat, PCPCombine, TCPairs, GridDiag, TCRMW, PairStat MODEL_LIST List of the specified the model names. @@ -3419,7 +3419,7 @@ METplus Configuration Glossary | *Used by:* PointStat POINT_STAT_OUTPUT_TEMPLATE - Sets the subdirectories below :term:`POINT_STAT_OUTPUT_DIR` using a template to allow run time information. If LOOP_BY = VALID, default value is valid time YYYYMMDDHHMM/point_stat. If LOOP_BY = INIT, default value is init time YYYYMMDDHHMM/point_stat. + Sets the subdirectories below :term:`POINT_STAT_OUTPUT_DIR` using a template to allow run time information. | *Used by:* PointStat @@ -5542,11 +5542,6 @@ METplus Configuration Glossary | *Used by:* EnsembleStat - ENSEMBLE_STAT_CLIMO_MEAN_MATCH_MONTH - Specify the value for 'climo_mean.match_month' in the MET configuration file for EnsembleStat. - - | *Used by:* EnsembleStat - ENSEMBLE_STAT_CLIMO_MEAN_DAY_INTERVAL Specify the value for 'climo_mean.day_interval' in the MET configuration file for EnsembleStat. @@ -5627,11 +5622,6 @@ METplus Configuration Glossary | *Used by:* EnsembleStat - ENSEMBLE_STAT_CLIMO_STDEV_MATCH_MONTH - Specify the value for 'climo_stdev.match_month' in the MET configuration file for EnsembleStat. - - | *Used by:* EnsembleStat - ENSEMBLE_STAT_CLIMO_STDEV_DAY_INTERVAL Specify the value for 'climo_stdev.day_interval' in the MET configuration file for EnsembleStat. @@ -6924,11 +6914,6 @@ METplus Configuration Glossary | *Used by:* PointStat - POINT_STAT_CLIMO_MEAN_MATCH_MONTH - Specify the value for 'climo_mean.match_month' in the MET configuration file for PointStat. - - | *Used by:* PointStat - POINT_STAT_CLIMO_MEAN_DAY_INTERVAL Specify the value for 'climo_mean.day_interval' in the MET configuration file for PointStat. @@ -7009,11 +6994,6 @@ METplus Configuration Glossary | *Used by:* PointStat - POINT_STAT_CLIMO_STDEV_MATCH_MONTH - Specify the value for 'climo_stdev.match_month' in the MET configuration file for PointStat. - - | *Used by:* PointStat - POINT_STAT_CLIMO_STDEV_DAY_INTERVAL Specify the value for 'climo_stdev.day_interval' in the MET configuration file for PointStat. @@ -7104,11 +7084,6 @@ METplus Configuration Glossary | *Used by:* GridStat - GRID_STAT_CLIMO_MEAN_MATCH_MONTH - Specify the value for 'climo_mean.match_month' in the MET configuration file for GridStat. - - | *Used by:* GridStat - GRID_STAT_CLIMO_MEAN_DAY_INTERVAL Specify the value for 'climo_mean.day_interval' in the MET configuration file for GridStat. @@ -7189,11 +7164,6 @@ METplus Configuration Glossary | *Used by:* GridStat - GRID_STAT_CLIMO_STDEV_MATCH_MONTH - Specify the value for 'climo_stdev.match_month' in the MET configuration file for GridStat. - - | *Used by:* GridStat - GRID_STAT_CLIMO_STDEV_DAY_INTERVAL Specify the value for 'climo_stdev.day_interval' in the MET configuration file for GridStat. @@ -7270,11 +7240,6 @@ METplus Configuration Glossary | *Used by:* SeriesAnalysis - SERIES_ANALYSIS_CLIMO_MEAN_MATCH_MONTH - Specify the value for 'climo_mean.match_month' in the MET configuration file for SeriesAnalysis. - - | *Used by:* SeriesAnalysis - SERIES_ANALYSIS_CLIMO_MEAN_DAY_INTERVAL Specify the value for 'climo_mean.day_interval' in the MET configuration file for SeriesAnalysis. @@ -7355,11 +7320,6 @@ METplus Configuration Glossary | *Used by:* SeriesAnalysis - SERIES_ANALYSIS_CLIMO_STDEV_MATCH_MONTH - Specify the value for 'climo_stdev.match_month' in the MET configuration file for SeriesAnalysis. - - | *Used by:* SeriesAnalysis - SERIES_ANALYSIS_CLIMO_STDEV_DAY_INTERVAL Specify the value for 'climo_stdev.day_interval' in the MET configuration file for SeriesAnalysis. @@ -8357,11 +8317,6 @@ METplus Configuration Glossary | *Used by:* GenEnsProd - GEN_ENS_PROD_CLIMO_MEAN_MATCH_MONTH - Specify the value for 'climo_mean.match_month' in the MET configuration file for GenEnsProd. - - | *Used by:* GenEnsProd - GEN_ENS_PROD_CLIMO_MEAN_DAY_INTERVAL Specify the value for 'climo_mean.day_interval' in the MET configuration file for GenEnsProd. @@ -8440,11 +8395,6 @@ METplus Configuration Glossary | *Used by:* GenEnsProd - GEN_ENS_PROD_CLIMO_STDEV_MATCH_MONTH - Specify the value for 'climo_stdev.match_month' in the MET configuration file for GenEnsProd. - - | *Used by:* GenEnsProd - GEN_ENS_PROD_CLIMO_STDEV_DAY_INTERVAL Specify the value for 'climo_stdev.day_interval' in the MET configuration file for GenEnsProd. @@ -13319,6 +13269,768 @@ METplus Configuration Glossary | *Used by:* WaveletStat + PAIR_STAT_PAIRS_INPUT_DIR + Input directory for pairs files to use with the MET tool pair_stat. + + | *Used by:* PairStat + + PAIR_STAT_PAIRS_INPUT_TEMPLATE + Template used to specify pairs input filenames for the MET tool pair_stat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_DIR + Specify the directory where output files from the MET pair_stat tool are written. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_TEMPLATE + Sets the subdirectories and output file name base below + :term:`PAIR_STAT_OUTPUT_DIR` using a template to allow run time information. + + | *Used by:* PairStat + + PAIR_STAT_FORMAT + Format of input to PairStat. + Sets the value for the -format command line argument to pair_stat. + + | *Used by:* PairStat + + PAIR_STAT_OFFSETS + A list of potential offsets (in hours) that can be found in the + :term:`PAIR_STAT_PAIRS_INPUT_TEMPLATE`. + METplus will check if a file with a given offset exists in the order + specified in this list, to be sure to put favored offset values first. + + | *Used by:* PairStat + + PAIR_STAT_CONFIG_FILE + Path to configuration file read by pair_stat. + If unset, parm/met_config/PairStatConfig_wrapped will be used. + + | *Used by:* PairStat + + PAIR_STAT_DESC + Specify the value for 'desc' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CENSOR_THRESH + Specify the value for 'censor_thresh' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CENSOR_VAL + Specify the value for 'censor_val' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CAT_THRESH + Specify the value for 'cat_thresh' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CNT_THRESH + Specify the value for 'cnt_thresh' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CNT_LOGIC + Specify the value for 'cnt_logic' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_WIND_THRESH + Specify the value for 'wind_thresh' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_WIND_LOGIC + Specify the value for 'wind_logic' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_MPR_COLUMN + Specify the value for 'mpr_column' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_MPR_THRESH + Specify the value for 'mpr_thresh' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_MPR_STR_INC + Specify the value for 'mpr_str_inc' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_MPR_STR_EXC + Specify the value for 'mpr_str_exc' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_ECLV_POINTS + Specify the value for 'eclv_points' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_HSS_EC_VALUE + Specify the value for 'hss_ec_value' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_RANK_CORR_FLAG + Specify the value for 'rank_corr_flag' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_MEAN_FILE_NAME + Specify the value for 'climo_mean.file_name' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_MEAN_FIELD + Specify the value for 'climo_mean.field' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_MEAN_TIME_INTERP_METHOD + Specify the value for 'climo_mean.time_interp_method' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_MEAN_DAY_INTERVAL + Specify the value for 'climo_mean.day_interval' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_MEAN_HOUR_INTERVAL + Specify the value for 'climo_mean.hour_interval' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_MEAN_REGRID_METHOD + Specify the value for 'climo_mean.regrid.method' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_MEAN_REGRID_WIDTH + Specify the value for 'climo_mean.regrid.width' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_MEAN_REGRID_VLD_THRESH + Specify the value for 'climo_mean.regrid.vld_thresh' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_MEAN_REGRID_SHAPE + Specify the value for 'climo_mean.regrid.shape' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_STDEV_FILE_NAME + Specify the value for 'climo_stdev.file_name' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_STDEV_FIELD + Specify the value for 'climo_stdev.field' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_STDEV_TIME_INTERP_METHOD + Specify the value for 'climo_stdev.time_interp_method' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_STDEV_DAY_INTERVAL + Specify the value for 'climo_stdev.day_interval' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_STDEV_HOUR_INTERVAL + Specify the value for 'climo_stdev.hour_interval' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_STDEV_REGRID_METHOD + Specify the value for 'climo_stdev.regrid.method' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_STDEV_REGRID_WIDTH + Specify the value for 'climo_stdev.regrid.width' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_STDEV_REGRID_VLD_THRESH + Specify the value for 'climo_stdev.regrid.vld_thresh' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_STDEV_REGRID_SHAPE + Specify the value for 'climo_stdev.regrid.shape' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_CDF_CDF_BINS + See :term:`PAIR_STAT_CLIMO_CDF_BINS` + + PAIR_STAT_CLIMO_CDF_BINS + Specify the value for 'climo_cdf.cdf_bins' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_CDF_CENTER_BINS + Specify the value for 'climo_cdf.center_bins' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_CDF_WRITE_BINS + Specify the value for 'climo_cdf.write_bins' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CLIMO_CDF_DIRECT_PROB + Specify the value for 'climo_cdf.direct_prob' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_WINDOW_BEG + Specify the value for 'obs_window.beg' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_WINDOW_END + Specify the value for 'obs_window.end' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_MASK_GRID + Specify the value for 'mask.grid' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_MASK_POLY + Specify the value for 'mask.poly' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_MASK_SID + Specify the value for 'mask.sid' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_MASK_LLPNT + Specify the value for 'mask.llpnt' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_CI_ALPHA + Specify the value for 'ci_alpha' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_BOOT_INTERVAL + Specify the value for 'boot.interval' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_BOOT_REP_PROP + Specify the value for 'boot.rep_prop' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_BOOT_N_REP + Specify the value for 'boot.n_rep' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_BOOT_RNG + Specify the value for 'boot.rng' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_BOOT_SEED + Specify the value for 'boot.seed' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_SEEPS_P1_THRESH + Specify the value for 'seeps_p1_thresh' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_FHO + Specify the value for 'output_flag.fho' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_CTC + Specify the value for 'output_flag.ctc' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_CTS + Specify the value for 'output_flag.cts' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_MCTC + Specify the value for 'output_flag.mctc' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_MCTS + Specify the value for 'output_flag.mcts' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_CNT + Specify the value for 'output_flag.cnt' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_SL1L2 + Specify the value for 'output_flag.sl1l2' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_SAL1L2 + Specify the value for 'output_flag.sal1l2' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_VL1L2 + Specify the value for 'output_flag.vl1l2' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_VAL1L2 + Specify the value for 'output_flag.val1l2' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_VCNT + Specify the value for 'output_flag.vcnt' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_PCT + Specify the value for 'output_flag.pct' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_PSTD + Specify the value for 'output_flag.pstd' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_PJC + Specify the value for 'output_flag.pjc' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_PRC + Specify the value for 'output_flag.prc' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_ECLV + Specify the value for 'output_flag.eclv' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_MPR + Specify the value for 'output_flag.mpr' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_SEEPS + Specify the value for 'output_flag.seeps' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OUTPUT_FLAG_SEEPS_MPR + Specify the value for 'output_flag.seeps_mpr' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_POINT_WEIGHT_FLAG + Specify the value for 'point_weight_flag' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + FCST_PAIR_STAT_VAR_NAME + Wrapper specific field info variable. See :term:`FCST_VAR_NAME`. + + | *Used by:* PairStat + + FCST_PAIR_STAT_VAR_LEVELS + Wrapper specific field info variable. See :term:`FCST_VAR_LEVELS`. + + | *Used by:* PairStat + + FCST_PAIR_STAT_VAR_THRESH + Wrapper specific field info variable. See :term:`FCST_VAR_THRESH`. + + | *Used by:* PairStat + + FCST_PAIR_STAT_VAR_OPTIONS + Wrapper specific field info variable. See :term:`FCST_VAR_OPTIONS`. + + | *Used by:* PairStat + + OBS_PAIR_STAT_VAR_NAME + Wrapper specific field info variable. See :term:`OBS_VAR_NAME`. + + | *Used by:* PairStat + + OBS_PAIR_STAT_VAR_LEVELS + Wrapper specific field info variable. See :term:`OBS_VAR_LEVELS`. + + | *Used by:* PairStat + + OBS_PAIR_STAT_VAR_THRESH + Wrapper specific field info variable. See :term:`OBS_VAR_THRESH`. + + | *Used by:* PairStat + + OBS_PAIR_STAT_VAR_OPTIONS + Wrapper specific field info variable. See :term:`OBS_VAR_OPTIONS`. + + | *Used by:* PairStat + + PAIR_STAT_SKIP_VALID_TIMES + List of valid times to skip for PairStat only. + If set, values set in :term:`SKIP_VALID_TIMES` are ignored for PairStat. + See :term:`SKIP_VALID_TIMES` for formatting information. + + | *Used by:* PairStat + + PAIR_STAT_INC_VALID_TIMES + List of valid times to include for PairStat only. + If set, values set in :term:`INC_VALID_TIMES` are ignored for PairStat. + See :term:`SKIP_VALID_TIMES` for formatting information. + + | *Used by:* PairStat + + PAIR_STAT_SKIP_INIT_TIMES + List of initialization times to skip for PairStat only. + If set, values set in :term:`SKIP_INIT_TIMES` are ignored for PairStat. + See :term:`SKIP_VALID_TIMES` for formatting information. + + | *Used by:* PairStat + + PAIR_STAT_INC_INIT_TIMES + List of initialization times to include for PairStat only. + If set, values set in :term:`INC_INIT_TIMES` are ignored for PairStat. + See :term:`SKIP_VALID_TIMES` for formatting information. + + | *Used by:* PairStat + + PAIR_STAT_ALLOW_MISSING_INPUTS + Activates allow missing inputs logic for PairStat only. + See :term:`ALLOW_MISSING_INPUTS` for details. + + | *Used by:* PairStat + + PAIR_STAT_INPUT_THRESH + Defines input threshold for PairStat only. + See :term:`INPUT_THRESH` for details. + + | *Used by:* PairStat + + PAIR_STAT_MET_CONFIG_OVERRIDES + Override any variables in the MET configuration file that are not + supported by the wrapper. This should be set to the full variable name + and value that you want to override, including the equal sign and the + ending semi-colon. The value is directly appended to the end of the + wrapped MET config file. + + Example: + PAIR_STAT_MET_CONFIG_OVERRIDES = desc = "override_desc"; model = "override_model"; + + See :ref:`Overriding Unsupported MET config file settings` for more information + + | *Used by:* PairStat + + PAIR_STAT_SKIP_IF_OUTPUT_EXISTS + If True, do not run app if output file already exists. + Set to False to overwrite files. + + | *Used by:* PairStat + + PAIR_STAT_CUSTOM_LOOP_LIST + Sets custom string loop list for a specific wrapper. + See :term:`CUSTOM_LOOP_LIST`. + + | *Used by:* PairStat + + LOG_PAIR_STAT_VERBOSITY + Overrides the log verbosity for PairStat only. + If not set, the verbosity level is controlled by :term:`LOG_MET_VERBOSITY`. + + | *Used by:* PairStat + + PAIR_STAT_PAIRS_INPUT_DATATYPE + Specify the data type of the input directory for pairs files used with the + MET pair_stat tool. Currently valid options are NETCDF, GRIB, and GEMPAK. + + | *Used by:* PairStat + + PAIR_STAT_FCST_FILE_TYPE + Specify the value for 'fcst.file_type' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_FILE_TYPE + Specify the value for 'obs.file_type' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_MEAN_FILE_NAME + Specify the value for 'fcst.climo_mean.file_name' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_MEAN_FIELD + Specify the value for 'fcst.climo_mean.field' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_MEAN_VAR_NAME + See: :term:`_CLIMO_MEAN_VAR_NAME` + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_MEAN_VAR_LEVELS + See: :term:`_CLIMO_MEAN_VAR_LEVELS` + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_MEAN_VAR_OPTIONS + See: :term:`_CLIMO_MEAN_VAR_OPTIONS` + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_MEAN_REGRID_METHOD + Specify the value for 'fcst.climo_mean.regrid.method' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_MEAN_REGRID_WIDTH + Specify the value for 'fcst.climo_mean.regrid.width' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_MEAN_REGRID_VLD_THRESH + Specify the value for 'fcst.climo_mean.regrid.vld_thresh' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_MEAN_REGRID_SHAPE + Specify the value for 'fcst.climo_mean.regrid.shape' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_MEAN_TIME_INTERP_METHOD + Specify the value for 'fcst.climo_mean.time_interp_method' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL + Specify the value for 'fcst.climo_mean.day_interval' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_MEAN_HOUR_INTERVAL + Specify the value for 'fcst.climo_mean.hour_interval' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_STDEV_FILE_NAME + Specify the value for 'fcst.climo_stdev.file_name' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_STDEV_FIELD + Specify the value for 'fcst.climo_stdev.field' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_STDEV_VAR_NAME + See: :term:`_CLIMO_STDEV_VAR_NAME` + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_STDEV_VAR_LEVELS + See: :term:`_CLIMO_STDEV_VAR_LEVELS` + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_STDEV_VAR_OPTIONS + See: :term:`_CLIMO_STDEV_VAR_OPTIONS` + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_STDEV_REGRID_METHOD + Specify the value for 'fcst.climo_stdev.regrid.method' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_STDEV_REGRID_WIDTH + Specify the value for 'fcst.climo_stdev.regrid.width' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_STDEV_REGRID_VLD_THRESH + Specify the value for 'fcst.climo_stdev.regrid.vld_thresh' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_STDEV_REGRID_SHAPE + Specify the value for 'fcst.climo_stdev.regrid.shape' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_STDEV_TIME_INTERP_METHOD + Specify the value for 'fcst.climo_stdev.time_interp_method' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL + Specify the value for 'fcst.climo_stdev.day_interval' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_FCST_CLIMO_STDEV_HOUR_INTERVAL + Specify the value for 'fcst.climo_stdev.hour_interval' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_MEAN_FILE_NAME + Specify the value for 'obs.climo_mean.file_name' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_MEAN_FIELD + Specify the value for 'obs.climo_mean.field' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_MEAN_VAR_NAME + See: :term:`_CLIMO_MEAN_VAR_NAME` + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_MEAN_VAR_LEVELS + See: :term:`_CLIMO_MEAN_VAR_LEVELS` + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_MEAN_VAR_OPTIONS + See: :term:`_CLIMO_MEAN_VAR_OPTIONS` + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_MEAN_REGRID_METHOD + Specify the value for 'obs.climo_mean.regrid.method' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_MEAN_REGRID_WIDTH + Specify the value for 'obs.climo_mean.regrid.width' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_MEAN_REGRID_VLD_THRESH + Specify the value for 'obs.climo_mean.regrid.vld_thresh' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_MEAN_REGRID_SHAPE + Specify the value for 'obs.climo_mean.regrid.shape' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_MEAN_TIME_INTERP_METHOD + Specify the value for 'obs.climo_mean.time_interp_method' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL + Specify the value for 'obs.climo_mean.day_interval' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_MEAN_HOUR_INTERVAL + Specify the value for 'obs.climo_mean.hour_interval' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_STDEV_FILE_NAME + Specify the value for 'obs.climo_stdev.file_name' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_STDEV_FIELD + Specify the value for 'obs.climo_stdev.field' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_STDEV_VAR_NAME + See: :term:`_CLIMO_STDEV_VAR_NAME` + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_STDEV_VAR_LEVELS + See: :term:`_CLIMO_STDEV_VAR_LEVELS` + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_STDEV_VAR_OPTIONS + See: :term:`_CLIMO_STDEV_VAR_OPTIONS` + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_STDEV_REGRID_METHOD + Specify the value for 'obs.climo_stdev.regrid.method' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_STDEV_REGRID_WIDTH + Specify the value for 'obs.climo_stdev.regrid.width' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_STDEV_REGRID_VLD_THRESH + Specify the value for 'obs.climo_stdev.regrid.vld_thresh' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_STDEV_REGRID_SHAPE + Specify the value for 'obs.climo_stdev.regrid.shape' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_STDEV_TIME_INTERP_METHOD + Specify the value for 'obs.climo_stdev.time_interp_method' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL + Specify the value for 'obs.climo_stdev.day_interval' in the MET configuration file for PairStat. + + | *Used by:* PairStat + + PAIR_STAT_OBS_CLIMO_STDEV_HOUR_INTERVAL + Specify the value for 'obs.climo_stdev.hour_interval' in the MET configuration file for PairStat. + + | *Used by:* PairStat + GRID_STAT_GRADIENT_DX Specify the value for 'gradient.dx' in the MET configuration file for GridStat. diff --git a/docs/Users_Guide/quicksearch.rst b/docs/Users_Guide/quicksearch.rst index 80e1aef693..f4f2815ae5 100644 --- a/docs/Users_Guide/quicksearch.rst +++ b/docs/Users_Guide/quicksearch.rst @@ -25,6 +25,7 @@ Use Cases by MET Tool: | `MADIS2NC <../search.html?q=MADIS2NCToolUseCase&check_keywords=yes&area=default>`_ | `MODE <../search.html?q=MODEToolUseCase&check_keywords=yes&area=default>`_ | `MTD <../search.html?q=MTDToolUseCase&check_keywords=yes&area=default>`_ + | `PairStat <../search.html?q=PairStatToolUseCase&check_keywords=yes&area=default>`_ | `PB2NC <../search.html?q=PB2NCToolUseCase&check_keywords=yes&area=default>`_ | `PCPCombine <../search.html?q=PCPCombineToolUseCase&check_keywords=yes&area=default>`_ | `Point2Grid <../search.html?q=Point2GridToolUseCase&check_keywords=yes&area=default>`_ @@ -53,6 +54,7 @@ Use Cases by MET Tool: | **MADIS2NC**: *MADIS2NCToolUseCase* | **MODE**: *MODEToolUseCase* | **MTD**: *MTDToolUseCase* + | **PairStat**: *PairStatToolUseCase* | **PB2NC**: *PB2NCToolUseCase* | **PCPCombine**: *PCPCombineToolUseCase* | **Point2Grid**: *Point2GridToolUseCase* diff --git a/docs/Users_Guide/wrappers.rst b/docs/Users_Guide/wrappers.rst index 0d2c50ff5a..bdb2b2cddc 100644 --- a/docs/Users_Guide/wrappers.rst +++ b/docs/Users_Guide/wrappers.rst @@ -260,7 +260,6 @@ METplus Configuration | :term:`ENSEMBLE_STAT_CLIMO_MEAN_REGRID_VLD_THRESH` | :term:`ENSEMBLE_STAT_CLIMO_MEAN_REGRID_SHAPE` | :term:`ENSEMBLE_STAT_CLIMO_MEAN_TIME_INTERP_METHOD` -| :term:`ENSEMBLE_STAT_CLIMO_MEAN_MATCH_MONTH` | :term:`ENSEMBLE_STAT_CLIMO_MEAN_DAY_INTERVAL` | :term:`ENSEMBLE_STAT_CLIMO_MEAN_HOUR_INTERVAL` | :term:`ENSEMBLE_STAT_CLIMO_MEAN_USE_FCST` @@ -275,7 +274,6 @@ METplus Configuration | :term:`ENSEMBLE_STAT_CLIMO_STDEV_REGRID_VLD_THRESH` | :term:`ENSEMBLE_STAT_CLIMO_STDEV_REGRID_SHAPE` | :term:`ENSEMBLE_STAT_CLIMO_STDEV_TIME_INTERP_METHOD` -| :term:`ENSEMBLE_STAT_CLIMO_STDEV_MATCH_MONTH` | :term:`ENSEMBLE_STAT_CLIMO_STDEV_DAY_INTERVAL` | :term:`ENSEMBLE_STAT_CLIMO_STDEV_HOUR_INTERVAL` | :term:`ENSEMBLE_STAT_CLIMO_STDEV_USE_FCST` @@ -915,8 +913,6 @@ ${METPLUS_CLIMO_MEAN_DICT} - climo_mean.regrid.shape * - :term:`ENSEMBLE_STAT_CLIMO_MEAN_TIME_INTERP_METHOD` - climo_mean.time_interp_method - * - :term:`ENSEMBLE_STAT_CLIMO_MEAN_MATCH_MONTH` - - climo_mean.match_month * - :term:`ENSEMBLE_STAT_CLIMO_MEAN_DAY_INTERVAL` - climo_mean.day_interval * - :term:`ENSEMBLE_STAT_CLIMO_MEAN_HOUR_INTERVAL` @@ -946,8 +942,6 @@ ${METPLUS_CLIMO_STDEV_DICT} - climo_stdev.regrid.shape * - :term:`ENSEMBLE_STAT_CLIMO_STDEV_TIME_INTERP_METHOD` - climo_stdev.time_interp_method - * - :term:`ENSEMBLE_STAT_CLIMO_STDEV_MATCH_MONTH` - - climo_stdev.match_month * - :term:`ENSEMBLE_STAT_CLIMO_STDEV_DAY_INTERVAL` - climo_stdev.day_interval * - :term:`ENSEMBLE_STAT_CLIMO_STDEV_HOUR_INTERVAL` @@ -1316,7 +1310,6 @@ METplus Configuration | :term:`GEN_ENS_PROD_CLIMO_MEAN_REGRID_VLD_THRESH` | :term:`GEN_ENS_PROD_CLIMO_MEAN_REGRID_SHAPE` | :term:`GEN_ENS_PROD_CLIMO_MEAN_TIME_INTERP_METHOD` -| :term:`GEN_ENS_PROD_CLIMO_MEAN_MATCH_MONTH` | :term:`GEN_ENS_PROD_CLIMO_MEAN_DAY_INTERVAL` | :term:`GEN_ENS_PROD_CLIMO_MEAN_HOUR_INTERVAL` | :term:`GEN_ENS_PROD_CLIMO_MEAN_USE_FCST` @@ -1331,7 +1324,6 @@ METplus Configuration | :term:`GEN_ENS_PROD_CLIMO_STDEV_REGRID_VLD_THRESH` | :term:`GEN_ENS_PROD_CLIMO_STDEV_REGRID_SHAPE` | :term:`GEN_ENS_PROD_CLIMO_STDEV_TIME_INTERP_METHOD` -| :term:`GEN_ENS_PROD_CLIMO_STDEV_MATCH_MONTH` | :term:`GEN_ENS_PROD_CLIMO_STDEV_DAY_INTERVAL` | :term:`GEN_ENS_PROD_CLIMO_STDEV_HOUR_INTERVAL` | :term:`GEN_ENS_PROD_CLIMO_STDEV_USE_FCST` @@ -1637,8 +1629,6 @@ ${METPLUS_CLIMO_MEAN_DICT} - climo_mean.regrid.shape * - :term:`GEN_ENS_PROD_CLIMO_MEAN_TIME_INTERP_METHOD` - climo_mean.time_interp_method - * - :term:`GEN_ENS_PROD_CLIMO_MEAN_MATCH_MONTH` - - climo_mean.match_month * - :term:`GEN_ENS_PROD_CLIMO_MEAN_DAY_INTERVAL` - climo_mean.day_interval * - :term:`GEN_ENS_PROD_CLIMO_MEAN_HOUR_INTERVAL` @@ -1667,8 +1657,6 @@ ${METPLUS_CLIMO_STDEV_DICT} - climo_stdev.regrid.shape * - :term:`GEN_ENS_PROD_CLIMO_STDEV_TIME_INTERP_METHOD` - climo_stdev.time_interp_method - * - :term:`GEN_ENS_PROD_CLIMO_STDEV_MATCH_MONTH` - - climo_stdev.match_month * - :term:`GEN_ENS_PROD_CLIMO_STDEV_DAY_INTERVAL` - climo_stdev.day_interval * - :term:`GEN_ENS_PROD_CLIMO_STDEV_HOUR_INTERVAL` @@ -3299,7 +3287,6 @@ METplus Configuration | :term:`GRID_STAT_CLIMO_MEAN_REGRID_VLD_THRESH` | :term:`GRID_STAT_CLIMO_MEAN_REGRID_SHAPE` | :term:`GRID_STAT_CLIMO_MEAN_TIME_INTERP_METHOD` -| :term:`GRID_STAT_CLIMO_MEAN_MATCH_MONTH` | :term:`GRID_STAT_CLIMO_MEAN_DAY_INTERVAL` | :term:`GRID_STAT_CLIMO_MEAN_HOUR_INTERVAL` | :term:`GRID_STAT_CLIMO_MEAN_USE_FCST` @@ -3314,7 +3301,6 @@ METplus Configuration | :term:`GRID_STAT_CLIMO_STDEV_REGRID_VLD_THRESH` | :term:`GRID_STAT_CLIMO_STDEV_REGRID_SHAPE` | :term:`GRID_STAT_CLIMO_STDEV_TIME_INTERP_METHOD` -| :term:`GRID_STAT_CLIMO_STDEV_MATCH_MONTH` | :term:`GRID_STAT_CLIMO_STDEV_DAY_INTERVAL` | :term:`GRID_STAT_CLIMO_STDEV_HOUR_INTERVAL` | :term:`GRID_STAT_CLIMO_STDEV_USE_FCST` @@ -3781,8 +3767,6 @@ ${METPLUS_CLIMO_MEAN_DICT} - climo_mean.regrid.shape * - :term:`GRID_STAT_CLIMO_MEAN_TIME_INTERP_METHOD` - climo_mean.time_interp_method - * - :term:`GRID_STAT_CLIMO_MEAN_MATCH_MONTH` - - climo_mean.match_month * - :term:`GRID_STAT_CLIMO_MEAN_DAY_INTERVAL` - climo_mean.day_interval * - :term:`GRID_STAT_CLIMO_MEAN_HOUR_INTERVAL` @@ -3811,8 +3795,6 @@ ${METPLUS_CLIMO_STDEV_DICT} - climo_stdev.regrid.shape * - :term:`GRID_STAT_CLIMO_STDEV_TIME_INTERP_METHOD` - climo_stdev.time_interp_method - * - :term:`GRID_STAT_CLIMO_STDEV_MATCH_MONTH` - - climo_stdev.match_month * - :term:`GRID_STAT_CLIMO_STDEV_DAY_INTERVAL` - climo_stdev.day_interval * - :term:`GRID_STAT_CLIMO_STDEV_HOUR_INTERVAL` @@ -5923,6 +5905,777 @@ ${METPLUS_MET_CONFIG_OVERRIDES} * - :term:`MTD_MET_CONFIG_OVERRIDES` - n/a + +.. _pair_stat_wrapper: + +PairStat +========= + +Description +----------- + +The PairStat wrapper is a Python script that encapsulates the MET +pair_stat tool. + +Configuration +------------- + +| :term:`LOG_PAIR_STAT_VERBOSITY` +| :term:`PAIR_STAT_PAIRS_INPUT_TEMPLATE` +| :term:`PAIR_STAT_PAIRS_INPUT_DIR` +| :term:`PAIR_STAT_PAIRS_INPUT_DATATYPE` +| :term:`PAIR_STAT_OUTPUT_TEMPLATE` +| :term:`PAIR_STAT_OUTPUT_DIR` +| :term:`PAIR_STAT_FORMAT` +| :term:`PAIR_STAT_OFFSETS` +| :term:`PAIR_STAT_CONFIG_FILE` +| :term:`MODEL` +| :term:`PAIR_STAT_DESC` +| :term:`PAIR_STAT_CENSOR_THRESH` +| :term:`PAIR_STAT_CENSOR_VAL` +| :term:`PAIR_STAT_CAT_THRESH` +| :term:`PAIR_STAT_CNT_THRESH` +| :term:`PAIR_STAT_CNT_LOGIC` +| :term:`PAIR_STAT_WIND_THRESH` +| :term:`PAIR_STAT_WIND_LOGIC` +| :term:`PAIR_STAT_MPR_COLUMN` +| :term:`PAIR_STAT_MPR_THRESH` +| :term:`PAIR_STAT_MPR_STR_INC` +| :term:`PAIR_STAT_MPR_STR_EXC` +| :term:`PAIR_STAT_ECLV_POINTS` +| :term:`PAIR_STAT_HSS_EC_VALUE` +| :term:`PAIR_STAT_RANK_CORR_FLAG` +| :term:`PAIR_STAT_CLIMO_MEAN_FILE_NAME` +| :term:`PAIR_STAT_CLIMO_MEAN_FIELD` +| :term:`PAIR_STAT_CLIMO_MEAN_TIME_INTERP_METHOD` +| :term:`PAIR_STAT_CLIMO_MEAN_DAY_INTERVAL` +| :term:`PAIR_STAT_CLIMO_MEAN_HOUR_INTERVAL` +| :term:`PAIR_STAT_CLIMO_STDEV_FILE_NAME` +| :term:`PAIR_STAT_CLIMO_STDEV_FIELD` +| :term:`PAIR_STAT_CLIMO_STDEV_TIME_INTERP_METHOD` +| :term:`PAIR_STAT_CLIMO_STDEV_DAY_INTERVAL` +| :term:`PAIR_STAT_CLIMO_STDEV_HOUR_INTERVAL` +| :term:`PAIR_STAT_FCST_CLIMO_MEAN_FILE_NAME` +| :term:`PAIR_STAT_FCST_CLIMO_MEAN_VAR_NAME` +| :term:`PAIR_STAT_FCST_CLIMO_MEAN_VAR_LEVELS` +| :term:`PAIR_STAT_FCST_CLIMO_MEAN_VAR_OPTIONS` +| :term:`PAIR_STAT_FCST_CLIMO_MEAN_FIELD` +| :term:`PAIR_STAT_FCST_CLIMO_MEAN_REGRID_METHOD` +| :term:`PAIR_STAT_FCST_CLIMO_MEAN_REGRID_WIDTH` +| :term:`PAIR_STAT_FCST_CLIMO_MEAN_REGRID_VLD_THRESH` +| :term:`PAIR_STAT_FCST_CLIMO_MEAN_REGRID_SHAPE` +| :term:`PAIR_STAT_FCST_CLIMO_MEAN_TIME_INTERP_METHOD` +| :term:`PAIR_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL` +| :term:`PAIR_STAT_FCST_CLIMO_MEAN_HOUR_INTERVAL` +| :term:`PAIR_STAT_FCST_CLIMO_STDEV_FILE_NAME` +| :term:`PAIR_STAT_FCST_CLIMO_STDEV_VAR_NAME` +| :term:`PAIR_STAT_FCST_CLIMO_STDEV_VAR_LEVELS` +| :term:`PAIR_STAT_FCST_CLIMO_STDEV_VAR_OPTIONS` +| :term:`PAIR_STAT_FCST_CLIMO_STDEV_FIELD` +| :term:`PAIR_STAT_FCST_CLIMO_STDEV_REGRID_METHOD` +| :term:`PAIR_STAT_FCST_CLIMO_STDEV_REGRID_WIDTH` +| :term:`PAIR_STAT_FCST_CLIMO_STDEV_REGRID_VLD_THRESH` +| :term:`PAIR_STAT_FCST_CLIMO_STDEV_REGRID_SHAPE` +| :term:`PAIR_STAT_FCST_CLIMO_STDEV_TIME_INTERP_METHOD` +| :term:`PAIR_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL` +| :term:`PAIR_STAT_FCST_CLIMO_STDEV_HOUR_INTERVAL` +| :term:`PAIR_STAT_OBS_CLIMO_MEAN_FILE_NAME` +| :term:`PAIR_STAT_OBS_CLIMO_MEAN_VAR_NAME` +| :term:`PAIR_STAT_OBS_CLIMO_MEAN_VAR_LEVELS` +| :term:`PAIR_STAT_OBS_CLIMO_MEAN_VAR_OPTIONS` +| :term:`PAIR_STAT_OBS_CLIMO_MEAN_FIELD` +| :term:`PAIR_STAT_OBS_CLIMO_MEAN_REGRID_METHOD` +| :term:`PAIR_STAT_OBS_CLIMO_MEAN_REGRID_WIDTH` +| :term:`PAIR_STAT_OBS_CLIMO_MEAN_REGRID_VLD_THRESH` +| :term:`PAIR_STAT_OBS_CLIMO_MEAN_REGRID_SHAPE` +| :term:`PAIR_STAT_OBS_CLIMO_MEAN_TIME_INTERP_METHOD` +| :term:`PAIR_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL` +| :term:`PAIR_STAT_OBS_CLIMO_MEAN_HOUR_INTERVAL` +| :term:`PAIR_STAT_OBS_CLIMO_STDEV_FILE_NAME` +| :term:`PAIR_STAT_OBS_CLIMO_STDEV_VAR_NAME` +| :term:`PAIR_STAT_OBS_CLIMO_STDEV_VAR_LEVELS` +| :term:`PAIR_STAT_OBS_CLIMO_STDEV_VAR_OPTIONS` +| :term:`PAIR_STAT_OBS_CLIMO_STDEV_FIELD` +| :term:`PAIR_STAT_OBS_CLIMO_STDEV_REGRID_METHOD` +| :term:`PAIR_STAT_OBS_CLIMO_STDEV_REGRID_WIDTH` +| :term:`PAIR_STAT_OBS_CLIMO_STDEV_REGRID_VLD_THRESH` +| :term:`PAIR_STAT_OBS_CLIMO_STDEV_REGRID_SHAPE` +| :term:`PAIR_STAT_OBS_CLIMO_STDEV_TIME_INTERP_METHOD` +| :term:`PAIR_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL` +| :term:`PAIR_STAT_OBS_CLIMO_STDEV_HOUR_INTERVAL` +| :term:`PAIR_STAT_CLIMO_CDF_CDF_BINS` +| :term:`PAIR_STAT_CLIMO_CDF_CENTER_BINS` +| :term:`PAIR_STAT_CLIMO_CDF_WRITE_BINS` +| :term:`PAIR_STAT_CLIMO_CDF_DIRECT_PROB` +| :term:`PAIR_STAT_OBS_WINDOW_BEG` +| :term:`PAIR_STAT_OBS_WINDOW_END` +| :term:`PAIR_STAT_MASK_GRID` +| :term:`PAIR_STAT_MASK_POLY` +| :term:`PAIR_STAT_MASK_SID` +| :term:`PAIR_STAT_MASK_LLPNT` +| :term:`PAIR_STAT_CI_ALPHA` +| :term:`PAIR_STAT_BOOT_INTERVAL` +| :term:`PAIR_STAT_BOOT_REP_PROP` +| :term:`PAIR_STAT_BOOT_N_REP` +| :term:`PAIR_STAT_BOOT_RNG` +| :term:`PAIR_STAT_BOOT_SEED` +| :term:`PAIR_STAT_SEEPS_P1_THRESH` +| :term:`PAIR_STAT_OUTPUT_FLAG_FHO` +| :term:`PAIR_STAT_OUTPUT_FLAG_CTC` +| :term:`PAIR_STAT_OUTPUT_FLAG_CTS` +| :term:`PAIR_STAT_OUTPUT_FLAG_MCTC` +| :term:`PAIR_STAT_OUTPUT_FLAG_MCTS` +| :term:`PAIR_STAT_OUTPUT_FLAG_CNT` +| :term:`PAIR_STAT_OUTPUT_FLAG_SL1L2` +| :term:`PAIR_STAT_OUTPUT_FLAG_SAL1L2` +| :term:`PAIR_STAT_OUTPUT_FLAG_VL1L2` +| :term:`PAIR_STAT_OUTPUT_FLAG_VAL1L2` +| :term:`PAIR_STAT_OUTPUT_FLAG_VCNT` +| :term:`PAIR_STAT_OUTPUT_FLAG_PCT` +| :term:`PAIR_STAT_OUTPUT_FLAG_PSTD` +| :term:`PAIR_STAT_OUTPUT_FLAG_PJC` +| :term:`PAIR_STAT_OUTPUT_FLAG_PRC` +| :term:`PAIR_STAT_OUTPUT_FLAG_ECLV` +| :term:`PAIR_STAT_OUTPUT_FLAG_MPR` +| :term:`PAIR_STAT_OUTPUT_FLAG_SEEPS` +| :term:`PAIR_STAT_OUTPUT_FLAG_SEEPS_MPR` +| :term:`PAIR_STAT_POINT_WEIGHT_FLAG` +| :term:`PAIR_STAT_CUSTOM_LOOP_LIST` +| :term:`PAIR_STAT_SKIP_IF_OUTPUT_EXISTS` +| :term:`PAIR_STAT_MET_CONFIG_OVERRIDES` +| :term:`FCST_PAIR_STAT_VAR_NAME` +| :term:`FCST_PAIR_STAT_VAR_LEVELS` +| :term:`FCST_PAIR_STAT_VAR_THRESH` +| :term:`FCST_PAIR_STAT_VAR_OPTIONS` +| :term:`OBS_PAIR_STAT_VAR_NAME` +| :term:`OBS_PAIR_STAT_VAR_LEVELS` +| :term:`OBS_PAIR_STAT_VAR_THRESH` +| :term:`OBS_PAIR_STAT_VAR_OPTIONS` +| :term:`PAIR_STAT_SKIP_VALID_TIMES` +| :term:`PAIR_STAT_INC_VALID_TIMES` +| :term:`PAIR_STAT_SKIP_INIT_TIMES` +| :term:`PAIR_STAT_INC_INIT_TIMES` +| :term:`PAIR_STAT_ALLOW_MISSING_INPUTS` +| :term:`PAIR_STAT_INPUT_THRESH` + +.. _pair-stat-met-conf: + +MET Configuration +----------------- + +Below is the wrapped MET configuration file used for this wrapper. +Environment variables are used to control entries in this configuration file. +The default value for each environment variable is obtained from +(except where noted below): + +`MET_INSTALL_DIR/share/met/config/PairStatConfig_default `_ + +Below the file contents are descriptions of each environment variable +referenced in this file and the corresponding METplus configuration item used +to set the value of the environment variable. For detailed examples showing +how METplus sets the values of these environment variables, +see :ref:`How METplus controls MET config file settings`. + +.. dropdown:: Click to view parm/met_config/PairStatConfig_wrapped + + .. literalinclude:: ../../parm/met_config/PairStatConfig_wrapped + +Environment variables in wrapped MET config +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +${METPLUS_MODEL} +"""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`MODEL` + - model + +${METPLUS_DESC} +""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`DESC` -or- :term:`PAIR_STAT_DESC` + - desc + +${METPLUS_FCST_FILE_TYPE} +""""""""""""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_FCST_FILE_TYPE` + - fcst.file_type + +${METPLUS_FCST_FIELD} +""""""""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`FCST_VAR_NAME` + - fcst.field.name + * - :term:`FCST_VAR_LEVELS` + - fcst.field.level + * - :term:`FCST_VAR_THRESH` + - fcst.field.cat_thresh + * - :term:`FCST_VAR_OPTIONS` + - n/a + +.. note:: For more information on controlling the forecast field attributes in METplus, please see the :ref:`Field_Info` section of the User's Guide. + + +${METPLUS_FCST_CLIMO_MEAN_DICT} +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_FCST_CLIMO_MEAN_FILE_NAME` + - fcst.climo_mean.file_name + * - :term:`PAIR_STAT_FCST_CLIMO_MEAN_FIELD` + - fcst.climo_mean.field + * - :term:`PAIR_STAT_FCST_CLIMO_MEAN_REGRID_METHOD` + - fcst.climo_mean.regrid.method + * - :term:`PAIR_STAT_FCST_CLIMO_MEAN_REGRID_WIDTH` + - fcst.climo_mean.regrid.width + * - :term:`PAIR_STAT_FCST_CLIMO_MEAN_REGRID_VLD_THRESH` + - fcst.climo_mean.regrid.vld_thresh + * - :term:`PAIR_STAT_FCST_CLIMO_MEAN_REGRID_SHAPE` + - fcst.climo_mean.regrid.shape + * - :term:`PAIR_STAT_FCST_CLIMO_MEAN_TIME_INTERP_METHOD` + - fcst.climo_mean.time_interp_method + * - :term:`PAIR_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL` + - fcst.climo_mean.day_interval + * - :term:`PAIR_STAT_FCST_CLIMO_MEAN_HOUR_INTERVAL` + - fcst.climo_mean.hour_interval + +${METPLUS_FCST_CLIMO_STDEV_DICT} +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_FCST_CLIMO_STDEV_FILE_NAME` + - fcst.climo_stdev.file_name + * - :term:`PAIR_STAT_FCST_CLIMO_STDEV_FIELD` + - fcst.climo_stdev.field + * - :term:`PAIR_STAT_FCST_CLIMO_STDEV_REGRID_METHOD` + - fcst.climo_stdev.regrid.method + * - :term:`PAIR_STAT_FCST_CLIMO_STDEV_REGRID_WIDTH` + - fcst.climo_stdev.regrid.width + * - :term:`PAIR_STAT_FCST_CLIMO_STDEV_REGRID_VLD_THRESH` + - fcst.climo_stdev.regrid.vld_thresh + * - :term:`PAIR_STAT_FCST_CLIMO_STDEV_REGRID_SHAPE` + - fcst.climo_stdev.regrid.shape + * - :term:`PAIR_STAT_FCST_CLIMO_STDEV_TIME_INTERP_METHOD` + - fcst.climo_stdev.time_interp_method + * - :term:`PAIR_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL` + - fcst.climo_stdev.day_interval + * - :term:`PAIR_STAT_FCST_CLIMO_STDEV_HOUR_INTERVAL` + - fcst.climo_stdev.hour_interval + +${METPLUS_OBS_FILE_TYPE} +"""""""""""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_OBS_FILE_TYPE` + - obs.file_type + +${METPLUS_OBS_FIELD} +"""""""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`OBS_VAR_NAME` + - obs.field.name + * - :term:`OBS_VAR_LEVELS` + - obs.field.level + * - :term:`OBS_VAR_THRESH` + - obs.field.cat_thresh + * - :term:`OBS_VAR_OPTIONS` + - n/a + +.. note:: For more information on controlling the observation field attributes in METplus, please see the :ref:`Field_Info` section of the User's Guide. + + +${METPLUS_OBS_CLIMO_MEAN_DICT} +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_OBS_CLIMO_MEAN_FILE_NAME` + - obs.climo_mean.file_name + * - :term:`PAIR_STAT_OBS_CLIMO_MEAN_FIELD` + - obs.climo_mean.field + * - :term:`PAIR_STAT_OBS_CLIMO_MEAN_REGRID_METHOD` + - obs.climo_mean.regrid.method + * - :term:`PAIR_STAT_OBS_CLIMO_MEAN_REGRID_WIDTH` + - obs.climo_mean.regrid.width + * - :term:`PAIR_STAT_OBS_CLIMO_MEAN_REGRID_VLD_THRESH` + - obs.climo_mean.regrid.vld_thresh + * - :term:`PAIR_STAT_OBS_CLIMO_MEAN_REGRID_SHAPE` + - obs.climo_mean.regrid.shape + * - :term:`PAIR_STAT_OBS_CLIMO_MEAN_TIME_INTERP_METHOD` + - obs.climo_mean.time_interp_method + * - :term:`PAIR_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL` + - obs.climo_mean.day_interval + * - :term:`PAIR_STAT_OBS_CLIMO_MEAN_HOUR_INTERVAL` + - obs.climo_mean.hour_interval + +${METPLUS_OBS_CLIMO_STDEV_DICT} +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_OBS_CLIMO_STDEV_FILE_NAME` + - obs.climo_stdev.file_name + * - :term:`PAIR_STAT_OBS_CLIMO_STDEV_FIELD` + - obs.climo_stdev.field + * - :term:`PAIR_STAT_OBS_CLIMO_STDEV_REGRID_METHOD` + - obs.climo_stdev.regrid.method + * - :term:`PAIR_STAT_OBS_CLIMO_STDEV_REGRID_WIDTH` + - obs.climo_stdev.regrid.width + * - :term:`PAIR_STAT_OBS_CLIMO_STDEV_REGRID_VLD_THRESH` + - obs.climo_stdev.regrid.vld_thresh + * - :term:`PAIR_STAT_OBS_CLIMO_STDEV_REGRID_SHAPE` + - obs.climo_stdev.regrid.shape + * - :term:`PAIR_STAT_OBS_CLIMO_STDEV_TIME_INTERP_METHOD` + - obs.climo_stdev.time_interp_method + * - :term:`PAIR_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL` + - obs.climo_stdev.day_interval + * - :term:`PAIR_STAT_OBS_CLIMO_STDEV_HOUR_INTERVAL` + - obs.climo_stdev.hour_interval + +${METPLUS_CENSOR_THRESH} +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_CENSOR_THRESH` + - censor_thresh + +${METPLUS_CENSOR_VAL} +^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_CENSOR_VAL` + - censor_val + +${METPLUS_CAT_THRESH} +^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_CAT_THRESH` + - cat_thresh + +${METPLUS_CNT_THRESH} +^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_CNT_THRESH` + - cnt_thresh + +${METPLUS_CNT_LOGIC} +^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_CNT_LOGIC` + - cnt_logic + +${METPLUS_WIND_THRESH} +^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_WIND_THRESH` + - wind_thresh + +${METPLUS_WIND_LOGIC} +^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_WIND_LOGIC` + - wind_logic + +${METPLUS_MPR_COLUMN} +^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_MPR_COLUMN` + - mpr_column + +${METPLUS_MPR_THRESH} +^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_MPR_THRESH` + - mpr_thresh + +${METPLUS_MPR_STR_INC} +^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_MPR_STR_INC` + - mpr_str_inc + +${METPLUS_MPR_STR_EXC} +^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_MPR_STR_EXC` + - mpr_str_exc + +${METPLUS_ECLV_POINTS} +^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_ECLV_POINTS` + - eclv_points + +${METPLUS_HSS_EC_VALUE} +^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_HSS_EC_VALUE` + - hss_ec_value + +${METPLUS_RANK_CORR_FLAG} +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_RANK_CORR_FLAG` + - rank_corr_flag + +${METPLUS_CLIMO_MEAN_DICT} +"""""""""""""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_CLIMO_MEAN_FILE_NAME` + - climo_mean.file_name + * - :term:`PAIR_STAT_CLIMO_MEAN_FIELD` + - climo_mean.field + * - :term:`PAIR_STAT_CLIMO_MEAN_REGRID_METHOD` + - climo_mean.regrid.method + * - :term:`PAIR_STAT_CLIMO_MEAN_REGRID_WIDTH` + - climo_mean.regrid.width + * - :term:`PAIR_STAT_CLIMO_MEAN_REGRID_VLD_THRESH` + - climo_mean.regrid.vld_thresh + * - :term:`PAIR_STAT_CLIMO_MEAN_REGRID_SHAPE` + - climo_mean.regrid.shape + * - :term:`PAIR_STAT_CLIMO_MEAN_TIME_INTERP_METHOD` + - climo_mean.time_interp_method + * - :term:`PAIR_STAT_CLIMO_MEAN_DAY_INTERVAL` + - climo_mean.day_interval + * - :term:`PAIR_STAT_CLIMO_MEAN_HOUR_INTERVAL` + - climo_mean.hour_interval + +${METPLUS_CLIMO_STDEV_DICT} +""""""""""""""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_CLIMO_STDEV_FILE_NAME` + - climo_stdev.file_name + * - :term:`PAIR_STAT_CLIMO_STDEV_FIELD` + - climo_stdev.field + * - :term:`PAIR_STAT_CLIMO_STDEV_REGRID_METHOD` + - climo_stdev.regrid.method + * - :term:`PAIR_STAT_CLIMO_STDEV_REGRID_WIDTH` + - climo_stdev.regrid.width + * - :term:`PAIR_STAT_CLIMO_STDEV_REGRID_VLD_THRESH` + - climo_stdev.regrid.vld_thresh + * - :term:`PAIR_STAT_CLIMO_STDEV_REGRID_SHAPE` + - climo_stdev.regrid.shape + * - :term:`PAIR_STAT_CLIMO_STDEV_TIME_INTERP_METHOD` + - climo_stdev.time_interp_method + * - :term:`PAIR_STAT_CLIMO_STDEV_DAY_INTERVAL` + - climo_stdev.day_interval + * - :term:`PAIR_STAT_CLIMO_STDEV_HOUR_INTERVAL` + - climo_stdev.hour_interval + +${METPLUS_CLIMO_CDF_DICT} +""""""""""""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_CLIMO_CDF_BINS` + - climo_cdf.cdf_bins + * - :term:`PAIR_STAT_CLIMO_CDF_CENTER_BINS` + - climo_cdf.center_bins + * - :term:`PAIR_STAT_CLIMO_CDF_WRITE_BINS` + - climo_cdf.write_bins + * - :term:`PAIR_STAT_CLIMO_CDF_DIRECT_PROB` + - climo_cdf.direct_prob + +${METPLUS_OBS_WINDOW_DICT} +"""""""""""""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`OBS_WINDOW_BEGIN` + - obs_window.beg + * - :term:`OBS_WINDOW_END` + - obs_window.end + +${METPLUS_MASK_DICT} +"""""""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_MASK_GRID` + - mask.grid + * - :term:`PAIR_STAT_MASK_POLY` + - mask.poly + * - :term:`PAIR_STAT_MASK_SID` + - mask.sid + * - :term:`PAIR_STAT_MASK_LLPNT` + - mask.llpnt + +${METPLUS_CI_ALPHA} +^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_CI_ALPHA` + - ci_alpha + +${METPLUS_BOOT_DICT} +^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_BOOT_INTERVAL` + - boot.interval + * - :term:`PAIR_STAT_BOOT_REP_PROP` + - boot.rep_prop + * - :term:`PAIR_STAT_BOOT_N_REP` + - boot.n_rep + * - :term:`PAIR_STAT_BOOT_RNG` + - boot.rng + * - :term:`PAIR_STAT_BOOT_SEED` + - boot.seed + +${METPLUS_SEEPS_P1_THRESH} +"""""""""""""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_SEEPS_P1_THRESH` + - seeps_p1_thresh + +${METPLUS_OUTPUT_FLAG_DICT} +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_OUTPUT_FLAG_FHO` + - output_flag.fho + * - :term:`PAIR_STAT_OUTPUT_FLAG_CTC` + - output_flag.ctc + * - :term:`PAIR_STAT_OUTPUT_FLAG_CTS` + - output_flag.cts + * - :term:`PAIR_STAT_OUTPUT_FLAG_MCTC` + - output_flag.mctc + * - :term:`PAIR_STAT_OUTPUT_FLAG_MCTS` + - output_flag.mcts + * - :term:`PAIR_STAT_OUTPUT_FLAG_CNT` + - output_flag.cnt + * - :term:`PAIR_STAT_OUTPUT_FLAG_SL1L2` + - output_flag.sl1l2 + * - :term:`PAIR_STAT_OUTPUT_FLAG_SAL1L2` + - output_flag.sal1l2 + * - :term:`PAIR_STAT_OUTPUT_FLAG_VL1L2` + - output_flag.vl1l2 + * - :term:`PAIR_STAT_OUTPUT_FLAG_VAL1L2` + - output_flag.val1l2 + * - :term:`PAIR_STAT_OUTPUT_FLAG_VCNT` + - output_flag.vcnt + * - :term:`PAIR_STAT_OUTPUT_FLAG_PCT` + - output_flag.pct + * - :term:`PAIR_STAT_OUTPUT_FLAG_PSTD` + - output_flag.pstd + * - :term:`PAIR_STAT_OUTPUT_FLAG_PJC` + - output_flag.pjc + * - :term:`PAIR_STAT_OUTPUT_FLAG_PRC` + - output_flag.prc + * - :term:`PAIR_STAT_OUTPUT_FLAG_ECLV` + - output_flag.eclv + * - :term:`PAIR_STAT_OUTPUT_FLAG_MPR` + - output_flag.mpr + * - :term:`PAIR_STAT_OUTPUT_FLAG_SEEPS` + - output_flag.seeps + * - :term:`PAIR_STAT_OUTPUT_FLAG_SEEPS_MPR` + - output_flag.seeps_mpr + +${METPLUS_POINT_WEIGHT_FLAG} +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_POINT_WEIGHT_FLAG` + - point_weight_flag + +${METPLUS_MET_CONFIG_OVERRIDES} +""""""""""""""""""""""""""""""" + +.. list-table:: + :widths: 5 5 + :header-rows: 1 + + * - METplus Config(s) + - MET Config File + * - :term:`PAIR_STAT_MET_CONFIG_OVERRIDES` + - n/a + + .. _pb2nc_wrapper: PB2NC @@ -7071,7 +7824,6 @@ Configuration | :term:`POINT_STAT_CLIMO_MEAN_REGRID_VLD_THRESH` | :term:`POINT_STAT_CLIMO_MEAN_REGRID_SHAPE` | :term:`POINT_STAT_CLIMO_MEAN_TIME_INTERP_METHOD` -| :term:`POINT_STAT_CLIMO_MEAN_MATCH_MONTH` | :term:`POINT_STAT_CLIMO_MEAN_DAY_INTERVAL` | :term:`POINT_STAT_CLIMO_MEAN_HOUR_INTERVAL` | :term:`POINT_STAT_CLIMO_MEAN_USE_FCST` @@ -7086,7 +7838,6 @@ Configuration | :term:`POINT_STAT_CLIMO_STDEV_REGRID_VLD_THRESH` | :term:`POINT_STAT_CLIMO_STDEV_REGRID_SHAPE` | :term:`POINT_STAT_CLIMO_STDEV_TIME_INTERP_METHOD` -| :term:`POINT_STAT_CLIMO_STDEV_MATCH_MONTH` | :term:`POINT_STAT_CLIMO_STDEV_DAY_INTERVAL` | :term:`POINT_STAT_CLIMO_STDEV_HOUR_INTERVAL` | :term:`POINT_STAT_CLIMO_STDEV_USE_FCST` @@ -7589,8 +8340,6 @@ ${METPLUS_CLIMO_MEAN_DICT} - climo_mean.regrid.shape * - :term:`POINT_STAT_CLIMO_MEAN_TIME_INTERP_METHOD` - climo_mean.time_interp_method - * - :term:`POINT_STAT_CLIMO_MEAN_MATCH_MONTH` - - climo_mean.match_month * - :term:`POINT_STAT_CLIMO_MEAN_DAY_INTERVAL` - climo_mean.day_interval * - :term:`POINT_STAT_CLIMO_MEAN_HOUR_INTERVAL` @@ -7619,8 +8368,6 @@ ${METPLUS_CLIMO_STDEV_DICT} - climo_stdev.regrid.shape * - :term:`POINT_STAT_CLIMO_STDEV_TIME_INTERP_METHOD` - climo_stdev.time_interp_method - * - :term:`POINT_STAT_CLIMO_STDEV_MATCH_MONTH` - - climo_stdev.match_month * - :term:`POINT_STAT_CLIMO_STDEV_DAY_INTERVAL` - climo_stdev.day_interval * - :term:`POINT_STAT_CLIMO_STDEV_HOUR_INTERVAL` @@ -8065,7 +8812,6 @@ METplus Configuration | :term:`SERIES_ANALYSIS_CLIMO_MEAN_REGRID_VLD_THRESH` | :term:`SERIES_ANALYSIS_CLIMO_MEAN_REGRID_SHAPE` | :term:`SERIES_ANALYSIS_CLIMO_MEAN_TIME_INTERP_METHOD` -| :term:`SERIES_ANALYSIS_CLIMO_MEAN_MATCH_MONTH` | :term:`SERIES_ANALYSIS_CLIMO_MEAN_DAY_INTERVAL` | :term:`SERIES_ANALYSIS_CLIMO_MEAN_HOUR_INTERVAL` | :term:`SERIES_ANALYSIS_CLIMO_MEAN_FILE_TYPE` @@ -8081,7 +8827,6 @@ METplus Configuration | :term:`SERIES_ANALYSIS_CLIMO_STDEV_REGRID_VLD_THRESH` | :term:`SERIES_ANALYSIS_CLIMO_STDEV_REGRID_SHAPE` | :term:`SERIES_ANALYSIS_CLIMO_STDEV_TIME_INTERP_METHOD` -| :term:`SERIES_ANALYSIS_CLIMO_STDEV_MATCH_MONTH` | :term:`SERIES_ANALYSIS_CLIMO_STDEV_DAY_INTERVAL` | :term:`SERIES_ANALYSIS_CLIMO_STDEV_HOUR_INTERVAL` | :term:`SERIES_ANALYSIS_CLIMO_STDEV_FILE_TYPE` @@ -8502,8 +9247,6 @@ ${METPLUS_CLIMO_MEAN_DICT} - climo_mean.regrid.shape * - :term:`SERIES_ANALYSIS_CLIMO_MEAN_TIME_INTERP_METHOD` - climo_mean.time_interp_method - * - :term:`SERIES_ANALYSIS_CLIMO_MEAN_MATCH_MONTH` - - climo_mean.match_month * - :term:`SERIES_ANALYSIS_CLIMO_MEAN_DAY_INTERVAL` - climo_mean.day_interval * - :term:`SERIES_ANALYSIS_CLIMO_MEAN_HOUR_INTERVAL` @@ -8534,8 +9277,6 @@ ${METPLUS_CLIMO_STDEV_DICT} - climo_stdev.regrid.shape * - :term:`SERIES_ANALYSIS_CLIMO_STDEV_TIME_INTERP_METHOD` - climo_stdev.time_interp_method - * - :term:`SERIES_ANALYSIS_CLIMO_STDEV_MATCH_MONTH` - - climo_stdev.match_month * - :term:`SERIES_ANALYSIS_CLIMO_STDEV_DAY_INTERVAL` - climo_stdev.day_interval * - :term:`SERIES_ANALYSIS_CLIMO_STDEV_HOUR_INTERVAL` diff --git a/docs/use_cases/met_tool_wrapper/PairStat/PairStat.py b/docs/use_cases/met_tool_wrapper/PairStat/PairStat.py new file mode 100644 index 0000000000..5e95ee2749 --- /dev/null +++ b/docs/use_cases/met_tool_wrapper/PairStat/PairStat.py @@ -0,0 +1,107 @@ +""" +PairStat: Basic Use Case +======================== + +met_tool_wrapper/PairStat/PairStat.conf + +""" +############################################################################## +# Scientific Objective +# -------------------- +# +# Compare hourly forecasts for temperature, u-, and v-wind components to observations +# in a 3-hour observation window. Generate statistics of the results. + +############################################################################## +# Datasets +# -------- +# +# | **Pairs:** PointStat matched pairs output: temperature, u-wind component, and v-wind component +# +# | **Location:** All of the input data required for this use case can be found in the met_test sample data tarball. Click here to the METplus releases page and download sample data for the appropriate release: https://github.com/dtcenter/METplus/releases +# | This tarball should be unpacked into the directory that you will set the value of INPUT_BASE. See `Running METplus`_ section for more information. +# +# | **Data Source:** Unknown +# | + +############################################################################## +# METplus Components +# ------------------ +# +# This use case utilizes the METplus PairStat wrapper to search for +# files that are valid at a given run time and generate a command to run +# the MET tool pair_stat if all required files are found. + +############################################################################## +# METplus Workflow +# ---------------- +# +# PairStat is the only tool called in this example. It processes the following +# run times: +# +# | **Valid:** 2007-03-30_0Z +# | + +############################################################################## +# METplus Configuration +# --------------------- +# +# parm/use_cases/met_tool_wrapper/PairStat/PairStat.conf +# +# .. highlight:: bash +# .. literalinclude:: ../../../../parm/use_cases/met_tool_wrapper/PairStat/PairStat.conf + +############################################################################## +# MET Configuration +# --------------------- +# +# .. note:: +# See the :ref:`PairStat MET Configuration` +# section of the User's Guide for more information on the environment +# variables used in the file below. +# +# parm/met_config/PairStatConfig_wrapped +# +# .. highlight:: bash +# .. literalinclude:: ../../../../parm/met_config/PairStatConfig_wrapped + +############################################################################## +# Running METplus +# --------------- +# +# Provide the use case .conf configuration file to the run_metplus.py script. +# +# /path/to/METplus/parm/use_cases/met_tool_wrapper/PairStat/PairStat.conf +# +# See the :ref:`running-metplus` section of the System Configuration chapter +# for more details. +# + +############################################################################## +# Expected Output +# --------------- +# +# A successful run will output the following both to the screen and to the logfile:: +# +# INFO: METplus has successfully finished running. +# +# Refer to the value set for **OUTPUT_BASE** to find where the output data was generated. +# Output for this use case will be found in pair_stat (relative to **OUTPUT_BASE**) +# and will contain the following files: +# +# * pair_stat_360000L_20070331_120000V.stat + +############################################################################## +# Keywords +# -------- +# +# .. note:: +# +# * PairStatToolUseCase +# +# Navigate to the :ref:`quick-search` page to discover other similar use cases. +# +# +# +# sphinx_gallery_thumbnail_path = '_static/met_tool_wrapper-PairStat.png' +# diff --git a/docs/use_cases/met_tool_wrapper/PairStat/README.rst b/docs/use_cases/met_tool_wrapper/PairStat/README.rst new file mode 100644 index 0000000000..8b041b277a --- /dev/null +++ b/docs/use_cases/met_tool_wrapper/PairStat/README.rst @@ -0,0 +1,2 @@ +PairStat +-------- diff --git a/internal/scripts/dev_tools/add_met_config_helper.py b/internal/scripts/dev_tools/add_met_config_helper.py index 3dd55af4ff..da7bac8c85 100755 --- a/internal/scripts/dev_tools/add_met_config_helper.py +++ b/internal/scripts/dev_tools/add_met_config_helper.py @@ -42,6 +42,9 @@ def print_doc_text(tool_name, input_dict): _print_script_info_text() wrapper_camel = get_wrapper_name(tool_name) + if wrapper_camel is None: + print(f'ERROR: Invalid tool name: {tool_name}') + sys.exit(1) # get info for each variable and store it in a dictionary met_vars = _get_met_vars(tool_name, input_dict) diff --git a/internal/tests/config/GridStatConfig_prob_precip b/internal/tests/config/GridStatConfig_prob_precip deleted file mode 100755 index 735ca6d4f7..0000000000 --- a/internal/tests/config/GridStatConfig_prob_precip +++ /dev/null @@ -1,222 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// Grid-Stat configuration file. -// -// For additional information, see the MET_BASE/config/README file. -// -//////////////////////////////////////////////////////////////////////////////// - -// -// Output model name to be written -// -model = "${MODEL}"; - -// -// Output description to be written -// May be set separately in each "obs.field" entry -// -desc = "NA"; - -// -// Output observation type to be written -// -obtype = "${OBTYPE}"; - -//////////////////////////////////////////////////////////////////////////////// - -// -// Verification grid -// -regrid = { - to_grid = NONE; - method = NEAREST; - width = 1; - vld_thresh = 0.5; - shape = SQUARE; -} - -//////////////////////////////////////////////////////////////////////////////// - -// -// May be set separately in each "field" entry -// -censor_thresh = []; -censor_val = []; -cat_thresh = []; -cnt_thresh = [ NA ]; -cnt_logic = UNION; -wind_thresh = [ NA ]; -wind_logic = UNION; -eclv_points = 0.05; -nc_pairs_var_str = ""; -rank_corr_flag = FALSE; - -// -// Forecast and observation fields to be verified -// -fcst = { - field = [ ${FCST_FIELD} ]; - }; - -obs = { - field = [ ${OBS_FIELD} ]; - }; - -//////////////////////////////////////////////////////////////////////////////// - -// -// Climatology data -// -climo_mean = fcst; -climo_mean = { - file_name = [ ${CLIMO_FILE} ]; - - regrid = { - method = BILIN; - width = 2; - vld_thresh = 0.5; - shape = SQUARE; - } - - time_interp_method = NEAREST; - match_month = TRUE; - match_day = TRUE; - time_step = 21600; -} - -climo_stdev = climo_mean; -climo_stdev = { - file_name = []; -} - -climo_cdf_bins = 1; -write_cdf_bins = FALSE; - -//////////////////////////////////////////////////////////////////////////////// - -// -// Verification masking regions -// -mask = { - grid = [ "FULL" ]; - poly = []; -} - -//////////////////////////////////////////////////////////////////////////////// - -// -// Confidence interval settings -// -ci_alpha = [ 0.05 ]; - -boot = { - interval = PCTILE; - rep_prop = 1.0; - n_rep = 0; - rng = "mt19937"; - seed = ""; -} - -//////////////////////////////////////////////////////////////////////////////// - -// -// Data smoothing methods -// -interp = { - field = NONE; - vld_thresh = 1.0; - shape = SQUARE; - - type = [ - { - method = NEAREST; - width = 1; - } - ]; -} - -//////////////////////////////////////////////////////////////////////////////// - -// -// Neighborhood methods -// -nbrhd = { - field = NONE; - width = [ 1 ]; - cov_thresh = [ >=0.5 ]; - vld_thresh = 1.0; - shape = SQUARE; -} - -//////////////////////////////////////////////////////////////////////////////// - -// -// Fourier decomposition -// -fourier = { - wave_1d_beg = [ 0, 0, 10, 4 ]; - wave_1d_end = [ 20, 3, 20, 9 ]; -} - -//////////////////////////////////////////////////////////////////////////////// - -// -// Gradient statistics -// May be set separately in each "obs.field" entry -// -gradient = { - dx = [ 1 ]; - dy = [ 1 ]; -} - -//////////////////////////////////////////////////////////////////////////////// - -// -// Statistical output types -// -output_flag = { - fho = NONE; - ctc = NONE; - cts = NONE; - mctc = NONE; - mcts = NONE; - cnt = NONE; - sl1l2 = NONE; - sal1l2 = NONE; - vl1l2 = NONE; - val1l2 = NONE; - vcnt = NONE; - pct = STAT; - pstd = STAT; - pjc = STAT; - prc = STAT; - eclv = NONE; - nbrctc = NONE; - nbrcts = NONE; - nbrcnt = NONE; - grad = NONE; -} - -// -// NetCDF matched pairs output file -// -nc_pairs_flag = { - latlon = FALSE; - raw = FALSE; - diff = FALSE; - climo = FALSE; - weight = FALSE; - nbrhd = FALSE; - fourier = FALSE; - gradient = FALSE; - apply_mask = FALSE; -} - -//////////////////////////////////////////////////////////////////////////////// - -grid_weight_flag = NONE; -tmp_dir = "/tmp"; -output_prefix = "height_${MODEL}_${FCST_VAR}_vs_${OBTYPE}_${OBS_VAR}_${LEVEL}"; -//version = "V8.1"; - -//////////////////////////////////////////////////////////////////////////////// diff --git a/internal/tests/config/STATAnalysisConfig b/internal/tests/config/STATAnalysisConfig deleted file mode 100644 index 844ee4243c..0000000000 --- a/internal/tests/config/STATAnalysisConfig +++ /dev/null @@ -1,94 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// STAT-Analysis configuration file. -// -// For additional information, see the MET_BASE/config/README file. -// -//////////////////////////////////////////////////////////////////////////////// - -// -// Filtering input STAT lines by the contents of each column -// -${METPLUS_MODEL} -${METPLUS_DESC} - -fcst_lead = [${FCST_LEAD}]; -obs_lead = [${OBS_LEAD}]; - -fcst_valid_beg = "${FCST_VALID_BEG}"; -fcst_valid_end = "${FCST_VALID_END}"; -fcst_valid_hour = [${FCST_VALID_HOUR}]; - -obs_valid_beg = "${OBS_VALID_BEG}"; -obs_valid_end = "${OBS_VALID_END}"; -obs_valid_hour = [${OBS_VALID_HOUR}]; - -fcst_init_beg = "${FCST_INIT_BEG}"; -fcst_init_end = "${FCST_INIT_END}"; -fcst_init_hour = [${FCST_INIT_HOUR}]; - -obs_init_beg = "${OBS_INIT_BEG}"; -obs_init_end = "${OBS_INIT_END}"; -obs_init_hour = [${OBS_INIT_HOUR}]; - -fcst_var = [${FCST_VAR}]; -obs_var = [${OBS_VAR}]; - -fcst_units = [${FCST_UNITS}]; -obs_units = [${OBS_UNITS}]; - -fcst_lev = [${FCST_LEVEL}]; -obs_lev = [${OBS_LEVEL}]; - -${METPLUS_OBTYPE} - -vx_mask = [${VX_MASK}]; - -interp_mthd = [${INTERP_MTHD}]; - -interp_pnts = [${INTERP_PNTS}]; - -fcst_thresh = [${FCST_THRESH}]; -obs_thresh = [${OBS_THRESH}]; -cov_thresh = [${COV_THRESH}]; - -alpha = [${ALPHA}]; - -line_type = [${LINE_TYPE}]; - -column = []; - -weight = []; - -//////////////////////////////////////////////////////////////////////////////// - -// -// Array of STAT-Analysis jobs to be performed on the filtered data -// -jobs = [ - "${JOB}" -]; - -//////////////////////////////////////////////////////////////////////////////// - -// -// Confidence interval settings -// -out_alpha = 0.05; - -boot = { - interval = PCTILE; - rep_prop = 1.0; - n_rep = 0; - rng = "mt19937"; - seed = ""; -} - -//////////////////////////////////////////////////////////////////////////////// - -rank_corr_flag = FALSE; -vif_flag = FALSE; -tmp_dir = "/tmp"; -//version = "V8.1"; - -//////////////////////////////////////////////////////////////////////////////// diff --git a/internal/tests/config/ascii2nc_mask_grid.conf b/internal/tests/config/ascii2nc_mask_grid.conf deleted file mode 100644 index d5af25b0ee..0000000000 --- a/internal/tests/config/ascii2nc_mask_grid.conf +++ /dev/null @@ -1,55 +0,0 @@ -[config] -## Configuration-related settings such as the process list, begin and end times, etc. -PROCESS_LIST = Ascii2Nc - -## LOOP_ORDER -## Options are: processes, times -## Looping by time- runs all items in the PROCESS_LIST for each -## initialization time and repeats until all times have been evaluated. -## Looping by processes- run each item in the PROCESS_LIST for all -## specified initialization times then repeat for the next item in the -## PROCESS_LIST. -LOOP_ORDER = times - -LOOP_BY = VALID -VALID_TIME_FMT = %Y%m%d%H -VALID_BEG = 2013082720 -VALID_END = 2013082720 -VALID_INCREMENT = 1M - -ASCII2NC_FILE_WINDOW_BEGIN = -7H -ASCII2NC_FILE_WINDOW_END = 0 - -LOG_ASCII2NC_VERBOSITY = 1 - -## MET Configuration file for ascii2nc - none used for this example -ASCII2NC_CONFIG_FILE = - -ASCII2NC_INPUT_FORMAT = - -ASCII2NC_MASK_GRID = /d3/personal/mccabe/mf_test/met-8.0_fortify/data/mnc/test_grid_valid.nc - -ASCII2NC_MASK_POLY = - -ASCII2NC_MASK_SID = - -ASCII2NC_TIME_SUMMARY_FLAG = False -ASCII2NC_TIME_SUMMARY_RAW_DATA = False -ASCII2NC_TIME_SUMMARY_BEG = 000000 -ASCII2NC_TIME_SUMMARY_END = 235959 -ASCII2NC_TIME_SUMMARY_STEP = 300 -ASCII2NC_TIME_SUMMARY_WIDTH = 600 -ASCII2NC_TIME_SUMMARY_GRIB_CODES = 11, 204, 211 -ASCII2NC_TIME_SUMMARY_VAR_NAMES = -ASCII2NC_TIME_SUMMARY_TYPES = min, max, range, mean, stdev, median, p80 -ASCII2NC_TIME_SUMMARY_VALID_FREQ = 0 -ASCII2NC_TIME_SUMMARY_VALID_THRESH = 0.0 - - -[dir] -ASCII2NC_INPUT_DIR = /d3/projects/MET/MET_test_data/unit_test/obs_data/insitu_ascii -ASCII2NC_OUTPUT_DIR = - -[filename_templates] -ASCII2NC_INPUT_TEMPLATE = {valid?fmt=%Y%m%d}/edr_hourly.{valid?fmt=%Y%m%d}.{valid?fmt=%H}.ascii -ASCII2NC_OUTPUT_TEMPLATE = {OUTPUT_BASE}/ascii2nc/edr_hourly.{valid?fmt=%Y%m%d}.mask_grid_data.nc diff --git a/internal/tests/config/ascii2nc_mask_poly.conf b/internal/tests/config/ascii2nc_mask_poly.conf deleted file mode 100644 index 01f25e505f..0000000000 --- a/internal/tests/config/ascii2nc_mask_poly.conf +++ /dev/null @@ -1,57 +0,0 @@ -[config] -## Configuration-related settings such as the process list, begin and end times, etc. -PROCESS_LIST = Ascii2Nc - -## LOOP_ORDER -## Options are: processes, times -## Looping by time- runs all items in the PROCESS_LIST for each -## initialization time and repeats until all times have been evaluated. -## Looping by processes- run each item in the PROCESS_LIST for all -## specified initialization times then repeat for the next item in the -## PROCESS_LIST. -LOOP_ORDER = times - -LOOP_BY = VALID -VALID_TIME_FMT = %d%b%Y_%H:%M -VALID_BEG = 09Apr2012_12:00 -VALID_END = 09Apr2012_12:00 -VALID_INCREMENT = 1M - -LEAD_SEQ = 3 - -ASCII2NC_FILE_WINDOW_BEGIN = 0 -ASCII2NC_FILE_WINDOW_END = 0 - -LOG_ASCII2NC_VERBOSITY = 1 - -## MET Configuration file for ascii2nc - none used for this example -ASCII2NC_CONFIG_FILE = - -ASCII2NC_INPUT_FORMAT = - -ASCII2NC_MASK_GRID = - -ASCII2NC_MASK_POLY = {INPUT_BASE}/met_test/data/poly/LMV.poly - -ASCII2NC_MASK_SID = - -ASCII2NC_TIME_SUMMARY_FLAG = False -ASCII2NC_TIME_SUMMARY_RAW_DATA = False -ASCII2NC_TIME_SUMMARY_BEG = 000000 -ASCII2NC_TIME_SUMMARY_END = 235959 -ASCII2NC_TIME_SUMMARY_STEP = 300 -ASCII2NC_TIME_SUMMARY_WIDTH = 600 -ASCII2NC_TIME_SUMMARY_GRIB_CODES = 11, 204, 211 -ASCII2NC_TIME_SUMMARY_VAR_NAMES = -ASCII2NC_TIME_SUMMARY_TYPES = min, max, range, mean, stdev, median, p80 -ASCII2NC_TIME_SUMMARY_VALID_FREQ = 0 -ASCII2NC_TIME_SUMMARY_VALID_THRESH = 0.0 - - -[dir] -ASCII2NC_INPUT_DIR = /d3/projects/MET/MET_test_data/unit_test/obs_data/trmm -ASCII2NC_OUTPUT_DIR = - -[filename_templates] -ASCII2NC_INPUT_TEMPLATE = TRMM_3B42.007.accumulated_precipitation.{valid?fmt=%H:%M?shift=-90M}Z{valid?fmt=%d%b%Y}.G3.output.mtxt -ASCII2NC_OUTPUT_TEMPLATE = {OUTPUT_BASE}/ascii2nc/trmm_{valid?fmt=%Y%m%d%H}_3hr_mask_poly_lmv.nc diff --git a/internal/tests/config/ascii2nc_mask_sid.conf b/internal/tests/config/ascii2nc_mask_sid.conf deleted file mode 100644 index c3dbaa2b33..0000000000 --- a/internal/tests/config/ascii2nc_mask_sid.conf +++ /dev/null @@ -1,55 +0,0 @@ -[config] -## Configuration-related settings such as the process list, begin and end times, etc. -PROCESS_LIST = Ascii2Nc - -## LOOP_ORDER -## Options are: processes, times -## Looping by time- runs all items in the PROCESS_LIST for each -## initialization time and repeats until all times have been evaluated. -## Looping by processes- run each item in the PROCESS_LIST for all -## specified initialization times then repeat for the next item in the -## PROCESS_LIST. -LOOP_ORDER = times - -LOOP_BY = VALID -VALID_TIME_FMT = %Y%m%d%H -VALID_BEG = 2013082720 -VALID_END = 2013082720 -VALID_INCREMENT = 1M - -ASCII2NC_FILE_WINDOW_BEGIN = -7H -ASCII2NC_FILE_WINDOW_END = 0 - -LOG_ASCII2NC_VERBOSITY = 1 - -## MET Configuration file for ascii2nc - none used for this example -ASCII2NC_CONFIG_FILE = - -ASCII2NC_INPUT_FORMAT = - -ASCII2NC_MASK_GRID = - -ASCII2NC_MASK_POLY = - -ASCII2NC_MASK_SID = "MY_STATIONS:N526UA,N567UA,N571UA,N594UA" - -ASCII2NC_TIME_SUMMARY_FLAG = False -ASCII2NC_TIME_SUMMARY_RAW_DATA = False -ASCII2NC_TIME_SUMMARY_BEG = 000000 -ASCII2NC_TIME_SUMMARY_END = 235959 -ASCII2NC_TIME_SUMMARY_STEP = 300 -ASCII2NC_TIME_SUMMARY_WIDTH = 600 -ASCII2NC_TIME_SUMMARY_GRIB_CODES = 11, 204, 211 -ASCII2NC_TIME_SUMMARY_VAR_NAMES = -ASCII2NC_TIME_SUMMARY_TYPES = min, max, range, mean, stdev, median, p80 -ASCII2NC_TIME_SUMMARY_VALID_FREQ = 0 -ASCII2NC_TIME_SUMMARY_VALID_THRESH = 0.0 - - -[dir] -ASCII2NC_INPUT_DIR = /d3/projects/MET/MET_test_data/unit_test/obs_data/insitu_ascii -ASCII2NC_OUTPUT_DIR = - -[filename_templates] -ASCII2NC_INPUT_TEMPLATE = {valid?fmt=%Y%m%d}/edr_hourly.{valid?fmt=%Y%m%d}.{valid?fmt=%H}.ascii -ASCII2NC_OUTPUT_TEMPLATE = {OUTPUT_BASE}/ascii2nc/edr_hourly.{valid?fmt=%Y%m%d}.mask_sid.nc diff --git a/internal/tests/config/ascii2nc_multi_file.conf b/internal/tests/config/ascii2nc_multi_file.conf deleted file mode 100644 index 9426ab1f27..0000000000 --- a/internal/tests/config/ascii2nc_multi_file.conf +++ /dev/null @@ -1,58 +0,0 @@ -[config] -## Configuration-related settings such as the process list, begin and end times, etc. -PROCESS_LIST = Ascii2Nc - -## LOOP_ORDER -## Options are: processes, times -## Looping by time- runs all items in the PROCESS_LIST for each -## initialization time and repeats until all times have been evaluated. -## Looping by processes- run each item in the PROCESS_LIST for all -## specified initialization times then repeat for the next item in the -## PROCESS_LIST. -LOOP_ORDER = times - -LOOP_BY = VALID -VALID_TIME_FMT = %Y%j -VALID_BEG = 2012137 -VALID_END = 2012137 -VALID_INCREMENT = 1M - -OBS_ASCII2NC_FILE_WINDOW_BEGIN = 0 -OBS_ASCII2NC_FILE_WINDOW_END = 0 - -LOG_ASCII2NC_VERBOSITY = 1 - -## MET Configuration file for ascii2nc - none used for this example -ASCII2NC_CONFIG_FILE = - -ASCII2NC_INPUT_FORMAT = - -ASCII2NC_MASK_GRID = - -ASCII2NC_MASK_POLY = - -ASCII2NC_MASK_SID = - -ASCII2NC_FILE_WINDOW_BEGIN = -1D -ASCII2NC_FILE_WINDOW_END = 1D - -ASCII2NC_TIME_SUMMARY_FLAG = False -ASCII2NC_TIME_SUMMARY_RAW_DATA = False -ASCII2NC_TIME_SUMMARY_BEG = 000000 -ASCII2NC_TIME_SUMMARY_END = 235959 -ASCII2NC_TIME_SUMMARY_STEP = 300 -ASCII2NC_TIME_SUMMARY_WIDTH = 600 -ASCII2NC_TIME_SUMMARY_GRIB_CODES = 11, 204, 211 -ASCII2NC_TIME_SUMMARY_VAR_NAMES = -ASCII2NC_TIME_SUMMARY_TYPES = min, max, range, mean, stdev, median, p80 -ASCII2NC_TIME_SUMMARY_VALID_FREQ = 0 -ASCII2NC_TIME_SUMMARY_VALID_THRESH = 0.0 - - -[dir] -ASCII2NC_INPUT_DIR = /d3/projects/MET/MET_test_data/unit_test/obs_data/ascii -ASCII2NC_OUTPUT_DIR = {OUTPUT_BASE}/ascii2nc - -[filename_templates] -ASCII2NC_INPUT_TEMPLATE = surfrad_tbl{valid?fmt=%y%j}.txt -ASCII2NC_OUTPUT_TEMPLATE = surfrad_tbl{valid?fmt=%y}.nc diff --git a/internal/tests/config/ascii2nc_single_file.conf b/internal/tests/config/ascii2nc_single_file.conf deleted file mode 100644 index dd73fbb8c2..0000000000 --- a/internal/tests/config/ascii2nc_single_file.conf +++ /dev/null @@ -1,55 +0,0 @@ -[config] -## Configuration-related settings such as the process list, begin and end times, etc. -PROCESS_LIST = Ascii2Nc - -## LOOP_ORDER -## Options are: processes, times -## Looping by time- runs all items in the PROCESS_LIST for each -## initialization time and repeats until all times have been evaluated. -## Looping by processes- run each item in the PROCESS_LIST for all -## specified initialization times then repeat for the next item in the -## PROCESS_LIST. -LOOP_ORDER = times - -LOOP_BY = VALID -VALID_TIME_FMT = %Y%m%d -VALID_BEG = 20170601 -VALID_END = 20170601 -VALID_INCREMENT = 1M - -ASCII2NC_FILE_WINDOW_BEGIN = 0 -ASCII2NC_FILE_WINDOW_END = 0 - -LOG_ASCII2NC_VERBOSITY = 1 - -## MET Configuration file for ascii2nc - none used for this example -ASCII2NC_CONFIG_FILE = - -ASCII2NC_INPUT_FORMAT = - -ASCII2NC_MASK_GRID = - -ASCII2NC_MASK_POLY = - -ASCII2NC_MASK_SID = - -ASCII2NC_TIME_SUMMARY_FLAG = False -ASCII2NC_TIME_SUMMARY_RAW_DATA = False -ASCII2NC_TIME_SUMMARY_BEG = 000000 -ASCII2NC_TIME_SUMMARY_END = 235959 -ASCII2NC_TIME_SUMMARY_STEP = 300 -ASCII2NC_TIME_SUMMARY_WIDTH = 600 -ASCII2NC_TIME_SUMMARY_GRIB_CODES = 11, 204, 211 -ASCII2NC_TIME_SUMMARY_VAR_NAMES = -ASCII2NC_TIME_SUMMARY_TYPES = min, max, range, mean, stdev, median, p80 -ASCII2NC_TIME_SUMMARY_VALID_FREQ = 0 -ASCII2NC_TIME_SUMMARY_VALID_THRESH = 0.0 - - -[dir] -ASCII2NC_INPUT_DIR = -ASCII2NC_OUTPUT_DIR = - -[filename_templates] -ASCII2NC_INPUT_TEMPLATE = /d3/projects/MET/MET_test_data/unit_test/obs_data/ascii/dup_test_qty.txt -ASCII2NC_OUTPUT_TEMPLATE = {OUTPUT_BASE}/ascii2nc/dup_test.nc diff --git a/internal/tests/config/grid_gefs_prob.conf b/internal/tests/config/grid_gefs_prob.conf deleted file mode 100644 index ca9ce8fd7d..0000000000 --- a/internal/tests/config/grid_gefs_prob.conf +++ /dev/null @@ -1,75 +0,0 @@ -# Grid to Grid Probability - -[config] -# loop by VALID time -LOOP_BY = INIT - -# Format of VALID_BEG and VALID_END -INIT_TIME_FMT = %Y%m%d%H - -# Start time for METplus run -INIT_BEG = 2019051400 - -# End time for METplus run -INIT_END = 2019051600 - -# Increment between METplus runs in seconds. Must be >= 60 -INIT_INCREMENT = 86400 - -# Options are times, processes -# times = run all items in the PROCESS_LIST for a single initialization -# time, then repeat until all times have been evaluated. -# processes = run each item in the PROCESS_LIST for all times -# specified, then repeat for the next item in the PROCESS_LIST. -LOOP_ORDER = times - -# List of applications to run -PROCESS_LIST = GridStat - -# list of variables to compare -FCST_VAR1_NAME = ProbConvWx -FCST_VAR1_LEVELS = "(0,0,*,*)" -FCST_GRID_STAT_PROB_THRESH = ge0.0, ge0.1, ge0.2, ge0.3, ge0.4, ge0.5, ge0.6, ge0.7, ge0.9, ge0.9, ge1.0 -FCST_VAR1_THRESH = ge2.0 - -OBS_VAR1_NAME = cmorph -OBS_VAR1_LEVELS = "(0,0,*,*)" -OBS_VAR1_THRESH = ge2.0 - -# list of forecast leads to process -LEAD_SEQ = 24 - -# description of data to be processed -# used in output file path -MODEL = GFS -OBTYPE = CMORPH - -# location of grid_stat MET config file -GRID_STAT_CONFIG_FILE = {CONFIG_DIR}/GridStatConfig_prob_precip - -# variables to describe format of forecast data -FCST_IS_PROB = true -FCST_GRID_STAT_INPUT_DATATYPE = NETCDF - -# variables to describe format of observation data -# none needed - - -[dir] -# location of configuration files used by MET applications -CONFIG_DIR=/home/kalb/MET/METplus/parm/use_cases/grid_to_grid/met_config - -# input and output data directories -FCST_GRID_STAT_INPUT_DIR = {INPUT_BASE}/prob/GEFS -OBS_GRID_STAT_INPUT_DIR = {INPUT_BASE}/obs/cmorph/3hr -GRID_STAT_OUTPUT_DIR = {INPUT_BASE}/met_out/prob/GEFS - -[filename_templates] -# format of filenames -# FCST -FCST_GRID_STAT_INPUT_TEMPLATE = {init?fmt=%Y%m%d}/g_{init?fmt=%H%M%S}/f_{lead?fmt=%8S}.mdv.nc - -# ANLYS -OBS_GRID_STAT_INPUT_TEMPLATE = {valid?fmt=%Y%m%d}/{valid?fmt=%H%M%S}.mdv.nc - -GRID_STAT_OUTPUT_TEMPLATE = {valid?fmt=%Y%m%d%H%M}/grid_stat diff --git a/internal/tests/pytests/component_versions/test_component_versions.py b/internal/tests/pytests/component_versions/test_component_versions.py index 64a594de4f..62072800de 100644 --- a/internal/tests/pytests/component_versions/test_component_versions.py +++ b/internal/tests/pytests/component_versions/test_component_versions.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import pytest +from unittest import mock from metplus import component_versions @@ -17,6 +18,7 @@ ('metcalcpy', 'v3.0.0-rc1', '6.0'), ('METplus', '6.0-latest', '6.0'), ('METplus', '3.0-latest', None), + ('METmrPlow', '6.1.0', None), ] ) @pytest.mark.util @@ -44,6 +46,8 @@ def test_get_coordinated_version(component, version, expected_result): # get latest bugfix version from main branch or X.Y version ('MET', 'main_v11.1', 'MET', '{X}.{Y}.{Z}{N}', '11.1.1'), ('MET', '11.1.Z', 'MET', '{X}.{Y}.{Z}{N}', '11.1.1'), + ('METmrPlow', '11.1.Z', 'MET', '{X}.{Y}.{Z}{N}', None), + ('MET', '11.1.Z', 'METmrPlow', '{X}.{Y}.{Z}{N}', None), ] ) @pytest.mark.util @@ -66,3 +70,18 @@ def test_get_component_version(input_component, input_version, output_component, @pytest.mark.util def test_get_component_version_get_dev(input_version, get_dev, rc_is_dev, expected_result): assert component_versions.get_component_version('METplus', input_version, 'METplus', get_dev=get_dev, rc_is_dev=rc_is_dev) == expected_result + +@pytest.mark.util +def test_main(): + with mock.patch.object(component_versions, '__name__', '__main__'): + with mock.patch.object(component_versions.sys, 'argv', ['component_versions.py', '-v', '6.0.0', '-o', 'METplus']): + assert component_versions.main() == 'v6.0.0' + +@pytest.mark.util +def test_init(): + with mock.patch.object(component_versions, 'main', return_value=None): + with mock.patch.object(component_versions, '__name__', '__main__'): + with mock.patch.object(component_versions.sys, 'argv', ['component_versions.py']): + with mock.patch.object(component_versions.sys, 'exit') as mock_exit: + component_versions.init() + assert mock_exit.call_args[0][0] == 1 diff --git a/internal/tests/pytests/conftest.py b/internal/tests/pytests/conftest.py index bf6396dcc9..833e200556 100644 --- a/internal/tests/pytests/conftest.py +++ b/internal/tests/pytests/conftest.py @@ -1,10 +1,9 @@ +import pytest + import sys import os -import shlex -import subprocess -import pytest -import getpass import shutil +from datetime import datetime, timedelta from unittest import mock from pathlib import Path from netCDF4 import Dataset @@ -33,6 +32,50 @@ print(f"ERROR: Cannot write to $METPLUS_TEST_OUTPUT_BASE: {output_base}") sys.exit(2) +# used in tests like ioda2nc and ascii2nc to test runtime frequency options +# values correspond to missing, run, thresh, errors, allow_missing, runtime_freq +obs_to_nc_runtime_freq_test_params = [ + (16, 24, 0.3, 0, True, 'RUN_ONCE_FOR_EACH'), + (16, 24, 0.7, 1, True, 'RUN_ONCE_FOR_EACH'), + (16, 24, 0.3, 16, False, 'RUN_ONCE_FOR_EACH'), + (2, 4, 0.4, 0, True, 'RUN_ONCE_PER_INIT_OR_VALID'), + (2, 4, 0.6, 1, True, 'RUN_ONCE_PER_INIT_OR_VALID'), + (2, 4, 0.6, 16, False, 'RUN_ONCE_PER_INIT_OR_VALID'), + (2, 5, 0.4, 0, True, 'RUN_ONCE_PER_LEAD'), + (2, 5, 0.7, 1, True, 'RUN_ONCE_PER_LEAD'), + (2, 5, 0.4, 17, False, 'RUN_ONCE_PER_LEAD'), + (0, 1, 0.4, 0, True, 'RUN_ONCE'), + (0, 1, 0.4, 16, False, 'RUN_ONCE'), + ] + + +# used in tests like grid_stat and wavelet_stat to test runtime frequency options +# values correspond to once_per_field, missing, run, thresh, errors, allow_missing +stat_runtime_freq_test_params = [ + (False, 6, 12, 0.5, 0, True), + (False, 6, 12, 0.6, 1, True), + (True, 12, 24, 0.5, 0, True), + (True, 12, 24, 0.6, 1, True), + (False, 6, 12, 0.5, 10, False), + (True, 12, 24, 0.5, 20, False), + ] + +@pytest.fixture +def fcst_and_obs_data(): + fcst_name = 'APCP' + fcst_level = 'A03' + obs_name = 'APCP_03' + obs_level = '"(*,*)"' + + inits = ['2005080700', '2005080712'] + time_fmt = '%Y%m%d%H' + lead_hour = 12 + valids = [] + for init in inits: + valid = datetime.strptime(init, time_fmt) + timedelta(hours=lead_hour) + valid = valid.strftime(time_fmt) + valids.append(valid) + return fcst_name, fcst_level, obs_name, obs_level, inits, valids, lead_hour, time_fmt @pytest.fixture(scope="session", autouse=True) def custom_tmpdir(): @@ -224,7 +267,7 @@ def do_comparison(all_commands, expected_cmds, env_var_values, wrapper, for (actual_cmd, env_vars), expected_cmd in zip(all_commands, expected_cmds): # ensure commands are generated as expected - assert expected_cmd == actual_cmd + assert actual_cmd == expected_cmd # check that environment variables were set properly # including deprecated env vars (not in wrapper env var keys) @@ -235,7 +278,7 @@ def do_comparison(all_commands, expected_cmds, env_var_values, wrapper, assert match is not None value = match.split('=', 1)[1] if special_values is not None and env_var_key in special_values: - assert special_values[env_var_key] == value + assert value == special_values[env_var_key] else: assert value == env_var_values.get(env_var_key, '') diff --git a/internal/tests/pytests/util/met_config/test_met_config.py b/internal/tests/pytests/util/met_config/test_met_config.py index 848fa73cef..346eabf794 100644 --- a/internal/tests/pytests/util/met_config/test_met_config.py +++ b/internal/tests/pytests/util/met_config/test_met_config.py @@ -96,9 +96,6 @@ def test_read_climo_field(metplus_config, config_overrides, expected_value): # 13 time_interp_method ({f'APP_CLIMO_{type_upper}_TIME_INTERP_METHOD': 'NEAREST', }, f'climo_{type_lower} = ' + '{time_interp_method = NEAREST;}'), - # 14 match_month - ({f'APP_CLIMO_{type_upper}_MATCH_MONTH': 'True', }, - f'climo_{type_lower} = ' + '{match_month = TRUE;}'), # 15 day_interval - int ({f'APP_CLIMO_{type_upper}_DAY_INTERVAL': '30', }, f'climo_{type_lower} = ' + '{day_interval = 30;}'), @@ -117,7 +114,6 @@ def test_read_climo_field(metplus_config, config_overrides, expected_value): f'APP_CLIMO_{type_upper}_REGRID_VLD_THRESH': '0.5', f'APP_CLIMO_{type_upper}_REGRID_SHAPE': 'SQUARE', f'APP_CLIMO_{type_upper}_TIME_INTERP_METHOD': 'NEAREST', - f'APP_CLIMO_{type_upper}_MATCH_MONTH': 'True', f'APP_CLIMO_{type_upper}_DAY_INTERVAL': '30', f'APP_CLIMO_{type_upper}_HOUR_INTERVAL': '12', }, @@ -127,7 +123,7 @@ def test_read_climo_field(metplus_config, config_overrides, expected_value): 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')), ] ) diff --git a/internal/tests/pytests/util/time_looping/test_time_looping.py b/internal/tests/pytests/util/time_looping/test_time_looping.py index 90159f142b..d4d8e6d178 100644 --- a/internal/tests/pytests/util/time_looping/test_time_looping.py +++ b/internal/tests/pytests/util/time_looping/test_time_looping.py @@ -5,6 +5,20 @@ from metplus.util import time_looping as tl from metplus.util.time_util import ti_calculate, ti_get_hours_from_relativedelta +from metplus.util.time_looping import get_start_and_end_times + + +@pytest.fixture +def mock_time_generator(monkeypatch): + """ + Mock the time_generator function used in get_start_and_end_times + to simulate various scenarios. + """ + + def mock_generator(config): + return config.get("mock_times", []) + + monkeypatch.setattr('metplus.util.time_looping.time_generator', mock_generator) @pytest.mark.parametrize( @@ -236,7 +250,8 @@ def test_get_start_and_end_times(metplus_config): config.set('config', f'{prefix}_TIME_FMT', time_format) config.set('config', f'{prefix}_BEG', start_time) config.set('config', f'{prefix}_END', end_time) - start_dt, end_dt = tl.get_start_and_end_times(config) + config.set('config', f'{prefix}_INCREMENT', '1Y') + start_dt, end_dt = get_start_and_end_times(config) assert start_dt.strftime(time_format) == start_time assert end_dt.strftime(time_format) == end_time @@ -250,7 +265,7 @@ def test_get_start_and_end_times_now(metplus_config): config.set('config', f'{prefix}_TIME_FMT', time_format) config.set('config', f'{prefix}_BEG', '{now?fmt=%Y%m%d%H%M%S?shift=-1d}') config.set('config', f'{prefix}_END', '{now?fmt=%Y%m%d%H%M%S}') - start_dt, end_dt = tl.get_start_and_end_times(config) + start_dt, end_dt = get_start_and_end_times(config) expected_end_time = config.getstr('config', 'CLOCK_TIME') yesterday_dt = datetime.strptime(expected_end_time, time_format) - relativedelta(days=1) expected_start_time = yesterday_dt.strftime(time_format) @@ -268,7 +283,7 @@ def test_get_start_and_end_times_today(metplus_config): config.set('config', f'{prefix}_TIME_FMT', time_format) config.set('config', f'{prefix}_BEG', '{today}') config.set('config', f'{prefix}_END', '{today}') - start_dt, end_dt = tl.get_start_and_end_times(config) + start_dt, end_dt = get_start_and_end_times(config) clock_time = config.getstr('config', 'CLOCK_TIME') clock_dt = datetime.strptime(clock_time, '%Y%m%d%H%M%S') expected_time = clock_dt.strftime(time_format) @@ -568,3 +583,52 @@ def test_get_lead_sequence_init_min_10(metplus_config): test_seq = tl.get_lead_sequence(conf, input_dict) lead_seq = [12, 24] assert test_seq == [relativedelta(hours=lead) for lead in lead_seq] + +def test_get_start_and_end_times_empty_times(mock_time_generator): + """Test empty times list returns (None, None)""" + config = {"mock_times": []} + start, end = get_start_and_end_times(config) + assert start is None + assert end is None + + +def test_get_start_and_end_times_single_entry(mock_time_generator): + """Test handling of a single entry in the times list""" + config = {"mock_times": [{"loop_by": "valid", "valid": "2023-10-15"}]} + start, end = get_start_and_end_times(config) + assert start == "2023-10-15" + assert end == "2023-10-15" + + +def test_get_start_and_end_times_missing_loop_by_key(mock_time_generator): + """Test when 'loop_by' key is missing in a dictionary""" + config = { + "mock_times": [ + {"valid": "2023-10-15"}, + {"loop_by": "valid", "valid": "2023-10-16"} + ] + } + with pytest.raises(KeyError): + get_start_and_end_times(config) # Should raise an error for missing key + + +def test_get_start_and_end_times_invalid_config(mock_time_generator): + """Test when time_generator produces invalid or corrupted time data""" + config = {"mock_times": [None]} # Simulate corrupted output + start, end = get_start_and_end_times(config) + assert start is None + assert end is None + + +def test_get_start_and_end_times_multiple_entries(mock_time_generator): + """Test handling multiple entries in the times list""" + config = { + "mock_times": [ + {"loop_by": "valid", "valid": "2023-10-15"}, + {"loop_by": "valid", "valid": "2023-10-16"}, + {"loop_by": "valid", "valid": "2023-10-17"} + ] + } + start, end = get_start_and_end_times(config) + assert start == "2023-10-15" + assert end == "2023-10-17" diff --git a/internal/tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py b/internal/tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py index 963966513b..14f29e5a59 100644 --- a/internal/tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py +++ b/internal/tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py @@ -39,12 +39,12 @@ def set_minimum_config_settings(config, set_fields=True, set_obs=True): config.set('config', 'INIT_INCREMENT', '12H') config.set('config', 'LEAD_SEQ', '12H') config.set('config', 'LOOP_ORDER', 'times') - config.set('config', 'ENSEMBLE_STAT_N_MEMBERS', 1) + config.set('config', 'ENSEMBLE_STAT_N_MEMBERS', 2) config.set('config', 'ENSEMBLE_STAT_CONFIG_FILE', '{PARM_BASE}/met_config/EnsembleStatConfig_wrapped') config.set('config', 'FCST_ENSEMBLE_STAT_INPUT_DIR', fcst_dir) config.set('config', 'FCST_ENSEMBLE_STAT_INPUT_TEMPLATE', - '{init?fmt=%Y%m%d%H}/fcst_file_F{lead?fmt=%3H}') + '{init?fmt=%Y%m%d%H}/fcst_file_F{lead?fmt=%3H},{init?fmt=%Y%m%d%H}/fcst_file_F{lead?fmt=%3H}') if set_obs: config.set('config', 'OBS_ENSEMBLE_STAT_GRID_INPUT_DIR', obs_dir) config.set('config', 'OBS_ENSEMBLE_STAT_GRID_INPUT_TEMPLATE', @@ -67,16 +67,16 @@ def set_minimum_config_settings(config, set_fields=True, set_obs=True): (False, None, 3, 8, 0.7, 3), (True, 'obs_grid', 4, 8, 0.4, 0), (True, 'obs_grid', 4, 8, 0.7, 1), - (False, 'obs_grid', 4, 8, 0.7, 4), + (False, 'obs_grid', 4, 8, 0.7, 7), (True, 'point_grid', 4, 8, 0.4, 0), (True, 'point_grid', 4, 8, 0.7, 1), - (False, 'point_grid', 4, 8, 0.7, 4), + (False, 'point_grid', 4, 8, 0.7, 7), (True, 'ens_mean', 4, 8, 0.4, 0), (True, 'ens_mean', 4, 8, 0.7, 1), - (False, 'ens_mean', 4, 8, 0.7, 4), + (False, 'ens_mean', 4, 8, 0.7, 7), (True, 'ctrl', 4, 8, 0.4, 0), (True, 'ctrl', 4, 8, 0.7, 1), - (False, 'ctrl', 4, 8, 0.7, 4), + (False, 'ctrl', 4, 8, 0.7, 7), # still errors if more members than n_members found (True, 'low_n_member', 8, 8, 0.7, 6), (False, 'low_n_member', 8, 8, 0.7, 8), @@ -135,11 +135,13 @@ def test_ensemble_stat_missing_inputs(metplus_config, get_test_data_dir, allow_m 'FCST_VAR1_LEVELS': 'A06', 'OBS_VAR1_NAME': 'obs_file', 'OBS_VAR1_LEVELS': 'A06', - 'FCST_ENSEMBLE_STAT_INPUT_TEMPLATE': '{fcst_name}_A{level?fmt=%3H}', + 'FCST_ENSEMBLE_STAT_INPUT_TEMPLATE': '{fcst_name}_A{level?fmt=%3H},{fcst_name}_A{level?fmt=%3H}', }, f'{fcst_dir}/fcst_file_A006'), # 1 - don't set forecast level - ({'FCST_ENSEMBLE_STAT_INPUT_TEMPLATE': 'fcst_file_A{level?fmt=%3H}'}, + ({'FCST_VAR1_NAME': 'fcst_file', + 'OBS_VAR1_NAME': 'obs_file', + 'FCST_ENSEMBLE_STAT_INPUT_TEMPLATE': 'fcst_file_A{level?fmt=%3H},fcst_file_A{level?fmt=%3H}'}, f'{fcst_dir}/fcst_file_A000'), ] ) @@ -159,16 +161,18 @@ def test_ensemble_stat_level_in_template(metplus_config, config_overrides, assert wrapper.isOK file_list_dir = wrapper.config.getdir('FILE_LISTS_DIR') - file_list_file = f"{file_list_dir}/20050807000000_12_ensemble_stat.txt" + file_list_file = f"{file_list_dir}/ensemble_stat_files_FCST_init_20050807000000_valid_20050807120000_lead_43200.txt" if os.path.exists(file_list_file): os.remove(file_list_file) wrapper.run_all_times() + assert os.path.exists(file_list_file) with open(file_list_file, 'r') as file_handle: filenames = file_handle.read().splitlines()[1:] - assert len(filenames) == 1 + assert len(filenames) == 2 assert filenames[0] == expected_filename + assert filenames[1] == expected_filename @pytest.mark.parametrize( @@ -505,9 +509,6 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, { 'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - ({'ENSEMBLE_STAT_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), - ({'ENSEMBLE_STAT_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), @@ -522,7 +523,6 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, 'ENSEMBLE_STAT_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'ENSEMBLE_STAT_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'ENSEMBLE_STAT_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'ENSEMBLE_STAT_CLIMO_MEAN_MATCH_MONTH': 'True', 'ENSEMBLE_STAT_CLIMO_MEAN_DAY_INTERVAL': '30', 'ENSEMBLE_STAT_CLIMO_MEAN_HOUR_INTERVAL': '12', }, @@ -532,7 +532,7 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # climo stdev @@ -562,9 +562,6 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, { 'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - ({'ENSEMBLE_STAT_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), - ({'ENSEMBLE_STAT_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), @@ -579,7 +576,6 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, 'ENSEMBLE_STAT_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'ENSEMBLE_STAT_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'ENSEMBLE_STAT_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'ENSEMBLE_STAT_CLIMO_STDEV_MATCH_MONTH': 'True', 'ENSEMBLE_STAT_CLIMO_STDEV_DAY_INTERVAL': '30', 'ENSEMBLE_STAT_CLIMO_STDEV_HOUR_INTERVAL': '12', }, @@ -589,7 +585,7 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), ({'ENSEMBLE_STAT_OBS_QUALITY_INC': '2,3,4', }, {'METPLUS_OBS_QUALITY_INC': 'obs_quality_inc = ["2", "3", "4"];'}), @@ -657,8 +653,6 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {shape = SQUARE;}}'}), ({'ENSEMBLE_STAT_FCST_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - ({'ENSEMBLE_STAT_FCST_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), ({'ENSEMBLE_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), ({'ENSEMBLE_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL': 'NA', }, @@ -674,7 +668,6 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, 'ENSEMBLE_STAT_FCST_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'ENSEMBLE_STAT_FCST_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'ENSEMBLE_STAT_FCST_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'ENSEMBLE_STAT_FCST_CLIMO_MEAN_MATCH_MONTH': 'True', 'ENSEMBLE_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL': '30', 'ENSEMBLE_STAT_FCST_CLIMO_MEAN_HOUR_INTERVAL': '12', }, {'METPLUS_FCST_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' @@ -683,7 +676,7 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # fcst climo_stdev ({'ENSEMBLE_STAT_FCST_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', }, @@ -705,8 +698,6 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {shape = SQUARE;}}'}), ({'ENSEMBLE_STAT_FCST_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - ({'ENSEMBLE_STAT_FCST_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), ({'ENSEMBLE_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), ({'ENSEMBLE_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL': 'NA', }, @@ -722,7 +713,6 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, 'ENSEMBLE_STAT_FCST_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'ENSEMBLE_STAT_FCST_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'ENSEMBLE_STAT_FCST_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'ENSEMBLE_STAT_FCST_CLIMO_STDEV_MATCH_MONTH': 'True', 'ENSEMBLE_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL': '30', 'ENSEMBLE_STAT_FCST_CLIMO_STDEV_HOUR_INTERVAL': '12', }, {'METPLUS_FCST_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' @@ -731,7 +721,7 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # obs climo_mean ({'ENSEMBLE_STAT_OBS_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', }, @@ -753,8 +743,6 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {shape = SQUARE;}}'}), ({'ENSEMBLE_STAT_OBS_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - ({'ENSEMBLE_STAT_OBS_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), ({'ENSEMBLE_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), ({'ENSEMBLE_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL': 'NA', }, @@ -770,7 +758,6 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, 'ENSEMBLE_STAT_OBS_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'ENSEMBLE_STAT_OBS_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'ENSEMBLE_STAT_OBS_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'ENSEMBLE_STAT_OBS_CLIMO_MEAN_MATCH_MONTH': 'True', 'ENSEMBLE_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL': '30', 'ENSEMBLE_STAT_OBS_CLIMO_MEAN_HOUR_INTERVAL': '12', }, {'METPLUS_OBS_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' @@ -779,7 +766,7 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # obs climo_stdev ({'ENSEMBLE_STAT_OBS_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', }, @@ -801,8 +788,6 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {shape = SQUARE;}}'}), ({'ENSEMBLE_STAT_OBS_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - ({'ENSEMBLE_STAT_OBS_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), ({'ENSEMBLE_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), ({'ENSEMBLE_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL': 'NA', }, @@ -818,7 +803,6 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, 'ENSEMBLE_STAT_OBS_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'ENSEMBLE_STAT_OBS_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'ENSEMBLE_STAT_OBS_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'ENSEMBLE_STAT_OBS_CLIMO_STDEV_MATCH_MONTH': 'True', 'ENSEMBLE_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL': '30', 'ENSEMBLE_STAT_OBS_CLIMO_STDEV_HOUR_INTERVAL': '12', }, {'METPLUS_OBS_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' @@ -827,7 +811,7 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), ({'ENSEMBLE_STAT_POINT_WEIGHT_FLAG': 'SID', }, {'METPLUS_POINT_WEIGHT_FLAG': 'point_weight_flag = SID;'}), @@ -860,20 +844,20 @@ def test_ensemble_stat_single_field(metplus_config, config_overrides, point_obs = ' ' ens_mean = ' ' if 'OBS_ENSEMBLE_STAT_POINT_INPUT_TEMPLATE' in config_overrides: - point_obs = f' -point_obs "{obs_dir}/{obs_point_template}" ' + point_obs = f' -point_obs {obs_dir}/{obs_point_template} ' if 'ENSEMBLE_STAT_ENS_MEAN_INPUT_TEMPLATE' in config_overrides: ens_mean = f' -ens_mean {ens_mean_dir}/{ens_mean_template} ' expected_cmds = [(f"{app_path} {verbosity} " - f"{file_list_dir}/20050807000000_12_ensemble_stat.txt " - f"{config_file}{point_obs}" - f'-grid_obs "{obs_dir}/2005080712/obs_file"{ens_mean}' - f"-outdir {out_dir}/2005080712"), + f"{file_list_dir}/ensemble_stat_files_FCST_init_20050807000000_valid_20050807120000_lead_43200.txt" + f"{point_obs}" + f'-grid_obs {obs_dir}/2005080712/obs_file{ens_mean}' + f"{config_file} -outdir {out_dir}/2005080712"), (f"{app_path} {verbosity} " - f"{file_list_dir}/20050807120000_12_ensemble_stat.txt " - f"{config_file}{point_obs}" - f'-grid_obs "{obs_dir}/2005080800/obs_file"{ens_mean}' - f"-outdir {out_dir}/2005080800"), + f"{file_list_dir}/ensemble_stat_files_FCST_init_20050807120000_valid_20050808000000_lead_43200.txt" + f"{point_obs}" + f'-grid_obs {obs_dir}/2005080800/obs_file{ens_mean}' + f"{config_file} -outdir {out_dir}/2005080800"), ] all_cmds = wrapper.run_all_times() @@ -905,7 +889,7 @@ def test_get_config_file(metplus_config): @pytest.mark.parametrize( 'config_overrides, expected_num_files', [ ({}, 4), - ({'ENSEMBLE_STAT_ENS_MEMBER_IDS': '1'}, 1), + ({'ENSEMBLE_STAT_ENS_MEMBER_IDS': '1'}, 2), ] ) @pytest.mark.wrapper_c @@ -926,13 +910,12 @@ def test_ensemble_stat_fill_missing(metplus_config, config_overrides, wrapper = EnsembleStatWrapper(config) file_list_file = os.path.join(wrapper.config.getdir('FILE_LISTS_DIR'), - '20050807000000_12_ensemble_stat.txt') + 'ensemble_stat_files_FCST_init_20050807000000_valid_20050807120000_lead_43200.txt') if os.path.exists(file_list_file): os.remove(file_list_file) all_cmds = wrapper.run_all_times() assert len(all_cmds) == 1 - with open(file_list_file, 'r') as file_handle: actual_num_files = len(file_handle.read().splitlines()) - 1 diff --git a/internal/tests/pytests/wrappers/gen_ens_prod/test_gen_ens_prod_wrapper.py b/internal/tests/pytests/wrappers/gen_ens_prod/test_gen_ens_prod_wrapper.py index 8c7cf09863..42bc91f1bd 100644 --- a/internal/tests/pytests/wrappers/gen_ens_prod/test_gen_ens_prod_wrapper.py +++ b/internal/tests/pytests/wrappers/gen_ens_prod/test_gen_ens_prod_wrapper.py @@ -274,9 +274,6 @@ def test_gen_ens_prod_missing_inputs(metplus_config, get_test_data_dir, allow_mi ({'GEN_ENS_PROD_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', }, { 'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - # 36 - ({'GEN_ENS_PROD_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), # 37 ({'GEN_ENS_PROD_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), @@ -292,7 +289,6 @@ def test_gen_ens_prod_missing_inputs(metplus_config, get_test_data_dir, allow_mi 'GEN_ENS_PROD_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'GEN_ENS_PROD_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'GEN_ENS_PROD_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'GEN_ENS_PROD_CLIMO_MEAN_MATCH_MONTH': 'True', 'GEN_ENS_PROD_CLIMO_MEAN_DAY_INTERVAL': '30', 'GEN_ENS_PROD_CLIMO_MEAN_HOUR_INTERVAL': '12', }, @@ -302,7 +298,7 @@ def test_gen_ens_prod_missing_inputs(metplus_config, get_test_data_dir, allow_mi 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # 40 climo stdev ({'GEN_ENS_PROD_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', }, @@ -330,9 +326,6 @@ def test_gen_ens_prod_missing_inputs(metplus_config, get_test_data_dir, allow_mi ({'GEN_ENS_PROD_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', }, { 'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - # 47 - ({'GEN_ENS_PROD_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), # 48 ({'GEN_ENS_PROD_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), @@ -348,7 +341,6 @@ def test_gen_ens_prod_missing_inputs(metplus_config, get_test_data_dir, allow_mi 'GEN_ENS_PROD_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'GEN_ENS_PROD_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'GEN_ENS_PROD_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'GEN_ENS_PROD_CLIMO_STDEV_MATCH_MONTH': 'True', 'GEN_ENS_PROD_CLIMO_STDEV_DAY_INTERVAL': '30', 'GEN_ENS_PROD_CLIMO_STDEV_HOUR_INTERVAL': '12', }, @@ -358,7 +350,7 @@ def test_gen_ens_prod_missing_inputs(metplus_config, get_test_data_dir, allow_mi 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # 51 ({'GEN_ENS_PROD_NBRHD_PROB_WIDTH': '5', }, @@ -461,8 +453,6 @@ def test_gen_ens_prod_missing_inputs(metplus_config, get_test_data_dir, allow_mi {'METPLUS_ENS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {shape = SQUARE;}}'}), ({'GEN_ENS_PROD_ENS_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_ENS_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - ({'GEN_ENS_PROD_ENS_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_ENS_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), ({'GEN_ENS_PROD_ENS_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_ENS_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), ({'GEN_ENS_PROD_ENS_CLIMO_MEAN_DAY_INTERVAL': 'NA', }, @@ -478,7 +468,6 @@ def test_gen_ens_prod_missing_inputs(metplus_config, get_test_data_dir, allow_mi 'GEN_ENS_PROD_ENS_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'GEN_ENS_PROD_ENS_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'GEN_ENS_PROD_ENS_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'GEN_ENS_PROD_ENS_CLIMO_MEAN_MATCH_MONTH': 'True', 'GEN_ENS_PROD_ENS_CLIMO_MEAN_DAY_INTERVAL': '30', 'GEN_ENS_PROD_ENS_CLIMO_MEAN_HOUR_INTERVAL': '12', }, {'METPLUS_ENS_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' @@ -487,7 +476,7 @@ def test_gen_ens_prod_missing_inputs(metplus_config, get_test_data_dir, allow_mi 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # ens climo_stdev (quietly supported) ({'GEN_ENS_PROD_ENS_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', }, @@ -504,8 +493,6 @@ def test_gen_ens_prod_missing_inputs(metplus_config, get_test_data_dir, allow_mi {'METPLUS_ENS_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {shape = SQUARE;}}'}), ({'GEN_ENS_PROD_ENS_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_ENS_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - ({'GEN_ENS_PROD_ENS_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_ENS_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), ({'GEN_ENS_PROD_ENS_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_ENS_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), ({'GEN_ENS_PROD_ENS_CLIMO_STDEV_DAY_INTERVAL': 'NA', }, @@ -521,7 +508,6 @@ def test_gen_ens_prod_missing_inputs(metplus_config, get_test_data_dir, allow_mi 'GEN_ENS_PROD_ENS_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'GEN_ENS_PROD_ENS_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'GEN_ENS_PROD_ENS_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'GEN_ENS_PROD_ENS_CLIMO_STDEV_MATCH_MONTH': 'True', 'GEN_ENS_PROD_ENS_CLIMO_STDEV_DAY_INTERVAL': '30', 'GEN_ENS_PROD_ENS_CLIMO_STDEV_HOUR_INTERVAL': '12', }, {'METPLUS_ENS_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' @@ -530,7 +516,7 @@ def test_gen_ens_prod_missing_inputs(metplus_config, get_test_data_dir, allow_mi 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), ({'GEN_ENS_PROD_CLIMO_MEAN_VAR1_NAME': 'UGRD', 'GEN_ENS_PROD_CLIMO_MEAN_VAR1_LEVELS': 'P850,P500,P250', }, diff --git a/internal/tests/pytests/wrappers/grid_diag/test_grid_diag.py b/internal/tests/pytests/wrappers/grid_diag/test_grid_diag.py index d190d37f06..79dd849499 100644 --- a/internal/tests/pytests/wrappers/grid_diag/test_grid_diag.py +++ b/internal/tests/pytests/wrappers/grid_diag/test_grid_diag.py @@ -3,6 +3,7 @@ import pytest import os +import re from datetime import datetime from dateutil.relativedelta import relativedelta @@ -102,71 +103,60 @@ def test_grid_diag_missing_inputs(metplus_config, get_test_data_dir, @pytest.mark.parametrize( - 'time_info, expected_subset', [ - # all files - ({'init': '*', 'valid': '*', 'lead': '*'}, - ['init_20141031213015_valid_20141031213015_lead_000.nc', + 'runtime_freq,init_or_valid,expected_subset', [ + # run once + ('RUN_ONCE', 'INIT', + [['init_20141031213015_valid_20141031213015_lead_000.nc', 'init_20141031213015_valid_20141101213015_lead_024.nc', 'init_20141101093015_valid_20141101093015_lead_000.nc', 'init_20141101093015_valid_20141102093015_lead_024.nc', + ]]), + # once per init + ('RUN_ONCE_PER_INIT_OR_VALID', 'INIT', + [['init_20141031213015_valid_20141031213015_lead_000.nc', + 'init_20141031213015_valid_20141101213015_lead_024.nc', ], + ['init_20141101093015_valid_20141101093015_lead_000.nc', + 'init_20141101093015_valid_20141102093015_lead_024.nc', + ]]), + + # once per valid + ('RUN_ONCE_PER_INIT_OR_VALID', 'VALID', + [['init_20141031213015_valid_20141031213015_lead_000.nc', + 'init_20141030213015_valid_20141031213015_lead_024.nc'], + ['init_20141101093015_valid_20141101093015_lead_000.nc', + 'init_20141031093015_valid_20141101093015_lead_024.nc'], ]), - # specific init - ({'init': datetime(2014, 10, 31, 21, 30, 15), 'valid': '*', 'lead': '*'}, - ['init_20141031213015_valid_20141031213015_lead_000.nc', - 'init_20141031213015_valid_20141101213015_lead_024.nc', - ]), - # specific valid - ({'init': '*', 'valid': datetime(2014, 11, 1, 9, 30, 15), 'lead': '*'}, - ['init_20141101093015_valid_20141101093015_lead_000.nc', - ]), - # specific lead integer zero - ({'init': '*', 'valid': '*', 'lead': 0}, - ['init_20141031213015_valid_20141031213015_lead_000.nc', - 'init_20141101093015_valid_20141101093015_lead_000.nc', - ]), - # specific lead relativedelta non-zero - ({'init': '*', 'valid': '*', 'lead': relativedelta(hours=24)}, + # once per lead + ('RUN_ONCE_PER_LEAD', 'INIT', + [['init_20141031213015_valid_20141031213015_lead_000.nc', + 'init_20141101093015_valid_20141101093015_lead_000.nc'], ['init_20141031213015_valid_20141101213015_lead_024.nc', - 'init_20141101093015_valid_20141102093015_lead_024.nc', - ]), - # specific lead integer non-zero - ({'init': '*', 'valid': '*', 'lead': 86400}, - ['init_20141031213015_valid_20141101213015_lead_024.nc', - 'init_20141101093015_valid_20141102093015_lead_024.nc', - ]), - # specific init/valid/lead integer zero - ({'init': datetime(2014, 10, 31, 21, 30, 15), - 'valid': datetime(2014, 10, 31, 21, 30, 15), - 'lead': 0}, - ['init_20141031213015_valid_20141031213015_lead_000.nc', - ]), - # specific init/valid/lead relativedelta non-zero - ({'init': datetime(2014, 10, 31, 21, 30, 15), - 'valid': datetime(2014, 11, 1, 21, 30, 15), - 'lead': relativedelta(hours=24)}, - ['init_20141031213015_valid_20141101213015_lead_024.nc', - ]), - # specific init/valid/lead integer non-zero - ({'init': datetime(2014, 10, 31, 21, 30, 15), - 'valid': datetime(2014, 11, 1, 21, 30, 15), - 'lead': 86400}, - ['init_20141031213015_valid_20141101213015_lead_024.nc', - ]), + 'init_20141101093015_valid_20141102093015_lead_024.nc', + ]]), + # once for each + ('RUN_ONCE_FOR_EACH', 'INIT', + [['init_20141031213015_valid_20141031213015_lead_000.nc'], + ['init_20141031213015_valid_20141101213015_lead_024.nc'], + ['init_20141101093015_valid_20141101093015_lead_000.nc'], + ['init_20141101093015_valid_20141102093015_lead_024.nc'], + ]), ] ) @pytest.mark.wrapper -def test_get_all_files_and_subset(metplus_config, time_info, expected_subset): +def test_grid_diag_runtime_freq(metplus_config, runtime_freq, init_or_valid, expected_subset): """! Test to ensure that get_all_files only gets the files that are relevant to the runtime settings and not every file in the directory """ config = metplus_config - config.set('config', 'LOOP_BY', 'INIT') - config.set('config', 'GRID_DIAG_RUNTIME_FREQ', 'RUN_ONCE') - config.set('config', 'INIT_TIME_FMT', '%Y%m%d%H%M%S') - config.set('config', 'INIT_BEG', '20141031213015') - config.set('config', 'INIT_END', '20141101093015') - config.set('config', 'INIT_INCREMENT', '12H') + config.set('config', 'LOOP_BY', init_or_valid) + config.set('config', 'GRID_DIAG_RUNTIME_FREQ', runtime_freq) + config.set('config', f'{init_or_valid}_TIME_FMT', '%Y%m%d%H%M%S') + config.set('config', f'{init_or_valid}_INCREMENT', '12H') + config.set('config', f'{init_or_valid}_BEG', '20141031213015') + config.set('config', f'{init_or_valid}_END', '20141101093015') config.set('config', 'LEAD_SEQ', '0H, 24H') + config.set('config', 'FCST_VAR1_NAME', 'FCST') + config.set('config', 'FCST_VAR1_LEVELS', 'L0') input_dir = os.path.join(config.getdir('METPLUS_BASE'), 'internal', 'tests', @@ -177,38 +167,29 @@ def test_get_all_files_and_subset(metplus_config, time_info, expected_subset): ('init_{init?fmt=%Y%m%d%H%M%S}_valid_{valid?fmt=%Y%m%d%H%M%S}_' 'lead_{lead?fmt=%3H}.nc') ) - - expected_files = [] - for init, valid, lead in [('20141031213015', '20141031213015', '000'), - ('20141031213015', '20141101213015', '024'), - ('20141101093015', '20141101093015', '000'), - ('20141101093015', '20141102093015', '024')]: - filename = f'init_{init}_valid_{valid}_lead_{lead}.nc' - expected_files.append(os.path.join(input_dir, filename)) + config.set('config', 'GRID_DIAG_OUTPUT_DIR', config.getdir('OUTPUT_BASE')) wrapper = GridDiagWrapper(config) - wrapper.c_dict['ALL_FILES'] = wrapper.get_all_files() - - # convert list of lists into a single list to compare to expected results - - actual_files = [item['input0'] for item in wrapper.c_dict['ALL_FILES']] - actual_files = [item for sub in actual_files for item in sub] - assert actual_files == expected_files - - file_list_dict = wrapper.subset_input_files(time_info) - assert file_list_dict - if len(expected_subset) == 1: - file_list = [file_list_dict['input0']] - else: - with open(file_list_dict['input0'], 'r') as file_handle: - file_list = file_handle.readlines() - - file_list = file_list[1:] - assert len(file_list) == len(expected_subset) - - for actual_file, expected_file in zip(file_list, expected_subset): - actual_file = actual_file.strip() - assert os.path.basename(actual_file) == expected_file + wrapper.run_all_times() + assert len(wrapper.all_commands) == len(expected_subset) + file_list_files = [] + print(wrapper.all_commands) + pattern = r'-data\s+([^\s]+)' + for cmd, _ in wrapper.all_commands: + match = re.search(pattern, cmd) + if match: + file_list_files.append(match.group(1)) + + assert len(file_list_files) == len(expected_subset) + for actual_file, expected_files in zip(file_list_files, expected_subset): + expected_files_full = [os.path.join(input_dir, item) for item in expected_files] + if len(expected_files) == 1: + assert actual_file == expected_files_full[0] + else: + with open(actual_file, 'r') as file_handle: + file_list = file_handle.read().splitlines()[1:] + print(f'ACTUAL: {file_list}') + assert sorted(file_list) == sorted(expected_files_full) @pytest.mark.parametrize( @@ -367,16 +348,12 @@ def test_grid_diag(metplus_config, config_overrides, env_var_values, out_dir = wrapper.c_dict.get('OUTPUT_DIR') expected_cmds = [ - (f"{app_path} -data {file_list_dir}/grid_diag_files_input0" - "_init_20160929000000_valid_ALL_lead_ALL.txt " - f"-data {file_list_dir}/grid_diag_files_input1" - "_init_20160929000000_valid_ALL_lead_ALL.txt " - f"-config {config_file} -out {out_dir}/grid_diag.all.nc {verbosity}"), - (f"{app_path} -data {file_list_dir}/grid_diag_files_input0" - "_init_20160929060000_valid_ALL_lead_ALL.txt " - f"-data {file_list_dir}/grid_diag_files_input1" - "_init_20160929060000_valid_ALL_lead_ALL.txt " - f"-config {config_file} -out {out_dir}/grid_diag.all.nc {verbosity}"), + (f"{app_path} -data {file_list_dir}/grid_diag_files_input0_init_20160929000000_valid_ALL_lead_ALL.txt" + f" -data {file_list_dir}/grid_diag_files_input1_init_20160929000000_valid_ALL_lead_ALL.txt" + f" -config {config_file} -out {out_dir}/grid_diag.all.nc {verbosity}"), + (f"{app_path} -data {file_list_dir}/grid_diag_files_input0_init_20160929060000_valid_ALL_lead_ALL.txt" + f" -data {file_list_dir}/grid_diag_files_input1_init_20160929060000_valid_ALL_lead_ALL.txt" + f" -config {config_file} -out {out_dir}/grid_diag.all.nc {verbosity}"), ] all_cmds = wrapper.run_all_times() diff --git a/internal/tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py b/internal/tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py index f760e65200..e1b3793353 100644 --- a/internal/tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py +++ b/internal/tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py @@ -3,9 +3,9 @@ import pytest import os -from datetime import datetime from metplus.wrappers.grid_stat_wrapper import GridStatWrapper +from internal.tests.pytests.conftest import stat_runtime_freq_test_params fcst_dir = '/some/path/fcst' obs_dir = '/some/path/obs' @@ -59,14 +59,7 @@ def set_minimum_config_settings(config): @pytest.mark.parametrize( - 'once_per_field, missing, run, thresh, errors, allow_missing', [ - (False, 6, 12, 0.5, 0, True), - (False, 6, 12, 0.6, 1, True), - (True, 12, 24, 0.5, 0, True), - (True, 12, 24, 0.6, 1, True), - (False, 6, 12, 0.5, 6, False), - (True, 12, 24, 0.5, 12, False), - ] + 'once_per_field, missing, run, thresh, errors, allow_missing', stat_runtime_freq_test_params ) @pytest.mark.wrapper_b def test_grid_stat_missing_inputs(metplus_config, get_test_data_dir, @@ -537,9 +530,6 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): { 'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - ({'GRID_STAT_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), - ({'GRID_STAT_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), @@ -560,7 +550,6 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): 'GRID_STAT_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'GRID_STAT_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'GRID_STAT_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'GRID_STAT_CLIMO_MEAN_MATCH_MONTH': 'True', 'GRID_STAT_CLIMO_MEAN_DAY_INTERVAL': '30', 'GRID_STAT_CLIMO_MEAN_HOUR_INTERVAL': '12', }, @@ -570,7 +559,7 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # climo stdev @@ -600,9 +589,6 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): { 'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - ({'GRID_STAT_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), - ({'GRID_STAT_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), @@ -617,7 +603,6 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): 'GRID_STAT_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'GRID_STAT_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'GRID_STAT_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'GRID_STAT_CLIMO_STDEV_MATCH_MONTH': 'True', 'GRID_STAT_CLIMO_STDEV_DAY_INTERVAL': '30', 'GRID_STAT_CLIMO_STDEV_HOUR_INTERVAL': '12', }, @@ -627,7 +612,7 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # ignore USE_FCST because FIELD is set ( @@ -744,8 +729,6 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {shape = SQUARE;}}'}), ({'GRID_STAT_FCST_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - ({'GRID_STAT_FCST_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), ({'GRID_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), ({'GRID_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL': 'NA', }, @@ -761,7 +744,6 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): 'GRID_STAT_FCST_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'GRID_STAT_FCST_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'GRID_STAT_FCST_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'GRID_STAT_FCST_CLIMO_MEAN_MATCH_MONTH': 'True', 'GRID_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL': '30', 'GRID_STAT_FCST_CLIMO_MEAN_HOUR_INTERVAL': '12', }, {'METPLUS_FCST_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' @@ -770,7 +752,7 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # fcst climo_stdev ({'GRID_STAT_FCST_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', }, @@ -792,8 +774,6 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {shape = SQUARE;}}'}), ({'GRID_STAT_FCST_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - ({'GRID_STAT_FCST_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), ({'GRID_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), ({'GRID_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL': 'NA', }, @@ -809,7 +789,6 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): 'GRID_STAT_FCST_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'GRID_STAT_FCST_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'GRID_STAT_FCST_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'GRID_STAT_FCST_CLIMO_STDEV_MATCH_MONTH': 'True', 'GRID_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL': '30', 'GRID_STAT_FCST_CLIMO_STDEV_HOUR_INTERVAL': '12', }, {'METPLUS_FCST_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' @@ -818,7 +797,7 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # obs climo_mean ({'GRID_STAT_OBS_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', }, @@ -840,8 +819,6 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {shape = SQUARE;}}'}), ({'GRID_STAT_OBS_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - ({'GRID_STAT_OBS_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), ({'GRID_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), ({'GRID_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL': 'NA', }, @@ -857,7 +834,6 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): 'GRID_STAT_OBS_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'GRID_STAT_OBS_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'GRID_STAT_OBS_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'GRID_STAT_OBS_CLIMO_MEAN_MATCH_MONTH': 'True', 'GRID_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL': '30', 'GRID_STAT_OBS_CLIMO_MEAN_HOUR_INTERVAL': '12', }, {'METPLUS_OBS_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' @@ -866,7 +842,7 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # obs climo_stdev ({'GRID_STAT_OBS_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', }, @@ -888,8 +864,6 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {shape = SQUARE;}}'}), ({'GRID_STAT_OBS_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - ({'GRID_STAT_OBS_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), ({'GRID_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), ({'GRID_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL': 'NA', }, @@ -905,7 +879,6 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): 'GRID_STAT_OBS_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'GRID_STAT_OBS_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'GRID_STAT_OBS_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'GRID_STAT_OBS_CLIMO_STDEV_MATCH_MONTH': 'True', 'GRID_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL': '30', 'GRID_STAT_OBS_CLIMO_STDEV_HOUR_INTERVAL': '12', }, {'METPLUS_OBS_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' @@ -914,7 +887,7 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), ({'GRID_STAT_GRADIENT_DX': '2', }, {'METPLUS_GRADIENT_DICT': 'gradient = {dx = [2];}'}), diff --git a/internal/tests/pytests/wrappers/ioda2nc/test_ioda2nc_wrapper.py b/internal/tests/pytests/wrappers/ioda2nc/test_ioda2nc_wrapper.py index c6e89fbd25..c3ca55eec2 100644 --- a/internal/tests/pytests/wrappers/ioda2nc/test_ioda2nc_wrapper.py +++ b/internal/tests/pytests/wrappers/ioda2nc/test_ioda2nc_wrapper.py @@ -3,6 +3,7 @@ import os from metplus.wrappers.ioda2nc_wrapper import IODA2NCWrapper +from internal.tests.pytests.conftest import obs_to_nc_runtime_freq_test_params time_fmt = '%Y%m%d%H' run_times = ['2020031012', '2020031100'] @@ -31,19 +32,7 @@ def set_minimum_config_settings(config): @pytest.mark.parametrize( - 'missing, run, thresh, errors, allow_missing, runtime_freq', [ - (16, 24, 0.3, 0, True, 'RUN_ONCE_FOR_EACH'), - (16, 24, 0.7, 1, True, 'RUN_ONCE_FOR_EACH'), - (16, 24, 0.3, 16, False, 'RUN_ONCE_FOR_EACH'), - (2, 4, 0.4, 0, True, 'RUN_ONCE_PER_INIT_OR_VALID'), - (2, 4, 0.6, 1, True, 'RUN_ONCE_PER_INIT_OR_VALID'), - (2, 4, 0.6, 2, False, 'RUN_ONCE_PER_INIT_OR_VALID'), - (2, 5, 0.4, 0, True, 'RUN_ONCE_PER_LEAD'), - (2, 5, 0.7, 1, True, 'RUN_ONCE_PER_LEAD'), - (2, 5, 0.4, 2, False, 'RUN_ONCE_PER_LEAD'), - (0, 1, 0.4, 0, True, 'RUN_ONCE'), - (0, 1, 0.4, 0, False, 'RUN_ONCE'), - ] + 'missing, run, thresh, errors, allow_missing, runtime_freq', obs_to_nc_runtime_freq_test_params ) @pytest.mark.wrapper def test_ioda2nc_missing_inputs(metplus_config, get_test_data_dir, missing, diff --git a/internal/tests/pytests/wrappers/mode/test_mode_wrapper.py b/internal/tests/pytests/wrappers/mode/test_mode_wrapper.py index cf2806c2e4..a97bc995aa 100644 --- a/internal/tests/pytests/wrappers/mode/test_mode_wrapper.py +++ b/internal/tests/pytests/wrappers/mode/test_mode_wrapper.py @@ -62,7 +62,7 @@ def set_minimum_config_settings(config): 'missing, run, thresh, errors, allow_missing', [ (6, 12, 0.5, 0, True), (6, 12, 0.6, 1, True), - (6, 12, 0.5, 6, False), + (6, 12, 0.5, 10, False), ] ) @pytest.mark.wrapper_a diff --git a/internal/tests/pytests/wrappers/mtd/test_mtd_wrapper.py b/internal/tests/pytests/wrappers/mtd/test_mtd_wrapper.py index 6506afbff5..d5546946b7 100644 --- a/internal/tests/pytests/wrappers/mtd/test_mtd_wrapper.py +++ b/internal/tests/pytests/wrappers/mtd/test_mtd_wrapper.py @@ -80,13 +80,13 @@ def set_minimum_config_settings(config, set_inputs=True): (1, 3, 0.3, 0, True, 'CHOCOLATE'), (1, 3, 0.3, 0, True, 'BOTH'), (1, 3, 0.8, 1, True, 'BOTH'), - (1, 3, 0.8, 1, False, 'BOTH'), + (1, 3, 0.8, 22, False, 'BOTH'), (1, 3, 0.3, 0, True, 'FCST'), (1, 3, 0.8, 1, True, 'FCST'), - (1, 3, 0.8, 1, False, 'FCST'), + (1, 3, 0.8, 12, False, 'FCST'), (1, 3, 0.3, 0, True, 'OBS'), (1, 3, 0.8, 1, True, 'OBS'), - (1, 3, 0.8, 1, False, 'OBS'), + (1, 3, 0.8, 10, False, 'OBS'), ] ) @pytest.mark.wrapper_a diff --git a/internal/tests/pytests/wrappers/pair_stat/test_pair_stat_wrapper.py b/internal/tests/pytests/wrappers/pair_stat/test_pair_stat_wrapper.py new file mode 100755 index 0000000000..de3aac2a0c --- /dev/null +++ b/internal/tests/pytests/wrappers/pair_stat/test_pair_stat_wrapper.py @@ -0,0 +1,753 @@ +#!/usr/bin/env python3 + +import pytest + +import os + +from metplus.wrappers.pair_stat_wrapper import PairStatWrapper + +pairs_dir = '/some/path/pairs' +pair_stat_format = 'mpr' + +def set_minimum_config_settings(config, fcst_and_obs_data): + _, _, _, _, inits, _, lead_hour, time_fmt = fcst_and_obs_data + # set config variables to prevent command from running and bypass check + # if input files actually exist + config.set('config', 'DO_NOT_RUN_EXE', True) + config.set('config', 'INPUT_MUST_EXIST', False) + + # set process and time config variables + config.set('config', 'PROCESS_LIST', 'PairStat') + config.set('config', 'LOOP_BY', 'INIT') + config.set('config', 'INIT_TIME_FMT', time_fmt) + config.set('config', 'INIT_BEG', inits[0]) + config.set('config', 'INIT_END', inits[-1]) + config.set('config', 'INIT_INCREMENT', '12H') + config.set('config', 'LEAD_SEQ', f'{lead_hour}H') + + config.set('config', 'PAIR_STAT_FORMAT', pair_stat_format) + config.set('config', 'PAIR_STAT_CONFIG_FILE', + '{PARM_BASE}/met_config/PairStatConfig_wrapped') + config.set('config', 'PAIR_STAT_PAIRS_INPUT_DIR', pairs_dir) + config.set('config', 'PAIR_STAT_PAIRS_INPUT_TEMPLATE', + '{init?fmt=%Y%m%d%H}/fcst_file_F{lead?fmt=%3H}') + config.set('config', 'PAIR_STAT_OUTPUT_DIR', + '{OUTPUT_BASE}/PairStat/output') + config.set('config', 'PAIR_STAT_OUTPUT_TEMPLATE', '{valid?fmt=%Y%m%d%H}') + + +@pytest.mark.parametrize( + 'once_per_field, missing, run, thresh, errors, allow_missing', [ + (False, 6, 12, 0.5, 0, True), + (False, 6, 12, 0.6, 1, True), + (True, 12, 24, 0.5, 0, True), + (True, 12, 24, 0.6, 1, True), + (False, 6, 12, 0.5, 6, False), + (True, 12, 24, 0.5, 12, False), + ] +) +@pytest.mark.wrapper_a +def test_pair_stat_missing_inputs(metplus_config, get_test_data_dir, + once_per_field, missing, run, thresh, errors, + allow_missing, fcst_and_obs_data): + fcst_name, fcst_level, obs_name, obs_level, _, _, _, _ = fcst_and_obs_data + config = metplus_config + set_minimum_config_settings(config, fcst_and_obs_data) + config.set('config', 'INPUT_MUST_EXIST', True) + config.set('config', 'PAIR_STAT_ALLOW_MISSING_INPUTS', allow_missing) + config.set('config', 'PAIR_STAT_INPUT_THRESH', thresh) + config.set('config', 'INIT_BEG', '2017051001') + config.set('config', 'INIT_END', '2017051003') + config.set('config', 'INIT_INCREMENT', '2H') + config.set('config', 'LEAD_SEQ', '1,2,3,6,9,12') + config.set('config', 'PAIR_STAT_PAIRS_INPUT_DIR', get_test_data_dir('fcst')) + config.set('config', 'PAIR_STAT_PAIRS_INPUT_TEMPLATE', + '{init?fmt=%Y%m%d}/{init?fmt=%Y%m%d_i%H}_f{lead?fmt=%3H}_HRRRTLE_PHPT.grb2') + # add 2 sets of fields to test ONCE_PER_FIELD + config.set('config', 'FCST_VAR1_NAME', fcst_name) + config.set('config', 'FCST_VAR1_LEVELS', fcst_level) + config.set('config', 'OBS_VAR1_NAME', obs_name) + config.set('config', 'OBS_VAR1_LEVELS', obs_level) + config.set('config', 'FCST_VAR2_NAME', fcst_name) + config.set('config', 'FCST_VAR2_LEVELS', fcst_level) + config.set('config', 'OBS_VAR2_NAME', obs_name) + config.set('config', 'OBS_VAR2_LEVELS', obs_level) + config.set('config', 'PAIR_STAT_ONCE_PER_FIELD', once_per_field) + + wrapper = PairStatWrapper(config) + assert wrapper.isOK + + all_cmds = wrapper.run_all_times() + for cmd, _ in all_cmds: + print(cmd) + + print(f'missing: {wrapper.missing_input_count} / {wrapper.run_count}, errors: {wrapper.errors}') + assert wrapper.missing_input_count == missing + assert wrapper.run_count == run + assert wrapper.errors == errors + + +@pytest.mark.parametrize( + 'config_overrides, env_var_values', [ + ({'MODEL': 'my_model'}, + {'METPLUS_MODEL': 'model = "my_model";'}), + ({'PAIR_STAT_DESC': 'my_desc'}, + {'METPLUS_DESC': 'desc = "my_desc";'}), + ({'DESC': 'my_desc'}, + {'METPLUS_DESC': 'desc = "my_desc";'}), + + ({'PAIR_STAT_CENSOR_THRESH': '<1, >2', }, + {'METPLUS_CENSOR_THRESH': 'censor_thresh = [<1, >2];'}), + + ({'PAIR_STAT_CENSOR_VAL': '3,4', }, + {'METPLUS_CENSOR_VAL': 'censor_val = [3, 4];'}), + + ({'PAIR_STAT_CAT_THRESH': 'gt12.7', }, + {'METPLUS_CAT_THRESH': 'cat_thresh = [gt12.7];'}), + + ({'PAIR_STAT_CNT_THRESH': 'gt12.8', }, + {'METPLUS_CNT_THRESH': 'cnt_thresh = [gt12.8];'}), + + ({'PAIR_STAT_CNT_LOGIC': 'UNION', }, + {'METPLUS_CNT_LOGIC': 'cnt_logic = UNION;'}), + + ({'PAIR_STAT_WIND_THRESH': 'gt12.9', }, + {'METPLUS_WIND_THRESH': 'wind_thresh = [gt12.9];'}), + + ({'PAIR_STAT_WIND_LOGIC': 'UNION', }, + {'METPLUS_WIND_LOGIC': 'wind_logic = UNION;'}), + + ({'PAIR_STAT_MPR_COLUMN': 'ABS(OBS-FCST), ABS(OBS-CLIMO_MEAN)', }, + {'METPLUS_MPR_COLUMN': 'mpr_column = ["ABS(OBS-FCST)", "ABS(OBS-CLIMO_MEAN)"];'}), + + ({'PAIR_STAT_MPR_THRESH': '<=10, <=10', }, + {'METPLUS_MPR_THRESH': 'mpr_thresh = [<=10, <=10];'}), + + ({'PAIR_STAT_MPR_STR_INC': 'STR1, STR2', }, + {'METPLUS_MPR_STR_INC': 'mpr_str_inc = ["STR1", "STR2"];'}), + + ({'PAIR_STAT_MPR_STR_EXC': 'STR3, STR4', }, + {'METPLUS_MPR_STR_EXC': 'mpr_str_exc = ["STR3", "STR4"];'}), + + ({'PAIR_STAT_ECLV_POINTS': '0.05', }, + {'METPLUS_ECLV_POINTS': 'eclv_points = 0.05;'}), + + ({'PAIR_STAT_RANK_CORR_FLAG': 'true', }, + {'METPLUS_RANK_CORR_FLAG': 'rank_corr_flag = TRUE;'}), + + ({'PAIR_STAT_CI_ALPHA': '0.05', }, + {'METPLUS_CI_ALPHA': 'ci_alpha = [0.05];'}), + + ({'PAIR_STAT_BOOT_INTERVAL': 'PCTILE', }, + {'METPLUS_BOOT_DICT': 'boot = {interval = PCTILE;}'}), + + ({'PAIR_STAT_BOOT_REP_PROP': '1.0', }, + {'METPLUS_BOOT_DICT': 'boot = {rep_prop = 1.0;}'}), + + ({'PAIR_STAT_BOOT_N_REP': '100', }, + {'METPLUS_BOOT_DICT': 'boot = {n_rep = 100;}'}), + + ({'PAIR_STAT_BOOT_RNG': 'mt19937', }, + {'METPLUS_BOOT_DICT': 'boot = {rng = "mt19937";}'}), + + ({'PAIR_STAT_BOOT_SEED': '2', }, + {'METPLUS_BOOT_DICT': 'boot = {seed = "2";}'}), + + ({'PAIR_STAT_BOOT_INTERVAL': 'PCTILE', + 'PAIR_STAT_BOOT_REP_PROP': '1.0', + 'PAIR_STAT_BOOT_N_REP': '100', + 'PAIR_STAT_BOOT_RNG': 'mt19937', + 'PAIR_STAT_BOOT_SEED': '2', + }, + {'METPLUS_BOOT_DICT': 'boot = {interval = PCTILE;rep_prop = 1.0;n_rep = 100;rng = "mt19937";seed = "2";}'}), + + # mask grid and poly (old config var) + ({'PAIR_STAT_MASK_GRID': 'FULL', + 'PAIR_STAT_VERIFICATION_MASK_TEMPLATE': 'one, two', + }, + {'METPLUS_MASK_DICT': 'mask = {grid = ["FULL"];poly = ["one", "two"];}', + }), + # mask grid and poly (new config var) + ({'PAIR_STAT_MASK_GRID': 'FULL', + 'PAIR_STAT_MASK_POLY': 'one, two', + }, + {'METPLUS_MASK_DICT': 'mask = {grid = ["FULL"];poly = ["one", "two"];}', + }), + # mask grid value + ({'PAIR_STAT_MASK_GRID': 'FULL', }, + {'METPLUS_MASK_DICT': 'mask = {grid = ["FULL"];}', + }), + # mask.poly complex example + ({'PAIR_STAT_MASK_POLY': ('["{ENV[MET_BUILD_BASE]}/share/met/poly/CAR.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/GLF.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/NAO.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/SAO.poly" ];'), + }, + {'METPLUS_MASK_DICT': + 'mask = {poly = ["{ENV[MET_BUILD_BASE]}/share/met/poly/CAR.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/GLF.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/NAO.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/SAO.poly"];}', + }), + # mask grid empty string (should create empty list) + ({'PAIR_STAT_MASK_GRID': '', }, + {'METPLUS_MASK_DICT': 'mask = {grid = [];}'}), + # mask poly (new config var) + ({'PAIR_STAT_MASK_POLY': 'one, two', }, + {'METPLUS_MASK_DICT': 'mask = {poly = ["one", "two"];}'}), + ({'PAIR_STAT_MASK_SID': 'one, two', }, + {'METPLUS_MASK_DICT': 'mask = {sid = ["one", "two"];}'}), + + ({'PAIR_STAT_OBS_WINDOW_BEGIN': '-2700', + 'PAIR_STAT_OBS_WINDOW_END': '2700', + }, + {'METPLUS_OBS_WINDOW_DICT': 'obs_window = {beg = -2700;end = 2700;}'}), + + ({'PAIR_STAT_CLIMO_CDF_CDF_BINS': '1', }, + {'METPLUS_CLIMO_CDF_DICT': 'climo_cdf = {cdf_bins = 1.0;}'}), + + ({'PAIR_STAT_CLIMO_CDF_CENTER_BINS': 'True', }, + {'METPLUS_CLIMO_CDF_DICT': 'climo_cdf = {center_bins = TRUE;}'}), + + ({'PAIR_STAT_CLIMO_CDF_WRITE_BINS': 'False', }, + {'METPLUS_CLIMO_CDF_DICT': 'climo_cdf = {write_bins = FALSE;}'}), + + ({'PAIR_STAT_CLIMO_CDF_DIRECT_PROB': 'False', }, + {'METPLUS_CLIMO_CDF_DICT': 'climo_cdf = {direct_prob = FALSE;}'}), + + ({'PAIR_STAT_CLIMO_CDF_CDF_BINS': '1', + 'PAIR_STAT_CLIMO_CDF_CENTER_BINS': 'True', + 'PAIR_STAT_CLIMO_CDF_WRITE_BINS': 'False', + 'PAIR_STAT_CLIMO_CDF_DIRECT_PROB': 'False',}, + {'METPLUS_CLIMO_CDF_DICT': 'climo_cdf = {cdf_bins = 1.0;center_bins = TRUE;write_bins = FALSE;direct_prob = FALSE;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_FHO': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {fho = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_CTC': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {ctc = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_CTS': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {cts = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_MCTC': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {mctc = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_MCTS': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {mcts = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_CNT': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {cnt = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_SL1L2': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {sl1l2 = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_SAL1L2': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {sal1l2 = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_VL1L2': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {vl1l2 = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_VAL1L2': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {val1l2 = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_VCNT': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {vcnt = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_PCT': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {pct = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_PSTD': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {pstd = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_PJC': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {pjc = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_PRC': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {prc = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_ECLV': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {eclv = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_MPR': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {mpr = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_SEEPS': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {seeps = BOTH;}'}), + + ({'PAIR_STAT_OUTPUT_FLAG_SEEPS_MPR': 'BOTH', }, + {'METPLUS_OUTPUT_FLAG_DICT': 'output_flag = {seeps_mpr = BOTH;}'}), + + ({ + 'PAIR_STAT_OUTPUT_FLAG_FHO': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_CTC': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_CTS': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_MCTC': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_MCTS': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_CNT': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_SL1L2': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_SAL1L2': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_VL1L2': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_VAL1L2': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_VCNT': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_PCT': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_PSTD': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_PJC': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_PRC': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_ECLV': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_MPR': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_SEEPS': 'BOTH', + 'PAIR_STAT_OUTPUT_FLAG_SEEPS_MPR': 'BOTH', + }, + {'METPLUS_OUTPUT_FLAG_DICT': ( + 'output_flag = {fho = BOTH;ctc = BOTH;cts = BOTH;mctc = BOTH;' + 'mcts = BOTH;cnt = BOTH;sl1l2 = BOTH;sal1l2 = BOTH;' + 'vl1l2 = BOTH;val1l2 = BOTH;vcnt = BOTH;pct = BOTH;pstd = BOTH;' + 'pjc = BOTH;prc = BOTH;' + 'eclv = BOTH;mpr = BOTH;seeps = BOTH;seeps_mpr = BOTH;' + '}' + )}), + + ({'PAIR_STAT_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', }, + {'METPLUS_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' + '["/some/climo_mean/file.txt"];}')}), + + ({'PAIR_STAT_CLIMO_MEAN_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', }, + {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {field = [{name="CLM_NAME"; level="(0,0,*,*)";}];}'}), + + ({'PAIR_STAT_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', }, + {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {method = NEAREST;}}'}), + + ({'PAIR_STAT_CLIMO_MEAN_REGRID_WIDTH': '1', }, + {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {width = 1;}}'}), + + ({'PAIR_STAT_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', }, + {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {vld_thresh = 0.5;}}'}), + + ({'PAIR_STAT_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', }, + {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {shape = SQUARE;}}'}), + + ({'PAIR_STAT_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', }, + {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), + + ({'PAIR_STAT_CLIMO_MEAN_DAY_INTERVAL': '30', }, + {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), + + ({'PAIR_STAT_CLIMO_MEAN_HOUR_INTERVAL': '12', }, + {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {hour_interval = 12;}'}), + + ({ + 'PAIR_STAT_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', + 'PAIR_STAT_CLIMO_MEAN_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', + 'PAIR_STAT_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', + 'PAIR_STAT_CLIMO_MEAN_REGRID_WIDTH': '1', + 'PAIR_STAT_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', + 'PAIR_STAT_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', + 'PAIR_STAT_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', + 'PAIR_STAT_CLIMO_MEAN_DAY_INTERVAL': '30', + 'PAIR_STAT_CLIMO_MEAN_HOUR_INTERVAL': '12', + }, + {'METPLUS_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' + '["/some/climo_mean/file.txt"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' + 'regrid = {method = NEAREST;width = 1;' + 'vld_thresh = 0.5;shape = SQUARE;}' + 'time_interp_method = NEAREST;' + 'day_interval = 30;' + 'hour_interval = 12;}')}), + + # climo stdev + ({'PAIR_STAT_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', }, + {'METPLUS_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' + '["/some/climo_stdev/file.txt"];}')}), + + ({'PAIR_STAT_CLIMO_STDEV_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', }, + {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {field = [{name="CLM_NAME"; level="(0,0,*,*)";}];}'}), + + ({'PAIR_STAT_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', }, + {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {method = NEAREST;}}'}), + + ({'PAIR_STAT_CLIMO_STDEV_REGRID_WIDTH': '1', }, + {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {width = 1;}}'}), + + ({'PAIR_STAT_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', }, + {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {vld_thresh = 0.5;}}'}), + + ({'PAIR_STAT_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', }, + {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {shape = SQUARE;}}'}), + + ({'PAIR_STAT_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', }, + {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), + + ({'PAIR_STAT_CLIMO_STDEV_DAY_INTERVAL': '30', }, + {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), + + ({'PAIR_STAT_CLIMO_STDEV_HOUR_INTERVAL': '12', }, + {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {hour_interval = 12;}'}), + + ({ + 'PAIR_STAT_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', + 'PAIR_STAT_CLIMO_STDEV_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', + 'PAIR_STAT_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', + 'PAIR_STAT_CLIMO_STDEV_REGRID_WIDTH': '1', + 'PAIR_STAT_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', + 'PAIR_STAT_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', + 'PAIR_STAT_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', + 'PAIR_STAT_CLIMO_STDEV_DAY_INTERVAL': '30', + 'PAIR_STAT_CLIMO_STDEV_HOUR_INTERVAL': '12', + }, + {'METPLUS_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' + '["/some/climo_stdev/file.txt"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' + 'regrid = {method = NEAREST;width = 1;' + 'vld_thresh = 0.5;shape = SQUARE;}' + 'time_interp_method = NEAREST;' + 'day_interval = 30;' + 'hour_interval = 12;}')}), + ({'PAIR_STAT_HSS_EC_VALUE': '0.5', }, + {'METPLUS_HSS_EC_VALUE': 'hss_ec_value = 0.5;'}), + ({'PAIR_STAT_MASK_LLPNT': ('{ name = "LAT30TO40"; lat_thresh = >=30&&<=40; lon_thresh = NA; },' + '{ name = "BOX"; lat_thresh = >=20&&<=40; lon_thresh = >=-110&&<=-90; }')}, + {'METPLUS_MASK_DICT': 'mask = {llpnt = [{ name = "LAT30TO40"; lat_thresh = >=30&&<=40; lon_thresh = NA; }, { name = "BOX"; lat_thresh = >=20&&<=40; lon_thresh = >=-110&&<=-90; }];}'}), + + ({'PAIR_STAT_FCST_FILE_TYPE': 'NETCDF_PINT', }, + {'METPLUS_FCST_FILE_TYPE': 'file_type = NETCDF_PINT;'}), + ({'PAIR_STAT_FCST_FILE_TYPE': 'NETCDF_PINT', }, + {'METPLUS_FCST_FILE_TYPE': 'file_type = NETCDF_PINT;'}), + ({'PAIR_STAT_SEEPS_P1_THRESH': 'ge0.1&&le0.85', }, + {'METPLUS_SEEPS_P1_THRESH': 'seeps_p1_thresh = ge0.1&&le0.85;'}), + # complex mask example + ({'PAIR_STAT_MASK_GRID': 'FULL', + 'PAIR_STAT_MASK_POLY': ('["{ENV[MET_BUILD_BASE]}/share/met/poly/CAR.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/GLF.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/NAO.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/SAO.poly" ];'), + 'PAIR_STAT_MASK_SID': 'one, two', + 'PAIR_STAT_MASK_LLPNT': ( + '{ name = "LAT30TO40"; lat_thresh = >=30&&<=40; lon_thresh = NA; },' + '{ name = "BOX"; lat_thresh = >=20&&<=40; lon_thresh = >=-110&&<=-90; }')}, + {'METPLUS_MASK_DICT': ( + 'mask = {grid = ["FULL"];' + 'poly = ["{ENV[MET_BUILD_BASE]}/share/met/poly/CAR.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/GLF.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/NAO.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/SAO.poly"];' + 'sid = ["one", "two"];' + 'llpnt = [{ name = "LAT30TO40"; lat_thresh = >=30&&<=40; lon_thresh = NA; }, { name = "BOX"; lat_thresh = >=20&&<=40; lon_thresh = >=-110&&<=-90; }];}' + )}), + # complex mask example, empty grid value + ({'PAIR_STAT_MASK_GRID': '', + 'PAIR_STAT_MASK_POLY': ( + '["{ENV[MET_BUILD_BASE]}/share/met/poly/CAR.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/GLF.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/NAO.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/SAO.poly" ];'), + 'PAIR_STAT_MASK_SID': 'one, two', + 'PAIR_STAT_MASK_LLPNT': ( + '{ name = "LAT30TO40"; lat_thresh = >=30&&<=40; lon_thresh = NA; },' + '{ name = "BOX"; lat_thresh = >=20&&<=40; lon_thresh = >=-110&&<=-90; }')}, + {'METPLUS_MASK_DICT': ( + 'mask = {grid = [];' + 'poly = ["{ENV[MET_BUILD_BASE]}/share/met/poly/CAR.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/GLF.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/NAO.poly", ' + '"{ENV[MET_BUILD_BASE]}/share/met/poly/SAO.poly"];' + 'sid = ["one", "two"];' + 'llpnt = [{ name = "LAT30TO40"; lat_thresh = >=30&&<=40; lon_thresh = NA; }, { name = "BOX"; lat_thresh = >=20&&<=40; lon_thresh = >=-110&&<=-90; }];}' + )}), + + # fcst climo_mean + ({'PAIR_STAT_FCST_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', }, + {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {file_name = ["/some/climo_mean/file.txt"];}'}), + ({'PAIR_STAT_FCST_CLIMO_MEAN_FIELD': '{name="UGRD"; level=["P850","P500","P250"];}', }, + {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {field = [{name="UGRD"; level=["P850","P500","P250"];}];}'}), + ({'PAIR_STAT_FCST_CLIMO_MEAN_VAR1_NAME': 'UGRD', 'PAIR_STAT_FCST_CLIMO_MEAN_VAR1_LEVELS':'P850,P500,P250', }, + {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {field = [{ name="UGRD"; level="P850"; }, { name="UGRD"; level="P500"; }, { name="UGRD"; level="P250"; }];}'}), + ({'PAIR_STAT_FCST_CLIMO_MEAN_VAR1_NAME': 'UGRD', 'PAIR_STAT_FCST_CLIMO_MEAN_VAR1_LEVELS': 'P850', + 'PAIR_STAT_FCST_CLIMO_MEAN_VAR2_NAME': 'VGRD', 'PAIR_STAT_FCST_CLIMO_MEAN_VAR2_LEVELS': 'P500',}, + {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {field = [{ name="UGRD"; level="P850"; }, { name="VGRD"; level="P500"; }];}'}), + ({'PAIR_STAT_FCST_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', }, + {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {method = NEAREST;}}'}), + ({'PAIR_STAT_FCST_CLIMO_MEAN_REGRID_WIDTH': '1', }, + {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {width = 1;}}'}), + ({'PAIR_STAT_FCST_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', }, + {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {vld_thresh = 0.5;}}'}), + ({'PAIR_STAT_FCST_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', }, + {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {shape = SQUARE;}}'}), + ({'PAIR_STAT_FCST_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', }, + {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), + ({'PAIR_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL': '30', }, + {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), + ({'PAIR_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL': 'NA', }, + {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = NA;}'}), + ({'PAIR_STAT_FCST_CLIMO_MEAN_HOUR_INTERVAL': '12', }, + {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {hour_interval = 12;}'}), + ({'PAIR_STAT_FCST_CLIMO_MEAN_HOUR_INTERVAL': 'NA', }, + {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {hour_interval = NA;}'}), + ({'PAIR_STAT_FCST_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', + 'PAIR_STAT_FCST_CLIMO_MEAN_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', + 'PAIR_STAT_FCST_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', + 'PAIR_STAT_FCST_CLIMO_MEAN_REGRID_WIDTH': '1', + 'PAIR_STAT_FCST_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', + 'PAIR_STAT_FCST_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', + 'PAIR_STAT_FCST_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', + 'PAIR_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL': '30', + 'PAIR_STAT_FCST_CLIMO_MEAN_HOUR_INTERVAL': '12', }, + {'METPLUS_FCST_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' + '["/some/climo_mean/file.txt"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' + 'regrid = {method = NEAREST;width = 1;' + 'vld_thresh = 0.5;shape = SQUARE;}' + 'time_interp_method = NEAREST;' + 'day_interval = 30;' + 'hour_interval = 12;}')}), + # fcst climo_stdev + ({'PAIR_STAT_FCST_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', }, + {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {file_name = ["/some/climo_stdev/file.txt"];}'}), + ({'PAIR_STAT_FCST_CLIMO_STDEV_FIELD': '{name="UGRD"; level=["P850","P500","P250"];}', }, + {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {field = [{name="UGRD"; level=["P850","P500","P250"];}];}'}), + ({'PAIR_STAT_FCST_CLIMO_STDEV_VAR1_NAME': 'UGRD', 'PAIR_STAT_FCST_CLIMO_STDEV_VAR1_LEVELS':'P850,P500,P250', }, + {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {field = [{ name="UGRD"; level="P850"; }, { name="UGRD"; level="P500"; }, { name="UGRD"; level="P250"; }];}'}), + ({'PAIR_STAT_FCST_CLIMO_STDEV_VAR1_NAME': 'UGRD', 'PAIR_STAT_FCST_CLIMO_STDEV_VAR1_LEVELS': 'P850', + 'PAIR_STAT_FCST_CLIMO_STDEV_VAR2_NAME': 'VGRD', 'PAIR_STAT_FCST_CLIMO_STDEV_VAR2_LEVELS': 'P500',}, + {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {field = [{ name="UGRD"; level="P850"; }, { name="VGRD"; level="P500"; }];}'}), + ({'PAIR_STAT_FCST_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', }, + {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {method = NEAREST;}}'}), + ({'PAIR_STAT_FCST_CLIMO_STDEV_REGRID_WIDTH': '1', }, + {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {width = 1;}}'}), + ({'PAIR_STAT_FCST_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', }, + {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {vld_thresh = 0.5;}}'}), + ({'PAIR_STAT_FCST_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', }, + {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {shape = SQUARE;}}'}), + ({'PAIR_STAT_FCST_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', }, + {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), + ({'PAIR_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL': '30', }, + {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), + ({'PAIR_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL': 'NA', }, + {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = NA;}'}), + ({'PAIR_STAT_FCST_CLIMO_STDEV_HOUR_INTERVAL': '12', }, + {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {hour_interval = 12;}'}), + ({'PAIR_STAT_FCST_CLIMO_STDEV_HOUR_INTERVAL': 'NA', }, + {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {hour_interval = NA;}'}), + ({'PAIR_STAT_FCST_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', + 'PAIR_STAT_FCST_CLIMO_STDEV_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', + 'PAIR_STAT_FCST_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', + 'PAIR_STAT_FCST_CLIMO_STDEV_REGRID_WIDTH': '1', + 'PAIR_STAT_FCST_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', + 'PAIR_STAT_FCST_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', + 'PAIR_STAT_FCST_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', + 'PAIR_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL': '30', + 'PAIR_STAT_FCST_CLIMO_STDEV_HOUR_INTERVAL': '12', }, + {'METPLUS_FCST_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' + '["/some/climo_stdev/file.txt"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' + 'regrid = {method = NEAREST;width = 1;' + 'vld_thresh = 0.5;shape = SQUARE;}' + 'time_interp_method = NEAREST;' + 'day_interval = 30;' + 'hour_interval = 12;}')}), + # obs climo_mean + ({'PAIR_STAT_OBS_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', }, + {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {file_name = ["/some/climo_mean/file.txt"];}'}), + ({'PAIR_STAT_OBS_CLIMO_MEAN_FIELD': '{name="UGRD"; level=["P850","P500","P250"];}', }, + {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {field = [{name="UGRD"; level=["P850","P500","P250"];}];}'}), + ({'PAIR_STAT_OBS_CLIMO_MEAN_VAR1_NAME': 'UGRD', 'PAIR_STAT_OBS_CLIMO_MEAN_VAR1_LEVELS':'P850,P500,P250', }, + {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {field = [{ name="UGRD"; level="P850"; }, { name="UGRD"; level="P500"; }, { name="UGRD"; level="P250"; }];}'}), + ({'PAIR_STAT_OBS_CLIMO_MEAN_VAR1_NAME': 'UGRD', 'PAIR_STAT_OBS_CLIMO_MEAN_VAR1_LEVELS': 'P850', + 'PAIR_STAT_OBS_CLIMO_MEAN_VAR2_NAME': 'VGRD', 'PAIR_STAT_OBS_CLIMO_MEAN_VAR2_LEVELS': 'P500',}, + {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {field = [{ name="UGRD"; level="P850"; }, { name="VGRD"; level="P500"; }];}'}), + ({'PAIR_STAT_OBS_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', }, + {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {method = NEAREST;}}'}), + ({'PAIR_STAT_OBS_CLIMO_MEAN_REGRID_WIDTH': '1', }, + {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {width = 1;}}'}), + ({'PAIR_STAT_OBS_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', }, + {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {vld_thresh = 0.5;}}'}), + ({'PAIR_STAT_OBS_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', }, + {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {shape = SQUARE;}}'}), + ({'PAIR_STAT_OBS_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', }, + {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), + ({'PAIR_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL': '30', }, + {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), + ({'PAIR_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL': 'NA', }, + {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = NA;}'}), + ({'PAIR_STAT_OBS_CLIMO_MEAN_HOUR_INTERVAL': '12', }, + {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {hour_interval = 12;}'}), + ({'PAIR_STAT_OBS_CLIMO_MEAN_HOUR_INTERVAL': 'NA', }, + {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {hour_interval = NA;}'}), + ({'PAIR_STAT_OBS_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', + 'PAIR_STAT_OBS_CLIMO_MEAN_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', + 'PAIR_STAT_OBS_CLIMO_MEAN_REGRID_METHOD': 'NEAREST', + 'PAIR_STAT_OBS_CLIMO_MEAN_REGRID_WIDTH': '1', + 'PAIR_STAT_OBS_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', + 'PAIR_STAT_OBS_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', + 'PAIR_STAT_OBS_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', + 'PAIR_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL': '30', + 'PAIR_STAT_OBS_CLIMO_MEAN_HOUR_INTERVAL': '12', }, + {'METPLUS_OBS_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' + '["/some/climo_mean/file.txt"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' + 'regrid = {method = NEAREST;width = 1;' + 'vld_thresh = 0.5;shape = SQUARE;}' + 'time_interp_method = NEAREST;' + 'day_interval = 30;' + 'hour_interval = 12;}')}), + # obs climo_stdev + ({'PAIR_STAT_OBS_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', }, + {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {file_name = ["/some/climo_stdev/file.txt"];}'}), + ({'PAIR_STAT_OBS_CLIMO_STDEV_FIELD': '{name="UGRD"; level=["P850","P500","P250"];}', }, + {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {field = [{name="UGRD"; level=["P850","P500","P250"];}];}'}), + ({'PAIR_STAT_OBS_CLIMO_STDEV_VAR1_NAME': 'UGRD', 'PAIR_STAT_OBS_CLIMO_STDEV_VAR1_LEVELS':'P850,P500,P250', }, + {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {field = [{ name="UGRD"; level="P850"; }, { name="UGRD"; level="P500"; }, { name="UGRD"; level="P250"; }];}'}), + ({'PAIR_STAT_OBS_CLIMO_STDEV_VAR1_NAME': 'UGRD', 'PAIR_STAT_OBS_CLIMO_STDEV_VAR1_LEVELS': 'P850', + 'PAIR_STAT_OBS_CLIMO_STDEV_VAR2_NAME': 'VGRD', 'PAIR_STAT_OBS_CLIMO_STDEV_VAR2_LEVELS': 'P500',}, + {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {field = [{ name="UGRD"; level="P850"; }, { name="VGRD"; level="P500"; }];}'}), + ({'PAIR_STAT_OBS_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', }, + {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {method = NEAREST;}}'}), + ({'PAIR_STAT_OBS_CLIMO_STDEV_REGRID_WIDTH': '1', }, + {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {width = 1;}}'}), + ({'PAIR_STAT_OBS_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', }, + {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {vld_thresh = 0.5;}}'}), + ({'PAIR_STAT_OBS_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', }, + {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {shape = SQUARE;}}'}), + ({'PAIR_STAT_OBS_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', }, + {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), + ({'PAIR_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL': '30', }, + {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), + ({'PAIR_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL': 'NA', }, + {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = NA;}'}), + ({'PAIR_STAT_OBS_CLIMO_STDEV_HOUR_INTERVAL': '12', }, + {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {hour_interval = 12;}'}), + ({'PAIR_STAT_OBS_CLIMO_STDEV_HOUR_INTERVAL': 'NA', }, + {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {hour_interval = NA;}'}), + ({'PAIR_STAT_OBS_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', + 'PAIR_STAT_OBS_CLIMO_STDEV_FIELD': '{name="CLM_NAME"; level="(0,0,*,*)";}', + 'PAIR_STAT_OBS_CLIMO_STDEV_REGRID_METHOD': 'NEAREST', + 'PAIR_STAT_OBS_CLIMO_STDEV_REGRID_WIDTH': '1', + 'PAIR_STAT_OBS_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', + 'PAIR_STAT_OBS_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', + 'PAIR_STAT_OBS_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', + 'PAIR_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL': '30', + 'PAIR_STAT_OBS_CLIMO_STDEV_HOUR_INTERVAL': '12', }, + {'METPLUS_OBS_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' + '["/some/climo_stdev/file.txt"];' + 'field = [{name="CLM_NAME"; level="(0,0,*,*)";}];' + 'regrid = {method = NEAREST;width = 1;' + 'vld_thresh = 0.5;shape = SQUARE;}' + 'time_interp_method = NEAREST;' + 'day_interval = 30;' + 'hour_interval = 12;}')}), + ({'PAIR_STAT_POINT_WEIGHT_FLAG': 'SID', }, + {'METPLUS_POINT_WEIGHT_FLAG': 'point_weight_flag = SID;'}), + + ] +) +@pytest.mark.wrapper_a +def test_pair_stat_all_fields(metplus_config, config_overrides, + env_var_values, compare_command_and_env_vars, + fcst_and_obs_data): + _, _, _, _, inits, valids, lead_hour, _ = fcst_and_obs_data + lead_hour_str = str(lead_hour).zfill(3) + level_no_quotes = '(*,*)' + level_with_quotes = f'"{level_no_quotes}"' + + + fcsts = [{'name': 'TMP', + 'level': 'P750-900', + 'thresh': '<=273,>273'}, + {'name': 'UGRD', + 'level': 'Z10', + 'thresh': '>=5'}, + ] + obss = [{'name': 'TMP', + 'level': level_no_quotes, + 'thresh': '<=273,>273'}, + {'name': 'UGRD', + 'level': 'Z10', + 'thresh': '>=5'}, + ] + + fcst_fmts = [] + obs_fmts = [] + for fcst, obs in zip(fcsts, obss): + fcst_name = fcst['name'] + fcst_level = fcst['level'] + fcst_thresh = fcst['thresh'] + obs_name = obs['name'] + obs_level = obs['level'] + obs_thresh = obs['thresh'] + + fcst_fmt = (f'{{ name="{fcst_name}"; level="{fcst_level}"; ' + f'cat_thresh=[ {fcst_thresh} ]; }}') + obs_fmt = (f'{{ name="{obs_name}"; level="{obs_level}"; ' + f'cat_thresh=[ {obs_thresh} ]; }}') + fcst_fmts.append(fcst_fmt) + obs_fmts.append(obs_fmt) + + config = metplus_config + set_minimum_config_settings(config, fcst_and_obs_data) + + for index, (fcst, obs) in enumerate(zip(fcsts, obss)): + idx = index + 1 + if obs['level'] == level_no_quotes: + obs['level'] = level_with_quotes + config.set('config', f'FCST_VAR{idx}_NAME', fcst['name']) + config.set('config', f'FCST_VAR{idx}_LEVELS', fcst['level']) + config.set('config', f'FCST_VAR{idx}_THRESH', fcst['thresh']) + config.set('config', f'OBS_VAR{idx}_NAME', obs['name']) + config.set('config', f'OBS_VAR{idx}_LEVELS', obs['level']) + config.set('config', f'OBS_VAR{idx}_THRESH', obs['thresh']) + + config.set('config', 'PAIR_STAT_ONCE_PER_FIELD', False) + + # set config variable overrides + for key, value in config_overrides.items(): + config.set('config', key, value) + + wrapper = PairStatWrapper(config) + assert wrapper.isOK + + app_path = os.path.join(config.getdir('MET_BIN_DIR'), wrapper.app_name) + verbosity = f"-v {wrapper.c_dict['VERBOSITY']}" + config_file = wrapper.c_dict.get('CONFIG_FILE') + out_dir = wrapper.c_dict.get('OUTPUT_DIR') + + # add extra command line arguments + extra_args = [' '] * len(inits) + + expected_cmds = [] + for index in range(0, len(inits)): + expected_cmds.append( + f"{app_path}" + f" -pairs {pairs_dir}/{inits[index]}/fcst_file_F{lead_hour_str}" + f" -format {pair_stat_format}" + f" -config {config_file}{extra_args[index]}" + f"-out {out_dir}/{valids[index]} {verbosity}" + ) + + fcst_fmt = f"pairs = [{','.join(fcst_fmts)}];" + obs_fmt = f"pairs = [{','.join(obs_fmts)}];" + + all_cmds = wrapper.run_all_times() + special_values = { + 'METPLUS_FCST_FIELD': fcst_fmt, + 'METPLUS_OBS_FIELD': obs_fmt, + } + compare_command_and_env_vars(all_cmds, expected_cmds, env_var_values, + wrapper, special_values) + + +@pytest.mark.wrapper_a +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', + 'PairStatConfig_wrapped') + + wrapper = PairStatWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == default_config_file + + config.set('config', 'PAIR_STAT_CONFIG_FILE', fake_config_name) + wrapper = PairStatWrapper(config) + assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name diff --git a/internal/tests/pytests/wrappers/pb2nc/test_pb2nc_wrapper.py b/internal/tests/pytests/wrappers/pb2nc/test_pb2nc_wrapper.py index 09f1807253..0c96e0021a 100644 --- a/internal/tests/pytests/wrappers/pb2nc/test_pb2nc_wrapper.py +++ b/internal/tests/pytests/wrappers/pb2nc/test_pb2nc_wrapper.py @@ -6,6 +6,7 @@ import datetime from metplus.wrappers.pb2nc_wrapper import PB2NCWrapper +from internal.tests.pytests.conftest import obs_to_nc_runtime_freq_test_params from metplus.util import time_util from metplus.util import do_string_sub @@ -27,19 +28,7 @@ def pb2nc_wrapper(metplus_config): @pytest.mark.parametrize( - 'missing, run, thresh, errors, allow_missing, runtime_freq', [ - (16, 24, 0.3, 0, True, 'RUN_ONCE_FOR_EACH'), - (16, 24, 0.7, 1, True, 'RUN_ONCE_FOR_EACH'), - (16, 24, 0.3, 16, False, 'RUN_ONCE_FOR_EACH'), - (2, 4, 0.4, 0, True, 'RUN_ONCE_PER_INIT_OR_VALID'), - (2, 4, 0.6, 1, True, 'RUN_ONCE_PER_INIT_OR_VALID'), - (2, 4, 0.6, 2, False, 'RUN_ONCE_PER_INIT_OR_VALID'), - (2, 5, 0.4, 0, True, 'RUN_ONCE_PER_LEAD'), - (2, 5, 0.7, 1, True, 'RUN_ONCE_PER_LEAD'), - (2, 5, 0.4, 2, False, 'RUN_ONCE_PER_LEAD'), - (0, 1, 0.4, 0, True, 'RUN_ONCE'), - (0, 1, 0.4, 0, False, 'RUN_ONCE'), - ] + 'missing, run, thresh, errors, allow_missing, runtime_freq', obs_to_nc_runtime_freq_test_params ) @pytest.mark.wrapper def test_pb2nc_missing_inputs(metplus_config, get_test_data_dir, missing, diff --git a/internal/tests/pytests/wrappers/point_stat/test_point_stat_wrapper.py b/internal/tests/pytests/wrappers/point_stat/test_point_stat_wrapper.py index b76ac81d53..6e553da7d4 100755 --- a/internal/tests/pytests/wrappers/point_stat/test_point_stat_wrapper.py +++ b/internal/tests/pytests/wrappers/point_stat/test_point_stat_wrapper.py @@ -6,29 +6,16 @@ from datetime import datetime, timedelta from metplus.wrappers.point_stat_wrapper import PointStatWrapper +from internal.tests.pytests.conftest import stat_runtime_freq_test_params fcst_dir = '/some/path/fcst' obs_dir = '/some/path/obs' -fcst_name = 'APCP' -fcst_level = 'A03' -obs_name = 'APCP_03' -obs_level = '"(*,*)"' - -inits = ['2005080700', '2005080712'] -time_fmt = '%Y%m%d%H' -lead_hour = 12 -lead_hour_str = str(lead_hour).zfill(3) -valids = [] -for init in inits: - valid = datetime.strptime(init, time_fmt) + timedelta(hours=lead_hour) - valid = valid.strftime(time_fmt) - valids.append(valid) - ugrid_config_file = '/some/path/UgridConfig_fake' -def set_minimum_config_settings(config): +def set_minimum_config_settings(config, fcst_and_obs_data): + _, _, _, _, inits, _, lead_hour, time_fmt = fcst_and_obs_data # set config variables to prevent command from running and bypass check # if input files actually exist config.set('config', 'DO_NOT_RUN_EXE', True) @@ -57,21 +44,15 @@ def set_minimum_config_settings(config): @pytest.mark.parametrize( - 'once_per_field, missing, run, thresh, errors, allow_missing', [ - (False, 6, 12, 0.5, 0, True), - (False, 6, 12, 0.6, 1, True), - (True, 12, 24, 0.5, 0, True), - (True, 12, 24, 0.6, 1, True), - (False, 6, 12, 0.5, 6, False), - (True, 12, 24, 0.5, 12, False), - ] + 'once_per_field, missing, run, thresh, errors, allow_missing', stat_runtime_freq_test_params ) @pytest.mark.wrapper_a def test_point_stat_missing_inputs(metplus_config, get_test_data_dir, once_per_field, missing, run, thresh, errors, - allow_missing): + allow_missing, fcst_and_obs_data): + fcst_name, fcst_level, obs_name, obs_level, _, _, _, _ = fcst_and_obs_data config = metplus_config - set_minimum_config_settings(config) + set_minimum_config_settings(config, fcst_and_obs_data) config.set('config', 'INPUT_MUST_EXIST', True) config.set('config', 'POINT_STAT_ALLOW_MISSING_INPUTS', allow_missing) config.set('config', 'POINT_STAT_INPUT_THRESH', thresh) @@ -110,9 +91,9 @@ def test_point_stat_missing_inputs(metplus_config, get_test_data_dir, @pytest.mark.wrapper_a -def test_met_dictionary_in_var_options(metplus_config): +def test_met_dictionary_in_var_options(metplus_config, fcst_and_obs_data): config = metplus_config - set_minimum_config_settings(config) + set_minimum_config_settings(config, fcst_and_obs_data) config.set('config', 'BOTH_VAR1_NAME', 'name') config.set('config', 'BOTH_VAR1_LEVELS', 'level') @@ -122,7 +103,7 @@ def test_met_dictionary_in_var_options(metplus_config): wrapper = PointStatWrapper(config) assert wrapper.isOK - all_cmds = wrapper.run_all_times() + wrapper.run_all_times() @pytest.mark.parametrize( @@ -427,9 +408,6 @@ def test_met_dictionary_in_var_options(metplus_config): { 'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - ({'POINT_STAT_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), - ({'POINT_STAT_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), @@ -444,7 +422,6 @@ def test_met_dictionary_in_var_options(metplus_config): 'POINT_STAT_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'POINT_STAT_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'POINT_STAT_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'POINT_STAT_CLIMO_MEAN_MATCH_MONTH': 'True', 'POINT_STAT_CLIMO_MEAN_DAY_INTERVAL': '30', 'POINT_STAT_CLIMO_MEAN_HOUR_INTERVAL': '12', }, @@ -454,7 +431,7 @@ def test_met_dictionary_in_var_options(metplus_config): 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # climo stdev @@ -486,9 +463,6 @@ def test_met_dictionary_in_var_options(metplus_config): { 'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - ({'POINT_STAT_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), - ({'POINT_STAT_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), @@ -503,7 +477,6 @@ def test_met_dictionary_in_var_options(metplus_config): 'POINT_STAT_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'POINT_STAT_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'POINT_STAT_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'POINT_STAT_CLIMO_STDEV_MATCH_MONTH': 'True', 'POINT_STAT_CLIMO_STDEV_DAY_INTERVAL': '30', 'POINT_STAT_CLIMO_STDEV_HOUR_INTERVAL': '12', }, @@ -513,7 +486,7 @@ def test_met_dictionary_in_var_options(metplus_config): 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), ({'POINT_STAT_HSS_EC_VALUE': '0.5', }, {'METPLUS_HSS_EC_VALUE': 'hss_ec_value = 0.5;'}), @@ -691,8 +664,6 @@ def test_met_dictionary_in_var_options(metplus_config): {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {shape = SQUARE;}}'}), ({'POINT_STAT_FCST_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - ({'POINT_STAT_FCST_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), ({'POINT_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), ({'POINT_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL': 'NA', }, @@ -708,7 +679,6 @@ def test_met_dictionary_in_var_options(metplus_config): 'POINT_STAT_FCST_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'POINT_STAT_FCST_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'POINT_STAT_FCST_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'POINT_STAT_FCST_CLIMO_MEAN_MATCH_MONTH': 'True', 'POINT_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL': '30', 'POINT_STAT_FCST_CLIMO_MEAN_HOUR_INTERVAL': '12', }, {'METPLUS_FCST_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' @@ -717,7 +687,7 @@ def test_met_dictionary_in_var_options(metplus_config): 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # fcst climo_stdev ({'POINT_STAT_FCST_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', }, @@ -739,8 +709,6 @@ def test_met_dictionary_in_var_options(metplus_config): {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {shape = SQUARE;}}'}), ({'POINT_STAT_FCST_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - ({'POINT_STAT_FCST_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), ({'POINT_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), ({'POINT_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL': 'NA', }, @@ -756,7 +724,6 @@ def test_met_dictionary_in_var_options(metplus_config): 'POINT_STAT_FCST_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'POINT_STAT_FCST_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'POINT_STAT_FCST_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'POINT_STAT_FCST_CLIMO_STDEV_MATCH_MONTH': 'True', 'POINT_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL': '30', 'POINT_STAT_FCST_CLIMO_STDEV_HOUR_INTERVAL': '12', }, {'METPLUS_FCST_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' @@ -765,7 +732,7 @@ def test_met_dictionary_in_var_options(metplus_config): 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # obs climo_mean ({'POINT_STAT_OBS_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', }, @@ -787,8 +754,6 @@ def test_met_dictionary_in_var_options(metplus_config): {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {shape = SQUARE;}}'}), ({'POINT_STAT_OBS_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - ({'POINT_STAT_OBS_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), ({'POINT_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), ({'POINT_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL': 'NA', }, @@ -804,7 +769,6 @@ def test_met_dictionary_in_var_options(metplus_config): 'POINT_STAT_OBS_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'POINT_STAT_OBS_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'POINT_STAT_OBS_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'POINT_STAT_OBS_CLIMO_MEAN_MATCH_MONTH': 'True', 'POINT_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL': '30', 'POINT_STAT_OBS_CLIMO_MEAN_HOUR_INTERVAL': '12', }, {'METPLUS_OBS_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' @@ -813,7 +777,7 @@ def test_met_dictionary_in_var_options(metplus_config): 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # obs climo_stdev ({'POINT_STAT_OBS_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', }, @@ -835,8 +799,6 @@ def test_met_dictionary_in_var_options(metplus_config): {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {shape = SQUARE;}}'}), ({'POINT_STAT_OBS_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - ({'POINT_STAT_OBS_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), ({'POINT_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), ({'POINT_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL': 'NA', }, @@ -852,7 +814,6 @@ def test_met_dictionary_in_var_options(metplus_config): 'POINT_STAT_OBS_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'POINT_STAT_OBS_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'POINT_STAT_OBS_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'POINT_STAT_OBS_CLIMO_STDEV_MATCH_MONTH': 'True', 'POINT_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL': '30', 'POINT_STAT_OBS_CLIMO_STDEV_HOUR_INTERVAL': '12', }, {'METPLUS_OBS_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' @@ -861,7 +822,7 @@ def test_met_dictionary_in_var_options(metplus_config): 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), ({'POINT_STAT_POINT_WEIGHT_FLAG': 'SID', }, {'METPLUS_POINT_WEIGHT_FLAG': 'point_weight_flag = SID;'}), @@ -871,7 +832,10 @@ def test_met_dictionary_in_var_options(metplus_config): ) @pytest.mark.wrapper_a def test_point_stat_all_fields(metplus_config, config_overrides, - env_var_values, compare_command_and_env_vars): + env_var_values, compare_command_and_env_vars, + fcst_and_obs_data): + _, _, _, _, inits, valids, lead_hour, time_fmt = fcst_and_obs_data + lead_hour_str = str(lead_hour).zfill(3) level_no_quotes = '(*,*)' level_with_quotes = f'"{level_no_quotes}"' @@ -881,9 +845,6 @@ def test_point_stat_all_fields(metplus_config, config_overrides, {'name': 'UGRD', 'level': 'Z10', 'thresh': '>=5'}, - # {'name': 'VGRD', - # 'level': 'Z10', - # 'thresh': '>=5'}, ] obss = [{'name': 'TMP', 'level': level_no_quotes, @@ -891,9 +852,6 @@ def test_point_stat_all_fields(metplus_config, config_overrides, {'name': 'UGRD', 'level': 'Z10', 'thresh': '>=5'}, - # {'name': 'VGRD', - # 'level': 'Z10', - # 'thresh': '>=5'}, ] fcst_fmts = [] @@ -914,7 +872,7 @@ def test_point_stat_all_fields(metplus_config, config_overrides, obs_fmts.append(obs_fmt) config = metplus_config - set_minimum_config_settings(config) + set_minimum_config_settings(config, fcst_and_obs_data) for index, (fcst, obs) in enumerate(zip(fcsts, obss)): idx = index + 1 @@ -942,29 +900,7 @@ def test_point_stat_all_fields(metplus_config, config_overrides, out_dir = wrapper.c_dict.get('OUTPUT_DIR') # add extra command line arguments - extra_args = [' '] * len(inits) - - if 'OBS_POINT_STAT_INPUT_TEMPLATE' in config_overrides: - for index in range(0, len(inits)): - extra_args[index] += f'-point_obs {obs_dir}/{valids[index]}/obs_file2 ' - # if obs_file3 is set, an additional point observation file is added - if 'obs_file3' in config_overrides['OBS_POINT_STAT_INPUT_TEMPLATE']: - extra_args[index] += f'-point_obs {obs_dir}/{valids[index]}/obs_file3 ' - - if 'POINT_STAT_UGRID_CONFIG_FILE' in config_overrides: - for index in range(0, len(inits)): - extra_args[index] += f'-ugrid_config {ugrid_config_file} ' - - for beg_end in ('BEG', 'END'): - if f'POINT_STAT_OBS_VALID_{beg_end}' in config_overrides: - for index in range(0, len(inits)): - valid_dt = datetime.strptime(valids[index], time_fmt) - if beg_end == 'BEG': - value = valid_dt - timedelta(hours=6) - else: - value = valid_dt + timedelta(hours=6) - value = value.strftime('%Y%m%d_%H') - extra_args[index] += f'-obs_valid_{beg_end.lower()} {value} ' + extra_args = _get_extra_args(inits, valids, config_overrides, time_fmt) expected_cmds = [] for index in range(0, len(inits)): @@ -986,6 +922,32 @@ def test_point_stat_all_fields(metplus_config, config_overrides, compare_command_and_env_vars(all_cmds, expected_cmds, env_var_values, wrapper, special_values) +def _get_extra_args(inits, valids, config_overrides, time_fmt): + extra_args = [' '] * len(inits) + + if 'OBS_POINT_STAT_INPUT_TEMPLATE' in config_overrides: + for index in range(0, len(inits)): + extra_args[index] += f'-point_obs {obs_dir}/{valids[index]}/obs_file2 ' + # if obs_file3 is set, an additional point observation file is added + if 'obs_file3' in config_overrides['OBS_POINT_STAT_INPUT_TEMPLATE']: + extra_args[index] += f'-point_obs {obs_dir}/{valids[index]}/obs_file3 ' + + if 'POINT_STAT_UGRID_CONFIG_FILE' in config_overrides: + for index in range(0, len(inits)): + extra_args[index] += f'-ugrid_config {ugrid_config_file} ' + + time_deltas = {'BEG': -6, 'END': 6} + for beg_end, delta in time_deltas.items(): + if f'POINT_STAT_OBS_VALID_{beg_end}' not in config_overrides: + continue + + for index in range(0, len(inits)): + valid_dt = datetime.strptime(valids[index], time_fmt) + value = (valid_dt + timedelta(hours=delta)).strftime('%Y%m%d_%H') + extra_args[index] += f'-obs_valid_{beg_end.lower()} {value} ' + + return extra_args + @pytest.mark.wrapper_a def test_get_config_file(metplus_config): diff --git a/internal/tests/pytests/wrappers/series_analysis/test_series_analysis.py b/internal/tests/pytests/wrappers/series_analysis/test_series_analysis.py index 5dc14a4c82..c1a399567b 100644 --- a/internal/tests/pytests/wrappers/series_analysis/test_series_analysis.py +++ b/internal/tests/pytests/wrappers/series_analysis/test_series_analysis.py @@ -217,9 +217,6 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, { 'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - ({'SERIES_ANALYSIS_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), - ({'SERIES_ANALYSIS_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), @@ -234,7 +231,6 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, 'SERIES_ANALYSIS_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'SERIES_ANALYSIS_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'SERIES_ANALYSIS_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'SERIES_ANALYSIS_CLIMO_MEAN_MATCH_MONTH': 'True', 'SERIES_ANALYSIS_CLIMO_MEAN_DAY_INTERVAL': '30', 'SERIES_ANALYSIS_CLIMO_MEAN_HOUR_INTERVAL': '12', }, @@ -244,7 +240,7 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # climo stdev @@ -274,9 +270,6 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, { 'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - ({'SERIES_ANALYSIS_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), - ({'SERIES_ANALYSIS_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), @@ -291,7 +284,6 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, 'SERIES_ANALYSIS_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'SERIES_ANALYSIS_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'SERIES_ANALYSIS_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'SERIES_ANALYSIS_CLIMO_STDEV_MATCH_MONTH': 'True', 'SERIES_ANALYSIS_CLIMO_STDEV_DAY_INTERVAL': '30', 'SERIES_ANALYSIS_CLIMO_STDEV_HOUR_INTERVAL': '12', }, @@ -301,7 +293,7 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), ({'SERIES_ANALYSIS_HSS_EC_VALUE': '0.5', }, {'METPLUS_HSS_EC_VALUE': 'hss_ec_value = 0.5;'}), @@ -450,8 +442,6 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {shape = SQUARE;}}'}), ({'SERIES_ANALYSIS_FCST_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - ({'SERIES_ANALYSIS_FCST_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), ({'SERIES_ANALYSIS_FCST_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_FCST_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), ({'SERIES_ANALYSIS_FCST_CLIMO_MEAN_DAY_INTERVAL': 'NA', }, @@ -467,7 +457,6 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, 'SERIES_ANALYSIS_FCST_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'SERIES_ANALYSIS_FCST_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'SERIES_ANALYSIS_FCST_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'SERIES_ANALYSIS_FCST_CLIMO_MEAN_MATCH_MONTH': 'True', 'SERIES_ANALYSIS_FCST_CLIMO_MEAN_DAY_INTERVAL': '30', 'SERIES_ANALYSIS_FCST_CLIMO_MEAN_HOUR_INTERVAL': '12', }, {'METPLUS_FCST_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' @@ -476,7 +465,7 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # fcst climo_stdev ({'SERIES_ANALYSIS_FCST_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', }, @@ -498,8 +487,6 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {shape = SQUARE;}}'}), ({'SERIES_ANALYSIS_FCST_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - ({'SERIES_ANALYSIS_FCST_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), ({'SERIES_ANALYSIS_FCST_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_FCST_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), ({'SERIES_ANALYSIS_FCST_CLIMO_STDEV_DAY_INTERVAL': 'NA', }, @@ -515,7 +502,6 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, 'SERIES_ANALYSIS_FCST_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'SERIES_ANALYSIS_FCST_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'SERIES_ANALYSIS_FCST_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'SERIES_ANALYSIS_FCST_CLIMO_STDEV_MATCH_MONTH': 'True', 'SERIES_ANALYSIS_FCST_CLIMO_STDEV_DAY_INTERVAL': '30', 'SERIES_ANALYSIS_FCST_CLIMO_STDEV_HOUR_INTERVAL': '12', }, {'METPLUS_FCST_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' @@ -524,7 +510,7 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # obs climo_mean ({'SERIES_ANALYSIS_OBS_CLIMO_MEAN_FILE_NAME': '/some/climo_mean/file.txt', }, @@ -546,8 +532,6 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {regrid = {shape = SQUARE;}}'}), ({'SERIES_ANALYSIS_OBS_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {time_interp_method = NEAREST;}'}), - ({'SERIES_ANALYSIS_OBS_CLIMO_MEAN_MATCH_MONTH': 'True', }, - {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {match_month = TRUE;}'}), ({'SERIES_ANALYSIS_OBS_CLIMO_MEAN_DAY_INTERVAL': '30', }, {'METPLUS_OBS_CLIMO_MEAN_DICT': 'climo_mean = {day_interval = 30;}'}), ({'SERIES_ANALYSIS_OBS_CLIMO_MEAN_DAY_INTERVAL': 'NA', }, @@ -563,7 +547,6 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, 'SERIES_ANALYSIS_OBS_CLIMO_MEAN_REGRID_VLD_THRESH': '0.5', 'SERIES_ANALYSIS_OBS_CLIMO_MEAN_REGRID_SHAPE': 'SQUARE', 'SERIES_ANALYSIS_OBS_CLIMO_MEAN_TIME_INTERP_METHOD': 'NEAREST', - 'SERIES_ANALYSIS_OBS_CLIMO_MEAN_MATCH_MONTH': 'True', 'SERIES_ANALYSIS_OBS_CLIMO_MEAN_DAY_INTERVAL': '30', 'SERIES_ANALYSIS_OBS_CLIMO_MEAN_HOUR_INTERVAL': '12', }, {'METPLUS_OBS_CLIMO_MEAN_DICT': ('climo_mean = {file_name = ' @@ -572,7 +555,7 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), # obs climo_stdev ({'SERIES_ANALYSIS_OBS_CLIMO_STDEV_FILE_NAME': '/some/climo_stdev/file.txt', }, @@ -594,8 +577,6 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {regrid = {shape = SQUARE;}}'}), ({'SERIES_ANALYSIS_OBS_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', }, {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {time_interp_method = NEAREST;}'}), - ({'SERIES_ANALYSIS_OBS_CLIMO_STDEV_MATCH_MONTH': 'True', }, - {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {match_month = TRUE;}'}), ({'SERIES_ANALYSIS_OBS_CLIMO_STDEV_DAY_INTERVAL': '30', }, {'METPLUS_OBS_CLIMO_STDEV_DICT': 'climo_stdev = {day_interval = 30;}'}), ({'SERIES_ANALYSIS_OBS_CLIMO_STDEV_DAY_INTERVAL': 'NA', }, @@ -611,7 +592,6 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, 'SERIES_ANALYSIS_OBS_CLIMO_STDEV_REGRID_VLD_THRESH': '0.5', 'SERIES_ANALYSIS_OBS_CLIMO_STDEV_REGRID_SHAPE': 'SQUARE', 'SERIES_ANALYSIS_OBS_CLIMO_STDEV_TIME_INTERP_METHOD': 'NEAREST', - 'SERIES_ANALYSIS_OBS_CLIMO_STDEV_MATCH_MONTH': 'True', 'SERIES_ANALYSIS_OBS_CLIMO_STDEV_DAY_INTERVAL': '30', 'SERIES_ANALYSIS_OBS_CLIMO_STDEV_HOUR_INTERVAL': '12', }, {'METPLUS_OBS_CLIMO_STDEV_DICT': ('climo_stdev = {file_name = ' @@ -620,7 +600,7 @@ def test_series_analysis_missing_inputs(metplus_config, get_test_data_dir, 'regrid = {method = NEAREST;width = 1;' 'vld_thresh = 0.5;shape = SQUARE;}' 'time_interp_method = NEAREST;' - 'match_month = TRUE;day_interval = 30;' + 'day_interval = 30;' 'hour_interval = 12;}')}), ({'SERIES_ANALYSIS_AGGR_INPUT_TEMPLATE': os.path.join(aggr_dir, aggr_template), }, {}), @@ -851,21 +831,40 @@ def test_get_all_files_and_subset(metplus_config, time_info, expect_fcst_subset, wrapper.c_dict['FCST_INPUT_DIR'] = fcst_input_dir wrapper.c_dict['OBS_INPUT_DIR'] = obs_input_dir - if time_info['storm_id'] == '*': - wrapper.c_dict['RUN_ONCE_PER_STORM_ID'] = False + wrapper.c_dict['RUN_ONCE_PER_STORM_ID'] = time_info['storm_id'] != '*' + + if time_info.get('lead') != '*': + wrapper.c_dict['ALL_FILES'] = ( + wrapper.get_all_files_for_leads(time_info, [time_info['lead']]) + ) + expected_fcst = [ + 'fcst/20141214_00/ML1201072014/FCST_TILE_F006_gfs_4_20141214_0000_006.nc', + 'fcst/20141214_00/ML1221072014/FCST_TILE_F006_gfs_4_20141214_0000_006.nc', + ] + expected_obs = [ + 'obs/20141214_00/ML1201072014/OBS_TILE_F006_gfs_4_20141214_0000_006.nc', + 'obs/20141214_00/ML1221072014/OBS_TILE_F006_gfs_4_20141214_0000_006.nc', + ] else: - wrapper.c_dict['RUN_ONCE_PER_STORM_ID'] = True - - wrapper.c_dict['ALL_FILES'] = wrapper.get_all_files() + wrapper.c_dict['ALL_FILES'] = wrapper.get_all_files() + expected_fcst = [ + 'fcst/20141214_00/ML1201072014/FCST_TILE_F000_gfs_4_20141214_0000_000.nc', + 'fcst/20141214_00/ML1201072014/FCST_TILE_F006_gfs_4_20141214_0000_006.nc', + 'fcst/20141214_00/ML1201072014/FCST_TILE_F012_gfs_4_20141214_0000_012.nc', + 'fcst/20141214_00/ML1221072014/FCST_TILE_F000_gfs_4_20141214_0000_000.nc', + 'fcst/20141214_00/ML1221072014/FCST_TILE_F006_gfs_4_20141214_0000_006.nc', + 'fcst/20141214_00/ML1221072014/FCST_TILE_F012_gfs_4_20141214_0000_012.nc', + ] + expected_obs = [ + 'obs/20141214_00/ML1201072014/OBS_TILE_F000_gfs_4_20141214_0000_000.nc', + 'obs/20141214_00/ML1201072014/OBS_TILE_F006_gfs_4_20141214_0000_006.nc', + 'obs/20141214_00/ML1201072014/OBS_TILE_F012_gfs_4_20141214_0000_012.nc', + 'obs/20141214_00/ML1221072014/OBS_TILE_F000_gfs_4_20141214_0000_000.nc', + 'obs/20141214_00/ML1221072014/OBS_TILE_F006_gfs_4_20141214_0000_006.nc', + 'obs/20141214_00/ML1221072014/OBS_TILE_F012_gfs_4_20141214_0000_012.nc', + ] print(f"ALL FILES: {wrapper.c_dict['ALL_FILES']}") - expected_fcst = [ - 'fcst/20141214_00/ML1201072014/FCST_TILE_F000_gfs_4_20141214_0000_000.nc', - 'fcst/20141214_00/ML1201072014/FCST_TILE_F006_gfs_4_20141214_0000_006.nc', - 'fcst/20141214_00/ML1201072014/FCST_TILE_F012_gfs_4_20141214_0000_012.nc', - 'fcst/20141214_00/ML1221072014/FCST_TILE_F000_gfs_4_20141214_0000_000.nc', - 'fcst/20141214_00/ML1221072014/FCST_TILE_F006_gfs_4_20141214_0000_006.nc', - 'fcst/20141214_00/ML1221072014/FCST_TILE_F012_gfs_4_20141214_0000_012.nc', - ] + if time_info['storm_id'] != '*': expected_fcst = [item for item in expected_fcst if time_info['storm_id'] in item] @@ -873,15 +872,6 @@ def test_get_all_files_and_subset(metplus_config, time_info, expect_fcst_subset, for expected in expected_fcst: expected_fcst_files.append(os.path.join(tile_input_dir, expected)) - - expected_obs = [ - 'obs/20141214_00/ML1201072014/OBS_TILE_F000_gfs_4_20141214_0000_000.nc', - 'obs/20141214_00/ML1201072014/OBS_TILE_F006_gfs_4_20141214_0000_006.nc', - 'obs/20141214_00/ML1201072014/OBS_TILE_F012_gfs_4_20141214_0000_012.nc', - 'obs/20141214_00/ML1221072014/OBS_TILE_F000_gfs_4_20141214_0000_000.nc', - 'obs/20141214_00/ML1221072014/OBS_TILE_F006_gfs_4_20141214_0000_006.nc', - 'obs/20141214_00/ML1221072014/OBS_TILE_F012_gfs_4_20141214_0000_012.nc', - ] if time_info['storm_id'] != '*': expected_obs = [item for item in expected_obs if time_info['storm_id'] in item] diff --git a/internal/tests/pytests/wrappers/wavelet_stat/test_wavelet_stat.py b/internal/tests/pytests/wrappers/wavelet_stat/test_wavelet_stat.py index 52e4528ad2..5f88358110 100644 --- a/internal/tests/pytests/wrappers/wavelet_stat/test_wavelet_stat.py +++ b/internal/tests/pytests/wrappers/wavelet_stat/test_wavelet_stat.py @@ -5,6 +5,7 @@ import os from metplus.wrappers.wavelet_stat_wrapper import WaveletStatWrapper +from internal.tests.pytests.conftest import stat_runtime_freq_test_params fcst_dir = '/some/path/fcst' obs_dir = '/some/path/obs' @@ -56,14 +57,7 @@ def set_minimum_config_settings(config): @pytest.mark.parametrize( - 'once_per_field, missing, run, thresh, errors, allow_missing', [ - (False, 6, 12, 0.5, 0, True), - (False, 6, 12, 0.6, 1, True), - (True, 12, 24, 0.5, 0, True), - (True, 12, 24, 0.6, 1, True), - (False, 6, 12, 0.5, 6, False), - (True, 12, 24, 0.5, 12, False), - ] + 'once_per_field, missing, run, thresh, errors, allow_missing', stat_runtime_freq_test_params ) @pytest.mark.wrapper_b def test_wavelet_stat_missing_inputs(metplus_config, get_test_data_dir, diff --git a/internal/tests/use_cases/all_use_cases.txt b/internal/tests/use_cases/all_use_cases.txt index a86d037e6a..c0a3038a03 100644 --- a/internal/tests/use_cases/all_use_cases.txt +++ b/internal/tests/use_cases/all_use_cases.txt @@ -65,6 +65,7 @@ Category: met_tool_wrapper 63::WaveletStat:: met_tool_wrapper/WaveletStat/WaveletStat.conf 64::MADIS2NC:: met_tool_wrapper/MADIS2NC/MADIS2NC.conf 65::SeriesAnalysis_aggr:: met_tool_wrapper/SeriesAnalysis/SeriesAnalysis_aggr.conf::netcdf4_env +66::PairStat:: met_tool_wrapper/PairStat/PairStat.conf Category: air_quality_and_comp 0::EnsembleStat_fcstICAP_obsMODIS_aod::model_applications/air_quality_and_comp/EnsembleStat_fcstICAP_obsMODIS_aod.conf diff --git a/metplus/component_versions.py b/metplus/component_versions.py index 10e4810e71..a886fef7b2 100755 --- a/metplus/component_versions.py +++ b/metplus/component_versions.py @@ -161,10 +161,12 @@ def main(): args.output_component, args.output_format, args.get_dev_version, args.rc_is_dev) +def init(): + if __name__ == "__main__": + out_version = main() + if not out_version: + sys.exit(1) -if __name__ == "__main__": - out_version = main() - if not out_version: - sys.exit(1) + print(out_version) - print(out_version) +init() diff --git a/metplus/util/constants.py b/metplus/util/constants.py index 3b5c0f7ff2..c080de2a67 100644 --- a/metplus/util/constants.py +++ b/metplus/util/constants.py @@ -25,6 +25,7 @@ 'mode': 'MODE', 'mtd': 'MTD', 'modetimedomain': 'MTD', + 'pairstat': 'PairStat', 'pb2nc': 'PB2NC', 'pcpcombine': 'PCPCombine', 'plotdataplane': 'PlotDataPlane', diff --git a/metplus/util/met_config.py b/metplus/util/met_config.py index 8763fba2c1..72247cd573 100644 --- a/metplus/util/met_config.py +++ b/metplus/util/met_config.py @@ -841,7 +841,6 @@ def handle_climo_dict(config, app_name, output_dict, sub_groups): 'shape': ('string', 'uppercase,remove_quotes'), }), 'time_interp_method': ('string', 'remove_quotes,uppercase'), - 'match_month': ('bool', 'uppercase'), 'day_interval': ('string', 'remove_quotes,uppercase'), 'hour_interval': ('string', 'remove_quotes,uppercase'), 'file_type': ('string', 'remove_quotes'), diff --git a/metplus/util/time_looping.py b/metplus/util/time_looping.py index 736b359538..b4c70f385c 100644 --- a/metplus/util/time_looping.py +++ b/metplus/util/time_looping.py @@ -92,41 +92,12 @@ def time_generator(config): def get_start_and_end_times(config): - prefix = get_time_prefix(config) - if not prefix: - return None, None - - # get clock time of when the run started - clock_dt = datetime.strptime( - config.getstr('config', 'CLOCK_TIME'), - '%Y%m%d%H%M%S' - ) - - time_format = config.getraw('config', f'{prefix}_TIME_FMT', '') - if not time_format: - config.logger.error(f'Could not read {prefix}_TIME_FMT') - return None, None - - start_string = config.getraw('config', f'{prefix}_BEG') - end_string = config.getraw('config', f'{prefix}_END', start_string) - - start_dt = _get_current_dt(start_string, - time_format, - clock_dt, - config.logger) - - end_dt = _get_current_dt(end_string, - time_format, - clock_dt, - config.logger) - - if not _validate_time_values(start_dt, - end_dt, - get_relativedelta('60'), - prefix, - config.logger): + times = list(time_generator(config)) + if not times or times[0] is None: return None, None + start_dt = times[0][times[0]['loop_by']] + end_dt = times[-1][times[-1]['loop_by']] return start_dt, end_dt diff --git a/metplus/wrappers/command_builder.py b/metplus/wrappers/command_builder.py index 9292bbd73d..15c93f3eb0 100755 --- a/metplus/wrappers/command_builder.py +++ b/metplus/wrappers/command_builder.py @@ -906,10 +906,10 @@ def _check_expected_ensembles(self, input_files): files were found, fill in input_files list with MISSING to allow valid threshold check inside MET tool to work properly. """ - num_expected = self.c_dict['N_MEMBERS'] + num_expected = self.c_dict.get('N_MEMBERS') # if expected members count is unset, skip check - if num_expected == MISSING_DATA_VALUE: + if num_expected is None or num_expected == MISSING_DATA_VALUE: return True num_found = len(input_files) diff --git a/metplus/wrappers/compare_gridded_wrapper.py b/metplus/wrappers/compare_gridded_wrapper.py index 86ef774cdc..7627809720 100755 --- a/metplus/wrappers/compare_gridded_wrapper.py +++ b/metplus/wrappers/compare_gridded_wrapper.py @@ -10,12 +10,9 @@ Condition codes: 0 for success, 1 for failure """ -import os - -from ..util import do_string_sub, ti_calculate +from ..util import do_string_sub from ..util import parse_var_list -from ..util import get_lead_sequence, skip_time, sub_var_list -from ..util import field_read_prob_info, add_field_info_to_time_info +from ..util import field_read_prob_info from . import LoopTimesWrapper '''!@namespace CompareGriddedWrapper @@ -91,110 +88,24 @@ def run_at_time_once(self, time_info): @param time_info dictionary containing timing information """ - var_list = sub_var_list(self.c_dict['VAR_LIST_TEMP'], time_info) - if not var_list and not self.c_dict.get('VAR_LIST_OPTIONAL', False): - self.log_error('No input fields were specified.' - ' [FCST/OBS]_VAR_NAME must be set.') - return - - if self.c_dict.get('ONCE_PER_FIELD', False): - # loop over all fields and levels (and probability thresholds) and - # call the app once for each - for var_info in var_list: - self.clear() - self.c_dict['CURRENT_VAR_INFO'] = var_info - add_field_info_to_time_info(time_info, var_info) - self.run_count += 1 - if not self.find_input_files(time_info): - self.missing_input_count += 1 - continue - self.run_at_time_one_field(time_info, var_info) - else: - # loop over all variables and all them to the field list, - # then call the app once + for file_dict in self.c_dict['ALL_FILES']: + if file_dict is None: continue + self.clear() + var_list = file_dict['var_list'] if var_list: self.c_dict['CURRENT_VAR_INFO'] = var_list[0] - add_field_info_to_time_info(time_info, var_list[0]) - - self.clear() - self.run_count += 1 - if not self.find_input_files(time_info): - self.missing_input_count += 1 - return - self.run_at_time_all_fields(time_info) - - def find_input_files(self, time_info): - # get model from first var to compare - model_path = self.find_model(time_info, - mandatory=True, - return_list=True) - if not model_path: - return None - - # if there is more than 1 file, create file list file - if len(model_path) > 1: - list_filename = (f"{time_info['init_fmt']}_" - f"{time_info['lead_hours']}_" - f"{self.app_name}_fcst.txt") - model_path = self.write_list_file(list_filename, model_path) - else: - model_path = model_path[0] - - self.infiles.append(model_path) - - # get observation to from first var compare - obs_path, offset_time_info = self.find_obs_offset(time_info, - mandatory=True, - return_list=True) - if obs_path is None: - return None - - # if there is more than 1 file, create file list file - if len(obs_path) > 1: - list_filename = (f"{offset_time_info['init_fmt']}_" - f"{offset_time_info['lead_hours']}_" - f"{self.app_name}_obs.txt") - obs_path = self.write_list_file(list_filename, obs_path) - else: - obs_path = obs_path[0] - - self.infiles.append(obs_path) - - return offset_time_info - - def run_at_time_one_field(self, time_info, var_info): - """! Build MET command for a single field for a given - init/valid time and forecast lead combination - Args: - @param time_info dictionary containing timing information - @param var_info object containing variable information - """ - # get field info field a single field to pass to the MET config file - fcst_field_list = self.format_field_info(var_info=var_info, - data_type='FCST') - - obs_field_list = self.format_field_info(var_info=var_info, - data_type='OBS') - - if fcst_field_list is None or obs_field_list is None: - return - - fcst_fields = ','.join(fcst_field_list) - obs_fields = ','.join(obs_field_list) + self.add_to_infiles(file_dict, time_info) - self.format_field('FCST', fcst_fields) - self.format_field('OBS', obs_fields) + runtime_info = file_dict.get('time_info', time_info) + self.run_at_time_all_fields(runtime_info, var_list) - self.process_fields(time_info) - - def run_at_time_all_fields(self, time_info): + def run_at_time_all_fields(self, time_info, var_list): """! Build MET command for all of the field/level combinations for a given init/valid time and forecast lead combination @param time_info dictionary containing timing information + @param var_list list of field info """ - var_list = sub_var_list(self.c_dict['VAR_LIST_TEMP'], time_info) - # set field info fcst_field = self.get_all_field_info(var_list, 'FCST') obs_field = self.get_all_field_info(var_list, 'OBS') @@ -248,7 +159,8 @@ def process_fields(self, time_info): self.set_current_field_config() # set up output dir with time info - if not self.find_and_check_output_file(time_info, is_directory=True): + is_dir = self.c_dict.get('OUTPUT_PATH_IS_DIR', True) + if not self.find_and_check_output_file(time_info, is_directory=is_dir): return # set command line arguments @@ -305,3 +217,44 @@ def handle_interp_dict(self, uses_field=False): items['field'] = ('string', 'remove_quotes') self.add_met_config_dict('interp', items) + + def handle_land_mask(self): + """!Handles the configuration of the 'land_mask' dictionary in the + MET configuration. This function defines the structure and + expected types of various parameters in the 'land_mask' configuration. + Used by PointStat and PairStat wrappers. + """ + self.add_met_config_dict('land_mask', { + 'flag': 'bool', + 'file_name': 'list', + 'field': ('dict', None, { + 'name': 'string', + 'level': 'string', + }), + 'regrid': ('dict', None, { + 'method': ('string', 'remove_quotes'), + 'width': 'int', + }), + 'thresh': 'thresh', + }) + + def handle_topo_mask(self): + """!Handles the configuration of the 'topo_mask' dictionary in the + MET configuration. This function defines the structure and + expected types of various parameters in the 'topo_mask' configuration. + Used by PointStat and PairStat wrappers. + """ + self.add_met_config_dict('topo_mask', { + 'flag': 'bool', + 'file_name': 'list', + 'field': ('dict', None, { + 'name': 'string', + 'level': 'string', + }), + 'regrid': ('dict', None, { + 'method': ('string', 'remove_quotes'), + 'width': 'int', + }), + 'use_obs_thresh': 'thresh', + 'interp_fcst_thresh': 'thresh', + }) diff --git a/metplus/wrappers/ensemble_stat_wrapper.py b/metplus/wrappers/ensemble_stat_wrapper.py index 8121eec8bf..de4ac9cdce 100755 --- a/metplus/wrappers/ensemble_stat_wrapper.py +++ b/metplus/wrappers/ensemble_stat_wrapper.py @@ -169,47 +169,13 @@ def create_c_dict(self): # fill inputs that are not found with fake path to note it is missing c_dict['FCST_FILL_MISSING'] = True - c_dict['OBS_POINT_INPUT_DIR'] = ( - self.config.getdir('OBS_ENSEMBLE_STAT_POINT_INPUT_DIR', '') - ) - - c_dict['OBS_POINT_INPUT_TEMPLATE'] = ( - self.config.getraw('config', - 'OBS_ENSEMBLE_STAT_POINT_INPUT_TEMPLATE') - ) - - c_dict['OBS_GRID_INPUT_DIR'] = ( - self.config.getdir('OBS_ENSEMBLE_STAT_GRID_INPUT_DIR', '') - ) - - c_dict['OBS_GRID_INPUT_TEMPLATE'] = ( - self.config.getraw('config', - 'OBS_ENSEMBLE_STAT_GRID_INPUT_TEMPLATE') - ) - - # The ensemble forecast files input directory and filename templates - c_dict['FCST_INPUT_DIR'] = ( - self.config.getdir('FCST_ENSEMBLE_STAT_INPUT_DIR', '') - ) - - c_dict['FCST_INPUT_TEMPLATE'] = ( - self.config.getraw('config', 'FCST_ENSEMBLE_STAT_INPUT_TEMPLATE') - ) - c_dict['FCST_INPUT_FILE_LIST'] = ( - self.config.getraw('config', 'FCST_ENSEMBLE_STAT_INPUT_FILE_LIST') - ) - if (not c_dict['FCST_INPUT_TEMPLATE'] and - not c_dict['FCST_INPUT_FILE_LIST']): - self.log_error("Must set FCST_ENSEMBLE_STAT_INPUT_TEMPLATE or " - "FCST_ENSEMBLE_STAT_INPUT_FILE_LIST") - - # optional -ens_mean argument path - c_dict['ENS_MEAN_INPUT_DIR'] = ( - self.config.getdir('ENSEMBLE_STAT_ENS_MEAN_INPUT_DIR', '')) - - c_dict['ENS_MEAN_INPUT_TEMPLATE'] = ( - self.config.getraw('config', - 'ENSEMBLE_STAT_ENS_MEAN_INPUT_TEMPLATE')) + self.get_input_templates(c_dict, { + 'CTRL': {'prefix': 'ENSEMBLE_STAT_CTRL', 'required': False}, + 'FCST': {'prefix': 'FCST_ENSEMBLE_STAT', 'required': True}, + 'OBS_POINT': {'prefix': 'OBS_ENSEMBLE_STAT_POINT', 'required': False}, + 'OBS_GRID': {'prefix': 'OBS_ENSEMBLE_STAT_GRID', 'required': False}, + 'ENS_MEAN': {'prefix': 'ENSEMBLE_STAT_ENS_MEAN', 'required': False}, + }) c_dict['OUTPUT_DIR'] = ( self.config.getdir('ENSEMBLE_STAT_OUTPUT_DIR', '') @@ -223,14 +189,6 @@ def create_c_dict(self): 'ENSEMBLE_STAT_OUTPUT_TEMPLATE') ) - # get ctrl (control) template/dir - optional - c_dict['CTRL_INPUT_TEMPLATE'] = ( - self.config.getraw('config', 'ENSEMBLE_STAT_CTRL_INPUT_TEMPLATE') - ) - c_dict['CTRL_INPUT_DIR'] = ( - self.config.getdir('ENSEMBLE_STAT_CTRL_INPUT_DIR', '') - ) - # get climatology config variables self.handle_climo_dict() @@ -367,31 +325,17 @@ def create_c_dict(self): data_type='list', extra_args={'remove_quotes': True}) - self.add_met_config(name='eclv_points', - data_type='float') + self.add_met_config(name='eclv_points', data_type='float') self.add_met_config(name='prob_cat_thresh', data_type='list', extra_args={'remove_quotes': True}) - # signifies that the tool can be run without setting - # field information for fcst and obs - c_dict['VAR_LIST_OPTIONAL'] = True - - # parse var list for ENS fields - c_dict['ENS_VAR_LIST_TEMP'] = parse_var_list( - self.config, - data_type='ENS', - met_tool=self.app_name - ) - # parse optional var list for FCST and/or OBS fields c_dict['VAR_LIST_TEMP'] = parse_var_list( self.config, met_tool=self.app_name ) - # skip RuntimeFreq input file logic - remove once integrated - c_dict['FIND_FILES'] = False return c_dict def get_command(self): @@ -401,7 +345,7 @@ def get_command(self): """ return (f"{self.app_path} -v {self.c_dict['VERBOSITY']}" f" {' '.join(self.infiles)} {self.param}" - f" {' '.join(self.args)} -outdir {self.outdir}") + f"{' '.join(self.args) if self.args else ''} -outdir {self.outdir}") def find_input_files(self, time_info): # get ensemble model files diff --git a/metplus/wrappers/grid_diag_wrapper.py b/metplus/wrappers/grid_diag_wrapper.py index f2228cde14..e7a98a072c 100755 --- a/metplus/wrappers/grid_diag_wrapper.py +++ b/metplus/wrappers/grid_diag_wrapper.py @@ -140,13 +140,10 @@ def get_command(self): return cmd def run_at_time_once(self, time_info): - # subset input files as appropriate - input_list_dict = self.subset_input_files(time_info) - if not input_list_dict: - return - - for input_list_file in input_list_dict.values(): - self.infiles.append(input_list_file) + for file_dict in self.c_dict['ALL_FILES']: + if file_dict is None: continue + self.clear() + self.add_to_infiles(file_dict, time_info) # get output path if not self.find_and_check_output_file(time_info): @@ -205,33 +202,3 @@ def set_command_line_arguments(self, time_info): """ config_file = do_string_sub(self.c_dict['CONFIG_FILE'], **time_info) self.args.append(f"-config {config_file}") - - def get_files_from_time(self, time_info): - """! Create dictionary containing time information (key time_info) and - any relevant files for that runtime. The parent implementation of - this function creates a dictionary and adds the time_info to it. - This wrapper gets all files for the current runtime and adds it to - the dictionary with key 'input' - - @param time_info dictionary containing time information - @returns dictionary containing time_info dict and any relevant - files with a key representing a description of that file - """ - input_files, offset_time_info = self.get_input_files(time_info) - if input_files is None: - return None - - file_dict = {'time_info': time_info.copy()} - for key, value in input_files.items(): - file_dict[key] = value - - return file_dict - - def _update_list_with_new_files(self, time_info, list_to_update): - new_files = self.get_files_from_time(time_info) - if not new_files: - return - if isinstance(new_files, list): - list_to_update.extend(new_files) - else: - list_to_update.append(new_files) diff --git a/metplus/wrappers/grid_stat_wrapper.py b/metplus/wrappers/grid_stat_wrapper.py index 805753aa26..b5b8edff96 100755 --- a/metplus/wrappers/grid_stat_wrapper.py +++ b/metplus/wrappers/grid_stat_wrapper.py @@ -282,6 +282,4 @@ def create_c_dict(self): self.add_met_config(name='seeps_p1_thresh', data_type='string', extra_args={'remove_quotes': True}) - # skip RuntimeFreq input file logic - remove once integrated - c_dict['FIND_FILES'] = False return c_dict diff --git a/metplus/wrappers/mode_wrapper.py b/metplus/wrappers/mode_wrapper.py index f650b26a29..a86835a5dd 100755 --- a/metplus/wrappers/mode_wrapper.py +++ b/metplus/wrappers/mode_wrapper.py @@ -168,30 +168,15 @@ def create_c_dict(self): self.logger.info(f'{tool}_MULTIVAR_LOGIC was set, so running ' 'multi-variate MODE') - # observation input file info - c_dict['OBS_INPUT_DIR'] = ( - self.config.getdir(f'OBS_{tool}_INPUT_DIR', '') - ) - c_dict['OBS_INPUT_TEMPLATE'] = ( - self.config.getraw('config', f'OBS_{tool}_INPUT_TEMPLATE') - ) - if not c_dict['OBS_INPUT_TEMPLATE']: - self.log_error(f'OBS_{tool}_INPUT_TEMPLATE must be set') + self.get_input_templates(c_dict, { + 'FCST': {'prefix': 'FCST_MODE', 'required': True}, + 'OBS': {'prefix': 'OBS_MODE', 'required': True}, + }) c_dict['OBS_INPUT_DATATYPE'] = ( self.config.getstr('config', f'OBS_{tool}_INPUT_DATATYPE', '') ) - # forecast input file info - c_dict['FCST_INPUT_DIR'] = ( - self.config.getdir(f'FCST_{tool}_INPUT_DIR', '') - ) - c_dict['FCST_INPUT_TEMPLATE'] = ( - self.config.getraw('config', f'FCST_{tool}_INPUT_TEMPLATE') - ) - if not c_dict['FCST_INPUT_TEMPLATE']: - self.log_error(f'FCST_{tool}_INPUT_TEMPLATE must be set') - c_dict['FCST_INPUT_DATATYPE'] = ( self.config.getstr('config', f'FCST_{tool}_INPUT_DATATYPE', '') ) @@ -448,18 +433,19 @@ def create_c_dict(self): self.add_met_config(name='multivar_intensity_compare_obs', data_type='list', extra_args={'remove_quotes': True}) - - # skip RuntimeFreq input file logic - remove once integrated - c_dict['FIND_FILES'] = False return c_dict - def run_at_time_one_field(self, time_info, var_info): + def run_at_time_all_fields(self, time_info, var_list): """! Runs mode once for each fcst/obs threshold. Overrides run_at_time_one_field function in compare_gridded_wrapper.py @param time_info dictionary containing timing information - @param var_info object containing variable information + @param var_list list of objects containing variable information """ + if len(var_list) > 1: + return super().run_at_time_all_fields(time_info, var_list) + + var_info = var_list[0] # if no thresholds are specified, run once fcst_thresh_list = [] obs_thresh_list = [] diff --git a/metplus/wrappers/mtd_wrapper.py b/metplus/wrappers/mtd_wrapper.py index c2c19a1e3c..8fb10e37d7 100755 --- a/metplus/wrappers/mtd_wrapper.py +++ b/metplus/wrappers/mtd_wrapper.py @@ -1,4 +1,4 @@ -''' +""" Program Name: mtd_wrapper.py Contact(s): George McCabe Abstract: Runs mode time domain @@ -8,7 +8,7 @@ Input Files: Output Files: Condition codes: 0 for success, 1 for failure -''' +""" import os @@ -62,6 +62,13 @@ def __init__(self, config, instance=None): super().__init__(config, instance=instance) def create_c_dict(self): + """!Create a configuration dictionary for the current execution. + + This function consolidates and prepares configuration settings based + on the inputs and other context. + + @returns dict: The created configuration dictionary for the current run. + """ c_dict = super().create_c_dict() c_dict['VERBOSITY'] = self.config.getstr('config', 'LOG_MTD_VERBOSITY', c_dict['VERBOSITY']) @@ -86,8 +93,8 @@ def create_c_dict(self): self.add_met_config(name='min_volume', data_type='int') input_info = { - 'FCST': {'prefix': 'FCST_MTD', 'required': True}, - 'OBS': {'prefix': 'OBS_MTD', 'required': True}, + 'FCST': {'prefix': 'FCST_MTD', 'required': False}, + 'OBS': {'prefix': 'OBS_MTD', 'required': False}, } c_dict['SINGLE_RUN'] = ( @@ -134,7 +141,17 @@ def create_c_dict(self): return c_dict def read_field_values(self, c_dict, read_type, write_type): + """!Read FCST or OBS read type field values and sets FCST or OBS write + type variables used in the wrapped MET config file. + If single input mode is specified, the FCST and OBS values are set using + values from that single input type. This function handles this situation. + + @param c_dict (dict) dictionary to set values from the METplusConfig + @param read_type (str) input type to read values from, e.g. FCST or OBS + @param write_type (str) input type to write values to, e.g. FCST or OBS + @returns None + """ c_dict[f'{write_type}_INPUT_DATATYPE'] = ( self.config.getstr('config', f'{read_type}_MTD_INPUT_DATATYPE', '') ) @@ -153,6 +170,15 @@ def read_field_values(self, c_dict, read_type, write_type): 'MTD_CONV_THRESH']) def run_at_time_once(self, time_info): + """!Process data for a single time step. + + This method calculates the valid time for the given time information, + prepares input files (forecast and observation) for the current step, + and processes fields for all thresholds. If input files are missing, + it handles them as per configuration settings. + + @param time_info (dict) Information about the current time being processed. + """ # calculate valid based on first forecast lead lead_seq = get_lead_sequence(self.config, time_info) if not lead_seq: @@ -164,37 +190,10 @@ def run_at_time_once(self, time_info): # get formatted time to use to name file list files time_fmt = f"{first_valid_time_info['valid_fmt']}" - # loop through the files found for each field (var_info) for file_dict in self.c_dict['ALL_FILES']: - var_info = file_dict['var_info'] - inputs = {} - for data_type in ('FCST', 'OBS'): - file_list = file_dict.get(data_type) - if not file_list: - continue - if len(file_list) == 1: - if not os.path.exists(file_list[0]): - self.log_error(f'{data_type} file does not exist: ' - f'{file_list[0]}') - continue - inputs[data_type] = file_list[0] - continue - - file_ext = self.check_for_python_embedding(data_type, var_info) - if not file_ext: - continue + var_info, inputs = self._prepare_inputs(file_dict, time_fmt) - dt = 'single' if self.c_dict['SINGLE_RUN'] else data_type - outfile = f"{time_fmt}_mtd_{dt.lower()}_{file_ext}.txt" - inputs[data_type] = self.write_list_file(outfile, file_list) - - if not inputs or (len(inputs) < 2 and not self.c_dict['SINGLE_RUN']): - self.missing_input_count += 1 - msg = 'Could not find all required inputs files' - if self.c_dict['ALLOW_MISSING_INPUTS']: - self.logger.warning(msg) - else: - self.log_error(msg) + if not self._validate_inputs(inputs): continue arg_dict = { @@ -204,113 +203,125 @@ def run_at_time_once(self, time_info): self.process_fields_one_thresh(first_valid_time_info, var_info, **arg_dict) - def process_fields_one_thresh(self, first_valid_time_info, var_info, - model_path, obs_path): - """! For each threshold, set up environment variables and run mode - Args: - @param first_valid_time_info dictionary containing timing information - @param var_info object containing variable information - @param model_path forecast file list path - @param obs_path observation file list path - """ - fcst_field_list = [] - obs_field_list = [] - - if model_path: - fcst_thresh_list = var_info['fcst_thresh'] + def _prepare_inputs(self, file_dict, time_fmt): + """!Prepare input files for observation and forecast. - # if probabilistic forecast and no thresholds specified, - # error and skip - if self.c_dict['FCST_IS_PROB'] and not fcst_thresh_list: - self.logger.error("Must specify thresholds for " - "probabilistic forecast data") - return - - # if no thresholds are specified, run once - if not fcst_thresh_list: - fcst_thresh_list = [""] - - # loop over thresholds and build field list with one thresh per item - for fcst_thresh in fcst_thresh_list: - fcst_field = ( - self.get_field_info(v_name=var_info['fcst_name'], - v_level=var_info['fcst_level'], - v_extra=var_info['fcst_extra'], - v_thresh=[fcst_thresh], - d_type='FCST') - ) + @param file_dict (dict): Dictionary with file information. + @param time_fmt (str): Formatted time string. + @returns tuple: A tuple containing var_info and inputs dictionary. + """ + var_info = file_dict['var_list'][0] + inputs = {} - if fcst_field is None: - self.log_error("No forecast fields found") - return + for data_type in ('FCST', 'OBS'): + file_list = file_dict.get(data_type) + if not file_list: + continue - fcst_field_list.extend(fcst_field) + if len(file_list) == 1: + if not os.path.exists(file_list[0]): + self.log_error(f'{data_type} file does not exist: {file_list[0]}') + continue + inputs[data_type] = file_list[0] + continue - if obs_path: - obs_thresh_list = var_info['obs_thresh'] + file_ext = self.check_for_python_embedding(data_type, var_info) + if not file_ext: + continue - if self.c_dict['OBS_IS_PROB'] and not obs_thresh_list: - self.logger.error("Must specify thresholds for " - "probabilistic obs data") - return + dt = 'single' if self.c_dict['SINGLE_RUN'] else data_type + outfile = f"{time_fmt}_mtd_{dt.lower()}_{file_ext}.txt" + inputs[data_type] = self.write_list_file(outfile, file_list) - # if no thresholds are specified, run once - if not obs_thresh_list: - obs_thresh_list = [""] + return var_info, inputs - # loop over thresholds and build field list w/ one thresh per item - for obs_thresh in obs_thresh_list: - obs_field = self.get_field_info(v_name=var_info['obs_name'], - v_level=var_info['obs_level'], - v_extra=var_info['obs_extra'], - v_thresh=[obs_thresh], - d_type='OBS') + def _validate_inputs(self, inputs): + """!Validate if sufficient input files are available. - if obs_field is None: - self.log_error("No observation fields found") - return + @param inputs (dict): Dictionary with input file paths. + @returns bool: True if inputs are valid, False otherwise. + """ + if not inputs or (len(inputs) < 2 and not self.c_dict['SINGLE_RUN']): + self.missing_input_count += 1 + msg = 'Could not find all required input files' + if self.c_dict['ALLOW_MISSING_INPUTS']: + self.logger.warning(msg) + else: + self.log_error(msg) + return False - obs_field_list.extend(obs_field) + return True - # if FCST is not set, set it to the OBS field list so - # the lists are the same length - if not fcst_field_list: - fcst_field_list = obs_field_list + def process_fields_one_thresh(self, first_valid_time_info, var_info, + model_path, obs_path): + """!Process fields for a specific threshold. - else: - # if OBS is not set, set it to the FCST field list so - # the lists are the same length - obs_field_list = fcst_field_list + This function performs processing tasks for fields at the given + threshold level. It leverages forecast and observation data + and applies the required transformations and operations. - # loop through fields and call MTD - for fcst_field, obs_field in zip(fcst_field_list, obs_field_list): - self.format_field('FCST', fcst_field, is_list=False) - self.format_field('OBS', obs_field, is_list=False) + @param first_valid_time_info (dict) The valid time details for the current threshold. + @param var_info (str) Information about the variable being processed. + @param model_path (str) The path to the forecast file. + @param obs_path (str) The path to the observation file. + """ + fcst_field_list = self._process_field_list( + var_info, 'FCST', model_path, self.c_dict.get('FCST_IS_PROB') + ) + obs_field_list = self._process_field_list( + var_info, 'OBS', obs_path, self.c_dict.get('OBS_IS_PROB') + ) - self.param = do_string_sub(self.c_dict['CONFIG_FILE'], - **first_valid_time_info) + fcst_field_list, obs_field_list = self._sync_field_lengths( + fcst_field_list, obs_field_list + ) - self.set_current_field_config(var_info) - self.set_environment_variables(first_valid_time_info) + self._run_once_per_field( + fcst_field_list, obs_field_list, first_valid_time_info, var_info, + model_path, obs_path + ) - if not self.find_and_check_output_file(first_valid_time_info, - is_directory=True): - return + def _process_field_list(self, var_info, data_type, path, is_probabilistic): + """!Process the field information for each threshold. - if self.c_dict['SINGLE_RUN']: - if self.c_dict.get('SINGLE_DATA_SRC') == 'OBS': - self.infiles.append(obs_path) - else: - self.infiles.append(model_path) - else: - self.infiles.extend([model_path, obs_path]) + This function takes variable information, a data type, a path, and a flag indicating + if the variable is probabilistic. It loops over each threshold in the field + information and builds a formatted list of field info strings for each threshold. - self.build() + @param var_info (dict) Information about the variable being processed. + @param data_type (str) The type of data (e.g., 'FCST' or 'OBS'). + @param path (str or None) The path to the field file, or None if not applicable. + @param is_probabilistic (bool) Whether the field represents probabilistic data. + @returns list: A list of formatted field info strings for each threshold. + """ + if not path: + return [] + + field_list = [] + thresh_list = var_info[f'{data_type.lower()}_thresh'] + if is_probabilistic and not thresh_list: + self.logger.error(f"Must specify thresholds for probabilistic {data_type} data") + return [] + if not thresh_list: + thresh_list = [""] + for thresh in thresh_list: + field = self.get_field_info( + v_name=var_info[f'{data_type.lower()}_name'], + v_level=var_info[f'{data_type.lower()}_level'], + v_extra=var_info[f'{data_type.lower()}_extra'], + v_thresh=[thresh], + d_type=data_type + ) + if not field: + self.log_error(f"No {data_type.lower()} fields found") + return [] + field_list.extend(field) + return field_list def get_command(self): - """! Builds the command to run the MET application + """!Builds the command to run the mtd application @rtype string - @return Returns a MET command with arguments that you can run + @return string containing mtd command with arguments """ cmd = f"{self.app_path} -v {self.c_dict['VERBOSITY']} " @@ -328,3 +339,66 @@ def get_command(self): cmd += '-outdir {}'.format(self.outdir) return cmd + + def get_files_from_time(self, time_info): + """!Retrieve input files based on the provided time. + + This method overrides the `RuntimeFreqWrapper` class's method. + It implements logic specific to fetching required input files within + a defined time window and filters files based on input requirements. + + @param time_info (dict) Dictionary containing time-related metadata. + @returns list: A list of input files applicable for the given time. + """ + file_dict_list = super().get_files_from_time(time_info) + if not self.c_dict['SINGLE_RUN']: + for file_dict in file_dict_list: + if file_dict.get('OBS') is None or file_dict.get('FCST') is None: + file_dict['OBS'] = None + file_dict['FCST'] = None + return file_dict_list + + def _run_once_per_field(self, fcst_field_list, obs_field_list, time_info, var_info, model_path, obs_path): + """!Loop over forecast and observation field lists. Build and run + mtd command for each. The same input files are used for each run. + + @param fcst_field_list (list): List of forecast field strings. + @param obs_field_list (list): List of observation field strings. + @param time_info (dict): Dictionary containing time information. + @param var_info (str): Dictionary containing field information. + @param model_path (str): Path to forecast file. + @param obs_path (str): Path to observation file. + """ + for fcst_field, obs_field in zip(fcst_field_list, obs_field_list): + self.format_field('FCST', fcst_field, is_list=False) + self.format_field('OBS', obs_field, is_list=False) + self.param = do_string_sub(self.c_dict['CONFIG_FILE'], **time_info) + self.set_current_field_config(var_info) + self.set_environment_variables(time_info) + if not self.find_and_check_output_file(time_info, is_directory=True): + return + if self.c_dict['SINGLE_RUN']: + if self.c_dict.get('SINGLE_DATA_SRC') == 'OBS': + self.infiles.append(obs_path) + else: + self.infiles.append(model_path) + else: + self.infiles.extend([model_path, obs_path]) + self.build() + + @staticmethod + def _sync_field_lengths(fcst_field_list, obs_field_list): + """!Synchronizes the lengths of two field lists by ensuring that if one + list is empty, it is replaced with the contents of the other. This + operation is performed in-place on the input lists. + + @param fcst_field_list (list): The forecast field list to synchronize. + @param obs_field_list (list): The observation field list to synchronize. + @returns tuple: A tuple containing the synchronized forecast and + observation field lists. + """ + if not fcst_field_list: + fcst_field_list = obs_field_list + elif not obs_field_list: + obs_field_list = fcst_field_list + return fcst_field_list, obs_field_list diff --git a/metplus/wrappers/pair_stat_wrapper.py b/metplus/wrappers/pair_stat_wrapper.py new file mode 100755 index 0000000000..5ffea15c39 --- /dev/null +++ b/metplus/wrappers/pair_stat_wrapper.py @@ -0,0 +1,248 @@ +""" +Program Name: pair_stat_wrapper.py +Contact(s): George McCabe +Abstract: Wrapper to MET pair_stat +History Log: Initial version +Usage: pair_stat_wrapper.py +Parameters: None +Input Files: netCDF data files +Output Files: ascii files +Condition codes: 0 for success, 1 for failure +""" + +import os + +from ..util import getlistint +from . import CompareGriddedWrapper + + +class PairStatWrapper(CompareGriddedWrapper): + """! Wrapper to the MET tool, Pair-Stat.""" + RUNTIME_FREQ_DEFAULT = 'RUN_ONCE_FOR_EACH' + RUNTIME_FREQ_SUPPORTED = ['RUN_ONCE_FOR_EACH'] + + WRAPPER_ENV_VAR_KEYS = [ + 'METPLUS_MODEL', + 'METPLUS_DESC', + 'METPLUS_FCST_FIELD', + 'METPLUS_FCST_FILE_TYPE', + 'METPLUS_FCST_CLIMO_MEAN_DICT', + 'METPLUS_FCST_CLIMO_STDEV_DICT', + 'METPLUS_OBS_FIELD', + 'METPLUS_OBS_FILE_TYPE', + 'METPLUS_OBS_CLIMO_MEAN_DICT', + 'METPLUS_OBS_CLIMO_STDEV_DICT', + 'METPLUS_CENSOR_THRESH', + 'METPLUS_CENSOR_VAL', + 'METPLUS_CAT_THRESH', + 'METPLUS_CNT_THRESH', + 'METPLUS_CNT_LOGIC', + 'METPLUS_WIND_THRESH', + 'METPLUS_WIND_LOGIC', + 'METPLUS_MPR_COLUMN', + 'METPLUS_MPR_THRESH', + 'METPLUS_MPR_STR_INC', + 'METPLUS_MPR_STR_EXC', + 'METPLUS_ECLV_POINTS', + 'METPLUS_HSS_EC_VALUE', + 'METPLUS_RANK_CORR_FLAG', + 'METPLUS_CLIMO_MEAN_DICT', + 'METPLUS_CLIMO_STDEV_DICT', + 'METPLUS_CLIMO_CDF_DICT', + 'METPLUS_OBS_WINDOW_DICT', + 'METPLUS_MASK_DICT', + 'METPLUS_CI_ALPHA', + 'METPLUS_BOOT_DICT', + 'METPLUS_SEEPS_P1_THRESH', + 'METPLUS_OUTPUT_FLAG_DICT', + 'METPLUS_POINT_WEIGHT_FLAG', + ] + + OUTPUT_FLAGS = [ + 'fho', + 'ctc', + 'cts', + 'mctc', + 'mcts', + 'cnt', + 'sl1l2', + 'sal1l2', + 'vl1l2', + 'val1l2', + 'vcnt', + 'pct', + 'pstd', + 'pjc', + 'prc', + 'eclv', + 'mpr', + 'seeps', + 'seeps_mpr', + ] + + def __init__(self, config, instance=None): + self.app_name = 'pair_stat' + self.app_path = os.path.join(config.getdir('MET_BIN_DIR', ''), + self.app_name) + super().__init__(config, instance=instance) + + def create_c_dict(self): + """!Create a dictionary that holds all the values set in the + METplus config file for the PairStat wrapper. + + @returns c_dict - A dictionary containing the key-value pairs set + in the METplus configuration file. + """ + c_dict = super().create_c_dict() + c_dict['VERBOSITY'] = ( + self.config.getstr('config', 'LOG_PAIR_STAT_VERBOSITY', + c_dict['VERBOSITY']) + ) + + # set variable to override how output path is handled + c_dict['OUTPUT_PATH_IS_DIR'] = False + + c_dict['OFFSETS'] = getlistint( + self.config.getstr('config', 'PAIR_STAT_OFFSETS', '0') + ) + + self.get_input_templates(c_dict, { + 'PAIRS': {'prefix': 'PAIR_STAT_PAIRS', 'required': True}, + }) + + c_dict['PAIRS_INPUT_DATATYPE'] = ( + self.config.getstr('config', 'PAIR_STAT_PAIRS_INPUT_DATATYPE', '') + ) + + c_dict['OUTPUT_DIR'] = self.config.getdir('PAIR_STAT_OUTPUT_DIR', '') + c_dict['OUTPUT_TEMPLATE'] = self.config.getraw('config', 'PAIR_STAT_OUTPUT_TEMPLATE') + + c_dict['FORMAT'] = self.config.getraw('config', 'PAIR_STAT_FORMAT') + if not c_dict['FORMAT']: + self.log_error('Must set PAIR_STAT_FORMAT') + + # get the MET config file path or use default + c_dict['CONFIG_FILE'] = self.get_config_file('PairStatConfig_wrapped') + + self.add_met_config(name='censor_thresh', data_type='list', + extra_args={'remove_quotes': True}) + + self.add_met_config(name='censor_val', data_type='list', + extra_args={'remove_quotes': True}) + + self.add_met_config(name='cat_thresh', data_type='list', + extra_args={'remove_quotes': True}) + + self.add_met_config(name='cnt_thresh', data_type='list', + extra_args={'remove_quotes': True}) + + self.add_met_config(name='cnt_logic', data_type='string', + extra_args={'remove_quotes': True}) + + self.add_met_config(name='wind_thresh', data_type='list', + extra_args={'remove_quotes': True}) + + self.add_met_config(name='wind_logic', data_type='string', + extra_args={'remove_quotes': True}) + + self.add_met_config(name='mpr_column', data_type='list') + + self.add_met_config(name='mpr_thresh', data_type='list', + extra_args={'remove_quotes': True}) + + self.add_met_config(name='mpr_str_inc', data_type='list') + + self.add_met_config(name='mpr_str_exc', data_type='list') + + self.add_met_config(name='eclv_points', data_type='float') + self.add_met_config(name='hss_ec_value', data_type='float') + self.add_met_config(name='rank_corr_flag', data_type='bool') + + self.handle_climo_dict() + self.handle_climo_cdf_dict() + self.add_met_config(name='message_type_group_map', data_type='list', + extra_args={'remove_quotes': True}) + + self.add_met_config_window('obs_window') + self.handle_mask(get_point=True) + + self.add_met_config(name='ci_alpha', + data_type='list', + extra_args={'remove_quotes': True}) + + self.add_met_config_dict('boot', { + 'interval': ('string', 'remove_quotes'), + 'rep_prop': 'float', + 'n_rep': 'int', + 'rng': 'string', + 'seed': 'string', + }) + + self.add_met_config(name='seeps_p1_thresh', data_type='string', + extra_args={'remove_quotes': True}) + + self.add_met_config(name='file_type', data_type='string', + env_var_name='FCST_FILE_TYPE', + metplus_configs=['PAIR_STAT_FCST_FILE_TYPE', + 'FCST_PAIR_STAT_FILE_TYPE', + 'PAIR_STAT_FILE_TYPE'], + extra_args={'remove_quotes': True, + 'uppercase': True}) + + self.add_met_config(name='file_type', data_type='string', + env_var_name='OBS_FILE_TYPE', + metplus_configs=['PAIR_STAT_OBS_FILE_TYPE', + 'OBS_PAIR_STAT_FILE_TYPE', + 'PAIR_STAT_FILE_TYPE'], + extra_args={'remove_quotes': True, + 'uppercase': True}) + + c_dict['FCST_PROB_THRESH'] = ( + self.config.getstr('config', 'FCST_PAIR_STAT_PROB_THRESH', '==0.1') + ) + c_dict['OBS_PROB_THRESH'] = ( + self.config.getstr('config', 'OBS_PAIR_STAT_PROB_THRESH', '==0.1') + ) + + c_dict['ONCE_PER_FIELD'] = ( + self.config.getbool('config', 'PAIR_STAT_ONCE_PER_FIELD', False) + ) + + self.handle_flags('output') + + self.handle_interp_dict() + + self.add_met_config(name='point_weight_flag', + data_type='string', + extra_args={'remove_quotes': True, + 'uppercase': True}) + + if not c_dict['OUTPUT_DIR']: + self.log_error('Must set PAIR_STAT_OUTPUT_DIR in config file') + + return c_dict + + def set_command_line_arguments(self, time_info): + """!Set command line arguments in self.args to add to command to run. + This function is overwritten from CompareGridded wrapper. + + @param time_info dictionary with time information + """ + # call CompareGridded function + super().set_command_line_arguments(time_info) + + # replace field with pairs in the field info + for data_type in ('FCST', 'OBS'): + key = f'METPLUS_{data_type}_FIELD' + self.env_var_dict[key] = self.env_var_dict[key].replace('field =', + 'pairs =') + + def get_command(self): + """!Builds the command to run pair_stat + @rtype string + @return Returns a pair_stat command with arguments that you can run + """ + return (f"{self.app_path} {' '.join(self.infiles)}" + f" -format {self.c_dict['FORMAT']} -config {self.param}" + f"{' ' + ' '.join(self.args) if self.args else ''}" + f" -out {self.get_output_path()} -v {self.c_dict['VERBOSITY']}") diff --git a/metplus/wrappers/point_stat_wrapper.py b/metplus/wrappers/point_stat_wrapper.py index bce6459366..f652f597f0 100755 --- a/metplus/wrappers/point_stat_wrapper.py +++ b/metplus/wrappers/point_stat_wrapper.py @@ -13,7 +13,6 @@ import os from ..util import getlistint -from ..util import time_util from ..util import do_string_sub from . import CompareGriddedWrapper @@ -129,6 +128,8 @@ def create_c_dict(self): c_dict['VERBOSITY']) ) c_dict['ALLOW_MULTIPLE_FILES'] = True + c_dict['SUPPORTS_FILE_LIST'] = False + c_dict['OFFSETS'] = getlistint( self.config.getstr('config', 'POINT_STAT_OFFSETS', '0') ) @@ -187,34 +188,8 @@ def create_c_dict(self): self.handle_climo_cdf_dict() - self.add_met_config_dict('land_mask', { - 'flag': 'bool', - 'file_name': 'list', - 'field': ('dict', None, { - 'name': 'string', - 'level': 'string', - }), - 'regrid': ('dict', None, { - 'method': ('string', 'remove_quotes'), - 'width': 'int', - }), - 'thresh': 'thresh', - }) - - self.add_met_config_dict('topo_mask', { - 'flag': 'bool', - 'file_name': 'list', - 'field': ('dict', None, { - 'name': 'string', - 'level': 'string', - }), - 'regrid': ('dict', None, { - 'method': ('string', 'remove_quotes'), - 'width': 'int', - }), - 'use_obs_thresh': 'thresh', - 'interp_fcst_thresh': 'thresh', - }) + self.handle_land_mask() + self.handle_topo_mask() c_dict['OBS_VALID_BEG'] = ( self.config.getraw('config', 'POINT_STAT_OBS_VALID_BEG', '') @@ -305,8 +280,6 @@ def create_c_dict(self): if not c_dict['OUTPUT_DIR']: self.log_error('Must set POINT_STAT_OUTPUT_DIR in config file') - # skip RuntimeFreq input file logic - remove once integrated - c_dict['FIND_FILES'] = False return c_dict def set_command_line_arguments(self, time_info): diff --git a/metplus/wrappers/runtime_freq_wrapper.py b/metplus/wrappers/runtime_freq_wrapper.py index 89f0ec7f56..2cd6cccbe3 100755 --- a/metplus/wrappers/runtime_freq_wrapper.py +++ b/metplus/wrappers/runtime_freq_wrapper.py @@ -438,7 +438,7 @@ def run_at_time_once(self, time_info): return self.build() def get_all_files(self, custom=None): - """! Get all files that can be processed with the app. + """!Get all files that can be processed with the app. @returns A dictionary where the key is the type of data that was found, i.e. fcst or obs, and the value is a list of files that fit in that category @@ -463,23 +463,52 @@ def get_all_files(self, custom=None): time_input, wildcard_if_empty=use_wildcard) lead_files = self.get_all_files_from_leads(time_input, lead_seq) - all_files.extend(lead_files) + self._update_list_with_new_files(lead_files, all_files) return all_files def _check_input_files(self): if self.c_dict['ALL_FILES'] is True: return True - self.run_count += 1 - if not self.c_dict['ALL_FILES'] and self.app_name != 'user_script': - self.missing_input_count += 1 - msg = 'A problem occurred trying to obtain input files' - if self.c_dict['ALLOW_MISSING_INPUTS']: - self.logger.warning(msg) - else: - self.log_error(msg) - return False - return True + + num_missing = 0 + + if self.c_dict['ALL_FILES']: + num_runs = len(self.c_dict['ALL_FILES']) + else: + num_runs = 1 + num_missing = 1 + self.run_count += num_runs + + for file_dict in self.c_dict['ALL_FILES']: + if self._has_missing_input_files(file_dict): + num_missing += 1 + + if self.app_name == 'user_script' or not num_missing: + return True + + self.missing_input_count += num_missing + msg = 'A problem occurred trying to obtain input files' + if self.c_dict['ALLOW_MISSING_INPUTS']: + self.logger.warning(msg) + else: + # increment error counter for GridDiag because it does not log error for each missing file + if self.app_name in ('grid_diag', 'series_analysis'): + self.errors += 1 + self.logger.error(msg) + return False + + @staticmethod + def _has_missing_input_files(file_dict): + if file_dict is None: + return True + + for key, value in file_dict.items(): + if key in ('var_list', 'time_info'): continue + if value is None or value == ['missing'] or all(item == 'missing' for item in value): + return True + + return False def get_all_files_from_leads(self, time_input, lead_seq): if not self.c_dict.get('FIND_FILES', True): @@ -497,7 +526,10 @@ def get_all_files_from_leads(self, time_input, lead_seq): if skip_time(time_info, self.c_dict): continue - self._update_list_with_new_files(time_info, lead_files) + new_files = self.get_files_from_time(time_info) + if not new_files: + continue + self._update_list_with_new_files(new_files, lead_files) return lead_files @@ -505,7 +537,7 @@ def get_all_files_for_lead(self, time_input): if not self.c_dict.get('FIND_FILES', True): return True - new_files = [] + all_files = [] for run_time in time_generator(self.config): if run_time is None: continue @@ -521,91 +553,115 @@ def get_all_files_for_lead(self, time_input): if skip_time(time_info, self.c_dict): continue - self._update_list_with_new_files(time_info, new_files) + new_files = self.get_files_from_time(time_info) + if not new_files: + continue + self._update_list_with_new_files(new_files, all_files) - return new_files + return all_files def get_all_files_for_each(self, time_info): if not self.c_dict.get('FIND_FILES', True): return True all_files = [] - self._update_list_with_new_files(time_info, all_files) + new_files = self.get_files_from_time(time_info) + if not new_files: + return [] + self._update_list_with_new_files(new_files, all_files) return all_files + def _get_var_lists(self, time_info): + var_list_temp = self.c_dict.get('VAR_LIST_TEMP') + # if VAR_LIST_TEMP was not set in c_dict, return a list with None + if var_list_temp is None: + return [None] + + var_list = sub_var_list(var_list_temp, time_info) + # if var list was not specified, log error and return empty list + if not var_list: + self.log_error('No input fields were specified.' + ' [FCST/OBS]_VAR_NAME must be set.') + return [] + + # if running once per field, return a list of lists each with 1 var + if self.c_dict.get('ONCE_PER_FIELD', False): + return [[item] for item in var_list] + + # if running once for all fields, return a list with 1 list of vars + return [var_list] + def get_files_from_time(self, time_info): - """! Create dictionary containing time information (key time_info) and - any relevant files for that runtime. The parent implementation of - this function creates a dictionary and adds the time_info to it. - This wrapper gets all files for the current runtime and adds it to - the dictionary with keys 'FCST' and 'OBS' + """!Create dictionary containing time information (key time_info) and + any relevant files for that runtime. The parent implementation of + this function creates a dictionary and adds the time_info to it. + This wrapper gets all files for the current runtime and adds it to + the dictionary with keys 'FCST' and 'OBS' @param time_info dictionary containing time information @returns dictionary containing time_info dict and any relevant files with a key representing a description of that file """ - var_list = [None] - if self.c_dict.get('ONCE_PER_FIELD', False): - var_list = sub_var_list(self.c_dict.get('VAR_LIST_TEMP'), time_info) + var_lists = self._get_var_lists(time_info) + if not var_lists: + return [] + + allow_missing = self.c_dict.get('ALLOW_MISSING_INPUTS', False) # create a dictionary for each field (var) with time_info and files file_dict_list = [] - for var_info in var_list: - file_dict = {'var_info': var_info} - if var_info: - add_field_info_to_time_info(time_info, var_info) + for var_list in var_lists: + file_dict = {'var_list': var_list} + current_var_info = var_list[0] if var_list else None + if current_var_info: + add_field_info_to_time_info(time_info, current_var_info) input_files, offset_time_info = ( - self.get_input_files(time_info, fill_missing=True) + self.get_input_files(time_info, fill_missing=allow_missing) ) file_dict['time_info'] = offset_time_info.copy() - # only add all input files if none are missing - no_missing = True if input_files: for key, value in input_files.items(): - if 'missing' in value: - no_missing = False file_dict[key] = value - if no_missing: - file_dict_list.append(file_dict) - return file_dict_list + file_dict_list.append(file_dict) - def _update_list_with_new_files(self, time_info, list_to_update): - new_files = self.get_files_from_time(time_info) - if not new_files: - return + return file_dict_list + def _update_list_with_new_files(self, new_files, list_to_update): if not isinstance(new_files, list): new_files = [new_files] # if list to update is empty, copy new items into list if not list_to_update: - for new_file in new_files: - list_to_update.append(new_file.copy()) + files_to_add = [item.copy() if item is not None else None for item in new_files] + list_to_update.extend(files_to_add) return # if list to update is not empty, add new files to each file list, # make sure new files correspond to the correct field (var) assert len(list_to_update) == len(new_files) for new_file, existing_item in zip(new_files, list_to_update): - assert new_file.get('var_info') == existing_item.get('var_info') + assert new_file.get('var_list') == existing_item.get('var_list') for key, value in new_file.items(): - if key == 'var_info' or key == 'time_info': + if key == 'var_list' or key == 'time_info' or value is None: continue + + if existing_item[key] is None: + existing_item[key] = [] existing_item[key].extend(value) @staticmethod def compare_time_info(runtime, filetime): - """! Compare current runtime dictionary to current file time dictionary - If runtime value for init, valid, or lead is not a wildcard and - it doesn't match the file's time value, return False. Otherwise - return True. - - @param runtime dictionary containing time info for current runtime - @param filetime dictionary containing time info for current file - @returns True if file's info matches the requirements for current - runtime or False if not. + """!Compare current runtime dictionary to current file time dictionary + If runtime value for init, valid, or lead is not a wildcard and + it doesn't match the file's time value, return False. Otherwise + return True. + + @param runtime dictionary containing time info for current runtime + @param filetime dictionary containing time info for current file + @returns True if file's info matches the requirements for current + runtime or False if not. """ # False if init/valid is not wildcard and the file time doesn't match for time_val in ['init', 'valid']: @@ -629,13 +685,13 @@ def compare_time_info(runtime, filetime): return runtime_lead == filetime_lead def get_input_files(self, time_info, fill_missing=False): - """! Loop over list of input templates and find files for each + """!Loop over list of input templates and find files for each - @param time_info time dictionary to use for string substitution - @param fill_missing if True, add a placeholder if a file is not - found. Defaults to False. - @returns Dictionary of key input number and value is list of - input file list if all files were found, None if not. + @param time_info time dictionary to use for string substitution + @param fill_missing if True, add a placeholder if a file is not + found. Defaults to False. + @returns Dictionary of key input number and value is list of + input file list if all files were found, None if not. """ all_input_files = {} if not self.c_dict.get('TEMPLATE_DICT'): @@ -663,34 +719,51 @@ def get_input_files(self, time_info, fill_missing=False): mandatory=mandatory) if not input_files: - if not fill_missing: - continue - # if no files are found and fill missing is set, add 'missing' - input_files = ['missing'] + input_files = ['missing'] if fill_missing else None + + elif label == 'FCST': + input_files = self._handle_fcst_inputs_for_ensemble(all_input_files, input_files) all_input_files[label] = input_files # return None if no matching input files were found if not all_input_files: - return None, None + return None, offset_time_info return all_input_files, offset_time_info + def _handle_fcst_inputs_for_ensemble(self, all_input_files, fcst_files): + # check if control file is found in ensemble list + ctrl_file = all_input_files.get('CTRL') + if ctrl_file in fcst_files: + # warn and remove control file if found + self.logger.warning(f"Control file found in ensemble list: " + f"{ctrl_file}. Removing from list.") + fcst_files.remove(ctrl_file) + + # check EnsembleStat number of files + if self.env_var_dict.get('METPLUS_ENS_MEMBER_IDS'): + self.logger.debug('Skipping logic to fill file list with MISSING') + elif not self._check_expected_ensembles(fcst_files): + fcst_files = None + + return fcst_files + def subset_input_files(self, time_info, output_dir=None, leads=None, force_list=False): - """! Obtain a subset of input files from the c_dict ALL_FILES based on - the time information for the current run. - - @param time_info dictionary containing time information - @param output_dir (optional) directory to write file list files. - If no directory is provided, files are written to staging dir - @param leads (optional) list of forecast leads to consider - @param force_list (optional) boolean - if True, write a file list - text file even only 1 file was found. Defaults to False. - @returns dictionary with keys of the input identifier and the - value is the path to a ascii file containing the list of files - or None if could not find any files + """!Obtain a subset of input files from the c_dict ALL_FILES based on + the time information for the current run. + + @param time_info dictionary containing time information + @param output_dir (optional) directory to write file list files. + If no directory is provided, files are written to staging dir + @param leads (optional) list of forecast leads to consider + @param force_list (optional) boolean - if True, write a file list + text file even only 1 file was found. Defaults to False. + @returns dictionary with keys of the input identifier and the + value is the path to a ascii file containing the list of files + or None if could not find any files """ all_input_files = {} if not self.c_dict.get('ALL_FILES') or self.c_dict.get('ALL_FILES') is True: @@ -709,6 +782,7 @@ def subset_input_files(self, time_info, output_dir=None, leads=None, # loop over all inputs and write a file list file for each list_file_dict = {} for identifier, input_files in all_input_files.items(): + if identifier.endswith('time_info'): continue if len(input_files) == 1 and not force_list: list_file_dict[identifier] = input_files[0] continue @@ -743,7 +817,7 @@ def _add_files_that_match_time(self, all_input_files, time_info, file_dict, lead for input_key in file_dict: # skip time info key - if input_key == 'time_info': + if input_key == 'time_info' or input_key == 'var_list': continue if input_key not in all_input_files: @@ -752,30 +826,23 @@ def _add_files_that_match_time(self, all_input_files, time_info, file_dict, lead all_input_files[input_key].extend(file_dict[input_key]) def get_list_file_name(self, time_info, identifier): - """! Build name of ascii file that contains a list of files to process. - If wildcard is set for init, valid, or lead then use the text ALL - in the filename. + """!Build name of ascii file that contains a list of files to process. + If wildcard is set for init, valid, or lead then use the text ALL + in the filename. @param time_info dictionary containing time information @param identifier string to identify which input is used @returns filename i.e. {app_name}_files_{identifier}_init_{init}_valid_{valid}_lead_{lead}.txt """ - if time_info['init'] == '*': - init = 'ALL' - else: - init = time_info['init'].strftime('%Y%m%d%H%M%S') - - if time_info['valid'] == '*': - valid = 'ALL' - else: - valid = time_info['valid'].strftime('%Y%m%d%H%M%S') + init = 'ALL' if time_info['init'] == '*' else time_info['init'].strftime('%Y%m%d%H%M%S') + valid = 'ALL' if time_info['valid'] == '*' else time_info['valid'].strftime('%Y%m%d%H%M%S') if time_info.get('lead', '*') == '*': lead = time_info.get('label') if time_info.get('label') else 'ALL' else: - lead = time_util.ti_get_seconds_from_lead(time_info['lead'], - time_info['valid']) + lead = time_util.ti_get_seconds_from_lead(time_info['lead'], time_info['valid']) + # use lead with letter if seconds cannot be computed e.g. 3m if lead is None: lead = time_util.ti_get_lead_string(time_info['lead'], @@ -784,3 +851,63 @@ def get_list_file_name(self, time_info, identifier): return (f"{self.app_name}_files_{identifier}_" f"init_{init}_valid_{valid}_lead_{lead}.txt") + + def add_to_infiles(self, file_dict, time_info): + """!Add relevant files from the file dictionary to the input files list. + + This function updates the input files list (`self.infiles`) with file paths + obtained from the file dictionary (`file_dict`) for the current run. It processes + files grouped by their data type (e.g., observation files, ensemble mean files). + + @param file_dict dictionary of files categorized by data type (e.g., FCST, OBS). + Each key contains lists of file paths or other file-related info. + @param time_info dictionary containing time information for string substitution + and file identification. + """ + for data_type in [item for item in file_dict.keys() if item not in ('var_list', 'time_info')]: + self._add_file(file_dict, time_info, data_type) + + + def _add_file(self, file_dict, time_info, data_type): + """!Add files of a specific data type from the file dictionary to the input list. + + This function identifies files of a specific data type (e.g., OBS_GRID, CTRL) from the + file dictionary and processes them for inclusion in the input files list. It supports + directly appending files, handling grouped commands for special data types, and creating + file list files if multiple files are provided. + + @param file_dict dictionary containing categorized file paths. + The keys represent data types (e.g., CTRL, FCST, OBS_GRID). + @param time_info dictionary containing time-related details for file name generation. + @param data_type string representing the category of files to process (e.g., CTRL, ENS_MEAN). + """ + file_list = file_dict.get(data_type) + if not file_list: + return + + # Dictionary to map data types to their corresponding commands + data_type_handlers = { + 'CTRL': lambda files: self.infiles.append(f'-ctrl {files[0]}'), + 'OBS_GRID': lambda files: self.infiles.extend(f'-grid_obs {file}' for file in files), + 'OBS_POINT': lambda files: self.infiles.extend(f'-point_obs {file}' for file in files), + 'ENS_MEAN': lambda files: self.infiles.extend(f'-ens_mean {file}' for file in files), + 'PAIRS': lambda files: self.infiles.extend(f'-pairs {file}' for file in files), + } + + # Handle data types using the mapping + if data_type in data_type_handlers: + data_type_handlers[data_type](file_list) + return + + # if there is more than 1 file, create file list file + if not self.c_dict.get('SUPPORTS_FILE_LIST', True): + self.infiles.extend(file_list) + return + + if len(file_list) > 1: + list_filename = self.get_list_file_name(time_info, data_type) + input_file = self.write_list_file(list_filename, file_list) + else: + input_file = file_list[0] + + self.infiles.append(input_file) diff --git a/metplus/wrappers/series_analysis_wrapper.py b/metplus/wrappers/series_analysis_wrapper.py index 14c54dc260..60c5cb01cd 100755 --- a/metplus/wrappers/series_analysis_wrapper.py +++ b/metplus/wrappers/series_analysis_wrapper.py @@ -165,13 +165,14 @@ def create_c_dict(self): data_types=('FCST', 'OBS'), app_name=self.app_name) - self.get_input_templates(c_dict, { + input_info = { 'FCST': {'prefix': 'FCST_SERIES_ANALYSIS', 'required': False}, 'OBS': {'prefix': 'OBS_SERIES_ANALYSIS', 'required': False}, 'BOTH': {'prefix': 'BOTH_SERIES_ANALYSIS', 'required': False}, 'TC_STAT': {'prefix': 'SERIES_ANALYSIS_TC_STAT', 'required': False}, 'AGGR': {'prefix': 'SERIES_ANALYSIS_AGGR', 'required': False}, - }) + } + self.get_input_templates(c_dict, input_info) self._handle_fcst_obs_or_both_c_dict(c_dict) @@ -306,8 +307,8 @@ def _handle_fcst_obs_or_both_c_dict(self, c_dict): extra_args={'remove_quotes': True} ) - c_dict['USING_BOTH'] = (c_dict['BOTH_INPUT_TEMPLATE'] or - c_dict.get('BOTH_INPUT_FILE_LIST')) + c_dict['USING_BOTH'] = bool(c_dict['BOTH_INPUT_TEMPLATE'] or + c_dict.get('BOTH_INPUT_FILE_LIST')) if c_dict['USING_BOTH']: @@ -468,7 +469,8 @@ def get_all_files_for_leads(self, input_dict, leads): for lead in leads: current_input_dict['lead'] = lead new_files = self.get_all_files_for_lead(current_input_dict) - all_files.extend(new_files) + self._update_list_with_new_files(new_files, all_files) + return all_files def run_at_time_once(self, time_info, lead_group=None): @@ -570,7 +572,7 @@ def get_files_from_time(self, time_info): file_dict = {'time_info': time_info.copy()} if self.c_dict['USING_BOTH']: fcst_files = self.find_input_files(time_info, 'BOTH') - obs_files = fcst_files + obs_files = fcst_files.copy() else: fcst_files = self.find_input_files(time_info, 'FCST') obs_files = self.find_input_files(time_info, 'OBS') @@ -582,6 +584,7 @@ def get_files_from_time(self, time_info): file_dict[fcst_key] = fcst_files file_dict[obs_key] = obs_files + file_dict['input_time_info'] = [time_info.copy()] file_dict_list.append(file_dict) return file_dict_list @@ -1091,8 +1094,7 @@ def _get_field_list(self, data_type, var_info, time_info): # loop through fcst/obs files to read time info # for each file apply time info to field info and add to list - for file_dict in self.c_dict['ALL_FILES']: - file_time_info = file_dict['time_info'] + for file_time_info in self.c_dict['ALL_FILES'][0].get('input_time_info', []): field = self._get_field_sub_level(data_type, var_info, file_time_info) if field: field_list.extend(field) @@ -1128,40 +1130,3 @@ def _get_field_sub_level(self, data_type, var_info, time_dict): v_extra=var_info[f'{data_type}_extra'], d_type=data_type.upper() ) - - @staticmethod - def _get_times_from_file_list(file_path, templates): - """!Generator that yields time info dictionaries. - Loops through file paths found in text file and use list of filename - templates to parse time information from each file. - - @param file_path path to file list file to parse - @param templates list of filename templates to use to parse time info - out of file paths found in file_path file - """ - try: - with open(file_path, 'r') as file_handle: - file_list = file_handle.read().splitlines()[1:] - except FileNotFoundError: - return - - for file_name in file_list: - found = False - file_time_info = None - for template in templates: - file_time_info = parse_template(template, file_name) - if file_time_info: - found = True - break - if not found: - continue - yield file_time_info - - def _update_list_with_new_files(self, time_info, list_to_update): - new_files = self.get_files_from_time(time_info) - if not new_files: - return - if isinstance(new_files, list): - list_to_update.extend(new_files) - else: - list_to_update.append(new_files) diff --git a/metplus/wrappers/wavelet_stat_wrapper.py b/metplus/wrappers/wavelet_stat_wrapper.py index 2c56c7408e..6b95778a38 100755 --- a/metplus/wrappers/wavelet_stat_wrapper.py +++ b/metplus/wrappers/wavelet_stat_wrapper.py @@ -76,24 +76,15 @@ def create_c_dict(self): # get the MET config file path or use default c_dict['CONFIG_FILE'] = self.get_config_file('WaveletStatConfig_wrapped') - c_dict['OBS_INPUT_DIR'] = self.config.getdir(f'OBS_{app}_INPUT_DIR', '') - c_dict['OBS_INPUT_TEMPLATE'] = ( - self.config.getraw('config', f'OBS_{app}_INPUT_TEMPLATE') - ) - if not c_dict['OBS_INPUT_TEMPLATE']: - self.log_error(f"OBS_{app}_INPUT_TEMPLATE required to run") + self.get_input_templates(c_dict, { + 'FCST': {'prefix': 'FCST_WAVELET_STAT', 'required': True}, + 'OBS': {'prefix': 'OBS_WAVELET_STAT', 'required': True}, + }) c_dict['OBS_INPUT_DATATYPE'] = ( self.config.getstr('config', f'OBS_{app}_INPUT_DATATYPE', '') ) - c_dict['FCST_INPUT_DIR'] = self.config.getdir(f'FCST_{app}_INPUT_DIR', '') - c_dict['FCST_INPUT_TEMPLATE'] = ( - self.config.getraw('config', f'FCST_{app}_INPUT_TEMPLATE') - ) - if not c_dict['FCST_INPUT_TEMPLATE']: - self.log_error(f"FCST_{app}_INPUT_TEMPLATE required to run") - c_dict['FCST_INPUT_DATATYPE'] = ( self.config.getstr('config', f'FCST_{app}_INPUT_DATATYPE', '') ) @@ -176,6 +167,4 @@ def create_c_dict(self): }) self.add_met_config(name='output_prefix', data_type='string') - # skip RuntimeFreq input file logic - remove once integrated - c_dict['FIND_FILES'] = False return c_dict diff --git a/parm/met_config/PairStatConfig_wrapped b/parm/met_config/PairStatConfig_wrapped new file mode 100644 index 0000000000..75a235eaf3 --- /dev/null +++ b/parm/met_config/PairStatConfig_wrapped @@ -0,0 +1,168 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Pair-Stat configuration file. +// +// For additional information, please see the MET Users Guide. +// +//////////////////////////////////////////////////////////////////////////////// + +// +// Output model name to be written +// Non-empty string overrides input data values +// +//model = +${METPLUS_MODEL} + +// +// Output description to be written +// Non-empty string overrides input data values +// May be set separately in each "obs.pairs" entry +// +//desc = +${METPLUS_DESC} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Forecast and observation pairs to be verified +// +fcst = { + ${METPLUS_FCST_FILE_TYPE} + //pairs = [ + ${METPLUS_FCST_FIELD} + ${METPLUS_FCST_CLIMO_MEAN_DICT} + ${METPLUS_FCST_CLIMO_STDEV_DICT} +} + +obs = { + ${METPLUS_OBS_FILE_TYPE} + //pairs = [ + ${METPLUS_OBS_FIELD} + ${METPLUS_OBS_CLIMO_MEAN_DICT} + ${METPLUS_OBS_CLIMO_STDEV_DICT} +} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Forecast and observation data censoring, thresholding, and filtering options +// May be set separately in each "fcst.pairs" or "obs.pairs" entry +// +//censor_thresh = +${METPLUS_CENSOR_THRESH} +//censor_val = +${METPLUS_CENSOR_VAL} +//cat_thresh = +${METPLUS_CAT_THRESH} +//cnt_thresh = +${METPLUS_CNT_THRESH} +//cnt_logic = +${METPLUS_CNT_LOGIC} +//wind_thresh = +${METPLUS_WIND_THRESH} +//wind_logic = +${METPLUS_WIND_LOGIC} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Matched pairs filtering and control options +// May be set separately in each "obs.pairs" entry +// +//mpr_column = +${METPLUS_MPR_COLUMN} +//mpr_thresh = +${METPLUS_MPR_THRESH} +//mpr_str_inc = +${METPLUS_MPR_STR_INC} +//mpr_str_exc = +${METPLUS_MPR_STR_EXC} +//eclv_points = +${METPLUS_ECLV_POINTS} +//hss_ec_value = +${METPLUS_HSS_EC_VALUE} +//rank_corr_flag = +${METPLUS_RANK_CORR_FLAG} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Climatology mean data +// May be set separately in the "fcst" and "obs" dictionaries +// +//climo_mean = { +${METPLUS_CLIMO_MEAN_DICT} + +// +// Climatology standard deviation data +// May be set separately in the "fcst" and "obs" dictionaries +// +//climo_stdev = { +${METPLUS_CLIMO_STDEV_DICT} + +// +// Climatology distribution settings +// May be set separately in each "obs.pairs" entry +// +//climo_cdf = { +${METPLUS_CLIMO_CDF_DICT} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Point observation time window +// May be set separately in each "obs.pairs" entry +// +//obs_window = { +${METPLUS_OBS_WINDOW_DICT} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Verification masking regions +// May be set separately in each "obs.pairs" entry +// +//mask = { +${METPLUS_MASK_DICT} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Confidence interval settings +// May be set separately in each "obs.pairs" entry +// +//ci_alpha = +${METPLUS_CI_ALPHA} + +//boot = { +${METPLUS_BOOT_DICT} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Threshold for SEEPS p1 (Probability of being dry) +// +//seeps_p1_thresh = +${METPLUS_SEEPS_P1_THRESH} + +//////////////////////////////////////////////////////////////////////////////// + +// +// Statistical output types +// May be set separately in each "obs.pairs" entry +// +//output_flag = { +${METPLUS_OUTPUT_FLAG_DICT} + +//////////////////////////////////////////////////////////////////////////////// + +//point_weight_flag = +${METPLUS_POINT_WEIGHT_FLAG} + +tmp_dir = "${MET_TMP_DIR}"; + +//version = "V12.0.0"; + +//////////////////////////////////////////////////////////////////////////////// + +${METPLUS_MET_CONFIG_OVERRIDES} diff --git a/parm/use_cases/met_tool_wrapper/PairStat/PairStat.conf b/parm/use_cases/met_tool_wrapper/PairStat/PairStat.conf new file mode 100644 index 0000000000..a455666077 --- /dev/null +++ b/parm/use_cases/met_tool_wrapper/PairStat/PairStat.conf @@ -0,0 +1,212 @@ +[config] + +# Documentation for this use case can be found at +# https://metplus.readthedocs.io/en/latest/generated/met_tool_wrapper/PairStat/PairStat.html + +# For additional information, please see the METplus Users Guide. +# https://metplus.readthedocs.io/en/latest/Users_Guide + +### +# Processes to run +# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#process-list +### + +PROCESS_LIST = PairStat + + +### +# Time Info +# LOOP_BY options are INIT, VALID, RETRO, and REALTIME +# If set to INIT or RETRO: +# INIT_TIME_FMT, INIT_BEG, INIT_END, and INIT_INCREMENT must also be set +# If set to VALID or REALTIME: +# VALID_TIME_FMT, VALID_BEG, VALID_END, and VALID_INCREMENT must also be set +# LEAD_SEQ is the list of forecast leads to process +# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#timing-control +### + +LOOP_BY = VALID +VALID_TIME_FMT = %Y%m%d%H +VALID_BEG = 2007033112 +VALID_END = 2007033112 +VALID_INCREMENT = 1H + +LEAD_SEQ = 36 + + +### +# File I/O +# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#directory-and-filename-template-info +### + +PAIR_STAT_PAIRS_INPUT_DIR = {INPUT_BASE}/met_test/out/point_stat +PAIR_STAT_PAIRS_INPUT_TEMPLATE = point_stat_{lead?fmt=%H%M%S}L_{valid?fmt=%Y%m%d_%H%M%S}V_mpr.txt + +#PAIR_STAT_OFFSETS = 0 + +PAIR_STAT_OUTPUT_DIR = {OUTPUT_BASE}/pair_stat +PAIR_STAT_OUTPUT_TEMPLATE = {valid?fmt=%Y%m%d_%H}_out + +#PAIR_STAT_CLIMO_MEAN_INPUT_DIR = +#PAIR_STAT_CLIMO_MEAN_INPUT_TEMPLATE = + +#PAIR_STAT_CLIMO_STDEV_INPUT_DIR = +#PAIR_STAT_CLIMO_STDEV_INPUT_TEMPLATE = + + +### +# Field Info +# https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#field-info +### + +PAIR_STAT_ONCE_PER_FIELD = False + +#PAIR_STAT_FCST_FILE_TYPE = +#PAIR_STAT_OBS_FILE_TYPE = + +FCST_VAR1_NAME = TMP +FCST_VAR1_LEVELS = P750-900 + +OBS_VAR1_NAME = TMP +OBS_VAR1_LEVELS = P750-900 + +FCST_VAR2_NAME = UGRD +FCST_VAR2_LEVELS = Z10 + +OBS_VAR2_NAME = UGRD +OBS_VAR2_LEVELS = Z10 + + +### +# PairStat Settings +# https://metplus.readthedocs.io/en/latest/Users_Guide/wrappers.html#pairstat +### + +PAIR_STAT_FORMAT = mpr + +#LOG_PAIR_STAT_VERBOSITY = 2 + +#PAIR_STAT_CONFIG_FILE = {PARM_BASE}/met_config/PairStatConfig_wrapped + +#MODEL = +#PAIR_STAT_DESC = + +#PAIR_STAT_CENSOR_THRESH = +#PAIR_STAT_CENSOR_VAL = +#PAIR_STAT_CAT_THRESH = +#PAIR_STAT_CNT_THRESH = +#PAIR_STAT_CNT_LOGIC = +#PAIR_STAT_WIND_THRESH = +#PAIR_STAT_WIND_LOGIC = +#PAIR_STAT_MPR_COLUMN = +#PAIR_STAT_MPR_THRESH = +#PAIR_STAT_MPR_STR_INC = +#PAIR_STAT_MPR_STR_EXC = +#PAIR_STAT_ECLV_POINTS = +#PAIR_STAT_HSS_EC_VALUE = +#PAIR_STAT_RANK_CORR_FLAG = + +#PAIR_STAT_CLIMO_MEAN_FILE_NAME = +#PAIR_STAT_CLIMO_MEAN_FIELD = +#PAIR_STAT_CLIMO_MEAN_REGRID_METHOD = +#PAIR_STAT_CLIMO_MEAN_REGRID_WIDTH = +#PAIR_STAT_CLIMO_MEAN_REGRID_VLD_THRESH = +#PAIR_STAT_CLIMO_MEAN_REGRID_SHAPE = +#PAIR_STAT_CLIMO_MEAN_TIME_INTERP_METHOD = +#PAIR_STAT_CLIMO_MEAN_DAY_INTERVAL = +#PAIR_STAT_CLIMO_MEAN_HOUR_INTERVAL = + +#PAIR_STAT_CLIMO_STDEV_FILE_NAME = +#PAIR_STAT_CLIMO_STDEV_FIELD = +#PAIR_STAT_CLIMO_STDEV_REGRID_METHOD = +#PAIR_STAT_CLIMO_STDEV_REGRID_WIDTH = +#PAIR_STAT_CLIMO_STDEV_REGRID_VLD_THRESH = +#PAIR_STAT_CLIMO_STDEV_REGRID_SHAPE = +#PAIR_STAT_CLIMO_STDEV_TIME_INTERP_METHOD = +#PAIR_STAT_CLIMO_STDEV_DAY_INTERVAL = +#PAIR_STAT_CLIMO_STDEV_HOUR_INTERVAL = + +#PAIR_STAT_FCST_CLIMO_MEAN_FILE_NAME = +#PAIR_STAT_FCST_CLIMO_MEAN_FIELD = +#PAIR_STAT_FCST_CLIMO_MEAN_REGRID_METHOD = +#PAIR_STAT_FCST_CLIMO_MEAN_REGRID_WIDTH = +#PAIR_STAT_FCST_CLIMO_MEAN_REGRID_VLD_THRESH = +#PAIR_STAT_FCST_CLIMO_MEAN_REGRID_SHAPE = +#PAIR_STAT_FCST_CLIMO_MEAN_TIME_INTERP_METHOD = +#PAIR_STAT_FCST_CLIMO_MEAN_DAY_INTERVAL = +#PAIR_STAT_FCST_CLIMO_MEAN_HOUR_INTERVAL = + +#PAIR_STAT_FCST_CLIMO_STDEV_FILE_NAME = +#PAIR_STAT_FCST_CLIMO_STDEV_FIELD = +#PAIR_STAT_FCST_CLIMO_STDEV_REGRID_METHOD = +#PAIR_STAT_FCST_CLIMO_STDEV_REGRID_WIDTH = +#PAIR_STAT_FCST_CLIMO_STDEV_REGRID_VLD_THRESH = +#PAIR_STAT_FCST_CLIMO_STDEV_REGRID_SHAPE = +#PAIR_STAT_FCST_CLIMO_STDEV_TIME_INTERP_METHOD = +#PAIR_STAT_FCST_CLIMO_STDEV_DAY_INTERVAL = +#PAIR_STAT_FCST_CLIMO_STDEV_HOUR_INTERVAL = + +#PAIR_STAT_OBS_CLIMO_MEAN_FILE_NAME = +#PAIR_STAT_OBS_CLIMO_MEAN_FIELD = +#PAIR_STAT_OBS_CLIMO_MEAN_REGRID_METHOD = +#PAIR_STAT_OBS_CLIMO_MEAN_REGRID_WIDTH = +#PAIR_STAT_OBS_CLIMO_MEAN_REGRID_VLD_THRESH = +#PAIR_STAT_OBS_CLIMO_MEAN_REGRID_SHAPE = +#PAIR_STAT_OBS_CLIMO_MEAN_TIME_INTERP_METHOD = +#PAIR_STAT_OBS_CLIMO_MEAN_DAY_INTERVAL = +#PAIR_STAT_OBS_CLIMO_MEAN_HOUR_INTERVAL = + +#PAIR_STAT_OBS_CLIMO_STDEV_FILE_NAME = +#PAIR_STAT_OBS_CLIMO_STDEV_FIELD = +#PAIR_STAT_OBS_CLIMO_STDEV_REGRID_METHOD = +#PAIR_STAT_OBS_CLIMO_STDEV_REGRID_WIDTH = +#PAIR_STAT_OBS_CLIMO_STDEV_REGRID_VLD_THRESH = +#PAIR_STAT_OBS_CLIMO_STDEV_REGRID_SHAPE = +#PAIR_STAT_OBS_CLIMO_STDEV_TIME_INTERP_METHOD = +#PAIR_STAT_OBS_CLIMO_STDEV_DAY_INTERVAL = +#PAIR_STAT_OBS_CLIMO_STDEV_HOUR_INTERVAL = + +#PAIR_STAT_CLIMO_CDF_BINS = +#PAIR_STAT_CLIMO_CDF_CENTER_BINS = +#PAIR_STAT_CLIMO_CDF_WRITE_BINS = +#PAIR_STAT_CLIMO_CDF_DIRECT_PROB = + +#PAIR_STAT_OBS_WINDOW_BEGIN = +#PAIR_STAT_OBS_WINDOW_END = + +#PAIR_STAT_MASK_GRID = +#PAIR_STAT_MASK_POLY = +#PAIR_STAT_MASK_SID = +#PAIR_STAT_MASK_LLPNT = + +#PAIR_STAT_CI_ALPHA = + +#PAIR_STAT_BOOT_INTERVAL = +#PAIR_STAT_BOOT_REP_PROP = +#PAIR_STAT_BOOT_N_REP = +#PAIR_STAT_BOOT_RNG = +#PAIR_STAT_BOOT_SEED = + +#PAIR_STAT_SEEPS_P1_THRESH = + +#PAIR_STAT_OUTPUT_FLAG_FHO = +#PAIR_STAT_OUTPUT_FLAG_CTC = +#PAIR_STAT_OUTPUT_FLAG_CTS = +#PAIR_STAT_OUTPUT_FLAG_MCTC = +#PAIR_STAT_OUTPUT_FLAG_MCTS = +#PAIR_STAT_OUTPUT_FLAG_CNT = +PAIR_STAT_OUTPUT_FLAG_SL1L2 = BOTH +#PAIR_STAT_OUTPUT_FLAG_SAL1L2 = +#PAIR_STAT_OUTPUT_FLAG_VL1L2 = +#PAIR_STAT_OUTPUT_FLAG_VAL1L2 = +#PAIR_STAT_OUTPUT_FLAG_VCNT = +#PAIR_STAT_OUTPUT_FLAG_PCT = +#PAIR_STAT_OUTPUT_FLAG_PSTD = +#PAIR_STAT_OUTPUT_FLAG_PJC = +#PAIR_STAT_OUTPUT_FLAG_PRC = +#PAIR_STAT_OUTPUT_FLAG_ECLV = +PAIR_STAT_OUTPUT_FLAG_MPR = BOTH +#PAIR_STAT_OUTPUT_FLAG_SEEPS = +#PAIR_STAT_OUTPUT_FLAG_SEEPS_MPR = + +#PAIR_STAT_POINT_WEIGHT_FLAG = diff --git a/parm/use_cases/model_applications/s2s/SeriesAnalysis_fcstCFSv2_obsGHCNCAMS_climoStandardized_MultiStatisticTool.conf b/parm/use_cases/model_applications/s2s/SeriesAnalysis_fcstCFSv2_obsGHCNCAMS_climoStandardized_MultiStatisticTool.conf index 2bbc1b76b3..810a179e0a 100644 --- a/parm/use_cases/model_applications/s2s/SeriesAnalysis_fcstCFSv2_obsGHCNCAMS_climoStandardized_MultiStatisticTool.conf +++ b/parm/use_cases/model_applications/s2s/SeriesAnalysis_fcstCFSv2_obsGHCNCAMS_climoStandardized_MultiStatisticTool.conf @@ -11,7 +11,7 @@ # https://metplus.readthedocs.io/en/latest/Users_Guide/systemconfiguration.html#process-list ### -PROCESS_LIST = SeriesAnalysis, GenEnsProd, SeriesAnalysis(run_two), GridStat +PROCESS_LIST = SeriesAnalysis(run_one), GenEnsProd, SeriesAnalysis(run_two), GridStat ### @@ -33,9 +33,11 @@ INIT_INCREMENT = 1Y LEAD_SEQ = +[run_one] SERIES_ANALYSIS_CUSTOM_LOOP_LIST = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 SERIES_ANALYSIS_RUNTIME_FREQ = RUN_ONCE +[config] ### # File I/O