ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_FireGrid.H
Go to the documentation of this file.
1 #ifndef ERF_FIRE_GRID_H
2 #define ERF_FIRE_GRID_H
3 
4 #include <AMReX_BoxArray.H>
5 #include <AMReX_DistributionMapping.H>
6 #include <AMReX_Geometry.H>
7 
8 /**
9  * @file ERF_FireGrid.H
10  * @brief Fire grid data structure and construction
11  *
12  * Defines a 2D grid for fire simulation on the horizontal plane, refined by
13  * factor C relative to one atmospheric AMR level (erf.fire.anchor_level, the
14  * finest level by default). The fire grid covers that level's boxes, keeps their
15  * x-y decomposition and their MPI owners, and its index space starts at zero at
16  * the lower corner of the region they cover.
17  */
18 
19 /**
20  * @struct FireGrid
21  * @brief 2D horizontal fire computational grid
22  *
23  * Represents a 2D slab (kmax=1) with dimensions C*nx × C*ny × 1, where C is the
24  * refinement factor and (nx, ny) is the size of the region the atmospheric
25  * level's boxes cover: the whole domain on level 0, the refined region on a
26  * finer level.
27  */
28 struct FireGrid
29 {
30  amrex::BoxArray ba; ///< 2D BoxArray (C*nx × C*ny × 1)
31  amrex::DistributionMapping dm; ///< Distribution map (x-y decomposition aligned with atm)
32  amrex::Geometry geom; ///< 2D Geometry in physical coordinates [m]
33  int C; ///< Refinement factor
34  /// Index, on the atmospheric level the grid refines, of the lower corner of
35  /// the region the fire grid covers. The fire grid's own index space starts at
36  /// zero at that corner, as every AMReX Geometry does, so fire cell (i, j) lies
37  /// in atmospheric cell (i / C + atm_lo[0], j / C + atm_lo[1]). Zero on level 0.
38  amrex::IntVect atm_lo {0, 0, 0};
39  int lev = 0; ///< The atmospheric level (erf.fire.anchor_level, resolved)
40 };
41 
42 /**
43  * @brief Construct a refined 2D fire grid over one atmospheric level's boxes
44  *
45  * @param[in] ba_atm BoxArray of the atmospheric level
46  * @param[in] dm_atm DistributionMapping of the atmospheric level
47  * @param[in] geom_atm Geometry of the atmospheric level
48  * @param[in] C Refinement factor (erf.fire.grid_ratio)
49  *
50  * @return FireGrid over the region the boxes cover; its lev is left at 0 for
51  * the caller to set.
52  *
53  * Algorithm:
54  * 1. Take the k = 0 slab of each box in ba_atm, keeping the box order.
55  * 2. The region is their bounding rectangle, which must be covered exactly
56  * (aborts otherwise); atm_lo is its lower corner.
57  * 3. Shift the slabs by -atm_lo and refine by {C, C, 1}; box n keeps the owner
58  * of atmospheric box n, so dm_atm is reused.
59  * 4. The Geometry spans the region: index space 0 to C * size - 1, the
60  * physical extent of the region (the domain's own bounds where the region
61  * spans the domain), periodic only in a direction the region spans.
62  */
63 FireGrid create_fire_grid(const amrex::BoxArray& ba_atm,
64  const amrex::DistributionMapping& dm_atm,
65  const amrex::Geometry& geom_atm,
66  int C);
67 
68 #include <AMReX_MultiFab.H>
69 #include <AMReX_MFIter.H>
70 
71 /**
72  * Exchange the ghost cells of a fire-grid MultiFab and fill the ones outside a
73  * non-periodic face of the domain by copying the nearest interior value.
74  *
75  * FillBoundary alone leaves those cells untouched, so with an inflow-outflow
76  * atmosphere (the fire grid inherits its periodicity) the gradient stencil of
77  * the level set read whatever the allocator had left there. Whether that was
78  * benign depended on the box layout: one box per rank happened to run, two
79  * boxes on a rank blew the level set up at the inflow face within three steps.
80  * Every fire-grid exchange goes through here.
81  */
82 inline void fire_fill_boundary (amrex::MultiFab& mf, const amrex::Geometry& geom)
83 {
84  mf.FillBoundary(geom.periodicity());
85  const auto ng = mf.nGrowVect();
86  const bool px = geom.isPeriodic(0) || ng[0] == 0;
87  const bool py = geom.isPeriodic(1) || ng[1] == 0;
88  const bool pz = geom.isPeriodic(2) || ng[2] == 0;
89  if (px && py && pz) { return; } // nothing outside a non-periodic face to fill
90  const amrex::Box& dom = geom.Domain();
91  const auto dlo = dom.smallEnd();
92  const auto dhi = dom.bigEnd();
93  const int ncomp = mf.nComp();
94  for (amrex::MFIter mfi(mf); mfi.isValid(); ++mfi) {
95  const amrex::Box& gbx = mfi.fabbox();
96  if (dom.contains(gbx)) { continue; }
97  auto a = mf.array(mfi);
98  amrex::ParallelFor(gbx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
99  {
100  int ic = i, jc = j, kc = k;
101  if (!px) { ic = amrex::min(amrex::max(i, dlo[0]), dhi[0]); }
102  if (!py) { jc = amrex::min(amrex::max(j, dlo[1]), dhi[1]); }
103  if (!pz) { kc = amrex::min(amrex::max(k, dlo[2]), dhi[2]); }
104  if (ic != i || jc != j || kc != k) { a(i, j, k, n) = a(ic, jc, kc, n); }
105  });
106  }
107 }
108 
109 /**
110  * Copy a k = 0 atmospheric field onto the fire grid: every fire cell takes the
111  * value of the atmospheric column it lies in, (i / C + atm_lo[0], j / C + atm_lo[1]).
112  * Box n of the fire grid refines box n of the atmospheric level and has the
113  * same owner, so one MFIter serves both MultiFabs.
114  */
115 inline void fill_fire_from_atm_k0 (amrex::MultiFab& fire_mf, const amrex::MultiFab& atm_k0,
116  const FireGrid& fg)
117 {
118  const int C = fg.C;
119  const int lo_x = fg.atm_lo[0];
120  const int lo_y = fg.atm_lo[1];
121  for (amrex::MFIter mfi(fire_mf, false); mfi.isValid(); ++mfi) {
122  auto f = fire_mf.array(mfi);
123  auto a = atm_k0.const_array(mfi);
124  amrex::ParallelFor(mfi.tilebox(), [=] AMREX_GPU_DEVICE (const amrex::IntVect& iv) noexcept
125  {
126  f(iv[0], iv[1], 0) = a(iv[0] / C + lo_x, iv[1] / C + lo_y, 0);
127  });
128  }
129 }
130 
131 #endif
void fire_fill_boundary(amrex::MultiFab &mf, const amrex::Geometry &geom)
Definition: ERF_FireGrid.H:82
FireGrid create_fire_grid(const amrex::BoxArray &ba_atm, const amrex::DistributionMapping &dm_atm, const amrex::Geometry &geom_atm, int C)
Construct a refined 2D fire grid over one atmospheric level's boxes.
void fill_fire_from_atm_k0(amrex::MultiFab &fire_mf, const amrex::MultiFab &atm_k0, const FireGrid &fg)
Definition: ERF_FireGrid.H:115
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);})
@ fg
Definition: ERF_AdvanceMorrison.cpp:111
@ ng
Definition: ERF_Morrison.H:51
2D horizontal fire computational grid
Definition: ERF_FireGrid.H:29
int lev
The atmospheric level (erf.fire.anchor_level, resolved)
Definition: ERF_FireGrid.H:39
amrex::BoxArray ba
2D BoxArray (C*nx × C*ny × 1)
Definition: ERF_FireGrid.H:30
amrex::IntVect atm_lo
Definition: ERF_FireGrid.H:38
amrex::Geometry geom
2D Geometry in physical coordinates [m]
Definition: ERF_FireGrid.H:32
amrex::DistributionMapping dm
Distribution map (x-y decomposition aligned with atm)
Definition: ERF_FireGrid.H:31
int C
Definition: ERF_FireGrid.H:33