Skip to content

Commit 5759664

Browse files
committed
EAMxx: allow diags in IO to perform some ops at beginning of a timestep
1 parent a73d48a commit 5759664

File tree

7 files changed

+93
-22
lines changed

7 files changed

+93
-22
lines changed

components/eamxx/src/control/atmosphere_driver.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,6 +1597,16 @@ void AtmosphereDriver::run (const int dt) {
15971597
// nano-opt of removing the call for the 1st timestep.
15981598
reset_accumulated_fields();
15991599

1600+
// Tell the output managers that we're starting a timestep. This is usually
1601+
// a no-op, but some diags *may* require to do something. E.g., a diag that
1602+
// computes tendency of an arbitrary quantity may want to store a copy of
1603+
// that quantity at the beginning of the timestep. Or they may need to store
1604+
// the timestamp at the beginning of the timestep, so that we can compute
1605+
// dt at the end.
1606+
for (auto it : m_output_managers) {
1607+
it.init_timestep(m_current_ts);
1608+
}
1609+
16001610
// The class AtmosphereProcessGroup will take care of dispatching arguments to
16011611
// the individual processes, which will be called in the correct order.
16021612
m_atm_process_group->run(dt);

components/eamxx/src/share/atm_process/atmosphere_diagnostic.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ class AtmosphereDiagnostic : public AtmosphereProcess
5151
// Getting the diagnostic output
5252
Field get_diagnostic () const;
5353

54+
virtual void init_timestep (const util::TimeStamp& /* start_of_step */) {}
55+
5456
void compute_diagnostic (const double dt = 0);
5557
protected:
5658

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,14 @@ void AtmosphereOutput::init()
361361
register_views();
362362
}
363363

