ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_IBSEBBalance.H
Go to the documentation of this file.
1 #ifndef ERF_IBSEB_BALANCE_H
2 #define ERF_IBSEB_BALANCE_H
3 
4 #include <AMReX_REAL.H>
5 #include <AMReX_GpuQualifiers.H>
6 #include <AMReX_Math.H>
7 #include <cmath>
8 
9 /**
10  * \file ERF_IBSEBBalance.H
11  * \brief Newton solve of the surface energy balance on one building face.
12  *
13  * Adapted from the ERF-SLUCM branch's ERF_UCMSEBSolver.H (``solve_facet_seb``).
14  * The face skin is massless, so at the end of every atmospheric step its
15  * temperature :math:`T_s` satisfies
16  *
17  * \f[
18  * F(T_s) = SW_{abs} + \epsilon\,Q_{ext} + \epsilon\,[LW_{ext} - (1 - f_b)\,\sigma T_s^4]
19  * - C_H\,(T_s/\Pi - \theta_a) - LE - (a\,T_s - b) = 0 ,
20  * \f]
21  *
22  * where every term keeps the sign convention of the module (radiation into
23  * the face positive, H, LE and G out of it positive):
24  * - ``SW_abs`` is the absorbed shortwave of the step;
25  * - ``Q_ext`` is an external incident flux (a fire's radiation), absorbed
26  * with the face's longwave emissivity by Kirchhoff's law since such
27  * sources are thermal; zero unless something sets it;
28  * - ``LW_ext`` is the incoming longwave from the sky and the ground;
29  * the part reflected back by the surrounding walls is
30  * ``f_b sigma T_s^4`` (isothermal surroundings) and folds into the
31  * emission term as the factor ``(1 - f_b)``;
32  * - ``C_H (T_s / Pi - theta_a)`` is the wall function's sensible flux
33  * with the coefficient ``C_H = rho c_p kappa u* / ln(delta/z0h)``
34  * frozen at its value from the wind of the step, ``Pi`` the Exner
35  * function of the fluid cell so the skin temperature enters as a
36  * potential temperature;
37  * - ``LE`` is the latent flux, held at its stored value (zero, not modelled);
38  * - ``a T_s - b`` is the conduction into the slab that the implicit slab
39  * step (ERF_IBSEBSlab.H) will produce for a skin temperature ``T_s``
40  * (ibseb::slab_skin_response()), so the balance and the slab agree
41  * exactly instead of lagging the slab's top layer by one step.
42  *
43  * Newton's method with the analytic Jacobian
44  * ``F' = -4 eps (1 - f_b) sigma T_s^3 - C_H / Pi - a`` (every term negative,
45  * so the iteration is monotone), a step cap and bounds on the temperature.
46  * The bounds are inputs (``erf.ibseb.T_skin_min`` / ``T_skin_max``) because a
47  * face under a fire's radiation legitimately exceeds the 380 K of the urban
48  * canopy model; a face pinned at a bound leaves a non-zero residual, which
49  * the caller stores and the summary counts.
50  */
51 namespace ibseb {
52 
53 /// Stefan-Boltzmann constant [W/m2/K4].
54 constexpr amrex::Real SIGMA_SB = 5.670374419e-8;
55 
56 /**
57  * Solve the face balance for the skin temperature.
58  *
59  * @param[in] T_start Starting guess, the skin temperature of the previous step [K].
60  * @param[in] SW_abs Absorbed shortwave [W/m2].
61  * @param[in] Q_abs Absorbed external flux ``eps Q_ext`` [W/m2].
62  * @param[in] LW_ext Incoming longwave from sky and ground [W/m2].
63  * @param[in] emis Face emissivity.
64  * @param[in] f_bldg Building view fraction of the face.
65  * @param[in] C_H Sensible coefficient ``rho c_p kappa u* / ln(delta/z0h)`` [W/m2/K].
66  * @param[in] exner Exner function of the fluid cell, ``T_a / theta_a``.
67  * @param[in] theta_air Potential temperature of the fluid cell [K].
68  * @param[in] LE Latent flux, frozen [W/m2].
69  * @param[in] slab_a, slab_b Slab response ``G = a T_s - b`` [W/m2/K], [W/m2].
70  * @param[in] T_min, T_max Bounds on the skin temperature [K].
71  * @param[in] max_step Largest Newton step [K].
72  * @param[in] tol Convergence: stop when a step changes T_s by less than this [K].
73  * @param[in] max_iter Iteration cap.
74  * @param[out] n_iter Iterations taken.
75  * @param[out] resid ``|F|`` at the returned temperature [W/m2]; non-zero
76  * when a bound was hit or the cap was reached.
77  * @return The skin temperature [K], within the bounds.
78  */
79 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
81  amrex::Real SW_abs, amrex::Real Q_abs, amrex::Real LW_ext,
82  amrex::Real emis, amrex::Real f_bldg,
83  amrex::Real C_H, amrex::Real exner, amrex::Real theta_air,
84  amrex::Real LE, amrex::Real slab_a, amrex::Real slab_b,
85  amrex::Real T_min, amrex::Real T_max,
86  amrex::Real max_step, amrex::Real tol, int max_iter,
87  int& n_iter, amrex::Real& resid)
88 {
89  using amrex::Real;
90  const Real e_eff = emis * (Real(1.0) - f_bldg) * SIGMA_SB; // effective emission coefficient
91  auto residual = [&](Real T) {
92  const Real T4 = T * T * T * T;
93  return SW_abs + Q_abs + emis * LW_ext - e_eff * T4
94  - C_H * (T / exner - theta_air) - LE - (slab_a * T - slab_b);
95  };
96  Real T = amrex::max(amrex::min(T_start, T_max), T_min);
97  n_iter = 0;
98  Real F = residual(T);
99  for (int it = 0; it < max_iter; ++it) {
100  ++n_iter;
101  const Real Fp = -Real(4.0) * e_eff * T * T * T - C_H / exner - slab_a;
102  Real step = -F / Fp; // Fp < 0 always
103  step = amrex::max(amrex::min(step, max_step), -max_step);
104  const Real T_new = amrex::max(amrex::min(T + step, T_max), T_min);
105  const Real dT = std::abs(T_new - T);
106  T = T_new;
107  F = residual(T);
108  if (dT < tol) break;
109  }
110  resid = std::abs(F);
111  return T;
112 }
113 
114 } // namespace ibseb
115 
116 #endif
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ T
Definition: ERF_IndexDefines.H:128
Definition: ERF_IBSEBBalance.H:51
constexpr amrex::Real SIGMA_SB
Stefan-Boltzmann constant [W/m2/K4].
Definition: ERF_IBSEBBalance.H:54
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real solve_skin_balance(amrex::Real T_start, amrex::Real SW_abs, amrex::Real Q_abs, amrex::Real LW_ext, amrex::Real emis, amrex::Real f_bldg, amrex::Real C_H, amrex::Real exner, amrex::Real theta_air, amrex::Real LE, amrex::Real slab_a, amrex::Real slab_b, amrex::Real T_min, amrex::Real T_max, amrex::Real max_step, amrex::Real tol, int max_iter, int &n_iter, amrex::Real &resid)
Definition: ERF_IBSEBBalance.H:80