Skip to content

fre.cmor: CMIP7 compatibility follow-up#730

Open
ilaflott wants to merge 33 commits intouse-cmip7-tablesv2from
cmip7-example-followup
Open

fre.cmor: CMIP7 compatibility follow-up#730
ilaflott wants to merge 33 commits intouse-cmip7-tablesv2from
cmip7-example-followup

Conversation

@ilaflott
Copy link
Member

@ilaflott ilaflott commented Feb 6, 2026

Describe your changes

companion to gitlab.gfdl ESM45xml MR19

Core changes

  • tweaks to cmor yaml reader logic
  • deliberate exception for no vertical bounds now
  • no more settings yaml for cmor yaml
  • adds cmor_constants.py — all hard-coded constants previously scattered across cmor_mixer, cmor_helpers, cmor_config, cmor_finder, and cmor_yamler now live in one transparent location under fre/cmor/
  • adds filter_brands() to cmor_helpers for CMIP7 multi-brand disambiguation. when multiple MIP-table brands match a variable by dimension count, the filter narrows down to one using time-type (time vs time1 via time_bnds presence) and vertical-coordinate (INPUT_TO_MIP_VERT_DIM mapping) signals from the input data
  • cmor_mixer brand selection block is now a 4-line call to filter_brands() instead of inline logic
  • PRINT_CLI_CALL is no longer a module-level constant — it is now --print_cli_call / --no-print_cli_call on fre cmor yaml, threaded through to cmor_yaml_subtool() as a bool parameter (defaults to True, same behaviour as before)

Test coverage improvements (102 tests across fre/cmor/tests/)

  • new test_cmor_yamler_subtool.py (17 tests): end-to-end dry-run and non-dry-run of cmor_yaml_subtool, plus every documented exception path — missing yamlfile, pp_dir, table_dir, exp_json, MIP table file; CMIP7 freq=None; CMIP6 freq=None with and without derivable MIP table frequency; get_bronx_freq_from_mip_table KeyError/TypeError caught; gridding dict with None values; outdir auto-creation and creation-failure OSError; start/stop/calendar_type missing from yaml; dry-run CLI and Python call printing
  • new test_cmor_run_subtool.py additions: missing mip_era in exp config raises KeyError; unsupported mip_era (e.g. CMIP99) raises ValueError
  • expanded test_cmor_helpers.py: coverage for find_gold_ocean_statics, check_path_existence, iso_to_bronx_chunk, conv_mip_to_bronx_freq, get_bronx_freq_from_mip_table, update_calendar_type, update_grid_and_label
  • new test_cmor_finder_make_simple_varlist.py: CLI runner coverage for fre cmor varlist
  • fixes to test_cmor_helpers.py for CI (archive-absent path assertion)

Backwards compatibility

  • backwards-compatible: no existing CLI invocations or tests break

Issue ticket number and link (if applicable)

Closes #639
#641, #638

Checklist before requesting a review

  • I ran my code
  • I tried to make my code readable
  • I tried to comment my code
  • I wrote a new test, if applicable
  • I wrote new instructions/documentation, if applicable
  • I ran pytest and inspected its output
  • I ran pylint and attempted to implement some of its feedback
  • No print statements; all user-facing info uses logging module

@ilaflott ilaflott changed the title \fre.cmor\: CMIP7 compatibility follow-up fre.cmor: CMIP7 compatibility follow-up Feb 6, 2026
…list (minus the brands), old behavior with no mip_vars arg. should be cmip6 backwards compatible actually
Add `fre cmor config`, a new CLI subtool that generates CMOR YAML
configuration files for `fre cmor yaml` to consume. Refactored from
the quick_script.py prototype.

New `fre cmor config` feature:
- fre/cmor/cmor_config.py: cmor_config_subtool() scans a pp directory
  tree, cross-references variables against MIP tables via
  make_simple_varlist(), filters out non-variable tables, and writes
  structured YAML output.
- fre/cmor/__init__.py: export cmor_config_subtool
- fre/cmor/frecmor.py: add `config` click command with 12 options
- fre/cmor/cmor_finder.py: update docstring for make_simple_varlist
- fre/tests/test_fre_cmor_cli.py: add 4 config tests
  (no-args, --help, opt-dne, end-to-end case1)
- test_call.sh: developer shell script with example CLI calls
- Remove quick_script.py (superseded by cmor_config module)

CMIP7 run/yaml improvements:
- fre/cmor/cmor_helpers.py: add find_gold_ocean_statics_file() to
  copy archived OM5_025 ocean_static.nc for tripolar grid handling
- fre/cmor/cmor_mixer.py: use find_gold_ocean_statics_file() in
  rewrite_netcdf_file_var instead of find_statics_file() for CMIP7
- fre/cmor/cmor_yamler.py: route cmor_run_subtool outdir per
  component/table_name for organized output structure
