ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
TurbulentPerturbation Struct Reference

Runtime state and operations for turbulent perturbation forcing. More...

#include <ERF_TurbPertStruct.H>

Collaboration diagram for TurbulentPerturbation:

Classes

struct  UniformClock
 

Public Member Functions

 ~TurbulentPerturbation ()
 Destroy turbulent perturbation state. More...
 
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. More...
 
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, const bool automatic=false)
 Initialize turbulent perturbation regions and per-box storage. More...
 
amrex::Box automatic_face_box (const amrex::Box &subdomain, int face) const
 
bool automatic_update_due (int lev, double time) const
 
bool uniform_refresh_enabled (int lev) const
 
bool uniform_wind_update_due (int lev, double time) const
 
amrex::Real uniform_sampling_height () const
 
amrex::Real uniform_sampling_period () const
 
bool uniform_sampling_face (int face) const
 
void set_uniform_advection (int lev, double time, amrex::Real speed, double wind_period)
 
double uniform_refresh_interval (int lev) const
 
bool calc_tpi_uniform_update (int lev, double time)
 
void configure_automatic (int lev, double time, double period, const amrex::Vector< AutomaticPerturbationFace > &faces, const amrex::Geometry &geom, const amrex::MultiFab *z_cc, const amrex::MultiFab *z_nd, const amrex::MultiFab *z_ground)
 
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. More...
 
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, const amrex::Real scale=one)
 Apply stored turbulent perturbations to a source or state array. More...
 
void calc_tpi_amp (const int &lev, const int &boxIdx, const double &interval)
 Compute the perturbation amplitude for one perturbation box. More...
 
void pseudoRandomPert (const int &boxIdx, const int &lev, const amrex::IndexType &m_ixtype)
 Assign pseudo-random perturbations to cells in one perturbation box. More...
 
void zero_amp (const int &boxIdx, const int &lev, const amrex::IndexType &m_ixtype)
 Reset CPM perturbation amplitudes in one perturbation box to zero. More...
 
void netZeroBuoyant (const int &lev)
 Remove the mean of the box-perturbation field, so the net buoyant forcing is zero. More...
 
void calc_tpi_meanMag (const int &lev, const amrex::Vector< int > &update_box, amrex::MultiFab &mf_cons, amrex::MultiFab &mf_xvel, amrex::MultiFab &mf_yvel)
 Compute the mean horizontal velocity magnitude and direction of the refreshing perturbation boxes. More...
 
void debug (double)
 Write perturbation debug information when debug output is enabled. More...
 

Static Public Member Functions

static amrex::Real box_sum (const amrex::Array4< const amrex::Real > &arr, const amrex::Box &bx)
 Sum an array over a box. More...
 

Public Attributes

amrex::Vector< int > pt_type
 Integer perturbation type for each AMR level. More...
 
amrex::Vector< amrex::BoxArray > pb_ba
 Perturbation-box BoxArray for each AMR level. More...
 
amrex::Vector< amrex::Vector< amrex::Real > > pb_mag
 Mean velocity magnitude for each perturbation box [m/s]. More...
 
amrex::Vector< amrex::Vector< amrex::Real > > pb_dir
 Mean velocity direction for each perturbation box. More...
 
amrex::Vector< amrex::MultiFab > pb_cell
 Per-cell perturbation amplitude storage. More...
 

Private Attributes

bool uniform_mode {true}
 
amrex::Real uniform_height {amrex::Real(20)}
 
amrex::Real uniform_wind_period {amrex::Real(600)}
 
amrex::Vector< UniformClock > uniform_clock
 
bool auto_mode {false}
 
amrex::Vector< amrex::Vector< amrex::Real > > pb_Ug
 
amrex::Vector< amrex::MultiFab > auto_mask
 
amrex::Vector< double > auto_next_update
 
amrex::Vector< int > auto_ready
 
int tpi_layers
 Number of layers of perturbation boxes. More...
 
int tpi_offset
 Cell offset for the start of the perturbation region. More...
 
amrex::Vector< int > tpi_boxDim
 Dimensions of each perturbation box. More...
 
amrex::Vector< int > tpi_direction
 Boundary directions where perturbations are applied. More...
 
amrex::Real tpi_nonDim
 Nondimensional number used by the perturbation formulation. More...
 
amrex::Real tpi_Ti
 Temperature intensity used by the perturbation formulation. More...
 
amrex::Real tpi_Tinf
 Reference temperature used by the perturbation formulation [K]. More...
 
amrex::Vector< amrex::Real > tpi_Hpb
 Perturbation-box height for each AMR level [m]. More...
 
amrex::Vector< amrex::Real > tpi_Lpb
 Perturbation-box length for each AMR level [m]. More...
 
amrex::Vector< amrex::Real > tpi_Wpb
 Perturbation-box width for each AMR level [m]. More...
 
amrex::Vector< amrex::Real > tpi_lref
 Perturbation-box reference length for each AMR level [m]. More...
 
amrex::Real tpi_net_buoyant
 Accumulated net buoyant perturbation used for correction. More...
 
amrex::Real tpi_pert_adjust
 Per-cell perturbation adjustment used for net-zero buoyancy. More...
 
amrex::Vector< amrex::IntVect > ref_ratio
 Refinement ratios used by multilevel perturbation scaling. More...
 
amrex::Real input_Ug
 Input geostrophic wind speed used to scale CPM perturbations. More...
 
amrex::Real input_w_amp
 Input vertical velocity perturbation amplitude. More...
 
int perturbation_klo
 Lower vertical index bound for perturbations. More...
 
int perturbation_khi
 Upper vertical index bound for perturbations. More...
 
amrex::Vector< amrex::Vector< double > > pb_interval
 Perturbation update interval for each box [s]. More...
 
amrex::Vector< amrex::Vector< double > > pb_local_etime
 Local elapsed time for each perturbation box [s]. More...
 
amrex::Vector< amrex::Vector< amrex::Real > > pb_amp
 Perturbation amplitude for each perturbation box. More...
 
amrex::Vector< amrex::Vector< amrex::Real > > pb_netZero
 Per-box storage used for net-zero buoyancy calculation. More...
 
amrex::Vector< amrex::Vector< int > > pb_nupdate
 Number of times each perturbation box has been refreshed; keys its random numbers. More...
 
std::uint64_t tpi_seed = 0
 Seed of the hashed random numbers (1024 with erf.fix_random_seed, else erf.random_seed, else 0). More...
 

Static Private Attributes

static constexpr int hash_comp_cell = 0
 Hash salt of the per-cell perturbation values. More...
 
static constexpr int hash_comp_cpm = 1
 Hash salt of the per-box CPM values. More...
 
static constexpr int hash_comp_interval = 2
 Hash salt of the refresh-interval variation. More...
 

Detailed Description

Runtime state and operations for turbulent perturbation forcing.

Constructor & Destructor Documentation

◆ ~TurbulentPerturbation()

TurbulentPerturbation::~TurbulentPerturbation ( )
inline

Destroy turbulent perturbation state.

49 {}

Member Function Documentation

◆ apply_tpi()

void TurbulentPerturbation::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,
const amrex::Real  scale = one 
)
inline

Apply stored turbulent perturbations to a source or state array.

