Snow model implementation#123
Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements a snow model on top of the existing 0-layer slab ice model in ClimaSeaIce. The snow layer is thermodynamically coupled to the ice using a resistors-in-series approach for conductive flux, while remaining transparent to the ice model via prescribed interface temperature. The PR also renames SlabSeaIceThermodynamics to SlabThermodynamics (with a backward-compatibility alias), generalizes PhaseTransitions field names from ice-specific to generic (ice_density → density, ice_heat_capacity → heat_capacity), and fixes the latent_heat formula to use solid density instead of liquid density, matching the documented equation.
Changes:
- Adds optional snow layer (
snow_thermodynamics,snow_precipitation) toSeaIceModel, with layered thermodynamic coupling, advection, flooding, and accumulation. - Renames
SlabSeaIceThermodynamics→SlabThermodynamicsand generalizesPhaseTransitionsfields; fixeslatent_heatformula to match documentation. - Updates all examples to compare bare-ice vs. snow-covered simulations and adds comprehensive tests for snow physics and energy conservation.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/SeaIceThermodynamics/SeaIceThermodynamics.jl |
Renames PhaseTransitions fields, fixes latent_heat formula |
src/SeaIceThermodynamics/slab_sea_ice_thermodynamics.jl |
Renames struct, adds SlabSnowThermodynamics constructor, removes thermodynamic_tendency field |
src/SeaIceThermodynamics/slab_heat_and_tracer_fluxes.jl |
Adds IceSnowConductiveFlux, ice_snow_conductive_flux, and interface_temperature |
src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl |
Variable renames (cosmetic) |
src/SeaIceThermodynamics/thermodynamic_time_step.jl |
Adds layered snow+ice kernel, ice_volume_update, snow accumulation, flooding |
src/sea_ice_model.jl |
Adds snow_thickness, snow_thermodynamics, snow_precipitation to model; wires layered coupling |
src/sea_ice_fe_step.jl |
Forward Euler snow stepping support |
src/sea_ice_rk_substep.jl |
Runge-Kutta snow stepping support |
src/tracer_tendency_kernel_functions.jl |
Snow advection tendency via div_Uℵh |
src/ClimaSeaIce.jl |
Exports new types |
test/test_snow_thermodynamics.jl |
New: comprehensive snow tests |
test/test_energy_conservation.jl |
New: energy conservation tests with/without snow |
test/test_time_stepping.jl |
Adds snow to time-stepping test matrix |
test/test_sea_ice_advection.jl |
Snow advection test |
test/test_checkpointing.jl |
Snow checkpointing test |
test/runtests.jl |
Registers new test groups |
.github/workflows/ci.yml |
Adds energy_conservation CI job |
examples/freezing_bucket.jl |
Bare vs snow comparison |
examples/freezing_of_a_lake.jl |
Bare vs snow comparison with energy budgets |
examples/melting_in_spring.jl |
Bare vs snow comparison |
examples/arctic_basin_seasonal_cycle.jl |
Bare vs snow with albedo dependence |
examples/perpetual_night.jl |
Rename to SlabThermodynamics |
docs/src/timestepping.md |
Updates show output for snow |
docs/src/physics/thermodynamics.md |
Adds snow thermodynamics documentation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
I think the main issue with this PR is that it does not implement an interface for snow models. Instead it hard code a snow model. This can work if we take the approach that we only want to use a 1-layer snow model with a 0 layer sea ice thermodynamics. If we take that stance then a more general interface for snow modeling would be tackled in the future. However, I think more generally what we need are interfaces for both thermodynamics and snow. In the long run, we probably want to use a snow model that comes from a land modeling package like ClimaLand or Terrarium. |
|
I tried to keep the implementation as general as possible such that
but yeah, this PR hinges on the fact that the possibility of a snow model is embedded directly inside the sea ice model. I think the difference with what you are proposing is to have a completely separate snow model? Something like Am I understanding it correctly? |
|
We could probably think at different thermodynamic formulations to see if they fit within this framework easily? |
| # Backward compatibility alias | ||
| const SlabSeaIceThermodynamics = SlabThermodynamics |
There was a problem hiding this comment.
I understand this to be a Claude-ism who loves backwards compatibility, but I don't think we should maintain this
There was a problem hiding this comment.
ah, aftrer reading through the PR I understand this is also wrong. You can define a constructor
sea_ice_slab_thermodynamics| internal_heat_flux = ConductiveFlux(eltype(grid), conductivity=2), | ||
| phase_transitions = PhaseTransitions(eltype(grid)), | ||
| concentration_evolution = ProportionalEvolution()) | ||
| function SlabThermodynamics(grid; |
There was a problem hiding this comment.
for slab models, don't snow and sea ice have identical thermodynamics structure? Why do we need a new type for snow?
| conductivity = 0.31 W/(m K), density = 330 kg/m³, heat capacity = 2090 J/(kg K), | ||
| and latent heat = 334000 J/kg. | ||
| """ | ||
| function SlabSnowThermodynamics(grid; |
There was a problem hiding this comment.
| function SlabSnowThermodynamics(grid; | |
| function snow_slab_thermodynamics(grid; |
after combing through the PR looking for the type SnowSlabThermodynamics I finally realized that this is not a type... and you are reusing SlabThermodynamics for snow and sea ice (which is great, provided we use the right names)
|
unfortunately I don't appear to be able to suggest changes and also comment :-( so I have to "request changes |
Your top level description should mention prominently that you have generalized Now that I understand the PR better I think it is a good addition. We can reserve a more general infrastructure for the cases where both snow and sea ice have a prognostic temperature/energy (eg going beyond mere "slab" models). For the slab models, simplicity is useful which I think motivates non-generalizability. For the record, what I meant is that there would be functions that could dispatch on the type of snow model being used. However, the slab model is an especially simple case. We would probably need a case with prognostic temperature/energy to motivate the implementation of a more generic interface (and I suspect things get a lot tricker as well, due to the need for an implicit solve that spans all layers). |
|
Doesn't this have implications for the skin temperature solver? Ie we need to solve through both layers and the solution includes both the sea-ice/snow interface temperature as well as the snow/air interface temperature. Does this come into play in the examples as well? There is a big diff for |
Indeed, for these simple slab models the implicit solve is rather simple. At the moment, the slab snow is aware of the sea ice and not the other way around, so basically the snow solves for the top temperature with the knowledge of the sea ice state and then computes the sea-ice snow interface temperature analytically which passes to the sea ice as a |
What happens when there is no snow. |
|
I am not sure if the same approach could be used also with multi-layer sea-ice where snow sees only the first sea ice layer from the top or if we need to do an implicit solve over the whole column to find the top temperature |
|
Thank you. It's clear how this captures the "no snow" case identically as if the snow were not included at all. Please put the algorithm + math in the top-level of this PR and in the docs! |
|
@glwagner I should have addressed all the comments |
| ice_density :: FT | ||
| ice_heat_capacity :: FT | ||
| density :: FT | ||
| heat_capacity :: FT |
There was a problem hiding this comment.
dont we need categories for snow and ice? confused what is going on here.
glwagner
left a comment
There was a problem hiding this comment.
semantics is still a little messy, but overall good
confused about PhaseTransitions though. I think we need a modular design for thermodynamic properties, with PhaseTransitions there is repeated info it seems. Eg if we have slab snow and slab ice, then the properties of liquid water are repeated. They can even be inconsistent. So that's a bad design.
We can improve in the future. It might be worth looking at now Breeze does this.
One thing to note; the thermodynamic properties should ultimately represent microscopic properties (eg microscopic density, etc). There is also a bulk density (for snow) which depends on porosity and would dynamically evolve.
The design should be enlightened to all of these facts.
|
Sounds good. I ll merge this and start a PR to generalize the phase transitions. Maybe we want only one |


Snow model implementation
This PR implements a layered snow model on top of the 0-layer slab ice model.
Algorithm
The snow layer sits on top of the ice as an independent thermodynamic layer. The ice model is completely unaware of the snow — only the boundary conditions change. The coupling is one-way: snow determines the ice top boundary condition.
Thermodynamic time step:
PrescribedTemperaturetop boundary conditionMath
Combined conductive flux (thermal resistors in series):
where h_s, k_s ≈ 0.31 W/(m·K) are the snow thickness and conductivity, and h_i, k_i ≈ 2 W/(m·K) are the ice thickness and conductivity. When h_s = 0, this reduces to the bare-ice conductive flux k_i (T_b - T_u) / h_i.
Interface temperature (from linear temperature profile through resistors):
where R_i = h_i / k_i and R_s = h_s / k_s. When h_s = 0, R_s = 0 and T_si = T_u — the ice sees its own surface temperature, identical to the no-snow case.
Surface melt energy partition: When T_u hits the melting point, the excess energy δQ = Q_external - Q_conductive first melts snow (up to its capacity ρ_s · L_s · h_s / Δt), and any remainder melts ice from the top.
Snow-ice formation (flooding): When the snow load pushes ice below the waterline (negative freeboard), seawater floods the snow:
The flooding conversion is energy-conserving.
Snow thickness evolution:
where P_s is precipitation rate (kg/m²/s), and the last term conserves snow volume when ice concentration ℵ changes.
"No snow" case
The formulation degrades gracefully when h_s = 0:
Design
snow_thickness(shares concentration ℵ with sea ice)IceSnowConductiveFluxtype handles the combined thermal resistancesnow_thermodynamicsis provided toSeaIceModel, the constructor automatically wires the layered couplingExamples
The thermodynamic examples include both "bare" (ice only) and "snow" modes showing that snow insulation delays both melting and freezing.