REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_DepthStretchTransform.H
Go to the documentation of this file.
1#ifndef _REMORA_STRETCH_H_
2#define _REMORA_STRETCH_H_
3
4#include <cmath>
5#include <REMORA_DataStruct.H>
6#include <REMORA.H>
8
9using namespace amrex;
10
11/**
12 * \brief Depth at stretched coordinate sc (stretching value Cs) in a column of depth hwater
13 * under free surface zeta, ROMS Vtransform 2. Shared by stretch_transform and
14 * stretch_transform_full_domain.
15 */
17Real stretched_depth (Real sc, Real Cs, Real hc, Real hwater, Real zeta)
18{
19 Real cff = hc*sc;
20 Real hinv = one/(hc+hwater);
21 Real cff2 = (cff+Cs*hwater)*hinv;
22 return zeta+(zeta+hwater)*cff2;
23}
24
25void
27{
28 BL_PROFILE("REMORA::calc_stretch_coeffs()");
29 int nz = geom[0].Domain().length(2);
30
31 auto N = nz; // Number of vertical "levels" aka, NZ
32 Real ds = one / Real(N);
33
34 Box bx(IntVect(0,0,0),IntVect(0,0,N));
37
38 //amrex::Vector<Real> s_w_vec(N+1); auto s_w_dat = s_w_vec.data();
39 //amrex::Vector<Real> Cs_w_vec(N+1); auto Cs_w_dat = Cs_w_vec.data();
40 //amrex::Vector<Real> s_r_vec(N); auto s_r_dat = s_r_vec.data();
41 //amrex::Vector<Real> Cs_r_vec(N); auto Cs_r_dat = Cs_r_vec.data();
42 auto s_w_dat = s_w.data();
43 auto Cs_w_dat = Cs_w.data();
44 auto s_r_dat = s_r.data();
45 auto Cs_r_dat = Cs_r.data();
46
47 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE (int , int , int k)
48 {
49 Real Csur,Cbot;
50
51 s_w_dat[k]=ds*(k-N);
52 if (local_theta_s > zero) {
53 Csur=(one-std::cosh(local_theta_s*s_w_dat[k]))/
54 (std::cosh(local_theta_s)-one);
55 } else {
57 }
58
59 if (local_theta_b > zero) {
60 Cbot=(std::exp(local_theta_b*Csur)-one)/
61 (one-std::exp(-local_theta_b));
63 } else {
65 }
66
67 if (k<N) {
68 s_r_dat[k]=ds*(k-N+Real(0.5));
69
70 if (local_theta_s > zero) {
71 Csur=(one-std::cosh(local_theta_s*s_r_dat[k]))/
72 (std::cosh(local_theta_s)-one);
73 } else {
75 }
76
77 if (local_theta_b > zero) {
78 Cbot=(std::exp(local_theta_b*Csur)-one)/
79 (one-std::exp(-local_theta_b));
81 } else {
83 }
84 }
85 });
86}
87
88/**
89 * @param[in] lev level to operate on
90 */
91void
93{
94 BL_PROFILE("REMORA::stretch_transform()");
95 std::unique_ptr<MultiFab>& mf_z_w = vec_z_w[lev];
96 std::unique_ptr<MultiFab>& mf_z_r = vec_z_r[lev];
97 std::unique_ptr<MultiFab>& mf_Hz = vec_Hz[lev];
98 std::unique_ptr<MultiFab>& mf_h = vec_h[lev];
99 std::unique_ptr<MultiFab>& mf_Zt_avg1 = vec_Zt_avg1[lev];
100 std::unique_ptr<MultiFab>& mf_z_phys_nd = vec_z_phys_nd[lev];
101
102 [[maybe_unused]] auto s_w_dat = s_w.data();
103 auto Cs_w_dat = Cs_w.data();
104 [[maybe_unused]] auto s_r_dat = s_r.data();
105 auto Cs_r_dat = Cs_r.data();
106
107 for ( MFIter mfi(*cons_new[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi )
108 {
109 Array4<Real> const& z_w = (mf_z_w)->array(mfi);
110 Array4<Real> const& z_r = (mf_z_r)->array(mfi);
111 Array4<Real> const& Hz = (mf_Hz)->array(mfi);
112 Array4<Real> const& h = (mf_h)->array(mfi);
113 Array4<Real> const& Zt_avg1 = (mf_Zt_avg1)->array(mfi);
114 Box bx = mfi.tilebox();
115 Box gbx2 = bx;
116 gbx2.grow(IntVect(NGROW,NGROW,0));
117 Box gbx3 = bx;
118 gbx3.grow(IntVect(NGROW+1,NGROW+1,0));
119 Box gbx2D = gbx2;
120 gbx2D.makeSlab(2,0);
121 Box gbx3D = gbx3;
122 gbx3D.makeSlab(2,0);
123 Box wgbx3 = gbx3;
124 wgbx3.surroundingNodes(2);
125
126 const auto & geomdata = Geom(lev).data();
127
128 int nz = geom[lev].Domain().length(2);
129
130 auto N = nz; // Number of vertical "levels" aka, NZ
131 //forcing tcline to be the same as probhi for now, one in DataStruct.H other in inputs
132
133 Real hc = -min(geomdata.ProbHi(2),-solverChoice.tcline); // Do we need to enforce min here?
134
135 Real ds = one / Real(N);
136
137 amrex::ParallelFor(wgbx3, [=] AMREX_GPU_DEVICE (int i, int j, int k)
138 {
139 z_w(i,j,k) = h(i,j,0);
140 });
141
142 // ROMS Transform 2
143 Gpu::streamSynchronize();
144
145 amrex::ParallelFor(wgbx3, [=] AMREX_GPU_DEVICE (int i, int j, int k)
146 {
147 if (k < N) {
148 Real sc_r=ds*(k-N+Real(0.5));
149 Real sc_w=ds*(k-N);
150 Real hwater=h(i,j,0);
151
152 if (k==0) {
153 h(i,j,0,1) = stretched_depth(sc_w, Cs_w_dat[k], hc, hwater, Zt_avg1(i,j,0));
154 z_w(i,j,0) = h(i,j,0,1);
155 } else {
157 }
158
160
161 } else { // k == N
162 // HACK: should actually be the normal expression with coeffs evaluated at k=N-1
163 z_w(i,j,N)=Zt_avg1(i,j,0);
164 }
165 });
166
167 Gpu::streamSynchronize();
168
169 amrex::ParallelFor(gbx3, [=] AMREX_GPU_DEVICE (int i, int j, int k)
170 {
171 Hz(i,j,k)=z_w(i,j,k+1)-z_w(i,j,k);
172 });
173
174 } // mfi
175
176 vec_z_w[lev]->FillBoundary(geom[lev].periodicity());
177 vec_z_r[lev]->FillBoundary(geom[lev].periodicity());
178 vec_Hz[lev]->FillBoundary(geom[lev].periodicity());
179
180 // Define nodal z as average of z on w-faces
181 for ( MFIter mfi(*cons_new[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi )
182 {
183 Array4<Real> const& z_w = (mf_z_w)->array(mfi);
184 Array4<Real> const& z_phys_nd = (mf_z_phys_nd)->array(mfi);
185
186 Box z_w_box = Box(z_w);
187 auto const lo = amrex::lbound(z_w_box);
188 auto const hi = amrex::ubound(z_w_box);
189
190 //
191 // NOTE: we assume that all boxes extend the full extent of the domain in the vertical direction
192 //
193 // NOTE: z_phys_nd(i,j,k) and z_w(i,j,k) both refer to the node on the LOW side of cell (i,j,k)
194 //
195
196 // Nodal in all three directions so that the outermost node in x and y is
197 // filled as well. We do not grow in the vertical since z_phys_nd has a
198 // ghost node there which we fill by extrapolation, not from z_w.
199 Box bx = mfi.grownnodaltilebox(-1,IntVect(NGROW,NGROW,0));
200
201 ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
202 {
203 // Node (i,j,k) is the low corner of cell (i,j,k), so the four water
204 // columns surrounding it are cells (i-1,j-1) through (i,j). This is
205 // the psi-point averaging ROMS uses in set_depth / metrics.
206 // Indices are clamped so that a node on the edge of the array picks up the
207 // one-sided value; for now we assume all boundaries are constant
208 // height -- we will enforce periodicity below.
209 int ii = std::min(std::max(i , lo.x), hi.x);
210 int im = std::min(std::max(i-1, lo.x), hi.x);
211 int jj = std::min(std::max(j , lo.y), hi.y);
212 int jm = std::min(std::max(j-1, lo.y), hi.y);
213
214 z_phys_nd(i,j,k)=Real(0.25)*( z_w(im,jm,k) + z_w(ii,jm,k) +
215 z_w(im,jj,k) + z_w(ii,jj,k) );
216 });
217
218 // Fill nodes below the surface (to avoid out of bounds errors in particle functions)
219 int klo = -1;
220 ParallelFor(makeSlab(bx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int)
221 {
222 z_phys_nd(i,j,klo) = two * z_phys_nd(i,j,klo+1) - z_phys_nd(i,j,klo+2);
223 });
224 } // mf
225
226 // Note that we do *not* want to do a multilevel fill here -- we have
227 // already filled z_phys_nd on the grown boxes, but we enforce periodicity just in case
228 vec_z_phys_nd[lev]->FillBoundary(geom[lev].periodicity());
229}
230/**
231 * z_r and z_w for a level not yet created, on full-domain arrays, over every grow cell of
232 * mf_z_w. mf_h and mf_zeta must cover the same region.
233 *
234 * @param[in ] lev level whose vertical grid to use
235 * @param[in ] mf_h bathymetry (component 0), 2D
236 * @param[in ] mf_zeta free surface (component 0), 2D
237 * @param[ out] mf_z_r depth of rho points
238 * @param[ out] mf_z_w depth of w points, on the same layout as mf_z_r but nodal in z
239 */
240void
241REMORA::stretch_transform_full_domain (int lev, const MultiFab& mf_h, const MultiFab& mf_zeta,
242 MultiFab& mf_z_r, MultiFab& mf_z_w)
243{
244 BL_PROFILE("REMORA::stretch_transform_full_domain()");
245 auto Cs_w_dat = Cs_w.data();
246 auto Cs_r_dat = Cs_r.data();
247
248 const auto& geomdata = Geom(lev).data();
249 const int N = geom[lev].Domain().length(2);
250 const Real hc = -min(geomdata.ProbHi(2),-solverChoice.tcline);
251 const Real ds = one / Real(N);
252
253 for (MFIter mfi(mf_z_w, TilingIfNotGPU()); mfi.isValid(); ++mfi)
254 {
255 const Box& wbx = mfi.growntilebox();
256 Array4<const Real> const& h = mf_h.const_array(mfi);
257 Array4<const Real> const& zeta = mf_zeta.const_array(mfi);
258 Array4< Real> const& z_r = mf_z_r.array(mfi);
259 Array4< Real> const& z_w = mf_z_w.array(mfi);
260
261 ParallelFor(wbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
262 {
263 if (k < N) {
264 z_w(i,j,k) = stretched_depth(ds*(k-N), Cs_w_dat[k], hc, h(i,j,0), zeta(i,j,0));
265 z_r(i,j,k) = stretched_depth(ds*(k-N+Real(0.5)), Cs_r_dat[k], hc, h(i,j,0),
266 zeta(i,j,0));
267 } else {
268 z_w(i,j,N) = zeta(i,j,0);
269 }
270 });
271 }
272 Gpu::streamSynchronize();
273}
274
275#endif
constexpr amrex::Real two
constexpr amrex::Real one
constexpr amrex::Real zero
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real stretched_depth(Real sc, Real Cs, Real hc, Real hwater, Real zeta)
Depth at stretched coordinate sc (stretching value Cs) in a column of depth hwater under free surface...
#define NGROW
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< amrex::MultiFab * > cons_new
multilevel data container for current step's scalar data: temperature, salinity, passive tracer
Definition REMORA.H:393
void stretch_transform(int lev)
Calculate vertical stretched coordinates.
amrex::Gpu::DeviceVector< amrex::Real > s_w
Scaled vertical coordinate (range [0,1]) that transforms to z, defined at w-points (cell faces)
Definition REMORA.H:460
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::Gpu::DeviceVector< amrex::Real > s_r
Scaled vertical coordinate (range [0,1]) that transforms to z, defined at rho points (cell centers)
Definition REMORA.H:458
void stretch_transform_full_domain(int lev, const amrex::MultiFab &mf_h, const amrex::MultiFab &mf_zeta, amrex::MultiFab &mf_z_r, amrex::MultiFab &mf_z_w)
Calculate z_r and z_w on full-domain arrays at a level not yet created.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_r
z coordinates at rho points (cell centers)
Definition REMORA.H:453
void calc_stretch_coeffs()
calculate vertical stretch coefficients
amrex::Gpu::DeviceVector< amrex::Real > Cs_r
Stretching coefficients at rho points.
Definition REMORA.H:468
static SolverChoice solverChoice
Container for algorithmic choices.
Definition REMORA.H:1949
amrex::Gpu::DeviceVector< amrex::Real > Cs_w
Stretching coefficients at w points.
Definition REMORA.H:470
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_phys_nd
z coordinates at psi points (cell nodes)
Definition REMORA.H:473
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Zt_avg1
Average of the free surface, zeta (2D)
Definition REMORA.H:476
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_w
z coordinates at w points (faces between z-cells)
Definition REMORA.H:456
amrex::Real theta_b
amrex::Real theta_s
amrex::Real tcline