Parameters
levAMR level index.
vbxValid box over which perturbations may be applied.
compComponent to modify, or -1 for vertical velocity perturbations.
m_ixtypeIndex type of the destination array.
src_arrDestination array receiving perturbation increments.
pert_cellStored perturbation amplitudes.
scaleFactor applied to the stored amplitudes (the time step for the direct perturbation, whose stored amplitudes are rates).
627  {
628  if (uniform_refresh_enabled(lev) && !uniform_clock[lev].apply) { return; }
629  for (int boxIdx = 0; boxIdx < pb_ba[lev].size(); boxIdx++) {
630  amrex::Box pbx = amrex::convert(pb_ba[lev][boxIdx], m_ixtype);
631  if (pt_type[lev] == 3) { pbx.setBig(2, pbx.bigEnd(2) - 1); } // prevent double counting after converting to k-faces
632  amrex::Box ubx = pbx & vbx;
633  if (ubx.ok()) {
634  if (comp == -1) { // vertical velocity perturbations
635  ParallelFor(ubx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
636  src_arr(i,j,k) += scale * pert_cell(i,j,k);
637  });
638  } else {
639  ParallelFor(ubx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
640  src_arr(i,j,k,comp) += scale * pert_cell(i,j,k);
641 
642  // For box region debug only
643  #ifdef INDEX_PERTURB
644  src_arr(i,j,k,comp) = (amrex::Real) (boxIdx + 5.);
645  #endif
646  });
647  }
648  }
649  }
650  }
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
void apply(const amrex::Box &bx, const amrex::Box &domain, const int quantity, const int flux_comp, const amrex::Array4< const amrex::Real > &state, const amrex::Array4< const amrex::Real > &prim, const amrex::Array4< const amrex::Real > &base_state, const amrex::Array4< const amrex::Real > &u, const amrex::Array4< const amrex::Real > &v, const amrex::Array4< const amrex::Real > &w, const amrex::Array4< amrex::Real > &rhs, const amrex::Array4< amrex::Real > &xflux, const amrex::Array4< amrex::Real > &yflux, const amrex::Array4< amrex::Real > &zflux, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dx_inv, const erf_wall_thermodynamics::Boundary &walls, const amrex::Real alpha_T, const amrex::Real alpha_C, const amrex::Real rdOcp, const bool cloudy=false)
Definition: ERF_CloudChamberWallFlux.H:1037
bool uniform_refresh_enabled(int lev) const
Definition: ERF_TurbPertStruct.H:405
amrex::Vector< UniformClock > uniform_clock
Definition: ERF_TurbPertStruct.H:939
amrex::Vector< amrex::BoxArray > pb_ba
Perturbation-box BoxArray for each AMR level.
Definition: ERF_TurbPertStruct.H:920
amrex::Vector< int > pt_type
Integer perturbation type for each AMR level.
Definition: ERF_TurbPertStruct.H:917

Referenced by make_sources().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ automatic_face_box()

amrex::Box TurbulentPerturbation::automatic_face_box ( const amrex::Box &  subdomain,
int  face 
) const
inline
386  {
387  amrex::Box region = subdomain;
388  region.grow(0, -tpi_offset);
389  region.grow(1, -tpi_offset);
390  const int dir = face % 2;
391  const int width = tpi_layers * tpi_boxDim[dir];
392  if (face < 2) {
393  region.setBig(dir, std::min(region.bigEnd(dir), region.smallEnd(dir)+width-1));
394  } else {
395  region.setSmall(dir, std::max(region.smallEnd(dir), region.bigEnd(dir)-width+1));
396  }
397  return region;
398  }
int tpi_layers
Number of layers of perturbation boxes.
Definition: ERF_TurbPertStruct.H:949
int tpi_offset
Cell offset for the start of the perturbation region.
Definition: ERF_TurbPertStruct.H:950
amrex::Vector< int > tpi_boxDim
Dimensions of each perturbation box.
Definition: ERF_TurbPertStruct.H:952

Referenced by init_tpi().

Here is the caller graph for this function:

◆ automatic_update_due()

bool TurbulentPerturbation::automatic_update_due ( int  lev,
double  time 
) const
inline
401  {
402  return !auto_ready[lev] || time >= auto_next_update[lev];
403  }
amrex::Vector< int > auto_ready
Definition: ERF_TurbPertStruct.H:946
amrex::Vector< double > auto_next_update
Definition: ERF_TurbPertStruct.H:944

◆ box_sum()

static amrex::Real TurbulentPerturbation::box_sum ( const amrex::Array4< const amrex::Real > &  arr,
const amrex::Box &  bx 
)
inlinestatic

Sum an array over a box.

Parameters
arrArray to sum.
bxBox to sum over.
Returns
Sum of arr over bx.
885  {
886  amrex::ReduceOps<amrex::ReduceOpSum> reduce_op;
887  amrex::ReduceData<amrex::Real> reduce_data(reduce_op);
888  using ReduceTuple = typename decltype(reduce_data)::Type;
889  reduce_op.eval(bx, reduce_data, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept -> ReduceTuple
890  {
891  return {arr(i,j,k)};
892  });
893  return amrex::get<0>(reduce_data.value(reduce_op));
894  }

Referenced by calc_tpi_meanMag(), and netZeroBuoyant().

Here is the caller graph for this function:

◆ calc_tpi_amp()

void TurbulentPerturbation::calc_tpi_amp ( const int &  lev,
const int &  boxIdx,
const double &  interval 
)
inline

Compute the perturbation amplitude for one perturbation box.

Parameters
levAMR level index.
boxIdxPerturbation-box index on the level.
intervalCurrent perturbation update interval [s].
661  {
662  pb_amp[lev][boxIdx] = zero; // Safety step
663  if (pt_type[lev] == 2) { // CPM
664  constexpr amrex::Real cp = amrex::Real(1004); // specific heat of air [J/(kg K)]
665  constexpr amrex::Real Ec = amrex::Real(0.2); // Eckert number
666  const amrex::Real Ug = auto_mode ? pb_Ug[lev][boxIdx] : input_Ug;
667  pb_amp[lev][boxIdx] = (Ug * Ug) / (Ec * cp);
668  } else if (pt_type[lev] == 3) {
669  pb_amp[lev][boxIdx] = input_w_amp;
670  } else { // box perturbation
671  amrex::Real Um = pb_mag[lev][boxIdx];
672 
673  // No mean wind: nothing to trip, and with tpi_Ti > 0 the amplitude below would be
674  // 0/0. The box is not refreshed again, so a zero amplitude also clears the
675  // perturbation it held
676  if (Um == zero) { return; }
677 
678  amrex::Real beta = one/tpi_Tinf; // Thermal expansion coefficient
679 
680  // Pseudo Random temperature the ignores scale when mechanically tripping turbulence
682  // get total refinement ratio on each level
683  int total_ref_ratio = 1;
684  for (int level = lev; level >= 1; level--) {
685  total_ref_ratio *= ref_ratio[level-1][2];
686  }
687  // calculation needs to be scale-aware since the formulation relies on the physical size of the box
688  if (tpi_Ti > zero) g = (tpi_nonDim * Um * Um) / (tpi_Ti * tpi_Hpb[lev]) * 1 / total_ref_ratio;
689 
690  // Ma and Senocak (2023) Eq. 8, solving for delta phi
691  pb_amp[lev][boxIdx] = (tpi_nonDim * Um * Um) / (g * beta * tpi_Hpb[lev]) * 1 / total_ref_ratio;
692 
693  // Divide by the interval to get a forcing rate [K/s], Ma & Senocak (2023) Eq. 7.
694  // The source perturbation adds this rate as a source term; the direct
695  // perturbation adds it times the time step to the state at every step. Either
696  // way the total over one refresh interval is delta phi, even if dt changes
697  pb_amp[lev][boxIdx] /= static_cast<amrex::Real>(interval);
698  }
699  }
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:72
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:37
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:36
@ beta
Definition: ERF_SLM.H:152
real(c_double), parameter g
Definition: ERF_module_model_constants.F90:19
real(c_double), parameter cp
Definition: ERF_module_model_constants.F90:22
amrex::Real tpi_Tinf
Reference temperature used by the perturbation formulation [K].
Definition: ERF_TurbPertStruct.H:958
amrex::Real input_w_amp
Input vertical velocity perturbation amplitude.
Definition: ERF_TurbPertStruct.H:971
amrex::Vector< amrex::Vector< amrex::Real > > pb_Ug
Definition: ERF_TurbPertStruct.H:942
bool auto_mode
Definition: ERF_TurbPertStruct.H:941
amrex::Vector< amrex::Vector< amrex::Real > > pb_mag
Mean velocity magnitude for each perturbation box [m/s].
Definition: ERF_TurbPertStruct.H:921
amrex::Real input_Ug
Input geostrophic wind speed used to scale CPM perturbations.
Definition: ERF_TurbPertStruct.H:970
amrex::Vector< amrex::IntVect > ref_ratio
Refinement ratios used by multilevel perturbation scaling.
Definition: ERF_TurbPertStruct.H:969
amrex::Real tpi_Ti
Temperature intensity used by the perturbation formulation.
Definition: ERF_TurbPertStruct.H:957
amrex::Vector< amrex::Vector< amrex::Real > > pb_amp
Perturbation amplitude for each perturbation box.
Definition: ERF_TurbPertStruct.H:978
amrex::Real tpi_nonDim
Nondimensional number used by the perturbation formulation.
Definition: ERF_TurbPertStruct.H:956
amrex::Vector< amrex::Real > tpi_Hpb
Perturbation-box height for each AMR level [m].
Definition: ERF_TurbPertStruct.H:961

Referenced by calc_tpi_uniform_update(), and calc_tpi_update().

