Skip to content

Commit 4eaaf68

Browse files
authored
Merge pull request #2861 from E3SM-Project/bogensch/DPxx_vtheta_dp_fix
DPxx - Use consistent formulation of vtheta_dp. Manually merging since we expect DP CIME cases to diff.
2 parents 48fe5a4 + f8211b9 commit 4eaaf68

File tree

1 file changed

+141
-150
lines changed

1 file changed

+141
-150
lines changed

components/eamxx/src/dynamics/homme/eamxx_homme_iop.cpp

Lines changed: 141 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010
// Homme includes
1111
#include "Context.hpp"
1212
#include "ColumnOps.hpp"
13-
#include "ElementOps.hpp"
14-
#include "EquationOfState.hpp"
1513
#include "HommexxEnums.hpp"
1614
#include "HybridVCoord.hpp"
17-
#include "KernelVariables.hpp"
1815
#include "SimulationParams.hpp"
1916
#include "Types.hpp"
2017

18+
// SCREAM includes
19+
#include "share/util/scream_common_physics_functions.hpp"
20+
2121
// EKAT includes
2222
#include "ekat/ekat_workspace.hpp"
2323
#include "ekat/kokkos/ekat_kokkos_types.hpp"
@@ -222,11 +222,7 @@ void HommeDynamics::
222222
apply_iop_forcing(const Real dt)
223223
{
224224
using ESU = ekat::ExeSpaceUtils<KT::ExeSpace>;
225-
226-
using EOS = Homme::EquationOfState;
227-
using ElementOps = Homme::ElementOps;
228-
using KV = Homme::KernelVariables;
229-
225+
using PF = PhysicsFunctions<DefaultDevice>;
230226
using ColOps = ColumnOps<DefaultDevice, Real>;
231227
using C = physics::Constants<Real>;
232228
constexpr Real Rair = C::Rair;
@@ -263,7 +259,7 @@ apply_iop_forcing(const Real dt)
263259
const auto hyai = m_dyn_grid->get_geometry_data("hyai").get_view<const Real*>();
264260
const auto hybi = m_dyn_grid->get_geometry_data("hybi").get_view<const Real*>();
265261

266-
// Homme element states and EOS/EO classes
262+
// Homme element states
267263
auto ps_dyn = get_internal_field("ps_dyn").get_view<Real***>();
268264
auto dp3d_dyn = get_internal_field("dp3d_dyn").get_view<Pack****>();
269265
auto vtheta_dp_dyn = get_internal_field("vtheta_dp_dyn").get_view<Pack****>();
@@ -272,11 +268,6 @@ apply_iop_forcing(const Real dt)
272268
auto Q_dyn = m_helper_fields.at("Q_dyn").get_view<Pack*****>();
273269
auto Qdp_dyn = get_internal_field("Qdp_dyn").get_view<Pack*****>();
274270

275-
EOS eos;
276-
eos.init(params.theta_hydrostatic_mode, hvcoord);
277-
278-
ElementOps elem_ops;
279-
elem_ops.init(hvcoord);
280271
const bool use_moisture = (params.moisture == Homme::MoistDry::MOIST);
281272

282273
// Load data from IOP files, if necessary
@@ -319,92 +310,66 @@ apply_iop_forcing(const Real dt)
319310
: m_iop->get_iop_field("v").get_view<const Pack*>();
320311
}
321312

322-
// Team policy and workspace manager for both homme and scream
323-
// related loops. We need separate policies since hommexx functions used here
324-
// assume they are called inside nested loops for elements and Gaussian points,
325-
// whereas EAMxx function we use expects a single level of parallelism
326-
// for elements and Guassian points.
327-
// TODO: scream::ColumnOps functions could take an arbitary loop boundary
328-
// (TeamVectorRange, TeamThreadRange, ThreadVectorRange) so that
329-
// all 3 kernel launches here could be combined.
330-
const auto policy_homme = ESU::get_default_team_policy(nelem, NLEV);
331-
const auto policy_eamxx = ESU::get_default_team_policy(nelem*NGP*NGP, NLEV);
313+
// Team policy and workspace manager for eamxx
314+
const auto policy_iop = ESU::get_default_team_policy(nelem*NGP*NGP, NLEV);
332315

