|
2 | 2 |
|
3 | 3 | #include "share/field/field_utils.hpp" |
4 | 4 | #include "share/property_checks/field_within_interval_check.hpp" |
| 5 | +#include "ekat/util/ekat_lin_interp.hpp" |
5 | 6 |
|
6 | 7 | #include <ekat_math_utils.hpp> |
7 | 8 |
|
@@ -169,128 +170,39 @@ advance_iop_subsidence(const MemberType& team, |
169 | 170 | const auto n_q_tracers = Q.extent_int(0); |
170 | 171 | const auto nlev_packs = ekat::npack<Pack>(nlevs); |
171 | 172 |
|
172 | | - // Get some temporary views from WS |
173 | | - uview_1d<Pack> omega_int, delta_u, delta_v, delta_T, tmp; |
174 | | - workspace.take_many_contiguous_unsafe<4>({"omega_int", "delta_u", "delta_v", "delta_T"}, |
175 | | - {&omega_int, &delta_u, &delta_v, &delta_T}); |
176 | | - const auto delta_Q_slot = workspace.take_macro_block("delta_Q", n_q_tracers); |
177 | | - uview_2d<Pack> delta_Q(delta_Q_slot.data(), n_q_tracers, nlev_packs); |
178 | | - |
179 | | - auto s_ref_p_mid = ekat::scalarize(ref_p_mid); |
180 | | - auto s_omega = ekat::scalarize(omega); |
181 | | - auto s_delta_u = ekat::scalarize(delta_u); |
182 | | - auto s_delta_v = ekat::scalarize(delta_v); |
183 | | - auto s_delta_T = ekat::scalarize(delta_T); |
184 | | - auto s_delta_Q = ekat::scalarize(delta_Q); |
185 | | - auto s_omega_int = ekat::scalarize(omega_int); |
186 | | - |
187 | | - // Compute omega on the interface grid by using a weighted average in pressure |
188 | | - const int pack_begin = 1/Pack::n, pack_end = (nlevs-1)/Pack::n; |
189 | | - Kokkos::parallel_for(Kokkos::TeamVectorRange(team, pack_begin, pack_end+1), [&] (const int k){ |
190 | | - auto range_pack = ekat::range<IntPack>(k*Pack::n); |
191 | | - range_pack.set(range_pack<1, 1); |
192 | | - Pack ref_p_mid_k, ref_p_mid_km1, omega_k, omega_km1; |
193 | | - ekat::index_and_shift<-1>(s_ref_p_mid, range_pack, ref_p_mid_k, ref_p_mid_km1); |
194 | | - ekat::index_and_shift<-1>(s_omega, range_pack, omega_k, omega_km1); |
195 | | - |
196 | | - const auto weight = (ref_p_int(k) - ref_p_mid_km1)/(ref_p_mid_k - ref_p_mid_km1); |
197 | | - omega_int(k).set(range_pack>=1 and range_pack<=nlevs-1, |
198 | | - weight*omega_k + (1-weight)*omega_km1); |
199 | | - }); |
200 | | - omega_int(0)[0] = 0; |
201 | | - omega_int(nlevs/Pack::n)[nlevs%Pack::n] = 0; |
202 | | - |
203 | | - // Compute delta views for u, v, T, and Q (e.g., u(k+1) - u(k), k=0,...,nlevs-2) |
204 | | - ColOps::compute_midpoint_delta(team, nlevs-1, u, delta_u); |
205 | | - ColOps::compute_midpoint_delta(team, nlevs-1, v, delta_v); |
206 | | - ColOps::compute_midpoint_delta(team, nlevs-1, T, delta_T); |
207 | | - for (int iq=0; iq<n_q_tracers; ++iq) { |
208 | | - auto tracer = Kokkos::subview(Q, iq, Kokkos::ALL()); |
209 | | - auto delta_tracer = Kokkos::subview(delta_Q, iq, Kokkos::ALL()); |
210 | | - ColOps::compute_midpoint_delta(team, nlevs-1, tracer, delta_tracer); |
211 | | - } |
212 | | - team.team_barrier(); |
213 | | - |
214 | | - // Compute updated temperature, horizontal winds, and tracers |
215 | | - Kokkos::parallel_for(Kokkos::TeamVectorRange(team, nlev_packs), [&] (const int k) { |
216 | | - auto range_pack = ekat::range<IntPack>(k*Pack::n); |
217 | | - const auto at_top = range_pack==0; |
218 | | - const auto not_at_top = not at_top; |
219 | | - const auto at_bot = range_pack==nlevs-1; |
220 | | - const auto not_at_bot = not at_bot; |
221 | | - const bool any_at_top = at_top.any(); |
222 | | - const bool any_at_bot = at_bot.any(); |
223 | | - |
224 | | - // Get delta(k-1) packs. The range pack should not |
225 | | - // contain index 0 (so that we don't attempt to access |
226 | | - // k=-1 index) or index > nlevs-2 (since delta_* views |
227 | | - // are size nlevs-1). |
228 | | - auto range_pack_for_m1_shift = range_pack; |
229 | | - range_pack_for_m1_shift.set(range_pack<1, 1); |
230 | | - range_pack_for_m1_shift.set(range_pack>nlevs-2, nlevs-2); |
231 | | - Pack delta_u_k, delta_u_km1, |
232 | | - delta_v_k, delta_v_km1, |
233 | | - delta_T_k, delta_T_km1; |
234 | | - ekat::index_and_shift<-1>(s_delta_u, range_pack_for_m1_shift, delta_u_k, delta_u_km1); |
235 | | - ekat::index_and_shift<-1>(s_delta_v, range_pack_for_m1_shift, delta_v_k, delta_v_km1); |
236 | | - ekat::index_and_shift<-1>(s_delta_T, range_pack_for_m1_shift, delta_T_k, delta_T_km1); |
237 | | - |
238 | | - // At the top and bottom of the model, set the end points for |
239 | | - // delta_*_k and delta_*_km1 to be the first and last entries |
240 | | - // of delta_*, respectively. |
241 | | - if (any_at_top) { |
242 | | - delta_u_k.set(at_top, s_delta_u(0)); |
243 | | - delta_v_k.set(at_top, s_delta_v(0)); |
244 | | - delta_T_k.set(at_top, s_delta_T(0)); |
245 | | - } |
246 | | - if (any_at_bot) { |
247 | | - delta_u_km1.set(at_bot, s_delta_u(nlevs-2)); |
248 | | - delta_v_km1.set(at_bot, s_delta_v(nlevs-2)); |
249 | | - delta_T_km1.set(at_bot, s_delta_T(nlevs-2)); |
250 | | - } |
251 | | - |
252 | | - // Get omega_int(k+1) pack. The range pack should not |
253 | | - // contain index > nlevs-1 (since omega_int is size nlevs+1). |
254 | | - auto range_pack_for_p1_shift = range_pack; |
255 | | - range_pack_for_p1_shift.set(range_pack>nlevs-1, nlevs-1); |
256 | | - Pack omega_int_k, omega_int_kp1; |
257 | | - ekat::index_and_shift<1>(s_omega_int, range_pack, omega_int_k, omega_int_kp1); |
258 | | - |
259 | | - const auto fac = (dt/2)/ref_p_del(k); |
260 | | - |
261 | | - // Update u |
262 | | - u(k).update(not_at_bot, fac*omega_int_kp1*delta_u_k, -1, 1); |
263 | | - u(k).update(not_at_top, fac*omega_int_k*delta_u_km1, -1, 1); |
264 | | - |
265 | | - // Update v |
266 | | - v(k).update(not_at_bot, fac*omega_int_kp1*delta_v_k, -1, 1); |
267 | | - v(k).update(not_at_top, fac*omega_int_k*delta_v_km1, -1, 1); |
268 | | - |
269 | | - // Before updating T, first scale using thermal |
270 | | - // expansion term due to LS vertical advection |
271 | | - T(k) *= 1 + (dt*Rair/Cpair)*omega(k)/ref_p_mid(k); |
272 | | - |
273 | | - // Update T |
274 | | - T(k).update(not_at_bot, fac*omega_int_kp1*delta_T_k, -1, 1); |
275 | | - T(k).update(not_at_top, fac*omega_int_k*delta_T_km1, -1, 1); |
276 | | - |
277 | | - // Update Q |
278 | | - Pack delta_tracer_k, delta_tracer_km1; |
279 | | - for (int iq=0; iq<n_q_tracers; ++iq) { |
280 | | - auto s_delta_tracer = Kokkos::subview(s_delta_Q, iq, Kokkos::ALL()); |
281 | | - ekat::index_and_shift<-1>(s_delta_tracer, range_pack_for_m1_shift, delta_tracer_k, delta_tracer_km1); |
282 | | - if (any_at_top) delta_tracer_k.set(at_top, s_delta_tracer(0)); |
283 | | - if (any_at_bot) delta_tracer_km1.set(at_bot, s_delta_tracer(nlevs-2)); |
284 | | - |
285 | | - Q(iq, k).update(not_at_bot, fac*omega_int_kp1*delta_tracer_k, -1, 1); |
286 | | - Q(iq, k).update(not_at_top, fac*omega_int_k*delta_tracer_km1, -1, 1); |
| 173 | + auto s_ref_p_mid = ekat::scalarize(ref_p_mid); |
| 174 | + auto s_omega = ekat::scalarize(omega); |
| 175 | + auto s_u = ekat::scalarize(u); |
| 176 | + auto s_v = ekat::scalarize(v); |
| 177 | + auto s_T = ekat::scalarize(T); |
| 178 | + |
| 179 | + // Semi-Lagrangian update loop |
| 180 | + Kokkos::parallel_for(Kokkos::TeamThreadRange(team, nlevs), [&](const int k) { |
| 181 | + const Real omega_k = s_omega(k); |
| 182 | + const Real p_ref = s_ref_p_mid(k); // reference pressure |
| 183 | + const Real p_tgt = p_ref - dt * omega_k; // target pressure |
| 184 | + |
| 185 | + // Set up linear interpolation |
| 186 | + ekat::LinInterp<Real,Pack::n> vert_interp(1, nlev_packs, nlev_packs); |
| 187 | + vert_interp.setup(team, p_ref, p_tgt); |
| 188 | + |
| 189 | + // Interpolate each field at the departure point |
| 190 | + vert_interp.lin_interp(team, p_ref, p_tgt, s_u, u(k)); |
| 191 | + vert_interp.lin_interp(team, p_ref, p_tgt, s_v, v(k)); |
| 192 | + vert_interp.lin_interp(team, p_ref, p_tgt, s_T, T(k)); |
| 193 | + |
| 194 | + // Add thermal expansion term to temperature |
| 195 | + T(k) *= 1 + (dt*Rair/Cpair)*omega_k/p_ref; |
| 196 | + |
| 197 | + // Interpolate tracers at the departure point |
| 198 | + for (int m = 0; m < n_q_tracers; ++m) { |
| 199 | + auto tracer = Kokkos::subview(Q, m, Kokkos::ALL()); |
| 200 | + vert_interp.lin_interp(team, p_ref, p_tgt, tracer, Q(m,k)); |
287 | 201 | } |
288 | 202 | }); |
289 | | - |
290 | | - // Release WS views |
291 | | - workspace.release_macro_block(delta_Q_slot, n_q_tracers); |
292 | | - workspace.release_many_contiguous<4>({&omega_int, &delta_u, &delta_v, &delta_T}); |
293 | 203 | } |
| 204 | + |
| 205 | + |
294 | 206 | // ========================================================================================= |
295 | 207 | KOKKOS_FUNCTION |
296 | 208 | void IOPForcing:: |
|
0 commit comments