Here is the caller graph for this function:

◆ calc_tpi_meanMag()

void TurbulentPerturbation::calc_tpi_meanMag ( const int &  lev,
const amrex::Vector< int > &  update_box,
amrex::MultiFab &  mf_cons,
amrex::MultiFab &  mf_xvel,
amrex::MultiFab &  mf_yvel 
)
inline

Compute the mean horizontal velocity magnitude and direction of the refreshing perturbation boxes.

The velocities are summed over every grid box a perturbation box overlaps, each face once, and over all ranks, so every rank gets the same mean whatever the domain decomposition.

Parameters
levAMR level index.
update_boxNonzero for the perturbation boxes that refresh this step.
mf_consConserved state used for iteration layout.
mf_xvelX-velocity field.
mf_yvelY-velocity field.
838  {
839  const amrex::BoxArray& m_pb_ba = pb_ba[lev];
840  const int nboxes = m_pb_ba.size();
841 
842  // Per box: sum of u, number of u faces, sum of v, number of v faces
843  amrex::Vector<amrex::Real> sums(4*nboxes, zero);
844  for (amrex::MFIter mfi(mf_cons); mfi.isValid(); ++mfi) {
845  const amrex::Box& vbx = mfi.validbox(); // cell centered
846  const amrex::Array4<const amrex::Real>& xvel_arry = mf_xvel.const_array(mfi);
847  const amrex::Array4<const amrex::Real>& yvel_arry = mf_yvel.const_array(mfi);
848  for (const auto& isect : m_pb_ba.intersections(vbx)) {
849  const int boxIdx = isect.first;
850  if (!update_box[boxIdx]) { continue; }
851  const amrex::Box& pbx = m_pb_ba[boxIdx];
852 
853  // The faces of the overlap's cells; the high face only where the overlap ends
854  // at the perturbation box's high side, so a face shared by two grid boxes counts once
855  amrex::Box ubx_u = amrex::surroundingNodes(isect.second, 0);
856  if (isect.second.bigEnd(0) < pbx.bigEnd(0)) { ubx_u.growHi(0, -1); }
857  amrex::Box ubx_v = amrex::surroundingNodes(isect.second, 1);
858  if (isect.second.bigEnd(1) < pbx.bigEnd(1)) { ubx_v.growHi(1, -1); }
859 
860  sums[4*boxIdx ] += box_sum(xvel_arry, ubx_u);
861  sums[4*boxIdx+1] += static_cast<amrex::Real>(ubx_u.numPts());
862  sums[4*boxIdx+2] += box_sum(yvel_arry, ubx_v);
863  sums[4*boxIdx+3] += static_cast<amrex::Real>(ubx_v.numPts());
864  }
865  }
866  amrex::ParallelAllReduce::Sum(sums.data(), static_cast<int>(sums.size()),
867  amrex::ParallelContext::CommunicatorSub());
868 
869  for (int boxIdx = 0; boxIdx < nboxes; boxIdx++) {
870  if (!update_box[boxIdx]) { continue; }
871  const amrex::Real u_avg = (sums[4*boxIdx+1] > zero) ? sums[4*boxIdx ] / sums[4*boxIdx+1] : zero;
872  const amrex::Real v_avg = (sums[4*boxIdx+3] > zero) ? sums[4*boxIdx+2] / sums[4*boxIdx+3] : zero;
873  pb_mag[lev][boxIdx] = std::sqrt(u_avg*u_avg + v_avg*v_avg);
874  pb_dir[lev][boxIdx] = std::atan(std::abs(u_avg) / std::abs(v_avg+std::numeric_limits<amrex::Real>::epsilon()));
875  }
876  }
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
amrex::Vector< amrex::Vector< amrex::Real > > pb_dir
Mean velocity direction for each perturbation box.
Definition: ERF_TurbPertStruct.H:922
static amrex::Real box_sum(const amrex::Array4< const amrex::Real > &arr, const amrex::Box &bx)
Sum an array over a box.
Definition: ERF_TurbPertStruct.H:884

Referenced by calc_tpi_update().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ calc_tpi_uniform_update()

bool TurbulentPerturbation::calc_tpi_uniform_update ( int  lev,
double  time 
)
inline
443  {
445  auto& clock = uniform_clock[lev];
446  clock.apply = false;
447  if ((auto_mode && !auto_ready[lev]) || clock.interval <= 0.0 ||
448  time < clock.next_refresh || pb_ba[lev].empty()) { return false; }
449 
450  clock.next_refresh = time + clock.interval;
451  clock.apply = true;
452  pb_cell[lev].setVal(zero);
453  const auto m_ixtype = pb_cell[lev].ixType();
454  for (int b = 0; b < pb_ba[lev].size(); ++b) {
455  pb_interval[lev][b] = clock.interval / double(tpi_layers);
456  pb_local_etime[lev][b] = zero;
457  calc_tpi_amp(lev, b, pb_interval[lev][b]);
458  pseudoRandomPert(b, lev, m_ixtype);
459  ++pb_nupdate[lev][b];
460  }
461  return true;
462  }
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
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:658
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:707
amrex::Vector< amrex::Vector< int > > pb_nupdate
Number of times each perturbation box has been refreshed; keys its random numbers.
Definition: ERF_TurbPertStruct.H:980
amrex::Vector< amrex::Vector< double > > pb_interval
Perturbation update interval for each box [s].
Definition: ERF_TurbPertStruct.H:976
amrex::Vector< amrex::Vector< double > > pb_local_etime
Local elapsed time for each perturbation box [s].
Definition: ERF_TurbPertStruct.H:977
amrex::Vector< amrex::MultiFab > pb_cell
Per-cell perturbation amplitude storage.
Definition: ERF_TurbPertStruct.H:926
Here is the call graph for this function:

◆ calc_tpi_update()

void TurbulentPerturbation::calc_tpi_update ( const int  lev,
const double  dt,
amrex::MultiFab &  mf_xvel,
amrex::MultiFab &  mf_yvel,
amrex::MultiFab &  mf_cons 
)
inline

Update perturbation amplitudes and intervals when each box is ready.

