Skip to content

Commit 501db3d

Browse files
committed
replace hidden alias operator ":=:" with "aliases" section
1 parent 599e63d commit 501db3d

File tree

4 files changed

+71
-58
lines changed

4 files changed

+71
-58
lines changed

components/eamxx/docs/user/diags/parsing_precedence.md

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -60,30 +60,35 @@ still resolves as `FieldPrevDiag(X)` because `binary_ops` does not match
6060
## Worked example: the `bt` family
6161

6262
```yaml
63-
field_names:
64-
# bt1 = atmospheric backward tendency of f
65-
- bt1:=f_minus_f_prev_over_dt
66-
# Parsing:
67-
# _over_dt suffix matched first → FieldOverDtDiag( f_minus_f_prev )
68-
# f_minus_f_prev → BinaryOpsDiag( f, minus, f_prev )
69-
# f_prev → FieldPrevDiag( f )
70-
# Result: (f(t1) − f(t0)) / (t1 − t0)
71-
72-
# bt2 = tendency of the tendency
73-
- bt2:=bt1_minus_bt1_prev
74-
# Only one "minus" token → BinaryOpsDiag( bt1, minus, bt1_prev )
75-
# bt1_prev → FieldPrevDiag( bt1 )
76-
77-
# bt_prod = product of the two tendencies
78-
- bt_prod:=bt1_times_bt2
79-
80-
# bt_osc_count = indicator that bt_prod is negative (tendency oscillating)
81-
- bt_osc_count:=count_where_bt_prod_lt_0
82-
# ConditionalSampling: input=count, condition_field=bt_prod, op=lt, value=0
63+
fields:
64+
physics_pg2:
65+
aliases:
66+
# bt1 is an intermediate — needed by bt2 and bt_prod but not written to NC
67+
- bt1:=f_minus_f_prev_over_dt
68+
# Parsing:
69+
# _over_dt suffix matched first → FieldOverDtDiag( f_minus_f_prev )
70+
# f_minus_f_prev → BinaryOpsDiag( f, minus, f_prev )
71+
# f_prev → FieldPrevDiag( f )
72+
# Result: (f(t1) − f(t0)) / (t1 − t0)
73+
74+
# bt2 = tendency of the tendency (also intermediate)
75+
- bt2:=bt1_minus_bt1_prev
76+
# Only one "minus" token → BinaryOpsDiag( bt1, minus, bt1_prev )
77+
# bt1_prev → FieldPrevDiag( bt1 )
78+
79+
field_names:
80+
# bt_prod = product of the two tendencies — IS written to NC
81+
- bt_prod:=bt1_times_bt2
82+
83+
# bt_osc_count = indicator that bt_prod is negative (tendency oscillating)
84+
- bt_osc_count:=count_where_bt_prod_lt_0
85+
# ConditionalSampling: input=count, condition_field=bt_prod, op=lt, value=0
8386
```
8487

85-
The `:=` alias syntax names each sub-expression before composing further,
86-
giving you full control over evaluation order.
88+
The `aliases` section names intermediate sub-expressions that are needed
89+
as inputs to other diagnostics but should not appear in the output file.
90+
The `field_names` section lists what actually gets written to NetCDF.
91+
Use `:=` in both sections to give a convenient name to a complex expression.
8792

8893
## Avoiding ambiguity
8994

components/eamxx/docs/user/io_aliases.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,28 +65,33 @@ alias of the former.
6565
are preserved from the original fields, and `alias_of`
6666
is added to the netcdf files to document aliasing
6767

68-
## Intermediate-only fields (`:=:` syntax)
68+
## Intermediate-only fields (`aliases` section)
6969

7070
Sometimes a diagnostic is only needed as a building block for another
7171
diagnostic, and you do not want it written to the NetCDF output file.
72-
Use `alias:=:original` (note the extra colon) to declare such fields:
72+
Declare such fields in an `aliases` subsection of the per-grid block:
7373

7474
```yaml
75-
field_names:
76-
- "MyDiag:=:T_mid_times_p_mid" # computed but NOT written to NC
77-
- "MyDiagSqrd:=MyDiag_times_MyDiag" # uses MyDiag; IS written to NC
75+
fields:
76+
physics_pg2:
77+
aliases:
78+
- "MyDiag:=T_mid_times_p_mid" # computed but NOT written to NC
79+
field_names:
80+
- "MyDiagSqrd:=MyDiag_times_MyDiag" # uses MyDiag; IS written to NC
7881
```
7982

8083
`MyDiag` is created and registered in the internal field manager so that
8184
`MyDiagSqrd` can depend on it, but it does not appear in the output file.
8285
Only `MyDiagSqrd` is written.
8386

87+
The `aliases` section uses the same `alias:=original` syntax as
88+
`field_names` entries, and the expression on the right-hand side is
89+
resolved with the same composable-diagnostic parser.
90+
8491
Rules:
8592

