ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
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 
18 AMREX_ENUM(PerturbationType,
19  Source, Direct, CPM, CPM_W, None
20 );
21 
22 /**
23  * @brief Runtime state and operations for turbulent perturbation forcing.
24  */
26 
27  public:
28 
29  /**
30  * @brief Destroy turbulent perturbation state.
31  */
33 
34  /**
35  * @brief Store the integer perturbation-type selector for one AMR level.
36  * @param lev AMR level index.
37  * @param pert_type Perturbation type selected for this level.
38  * @param max_level Maximum AMR level configured for the run.
39  */
40  void init_tpi_type (const int lev,
41  const PerturbationType& pert_type,
42  const int max_level)
43  {
44  if (pt_type.size() < max_level + 1) {
45  pt_type.resize(max_level + 1, -1);
46  }
47 
48  if (pert_type == PerturbationType::Source) {
49  pt_type[lev] = 0;
50  } else if (pert_type == PerturbationType::Direct) {
51  pt_type[lev] = 1;
52  } else if (pert_type == PerturbationType::CPM) {
53  pt_type[lev] = 2;
54  } else if (pert_type == PerturbationType::CPM_W) {
55  pt_type[lev] = 3;
56  } else {
57  pt_type[lev] = -1;
58  }
59  }
60 
61  /**
62  * @brief Initialize turbulent perturbation regions and per-box storage.
63  * @param lev AMR level index.
64  * @param subdomains_lev Rectangular level subdomains used to define perturbation regions.
65  * @param dx Cell spacing for this level.
66  * @param ba State BoxArray used for perturbation-cell storage.
67  * @param dm DistributionMapping for perturbation-cell storage.
68  * @param ngrow_state Number of ghost cells for perturbation-cell storage.
69  * @param pp_prefix ParmParse prefix for the ERF input namespace.
70  * @param refRatio Refinement ratios between AMR levels.
71  * @param max_level Maximum AMR level configured for the run.
72  */
73  void init_tpi (const int lev,
74  const amrex::Vector<amrex::BoxArray>& subdomains_lev,
75  const amrex::GpuArray<amrex::Real,3> dx,
76  const amrex::BoxArray& ba,
77  const amrex::DistributionMapping& dm,
78  const int ngrow_state,
79  std::string pp_prefix,
80  const amrex::Vector<amrex::IntVect> refRatio,
81  const int max_level)
82 
83  {
84  ref_ratio = refRatio;
85 
86  amrex::ParmParse pp(pp_prefix);
87 
88  // Reading inputs, and placing assertion for the perturbation inflow to work
89  pp.getarr("perturbation_box_dims",tpi_boxDim);
90  pp.getarr("perturbation_direction",tpi_direction);
91  pp.get("perturbation_layers",tpi_layers);
92  pp.get("perturbation_offset",tpi_offset);
93 
94  tpi_nonDim = zero;
95  pp.query("perturbation_nondimensional",tpi_nonDim);
96 
97  tpi_Tinf = amrex::Real(300.);
98  pp.query("perturbation_T_infinity",tpi_Tinf);
99 
100  tpi_Ti = zero;
101  pp.query("perturbation_T_intensity",tpi_Ti);
102 
103  input_Ug = zero;
104  pp.query("perturbation_Ug",input_Ug);
105 
106  input_w_amp = zero;
107  pp.query("perturbation_w_amp",input_w_amp);
108 
109  perturbation_klo = 0;
110  bool have_klo = pp.query("perturbation_klo", perturbation_klo);
111 
112  perturbation_khi = 0;
113  bool have_khi = pp.query("perturbation_khi", perturbation_khi);
114 
115  // Check variables message
116  if (tpi_offset < 0) { amrex::Abort("Please provide a valid inflow cell offset value for perturbation region (ie. 0-5)"); }
117  if (tpi_layers < 0) { amrex::Abort("Please provide a valid perturbation layer value (ie. 3-5)"); }
118  if (tpi_nonDim < zero) { amrex::Abort("Please provide a valid nondimensional number (ie. Ri = amrex::Real(0.042))"); }
119  for (int i = 0; i < tpi_boxDim.size(); i++) {
120  if (tpi_boxDim[i] < 3) { amrex::Abort("Please provide valid dimensions for perturbation boxes."); }
121  }
122  if (input_Ug < zero) { amrex::Abort("Please provide a valid geostrophic wind speed (ie. Ug = amrex::Real(10.0) m/s)"); }
123  if (tpi_Tinf < zero) { amrex::Abort("Please provide a valid ambient temperature value (ie. T_0 = T_infty)"); }
124  if (tpi_Ti < zero) { amrex::Abort("Please provide a valid temperature intensity value (ie. 0-one)"); }
125 
126  // Create a temporary box list to accumulate all the perturbation regions after box modification
127  amrex::BoxList tmp_bl;
128 
129  // boxSize for individual boxes
130  amrex::IntVect boxSize(tpi_boxDim[0],tpi_boxDim[1],tpi_boxDim[2]);
131 
132  if (tpi_direction[2] || tpi_direction[5]) { amrex::Abort("Currently not supporting z-direction flow perturbation"); }
133 
134  for (int isub = 0; isub < subdomains_lev.size(); ++isub) {
135  const amrex::BoxArray& subdomain = subdomains_lev[isub];
136  amrex::Box subdomain_box(subdomain.minimalBox());
137 
138  if (subdomain_box.numPts() != subdomain.numPts()) {
139  amrex::Abort("Turbulent perturbations require rectangular subdomains. "
140  "Level " + std::to_string(lev) +
141  ", subdomain " + std::to_string(isub) +
142  " is not a rectangular region fully covered by grids.");
143  }
144 
145  const amrex::IntVect& valid_box_lo = subdomain_box.smallEnd();
146  const amrex::IntVect& valid_box_hi = subdomain_box.bigEnd();
147 
148  // default perturbation region to be the entire z extent if the user does not specify bounds
149  if (!have_klo) { perturbation_klo = valid_box_lo[2]; }
150  if (!have_khi) { perturbation_khi = valid_box_hi[2]; }
151 
152  // Creating perturbation regions and initializing with generic size.
153  amrex::Box lo_x_bx(amrex::IntVect(0), amrex::IntVect(1), amrex::IntVect(0));
154  amrex::Box hi_x_bx(amrex::IntVect(0), amrex::IntVect(1), amrex::IntVect(0));
155  amrex::Box lo_y_bx(amrex::IntVect(0), amrex::IntVect(1), amrex::IntVect(0));
156  amrex::Box hi_y_bx(amrex::IntVect(0), amrex::IntVect(1), amrex::IntVect(0));
157 
158  // Starting logic to set the size of the perturbation region(s)
159  //amrex::PrintToFile("BoxPerturbationOutput") << "Setting perturbation region in:";
160  // ***** X-direction perturbation *****
161  if (tpi_direction[0]) { // West
162  lo_x_bx.setSmall(amrex::IntVect(valid_box_lo[0]+tpi_offset, valid_box_lo[1]+tpi_direction[1]*tpi_offset, perturbation_klo));
163  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), perturbation_khi));
164  amrex::PrintToFile("BoxPerturbationOutput") << " West face";
165  }
166 
167  if (tpi_direction[3]) { // East
168  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, perturbation_klo));
169  hi_x_bx.setBig(amrex::IntVect(valid_box_hi[0]-tpi_offset, valid_box_hi[1]-(tpi_direction[4]*tpi_offset), perturbation_khi));
170  amrex::PrintToFile("BoxPerturbationOutput") << " East face";
171  }
172 
173  // ***** Y-direction Perturbation *****
174  if (tpi_direction[1]) { // North
175  lo_y_bx.setSmall(amrex::IntVect(valid_box_lo[0]+tpi_direction[0]*tpi_offset, valid_box_lo[1]+tpi_offset, perturbation_klo));
176  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, perturbation_khi));
177  amrex::PrintToFile("BoxPerturbationOutput") << " North face";
178  }
179 
180  if (tpi_direction[4]) { // South
181  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), perturbation_klo));
182  hi_y_bx.setBig(amrex::IntVect(valid_box_hi[0]-tpi_direction[3]*tpi_offset, valid_box_hi[1]-tpi_offset, perturbation_khi));
183  amrex::PrintToFile("BoxPerturbationOutput") << " South face";
184  }
185 
186  // Performing box union for intersecting perturbation regions to avoid overlapping sections (double counting at corners)
187  if (tpi_direction[0] && tpi_direction[1]) { // Reshaping South smallEnd
188  amrex::Box lo_x_lo_y_u = lo_x_bx & lo_y_bx;
189  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)));
190  }
191 
192  if (tpi_direction[3] && tpi_direction[1]) { // Reshaping South bigEnd
193  amrex::Box hi_x_lo_y_u = hi_x_bx & lo_y_bx;
194  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)));
195  }
196 
197  if (tpi_direction[0] && tpi_direction[4]) { // Reshaping North smallEnd
198  amrex::Box lo_x_hi_y_u = lo_x_bx & hi_y_bx;
199  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)));
200  }
201 
202  if (tpi_direction[3] && tpi_direction[4]) { // Reshaping North bigEnd
203  amrex::Box hi_x_hi_y_u = hi_x_bx & hi_y_bx;
204  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)));
205  }
206 
207  // Creating structure box array for conserved quantity
208  if (tpi_direction[0]) { tmp_bl.push_back(lo_x_bx); }
209  if (tpi_direction[1]) { tmp_bl.push_back(lo_y_bx); }
210  if (tpi_direction[3]) { tmp_bl.push_back(hi_x_bx); }
211  if (tpi_direction[4]) { tmp_bl.push_back(hi_y_bx); }
212  }
213 
214  //amrex::PrintToFile("BoxPerturbationOutput") << "\nBoxList: " << tmp_bl << "\n";
215  amrex::BoxArray tmp_ba(tmp_bl);
216  tmp_ba.maxSize(boxSize);
217 
218  const int num_levels = max_level + 1;
219  if (pb_ba.size() < num_levels) {
220  pb_ba.resize(num_levels);
221  pb_mag.resize(num_levels);
222  pb_dir.resize(num_levels);
223  pb_netZero.resize(num_levels);
224  pb_interval.resize(num_levels);
225  pb_local_etime.resize(num_levels);
226  pb_amp.resize(num_levels);
227  pb_cell.resize(num_levels);
228  tpi_Lpb.resize(num_levels);
229  tpi_Wpb.resize(num_levels);
230  tpi_Hpb.resize(num_levels);
231  tpi_lref.resize(num_levels);
232  }
233 
234  pb_ba[lev] = tmp_ba;
235 
236  // Initializing mean magnitude and direction vectors
237  pb_mag[lev].resize(pb_ba[lev].size(), zero);
238  pb_dir[lev].resize(pb_ba[lev].size(), zero);
239  pb_netZero[lev].resize(pb_ba[lev].size(), zero);
240 
241  // Set size of vector and initialize
242  pb_interval[lev].resize(pb_ba[lev].size(), -one);
243  pb_local_etime[lev].resize(pb_ba[lev].size(), zero);
244  pb_amp[lev].resize(pb_ba[lev].size(), zero);
245 
246  // Creating data array for perturbation amplitude storage
247  if (pt_type[lev] == 3) { // CPM_W converts to the k-face ba.
248  pb_cell[lev].define(convert(ba, amrex::IntVect(0,0,1)), dm, 1, ngrow_state);
249  } else {
250  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...
251  }
252  pb_cell[lev].setVal(0.);
253 
254  // Computing perturbation reference length
255  tpi_Lpb[lev] = tpi_boxDim[0]*dx[0];
256  tpi_Wpb[lev] = tpi_boxDim[1]*dx[1];
257  tpi_Hpb[lev] = tpi_boxDim[2]*dx[2];
258  tpi_lref[lev] = std::sqrt(tpi_Lpb[lev]*tpi_Lpb[lev] + tpi_Wpb[lev]*tpi_Wpb[lev]);
259 
262 
263  /*
264  // Function check point message
265  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_box_dims: "
266  << tpi_boxDim[0] << " "
267  << tpi_boxDim[1] << " "
268  << tpi_boxDim[2] << "\n";
269  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_direction: "
270  << tpi_direction[0] << " "
271  << tpi_direction[1] << " "
272  << tpi_direction[2] << "\n\n";
273  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_layers: " << tpi_layers << "\n";
274  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_offset: " << tpi_offset << "\n\n";
275  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_nondimensional: " << tpi_nonDim << "\n";
276  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_T_infinity: " << tpi_Tinf << "\n";
277  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_T_intensity: " << tpi_Ti << "\n";
278  amrex::PrintToFile("BoxPerturbationOutput") << "Reference length per box = " << tpi_lref[lev] << "\n\n";
279  amrex::PrintToFile("BoxPerturbationOutput") << "Turbulent perturbation BoxArray:\n" << pb_ba[lev] << "\n";
280  */
281  }
282 
283 
284  /**
285  * @brief Update perturbation amplitudes and intervals when each box is ready.
286  * @param lev AMR level index.
287  * @param dt Time step for elapsed-time accumulation.
288  * @param mf_xvel X-velocity field used to compute box mean velocities.
289  * @param mf_yvel Y-velocity field used to compute box mean velocities.
290  * @param mf_cons Conserved state used for iteration layout.
291  */
292  void calc_tpi_update (const int lev,
293  const double dt,
294  amrex::MultiFab& mf_xvel,
295  amrex::MultiFab& mf_yvel,
296  amrex::MultiFab& mf_cons)
297  {
298  // Resetting the net buoyant force value
300 
301  // Setting random number generator for update interval
302  srand( (unsigned) time(NULL) );
303 
304  auto m_ixtype = mf_cons.boxArray().ixType(); // safety step
305  if (pt_type[lev] == 3) { m_ixtype = amrex::IndexType(amrex::IntVect(0,0,1)); }
306 
307  // Seed the random generator at 1024UL for regression testing
308  int fix_random_seed = 0;
309  amrex::ParmParse pp("erf");
310  pp.query("fix_random_seed", fix_random_seed);
311  if (fix_random_seed) {
312  // We need this one for the ParalleForRNG used in calc_tpi
313  amrex::InitRandom(1024UL, amrex::ParallelDescriptor::NProcs(), 1024UL);
314 
315  // We need this one for the RandomReal below
316  srand(1024UL);
317  }
318 
319  for (int boxIdx = 0; boxIdx < pb_ba[lev].size(); boxIdx++) {
320 
321  bool update_box = true; // initialize flag
322  // Check if the local elapsed time is greater than the update interval and don't go into boxes the rank doesn't own
323  if (pt_type[lev] == 2 || pt_type[lev] == 3) {
324  // for CPM, perturbation boxes/cells refresh after boxes have advected box width/length * num_layers (advective time scale)
325  update_box = ((pb_local_etime[lev][boxIdx] >= pb_interval[lev][boxIdx]*tpi_layers) && pb_local_etime[lev][boxIdx] != -one);
326  } else {
327  update_box = ( pb_local_etime[lev][boxIdx] >= pb_interval[lev][boxIdx] && pb_local_etime[lev][boxIdx] != -one );
328  }
329  if ( update_box ) {
330 
331  // Compute mean velocity of each perturbation box
332  calc_tpi_meanMag_perBox(boxIdx, lev, mf_cons, mf_xvel, mf_yvel);
333 
334  // Only the rank owning the box will be able to access the storage location
335  // Done for parallelism to avoid Inf being stored in array
336  if (pb_mag[lev][boxIdx] !=zero) {
337  double interval = 0.0;
338  if (pt_type[lev] == 2 || pt_type[lev] == 3) {
339  // Wind direction correction for angled wind
340  amrex::Real wind_direction = pb_dir[lev][boxIdx];
341  if (wind_direction > PI / 4) { wind_direction = PI / 2 - wind_direction; }
342  // CPM only cares about the side length of a box, min call maintains flexibility.
343  interval = one / std::cos(wind_direction) * std::min(tpi_Lpb[lev], tpi_Wpb[lev]) / pb_mag[lev][boxIdx];
344  } else {
345  interval = tpi_lref[lev] / pb_mag[lev][boxIdx];
346  }
347  pb_interval[lev][boxIdx] = RandomReal(static_cast<amrex::Real>(0.9*interval),
348  static_cast<amrex::Real>(1.1*interval)); // 10% variation
349 
350  // Reset local elapsed time
351  pb_local_etime[lev][boxIdx] = zero;
352  } else {
353  // this box is not on this rank, we shouldn't enter it again.
354  // Technically, all boxes are looped through on the very first step of a simulation and this is when this is set
355  pb_local_etime[lev][boxIdx] = -one;
356  }
357 
358  // Trigger amplitude calculation per perturbation box
359  calc_tpi_amp(lev, boxIdx, pb_interval[lev][boxIdx]);
360 
361  // Trigger random amplitude storage per cell within perturbation box
362  pseudoRandomPert(boxIdx, lev, m_ixtype);
363 
364  } else {
365  // set perturbation amplitudes to 0 for CPM. A little inefficient but leverages as much as existing code as possible.
366  if (pt_type[lev] == 2 || pt_type[lev] == 3) { zero_amp(boxIdx, lev, m_ixtype); }
367 
368  // Increase by timestep of level 0 (but only if the box is owned by this rank)
369  if (pb_local_etime[lev][boxIdx] != -one) { pb_local_etime[lev][boxIdx] += dt; }
370  } // if
371 
372  if (pt_type[lev] < 2) { // box perturbation method only
373  // Per iteration operation of net-zero buoyant force check
374  if (pb_mag[lev][boxIdx] !=zero) netZeroBuoyantAdd(boxIdx, lev);
375  tpi_net_buoyant += pb_netZero[lev][boxIdx];
376  }
377  } // for
378 
379  if (pt_type[lev] < 2) { // box perturbation method only
380  // Normalizing the adjustment based on how many boxes there are
381  // the values within the array is already normalized by the number
382  // of cells within each box
384 
385  // Per iteration operation of net-zero buoyant force adjustment
386  for (int boxIdx = 0; boxIdx < pb_ba[lev].size(); boxIdx++) {
387  if (pb_mag[lev][boxIdx] !=zero) netZeroBuoyantAdjust(boxIdx, lev);
388  }
389  }
390  }
391 
392  /**
393  * @brief Apply stored turbulent perturbations to a source or state array.
394  * @param lev AMR level index.
395  * @param vbx Valid box over which perturbations may be applied.
396  * @param comp Component to modify, or -1 for vertical velocity perturbations.
397  * @param m_ixtype Index type of the destination array.
398  * @param src_arr Destination array receiving perturbation increments.
399  * @param pert_cell Stored perturbation amplitudes.
400  */
401  void apply_tpi (const int& lev,
402  const amrex::Box& vbx, // box union from upper level
403  const int& comp, // Component to modify
404  const amrex::IndexType& m_ixtype, // IntVect type of src_arr
405  const amrex::Array4<amrex::Real>& src_arr, // Array to apply perturbation
406  const amrex::Array4<amrex::Real const>& pert_cell)
407  {
408  for (int boxIdx = 0; boxIdx < pb_ba[lev].size(); boxIdx++) {
409  amrex::Box pbx = amrex::convert(pb_ba[lev][boxIdx], m_ixtype);
410  if (pt_type[lev] == 3) { pbx.setBig(2, pbx.bigEnd(2) - 1); } // prevent double counting after converting to k-faces
411  amrex::Box ubx = pbx & vbx;
412  if (ubx.ok()) {
413  if (comp == -1) { // vertical velocity perturbations
414  ParallelFor(ubx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
415  src_arr(i,j,k) += pert_cell(i,j,k);
416  });
417  } else {
418  ParallelFor(ubx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
419  src_arr(i,j,k,comp) += pert_cell(i,j,k);
420 
421  // For box region debug only
422  #ifdef INDEX_PERTURB
423  src_arr(i,j,k,comp) = (amrex::Real) (boxIdx + 5.);
424  #endif
425  });
426  }
427  }
428  }
429  }
430 
431  /**
432  * @brief Compute the perturbation amplitude for one perturbation box.
433  * @param lev AMR level index.
434  * @param boxIdx Perturbation-box index on the level.
435  * @param interval Current perturbation update interval [s].
436  */
437  void calc_tpi_amp (const int& lev,
438  const int& boxIdx,
439  const double& interval)
440  {
441  pb_amp[lev][boxIdx] = zero; // Safety step
442  if (pt_type[lev] == 2) { // CPM
443  amrex::Real cp = 1004; // specific heat of air [J/(kg K)]
444  amrex::Real Ec = amrex::Real(0.2); // Eckert number
445  pb_amp[lev][boxIdx] = (input_Ug * input_Ug) / (Ec * cp);
446  } else if (pt_type[lev] == 3) {
447  pb_amp[lev][boxIdx] = input_w_amp;
448  } else { // box perturbation
449  amrex::Real Um = pb_mag[lev][boxIdx];
450  amrex::Real beta = one/tpi_Tinf; // Thermal expansion coefficient
451 
452  // Pseudo Random temperature the ignores scale when mechanically tripping turbulence
454  // get total refinement ratio on each level
455  int total_ref_ratio = 1;
456  for (int level = lev; level >= 1; level--) {
457  total_ref_ratio *= ref_ratio[level-1][2];
458  }
459  // calculation needs to be scale-aware since the formulation relies on the physical size of the box
460  if (tpi_Ti > zero) g = (tpi_nonDim * Um * Um) / (tpi_Ti * tpi_Hpb[lev]) * 1 / total_ref_ratio;
461 
462  // Ma and Senocak (2023) Eq. 8, solving for delta phi
463  pb_amp[lev][boxIdx] = (tpi_nonDim * Um * Um) / (g * beta * tpi_Hpb[lev]) * 1 / total_ref_ratio;
464 
465  if (pt_type[lev] == 0) {
466  // Performing this step converts the perturbation proportionality into
467  // the forcing term
468  // Ma & Senocak (2023) Eq. 7
469  pb_amp[lev][boxIdx] /= static_cast<amrex::Real>(interval);
470  }
471  }
472  }
473 
474  /**
475  * @brief Assign pseudo-random perturbations to cells in one perturbation box.
476  * @param boxIdx Perturbation-box index on the level.
477  * @param lev AMR level index.
478  * @param m_ixtype Index type used to align perturbation storage.
479  */
480  void pseudoRandomPert (const int& boxIdx,
481  const int& lev,
482  const amrex::IndexType& m_ixtype)
483  {
484  for (amrex::MFIter mfi(pb_cell[lev],TileNoZ()); mfi.isValid(); ++mfi) {
485  amrex::Box vbx = mfi.validbox();
486  amrex::Box pbx = amrex::convert(pb_ba[lev][boxIdx], m_ixtype);
487  if (pt_type[lev] == 3) { pbx.setBig(2, pbx.bigEnd(2) - 1); } // prevent double counting after converting to k-faces
488  amrex::Box ubx = pbx & vbx;
489  if (ubx.ok()) {
490  amrex::Real amp_copy = pb_amp[lev][boxIdx];
491  const amrex::Array4<amrex::Real>& pert_cell = pb_cell[lev].array(mfi);
492 
493  if (pt_type[lev] == 2 || pt_type[lev] == 3) { // CPM
494  amrex::Real rand_number_const = RandomReal(-one, one);
495  ParallelFor(ubx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
496  pert_cell(i,j,k) = rand_number_const * amp_copy;
497  });
498  } else {
499  ParallelForRNG(ubx, [=] AMREX_GPU_DEVICE(int i, int j, int k, const amrex::RandomEngine& engine) noexcept {
500  amrex::Real rand_double = amrex::Random(engine);
501  pert_cell(i,j,k) = (rand_double*two - one) * amp_copy;
502  });
503  }
504  }
505  }
506  }
507 
508  /**
509  * @brief Reset CPM perturbation amplitudes in one perturbation box to zero.
510  * @param boxIdx Perturbation-box index on the level.
511  * @param lev AMR level index.
512  * @param m_ixtype Index type used to align perturbation storage.
513  */
514  void zero_amp (const int& boxIdx,
515  const int& lev,
516  const amrex::IndexType& m_ixtype)
517  {
518 
519  for (amrex::MFIter mfi(pb_cell[lev],TileNoZ()); mfi.isValid(); ++mfi) {
520  amrex::Box vbx = mfi.validbox();
521  amrex::Box pbx = amrex::convert(pb_ba[lev][boxIdx], m_ixtype);
522  if (pt_type[lev] == 3) { pbx.setBig(2, pbx.bigEnd(2) - 1); } // prevent double counting after converting to k-faces
523  amrex::Box ubx = pbx & vbx;
524  if (ubx.ok()) {
525  const amrex::Array4<amrex::Real>& pert_cell = pb_cell[lev].array(mfi);
526  ParallelFor(ubx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
527  pert_cell(i,j,k) = zero;
528  });
529  }
530  }
531  }
532 
533  /**
534  * @brief Accumulate the mean perturbation contribution for net-zero buoyancy enforcement.
535  * @param boxIdx Perturbation-box index on the level.
536  * @param lev AMR level index.
537  */
538  void netZeroBuoyantAdd (const int& boxIdx,
539  const int& lev)
540  {
541  // Creating local copy of PB box array and magnitude
542  const amrex::BoxArray m_pb_ba = pb_ba[lev];
543  amrex::Real* m_pb_netZero = pb_netZero[lev].data();
544 
545  // Create device array for summation
546  amrex::Vector<amrex::Real> avg_h(1,zero);
547  amrex::Gpu::DeviceVector<amrex::Real> avg_d(1,zero);
548  amrex::Real* avg = avg_d.data();
549 
550  // Iterates through the cells of each box and sum the white noise perturbation
551  for (amrex::MFIter mfi(pb_cell[lev], TileNoZ()) ; mfi.isValid(); ++mfi) {
552  const amrex::Box& vbx = mfi.validbox();
553  amrex::Box pbx = amrex::convert(m_pb_ba[boxIdx], vbx.ixType());
554  amrex::Box ubx = pbx & vbx;
555  if (ubx.ok()) {
556  const amrex::Array4<const amrex::Real>& pert_cell = pb_cell[lev].const_array(mfi);
557  amrex::Real norm = one / static_cast<amrex::Real>(ubx.numPts());
558  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubx, [=]
559  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
560  amrex::Gpu::deviceReduceSum(&avg[0], pert_cell(i,j,k)*norm, handler);
561  });
562  amrex::Gpu::copy(amrex::Gpu::deviceToHost, avg_d.begin(), avg_d.end(), avg_h.begin());
563 
564  // Assigning onto storage array
565  m_pb_netZero[boxIdx] = avg_h[0];
566  }
567  }
568  }
569 
570  /**
571  * @brief Adjust perturbation cells so the net buoyant forcing is zero.
572  * @param boxIdx Perturbation-box index on the level.
573  * @param lev AMR level index.
574  */
575  void netZeroBuoyantAdjust (const int& boxIdx,
576  const int& lev)
577  {
578  // Creating local copy of PB box array and magnitude
579  const amrex::BoxArray m_pb_ba = pb_ba[lev];
580  for (amrex::MFIter mfi(pb_cell[lev], TileNoZ()) ; mfi.isValid(); ++mfi) {
581  const amrex::Box& vbx = mfi.validbox();
582  amrex::Box pbx = amrex::convert(m_pb_ba[boxIdx], vbx.ixType());
583  amrex::Box ubx = pbx & vbx;
584  if (ubx.ok()) {
585  const amrex::Real adjust = tpi_pert_adjust;
586  const amrex::Array4<amrex::Real>& pert_cell = pb_cell[lev].array(mfi);
587  ParallelFor(ubx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
588  pert_cell(i,j,k) -= adjust;
589  });
590  }
591  }
592  }
593 
594 // TODO: Test the difference between these two for Source term perturbation
595 #define USE_VOLUME_AVERAGE
596  /**
597  * @brief Compute mean horizontal velocity magnitude and direction for one perturbation box.
598  * @param boxIdx Perturbation-box index on the level.
599  * @param lev AMR level index.
600  * @param mf_cons Conserved state used for iteration layout.
601  * @param mf_xvel X-velocity field.
602  * @param mf_yvel Y-velocity field.
603  */
604  void calc_tpi_meanMag_perBox (const int& boxIdx,
605  const int& lev,
606  amrex::MultiFab& mf_cons,
607  amrex::MultiFab& mf_xvel,
608  amrex::MultiFab& mf_yvel)
609 
610  {
611  // Creating local copy of PB box array and magnitude
612  const amrex::BoxArray m_pb_ba = pb_ba[lev];
613  amrex::Real* m_pb_mag = pb_mag[lev].data();
614  amrex::Real* m_pb_dir = pb_dir[lev].data();
615  m_pb_mag[boxIdx] = zero; // Safety step
616  m_pb_dir[boxIdx] = zero; // Safety step
617 
618  // Storage of averages per PB
619  // Index: 0=u (vol/slab_lo), 1=v (vol/slab_lo)
620  // 2=u (slab_hi), 3=v (slab_hi)
621  int n_avg = 4;
622  amrex::Vector<amrex::Real> avg_h(n_avg,zero);
623  amrex::Gpu::DeviceVector<amrex::Real> avg_d(n_avg,zero);
624  amrex::Real* avg = avg_d.data();
625 
626  // Averaging u & v components in single MFIter
627  for (amrex::MFIter mfi(mf_cons, TileNoZ()); mfi.isValid(); ++mfi) {
628 
629  // CC valid box (inherited from mf_cons)
630  const amrex::Box& vbx = mfi.validbox();
631 
632  // Box logic for u velocity
633  auto ixtype_u = mf_xvel.boxArray().ixType();
634  amrex::Box vbx_u = amrex::convert(vbx,ixtype_u);
635  amrex::Box pbx_u = amrex::convert(m_pb_ba[boxIdx], ixtype_u);
636  amrex::Box ubx_u = pbx_u & vbx_u;
637 
638  // Box logic for v velocity
639  auto ixtype_v = mf_yvel.boxArray().ixType();
640  amrex::Box vbx_v = amrex::convert(vbx,ixtype_v);
641  amrex::Box pbx_v = amrex::convert(m_pb_ba[boxIdx], ixtype_v);
642  amrex::Box ubx_v = pbx_v & vbx_v;
643 
644  // Operation over box union (U)
645  if (ubx_u.ok()) {
646  const amrex::Array4<const amrex::Real>& xvel_arry = mf_xvel.const_array(mfi);
647 
648  #ifdef USE_VOLUME_AVERAGE
649  amrex::Real norm = one / static_cast<amrex::Real>(ubx_u.numPts());
650  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubx_u, [=]
651  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
652  amrex::Gpu::deviceReduceSum(&avg[0], xvel_arry(i,j,k)*norm, handler);
653  });
654  #endif // USE_VOLUME_AVERAGE
655 
656  #ifdef USE_SLAB_AVERAGE
657  amrex::Box ubxSlab_lo = makeSlab(ubx_u,2,ubx_u.smallEnd(2));
658  amrex::Box ubxSlab_hi = makeSlab(ubx_u,2,ubx_u.bigEnd(2));
659  amrex::Real norm_lo = one / static_cast<amrex::Real>(ubxSlab_lo.numPts());
660  amrex::Real norm_hi = one / static_cast<amrex::Real>(ubxSlab_hi.numPts());
661 
662  // Average u in the low slab
663  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubxSlab_lo, [=]
664  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
665  amrex::Gpu::deviceReduceSum(&avg[0], xvel_arry(i,j,k)*norm_lo, handler);
666  });
667 
668  // Average u in the high slab
669  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubxSlab_hi, [=]
670  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
671  amrex::Gpu::deviceReduceSum(&avg[2], xvel_arry(i,j,k)*norm_hi, handler);
672  });
673  #endif // USE_SLAB_AVERAGE
674  } // if
675 
676  // Operation over box union (V)
677  if (ubx_v.ok()) {
678  const amrex::Array4<const amrex::Real>& yvel_arry = mf_yvel.const_array(mfi);
679 
680  #ifdef USE_VOLUME_AVERAGE
681  amrex::Real norm = one / static_cast<amrex::Real>(ubx_v.numPts());
682  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubx_v, [=]
683  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
684  amrex::Gpu::deviceReduceSum(&avg[1], yvel_arry(i,j,k)*norm, handler);
685  });
686  #endif // USE_VOLUME_AVERAGE
687 
688  #ifdef USE_SLAB_AVERAGE
689  amrex::Box ubxSlab_lo = makeSlab(ubx_v,2,ubx_v.smallEnd(2));
690  amrex::Box ubxSlab_hi = makeSlab(ubx_v,2,ubx_v.bigEnd(2));
691  amrex::Real norm_lo = one / static_cast<amrex::Real>(ubxSlab_lo.numPts());
692  amrex::Real norm_hi = one / static_cast<amrex::Real>(ubxSlab_hi.numPts());
693 
694  // Average v in the low slab
695  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubxSlab_lo, [=]
696  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
697  amrex::Gpu::deviceReduceSum(&avg[1], yvel_arry(i,j,k)*norm_lo, handler);
698  });
699 
700  // Average v in the high slab
701  ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), ubxSlab_hi, [=]
702  AMREX_GPU_DEVICE(int i, int j, int k, amrex::Gpu::Handler const& handler) noexcept {
703  amrex::Gpu::deviceReduceSum(&avg[3], yvel_arry(i,j,k)*norm_hi, handler);
704  });
705  #endif // USE_SLAB_AVERAGE
706  } // if
707  } // MFIter
708 
709  // Copy from device back to host
710  amrex::Gpu::copy(amrex::Gpu::deviceToHost, avg_d.begin(), avg_d.end(), avg_h.begin());
711 
712  // Computing the average magnitude within PB
713  #ifdef USE_VOLUME_AVERAGE
714  m_pb_mag[boxIdx] = std::sqrt(avg_h[0]*avg_h[0] + avg_h[1]*avg_h[1]);
715  m_pb_dir[boxIdx] = std::atan(std::abs(avg_h[0]) / std::abs(avg_h[1]+std::numeric_limits<amrex::Real>::epsilon()));
716  #endif
717 
718  #ifdef USE_SLAB_AVERAGE
719  m_pb_mag[boxIdx] = myhalf*( std::sqrt(avg_h[0]*avg_h[0] + avg_h[1]*avg_h[1])
720  + std::sqrt(avg_h[2]*avg_h[2] + avg_h[3]*avg_h[3]));
721  m_pb_dir[boxIdx] = std::atan(std::abs(myhalf*(avg_h[0]+avg_h[2])) / std::abs(myhalf*(avg_h[1]+avg_h[3])+std::numeric_limits<amrex::Real>::epsilon()));
722  #endif
723  }
724 
725  /**
726  * @brief Write perturbation debug information when debug output is enabled.
727  */
728  void debug (double /*time*/)
729  {
730  /*
731  amrex::PrintToFile("BoxPerturbationOutput") << "#################### PB output at time = "
732  << time << " ####################\n";
733  amrex::PrintToFile("BoxPerturbationOutput") << " Using type: " << pt_type << "\n";
734  amrex::PrintToFile("BoxPerturbationOutput") << " Net: " << tpi_net_buoyant << " Adjust : " << tpi_pert_adjust << "\n";
735  for (int i = 0; i < pb_mag.size(); i++) {
736  amrex::PrintToFile("BoxPerturbationOutput") << "[" << i
737  << "] pb_Umag=" << pb_mag[i]
738  << " | pb_interval=" << pb_interval[i]
739  << " (" << pb_local_etime[i]
740  << ") | pb_amp=" << pb_amp[i] << "\n";
741  }
742  amrex::PrintToFile("BoxPerturbationOutput") << "\n";
743  */
744  }
745 
746  amrex::Vector<int> pt_type;
747 
748  // Public data members
749  amrex::Vector<amrex::BoxArray> pb_ba; // PB box array
750  amrex::Vector<amrex::Vector<amrex::Real>> pb_mag; // BP mean magnitude [m/s]
751  amrex::Vector<amrex::Vector<amrex::Real>> pb_dir; // BP mean direction [deg]
752 
753  // Perturbation amplitude cell storage
754  // This is after random assignment of equation (10) in Ma and Senocak 2023
755  amrex::Vector<amrex::MultiFab> pb_cell;
756 
757  private:
758 
759  // Private data members
760  int tpi_layers; // Number of layers of perturbation boxes
761  int tpi_offset; // Cells to offset the start of the perturbation region
762 
763  amrex::Vector<int> tpi_boxDim; // Dimensions of each perturbation box
764  amrex::Vector<int> tpi_direction; // Direction of the perturbation
765 
766  // Richardson Formulation
767  amrex::Real tpi_nonDim; // Richardson number
768  amrex::Real tpi_Ti; // Temperature intensity value
769  amrex::Real tpi_Tinf; // Reference temperature [K]
770 
771  // Physical dimensions
772  amrex::Vector<amrex::Real> tpi_Hpb; // PB height [m]
773  amrex::Vector<amrex::Real> tpi_Lpb; // PB length [m]
774  amrex::Vector<amrex::Real> tpi_Wpb; // PB width [m]
775  amrex::Vector<amrex::Real> tpi_lref; // PB reference length [m]
776 
777  amrex::Real tpi_net_buoyant; // Perturbation net-zero calculation storage
778  amrex::Real tpi_pert_adjust; // Perturbation adjust for net-zero per cell adjustment
779 
780  amrex::Vector<amrex::IntVect> ref_ratio; // ref_ratio for multilevel run
781  amrex::Real input_Ug; // input geostrophic wind speed to scale perturbations when using CPM
782  amrex::Real input_w_amp; // input vertical velocity amplitude for w
783  int perturbation_klo; // upper bound in Nz for perturbations
784  int perturbation_khi; // lower bound in Nz for perturbations
785 
786  // Perturbation data arrays
787  amrex::Vector<amrex::Vector<double>> pb_interval; // PB update time [s]
788  amrex::Vector<amrex::Vector<double>> pb_local_etime; // PB local elapsed time [s]
789  amrex::Vector<amrex::Vector<amrex::Real>> pb_amp; // PB perturbation amplitude Ri:[K]
790  amrex::Vector<amrex::Vector<amrex::Real>> pb_netZero; // PB array used for net zero sum calculation
791 
792  /**
793  * @brief Return a pseudo-random real value in a closed interval.
794  * @param min Lower bound of the interval.
795  * @param max Upper bound of the interval.
796  * @return Pseudo-random value between min and max.
797  */
799  {
800  amrex::Real r = (amrex::Real) rand() / (amrex::Real) RAND_MAX;
801  return min + r * (max - min);
802  }
803 
804 };
805 
806 /**
807  * @var TurbulentPerturbation::pt_type
808  * @brief Integer perturbation type for each AMR level.
809  * @var TurbulentPerturbation::pb_ba
810  * @brief Perturbation-box BoxArray for each AMR level.
811  * @var TurbulentPerturbation::pb_mag
812  * @brief Mean velocity magnitude for each perturbation box [m/s].
813  * @var TurbulentPerturbation::pb_dir
814  * @brief Mean velocity direction for each perturbation box.
815  * @var TurbulentPerturbation::pb_cell
816  * @brief Per-cell perturbation amplitude storage.
817  * @var TurbulentPerturbation::tpi_layers
818  * @brief Number of layers of perturbation boxes.
819  * @var TurbulentPerturbation::tpi_offset
820  * @brief Cell offset for the start of the perturbation region.
821  * @var TurbulentPerturbation::tpi_boxDim
822  * @brief Dimensions of each perturbation box.
823  * @var TurbulentPerturbation::tpi_direction
824  * @brief Boundary directions where perturbations are applied.
825  * @var TurbulentPerturbation::tpi_nonDim
826  * @brief Nondimensional number used by the perturbation formulation.
827  * @var TurbulentPerturbation::tpi_Ti
828  * @brief Temperature intensity used by the perturbation formulation.
829  * @var TurbulentPerturbation::tpi_Tinf
830  * @brief Reference temperature used by the perturbation formulation [K].
831  * @var TurbulentPerturbation::tpi_Hpb
832  * @brief Perturbation-box height for each AMR level [m].
833  * @var TurbulentPerturbation::tpi_Lpb
834  * @brief Perturbation-box length for each AMR level [m].
835  * @var TurbulentPerturbation::tpi_Wpb
836  * @brief Perturbation-box width for each AMR level [m].
837  * @var TurbulentPerturbation::tpi_lref
838  * @brief Perturbation-box reference length for each AMR level [m].
839  * @var TurbulentPerturbation::tpi_net_buoyant
840  * @brief Accumulated net buoyant perturbation used for correction.
841  * @var TurbulentPerturbation::tpi_pert_adjust
842  * @brief Per-cell perturbation adjustment used for net-zero buoyancy.
843  * @var TurbulentPerturbation::ref_ratio
844  * @brief Refinement ratios used by multilevel perturbation scaling.
845  * @var TurbulentPerturbation::input_Ug
846  * @brief Input geostrophic wind speed used to scale CPM perturbations.
847  * @var TurbulentPerturbation::input_w_amp
848  * @brief Input vertical velocity perturbation amplitude.
849  * @var TurbulentPerturbation::perturbation_klo
850  * @brief Lower vertical index bound for perturbations.
851  * @var TurbulentPerturbation::perturbation_khi
852  * @brief Upper vertical index bound for perturbations.
853  * @var TurbulentPerturbation::pb_interval
854  * @brief Perturbation update interval for each box [s].
855  * @var TurbulentPerturbation::pb_local_etime
856  * @brief Local elapsed time for each perturbation box [s].
857  * @var TurbulentPerturbation::pb_amp
858  * @brief Perturbation amplitude for each perturbation box.
859  * @var TurbulentPerturbation::pb_netZero
860  * @brief Per-box storage used for net-zero buoyancy calculation.
861  */
862 #endif
constexpr amrex::Real two
Definition: ERF_Constants.H:10
constexpr amrex::Real one
Definition: ERF_Constants.H:9
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
constexpr amrex::Real PI
Definition: ERF_Constants.H:42
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:64
ParmParse pp("prob")
ParallelForRNG(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k, const amrex::RandomEngine &engine) noexcept { const Real x=prob_lo_x+(i+myhalf) *dx;const Real y=prob_lo_y+(j+myhalf) *dy;const Real z=z_cc(i, j, k);const Real r=std::sqrt((x-xc) *(x-xc)+(y-yc) *(y-yc)+(z-zc) *(z-zc));if((z<=pert_ref_height) &&(T_0_Pert_Mag !=amrex::Real(0))) { Real rand_double=amrex::Random(engine);state_pert(i, j, k, RhoTheta_comp)=(rand_double *amrex::Real(2) - amrex::Real(1)) *T_0_Pert_Mag;if(!pert_rhotheta) { state_pert(i, j, k, RhoTheta_comp) *=r_hse(i, j, k);} } state_pert(i, j, k, RhoScalar_comp)=A_0 *std::exp(-amrex::Real(10.) *r *r);if(state_pert.nComp() > RhoKE_comp) { if(rhoKE_0 > 0) { state_pert(i, j, k, RhoKE_comp)=rhoKE_0;} else { state_pert(i, j, k, RhoKE_comp)=r_hse(i, j, k) *KE_0;} if(KE_decay_height > 0) { state_pert(i, j, k, RhoKE_comp) *=amrex::max(std::pow(1 - amrex::min(z/KE_decay_height, amrex::Real(1)), KE_decay_order), Real(1e-12));} } })
const Real dx
Definition: ERF_InitCustomPert_ABL.H:23
amrex::Real beta
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:10
ParallelFor(fab_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
AMREX_ENUM(PerturbationType, Source, Direct, CPM, CPM_W, None)
real(c_double), parameter g
Definition: ERF_module_model_constants.F90:19
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
real(c_double), parameter cp
Definition: ERF_module_model_constants.F90:22
integer, private isub
Definition: ERF_module_mp_morr_two_moment.F90:164
Runtime state and operations for turbulent perturbation forcing.
Definition: ERF_TurbPertStruct.H:25
amrex::Real tpi_Tinf
Reference temperature used by the perturbation formulation [K].
Definition: ERF_TurbPertStruct.H:769
int tpi_layers
Number of layers of perturbation boxes.
Definition: ERF_TurbPertStruct.H:760
amrex::Vector< amrex::Real > tpi_Wpb
Perturbation-box width for each AMR level [m].
Definition: ERF_TurbPertStruct.H:774
amrex::Real input_w_amp
Input vertical velocity perturbation amplitude.
Definition: ERF_TurbPertStruct.H:782
void calc_tpi_update(const int lev, const double dt, amrex::MultiFab &mf_xvel, amrex::MultiFab &mf_yvel, amrex::MultiFab &mf_cons)
Update perturbation amplitudes and intervals when each box is ready.
Definition: ERF_TurbPertStruct.H:292
void calc_tpi_amp(const int &lev, const int &boxIdx, const double &interval)
Compute the perturbation amplitude for one perturbation box.
Definition: ERF_TurbPertStruct.H:437
void pseudoRandomPert(const int &boxIdx, const int &lev, const amrex::IndexType &m_ixtype)
Assign pseudo-random perturbations to cells in one perturbation box.
Definition: ERF_TurbPertStruct.H:480
amrex::Real tpi_pert_adjust
Per-cell perturbation adjustment used for net-zero buoyancy.
Definition: ERF_TurbPertStruct.H:778
amrex::Vector< int > tpi_direction
Boundary directions where perturbations are applied.
Definition: ERF_TurbPertStruct.H:764
int perturbation_klo
Lower vertical index bound for perturbations.
Definition: ERF_TurbPertStruct.H:783
int tpi_offset
Cell offset for the start of the perturbation region.
Definition: ERF_TurbPertStruct.H:761
amrex::Vector< amrex::Real > tpi_Lpb
Perturbation-box length for each AMR level [m].
Definition: ERF_TurbPertStruct.H:773
amrex::Vector< amrex::Vector< amrex::Real > > pb_netZero
Per-box storage used for net-zero buoyancy calculation.
Definition: ERF_TurbPertStruct.H:790
void netZeroBuoyantAdjust(const int &boxIdx, const int &lev)
Adjust perturbation cells so the net buoyant forcing is zero.
Definition: ERF_TurbPertStruct.H:575
amrex::Vector< amrex::Vector< amrex::Real > > pb_mag
Mean velocity magnitude for each perturbation box [m/s].
Definition: ERF_TurbPertStruct.H:750
amrex::Real tpi_net_buoyant
Accumulated net buoyant perturbation used for correction.
Definition: ERF_TurbPertStruct.H:777
amrex::Vector< amrex::Vector< amrex::Real > > pb_dir
Mean velocity direction for each perturbation box.
Definition: ERF_TurbPertStruct.H:751
amrex::Real input_Ug
Input geostrophic wind speed used to scale CPM perturbations.
Definition: ERF_TurbPertStruct.H:781
amrex::Vector< int > tpi_boxDim
Dimensions of each perturbation box.
Definition: ERF_TurbPertStruct.H:763
amrex::Vector< amrex::Vector< double > > pb_interval
Perturbation update interval for each box [s].
Definition: ERF_TurbPertStruct.H:787
void init_tpi_type(const int lev, const PerturbationType &pert_type, const int max_level)
Store the integer perturbation-type selector for one AMR level.
Definition: ERF_TurbPertStruct.H:40
void debug(double)
Write perturbation debug information when debug output is enabled.
Definition: ERF_TurbPertStruct.H:728
void init_tpi(const int lev, const amrex::Vector< amrex::BoxArray > &subdomains_lev, 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)
Initialize turbulent perturbation regions and per-box storage.
Definition: ERF_TurbPertStruct.H:73
amrex::Vector< amrex::Vector< double > > pb_local_etime
Local elapsed time for each perturbation box [s].
Definition: ERF_TurbPertStruct.H:788
amrex::Vector< amrex::Real > tpi_lref
Perturbation-box reference length for each AMR level [m].
Definition: ERF_TurbPertStruct.H:775
amrex::Vector< amrex::MultiFab > pb_cell
Per-cell perturbation amplitude storage.
Definition: ERF_TurbPertStruct.H:755
void zero_amp(const int &boxIdx, const int &lev, const amrex::IndexType &m_ixtype)
Reset CPM perturbation amplitudes in one perturbation box to zero.
Definition: ERF_TurbPertStruct.H:514
amrex::Vector< amrex::IntVect > ref_ratio
Refinement ratios used by multilevel perturbation scaling.
Definition: ERF_TurbPertStruct.H:780
int perturbation_khi
Upper vertical index bound for perturbations.
Definition: ERF_TurbPertStruct.H:784
void calc_tpi_meanMag_perBox(const int &boxIdx, const int &lev, amrex::MultiFab &mf_cons, amrex::MultiFab &mf_xvel, amrex::MultiFab &mf_yvel)
Compute mean horizontal velocity magnitude and direction for one perturbation box.
Definition: ERF_TurbPertStruct.H:604
amrex::Real tpi_Ti
Temperature intensity used by the perturbation formulation.
Definition: ERF_TurbPertStruct.H:768
amrex::Vector< amrex::BoxArray > pb_ba
Perturbation-box BoxArray for each AMR level.
Definition: ERF_TurbPertStruct.H:749
void netZeroBuoyantAdd(const int &boxIdx, const int &lev)
Accumulate the mean perturbation contribution for net-zero buoyancy enforcement.
Definition: ERF_TurbPertStruct.H:538
~TurbulentPerturbation()
Destroy turbulent perturbation state.
Definition: ERF_TurbPertStruct.H:32
amrex::Real RandomReal(const amrex::Real min, const amrex::Real max)
Return a pseudo-random real value in a closed interval.
Definition: ERF_TurbPertStruct.H:798
amrex::Vector< int > pt_type
Integer perturbation type for each AMR level.
Definition: ERF_TurbPertStruct.H:746
amrex::Vector< amrex::Vector< amrex::Real > > pb_amp
Perturbation amplitude for each perturbation box.
Definition: ERF_TurbPertStruct.H:789
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)
Apply stored turbulent perturbations to a source or state array.
Definition: ERF_TurbPertStruct.H:401
amrex::Real tpi_nonDim
Nondimensional number used by the perturbation formulation.
Definition: ERF_TurbPertStruct.H:767
amrex::Vector< amrex::Real > tpi_Hpb
Perturbation-box height for each AMR level [m].
Definition: ERF_TurbPertStruct.H:772