Parameters
levAMR level index.
dtTime step for elapsed-time accumulation.
mf_xvelX-velocity field used to compute box mean velocities.
mf_yvelY-velocity field used to compute box mean velocities.
mf_consConserved state used for iteration layout.
539  {
540  auto m_ixtype = mf_cons.boxArray().ixType(); // safety step
541  if (pt_type[lev] == 3) { m_ixtype = amrex::IndexType(amrex::IntVect(0,0,1)); }
542 
543  const int nboxes = pb_ba[lev].size();
544  if (nboxes == 0 || (auto_mode && !auto_ready[lev])) { return; }
545 
546  // Which boxes refresh this step. pb_interval and pb_local_etime hold the same values on
547  // every rank, so every rank makes the same choice.
548  amrex::Vector<int> update_box(nboxes, 0);
549  for (int boxIdx = 0; boxIdx < nboxes; boxIdx++) {
550  if (pt_type[lev] == 2 || pt_type[lev] == 3) {
551  // for CPM, perturbation boxes/cells refresh after boxes have advected box width/length * num_layers (advective time scale)
552  update_box[boxIdx] = ((pb_local_etime[lev][boxIdx] >= pb_interval[lev][boxIdx]*tpi_layers) && pb_local_etime[lev][boxIdx] != -one);
553  } else {
554  update_box[boxIdx] = ( pb_local_etime[lev][boxIdx] >= pb_interval[lev][boxIdx] && pb_local_etime[lev][boxIdx] != -one );
555  }
556  }
557 
558  // Compute mean velocity of each refreshing perturbation box
559  if (!auto_mode) { calc_tpi_meanMag(lev, update_box, mf_cons, mf_xvel, mf_yvel); }
560 
561  for (int boxIdx = 0; boxIdx < nboxes; boxIdx++) {
562  if ( update_box[boxIdx] ) {
563 
564  if (pb_mag[lev][boxIdx] !=zero) {
565  double interval = 0.0;
566  if (pt_type[lev] == 2 || pt_type[lev] == 3) {
567  // Wind direction correction for angled wind
568  amrex::Real wind_direction = pb_dir[lev][boxIdx];
569  if (wind_direction > PI / 4) { wind_direction = PI / 2 - wind_direction; }
570  // CPM only cares about the side length of a box, min call maintains flexibility.
571  interval = one / std::cos(wind_direction) * std::min(tpi_Lpb[lev], tpi_Wpb[lev]) / pb_mag[lev][boxIdx];
572  } else {
573  interval = tpi_lref[lev] / pb_mag[lev][boxIdx];
574  }
575  // 10% variation
576  const amrex::Real r = erf_hash_rng::hash_uniform(boxIdx, pb_nupdate[lev][boxIdx], 0,
578  pb_interval[lev][boxIdx] = (amrex::Real(0.9) + amrex::Real(0.2)*r) * interval;
579 
580  // Reset local elapsed time
581  pb_local_etime[lev][boxIdx] = zero;
582  } else {
583  // No mean wind through this box, so no advective time scale: it is not refreshed again
584  pb_local_etime[lev][boxIdx] = -one;
585  }
586 
587  // Trigger amplitude calculation per perturbation box
588  calc_tpi_amp(lev, boxIdx, pb_interval[lev][boxIdx]);
589 
590  // Trigger random amplitude storage per cell within perturbation box
591  pseudoRandomPert(boxIdx, lev, m_ixtype);
592 
593  pb_nupdate[lev][boxIdx]++;
594 
595  } else {
596  // set perturbation amplitudes to 0 for CPM. A little inefficient but leverages as much as existing code as possible.
597  if (pt_type[lev] == 2 || pt_type[lev] == 3) { zero_amp(boxIdx, lev, m_ixtype); }
598 
599  // Increase by timestep of level 0
600  if (pb_local_etime[lev][boxIdx] != -one) { pb_local_etime[lev][boxIdx] += dt; }
601  } // if
602  } // for
603 
604  if (pt_type[lev] < 2) { // box perturbation method only
605  netZeroBuoyant(lev);
606  }
607  }
constexpr amrex::Real PI
Definition: ERF_NumericalConstants.H:46
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real hash_uniform(int i, int j, int k, int comp, int lev, std::uint64_t seed) noexcept
Definition: ERF_HashRNG.H:68
amrex::Vector< amrex::Real > tpi_Wpb
Perturbation-box width for each AMR level [m].
Definition: ERF_TurbPertStruct.H:963
void netZeroBuoyant(const int &lev)
Remove the mean of the box-perturbation field, so the net buoyant forcing is zero.
Definition: ERF_TurbPertStruct.H:773
static constexpr int hash_comp_interval
Hash salt of the refresh-interval variation.
Definition: ERF_TurbPertStruct.H:986
amrex::Vector< amrex::Real > tpi_Lpb
Perturbation-box length for each AMR level [m].
Definition: ERF_TurbPertStruct.H:962
void calc_tpi_meanMag(const int &lev, const amrex::Vector< int > &update_box, amrex::MultiFab &mf_cons, amrex::MultiFab &mf_xvel, amrex::MultiFab &mf_yvel)
Compute the mean horizontal velocity magnitude and direction of the refreshing perturbation boxes.
Definition: ERF_TurbPertStruct.H:833
amrex::Vector< amrex::Real > tpi_lref
Perturbation-box reference length for each AMR level [m].
Definition: ERF_TurbPertStruct.H:964
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:747
std::uint64_t tpi_seed
Seed of the hashed random numbers (1024 with erf.fix_random_seed, else erf.random_seed,...
Definition: ERF_TurbPertStruct.H:983
Here is the call graph for this function:

◆ configure_automatic()

void TurbulentPerturbation::configure_automatic ( int  lev,
double  time,
double  period,
const amrex::Vector< AutomaticPerturbationFace > &  faces,
const amrex::Geometry &  geom,
const amrex::MultiFab *  z_cc,
const amrex::MultiFab *  z_nd,
const amrex::MultiFab *  z_ground 
)
inline
472  {
473  auto_mask[lev].setVal(zero);
474  pb_cell[lev].setVal(zero);
475  pb_mag[lev].assign(pb_ba[lev].size(), zero);
476  pb_Ug[lev].assign(pb_ba[lev].size(), zero);
477  pb_dir[lev].assign(pb_ba[lev].size(), zero);
478  const bool on_w_faces = (pt_type[lev] == 3);
479  const bool terrain = (z_cc != nullptr);
480  AMREX_ALWAYS_ASSERT(!terrain || (z_nd && z_ground));
481  const int klo = geom.Domain().smallEnd(2);
482  const auto dx = geom.CellSizeArray();
483  const auto problo = geom.ProbLoArray();
484  for (const auto& face : faces) {
485  if (!face.inflow()) { continue; }
486  const amrex::Real speed = std::hypot(face.u, face.v);
487  for (const auto& isect : pb_ba[lev].intersections(face.region)) {
488  const int b = isect.first;
489  pb_Ug[lev][b] = std::max(pb_Ug[lev][b], face.Ug);
490  if (speed > pb_mag[lev][b]) {
491  pb_mag[lev][b] = speed;
492  pb_dir[lev][b] = std::atan2(std::abs(face.u), std::abs(face.v));
493  }
494  }
495  const amrex::Real height = face.height;
496  const amrex::Box region = amrex::convert(face.region, auto_mask[lev].ixType());
497  for (amrex::MFIter mfi(auto_mask[lev]); mfi.isValid(); ++mfi) {
498  const amrex::Box bx = mfi.validbox() & region;
499  if (!bx.ok()) { continue; }
500  const auto mask = auto_mask[lev].array(mfi);
501  const auto cc = terrain ? z_cc->const_array(mfi) : amrex::Array4<const amrex::Real>{};
502  const auto nd = terrain ? z_nd->const_array(mfi) : amrex::Array4<const amrex::Real>{};
503  const auto ground = terrain ? z_ground->const_array(mfi) : amrex::Array4<const amrex::Real>{};
504  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
505  amrex::Real z = problo[2] + (k-klo + (on_w_faces ? zero : myhalf))*dx[2];
506  amrex::Real zs = problo[2];
507  if (terrain) {
508  zs = fourth*(ground(i,j,klo) + ground(i+1,j,klo) +
509  ground(i,j+1,klo) + ground(i+1,j+1,klo));
510  z = on_w_faces ? fourth*(nd(i,j,k) + nd(i+1,j,k) +
511  nd(i,j+1,k) + nd(i+1,j+1,k))
512  : cc(i,j,k);
513  }
514  if (z > zs && z-zs <= height) { mask(i,j,k) = one; }
515  });
516  }
517  }
518  // Reconsider even boxes that previously had calm winds. They must be able
519  // to start again after a later parent update changes the inflow.
520  pb_interval[lev].assign(pb_ba[lev].size(), -one);
521  pb_local_etime[lev].assign(pb_ba[lev].size(), zero);
522  auto_ready[lev] = 1;
523  auto_next_update[lev] = time + period;
524  }
auto problo
Definition: ERF_InitCustomPertVels_ABL.H:36
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
const int klo
Definition: ERF_InitCustomPert_KE.H:9
Real height
Definition: ERF_InitCustomPert_SquallLine.H:37
constexpr amrex::Real fourth
Definition: ERF_NumericalConstants.H:42
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:41
amrex::Vector< amrex::MultiFab > auto_mask
Definition: ERF_TurbPertStruct.H:943
Here is the call graph for this function:

◆ debug()

void TurbulentPerturbation::debug ( double  )
inline

Write perturbation debug information when debug output is enabled.

900  {
901  /*
902  amrex::PrintToFile("BoxPerturbationOutput") << "#################### PB output at time = "
903  << time << " ####################\n";
904  amrex::PrintToFile("BoxPerturbationOutput") << " Using type: " << pt_type << "\n";
905  amrex::PrintToFile("BoxPerturbationOutput") << " Net: " << tpi_net_buoyant << " Adjust : " << tpi_pert_adjust << "\n";
906  for (int i = 0; i < pb_mag.size(); i++) {
907  amrex::PrintToFile("BoxPerturbationOutput") << "[" << i
908  << "] pb_Umag=" << pb_mag[i]
909  << " | pb_interval=" << pb_interval[i]
910  << " (" << pb_local_etime[i]
911  << ") | pb_amp=" << pb_amp[i] << "\n";
912  }
913  amrex::PrintToFile("BoxPerturbationOutput") << "\n";
914  */
915  }

◆ init_tpi()

void TurbulentPerturbation::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,
const bool  automatic = false 
)
inline

Initialize turbulent perturbation regions and per-box storage.