333316
// TODO: Create a memory buffer for this class
334317
// and add the below WSM and views
335-
WorkspaceMgr eamxx_wsm(NLEVI, 7+qsize, policy_eamxx);
336-
WorkspaceMgr homme_wsm(NLEV, 16 + (theta_hydrostatic_mode ? 16 : 0), policy_homme);
318+
WorkspaceMgr iop_wsm(NLEVI, 7+qsize, policy_iop);
337319
view_Nd<Pack, 4>
338-
rstar ("rstar", nelem, NGP, NGP, NLEV),
339-
exner ("exner", nelem, NGP, NGP, NLEV),
340320
temperature("temperature", nelem, NGP, NGP, NLEV);
341321

342-
// Lambda for computing rstar, exner, and temperature from Hommexx
343-
auto compute_homme_states = [&] () {
344-
Kokkos::parallel_for("compute_rstar_exner_and_temperature", policy_homme, KOKKOS_LAMBDA (const KT::MemberType& team) {
345-
KV kv(team);
346-
const int ie = team.league_rank();
322+
// Lambda for computing temperature
323+
auto compute_temperature = [&] () {
324+
Kokkos::parallel_for("compute_temperature_for_iop", policy_iop, KOKKOS_LAMBDA (const KT::MemberType& team) {
325+
const int ie = team.league_rank()/(NGP*NGP);
326+
const int igp = (team.league_rank()/NGP)%NGP;
327+
const int jgp = team.league_rank()%NGP;
347328

348329
// Get temp views from workspace
349-
auto ws = homme_wsm.get_workspace(team);
350-
auto pnh_slot = ws.take_macro_block("pnh" , NGP*NGP);
351-
uview_2d<Pack> pnh(reinterpret_cast<Pack*>(pnh_slot.data()), NGP*NGP, NLEV);
352-
353-
Kokkos::parallel_for(Kokkos::TeamThreadRange(kv.team, NGP*NGP), [&] (const int idx) {
354-
const int igp = idx/NGP;
355-
const int jgp = idx%NGP;
356-
357-
auto dp3d_i = ekat::subview(dp3d_dyn, ie, igp, jgp);
358-
auto vtheta_dp_i = ekat::subview(vtheta_dp_dyn, ie, igp, jgp);
359-
auto phi_int_i = ekat::subview(phi_int_dyn, ie, igp, jgp);
360-
auto qv_i = ekat::subview(Q_dyn, ie, 0, igp, jgp);
361-
auto pnh_i = ekat::subview(pnh, idx);
362-
auto rstar_i = ekat::subview(rstar, ie, igp, jgp);
363-
auto exner_i = ekat::subview(exner, ie, igp, jgp);
364-
auto temperature_i = ekat::subview(temperature, ie, igp, jgp);
365-
366-
// Reinterperate into views of Homme::Scalar for calling Hommexx function.
367-
Homme::ExecViewUnmanaged<Homme::Scalar[NLEV]> dp3d_scalar(reinterpret_cast<Homme::Scalar*>(dp3d_i.data()), NLEV);
368-
Homme::ExecViewUnmanaged<Homme::Scalar[NLEV]> vtheta_dp_scalar(reinterpret_cast<Homme::Scalar*>(vtheta_dp_i.data()), NLEV);
369-
Homme::ExecViewUnmanaged<Homme::Scalar[NLEVI]> phi_int_scalar(reinterpret_cast<Homme::Scalar*>(phi_int_i.data()), NLEVI);
370-
Homme::ExecViewUnmanaged<Homme::Scalar[NLEV]> qv_scalar(reinterpret_cast<Homme::Scalar*>(qv_i.data()), NLEV);
371-
Homme::ExecViewUnmanaged<Homme::Scalar[NLEV]> pnh_scalar(reinterpret_cast<Homme::Scalar*>(pnh_i.data()), NLEV);
372-
Homme::ExecViewUnmanaged<Homme::Scalar[NLEV]> exner_scalar(reinterpret_cast<Homme::Scalar*>(exner_i.data()), NLEV);
373-
Homme::ExecViewUnmanaged<Homme::Scalar[NLEV]> rstar_scalar(reinterpret_cast<Homme::Scalar*>(rstar_i.data()), NLEV);
374-
Homme::ExecViewUnmanaged<Homme::Scalar[NLEV]> temperature_scalar(reinterpret_cast<Homme::Scalar*>(temperature_i.data()), NLEV);
375-
376-
// Compute exner from EOS
377-
if (theta_hydrostatic_mode) {
378-
auto hydro_p_int = ws.take("hydro_p_int");
379-
Homme::ExecViewUnmanaged<Homme::Scalar[NLEVI]> hydro_p_int_scalar(reinterpret_cast<Homme::Scalar*>(hydro_p_int.data()), NLEVI);
380-
elem_ops.compute_hydrostatic_p(kv, dp3d_scalar, hydro_p_int_scalar, pnh_scalar);
381-
eos.compute_exner(kv, pnh_scalar, exner_scalar);
382-
ws.release(hydro_p_int);
383-
} else {
384-
eos.compute_pnh_and_exner(kv, vtheta_dp_scalar, phi_int_scalar, pnh_scalar, exner_scalar);
385-
}
330+
auto ws = iop_wsm.get_workspace(team);
331+
uview_1d<Pack> pmid;
332+
ws.take_many_contiguous_unsafe<1>({"pmid"},{&pmid});
333+
334+
auto ps_i = ps_dyn(ie, igp, jgp);
335+
auto dp3d_i = ekat::subview(dp3d_dyn, ie, igp, jgp);
336+
auto vtheta_dp_i = ekat::subview(vtheta_dp_dyn, ie, igp, jgp);
337+
auto qv_i = ekat::subview(Q_dyn, ie, 0, igp, jgp);
338+
auto temperature_i = ekat::subview(temperature, ie, igp, jgp);
339+
340+
// Compute reference pressures and layer thickness.
341+
// TODO: Allow geometry data to allocate packsize
342+
auto s_pmid = ekat::scalarize(pmid);
343+
Kokkos::parallel_for(Kokkos::TeamVectorRange(team, total_levels), [&](const int& k) {
344+
s_pmid(k) = hyam(k)*ps0 + hybm(k)*ps_i;
345+
});
346+
team.team_barrier();
386347

387-
// Get the temperature from dynamics states
388-
elem_ops.get_temperature(kv, eos, use_moisture, dp3d_scalar, exner_scalar, vtheta_dp_scalar, qv_scalar, rstar_scalar, temperature_scalar);
348+
// Compute temperature from virtual potential temperature
349+
Kokkos::parallel_for(Kokkos::TeamVectorRange(team, NLEV), [&] (const int k) {
350+
auto T_val = vtheta_dp_i(k);
351+
T_val /= dp3d_i(k);
352+
T_val = PF::calculate_temperature_from_virtual_temperature(T_val,qv_i(k));
353+
temperature_i(k) = PF::calculate_T_from_theta(T_val,pmid(k));
389354
});
390355

391356
// Release WS views
392-
ws.release_macro_block(pnh_slot, NGP*NGP);
357+
ws.release_many_contiguous<1>({&pmid});
393358
});
394359
};
395360

396-
// Preprocess some homme states to get temperature and exner
397-
compute_homme_states();
361+
// Preprocess some homme states to get temperature
362+
compute_temperature();
398363
Kokkos::fence();
399364

400365
// Apply IOP forcing
401-
Kokkos::parallel_for("apply_iop_forcing", policy_eamxx, KOKKOS_LAMBDA (const KT::MemberType& team) {
366+
Kokkos::parallel_for("apply_iop_forcing", policy_iop, KOKKOS_LAMBDA (const KT::MemberType& team) {
402367
const int ie = team.league_rank()/(NGP*NGP);
403368
const int igp = (team.league_rank()/NGP)%NGP;
404369
const int jgp = team.league_rank()%NGP;
405370

406371
// Get temp views from workspace
407-
auto ws = eamxx_wsm.get_workspace(team);
372+
auto ws = iop_wsm.get_workspace(team);
408373
uview_1d<Pack> pmid, pint, pdel;
409374
ws.take_many_contiguous_unsafe<3>({"pmid", "pint", "pdel"},
410375
{&pmid, &pint, &pdel});
@@ -449,53 +414,66 @@ apply_iop_forcing(const Real dt)
449414
Kokkos::fence();
450415

451416
// Postprocess homme states Qdp and vtheta_dp
452-
Kokkos::parallel_for("compute_qdp_and_vtheta_dp", policy_homme, KOKKOS_LAMBDA (const KT::MemberType& team) {
453-
KV kv(team);
454-
const int ie = team.league_rank();
417+
Kokkos::parallel_for("compute_qdp_and_vtheta_dp", policy_iop, KOKKOS_LAMBDA (const KT::MemberType& team) {
418+
const int ie = team.league_rank()/(NGP*NGP);
419+
const int igp = (team.league_rank()/NGP)%NGP;
420+
const int jgp = team.league_rank()%NGP;
455421

456-
Kokkos::parallel_for(Kokkos::TeamThreadRange(kv.team, NGP*NGP), [&] (const int idx) {
457-
const int igp = idx/NGP;
458-
const int jgp = idx%NGP;
422+
// Get temp views from workspace
423+
auto ws = iop_wsm.get_workspace(team);
424+
uview_1d<Pack> pmid, pint, pdel;
425+
ws.take_many_contiguous_unsafe<3>({"pmid", "pint", "pdel"},
426+
{&pmid, &pint, &pdel});
459427

460-
auto dp3d_i = ekat::subview(dp3d_dyn, ie, igp, jgp);
461-
auto vtheta_dp_i = ekat::subview(vtheta_dp_dyn, ie, igp, jgp);
462-
auto qv_i = ekat::subview(Q_dyn, ie, 0, igp, jgp);
463-
auto Q_i = Kokkos::subview(Q_dyn, ie, Kokkos::ALL(), igp, jgp, Kokkos::ALL());
464-
auto Qdp_i = Kokkos::subview(Qdp_dyn, ie, Kokkos::ALL(), igp, jgp, Kokkos::ALL());
465-
auto rstar_i = ekat::subview(rstar, ie, igp, jgp);
466-
auto exner_i = ekat::subview(exner, ie, igp, jgp);
467-
auto temperature_i = ekat::subview(temperature, ie, igp, jgp);
428+
auto ps_i = ps_dyn(ie, igp, jgp);
429+
auto dp3d_i = ekat::subview(dp3d_dyn, ie, igp, jgp);
430+
auto vtheta_dp_i = ekat::subview(vtheta_dp_dyn, ie, igp, jgp);
431+
auto qv_i = ekat::subview(Q_dyn, ie, 0, igp, jgp);
432+
auto Q_i = Kokkos::subview(Q_dyn, ie, Kokkos::ALL(), igp, jgp, Kokkos::ALL());
433+
auto Qdp_i = Kokkos::subview(Qdp_dyn, ie, Kokkos::ALL(), igp, jgp, Kokkos::ALL());
434+
auto temperature_i = ekat::subview(temperature, ie, igp, jgp);
435+
436+
// Compute reference pressures and layer thickness.
437+
// TODO: Allow geometry data to allocate packsize
438+
auto s_pmid = ekat::scalarize(pmid);
439+
auto s_pint = ekat::scalarize(pint);
440+
Kokkos::parallel_for(Kokkos::TeamVectorRange(team, total_levels+1), [&](const int& k) {
441+
s_pint(k) = hyai(k)*ps0 + hybi(k)*ps_i;
442+
if (k < total_levels) {
443+
s_pmid(k) = hyam(k)*ps0 + hybm(k)*ps_i;
444+
}
445+
});
468446

469-
// Reinterperate into views of Homme::Scalar for calling Hommexx function.
470-
Homme::ExecViewUnmanaged<Homme::Scalar[NLEV]> qv_scalar(reinterpret_cast<Homme::Scalar*>(qv_i.data()), NLEV);
471-
Homme::ExecViewUnmanaged<Homme::Scalar[NLEV]> rstar_scalar(reinterpret_cast<Homme::Scalar*>(rstar_i.data()), NLEV);
447+
team.team_barrier();
472448

473-
// Compute Qdp from updated Q
474-
Kokkos::parallel_for(Kokkos::ThreadVectorRange(team, NLEV*qsize), [&] (const int k) {
475-
const int ilev = k/qsize;
476-
const int q = k%qsize;
449+
// Compute Qdp from updated Q
450+
Kokkos::parallel_for(Kokkos::TeamVectorRange(team, NLEV*qsize), [&] (const int k) {
451+
const int ilev = k/qsize;
452+
const int q = k%qsize;
477453

478-
Qdp_i(q, ilev) = Q_i(q, ilev)*dp3d_i(ilev);
479-
// For BFB on restarts, Q needs to be updated after we compute Qdp
480-
Q_i(q, ilev) = Qdp_i(q, ilev)/dp3d_i(ilev);
481-
});
454+
Qdp_i(q, ilev) = Q_i(q, ilev)*dp3d_i(ilev);
455+
// For BFB on restarts, Q needs to be updated after we compute Qdp
456+
Q_i(q, ilev) = Qdp_i(q, ilev)/dp3d_i(ilev);
457+
});
482458
team.team_barrier();
483459

484-
// Recompute rstar with updated qv, and convert updated temperature back to potential temperature
485-
elem_ops.get_R_star(kv, use_moisture, qv_scalar, rstar_scalar);
486-
Kokkos::parallel_for(Kokkos::ThreadVectorRange(team, NLEV), [&] (const int k) {
487-
vtheta_dp_i(k) = temperature_i(k)*rstar_i(k)*dp3d_i(k)/(Rair*exner_i(k));
488-
});
460+
// Convert updated temperature back to psuedo density virtual potential temperature
461+
Kokkos::parallel_for(Kokkos::TeamVectorRange(team, NLEV), [&] (const int k) {
462+
const auto th = PF::calculate_theta_from_T(temperature_i(k),pmid(k));
463+
vtheta_dp_i(k) = PF::calculate_virtual_temperature(th,qv_i(k))*dp3d_i(k);
489464
});
465+
466+
// Release WS views
467+
ws.release_many_contiguous<3>({&pmid, &pint, &pdel});
490468
});
491469

492470
if (iop_nudge_tq or iop_nudge_uv) {
493471
// Nudge the domain based on the domain mean
494472
// and observed quantities of T, Q, u, and
495473

496474
if (iop_nudge_tq) {
497-
// Compute rstar, exner and temperature from Hommexx
498-
compute_homme_states();
475+
// Compute temperature
476+
compute_temperature();
499477
Kokkos::fence();
500478
}
501479

@@ -571,50 +549,63 @@ apply_iop_forcing(const Real dt)
571549
// Apply relaxation
572550
const auto rtau = std::max(dt, iop_nudge_tscale);
573551
Kokkos::parallel_for("apply_domain_relaxation",
574-
policy_homme,
552+
policy_iop,
575553
KOKKOS_LAMBDA (const KT::MemberType& team) {
576-
KV kv(team);
577-
const int ie = team.league_rank();
578-
579-
Kokkos::parallel_for(Kokkos::TeamThreadRange(kv.team, NGP*NGP), [&] (const int idx) {
580-
const int igp = idx/NGP;
581-
const int jgp = idx%NGP;
582-
583-
auto dp3d_i = ekat::subview(dp3d_dyn, ie, igp, jgp);
584-
auto vtheta_dp_i = ekat::subview(vtheta_dp_dyn, ie, igp, jgp);
585-
auto rstar_i = ekat::subview(rstar, ie, igp, jgp);
586-
auto exner_i = ekat::subview(exner, ie, igp, jgp);
587-
auto qv_i = ekat::subview(Q_dyn, ie, 0, igp, jgp);
588-
auto temperature_i = ekat::subview(temperature, ie, igp, jgp);
589-
auto u_i = ekat::subview(v_dyn, ie, 0, igp, jgp);
590-
auto v_i = ekat::subview(v_dyn, ie, 1, igp, jgp);
591-
592-
Kokkos::parallel_for(Kokkos::ThreadVectorRange(team, NLEV), [&](const int& k) {
593-
if (iop_nudge_tq) {
594-
// Restrict nudging of T and qv to certain levels if requested by user
595-
// IOP pressure variable is in unitis of [Pa], while iop_nudge_tq_low/high
596-
// is in units of [hPa], thus convert iop_nudge_tq_low/high
597-
Mask nudge_level(false);
598-
int max_size = hyam.size();
599-
for (int lev=k*Pack::n, p = 0; p < Pack::n && lev < max_size; ++lev, ++p) {
600-
const auto pressure_from_iop = hyam(lev)*ps0 + hybm(lev)*ps_iop;
601-
nudge_level.set(p, pressure_from_iop <= iop_nudge_tq_low*100
602-
and
603-
pressure_from_iop >= iop_nudge_tq_high*100);
604-
}
605-
606-
qv_i(k).update(nudge_level, qv_mean(k) - qv_iop(k), -dt/rtau, 1.0);
607-
temperature_i(k).update(nudge_level, t_mean(k) - t_iop(k), -dt/rtau, 1.0);
608-
609-
// Convert updated temperature back to potential temperature
610-
vtheta_dp_i(k) = temperature_i(k)*rstar_i(k)*dp3d_i(k)/(Rair*exner_i(k));
611-
}
612-
if (iop_nudge_uv) {
613-
u_i(k).update(u_mean(k) - u_iop(k), -dt/rtau, 1.0);
614-
v_i(k).update(v_mean(k) - v_iop(k), -dt/rtau, 1.0);
554+
555+
const int ie = team.league_rank()/(NGP*NGP);
556+
const int igp = (team.league_rank()/NGP)%NGP;
557+
const int jgp = team.league_rank()%NGP;
558+
559+
// Get temp views from workspace
560+
auto ws = iop_wsm.get_workspace(team);
561+
uview_1d<Pack> pmid;
562+
ws.take_many_contiguous_unsafe<1>({"pmid"},{&pmid});
563+
564+
auto ps_i = ps_dyn(ie, igp, jgp);
565+
auto dp3d_i = ekat::subview(dp3d_dyn, ie, igp, jgp);
566+
auto vtheta_dp_i = ekat::subview(vtheta_dp_dyn, ie, igp, jgp);
567+
auto qv_i = ekat::subview(Q_dyn, ie, 0, igp, jgp);
568+
auto temperature_i = ekat::subview(temperature, ie, igp, jgp);
569+
auto u_i = ekat::subview(v_dyn, ie, 0, igp, jgp);
570+
auto v_i = ekat::subview(v_dyn, ie, 1, igp, jgp);
571+
572+
// Compute reference pressures and layer thickness.
573+
// TODO: Allow geometry data to allocate packsize
574+
auto s_pmid = ekat::scalarize(pmid);
575+
Kokkos::parallel_for(Kokkos::TeamVectorRange(team, total_levels), [&](const int& k) {
576+
s_pmid(k) = hyam(k)*ps0 + hybm(k)*ps_i;
577+
});
578+
team.team_barrier();
579+
580+
Kokkos::parallel_for(Kokkos::ThreadVectorRange(team, NLEV), [&](const int& k) {
581+
if (iop_nudge_tq) {
582+
// Restrict nudging of T and qv to certain levels if requested by user
583+
// IOP pressure variable is in unitis of [Pa], while iop_nudge_tq_low/high
584+
// is in units of [hPa], thus convert iop_nudge_tq_low/high
585+
Mask nudge_level(false);
586+
int max_size = hyam.size();
587+
for (int lev=k*Pack::n, p = 0; p < Pack::n && lev < max_size; ++lev, ++p) {
588+
const auto pressure_from_iop = hyam(lev)*ps0 + hybm(lev)*ps_iop;
589+
nudge_level.set(p, pressure_from_iop <= iop_nudge_tq_low*100
590+
and
591+
pressure_from_iop >= iop_nudge_tq_high*100);
615592
}
616-
});
593+
594+
qv_i(k).update(nudge_level, qv_mean(k) - qv_iop(k), -dt/rtau, 1.0);
595+
temperature_i(k).update(nudge_level, t_mean(k) - t_iop(k), -dt/rtau, 1.0);
596+
597+
// Convert updated temperature back to virtual potential temperature
598+
const auto th = PF::calculate_theta_from_T(temperature_i(k),pmid(k));
599+
vtheta_dp_i(k) = PF::calculate_virtual_temperature(th,qv_i(k))*dp3d_i(k);
600+
}
601+
if (iop_nudge_uv) {
602+
u_i(k).update(u_mean(k) - u_iop(k), -dt/rtau, 1.0);
603+
v_i(k).update(v_mean(k) - v_iop(k), -dt/rtau, 1.0);
604+
}
617605
});
606+
607+
// Release WS views
608+
ws.release_many_contiguous<1>({&pmid});
618609
});
619610
}
620611
}

0 commit comments

Comments
 (0)