ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ERF_TurbPertStruct.H
Go to the documentation of this file.
1 #ifndef ERF_TURB_PERT_STRUCT_H_
2 #define ERF_TURB_PERT_STRUCT_H_
3 
4 #include <ERF_DataStruct.H>
5 #include <AMReX_MultiFabUtil.H>
6 #include <ERF_TileNoZ.H>
7 #include <time.h>
8 /**
9  * Container holding quantities related to turbulent perturbation parameters
10  */
11 
12 /* The general rule of thumb is to create a perturbation box size of 1/8th of the boundary layer height.
13  The boundary layer height can't be the height of the domain. The length and width of the box should
14  be twice the height of the box. If meandering flow is present, the width of the box should be take
15  the angle of the inflow into consideration.
16 */
17 
19 
20  public:
21 
23 
24  // Initializing Perturbation Region
25  // Currently only support perturbation in the x and y direction
26  // This portion of the code is only called once for initialization,
27  // therefore efficiency is not required. Dont spend too much time
28  // here other than to correctly setup the perturbation box region
29  void init_tpi (const int lev,
30  const amrex::IntVect& valid_box_lo, // start points of the valid box
31  const amrex::IntVect& valid_box_hi, // end points of the valid box
32  const amrex::GpuArray<amrex::Real,3> dx,
33  const amrex::BoxArray& ba,
34  const amrex::DistributionMapping& dm,
35  const int ngrow_state,
36  std::string pp_prefix,
37  const amrex::Vector<amrex::IntVect> refRatio,
38  const int max_level)
39 
40  {
41  // Initialization for some 0 dependent terms
42  tpi_Ti = 0.;
43  tpi_Tinf = 300.;
44 
45  ref_ratio = refRatio;
46 
47  amrex::ParmParse pp(pp_prefix);
48 
49  // Reading inputs, and placing assertion for the perturbation inflow to work
50  pp.queryarr("perturbation_box_dims",tpi_boxDim);
51  pp.queryarr("perturbation_direction",tpi_direction);
52  pp.query("perturbation_layers",tpi_layers);
53  pp.query("perturbation_offset",tpi_offset);
54 
55  pp.query("perturbation_nondimensional",tpi_nonDim);
56  pp.query("perturbation_T_infinity",tpi_Tinf);
57  pp.query("perturbation_T_intensity",tpi_Ti);
58 
59  // Check variables message
60  if (tpi_layers < 0) { amrex::Abort("Please provide a valid perturbation layer value (ie. 3-5)"); }
61  if (tpi_offset < 0) { amrex::Abort("Please provide a valid inflow cell offset value for perturbation region (ie. 0-5)"); }
62  for (int i = 0; i < tpi_boxDim.size(); i++) {
63  if (tpi_boxDim[i] == 0) { amrex::Abort("Please provide valid dimensions for perturbation boxes."); }
64  }
65  if (tpi_nonDim < 0.) { amrex::Abort("Please provide a valid nondimensional number (ie. Ri = 0.042)"); }
66  if (tpi_Tinf < 0.) { amrex::Abort("Please provide a valid ambient temperature value (ie. T_0 = T_infty)"); }
67  if (tpi_Ti < 0.) { amrex::Abort("Please provide a valid temperature intensity value (ie. 0-1.0)"); }
68 
69  // Creating perturbation regions and initializing with generic size. Temporary size for now
70  amrex::Box lo_x_bx(amrex::IntVect(0), amrex::IntVect(1), amrex::IntVect(0));
71  amrex::Box hi_x_bx(amrex::IntVect(0), amrex::IntVect(1), amrex::IntVect(0));
72  amrex::Box lo_y_bx(amrex::IntVect(0), amrex::IntVect(1), amrex::IntVect(0));
73  amrex::Box hi_y_bx(amrex::IntVect(0), amrex::IntVect(1), amrex::IntVect(0));
74 
75  // Create a temporary box list to accumulate all the perturbation regions after box modification
76  amrex::BoxList tmp_bl;
77 
78  // boxSize for individual boxes
79  amrex::IntVect boxSize(tpi_boxDim[0],tpi_boxDim[1],tpi_boxDim[2]);
80 
81  // Starting logic to set the size of the perturbation region(s)
82  //amrex::PrintToFile("BoxPerturbationOutput") << "Setting perturbation region in:";
83  // ***** X-direction perturbation *****
84  if (tpi_direction[0]) { // West
85  lo_x_bx.setSmall(amrex::IntVect(valid_box_lo[0]+tpi_offset, valid_box_lo[1]+tpi_direction[1]*tpi_offset, valid_box_lo[2]));
86  lo_x_bx.setBig(amrex::IntVect(valid_box_lo[0]+(tpi_layers*tpi_boxDim[0]-1)+tpi_offset, valid_box_hi[1]-(tpi_direction[4]*tpi_offset), valid_box_hi[2]));
87  //amrex::PrintToFile("BoxPerturbationOutput") << " West face";
88  }
89 
90  if (tpi_direction[3]) { // East
91  hi_x_bx.setSmall(amrex::IntVect(valid_box_hi[0]-((tpi_layers*tpi_boxDim[0]-1)+tpi_offset), valid_box_lo[1]+tpi_direction[1]*tpi_offset, valid_box_lo[2]));
92  hi_x_bx.setBig(amrex::IntVect(valid_box_hi[0]-tpi_offset, valid_box_hi[1]-(tpi_direction[4]*tpi_offset), valid_box_hi[2]));
93  //amrex::PrintToFile("BoxPerturbationOutput") << " East face";
94  }
95 
96  // ***** Y-direction Perturbation *****
97  if (tpi_direction[1]) { // North
98  lo_y_bx.setSmall(amrex::IntVect(valid_box_lo[0]+tpi_direction[0]*tpi_offset, valid_box_lo[1]+tpi_offset, valid_box_lo[2]));
99  lo_y_bx.setBig(amrex::IntVect(valid_box_hi[0]-tpi_direction[3]*tpi_offset, valid_box_lo[1]+((tpi_layers*tpi_boxDim[1])-1)+tpi_offset, valid_box_hi[2]));
100  //amrex::PrintToFile("BoxPerturbationOutput") << " North face";
101  }
102 
103  if (tpi_direction[4]) { // South
104  hi_y_bx.setSmall(amrex::IntVect(valid_box_lo[0]+tpi_direction[0]*tpi_offset, valid_box_hi[1]-((tpi_layers*tpi_boxDim[1]-1)+tpi_offset), valid_box_lo[2]));
105  hi_y_bx.setBig(amrex::IntVect(valid_box_hi[0]-tpi_direction[3]*tpi_offset, valid_box_hi[1]-tpi_offset, valid_box_hi[2]));
106  //amrex::PrintToFile("BoxPerturbationOutput") << " South face";
107  }
108 
109  if (tpi_direction[2] || tpi_direction[5]) { amrex::Abort("Currently not supporting z-direction flow perturbation"); }
110 
111  // Performing box union for intersecting perturbation regions to avoid overlapping sections (double counting at corners)
112  if (tpi_direction[0] && tpi_direction[1]) { // Reshaping South smallEnd
113  amrex::Box lo_x_lo_y_u = lo_x_bx & lo_y_bx;
114  lo_y_bx.setSmall(amrex::IntVect(lo_x_lo_y_u.bigEnd(0)+1, lo_x_lo_y_u.smallEnd(1), lo_x_lo_y_u.smallEnd(2)));
115  }
116 
117  if (tpi_direction[3] && tpi_direction[1]) { // Reshaping South bigEnd
118  amrex::Box hi_x_lo_y_u = hi_x_bx & lo_y_bx;
119  lo_y_bx.setBig(amrex::IntVect(hi_x_lo_y_u.smallEnd(0)-1, hi_x_lo_y_u.bigEnd(1), hi_x_lo_y_u.bigEnd(2)));
120  }
121 
122  if (tpi_direction[0] && tpi_direction[4]) { // Reshaping North smallEnd
123  amrex::Box lo_x_hi_y_u = lo_x_bx & hi_y_bx;
124  hi_y_bx.setSmall(amrex::IntVect(lo_x_hi_y_u.bigEnd(0)+1, lo_x_hi_y_u.smallEnd(1), lo_x_hi_y_u.smallEnd(2)));
125  }
126 
127  if (tpi_direction[3] && tpi_direction[4]) { // Reshaping North bigEnd
128  amrex::Box hi_x_hi_y_u = hi_x_bx & hi_y_bx;
129  hi_y_bx.setBig(amrex::IntVect(hi_x_hi_y_u.smallEnd(0)-1, hi_x_hi_y_u.bigEnd(1), hi_x_hi_y_u.bigEnd(2)));
130  }
131 
132  // Creating structure box array for conserved quantity
133  if (tpi_direction[0]) { tmp_bl.push_back(lo_x_bx); }
134  if (tpi_direction[1]) { tmp_bl.push_back(lo_y_bx); }
135  if (tpi_direction[3]) { tmp_bl.push_back(hi_x_bx); }
136  if (tpi_direction[4]) { tmp_bl.push_back(hi_y_bx); }
137  //amrex::PrintToFile("BoxPerturbationOutput") << "\nBoxList: " << tmp_bl << "\n";
138  amrex::BoxArray tmp_ba(tmp_bl);
139  tmp_ba.maxSize(boxSize);
140  pb_ba.push_back(tmp_ba);
141 
142  if (lev == 0) {
143  pb_mag.resize(max_level+1);
144  pb_netZero.resize(max_level+1);
145  pb_interval.resize(max_level+1);
146  pb_local_etime.resize(max_level+1);
147  pb_amp.resize(max_level+1);
148  pb_cell.resize(max_level+1);
149  }
150 
151  // Initializing mean magnitude vector
152  pb_mag[lev].resize(pb_ba[lev].size(), 0.);
153  pb_netZero[lev].resize(pb_ba[lev].size(), 0.);
154 
155  // Set size of vector and initialize
156  pb_interval[lev].resize(pb_ba[lev].size(), -1.0);
157  pb_local_etime[lev].resize(pb_ba[lev].size(), 0.0);
158  pb_amp[lev].resize(pb_ba[lev].size(), 0.0);
159 
160  // Creating data array for perturbation amplitude storage
161  pb_cell[lev].define(ba, dm, 1, ngrow_state); // this is the only place ba is used. Maybe we can print here to determine what's valid...
162  pb_cell[lev].setVal(0.);
163 
164  // Computing perturbation reference length
165  tpi_Lpb = tpi_boxDim[0]*dx[0];
166  tpi_Wpb = tpi_boxDim[1]*dx[1];
167  tpi_Hpb = tpi_boxDim[2]*dx[2];
168  if (lev == 0) { // only need to define reference length scale for coarses level
170  }
171 
172  tpi_pert_adjust = 0.;
173  tpi_net_buoyant = 0.;
174 
175  /*
176  // Function check point message
177  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_box_dims: "
178  << tpi_boxDim[0] << " "
179  << tpi_boxDim[1] << " "
180  << tpi_boxDim[2] << "\n";
181  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_direction: "
182  << tpi_direction[0] << " "
183  << tpi_direction[1] << " "
184  << tpi_direction[2] << "\n\n";
185  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_layers: " << tpi_layers << "\n";
186  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_offset: " << tpi_offset << "\n\n";
187  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_nondimensional: " << tpi_nonDim << "\n";
188  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_T_infinity: " << tpi_Tinf << "\n";
189  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_T_intensity: " << tpi_Ti << "\n";
190  amrex::PrintToFile("BoxPerturbationOutput") << "Reference length per box = " << tpi_lref << "\n\n";
191  amrex::PrintToFile("BoxPerturbationOutput") << "Turbulent perturbation BoxArray:\n" << pb_ba[lev] << "\n";
192  */
193  }
194 
195 
196  // Perturbation update frequency check.
197  // This function will trigger calc_tpi_meanMag() and calc_tpi_amp(), when
198  // the local elapsed time is greater than the perturbation frequency.
199  // At each timestep a total buoyant force sanity check is made to ensure
200  // that the sum of the buoyant force introduced into the system is net-zero
201  void calc_tpi_update (const int lev,
202  const amrex::Real dt,
203  amrex::MultiFab& mf_xvel,
204  amrex::MultiFab& mf_yvel,
205  amrex::MultiFab& mf_cons)
206  {
207  // Resettubg the net buoyant force value
208  tpi_net_buoyant = 0.;
209 
210  // Setting random number generator for update interval
211  srand( (unsigned) time(NULL) );
212 
213  auto m_ixtype = mf_cons.boxArray().ixType(); // safety step
214 
215  for (int boxIdx = 0; boxIdx < pb_ba[lev].size(); boxIdx++) {
216 
217  // Check if the local elapsed time is greater than the update interval
218  if ( pb_interval[lev][boxIdx] <= pb_local_etime[lev][boxIdx] ) {
219 
220  // Compute mean velocity of each perturbation box
221  calc_tpi_meanMag_perBox(boxIdx, lev, mf_cons, mf_xvel, mf_yvel);
222 
223  // Only the rank owning the box will be able to access the storage location
224  // Done for parallelism to avoid Inf being stored in array
225  if (pb_mag[lev][boxIdx] !=0.) {
226  amrex::Real interval = tpi_lref / pb_mag[lev][boxIdx];
227  pb_interval[lev][boxIdx] = RandomReal(0.9*interval,1.1*interval); // 10% variation
228  }
229 
230  // Trigger amplitude calculation per perturbation box
231  calc_tpi_amp(lev, boxIdx, pb_interval[lev][boxIdx]);
232 
233  // Trigger random amplitude storage per cell within perturbation box
234  pseudoRandomPert(boxIdx, lev, m_ixtype);
235 
236  // Reset local elapsed time
237  pb_local_etime[lev][boxIdx] = 0.;
238  } else {
239  // Increase by timestep of level 0
240  pb_local_etime[lev][boxIdx] += dt;
241  } // if
242 
243  // Per iteration operation of net-zero buoyant force check
244  if (pb_mag[lev][boxIdx] !=0.) netZeroBuoyantAdd(boxIdx, lev);
245  tpi_net_buoyant += pb_netZero[lev][boxIdx];
246  } // for
247 
248  // Normalizing the adjustment based on how many boxes there are
249  // the values within the array is already normalized by the number
250  // of cells within each box
251  tpi_pert_adjust = tpi_net_buoyant / (amrex::Real) pb_ba[lev].size();
252 
253  // Per iteration operation of net-zero buoyant force adjustment
254  for (int boxIdx = 0; boxIdx < pb_ba[lev].size(); boxIdx++) {
255  if (pb_mag[lev][boxIdx] !=0.) netZeroBuoyantAdjust(boxIdx, lev);
256  }
257  }
258 
259  // Applying perturbation amplitude onto source term (Umphrey and Senocak 2016)
260  // This function does per cell application within the box union. Random perturbation
261  // is assigned in calc_tpi_update.
262  void apply_tpi (const int& lev,
263  const amrex::Box& vbx, // box union from upper level
264  const int& comp, // Component to modify
265  const amrex::IndexType& m_ixtype, // IntVect type of src_arr
266  const amrex::Array4<amrex::Real>& src_arr, // Array to apply perturbation
267  const amrex::Array4<amrex::Real const>& pert_cell)
268  {
269  for (int boxIdx = 0; boxIdx < pb_ba[lev].size(); boxIdx++) {
270  amrex::Box pbx = amrex::convert(pb_ba[lev][boxIdx], m_ixtype);
271  amrex::Box ubx = pbx & vbx;
272  if (ubx.ok()) {
273  ParallelFor(ubx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
274  src_arr(i,j,k,comp) += pert_cell(i,j,k);
275 
276  // For box region debug only
277  #ifdef INDEX_PERTURB
278  src_arr(i,j,k,comp) = (amrex::Real) (boxIdx + 5.);
279  #endif
280  });
281  }
282  }
283  }
284 
285  // Perturbation amplitude calculation
286  void calc_tpi_amp (const int& lev,
287  const int& boxIdx,
288  const amrex::Real& interval)
289  {
290  amrex::Real Um = pb_mag[lev][boxIdx];
291  pb_amp[lev][boxIdx] = 0.; // Safety step
292 
293  amrex::Real beta = 1./tpi_Tinf; // Thermal expansion coefficient
294 
295  // Pseudo Random temperature the ignores scale when mechanically tripping turbulence
296  amrex::Real g = CONST_GRAV;
297  // get total refinement ratio on each level
298  int total_ref_ratio = 1;
299  for (int level = lev; level >= 1; level--) {
300  total_ref_ratio *= ref_ratio[level-1][2];
301  }
302  // calculation needs to be scale-aware since the formulation relies on the physical size of the box
303  if (tpi_Ti > 0.) g = (tpi_nonDim * Um * Um) / (tpi_Ti * tpi_Hpb) * 1 / total_ref_ratio;
304 
305  // Ma and Senocak (2023) Eq. 8, solving for delta phi
306  pb_amp[lev][boxIdx] = (tpi_nonDim * Um * Um) / (g * beta * tpi_Hpb) * 1 / total_ref_ratio;
307 
308  if (pt_type == 0) {
309  // Performing this step converts the perturbation proportionality into
310  // the forcing term
311  // Ma & Senocak (2023) Eq. 7
312  pb_amp[lev][boxIdx] /= interval;
313  }
314  }
315 
316  // Assigns pseudo-random (ie. white noise) perturbation to a storage cell, this
317  // value is then held constant for the duration of the update interval and assigned onto
318  // the source term
319  void pseudoRandomPert (const int& boxIdx,
320  const int& lev,
321  const amrex::IndexType& m_ixtype)
322  {
323  // Seed the random generator at 1024UL for regression testing
324  int fix_random_seed = 0;
325  amrex::ParmParse pp("erf"); pp.query("fix_random_seed", fix_random_seed);
326  if (fix_random_seed) {
327  amrex::InitRandom(1024UL);
328  }
329 
330  for (amrex::MFIter mfi(pb_cell[lev],TileNoZ()); mfi.isValid(); ++mfi) {
331  amrex::Box vbx = mfi.validbox();
332  amrex::Box pbx = amrex::convert(pb_ba[lev][boxIdx], m_ixtype);
333  amrex::Box ubx = pbx & vbx;
334  if (ubx.ok()) {
335  amrex::Real amp_copy = pb_amp[lev][boxIdx];
336  const amrex::Array4<amrex::Real>& pert_cell = pb_cell[lev].array(mfi);
337  ParallelForRNG(ubx, [=] AMREX_GPU_DEVICE(int i, int j, int k, const amrex::RandomEngine& engine) noexcept {
338  amrex::Real rand_double = amrex::Random(engine);
339  pert_cell(i,j,k) = (rand_double*2.0 - 1.0) * amp_copy;
340  });
341  }
342  }
343  }
344 
345  // Checks for net-zero buoyant force introduction into the system
346  void netZeroBuoyantAdd (const int& boxIdx,
347  const int& lev)
348  {
349  // Creating local copy of PB box array and magnitude
350  const amrex::BoxArray m_pb_ba = pb_ba[lev];
351  amrex::Real* m_pb_netZero = pb_netZero[lev].data();
352 
353  // Create device array for summation
354  amrex::Vector<amrex::Real> avg_h(1,0.);
355  amrex::Gpu::DeviceVector<amrex::Real> avg_d(1,0.);
356  amrex::Real* avg = avg_d.data();
357 
358  // Iterates through the cells of each box and sum the white noise perturbation
359  for (amrex::MFIter mfi(pb_cell[lev], TileNoZ()) ; mfi.isValid(); ++mfi) {
360  const amrex::Box& vbx = mfi.validbox();
361  amrex::Box pbx = amrex::convert(m_pb_ba[boxIdx], vbx.ixType());
362  amrex::Box ubx = pbx & vbx;
363  if (ubx.ok()) {
364  const amrex::Array4<const amrex::Real>& pert_cell = pb_cell[lev].const_array(mfi);
365  int npts = ubx.numPts();
366  amrex::Real norm = 1.0 / (amrex::Real) npts;
367  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubx, [=]
368  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
369  amrex::Gpu::deviceReduceSum(&avg[0], pert_cell(i,j,k)*norm, handler);
370  });
371  amrex::Gpu::copy(amrex::Gpu::deviceToHost, avg_d.begin(), avg_d.end(), avg_h.begin());
372 
373  // Assigning onto storage array
374  m_pb_netZero[boxIdx] = avg_h[0];
375  }
376  }
377  }
378 
379  // If it's not a net-zero buoyant force, then adjust all cell by a normalized value
380  // to achieve this logic
381  void netZeroBuoyantAdjust (const int& boxIdx,
382  const int& lev)
383  {
384  // Creating local copy of PB box array and magnitude
385  const amrex::BoxArray m_pb_ba = pb_ba[lev];
386  for (amrex::MFIter mfi(pb_cell[lev], TileNoZ()) ; mfi.isValid(); ++mfi) {
387  const amrex::Box& vbx = mfi.validbox();
388  amrex::Box pbx = amrex::convert(m_pb_ba[boxIdx], vbx.ixType());
389  amrex::Box ubx = pbx & vbx;
390  if (ubx.ok()) {
391  const amrex::Real adjust = tpi_pert_adjust;
392  const amrex::Array4<amrex::Real>& pert_cell = pb_cell[lev].array(mfi);
393  ParallelFor(ubx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
394  pert_cell(i,j,k) -= adjust;
395  });
396  }
397  }
398  }
399 
400 // TODO: Test the difference between these two for Source term perturbation
401 #define USE_VOLUME_AVERAGE
402  // Perturbation box mean velocity magnitude calculation
403  // This is pulled into the structure to also utilize during runtime
404  void calc_tpi_meanMag_perBox (const int& boxIdx,
405  const int& lev,
406  amrex::MultiFab& mf_cons,
407  amrex::MultiFab& mf_xvel,
408  amrex::MultiFab& mf_yvel)
409 
410  {
411  // Creating local copy of PB box array and magnitude
412  const amrex::BoxArray m_pb_ba = pb_ba[lev];
413  amrex::Real* m_pb_mag = pb_mag[lev].data();
414  m_pb_mag[boxIdx] = 0.; // Safety step
415 
416  // Storage of averages per PB
417  // Index: 0=u (vol/slab_lo), 1=v (vol/slab_lo)
418  // 2=u (slab_hi), 3=v (slab_hi)
419  int n_avg = 4;
420  amrex::Vector<amrex::Real> avg_h(n_avg,0.);
421  amrex::Gpu::DeviceVector<amrex::Real> avg_d(n_avg,0.);
422  amrex::Real* avg = avg_d.data();
423 
424  // Averaging u & v components in single MFIter
425  for (amrex::MFIter mfi(mf_cons, TileNoZ()); mfi.isValid(); ++mfi) {
426 
427  // CC valid box (inherited from mf_cons)
428  const amrex::Box& vbx = mfi.validbox();
429 
430  // Box logic for u velocity
431  auto ixtype_u = mf_xvel.boxArray().ixType();
432  amrex::Box vbx_u = amrex::convert(vbx,ixtype_u);
433  amrex::Box pbx_u = amrex::convert(m_pb_ba[boxIdx], ixtype_u);
434  amrex::Box ubx_u = pbx_u & vbx_u;
435 
436  // Box logic for v velocity
437  auto ixtype_v = mf_yvel.boxArray().ixType();
438  amrex::Box vbx_v = amrex::convert(vbx,ixtype_v);
439  amrex::Box pbx_v = amrex::convert(m_pb_ba[boxIdx], ixtype_v);
440  amrex::Box ubx_v = pbx_v & vbx_v;
441 
442  // Operation over box union (U)
443  if (ubx_u.ok()) {
444  const amrex::Array4<const amrex::Real>& xvel_arry = mf_xvel.const_array(mfi);
445 
446  #ifdef USE_VOLUME_AVERAGE
447  int npts = ubx_u.numPts();
448  amrex::Real norm = 1.0 / (amrex::Real) npts;
449  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubx_u, [=]
450  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
451  amrex::Gpu::deviceReduceSum(&avg[0], xvel_arry(i,j,k)*norm, handler);
452  });
453  #endif // USE_VOLUME_AVERAGE
454 
455  #ifdef USE_SLAB_AVERAGE
456  amrex::Box ubxSlab_lo = makeSlab(ubx_u,2,ubx_u.smallEnd(2));
457  amrex::Box ubxSlab_hi = makeSlab(ubx_u,2,ubx_u.bigEnd(2));
458  int npts_lo = ubxSlab_lo.numPts();
459  int npts_hi = ubxSlab_hi.numPts();
460  amrex::Real norm_lo = 1.0 / (amrex::Real) npts_lo;
461  amrex::Real norm_hi = 1.0 / (amrex::Real) npts_hi;
462 
463  // Average u in the low slab
464  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubxSlab_lo, [=]
465  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
466  amrex::Gpu::deviceReduceSum(&avg[0], xvel_arry(i,j,k)*norm_lo, handler);
467  });
468 
469  // Average u in the high slab
470  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubxSlab_hi, [=]
471  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
472  amrex::Gpu::deviceReduceSum(&avg[2], xvel_arry(i,j,k)*norm_hi, handler);
473  });
474  #endif // USE_SLAB_AVERAGE
475  } // if
476 
477  // Operation over box union (V)
478  if (ubx_v.ok()) {
479  const amrex::Array4<const amrex::Real>& yvel_arry = mf_yvel.const_array(mfi);
480 
481  #ifdef USE_VOLUME_AVERAGE
482  int npts = ubx_v.numPts();
483  amrex::Real norm = 1.0 / (amrex::Real) npts;
484  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubx_v, [=]
485  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
486  amrex::Gpu::deviceReduceSum(&avg[1], yvel_arry(i,j,k)*norm, handler);
487  });
488  #endif // USE_VOLUME_AVERAGE
489 
490  #ifdef USE_SLAB_AVERAGE
491  amrex::Box ubxSlab_lo = makeSlab(ubx_v,2,ubx_v.smallEnd(2));
492  amrex::Box ubxSlab_hi = makeSlab(ubx_v,2,ubx_v.bigEnd(2));
493  int npts_lo = ubxSlab_lo.numPts();
494  int npts_hi = ubxSlab_hi.numPts();
495  amrex::Real norm_lo = 1.0 / (amrex::Real) npts_lo;
496  amrex::Real norm_hi = 1.0 / (amrex::Real) npts_hi;
497 
498  // Average v in the low slab
499  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubxSlab_lo, [=]
500  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
501  amrex::Gpu::deviceReduceSum(&avg[1], yvel_arry(i,j,k)*norm_lo, handler);
502  });
503 
504  // Average v in the high slab
505  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubxSlab_hi, [=]
506  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
507  amrex::Gpu::deviceReduceSum(&avg[3], yvel_arry(i,j,k)*norm_hi, handler);
508  });
509  #endif // USE_SLAB_AVERAGE
510  } // if
511  } // MFIter
512 
513  // Copy from device back to host
514  amrex::Gpu::copy(amrex::Gpu::deviceToHost, avg_d.begin(), avg_d.end(), avg_h.begin());
515 
516  // Computing the average magnitude within PB
517  #ifdef USE_VOLUME_AVERAGE
518  m_pb_mag[boxIdx] = sqrt(avg_h[0]*avg_h[0] + avg_h[1]*avg_h[1]);
519  #endif
520 
521  #ifdef USE_SLAB_AVERAGE
522  m_pb_mag[boxIdx] = 0.5*( sqrt(avg_h[0]*avg_h[0] + avg_h[1]*avg_h[1])
523  + sqrt(avg_h[2]*avg_h[2] + avg_h[3]*avg_h[3]));
524  #endif
525  }
526 
527  // Output debug message into a file
528  void debug (amrex::Real /*time*/)
529  {
530  /*
531  amrex::PrintToFile("BoxPerturbationOutput") << "#################### PB output at time = "
532  << time << " ####################\n";
533  amrex::PrintToFile("BoxPerturbationOutput") << " Using type: " << pt_type << "\n";
534  amrex::PrintToFile("BoxPerturbationOutput") << " Net: " << tpi_net_buoyant << " Adjust : " << tpi_pert_adjust << "\n";
535  for (int i = 0; i < pb_mag.size(); i++) {
536  amrex::PrintToFile("BoxPerturbationOutput") << "[" << i
537  << "] pb_Umag=" << pb_mag[i]
538  << " | pb_interval=" << pb_interval[i]
539  << " (" << pb_local_etime[i]
540  << ") | pb_amp=" << pb_amp[i] << "\n";
541  }
542  amrex::PrintToFile("BoxPerturbationOutput") << "\n";
543  */
544  }
545 
546  int pt_type; // TODO: May need to pass in solverChoice to replace this
547 
548  // Public data members
549  amrex::Vector<amrex::BoxArray> pb_ba; // PB box array
550  amrex::Vector<amrex::Vector<amrex::Real>> pb_mag; // BP mean magnitude [m/s]
551 
552  // Perturbation amplitude cell storage
553  // This is after random assignment of equation (10) in Ma and Senocak 2023
554  amrex::Vector<amrex::MultiFab> pb_cell;
555 
556  private:
557 
558  // Private data members
559  int tpi_layers; // Number of layers of perturbation boxes
560  int tpi_offset; // Cells to offset the start of the perturbation region
561 
562  amrex::Vector<int> tpi_boxDim; // Dimensions of each perturbation box
563  amrex::Vector<int> tpi_direction; // Direction of the peturbation
564 
565  // Richardson Formulation
566  amrex::Real tpi_nonDim; // Richardson number
567  amrex::Real tpi_Ti; // Temperature intensity value
568  amrex::Real tpi_Tinf; // Reference temperature [K]
569 
570  // Physical dimensions
571  amrex::Real tpi_Hpb; // PB height [m]
572  amrex::Real tpi_Lpb; // PB length [m]
573  amrex::Real tpi_Wpb; // PB width [m]
574  amrex::Real tpi_lref; // PB reference length [m]
575 
576  amrex::Real tpi_net_buoyant; // Perturbation net-zero calculation storage
577  amrex::Real tpi_pert_adjust; // Perturbation adjust for net-zero per cell adjustment
578 
579  amrex::Vector<amrex::IntVect> ref_ratio; // ref_ratio for multilevel run
580 
581  // Perturbation data arrays
582  amrex::Vector<amrex::Vector<amrex::Real>> pb_interval; // PB update time [s]
583  amrex::Vector<amrex::Vector<amrex::Real>> pb_local_etime; // PB local elapsed time [s]
584  amrex::Vector<amrex::Vector<amrex::Real>> pb_amp; // PB perturbation amplitude Ri:[K]
585  amrex::Vector<amrex::Vector<amrex::Real>> pb_netZero; // PB array used for net zero sum calculation
586 
587  // Random number generation between range (used for interval calculation)
588  amrex::Real RandomReal (const amrex::Real min, const amrex::Real max)
589  {
590  amrex::Real r = (amrex::Real) rand() / (amrex::Real) RAND_MAX;
591  return min + r * (max - min);
592  }
593 
594 };
595 #endif
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:21
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real pp(amrex::Real y)
Definition: ERF_MicrophysicsUtils.H:219
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real avg_h(amrex::Real XXXm, amrex::Real XXXp)
Definition: ERF_EBUtils.H:10
Definition: ERF_TurbPertStruct.H:18
amrex::Vector< amrex::MultiFab > pb_cell
Definition: ERF_TurbPertStruct.H:554
int tpi_layers
Definition: ERF_TurbPertStruct.H:559
void pseudoRandomPert(const int &boxIdx, const int &lev, const amrex::IndexType &m_ixtype)
Definition: ERF_TurbPertStruct.H:319
amrex::Real tpi_Tinf
Definition: ERF_TurbPertStruct.H:568
amrex::Real tpi_Wpb
Definition: ERF_TurbPertStruct.H:573
amrex::Real tpi_Lpb
Definition: ERF_TurbPertStruct.H:572
amrex::Vector< amrex::Vector< amrex::Real > > pb_local_etime
Definition: ERF_TurbPertStruct.H:583
amrex::Vector< amrex::IntVect > ref_ratio
Definition: ERF_TurbPertStruct.H:579
void calc_tpi_amp(const int &lev, const int &boxIdx, const amrex::Real &interval)
Definition: ERF_TurbPertStruct.H:286
amrex::Vector< amrex::BoxArray > pb_ba
Definition: ERF_TurbPertStruct.H:549
void netZeroBuoyantAdjust(const int &boxIdx, const int &lev)
Definition: ERF_TurbPertStruct.H:381
void calc_tpi_update(const int lev, const amrex::Real dt, amrex::MultiFab &mf_xvel, amrex::MultiFab &mf_yvel, amrex::MultiFab &mf_cons)
Definition: ERF_TurbPertStruct.H:201
amrex::Vector< amrex::Vector< amrex::Real > > pb_mag
Definition: ERF_TurbPertStruct.H:550
int tpi_offset
Definition: ERF_TurbPertStruct.H:560
amrex::Vector< amrex::Vector< amrex::Real > > pb_netZero
Definition: ERF_TurbPertStruct.H:585
amrex::Real tpi_Ti
Definition: ERF_TurbPertStruct.H:567
amrex::Real tpi_pert_adjust
Definition: ERF_TurbPertStruct.H:577
void debug(amrex::Real)
Definition: ERF_TurbPertStruct.H:528
amrex::Vector< amrex::Vector< amrex::Real > > pb_interval
Definition: ERF_TurbPertStruct.H:582
amrex::Real tpi_lref
Definition: ERF_TurbPertStruct.H:574
amrex::Vector< int > tpi_boxDim
Definition: ERF_TurbPertStruct.H:562
amrex::Real tpi_nonDim
Definition: ERF_TurbPertStruct.H:566
void calc_tpi_meanMag_perBox(const int &boxIdx, const int &lev, amrex::MultiFab &mf_cons, amrex::MultiFab &mf_xvel, amrex::MultiFab &mf_yvel)
Definition: ERF_TurbPertStruct.H:404
void init_tpi(const int lev, const amrex::IntVect &valid_box_lo, const amrex::IntVect &valid_box_hi, const amrex::GpuArray< amrex::Real, 3 > dx, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm, const int ngrow_state, std::string pp_prefix, const amrex::Vector< amrex::IntVect > refRatio, const int max_level)
Definition: ERF_TurbPertStruct.H:29
void netZeroBuoyantAdd(const int &boxIdx, const int &lev)
Definition: ERF_TurbPertStruct.H:346
int pt_type
Definition: ERF_TurbPertStruct.H:546
~TurbulentPerturbation()
Definition: ERF_TurbPertStruct.H:22
amrex::Real RandomReal(const amrex::Real min, const amrex::Real max)
Definition: ERF_TurbPertStruct.H:588
amrex::Real tpi_net_buoyant
Definition: ERF_TurbPertStruct.H:576
void apply_tpi(const int &lev, const amrex::Box &vbx, const int &comp, const amrex::IndexType &m_ixtype, const amrex::Array4< amrex::Real > &src_arr, const amrex::Array4< amrex::Real const > &pert_cell)
Definition: ERF_TurbPertStruct.H:262
amrex::Real tpi_Hpb
Definition: ERF_TurbPertStruct.H:571
amrex::Vector< int > tpi_direction
Definition: ERF_TurbPertStruct.H:563
amrex::Vector< amrex::Vector< amrex::Real > > pb_amp
Definition: ERF_TurbPertStruct.H:584