- fre/tests/test_files/CMOR_CMIP7_input_example.json: fix calendar
  (julian -> noleap) and grid placeholder values
…ponent, freq fallback from MIP tables

- Deprecate settings.yaml from CMOR yaml combining path; cmor yamls are
  now self-contained (pp_dir, table_dir, outdir defined inline)
- Move variable_list from table-level to component-level in cmor yaml
  schema, cmor_yamler.py, and test fixtures
- Derive freq from CMIP6 MIP tables when not specified in cmor yaml
  (CMIP7 tables lack freq, so user must supply it explicitly)
- Fix gold ocean statics: guard against stale directory at file path,
  use is_file() instead of exists(), add proper fallback to legacy
  find_statics_file
- Monkeypatch find_gold_ocean_statics_file in Omon_sos_gn test to use
  local CDL-generated statics instead of /archive
- Rewrite calendar integration tests for readability (add outpath to
  fixture, shared mock list, concise docstrings)
- Update COMPARE_TEST_OUTPUT_cmor.yaml and cmor.am5.yaml to match

All 99 tests pass, 1 skipped.
@codecov
Copy link

codecov bot commented Feb 13, 2026

Codecov Report

❌ Patch coverage is 84.64286% with 43 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.42%. Comparing base (a8532f1) to head (a8bfe08).

Files with missing lines Patch % Lines
fre/cmor/cmor_helpers.py 73.25% 23 Missing ⚠️
fre/cmor/cmor_config.py 82.71% 14 Missing ⚠️
fre/cmor/cmor_mixer.py 68.42% 6 Missing ⚠️
Additional details and impacted files
@@                  Coverage Diff                   @@
##           use-cmip7-tablesv2     #730      +/-   ##
======================================================
+ Coverage               82.65%   83.42%   +0.76%     
======================================================
  Files                      68       70       +2     
  Lines                    4578     4807     +229     
