Skip to content

Commit b0cfca0

Browse files
Peter BogenschutzThomas Clevenger
authored andcommitted
change IOP subsidence calculation from finite centered to semi lagrangian
1 parent 6918a79 commit b0cfca0

1 file changed

Lines changed: 31 additions & 119 deletions

File tree

components/eamxx/src/physics/iop_forcing/eamxx_iop_forcing_process_interface.cpp

Lines changed: 31 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "share/field/field_utils.hpp"
44
#include "share/property_checks/field_within_interval_check.hpp"
5+
#include "ekat/util/ekat_lin_interp.hpp"
56

67
#include <ekat_math_utils.hpp>
78

@@ -169,128 +170,39 @@ advance_iop_subsidence(const MemberType& team,
169170
const auto n_q_tracers = Q.extent_int(0);
170171
const auto nlev_packs = ekat::npack<Pack>(nlevs);
171172

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));
287201
}
288202
});
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});
293203
}
204+
205+
294206
// =========================================================================================
295207
KOKKOS_FUNCTION
296208
void IOPForcing::

0 commit comments

Comments
 (0)