86-
- A name declared with `:=:` must not also appear as a regular output field.
87-
- The same alias name cannot be used for both `:=:` and `:=`.
88-
- The expression after `:=:` is resolved with the same composable-diagnostic
89-
parser as any other field name.
93+
- A name declared in `aliases` must not also appear in `field_names`.
94+
- The same alias name cannot appear in both `aliases` and `field_names`.
9095

9196
## Caveats
9297

components/eamxx/src/share/io/scorpio_output.cpp

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,13 @@ AtmosphereOutput::AtmosphereOutput(const ekat::Comm &comm, const ekat::Parameter
137137
m_fields_names.clear();
138138
}
139139
}
140+
if (pl.isParameter("aliases")) {
141+
if (pl.isType<vos_t>("aliases")) {
142+
m_intermediate_aliases = pl.get<vos_t>("aliases");
143+
} else {
144+
m_intermediate_aliases.push_back(pl.get<std::string>("aliases"));
145+
}
146+
}
140147
}
141148
}
142149
EKAT_REQUIRE_MSG (grid_found,
@@ -982,40 +989,31 @@ process_requested_fields()
982989
auto fm_model = m_field_mgrs[FromModel];
983990
auto fm_grid = m_field_mgrs[FromModel]->get_grid();
984991

985-
// First, find intermediate-only fields declared with the ':=:' syntax.
986-
// These are created and added to the field manager (so dependents can use them),
987-
// but are NOT registered with scorpio and do NOT appear in NC output.
992+
// Process intermediate-only fields declared in the 'aliases' YAML section.
993+
// Each entry has the form "alias:=original". These are created and registered
994+
// in the field manager so that dependents can use them, but are NOT written
995+
// to the NC output file.
988996
std::set<std::string> intermediate_names;
989-
for (auto& name : m_fields_names) {
990-
auto sep3 = name.find(":=:");
991-
if (sep3 != std::string::npos) {
992-
auto alias = name.substr(0, sep3);
993-
auto orig = name.substr(sep3 + 3);
994-
EKAT_REQUIRE_MSG(!alias.empty() && !orig.empty(),
995-
"Error! Invalid intermediate field request. Should be 'alias:=:original'.\n"
996-
" - request: " + name + "\n");
997-
EKAT_REQUIRE_MSG (m_alias_to_orig.count(alias)==0,
998-
"Error! Intermediate-only alias conflicts with an existing alias.\n"
999-
" - stream name: " + m_stream_name + "\n"
1000-
" - alias: " + alias + "\n");
1001-
m_alias_to_orig[alias] = orig;
1002-
intermediate_names.insert(alias);
1003-
name = ""; // mark for removal from m_fields_names
1004-
}
1005-
}
1006-
// Remove blanked entries (intermediate-only fields)
1007-
{
1008-
std::vector<std::string> tmp;
1009-
for (const auto& n : m_fields_names)
1010-
if (!n.empty()) tmp.push_back(n);
1011-
m_fields_names = std::move(tmp);
997+
for (const auto& spec : m_intermediate_aliases) {
998+
auto tokens = ekat::split(spec, ":=");
999+
EKAT_REQUIRE_MSG(tokens.size()==2 && !tokens[0].empty() && !tokens[1].empty(),
1000+
"Error! Invalid entry in 'aliases' section. Should be 'alias:=original'.\n"
1001+
" - entry: " + spec + "\n");
1002+
const auto& alias = tokens[0];
1003+
const auto& orig = tokens[1];
1004+
EKAT_REQUIRE_MSG(m_alias_to_orig.count(alias)==0,
1005+
"Error! Intermediate alias conflicts with an existing alias.\n"
1006+
" - stream name: " + m_stream_name + "\n"
1007+
" - alias: " + alias + "\n");
1008+
m_alias_to_orig[alias] = orig;
1009+
intermediate_names.insert(alias);
10121010
}
10131011

10141012
// Check that no intermediate name also appears as a regular output field
10151013
for (const auto& iname : intermediate_names) {
10161014
for (const auto& fname : m_fields_names) {
10171015
EKAT_REQUIRE_MSG(fname != iname,
1018-
"Error! A field declared as intermediate-only (':=:') also appears as a regular output.\n"
1016+
"Error! A field declared in the 'aliases' section also appears in 'field_names'.\n"
10191017
" - stream name: " + m_stream_name + "\n"
10201018
" - field name: " + iname + "\n");
10211019
}

components/eamxx/src/share/io/scorpio_output.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ class AtmosphereOutput
214214
// Internal maps to the output fields, how the columns are distributed, the file dimensions and
215215
// the global ids.
216216
strvec_t m_fields_names;
217+
// Intermediate-only aliases declared in the 'aliases' YAML section.
218+
// Each entry has the form "alias:=original". These fields are created and
219+
// registered in the field manager so that other diagnostics can depend on
220+
// them, but they are NOT written to the NC output file.
221+
strvec_t m_intermediate_aliases;
217222
strmap_t<Field> m_field_to_avg_count;
218223
std::vector<Field> m_avg_counts;
219224
strmap_t<std::string> m_field_to_avg_cnt_suffix;

0 commit comments

Comments
 (0)