364+
void AtmosphereOutput::
365+
init_timestep (const util::TimeStamp& start_of_step)
366+
{
367+
for (auto& it : m_diagnostics) {
368+
it.second->init_timestep(start_of_step);
369+
}
370+
}
371+
364372
void AtmosphereOutput::
365373
run (const std::string& filename,
366374
const bool output_step, const bool checkpoint_step,

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ class AtmosphereOutput
139139
void reset_dev_views();
140140
void update_avg_cnt_view(const Field&, view_1d_dev& dev_view);
141141
void setup_output_file (const std::string& filename, const std::string& fp_precision, const scorpio::FileMode mode);
142+
143+
void init_timestep (const util::TimeStamp& start_of_step);
142144
void run (const std::string& filename,
143145
const bool output_step, const bool checkpoint_step,
144146
const int nsteps_since_last_output,

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@ setup (const ekat::Comm& io_comm, const ekat::ParameterList& params,
265265
{
266266
// In order to trigger a t0 write, we need to have next_write_ts matching run_t0
267267
m_output_control.next_write_ts = m_run_t0;
268+
// This is in case some diags need to init the timestep. Most likely, their output
269+
// is meaningless at t0, but they may still require the start-of-step timestamp to be valid
270+
init_timestep(m_run_t0);
268271
this->run(m_run_t0);
269272
}
270273

@@ -282,6 +285,22 @@ add_global (const std::string& name, const ekat::any& global) {
282285
}
283286

284287
/*===============================================================================================*/
288+
void OutputManager::init_timestep (const util::TimeStamp& start_of_step)
289+
{
290+
// In case output is disabled, no point in doing anything else
291+
if (not m_output_control.output_enabled()) {
292+
return;
293+
}
294+
295+
if (m_atm_logger) {
296+
m_atm_logger->debug("[OutputManager::init_timestep] filename_prefix: " + m_filename_prefix + "\n");
297+
}
298+
299+
for (auto s : m_output_streams) {
300+
s->init_timestep(start_of_step);
301+
}
302+
}
303+
285304
void OutputManager::run(const util::TimeStamp& timestamp)
286305
{
287306
// In case output is disabled, no point in doing anything else

components/eamxx/src/share/io/scream_output_manager.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ class OutputManager
112112
m_atm_logger = atm_logger;
113113
}
114114
void add_global (const std::string& name, const ekat::any& global);
115+
116+
void init_timestep (const util::TimeStamp& start_of_step);
115117
void run (const util::TimeStamp& current_ts);
116118
void finalize();
117119

components/eamxx/src/share/io/tests/io_diags.cpp

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,6 @@
2626

2727
namespace scream {
2828

29-
void multiply (const Field& f, const double v) {
30-
auto data = f.get_internal_view_data<Real,Host>();
31-
auto nscalars = f.get_header().get_alloc_properties().get_num_scalars();
32-
for (int i=0; i<nscalars; ++i) {
33-
data[i] *= v;
34-
}
35-
f.sync_to_dev();
36-
}
37-
3829
class MyDiag : public AtmosphereDiagnostic
3930
{
4031
public:
@@ -69,16 +60,24 @@ class MyDiag : public AtmosphereDiagnostic
6960
FieldIdentifier fid (name(), lt, units, grid_name);
7061
m_diagnostic_output = Field(fid);
7162
m_diagnostic_output.allocate_view();
63+
m_one = m_diagnostic_output.clone("one");
64+
m_one.deep_copy(1.0);
65+
}
66+
67+
void init_timestep (const util::TimeStamp& start_of_step) {
68+
m_t_beg = start_of_step;
7269
}
7370

7471
protected:
7572

7673
void compute_diagnostic_impl () {
7774
const auto& f_in = get_field_in(m_f_in);
7875

79-
m_diagnostic_output.deep_copy<Host>(f_in);
80-
multiply(m_diagnostic_output,2.0);
81-
m_diagnostic_output.sync_to_dev();
76+
const auto& t = f_in.get_header().get_tracking().get_time_stamp();
77+
const double dt = t - m_t_beg;
78+
79+
m_diagnostic_output.deep_copy(f_in);
80+
m_diagnostic_output.update(m_one,dt,2.0);
8281
}
8382

8483
void initialize_impl (const RunType /* run_type */ ) {
@@ -92,12 +91,19 @@ class MyDiag : public AtmosphereDiagnostic
9291
int m_num_cols, m_num_levs;
9392

9493
std::string m_f_in;
94+
95+
util::TimeStamp m_t_beg;
96+
Field m_one;
9597
};
9698

9799
util::TimeStamp get_t0 () {
98100
return util::TimeStamp({2023,2,17},{0,0,0});
99101
}
100102

103+
constexpr double get_dt () {
104+
return 10;
105+
};
106+
101107
std::shared_ptr<const GridsManager>
102108
get_gm (const ekat::Comm& comm)
103109
{
@@ -167,6 +173,7 @@ void write (const int seed, const ekat::Comm& comm)
167173

168174
// Time advance parameters
169175
auto t0 = get_t0();
176+
auto dt = get_dt();
170177

171178
// Create some fields
172179
auto fm = get_fm(grid,t0,seed);
@@ -193,7 +200,15 @@ void write (const int seed, const ekat::Comm& comm)
193200
om.setup(comm,om_pl,fm,gm,t0,t0,false);
194201

195202
// Run output manager
196-
om.run (t0);
203+
for (auto it : *fm) {
204+
auto& f = *it.second;
205+
Field one = f.clone("one");
206+
one.deep_copy(1.0);
207+
f.get_header().get_tracking().update_time_stamp(t0+dt);
208+
f.update(one,1.0,1.0);
209+
}
210+
om.init_timestep(t0);
211+
om.run (t0+dt);
197212

198213
// Close file and cleanup
199214
om.finalize();
@@ -211,6 +226,7 @@ void read (const int seed, const ekat::Comm& comm)
211226
// Get initial fields
212227
auto fm0 = get_fm(grid,t0,seed);
213228
auto fm = get_fm(grid,t0,seed,true);
229+
214230
std::vector<std::string> fnames;
215231
std::string f_name;
216232
for (auto it : *fm) {
@@ -220,6 +236,10 @@ void read (const int seed, const ekat::Comm& comm)
220236
f_name = fn;
221237
}
222238
}
239+
240+
auto f0 = fm0->get_field(f_name).clone();
241+
auto f = fm->get_field(f_name);
242+
223243
// Sanity check
224244
REQUIRE (f_name!="");
225245

@@ -235,17 +255,25 @@ void read (const int seed, const ekat::Comm& comm)
235255
reader_pl.set("Field Names",fnames);
236256
AtmosphereInput reader(reader_pl,fm);
237257

238-
reader.read_variables();
258+
Field one = f0.clone("one");
259+
one.deep_copy(1.0);
260+
for (int i=0; i<2; ++i) {
261+
reader.read_variables(i);
239262

240-
// Check regular field is correct
241-
auto f0 = fm0->get_field(f_name).clone();
242-
auto f = fm->get_field(f_name);
243-
REQUIRE (views_are_equal(f,f0));
263+
// Check regular field is correct
264+
REQUIRE (views_are_equal(f,f0));
244265

245-
// Check diag field is correct
246-
multiply (f0,2.0);
247-
auto d = fm->get_field("MyDiag");
248-
REQUIRE (views_are_equal(d,f0));
266+
// Check diag field is correct
267+
const auto t = t0+i*get_dt();
268+
const double dt = t-t0;
269+
auto d = fm->get_field("MyDiag");
270+
auto d0 = f0.clone();
271+
d0.update(one,dt,2.0);
272+
REQUIRE (views_are_equal(d,d0));
273+
274+
// Update f0
275+
f0.update(one,1.0,1.0);
276+
}
249277
}
250278

251279
TEST_CASE ("io_diags") {

0 commit comments

Comments
 (0)