Zppy pcmdi enhancement#815
Conversation
|
@forsyth2: I submit this pull request for some changes I made based on my observations during the testing I did when I try to resolve the reported issues for #807. I noticed in my log file for the mean climate metrics calculation:
I did some debugging and found that there is a missing condition that is not fully handled in https://github.com/E3SM-Project/zppy/pull/815/changes#diff-47eb0fa6e3e0e191a714b640eddb33424a2516b9dab2c7ec57e46193d8e160bdL238-R301 Specifically, for the mean climate metrics calculation of radiative flux variables such as rsut, when CERES-EBAF is used as the observational reference, the observational dataset only covers 2001–2018. If the target model output period is, for example, 1985–1994, which is completely outside the observational coverage period, then the current code path is not able to correctly perform the observational subselection and can lead to unexpected behavior in the downstream metrics calculation. Although the metrics processing will still proceed and produce outputs, this is not the expected behavior. Instead, when this situation occurs, we would expect the workflow to fall back to using the full available observational period (2001–2018 in this case) to calculate the observational climatology for comparison, rather than attempting an invalid temporal subselection. |
ace60e0 to
f50333f
Compare
|
@zhangshixuan1987 Should we convert this PR back to draft? From another thread, we decided that the pcmdi feature will remain as beta version until enso set is being finalized and integrated. We are at the end of e3sm-unified testing period, if you'd like to make more changes, we can choose to review and merge this PR after this e3sm-unified release. |
|
@chengzhuzhang : Sure, I converted this to a draft. Please take any necessary actions and let me know if you need any help from me. |
|
Thank you @zhangshixuan1987. Let's plan to keep this PR open and integrate in the next release. Thanks for the testing and keeping enhance this feature. |
1057e0f to
cd718e4
Compare
|
@zhangshixuan1987 Can you please rebase this off the latest Options to rebase: Option 1 allows you to keep the commit history intact, but you'll have to address conflicts commit-by-commit which could be tedious. Option 2 allows you to address conflicts in one go, but it puts the work from all 18 commits under the first commit. |
cd718e4 to
c35d90e
Compare
|
@forsyth2 : Hi Ryan, I did a rebased following your suggestions. Please take a look and let me know if you have any questions. Thank you! |
|
Great, thanks @zhangshixuan1987! I'll work on reviewing this PR and E3SM-Project/zppy-interfaces#49 today. |
forsyth2
left a comment
There was a problem hiding this comment.
I'm reviewing this PR and the corresponding zppy-interfaces PR together. I had Claude do an initial review (for some reason, Copilot won't let me request a review). I then did a very-high level visual inspection based on its comments. I already posted a similar review for that PR.
Once these comments are addressed, I can run zppy's integration tests on it. I'm also updating the zppy docs in #839, so I'll want to update those to reflect important changes from this PR.
A few notes:
- Re: the rebase -- you can combine changes rather than picking one or the other. It looks like all the
vrtchanges of #827 were removed here. - Some of the error numbers need fixing, but I do like the new count-by-10s incrementing system
- It looks like this PR enables ENSO support. Is that correct? That is, we can use ENSO after this merges?
Claude's summary
These two PRs were made in conjunction and should be reviewed together. At a high level, they extend the zppy/zppy-interfaces pipeline with MPAS ocean and sea-ice component support, while also cleaning up configuration, fixing bugs, and expanding test coverage.
High-Level Summary
| Area | zppy |
zppy-interfaces |
|---|---|---|
| New feature | MPAS ocean/sea-ice time-series processing | — |
| Config changes | New MPAS parameters; removal of vertical remap params from [ts] |
Default EMOV modes list updated |
| Bug fixes | enso set handling; dependency wiring for e3sm_to_cmip |
Typos in module names, paths, and variable names |
| Robustness | Multi-subsection dependency support; richer error codes in shell templates | Output dir auto-creation; Jinja2 template validation; sorted glob; normalized mode inputs |
| Logging | print → logger.debug in pcmdi_diags.py |
print → logger.info in viewer.py |
| Tests | — | New tests for synthetic_metrics_plotter, expanded test_viewer.py |
zppy PR
1. New MPAS component support (utils.py)
set_component_and_prc_typ() gains two new cases:
mpaso→component="ocn",prc_typ="mpasocean"mpassi→component="ice",prc_typ="mpasseaice"
This is the foundation that enables the rest of the MPAS work.
2. New configuration parameters (default.ini)
Added to [climo]:
mpas_rst_template— MPAS restart file template to guide time-series processing.
Added to [ts]:
mpas_calendar(default:"noleap") — Calendar type for constructing a CF-style numeric time coordinate.mpas_start_time(default:"0001-01-01 00:00:00") — Reference time fortime:units = "days since ...".
Removed from [ts]:
vrt_remap_vars,vrt_remap_file,vrt_in_file— The vertical remap parameters are removed from[ts].⚠️ Breaking change for existing users who configured vertical remapping via these keys. Confirm whether this functionality has been moved elsewhere or intentionally dropped. The template still contains a{%- if vrt_remap_vars != '' %}block (visible at the end of the diff), suggesting some vertical remap logic may still be active — this inconsistency should be clarified.
Modified in [e3sm_to_cmip]:
vrt_in_fileremoved.interp_varsdefault changed from"U,V,T,Q,RELHUM,OMEGA,Z3"to""(empty).⚠️ Behavioral change: previously, vertical interpolation ran by default for a standard set of 3D variables. Now users must opt in explicitly. Existing workflows that relied on the default will silently stop interpolating. This deserves prominent documentation.
Modified in [pcmdi_diags]:
enso_varsdefault updated:prsnremoved,hflx→hfls. These look like correctness fixes (matching standard CMIP variable names), but should be confirmed.
3. pcmdi_diags.py: enso set handling changed
enso set previously triggered a break to skip the task entirely, with a warning message saying it was "not yet supported." This PR changes the warning to say it is "currently a testing mode" and comments out the continue (which was itself commented out, not active). The net result is that the enso set now runs. Whether this is intentional and whether enso is actually stable enough to run by default should be confirmed with the author.
4. Multi-subsection dependency support (pcmdi_diags.py)
add_ts_dependencies() now supports comma-separated ts_subsection values, iterating over each and registering dependencies for all of them. Similarly, a new add_e3sm_to_cmip_dependencies() function was extracted and applies the same pattern for e3sm_to_cmip_atm_subsection. Both functions also defensively initialize the subsection key if it is absent before calling set_value_of_parameter_if_undefined() (which requires the key to exist). This is a solid improvement for multi-component workflows.
5. MPAS time-series shell template (large addition)
The most substantial change is a new block in the ts shell template for processing MPAS ocean and sea-ice variables. Key steps:
-
SGS (sub-grid-scale) helper construction for
mpasseaice: Derives a static binary mask (sgs_msk_static) fromsgs_frc_static(static ice fraction), avoiding reliance ontimeMonthly_avg_icePresentIntMaskwhich may not be present in every split file. Falls back gracefully iftimeMonthly_avg_iceAreaCellis missing. -
ncremapinvocation: Two code paths — with and without SGS helpers — both using--d2f,--no_stagger, and a provided mapping file. The SGS path additionally passes--sgs_frc,--sgs_msk, and--sgs_nrm. -
Post-remap cleanup: Removes helper variables (
sgs_frc_static,sgs_msk_static, and conditionallytimeMonthly_avg_iceAreaCell) from the output, but only if they are actually present. The presence check uses agrepagainstncks -moutput. -
MPAS time metadata restoration: After remap, restores
xtime_startMonthly,xtime_endMonthly, andtimeMonthly_avg_daysSinceStartOfSimfrom the pre-remap temp file if they were dropped. -
CF-style time coordinate construction: If the output lacks a standard
timevariable, derives one fromtimeMonthly_avg_daysSinceStartOfSim, renames it, and attachesunits,calendar,long_name, andstandard_nameattributes. Usesmpas_start_timeandmpas_calendarfrom config. -
missing_valueattribute: Addsmissing_value = 1.0e36to the main floating-point variable if it is absent, without touching_FillValue. -
Cleanup: All temp files removed at the end.
Concerns:
- The
greppattern used to check variable presence —grep -qE "^[[:space:]]*[^:]+[[:space:]]+${main_var}(\(|\[)"— is somewhat fragile. A variable whose name is a substring of another could potentially match incorrectly. Consider usingncks --varor a more precise pattern. - Error code numbering: The existing "move output ts files" step was bumped from
ERROR (3)toERROR (5)to make room for the new MPAS error codes (3 and 4). Ensure all error code documentation and any monitoring/alerting is updated accordingly. - The
{%- if vrt_remap_vars != '' %}block visible at the end of the diff suggests vertical remap logic is still present in the template, even thoughvrt_remap_varswas removed fromdefault.ini. This is either dead code or an inconsistency that needs resolution.
| mpas_calendar = string(default="noleap") | ||
| # MPAS simulation start time used as the reference time for constructing | ||
| # a CF-style numeric time coordinate: | ||
| # time:units = "days since <mpas_start_time>" | ||
| # This should match the MPAS/E3SM config_start_time when possible. | ||
| mpas_start_time = string(default="0001-01-01 00:00:00") |
There was a problem hiding this comment.
From the removal of the vrt code, it looks like the rebase conflicts weren't handled correctly. This appears to be undoing the work of #827
From Claude:
Removing vrt_remap_vars, vrt_remap_file, and vrt_in_file from [ts] is a breaking change for any existing users configured for vertical remapping. Also, the ts shell template still appears to contain a {%- if vrt_remap_vars != '' %} block — if these keys are removed from config, that block becomes unreachable dead code (or worse, always evaluates to empty string and silently skips). Should be reconciled.
| # to pressure levels before running e3sm_to_cmip. If empty, the vertical- | ||
| # interpolation preprocessing step is skipped. The default is empty; users | ||
| # should explicitly set this option when pressure-level interpolation is needed. | ||
| interp_vars = string(default="") |
There was a problem hiding this comment.
From Claude:
Changing the default from a standard set of 3D variables to empty means vertical interpolation is now opt-in. Any existing workflow that relied on this default will silently stop interpolating without any error or warning. This should be prominently noted in the changelog, and ideally a deprecation warning should be emitted when the old behavior would have applied.
There was a problem hiding this comment.
I can update the docs (being added in #839) to reflect this.
I think we're still in a "beta release" for pcmdi_diags so it's ok to change functionality now. Ideally though, we're no longer in a prototype/beta release by the Fall E3SM Unified release deadline.
| "The 'enso' set is currently a testing mode in PCMDI Diags. Turn on it with care." | ||
| ) | ||
| break # Skip this task | ||
| # continue # Skip this task |
There was a problem hiding this comment.
From Claude:
The enso set now runs instead of being skipped. A few questions: (1) Is this intentional and is enso considered stable enough for general use? (2) The commented-out continue is misleading — if the intent is to allow it to run, the comment should be removed entirely rather than left as dead code. (3) The warning message has a grammatical issue: "Turn on it with care" should be "Use with care" or "Enable with care."
There was a problem hiding this comment.
This is a big change -- is this the PR that enables ENSO support for pcmdi_diags?
| def add_e3sm_to_cmip_dependencies( | ||
| c: Dict[str, Any], dependencies: List[str], script_dir: str, yr: int | ||
| ): |
There was a problem hiding this comment.
From Claude:
_as_subsection_list() was defined earlier in this file but doesn't appear to be used here — the subsection splitting is re-implemented inline with a list comprehension. Consider using the helper for consistency, or remove it if it isn't used elsewhere.
| # Remove helper variables only if at least one is present in the output. | ||
| remove_present="false" | ||
| for rm_var in $(echo "${vars_to_remove}" | tr ',' ' '); do | ||
| if run_nco ncks -m "${out_f}" | grep -qE "^[[:space:]]*[^:]+[[:space:]]+${rm_var}(\(|\[)"; then |
There was a problem hiding this comment.
From Claude:
This grep pattern checks whether ${main_var} appears in ncks -m output, but it could false-positive if ${main_var} is a substring of another variable name (e.g. pr matching prsn). Consider anchoring the match more tightly or using ncks --var=${main_var} and checking the exit code instead.
| ======= | ||
| {% if interp_vars | default('') | trim -%} | ||
| >>>>>>> 1bf85310 (Add fix to allow the vertical interpolation for EAMxx 3D variables) |
There was a problem hiding this comment.
Missed this in the rebase.
| else | ||
| echo "ERROR: timeMonthly_avg_iceAreaCell is missing in ${tmp_f}; cannot construct static SGS helpers for MPAS-Seaice." | ||
| cd {{ scriptDir }} | ||
| echo 'ERROR (3)' > {{ prefix }}.status |
There was a problem hiding this comment.
Repeats ERROR (3). Should be ERROR (4), exit 4
| if [ ${rc} != 0 ]; then | ||
| echo "ERROR: MPAS component regridding failed for ${bname}" | ||
| cd {{ scriptDir }} | ||
| echo 'ERROR (4)' > {{ prefix }}.status |
There was a problem hiding this comment.
Should now be ERROR (5), exit 5
| local end_year="$4" | ||
| local name_key="$5" | ||
| local error_num="$6" | ||
| local error_num="$6" # Will use error_num +0,+1,+2 |
There was a problem hiding this comment.
Should be # Will use error_num +1,+2,+3,+4,+5
There was a problem hiding this comment.
Similarly:
create_links_acyc_climo_obs: # Will use error_num +1,+2,+3,+4,+5,+6,+7 (also note the last error line in that function doesn't add a number to error_num, so that should become the +7).
create_links_ts: # Will use error_num +1,+2,+3,+4,+5,+6
create_links_ts_obs: # Will use error_num +1,+2,+3,+4,+5,+6
| climo_dir_source="{{ output }}/post/atm/{{ grid }}/cmip_ts/monthly" | ||
| # Link and process primary model climo data | ||
| create_links_acyc_climo "${climo_dir_source}" "${climo_dir_primary}" "${Y1}" "${Y2}" "${model_name}.${tableID}" 1 | ||
| create_links_acyc_climo "${climo_dir_source}" "${climo_dir_primary}" "${Y1}" "${Y2}" "${model_name}.${tableID}" 10 |
There was a problem hiding this comment.
✅ Clever to increment the exit codes by 10s so we can have "sub-codes" within each function. This will be much easier to maintain than keeping track of the total count.
zppy/pcmdi_diags.py:
- Replace print() with logger.debug() for prefix logging
- Fix break -> continue in commented-out enso skip block
- Add missing else branch with ValueError in add_pcmdi_dependencies
to prevent UnboundLocalError on invalid run_type
zppy/templates/pcmdi_diags.bash:
- Move $? check before rm in create_links_acyc_climo so ncap2
failures are caught instead of silently swallowed
- Fix .nc file filter from regex =~ ".nc" to glob != *.nc in
create_links_acyc_climo_obs and create_links_ts_obs
- Remove unused dofm variable from create_links_acyc_climo and
create_links_acyc_climo_obs
- Fix double space before --enso_groups in zi-pcmdi-enso command
- Fix Jinja2 spacing: {{var}} -> {{ var }} for model_tableID,
save_all_data, enso_viewer, enso_vars, enso_years
zppy/defaults/default.ini:
- Fix enso_vars default: hflx -> hfls (correct CMIP variable name)
…rnings
zppy/templates/pcmdi_diags.bash:
- Normalize time and time_bnds to the nearest second after ncrcat
subsetting in create_links_ts and create_links_ts_obs to prevent
floating-point precision artifacts causing cftime to decode boundary
values as second=60 (e.g. 2015-12-31 23:59:60 instead of 2016-01-01)
- Guard defdim("bnds",2) with if(!exists("bnds")) in all three ncap2
call sites to suppress spurious warnings when the bnds dimension is
already present in the input file
This commit contains the changes made to the pcmdi.py so that it will allow users to pass the dependencies of the pcmdi section on the subsection of ts and e3sm_to_cmip
Use a non-interactive Matplotlib backend for batch diagnostics. Safe for workflows that save figures to files and avoids GUI/Tkinter errors on systems without a display.
Split MPAS-Ocean and MPAS-Seaice time-series processing into native ncclimo generation followed by explicit ncremap regridding. This allows the workflow to restore MPAS-native time metadata, construct a CF-style numeric time coordinate from timeMonthly_avg_daysSinceStartOfSim, and preserve missing_value metadata after regridding. For MPAS-Seaice, construct spatial-only SGS helper variables from timeMonthly_avg_iceAreaCell before regridding. This avoids failures when ncremap attempts to use the time-dependent timeMonthly_avg_iceAreaCell field directly as the SGS fraction. Temporary helper variables are removed from final output, and timeMonthly_avg_iceAreaCell is retained only when it is the requested output variable.
c35d90e to
740f231
Compare
Keep existing vertical regridding behavior intact while adding MPAS ocean/sea-ice post-ncclimo regridding and defaults for MPAS time metadata.
Include mpas_calendar and mpas_start_time in expected TS config dictionaries, and provide infer_section_parameters for the PCMDI dependency unit test.
Summary
Objectives:
Issue resolution:
pcmdi_diags#807.This pull request is to:
Small Change
1. Does this do what we want it to do?
2. Are the implementation details accurate & efficient?
3. Is this well documented?
4. Is this code clean?
-Pre-commit checks: All the pre-commits checks have passed.