REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_FennelBridge.cpp
Go to the documentation of this file.
1/**
2 * \file REMORA_FennelBridge.cpp
3 *
4 * Path A of the Fennel parity campaign: pack REMORA state into ROMS-shaped
5 * buffers, call the unmodified ROMS kernel through the isohelper, and unpack
6 * the result. Selected at run time by remora.use_biology_cpp_answer = 0.
7 *
8 * The pack/unpack boundary is deliberately explicit rather than aliasing
9 * FArrayBox memory: REMORA FABs carry ghost cells and index k from 0 at the
10 * bottom, while ROMS wants a contiguous 1-based k over the valid region. An
11 * explicit copy keeps both paths auditable and immune to ghost-layout
12 * surprises, at a cost that is irrelevant for validation runs.
13 *
14 * See Source/Biology/Fortran/tag_map.md for the diagnostic contract.
15 */
16
17#include <AMReX_MultiFab.H>
18
19#include <REMORA.H>
20#include <REMORA_Biology.H>
21#include <REMORA_Constants.H>
22
24
25using namespace amrex;
26
27namespace {
28
29// Column-major (Fortran) linear index into a packed buffer covering
30// [ilo,ihi] x [jlo,jhi] with nk levels and nc components.
31AMREX_FORCE_INLINE
32int fidx (int i, int j, int k, int n,
33 int ilo, int jlo, int ni, int nj, int nk) noexcept
34{
35 return ((n * nk + k) * nj + (j - jlo)) * ni + (i - ilo);
36}
37
38} // namespace
39
40void
41REMORA::advance_biology_fortran (int lev, MultiFab const& mf_cons_old,
42 MultiFab& mf_cons_new,
43 int N, Real dt_lev)
44{
45#ifdef AMREX_USE_GPU
46 amrex::ignore_unused(lev, mf_cons_old, mf_cons_new, N, dt_lev);
47 amrex::Abort("Fennel Fortran bridge is host-only. Serial-first acceptance "
48 "is the default validation scope; GPU parity is a separate "
49 "lane and is not opened by this campaign.");
50#else
51
52 const auto parms = fennel_params;
53
54 const bool need_gas = parms.oxygen || parms.carbon;
55 const bool do_bulk_flux = solverChoice.bulk_fluxes;
56
57 if (vec_Hz[lev] == nullptr || vec_z_w[lev] == nullptr ||
58 vec_z_r[lev] == nullptr || vec_mskr[lev] == nullptr ||
59 vec_srflx[lev] == nullptr ||
60 (need_gas && do_bulk_flux &&
61 (vec_uwind[lev] == nullptr || vec_vwind[lev] == nullptr)) ||
62 (need_gas && !do_bulk_flux &&
63 (vec_sustr[lev] == nullptr || vec_svstr[lev] == nullptr))) {
64 amrex::Abort("Fennel Fortran bridge requires Hz, z_r, z_w, rmask, "
65 "srflx, and, when carbon or oxygen is active, either "
66 "uwind/vwind (bulk_fluxes) or sustr/svstr");
67 }
68
69 // The ROMS buffer is temperature, salinity, then the biology tracers packed
70 // contiguously: buffer slot 0 is itemp, slot 1 is isalt, slot 2 is iNO3_, and so on.
71 // REMORA may carry passive (dye) scalars between salinity and the biology block, so
72 // the buffer slot maps to a REMORA component through cons_comp_for_slot below rather
73 // than being the component itself. The Fennel ordering in
74 // REMORA_Biology.H::components() matches fennel_mod.h:527-561 exactly, so one rule
75 // covers every biology tracer.
76 const int nbt = REMORABiology::Fennel::num_tracers(parms);
77 const int ntrc = nbt + 2;
78
79 const int l_bio_comp = Bio_comp;
80 auto cons_comp_for_slot = [l_bio_comp] (int slot) noexcept {
81 return (slot < Tracer_comp) ? slot : l_bio_comp + (slot - Tracer_comp);
82 };
83
84 // ROMS N(ng) is a level count; REMORA N is the last valid k index.
85 const int nz = N + 1;
86
87 // REMORA stores shortwave radiation in W/m2. ROMS fennel.h expects
88 // kinematic srflx and converts it back with rho0*Cp when forming PARsur.
89 const Real srflx_to_roms_kinematic =
90 Real(1.0) / (solverChoice.rho0 * Cp);
91
92 for (MFIter mfi(mf_cons_new, false); mfi.isValid(); ++mfi) {
93 const Box bx = mfi.validbox();
94
95 const int ilo = bx.smallEnd(0);
96 const int ihi = bx.bigEnd(0);
97 const int jlo = bx.smallEnd(1);
98 const int jhi = bx.bigEnd(1);
99
100 const int ni = ihi - ilo + 1;
101 const int nj = jhi - jlo + 1;
102
103 // Stress buffers are one cell wider in each direction because
104 // fennel.h averages sustr(i+1,j) and svstr(i,j+1) to cell centres.
105 const int nis = ni + 1;
106 const int njs = nj + 1;
107
108 Array4<Real const> const& state_old = mf_cons_old.const_array(mfi);
109 Array4<Real const> const& state_new = mf_cons_new.const_array(mfi);
110 Array4<Real> const& state_out = mf_cons_new.array(mfi);
111 Array4<Real const> const& Hz_a = vec_Hz[lev]->const_array(mfi);
112 Array4<Real const> const& z_r_a = vec_z_r[lev]->const_array(mfi);
113 Array4<Real const> const& z_w_a = vec_z_w[lev]->const_array(mfi);
114 Array4<Real const> const& srflx_a = vec_srflx[lev]->const_array(mfi);
115 Array4<Real const> const& mskr_a = vec_mskr[lev]->const_array(mfi);
116
117 Array4<Real const> sustr_a;
118 Array4<Real const> svstr_a;
119 Array4<Real const> uwind_a;
120 Array4<Real const> vwind_a;
121 if (need_gas) {
122 if (do_bulk_flux) {
123 uwind_a = vec_uwind[lev]->const_array(mfi);
124 vwind_a = vec_vwind[lev]->const_array(mfi);
125 } else {
126 sustr_a = vec_sustr[lev]->const_array(mfi);
127 svstr_a = vec_svstr[lev]->const_array(mfi);
128 }
129 }
130
131 Vector<double> b_rmask(std::size_t(ni) * nj, 0.0);
132 Vector<double> b_srflx(std::size_t(ni) * nj, 0.0);
133 Vector<double> b_pH (std::size_t(ni) * nj, 0.0);
134 Vector<double> b_Hz (std::size_t(ni) * nj * nz, 0.0);
135 Vector<double> b_z_r (std::size_t(ni) * nj * nz, 0.0);
136 Vector<double> b_z_w (std::size_t(ni) * nj * (nz + 1), 0.0);
137 Vector<double> b_sustr(std::size_t(nis) * njs, 0.0);
138 Vector<double> b_svstr(std::size_t(nis) * njs, 0.0);
139 Vector<double> b_uwind(std::size_t(ni) * nj, 0.0);
140 Vector<double> b_vwind(std::size_t(ni) * nj, 0.0);
141 Vector<double> b_told (std::size_t(ni) * nj * nz * ntrc, 0.0);
142 Vector<double> b_tnew (std::size_t(ni) * nj * nz * ntrc, 0.0);
143
144 // ---- pack -------------------------------------------------------
145 for (int j = jlo; j <= jhi; ++j) {
146 for (int i = ilo; i <= ihi; ++i) {
147 const int s = fidx(i, j, 0, 0, ilo, jlo, ni, nj, 1);
148 b_rmask[s] = mskr_a(i, j, 0);
149 b_srflx[s] = srflx_a(i, j, 0) * srflx_to_roms_kinematic;
150 }
151 }
152
153 // ROMS k is 1-based over the valid column: ROMS k = REMORA k + 1.
154 // This is the only index-base translation in the bridge.
155 for (int k = 0; k < nz; ++k) {
156 for (int j = jlo; j <= jhi; ++j) {
157 for (int i = ilo; i <= ihi; ++i) {
158 const int s = fidx(i, j, k, 0, ilo, jlo, ni, nj, nz);
159 b_Hz [s] = Hz_a (i, j, k);
160 b_z_r[s] = z_r_a(i, j, k);
161 }
162 }
163 }
164
165 // z_w has no shift: ROMS z_w(0:N(ng)) and REMORA z_w k=0..N+1 both
166 // index faces from the bottom, and both hold nz+1 values.
167 for (int k = 0; k <= nz; ++k) {
168 for (int j = jlo; j <= jhi; ++j) {
169 for (int i = ilo; i <= ihi; ++i) {
170 b_z_w[fidx(i, j, k, 0, ilo, jlo, ni, nj, nz + 1)] =
171 z_w_a(i, j, k);
172 }
173 }
174 }
175
176 if (need_gas && !do_bulk_flux) {
177 for (int j = jlo; j <= jhi + 1; ++j) {
178 for (int i = ilo; i <= ihi + 1; ++i) {
179 const int s = fidx(i, j, 0, 0, ilo, jlo, nis, njs, 1);
180 b_sustr[s] = sustr_a(i, j, 0);
181 b_svstr[s] = svstr_a(i, j, 0);
182 }
183 }
184 }
185
186 if (need_gas && do_bulk_flux) {
187 for (int j = jlo; j <= jhi; ++j) {
188 for (int i = ilo; i <= ihi; ++i) {
189 const int s = fidx(i, j, 0, 0, ilo, jlo, ni, nj, 1);
190 b_uwind[s] = uwind_a(i, j, 0);
191 b_vwind[s] = vwind_a(i, j, 0);
192 }
193 }
194 }
195
196 // Buffer slot n is ROMS tracer n+1; cons_comp_for_slot skips any dye scalars.
197 for (int n = 0; n < ntrc; ++n) {
198 const int comp = cons_comp_for_slot(n);
199 for (int k = 0; k < nz; ++k) {
200 for (int j = jlo; j <= jhi; ++j) {
201 for (int i = ilo; i <= ihi; ++i) {
202 const int s =
203 fidx(i, j, k, n, ilo, jlo, ni, nj, nz);
204 b_told[s] = state_old(i, j, k, comp);
205 b_tnew[s] = state_new(i, j, k, comp);
206 }
207 }
208 }
209 }
210
211 // ---- call -------------------------------------------------------
213 ilo, ihi, jlo, jhi, nz, nbt,
214 ilo, ihi, jlo, jhi,
215 parms.po4 ? 1 : 0,
216 parms.carbon ? 1 : 0,
217 parms.oxygen ? 1 : 0,
218 parms.odu ? 1 : 0,
219 parms.denitrification ? 1 : 0,
220 parms.bio_sediment ? 1 : 0,
221 do_bulk_flux ? 1 : 0,
222 parms.river_don ? 1 : 0,
223 parms.talk_nonconserv ? 1 : 0,
224 // Enum ordinals rather than bools: these options are three- and
225 // two-way choices, and the Fortran side compares them against the
226 // CPP set it was compiled with.
227 static_cast<int>(parms.pco2air_type),
228 static_cast<int>(parms.co2_schmidt),
229 static_cast<int>(parms.o2_schmidt),
230 parms.BioIter,
231 double(dt_lev), double(solverChoice.rho0), double(Cp),
232 double(parms.AttSW), double(parms.AttChl), double(parms.PARfrac),
233 double(parms.Vp0), double(parms.I_thNH4), double(parms.D_p5NH4),
234 double(parms.NitriR), double(parms.K_NO3), double(parms.K_NH4),
235 double(parms.K_PO4), double(parms.K_Phy), double(parms.Chl2C_m),
236 double(parms.ChlMin), double(parms.PhyCN), double(parms.R_P2N),
237 double(parms.PhyIP), double(parms.PhyIS), double(parms.PhyMin),
238 double(parms.PhyMR), double(parms.ZooAE_N), double(parms.ZooCN),
239 double(parms.ZooBM), double(parms.ZooER), double(parms.ZooGR),
240 double(parms.ZooMin), double(parms.ZooMR), double(parms.LDeRRN),
241 double(parms.LDeRRC), double(parms.CoagR), double(parms.SDeRRN),
242 double(parms.SDeRRC), double(parms.RDeRRN), double(parms.RDeRRC),
243 double(parms.wPhy), double(parms.wLDet),
244 double(parms.wSDet), double(parms.pCO2air),
245 b_rmask.data(), b_Hz.data(), b_z_r.data(), b_z_w.data(),
246 b_srflx.data(), b_sustr.data(), b_svstr.data(),
247 b_uwind.data(), b_vwind.data(), b_pH.data(),
248 b_told.data(), b_tnew.data(),
250
251 // ---- unpack -----------------------------------------------------
252 // biology_tile updates only the biology tracers, so only those are
253 // written back; temperature and salinity in cons_new stay untouched.
254 for (int n = Tracer_comp; n < ntrc; ++n) {
255 const int comp = cons_comp_for_slot(n);
256 for (int k = 0; k < nz; ++k) {
257 for (int j = jlo; j <= jhi; ++j) {
258 for (int i = ilo; i <= ihi; ++i) {
259 state_out(i, j, k, comp) =
260 Real(b_tnew[fidx(i, j, k, n, ilo, jlo,
261 ni, nj, nz)]);
262 }
263 }
264 }
265 }
266 }
267#endif // AMREX_USE_GPU
268}
constexpr amrex::Real Cp
void fennel_bridge_advance_c(int ilo, int ihi, int jlo, int jhi, int nz, int nbt, int istr, int iend, int jstr, int jend, int use_po4, int use_carbon, int use_oxygen, int use_odu, int use_denit, int use_biosed, int use_bulk, int use_rivdon, int use_talknc, int pco2air_kind, int co2_sc_kind, int o2_sc_kind, int bioiter, double dt_sec, double rho0, double cp_heat, double p_AttSW, double p_AttChl, double p_PARfrac, double p_Vp0, double p_I_thNH4, double p_D_p5NH4, double p_NitriR, double p_K_NO3, double p_K_NH4, double p_K_PO4, double p_K_Phy, double p_Chl2C_m, double p_ChlMin, double p_PhyCN, double p_R_P2N, double p_PhyIP, double p_PhyIS, double p_PhyMin, double p_PhyMR, double p_ZooAE_N, double p_ZooCN, double p_ZooBM, double p_ZooER, double p_ZooGR, double p_ZooMin, double p_ZooMR, double p_LDeRRN, double p_LDeRRC, double p_CoagR, double p_SDeRRN, double p_SDeRRC, double p_RDeRRN, double p_RDeRRC, double p_wPhy, double p_wLDet, double p_wSDet, double p_pCO2air, const double *rmask, const double *Hz, const double *z_r, const double *z_w, const double *srflx, const double *sustr, const double *svstr, const double *uwind, const double *vwind, double *pH, const double *t_nstp, double *t_nnew, int dbg_level, int dbg_i, int dbg_j)
#define Tracer_comp
int biology_debug_i
Target column i index for biology_debug = 1.
Definition REMORA.H:1661
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_vwind
Wind in the v direction, defined at rho-points.
Definition REMORA.H:474
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr
land/sea mask at cell centers (2D)
Definition REMORA.H:557
int biology_debug
Biology diagnostic verbosity: 0 off, 1 target column, 2 all columns. See Source/Biology/Fortran/tag_m...
Definition REMORA.H:1659
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_sustr
Surface stress in the u direction.
Definition REMORA.H:467
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Hz
Width of cells in the vertical (z-) direction (3D, Hz in ROMS)
Definition REMORA.H:412
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_uwind
Wind in the u direction, defined at rho-points.
Definition REMORA.H:472
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_r
z coordinates at rho points (cell centers)
Definition REMORA.H:441
int Bio_comp
First cons component of the biology block, i.e. Tracer_comp + nscalar. The state is laid out as temp,...
Definition REMORA.H:1641
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_svstr
Surface stress in the v direction.
Definition REMORA.H:469
static SolverChoice solverChoice
Container for algorithmic choices.
Definition REMORA.H:1717
int biology_debug_j
Target column j index for biology_debug = 1.
Definition REMORA.H:1663
REMORABiology::FennelParameters fennel_params
Runtime parameters for the Fennel biology package.
Definition REMORA.H:1648
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_srflx
Shortwave radiation flux [W/m²], defined at rho-points.
Definition REMORA.H:483
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_w
z coordinates at w points (faces between z-cells)
Definition REMORA.H:444
int num_tracers(FennelParameters const &parameters) noexcept
@ data
annual climatology of Laurent et al. (2017)
integer, dimension(ngrids) n