Skip to content

Commit d3ac406

Browse files
authored
Merge branch 'bartgol/eamxx/fix-horiz-remap-bug' (PR #7350)
Fixes an issue in coarsening remap in case not all the fine grid cols participate to the remap. This bug affected ARM site output. Fixes #7346 [BFB] except for EAMxx ARM output
2 parents bfea0b1 + 1a426a5 commit d3ac406

File tree

10 files changed

+224
-73
lines changed

10 files changed

+224
-73
lines changed

components/eamxx/src/diagnostics/precip_surf_mass_flux.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,27 +63,29 @@ void PrecipSurfMassFlux::compute_diagnostic_impl()
6363
const auto use_liq = m_type & s_liq;
6464
const auto use_ice = m_type & s_ice;
6565

66-
std::int64_t dt;
66+
double dt = 0;
6767
if (use_ice) {
6868
auto mass_ice = get_field_in("precip_ice_surf_mass");
6969
mass_ice_d = mass_ice.get_view<const Real*>();
7070

7171
const auto& t_start = mass_ice.get_header().get_tracking().get_accum_start_time ();
7272
const auto& t_now = mass_ice.get_header().get_tracking().get_time_stamp ();
73-
dt = t_now-t_start;
74-
}
75-
if (use_liq) {
73+
dt = t_now.seconds_from(t_start);
74+
if (use_liq) {
75+
// Ensure liq/ice have same accumulation times
76+
auto mass_liq = get_field_in("precip_liq_surf_mass");
77+
mass_liq_d = mass_liq.get_view<const Real*>();
78+
EKAT_REQUIRE_MSG (t_now==mass_liq.get_header().get_tracking().get_time_stamp() and
79+
t_start==mass_liq.get_header().get_tracking().get_accum_start_time(),
80+
"Error! Liquid and ice precip mass fields have different accumulation time stamps!\n");
81+
}
82+
} else if (use_liq) {
7683
auto mass_liq = get_field_in("precip_liq_surf_mass");
7784
mass_liq_d = mass_liq.get_view<const Real*>();
7885

7986
const auto& t_start = mass_liq.get_header().get_tracking().get_accum_start_time ();
8087
const auto& t_now = mass_liq.get_header().get_tracking().get_time_stamp ();
81-
if (use_ice) {
82-
EKAT_REQUIRE_MSG (dt==(t_now-t_start),
83-
"Error! Liquid and ice precip mass fields have different accumulation time stamps!\n");
84-
} else {
85-
dt = t_now-t_start;
86-
}
88+
dt = t_now.seconds_from(t_start);
8789
}
8890

8991
if (dt==0) {

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

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ void run(std::mt19937_64& engine)
4848
auto gm = create_gm(comm,ncols);
4949

5050
// Create timestep
51-
const int dt=1800;
51+
const double dt=1800;
5252

5353
// Construct random input data
5454
using RPDF = std::uniform_real_distribution<Real>;
@@ -120,28 +120,29 @@ void run(std::mt19937_64& engine)
120120
diag_liq->compute_diagnostic();
121121
diag_ice->compute_diagnostic();
122122

123-
Field preicp_total_f = diag_total->get_diagnostic().clone();
124-
Field preicp_liq_f = diag_liq->get_diagnostic().clone();
125-
Field preicp_ice_f = diag_ice->get_diagnostic().clone();
126-
preicp_total_f.deep_copy(0);
127-
preicp_liq_f.deep_copy(0);
128-
preicp_ice_f.deep_copy(0);
129-
auto precip_total_v = preicp_total_f.get_view<Real*>();
130-
auto precip_liq_v = preicp_liq_f.get_view<Real*>();
131-
auto precip_ice_v = preicp_ice_f.get_view<Real*>();
123+
Field precip_total_f = diag_total->get_diagnostic().clone();
124+
Field precip_liq_f = diag_liq->get_diagnostic().clone();
125+
Field precip_ice_f = diag_ice->get_diagnostic().clone();
126+
precip_total_f.deep_copy(0);
127+
precip_liq_f.deep_copy(0);
128+
precip_ice_f.deep_copy(0);
129+
auto precip_total_v = precip_total_f.get_view<Real*>();
130+
auto precip_liq_v = precip_liq_f.get_view<Real*>();
131+
auto precip_ice_v = precip_ice_f.get_view<Real*>();
132132
const auto rhodt = PC::RHO_H2O*dt;
133133
Kokkos::parallel_for("precip_total_surf_mass_flux_test",
134134
typename KT::RangePolicy(0,ncols),
135135
KOKKOS_LAMBDA(const int& icol) {
136136
precip_liq_v(icol) = precip_liq_surf_mass_v(icol)/rhodt;
137137
precip_ice_v(icol) = precip_ice_surf_mass_v(icol)/rhodt;
138-
precip_total_v(icol) = precip_liq_v(icol) + precip_ice_v(icol);
138+
precip_total_v(icol) = precip_ice_v(icol);
139+
precip_total_v(icol) += precip_liq_surf_mass_v(icol)/rhodt;
139140
});
140141
Kokkos::fence();
141142

142-
REQUIRE(views_are_equal(diag_total->get_diagnostic(),preicp_total_f));
143-
REQUIRE(views_are_equal(diag_liq->get_diagnostic(),preicp_liq_f));
144-
REQUIRE(views_are_equal(diag_ice->get_diagnostic(),preicp_ice_f));
143+
REQUIRE(views_are_equal(diag_total->get_diagnostic(),precip_total_f));
144+
REQUIRE(views_are_equal(diag_liq->get_diagnostic(),precip_liq_f));
145+
REQUIRE(views_are_equal(diag_ice->get_diagnostic(),precip_ice_f));
145146

146147
// Finalize the diagnostic
147148
diag_total->finalize();

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,10 @@ void run (const std::string& diag_name, const std::string& location)
134134
// If interface, check value, otherwise perform int->mid averaging and check value
135135
auto int_val = prev_int_val + delta;
136136
if (location=="interfaces") {
137-
REQUIRE_THAT(d_h(icol,ilev), Catch::Matchers::WithinRel(int_val,1e-5));
137+
REQUIRE_THAT(d_h(icol,ilev), Catch::Matchers::WithinRel(int_val,Real(1e-5)));
138138
} else {
139139
auto mid_val = (int_val + prev_int_val) / 2;
140-
REQUIRE_THAT(d_h(icol,ilev), Catch::Matchers::WithinRel(mid_val,1e-5));
140+
REQUIRE_THAT(d_h(icol,ilev), Catch::Matchers::WithinRel(mid_val,Real(1e-5)));
141141
}
142142
prev_int_val = int_val;
143143
}

components/eamxx/src/dynamics/homme/interface/dyn_grid_mod.F90

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ module dyn_grid_mod
44
use shr_kind_mod, only: r8 => shr_kind_r8
55
use dimensions_mod, only: nelem, nelemd, nelemdmax, np
66
use edgetype_mod, only: EdgeBuffer_t
7+
use mpi
78

89
implicit none
910
private
1011

11-
! We need MPI in here, so include it
12-
#include <mpif.h>
13-
1412
public :: dyn_grid_init, get_my_dyn_data, cleanup_grid_init_data
1513

1614
type (EdgeBuffer_t) :: edge

components/eamxx/src/dynamics/homme/interface/phys_grid_mod.F90

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module phys_grid_mod
33
use iso_c_binding, only: c_int, c_double
44
use parallel_mod, only: abortmp, MPIinteger_t, MPIreal_t
55
use kinds, only: iulog
6+
use mpi
67

78
implicit none
89
private
@@ -55,8 +56,6 @@ module phys_grid_mod
5556

5657
type(pg_specs_t), target :: pg_specs (pgN_min:pgN_max)
5758

58-
! To get MPI_IN_PLACE and MPI_DATATYPE_NULL
59-
#include <mpif.h>
6059
! Note: in this module, we often use MPI_IN_PLACE,0,MPI_DATATYPE_NULL
6160
! for the src array specs in Allgatherv calls. These special values
6261
! inform MPI that src array is aliasing the dst one, so MPI will

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

Lines changed: 13 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -88,41 +88,10 @@ get_my_triplets (const std::string& map_file) const
8888

8989
scorpio::release_file(map_file);
9090

91-
// 1.2 Dofs in grid are likely 0-based, while row/col ids in map file
92-
// are likely 1-based. To match dofs, we need to offset the row/cols
93-
// ids we just read in.
94-
int map_file_min_row = std::numeric_limits<int>::max();
95-
int map_file_min_col = std::numeric_limits<int>::max();
96-
for (int id=0; id<nlweights; id++) {
97-
map_file_min_row = std::min(rows[id],map_file_min_row);
98-
map_file_min_col = std::min(cols[id],map_file_min_col);
99-
}
100-
int global_map_file_min_row, global_map_file_min_col;
101-
comm.all_reduce(&map_file_min_row,&global_map_file_min_row,1,MPI_MIN);
102-
comm.all_reduce(&map_file_min_col,&global_map_file_min_col,1,MPI_MIN);
103-
104-
gid_type row_offset = global_map_file_min_row;
105-
gid_type col_offset = global_map_file_min_col;
106-
if (type==InterpType::Refine) {
107-
row_offset -= fine_grid->get_global_min_dof_gid();
108-
} else {
109-
col_offset -= fine_grid->get_global_min_dof_gid();
110-
}
111-
for (auto& id : rows) {
112-
id -= row_offset;
113-
}
114-
for (auto& id : cols) {
115-
id -= col_offset;
116-
}
117-
11891
// Create a grid based on the row gids I read in (may be duplicated across ranks)
119-
std::vector<gid_type> unique_gids;
12092
const auto& gids = type==InterpType::Refine ? rows : cols;
121-
for (auto gid : gids) {
122-
if (not ekat::contains(unique_gids,gid)) {
123-
unique_gids.push_back(gid);
124-
}
125-
}
93+
std::set<gid_type> temp (gids.begin(),gids.end());
94+
std::vector<gid_type> unique_gids (temp.begin(),temp.end());
12695
auto io_grid = std::make_shared<PointGrid> ("helper",unique_gids.size(),0,comm);
12796
auto io_grid_gids_h = io_grid->get_dofs_gids().get_view<gid_type*,Host>();
12897
int k = 0;
@@ -150,10 +119,18 @@ get_my_triplets (const std::string& map_file) const
150119
MPI_Type_create_struct (3,lengths,displacements,types,&mpi_triplet_t);
151120
MPI_Type_commit(&mpi_triplet_t);
152121

153-
// Create import-export
154-
GridImportExport imp_exp (fine_grid,io_grid);
122+
// Create import-export and gather my triplets
155123
std::map<int,std::vector<Triplet>> my_triplets_map;
156-
imp_exp.gather(mpi_triplet_t,io_triplets,my_triplets_map);
124+
try {
125+
GridImportExport imp_exp (fine_grid,io_grid);
126+
imp_exp.gather(mpi_triplet_t,io_triplets,my_triplets_map);
127+
} catch (...) {
128+
// Print the map file name, to help debugging
129+
std::cout << "[HorizRemapperData] Something went wrong while performing a grid import-export operation.\n"
130+
<< " - map file name : " << map_file << "\n"
131+
<< " - fine grid name: " << fine_grid->name() << "\n";
132+
throw;
133+
}
157134
MPI_Type_free(&mpi_triplet_t);
158135

159136
std::vector<Triplet> my_triplets;

components/eamxx/src/share/grid/remap/horiz_interp_remapper_data.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ enum class InterpType {
1717
Coarsen
1818
};
1919

20-
// A small struct to hold horiz remap data, which can
21-
// be shared across multiple horiz remappers
20+
// A small struct to hold horiz remap data, which can be shared across multiple horiz remappers
21+
// NOTE: the client will call the build method, which will read the map file, and create the
22+
// CRS matrix data for online interpolation.
2223
struct HorizRemapperData {
2324
using KT = KokkosTypes<DefaultDevice>;
2425
template<typename T>
2526
using view_1d = typename KT::template view_1d<T>;
2627

28+
// The last argument specifies the base index for gids in the map file
29+
// For ncremap-type files, all indices are 1-based
2730
void build (const std::string& map_file,
2831
const std::shared_ptr<const AbstractGrid>& fine_grid,
2932
const ekat::Comm& comm,

components/eamxx/src/share/io/tests/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ CreateUnitTest(io_remap_test "io_remap_test.cpp"
8888
MPI_RANKS 1 ${SCREAM_TEST_MAX_RANKS}
8989
)
9090

91+
## Test remap output when map file is sub-sampling (ARM-style)
92+
CreateUnitTest(io_horiz_sampling "io_horiz_sampling.cpp"
93+
LIBS scream_io LABELS io remap
94+
MPI_RANKS 1 ${SCREAM_TEST_MAX_RANKS}
95+
)
96+
9197
## Test single-column reader
9298
CreateUnitTest(io_scm_reader "io_scm_reader.cpp"
9399
LIBS scream_io LABELS io

0 commit comments

Comments
 (0)