Skip to content

Commit 88d3532

Browse files
committed
TMP: fix aggr, add test, will be revised
1 parent bb1e9de commit 88d3532

File tree

6 files changed

+165
-3
lines changed

6 files changed

+165
-3
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env python3
2+
3+
4+
import os
5+
import sys
6+
7+
# add eamxx scripts dir to get stuff from there
8+
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../../../scripts'))
9+
# get the pylib stuff and ensure we have xarray
10+
from utils import _ensure_pylib_impl
11+
_ensure_pylib_impl('xarray')
12+
_ensure_pylib_impl('numpy')
13+
14+
15+
# get the input arg which is the case dir
16+
case_dir = sys.argv[1]
17+
# parse the case name, which is the string after the last "/"
18+
case_name = case_dir.split('/')[-1]
19+
20+
import xarray as xr
21+
22+
# before the restart
23+
ds_avg = xr.open_dataset(case_dir + '/run/' + case_name + '.scream.average_5_steps.h.AVERAGE.nsteps_x5.0001-01-01-00000.nc')
24+
ds_ins = xr.open_dataset(case_dir + '/run/' + case_name + '.scream.instant_every_step.h.INSTANT.nsteps_x1.0001-01-01-03600.nc')
25+
26+
import numpy as np
27+
assert np.allclose(ds_avg.isccp_cldtot.values, ds_ins.isccp_cldtot.mean('time', skipna=True).values, equal_nan=True)
28+
29+
print("PASS")
30+
sys.exit(0)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/bin/bash
2+
3+
cime_root=$(./xmlquery --value CIMEROOT)
4+
input_data_dir=$(./xmlquery --value DIN_LOC_ROOT)
5+
atmchange=$cime_root/../components/eamxx/scripts/atmchange
6+
case_name=$(./xmlquery --value CASE)
7+
8+
# Turn on cosp and set frequency
9+
$atmchange -b physics::atm_procs_list="mac_aero_mic,rrtmgp,cosp"
10+
$atmchange -b physics::cosp::cosp_frequency_units="steps"
11+
$atmchange -b physics::cosp::cosp_frequency=4
12+
13+
# set rrtmgp frequency to be half of that of cosp
14+
$atmchange -b physics::rrtmgp::rad_frequency=1
15+
16+
# Need to explicitly turn on computing tendencies
17+
$atmchange -b physics::mac_aero_mic::shoc::compute_tendencies=T_mid,qv
18+
$atmchange -b physics::mac_aero_mic::p3::compute_tendencies=T_mid,qv
19+
$atmchange -b physics::rrtmgp::compute_tendencies=T_mid
20+
$atmchange -b homme::compute_tendencies=T_mid,qv
21+
22+
# determine grid and set remap files
23+
atm_grid=$(./xmlquery --value ATM_GRID)
24+
if [[ "${atm_grid}" = "ne30np4.pg2" ]]; then
25+
hmapfile="${input_data_dir}/atm/scream/maps/map_ne30pg2_to_ne4pg2_20231201.nc"
26+
armmapfile="${input_data_dir}/atm/scream/maps/map_ne30pg2_to_DecadalSites_c20240130.nc"
27+
# Run with bugfixed SPA file
28+
$atmchange -b spa_data_file="${input_data_dir}/atm/scream/init/spa_v3.LR.F2010.2011-2025.c_20240405.nc"
29+
elif [[ "${atm_grid}" = "ne4np4.pg2" ]]; then
30+
hmapfile="${input_data_dir}/atm/scream/maps/map_ne4pg2_to_ne2pg2_c20240902.nc"
31+
echo "Note: arm remap only works for ne30pg2 atm grids for now"
32+
armmapfile="not-supported-yet"
33+
# Keep default SPA file
34+
# ... (do nothing)
35+
else
36+
echo "Note: horiz/arm remaps only work for ne30pg2 and ne4pg2 atm grids for now"
37+
hmapfile="not-supported-yet"
38+
armmapfile="not-supported-yet"
39+
fi
40+
41+
# set the output yaml files
42+
output_yaml_files=$(find ${cime_root}/../components/eamxx/cime_config/testdefs/testmods_dirs/eamxx/rad_cosp_async/yaml_outs/ -maxdepth 1 -type f)
43+
for file in ${output_yaml_files[@]}; do
44+
# if the word "coarse" is in the file name, do nothing
45+
if [[ "${file}" == *"_coarse.yaml" && "${hmapfile}" == "not-supported-yet" ]]; then
46+
continue
47+
elif [[ "${file}" == *"_arm.yaml" && "${armmapfile}" == "not-supported-yet" ]]; then
48+
continue
49+
else
50+
# TODO: add remap file replacement for different grids
51+
cp -v ${file} ./
52+
if [ "${file}" == "${output_yaml_files[0]}" ]; then
53+
# First file, reset output list
54+
$atmchange -b output_yaml_files="./$(basename ${file})"
55+
else
56+
# Append to output list
57+
$atmchange -b output_yaml_files+="./$(basename ${file})"
58+
fi
59+
# Replace remap files
60+
sed -i "s|horiz_remap_file:.*_to_ne30.*|horiz_remap_file: ${hmapfile}|" ./$(basename ${file})
61+
sed -i "s|horiz_remap_file:.*_to_DecadalSites.*|horiz_remap_file: ${armmapfile}|" ./$(basename ${file})
62+
fi
63+
# replace all filename prefixes so that st_archive works...
64+
sed -i "s|eamxx_output.decadal|${case_name}.scream|" ./$(basename ${file})
65+
done
66+
67+
chmod +x postrun_check.py
68+
./xmlchange POSTRUN_SCRIPT=${cime_root}/../components/eamxx/cime_config/testdefs/testmods_dirs/eamxx/rad_cosp_async/postrun_check.py
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
%YAML 1.1
2+
---
3+
filename_prefix: eamxx_output.decadal.average_5_steps.h
4+
iotype: pnetcdf
5+
averaging_type: average
6+
max_snapshots_per_file: 1
7+
fields:
8+
physics_pg2:
9+
field_names:
10+
- isccp_ctptau
11+
- modis_ctptau
12+
- misr_cthtau
13+
- isccp_cldtot
14+
output_control:
15+
frequency: 5
16+
frequency_units: nsteps
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
%YAML 1.1
2+
---
3+
filename_prefix: eamxx_output.decadal.instant_every_step.h
4+
iotype: pnetcdf
5+
averaging_type: instant
6+
max_snapshots_per_file: 5
7+
fields:
8+
physics_pg2:
9+
field_names:
10+
- isccp_ctptau
11+
- modis_ctptau
12+
- misr_cthtau
13+
- isccp_cldtot
14+
output_control:
15+
frequency: 1
16+
frequency_units: nsteps

components/eamxx/src/physics/cosp/eamxx_cosp.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ void Cosp::initialize_impl (const RunType /* run_type */)
113113
for (const auto& field_name : vnames) {
114114
// the mask here is just the sunlit mask, so set it
115115
get_field_out(field_name).get_header().set_extra_data("mask_field", get_field_in("sunlit_mask"));
116+
get_field_out(field_name).get_header().set_may_be_filled(true);
116117
}
117118
}
118119

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

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ AtmosphereOutput (const ekat::Comm& comm, const ekat::ParameterList& params,
146146
EKAT_REQUIRE_MSG (not has_duplicates(m_fields_names),
147147
"[AtmosphereOutput] Error! One of the output yaml files has duplicate field entries.\n"
148148
" - yaml file: " + params.name() + "\n"
149-
" - fields names; " + ekat::join(m_fields_names,",") + "\n");
149+
" - fields names: " + ekat::join(m_fields_names,",") + "\n");
150150

151151
// Check if remapping and if so create the appropriate remapper
152152
// Note: We currently support three remappers
@@ -174,6 +174,7 @@ AtmosphereOutput (const ekat::Comm& comm, const ekat::ParameterList& params,
174174
}
175175

176176
// ... then add diagnostic fields
177+
// ... and we also use this to init track avg cnt for regular fields
177178
init_diagnostics ();
178179

179180
// Avg count only makes sense if we have
@@ -462,8 +463,8 @@ run (const std::string& filename,
462463
auto field = fm_after_hr->get_field(fname);
463464
auto mask = count.get_header().get_extra_data<Field>("mask");
464465

465-
// Find where the field is NOT equal to fill_value
466-
compute_mask<Comparison::NE>(field,constants::fill_value<Real>,mask);
466+
// Find where the field is NOT equal to fill_value
467+
compute_mask<Comparison::NE>(field,constants::fill_value<Real>,mask);
467468

468469
// mask=1 for "good" entries, and mask=0 otherwise.
469470
count.update(mask,1,1);
@@ -508,6 +509,16 @@ run (const std::string& filename,
508509
const auto& f_in = fm_after_hr->get_field(field_name);
509510
auto& f_out = fm_scorpio->get_field(field_name);
510511

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+
511522
switch (m_avg_type) {
512523
case OutputAvgType::Instant:
513524
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 ()
10031014
for (const auto& fname : m_fields_names) {
10041015
if (not m_field_mgrs[FromModel]->has_field(fname)) {
10051016
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+
}
10061037
}
10071038
}
10081039
}

0 commit comments

Comments
 (0)