@@ -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
@@ -512,6 +513,16 @@ run (const std::string& filename,
512
513
const auto & f_in = fm_after_hr->get_field (field_name);
513
514
auto & f_out = fm_scorpio->get_field (field_name);
514
515
516
+ // Safety check: if a field may contain fill values and we are computing an Average,
517
+ // we must have created an avg-count tracking field; otherwise division by the raw
518
+ // number of steps would bias the result wherever fill values occurred.
519
+ if (m_avg_type==OutputAvgType::Average && f_in.get_header ().may_be_filled ()) {
520
+ EKAT_REQUIRE_MSG (m_field_to_avg_count.count (field_name),
521
+ " [AtmosphereOutput::run] Error! Averaging a fill-aware field without avg-count tracking.\n "
522
+ " - field name : " + field_name + " \n "
523
+ " This indicates the field was marked may_be_filled after output initialization or tracking logic missed it." );
524
+ }
525
+
515
526
switch (m_avg_type) {
516
527
case OutputAvgType::Instant:
517
528
f_out.deep_copy (f_in); break ; // Note: if f_in aliases f_out, this is a no-op
@@ -1005,6 +1016,26 @@ init_diagnostics ()
1005
1016
for (const auto & fname : m_fields_names) {
1006
1017
if (not m_field_mgrs[FromModel]->has_field (fname)) {
1007
1018
create_diag (fname);
1019
+ } else {
1020
+ // This is a regular field, not a diagnostic.
1021
+ // Still, we might need to do some extra setup, like for avg_count.
1022
+ const auto & f = m_field_mgrs[FromModel]->get_field (fname);
1023
+ // We need avg-count tracking for any averaged (non-instant) field that:
1024
+ // - supplies explicit mask info (mask_data or mask_field), OR
1025
+ // - is marked as potentially containing fill values (may_be_filled()).
1026
+ // Without this, fill-aware updates skip fill_value during accumulation (good)
1027
+ // but we would still divide by the raw nsteps, biasing the result low.
1028
+ if (m_avg_type!=OutputAvgType::Instant) {
1029
+ const bool has_mask = f.get_header ().has_extra_data (" mask_data" ) || f.get_header ().has_extra_data (" mask_field" );
1030
+ const bool may_be_filled = f.get_header ().may_be_filled ();
1031
+ if (has_mask || may_be_filled) {
1032
+ m_track_avg_cnt = true ;
1033
+ // Avoid duplicate insertion if already present (e.g., mask + filled both true)
1034
+ if (m_field_to_avg_cnt_suffix.count (f.name ())==0 ) {
1035
+ m_field_to_avg_cnt_suffix.emplace (f.name (), " _" + f.name ());
1036
+ }
1037
+ }
1038
+ }
1008
1039
}
1009
1040
}
1010
1041
}
0 commit comments