Skip to content

Commit 02c9644

Browse files
committed
Merge branch 'bartgol/eamxx/rad-cosp-freq-adjustments' into next (PR #7337)
Currently, rad runs on steps 1, rad_freq+1, 2*rad_freq+1, etc., which is not what one would expect, and makes it hard to craft an instant output yaml file to catch rad steps (if freq>1). This PR makes the frequency logic of rad similar to the output, with the only exception that we always run it on the 1st step (regardless of frequency). [non-BFB] for EAMxx cases
2 parents 688a287 + b534b2c commit 02c9644

File tree

11 files changed

+80
-186
lines changed

11 files changed

+80
-186
lines changed

components/eamxx/cime_config/eamxx_buildnml.py

Lines changed: 1 addition & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
# SCREAM imports
1717
from eamxx_buildnml_impl import get_valid_selectors, get_child, refine_type, \
18-
resolve_all_inheritances, gen_atm_proc_group, check_all_values, find_node
18+
resolve_all_inheritances, gen_atm_proc_group, check_all_values
1919
from atm_manip import apply_atm_procs_list_changes_from_buffer, apply_non_atm_procs_list_changes_from_buffer
2020

2121
from utils import ensure_yaml # pylint: disable=no-name-in-module
@@ -29,7 +29,6 @@
2929
# Cime imports
3030
from standard_script_setup import * # pylint: disable=wildcard-import
3131
from CIME.utils import expect, safe_copy, SharedArea
32-
from CIME.test_status import TestStatus, RUN_PHASE
3332

3433
logger = logging.getLogger(__name__) # pylint: disable=undefined-variable
3534

@@ -105,121 +104,6 @@ def do_cime_vars(entry, case, refine=False, extra=None):
105104

106105
return entry
107106

108-
###############################################################################
109-
def perform_consistency_checks(case, xml):
110-
###############################################################################
111-
"""
112-
There may be separate parts of the xml that must satisfy some consistency
113-
Here, we run any such check, so we can catch errors before submit time
114-
115-
>>> from eamxx_buildnml_impl import MockCase
116-
>>> xml_str = '''
117-
... <params>
118-
... <rrtmgp>
119-
... <rad_frequency type="integer">3</rad_frequency>
120-
... </rrtmgp>
121-
... </params>
122-
... '''
123-
>>> import xml.etree.ElementTree as ET
124-
>>> xml = ET.fromstring(xml_str)
125-
>>> case = MockCase({'ATM_NCPL':'24', 'REST_N':24, 'REST_OPTION':'nsteps'})
126-
>>> perform_consistency_checks(case,xml)
127-
>>> case = MockCase({'ATM_NCPL':'24', 'REST_N':2, 'REST_OPTION':'nsteps'})
128-
>>> perform_consistency_checks(case,xml)
129-
Traceback (most recent call last):
130-
CIME.utils.CIMEError: ERROR: rrtmgp::rad_frequency (3 steps) incompatible with restart frequency (2 steps).
131-
Please, ensure restart happens on a step when rad is ON
132-
>>> case = MockCase({'ATM_NCPL':'24', 'REST_N':10800, 'REST_OPTION':'nseconds'})
133-
>>> perform_consistency_checks(case,xml)
134-
>>> case = MockCase({'ATM_NCPL':'24', 'REST_N':7200, 'REST_OPTION':'nseconds'})
135-
>>> perform_consistency_checks(case,xml)
136-
Traceback (most recent call last):
137-
CIME.utils.CIMEError: ERROR: rrtmgp::rad_frequency incompatible with restart frequency.
138-
Please, ensure restart happens on a step when rad is ON
139-
rest_tstep: 7200
140-
rad_testep: 10800.0
141-
>>> case = MockCase({'ATM_NCPL':'24', 'REST_N':180, 'REST_OPTION':'nminutes'})
142-
>>> perform_consistency_checks(case,xml)
143-
>>> case = MockCase({'ATM_NCPL':'24', 'REST_N':120, 'REST_OPTION':'nminutes'})
144-
>>> perform_consistency_checks(case,xml)
145-
Traceback (most recent call last):
146-
CIME.utils.CIMEError: ERROR: rrtmgp::rad_frequency incompatible with restart frequency.
147-
Please, ensure restart happens on a step when rad is ON
148-
rest_tstep: 7200
149-
rad_testep: 10800.0
150-
>>> case = MockCase({'ATM_NCPL':'24', 'REST_N':6, 'REST_OPTION':'nhours'})
151-
>>> perform_consistency_checks(case,xml)
152-
>>> case = MockCase({'ATM_NCPL':'24', 'REST_N':8, 'REST_OPTION':'nhours'})
153-
>>> perform_consistency_checks(case,xml)
154-
Traceback (most recent call last):
155-
CIME.utils.CIMEError: ERROR: rrtmgp::rad_frequency incompatible with restart frequency.
156-
Please, ensure restart happens on a step when rad is ON
157-
rest_tstep: 28800
158-
rad_testep: 10800.0
159-
>>> case = MockCase({'ATM_NCPL':'12', 'REST_N':2, 'REST_OPTION':'ndays'})
160-
>>> perform_consistency_checks(case,xml)
161-
>>> case = MockCase({'ATM_NCPL':'10', 'REST_N':2, 'REST_OPTION':'ndays'})
162-
>>> perform_consistency_checks(case,xml)
163-
Traceback (most recent call last):
164-
CIME.utils.CIMEError: ERROR: rrtmgp::rad_frequency incompatible with restart frequency.
165-
Please, ensure restart happens on a step when rad is ON
166-
For daily (or less frequent) restart, rad_frequency must divide ATM_NCPL
167-
"""
168-
169-
# RRTMGP can be supercycled. Restarts cannot fall in the middle
170-
# of a rad superstep
171-
rrtmgp = find_node(xml,"rrtmgp")
172-
rest_opt = case.get_value("REST_OPTION")
173-
is_test = case.get_value("TEST")
174-
caseraw = case.get_value("CASE")
175-
caseroot = case.get_value("CASEROOT")
176-
casebaseid = case.get_value("CASEBASEID")
177-
if rrtmgp is not None and rest_opt is not None and rest_opt not in ["never","none"]:
178-
rest_n = int(case.get_value("REST_N"))
179-
rad_freq = int(find_node(rrtmgp,"rad_frequency").text)
180-
atm_ncpl = int(case.get_value("ATM_NCPL"))
181-
atm_tstep = 86400 / atm_ncpl
182-
rad_tstep = atm_tstep * rad_freq
183-
184-
# Some tests (ERS) make late (run-phase) changes, so we cannot validate restart
185-
# settings until RUN phase
186-
is_test_not_yet_run = False
187-
if is_test:
188-
test_name = casebaseid if casebaseid is not None else caseraw
189-
ts = TestStatus(test_dir=caseroot, test_name=test_name)
190-
phase = ts.get_latest_phase()
191-
if phase != RUN_PHASE:
192-
is_test_not_yet_run = True
193-
194-
if rad_freq==1 or is_test_not_yet_run:
195-
pass
196-
elif rest_opt in ["nsteps", "nstep"]:
197-
expect (rest_n % rad_freq == 0,
198-
f"rrtmgp::rad_frequency ({rad_freq} steps) incompatible with "
199-
f"restart frequency ({rest_n} steps).\n"
200-
" Please, ensure restart happens on a step when rad is ON")
201-
elif rest_opt in ["nseconds", "nsecond", "nminutes", "nminute", "nhours", "nhour"]:
202-
if rest_opt in ["nseconds", "nsecond"]:
203-
factor = 1
204-
elif rest_opt in ["nminutes", "nminute"]:
205-
factor = 60
206-
else:
207-
factor = 3600
208-
209-
rest_tstep = factor*rest_n
210-
expect (rest_tstep % rad_tstep == 0,
211-
"rrtmgp::rad_frequency incompatible with restart frequency.\n"
212-
" Please, ensure restart happens on a step when rad is ON\n"
213-
f" rest_tstep: {rest_tstep}\n"
214-
f" rad_testep: {rad_tstep}")
215-
216-
else:
217-
# for "very infrequent" restarts, we request rad_freq to divide atm_ncpl
218-
expect (atm_ncpl % rad_freq ==0,
219-
"rrtmgp::rad_frequency incompatible with restart frequency.\n"
220-
" Please, ensure restart happens on a step when rad is ON\n"
221-
" For daily (or less frequent) restart, rad_frequency must divide ATM_NCPL")
222-
223107
###############################################################################
224108
def ordered_dump(data, item, Dumper=yaml.SafeDumper, **kwds):
225109
###############################################################################
@@ -743,8 +627,6 @@ def _create_raw_xml_file_impl(case, xml, filepath=None):
743627

744628
raise e
745629

746-
perform_consistency_checks (case, xml)
747-
748630
return xml
749631

750632
###############################################################################

components/eamxx/cime_config/namelist_defaults_eamxx.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ be lost if SCREAM_HACK_XML is not enabled.
551551
true
552552
</do_subcol_sampling>
553553
<pool_size_multiplier type="real">1.0</pool_size_multiplier>
554+
<force_run_after_restart type="logical" doc="Force rad to run on first step after restart, regardless of rad frequency">false</force_run_after_restart>
554555
</rrtmgp>
555556

556557
<mac_aero_mic inherit="atm_proc_group">

components/eamxx/src/control/atmosphere_driver.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,9 +1620,11 @@ void AtmosphereDriver::run (const int dt) {
16201620
EKAT_REQUIRE_MSG (dt>0, "Error! Input time step must be positive.\n");
16211621

16221622
// Print current timestamp information
1623+
auto end_of_step = m_current_ts + dt;
16231624
m_atm_logger->log(ekat::logger::LogLevel::info,
1624-
"Atmosphere step = " + std::to_string(m_current_ts.get_num_steps()) + "\n" +
1625-
" model start-of-step time = " + m_current_ts.get_date_string() + " " + m_current_ts.get_time_string() + "\n");
1625+
"Atmosphere step = " + std::to_string(end_of_step.get_num_steps()) + "\n" +
1626+
" model beg-of-step timestamp: " + m_current_ts.get_date_string() + " " + m_current_ts.get_time_string() + "\n"
1627+
" model end-of-step timestamp: " + end_of_step.get_date_string() + " " + end_of_step.get_time_string() + "\n");
16261628

16271629
// Reset accum fields to 0
16281630
// Note: at the 1st timestep this is redundant, since we did it at init,

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,13 @@ class Cosp : public AtmosphereProcess
3232
// Set the grid
3333
void set_grids (const std::shared_ptr<const GridsManager> grids_manager);
3434

35-
inline bool cosp_do(const int icosp, const int nstep) {
35+
inline bool cosp_do(const int cosp_freq, const int nstep) {
3636
// If icosp == 0, then never do cosp;
37-
// Otherwise, we always call cosp at the first step,
38-
// and afterwards we do cosp if the timestep is divisible
39-
// by icosp
40-
if (icosp == 0) {
37+
// Otherwise, do cosp if the timestep is divisible by cosp_freq
38+
if (cosp_freq == 0) {
4139
return false;
4240
} else {
43-
return ( (nstep == 0) || (nstep % icosp == 0) );
41+
return nstep % cosp_freq == 0;
4442
}
4543
}
4644

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

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ RRTMGPRadiation (const ekat::Comm& comm, const ekat::ParameterList& params)
7979
}
8080

8181
m_ngas = m_gas_names.size();
82+
83+
// Determine rad timestep, specified as number of atm steps
84+
m_rad_freq_in_steps = m_params.get<Int>("rad_frequency", 1);
8285
}
8386

8487
void RRTMGPRadiation::set_grids(const std::shared_ptr<const GridsManager> grids_manager) {
@@ -198,7 +201,6 @@ void RRTMGPRadiation::set_grids(const std::shared_ptr<const GridsManager> grids_
198201
add_field<Computed>("LW_clrsky_flux_dn", scalar3d_int, W/m2, grid_name);
199202
add_field<Computed>("LW_clnsky_flux_up", scalar3d_int, W/m2, grid_name);
200203
add_field<Computed>("LW_clnsky_flux_dn", scalar3d_int, W/m2, grid_name);
201-
add_field<Computed>("rad_heating_pdel", scalar3d_mid, Pa*K/s, grid_name);
202204
// Cloud properties added as computed fields for diagnostic purposes
203205
add_field<Computed>("cldlow" , scalar2d, nondim, grid_name);
204206
add_field<Computed>("cldmed" , scalar2d, nondim, grid_name);
@@ -219,6 +221,11 @@ void RRTMGPRadiation::set_grids(const std::shared_ptr<const GridsManager> grids_
219221
add_field<Computed>("eff_radius_qc_at_cldtop", scalar2d, micron, grid_name);
220222
add_field<Computed>("eff_radius_qi_at_cldtop", scalar2d, micron, grid_name);
221223

224+
// This field is needed for restart
225+
Field rad_heating_pdel (FieldIdentifier("rad_heating_pdel", scalar3d_mid, Pa*K/s, grid_name));
226+
rad_heating_pdel.allocate_view();
227+
add_internal_field(rad_heating_pdel);
228+
222229
// Translation of variables from EAM
223230
// --------------------------------------------------------------
224231
// EAM name | EAMXX name | Description
@@ -230,12 +237,19 @@ void RRTMGPRadiation::set_grids(const std::shared_ptr<const GridsManager> grids_
230237
// netsw sfc_flux_sw_net net (down - up) SW flux at surface
231238
// flwds sfc_flux_lw_dn downwelling LW flux at surface
232239
// --------------------------------------------------------------
233-
add_field<Computed>("sfc_flux_dir_nir", scalar2d, W/m2, grid_name);
234-
add_field<Computed>("sfc_flux_dir_vis", scalar2d, W/m2, grid_name);
235-
add_field<Computed>("sfc_flux_dif_nir", scalar2d, W/m2, grid_name);
236-
add_field<Computed>("sfc_flux_dif_vis", scalar2d, W/m2, grid_name);
237-
add_field<Computed>("sfc_flux_sw_net" , scalar2d, W/m2, grid_name);
238-
add_field<Computed>("sfc_flux_lw_dn" , scalar2d, W/m2, grid_name);
240+
241+
// We need to ensure that these fields are added to the RESTART group,
242+
// since the cpl will need them at every step, and rrtmgp may not run
243+
// the 1st step after restart (depending on rad freq).
244+
// NOTE: technically, we know rad freq, so we *could* avoid adding them
245+
// to the rest file if rad_freq=1. But a) that is not common at high
246+
// res anyways, and b) that could prevent changing rad_freq upon restart
247+
add_field<Computed>("sfc_flux_dir_nir", scalar2d, W/m2, grid_name, "RESTART");
248+
add_field<Computed>("sfc_flux_dir_vis", scalar2d, W/m2, grid_name, "RESTART");
249+
add_field<Computed>("sfc_flux_dif_nir", scalar2d, W/m2, grid_name, "RESTART");
250+
add_field<Computed>("sfc_flux_dif_vis", scalar2d, W/m2, grid_name, "RESTART");
251+
add_field<Computed>("sfc_flux_sw_net" , scalar2d, W/m2, grid_name, "RESTART");
252+
add_field<Computed>("sfc_flux_lw_dn" , scalar2d, W/m2, grid_name, "RESTART");
239253

240254
// Boundary flux fields for energy and mass conservation checks
241255
if (has_column_conservation_check()) {
@@ -453,12 +467,9 @@ void RRTMGPRadiation::init_buffers(const ATMBufferManager &buffer_manager)
453467
EKAT_REQUIRE_MSG(used_mem==requested_buffer_size_in_bytes(), "Error! Used memory != requested memory for RRTMGPRadiation.");
454468
} // RRTMGPRadiation::init_buffers
455469

456-
void RRTMGPRadiation::initialize_impl(const RunType /* run_type */) {
470+
void RRTMGPRadiation::initialize_impl(const RunType run_type) {
457471
using PC = scream::physics::Constants<Real>;
458472

459-
// Determine rad timestep, specified as number of atm steps
460-
m_rad_freq_in_steps = m_params.get<Int>("rad_frequency", 1);
461-
462473
// Determine orbital year. If orbital_year is negative, use current year
463474
// from timestamp for orbital year; if positive, use provided orbital year
464475
// for duration of simulation.
@@ -532,6 +543,13 @@ void RRTMGPRadiation::initialize_impl(const RunType /* run_type */) {
532543
auto co_vmr = get_field_out("co_volume_mix_ratio").get_view<Real**>();
533544
Kokkos::deep_copy(co_vmr, m_params.get<double>("covmr", 1.0e-7));
534545
}
546+
547+
// Ensure rad_heating_pdel is recognized as initialized by the driver
548+
auto& rad_heating = get_internal_field("rad_heating_pdel");
549+
rad_heating.get_header().get_tracking().update_time_stamp(start_of_step_ts());
550+
551+
m_force_run_on_next_step = run_type==RunType::Initial or
552+
m_params.get("force_run_after_restart",false);
535553
}
536554

537555
// =========================================================================================
@@ -597,7 +615,7 @@ void RRTMGPRadiation::run_impl (const double dt) {
597615
auto d_lw_clrsky_flux_dn = get_field_out("LW_clrsky_flux_dn").get_view<Real**>();
598616
auto d_lw_clnsky_flux_up = get_field_out("LW_clnsky_flux_up").get_view<Real**>();
599617
auto d_lw_clnsky_flux_dn = get_field_out("LW_clnsky_flux_dn").get_view<Real**>();
600-
auto d_rad_heating_pdel = get_field_out("rad_heating_pdel").get_view<Real**>();
618+
auto d_rad_heating_pdel = get_internal_field("rad_heating_pdel").get_view<Real**>();
601619
auto d_sfc_flux_dir_vis = get_field_out("sfc_flux_dir_vis").get_view<Real*>();
602620
auto d_sfc_flux_dir_nir = get_field_out("sfc_flux_dir_nir").get_view<Real*>();
603621
auto d_sfc_flux_dif_vis = get_field_out("sfc_flux_dif_vis").get_view<Real*>();
@@ -638,8 +656,8 @@ void RRTMGPRadiation::run_impl (const double dt) {
638656
const auto do_aerosol_rad = m_do_aerosol_rad;
639657

640658
// Are we going to update fluxes and heating this step?
641-
auto ts = start_of_step_ts();
642-
auto update_rad = scream::rrtmgp::radiation_do(m_rad_freq_in_steps, ts.get_num_steps());
659+
auto ts = end_of_step_ts();
660+
auto update_rad = m_force_run_on_next_step or scream::rrtmgp::radiation_do(m_rad_freq_in_steps, ts.get_num_steps());
643661

644662
if (update_rad) {
645663
// On each chunk, we internally "reset" the GasConcs object to subview the concs 3d array
@@ -1211,6 +1229,7 @@ void RRTMGPRadiation::run_impl (const double dt) {
12111229
});
12121230
}
12131231

1232+
m_force_run_on_next_step = false;
12141233
}
12151234
// =========================================================================================
12161235

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,8 @@ class RRTMGPRadiation : public AtmosphereProcess {
240240

241241
// Struct which contains local variables
242242
Buffer m_buffer;
243+
244+
bool m_force_run_on_next_step = false;
243245
}; // class RRTMGPRadiation
244246

245247
} // namespace scream

components/eamxx/src/physics/rrtmgp/rrtmgp_utils.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ void compute_heating_rate (
3737
));
3838
}
3939

40-
inline bool radiation_do(const int irad, const int nstep) {
41-
// If irad == 0, then never do radiation;
40+
inline bool radiation_do(const int rad_freq, const int nstep) {
41+
// If rad_freq == 0, then never do radiation;
4242
// Otherwise, we always call radiation at the first step,
4343
// and afterwards we do radiation if the timestep is divisible
44-
// by irad
45-
if (irad == 0) {
44+
// by rad_freq
45+
if (rad_freq == 0) {
4646
return false;
4747
} else {
48-
return ( (nstep == 0) || (nstep % irad == 0) );
48+
return nstep % rad_freq == 0;
4949
}
5050
}
5151

components/eamxx/src/physics/rrtmgp/tests/rrtmgp_unit_tests.cpp

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -411,25 +411,14 @@ TEST_CASE("rrtmgp_test_compute_broadband_surface_flux_k") {
411411
}
412412

413413
TEST_CASE("rrtmgp_test_radiation_do_k") {
414-
// If we specify rad every step, radiation_do should always be true
415-
REQUIRE(scream::rrtmgp::radiation_do(1, 0) == true);
416-
REQUIRE(scream::rrtmgp::radiation_do(1, 1) == true);
417-
REQUIRE(scream::rrtmgp::radiation_do(1, 2) == true);
418-
419-
// Test cases where we want rad called every other step
420-
REQUIRE(scream::rrtmgp::radiation_do(2, 0) == true);
421-
REQUIRE(scream::rrtmgp::radiation_do(2, 1) == false);
422-
REQUIRE(scream::rrtmgp::radiation_do(2, 2) == true);
423-
REQUIRE(scream::rrtmgp::radiation_do(2, 3) == false);
424-
425-
// Test cases where we want rad every third step
426-
REQUIRE(scream::rrtmgp::radiation_do(3, 0) == true);
427-
REQUIRE(scream::rrtmgp::radiation_do(3, 1) == false);
428-
REQUIRE(scream::rrtmgp::radiation_do(3, 2) == false);
429-
REQUIRE(scream::rrtmgp::radiation_do(3, 3) == true);
430-
REQUIRE(scream::rrtmgp::radiation_do(3, 4) == false);
431-
REQUIRE(scream::rrtmgp::radiation_do(3, 5) == false);
432-
REQUIRE(scream::rrtmgp::radiation_do(3, 6) == true);
414+
// Rad runs whenever step is a multiple of freq
415+
// NOTE: the rrtmgp process class handles logic for running on 1st step
416+
for (int istep : {1,2,3,4,5,6}) {
417+
for (int rad_freq : {1,2,3}) {
418+
bool divides = istep%rad_freq == 0;
419+
REQUIRE( scream::rrtmgp::radiation_do(rad_freq,istep)== divides );
420+
}
421+
}
433422
}
434423

435424
TEST_CASE("rrtmgp_test_check_range_k") {

components/eamxx/src/share/atm_process/atmosphere_process.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ void AtmosphereProcess::run (const double dt) {
130130
// Print hash of INPUTS before run
131131
print_global_state_hash(name() + "-pre-sc-" + std::to_string(m_subcycle_iter),
132132
m_start_of_step_ts,
133-
true, false, false);
133+
true, false, true);
134134

135135
// Run derived class implementation
136136
run_impl(dt_sub);
@@ -139,7 +139,7 @@ void AtmosphereProcess::run (const double dt) {
139139
// Print hash of OUTPUTS/INTERNALS after run
140140
print_global_state_hash(name() + "-pst-sc-" + std::to_string(m_subcycle_iter),
141141
m_end_of_step_ts,
142-
true, true, true);
142+
false, true, true);
143143

144144
if (has_column_conservation_check()) {
145145
// Run the column local mass and energy conservation checks

0 commit comments

Comments
 (0)