Parameters
levAMR level index.
subdomains_levRectangular level subdomains used to define perturbation regions.
dxCell spacing for this level.
baState BoxArray used for perturbation-cell storage.
dmDistributionMapping for perturbation-cell storage.
ngrow_stateNumber of ghost cells for perturbation-cell storage.
pp_prefixParmParse prefix for the ERF input namespace.
refRatioRefinement ratios between AMR levels.
max_levelMaximum AMR level configured for the run.
automaticSelect runtime inflow/height masks driven by the parent.
102  {
103  ref_ratio = refRatio;
104  auto_mode = automatic;
105 
106  amrex::ParmParse pp(pp_prefix);
107 
108  uniform_mode = true;
109  pp.queryAdd("perturbation_uniform_refresh", uniform_mode);
111  pp.queryAdd("perturbation_refresh_height", uniform_height);
113  pp.queryAdd("perturbation_refresh_wind_period", uniform_wind_period);
114  if (uniform_refresh_enabled(lev) &&
115  (!(uniform_height > zero) || !std::isfinite(uniform_height) ||
116  !(uniform_wind_period > zero) || !std::isfinite(uniform_wind_period))) {
117  amrex::Abort("Uniform CPM refresh requires positive, finite "
118  "perturbation_refresh_height and perturbation_refresh_wind_period");
119  }
120 
121  // Reading inputs, and placing assertion for the perturbation inflow to work
122  pp.getarr("perturbation_box_dims",tpi_boxDim);
123  if (auto_mode) {
124  tpi_direction = {1, 1, 0, 1, 1, 0};
125  } else {
126  pp.getarr("perturbation_direction",tpi_direction);
127  }
128  if (tpi_boxDim.size() != 3) {
129  amrex::Abort("perturbation_box_dims must have 3 entries (Nx Ny Nz)");
130  }
131  if (tpi_direction.size() != 6) {
132  amrex::Abort("perturbation_direction must have 6 entries (west south bottom east north top)");
133  }
134  pp.get("perturbation_layers",tpi_layers);
135  pp.get("perturbation_offset",tpi_offset);
136 
137  tpi_nonDim = zero;
138  pp.queryAdd("perturbation_nondimensional",tpi_nonDim);
139 
140  tpi_Tinf = amrex::Real(300.);
141  pp.queryAdd("perturbation_T_infinity",tpi_Tinf);
142 
143  tpi_Ti = zero;
144  pp.queryAdd("perturbation_T_intensity",tpi_Ti);
145 
146  input_Ug = zero;
147  pp.queryAdd("perturbation_Ug",input_Ug);
148 
149  input_w_amp = zero;
150  pp.queryAdd("perturbation_w_amp",input_w_amp);
151 
152  // Seed of the perturbation's random numbers, read the way erf.deterministic_ic_pert reads
153  // it. The numbers are hashed from the global cell index (or the perturbation-box index) and
154  // the box's refresh count, so they do not depend on the domain decomposition (#4317).
155  {
156  int fix_random_seed = 0;
157  long random_seed = -1;
158  amrex::ParmParse pp_erf("erf");
159  pp_erf.query("fix_random_seed", fix_random_seed);
160  pp_erf.query("random_seed", random_seed);
161  tpi_seed = fix_random_seed ? std::uint64_t(1024)
162  : ((random_seed >= 0) ? static_cast<std::uint64_t>(random_seed) : std::uint64_t(0));
163  }
164 
165  // Negative sentinels rather than the queryAdd return values: k = 0 is a legal
166  // bound, so the natural default cannot double as "unset", and the return value
167  // only reports whether the key existed before the call. init_tpi runs once per
168  // level, so testing the return would make every level above 0 see "specified"
169  // with the value level 0 planted, skipping the valid_box_lo/hi defaults below.
170  perturbation_klo = -1;
171  pp.queryAdd("perturbation_klo", perturbation_klo);
172  const bool have_klo = (perturbation_klo >= 0);
173 
174  perturbation_khi = -1;
175  pp.queryAdd("perturbation_khi", perturbation_khi);
176  const bool have_khi = (perturbation_khi >= 0);
177 
178  // Check variables message
179  if (tpi_offset < 0) { amrex::Abort("Please provide a valid inflow cell offset value for perturbation region (ie. 0-5)"); }
180  if (tpi_layers < 0) { amrex::Abort("Please provide a valid perturbation layer value (ie. 3-5)"); }
181  if ((auto_mode || uniform_refresh_enabled(lev)) && tpi_layers == 0) {
182  amrex::Abort("Automatic perturbations and uniform CPM refresh require perturbation_layers > 0");
183  }
184  if (tpi_nonDim < zero) { amrex::Abort("Please provide a valid nondimensional number (ie. Ri = amrex::Real(0.042))"); }
185  for (int i = 0; i < tpi_boxDim.size(); i++) {
186  if (tpi_boxDim[i] < 3) { amrex::Abort("Please provide valid dimensions for perturbation boxes."); }
187  }
188  if (input_Ug < zero) { amrex::Abort("Please provide a valid geostrophic wind speed (ie. Ug = amrex::Real(10.0) m/s)"); }
189  if (tpi_Tinf < zero) { amrex::Abort("Please provide a valid ambient temperature value (ie. T_0 = T_infty)"); }
190  if (tpi_Ti < zero) { amrex::Abort("Please provide a valid temperature intensity value (ie. 0-one)"); }
191 
192  // Create a temporary box list to accumulate all the perturbation regions after box modification
193  amrex::BoxList tmp_bl;
194 
195  // boxSize for individual boxes
196  amrex::IntVect boxSize(tpi_boxDim[0],tpi_boxDim[1],tpi_boxDim[2]);
197 
198  if (tpi_direction[2] || tpi_direction[5]) { amrex::Abort("Currently not supporting z-direction flow perturbation"); }
199 
200  for (int isub = 0; isub < subdomains_lev.size(); ++isub) {
201  const amrex::BoxArray& subdomain = subdomains_lev[isub];
202  amrex::Box subdomain_box(subdomain.minimalBox());
203 
204  if (subdomain_box.numPts() != subdomain.numPts()) {
205  amrex::Abort("Turbulent perturbations require rectangular subdomains. "
206  "Level " + std::to_string(lev) +
207  ", subdomain " + std::to_string(isub) +
208  " is not a rectangular region fully covered by grids.");
209  }
210 
211  const amrex::IntVect& valid_box_lo = subdomain_box.smallEnd();
212  const amrex::IntVect& valid_box_hi = subdomain_box.bigEnd();
213 
214  if (auto_mode) {
215  // Allocate all possible edges once. A mask chooses inflow and height
216  // at runtime, including corners when either adjoining edge is inflow.
217  for (int face = 0; face < 4; ++face) {
218  const amrex::Box region = automatic_face_box(subdomain_box, face);
219  if (region.ok()) { tmp_bl.push_back(region); }
220  }
221  continue;
222  }
223 
224  // default perturbation region to be the entire z extent if the user does not specify bounds
225  if (!have_klo) { perturbation_klo = valid_box_lo[2]; }
226  if (!have_khi) { perturbation_khi = valid_box_hi[2]; }
227 
228  // Creating perturbation regions and initializing with generic size.
229  amrex::Box lo_x_bx(amrex::IntVect(0), amrex::IntVect(1), amrex::IntVect(0));
230  amrex::Box hi_x_bx(amrex::IntVect(0), amrex::IntVect(1), amrex::IntVect(0));
231  amrex::Box lo_y_bx(amrex::IntVect(0), amrex::IntVect(1), amrex::IntVect(0));
232  amrex::Box hi_y_bx(amrex::IntVect(0), amrex::IntVect(1), amrex::IntVect(0));
233 
234  // Starting logic to set the size of the perturbation region(s)
235  //amrex::PrintToFile("BoxPerturbationOutput") << "Setting perturbation region in:";
236  // ***** X-direction perturbation *****
237  if (tpi_direction[0]) { // West
238  lo_x_bx.setSmall(amrex::IntVect(valid_box_lo[0]+tpi_offset, valid_box_lo[1]+tpi_direction[1]*tpi_offset, perturbation_klo));
239  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));
240  amrex::PrintToFile("BoxPerturbationOutput") << " West face";
241  }
242 
243  if (tpi_direction[3]) { // East
244  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));
245  hi_x_bx.setBig(amrex::IntVect(valid_box_hi[0]-tpi_offset, valid_box_hi[1]-(tpi_direction[4]*tpi_offset), perturbation_khi));
246  amrex::PrintToFile("BoxPerturbationOutput") << " East face";
247  }
248 
249  // ***** Y-direction Perturbation *****
250  if (tpi_direction[1]) { // North
251  lo_y_bx.setSmall(amrex::IntVect(valid_box_lo[0]+tpi_direction[0]*tpi_offset, valid_box_lo[1]+tpi_offset, perturbation_klo));
252  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));
253  amrex::PrintToFile("BoxPerturbationOutput") << " North face";
254  }
255 
256  if (tpi_direction[4]) { // South
257  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));
258  hi_y_bx.setBig(amrex::IntVect(valid_box_hi[0]-tpi_direction[3]*tpi_offset, valid_box_hi[1]-tpi_offset, perturbation_khi));
259  amrex::PrintToFile("BoxPerturbationOutput") << " South face";
260  }
261 
262  // Performing box union for intersecting perturbation regions to avoid overlapping sections (double counting at corners)
263  if (tpi_direction[0] && tpi_direction[1]) { // Reshaping South smallEnd
264  amrex::Box lo_x_lo_y_u = lo_x_bx & lo_y_bx;
265  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)));
266  }
267 
268  if (tpi_direction[3] && tpi_direction[1]) { // Reshaping South bigEnd
269  amrex::Box hi_x_lo_y_u = hi_x_bx & lo_y_bx;
270  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)));
271  }
272 
273  if (tpi_direction[0] && tpi_direction[4]) { // Reshaping North smallEnd
274  amrex::Box lo_x_hi_y_u = lo_x_bx & hi_y_bx;
275  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)));
276  }
277 
278  if (tpi_direction[3] && tpi_direction[4]) { // Reshaping North bigEnd
279  amrex::Box hi_x_hi_y_u = hi_x_bx & hi_y_bx;
280  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)));
281  }
282 
283  // Creating structure box array for conserved quantity
284  if (tpi_direction[0]) { tmp_bl.push_back(lo_x_bx); }
285  if (tpi_direction[1]) { tmp_bl.push_back(lo_y_bx); }
286  if (tpi_direction[3]) { tmp_bl.push_back(hi_x_bx); }
287  if (tpi_direction[4]) { tmp_bl.push_back(hi_y_bx); }
288  }
289 
290  //amrex::PrintToFile("BoxPerturbationOutput") << "\nBoxList: " << tmp_bl << "\n";
291  amrex::BoxArray tmp_ba(tmp_bl);
292  if (auto_mode) { tmp_ba.removeOverlap(); }
293  tmp_ba.maxSize(boxSize);
294  if (auto_mode && tmp_ba.empty()) {
295  amrex::Abort("Automatic perturbation strips are empty; reduce perturbation_offset");
296  }
297 
298  const int num_levels = max_level + 1;
299  if (pb_ba.size() < num_levels) {
300  pb_ba.resize(num_levels);
301  pb_mag.resize(num_levels);
302  pb_Ug.resize(num_levels);
303  pb_dir.resize(num_levels);
304  pb_netZero.resize(num_levels);
305  pb_interval.resize(num_levels);
306  pb_local_etime.resize(num_levels);
307  pb_amp.resize(num_levels);
308  pb_nupdate.resize(num_levels);
309  pb_cell.resize(num_levels);
310  tpi_Lpb.resize(num_levels);
311  tpi_Wpb.resize(num_levels);
312  tpi_Hpb.resize(num_levels);
313  tpi_lref.resize(num_levels);
314  auto_mask.resize(num_levels);
315  auto_next_update.resize(num_levels, -1.0);
316  auto_ready.resize(num_levels, 0);
317  uniform_clock.resize(num_levels);
318  }
319  uniform_clock[lev] = UniformClock{};
320 
321  pb_ba[lev] = tmp_ba;
322 
323  // Initializing mean magnitude and direction vectors
324  pb_mag[lev].resize(pb_ba[lev].size(), zero);
325  pb_Ug[lev].resize(pb_ba[lev].size(), zero);
326  pb_dir[lev].resize(pb_ba[lev].size(), zero);
327  pb_netZero[lev].resize(pb_ba[lev].size(), zero);
328 
329  // Set size of vector and initialize
330  pb_interval[lev].resize(pb_ba[lev].size(), -one);
331  pb_local_etime[lev].resize(pb_ba[lev].size(), zero);
332  pb_amp[lev].resize(pb_ba[lev].size(), zero);
333  pb_nupdate[lev].resize(pb_ba[lev].size(), 0);
334 
335  // Creating data array for perturbation amplitude storage
336  if (pt_type[lev] == 3) { // CPM_W converts to the k-face ba.
337  pb_cell[lev].define(convert(ba, amrex::IntVect(0,0,1)), dm, 1, ngrow_state);
338  } else {
339  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...
340  }
341  pb_cell[lev].setVal(0.);
342  if (auto_mode) {
343  auto_mask[lev].define(pb_cell[lev].boxArray(), dm, 1, 0);
344  auto_mask[lev].setVal(zero);
345  auto_next_update[lev] = -1.0;
346  auto_ready[lev] = 0;
347  // Regridding can leave vectors at the same size; resize alone would retain
348  // clocks/amplitudes belonging to a different set of boxes.
349  pb_interval[lev].assign(pb_ba[lev].size(), -one);
350  pb_local_etime[lev].assign(pb_ba[lev].size(), zero);
351  pb_nupdate[lev].assign(pb_ba[lev].size(), 0);
352  }
353 
354  // Computing perturbation reference length
355  tpi_Lpb[lev] = tpi_boxDim[0]*dx[0];
356  tpi_Wpb[lev] = tpi_boxDim[1]*dx[1];
357  tpi_Hpb[lev] = tpi_boxDim[2]*dx[2];
358  tpi_lref[lev] = std::sqrt(tpi_Lpb[lev]*tpi_Lpb[lev] + tpi_Wpb[lev]*tpi_Wpb[lev]);
359 
362 
363  /*
364  // Function check point message
365  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_box_dims: "
366  << tpi_boxDim[0] << " "
367  << tpi_boxDim[1] << " "
368  << tpi_boxDim[2] << "\n";
369  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_direction: "
370  << tpi_direction[0] << " "
371  << tpi_direction[1] << " "
372  << tpi_direction[2] << "\n\n";
373  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_layers: " << tpi_layers << "\n";
374  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_offset: " << tpi_offset << "\n\n";
375  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_nondimensional: " << tpi_nonDim << "\n";
376  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_T_infinity: " << tpi_Tinf << "\n";
377  amrex::PrintToFile("BoxPerturbationOutput") << "perturbation_T_intensity: " << tpi_Ti << "\n";
378  amrex::PrintToFile("BoxPerturbationOutput") << "Reference length per box = " << tpi_lref[lev] << "\n\n";
379  amrex::PrintToFile("BoxPerturbationOutput") << "Turbulent perturbation BoxArray:\n" << pb_ba[lev] << "\n";
380  */
381  }
long random_seed
Definition: ERF_InitCustomPert_ABL.H:6
int fix_random_seed
Definition: ERF_InitCustomPert_ABL.H:5
ParmParse pp("prob")
integer, private isub
Definition: ERF_module_mp_morr_two_moment.F90:164
amrex::Box automatic_face_box(const amrex::Box &subdomain, int face) const
Definition: ERF_TurbPertStruct.H:385
amrex::Real tpi_pert_adjust
Per-cell perturbation adjustment used for net-zero buoyancy.
Definition: ERF_TurbPertStruct.H:967
amrex::Vector< int > tpi_direction
Boundary directions where perturbations are applied.
Definition: ERF_TurbPertStruct.H:953
int perturbation_klo
Lower vertical index bound for perturbations.
Definition: ERF_TurbPertStruct.H:972
amrex::Vector< amrex::Vector< amrex::Real > > pb_netZero
Per-box storage used for net-zero buoyancy calculation.
Definition: ERF_TurbPertStruct.H:979
amrex::Real uniform_wind_period
Definition: ERF_TurbPertStruct.H:938
amrex::Real uniform_height
Definition: ERF_TurbPertStruct.H:937
amrex::Real tpi_net_buoyant
Accumulated net buoyant perturbation used for correction.
Definition: ERF_TurbPertStruct.H:966
int perturbation_khi
Upper vertical index bound for perturbations.
Definition: ERF_TurbPertStruct.H:973
bool uniform_mode
Definition: ERF_TurbPertStruct.H:936
Here is the call graph for this function:

