Skip to content

Commit 35736e3

Browse files
authored
Merge branch 'copilot/support-latlon-input-class' (PR #8472)
Fix FieldReader lat/lon decomposition for point-grid reads [BFB]
2 parents 1df54a1 + 2b12748 commit 35736e3

2 files changed

Lines changed: 158 additions & 29 deletions

File tree

components/eamxx/src/share/field/field_reader.cpp

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -143,58 +143,92 @@ void FieldReader::set_dim_decomp(const Field& gids, const ekat::Comm& comm)
143143

144144
void FieldReader::setup_internals ()
145145
{
146-
if (m_dim_decomp_name!="" and m_reader_state & (NEW_FILE | NEW_DECOMP)) {
147-
if (std::is_same_v<std::int64_t,scorpio::offset_t>) {
148-
scorpio::set_dim_decomp(m_filename, m_dim_decomp_name, m_dim_decomp_offsets);
149-
} else {
150-
std::vector<scorpio::offset_t> offsets(m_dim_decomp_offsets.begin(),
151-
m_dim_decomp_offsets.end());
152-
scorpio::set_dim_decomp(m_filename, m_dim_decomp_name, offsets);
153-
}
146+
using namespace ShortFieldTagsNames;
147+
148+
auto name_lat = m_tag_rename.count("lat")>0 ? m_tag_rename["lat"] : "lat";
149+
auto name_lon = m_tag_rename.count("lon")>0 ? m_tag_rename["lon"] : "lon";
150+
auto name_col = m_tag_rename.count("col")>0 ? m_tag_rename["col"] : "ncol";
151+
152+
// 1. Check if lat/lon dims are in the file
153+
bool latlon_in_file = scorpio::has_dim(m_filename,name_lat) and scorpio::has_dim(m_filename,name_lon);
154+
155+
if (m_reader_state & NEW_FILE) {
156+
EKAT_REQUIRE_MSG (not (latlon_in_file and scorpio::has_dim(m_filename,name_col)),
157+
"Error! We cannot read from files that mix lat/lon vars and ncol vars.\n"
158+
" - file name: " + m_filename + "\n");
154159
}
155160

161+
// 2. Check fields are in the file, with correct dim names
162+
bool latlon_decomp = false;
156163
if (m_reader_state & (NEW_FIELDS | NEW_FILE)) {
157-
// First, check fields are in the file, with correct layout
158164
for (const auto & f : m_fields) {
159165
// Check that the variable is in the file.
160166
EKAT_REQUIRE_MSG (scorpio::has_var(m_filename,f.name()),
161167
"Error! Input file does not store a required variable.\n"
162168
" - filename: " + m_filename + "\n"
163169
" - varname : " + f.name() + "\n");
164170

165-
const auto& layout = f.get_header().get_identifier().get_layout();
166-
auto io_dims = layout.names();
167-
for (int i=0; i<layout.rank(); ++i) {
168-
if (io_dims[i]=="dim")
169-
io_dims[i] += std::to_string(layout.dim(i));
170-
if (m_tag_rename.count(io_dims[i])>0) {
171-
io_dims[i] = m_tag_rename.at(io_dims[i]);
172-
}
171+
const auto& fl = f.get_header().get_identifier().get_layout();
172+
const auto& var = scorpio::get_var(m_filename,f.name());
173+
174+
auto f_dims = fl.names();
175+
auto f_extents = fl.dims();
176+
auto var_dims = var.dim_names();
177+
178+
if (fl.has_tag(COL) and latlon_in_file) {
179+
auto idx = fl.dim_idx(COL);
180+
f_extents.erase(f_extents.begin()+idx);
181+
ekat::erase(f_dims,name_col);
182+
ekat::erase(var_dims,name_col);
183+
ekat::erase(var_dims,name_lat);
184+
ekat::erase(var_dims,name_lon);
185+
latlon_decomp |= m_dim_decomp_name==fl.name(idx);
173186
}
174187

175-
const auto& var = scorpio::get_var(m_filename,f.name());
176-
EKAT_REQUIRE_MSG (var.dim_names()==io_dims,
188+
EKAT_REQUIRE_MSG (var_dims==f_dims,
177189
"Error! Layout mismatch for input file variable.\n"
178190
" - filename: " + m_filename + "\n"
179191
" - varname : " + f.name() + "\n"
180-
" - expected dim names: " + ekat::join(io_dims,",") + "\n"
181-
" - dims from file : " + ekat::join(var.dim_names(),",") + "\n");
192+
" - field dim names: " + ekat::join(fl.names(),",") + "\n"
193+
" - file var dim names: " + ekat::join(var.dim_names(),",") + "\n");
182194

183-
for (int i=0; i<layout.rank(); ++i) {
184-
if (io_dims[i]!=m_dim_decomp_name) {
185-
const int file_len = scorpio::get_dimlen(m_filename,io_dims[i]);
186-
EKAT_REQUIRE_MSG (layout.dim(i)==file_len,
195+
for (auto [f_dim,var_dim,f_ext] : ekat::zip(f_dims,var_dims,f_extents)) {
196+
if (f_dim!=m_dim_decomp_name) {
197+
const int file_len = scorpio::get_dimlen(m_filename,f_dim);
198+
EKAT_REQUIRE_MSG (f_ext==file_len,
187199
"Error! Dimension mismatch for input file variable.\n"
188200
" - filename : " + m_filename + "\n"
189201
" - varname : " + f.name() + "\n"
190-
" - var dims : " + ekat::join(io_dims,",") + "\n"
191-
" - dim name : " + io_dims[i] + "\n"
192-
" - expected extent : " + std::to_string(layout.dim(i)) + "\n"
202+
" - var dims : " + ekat::join(var_dims,",") + "\n"
203+
" - dim name : " + var_dim + "\n"
204+
" - expected extent : " + std::to_string(f_ext) + "\n"
193205
" - extent from file: " + std::to_string(file_len) + "\n");
194206
}
195207
}
196208
}
209+
}
197210

211+
// 3. Set the decomp
212+
if (m_dim_decomp_name!="" and m_reader_state & (NEW_FILE | NEW_FIELDS | NEW_DECOMP)) {
213+
if (std::is_same_v<std::int64_t,scorpio::offset_t>) {
214+
if (latlon_decomp) {
215+
scorpio::set_dims_decomp(m_filename,{name_lat,name_lon},m_dim_decomp_offsets);
216+
} else {
217+
scorpio::set_dim_decomp(m_filename, m_dim_decomp_name, m_dim_decomp_offsets);
218+
}
219+
} else {
220+
std::vector<scorpio::offset_t> offsets(m_dim_decomp_offsets.begin(),
221+
m_dim_decomp_offsets.end());
222+
if (latlon_decomp) {
223+
scorpio::set_dims_decomp(m_filename,{name_lat,name_lon},offsets);
224+
} else {
225+
scorpio::set_dim_decomp(m_filename, m_dim_decomp_name, offsets);
226+
}
227+
}
228+
}
229+
230+
// 4. Create IO fields
231+
if (m_reader_state & (NEW_FIELDS | NEW_FILE)) {
198232
// Now create io_fields
199233
// IO fields MUST be contiguous, so if padded or with parent, create a new one.
200234
// Otherwise, we can alias input field, possibly with renamed tags

components/eamxx/src/share/field/tests/field_reader_tests.cpp

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ TEST_CASE ("read_fields_no_decomp")
140140
// Test: decomposed read (distributed column dimension)
141141
// ============================================================ //
142142

143-
TEST_CASE ("read_fields_with_decomp", "[field_utils][io]")
143+
TEST_CASE ("read_fields_with_decomp")
144144
{
145145
using namespace ShortFieldTagsNames;
146146
using namespace ekat::units;
@@ -236,6 +236,101 @@ TEST_CASE ("read_fields_with_decomp", "[field_utils][io]")
236236
scorpio::finalize_subsystem();
237237
}
238238

239+
// ============================================================ //
240+
// Test: decomposed read from lat/lon file into point-grid fields
241+
// ============================================================ //
242+
243+
TEST_CASE ("read_fields_with_latlon_decomp", "[field_utils][io]")
244+
{
245+
using namespace ShortFieldTagsNames;
246+
247+
ekat::Comm comm(MPI_COMM_WORLD);
248+
scorpio::init_subsystem(comm);
249+
250+
const std::string filename =
251+
"field_io_test_latlon_decomp_np" + std::to_string(comm.size()) + ".nc";
252+
253+
const int lcols_per_rank = 3;
254+
const int nlat = comm.size();
255+
const int nlon = lcols_per_rank;
256+
const int nlevs = 4;
257+
258+
FieldLayout lay2d({COL, LEV}, {lcols_per_rank, nlevs});
259+
FieldLayout lay1d({COL}, {lcols_per_rank});
260+
261+
auto my_gids = make_field("gids",lay1d,DataType::IntType);
262+
auto my_gids_beg = my_gids.get_internal_view_data<int,Host>();
263+
auto my_gids_end = my_gids_beg + lcols_per_rank;
264+
std::iota(my_gids_beg,my_gids_end,1 + comm.rank() * lcols_per_rank);
265+
my_gids.sync_to_dev();
266+
267+
// ---- Write phase ---- //
268+
{
269+
scorpio::register_file(filename, scorpio::Write);
270+
scorpio::define_dim(filename, "lat", nlat);
271+
scorpio::define_dim(filename, "lon", nlon);
272+
scorpio::define_dim(filename, "lev", nlevs);
273+
274+
std::vector<scorpio::offset_t> offsets(lcols_per_rank);
275+
for (int i = 0; i < lcols_per_rank; ++i) {
276+
offsets[i] = my_gids_beg[i] - 1;
277+
}
278+
scorpio::set_dims_decomp(filename, {"lat","lon"}, offsets);
279+
280+
scorpio::define_var(filename, "f2d", {"lat", "lon", "lev"}, "real", false);
281+
scorpio::define_var(filename, "f1d", {"lat", "lon"}, "int", false);
282+
scorpio::enddef(filename);
283+
284+
Field f2d = make_field("f2d", lay2d);
285+
Field f1d = make_field("f1d", lay1d, DataType::IntType);
286+
f2d.sync_to_host();
287+
f1d.sync_to_host();
288+
289+
auto v2d = f2d.get_view<Real**, Host>();
290+
auto v1d = f1d.get_view<int*, Host>();
291+
for (int li = 0; li < lcols_per_rank; ++li) {
292+
const int gcol = my_gids_beg[li];
293+
v1d(li) = gcol;
294+
for (int j = 0; j < nlevs; ++j) {
295+
v2d(li, j) = Real(gcol * nlevs + j);
296+
}
297+
}
298+
f2d.sync_to_dev();
299+
f1d.sync_to_dev();
300+
301+
write_field_to_file<Real>(filename, f2d);
302+
write_field_to_file<int>(filename, f1d);
303+
304+
scorpio::release_file(filename);
305+
}
306+
307+
// ---- Read phase ---- //
308+
{
309+
Field f2d = make_field("f2d", lay2d);
310+
Field f1d = make_field("f1d", lay1d, DataType::IntType);
311+
f2d.deep_copy(-1);
312+
f1d.deep_copy(-1);
313+
314+
read_fields(filename, {f2d, f1d}, my_gids, comm);
315+
316+
f2d.sync_to_host();
317+
f1d.sync_to_host();
318+
319+
auto v2d = f2d.get_view<const Real**, Host>();
320+
auto v1d = f1d.get_view<const int*, Host>();
321+
322+
for (int li = 0; li < lcols_per_rank; ++li) {
323+
const int gcol = my_gids_beg[li];
324+
REQUIRE (v1d(li) == gcol);
325+
for (int j = 0; j < nlevs; ++j) {
326+
REQUIRE (v2d(li, j) == Real(gcol * nlevs + j));
327+
}
328+
}
329+
}
330+
331+
scorpio::finalize_subsystem();
332+
}
333+
239334
// ============================================================ //
240335
// Test: reading a specific time slice
241336
// ============================================================ //

0 commit comments

Comments
 (0)