ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_IBSEBSlab.H
Go to the documentation of this file.
1 #ifndef ERF_IBSEB_SLAB_H
2 #define ERF_IBSEB_SLAB_H
3 
4 #include <AMReX_REAL.H>
5 #include <AMReX_GpuQualifiers.H>
6 
7 /**
8  * \file ERF_IBSEBSlab.H
9  * \brief One-dimensional heat conduction through a wall or roof slab.
10  *
11  * Adapted from the ERF-SLUCM branch's ERF_UCMSlabConduction.H (implicit
12  * Euler, Thomas algorithm, all-plus tridiagonal convention). The one change
13  * is the top boundary: the SLUCM solver takes a flux at the top, this one
14  * takes the skin temperature, which is what the face balance holds, and
15  * returns the conduction into the slab that results. The bottom boundary
16  * is the interior temperature of the building.
17  *
18  * Layers are uniform, ``dz = L / N``, with layer centres at
19  * ``(l + 1/2) dz`` below the skin; the skin sits half a layer above the
20  * first centre and the interior half a layer below the last, so both
21  * boundary fluxes use the half-layer spacing ``2 k / dz``.
22  */
23 namespace ibseb {
24 
25 /// Largest number of layers the stack arrays allow. The kernels keep three
26 /// such arrays per thread, so the bound is kept small for GPU occupancy; 32
27 /// layers resolve any wall (erf.ibseb.n_slab_layers is checked against it).
28 constexpr int SLAB_MAX_LAYERS = 32;
29 
30 /**
31  * Advance one slab column by one step and return the conduction into it.
32  *
33  * Solves, for layers ``l = 0 .. N-1`` with implicit Euler,
34  * ``rho_cp dz (T_l^{n+1} - T_l^n) / dt = flux_in - flux_out``, the fluxes
35  * between neighbouring centres being ``k (T_{l-1} - T_l) / dz``, the flux
36  * from the skin ``2 k (T_skin - T_0) / dz`` and the flux to the interior
37  * ``2 k (T_{N-1} - T_int) / dz``. Unconditionally stable.
38  *
39  * @param[in,out] T Layer temperatures [K], ``N`` values, top first.
40  * @param[in] T_skin Skin temperature at the top [K] (Dirichlet).
41  * @param[in] T_int Interior temperature at the bottom [K] (Dirichlet).
42  * @param[in] k Thermal conductivity [W/m/K].
43  * @param[in] rho_cp Volumetric heat capacity [J/m3/K].
44  * @param[in] dz Layer thickness [m].
45  * @param[in] dt Time step [s].
46  * @param[in] N Number of layers, at most SLAB_MAX_LAYERS.
47  * @return Conduction into the slab through the skin at the new time,
48  * ``2 k (T_skin - T_0^{n+1}) / dz`` [W/m2], positive into the slab.
49  */
50 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
53  amrex::Real dt, int N)
54 {
55  if (N < 1 || N > SLAB_MAX_LAYERS) return 0.0;
56  const amrex::Real Fourier = k * dt / (rho_cp * dz * dz); // interior Fourier number
57  // Coefficients of a l T_{l-1} + b_l T_l + c_l T_{l+1} = d_l (all-plus form).
59  // Row 0: skin above at half spacing (coefficient 2 Fourier), layer 1 below.
60  {
61  const amrex::Real b0 = 1.0 + 2.0 * Fourier + ((N > 1) ? Fourier : 2.0 * Fourier);
62  const amrex::Real c0 = (N > 1) ? -Fourier : 0.0;
63  const amrex::Real d0 = T[0] + 2.0 * Fourier * T_skin + ((N > 1) ? 0.0 : 2.0 * Fourier * T_int);
64  alpha[0] = c0 / b0;
65  gamma[0] = d0 / b0;
66  }
67  // Interior rows.
68  for (int l = 1; l < N - 1; ++l) {
69  const amrex::Real a = -Fourier, b = 1.0 + 2.0 * Fourier, c = -Fourier;
70  const amrex::Real bm = b - a * alpha[l - 1];
71  gamma[l] = (T[l] - a * gamma[l - 1]) / bm;
72  alpha[l] = c / bm;
73  }
74  // Last row (N > 1): interior below at half spacing (coefficient 2 Fourier).
75  if (N > 1) {
76  const int l = N - 1;
77  const amrex::Real a = -Fourier, b = 1.0 + Fourier + 2.0 * Fourier;
78  const amrex::Real bm = b - a * alpha[l - 1];
79  gamma[l] = (T[l] + 2.0 * Fourier * T_int - a * gamma[l - 1]) / bm;
80  alpha[l] = 0.0;
81  }
82  // Back substitution.
83  T[N - 1] = gamma[N - 1];
84  for (int l = N - 2; l >= 0; --l) { T[l] = gamma[l] - alpha[l] * T[l + 1]; }
85  return 2.0 * k * (T_skin - T[0]) / dz;
86 }
87 
88 /**
89  * Linear response of the implicit slab step to the skin temperature.
90  *
91  * The step of advance_slab_dirichlet() is linear in ``T_skin``, so the
92  * conduction it returns is ``G = a T_skin - b`` for the slab's current state,
93  * interior temperature and time step. The two coefficients come from two
94  * trial steps on copies of the column (``T_skin = 0`` and ``T_skin = 1``), the
95  * same arithmetic as the real step, so a balance solved with this response
96  * and then advanced with the resulting skin temperature closes to rounding.
97  * The column itself is not modified.
98  *
99  * @param[in] T Layer temperatures [K] at the start of the step.
100  * @param[out] a Slope of G with respect to T_skin [W/m2/K], positive.
101  * @param[out] b Offset: G at T_skin = 0 is -b [W/m2].
102  * Other arguments as in advance_slab_dirichlet().
103  */
104 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
107  amrex::Real dt, int N, amrex::Real& a, amrex::Real& b)
108 {
109  if (N < 1 || N > SLAB_MAX_LAYERS) { a = 0.0; b = 0.0; return; }
111  for (int l = 0; l < N; ++l) { W[l] = T[l]; }
112  const amrex::Real G0 = advance_slab_dirichlet(W, 0.0, T_int, k, rho_cp, dz, dt, N);
113  for (int l = 0; l < N; ++l) { W[l] = T[l]; }
114  const amrex::Real G1 = advance_slab_dirichlet(W, 1.0, T_int, k, rho_cp, dz, dt, N);
115  a = G1 - G0;
116  b = -G0;
117 }
118 
119 } // namespace ibseb
120 
121 #endif
amrex::Real gamma
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:9
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ T
Definition: ERF_IndexDefines.H:128
@ dz
Definition: ERF_AdvanceWDM6.cpp:272
Definition: ERF_IBSEBBalance.H:51
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real advance_slab_dirichlet(amrex::Real *T, amrex::Real T_skin, amrex::Real T_int, amrex::Real k, amrex::Real rho_cp, amrex::Real dz, amrex::Real dt, int N)
Definition: ERF_IBSEBSlab.H:51
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void slab_skin_response(const amrex::Real *T, amrex::Real T_int, amrex::Real k, amrex::Real rho_cp, amrex::Real dz, amrex::Real dt, int N, amrex::Real &a, amrex::Real &b)
Definition: ERF_IBSEBSlab.H:105
constexpr int SLAB_MAX_LAYERS
Definition: ERF_IBSEBSlab.H:28
real(kind=kind_phys), parameter, private alpha
Definition: ERF_module_mp_wdm6.F90:62