Skin temperature
This page is a work in progress. If you have any questions or notice any errors, please raise an issue.
Overview
The skin temperature $T_s$ is the effective radiative temperature of the land surface. It represents the temperature at which the surface emits longwave radiation and is the temperature that directly controls evaporation and sensible heat flux in the surface energy balance.
Skin temperature differs from the air temperature at height $z$ by the effects of turbulent mixing and surface properties. It is also not necessarily the same as the subsurface ground temperature $T_g$ (the temperature of the uppermost soil or snow layer) due to the thermal resistance between the surface and the ground.
The skin temperature $T_s$ is determined by balancing the energy arriving at the skin from below (the ground heat flux $G$) with the radiative and turbulent losses above,
\[R_{\text{net}}(T_s) + H_s(T_s) + H_l(T_s) = G(T_s, T_g)\]
where $R_{\text{net}}$ is the net radiation budget, $H_s$ is the sensible heat flux, and $H_l$ is the latent heat flux from sublimation and evapotranspiration.
Implicit skin temperature
Terrarium.ImplicitSkinTemperature — Type
struct ImplicitSkinTemperature{NF, Solver} <: Terrarium.AbstractSkinTemperature{NF}Scheme for an implicit skin temperature $T_s$ satisfying:
\[R_{\text{net}}(T_s) + H_s(T_s) + H_l(T_s) - (1 - f_{\text{snow}})\, G(T_s, T_g) - f_{\text{snow}}\, S(T_s, T_{\text{snow}}) = 0\]
where $R_{\text{net}}$ is the net radiation budget, $H_s$ is the sensible heat flux, $H_l$ is the latent heat flux from sublimation and evapotranspiration, $G$ is the conductive flux from the skin into the snow-free ground ($T_g$ its temperature), $S$ is the conductive flux from the skin into the top of the snowpack over the snow-covered fraction ($T_{\text{snow}}$ its temperature), and $f_{\text{snow}}$ is the snow-covered area fraction ($f_{\text{snow}} = 0$ and $S$ absent without snow). $G$ and $S$ are each computed from their own unblended conduction target (see ground_thermal_interface and snow_thermal_interface).
Properties:
κₛ: Assumed thermal conductivity at the surfacesolver: Numerical solver for the implicit skin temperature
Skin temperature can be determined instantaneously at each time step by finding the roots of the above nonlinear energy balance equation at each grid point. Given a trial skin temperature $T_s$, the demanded flux required to close the energy balance is
\[G^\star = R_{\text{net}}(T_s) + H_s(T_s) + H_l(T_s)\]
This demanded flux is equated to the area-weighted sum of two unblended conductive targets: the flux $G$ between the skin and the snow-free ground surface (uppermost soil layer) at temperature $T_g$, across the half-cell of thickness $\Delta z_1 / 2$,
\[G(T_s, T_g) = \frac{2 \kappa_s}{\Delta z_1} (T_g - T_s)\]
and, over the snow-covered fraction $f_{\text{snow}}$, the flux $S$ between the skin and the top of the snowpack at temperature $T_{\text{snow}}$, across its (floored) depth $d_{\text{snow}}$,
\[S(T_s, T_{\text{snow}}) = \frac{2 \kappa_{\text{snow}}}{d_{\text{snow}}} (T_{\text{snow}} - T_s)\]
Keeping $G$ and $S$ unblended (rather than averaging the ground and snow conduction targets into a single cell-wide target) prevents the flux delivered to a thin or patchy snowpack from being diluted by the bare-ground share. Setting the demanded flux equal to the area-weighted conductive fluxes and solving for $T_s$ (both conductive terms are affine in $T_s$, so this is an exact linear solve) yields
\[T_s^\star = \frac{(1 - f_{\text{snow}}) \frac{2\kappa_s}{\Delta z_1} T_g + f_{\text{snow}} \frac{2\kappa_{\text{snow}}}{d_{\text{snow}}} T_{\text{snow}} - G^\star}{(1 - f_{\text{snow}}) \frac{2\kappa_s}{\Delta z_1} + f_{\text{snow}} \frac{2\kappa_{\text{snow}}}{d_{\text{snow}}}}\]
which reduces to $T_s^\star = T_g - G^\star \Delta z_1 / (2\kappa_s)$ without snow ($f_{\text{snow}} = 0$). The residual driving the nonlinear solve is the difference between the current and implied skin temperatures,
\[r(T_s) = T_s - T_s^\star\]
The root $r(T_s) = 0$ is the skin temperature at which the conductive fluxes balance the radiative and turbulent fluxes, i.e. the solution of the surface energy balance. Note that the stored ground_heat_flux field is not $G^\star$ but the explicit conductive flux $G(T_s, T_g)$ evaluated at the converged $T_s$; the two coincide only at convergence. Both the demand $G^\star$ and the stored flux are owned by the ground heat flux sub-process.
The solve is performed by solve_skin_temperature!, which wraps compute_skin_temperature_residual! in an ObjectiveFunction targeting the skin_temperature field and hands it to the configured solver. The default solver, default_skin_temperature_solver, is a Newton-Raphson (RootSolver provided by RootSolvers.jl) with a fixed iteration budget of $n = 5$ to balance accuracy with GPU efficiency. The prognostic skin_temperature is seeded with the current ground_temperature by initialize! so that the iteration starts from a physically sensible guess close to the root. After the solve converges, the surface energy fluxes are recomputed from the final skin temperature.
Prescribed skin temperature
Terrarium.PrescribedSkinTemperature — Type
struct PrescribedSkinTemperature{NF} <: Terrarium.AbstractSkinTemperature{NF}Simple scheme for prescribed skin temperatures from input variables.
Properties:
κₛ: Assumed thermal conductivity at the surface
When the skin temperature is prescribed, it is supplied directly as the skin_temperature input field (for example by an external coupler) and no nonlinear solve is performed: solve_skin_temperature! is a no-op for PrescribedSkinTemperature. The fused surface-energy-balance kernel then evaluates the radiative and turbulent fluxes from the prescribed skin temperature. Paired with DiagnosedGroundHeatFlux, the ground heat flux is closed as the residual
\[G = R_{\text{net}} + H_s + H_l\]
This configuration can be used to defer the surface energy balance to an external solver, often in tandem with PrescribedTurbulentFluxes when the turbulent fluxes are also supplied as inputs (see Surface energy balance).
Process interface
Terrarium.initialize! — Method
initialize!(state, grid, ::ImplicitSkinTemperature, args...)Seed the prognostic skin_temperature with the current ground_temperature so the implicit nonlinear solve starts from a physically sensible guess close to the root.
Methods
Terrarium.skin_temperature — Method
skin_temperature(i, j, grid, fields, ::AbstractSkinTemperature)Return the current skin temperature at the given indices.
Terrarium.ground_thermal_interface — Function
ground_thermal_interface(
i,
j,
grid,
fields,
skinT::ImplicitSkinTemperature
) -> Tuple{Any, Any, Any}Return the conduction target (Tg, κ, Δz) for the snow-free ground: the uppermost ground layer's ground_temperature, the assumed surface conductivity κₛ, and the top ground cell thickness. This is always the unblended ground-only target, used both when there is no snow and (weighted by 1 - f_snow) for the bare-ground share of the skin-temperature solve when there is.
Terrarium.snow_thermal_interface — Function
snow_thermal_interface(
i,
j,
grid,
fields,
snow::Terrarium.AbstractSnow,
constants::PhysicalConstants
) -> Tuple{Any, Any, Any}Return the conduction target (Tsnow, κsnow, dsnow) for the snow-covered fraction: the snow's own (bulk) temperature, its thermal conductivity recovered from the density scheme, and its depth floored at min_snow_conduction_thickness. Without snow (snow === nothing), returns a placeholders with κsnow = 0 such that the snow conductive heat flux reduces to zero.
Terrarium.default_skin_temperature_solver — Function
default_skin_temperature_solver(
::Type{NF}
) where NF -> Terrarium.RootSolver{NF, RootSolvers.NewtonsMethod{T}, RootSolvers.CompactSolution, RootSolvers.ResidualTolerance{NF}} where {NF, T<:Union{Real, AbstractArray}}Construct the default solver for the implicit skin temperature: a Newton root-finder (RootSolver backed by RootSolvers.jl) with a small iteration budget.
Kernel functions
Terrarium.compute_skin_temperature — Method
compute_skin_temperature(
i,
j,
grid,
fields,
skinT::ImplicitSkinTemperature{NF},
seb::Terrarium.AbstractSurfaceEnergyBalance,
args...
) where NFInvert the (linear) ground-only conduction relation for the implicit skin temperature Ts given the atmosphere-side demanded flux G* (see compute_ground_heat_flux_demand): Ts = Tg − G*/(2κg/Δzg). This is the no-snow special case (f_snow = 0) of the snow-aware method below; it is a separate method (rather than a default snow = nothing) purely so it can skip the unused snow_thermal_interface/snow_cover_fraction calls.
Terrarium.compute_skin_temperature — Method
compute_skin_temperature(
i,
j,
grid,
fields,
skinT::ImplicitSkinTemperature{NF},
seb::Terrarium.AbstractSurfaceEnergyBalance,
constants::PhysicalConstants,
snow::Terrarium.AbstractSnow
) where NFInvert the (linear) area-weighted conduction relation for the implicit skin temperature Ts given the atmosphere-side demanded flux G (see compute_ground_heat_flux_demand), by equating G to the area-weighted sum of the unblended ground and snow-top conductive fluxes, (1 − f_snow)·2κg(Tg − Ts)/Δzg + f_snow·2κsnow(Tsnow − Ts)/dsnow.
Terrarium.compute_skin_temperature_residual! — Function
compute_skin_temperature_residual!(
out,
i,
j,
grid,
fields,
skinT::ImplicitSkinTemperature,
seb::Terrarium.AbstractSurfaceEnergyBalance,
constants::PhysicalConstants,
atmos::Terrarium.AbstractAtmosphere
)
compute_skin_temperature_residual!(
out,
i,
j,
grid,
fields,
skinT::ImplicitSkinTemperature,
seb::Terrarium.AbstractSurfaceEnergyBalance,
constants::PhysicalConstants,
atmos::Terrarium.AbstractAtmosphere,
hydrology::Union{Nothing, Terrarium.AbstractSurfaceHydrology}
)
compute_skin_temperature_residual!(
out,
i,
j,
grid,
fields,
skinT::ImplicitSkinTemperature,
seb::Terrarium.AbstractSurfaceEnergyBalance,
constants::PhysicalConstants,
atmos::Terrarium.AbstractAtmosphere,
hydrology::Union{Nothing, Terrarium.AbstractSurfaceHydrology},
snow::Union{Nothing, Terrarium.AbstractSnow}
)Surface-energy-balance residual at grid cell i, j, in temperature space: Ts_prev − Ts_implicit, where Ts_implicit is the exact conduction-side inverse (see compute_skin_temperature) of the atmosphere-side demanded flux supplied by the ground heat flux sub-process (see compute_ground_heat_flux_demand), which for DiagnosedGroundHeatFlux is G_demand = R_net(Ts_prev) + H(Ts_prev) + LE(Ts_prev).
Terrarium.solve_skin_temperature! — Function
solve_skin_temperature!(
out,
i,
j,
grid,
fields,
skinT::ImplicitSkinTemperature,
seb::Terrarium.AbstractSurfaceEnergyBalance,
args...
)Run a full nonlinear solve to determine the skin_temperature at grid cell i, j that solves the surface energy balance.