forked from NOAA-EMC/global-workflow
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CTests extended validation for C48_ATM and staged C48_S2SW for gfs_fc…
…st and gfs_atmos (NOAA-EMC#3256) This PR extends the CTest Functional Tests: - Added `output_files` for **C48_ATM** for the validation step - Added two new JJOB Cases for **C48_S2SW** - gfs_fcts_seg0 - gfs_atmos_prod_f000-f003
- Loading branch information
1 parent
380946c
commit 1939dac
Showing
11 changed files
with
530 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
{% set H_offset = "-6H" %} | ||
{% set H_timedelta = H_offset | to_timedelta %} | ||
{% set TEST_DATE_offset = TEST_DATE | add_to_datetime(H_timedelta) %} | ||
|
||
{% set cyc = TEST_DATE | strftime('%H') %} | ||
{% set cyc_offset = TEST_DATE_offset | strftime('%H') %} | ||
|
||
{% set PDY = TEST_DATE | to_YMD %} | ||
{% set PDY_offset = TEST_DATE_offset | to_YMD %} | ||
|
||
input_files: | ||
mkdir: | ||
- "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input" | ||
- "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/wave/prep" | ||
- "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gdas.{{ PDY }}/{{cyc_offset}}/model/ocean/restart" | ||
- "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gdas.{{ PDY }}/{{cyc_offset}}/model/ice/restart" | ||
copy: | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_ctrl.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_ctrl.nc"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_data.tile1.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_data.tile1.nc"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_data.tile2.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_data.tile2.nc"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_data.tile3.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_data.tile3.nc"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_data.tile4.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_data.tile4.nc"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_data.tile5.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_data.tile5.nc"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_data.tile6.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/gfs_data.tile6.nc"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile1.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile1.nc"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile2.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile2.nc"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile3.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile3.nc"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile4.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile4.nc"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile5.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile5.nc"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile5.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile5.nc"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile6.nc", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/atmos/input/sfc_data.tile6.nc"] | ||
|
||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/wave/prep/gfswave.mod_def.glo_200", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/wave/prep/gfswave.mod_def.glo_200"] | ||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gfs.{{ PDY }}/{{ cyc }}/model/wave/prep/gfswave.mod_def.uglo_100km", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/model/wave/prep/gfswave.mod_def.uglo_100km"] | ||
|
||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gdas.{{ PDY_offset }}/{{ cyc_offset }}/model/ocean/restart/{{ TEST_DATE | strftime('%Y%m%d.%H0000') }}.MOM.res.nc", | ||
"{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gdas.{{ PDY_offset }}/{{ cyc_offset }}/model/ocean/restart/{{ TEST_DATE | strftime('%Y%m%d.%H0000') }}.MOM.res.nc" ] | ||
|
||
- ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/input_files/gdas.{{ PDY_offset }}/{{ cyc_offset }}/model/ice/restart/{{ TEST_DATE | strftime('%Y%m%d.%H0000') }}.cice_model.res.nc", | ||
"{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gdas.{{ PDY_offset }}/{{ cyc_offset }}/model/ice/restart/{{ TEST_DATE | strftime('%Y%m%d.%H0000') }}.cice_model.res.nc" ] | ||
|
||
# TODO - To enable the validation step include specific files to compare against the results from running the test. | ||
# Note: The below three files is only an example. The cmpfiles tag means it will use checksum to compare the files in the two locations. | ||
# Other extensions can be added and/or folded into the FileUtils class in wxflow FileUtilities.py | ||
# | ||
#output_files: | ||
# cmpfiles: | ||
# - ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/output_files/gfs.{{ PDY }}/{{ cyc }}/conf/ufs.diag_table", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/conf/ufs.diag_table"] | ||
# - ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/output_files/gfs.{{ PDY }}/{{ cyc }}/conf/ufs.input.nml", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/conf/ufs.input.nml"] | ||
# - ["{{ 'STAGED_TESTS_DIR' | getenv }}/{{ 'TEST_NAME' | getenv }}/output_files/gfs.{{ PDY }}/{{ cyc }}/conf/ufs.model_configure", "{{ 'RUNTESTS' | getenv }}/COMROOT/{{ 'TEST_NAME' | getenv }}/gfs.{{ PDY }}/{{ cyc }}/conf/ufs.model_configure"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
#!/usr/bin/env python3 | ||
""" | ||
validate.py | ||
Validation script that checks file checksums and verifies test outputs. | ||
Usage | ||
----- | ||
validate.py --yaml <path_to_yaml> --test_date <YYYYMMDDHH> | ||
Parameters | ||
---------- | ||
--yaml : str | ||
Path to the YAML configuration file. | ||
--test_date : str | ||
Test date in the format YYYYMMDDHH. | ||
""" | ||
|
||
import sys | ||
import argparse | ||
from pathlib import Path | ||
import hashlib | ||
from wxflow import parse_j2yaml, Logger, logit, to_datetime | ||
|
||
logger = Logger(level="DEBUG", colored_log=True) | ||
|
||
|
||
def parse_args(): | ||
""" | ||
parse_args | ||
Parses command line arguments. | ||
Returns | ||
------- | ||
argparse.Namespace | ||
Parsed command line arguments. | ||
""" | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("--yaml", required=True) | ||
parser.add_argument("--test_date", required=True) | ||
return parser.parse_args() | ||
|
||
|
||
def file_checksum(path): | ||
""" | ||
file_checksum | ||
Computes the MD5 checksum of a file. | ||
Parameters | ||
---------- | ||
path : str | ||
Path to the file. | ||
Returns | ||
------- | ||
str | ||
MD5 checksum of the file. | ||
""" | ||
hasher = hashlib.md5() | ||
with open(path, "rb") as f: | ||
for chunk in iter(lambda: f.read(4096), b""): | ||
hasher.update(chunk) | ||
return hasher.hexdigest() | ||
|
||
|
||
def validate_cmpfiles(config): | ||
""" | ||
validate_cmpfiles | ||
Validates that the checksums of paired files match. | ||
Parameters | ||
---------- | ||
config : dict | ||
Configuration dictionary containing file pairs to compare. | ||
Raises | ||
------ | ||
ValueError | ||
If the checksums of any paired files do not match. | ||
""" | ||
cmpfiles = config.get("output_files", {}).get("cmpfiles", []) | ||
for pair in cmpfiles: | ||
file_a, file_b = pair | ||
if file_checksum(file_a) != file_checksum(file_b): | ||
logger.error(f"Checksum mismatch: {file_a} vs {file_b}") | ||
raise ValueError(f"Checksum mismatch: {file_a} vs {file_b}") | ||
logger.info(f"checksums match: {file_a} vs {file_b}") | ||
|
||
|
||
@logit(logger) | ||
def main(): | ||
""" | ||
main | ||
Main function that parses arguments, reads configuration, and validates file checksums. | ||
Raises | ||
------ | ||
SystemExit | ||
If no output files are found in the configuration. | ||
""" | ||
args = parse_args() | ||
|
||
data = {} | ||
if args.test_date: | ||
# Parse test date from string to datetime object | ||
data['TEST_DATE'] = to_datetime(args.test_date) | ||
|
||
files = parse_j2yaml(path=args.yaml, data=data) | ||
if 'output_files' not in files: | ||
logger.info(f"No output files found for test: {args.yaml}") | ||
logger.info("Nothing to validate (TODO - Stubbed).") | ||
sys.exit(0) | ||
|
||
validate_cmpfiles(files) | ||
logger.info(f"All files exist and pass checksum for test: {args.yaml}") | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters