Skip to content

Commit f221fc0

Browse files
committed
Merge branch 'mahf708/eamxx/cosp-expected-behavior' into next (PR #7652)
persist cosp-computed fields when cosp in not run, properly handle fill-value treatment for night indices, and update docs to reflect new behavior. [NBFB]
2 parents f805a61 + bb1e9de commit f221fc0

File tree

10 files changed

+30
-86
lines changed

10 files changed

+30
-86
lines changed

components/eamxx/cime_config/testdefs/testmods_dirs/eamxx/mvkpert/yaml_outs/monthly_average_coarse.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ fields:
8484
- isccp_ctptau
8585
- modis_ctptau
8686
- misr_cthtau
87-
- cosp_sunlit
8887
- isccp_cldtot
8988

9089
output_control:

components/eamxx/cime_config/testdefs/testmods_dirs/eamxx/prod/yaml_outs/eamxx_output.decadal.1hourlyAVG_native.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ fields:
1111
- isccp_ctptau
1212
- modis_ctptau
1313
- misr_cthtau
14-
- cosp_sunlit
1514
- isccp_cldtot
1615
output_control:
1716
frequency: 1

components/eamxx/cime_config/testdefs/testmods_dirs/eamxx/prod/yaml_outs/eamxx_output.decadal.hourlyAVG_coarse.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ fields:
8484
- isccp_ctptau
8585
- modis_ctptau
8686
- misr_cthtau
87-
- cosp_sunlit
8887
- isccp_cldtot
8988

9089
output_control:

components/eamxx/docs/user/cosp.md

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ fields:
5555
- isccp_ctptau
5656
- modis_ctptau
5757
- misr_cthtau
58-
- cosp_sunlit
5958
max_snapshots_per_file: 1
6059
filename_prefix: eamxx
6160
output_control:
@@ -76,19 +75,3 @@ The following output fields are available:
7675
- MODIS-simulated cloud top pressure/optical depth joint histogram
7776
- `misr_cthtau`
7877
- MISR-simulated cloud top height/optical depth joint histogram
79-
- `cosp_sunlit`
80-
- sunlit flag aggregated at COSP frequency for renormalizing daytime averages
81-
82-
ISCCP, MODIS, and MISR outputs are valid only for daytime/sunlit columns
83-
(to be consistent with available satellite retrievals).
84-
In order to aggregate only daytime columns in time averages, these outputs are
85-
multiplied by the sunlit flag (0 or 1) at each COSP calculation time.
86-
Time averages of these quantities are then aggregated, along with the COSP
87-
sunlit flag each time COSP is called.
88-
In order to back out the daytime-only time averages from the outputs,
89-
one needs to divide the output fields by `cosp_sunlit`.
90-
E.g.,
91-
92-
```shell
93-
isccp_ctptau = mean(isccp_ctptau) / mean(cosp_sunlit)
94-
```

components/eamxx/src/diagnostics/aodvis.cpp

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ set_grids(const std::shared_ptr<const GridsManager> grids_manager)
3131

3232
// The fields required for this diagnostic to be computed
3333
add_field<Required>("aero_tau_sw", vector3d, nondim, grid_name);
34-
add_field<Required>("sunlit", scalar2d, nondim, grid_name);
34+
add_field<Required>("sunlit_mask", scalar2d, nondim, grid_name);
3535

3636
// Construct and allocate the aodvis field
3737
FieldIdentifier fid(name(), scalar2d, nondim, grid_name);
@@ -41,21 +41,7 @@ set_grids(const std::shared_ptr<const GridsManager> grids_manager)
4141
}
4242

4343
void AODVis::initialize_impl(const RunType /*run_type*/) {
44-
// we use initialize_impl to primarily deal with the mask
45-
using namespace ekat::units;
46-
using namespace ShortFieldTagsNames;
47-
48-
auto nondim = ekat::units::Units::nondimensional();
49-
const auto &grid_name =
50-
m_diagnostic_output.get_header().get_identifier().get_grid_name();
51-
52-
std::string mask_name = name() + " mask";
53-
FieldLayout mask_layout({COL}, {m_ncols});
54-
FieldIdentifier mask_fid(mask_name, mask_layout, nondim, grid_name);
55-
Field diag_mask(mask_fid);
56-
diag_mask.allocate_view();
57-
58-
m_diagnostic_output.get_header().set_extra_data("mask_field", diag_mask);
44+
m_diagnostic_output.get_header().set_extra_data("mask_field", get_field_in("sunlit_mask"));
5945
}
6046

