ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_TwoStreamCanopyForcing.H
Go to the documentation of this file.
1 #ifndef ERF_TWO_STREAM_CANOPY_FORCING_H_
2 #define ERF_TWO_STREAM_CANOPY_FORCING_H_
3 
4 #include <AMReX_Array4.H>
5 #include <AMReX_Extension.H>
6 #include <AMReX_GpuQualifiers.H>
7 #include <AMReX_MultiFab.H>
8 #include <AMReX_REAL.H>
9 #include <limits>
10 
11 /**
12  * \file ERF_TwoStreamCanopyForcing.H
13  * \brief What the two-stream sweep hands the building faces of the
14  * immersed-boundary surface energy balance (erf.ibseb.radiation = two_stream),
15  * and how a face reads it.
16  *
17  * The sweep already writes the interface fluxes of every column into ERF's
18  * ``rad_fluxes`` (RRTMGP's level layout: index k the lower interface of layer k,
19  * component 0 SW up, 1 SW down, 2 LW up, 3 LW down, after the clear/cloudy
20  * blend). What it does not keep is the direct beam, which only its scratch holds,
21  * and the cosine of the zenith it used. On the levels where ERF::init_ibseb()
22  * asks for them (TwoStreamRadiation::supply_canopy_forcing()) every sweep also
23  * writes:
24  * - the direct beam on a horizontal surface at the interfaces m = 0 .. m_top of
25  * every column, counted from the surface, m_top the highest interface a face
26  * of the level reads (a field on the level's grids flattened in z and then
27  * given those interfaces, index k = m), blended clear/cloudy like the fluxes;
28  * - the cosine of the zenith angle, floored at zero (a 2D field).
29  * A face reads the column of its fluid cell at its own height
30  * (two_stream_canopy_sample()): the downwelling fluxes there are the sun and
31  * the sky that reach it (for height-map buildings no building stands above a
32  * face in its own column; a face under the overhang of an STL building sees only
33  * building in IBFaceSet's view fractions, which take each column as solid up to
34  * its top, so its column's fluxes carry no weight), and the
35  * upwelling fluxes there are the ground as seen from that height, through the air
36  * in between.
37  */
38 
39 /**
40  * @brief The fields a level's faces read for one step: the beam and the cosine
41  * of the zenith the sweep of that step wrote, and that sweep's interface fluxes.
42  * All null when the level has not swept this step (the initial report).
43  */
45 {
46  const amrex::MultiFab* beam = nullptr; ///< Direct beam on a horizontal surface, interfaces 0 .. m_top [W/m^2]
47  const amrex::MultiFab* cos_zenith = nullptr; ///< Cosine of the zenith of the sweep, floored at zero (2D) [1]
48  const amrex::MultiFab* fluxes = nullptr; ///< The level's rad_fluxes (SW up, SW down, LW up, LW down) [W/m^2]
49  [[nodiscard]] bool valid () const noexcept { return beam != nullptr && cos_zenith != nullptr && fluxes != nullptr; }
50 };
51 
52 /**
53  * @brief Write the direct beam on a horizontal surface at the interfaces
54  * 0 .. m_top of column (i,j), from the scratch of the evaluation that has just
55  * run there, into @p beam: the clear-sky evaluation's beam as it is, and the
56  * cloudy evaluation's blended in with the cloud fraction, by the same expression
57  * as the fluxes ((1 - f) clear + f cloudy), so the two round alike in single
58  * precision and the shortwave down less the beam is the diffuse light.
59  *
60  * vertical_two_stream_sweep() keeps the Beer-Lambert beam of every interface in
61  * its scratch (TwoStreamScratch::F_DIR), but writes it only when the shortwave is
62  * on and the sun is up; otherwise the scratch holds the other evaluation's beam or
63  * nothing at all, and the beam is zero. Call it right after each evaluation, before
64  * the next one overwrites the scratch.
65  *
66  * @param scratch the sweep's scratch, as vertical_two_stream_sweep() takes it
67  * @param f_dir_comp the scratch component of the beam (TwoStreamScratch::F_DIR)
68  * @param kmin lowest cell of the column (its surface interface)
69  * @param m_top highest interface of the beam field, at most the column's top
70  * @param sw_on whether the sweep ran its shortwave
71  * @param cos_zenith cosine of the zenith angle the sweep used (unfloored)
72  * @param cloud_fraction the cloud fraction (used only when @p blend)
73  * @param blend false for the clear-sky evaluation (overwrite), true for the cloudy one
74  * @param beam the beam field (component 0, index k = m)
75  */
76 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
77 void two_stream_canopy_beam (int i, int j, int kmin, int m_top,
78  const amrex::Array4<const amrex::Real>& scratch, int f_dir_comp,
79  bool sw_on, amrex::Real cos_zenith, amrex::Real cloud_fraction, bool blend,
80  const amrex::Array4<amrex::Real>& beam)
81 {
82  const bool lit = sw_on && (cos_zenith > amrex::Real(0.0));
83  for (int m = 0; m <= m_top; ++m) {
84  const amrex::Real b = lit ? scratch(i, j, kmin + m, f_dir_comp) : amrex::Real(0.0);
85  beam(i, j, m) = blend ? (1.0 - cloud_fraction) * beam(i, j, m) + cloud_fraction * b : b;
86  }
87 }
88 
89 /**
90  * @brief Sample interface of a face and the weight of the one above. A z face
91  * takes the interface it lies on: a roof (solid below, side -1) the bottom of its
92  * fluid cell (m = k - k0), a ceiling (solid above, side +1, as under an overhang
93  * of a building from an STL file) the top of it (m = k - k0 + 1). A wall takes the
94  * mean of the two interfaces of its cell (m and m + 1), its mean height.
95  *
96  * @param k fluid cell of the face (the level's index)
97  * @param k0 the level's domain bottom
98  * @param dir face direction (0 x, 1 y, 2 z)
99  * @param side side of the fluid cell the solid is on (-1 or +1)
100  * @param w_up weight of interface m + 1
101  * @return the lower sample interface m
102  */
103 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
104 int two_stream_canopy_sample (int k, int k0, int dir, int side, amrex::Real& w_up)
105 {
106  w_up = (dir == 2) ? amrex::Real(0.0) : amrex::Real(0.5);
107  return k - k0 + ((dir == 2 && side > 0) ? 1 : 0);
108 }
109 
110 /** @brief What one face takes from its column (all W/m^2). */
112 {
113  amrex::Real dni = 0.0; ///< Direct-normal irradiance at the face's height
114  amrex::Real diffuse = 0.0; ///< Diffuse shortwave down at the face's height
115  amrex::Real lw_down = 0.0; ///< Longwave down at the face's height
116  amrex::Real sw_up = 0.0; ///< Shortwave up at the face's height (the ground's reflection, seen from there)
117  amrex::Real lw_up = 0.0; ///< Longwave up at the face's height (the ground's emission and the air's below)
118 };
119 
120 /**
121  * @brief The column radiation a face at fluid cell (i,j,k) of direction @p dir,
122  * with its solid on side @p side, takes: at its sample (two_stream_canopy_sample()), the direct-normal
123  * irradiance (the beam over the column's cosine of the zenith), the diffuse
124  * light (the shortwave down less the beam, floored at zero against round-off on
125  * a transparent column), the longwave down, and the shortwave and longwave up,
126  * which the face takes through its view of the ground.
127  *
128  * @param beam beam field (index k = m)
129  * @param cosz cosine of the zenith, floored at zero (2D)
130  * @param fluxes interface fluxes in RRTMGP's level layout (absolute k)
131  */
132 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
133 TwoStreamFaceSky two_stream_face_sky (int i, int j, int k, int k0, int dir, int side,
134  const amrex::Array4<const amrex::Real>& beam,
135  const amrex::Array4<const amrex::Real>& cosz,
136  const amrex::Array4<const amrex::Real>& fluxes)
137 {
138  amrex::Real w_up = 0.0;
139  const int ms = two_stream_canopy_sample(k, k0, dir, side, w_up);
140  const int mu = ms + ((w_up > amrex::Real(0.0)) ? 1 : 0);
141  const amrex::Real w_lo = amrex::Real(1.0) - w_up;
142  const amrex::Real beam_h = w_lo * beam(i, j, ms) + w_up * beam(i, j, mu);
143  const amrex::Real sw_dn = w_lo * fluxes(i, j, k0 + ms, 1) + w_up * fluxes(i, j, k0 + mu, 1);
145  const amrex::Real cz = cosz(i, j, 0);
146  // Both operands valid in both arms: an optimiser may evaluate the division
147  // before the selection, and 0 / 0 at night would raise the invalid flag.
148  s.dni = (cz > amrex::Real(0.0)) ? beam_h / (cz + std::numeric_limits<amrex::Real>::min()) : amrex::Real(0.0);
149  s.diffuse = (sw_dn - beam_h > amrex::Real(0.0)) ? sw_dn - beam_h : amrex::Real(0.0);
150  s.lw_down = w_lo * fluxes(i, j, k0 + ms, 3) + w_up * fluxes(i, j, k0 + mu, 3);
151  s.sw_up = w_lo * fluxes(i, j, k0 + ms, 0) + w_up * fluxes(i, j, k0 + mu, 0);
152  s.lw_up = w_lo * fluxes(i, j, k0 + ms, 2) + w_up * fluxes(i, j, k0 + mu, 2);
153  return s;
154 }
155 
156 #endif // ERF_TWO_STREAM_CANOPY_FORCING_H_
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void two_stream_canopy_beam(int i, int j, int kmin, int m_top, const amrex::Array4< const amrex::Real > &scratch, int f_dir_comp, bool sw_on, amrex::Real cos_zenith, amrex::Real cloud_fraction, bool blend, const amrex::Array4< amrex::Real > &beam)
Write the direct beam on a horizontal surface at the interfaces 0 .. m_top of column (i,...
Definition: ERF_TwoStreamCanopyForcing.H:77
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE TwoStreamFaceSky two_stream_face_sky(int i, int j, int k, int k0, int dir, int side, const amrex::Array4< const amrex::Real > &beam, const amrex::Array4< const amrex::Real > &cosz, const amrex::Array4< const amrex::Real > &fluxes)
The column radiation a face at fluid cell (i,j,k) of direction dir, with its solid on side side,...
Definition: ERF_TwoStreamCanopyForcing.H:133
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int two_stream_canopy_sample(int k, int k0, int dir, int side, amrex::Real &w_up)
Sample interface of a face and the weight of the one above. A z face takes the interface it lies on: ...
Definition: ERF_TwoStreamCanopyForcing.H:104
@ mu
Definition: ERF_AdvanceMorrison.cpp:92
The fields a level's faces read for one step: the beam and the cosine of the zenith the sweep of that...
Definition: ERF_TwoStreamCanopyForcing.H:45
const amrex::MultiFab * fluxes
The level's rad_fluxes (SW up, SW down, LW up, LW down) [W/m^2].
Definition: ERF_TwoStreamCanopyForcing.H:48
const amrex::MultiFab * beam
Direct beam on a horizontal surface, interfaces 0 .. m_top [W/m^2].
Definition: ERF_TwoStreamCanopyForcing.H:46
const amrex::MultiFab * cos_zenith
Cosine of the zenith of the sweep, floored at zero (2D) [1].
Definition: ERF_TwoStreamCanopyForcing.H:47
bool valid() const noexcept
Definition: ERF_TwoStreamCanopyForcing.H:49
What one face takes from its column (all W/m^2).
Definition: ERF_TwoStreamCanopyForcing.H:112
amrex::Real sw_up
Shortwave up at the face's height (the ground's reflection, seen from there)
Definition: ERF_TwoStreamCanopyForcing.H:116
amrex::Real diffuse
Diffuse shortwave down at the face's height.
Definition: ERF_TwoStreamCanopyForcing.H:114
amrex::Real lw_up
Longwave up at the face's height (the ground's emission and the air's below)
Definition: ERF_TwoStreamCanopyForcing.H:117
amrex::Real lw_down
Longwave down at the face's height.
Definition: ERF_TwoStreamCanopyForcing.H:115
amrex::Real dni
Direct-normal irradiance at the face's height.
Definition: ERF_TwoStreamCanopyForcing.H:113