Layered snow + ice thermodynamics — implementation notes
This page documents the snow-surface energy balance and the closed-form implicit solve for end-of-step ice concentration used by _layered_thermodynamic_time_step!. It assumes familiarity with the slab mass balance described in the Thermodynamics chapter.
Column geometry
Each cell contains an ice-covered fraction $\aleph$; snow sits on the ice only. The prognostic variables are
\[h_i\]
— ice thickness on the ice-covered fraction (m, per-ice)\[\aleph\]
— ice concentration (unitless, per-cell area fraction)\[h_s\]
— snow thickness on the ice-covered fraction (m, per-ice)
with the ice-volume invariant $V = h_i \, \aleph$ tracked per unit cell area. Snow volume per unit cell area is $h_s \, \aleph$ and is conserved across changes in $\aleph$ by the kernel's $h_s \leftarrow h_s \, \aleph^n / \aleph^{n+1}$ rescale.
Per-ice vs. per-cell fluxes
Two conventions coexist and must be treated consistently:
\[Q_{ui}\]
(top external heat flux) and $Q_{bi}$ (bottom external heat flux) are delivered by the coupler per unit cell area (radiation and turbulent fluxes $\times \aleph$ on the ice path, interface heat $\times \aleph$ on the ocean path).\[Q_{is}\]
(column conductive flux $(T_b - T_u)/R$, with $R = h_s/k_s + h_i/k_i$) is intrinsically per unit ice area: the thermal resistance only applies where ice exists.\[Q_{ii}\]
(ice-only internal conductive flux) is similarly per-ice.
Snow-surface energy balance
The snow surface sits on the ice-covered fraction only, so the surface energy balance is a per-ice balance:
\[\delta Q = \frac{Q_{ui}}{\aleph^n} - Q_{is}, \qquad Q_s = \min\!\left(\max(0,\, -\delta Q),\; \frac{\rho_s \, \mathscr{L} \, h_s^n}{\Delta t}\right).\]
\[Q_s\]
(positive when melting) is the per-ice latent power absorbed by snow melt, giving a thickness rate $G_s^- = Q_s / (\rho_s \, \mathscr{L})$.
The atmospheric flux $Q_{ui}$ passed by the coupler is per-cell, so we divide by $\aleph^n$ before comparing with $Q_{is}$. When $\aleph^n = 1$ this reduces to the original formulation; when $\aleph^n < 1$ it avoids the spurious factor of $\aleph^n$ that would otherwise suppress the snow melt rate.
Implicit concentration update
The slab mass balance and the ProportionalEvolution concentration rule are both linear in the end-of-step concentration $\aleph^{n+1}$:
\[\partial_t V = \frac{(Q_{ui} + Q_s \, \aleph^{n+1}) - Q_{bi}}{\rho_i \, \mathscr{L}} \;\equiv\; \alpha + \beta \, \aleph^{n+1},\]
with
\[\alpha = \frac{Q_{ui} - Q_{bi}}{\rho_i \, \mathscr{L}}, \qquad \beta = \frac{Q_s}{\rho_i \, \mathscr{L}},\]
and
\[\aleph^{n+1} = \aleph^n + \Delta t \, C \, \partial_t V,\]
with
\[C = \begin{cases} \dfrac{\aleph^n}{2 \, h_i^n} & \text{if } \partial_t V < 0 \quad \text{(melt)} \\[6pt] \dfrac{1 - \aleph^n}{h^c} & \text{if } \partial_t V \ge 0 \quad \text{(freeze)}. \end{cases}\]
Defining $K = \Delta t \, C$, the fixed point $\aleph^{n+1} \, (1 - K \beta) = \aleph^n + K \alpha$ has the closed-form solution
\[\boxed{\,\aleph^{n+1} = \frac{\aleph^n + K \alpha}{1 - K \beta}\,}.\]
Branch selection
The correct branch (melt vs. freeze) depends on the sign of $\partial_t V$ at the solution, which isn't known up front. The kernel computes both branches, $\aleph^m$ (melt) and $\aleph^f$ (freeze), and picks the one where $\operatorname{sign}(\partial_t V(\aleph^{n+1}))$ is consistent with the branch assumption:
- if $\alpha + \beta \, \aleph^m < 0$, use the melt-branch solution $\aleph^m$
- otherwise, use the freeze-branch solution $\aleph^f$
This is branchless (via ifelse) and correct at the $\alpha \approx 0$ boundary where the first-guess sign could be misleading. On ocean scales $|K \beta| \lesssim 10^{-6}$, so the sign rarely flips between guess and solution; the test is defensive for the atypical case where atmospheric heating almost exactly cancels the column conductive flux.
NaN guards
All divisions are protected:
\[Q_{ui} / \aleph^n\]
falls back to $0$ if $\aleph^n \le 0$\[\aleph^n / (2 \, h_i^n)\]
falls back to $0$ if $h_i^n \le 0$\[(1 - \aleph^n) / h^c\]
falls back to $0$ if $h^c \le 0$\[1 / (1 - K \beta)\]
falls back to the numerator $\aleph^n + K \alpha$ if the denominator is within $\varepsilon$ of zero
The $Q_s$ cap $\rho_s \, \mathscr{L} \, h_s^n / \Delta t$ keeps $K \beta$ comfortably below 1 in practice; the guard is a defensive fallback rather than a routine path.
Edge cases deferred to ice_volume_update
The closed-form solves the implicit $\aleph^{n+1}$ update but doesn't apply volume clipping, ridging, or pathological-case handling. After the solve, the kernel calls ice_melt_freeze_tendency once at the self-consistent $Q_{ui}^{\mathrm{eff}} = Q_{ui} + Q_s \, \aleph^{n+1}$ and passes the resulting $\partial_t V$ through ice_volume_update, which handles:
\[V^{n+1} = \max(0,\, V^n + \Delta t \, \partial_t V)\]
when the ice melts completely- ridging cap $\aleph^+ \to 1$ with compensating thickness adjustment
- degenerate states ($\aleph \le 0$, $\partial_t V = 0$, $h^+ = 0$)
Energy conservation
The implicit solve eliminates the per-step $Q_s \, (1 - \aleph^{n+1}) \, \Delta t \, A$ leak that an explicit or single-pass formulation would incur.