6147
void AODVis::compute_diagnostic_impl() {
@@ -67,13 +53,10 @@ void AODVis::compute_diagnostic_impl() {
6753
constexpr auto fill_value = constants::fill_value<Real>;
6854

6955
const auto aod = m_diagnostic_output.get_view<Real *>();
70-
const auto mask = m_diagnostic_output.get_header()
71-
.get_extra_data<Field>("mask_field")
72-
.get_view<Real *>();
7356
const auto tau_vis = get_field_in("aero_tau_sw")
7457
.subfield(1, m_vis_bnd)
7558
.get_view<const Real **>();
76-
const auto sunlit = get_field_in("sunlit").get_view<const Real *>();
59+
const auto sunlit = get_field_in("sunlit_mask").get_view<const Real *>();
7760

7861
const auto num_levs = m_nlevs;
7962
const auto policy = TPF::get_default_team_policy(m_ncols, m_nlevs);
@@ -82,11 +65,9 @@ void AODVis::compute_diagnostic_impl() {
8265
const int icol = team.league_rank();
8366
if(sunlit(icol) == 0.0) {
8467
aod(icol) = fill_value;
85-
Kokkos::single(Kokkos::PerTeam(team), [&] { mask(icol) = 0; });
8668
} else {
8769
auto tau_icol = ekat::subview(tau_vis, icol);
8870
aod(icol) = RU::view_reduction(team, 0, num_levs, tau_icol);
89-
Kokkos::single(Kokkos::PerTeam(team), [&] { mask(icol) = 1; });
9071
}
9172
});
9273
}