◆ init_tpi_type()

void TurbulentPerturbation::init_tpi_type ( const int  lev,
const PerturbationType &  pert_type,
const int  max_level 
)
inline

Store the integer perturbation-type selector for one AMR level.

Parameters
levAMR level index.
pert_typePerturbation type selected for this level.
max_levelMaximum AMR level configured for the run.
60  {
61  if (pt_type.size() < max_level + 1) {
62  pt_type.resize(max_level + 1, -1);
63  }
64 
65  if (pert_type == PerturbationType::Source) {
66  pt_type[lev] = 0;
67  } else if (pert_type == PerturbationType::Direct) {
68  pt_type[lev] = 1;
69  } else if (pert_type == PerturbationType::CPM) {
70  pt_type[lev] = 2;
71  } else if (pert_type == PerturbationType::CPM_W) {
72  pt_type[lev] = 3;
73  } else {
74  pt_type[lev] = -1;
75  }
76  }

◆ netZeroBuoyant()

void TurbulentPerturbation::netZeroBuoyant ( const int &  lev)
inline

Remove the mean of the box-perturbation field, so the net buoyant forcing is zero.

Each perturbation box's mean is summed over every grid box it overlaps and over all ranks, so the correction does not depend on the domain decomposition.

Parameters
levAMR level index.
774  {
775  const int nboxes = pb_ba[lev].size();
776  if (nboxes == 0) { return; }
777 
778  // Sum and cell count of each perturbation box, then one reduction over the ranks
779  amrex::Vector<amrex::Real> sums(2*nboxes, zero);
780  for (amrex::MFIter mfi(pb_cell[lev]); mfi.isValid(); ++mfi) {
781  const amrex::Box& vbx = mfi.validbox();
782  const amrex::Array4<const amrex::Real>& pert_cell = pb_cell[lev].const_array(mfi);
783  for (const auto& isect : pb_ba[lev].intersections(vbx)) {
784  sums[2*isect.first ] += box_sum(pert_cell, isect.second);
785  sums[2*isect.first+1] += auto_mode
786  ? box_sum(auto_mask[lev].const_array(mfi), isect.second)
787  : static_cast<amrex::Real>(isect.second.numPts());
788  }
789  }
790  amrex::ParallelAllReduce::Sum(sums.data(), static_cast<int>(sums.size()),
791  amrex::ParallelContext::CommunicatorSub());
792 
793  // Normalizing the adjustment based on how many boxes there are; each box's
794  // value is already normalized by the number of cells within it
796  for (int boxIdx = 0; boxIdx < nboxes; boxIdx++) {
797  pb_netZero[lev][boxIdx] = (sums[2*boxIdx+1] > zero) ? sums[2*boxIdx] / sums[2*boxIdx+1] : zero;
798  tpi_net_buoyant += pb_netZero[lev][boxIdx];
799  }
800  tpi_pert_adjust = tpi_net_buoyant / static_cast<amrex::Real>(nboxes);
801  if (auto_mode) {
802  amrex::Real sum = zero, count = zero;
803  for (int b = 0; b < nboxes; ++b) { sum += sums[2*b]; count += sums[2*b+1]; }
804  tpi_pert_adjust = count > zero ? sum / count : zero;
805  }
806 
807  const amrex::Real adjust = tpi_pert_adjust;
808  for (amrex::MFIter mfi(pb_cell[lev]); mfi.isValid(); ++mfi) {
809  const amrex::Box& vbx = mfi.validbox();
810  const amrex::Array4<amrex::Real>& pert_cell = pb_cell[lev].array(mfi);
811  const bool use_mask = auto_mode;
812  const auto mask = use_mask ? auto_mask[lev].const_array(mfi)
813  : amrex::Array4<const amrex::Real>{};
814  for (const auto& isect : pb_ba[lev].intersections(vbx)) {
815  ParallelFor(isect.second, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
816  if (!use_mask || mask(i,j,k) > zero) { pert_cell(i,j,k) -= adjust; }
817  });
818  }
819  }
820  }
Definition: ERF_ConsoleIO.cpp:17

