ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_InitTurbPert.cpp File Reference
#include <ERF.H>
#include <ERF_TerrainSurfaceSlab.H>
#include <ERF_PerturbationWind.H>
#include <AMReX_MultiFabUtil.H>
#include <ERF_TileNoZ.H>
#include <ERF_ProbCommon.H>
Include dependency graph for ERF_InitTurbPert.cpp:

Functions

PerturbationWindSample sample_perturbation_wind (const Box &region, Real height_agl, const Geometry &geometry, const MultiFab &state, const MultiFab &xvel, const MultiFab &yvel, const MultiFab *z_cc, const MultiFab *z_nd)
 

Function Documentation

◆ sample_perturbation_wind()

PerturbationWindSample sample_perturbation_wind ( const Box &  region,
Real  height_agl,
const Geometry &  geometry,
const MultiFab &  state,
const MultiFab &  xvel,
const MultiFab &  yvel,
const MultiFab *  z_cc,
const MultiFab *  z_nd 
)
35 {
36  // Keep only the edge strip, with the source grids' ownership. A vertical
37  // halo exchanges cell-centered winds/heights across stacked boxes without
38  // allocating a full-volume copy or depending on source ghost values.
39  BoxList boxes;
40  Vector<int> source_indices, owners;
41  const auto& source_ba = state.boxArray();
42  const auto& source_owners = state.DistributionMap().ProcessorMap();
43  for (int b = 0; b < source_ba.size(); ++b) {
44  const Box bx = source_ba[b] & region;
45  if (!bx.ok()) { continue; }
46  boxes.push_back(bx);
47  source_indices.push_back(b);
48  owners.push_back(source_owners[b]);
49  }
50  if (boxes.isEmpty()) { return {}; }
51  const BoxArray ba(boxes);
52  const DistributionMapping dm(owners);
53  // Components are u, v, cell-center height, bottom height, top height (AGL).
54  MultiFab samples(ba, dm, 5, IntVect(0,0,1));
55  samples.setVal(bogus_large_value);
56  const int klo = geometry.Domain().smallEnd(2);
57  const auto dx = geometry.CellSizeArray();
58  auto ground = perturbation_ground(z_nd, samples, geometry);
59  for (MFIter mfi(samples); mfi.isValid(); ++mfi) {
60  const int src = source_indices[mfi.index()];
61  const auto ua = xvel.const_array(src);
62  const auto va = yvel.const_array(src);
63  const auto cc = z_cc ? z_cc->const_array(src) : Array4<const Real>{};
64  const auto nd = z_nd ? z_nd->const_array(src) : Array4<const Real>{};
65  const auto ga = ground ? ground->const_array(mfi) : Array4<const Real>{};
66  const auto a = samples.array(mfi);
67  ParallelFor(mfi.validbox(), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
68  Real zlo = (k-klo)*dx[2];
69  Real zhi = zlo + dx[2];
70  Real z = myhalf*(zlo+zhi);
71  if (nd) {
72  const Real zs = fourth*(ga(i,j,klo) + ga(i+1,j,klo) +
73  ga(i,j+1,klo) + ga(i+1,j+1,klo));
74  zlo = fourth*(nd(i,j,k) + nd(i+1,j,k) +
75  nd(i,j+1,k) + nd(i+1,j+1,k)) - zs;
76  zhi = fourth*(nd(i,j,k+1) + nd(i+1,j,k+1) +
77  nd(i,j+1,k+1) + nd(i+1,j+1,k+1)) - zs;
78  z = cc ? cc(i,j,k) - zs : myhalf*(zlo+zhi);
79  }
80  a(i,j,k,0) = myhalf*(ua(i,j,k)+ua(i+1,j,k));
81  a(i,j,k,1) = myhalf*(va(i,j,k)+va(i,j+1,k));
82  a(i,j,k,2) = z;
83  a(i,j,k,3) = zlo;
84  a(i,j,k,4) = zhi;
85  });
86  }
87  samples.FillBoundary(geometry.periodicity());
88 
89  Real sums[3] = {zero, zero, zero};
90  for (MFIter mfi(samples); mfi.isValid(); ++mfi) {
91  const auto a = samples.const_array(mfi);
92  ReduceOps<ReduceOpSum, ReduceOpSum, ReduceOpSum> op;
93  ReduceData<Real, Real, Real> data(op);
94  using Tuple = typename decltype(data)::Type;
95  op.eval(mfi.validbox(), data, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept -> Tuple {
96  const Real z = a(i,j,k,2);
97  const Real znext = a(i,j,k+1,2);
98  Real u = a(i,j,k,0);
99  Real v = a(i,j,k,1);
100  if (znext != bogus_large_value && znext > z && height_agl >= z && height_agl < znext) {
101  const Real weight = (height_agl-z)/(znext-z);
102  u += weight*(a(i,j,k+1,0)-u);
103  v += weight*(a(i,j,k+1,1)-v);
104  } else if ((k == klo && height_agl >= a(i,j,k,3) && height_agl < z) ||
105  (znext == bogus_large_value && height_agl >= z && height_agl <= a(i,j,k,4))) {
106  // Constant extension within the first/last half-cell only.
107  } else {
108  return {Real(zero), Real(zero), Real(zero)};
109  }
110  return {u, v, Real(one)};
111  });
112  const auto values = data.value(op);
113  sums[0] += amrex::get<0>(values);
114  sums[1] += amrex::get<1>(values);
115  sums[2] += amrex::get<2>(values);
116  }
117  ParallelAllReduce::Sum(sums, 3, ParallelContext::CommunicatorSub());
118  Box footprint = region;
119  footprint.setRange(2, klo);
120  if (sums[2] != Real(footprint.numPts()) || sums[2] == zero) {
121  // Report the actual sampling failure: an incomplete count does not
122  // necessarily mean the ancestor's domain is too shallow. These
123  // collective reductions only run when a source level is rejected.
124  const Real min_height = samples.min(2);
125  const Real max_height = samples.max(2);
126  Print() << "Automatic perturbation wind sampling: region=" << region
127  << " target_height_agl=" << height_agl << " m"
128  << " samples=" << sums[2]
129  << " expected_columns=" << footprint.numPts()
130  << " cell_height_agl_min=" << min_height << " m"
131  << " cell_height_agl_max=" << max_height << " m"
132  << " source_boxes=" << ba.size() << "\n";
133  return {};
134  }
135  const Real u = sums[0]/sums[2];
136  const Real v = sums[1]/sums[2];
137  if (!std::isfinite(u) || !std::isfinite(v)) {
138  Abort("Automatic perturbations: non-finite wind at the CPM sampling height");
139  }
140  return {u, v, true};
141 }
constexpr amrex::Real bogus_large_value
Definition: ERF_Constants.H:19
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
const int klo
Definition: ERF_InitCustomPert_KE.H:9
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);})
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:37
constexpr amrex::Real fourth
Definition: ERF_NumericalConstants.H:42
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:36
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:41
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ xvel
Definition: ERF_IndexDefines.H:255
@ yvel
Definition: ERF_IndexDefines.H:256
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real height_agl(amrex::Real z, amrex::Real z_sfc) noexcept
Definition: ERF_ShocGpuUtils.H:50

Referenced by ERF::turbPert_update_automatic(), and ERF::turbPert_update_uniform_wind().

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