components/eamxx/src/diagnostics/tests/aodvis_test.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ TEST_CASE("aodvis") {
6262
tau.get_header().get_tracking().update_time_stamp(t0);
6363
// Input (randomized) sunlit
6464
FieldLayout scalar2d_layout = grid->get_2d_scalar_layout();
65-
FieldIdentifier sunlit_fid("sunlit", scalar2d_layout, nondim, grid->name());
65+
FieldIdentifier sunlit_fid("sunlit_mask", scalar2d_layout, nondim, grid->name());
6666
Field sunlit(sunlit_fid);
6767
sunlit.allocate_view();
6868
sunlit.get_header().get_tracking().update_time_stamp(t0);
@@ -112,6 +112,7 @@ TEST_CASE("aodvis") {
112112

113113
const auto tau_h = tau.get_view<const Real ***, Host>();
114114
const auto aod_hf = diag->get_diagnostic();
115+
const auto aod_mask = aod_hf.get_header().get_extra_data<Field>("mask_field");
115116

116117
Field aod_tf = diag->get_diagnostic().clone();
117118
auto aod_t = aod_tf.get_view<Real *, Host>();
@@ -133,6 +134,8 @@ TEST_CASE("aodvis") {
133134
if(SCREAM_BFB_TESTING) {
134135
REQUIRE(views_are_equal(aod_hf, aod_tf));
135136
}
137+
// Ensure the masks are identical (should be pointing exactly to each other)
138+
REQUIRE(views_are_equal(aod_mask, sunlit));
136139
}
137140
}
138141

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

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "eamxx_cosp.hpp"
22
#include "cosp_functions.hpp"
33
#include "physics/share/physics_constants.hpp"
4+
#include "share/util/eamxx_universal_constants.hpp"
45
#include "share/property_checks/field_within_interval_check.hpp"
56
#include "share/field/field_utils.hpp"
67

@@ -65,7 +66,7 @@ void Cosp::set_grids(const std::shared_ptr<const GridsManager> grids_manager)
6566
add_field<Required>("surf_radiative_T", scalar2d , K, grid_name);
6667
//add_field<Required>("surfelev", scalar2d , m, grid_name);
6768
//add_field<Required>("landmask", scalar2d , nondim, grid_name);
68-
add_field<Required>("sunlit", scalar2d , nondim, grid_name);
69+
add_field<Required>("sunlit_mask", scalar2d , nondim, grid_name);
6970
add_field<Required>("p_mid", scalar3d_mid, Pa, grid_name);
7071
add_field<Required>("p_int", scalar3d_int, Pa, grid_name);
7172
//add_field<Required>("height_mid", scalar3d_mid, m, grid_name);
@@ -88,11 +89,11 @@ void Cosp::set_grids(const std::shared_ptr<const GridsManager> grids_manager)
8889
add_field<Required>("eff_radius_qc", scalar3d_mid, micron, grid_name);
8990
add_field<Required>("eff_radius_qi", scalar3d_mid, micron, grid_name);
9091
// Set of fields used strictly as output
92+
// NOTE we set their corresponding masks in init impl
9193
add_field<Computed>("isccp_cldtot", scalar2d, percent, grid_name);
9294
add_field<Computed>("isccp_ctptau", scalar4d_ctptau, percent, grid_name, 1);
9395
add_field<Computed>("modis_ctptau", scalar4d_ctptau, percent, grid_name, 1);
9496
add_field<Computed>("misr_cthtau", scalar4d_cthtau, percent, grid_name, 1);
95-
add_field<Computed>("cosp_sunlit", scalar2d, nondim, grid_name);
9697

9798
// We can allocate these now
9899
m_z_mid = Field(FieldIdentifier("z_mid",scalar3d_mid,m,grid_name));
@@ -107,15 +108,11 @@ void Cosp::initialize_impl (const RunType /* run_type */)
107108
// Set property checks for fields in this process
108109
CospFunc::initialize(m_num_cols, m_num_subcols, m_num_levs);
109110

110-
111-
// Add note to output files about processing ISCCP fields that are only valid during
112-
// daytime. This can go away once I/O can handle masked time averages.
113-
using stratts_t = std::map<std::string,std::string>;
111+
// Set the mask field for each of the cosp computed fields
114112
std::list<std::string> vnames = {"isccp_cldtot", "isccp_ctptau", "modis_ctptau", "misr_cthtau"};
115-
for (const auto field_name : {"isccp_cldtot", "isccp_ctptau", "modis_ctptau", "misr_cthtau"}) {
116-
auto& f = get_field_out(field_name);
117-
auto& atts = f.get_header().get_extra_data<stratts_t>("io: string attributes");
118-
atts["note"] = "Night values are zero; divide by cosp_sunlit to get daytime mean";
113+
for (const auto& field_name : vnames) {
114+
// the mask here is just the sunlit mask, so set it
115+
get_field_out(field_name).get_header().set_extra_data("mask_field", get_field_in("sunlit_mask"));
119116
}
120117
}
121118

@@ -150,7 +147,7 @@ void Cosp::run_impl (const double dt)
150147
get_field_in("qv").sync_to_host();
151148
get_field_in("qc").sync_to_host();
152149
get_field_in("qi").sync_to_host();
153-
get_field_in("sunlit").sync_to_host();
150+
get_field_in("sunlit_mask").sync_to_host();
154151
get_field_in("surf_radiative_T").sync_to_host();
155152
get_field_in("T_mid").sync_to_host();
156153
get_field_in("p_mid").sync_to_host();
@@ -211,7 +208,7 @@ void Cosp::run_impl (const double dt)
211208
const auto p_mid_h = get_field_in("p_mid").get_view<const Real**,Host>();
212209
const auto qc_h = get_field_in("qc").get_view<const Real**, Host>();
213210
const auto qi_h = get_field_in("qi").get_view<const Real**, Host>();
214-
const auto sunlit_h = get_field_in("sunlit").get_view<const Real*, Host>();
211+
const auto sunlit_h = get_field_in("sunlit_mask").get_view<const Real*, Host>();
215212
const auto skt_h = get_field_in("surf_radiative_T").get_view<const Real*, Host>();
216213
const auto p_int_h = get_field_in("p_int").get_view<const Real**, Host>();
217214
const auto cldfrac_h = get_field_in("cldfrac_rad").get_view<const Real**, Host>();
@@ -223,29 +220,28 @@ void Cosp::run_impl (const double dt)
223220
auto isccp_cldtot_h = get_field_out("isccp_cldtot").get_view<Real*, Host>();
224221
auto isccp_ctptau_h = get_field_out("isccp_ctptau").get_view<Real***, Host>();
225222
auto modis_ctptau_h = get_field_out("modis_ctptau").get_view<Real***, Host>();
226-
auto misr_cthtau_h = get_field_out("misr_cthtau").get_view<Real***, Host>();
227-
auto cosp_sunlit_h = get_field_out("cosp_sunlit").get_view<Real*, Host>(); // Copy of sunlit flag with COSP frequency for proper averaging
223+
auto misr_cthtau_h = get_field_out("misr_cthtau"). get_view<Real***, Host>();
228224

229225
Real emsfc_lw = 0.99;
230-
Kokkos::deep_copy(cosp_sunlit_h, sunlit_h);
231226
CospFunc::main(
232227
m_num_cols, m_num_subcols, m_num_levs, m_num_tau, m_num_ctp, m_num_cth, emsfc_lw,
233228
sunlit_h, skt_h, T_mid_h, p_mid_h, p_int_h, z_mid_h, qv_h, qc_h, qi_h,
234229
cldfrac_h, reff_qc_h, reff_qi_h, dtau067_h, dtau105_h,
235230
isccp_cldtot_h, isccp_ctptau_h, modis_ctptau_h, misr_cthtau_h
236231
);
237-
// Remask night values to ZERO since our I/O does not know how to handle masked/missing values
238-
// in temporal averages; this is all host data, so we can just use host loops like its the 1980s
232+
// Mask night values
233+
constexpr auto fill_value = constants::fill_value<Real>;
239234
for (int i = 0; i < m_num_cols; i++) {
240235
if (sunlit_h(i) == 0) {
241-
isccp_cldtot_h(i) = 0;
236+
// if night, set to fill val
237+
isccp_cldtot_h(i) = fill_value;
242238
for (int j = 0; j < m_num_tau; j++) {
243239
for (int k = 0; k < m_num_ctp; k++) {
244-
isccp_ctptau_h(i,j,k) = 0;
245-
modis_ctptau_h(i,j,k) = 0;
240+
isccp_ctptau_h(i,j,k) = fill_value;
241+
modis_ctptau_h(i,j,k) = fill_value;
246242
}
247243
for (int k = 0; k < m_num_cth; k++) {
248-
misr_cthtau_h (i,j,k) = 0;
244+
misr_cthtau_h (i,j,k) = fill_value;
249245
}
250246
}
251247
}
@@ -256,22 +252,6 @@ void Cosp::run_impl (const double dt)
256252
get_field_out("isccp_ctptau").sync_to_dev();
257253
get_field_out("modis_ctptau").sync_to_dev();
258254
get_field_out("misr_cthtau").sync_to_dev();
259-
get_field_out("cosp_sunlit").sync_to_dev();
260-
} else {
261-
// If not updating COSP statistics, set these to ZERO; this essentially weights
262-
// the ISCCP cloud properties by the sunlit mask. What will be output for time-averages
263-
// then is the time-average mask-weighted statistics; to get true averages, we need to
264-
// divide by the time-average of the mask. I.e., if M is the sunlit mask, and X is the ISCCP
265-
// statistic, then
266-
//
267-
// avg(X) = sum(M * X) / sum(M) = (sum(M * X)/N) / (sum(M)/N) = avg(M * X) / avg(M)
268-
//
269-
// TODO: mask this when/if the AD ever supports masked averages
270-
get_field_out("isccp_cldtot").deep_copy(0);
271-
get_field_out("isccp_ctptau").deep_copy(0);
272-
get_field_out("modis_ctptau").deep_copy(0);
273-
get_field_out("misr_cthtau").deep_copy(0);
274-
get_field_out("cosp_sunlit").deep_copy(0);
275255
}
276256
}
277257

