ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_PlaneAverage.H
Go to the documentation of this file.
1 #ifndef ERF_PlaneAverage_H
2 #define ERF_PlaneAverage_H
3 
4 #include "AMReX_Gpu.H"
5 #include "AMReX_iMultiFab.H"
6 #include "AMReX_MultiFab.H"
7 #include "AMReX_GpuContainers.H"
9 
10 /**
11  * Basic averaging and interpolation operations
12  */
13 
14 class PlaneAverage {
15 public:
16  /**
17  * Construct a plane averaging object.
18  * @param[in] field_in MultiFab to be averaged.
19  * @param[in] geom_in Geometry of the domain.
20  * @param[in] axis_in Axis along which to average.
21  * @param[in] n_ghost_to_inc Ghost cells to include in the average.
22  */
23  AMREX_FORCE_INLINE
24  explicit PlaneAverage (const amrex::MultiFab* field_in,
25  amrex::Geometry geom_in,
26  int axis_in,
27  amrex::IntVect n_ghost_to_inc=amrex::IntVect{0});
28  PlaneAverage () = delete;
29  ~PlaneAverage () = default;
30 
31  AMREX_FORCE_INLINE
32  void operator()();
33 
34  /** evaluate line average at specific location for any average component */
35  [[nodiscard]] AMREX_FORCE_INLINE
37 
38  /** change precision of text file output */
39  void set_precision (int p) { m_precision = p; }
40 
41  [[nodiscard]] amrex::Real dx () const { return m_dx; }
42  [[nodiscard]] amrex::Real xlo () const { return m_xlo; }
43 
44  [[nodiscard]] int axis () const { return m_axis; }
45  [[nodiscard]] int level () const { return m_level; }
46  [[nodiscard]] int ncomp () const { return m_ncomp; }
47  [[nodiscard]] int ncell_plane () const { return m_ncell_plane; }
48  [[nodiscard]] int ncell_line () const { return m_ncell_line; }
49 
50  [[nodiscard]] const amrex::Vector<amrex::Real>& line_average () const
51  {
52  return m_line_average;
53  }
54 
55  /**
56  * Copy the line average of a specific component to a host vector.
57  * @param[in] comp Component index.
58  * @param[out] l_vec Host vector to store the line average.
59  */
60  AMREX_FORCE_INLINE
61  void line_average (int comp, amrex::Gpu::HostVector<amrex::Real>& l_vec);
62 
63  [[nodiscard]] const amrex::Vector<amrex::Real>& line_centroids () const
64  {
65  return m_line_xcentroid;
66  }
67 
68  [[nodiscard]] const amrex::MultiFab& field () const { return *m_field; }
69 
70 protected:
71  int m_ncomp; /** number of average components */
72 
73  /** line storage for the average velocity and tracer variables */
74  amrex::Vector<amrex::Real> m_line_average;
75 
76  amrex::Vector<amrex::Real> m_line_xcentroid; /** line storage for centroids of each cell along a line*/
77 
78  amrex::Real m_dx; /** grid spacing in axis direction*/
79  amrex::Real m_xlo; /** bottom of domain in axis direction */
80 
81  int m_ncell_plane; /** number of cells in plane */
82  int m_ncell_line; /** number of cells along line */
83 
84  int m_precision = 4; /** precision for line plot text file */
85  const int m_level = 0; /** level for plane averaging for now fixed at level=0 */
86 
87  const amrex::MultiFab* m_field;
88  amrex::Geometry m_geom;
89  const int m_axis;
90  amrex::IntVect m_ghost_to_inc = amrex::IntVect{0};
91  amrex::IntVect m_ixtype;
92 
93 public:
94  /** fill line storage with averages */
95  template <typename IndexSelector>
96  AMREX_FORCE_INLINE
97  void compute_averages (const IndexSelector& idxOp, const amrex::MultiFab& mfab);
98 };
99 
100 
101 /**
102  * Construct a plane averaging object.
103  * @param[in] field_in MultiFab to be averaged.
104  * @param[in] geom_in Geometry of the domain.
105  * @param[in] axis_in Axis along which to average.
106  * @param[in] n_ghost_to_inc Ghost cells to include in the average.
107  */
108 PlaneAverage::PlaneAverage (const amrex::MultiFab* field_in,
109  amrex::Geometry geom_in,
110  int axis_in,
111  amrex::IntVect n_ghost_to_inc)
112  : m_field(field_in), m_geom(geom_in), m_axis(axis_in), m_ghost_to_inc(n_ghost_to_inc)
113 {
114  AMREX_ALWAYS_ASSERT(m_axis >= 0 && m_axis < AMREX_SPACEDIM);
115 
116  m_xlo = m_geom.ProbLo(m_axis);
117  m_dx = m_geom.CellSize(m_axis);
118  m_ncomp = m_field->nComp();
119  m_ixtype = m_field->boxArray().ixType().toIntVect();
120 
121  amrex::Box domain = m_geom.Domain();
122  m_ghost_to_inc = std::min(field_in->nGrowVect(),m_ghost_to_inc);
123  domain.grow(axis_in, m_ghost_to_inc[axis_in]);
124 
125  amrex::IntVect dom_lo(domain.loVect());
126  amrex::IntVect dom_hi(domain.hiVect());
127 
128  // Domain guarantees enough space and we usually average in z-dir (spans domain)
129  m_ncell_line = dom_hi[m_axis] - dom_lo[m_axis] + 1 + m_ixtype[m_axis];
130 
131  // First estimate is with domain (updated with MFiter later)
132  m_ncell_plane = 1;
133  auto period = m_geom.periodicity();
134  for (int i = 0; i < AMREX_SPACEDIM; ++i) {
135  int p_fac = (!period.isPeriodic(i)) ? 1 : 0;
136  if (i != m_axis) m_ncell_plane *= (dom_hi[i] - dom_lo[i] + 1 + p_fac*m_ixtype[i]);
137  }
138 
139  m_line_average.resize(static_cast<size_t>(m_ncell_line) * m_ncomp, zero);
141 
142  for (int i = 0; i < m_ncell_line; ++i) {
143  m_line_xcentroid[i] = m_xlo + (i + myhalf) * m_dx;
144  }
145 }
146 
147 /**
148  * Evaluate line average at specific location for any average component.
149  * @param[in] x Physical coordinate along the averaging axis.
150  * @param[in] comp Component index.
151  * @return Interpolated value of the line average.
152  */
155 {
156  AMREX_ALWAYS_ASSERT(comp >= 0 && comp < m_ncomp);
157 
158  amrex::Real c = zero;
159  int ind = 0;
160 
161  if (x > m_xlo + myhalf * m_dx) {
162  ind = static_cast<int>(floor((x - m_xlo) / m_dx - myhalf));
163  const amrex::Real x1 = m_xlo + (ind + myhalf) * m_dx;
164  c = (x - x1) / m_dx;
165  }
166 
167  if (ind + 1 >= m_ncell_line) {
168  ind = m_ncell_line - 2;
169  c = one;
170  }
171 
172  AMREX_ALWAYS_ASSERT(ind >= 0 && ind + 1 < m_ncell_line);
173 
174  return m_line_average[m_ncomp * ind + comp] * (one - c) +
175  m_line_average[m_ncomp * (ind + 1) + comp] * c;
176 }
177 
178 /**
179  * Copy the line average of a specific component to a host vector.
180  * @param[in] comp Component index.
181  * @param[out] l_vec Host vector to store the line average.
182  */
183 void
184 PlaneAverage::line_average (int comp, amrex::Gpu::HostVector<amrex::Real>& l_vec)
185 {
186  AMREX_ALWAYS_ASSERT(comp >= 0 && comp < m_ncomp);
187 
188  for (int i = 0; i < m_ncell_line; i++) {
189  l_vec[i] = m_line_average[m_ncomp * i + comp];
190  }
191 }
192 
193 /**
194  * Compute the plane averages for the stored field.
195  */
196 void
198 {
199  std::fill(m_line_average.begin(), m_line_average.end(), zero);
200  switch (m_axis) {
201  case 0:
203  break;
204  case 1:
206  break;
207  case 2:
209  break;
210  default:
211  amrex::Abort("axis must be equal to 0, 1, or 2");
212  break;
213  }
214 }
215 
216 /**
217  * Compute averages across a plane.
218  * @tparam IndexSelector Type of index selector functor.
219  * @param[in] idxOp Index selector to map cell indices to the line index.
220  * @param[in] mfab MultiFab containing the data to average.
221  */
222 template <typename IndexSelector>
223 void
224 PlaneAverage::compute_averages (const IndexSelector& idxOp, const amrex::MultiFab& mfab)
225 {
226  amrex::Vector<amrex::Real> cnt_h(m_ncell_line,zero);
227  amrex::AsyncArray<amrex::Real> cnt_d(cnt_h.data(), cnt_h.size());
228  amrex::AsyncArray<amrex::Real> lavg(m_line_average.data(), m_line_average.size());
229  amrex::Real* cnt_avg = cnt_d.data();
230  amrex::Real* line_avg = lavg.data();
231  const int ncomp = m_ncomp;
232 
233  amrex::Box domain = amrex::convert(m_geom.Domain(),m_ixtype);
234 
235  amrex::IntVect ng = amrex::IntVect(0);
237  ng[m_axis] = offset;
238 
239  std::unique_ptr<amrex::iMultiFab> mask = OwnerMask(*m_field, m_geom.periodicity(), ng);
240 
241 #ifdef _OPENMP
242 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
243 #endif
244  for (amrex::MFIter mfi(mfab, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi) {
245  amrex::Box tbx = mfi.tilebox();
246  if (tbx.smallEnd(m_axis) == domain.smallEnd(m_axis)) tbx.growLo(m_axis,offset);
247  if (tbx.bigEnd (m_axis) == domain.bigEnd (m_axis)) tbx.growHi(m_axis,offset);
248  amrex::Box pbx = PerpendicularBox<IndexSelector>(tbx, amrex::IntVect(0));
249 
250  const amrex::Array4<const amrex::Real>& fab_arr = mfab.const_array(mfi);
251  const amrex::Array4<const int >& mask_arr = mask->const_array(mfi);
252 
253  amrex::ParallelFor(amrex::Gpu::KernelInfo().setReduction(true), pbx, [=]
254  AMREX_GPU_DEVICE( int p_i, int p_j, int p_k,
255  amrex::Gpu::Handler const& handler) noexcept
256  {
257  // Loop over the direction perpendicular to the plane.
258  // This reduces the atomic pressure on the destination arrays.
259  amrex::Box lbx = ParallelBox<IndexSelector>(tbx, amrex::IntVect{p_i, p_j, p_k});
260 
261  for (int k = lbx.smallEnd(2); k <= lbx.bigEnd(2); ++k) {
262  for (int j = lbx.smallEnd(1); j <= lbx.bigEnd(1); ++j) {
263  for (int i = lbx.smallEnd(0); i <= lbx.bigEnd(0); ++i) {
264  int ind = idxOp.getIndx(i, j, k) + offset;
265  // NOTE: This factor is to avoid an if statement that will break
266  // the devicereducesum since all threads won't participate.
267  // This more performant than Gpu::Atomic::Add.
268  amrex::Real fac = (mask_arr(i,j,k)) ? one : zero;
269  amrex::Gpu::deviceReduceSum(&cnt_avg[ind], fac, handler);
270  for (int n = 0; n < ncomp; ++n) {
271  amrex::Gpu::deviceReduceSum(&line_avg[ncomp * ind + n],
272  fab_arr(i, j, k, n) * fac, handler);
273  }
274  }
275  }
276  }
277  });
278  }
279 
280  cnt_d.copyToHost(cnt_h.data(), cnt_h.size());
281  lavg.copyToHost(m_line_average.data(), m_line_average.size());
282  amrex::ParallelDescriptor::ReduceRealSum(cnt_h.data(), cnt_h.size());
283  amrex::ParallelDescriptor::ReduceRealSum(m_line_average.data(), m_line_average.size());
284  //
285  // With AMR, a level need not cover every plane in the direction we average over: a
286  // fine level typically covers only the bottom of the domain. A plane holding no
287  // cells has nothing to divide by, and leaving it at the accumulated sum -- exactly
288  // zero, since m_line_average was zeroed before the reduction -- is indistinguishable
289  // from a legitimate average of zero. A consumer that divides one profile by another,
290  // as the subsidence forcing in MakeSources does, then gets 0/0 = NaN far from the
291  // cause. So fill each empty plane from the closest plane that does hold cells,
292  // preferring the one below it, which extends the profile as a constant beyond the
293  // range of the level.
294  //
295  amrex::Vector<int> plane_has_cells(m_ncell_line);
296  int ncell_line_empty = 0;
297  for (int ind(0); ind<m_ncell_line; ++ind) {
298  plane_has_cells[ind] = (cnt_h[ind] > zero);
299  if (plane_has_cells[ind]) {
300  for (int n(0); n<ncomp; ++n) {
301  m_line_average[ncomp*ind + n] /= cnt_h[ind];
302  }
303  } else {
304  ++ncell_line_empty;
305  }
306  }
307 
308  if (ncell_line_empty == m_ncell_line) {
309  amrex::Abort("PlaneAverage: no cells were found in any plane");
310  }
311 
312  if (ncell_line_empty > 0) {
313  // Sweeping up first and then down leaves every empty plane holding the value of
314  // the nearest non-empty plane below it, or above it if there is none below.
315  for (int ind(1); ind<m_ncell_line; ++ind) {
316  if (!plane_has_cells[ind] && plane_has_cells[ind-1]) {
317  for (int n(0); n<ncomp; ++n) {
318  m_line_average[ncomp*ind + n] = m_line_average[ncomp*(ind-1) + n];
319  }
320  plane_has_cells[ind] = 1;
321  }
322  }
323  for (int ind(m_ncell_line-2); ind>=0; --ind) {
324  if (!plane_has_cells[ind] && plane_has_cells[ind+1]) {
325  for (int n(0); n<ncomp; ++n) {
326  m_line_average[ncomp*ind + n] = m_line_average[ncomp*(ind+1) + n];
327  }
328  plane_has_cells[ind] = 1;
329  }
330  }
331  }
332 
333  // Take the size of a plane from one that actually holds cells: plane 0 need not be
334  // one of them on a level whose grids do not reach the bottom of the domain.
335  amrex::Real cnt_max = zero;
336  for (int ind(0); ind<m_ncell_line; ++ind) { cnt_max = amrex::max(cnt_max,cnt_h[ind]); }
337  m_ncell_plane = static_cast<int>(cnt_max);
338 }
339 #endif /* ERF_PlaneAverage.H */
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
DirectionSelector< 0 > XDir
Definition: ERF_DirectionSelector.H:53
DirectionSelector< 1 > YDir
Definition: ERF_DirectionSelector.H:54
DirectionSelector< 2 > ZDir
Definition: ERF_DirectionSelector.H:55
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
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_FORCE_INLINE IntVect offset(const int face_dir, const int normal)
Definition: ERF_ReadBndryPlanes.cpp:31
amrex::Real Real
Definition: ERF_ShocInterface.H:19
Definition: ERF_PlaneAverage.H:14
const amrex::MultiFab & field() const
Definition: ERF_PlaneAverage.H:68
int m_ncell_line
Definition: ERF_PlaneAverage.H:82
AMREX_FORCE_INLINE amrex::Real line_average_interpolated(amrex::Real x, int comp) const
Definition: ERF_PlaneAverage.H:154
int m_precision
Definition: ERF_PlaneAverage.H:84
int ncomp() const
Definition: ERF_PlaneAverage.H:46
amrex::Real m_xlo
Definition: ERF_PlaneAverage.H:79
amrex::IntVect m_ixtype
Definition: ERF_PlaneAverage.H:91
amrex::Vector< amrex::Real > m_line_xcentroid
Definition: ERF_PlaneAverage.H:76
const amrex::Vector< amrex::Real > & line_centroids() const
Definition: ERF_PlaneAverage.H:63
AMREX_FORCE_INLINE void compute_averages(const IndexSelector &idxOp, const amrex::MultiFab &mfab)
void set_precision(int p)
Definition: ERF_PlaneAverage.H:39
const int m_level
Definition: ERF_PlaneAverage.H:85
const amrex::MultiFab * m_field
Definition: ERF_PlaneAverage.H:87
int level() const
Definition: ERF_PlaneAverage.H:45
~PlaneAverage()=default
int m_ncell_plane
Definition: ERF_PlaneAverage.H:81
amrex::Vector< amrex::Real > m_line_average
Definition: ERF_PlaneAverage.H:74
amrex::Real xlo() const
Definition: ERF_PlaneAverage.H:42
AMREX_FORCE_INLINE void operator()()
Definition: ERF_PlaneAverage.H:197
int ncell_plane() const
Definition: ERF_PlaneAverage.H:47
const int m_axis
Definition: ERF_PlaneAverage.H:89
int ncell_line() const
Definition: ERF_PlaneAverage.H:48
amrex::Real dx() const
Definition: ERF_PlaneAverage.H:41
amrex::Real m_dx
Definition: ERF_PlaneAverage.H:78
PlaneAverage()=delete
amrex::Geometry m_geom
Definition: ERF_PlaneAverage.H:88
int axis() const
Definition: ERF_PlaneAverage.H:44
int m_ncomp
Definition: ERF_PlaneAverage.H:71
const amrex::Vector< amrex::Real > & line_average() const
Definition: ERF_PlaneAverage.H:50
amrex::IntVect m_ghost_to_inc
Definition: ERF_PlaneAverage.H:90
@ ng
Definition: ERF_Morrison.H:49
@ p
Definition: ERF_WSM6.H:191
void fill(MultiFab &dst, int temperature_comp, int mixing_ratio_comp, int source_comp, const Sources &sources, Real missing_value)
Definition: ERF_NearSurfaceDiagnostics.cpp:41