======================================================
+ Hits                     3784     4010     +226     
- Misses                    794      797       +3     
Flag Coverage Δ
unittests 83.42% <84.64%> (+0.76%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
fre/cmor/__init__.py 100.00% <100.00%> (ø)
fre/cmor/cmor_constants.py 100.00% <100.00%> (ø)
fre/cmor/cmor_finder.py 86.32% <100.00%> (+12.92%) ⬆️
fre/cmor/cmor_yamler.py 100.00% <100.00%> (+12.08%) ⬆️
fre/cmor/frecmor.py 100.00% <100.00%> (+1.92%) ⬆️
fre/yamltools/combine_yamls_script.py 86.30% <100.00%> (+2.30%) ⬆️
fre/cmor/cmor_mixer.py 91.27% <68.42%> (+1.93%) ⬆️
fre/cmor/cmor_config.py 82.71% <82.71%> (ø)
fre/cmor/cmor_helpers.py 89.10% <73.25%> (-0.28%) ⬇️

... and 1 file with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a8532f1...a8bfe08. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ilaflott ilaflott linked an issue Feb 13, 2026 that may be closed by this pull request
Add 21 new tests across two test files covering previously untested
functions and edge cases:

test_cmor_helpers.py (19 new):
- create_tmp_dir: success, with exp_config, OSError on bad path
- get_json_file_data: success, nonexistent file, invalid JSON
- update_grid_and_label: ValueError when grid_label/grid/nom_res is None
- get_bronx_freq_from_mip_table: success (mon→monthly), invalid freq
- update_outpath: ValueError when json_file_path or output is None
- filter_brands: time filter (mean/inst), vertical filter, all
  eliminated, multiple remaining

test_cmor_finder_make_simple_varlist.py (2 new):
- Duplicate var_name deduplication
- MIP table filtering (only vars in table survive)

Also remove unreachable dead code (else branch with bare raise) at the
end of cmor_find_subtool — the preceding if/elif already covers all
reachable paths and a ValueError guard earlier makes the else logically
impossible.
…ve_missing

The test asserted the mirrored directory tree included 'gold/' but the
function's split('/')[3:] strips the first three path segments
('', 'archive', 'gold') from '/archive/gold/datasets/...', so the
created subdirectory starts at 'datasets/'.

Locally the archive file exists, so the test took the else-branch and
never hit the bad assertion.  On GitHub CI the archive is absent, the
if-branch ran, and the wrong expected path caused the failure.
@ilaflott ilaflott self-assigned this Feb 13, 2026
@ilaflott ilaflott added documentation Improvements or additions to documentation new functioning New feature or request tricky Likely to encounter significant friction to solution clean up priority: HIGH labels Feb 13, 2026
@ilaflott ilaflott marked this pull request as ready for review February 13, 2026 22:44
@ilaflott ilaflott requested a review from Copilot February 13, 2026 22:45
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements CMIP7 compatibility features for the fre.cmor tool, building on previous work to support both CMIP6 and CMIP7 workflows. The changes centralize constants, add brand disambiguation for CMIP7 variables, introduce a YAML configuration generator, and significantly expand test coverage.

Changes:

  • Introduces cmor_constants.py to centralize all hard-coded constants previously scattered across multiple modules
  • Adds filter_brands() helper function for CMIP7 multi-brand variable disambiguation using time-type and vertical-coordinate signals
  • Implements new fre cmor config command to auto-generate CMOR YAML configurations from post-processing directory trees
  • Expands test suite to 102 tests with comprehensive coverage of exception paths, brand filtering, and frequency derivation logic
  • Deprecates settings.yaml imports for the cmor path — cmor yamls are now self-contained
  • Adds --print_cli_call/--no-print_cli_call option to control dry-run output format

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
test_call.sh Development/testing script with manual test cases (should not be in repo)
pylintrc Raises minimum quality threshold from 8.00 to 8.30
fre/yamltools tests Updates test fixtures to reflect settings.yaml deprecation for cmor
fre/yamltools/combine_yamls_script.py Removes settings.yaml merge for cmor path
fre/tests/test_fre_cmor_cli.py Adds CLI tests for config and varlist commands with MIP table filtering
fre/tests/test_files/CMOR_CMIP7_input_example.json Updates test data calendar and grid fields
fre/cmor/tests/test_cmor_yamler_subtool.py New comprehensive test suite for yaml subtool (17 tests)
fre/cmor/tests/test_cmor_run_subtool*.py Adds tests for mip_era validation and monkeypatching for CI
fre/cmor/tests/test_cmor_mixer_calendar_integration.py Refactors calendar integration tests for clarity
fre/cmor/tests/test_cmor_helpers.py Expands coverage for helpers including filter_brands, gold statics, frequency derivation
fre/cmor/tests/test_cmor_finder_make_simple_varlist.py Adds CLI runner coverage and edge case tests
fre/cmor/frecmor.py Adds config command and print_cli_call option to yaml command
fre/cmor/cmor_yamler.py Implements CMIP7 freq handling, print_cli_call parameter, moves variable_list to component level
fre/cmor/cmor_mixer.py Integrates filter_brands, gold ocean statics lookup, update_outpath call, improved calendar handling
fre/cmor/cmor_helpers.py Adds find_gold_ocean_statics_file, update_outpath, filter_brands functions
fre/cmor/cmor_finder.py Adds optional MIP table filtering to make_simple_varlist
fre/cmor/cmor_constants.py New module consolidating all constants from multiple files
fre/cmor/cmor_config.py New module implementing the config subtool
fre/cmor/init.py Exports cmor_config_subtool
docs/* Updates documentation for new features and CMIP7 support
.github/workflows/create_test_conda_env.yml Removes main branch restriction from PR trigger
Comments suppressed due to low confidence (1)

fre/cmor/cmor_yamler.py:284

  • The cmor_run_subtool call at line 270 is missing the calendar_type parameter that appears in both dry-run outputs (lines 250 and 267). This inconsistency means the actual call won't pass the calendar_type even though the dry-run shows it would be passed. Either add calendar_type = calendar_type to the call or remove it from the dry-run outputs.
            cmor_run_subtool( #uncovered
                indir = indir ,
                json_var_list = json_var_list ,
                json_table_config = json_mip_table_config ,
                json_exp_config = json_exp_config ,
#                outdir = cmorized_outdir ,
                outdir = cmor_run_call_outdir ,
                run_one_mode = run_one_mode ,
                opt_var_name = opt_var_name ,
                grid = grid_desc ,
                grid_label = grid_label ,
                nom_res = nom_res ,
                start = start,
                stop = stop
            )

@ilaflott
Copy link
Member Author

Pull request overview

This PR implements CMIP7 compatibility features for the fre.cmor tool, building on previous work to support both CMIP6 and CMIP7 workflows. The changes centralize constants, add brand disambiguation for CMIP7 variables, introduce a YAML configuration generator, and significantly expand test coverage.
...
...
...
Comments suppressed due to low confidence (1)

fre/cmor/cmor_yamler.py:284

The cmor_run_subtool call at line 270 is missing the calendar_type parameter that appears in both dry-run outputs (lines 250 and 267). This inconsistency means the actual call won't pass the calendar_type even though the dry-run shows it would be passed. Either add calendar_type = calendar_type to the call or remove it from the dry-run outputs.

first time i found something useful in the "low-confidence" section

…other files- chasing down a bug seen in CI with no local equivalent, from what i can currently tell
…t the many tools which name their temp dirs, "tmp"

so, new name, CMOR_tmp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clean up documentation Improvements or additions to documentation new functioning New feature or request priority: HIGH tricky Likely to encounter significant friction to solution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fre.cmor: try using the few CMIP7 CMOR tables in PCMDI/cmor

2 participants