components/eamxx/src/physics/rrtmgp/eamxx_rrtmgp_process_interface.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ void RRTMGPRadiation::set_grids(const std::shared_ptr<const GridsManager> grids_
209209
// 0.67 micron and 10.5 micron optical depth (needed for COSP)
210210
add_field<Computed>("dtau067" , scalar3d_mid, nondim, grid_name);
211211
add_field<Computed>("dtau105" , scalar3d_mid, nondim, grid_name);
212-
add_field<Computed>("sunlit" , scalar2d , nondim, grid_name);
212+
add_field<Computed>("sunlit_mask" , scalar2d , nondim, grid_name);
213213
add_field<Computed>("cldfrac_rad" , scalar3d_mid, nondim, grid_name);
214214
// Cloud-top diagnostics following AeroCom recommendation
215215
add_field<Computed>("T_mid_at_cldtop", scalar2d, K, grid_name);
@@ -612,7 +612,7 @@ void RRTMGPRadiation::run_impl (const double dt) {
612612
// Outputs for COSP
613613
auto d_dtau067 = get_field_out("dtau067").get_view<Real**>();
614614
auto d_dtau105 = get_field_out("dtau105").get_view<Real**>();
615-
auto d_sunlit = get_field_out("sunlit").get_view<Real*>();
615+
auto d_sunlit = get_field_out("sunlit_mask").get_view<Real*>();
616616

617617
Kokkos::deep_copy(d_dtau067,0.0);
618618
Kokkos::deep_copy(d_dtau105,0.0);

components/eamxx/src/share/grid/remap/coarsening_remapper.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -432,9 +432,9 @@ local_mat_vec (const Field& x, const Field& y, const Field& mask) const
432432
// Unlike get_view, get_strided_view returns a LayoutStride view,
433433
// therefore allowing the 1d field to be a subfield of a 2d field
434434
// along the 2nd dimension.
435-
auto x_view = x.get_strided_view<const Real*>();
436-
auto y_view = y.get_strided_view< Real*>();
437-
auto mask_view = mask.get_strided_view<Real*>();
435+
auto x_view = x.get_strided_view<const Real*>();
436+
auto y_view = y.get_strided_view< Real*>();
437+
auto mask_view = mask.get_strided_view<const Real*>();
438438
Kokkos::parallel_for(RangePolicy(0,nrows),
439439
KOKKOS_LAMBDA(const int& row) {
440440
const auto beg = row_offsets(row);

components/eamxx/tests/single-process/cosp/input.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ initial_conditions:
3434
cldfrac_rad: 0.5
3535
eff_radius_qc: 10.0
3636
eff_radius_qi: 10.0
37-
sunlit: 1.0
37+
sunlit_mask: 1.0
3838
surf_radiative_T: 288.0
3939
pseudo_density: 1.0
4040

0 commit comments

Comments
 (0)