@@ -146,7 +146,7 @@ AtmosphereOutput (const ekat::Comm& comm, const ekat::ParameterList& params,
146
146
EKAT_REQUIRE_MSG (not has_duplicates (m_fields_names),
147
147
" [AtmosphereOutput] Error! One of the output yaml files has duplicate field entries.\n "
148
148
" - yaml file: " + params.name () + " \n "
149
- " - fields names; " + ekat::join (m_fields_names," ," ) + " \n " );
149
+ " - fields names: " + ekat::join (m_fields_names," ," ) + " \n " );
150
150
151
151
// Check if remapping and if so create the appropriate remapper
152
152
// Note: We currently support three remappers
@@ -174,6 +174,7 @@ AtmosphereOutput (const ekat::Comm& comm, const ekat::ParameterList& params,
174
174
}
175
175
176
176
// ... then add diagnostic fields
177
+ // ... and we also use this to init track avg cnt for regular fields
177
178
init_diagnostics ();
178
179
179
180
// Avg count only makes sense if we have
@@ -508,6 +509,16 @@ run (const std::string& filename,
508
509
const auto & f_in = fm_after_hr->get_field (field_name);
509
510
auto & f_out = fm_scorpio->get_field (field_name);
510
511
512
+ // Safety check: if a field may contain fill values and we are computing an Average,
513
+ // we must have created an avg-count tracking field; otherwise division by the raw
514
+ // number of steps would bias the result wherever fill values occurred.
515
+ if (m_avg_type==OutputAvgType::Average && f_in.get_header ().may_be_filled ()) {
516
+ EKAT_REQUIRE_MSG (m_field_to_avg_count.count (field_name),
517
+ " [AtmosphereOutput::run] Error! Averaging a fill-aware field without avg-count tracking.\n "
518
+ " - field name : " + field_name + " \n "
519
+ " This indicates the field was marked may_be_filled after output initialization or tracking logic missed it." );
520
+ }
521
+
511
522
switch (m_avg_type) {
512
523
case OutputAvgType::Instant:
513
524
f_out.deep_copy (f_in); break ; // Note: if f_in aliases f_out, this is a no-op
@@ -1003,6 +1014,26 @@ init_diagnostics ()
1003
1014
for (const auto & fname : m_fields_names) {
1004
1015
if (not m_field_mgrs[FromModel]->has_field (fname)) {
1005
1016
create_diag (fname);
1017
+ } else {
1018
+ // This is a regular field, not a diagnostic.
1019
+ // Still, we might need to do some extra setup, like for avg_count.
1020
+ const auto & f = m_field_mgrs[FromModel]->get_field (fname);
1021
+ // We need avg-count tracking for any averaged (non-instant) field that:
1022
+ // - supplies explicit mask info (mask_data or mask_field), OR
1023
+ // - is marked as potentially containing fill values (may_be_filled()).
1024
+ // Without this, fill-aware updates skip fill_value during accumulation (good)
1025
+ // but we would still divide by the raw nsteps, biasing the result low.
1026
+ if (m_avg_type!=OutputAvgType::Instant) {
1027
+ const bool has_mask = f.get_header ().has_extra_data (" mask_data" ) || f.get_header ().has_extra_data (" mask_field" );
1028
+ const bool may_be_filled = f.get_header ().may_be_filled ();
1029
+ if (has_mask || may_be_filled) {
1030
+ m_track_avg_cnt = true ;
1031
+ // Avoid duplicate insertion if already present (e.g., mask + filled both true)
1032
+ if (m_field_to_avg_cnt_suffix.count (f.name ())==0 ) {
1033
+ m_field_to_avg_cnt_suffix.emplace (f.name (), " _" + f.name ());
1034
+ }
1035
+ }
1036
+ }
1006
1037
}
1007
1038
}
1008
1039
}
0 commit comments