Skip to content

Give analysis output fields the units, standard name and cell methods of the field they reduce - #548

Draft
xylar wants to merge 3 commits into
E3SM-Project:developfrom
xylar:omega/fix-analysis-units
Draft

xylar wants to merge 3 commits into
E3SM-Project:developfrom
xylar:omega/fix-analysis-units

Conversation

@xylar

@xylar xylar commented Sep 13, 2026

Copy link
Copy Markdown

The analysis operators wrote every global statistic with empty units and standard name (#545), so the Polaris global-statistics plots had no axis labels. A spatial mean, minimum, maximum, standard deviation or time mean now carries the units and standard_name of its input and a CF cell_methods attribute naming the reduction, chained in the order applied (area: depth: mean time: mean for a time mean of a spatial mean).

For a reviewer to decide:

  • The spatial mean is unweighted, which area: mean does not say. A (comment: ...) clause could; I left it out.
  • time: mean makes cfchecks warn that time has no bounds. Time bounds are a separate change (Time-mean analysis output has no time bounds #549).

Operators whose result has different units (an area-weighted sum, a product) derive them with the new CFUnits class; nothing on develop needs that yet, but #481 does. Only the CF attribute names are read and written, so this is independent of #546.

Fixes #545

Checklist

  • Documentation:

  • Linting

  • Building

    • CMake build does not produce any new warnings from changes in this PR
  • Testing

    chrysalis, intel, openmpi

    • CTests Pass
    • Polaris omega_pr Pass
  • Provide relevant details in a comment to the PR titled Testing

  • New tests:

    • CTest unit tests for new features have been added per the approved design.

Posted by Claude Code on @xylar's behalf. The testing, analysis and wording above are AI-authored; please check them accordingly.

🤖 Generated with Claude Code

xylar and others added 3 commits September 13, 2026 02:56
Fields derived from other fields need units derived from theirs, and an
area-weighted sum or a product cannot simply copy them. CFUnits parses a
units string in the CF plain form (m s-1, kg m-3, 1), or the udunits caret
and slash spellings still used by some field definitions, into unit symbols
with integer exponents; supports the product, quotient and integer power;
and formats the result in the plain form with merged exponents, positive
exponents first.

An empty string is the unknown units of a field with no units attribute,
and unknown units propagate through every operation so a derived field
never claims units its inputs did not have. A malformed string is a Fail
error from parse(), and the string interfaces multiply(), divide() and
power() abort on it, since a bad units string is a programming error in a
field definition that should stop the run at initialization.

Includes a CTest covering parsing, formatting, the algebra, unknown
propagation and rejected strings, and a developer-guide page.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The analysis operators registered their output fields with empty units and
standard name, so every global statistic Omega wrote arrived with no units
(E3SM-Project#545). A mean, minimum, maximum, standard deviation or time mean has the
units and standard name of the field it reduces, and CF describes the
reduction itself with a cell_methods attribute rather than a different
standard name.

AnalysisOperator gains helpers for this: inheritMetadata() collects an
input's units, standard_name and cell_methods, each empty if the input has
none, and appends the cell method of the new reduction so the attribute
lists reductions in the order applied; spatialCellMethod() gives
"area: <method>" for a horizontal field and "area: depth: <method>" for a
layered one; createOutputField() creates the output Field with them. The
five operators use these, so a time mean of a spatial mean now carries
"area: depth: mean time: mean". An operator whose result has different
units derives them with CFUnits before creating its output.

Only the CF attribute names are read and written, and a missing attribute
is treated as empty, so this works the same whether or not Field::create
stores empty attributes.

The operator CTest checks the attributes every operator writes, a chained
time mean of a spatial mean, and that a field without units yields outputs
without units. The Analysis developer's and user's guides describe the
output metadata and how a new operator derives it.

Fixes E3SM-Project#545

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Error::createErrMsg resumed its scan for {} placeholders at the position
of the placeholder it had just replaced, so an argument that itself
contained braces (a units string such as m^{-2}) was scanned again and
its braces taken for further placeholders, logging "Not enough arguments
for placeholders in error msg". Resume the scan after the inserted
argument instead. ErrorTest now covers an argument with braces.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@xylar

xylar commented Sep 13, 2026

Copy link
Copy Markdown
Author

Testing

CTests on chrysalis with intel and openmpi: 50/50 pass, including the new CFUNITS_TEST and the extended ANALYSIS_OP_TEST and ERROR_TEST (build ~/e3sm_work/polaris/main/build_omega/build_chrysalis_intel).

Polaris omega_pr on chrysalis, intel, openmpi, built from this branch with Polaris main (ea15edbdbd): all passed, bit-for-bit with a develop (d086d49) baseline. Work directory /lcrc/group/e3sm/ac.xylar/polaris_1.1/chrysalis/test_20260913/omega_pr_fix_analysis_units_23181ea, baseline .../test_20260912/omega_pr_baseline_develop_d086d49. The four barotropic-channel property-check failures are present in the baseline too.

The global_stats_1DayInstants file from analysis_members_test now carries units, standard_name and cell_methods (e.g. area: depth: mean) on every statistic, and Polaris ocean/analysis/global_stats labels its axes with them (.../test_20260913/omega_analysis_fix_units). cfchecks on that file reports no cell_methods errors; its remaining errors (the sea_water_velocity standard name, no Conventions) are the ones #546 fixes.


Posted by Claude Code on @xylar's behalf. The testing, analysis and wording above are AI-authored; please check them accordingly.

@xylar

xylar commented Sep 13, 2026

Copy link
Copy Markdown
Author

The spatial mean is unweighted, which area: mean does not say. A (comment: ...) clause could; I left it out.

@brian-oneill, is that true? I would think we would always want an area-weighted horizontal mean, so if that isn't the current case, it sounds like a bug.

@xylar

xylar commented Sep 13, 2026

Copy link
Copy Markdown
Author

Need a fix for #551 before this can go in.

@xylar
xylar marked this pull request as draft September 13, 2026 15:19
@xylar
xylar marked this pull request as ready for review September 14, 2026 13:55
@xylar
xylar marked this pull request as draft September 14, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Analysis operators drop the units of the field they reduce

2 participants