Referenced by calc_tpi_update().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ pseudoRandomPert()

void TurbulentPerturbation::pseudoRandomPert ( const int &  boxIdx,
const int &  lev,
const amrex::IndexType &  m_ixtype 
)
inline

Assign pseudo-random perturbations to cells in one perturbation box.

Parameters
boxIdxPerturbation-box index on the level.
levAMR level index.
m_ixtypeIndex type used to align perturbation storage.
710  {
711  for (amrex::MFIter mfi(pb_cell[lev],TileNoZ()); mfi.isValid(); ++mfi) {
712  amrex::Box vbx = mfi.tilebox();
713  amrex::Box pbx = amrex::convert(pb_ba[lev][boxIdx], m_ixtype);
714  if (pt_type[lev] == 3) { pbx.setBig(2, pbx.bigEnd(2) - 1); } // prevent double counting after converting to k-faces
715  amrex::Box ubx = pbx & vbx;
716  if (ubx.ok()) {
717  amrex::Real amp_copy = pb_amp[lev][boxIdx];
718  const amrex::Array4<amrex::Real>& pert_cell = pb_cell[lev].array(mfi);
719  const bool use_mask = auto_mode;
720  const auto mask = use_mask ? auto_mask[lev].const_array(mfi)
721  : amrex::Array4<const amrex::Real>{};
722 
723  if (pt_type[lev] == 2 || pt_type[lev] == 3) { // CPM: one value per perturbation box
724  const amrex::Real rand_number_const = erf_hash_rng::hash_symmetric(
725  boxIdx, pb_nupdate[lev][boxIdx], 0, hash_comp_cpm, lev, tpi_seed);
726  ParallelFor(ubx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
727  pert_cell(i,j,k) = (use_mask && mask(i,j,k) == zero) ? zero : rand_number_const * amp_copy;
728  });
729  } else { // one value per cell, keyed by its global index and the box's refresh count
730  const std::uint64_t cell_seed = erf_hash_rng::splitmix64(
731  tpi_seed ^ erf_hash_rng::splitmix64(static_cast<std::uint64_t>(pb_nupdate[lev][boxIdx])));
732  ParallelFor(ubx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
733  pert_cell(i,j,k) = (use_mask && mask(i,j,k) == zero) ? zero
734  : erf_hash_rng::hash_symmetric(i, j, k, hash_comp_cell, lev, cell_seed) * amp_copy;
735  });
736  }
737  }
738  }
739  }
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real hash_symmetric(int i, int j, int k, int comp, int lev, std::uint64_t seed) noexcept
Definition: ERF_HashRNG.H:91
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE std::uint64_t splitmix64(std::uint64_t x) noexcept
Definition: ERF_HashRNG.H:19
static constexpr int hash_comp_cpm
Hash salt of the per-box CPM values.
Definition: ERF_TurbPertStruct.H:985
static constexpr int hash_comp_cell
Hash salt of the per-cell perturbation values.
Definition: ERF_TurbPertStruct.H:984

Referenced by calc_tpi_uniform_update(), and calc_tpi_update().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_uniform_advection()

void TurbulentPerturbation::set_uniform_advection ( int  lev,
double  time,
amrex::Real  speed,
double  wind_period 
)
inline
426  {
428  AMREX_ALWAYS_ASSERT(speed >= zero && std::isfinite(speed));
429  auto& clock = uniform_clock[lev];
430  clock.interval = speed > zero
431  ? double(tpi_layers) * double(std::min(tpi_Lpb[lev], tpi_Wpb[lev])) / double(speed)
432  : 0.0;
433  clock.next_refresh = time;
434  clock.next_wind_update = time + wind_period;
435  clock.apply = false;
436  }
Here is the call graph for this function:

◆ uniform_refresh_enabled()

bool TurbulentPerturbation::uniform_refresh_enabled ( int  lev) const
inline
406  {
407  return uniform_mode && (pt_type[lev] == 2 || pt_type[lev] == 3);
408  }

Referenced by apply_tpi(), calc_tpi_uniform_update(), init_tpi(), and set_uniform_advection().

Here is the caller graph for this function:

◆ uniform_refresh_interval()

double TurbulentPerturbation::uniform_refresh_interval ( int  lev) const
inline
438 { return uniform_clock[lev].interval; }

◆ uniform_sampling_face()

bool TurbulentPerturbation::uniform_sampling_face ( int  face) const
inline
419  {
420  return tpi_direction[face < 2 ? face : face+1] != 0;
421  }

◆ uniform_sampling_height()

amrex::Real TurbulentPerturbation::uniform_sampling_height ( ) const
inline
415 { return uniform_height; }

◆ uniform_sampling_period()

amrex::Real TurbulentPerturbation::uniform_sampling_period ( ) const
inline
416 { return uniform_wind_period; }

