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 * @param[in] lev level to operate on
13 */
14void
16{
17 std::unique_ptr<MultiFab>& mf_z_w = vec_z_w[lev];
18 std::unique_ptr<MultiFab>& mf_z_r = vec_z_r[lev];
19 std::unique_ptr<MultiFab>& mf_s_r = vec_s_r[lev];
20 std::unique_ptr<MultiFab>& mf_s_w = vec_s_w[lev];
21 std::unique_ptr<MultiFab>& mf_Cs_r = vec_Cs_r[lev];
22 std::unique_ptr<MultiFab>& mf_Cs_w = vec_Cs_w[lev];
23 std::unique_ptr<MultiFab>& mf_Hz = vec_Hz[lev];
24 std::unique_ptr<MultiFab>& mf_h = vec_hOfTheConfusingName[lev];
25 std::unique_ptr<MultiFab>& mf_Zt_avg1 = vec_Zt_avg1[lev];
26 std::unique_ptr<MultiFab>& mf_z_phys_nd = vec_z_phys_nd[lev];
27 auto N_loop = Geom(lev).Domain().size()[2]; // Number of vertical "levs" aka, NZ
28
29 for ( MFIter mfi(*cons_new[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi )
30 {
31 Array4<Real> const& z_w = (mf_z_w)->array(mfi);
32 Array4<Real> const& z_r = (mf_z_r)->array(mfi);
33 Array4<Real> const& s_r = (mf_s_r)->array(mfi);
34 Array4<Real> const& s_w = (mf_s_w)->array(mfi);
35 Array4<Real> const& Cs_r_arr = (mf_Cs_r)->array(mfi);
36 Array4<Real> const& Cs_w_arr = (mf_Cs_w)->array(mfi);
37 Array4<Real> const& Hz = (mf_Hz)->array(mfi);
38 Array4<Real> const& h = (mf_h)->array(mfi);
39 Array4<Real> const& Zt_avg1 = (mf_Zt_avg1)->array(mfi);
40 Box bx = mfi.tilebox();
41 Box gbx2 = bx;
42 gbx2.grow(IntVect(NGROW,NGROW,0));
43 Box gbx3 = bx;
44 gbx3.grow(IntVect(NGROW+1,NGROW+1,0));
45 Box gbx2D = gbx2;
46 gbx2D.makeSlab(2,0);
47 Box gbx3D = gbx3;
48 gbx3D.makeSlab(2,0);
49 Box wgbx3 = gbx3;
50 wgbx3.surroundingNodes(2);
51
52 const auto & geomdata = Geom(lev).data();
53
54 int nz = geom[lev].Domain().length(2);
55
56 auto N = nz; // Number of vertical "levels" aka, NZ
57 //forcing tcline to be the same as probhi for now, one in DataStruct.H other in inputs
58 Real hc=-min(geomdata.ProbHi(2),-solverChoice.tcline); // Do we need to enforce min here?
59 const auto local_theta_s = solverChoice.theta_s;
60 const auto local_theta_b = solverChoice.theta_b;
61
62 amrex::ParallelFor(wgbx3, [=] AMREX_GPU_DEVICE (int i, int j, int k)
63 {
64 z_w(i,j,k) = h(i,j,0);
65 });
66
67 // ROMS Transform 2
68 Gpu::streamSynchronize();
69
70 amrex::ParallelFor(gbx3D, [=] AMREX_GPU_DEVICE (int i, int j, int )
71 {
72 for (int k=0; k<=N_loop; k++) {
73 // const Real z = prob_lo[2] + (k + 0.5) * dx[2];
74 // const auto prob_lo = geomdata.ProbLo();
75 // const auto dx = geomdata.CellSize();
76 // This is the z for the bottom of the cell this k corresponds to
77 // if we weren't stretching and transforming
78 // const Real z = prob_lo[2] + (k) * dx[2];
79 // h(i,j,0) = -prob_lo[2]; // conceptually
80
81 ////////////////////////////////////////////////////////////////////
82 //ROMS Stretching 4
83 // Move this block to it's own function for maintainability if needed
84 // Information about the problem dimension would need to be added
85 // This file would need a k dependent function to return the
86 // stretching scalars, or access to 4 vectors of length prob_length(2)
87 /////////////////////////////////////////////////////////////////////
88 Real ds = 1.0_rt / Real(N);
89
90 Real cff_r, cff_w, cff1_r, cff1_w, cff2_r, cff2_w, Csur, Cbot;
91 Real sc_r,sc_w,Cs_r,Cs_w;
92
93 if (k==N) // end of array // pretend we're storing 0?
94 {
95 sc_w=0.0; //sc_w / hc
96 Cs_w=0.0; //Cs_w
97 }
98 else if (k==0) // beginning of array
99 {
100 sc_w=-1.0; //sc_w / hc
101 Cs_w=-1.0; //Cs_w
102 }
103 else
104 {
105 sc_w=ds*(k-N);
106
107 if (local_theta_s > 0.0_rt) {
108 Csur=(1.0_rt-std::cosh(local_theta_s*sc_w))/
109 (std::cosh(local_theta_s)-1.0_rt);
110 } else {
111 Csur=-sc_w*sc_w;
112 }
113
114 if (local_theta_b > 0.0_rt) {
115 Cbot=(std::exp(local_theta_b*Csur)-1.0_rt)/
116 (1.0_rt-std::exp(-local_theta_b));
117 Cs_w=Cbot;
118 } else {
119 Cs_w=Csur;
120 }
121 } // k test
122
123 cff_w=hc*sc_w;
124 cff1_w=Cs_w;
125
126 //cff_r => sc_r *hc
127 //cff1_r => Cs_r
128 //Don't do anything special for first/last index
129 {
130 sc_r=ds*(k-N+0.5_rt);
131
132 if (local_theta_s > 0.0_rt) {
133 Csur=(1.0_rt-std::cosh(local_theta_s*sc_r))/
134 (std::cosh(local_theta_s)-1.0_rt);
135 } else {
136 Csur=-sc_r*sc_r;
137 }
138
139 if (local_theta_b > 0.0_rt) {
140 Cbot=(std::exp(local_theta_b*Csur)-1.0_rt)/
141 (1.0_rt-std::exp(-local_theta_b));
142 Cs_r=Cbot;
143 } else {
144 Cs_r=Csur;
145 }
146 }
147
148 if (i==0&&j==0&&k<N&&k>=0) {
149 s_r(0,0,k) = sc_r;
150 Cs_r_arr(0,0,k) = Cs_r;
151 }
152 if (i==0&&j==0&&k<=N&&k>=0) {
153 s_w(0,0,k) = sc_w;
154 Cs_w_arr(0,0,k) = Cs_w;
155 }
156
157 cff_r=hc*sc_r;
158 cff1_r=Cs_r;
159
160 ////////////////////////////////////////////////////////////////////
161 Real hwater=h(i,j,0);
162 //
163 // if (k==0) //extra guess added (maybe not actually defined in ROMS)
164 // {
165 // Real hinv=1.0_rt/(hc+hwater);
166 // cff2_r=(cff_r+cff1_r*hwater)*hinv;
167 // // z_w(i,j,k-2) = hwater;
168 // // z_w(i,j,k-1)= -hwater;
169
170 // z_r(i,j,k) = Zt_avg1(i,j,0)+(Zt_avg1(i,j,0)+hwater)*cff2_r;
171 // Hz(i,j,k)=z_w(i,j,k)+hwater;//-z_w(i,j,k-1);
172 // } else
173
174 //Note, we are not supporting ICESHELF flag
175
176 Real hinv=1.0_rt/(hc+hwater);
177 cff2_r=(cff_r+cff1_r*hwater)*hinv;
178 cff2_w=(cff_w+cff1_w*hwater)*hinv;
179
180 if(k==N) {
181 // HACK: should actually be the normal expression with coeffs evaluated at k=N-1
182 z_w(i,j,N)=Zt_avg1(i,j,0);
183
184 } else if (k==0) {
185 h(i,j,0,1) = Zt_avg1(i,j,0)+(Zt_avg1(i,j,0)+hwater)*cff2_w;
186 z_w(i,j,0) = h(i,j,0,1);
187
188 } else {
189 z_w(i,j,k)=Zt_avg1(i,j,0)+(Zt_avg1(i,j,0)+hwater)*cff2_w;
190
191 }
192
193 if(k!=N) {
194 z_r(i,j,k)=Zt_avg1(i,j,0)+(Zt_avg1(i,j,0)+hwater)*cff2_r;
195 }
196 } // k
197 });
198
199 Gpu::streamSynchronize();
200
201 amrex::ParallelFor(gbx3, [=] AMREX_GPU_DEVICE (int i, int j, int k)
202 {
203 Hz(i,j,k)=z_w(i,j,k+1)-z_w(i,j,k);
204 });
205 } // mfi
206
207 vec_z_w[lev]->FillBoundary(geom[lev].periodicity());
208 vec_z_r[lev]->FillBoundary(geom[lev].periodicity());
209 vec_s_r[lev]->FillBoundary(geom[lev].periodicity());
210 vec_Hz[lev]->FillBoundary(geom[lev].periodicity());
211
212 // Define nodal z as average of z on w-faces
213 for ( MFIter mfi(*cons_new[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi )
214 {
215 Array4<Real> const& z_w = (mf_z_w)->array(mfi);
216 Array4<Real> const& z_phys_nd = (mf_z_phys_nd)->array(mfi);
217
218 Box z_w_box = Box(z_w);
219 auto const lo = amrex::lbound(z_w_box);
220 auto const hi = amrex::ubound(z_w_box);
221
222 //
223 // NOTE: we assume that all boxes extend the full extent of the domain in the vertical direction
224 //
225 // 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)
226 //
227
228 // We shrink the box in the vertical since z_phys_nd has a ghost cell in the vertical
229 // which we will fill by extrapolation, not from z_w
230 Box bx = Box(z_phys_nd); bx.grow(2,-1);
231
232 ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
233 {
234 // For now assume all boundaries are constant height --
235 // we will enforce periodicity below
236 if ( i >= lo.x && i <= hi.x-1 && j >= lo.y && j <= hi.y-1 )
237 {
238 z_phys_nd(i,j,k)=0.25_rt*( z_w(i,j ,k) + z_w(i+1,j ,k) +
239 z_w(i,j+1,k) + z_w(i+1,j+1,k) );
240 } else {
241 int ii = std::min(std::max(i, lo.x), hi.x);
242 int jj = std::min(std::max(j, lo.y), hi.y);
243 z_phys_nd(i,j,k) = z_w(ii,jj,k);
244 }
245 });
246
247 // Fill nodes below the surface (to avoid out of bounds errors in particle functions)
248 int klo = -1;
249 ParallelFor(makeSlab(bx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int)
250 {
251 z_phys_nd(i,j,klo) = 2.0_rt * z_phys_nd(i,j,klo+1) - z_phys_nd(i,j,klo+2);
252 });
253 } // mf
254
255 // Note that we do *not* want to do a multilevel fill here -- we have
256 // already filled z_phys_nd on the grown boxes, but we enforce periodicity just in case
257 vec_z_phys_nd[lev]->FillBoundary(geom[lev].periodicity());
258}
259
260#endif
#define NGROW
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Cs_r
Stretching coefficients at rho points.
Definition REMORA.H:271
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_hOfTheConfusingName
Bathymetry data (2D, positive valued, h in ROMS)
Definition REMORA.H:229
amrex::Vector< amrex::MultiFab * > cons_new
multilevel data container for current step's scalar data: temperature, salinity, passive scalar
Definition REMORA.H:217
void stretch_transform(int lev)
Calculate vertical stretched coordinates.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Hz
Width of cells in the vertical (z-) direction (3D, Hz in ROMS)
Definition REMORA.H:232
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_r
z coordinates at rho points (cell centers)
Definition REMORA.H:261
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_s_w
Scaled vertical coordinate (range [0,1]) that transforms to z, defined at w-points (cell faces)
Definition REMORA.H:268
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Cs_w
Stretching coefficients at w points.
Definition REMORA.H:273
static SolverChoice solverChoice
Container for algorithmic choices.
Definition REMORA.H:1221
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_phys_nd
z coordinates at psi points (cell nodes)
Definition REMORA.H:276
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Zt_avg1
Average of the free surface, zeta (2D)
Definition REMORA.H:279
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_w
z coordinates at w points (faces between z-cells)
Definition REMORA.H:264
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_s_r
Scaled vertical coordinate (range [0,1]) that transforms to z, defined at rho points (cell centers)
Definition REMORA.H:266
amrex::Real theta_b
amrex::Real theta_s
amrex::Real tcline