3#include <AMReX_BCRec.H>
5#include <AMReX_FillPatchUtil.H>
6#include <AMReX_Geometry.H>
7#include <AMReX_Interpolater.H>
8#include <AMReX_MFIter.H>
9#include <AMReX_MultiFabUtil.H>
10#include <AMReX_iMultiFab.H>
11#include <AMReX_Print.H>
12#include <AMReX_Reduce.H>
37constexpr int SSTIndex = 0;
54StagedSourceBoxArray (
const amrex::MultiFab& src,
55 const amrex::MultiFab& dst,
56 const amrex::iMultiFab& index_mf,
59 using namespace amrex;
61 const Box src_domain = src.boxArray().minimalBox();
62 const int nboxes =
static_cast<int>(dst.boxArray().size());
63 constexpr int int_big = std::numeric_limits<int>::max();
65 Vector<int> lo(2 * nboxes, int_big);
66 Vector<int> hi(2 * nboxes, -int_big);
68 for (MFIter mfi(dst); mfi.isValid(); ++mfi) {
69 const int b = mfi.index();
70 const Box bx = mfi.validbox();
71 auto const& idx = index_mf.const_array(mfi);
73 ReduceOps<ReduceOpMin, ReduceOpMin, ReduceOpMax, ReduceOpMax> reduce_op;
74 ReduceData<int, int, int, int> reduce_data(reduce_op);
75 using ReduceTuple =
typename decltype(reduce_data)::Type;
77 reduce_op.eval(bx, reduce_data,
78 [=] AMREX_GPU_DEVICE (
int i,
int j,
int k) -> ReduceTuple
80 int i_min = int_big, j_min = int_big, i_max = -int_big, j_max = -int_big;
81 for (
int m = 0; m < max_stencil_size; ++m) {
82 const int si = idx(i, j, k, m * 3);
83 const int sj = idx(i, j, k, m * 3 + 1);
84 if (si < 0 || sj < 0) {
continue; }
85 i_min = amrex::min(i_min, si); i_max = amrex::max(i_max, si);
86 j_min = amrex::min(j_min, sj); j_max = amrex::max(j_max, sj);
88 return {i_min, j_min, i_max, j_max};
91 auto const& hv = reduce_data.value(reduce_op);
92 lo[2*b ] = amrex::get<0>(hv); lo[2*b+1] = amrex::get<1>(hv);
93 hi[2*b ] = amrex::get<2>(hv); hi[2*b+1] = amrex::get<3>(hv);
96 ParallelDescriptor::ReduceIntMin(lo.dataPtr(),
static_cast<int>(lo.size()));
97 ParallelDescriptor::ReduceIntMax(hi.dataPtr(),
static_cast<int>(hi.size()));
102 const IndexType src_ixtype = src.boxArray().ixType();
104 BoxList bl(src_ixtype);
105 for (
int b = 0; b < nboxes; ++b) {
107 if (lo[2*b] > hi[2*b] || lo[2*b+1] > hi[2*b+1]) {
110 need = Box(src_domain.smallEnd(), src_domain.smallEnd(), src_ixtype);
112 need = Box(IntVect(lo[2*b], lo[2*b+1], src_domain.smallEnd(2)),
113 IntVect(hi[2*b], hi[2*b+1], src_domain.bigEnd(2)),
119 return BoxArray(std::move(bl));
123ApplyConservativeRemap (
const amrex::MultiFab& src,
124 amrex::MultiFab& dst,
125 const amrex::MultiFab& weight_mf,
126 const amrex::iMultiFab& index_mf,
127 int max_stencil_size,
128 const amrex::MultiFab* dst_mask =
nullptr,
129 const amrex::iMultiFab* dst_land_mask =
nullptr)
131 using namespace amrex;
135 MultiFab src_on_dst(StagedSourceBoxArray(src, dst, index_mf, max_stencil_size),
136 dst.DistributionMap(), src.nComp(), 0);
137 src_on_dst.setVal(0.0);
138 src_on_dst.ParallelCopy(src);
143 for (MFIter mfi(dst, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
144 Box bx = mfi.tilebox();
146 auto const& w_arr = weight_mf.const_array(mfi);
147 auto const& idx_arr = index_mf.const_array(mfi);
148 auto const& src_arr = src_on_dst.const_array(mfi);
149 auto dst_arr = dst.array(mfi);
150 const bool has_mask = (dst_mask !=
nullptr);
151 auto const& mask_arr = has_mask ? dst_mask->const_array(mfi) : Array4<const Real>{};
154 const bool has_land_mask = (dst_land_mask !=
nullptr);
155 auto const& land_arr = has_land_mask ? dst_land_mask->const_array(mfi) : Array4<const int>{};
157 ParallelFor(bx, [=] AMREX_GPU_DEVICE (
int i,
int j,
int k) {
159 for (
int m = 0; m < max_stencil_size; ++m) {
160 Real w = w_arr(i, j, k, m);
162 int src_i = idx_arr(i, j, k, m * 3);
163 int src_j = idx_arr(i, j, k, m * 3 + 1);
164 int src_k = idx_arr(i, j, k, m * 3 + 2);
166 sum += w * src_arr(src_i, src_j, src_k);
169 if (has_mask) { sum *= mask_arr(i, j, k); }
170 if (has_land_mask && land_arr(i, j, k) == 1) { sum = Real(0.0); }
171 dst_arr(i, j, k) = sum;
180 Real cur_time =
t_new[0];
181 const int step =
istep[0];
191 if (max_level == 0) {
208 bool use_two_way_coupling,
224 amrex::DistributionMapping& dm)
const
226 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(
228 "REMORA::GetAtmosToOceanRhoLayout requires post-InitData rho-point forcing storage.");
235 amrex::DistributionMapping& dm)
const
237 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(
239 "REMORA::GetAtmosToOceanUFaceLayout requires post-InitData u-face forcing storage.");
246 amrex::DistributionMapping& dm)
const
248 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(
250 "REMORA::GetAtmosToOceanVFaceLayout requires post-InitData v-face forcing storage.");
257 const amrex::MultiFab*& y_psi)
const
271 const amrex::MultiFab*& lat_psi)
const
285 const amrex::MultiFab*& msku,
286 const amrex::MultiFab*& mskv)
const
302 const amrex::MultiFab* weight_o2a_mf,
303 const amrex::iMultiFab* index_o2a_mf,
304 int max_stencil_size,
305 const amrex::iMultiFab* dst_land_mask)
307 if (state.empty() || state[SSTIndex] ==
nullptr) {
return; }
311 const int k_sfc =
cons_new[lev]->boxArray().minimalBox().bigEnd(2);
314 BoxList bl2d =
cons_new[lev]->boxArray().boxList();
315 for (
auto& b : bl2d) { b.setRange(2, 0); }
316 BoxArray ba2d(std::move(bl2d));
317 MultiFab tmp(ba2d,
cons_new[lev]->DistributionMap(), 1, 0);
319 for (MFIter mfi(*
cons_new[lev]); mfi.isValid(); ++mfi) {
320 auto const& c =
cons_new[lev]->const_array(mfi);
321 auto t = tmp.array(mfi);
322 Box bx = makeSlab(mfi.validbox(), 2, k_sfc);
323 ParallelFor(bx, [=] AMREX_GPU_DEVICE (
int i,
int j,
int) {
325 t(i, j, 0) = c(i, j, k_sfc,
Temp_comp) + Real(273.15);
329 MultiFab& dst = *state[SSTIndex];
331 if (weight_o2a_mf !=
nullptr && index_o2a_mf !=
nullptr) {
333 ApplyConservativeRemap(tmp, dst, *weight_o2a_mf, *index_o2a_mf, max_stencil_size,
334 nullptr, dst_land_mask);
338 dst.ParallelCopy(tmp, 0, 0, 1);
351 if (finest_level < 0) {
return; }
357 vec_uwind[0]->FillBoundary(geom[0].periodicity());
364 vec_vwind[0]->FillBoundary(geom[0].periodicity());
373 vec_Pair[0]->mult(Real(0.01), 0, 1);
374 vec_Pair[0]->FillBoundary(geom[0].periodicity());
383 vec_qair[0]->FillBoundary(geom[0].periodicity());
392 vec_Tair[0]->plus(Real(-273.15), 0, 1);
393 vec_Tair[0]->FillBoundary(geom[0].periodicity());
402 vec_cloud[0]->FillBoundary(geom[0].periodicity());
409 vec_rain[0]->FillBoundary(geom[0].periodicity());
416 vec_srflx[0]->FillBoundary(geom[0].periodicity());
436 if (finest_level < 0) {
return; }
470 tau_x_tmp.setVal(
zero);
472 tau_x_tmp.FillBoundary(geom[0].periodicity());
474 tau_y_tmp.setVal(
zero);
476 tau_y_tmp.FillBoundary(geom[0].periodicity());
478 shflux_tmp.setVal(
zero);
480 shflux_tmp.FillBoundary(geom[0].periodicity());
482 lhflux_tmp.setVal(
zero);
484 lhflux_tmp.FillBoundary(geom[0].periodicity());
486 lwflux_tmp.setVal(
zero);
488 lwflux_tmp.FillBoundary(geom[0].periodicity());
492 vec_srflx[0]->FillBoundary(geom[0].periodicity());
496 vec_rain[0]->FillBoundary(geom[0].periodicity());
500 vec_evap[0]->FillBoundary(geom[0].periodicity());
507 for (MFIter mfi(*
vec_sustr[0], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
508 Array4<Real>
const& sustr =
vec_sustr[0]->array(mfi);
509 Array4<const Real>
const& msku =
vec_msku[0]->const_array(mfi);
510 Array4<const Real>
const& tau_x = tau_x_tmp.const_array(mfi);
511 Box ubx = mfi.grownnodaltilebox(0, IntVect(
NGROW,
NGROW,0));
514 ParallelFor(ubxD, [=] AMREX_GPU_DEVICE (
int i,
int j,
int ) {
515 sustr(i,j,0) = -tau_x(i,j,0) /
rho0 * msku(i,j,0);
519 for (MFIter mfi(*
vec_svstr[0], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
520 Array4<Real>
const& svstr =
vec_svstr[0]->array(mfi);
521 Array4<const Real>
const& mskv =
vec_mskv[0]->const_array(mfi);
522 Array4<const Real>
const& tau_y = tau_y_tmp.const_array(mfi);
523 Box vbx = mfi.grownnodaltilebox(1, IntVect(
NGROW,
NGROW,0));
527 ParallelFor(vbxD, [=] AMREX_GPU_DEVICE (
int i,
int j,
int ) {
528 svstr(i,j,0) = -tau_y(i,j,0) /
rho0 * mskv(i,j,0);
532 for (MFIter mfi(*
vec_stflux[0], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
533 Array4<Real>
const& stflux =
vec_stflux[0]->array(mfi);
534 Array4<Real>
const& lrflx =
vec_lrflx[0]->array(mfi);
535 Array4<Real>
const& lhflx =
vec_lhflx[0]->array(mfi);
536 Array4<Real>
const& shflx =
vec_shflx[0]->array(mfi);
537 Array4<const Real>
const& mskr =
vec_mskr[0]->const_array(mfi);
538 Array4<const Real>
const& srflx =
vec_srflx[0]->const_array(mfi);
539 Array4<const Real>
const& rain =
vec_rain[0]->const_array(mfi);
540 Array4<const Real>
const& evap =
vec_evap[0]->const_array(mfi);
541 Array4<const Real>
const& shflux = shflux_tmp.const_array(mfi);
542 Array4<const Real>
const& lhflux = lhflux_tmp.const_array(mfi);
543 Array4<const Real>
const& lwflux = lwflux_tmp.const_array(mfi);
545 Box gbx2 = mfi.growntilebox(IntVect(
NGROW,
NGROW,0));
549 ParallelFor(gbx2D, [=] AMREX_GPU_DEVICE (
int i,
int j,
int ) {
550 lrflx(i,j,0) = lwflux(i,j,0) * Hscale2;
551 lhflx(i,j,0) = -lhflux(i,j,0) * Hscale2;
552 shflx(i,j,0) = -shflux(i,j,0) * Hscale2;
554 (srflx(i,j,0) * Hscale2 + lrflx(i,j,0) + lhflx(i,j,0) + shflx(i,j,0))
557 mskr(i,j,0) * (evap(i,j,0) - rain(i,j,0)) /
rhow;
561 vec_sustr[0]->FillBoundary(geom[0].periodicity());
562 vec_svstr[0]->FillBoundary(geom[0].periodicity());
563 vec_srflx[0]->FillBoundary(geom[0].periodicity());
564 vec_lrflx[0]->FillBoundary(geom[0].periodicity());
565 vec_lhflx[0]->FillBoundary(geom[0].periodicity());
566 vec_shflx[0]->FillBoundary(geom[0].periodicity());
567 vec_stflux[0]->FillBoundary(geom[0].periodicity());
568 vec_rain[0]->FillBoundary(geom[0].periodicity());
569 vec_evap[0]->FillBoundary(geom[0].periodicity());
570 vec_stflux[0]->FillBoundary(geom[0].periodicity());
572 const Real sustr_min =
vec_sustr[0]->min(0);
573 const Real sustr_max =
vec_sustr[0]->max(0);
574 const Real svstr_min =
vec_svstr[0]->min(0);
575 const Real svstr_max =
vec_svstr[0]->max(0);
580 const Real srflx_min =
vec_srflx[0]->min(0);
581 const Real srflx_max =
vec_srflx[0]->max(0);
582 const Real lrflx_min =
vec_lrflx[0]->min(0);
583 const Real lrflx_max =
vec_lrflx[0]->max(0);
584 const Real lhflx_min =
vec_lhflx[0]->min(0);
585 const Real lhflx_max =
vec_lhflx[0]->max(0);
586 const Real shflx_min =
vec_shflx[0]->min(0);
587 const Real shflx_max =
vec_shflx[0]->max(0);
589 amrex::Print() <<
"REMORA ApplyAtmosphericFluxes validation:\n"
590 <<
" sustr: min=" << sustr_min <<
" max=" << sustr_max <<
"\n"
591 <<
" svstr: min=" << svstr_min <<
" max=" << svstr_max <<
"\n"
592 <<
" stflux(Temp): min=" << stflux_temp_min <<
" max=" << stflux_temp_max <<
"\n"
593 <<
" stflux(Salt): min=" << stflux_salt_min <<
" max=" << stflux_salt_max <<
"\n"
594 <<
" srflx: min=" << srflx_min <<
" max=" << srflx_max <<
"\n"
595 <<
" lrflx: min=" << lrflx_min <<
" max=" << lrflx_max <<
"\n"
596 <<
" lhflx: min=" << lhflx_min <<
" max=" << lhflx_max <<
"\n"
597 <<
" shflx: min=" << shflx_min <<
" max=" << shflx_max <<
"\n";
constexpr amrex::Real one
constexpr amrex::Real zero
constexpr amrex::Real rhow
void ConfigureDriverAtmosToOceanCoupling(bool use_coupling_driver, bool use_two_way_coupling, DriverAtmosForcingMode active_mode)
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_evap
evaporation rate [kg/m^2/s]
bool running_with_coupling_driver
True once REMORA has received forcing through the coupling driver.
void GetLandSeaMasks(const amrex::MultiFab *&mskr, const amrex::MultiFab *&msku, const amrex::MultiFab *&mskv) const
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_lrflx
longwave radiation
amrex::Vector< amrex::MultiFab * > cons_new
multilevel data container for current step's scalar data: temperature, salinity, passive tracer
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_vwind
Wind in the v direction, defined at rho-points.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr
land/sea mask at cell centers (2D)
amrex::Real stop_time
Time to stop.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rain
precipitation rate [kg/m^2/s]
void GetAtmosToOceanPsiLonLat(const amrex::MultiFab *&lon_psi, const amrex::MultiFab *&lat_psi) const
void ApplyAtmosphericFluxes(const amrex::Vector< amrex::MultiFab * > &states, amrex::Real time)
Receives atmospheric flux lanes from the driver and assembles REMORA flux inputs.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_sustr
Surface stress in the u direction.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_yp
y_grid on psi-points (2D)
void GetAtmosToOceanVFaceLayout(amrex::BoxArray &ba, amrex::DistributionMapping &dm) const
void GetAtmosToOceanRhoLayout(amrex::BoxArray &ba, amrex::DistributionMapping &dm) const
void WriteAtIntermediateTime(int step, amrex::Real cur_time)
Write checkpoint and plotfiles at intermediate point of simulation, if needed.
amrex::Real EvolveOneStep(amrex::Real time, amrex::Real dt_request)
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_msku
land/sea mask at x-faces (2D)
std::array< bool, AtmosState::NumTypes > driver_atmos_state_from_driver
provenance flags for driver-supplied atmospheric forcing lanes
void GetAtmosToOceanPsiCoordinates(const amrex::MultiFab *&x_psi, const amrex::MultiFab *&y_psi) const
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_uwind
Wind in the u direction, defined at rho-points.
DriverAtmosForcingMode driver_atmos_forcing_mode
Active atmosphere-to-ocean forcing contract on the most recent driver apply.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_shflx
sensible heat flux
void post_timestep(int nstep, amrex::Real time, amrex::Real dt_lev)
Called after every level 0 timestep.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_lhflx
latent heat flux
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_lonp
longitude on psi-points (2D, degrees east); only filled when the grid NetCDF file carries lon_psi
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskv
land/sea mask at y-faces (2D)
void ComputeDt()
a wrapper for estTimeStep()
amrex::Vector< int > istep
which step?
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_svstr
Surface stress in the v direction.
amrex::Vector< amrex::Real > t_new
new time at each level
static SolverChoice solverChoice
Container for algorithmic choices.
void ApplyAtmosphericStates(const amrex::Vector< amrex::MultiFab * > &states, amrex::Real time)
Receives atmospheric states from the driver and applies unit conversions.
bool driver_uses_two_way_coupling
Driver-level direction flag copied in before InitData.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_xp
x_grid on psi-points (2D)
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_longwave_down
Downward longwave radiation.
void GetAtmosToOceanUFaceLayout(amrex::BoxArray &ba, amrex::DistributionMapping &dm) const
void SetDriverAtmosToOceanForcingMode(DriverAtmosForcingMode mode)
void timeStep(int lev, amrex::Real time, int iteration)
advance a level by dt, includes a recursive call for finer levels
void timeStepML(amrex::Real time, int iteration)
advance all levels by dt, loops over finer levels
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_stflux
Surface tracer flux; input arrays.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_cloud
cloud cover fraction [0-1], defined at rho-points
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_srflx
Shortwave radiation flux [W/m²], defined at rho-points.
amrex::Vector< amrex::Real > dt
time step at each level
void PackSurfaceState(amrex::Vector< amrex::MultiFab * > &state, amrex::Real time, const amrex::MultiFab *weight_o2a_mf, const amrex::iMultiFab *index_o2a_mf, int max_stencil_size, const amrex::iMultiFab *dst_land_mask=nullptr)
Extracts SST from the 3D conservative state for the atmospheric driver.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Pair
Air pressure [mb], defined at rho-points.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_qair
Specific humidity [kg/kg], defined at rho-points.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Tair
Air temperature [°C], defined at rho-points.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_latp
latitude on psi-points (2D, degrees north); only filled when the grid NetCDF file carries lat_psi
@ LWrad
longwave flux lane
@ LHflux
latent heat flux lane
@ Rain
precipitation rate lane
@ Evap
evaporation rate lane
@ SHflux
sensible heat flux lane
@ TauX
surface zonal stress lane
@ TauY
surface meridional stress lane
@ SWrad
shortwave radiation lane
@ Pair
atmospheric pressure [Pa from driver, mb in REMORA]
@ Vwind
10-m meridional wind [m/s]
@ Qair
specific humidity [kg/kg]
@ SWrad
downward shortwave radiation [W/m^2]
@ LWrad
downward longwave radiation [W/m^2]
@ Uwind
10-m zonal wind [m/s]
@ Rain
precipitation rate [kg/m^2/s]
@ Cloud
cloud fraction [0-1]
@ Tair
air temperature [K from driver, degC in REMORA]