◆ uniform_wind_update_due()

bool TurbulentPerturbation::uniform_wind_update_due ( int  lev,
double  time 
) const
inline
411  {
412  return time >= uniform_clock[lev].next_wind_update;
413  }

◆ zero_amp()

void TurbulentPerturbation::zero_amp ( const int &  boxIdx,
const int &  lev,
const amrex::IndexType &  m_ixtype 
)
inline

Reset CPM perturbation amplitudes in one perturbation box to zero.

Parameters
boxIdxPerturbation-box index on the level.
levAMR level index.
m_ixtypeIndex type used to align perturbation storage.
750  {
751 
752  for (amrex::MFIter mfi(pb_cell[lev],TileNoZ()); mfi.isValid(); ++mfi) {
753  amrex::Box vbx = mfi.tilebox();
754  amrex::Box pbx = amrex::convert(pb_ba[lev][boxIdx], m_ixtype);
755  if (pt_type[lev] == 3) { pbx.setBig(2, pbx.bigEnd(2) - 1); } // prevent double counting after converting to k-faces
756  amrex::Box ubx = pbx & vbx;
757  if (ubx.ok()) {
758  const amrex::Array4<amrex::Real>& pert_cell = pb_cell[lev].array(mfi);
759  ParallelFor(ubx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
760  pert_cell(i,j,k) = zero;
761  });
762  }
763  }
764  }

Referenced by calc_tpi_update().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ auto_mask

amrex::Vector<amrex::MultiFab> TurbulentPerturbation::auto_mask
private

◆ auto_mode

bool TurbulentPerturbation::auto_mode {false}
private

◆ auto_next_update

amrex::Vector<double> TurbulentPerturbation::auto_next_update
private

◆ auto_ready

amrex::Vector<int> TurbulentPerturbation::auto_ready
private

◆ hash_comp_cell

TurbulentPerturbation::hash_comp_cell = 0
staticconstexprprivate

Hash salt of the per-cell perturbation values.

Referenced by pseudoRandomPert().

◆ hash_comp_cpm

TurbulentPerturbation::hash_comp_cpm = 1
staticconstexprprivate

Hash salt of the per-box CPM values.

Referenced by pseudoRandomPert().

◆ hash_comp_interval

TurbulentPerturbation::hash_comp_interval = 2
staticconstexprprivate

Hash salt of the refresh-interval variation.

Referenced by calc_tpi_update().

◆ input_Ug

TurbulentPerturbation::input_Ug
private

Input geostrophic wind speed used to scale CPM perturbations.

Referenced by calc_tpi_amp(), and init_tpi().

◆ input_w_amp

TurbulentPerturbation::input_w_amp
private

Input vertical velocity perturbation amplitude.

Referenced by calc_tpi_amp(), and init_tpi().

◆ pb_amp

TurbulentPerturbation::pb_amp
private

Perturbation amplitude for each perturbation box.

Referenced by calc_tpi_amp(), init_tpi(), and pseudoRandomPert().

◆ pb_ba

TurbulentPerturbation::pb_ba

◆ pb_cell

TurbulentPerturbation::pb_cell

◆ pb_dir

TurbulentPerturbation::pb_dir

Mean velocity direction for each perturbation box.

Referenced by calc_tpi_meanMag(), calc_tpi_update(), configure_automatic(), and init_tpi().

◆ pb_interval

TurbulentPerturbation::pb_interval
private

Perturbation update interval for each box [s].

Referenced by calc_tpi_uniform_update(), calc_tpi_update(), configure_automatic(), and init_tpi().

◆ pb_local_etime

TurbulentPerturbation::pb_local_etime
private

Local elapsed time for each perturbation box [s].

Referenced by calc_tpi_uniform_update(), calc_tpi_update(), configure_automatic(), and init_tpi().

◆ pb_mag

TurbulentPerturbation::pb_mag

Mean velocity magnitude for each perturbation box [m/s].

Referenced by calc_tpi_amp(), calc_tpi_meanMag(), calc_tpi_update(), configure_automatic(), and init_tpi().

◆ pb_netZero

TurbulentPerturbation::pb_netZero
private

Per-box storage used for net-zero buoyancy calculation.

Referenced by init_tpi(), and netZeroBuoyant().

◆ pb_nupdate

TurbulentPerturbation::pb_nupdate
private

Number of times each perturbation box has been refreshed; keys its random numbers.

Referenced by calc_tpi_uniform_update(), calc_tpi_update(), init_tpi(), and pseudoRandomPert().

◆ pb_Ug

amrex::Vector<amrex::Vector<amrex::Real> > TurbulentPerturbation::pb_Ug
private

◆ perturbation_khi

TurbulentPerturbation::perturbation_khi
private

Upper vertical index bound for perturbations.

Referenced by init_tpi().

◆ perturbation_klo

TurbulentPerturbation::perturbation_klo
private

Lower vertical index bound for perturbations.

Referenced by init_tpi().

◆ pt_type

TurbulentPerturbation::pt_type

◆ ref_ratio

TurbulentPerturbation::ref_ratio
private

Refinement ratios used by multilevel perturbation scaling.

Referenced by calc_tpi_amp(), and init_tpi().

◆ tpi_boxDim

TurbulentPerturbation::tpi_boxDim
private

Dimensions of each perturbation box.

Referenced by automatic_face_box(), and init_tpi().

◆ tpi_direction

TurbulentPerturbation::tpi_direction
private

Boundary directions where perturbations are applied.

Referenced by init_tpi(), and uniform_sampling_face().

◆ tpi_Hpb

TurbulentPerturbation::tpi_Hpb
private

Perturbation-box height for each AMR level [m].

Referenced by calc_tpi_amp(), and init_tpi().

◆ tpi_layers

TurbulentPerturbation::tpi_layers
private

Number of layers of perturbation boxes.

Referenced by automatic_face_box(), calc_tpi_uniform_update(), calc_tpi_update(), init_tpi(), and set_uniform_advection().

◆ tpi_Lpb

TurbulentPerturbation::tpi_Lpb
private

Perturbation-box length for each AMR level [m].

Referenced by calc_tpi_update(), init_tpi(), and set_uniform_advection().

◆ tpi_lref

TurbulentPerturbation::tpi_lref
private

Perturbation-box reference length for each AMR level [m].

Referenced by calc_tpi_update(), and init_tpi().

◆ tpi_net_buoyant

TurbulentPerturbation::tpi_net_buoyant
private

Accumulated net buoyant perturbation used for correction.

Referenced by init_tpi(), and netZeroBuoyant().

◆ tpi_nonDim

TurbulentPerturbation::tpi_nonDim
private

Nondimensional number used by the perturbation formulation.

Referenced by calc_tpi_amp(), and init_tpi().

◆ tpi_offset

TurbulentPerturbation::tpi_offset
private

Cell offset for the start of the perturbation region.

Referenced by automatic_face_box(), and init_tpi().

◆ tpi_pert_adjust

TurbulentPerturbation::tpi_pert_adjust
private

Per-cell perturbation adjustment used for net-zero buoyancy.

Referenced by init_tpi(), and netZeroBuoyant().

◆ tpi_seed

TurbulentPerturbation::tpi_seed = 0
private

Seed of the hashed random numbers (1024 with erf.fix_random_seed, else erf.random_seed, else 0).

Referenced by calc_tpi_update(), init_tpi(), and pseudoRandomPert().

◆ tpi_Ti

TurbulentPerturbation::tpi_Ti
private

Temperature intensity used by the perturbation formulation.

Referenced by calc_tpi_amp(), and init_tpi().

◆ tpi_Tinf

TurbulentPerturbation::tpi_Tinf
private

Reference temperature used by the perturbation formulation [K].

Referenced by calc_tpi_amp(), and init_tpi().

◆ tpi_Wpb

TurbulentPerturbation::tpi_Wpb
private

Perturbation-box width for each AMR level [m].

Referenced by calc_tpi_update(), init_tpi(), and set_uniform_advection().

◆ uniform_clock

amrex::Vector<UniformClock> TurbulentPerturbation::uniform_clock
private

◆ uniform_height

amrex::Real TurbulentPerturbation::uniform_height {amrex::Real(20)}
private

◆ uniform_mode

bool TurbulentPerturbation::uniform_mode {true}
private

◆ uniform_wind_period

amrex::Real TurbulentPerturbation::uniform_wind_period {amrex::Real(600)}
private

The documentation for this struct was generated from the following file: