REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_Coupling.cpp
Go to the documentation of this file.
1#include <REMORA.H>
2
3#include <AMReX_BCRec.H>
4#include <AMReX_Box.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_Print.H>
11
12using namespace amrex;
13
14/*
15 Coupling reference context (implementation-side):
16
17 1) Legacy state-passing contract:
18 Warner et al. (2010), COAWST, Fig. 5 / Block B.
19 REMORA receives atmospheric states and computes surface fluxes internally
20 (bulk-physics/COARE-style path).
21
22 2) Future direct flux-passing roadmap:
23 COAWST's ATM2OCN_FLUXES pathway (documented in COAWST manuals/workshops
24 and exercised in Zambon et al., 2014) motivates direct flux exchange
25 (tau_x, tau_y, heat/moisture) instead of state-only exchange.
26 COAWST code anchors:
27 - Master/mct_roms_wrf.h
28 - ROMS/Nonlinear/atm2ocn_flux.F
29 - ROMS/Nonlinear/bulk_flux.F
30*/
31
32namespace {
33constexpr int SSTIndex = 0;
34
35Geometry
36make_unit_slab_geometry (Box const& domain)
37{
38 static constexpr Real lo[AMREX_SPACEDIM] = {0.0_rt, 0.0_rt, 0.0_rt};
39 static constexpr Real hi[AMREX_SPACEDIM] = {1.0_rt, 1.0_rt, 1.0_rt};
40 static constexpr int periodicity[AMREX_SPACEDIM] = {0, 0, 0};
41 RealBox rb(lo, hi);
42 return Geometry(domain, &rb, CoordSys::cartesian, periodicity);
43}
44
45Vector<BCRec>
46make_internal_bcs (int ncomp)
47{
48 Vector<BCRec> bcs(ncomp);
49 for (auto& bc : bcs) {
50 for (int dir = 0; dir < AMREX_SPACEDIM; ++dir) {
51 bc.setLo(dir, BCType::int_dir);
52 bc.setHi(dir, BCType::int_dir);
53 }
54 }
55 return bcs;
56}
57}
58
59amrex::Real
60REMORA::EvolveOneStep (amrex::Real /*time*/, amrex::Real /*dt_request*/)
61{
62 Real cur_time = t_new[0];
63 const int step = istep[0];
64
65 if (cur_time >= stop_time) {
66 return Real(0.0);
67 }
68
69 ComputeDt();
70
71 int lev = 0;
72 int iteration = 1;
73 if (max_level == 0) {
74 timeStep(lev, cur_time, iteration);
75 } else {
76 timeStepML(cur_time, iteration);
77 }
78
79 cur_time += dt[0];
80
81 if ( (plot_int > 0 && (step+1 - last_plot_file_step) == plot_int ) ||
82 (plot_int_time > 0 && (cur_time >= (last_plot_file_time + plot_int_time))) )
83 {
84 last_plot_file_step = step+1;
85 last_plot_file_time = cur_time;
86 WritePlotFile(step+1);
88 }
89
90 if ((check_int > 0 && (step+1 - last_check_file_step) == check_int)
91 || (check_int_time > 0 && cur_time >= (last_check_file_time + check_int_time))) {
92 last_check_file_step = step+1;
93 last_check_file_time = cur_time;
95 }
96
97 post_timestep(step, cur_time, dt[0]);
98
99 return dt[0];
100}
101
102/*
103 * \brief Extracts SST from the 3D conservative state for the atmospheric driver.
104 *
105 * Reads Temp_comp at the top water-column cell (k_sfc), converts from
106 * Celsius to Kelvin, and copies the result into state[SSTIndex].
107 *
108 * @param[in,out] state OCN2ATM slab buffer sized by the driver (one MultiFab
109 * per ocean-to-atmosphere export layer). state[SSTIndex]
110 * (index 0) is overwritten with sea-surface temperature
111 * (SST) sampled from the Temp_comp tracer at the uppermost
112 * sigma level (k = Nz) and converted from degrees Celsius
113 * to Kelvin for the atmospheric driver. An empty vector or
114 * null state[0] is treated as a no-op.
115 * @param[in ] time Current ocean model time (unused; retained for driver
116 * interface conformance).
117 */
118void
119REMORA::PackSurfaceState (Vector<MultiFab*>& state, Real /*time*/)
120{
121 if (state.empty() || state[SSTIndex] == nullptr) { return; }
122 const int lev = 0;
123
124 // REMORA stores temperature in Celsius. Surface is at k=N (top of water column).
125 const int k_sfc = cons_new[lev]->boxArray().minimalBox().bigEnd(2);
126
127 // Build a temp MultiFab on REMORA's ba2d (k=0) derived from cons_new's BoxArray.
128 // Same DistributionMap ensures each box is local; we fill at k=0 from cons at k=k_sfc.
129 BoxList bl2d = cons_new[lev]->boxArray().boxList();
130 for (auto& b : bl2d) { b.setRange(2, 0); }
131 BoxArray ba2d(std::move(bl2d));
132 MultiFab tmp(ba2d, cons_new[lev]->DistributionMap(), 1, 0);
133
134 for (MFIter mfi(*cons_new[lev]); mfi.isValid(); ++mfi) {
135 auto const& c = cons_new[lev]->const_array(mfi);
136 auto t = tmp.array(mfi);
137 Box bx = makeSlab(mfi.validbox(), 2, k_sfc);
138 ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int) {
139 // Write to k=0 in tmp (ba2d range); convert Celsius → Kelvin.
140 t(i, j, 0) = c(i, j, k_sfc, Temp_comp) + 273.15_rt;
141 });
142 }
143
144 MultiFab& dst = *state[SSTIndex];
145 const Box src_domain = tmp.boxArray().minimalBox();
146 const Box dst_domain = dst.boxArray().minimalBox();
147
148 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(
149 src_domain.smallEnd(2) == src_domain.bigEnd(2) &&
150 dst_domain.smallEnd(2) == dst_domain.bigEnd(2),
151 "REMORA::PackSurfaceState expects 2D slab source and destination MultiFabs.");
152 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(
153 src_domain.smallEnd(0) == dst_domain.smallEnd(0) &&
154 src_domain.smallEnd(1) == dst_domain.smallEnd(1),
155 "REMORA::PackSurfaceState requires aligned atmosphere/ocean slab origins.");
156
157 const IntVect src_len = src_domain.length();
158 const IntVect dst_len = dst_domain.length();
159 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(
160 src_len[2] == 1 && dst_len[2] == 1,
161 "REMORA::PackSurfaceState expects unit-thickness source and destination slabs.");
162
163 const bool same_xy = (src_len[0] == dst_len[0] && src_len[1] == dst_len[1]);
164 const bool dst_finer =
165 (dst_len[0] >= src_len[0] && dst_len[1] >= src_len[1] &&
166 dst_len[0] % src_len[0] == 0 && dst_len[1] % src_len[1] == 0);
167 const bool dst_coarser =
168 (src_len[0] >= dst_len[0] && src_len[1] >= dst_len[1] &&
169 src_len[0] % dst_len[0] == 0 && src_len[1] % dst_len[1] == 0);
170
171 if (same_xy) {
172 dst.ParallelCopy(tmp, 0, 0, 1);
173 return;
174 }
175
176 if (dst_finer) {
177 const IntVect ratio(dst_len[0] / src_len[0], dst_len[1] / src_len[1], 1);
178 const auto src_geom = make_unit_slab_geometry(src_domain);
179 const auto dst_geom = make_unit_slab_geometry(dst_domain);
180 const auto bcs = make_internal_bcs(1);
181 amrex::InterpFromCoarseLevel(dst, IntVect(0), IntVect(0),
182 tmp, 0, 0, 1,
183 src_geom, dst_geom,
184 ratio, &pc_interp, bcs, 0);
185 return;
186 }
187
188 if (dst_coarser) {
189 const IntVect ratio(src_len[0] / dst_len[0], src_len[1] / dst_len[1], 1);
190 amrex::average_down(tmp, dst, 0, 1, ratio);
191 return;
192 }
193
194 amrex::Abort("REMORA::PackSurfaceState requires matching horizontal extents and integer-ratio atmosphere/ocean slab resolutions.");
195}
196
197/*
198 * \brief Receives atmospheric states from the driver and applies unit conversions.
199 *
200 * Fills REMORA's internal forcing MultiFabs from states and records which
201 * lanes were successfully updated in driver_atmos_state_from_driver.
202 * Unit conversions applied: Pair Pa to mb; Tair K to Celsius.
203 *
204 * @param[in] states ATM2OCN forcing slab buffer from the driver (Warner et al.
205 * 2010, Block B state-passing contract), indexed by AtmosState.
206 * Expected units per lane:
207 * Uwind/Vwind: 10-m winds [m/s];
208 * Pair: mean sea-level pressure [Pa, converted to mb];
209 * Qair: near-surface specific humidity [kg/kg];
210 * Tair: 2-m air temperature [K, converted to degC];
211 * Cloud: cloud fraction [0-1];
212 * Rain: precipitation rate [kg/m^2/s];
213 * SWrad/LWrad: downwelling shortwave/longwave radiation [W/m^2].
214 * Missing lanes (null pointer or index out of range) are skipped;
215 * driver_atmos_state_from_driver tracks populated lanes for the
216 * bulk-flux parameterization fallback logic.
217 * @param[in] time Current ocean model time (unused; retained for driver
218 * interface conformance).
219 */
220void
221REMORA::ApplyAtmosphericStates (const Vector<MultiFab*>& states, Real /*time*/)
222{
224 if (finest_level < 0) { return; }
225
226 // Wind (m/s) — no unit conversion
227 if (vec_uwind[0] != nullptr) {
228 if (states.size() > AtmosState::Uwind && states[AtmosState::Uwind] != nullptr) {
229 vec_uwind[0]->ParallelCopy(*states[AtmosState::Uwind], 0, 0, 1);
230 vec_uwind[0]->FillBoundary(geom[0].periodicity());
232 }
233 }
234 if (vec_vwind[0] != nullptr) {
235 if (states.size() > AtmosState::Vwind && states[AtmosState::Vwind] != nullptr) {
236 vec_vwind[0]->ParallelCopy(*states[AtmosState::Vwind], 0, 0, 1);
237 vec_vwind[0]->FillBoundary(geom[0].periodicity());
239 }
240 }
241
242 // Atmospheric pressure: Pa → mb (REMORA bulk flux expects mb)
243 if (vec_Pair[0] != nullptr) {
244 if (states.size() > AtmosState::Pair && states[AtmosState::Pair] != nullptr) {
245 vec_Pair[0]->ParallelCopy(*states[AtmosState::Pair], 0, 0, 1);
246 vec_Pair[0]->mult(0.01_rt, 0, 1);
247 vec_Pair[0]->FillBoundary(geom[0].periodicity());
249 }
250 }
251
252 // Specific humidity (kg/kg) — no conversion
253 if (vec_qair[0] != nullptr) {
254 if (states.size() > AtmosState::Qair && states[AtmosState::Qair] != nullptr) {
255 vec_qair[0]->ParallelCopy(*states[AtmosState::Qair], 0, 0, 1);
256 vec_qair[0]->FillBoundary(geom[0].periodicity());
258 }
259 }
260
261 // Air temperature: K → °C (REMORA stores/uses Celsius internally)
262 if (vec_Tair[0] != nullptr) {
263 if (states.size() > AtmosState::Tair && states[AtmosState::Tair] != nullptr) {
264 vec_Tair[0]->ParallelCopy(*states[AtmosState::Tair], 0, 0, 1);
265 vec_Tair[0]->plus(-273.15_rt, 0, 1);
266 vec_Tair[0]->FillBoundary(geom[0].periodicity());
268 }
269 }
270
271 // Cloud fraction [0-1], rain, SW/LW radiation — no unit conversion
272 if (vec_cloud[0] != nullptr) {
273 if (states.size() > AtmosState::Cloud && states[AtmosState::Cloud] != nullptr) {
274 vec_cloud[0]->ParallelCopy(*states[AtmosState::Cloud], 0, 0, 1);
275 vec_cloud[0]->FillBoundary(geom[0].periodicity());
277 }
278 }
279 if (vec_rain[0] != nullptr) {
280 if (states.size() > AtmosState::Rain && states[AtmosState::Rain] != nullptr) {
281 vec_rain[0]->ParallelCopy(*states[AtmosState::Rain], 0, 0, 1);
282 vec_rain[0]->FillBoundary(geom[0].periodicity());
284 }
285 }
286 if (vec_srflx[0] != nullptr) {
287 if (states.size() > AtmosState::SWrad && states[AtmosState::SWrad] != nullptr) {
288 vec_srflx[0]->ParallelCopy(*states[AtmosState::SWrad], 0, 0, 1);
289 vec_srflx[0]->FillBoundary(geom[0].periodicity());
291 }
292 }
293 if (vec_longwave_down[0] != nullptr) {
294 if (states.size() > AtmosState::LWrad && states[AtmosState::LWrad] != nullptr) {
295 vec_longwave_down[0]->ParallelCopy(*states[AtmosState::LWrad], 0, 0, 1);
296 vec_longwave_down[0]->FillBoundary(geom[0].periodicity());
298 }
299 }
300
301}
#define Temp_comp
void PackSurfaceState(amrex::Vector< amrex::MultiFab * > &state, amrex::Real time)
Extracts SST from the 3D conservative state for the atmospheric driver.
amrex::Real last_plot_file_time
Simulation time when we last output a plotfile.
Definition REMORA.H:1411
amrex::Vector< amrex::MultiFab * > cons_new
multilevel data container for current step's scalar data: temperature, salinity, passive tracer
Definition REMORA.H:294
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_vwind
Wind in the v direction, defined at rho-points.
Definition REMORA.H:382
int history_count
Counter for which time index we are writing to in the netcdf history file.
Definition REMORA.H:1481
amrex::Real stop_time
Time to stop.
Definition REMORA.H:1426
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rain
precipitation rate [kg/m^2/s]
Definition REMORA.H:411
amrex::Real EvolveOneStep(amrex::Real time, amrex::Real dt_request)
std::array< bool, AtmosState::NumTypes > driver_atmos_state_from_driver
provenance flags for driver-supplied atmospheric forcing lanes
Definition REMORA.H:420
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_uwind
Wind in the u direction, defined at rho-points.
Definition REMORA.H:380
amrex::Real check_int_time
Checkpoint output interval in seconds.
Definition REMORA.H:1471
amrex::Real plot_int_time
Plotfile output interval in seconds.
Definition REMORA.H:1465
void post_timestep(int nstep, amrex::Real time, amrex::Real dt_lev)
Called after every level 0 timestep.
Definition REMORA.cpp:326
int last_check_file_step
Step when we last output a checkpoint file.
Definition REMORA.H:1414
void ComputeDt()
a wrapper for estTimeStep()
int plot_int
Plotfile output interval in iterations.
Definition REMORA.H:1463
amrex::Vector< int > istep
which step?
Definition REMORA.H:1360
void WriteCheckpointFile()
write checkpoint file to disk
amrex::Vector< amrex::Real > t_new
new time at each level
Definition REMORA.H:1364
void ApplyAtmosphericStates(const amrex::Vector< amrex::MultiFab * > &states, amrex::Real time)
Receives atmospheric states from the driver and applies unit conversions.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_longwave_down
Downward longwave radiation.
Definition REMORA.H:395
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_cloud
cloud cover fraction [0-1], defined at rho-points
Definition REMORA.H:415
int check_int
Checkpoint output interval in iterations.
Definition REMORA.H:1469
void WritePlotFile(int istep)
main driver for writing AMReX plotfiles
int last_plot_file_step
Step when we last output a plotfile.
Definition REMORA.H:1409
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_srflx
Shortwave radiation flux [W/m²], defined at rho-points.
Definition REMORA.H:391
amrex::Real last_check_file_time
Simulation time when we last output a checkpoint file.
Definition REMORA.H:1416
amrex::Vector< amrex::Real > dt
time step at each level
Definition REMORA.H:1368
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Pair
Air pressure [mb], defined at rho-points.
Definition REMORA.H:388
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_qair
Specific humidity [kg/kg], defined at rho-points.
Definition REMORA.H:386
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Tair
Air temperature [°C], defined at rho-points.
Definition REMORA.H:384
@ 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]