Diagnostic parsing precedence
EAMxx resolves diagnostic field names through a series of pattern tests applied in a fixed priority order. Understanding this order lets you predict — and control — how composite names are parsed.
Priority order (highest to lowest)
-
Plain model field — the output layer first checks whether the name exists directly in the model field manager (FM). If it does, no diagnostic parsing is performed at all; the field is used as-is.
-
User-defined
:=aliases — if the name was declared as a:=alias in the output YAML, it is resolved to the aliased field or expression before any diagnostic parsing.
Items 3–9 below apply only when the name is not found by steps 1–2, i.e.
they describe the priority order inside the diagnostic parser
(create_diagnostic).
-
Built-in aliases — recognized shorthands expand before anything else. E.g.,
X_atm_backtend→X_minus_X_prev_over_dt. See Built-in aliases. -
_over_dtsuffix — checked before binary ops to preventX_over_dtfrom being misinterpreted asBinaryOpsDiag(X, over, dt). -
Specific named patterns —
_at_<level>,_at_<pressure>,_at_<height>,_horiz_avg,_vert_avg,_vert_sum,_zonal_avg,_pvert_derivative,_zvert_derivative,_where_..._op_<val|field>(conditional sampling), etc. -
Binary ops —
A_(plus|minus|times|over)_B. The first capture group is greedy, so the left operand extends as far as possible:A_minus_B_over_Cparses as(A_minus_B) over C, i.e.,(A−B)/C. Binary ops are checked before_prevso thatX_minus_X_prevcorrectly resolves asBinaryOpsDiag(X, minus, X_prev)rather thanFieldPrevDiag(X_minus_X). -
_prevsuffix — checked after binary ops so that the right-hand operand of a binary op can itself be a_prevfield. -
Histograms —
X_histogram_<bin_config>. -
Plain diagnostic name — a name none of the patterns above claimed, and that is a bare identifier, is looked up directly in the atmosphere-diagnostic factory.
-
Expressions — anything left is parsed as a diagnostic expression, e.g.
(qc+qv)*p_midorT_mid.mean('lev'). This is the last resort: there is nothing after it, so an expression that does not parse, or that no diagnostic implements, is an error rather than a fall-through. Being last is what keeps every name above working unchanged; it is not a statement about which syntax to prefer. Expressions are the intended replacement for items 3–8, which will eventually go away.
Key rules
Rule 1: greedy left operand for binary ops
The binary-ops regex is:
([A-Za-z0-9_.+\-\*\÷]+)_(plus|minus|times|over)_([A-Za-z0-9_.+\-\*\÷]+)$
The first group is greedy. For a string with multiple operator tokens, the rightmost operator becomes the outermost (lowest-precedence) operation:
A_minus_B_over_C → (A_minus_B) over C → (A − B) / C
Rule 2: _prev is resolved after binary ops
binary_ops is tested before field_prev, so X_minus_X_prev parses
as BinaryOpsDiag(X, minus, X_prev). Then X_prev recurses and is
resolved as FieldPrevDiag(X). A bare X_prev (no operator keyword)
still resolves as FieldPrevDiag(X) because binary_ops does not match
(there is no operator keyword in the string).
Worked example: the bt family
fields:
physics_pg2:
aliases:
# bt1 is an intermediate — needed by bt2 and bt_prod but not written to NC
- bt1:=f_minus_f_prev_over_dt
# Parsing:
# _over_dt suffix matched first → FieldOverDtDiag( f_minus_f_prev )
# f_minus_f_prev → BinaryOpsDiag( f, minus, f_prev )
# f_prev → FieldPrevDiag( f )
# Result: (f(t1) − f(t0)) / (t1 − t0)
# bt2 = tendency of the tendency (also intermediate)
- bt2:=bt1_minus_bt1_prev
# Only one "minus" token → BinaryOpsDiag( bt1, minus, bt1_prev )
# bt1_prev → FieldPrevDiag( bt1 )
field_names:
# bt_prod = product of the two tendencies — IS written to NC
- bt_prod:=bt1_times_bt2
# bt_osc_count = indicator that bt_prod is negative (tendency oscillating)
- bt_osc_count:=mask_where_bt_prod_lt_0
# ConditionalSampling: input=mask, condition_lhs=bt_prod, condition_cmp=lt, condition_rhs=0
The aliases section names intermediate sub-expressions that are needed
as inputs to other diagnostics but should not appear in the output file.
The field_names section lists what actually gets written to NetCDF.
Use := in both sections to give a convenient name to a complex expression.
Avoiding ambiguity
Use := aliases to break complex expressions into named sub-expressions.
This is always clearer than relying on implicit precedence.
Better still, write the thing as an expression, where the grouping is explicit and the two rules above do not apply at all:
A_minus_B_over_C → (A-B)/C by rule 1 above
(A-B)/C → (A-B)/C because you said so
A-(B/C) → A-(B/C) which rule 1 gives you no way to ask for