Skip to content
23 changes: 23 additions & 0 deletions components/eamxx/docs/user/diags/field_contraction.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
In EAMxx, we can automatically calculate field reductions
across the horizontal columns and across the model vertical levels.
We call these horizontal and vertical reductions.
We can also automatically calculate zonal averages,
albeit with additional flexibility (see below).

## Horizontal reduction

Expand Down Expand Up @@ -58,6 +60,25 @@ The supported weighting options for now are
In the case of `pseudo_density`, the weighting is scaled by 1/g,
where g is the gravitational acceleration, in units of m/s$^2$.

## Zonal reduction

We currently have a utility to calculate zonal averages online.
To select the zonal average, you only need to suffix
a field name `X` with `_zonal_avg` and optionally the
number of bins `num_lat`. All zonal averages are calculated
using the area fraction in each bin as the weight.

For 180 latitude bins (the default), the bins are defined
as follows: [-90, -89), [-89, -88), ..., [89, 90).
For 90 latitude bins, the bins are defined as follows:
[-90, -88), [-88, -85), ..., [88, 90).
And so on...

| Reduction | Weight | Description |
| --------- | ------ | ----------- |
| `X_zonal_avg` | Area fraction | Average across 180 latitude bins |
| `X_zonal_avg_with_Y_bins` | Area fraction | Average across Y latitude bins |

## Example

```yaml
Expand All @@ -77,6 +98,8 @@ fields:
- T_mid_vert_sum_dz_weighted # K * m
- T_mid_vert_avg # K
- T_mid_vert_sum # K
- T_mid_zonal_avg # K
- T_mid_zonal_avg_with_90_bins # K
output_control:
frequency: 1
frequency_units: nmonths
Expand Down
11 changes: 9 additions & 2 deletions components/eamxx/src/share/io/eamxx_io_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ create_diagnostic (const std::string& diag_field_name,
std::regex vert_layer ("(z|geopotential|height)_(mid|int)$");
std::regex horiz_avg ("([A-Za-z0-9_]+)_horiz_avg$");
std::regex vert_contract ("([A-Za-z0-9_]+)_vert_(avg|sum)(_((dp|dz)_weighted))?$");
std::regex zonal_avg (R"(([A-Za-z0-9_]+)_zonal_avg_with_(\d+)_bins$)");
std::regex zonal_avg (R"(([A-Za-z0-9_]+)_zonal_avg(_with_(\d+)_bins)?$)");
Copy link
Contributor

@mahf708 mahf708 May 24, 2025

Choose a reason for hiding this comment

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

any last-minute thoughts about the naming here? I am not sure how I feel about "with X bins"

Maybe X_zonal_avg_in_Y_lats?

Also, we don't make the "lat" available and instead we rely on users to figure it out. Tha'ts ok, but we should likely add a helper string in the output ... lemme know what you think and I will address it (here or later)

Edit: I think it may make sense to rename "binN" to "latN" and then we can endow this dim with its actual values (the index is there automatically). But can do later, not necessarily now...

Copy link
Contributor

Choose a reason for hiding this comment

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

I would vote for "bandN" or "zoneN". "latN" seems to suggest a single latitude value.

Copy link
Contributor

Choose a reason for hiding this comment

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

On a different note: why is _with_ part of the captured group? I don't think using a default number of bins is a great idea, since ppl may forget to set that. I'd rather error out right away, and have users fix the diag name to specify the number of bins.

Copy link
Contributor

Choose a reason for hiding this comment

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

If you want to do a "default" zonal decomp, maybe we should use more "meaningful" (and not uniform) bounds, to get something like 2x polar zones, 2x temperate zones, and 1-2 tropical zones?

Copy link
Contributor

Choose a reason for hiding this comment

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

I just followed the description of the PR. I am fine with forcing a no-default and erroring out. So, let's see what @cjvogl prefers and we can tweak it.

If you want to do a "default" zonal decomp, maybe we should use more "meaningful"
(and not uniform) bounds, to get something like 2x polar zones, 2x temperate zones, and 1-2 tropical zones?

can you elaborate?

Copy link
Contributor

Choose a reason for hiding this comment

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

Well, doing 180 bins as default seems very arbitrary, and also maybe too little of a size for bin 1 and 180... I was thinking maybe something like

  • polar N: [66,90]
  • temperate N: [35,66.5]
  • sub-tropical N: [23.5,35]
  • tropical: [-23.5,23.5]
  • sub-tropical S: [-35,-23.5]
  • temperate S: [-66.5,-35]
  • polar S: [-90,-66.5]

They are still somewhat arbitrary, but probably more linked to relevant regions of analysis?

Copy link
Contributor

@mahf708 mahf708 May 28, 2025

Choose a reason for hiding this comment

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

It's often the case people tend to look at zonal means like designed here, e.g., e3sm_diags zonal plots and zonal-annual plots. The default of 180 (one-lat-deg spacing) is pretty reasonable if we want to keep a default. However, I am fine with getting rid of the defaults, and forcing users to specify one.

What you're listing (polar, mid-lat, subtrop, trop, etc.) is a derivative of the more general zonal average stuff, but it is more tightly related to horiz avg. I would consider adding those regions as special cases of horiz_avg instead of here...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would vote for "bandN" or "zoneN". "latN" seems to suggest a single latitude value.

The motivation behind bin is that one can think of a zonal average as a histogram, and thus a name like "bin" could be reused if someone wants to have a histogram in a different dimension than along latitude. As a concrete example, we intend to introduce a diagnostic where one can output a histogram in the vertical direction to reduce the output size from a high resolution run.

The default of 180 (one-lat-deg spacing) is pretty reasonable if we want to keep a default.

The one-lat-deg spacing was the original idea, but I was not beholden to it (or any default).

Copy link
Contributor

Choose a reason for hiding this comment

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

That makes sense. But the [-90,-89) band (e.g.) will have much fewer cols than the [-1,0) band, so is the histogram meaningful there? But that's beyond the scope, i am ok with bin as a name. I will integrate this as soon as testing is done.


std::string diag_name;
std::smatch matches;
Expand Down Expand Up @@ -215,7 +215,14 @@ create_diagnostic (const std::string& diag_field_name,
diag_name = "ZonalAvgDiag";
params.set("grid_name", grid->name());
params.set<std::string>("field_name", matches[1].str());
params.set<std::string>("number_of_zonal_bins", matches[2].str());
// The second match is optional
if (matches[2].matched) {
// note that the 3rd match is the number of bins
params.set<std::string>("number_of_zonal_bins", matches[3].str());
} else {
// set number_of_zonal_bins to default 180
params.set<std::string>("number_of_zonal_bins", "180");
}
}
else
{
Expand Down
Loading