REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_compute_W.cpp
Go to the documentation of this file.
1#include <REMORA.H>
2
3using namespace amrex;
4
5/**
6 * Vertical volume flux across the w-levels (ROMS omega times the cell area), from the
7 * vertical integral of the horizontal mass flux divergence. W is zero at the bed and at
8 * the free surface.
9 *
10 * @param[in ] bx2d k = 0 slab of the columns to compute
11 * @param[ out] W vertical volume flux
12 * @param[in ] Huon u-volume flux
13 * @param[in ] Hvom v-volume flux
14 * @param[in ] z_w z coordinates at w points
15 * @param[in ] h bathymetry
16 * @param[in ] N number of vertical levels minus one
17 */
18void
20 const Array4<Real >& W,
23 const Array4<Real const>& z_w,
24 const Array4<Real const>& h,
25 const int N)
26{
27 ParallelFor(bx2d, [=] AMREX_GPU_DEVICE (int i, int j, int )
28 {
29 W(i,j,0) = zero;
30 });
32 [=] AMREX_GPU_DEVICE (int i, int j, int , int kk)
33 {
34 // Starting with zero vertical velocity at the bottom, integrate
35 // from the bottom (k=0) to the free-surface (k=N). The w(:,:,N(ng))
36 // contains the vertical velocity at the free-surface, d(zeta)/d(t).
37 // Notice that barotropic mass flux divergence is not used directly.
38 //
39 int k = kk + 1;
40 W(i,j,k) = W(i,j,k-1) - (Huon(i+1,j,k-1)-Huon(i,j,k-1)) - (Hvom(i,j+1,k-1)-Hvom(i,j,k-1));
41 });
42 ParallelFor(bx2d, [=] AMREX_GPU_DEVICE (int i, int j, int )
43 {
44 W(i,j,N+1)=W(i,j,N+1)/(z_w(i,j,N+1)+h(i,j,0,0)); // wrk_i
45 });
47 [=] AMREX_GPU_DEVICE (int i, int j, int , int kk)
48 {
49 int k = kk + 1;
50 W(i,j,k) = W(i,j,k)- W(i,j,N+1)*(z_w(i,j,k)+h(i,j,0,0));
51 });
52 ParallelFor(bx2d, [=] AMREX_GPU_DEVICE (int i, int j, int )
53 {
54 W(i,j,N+1) = zero;
55 });
56}
57
58/**
59 * W converted from a volume flux to a velocity, as ROMS scale_omega. It is zero at the bed
60 * and the free surface.
61 *
62 * @param[in ] lev level
63 * @param[out] mf_omega z-nodal omega (m/s); ghost cells are filled across grids and
64 * periodic boundaries, and zero outside the domain
65 * @param[in ] mf_Huon u-volume flux on at least the valid faces
66 * @param[in ] mf_Hvom v-volume flux on at least the valid faces
67 */
68void
70 const MultiFab& mf_Huon, const MultiFab& mf_Hvom)
71{
72 const int N = Geom(lev).Domain().size()[2]-1;
73
74 mf_omega.setVal(zero);
75
76 // Not tiled: compute_W integrates whole columns
77#ifdef _OPENMP
78#pragma omp parallel if (Gpu::notInLaunchRegion())
79#endif
80 for (MFIter mfi(mf_omega); mfi.isValid(); ++mfi)
81 {
82 Array4<Real> const& W = mf_omega.array(mfi);
83
84 Array4<Real const> const& pm = vec_pm[lev]->const_array(mfi);
85 Array4<Real const> const& pn = vec_pn[lev]->const_array(mfi);
86
87 Box bx2d = makeSlab(mfi.validbox(),2,0);
88 compute_W(bx2d, W, mf_Huon.const_array(mfi), mf_Hvom.const_array(mfi),
89 vec_z_w[lev]->const_array(mfi), vec_h[lev]->const_array(mfi), N);
90
91 ParallelFor(bx2d, N, [=] AMREX_GPU_DEVICE (int i, int j, int, int kk)
92 {
93 W(i,j,kk+1) *= pm(i,j,0) * pn(i,j,0);
94 });
95 }
96
97 mf_omega.FillBoundary(geom[lev].periodicity());
98}
99
100/**
101 * omega as ROMS writes it to its history file: main3d calls set_massflux on the current
102 * velocities and then omega, before output. The fluxes left in vec_Huon and vec_Hvom by
103 * update_massflux_3d are instead time-averaged over the step (the ones the tracers move
104 * with), so they are not used here.
105 *
106 * @param[in ] lev level
107 * @param[out] mf_omega z-nodal omega (m/s)
108 */
109void
111{
112 MultiFab mf_Huon(convert(grids[lev],IntVect(1,0,0)), dmap[lev], 1, IntVect(0));
113 MultiFab mf_Hvom(convert(grids[lev],IntVect(0,1,0)), dmap[lev], 1, IntVect(0));
116}
117
118/**
119 * The "true" vertical velocity w (m/s) at w-points, a port of ROMS wvelocity. In
120 * terrain-following coordinates
121 *
122 * Hz * omega = w - d(z)/d(t) - div(z), div(z) = pm * u * d(z)/d(xi) + pn * v * d(z)/d(eta)
123 *
124 * so w adds back to omega the stretching of the levels by the moving free surface
125 * (from the barotropic flux divergence) and the motion along the sloping levels, which
126 * is computed at rho-points and shifted to w-points with ROMS's cubic interpolation.
127 * Like ROMS, this is for output only: nothing in the dynamics reads it, and omega comes
128 * from the mass fluxes of the current velocities (compute_diagnostic_omega).
129 *
130 * @param[in ] lev level
131 * @param[out] mf_w z-nodal w on the valid region
132 */
133void
134REMORA::compute_wvel (int lev, MultiFab& mf_w)
135{
136 MultiFab mf_omega(convert(grids[lev],IntVect(0,0,1)), dmap[lev], 1, IntVect(0));
139}
140
141/**
142 * @param[in ] lev level
143 * @param[out] mf_w z-nodal w on the valid region
144 * @param[in ] mf_omega z-nodal omega from compute_diagnostic_omega, on the valid region
145 */
146void
147REMORA::compute_wvel (int lev, MultiFab& mf_w, const MultiFab& mf_omega)
148{
149 const int nz = Geom(lev).Domain().length(2);
150 // The cubic stencil reaches three rho-levels in from each end
151 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(nz >= 3, "compute_wvel needs at least 3 vertical levels");
152
153 const Real cff1 = Real(3.0)/Real(8.0);
154 const Real cff2 = Real(3.0)/Real(4.0);
155 const Real cff3 = Real(1.0)/Real(8.0);
156 const Real cff4 = Real(9.0)/Real(16.0);
157 const Real cff5 = Real(1.0)/Real(16.0);
158
159 // Not tiled: each column of w reads the whole column of vert
160#ifdef _OPENMP
161#pragma omp parallel if (Gpu::notInLaunchRegion())
162#endif
163 for (MFIter mfi(mf_omega); mfi.isValid(); ++mfi)
164 {
165 Array4<Real > const& wvel = mf_w.array(mfi);
166 Array4<Real const> const& omega = mf_omega.const_array(mfi);
167
168 Array4<Real const> const& u = xvel_new[lev]->const_array(mfi);
169 Array4<Real const> const& v = yvel_new[lev]->const_array(mfi);
170 Array4<Real const> const& z_r = vec_z_r[lev]->const_array(mfi);
171 Array4<Real const> const& z_w = vec_z_w[lev]->const_array(mfi);
172 Array4<Real const> const& pm = vec_pm[lev]->const_array(mfi);
173 Array4<Real const> const& pn = vec_pn[lev]->const_array(mfi);
174 Array4<Real const> const& DU_avg1 = vec_DU_avg1[lev]->const_array(mfi);
175 Array4<Real const> const& DV_avg1 = vec_DV_avg1[lev]->const_array(mfi);
176
177 const Box bx = mfi.validbox().enclosedCells(2);
178 FArrayBox fab_vert(bx,1,amrex::The_Async_Arena());
179 auto vert = fab_vert.array();
180
181 // Motion along the s-levels, (Ui + Vj) * GRADs(z), at rho-points
182 ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
183 {
184 const Real wrk_ulo = u(i ,j,k) * (z_r(i ,j,k)-z_r(i-1,j,k)) * (pm(i-1,j,0)+pm(i ,j,0));
185 const Real wrk_uhi = u(i+1,j,k) * (z_r(i+1,j,k)-z_r(i ,j,k)) * (pm(i ,j,0)+pm(i+1,j,0));
186 const Real wrk_vlo = v(i,j ,k) * (z_r(i,j ,k)-z_r(i,j-1,k)) * (pn(i,j-1,0)+pn(i,j ,0));
187 const Real wrk_vhi = v(i,j+1,k) * (z_r(i,j+1,k)-z_r(i,j ,k)) * (pn(i,j ,0)+pn(i,j+1,0));
188 vert(i,j,k) = Real(0.25)*(wrk_ulo+wrk_uhi) + Real(0.25)*(wrk_vlo+wrk_vhi);
189 });
190
191 // ROMS k (w-levels 0..N, rho-levels 1..N) maps to REMORA k (w-levels 0..nz,
192 // rho-levels 0..nz-1), so ROMS vert(k) is vert(k-1) here
193 ParallelFor(makeSlab(bx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int)
194 {
195 // Free-surface stretching: d(zeta)/d(t) spread linearly through the column
196 const Real wrk = (DU_avg1(i,j,0)-DU_avg1(i+1,j,0) + DV_avg1(i,j,0)-DV_avg1(i,j+1,0))
197 / (z_w(i,j,nz)-z_w(i,j,0));
198 const Real pmpn = pm(i,j,0)*pn(i,j,0);
199 auto interior = [&] (int k) {
200 return omega(i,j,k) + pmpn*wrk*(z_w(i,j,k)-z_w(i,j,0));
201 };
202
203 Real slope = (z_r(i,j,0)-z_w(i,j,0)) / (z_r(i,j,1)-z_r(i,j,0)); // extrapolation slope
204 wvel(i,j,0) = cff1*(vert(i,j,0) - slope*(vert(i,j,1)-vert(i,j,0)))
205 + cff2*vert(i,j,0) - cff3*vert(i,j,1);
206 wvel(i,j,1) = interior(1) + cff1*vert(i,j,0) + cff2*vert(i,j,1) - cff3*vert(i,j,2);
207 for (int k = 2; k <= nz-2; ++k) {
208 wvel(i,j,k) = interior(k) + cff4*(vert(i,j,k-1)+vert(i,j,k ))
209 - cff5*(vert(i,j,k-2)+vert(i,j,k+1));
210 }
211 slope = (z_w(i,j,nz)-z_r(i,j,nz-1)) / (z_r(i,j,nz-1)-z_r(i,j,nz-2)); // extrapolation slope
212 wvel(i,j,nz) = pmpn*wrk*(z_w(i,j,nz)-z_w(i,j,0))
213 + cff1*(vert(i,j,nz-1) + slope*(vert(i,j,nz-1)-vert(i,j,nz-2)))
214 + cff2*vert(i,j,nz-1) - cff3*vert(i,j,nz-2);
215 wvel(i,j,nz-1) = interior(nz-1) + cff1*vert(i,j,nz-1) + cff2*vert(i,j,nz-2)
216 - cff3*vert(i,j,nz-3);
217 });
218 }
219}
220
221/**
222 * Horizontal mass fluxes Hz*u/n and Hz*v/m of the current velocities on the valid faces,
223 * as ROMS set_massflux.
224 *
225 * @param[in ] lev level
226 * @param[out] mf_Huon u-volume flux
227 * @param[out] mf_Hvom v-volume flux
228 */
229void
230REMORA::set_massflux_3d (int lev, MultiFab& mf_Huon, MultiFab& mf_Hvom)
231{
232#ifdef _OPENMP
233#pragma omp parallel if (Gpu::notInLaunchRegion())
234#endif
235 for (MFIter mfi(mf_Huon, TilingIfNotGPU()); mfi.isValid(); ++mfi)
236 {
237 Array4<Real > const& Huon = mf_Huon.array(mfi);
238 Array4<Real const> const& u = xvel_new[lev]->const_array(mfi);
239 Array4<Real const> const& Hz = vec_Hz[lev]->const_array(mfi);
240 Array4<Real const> const& pn = vec_pn[lev]->const_array(mfi);
241
242 ParallelFor(mfi.tilebox(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
243 {
244 Real on_u = two / (pn(i-1,j,0)+pn(i,j,0));
245 Huon(i,j,k)=Real(0.5)*(Hz(i,j,k)+Hz(i-1,j,k))*u(i,j,k)* on_u;
246 });
247 }
248#ifdef _OPENMP
249#pragma omp parallel if (Gpu::notInLaunchRegion())
250#endif
251 for (MFIter mfi(mf_Hvom, TilingIfNotGPU()); mfi.isValid(); ++mfi)
252 {
253 Array4<Real > const& Hvom = mf_Hvom.array(mfi);
254 Array4<Real const> const& v = yvel_new[lev]->const_array(mfi);
255 Array4<Real const> const& Hz = vec_Hz[lev]->const_array(mfi);
256 Array4<Real const> const& pm = vec_pm[lev]->const_array(mfi);
257
258 ParallelFor(mfi.tilebox(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
259 {
260 Real om_v= two / (pm(i,j-1,0)+pm(i,j,0));
261 Hvom(i,j,k)=Real(0.5)*(Hz(i,j,k)+Hz(i,j-1,k))*v(i,j,k)* om_v;
262 });
263 }
264}
constexpr amrex::Real two
constexpr amrex::Real zero
mf_h setVal(geomdata.ProbHi(2))
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_h
multilevel data container for current step's z velocities (largely unused; W stored separately)
Definition REMORA.H:413
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pm
horizontal scaling factor: 1 / dx (2D)
Definition REMORA.H:603
void compute_diagnostic_omega(int lev, amrex::MultiFab &mf_omega)
omega (m/s) as ROMS writes it: from mass fluxes of the current velocities
void set_massflux_3d(int lev, amrex::MultiFab &mf_Huon, amrex::MultiFab &mf_Hvom)
Horizontal mass fluxes Hz*u/n and Hz*v/m of the current velocities, as ROMS set_massflux.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Hz
Width of cells in the vertical (z-) direction (3D, Hz in ROMS)
Definition REMORA.H:424
amrex::Vector< amrex::MultiFab * > yvel_new
multilevel data container for current step's y velocities (v in ROMS)
Definition REMORA.H:397
void compute_omega(int lev, amrex::MultiFab &mf_omega, const amrex::MultiFab &mf_Huon, const amrex::MultiFab &mf_Hvom)
Velocity across the w-levels (omega, m/s) from the given mass fluxes.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_r
z coordinates at rho points (cell centers)
Definition REMORA.H:453
amrex::Vector< amrex::MultiFab * > xvel_new
multilevel data container for current step's x velocities (u in ROMS)
Definition REMORA.H:395
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_DV_avg1
time average of barotropic y velocity flux
Definition REMORA.H:549
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_DU_avg1
time average of barotropic x velocity flux (2D)
Definition REMORA.H:545
void compute_wvel(int lev, amrex::MultiFab &mf_w)
"True" vertical velocity w (m/s) at w-points, as ROMS wvelocity
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pn
horizontal scaling factor: 1 / dy (2D)
Definition REMORA.H:605
static void compute_W(const amrex::Box &bx2d, const amrex::Array4< amrex::Real > &W, const amrex::Array4< amrex::Real const > &Huon, const amrex::Array4< amrex::Real const > &Hvom, const amrex::Array4< amrex::Real const > &z_w, const amrex::Array4< amrex::Real const > &h, const int N)
Vertical volume flux W from the horizontal mass flux divergence.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_w
z coordinates at w points (faces between z-cells)
Definition REMORA.H:456