From 4471c04708a8232e12e76127ca0897da022b22ee Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 25 Feb 2025 12:30:28 +0100 Subject: [PATCH 001/108] try it out --- .../elasto_visco_plastic_rheology.jl | 7 ++--- .../explicit_momentum_equations.jl | 13 ++++------ .../momentum_tendencies_kernel_functions.jl | 26 +++++++++---------- .../sea_ice_external_stress.jl | 8 +++--- .../split_explicit_momentum_equations.jl | 12 ++++----- src/sea_ice_advection.jl | 1 + 6 files changed, 32 insertions(+), 35 deletions(-) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index 2648a378..edb3fe1a 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -97,16 +97,17 @@ function required_auxiliary_fields(r::ElastoViscoPlasticRheology, grid) σ₂₂ = Field{Center, Center, Nothing}(grid) σ₁₂ = Field{Face, Face, Nothing}(grid) - uⁿ = Field{Face, Center, Nothing}(grid) - vⁿ = Field{Center, Face, Nothing}(grid) P = Field{Center, Center, Nothing}(grid) - α = Field{Center, Center, Nothing}(grid) # Dynamic substeps a la Kimmritz et al (2016) ζ = Field{Center, Center, Nothing}(grid) Δ = Field{Center, Center, Nothing}(grid) # An initial (safe) educated guess fill!(α, r.max_relaxation_parameter) + α = Field{Face, Face, Nothing}(grid) # Dynamic substeps a la Kimmritz et al (2016) + uⁿ = Field{Face, Face, Nothing}(grid) + vⁿ = Field{Face, Face, Nothing}(grid) + return (; σ₁₁, σ₂₂, σ₁₂, ζ, Δ, α, uⁿ, vⁿ, P) end diff --git a/src/SeaIceMomentumEquations/explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/explicit_momentum_equations.jl index 2a1bcf85..bdc21fc2 100644 --- a/src/SeaIceMomentumEquations/explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/explicit_momentum_equations.jl @@ -43,11 +43,9 @@ end i, j = @index(Global, NTuple) - ℵᶠᶜ = ℑxᶠᵃᵃ(i, j, 1, grid, fields.ℵ) - ℵᶜᶠ = ℑyᵃᶠᵃ(i, j, 1, grid, fields.ℵ) - mᶠᶜ = ℑxᶠᵃᵃ(i, j, 1, grid, ice_mass, fields.h, fields.ℵ, fields.ρ) - mᶜᶠ = ℑyᵃᶠᵃ(i, j, 1, grid, ice_mass, fields.h, fields.ℵ, fields.ρ) - + ℵᶠᶠ = ℑxyᶠᶠᵃ(i, j, 1, grid, fields.ℵ) + mᶠᶠ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, fields.h, fields.ℵ, fields.ρ) + # Implicit part of the stress that depends linearly on the velocity τuᵢ = ( implicit_τx_coefficient(i, j, 1, grid, bottom_stress, clock, fields) + implicit_τx_coefficient(i, j, 1, grid, top_stress, clock, fields)) / mᶠᶜ * ℵᶠᶜ @@ -62,10 +60,9 @@ end uᶠ = free_drift_u(i, j, 1, grid, ocean_velocities) vᶠ = free_drift_v(i, j, 1, grid, ocean_velocities) - sea_ice = (mᶠᶜ ≥ minimum_mass) & (ℵᶠᶜ ≥ minimum_concentration) + sea_ice = (mᶠᶠ ≥ minimum_mass) & (ℵᶠᶠ ≥ minimum_concentration) + u[i, j, 1] = ifelse(sea_ice, uᴰ, uᶠ) - - sea_ice = (mᶜᶠ ≥ minimum_mass) & (ℵᶜᶠ ≥ minimum_concentration) v[i, j, 1] = ifelse(sea_ice, vᴰ, vᶠ) end end diff --git a/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl b/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl index 43d57cfc..e4ef8956 100644 --- a/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl +++ b/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl @@ -1,4 +1,4 @@ -using Oceananigans.Coriolis: y_f_cross_U, x_f_cross_U +using Oceananigans.Coriolis: y_f_cross_U, x_f_cross_U, fᶠᶠᵃ using Oceananigans.ImmersedBoundaries: active_linear_index_to_tuple """compute explicit ice u-velocity tendencies""" @@ -15,18 +15,17 @@ using Oceananigans.ImmersedBoundaries: active_linear_index_to_tuple h = model_fields.h ℵ = model_fields.ℵ ρ = model_fields.ρ - U = (u = model_fields.u, v = model_fields.v) # Ice mass (per unit area) interpolated on u points - ℵᵢ = ℑxᶠᵃᵃ(i, j, 1, grid, ℵ) - mᵢ = ℑxᶠᵃᵃ(i, j, 1, grid, ice_mass, h, ℵ, ρ) + ℵᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ℵ) + mᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, h, ℵ, ρ) - Gᵁ = ( - x_f_cross_U(i, j, 1, grid, coriolis, U) - + explicit_τx(i, j, 1, grid, u_top_stress, clock, model_fields) / mᵢ * ℵᵢ - + explicit_τx(i, j, 1, grid, u_bottom_stress, clock, model_fields) / mᵢ * ℵᵢ - + ∂ⱼ_σ₁ⱼ(i, j, 1, grid, rheology, clock, model_fields) / mᵢ - + immersed_∂ⱼ_σ₁ⱼ(i, j, 1, grid, u_immersed_bc, rheology, clock, model_fields) / mᵢ - + sum_of_forcing_u(i, j, 1, grid, rheology, u_forcing, model_fields, Δt)) # sum of user defined forcing and possibly other forcing terms that are rheology-dependent + @inbounds Gᵁ = ( + fᶠᶠᵃ(i, j, 1, grid, coriolis) * model_fields.v[i, j, 1] + + explicit_τx(i, j, 1, grid, u_top_stress, clock, model_fields) / mᵢ * ℵᵢ + + explicit_τx(i, j, 1, grid, u_bottom_stress, clock, model_fields) / mᵢ * ℵᵢ + + ∂ⱼ_σ₁ⱼ(i, j, 1, grid, rheology, clock, model_fields) / mᵢ + + immersed_∂ⱼ_σ₁ⱼ(i, j, 1, grid, u_immersed_bc, rheology, clock, model_fields) / mᵢ + + sum_of_forcing_u(i, j, 1, grid, rheology, u_forcing, model_fields, Δt)) # sum of user defined forcing and possibly other forcing terms that are rheology-dependent Gᵁ = ifelse(mᵢ ≤ 0, zero(grid), Gᵁ) @@ -47,13 +46,12 @@ end h = model_fields.h ℵ = model_fields.ℵ ρ = model_fields.ρ - U = (u = model_fields.u, v = model_fields.v) # Ice mass (per unit area) interpolated on v points - ℵᵢ = ℑyᵃᶠᵃ(i, j, 1, grid, ℵ) - mᵢ = ℑyᵃᶠᵃ(i, j, 1, grid, ice_mass, h, ℵ, ρ) + ℵᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ℵ) + mᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, h, ℵ, ρ) - Gⱽ = ( - y_f_cross_U(i, j, 1, grid, coriolis, U) + Gⱽ = ( - fᶠᶠᵃ(i, j, 1, grid, coriolis) * model_fields.u[i, j, 1] + explicit_τy(i, j, 1, grid, v_top_stress, clock, model_fields) / mᵢ * ℵᵢ + explicit_τy(i, j, 1, grid, v_bottom_stress, clock, model_fields) / mᵢ * ℵᵢ + ∂ⱼ_σ₂ⱼ(i, j, 1, grid, rheology, clock, model_fields) / mᵢ diff --git a/src/SeaIceMomentumEquations/sea_ice_external_stress.jl b/src/SeaIceMomentumEquations/sea_ice_external_stress.jl index be7391a6..bd4f0061 100644 --- a/src/SeaIceMomentumEquations/sea_ice_external_stress.jl +++ b/src/SeaIceMomentumEquations/sea_ice_external_stress.jl @@ -81,25 +81,25 @@ Adapt.adapt_structure(to, τ::SemiImplicitStress) = @inline function explicit_τx(i, j, k, grid, τ::SemiImplicitStress, clock, fields) uₑ = @inbounds τ.uₑ[i, j, k] Δu = @inbounds τ.uₑ[i, j, k] - fields.u[i, j, k] - Δv = ℑxyᶠᶜᵃ(i, j, k, grid, τ.vₑ) - ℑxyᶠᶜᵃ(i, j, k, grid, fields.v) + Δv = @inbounds τ.vₑ[i, j, k] - fields.v[i, j, k] return τ.ρₑ * τ.Cᴰ * sqrt(Δu^2 + Δv^2) * uₑ end @inline function explicit_τy(i, j, k, grid, τ::SemiImplicitStress, clock, fields) vₑ = @inbounds τ.vₑ[i, j, k] Δv = @inbounds τ.vₑ[i, j, k] - fields.v[i, j, k] - Δu = ℑxyᶜᶠᵃ(i, j, k, grid, τ.uₑ) - ℑxyᶜᶠᵃ(i, j, k, grid, fields.u) + Δu = @inbounds τ.uₑ[i, j, k] - fields.u[i, j, k] return τ.ρₑ * τ.Cᴰ * sqrt(Δu^2 + Δv^2) * vₑ end @inline function implicit_τx_coefficient(i, j, k, grid, τ::SemiImplicitStress, clock, fields) Δu = @inbounds τ.uₑ[i, j, k] - fields.u[i, j, k] - Δv = ℑxyᶠᶜᵃ(i, j, k, grid, τ.vₑ) - ℑxyᶠᶜᵃ(i, j, k, grid, fields.v) + Δv = @inbounds τ.vₑ[i, j, k] - fields.v[i, j, k] return τ.ρₑ * τ.Cᴰ * sqrt(Δu^2 + Δv^2) end @inline function implicit_τy_coefficient(i, j, k, grid, τ::SemiImplicitStress, clock, fields) - Δu = ℑxyᶜᶠᵃ(i, j, k, grid, τ.uₑ) - ℑxyᶜᶠᵃ(i, j, k, grid, fields.u) + Δu = @inbounds τ.uₑ[i, j, k] - fields.u[i, j, k] Δv = @inbounds τ.vₑ[i, j, k] - fields.v[i, j, k] return τ.ρₑ * τ.Cᴰ * sqrt(Δu^2 + Δv^2) end diff --git a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl index 633f72ec..d4efe6d0 100644 --- a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl @@ -115,10 +115,10 @@ end i, j = @index(Global, NTuple) - mᵢ = ℑxᶠᵃᵃ(i, j, 1, grid, ice_mass, model_fields.h, model_fields.ℵ, model_fields.ρ) - ℵᵢ = ℑxᶠᵃᵃ(i, j, 1, grid, model_fields.ℵ) + mᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, model_fields.h, model_fields.ℵ, model_fields.ρ) + ℵᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, model_fields.ℵ) - Δτ = compute_substep_Δtᶠᶜᶜ(i, j, grid, Δt, rheology, substeps, model_fields) + Δτ = compute_substep_Δtᶠᶠᶜ(i, j, grid, Δt, rheology, substeps, model_fields) Gu = u_velocity_tendency(i, j, grid, Δτ, rheology, model_fields, clock, coriolis, u_immersed_bc, u_top_stress, u_bottom_stress, u_forcing) # Implicit part of the stress that depends linearly on the velocity @@ -145,10 +145,10 @@ end i, j = @index(Global, NTuple) - mᵢ = ℑyᵃᶠᵃ(i, j, 1, grid, ice_mass, model_fields.h, model_fields.ℵ, model_fields.ρ) - ℵᵢ = ℑyᵃᶠᵃ(i, j, 1, grid, model_fields.ℵ) + mᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, model_fields.h, model_fields.ℵ, model_fields.ρ) + ℵᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, model_fields.ℵ) - Δτ = compute_substep_Δtᶜᶠᶜ(i, j, grid, Δt, rheology, substeps, model_fields) + Δτ = compute_substep_Δtᶠᶠᶜ(i, j, grid, Δt, rheology, substeps, model_fields) Gv = v_velocity_tendency(i, j, grid, Δτ, rheology, model_fields, clock, coriolis, v_immersed_bc, v_top_stress, v_bottom_stress, v_forcing) # Implicit part of the stress that depends linearly on the velocity diff --git a/src/sea_ice_advection.jl b/src/sea_ice_advection.jl index 5e7bd67f..adac0f94 100644 --- a/src/sea_ice_advection.jl +++ b/src/sea_ice_advection.jl @@ -61,6 +61,7 @@ end end @inline horizontal_div_Uc(i, j, k, grid, ::Nothing, U, c) = zero(grid) + @inline horizontal_div_Uc(i, j, k, grid, advection, U, c) = 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, _advective_tracer_flux_x, advection, U.u, c) + δyᵃᶜᵃ(i, j, k, grid, _advective_tracer_flux_y, advection, U.v, c)) From dcf8162a3402985815e0347f1cc11d6701c68fa5 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 25 Feb 2025 12:37:25 +0100 Subject: [PATCH 002/108] almost done --- .../elasto_visco_plastic_rheology.jl | 61 ++++++++----------- src/Rheologies/ice_stress_divergence.jl | 8 +-- 2 files changed, 28 insertions(+), 41 deletions(-) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index edb3fe1a..ee2db1fe 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -171,17 +171,18 @@ function compute_stresses!(model, dynamics, rheology::ElastoViscoPlasticRheology parameters = KernelParameters(-Hx+2:Nx+Hx-1, -Hy+2:Ny+Hy-1) - launch!(arch, grid, parameters, _compute_evp_viscosities!, fields, grid, rheology, u, v) - launch!(arch, grid, parameters, _compute_evp_stresses!, fields, grid, rheology, u, v, h, ℵ, ρᵢ, Δt) + launch!(arch, grid, parameters, _compute_evp_viscosities!, fields, grid, rheology, u, v, h, ℵ, ρᵢ, Δt) return nothing end -@inline strain_rate_xx(i, j, k, grid, u, v) = δxᶜᵃᵃ(i, j, k, grid, Δy_qᶠᶜᶜ, u) / Azᶜᶜᶜ(i, j, k, grid) -@inline strain_rate_yy(i, j, k, grid, u, v) = δyᵃᶜᵃ(i, j, k, grid, Δx_qᶜᶠᶜ, v) / Azᶜᶜᶜ(i, j, k, grid) -@inline strain_rate_xy(i, j, k, grid, u, v) = (δxᶠᵃᵃ(i, j, k, grid, Δy_qᶜᶠᶜ, v) + δyᵃᶠᵃ(i, j, k, grid, Δx_qᶠᶜᶜ, u)) / Azᶠᶠᶜ(i, j, k, grid) / 2 +@inline strain_rate_xx(i, j, k, grid, u, v) = ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, u) / Azᶜᶜᶜ(i, j, k, grid) +@inline strain_rate_yy(i, j, k, grid, u, v) = ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, v) / Azᶜᶜᶜ(i, j, k, grid) +@inline strain_rate_xy(i, j, k, grid, u, v) = + (ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, v) + + ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, u)) / Azᶜᶜᶜ(i, j, k, grid) / 2 -@kernel function _compute_evp_viscosities!(fields, grid, rheology, u, v) +@kernel function _compute_evp_viscosities!(fields, grid, rheology, u, v, h, ℵ, ρᵢ, Δt) i, j = @index(Global, NTuple) P = fields.P @@ -195,31 +196,22 @@ end # Strain rates ϵ̇₁₁ = strain_rate_xx(i, j, 1, grid, u, v) ϵ̇₂₂ = strain_rate_yy(i, j, 1, grid, u, v) - - # Center - Center variables: - ϵ̇₁₂ᶜᶜᶜ = ℑxyᶜᶜᵃ(i, j, 1, grid, strain_rate_xy, u, v) + ϵ̇₁₂ = strain_rate_xy(i, j, 1, grid, u, v) # Ice divergence δ = ϵ̇₁₁ + ϵ̇₂₂ # Ice shear (at Centers) - s = sqrt((ϵ̇₁₁ - ϵ̇₂₂)^2 + 4ϵ̇₁₂ᶜᶜᶜ^2) + s = sqrt((ϵ̇₁₁ - ϵ̇₂₂)^2 + 4ϵ̇₁₂^2) # Visco - Plastic parameter # if Δ is very small we assume a linear viscous response # adding a minimum Δ_min (at Centers) - Δᶜᶜᶜ = max(sqrt(δ^2 + s^2 * e⁻²), Δm) - Pᶜᶜᶜ = @inbounds P[i, j, 1] + Δ = max(sqrt(δ^2 + s^2 * e⁻²), Δm) + P = @inbounds P[i, j, 1] - @inbounds fields.ζ[i, j, 1] = Pᶜᶜᶜ / 2Δᶜᶜᶜ - @inbounds fields.Δ[i, j, 1] = Δᶜᶜᶜ -end - -# Compute the visco-plastic stresses for a slab sea ice model. -# The function updates the internal stress variables `σ₁₁`, `σ₂₂`, and `σ₁₂` in the `rheology` object -# following the αEVP formulation of Kimmritz et al (2016). -@kernel function _compute_evp_stresses!(fields, grid, rheology, u, v, h, ℵ, ρᵢ, Δt) - i, j = @index(Global, NTuple) + @inbounds fields.ζ[i, j, 1] = P / 2Δ + @inbounds fields.Δ[i, j, 1] = Δ e⁻² = rheology.yield_curve_eccentricity^(-2) Δm = rheology.minimum_plastic_stress @@ -231,10 +223,6 @@ end σ₁₂ = fields.σ₁₂ α = fields.α - # Strain rates - ϵ̇₁₁ = strain_rate_xx(i, j, 1, grid, u, v) - ϵ̇₂₂ = strain_rate_yy(i, j, 1, grid, u, v) - ϵ̇₁₂ = strain_rate_xy(i, j, 1, grid, u, v) Pᶜᶜᶜ = @inbounds fields.P[i, j, 1] ζᶜᶜᶜ = @inbounds fields.ζ[i, j, 1] @@ -245,13 +233,12 @@ end Pᵣ = Pᶜᶜᶜ * Δᶜᶜᶜ / (Δᶜᶜᶜ + Δm) ηᶜᶜᶜ = ζᶜᶜᶜ * e⁻² - ηᶠᶠᶜ = ζᶠᶠᶜ * e⁻² # σ(uᵖ): the tangential stress depends only shear viscosity # while the compressive stresses depend on the bulk viscosity and the ice strength σ₁₁ᵖ⁺¹ = 2 * ηᶜᶜᶜ * ϵ̇₁₁ + ((ζᶜᶜᶜ - ηᶜᶜᶜ) * (ϵ̇₁₁ + ϵ̇₂₂) - Pᵣ / 2) σ₂₂ᵖ⁺¹ = 2 * ηᶜᶜᶜ * ϵ̇₂₂ + ((ζᶜᶜᶜ - ηᶜᶜᶜ) * (ϵ̇₁₁ + ϵ̇₂₂) - Pᵣ / 2) - σ₁₂ᵖ⁺¹ = 2 * ηᶠᶠᶜ * ϵ̇₁₂ + σ₁₂ᵖ⁺¹ = 2 * ηᶜᶜᶜ * ϵ̇₁₂ mᵢᶜᶜᶜ = ice_mass(i, j, 1, grid, h, ℵ, ρᵢ) mᵢᶠᶠᶜ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, h, ℵ, ρᵢ) @@ -271,12 +258,12 @@ end # dynamic substepping coefficient α σ₁₁★ = (σ₁₁ᵖ⁺¹ - σ₁₁[i, j, 1]) / γᶜᶜᶜ σ₂₂★ = (σ₂₂ᵖ⁺¹ - σ₂₂[i, j, 1]) / γᶜᶜᶜ - σ₁₂★ = (σ₁₂ᵖ⁺¹ - σ₁₂[i, j, 1]) / γᶠᶠᶜ + σ₁₂★ = (σ₁₂ᵖ⁺¹ - σ₁₂[i, j, 1]) / γᶜᶜᶜ σ₁₁[i, j, 1] += ifelse(mᵢᶜᶜᶜ > 0, σ₁₁★, zero(grid)) σ₂₂[i, j, 1] += ifelse(mᵢᶜᶜᶜ > 0, σ₂₂★, zero(grid)) σ₁₂[i, j, 1] += ifelse(mᵢᶠᶠᶜ > 0, σ₁₂★, zero(grid)) - α[i, j, 1] = γᶜᶜᶜ + α[i, j, 1] = γᶠᶠᶜ end end @@ -285,14 +272,14 @@ end ##### # Here we extend all the functions that a rheology model needs to support: -@inline ice_stress_ux(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = @inbounds fields.σ₁₁[i, j, k] -@inline ice_stress_vx(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = @inbounds fields.σ₁₂[i, j, k] -@inline ice_stress_uy(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = @inbounds fields.σ₁₂[i, j, k] -@inline ice_stress_vy(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = @inbounds fields.σ₂₂[i, j, k] +@inline ice_stress_ux(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, fields.σ₁₁) +@inline ice_stress_vx(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, fields.σ₁₂) +@inline ice_stress_uy(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, fields.σ₁₂) +@inline ice_stress_vy(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, fields.σ₂₂) # To help convergence to the right velocities -@inline compute_substep_Δtᶠᶜᶜ(i, j, grid, Δt, ::ElastoViscoPlasticRheology, substeps, fields) = Δt / ℑxᶠᵃᵃ(i, j, 1, grid, fields.α) -@inline compute_substep_Δtᶜᶠᶜ(i, j, grid, Δt, ::ElastoViscoPlasticRheology, substeps, fields) = Δt / ℑyᵃᶠᵃ(i, j, 1, grid, fields.α) +@inline compute_substep_Δtᶠᶜᶜ(i, j, grid, Δt, ::ElastoViscoPlasticRheology, substeps, fields) = @inbounds Δt / fields.α[i, j, 1] +@inline compute_substep_Δtᶜᶠᶜ(i, j, grid, Δt, ::ElastoViscoPlasticRheology, substeps, fields) = @inbounds Δt / fields.α[i, j, 1] ##### ##### Numerical forcing to help convergence @@ -300,12 +287,12 @@ end @inline function sum_of_forcing_u(i, j, k, grid, ::ElastoViscoPlasticRheology, u_forcing, fields, Δt) user_forcing = u_forcing(i, j, k, grid, fields) - rheology_forcing = @inbounds (fields.uⁿ[i, j, k] - fields.u[i, j, k]) / Δt / ℑxᶠᵃᵃ(i, j, k, grid, fields.α) + rheology_forcing = @inbounds (fields.uⁿ[i, j, k] - fields.u[i, j, k]) / Δt / fields.α[i, j, 1] return user_forcing + rheology_forcing end @inline function sum_of_forcing_v(i, j, k, grid, ::ElastoViscoPlasticRheology, v_forcing, fields, Δt) user_forcing = v_forcing(i, j, k, grid, fields) - rheology_forcing = @inbounds (fields.vⁿ[i, j, k] - fields.v[i, j, k]) / Δt / ℑyᵃᶠᵃ(i, j, k, grid, fields.α) + rheology_forcing = @inbounds (fields.vⁿ[i, j, k] - fields.v[i, j, k]) / Δt / fields.α[i, j, 1] return user_forcing + rheology_forcing end \ No newline at end of file diff --git a/src/Rheologies/ice_stress_divergence.jl b/src/Rheologies/ice_stress_divergence.jl index 3515c1f0..75506449 100644 --- a/src/Rheologies/ice_stress_divergence.jl +++ b/src/Rheologies/ice_stress_divergence.jl @@ -29,13 +29,13 @@ const f = Face() ##### @inline function ∂ⱼ_σ₁ⱼ(i, j, k, grid, rheology, clock, fields) - return 1 / Azᶠᶜᶜ(i, j, k, grid) * (δxᶠᵃᵃ(i, j, k, grid, Δy_qᶜᶜᶜ, _ice_stress_ux, rheology, clock, fields) + - δyᵃᶜᵃ(i, j, k, grid, Δx_qᶠᶠᶜ, _ice_stress_uy, rheology, clock, fields)) + return 1 / Azᶠᶠᶜ(i, j, k, grid) * (δxᶠᵃᵃ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_ux, rheology, clock, fields) + + δyᵃᶠᵃ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_uy, rheology, clock, fields)) end @inline function ∂ⱼ_σ₂ⱼ(i, j, k, grid, rheology, clock, fields) - return 1 / Azᶠᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, Δy_qᶠᶠᶜ, _ice_stress_vx, rheology, clock, fields) + - δyᵃᶠᵃ(i, j, k, grid, Δx_qᶜᶜᶜ, _ice_stress_vy, rheology, clock, fields)) + return 1 / Azᶠᶠᶜ(i, j, k, grid) * (δxᶠᵃᵃ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_vx, rheology, clock, fields) + + δyᵃᶠᵃ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_vy, rheology, clock, fields)) end ##### From 0e428a7ea524c34fa599e3f3e6f26c648f273e2f Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 25 Feb 2025 12:39:32 +0100 Subject: [PATCH 003/108] better --- .../elasto_visco_plastic_rheology.jl | 2 +- src/sea_ice_advection.jl | 58 +++++-------------- src/sea_ice_model.jl | 4 +- 3 files changed, 16 insertions(+), 48 deletions(-) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index ee2db1fe..5e2be833 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -272,7 +272,7 @@ end ##### # Here we extend all the functions that a rheology model needs to support: -@inline ice_stress_ux(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, fields.σ₁₁) +@inline ice_stress_ux(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑxᵃᶠᵃ(i, j, 1, grid, fields.σ₁₁) @inline ice_stress_vx(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, fields.σ₁₂) @inline ice_stress_uy(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, fields.σ₁₂) @inline ice_stress_vy(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, fields.σ₂₂) diff --git a/src/sea_ice_advection.jl b/src/sea_ice_advection.jl index adac0f94..0a37d4f9 100644 --- a/src/sea_ice_advection.jl +++ b/src/sea_ice_advection.jl @@ -15,58 +15,26 @@ using Oceananigans.Advection: FluxFormAdvection, # # A = ∇ ⋅ (uh) -_advective_thickness_flux_x(i, j, k, grid, scheme, U, ℵ, h) = advective_thickness_flux_x(i, j, k, grid, scheme, U, ℵ, h) -_advective_thickness_flux_y(i, j, k, grid, scheme, U, ℵ, h) = advective_thickness_flux_y(i, j, k, grid, scheme, U, ℵ, h) +using Oceananigans.Advection: _biased_interpolate_xᶠᵃᵃ, _biased_interpolate_yᵃᶠᵃ, bias -_advective_thickness_flux_x(i, j, k, ibg::ImmersedBoundaryGrid, scheme, U, ℵ, h) = - conditional_flux_fcc(i, j, k, ibg, zero(ibg), advective_thickness_flux_x(i, j, k, ibg, scheme, U, ℵ, h)) - -_advective_thickness_flux_y(i, j, k, ibg::ImmersedBoundaryGrid, scheme, U, ℵ, h) = - conditional_flux_cfc(i, j, k, ibg, zero(ibg), advective_thickness_flux_y(i, j, k, ibg, scheme, U, ℵ, h)) - -@inline function advective_thickness_flux_x(i, j, k, grid, advection, U, ℵ, h) - ϕℵ = advective_tracer_flux_x(i, j, k, grid, advection, U, ℵ) / Axᶠᶜᶜ(i, j, k, grid) - Uϕℵh = ϕℵ * advective_tracer_flux_x(i, j, k, grid, advection, U, h) - @inbounds ϕℵh = ifelse(U[i, j, k] == 0, zero(grid), Uϕℵh / U[i, j, k]) - return ϕℵh -end - -@inline function advective_thickness_flux_y(i, j, k, grid, advection, V, ℵ, h) - ϕℵ = advective_tracer_flux_y(i, j, k, grid, advection, V, ℵ) / Ayᶜᶠᶜ(i, j, k, grid) - Vϕℵh = ϕℵ * advective_tracer_flux_y(i, j, k, grid, advection, V, h) - @inbounds ϕℵh = ifelse(V[i, j, k] == 0, zero(grid), Vϕℵh / V[i, j, k]) - return ϕℵh -end - -@inline div_Uℵh(i, j, k, grid, ::Nothing, U, ℵ, h) = zero(grid) - -# For thickness, we compute [ℵ⁻¹ ∇ ⋅ (uℵh)] -@inline function div_Uℵh(i, j, k, grid, advection, U, ℵ, h) - ∇Uℵh = 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, _advective_thickness_flux_x, advection, U.u, ℵ, h) + - δyᵃᶜᵃ(i, j, k, grid, _advective_thickness_flux_y, advection, U.v, ℵ, h)) - - @inbounds ℵ⁻¹ = ifelse(ℵ[i, j, k] != 0, 1 / ℵ[i, j, k], zero(grid)) - - return ℵ⁻¹ * ∇Uℵh +@inline function advective_new_tracer_flux_x(i, j, k, grid, advection, u, c) + ũ = ℑyᵃᶜᵃ(i, j, k, grid, u) + cᴿ = _biased_interpolate_xᶠᵃᵃ(i, j, k, grid, advection, bias(ũ), c) + return Axᶠᶜᶜ(i, j, k, grid) * ũ * cᴿ end -# For thickness, we compute [ℵ⁻¹ ∇ ⋅ (uℵh)] -@inline function div_Uℵh(i, j, k, grid, advection::FluxFormAdvection, U, ℵ, h) - ∇Uℵh = 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, _advective_thickness_flux_x, advection.x, U.u, ℵ, h) + - δyᵃᶜᵃ(i, j, k, grid, _advective_thickness_flux_y, advection.y, U.v, ℵ, h)) - - @inbounds ℵ⁻¹ = ifelse(ℵ[i, j, k] != 0, 1 / ℵ[i, j, k], zero(grid)) - - return ℵ⁻¹ * ∇Uℵh +@inline function advective_new_tracer_flux_y(i, j, k, grid, advection, v, c) + ṽ = ℑxᶜᵃᵃ(i, j, k, grid, v) + cᴿ = _biased_interpolate_yᵃᶠᵃ(i, j, k, grid, advection, bias(ṽ), c) + return Ayᶜᶠᶜ(i, j, k, grid) * ṽ * cᴿ end @inline horizontal_div_Uc(i, j, k, grid, ::Nothing, U, c) = zero(grid) - @inline horizontal_div_Uc(i, j, k, grid, advection, U, c) = - 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, _advective_tracer_flux_x, advection, U.u, c) + - δyᵃᶜᵃ(i, j, k, grid, _advective_tracer_flux_y, advection, U.v, c)) + 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, advective_new_tracer_flux_x, advection, U.u, c) + + δyᵃᶜᵃ(i, j, k, grid, advective_new_tracer_flux_y, advection, U.v, c)) @inline horizontal_div_Uc(i, j, k, grid, advection::FluxFormAdvection, U, c) = - 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, _advective_tracer_flux_x, advection.x, U.u, c) + - δyᵃᶜᵃ(i, j, k, grid, _advective_tracer_flux_y, advection.y, U.v, c)) + 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, advective_new_tracer_flux_x, advection.x, U.u, c) + + δyᵃᶜᵃ(i, j, k, grid, advective_new_tracer_flux_y, advection.y, U.v, c)) diff --git a/src/sea_ice_model.jl b/src/sea_ice_model.jl index 9f99f3c9..0eda7941 100644 --- a/src/sea_ice_model.jl +++ b/src/sea_ice_model.jl @@ -64,8 +64,8 @@ function SeaIceModel(grid; boundary_conditions = regularize_field_boundary_conditions(boundary_conditions, grid, field_names) if isnothing(velocities) - u = Field{Face, Center, Center}(grid, boundary_conditions=boundary_conditions.u) - v = Field{Center, Face, Center}(grid, boundary_conditions=boundary_conditions.v) + u = Field{Face, Face, Center}(grid, boundary_conditions=boundary_conditions.u) + v = Field{Face, Face, Center}(grid, boundary_conditions=boundary_conditions.v) velocities = (; u, v) end From acbfb1079c2ade529183bba166a28d38303919cd Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 25 Feb 2025 12:49:02 +0100 Subject: [PATCH 004/108] complete Bgrid rheology --- examples/ice_advected_by_anticyclone.jl | 8 +-- src/Rheologies/Rheologies.jl | 3 +- .../elasto_visco_plastic_rheology.jl | 71 ++++++++----------- .../SeaIceMomentumEquations.jl | 3 +- src/sea_ice_model.jl | 4 +- 5 files changed, 38 insertions(+), 51 deletions(-) diff --git a/examples/ice_advected_by_anticyclone.jl b/examples/ice_advected_by_anticyclone.jl index b1f9fa49..2bc158e9 100644 --- a/examples/ice_advected_by_anticyclone.jl +++ b/examples/ice_advected_by_anticyclone.jl @@ -46,8 +46,8 @@ v_bcs = FieldBoundaryConditions(west=ValueBoundaryCondition(0), ##### # Constant ocean velocities corresponding to a cyclonic eddy -Uₒ = XFaceField(grid) -Vₒ = YFaceField(grid) +Uₒ = Field{Face, Face, Nothing}(grid) +Vₒ = Field{Face, Face, Nothing}(grid) set!(Uₒ, (x, y) -> 𝓋ₒ * (2y - L) / L) set!(Vₒ, (x, y) -> 𝓋ₒ * (L - 2x) / L) @@ -59,8 +59,8 @@ fill_halo_regions!((Uₒ, Vₒ)) #### Atmosphere - sea ice stress #### -Uₐ = XFaceField(grid) -Vₐ = YFaceField(grid) +Uₐ = Field{Face, Face, Nothing}(grid) +Vₐ = Field{Face, Face, Nothing}(grid) τₐ = SemiImplicitStress(; uₑ=Uₐ, vₑ=Vₐ, ρₑ=1.3, Cᴰ=1.2e-3) diff --git a/src/Rheologies/Rheologies.jl b/src/Rheologies/Rheologies.jl index 276ff632..730097aa 100644 --- a/src/Rheologies/Rheologies.jl +++ b/src/Rheologies/Rheologies.jl @@ -13,8 +13,7 @@ initialize_rheology!(model, rheology) = nothing compute_stresses!(model, dynamics, rheology, Δt) = nothing # Nothing rheology or viscous rheology -@inline compute_substep_Δtᶠᶜᶜ(i, j, grid, Δt, rheology, substeps, fields) = Δt / substeps -@inline compute_substep_Δtᶜᶠᶜ(i, j, grid, Δt, rheology, substeps, fields) = Δt / substeps +@inline compute_substep_Δtᶠᶠᶜ(i, j, grid, Δt, rheology, substeps, fields) = Δt / substeps # Fallback @inline sum_of_forcing_u(i, j, k, grid, rheology, u_forcing, fields, Δt) = u_forcing(i, j, k, grid, fields) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index 5e2be833..fc43fac9 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -101,13 +101,13 @@ function required_auxiliary_fields(r::ElastoViscoPlasticRheology, grid) ζ = Field{Center, Center, Nothing}(grid) Δ = Field{Center, Center, Nothing}(grid) - # An initial (safe) educated guess - fill!(α, r.max_relaxation_parameter) - α = Field{Face, Face, Nothing}(grid) # Dynamic substeps a la Kimmritz et al (2016) uⁿ = Field{Face, Face, Nothing}(grid) vⁿ = Field{Face, Face, Nothing}(grid) - + + # An initial (safe) educated guess + fill!(α, r.max_relaxation_parameter) + return (; σ₁₁, σ₂₂, σ₁₂, ζ, Δ, α, uⁿ, vⁿ, P) end @@ -209,8 +209,9 @@ end # adding a minimum Δ_min (at Centers) Δ = max(sqrt(δ^2 + s^2 * e⁻²), Δm) P = @inbounds P[i, j, 1] + ζ = P / 2Δ - @inbounds fields.ζ[i, j, 1] = P / 2Δ + @inbounds fields.ζ[i, j, 1] = ζ @inbounds fields.Δ[i, j, 1] = Δ e⁻² = rheology.yield_curve_eccentricity^(-2) @@ -223,47 +224,36 @@ end σ₁₂ = fields.σ₁₂ α = fields.α - - Pᶜᶜᶜ = @inbounds fields.P[i, j, 1] - ζᶜᶜᶜ = @inbounds fields.ζ[i, j, 1] - Δᶜᶜᶜ = @inbounds fields.Δ[i, j, 1] - ζᶠᶠᶜ = ℑxyᶠᶠᵃ(i, j, 1, grid, fields.ζ) - # replacement pressure? - Pᵣ = Pᶜᶜᶜ * Δᶜᶜᶜ / (Δᶜᶜᶜ + Δm) - - ηᶜᶜᶜ = ζᶜᶜᶜ * e⁻² + Pᵣ = P * Δ / (Δ + Δm) + η = ζ * e⁻² # σ(uᵖ): the tangential stress depends only shear viscosity # while the compressive stresses depend on the bulk viscosity and the ice strength - σ₁₁ᵖ⁺¹ = 2 * ηᶜᶜᶜ * ϵ̇₁₁ + ((ζᶜᶜᶜ - ηᶜᶜᶜ) * (ϵ̇₁₁ + ϵ̇₂₂) - Pᵣ / 2) - σ₂₂ᵖ⁺¹ = 2 * ηᶜᶜᶜ * ϵ̇₂₂ + ((ζᶜᶜᶜ - ηᶜᶜᶜ) * (ϵ̇₁₁ + ϵ̇₂₂) - Pᵣ / 2) - σ₁₂ᵖ⁺¹ = 2 * ηᶜᶜᶜ * ϵ̇₁₂ - - mᵢᶜᶜᶜ = ice_mass(i, j, 1, grid, h, ℵ, ρᵢ) - mᵢᶠᶠᶜ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, h, ℵ, ρᵢ) + σ₁₁ᵖ⁺¹ = 2 * η * ϵ̇₁₁ + ((ζ - η) * (ϵ̇₁₁ + ϵ̇₂₂) - Pᵣ / 2) + σ₂₂ᵖ⁺¹ = 2 * η * ϵ̇₂₂ + ((ζ - η) * (ϵ̇₁₁ + ϵ̇₂₂) - Pᵣ / 2) + σ₁₂ᵖ⁺¹ = 2 * η * ϵ̇₁₂ + mᵢ = ice_mass(i, j, 1, grid, h, ℵ, ρᵢ) + # Update coefficients for substepping using dynamic substepping # with spatially varying coefficients as in Kimmritz et al (2016) - γ²ᶜᶜᶜ = ζᶜᶜᶜ * π^2 * Δt / mᵢᶜᶜᶜ / Azᶜᶜᶜ(i, j, 1, grid) - γ²ᶜᶜᶜ = ifelse(isnan(γ²ᶜᶜᶜ), α⁺^2, γ²ᶜᶜᶜ) # In case both ζᶜᶜᶜ and mᵢᶜᶜᶜ are zero - γᶜᶜᶜ = clamp(sqrt(γ²ᶜᶜᶜ), α⁻, α⁺) - - γ²ᶠᶠᶜ = ζᶠᶠᶜ * π^2 * Δt / mᵢᶠᶠᶜ / Azᶠᶠᶜ(i, j, 1, grid) - γ²ᶠᶠᶜ = ifelse(isnan(γ²ᶠᶠᶜ), α⁺^2, γ²ᶠᶠᶜ) # In case both ζᶠᶠᶜ and mᵢᶠᶠᶜ are zero - γᶠᶠᶜ = clamp(sqrt(γ²ᶠᶠᶜ), α⁻, α⁺) + γ = ζ * π^2 * Δt / mᵢ / Azᶜᶜᶜ(i, j, 1, grid) + α = clamp(sqrt(γ), α⁻, α⁺) + α = ifelse(isnan(α), α⁺, α) @inbounds begin # Compute the new stresses and store the value of the # dynamic substepping coefficient α - σ₁₁★ = (σ₁₁ᵖ⁺¹ - σ₁₁[i, j, 1]) / γᶜᶜᶜ - σ₂₂★ = (σ₂₂ᵖ⁺¹ - σ₂₂[i, j, 1]) / γᶜᶜᶜ - σ₁₂★ = (σ₁₂ᵖ⁺¹ - σ₁₂[i, j, 1]) / γᶜᶜᶜ - - σ₁₁[i, j, 1] += ifelse(mᵢᶜᶜᶜ > 0, σ₁₁★, zero(grid)) - σ₂₂[i, j, 1] += ifelse(mᵢᶜᶜᶜ > 0, σ₂₂★, zero(grid)) - σ₁₂[i, j, 1] += ifelse(mᵢᶠᶠᶜ > 0, σ₁₂★, zero(grid)) - α[i, j, 1] = γᶠᶠᶜ + σ₁₁★ = (σ₁₁ᵖ⁺¹ - σ₁₁[i, j, 1]) / α + σ₂₂★ = (σ₂₂ᵖ⁺¹ - σ₂₂[i, j, 1]) / α + σ₁₂★ = (σ₁₂ᵖ⁺¹ - σ₁₂[i, j, 1]) / α + + σ₁₁[i, j, 1] += ifelse(mᵢ > 0, σ₁₁★, zero(grid)) + σ₂₂[i, j, 1] += ifelse(mᵢ > 0, σ₂₂★, zero(grid)) + σ₁₂[i, j, 1] += ifelse(mᵢ > 0, σ₁₂★, zero(grid)) + + fields.α[i, j, 1] = α end end @@ -272,14 +262,13 @@ end ##### # Here we extend all the functions that a rheology model needs to support: -@inline ice_stress_ux(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑxᵃᶠᵃ(i, j, 1, grid, fields.σ₁₁) -@inline ice_stress_vx(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, fields.σ₁₂) +@inline ice_stress_ux(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, fields.σ₁₁) @inline ice_stress_uy(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, fields.σ₁₂) +@inline ice_stress_vx(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, fields.σ₁₂) @inline ice_stress_vy(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, fields.σ₂₂) # To help convergence to the right velocities -@inline compute_substep_Δtᶠᶜᶜ(i, j, grid, Δt, ::ElastoViscoPlasticRheology, substeps, fields) = @inbounds Δt / fields.α[i, j, 1] -@inline compute_substep_Δtᶜᶠᶜ(i, j, grid, Δt, ::ElastoViscoPlasticRheology, substeps, fields) = @inbounds Δt / fields.α[i, j, 1] +@inline compute_substep_Δtᶠᶠᶜ(i, j, grid, Δt, ::ElastoViscoPlasticRheology, substeps, fields) = @inbounds Δt / ℑxyᶠᶠᵃ(i, j, 1, grid, fields.α) ##### ##### Numerical forcing to help convergence @@ -287,12 +276,12 @@ end @inline function sum_of_forcing_u(i, j, k, grid, ::ElastoViscoPlasticRheology, u_forcing, fields, Δt) user_forcing = u_forcing(i, j, k, grid, fields) - rheology_forcing = @inbounds (fields.uⁿ[i, j, k] - fields.u[i, j, k]) / Δt / fields.α[i, j, 1] + rheology_forcing = @inbounds (fields.uⁿ[i, j, k] - fields.u[i, j, k]) / Δt / ℑxyᶠᶠᵃ(i, j, 1, grid, fields.α) return user_forcing + rheology_forcing end @inline function sum_of_forcing_v(i, j, k, grid, ::ElastoViscoPlasticRheology, v_forcing, fields, Δt) user_forcing = v_forcing(i, j, k, grid, fields) - rheology_forcing = @inbounds (fields.vⁿ[i, j, k] - fields.v[i, j, k]) / Δt / fields.α[i, j, 1] + rheology_forcing = @inbounds (fields.vⁿ[i, j, k] - fields.v[i, j, k]) / Δt / ℑxyᶠᶠᵃ(i, j, 1, grid, fields.α) return user_forcing + rheology_forcing end \ No newline at end of file diff --git a/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl b/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl index ae5cc4d1..19d0ca3e 100644 --- a/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl +++ b/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl @@ -21,8 +21,7 @@ using ClimaSeaIce.Rheologies: ∂ⱼ_σ₁ⱼ, required_auxiliary_fields, compute_stresses!, initialize_rheology!, - compute_substep_Δtᶠᶜᶜ, - compute_substep_Δtᶜᶠᶜ, + compute_substep_Δtᶠᶠᶜ, sum_of_forcing_u, sum_of_forcing_v diff --git a/src/sea_ice_model.jl b/src/sea_ice_model.jl index 0eda7941..68b190f1 100644 --- a/src/sea_ice_model.jl +++ b/src/sea_ice_model.jl @@ -64,8 +64,8 @@ function SeaIceModel(grid; boundary_conditions = regularize_field_boundary_conditions(boundary_conditions, grid, field_names) if isnothing(velocities) - u = Field{Face, Face, Center}(grid, boundary_conditions=boundary_conditions.u) - v = Field{Face, Face, Center}(grid, boundary_conditions=boundary_conditions.v) + u = Field{Face, Face, Center}(grid) #, boundary_conditions=boundary_conditions.u) + v = Field{Face, Face, Center}(grid) #, boundary_conditions=boundary_conditions.v) velocities = (; u, v) end From f855c8bc687b4a2ac12765d42d9fca3b513c4e98 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 25 Feb 2025 13:16:02 +0100 Subject: [PATCH 005/108] kind of works? --- examples/ice_advected_by_anticyclone.jl | 2 +- .../split_explicit_momentum_equations.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/ice_advected_by_anticyclone.jl b/examples/ice_advected_by_anticyclone.jl index 2bc158e9..fc9e90b6 100644 --- a/examples/ice_advected_by_anticyclone.jl +++ b/examples/ice_advected_by_anticyclone.jl @@ -25,7 +25,7 @@ L = 512kilometers # 2 km domain grid = RectilinearGrid(arch; - size = (128, 128), + size = (256, 256), x = (0, L), y = (0, L), halo = (7, 7), diff --git a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl index d4efe6d0..73a105c3 100644 --- a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl @@ -99,8 +99,8 @@ function step_momentum!(model, dynamics::SplitExplicitMomentumEquation, Δt, arg # TODO: This needs to be removed in some way! fill_halo_regions!(model.velocities) - mask_immersed_field_xy!(model.velocities.u, k=1) - mask_immersed_field_xy!(model.velocities.v, k=1) + mask_immersed_field_xy!(model.velocities.u, k=size(grid, 3)) + mask_immersed_field_xy!(model.velocities.v, k=size(grid, 3)) end return nothing From 3cd0dc6ab70b88a553dd12e798a2b26b9a18bd31 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 25 Feb 2025 14:05:26 +0100 Subject: [PATCH 006/108] adapt --- examples/ice_advected_by_anticyclone.jl | 3 +- src/Rheologies/Rheologies.jl | 3 +- .../brittle_bingham_maxwell_rheology.jl | 303 ++++++++++++++++++ .../elasto_visco_plastic_rheology.jl | 2 +- .../split_explicit_momentum_equations.jl | 2 +- src/sea_ice_time_stepping.jl | 5 + 6 files changed, 314 insertions(+), 4 deletions(-) create mode 100644 src/Rheologies/brittle_bingham_maxwell_rheology.jl diff --git a/examples/ice_advected_by_anticyclone.jl b/examples/ice_advected_by_anticyclone.jl index fc9e90b6..0609f915 100644 --- a/examples/ice_advected_by_anticyclone.jl +++ b/examples/ice_advected_by_anticyclone.jl @@ -90,7 +90,7 @@ momentum_equations = SeaIceMomentumEquation(grid; bottom_momentum_stress = τₒ, coriolis = FPlane(f=1e-4), ocean_velocities = (u = Uₒ, v = Vₒ), - rheology = ElastoViscoPlasticRheology(), + rheology = BrittleBinghamMaxwellRheology(), solver = SplitExplicitSolver(substeps=150)) # Define the model! @@ -99,6 +99,7 @@ model = SeaIceModel(grid; dynamics = momentum_equations, ice_thermodynamics = nothing, # No thermodynamics here advection = WENO(order=7), + tracers = :d, boundary_conditions = (u=u_bcs, v=v_bcs)) # We start with a concentration of ℵ = 1 and an diff --git a/src/Rheologies/Rheologies.jl b/src/Rheologies/Rheologies.jl index 730097aa..31305ca2 100644 --- a/src/Rheologies/Rheologies.jl +++ b/src/Rheologies/Rheologies.jl @@ -1,6 +1,6 @@ module Rheologies -export ViscousRheology, ElastoViscoPlasticRheology +export ViscousRheology, ElastoViscoPlasticRheology, BrittleBinghamMaxwellRheology export ∂ⱼ_σ₁ⱼ, ∂ⱼ_σ₂ⱼ, required_auxiliary_fields using Oceananigans @@ -22,5 +22,6 @@ compute_stresses!(model, dynamics, rheology, Δt) = nothing include("ice_stress_divergence.jl") include("viscous_rheology.jl") include("elasto_visco_plastic_rheology.jl") +include("brittle_bingham_maxwell_rheology.jl") end \ No newline at end of file diff --git a/src/Rheologies/brittle_bingham_maxwell_rheology.jl b/src/Rheologies/brittle_bingham_maxwell_rheology.jl new file mode 100644 index 00000000..6c2ae7ba --- /dev/null +++ b/src/Rheologies/brittle_bingham_maxwell_rheology.jl @@ -0,0 +1,303 @@ +using Oceananigans.Grids: halo_size + +struct BrittleBinghamMaxwellRheology{FT, A} + ice_ridging_strength :: FT # compressive strength + ice_compaction_hardening :: FT # compaction hardening + ice_cohesion :: FT # cohesion + undamaged_elastic_modulus :: FT # minimum plastic parameter (transitions to viscous behaviour) + undamaged_viscous_relaxation_time :: FT # minimum number of substeps expressed as the dynamic coefficient + ridging_ice_thickness :: FT # maximum number of substeps expressed as the dynamic coefficient + poisson_ratio :: FT + friction_coefficient :: FT + maximum_compressive_strength :: FT + healing_constant :: FT + damage_parameter :: FT + interpolation_scheme :: A # Interpolation of cc variables to faces +end + +function BrittleBinghamMaxwellRheology(FT::DataType = Float64; + ice_ridging_strength = 1e4, + ice_compaction_hardening = 20, + ice_cohesion = 5.7e3, + undamaged_elastic_modulus = 5.96e8, + undamaged_viscous_relaxation_time = 1e7, + ridging_ice_thickness = 1, + poisson_ratio = 1 / 3, + friction_coefficient = 0.7, + maximum_compressive_strength = 2.9e7, + healing_constant = 26, # Ks + damage_parameter = 5) + + return BrittleBinghamMaxwellRheology(convert(FT, ice_ridging_strength), + convert(FT, ice_compaction_hardening), + convert(FT, ice_cohesion), + convert(FT, undamaged_elastic_modulus), + convert(FT, undamaged_viscous_relaxation_time), + convert(FT, ridging_ice_thickness), + convert(FT, poisson_ratio), + convert(FT, friction_coefficient), + convert(FT, maximum_compressive_strength), + convert(FT, healing_constant), + convert(FT, damage_parameter), + nothing) +end + +required_prognostic_tracers(::BrittleBinghamMaxwellRheology, grid) = + (; d = Field{Center, Center, Nothing}(grid)) # damage tracer + +function required_auxiliary_fields(::BrittleBinghamMaxwellRheology, grid) + + # TODO: What about boundary conditions? + P = Field{Center, Center, Nothing}(grid) + E = Field{Center, Center, Nothing}(grid) + λ = Field{Center, Center, Nothing}(grid) + + σ₁₁ = Field{Center, Center, Nothing}(grid) + σ₂₂ = Field{Center, Center, Nothing}(grid) + σ₁₂ = Field{Center, Center, Nothing}(grid) + σ₁₁ₙ = Field{Center, Center, Nothing}(grid) + σ₂₂ₙ = Field{Center, Center, Nothing}(grid) + σ₁₂ₙ = Field{Center, Center, Nothing}(grid) + uₙ = Field{Face, Face, Center}(grid) + vₙ = Field{Face, Face, Center}(grid) + dₙ = Field{Center, Center, Nothing}(grid) + return (; σ₁₁, σ₂₂, σ₁₂, σ₁₁ₙ, σ₂₂ₙ, σ₁₂ₙ, dₙ, P, E, λ, uₙ, vₙ) +end + +# Extend the `adapt_structure` function for the ElastoViscoPlasticRheology +Adapt.adapt_structure(to, r::BrittleBinghamMaxwellRheology) = + BrittleBinghamMaxwellRheology(Adapt.adapt(to, r.ice_ridging_strength), + Adapt.adapt(to, r.ice_compaction_hardening), + Adapt.adapt(to, r.ice_cohesion), + Adapt.adapt(to, r.undamaged_elastic_modulus), + Adapt.adapt(to, r.undamaged_viscous_relaxation_time), + Adapt.adapt(to, r.ridging_ice_thickness), + Adapt.adapt(to, r.poisson_ratio), + Adapt.adapt(to, r.friction_coefficient), + Adapt.adapt(to, r.maximum_compressive_strength), + Adapt.adapt(to, r.healing_constant), + Adapt.adapt(to, r.damage_parameter), + Adapt.adapt(to, r.interpolation_scheme)) + +##### +##### Computation of the stresses +##### + +function initialize_rheology!(model, rheology::BrittleBinghamMaxwellRheology) + h = model.ice_thickness + ℵ = model.ice_concentration + + fields = model.dynamics.auxiliary_fields + + P★ = rheology.ice_ridging_strength + E★ = rheology.undamaged_elastic_modulus + λ★ = rheology.undamaged_viscous_relaxation_time + h★ = rheology.ridging_ice_thickness + α = rheology.damage_parameter + C = rheology.ice_compaction_hardening + + # compute on the whole grid including halos + parameters = KernelParameters(size(fields.P.data)[1:2], fields.P.data.offsets[1:2]) + launch!(architecture(model.grid), model.grid, parameters, _initialize_bbm_rheology!, fields, model.grid, P★, E★, λ★, h★, α, C, h, ℵ) + + return nothing +end + +@kernel function _initialize_bbm_rheology!(fields, grid, P★, E★, λ★, h★, α, C, h, ℵ) + i, j = @index(Global, NTuple) + @inbounds begin + ecc = exp(- C * (1 - ℵ[i, j, 1])) + # Center - Center fields + fields.P[i, j, 1] = P★ * (h[i, j, 1] / h★)^(3/2) * ecc + fields.E[i, j, 1] = E★ * ecc + fields.λ[i, j, 1] = λ★ * ecc^(α - 1) + end +end + +# Specific compute stresses for the EVP rheology +function compute_stresses!(model, dynamics, rheology::BrittleBinghamMaxwellRheology, Δt, Ns) + + grid = model.grid + arch = architecture(grid) + + ρᵢ = model.ice_density + d = model.tracers.d + u, v = model.velocities + fields = dynamics.auxiliary_fields + + Nx, Ny, _ = size(grid) + Hx, Hy, _ = halo_size(grid) + + parameters = KernelParameters(-Hx+2:Nx+Hx-1, -Hy+2:Ny+Hy-1) + + # Pretty simple timestepping + Δτ = Δt / Ns + + launch!(arch, grid, parameters, _compute_stress_predictors!, fields, grid, rheology, d, u, v, ρᵢ, Δτ) + launch!(arch, grid, parameters, _advance_stresses!, fields, grid, rheology, d, u, v, ρᵢ, Δτ) + + return nothing +end + +@inline σᴵ(i, j, k, grid, fields) = @inbounds (fields.σ₁₁[i, j, k] + fields.σ₂₂[i, j, k]) / 2 +@inline σᴵᴵ(i, j, k, grid, fields) = @inbounds sqrt((fields.σ₁₁[i, j, k] - fields.σ₂₂[i, j, k])^2 / 4 + fields.σ₁₂[i, j, k]^2) + +@inline function dcritical(i, j, k, grid, N, c, μ, fields) + σI = σᴵ(i, j, k, grid, fields) + σII = σᴵᴵ(i, j, k, grid, fields) + return one(grid) - ifelse(σI > - N, c / (σII + μ * σI), - N / σI) +end + +@inline function reconstruction_2d(i, j, k, grid, f, args...) + fij = f(i, j, k, grid, args...) + fmj = f(i-1, j, k, grid, args...) + fpj = f(i+1, j, k, grid, args...) + fim = f(i, j-1, k, grid, args...) + fip = f(i, j+1, k, grid, args...) + fmm = f(i-1, j-1, k, grid, args...) + fmp = f(i-1, j+1, k, grid, args...) + fpm = f(i+1, j-1, k, grid, args...) + fpp = f(i+1, j+1, k, grid, args...) + + # remove NaNs + isnanij = isnan(fij) + isnanmj = isnan(fmj) + isnanpj = isnan(fpj) + isnanim = isnan(fim) + isnanip = isnan(fip) + isnanmm = isnan(fmm) + isnanmp = isnan(fmp) + isnanpm = isnan(fpm) + isnanpp = isnan(fpp) + + fij = ifelse(isnanij, zero(grid), fij) / 4 + fmj = ifelse(isnanmj, zero(grid), fmj) / 8 + fpj = ifelse(isnanpj, zero(grid), fpj) / 8 + fim = ifelse(isnanim, zero(grid), fim) / 8 + fip = ifelse(isnanip, zero(grid), fip) / 8 + fmm = ifelse(isnanmm, zero(grid), fmm) / 16 + fmp = ifelse(isnanmp, zero(grid), fmp) / 16 + fpm = ifelse(isnanpm, zero(grid), fpm) / 16 + fpp = ifelse(isnanpp, zero(grid), fpp) / 16 + + return (fij + fmj + fpj + fim + fip + fmm + fmp + fpm + fpp) +end + +@inline function dcrit2(i, j, k, grid, N, c, μ, fields) + + σI = σᴵ(i, j, k, grid, fields) + σII = σᴵᴵ(i, j, k, grid, fields) + + m = tand(90 - atand(μ)) + q = σII - m * σI + + # # Move towards the yield curve in a perpendicular fashion + σIf = (c - q) / (m + μ) + σIIf = m * σIf + q + + dcrit = one(grid) - sqrt(σIf^2 + σIIf^2) / sqrt(σI^2 + σII^2) + dcrit = ifelse(isnan(dcrit), zero(grid), dcrit) + + return dcrit * (σII > c - μ * σI) +end + +@kernel function _compute_stress_predictors!(fields, grid, rheology, d, u, v, ρᵢ, Δτ) + i, j = @index(Global, NTuple) + + α = rheology.damage_parameter + ν = rheology.poisson_ratio + + ϵ̇₁₁ = strain_rate_xx(i, j, 1, grid, u, v) + ϵ̇₂₂ = strain_rate_yy(i, j, 1, grid, u, v) + ϵ̇₁₂ = strain_rate_xy(i, j, 1, grid, u, v) + + Kϵ₁₁ = (ϵ̇₁₁ + ν * ϵ̇₂₂) / (1 - ν^2) + Kϵ₂₂ = (ϵ̇₂₂ + ν * ϵ̇₁₁) / (1 - ν^2) + Kϵ₁₂ = (1 - ν) * ϵ̇₁₂ / (1 - ν^2) + + dᵢ = @inbounds d[i, j, 1] + P = @inbounds fields.P[i, j, 1] + E₀ = @inbounds fields.E[i, j, 1] + λ₀ = @inbounds fields.λ[i, j, 1] + + E = E₀ * (1 - dᵢ) + λ = λ₀ * (1 - dᵢ)^(α - 1) + + # Test which isotropic stress to use + P̃ = zero(grid) + + # Implicit diagonal operator + Ω = 1 / (1 + Δτ * (1 + P̃) / λ) + + @inbounds fields.σ₁₁[i, j, 1] = Ω * (fields.σ₁₁ₙ[i, j, 1] + Δτ * E * Kϵ₁₁) + @inbounds fields.σ₂₂[i, j, 1] = Ω * (fields.σ₂₂ₙ[i, j, 1] + Δτ * E * Kϵ₂₂) + @inbounds fields.σ₁₂[i, j, 1] = Ω * (fields.σ₁₂ₙ[i, j, 1] + Δτ * E * Kϵ₁₂) +end + +@kernel function _advance_stresses!(fields, grid, rheology, d, u, v, ρᵢ, Δτ) + i, j = @index(Global, NTuple) + + α = rheology.damage_parameter + ν = rheology.poisson_ratio + N = rheology.maximum_compressive_strength + c = rheology.ice_cohesion + μ = rheology.friction_coefficient + + dᵢ = @inbounds d[i, j, 1] + ρ = @inbounds ρᵢ[i, j, 1] + P = @inbounds fields.P[i, j, 1] + E₀ = @inbounds fields.E[i, j, 1] + λ₀ = @inbounds fields.λ[i, j, 1] + + E = E₀ * (1 - dᵢ) + dcrit = reconstruction_2d(i, j, 1, grid, dcrit2, N, c, μ, fields) + + σI = σᴵ(i, j, 1, grid, fields) + σII = σᴵᴵ(i, j, 1, grid, fields) + + # Relaxation time (constant) + td = sqrt(2 * (1 + ν) * ρ / E * Azᶜᶜᶜ(i, j, 1, grid)) + + # Damage update + @inbounds dᵢ += dcrit * Δτ / td * (1 - dᵢ) + + # Clamp damage between 0 and a value close to 1 (cannot do 1 because of the relaxation time) + dᵢ = clamp(dᵢ, zero(grid), 99999 * one(grid) / 100000) + Δd = @inbounds (dᵢ - d[i, j, 1]) + + # Now we readvance the stresses with the new information + ϵ̇₁₁ = strain_rate_xx(i, j, 1, grid, u, v) + ϵ̇₂₂ = strain_rate_yy(i, j, 1, grid, u, v) + ϵ̇₁₂ = strain_rate_xy(i, j, 1, grid, u, v) + + Kϵ₁₁ = (ϵ̇₁₁ + ν * ϵ̇₂₂) / (1 - ν^2) + Kϵ₂₂ = (ϵ̇₂₂ + ν * ϵ̇₁₁) / (1 - ν^2) + Kϵ₁₂ = (1 - ν) * ϵ̇₁₂ / (1 - ν^2) + + E = E₀ * (1 - dᵢ) + λ = λ₀ * (1 - dᵢ)^(α - 1) + P̃ = zero(grid) + + # Implicit diagonal operator + Ω = @inbounds 1 / (1 + Δτ * (1 + P̃) / λ + Δd / (1 - dᵢ)) + + @inbounds fields.σ₁₁[i, j, 1] = Ω * (fields.σ₁₁ₙ[i, j, 1] + Δτ * E * Kϵ₁₁) + @inbounds fields.σ₂₂[i, j, 1] = Ω * (fields.σ₂₂ₙ[i, j, 1] + Δτ * E * Kϵ₂₂) + @inbounds fields.σ₁₂[i, j, 1] = Ω * (fields.σ₁₂ₙ[i, j, 1] + Δτ * E * Kϵ₁₂) + @inbounds d[i, j, 1] = dᵢ +end + +##### +##### Methods for the BBM rheology +##### + +# In the BBM rheology, the stresses need to be vertically integrated +@inline hσ₁₁(i, j, k, grid, fields) = @inbounds fields.σ₁₁[i, j, k] * fields.h[i, j, k] +@inline hσ₂₂(i, j, k, grid, fields) = @inbounds fields.σ₂₂[i, j, k] * fields.h[i, j, k] +@inline hσ₁₂(i, j, k, grid, fields) = @inbounds fields.σ₁₂[i, j, k] * fields.h[i, j, k] + +# Here we extend all the functions that a rheology model needs to support: +@inline ice_stress_ux(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, hσ₁₁, fields) +@inline ice_stress_uy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₁₂, fields) +@inline ice_stress_vx(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, hσ₁₂, fields) +@inline ice_stress_vy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₂₂, fields) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index fc43fac9..d0fe9656 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -154,7 +154,7 @@ end @inline ice_strength(i, j, k, grid, P★, C, h, ℵ) = @inbounds P★ * h[i, j, k] * exp(- C * (1 - ℵ[i, j, k])) # Specific compute stresses for the EVP rheology -function compute_stresses!(model, dynamics, rheology::ElastoViscoPlasticRheology, Δt) +function compute_stresses!(model, dynamics, rheology::ElastoViscoPlasticRheology, Δt, Ns) grid = model.grid arch = architecture(grid) diff --git a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl index 73a105c3..a885d83b 100644 --- a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl @@ -66,7 +66,7 @@ function step_momentum!(model, dynamics::SplitExplicitMomentumEquation, Δt, arg for substep in 1 : substeps # Compute stresses! depending on the particular rheology implementation - compute_stresses!(model, dynamics, rheology, Δt) + compute_stresses!(model, dynamics, rheology, Δt, substeps) # The momentum equations are solved using an alternating leap-frog algorithm # for u and v (used for the ocean - ice stresses and the coriolis term) diff --git a/src/sea_ice_time_stepping.jl b/src/sea_ice_time_stepping.jl index e9e5c8df..1e4490c1 100644 --- a/src/sea_ice_time_stepping.jl +++ b/src/sea_ice_time_stepping.jl @@ -33,9 +33,11 @@ end Ghⁿ = Gⁿ.h Gℵⁿ = Gⁿ.ℵ + Gdⁿ = Gⁿ.d Gh⁻ = G⁻.h Gℵ⁻ = G⁻.ℵ + Gd⁻ = G⁻.d # Update ice thickness, clipping negative values @inbounds begin @@ -50,6 +52,9 @@ end ℵ[i, j, k] = ℵt h[i, j, k] = ht + dᵢ = tracers.d[i, j, k] + Δt * (α * Gdⁿ[i, j, k] + β * Gd⁻[i, j, k]) + dᵢ = clamp(dᵢ, zero(dᵢ), 99999 * one(dᵢ) / 100000) + tracers.d[i, j, k] = dᵢ end end From 05147ad4fb2d2d49c4c2ff50767fce30aacea9d2 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Feb 2025 10:04:01 +0100 Subject: [PATCH 007/108] add --- .../brittle_bingham_maxwell_rheology.jl | 29 +++++++++---------- .../split_explicit_momentum_equations.jl | 3 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Rheologies/brittle_bingham_maxwell_rheology.jl b/src/Rheologies/brittle_bingham_maxwell_rheology.jl index 6c2ae7ba..8fa1389b 100644 --- a/src/Rheologies/brittle_bingham_maxwell_rheology.jl +++ b/src/Rheologies/brittle_bingham_maxwell_rheology.jl @@ -121,7 +121,6 @@ function compute_stresses!(model, dynamics, rheology::BrittleBinghamMaxwellRheol arch = architecture(grid) ρᵢ = model.ice_density - d = model.tracers.d u, v = model.velocities fields = dynamics.auxiliary_fields @@ -133,8 +132,8 @@ function compute_stresses!(model, dynamics, rheology::BrittleBinghamMaxwellRheol # Pretty simple timestepping Δτ = Δt / Ns - launch!(arch, grid, parameters, _compute_stress_predictors!, fields, grid, rheology, d, u, v, ρᵢ, Δτ) - launch!(arch, grid, parameters, _advance_stresses!, fields, grid, rheology, d, u, v, ρᵢ, Δτ) + launch!(arch, grid, parameters, _compute_stress_predictors!, fields, grid, rheology, model.tracers, u, v, ρᵢ, Δτ) + launch!(arch, grid, parameters, _advance_stresses!, fields, grid, rheology, model.tracers, u, v, ρᵢ, Δτ) return nothing end @@ -201,7 +200,7 @@ end return dcrit * (σII > c - μ * σI) end -@kernel function _compute_stress_predictors!(fields, grid, rheology, d, u, v, ρᵢ, Δτ) +@kernel function _compute_stress_predictors!(fields, grid, rheology, tracers, u, v, ρᵢ, Δτ) i, j = @index(Global, NTuple) α = rheology.damage_parameter @@ -215,7 +214,7 @@ end Kϵ₂₂ = (ϵ̇₂₂ + ν * ϵ̇₁₁) / (1 - ν^2) Kϵ₁₂ = (1 - ν) * ϵ̇₁₂ / (1 - ν^2) - dᵢ = @inbounds d[i, j, 1] + dᵢ = @inbounds tracers.d[i, j, 1] P = @inbounds fields.P[i, j, 1] E₀ = @inbounds fields.E[i, j, 1] λ₀ = @inbounds fields.λ[i, j, 1] @@ -229,12 +228,12 @@ end # Implicit diagonal operator Ω = 1 / (1 + Δτ * (1 + P̃) / λ) - @inbounds fields.σ₁₁[i, j, 1] = Ω * (fields.σ₁₁ₙ[i, j, 1] + Δτ * E * Kϵ₁₁) - @inbounds fields.σ₂₂[i, j, 1] = Ω * (fields.σ₂₂ₙ[i, j, 1] + Δτ * E * Kϵ₂₂) - @inbounds fields.σ₁₂[i, j, 1] = Ω * (fields.σ₁₂ₙ[i, j, 1] + Δτ * E * Kϵ₁₂) + @inbounds tracers.σ₁₁[i, j, 1] = Ω * (fields.σ₁₁ₙ[i, j, 1] + Δτ * E * Kϵ₁₁) + @inbounds tracers.σ₂₂[i, j, 1] = Ω * (fields.σ₂₂ₙ[i, j, 1] + Δτ * E * Kϵ₂₂) + @inbounds tracers.σ₁₂[i, j, 1] = Ω * (fields.σ₁₂ₙ[i, j, 1] + Δτ * E * Kϵ₁₂) end -@kernel function _advance_stresses!(fields, grid, rheology, d, u, v, ρᵢ, Δτ) +@kernel function _advance_stresses!(fields, grid, rheology, tracers, u, v, ρᵢ, Δτ) i, j = @index(Global, NTuple) α = rheology.damage_parameter @@ -243,17 +242,17 @@ end c = rheology.ice_cohesion μ = rheology.friction_coefficient - dᵢ = @inbounds d[i, j, 1] ρ = @inbounds ρᵢ[i, j, 1] + dᵢ = @inbounds tracers.d[i, j, 1] P = @inbounds fields.P[i, j, 1] E₀ = @inbounds fields.E[i, j, 1] λ₀ = @inbounds fields.λ[i, j, 1] E = E₀ * (1 - dᵢ) - dcrit = reconstruction_2d(i, j, 1, grid, dcrit2, N, c, μ, fields) + dcrit = reconstruction_2d(i, j, 1, grid, dcrit2, N, c, μ, tracers) - σI = σᴵ(i, j, 1, grid, fields) - σII = σᴵᴵ(i, j, 1, grid, fields) + σI = σᴵ(i, j, 1, grid, tracers) + σII = σᴵᴵ(i, j, 1, grid, tracers) # Relaxation time (constant) td = sqrt(2 * (1 + ν) * ρ / E * Azᶜᶜᶜ(i, j, 1, grid)) @@ -284,7 +283,7 @@ end @inbounds fields.σ₁₁[i, j, 1] = Ω * (fields.σ₁₁ₙ[i, j, 1] + Δτ * E * Kϵ₁₁) @inbounds fields.σ₂₂[i, j, 1] = Ω * (fields.σ₂₂ₙ[i, j, 1] + Δτ * E * Kϵ₂₂) @inbounds fields.σ₁₂[i, j, 1] = Ω * (fields.σ₁₂ₙ[i, j, 1] + Δτ * E * Kϵ₁₂) - @inbounds d[i, j, 1] = dᵢ + @inbounds tracers.d[i, j, 1] = dᵢ end ##### @@ -300,4 +299,4 @@ end @inline ice_stress_ux(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, hσ₁₁, fields) @inline ice_stress_uy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₁₂, fields) @inline ice_stress_vx(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, hσ₁₂, fields) -@inline ice_stress_vy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₂₂, fields) +@inline ice_stress_vy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₂₂, fields) \ No newline at end of file diff --git a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl index a885d83b..ca5d6f73 100644 --- a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl @@ -54,7 +54,8 @@ function step_momentum!(model, dynamics::SplitExplicitMomentumEquation, Δt, arg model_fields = merge(dynamics.auxiliary_fields, model.velocities, (; h = model.ice_thickness, ℵ = model.ice_concentration, - ρ = model.ice_density)) + ρ = model.ice_density), + model.tracers) u_velocity_kernel!, _ = configure_kernel(arch, grid, :xy, _u_velocity_step!) v_velocity_kernel!, _ = configure_kernel(arch, grid, :xy, _v_velocity_step!) From 30cd58bd91ee298a92928baa3f0341975427e360 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Mon, 3 Mar 2025 21:37:31 +0100 Subject: [PATCH 008/108] will this work? --- examples/freezing_bucket.jl | 4 +- .../SeaIceThermodynamics.jl | 3 +- .../slab_thermodynamics_tendencies.jl | 64 ++++++++++++------- src/sea_ice_model.jl | 2 +- src/sea_ice_time_stepping.jl | 8 +-- src/tracer_tendency_kernel_functions.jl | 58 ++++++----------- 6 files changed, 69 insertions(+), 70 deletions(-) diff --git a/examples/freezing_bucket.jl b/examples/freezing_bucket.jl index 143d2874..d21e7477 100644 --- a/examples/freezing_bucket.jl +++ b/examples/freezing_bucket.jl @@ -58,9 +58,11 @@ ice_thermodynamics = SlabSeaIceThermodynamics(grid; top_heat_boundary_condition) # Then we assemble it all into a model, - model = SeaIceModel(grid; ice_thermodynamics) +# The ice concentration is set to 1 everywhere, otherwise the ice cannot grow! +fill!(model.ice_concentration, 1) + # Note that the default bottom heat boundary condition for `SlabSeaIceThermodynamics` is # `IceWaterThermalEquilibrium` with freshwater. That's what we want! diff --git a/src/SeaIceThermodynamics/SeaIceThermodynamics.jl b/src/SeaIceThermodynamics/SeaIceThermodynamics.jl index 1f325dd7..aa067c32 100644 --- a/src/SeaIceThermodynamics/SeaIceThermodynamics.jl +++ b/src/SeaIceThermodynamics/SeaIceThermodynamics.jl @@ -121,7 +121,8 @@ end end # Fallback for no thermodynamics -@inline thickness_thermodynamic_tendency(i, j, grid, args...) = zero(grid) +@inline bottom_ice_formation(i, j, k, grid, ::Nothing, args...) = zero(grid) +@inline thickness_growth(i, j, k, grid, ::Nothing, args...) = zero(grid) include("HeatBoundaryConditions/HeatBoundaryConditions.jl") diff --git a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl index e60f2005..9ad5cc70 100644 --- a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl +++ b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl @@ -1,14 +1,40 @@ using ClimaSeaIce.SeaIceThermodynamics.HeatBoundaryConditions: bottom_temperature, top_surface_temperature -import ClimaSeaIce.SeaIceThermodynamics: thickness_thermodynamic_tendency -@inline function thickness_thermodynamic_tendency(i, j, k, grid, - ice_thickness, - ice_concentration, - ice_consolidation_thickness, - thermodynamics::SlabSeaIceThermodynamics, - top_external_heat_flux, - bottom_external_heat_flux, - clock, model_fields) +# Frazil ice formation +@inline function bottom_ice_formation(i, j, k, grid, + thermodynamics::SlabSeaIceThermodynamics, + bottom_external_heat_flux, + clock, model_fields) + + phase_transitions = thermodynamics.phase_transitions + bottom_heat_bc = thermodynamics.heat_boundary_conditions.bottom + liquidus = phase_transitions.liquidus + Tu = thermodynamics.top_surface_temperature + @inbounds Tuᵢ = Tu[i, j, k] + + Qb = bottom_external_heat_flux + + Tbᵢ = bottom_temperature(i, j, grid, bottom_heat_bc, liquidus) + ℰb = latent_heat(phase_transitions, Tbᵢ) + + # Retrieve bottom flux + Qbᵢ = getflux(Qb, i, j, grid, Tuᵢ, clock, model_fields) + + # If ice is consolidated, compute tendency for an ice slab; otherwise + # just add ocean fluxes from frazil ice formation or melting + wb = - Qbᵢ / ℰb + + return wb +end + +@inline function thickness_growth(i, j, k, grid, + thermodynamics::SlabSeaIceThermodynamics, + ice_thickness, + ice_concentration, + ice_consolidation_thickness, + top_external_heat_flux, + bottom_external_heat_flux, + clock, model_fields) phase_transitions = thermodynamics.phase_transitions @@ -18,17 +44,13 @@ import ClimaSeaIce.SeaIceThermodynamics: thickness_thermodynamic_tendency Qi = thermodynamics.internal_heat_flux Qu = top_external_heat_flux - Qb = bottom_external_heat_flux Tu = thermodynamics.top_surface_temperature @inbounds begin - hᶜ = ice_consolidation_thickness[i, j, k] hᵢ = ice_thickness[i, j, k] + ℵᵢ = ice_concentration[i, j, k] end - # Consolidation criteria - consolidated_ice = hᵢ >= hᶜ - # Determine top surface temperature. # Does this really fit here? # This is updating the temperature inside the thermodynamics module @@ -52,18 +74,14 @@ import ClimaSeaIce.SeaIceThermodynamics: thickness_thermodynamic_tendency # Retrieve fluxes Quᵢ = getflux(Qu, i, j, grid, Tuᵢ, clock, model_fields) Qiᵢ = getflux(Qi, i, j, grid, Tuᵢ, clock, model_fields) - Qbᵢ = getflux(Qb, i, j, grid, Tuᵢ, clock, model_fields) - - # If ice is consolidated, compute tendency for an ice slab; otherwise - # just add ocean fluxes from frazil ice formation or melting - slushy_Gh = - Qbᵢ / ℰb # Upper (top) and bottom interface velocities - wu = (Quᵢ - Qiᵢ) / ℰu # < 0 => melting - wb = (Qiᵢ - Qbᵢ) / ℰb # < 0 => freezing + wu = (Quᵢ - Qiᵢ) / ℰu * ℵᵢ # < 0 => melting + wb = + Qiᵢ / ℰb * ℵᵢ # < 0 => freezing slabby_Gh = wu + wb - return ifelse(consolidated_ice, slabby_Gh, slushy_Gh) -end + @show wu, wb, slabby_Gh, hᵢ, ℵᵢ, Quᵢ, Qiᵢ, ℰu, ℰb, Tuᵢ, Tbᵢ + return slabby_Gh +end \ No newline at end of file diff --git a/src/sea_ice_model.jl b/src/sea_ice_model.jl index 1d776bf1..23351813 100644 --- a/src/sea_ice_model.jl +++ b/src/sea_ice_model.jl @@ -141,7 +141,7 @@ const SIM = SeaIceModel ℵ⁺ = ℵ[i, j, 1] h⁻ = hmin[i, j, 1] - ht, ℵt = cap_ice_thickness(h⁺, h⁻, ℵ⁺) + ht, ℵt = conservative_adjustment(h⁺, h⁻, ℵ⁺) ℵ[i, j, 1] = ℵt h[i, j, 1] = ht diff --git a/src/sea_ice_time_stepping.jl b/src/sea_ice_time_stepping.jl index 03dd5b13..c7b20ee6 100644 --- a/src/sea_ice_time_stepping.jl +++ b/src/sea_ice_time_stepping.jl @@ -61,7 +61,7 @@ end ℵ⁺ = max(zero(ℵ⁺), ℵ⁺) # Concentration cannot be negative, clip it up h⁺ = max(zero(h⁺), h⁺) # Thickness cannot be negative, clip it up - ht, ℵt = cap_ice_thickness(h⁺, h⁻, ℵ⁺) + ht, ℵt = conservative_adjustment(h⁺, h⁻, ℵ⁺) ℵ[i, j, k] = ℵt h[i, j, k] = ht @@ -72,14 +72,14 @@ end # to maintain a constant ice volume. # A no ice condition is represented by h = hmin and ℵ = 0 since ice_volume = (h * ℵ) # The thickness should _NEVER_ be zero! -@inline function cap_ice_thickness(h⁺, h⁻, ℵ⁺) +@inline function conservative_adjustment(h⁺, h⁻, ℵ⁺) # Remove ice if h⁺ == 0 thin_ice = (0 < h⁺ < h⁻) # Thin ice condition - ht = ifelse(thin_ice, h⁻, h⁺) + ht = ifelse(thin_ice, h⁻, h⁺) # Non conservative adjustement of thickness + V⁺ = h⁺ * ℵ⁺ # Total sea ice volume ht = ifelse(ℵ⁺ > 1, ht * ℵ⁺, ht) - ℵt = ifelse(ht == 0, zero(ℵ⁺), ℵ⁺) ht = ifelse(ht == 0, h⁻, ht) ℵt = ifelse(ℵt > 1, one(ℵt), ℵt) diff --git a/src/tracer_tendency_kernel_functions.jl b/src/tracer_tendency_kernel_functions.jl index 6646f5b0..ec82a990 100644 --- a/src/tracer_tendency_kernel_functions.jl +++ b/src/tracer_tendency_kernel_functions.jl @@ -1,5 +1,5 @@ using Oceananigans.Advection -using ClimaSeaIce.SeaIceThermodynamics: thickness_thermodynamic_tendency +using ClimaSeaIce.SeaIceThermodynamics: thickness_growth, bottom_ice_formation using ClimaSeaIce.SeaIceMomentumEquations: compute_momentum_tendencies! function compute_tendencies!(model::SIM, Δt) @@ -46,49 +46,27 @@ end i, j = @index(Global, NTuple) - @inbounds Gⁿ.h[i, j, 1] = ice_thickness_tendency(i, j, 1, grid, clock, - velocities, - advection, - ice_thickness, - ice_concentration, - ice_consolidation_thickness, - thermodynamics, - top_external_heat_flux, - bottom_external_heat_flux, - h_forcing, - model_fields) - - @inbounds Gⁿ.ℵ[i, j, 1] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_concentration) -end + @inbounds hᵢ = ice_thickness[i, j, 1] + @inbounds ℵᵢ = ice_concentration[i, j, 1] -# Thickness change due to accretion and melting, restricted by minimum allowable value -function ice_thickness_tendency(i, j, k, grid, clock, - velocities, - advection, - ice_thickness, - ice_concentration, - ice_consolidation_thickness, - thermodynamics, - top_external_heat_flux, - bottom_external_heat_flux, - h_forcing, - model_fields) + Gh⁺ = thickness_growth(i, j, 1, grid, + thermodynamics, + ice_thickness, + ice_concentration, + ice_consolidation_thickness, + top_external_heat_flux, + bottom_external_heat_flux, + clock, model_fields) - Gh_advection = - horizontal_div_Uc(i, j, k, grid, advection, velocities, ice_thickness) # div_Uℵh(i, j, k, grid, advection, velocities, ice_concentration, ice_thickness) + GV⁺ = bottom_ice_formation(i, j, 1, grid, thermodynamics, bottom_external_heat_flux, clock, model_fields) - Gh_thermodynamics = thickness_thermodynamic_tendency(i, j, k, grid, - ice_thickness, - ice_concentration, - ice_consolidation_thickness, - thermodynamics, - top_external_heat_flux, - bottom_external_heat_flux, - clock, model_fields) + Gℵ = GV⁺ / hᵢ + Gh = Gh⁺ * ℵᵢ - - # Compute forcing - Fh = zero(grid) #h_forcing(i, j, grid, clock, model_fields) + Guh = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_thickness) + Guℵ = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_concentration) - return Gh_advection + Gh_thermodynamics + Fh + @inbounds Gⁿ.h[i, j, 1] = Gh + Guh + @inbounds Gⁿ.ℵ[i, j, 1] = Gℵ + Guℵ end From 31751e433b4707cb2a468a60e1930d8b4ef9043a Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Mon, 3 Mar 2025 21:39:11 +0100 Subject: [PATCH 009/108] remove the show --- src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl index 9ad5cc70..f45dc2f9 100644 --- a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl +++ b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl @@ -81,7 +81,5 @@ end slabby_Gh = wu + wb - @show wu, wb, slabby_Gh, hᵢ, ℵᵢ, Quᵢ, Qiᵢ, ℰu, ℰb, Tuᵢ, Tbᵢ - return slabby_Gh end \ No newline at end of file From 94c3aa2b16b2e8b5e23f7b0425fd26e8b1467ba0 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Mon, 3 Mar 2025 23:27:04 +0100 Subject: [PATCH 010/108] bump to 0.2.1 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index bea8d895..4c773e5d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ClimaSeaIce" uuid = "6ba0ff68-24e6-4315-936c-2e99227c95a4" authors = ["Climate Modeling Alliance and contributors"] -version = "0.2.0" +version = "0.2.1" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" From f840534301a120dd5db8f3a44f250454131bfeb3 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Mon, 3 Mar 2025 23:41:17 +0100 Subject: [PATCH 011/108] add this --- src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl index f45dc2f9..15c07aed 100644 --- a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl +++ b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl @@ -48,9 +48,12 @@ end @inbounds begin hᵢ = ice_thickness[i, j, k] + hc = ice_consolidation_thickness[i, j, k] ℵᵢ = ice_concentration[i, j, k] end + consolidated_ice = hᵢ > hc + # Determine top surface temperature. # Does this really fit here? # This is updating the temperature inside the thermodynamics module From f8739e54f6f1537c608fc76b9a755c5f46f4f658 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 11:16:43 +0100 Subject: [PATCH 012/108] make sure we do not mask point 1 but the top --- .../split_explicit_momentum_equations.jl | 4 ++-- src/sea_ice_time_stepping.jl | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl index 633f72ec..6ba70c54 100644 --- a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl @@ -99,8 +99,8 @@ function step_momentum!(model, dynamics::SplitExplicitMomentumEquation, Δt, arg # TODO: This needs to be removed in some way! fill_halo_regions!(model.velocities) - mask_immersed_field_xy!(model.velocities.u, k=1) - mask_immersed_field_xy!(model.velocities.v, k=1) + mask_immersed_field_xy!(model.velocities.u, k=size(grid, 3)) + mask_immersed_field_xy!(model.velocities.v, k=size(grid, 3)) end return nothing diff --git a/src/sea_ice_time_stepping.jl b/src/sea_ice_time_stepping.jl index c7b20ee6..2aa670e6 100644 --- a/src/sea_ice_time_stepping.jl +++ b/src/sea_ice_time_stepping.jl @@ -78,7 +78,6 @@ end thin_ice = (0 < h⁺ < h⁻) # Thin ice condition ht = ifelse(thin_ice, h⁻, h⁺) # Non conservative adjustement of thickness - V⁺ = h⁺ * ℵ⁺ # Total sea ice volume ht = ifelse(ℵ⁺ > 1, ht * ℵ⁺, ht) ℵt = ifelse(ht == 0, zero(ℵ⁺), ℵ⁺) ht = ifelse(ht == 0, h⁻, ht) @@ -90,7 +89,7 @@ end function update_state!(model::SIM) foreach(prognostic_fields(model)) do field - mask_immersed_field!(field) + mask_immersed_field_xy!(field, k=size(grid, 3)) end fill_halo_regions!(prognostic_fields(model), model.clock, fields(model)) From b27d6b875a27cdb3763553945513a9eaaa4d0262 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 14:18:16 +0100 Subject: [PATCH 013/108] bugfix --- src/sea_ice_time_stepping.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sea_ice_time_stepping.jl b/src/sea_ice_time_stepping.jl index 2aa670e6..810409b8 100644 --- a/src/sea_ice_time_stepping.jl +++ b/src/sea_ice_time_stepping.jl @@ -89,7 +89,7 @@ end function update_state!(model::SIM) foreach(prognostic_fields(model)) do field - mask_immersed_field_xy!(field, k=size(grid, 3)) + mask_immersed_field_xy!(field, k=size(model.grid, 3)) end fill_halo_regions!(prognostic_fields(model), model.clock, fields(model)) From b0843b0e66f923679a04c431b0f3044436b87887 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 14:27:04 +0100 Subject: [PATCH 014/108] update --- src/sea_ice_time_stepping.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sea_ice_time_stepping.jl b/src/sea_ice_time_stepping.jl index 810409b8..88ec54cd 100644 --- a/src/sea_ice_time_stepping.jl +++ b/src/sea_ice_time_stepping.jl @@ -1,6 +1,7 @@ using Oceananigans.Utils: Time using Oceananigans.Fields: flattened_unique_values using Oceananigans.OutputReaders: extract_field_time_series, update_field_time_series! +using Oceananigans.ImmersedBoundaries: mask_immersed_field_xy! using ClimaSeaIce.SeaIceMomentumEquations: step_momentum! From 5643b956d534a9a65d1c785819594ffd590d2fa3 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 17:16:27 +0100 Subject: [PATCH 015/108] start separating the two --- examples/freezing_bucket.jl | 6 +- examples/ice_advected_by_anticyclone.jl | 2 +- examples/ice_advected_on_coastline.jl | 2 +- .../SeaIceThermodynamics.jl | 7 +- .../slab_thermodynamics_tendencies.jl | 28 +++---- .../thermodynamic_time_step.jl | 83 +++++++++++++++++++ src/sea_ice_model.jl | 16 ++-- src/sea_ice_time_stepping.jl | 47 ++++------- src/tracer_tendency_kernel_functions.jl | 63 +++++--------- test/test_sea_ice_advection.jl | 2 +- test/test_time_stepping.jl | 10 +-- 11 files changed, 156 insertions(+), 110 deletions(-) create mode 100644 src/SeaIceThermodynamics/thermodynamic_time_step.jl diff --git a/examples/freezing_bucket.jl b/examples/freezing_bucket.jl index d21e7477..7df35b09 100644 --- a/examples/freezing_bucket.jl +++ b/examples/freezing_bucket.jl @@ -52,13 +52,13 @@ top_heat_boundary_condition = PrescribedTemperature(-10) # Construct the thermodynamics of sea ice, for this we use a simple # slab sea ice representation of thermodynamics -ice_thermodynamics = SlabSeaIceThermodynamics(grid; +thermodynamics = SlabSeaIceThermodynamics(grid; internal_heat_flux, phase_transitions, top_heat_boundary_condition) # Then we assemble it all into a model, -model = SeaIceModel(grid; ice_thermodynamics) +model = SeaIceModel(grid; thermodynamics) # The ice concentration is set to 1 everywhere, otherwise the ice cannot grow! fill!(model.ice_concentration, 1) @@ -66,7 +66,7 @@ fill!(model.ice_concentration, 1) # Note that the default bottom heat boundary condition for `SlabSeaIceThermodynamics` is # `IceWaterThermalEquilibrium` with freshwater. That's what we want! -model.ice_thermodynamics.heat_boundary_conditions.bottom +model.thermodynamics.heat_boundary_conditions.bottom # Ok, we're ready to freeze the bucket for 10 straight days with an initial ice # thickness of 1 cm, diff --git a/examples/ice_advected_by_anticyclone.jl b/examples/ice_advected_by_anticyclone.jl index 5b4bfb20..ecb968d6 100644 --- a/examples/ice_advected_by_anticyclone.jl +++ b/examples/ice_advected_by_anticyclone.jl @@ -97,7 +97,7 @@ momentum_equations = SeaIceMomentumEquation(grid; model = SeaIceModel(grid; dynamics = momentum_equations, - ice_thermodynamics = nothing, # No thermodynamics here + thermodynamics = nothing, # No thermodynamics here advection = WENO(order=7), boundary_conditions = (u=u_bcs, v=v_bcs)) diff --git a/examples/ice_advected_on_coastline.jl b/examples/ice_advected_on_coastline.jl index ae3a0e8a..5bb2aa72 100644 --- a/examples/ice_advected_on_coastline.jl +++ b/examples/ice_advected_on_coastline.jl @@ -73,7 +73,7 @@ model = SeaIceModel(grid; advection = WENO(order=7), dynamics = dynamics, boundary_conditions = (; u=u_bcs), - ice_thermodynamics = nothing) + thermodynamics = nothing) # We start with a concentration of ℵ = 1 everywhere set!(model, h = 1) diff --git a/src/SeaIceThermodynamics/SeaIceThermodynamics.jl b/src/SeaIceThermodynamics/SeaIceThermodynamics.jl index aa067c32..264de3f3 100644 --- a/src/SeaIceThermodynamics/SeaIceThermodynamics.jl +++ b/src/SeaIceThermodynamics/SeaIceThermodynamics.jl @@ -121,8 +121,8 @@ end end # Fallback for no thermodynamics -@inline bottom_ice_formation(i, j, k, grid, ::Nothing, args...) = zero(grid) -@inline thickness_growth(i, j, k, grid, ::Nothing, args...) = zero(grid) +@inline vertical_growth(i, j, k, grid, ::Nothing, args...) = zero(grid) +@inline lateral_growth(i, j, k, grid, ::Nothing, args...) = zero(grid) include("HeatBoundaryConditions/HeatBoundaryConditions.jl") @@ -150,8 +150,9 @@ import Oceananigans.Utils: prettytime # TODO: Fix this after this PR # include("EnthalpyMethodThermodynamics.jl") -include("slab_sea_ice_thermodynamics.jl") +include("slab_sea_thermodynamics.jl") include("slab_heat_and_tracer_fluxes.jl") include("slab_thermodynamics_tendencies.jl") +include("thermodynamic_time_step.jl") end diff --git a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl index 15c07aed..d20a5f02 100644 --- a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl +++ b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl @@ -1,10 +1,10 @@ using ClimaSeaIce.SeaIceThermodynamics.HeatBoundaryConditions: bottom_temperature, top_surface_temperature # Frazil ice formation -@inline function bottom_ice_formation(i, j, k, grid, - thermodynamics::SlabSeaIceThermodynamics, - bottom_external_heat_flux, - clock, model_fields) +@inline function lateral_growth(i, j, k, grid, + thermodynamics::SlabSeaIceThermodynamics, + bottom_external_heat_flux, + clock, model_fields) phase_transitions = thermodynamics.phase_transitions bottom_heat_bc = thermodynamics.heat_boundary_conditions.bottom @@ -22,19 +22,17 @@ using ClimaSeaIce.SeaIceThermodynamics.HeatBoundaryConditions: bottom_temperatur # If ice is consolidated, compute tendency for an ice slab; otherwise # just add ocean fluxes from frazil ice formation or melting - wb = - Qbᵢ / ℰb - - return wb + return - Qbᵢ / ℰb end -@inline function thickness_growth(i, j, k, grid, - thermodynamics::SlabSeaIceThermodynamics, - ice_thickness, - ice_concentration, - ice_consolidation_thickness, - top_external_heat_flux, - bottom_external_heat_flux, - clock, model_fields) +@inline function vertical_growth(i, j, k, grid, + thermodynamics::SlabSeaIceThermodynamics, + ice_thickness, + ice_concentration, + ice_consolidation_thickness, + top_external_heat_flux, + bottom_external_heat_flux, + clock, model_fields) phase_transitions = thermodynamics.phase_transitions diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl new file mode 100644 index 00000000..44ba3c37 --- /dev/null +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -0,0 +1,83 @@ + +thermodynamic_step!(model, ::Nothing, Δt) = nothing + +function thermodynamic_step!(model, ::SlabSeaIceThermodynamics, Δt) + grid = model.grid + arch = architecture(grid) + + launch!(arch, grid, :xy, + _slab_thermodynamic_step!, + model.ice_thickness, + model.ice_concentration, + grid, + model.clock, + model.ice_consolidation_thickness, + model.thermodynamics, + model.external_heat_fluxes.top, + model.external_heat_fluxes.bottom, + fields(model)) + + + return nothing +end + +# The thermodynamic step is computed in a single kernel following: +# +# ∂t_V = ∂t_h * ℵ + h * ∂t_ℵ +# +# We consider the lateral growth as increasing ℵ and the vertical growth as increasing h. +# This equation is solved semi-implicitly to avoid NaNs coming from the division by quantities that can go to zero. +# Therefore: +# +# hⁿ⁺¹ - hⁿ ℵⁿ⁺¹ - ℵⁿ +# ∂t_V = Gᴸ + Gⱽ = --------- ⋅ ℵⁿ⁺¹ + --------- ⋅ hⁿ⁺¹ +# Δt Δt +# +# Leading to: +# +# ℵⁿ⁺¹ - ℵⁿ Gᴸ +# --------- = ----- +# Δt hⁿ⁺¹ +# +# And +# +# hⁿ⁺¹ - hⁿ hⁿ⁺¹ - 1 1 +# --------- = [Gⱽ + Gᴸ --------] ----- +# Δt hⁿ⁺¹ ℵⁿ⁺¹ +# +# The two will be adjusted conservatively after the thermodynamic step to ensure that ℵ ≤ 1. +@kernel function _slab_thermodynamic_step!(ice_thickness, + ice_concentration, + grid, + clock, + ice_consolidation_thickness, + thermodynamics, + top_external_heat_flux, + bottom_external_heat_flux, + model_fields) + + i, j = @index(Global, NTuple) + + @inbounds hᵢ = ice_thickness[i, j, 1] + @inbounds ℵᵢ = ice_concentration[i, j, 1] + + Gh⁺ = lateral_growth(i, j, 1, grid, + thermodynamics, + ice_thickness, + ice_concentration, + ice_consolidation_thickness, + top_external_heat_flux, + bottom_external_heat_flux, + clock, model_fields) + + GV⁺ = vertical_growth(i, j, 1, grid, thermodynamics, bottom_external_heat_flux, clock, model_fields) + + Gℵ = GV⁺ / hᵢ + Gh = Gh⁺ * ℵᵢ + + Guh = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_thickness) + Guℵ = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_concentration) + + @inbounds Gⁿ.h[i, j, 1] = Gh + Guh + @inbounds Gⁿ.ℵ[i, j, 1] = Gℵ + Guℵ +end \ No newline at end of file diff --git a/src/sea_ice_model.jl b/src/sea_ice_model.jl index 23351813..d504845b 100644 --- a/src/sea_ice_model.jl +++ b/src/sea_ice_model.jl @@ -19,7 +19,7 @@ struct SeaIceModel{GR, TD, D, TS, CL, U, T, IT, IC, ID, CT, STF, A, F} <: Abstra ice_density :: ID ice_consolidation_thickness :: CT # Thermodynamics - ice_thermodynamics :: TD + thermodynamics :: TD # Dynamics dynamics :: D # External boundary conditions @@ -44,7 +44,7 @@ function SeaIceModel(grid; advection = nothing, tracers = (), boundary_conditions = NamedTuple(), - ice_thermodynamics = SlabSeaIceThermodynamics(grid), + thermodynamics = SlabSeaIceThermodynamics(grid), dynamics = nothing, forcing = NamedTuple()) @@ -97,11 +97,11 @@ function SeaIceModel(grid; tracers = merge(tracers, (; S = ice_salinity)) timestepper = ForwardEulerTimeStepper(grid, prognostic_fields) - if !isnothing(ice_thermodynamics) + if !isnothing(thermodynamics) if isnothing(top_heat_flux) - if ice_thermodynamics.heat_boundary_conditions.top isa PrescribedTemperature + if thermodynamics.heat_boundary_conditions.top isa PrescribedTemperature # Default: external top flux is in equilibrium with internal fluxes - top_heat_flux = ice_thermodynamics.internal_heat_flux + top_heat_flux = thermodynamics.internal_heat_flux else # Default: no external top surface flux top_heat_flux = 0 @@ -124,7 +124,7 @@ function SeaIceModel(grid; ice_concentration, ice_density, ice_consolidation_thickness, - ice_thermodynamics, + thermodynamics, dynamics, external_heat_fluxes, timestepper, @@ -176,7 +176,7 @@ function Base.show(io::IO, model::SIM) print(io, "SeaIceModel{", typeof(arch), ", ", gridname, "}", timestr, '\n') print(io, "├── grid: ", summary(model.grid), '\n') - print(io, "├── ice_thermodynamics: ", summary(model.ice_thermodynamics), '\n') + print(io, "├── thermodynamics: ", summary(model.thermodynamics), '\n') print(io, "├── advection: ", summary(model.advection), '\n') print(io, "└── external_heat_fluxes: ", '\n') print(io, " ├── top: ", flux_summary(model.external_heat_fluxes.top, " │"), '\n') @@ -194,7 +194,7 @@ fields(model::SIM) = merge((; h = model.ice_thickness, ℵ = model.ice_concentration), model.tracers, model.velocities, - fields(model.ice_thermodynamics), + fields(model.thermodynamics), fields(model.dynamics)) # TODO: make this correct diff --git a/src/sea_ice_time_stepping.jl b/src/sea_ice_time_stepping.jl index 88ec54cd..3de4a1ef 100644 --- a/src/sea_ice_time_stepping.jl +++ b/src/sea_ice_time_stepping.jl @@ -4,21 +4,29 @@ using Oceananigans.OutputReaders: extract_field_time_series, update_field_time_s using Oceananigans.ImmersedBoundaries: mask_immersed_field_xy! using ClimaSeaIce.SeaIceMomentumEquations: step_momentum! +using ClimaSeaIce.SeaIceThermodynamics: thermodynamic_step! import Oceananigans.Models: update_model_field_time_series! const FESeaIceModel = SeaIceModel{<:Any, <:Any, <:Any, <:ForwardEulerTimeStepper} +# We separate the thermodynamic step from the advection (dynamic) step. +# The thermodynamic step is column physics and is performed all at once. function time_step!(model::FESeaIceModel, Δt; callbacks = []) # Be paranoid and update state at iteration 0 model.clock.iteration == 0 && update_state!(model) + # Perform the thermodynamic step + thermodynamic_step!(model, model.thermodynamics, Δt) + + # Compute advective tendencies and update + # advected tracers compute_tendencies!(model, Δt) step_tracers!(model, Δt) - # TODO: This is an implicit (or split-explicit) step to advance momentum! - step_momentum!(model, model.dynamics, Δt, 1) + # TODO: This is an implicit (or split-explicit) step to advance momentum. + step_momentum!(model, model.dynamics, Δt) tick!(model.clock, Δt) update_state!(model) @@ -30,9 +38,8 @@ function step_tracers!(model::SIM, Δt) grid = model.grid arch = architecture(grid) - h = model.ice_thickness - ℵ = model.ice_concentration - hmin = model.ice_consolidation_thickness + h = model.ice_thickness + ℵ = model.ice_concentration tracers = model.tracers Gⁿ = model.timestepper.Gⁿ @@ -46,7 +53,7 @@ end # We compute hⁿ⁺¹ and ℵⁿ⁺¹ in the same kernel to account for ridging: # if ℵ > 1, we reset the concentration to 1 and adjust the thickness # to conserve the total ice volume in the cell. -@kernel function _step_tracers!(h, ℵ, hmin, tracers, Gⁿ, Δt) +@kernel function _step_tracers!(h, ℵ, tracers, Gⁿ, Δt) i, j = @index(Global, NTuple) k = 1 @@ -55,38 +62,20 @@ end # Update ice thickness, clipping negative values @inbounds begin - h⁻ = hmin[i, j, k] h⁺ = h[i, j, k] + Δt * Ghⁿ[i, j, k] ℵ⁺ = ℵ[i, j, k] + Δt * Gℵⁿ[i, j, k] ℵ⁺ = max(zero(ℵ⁺), ℵ⁺) # Concentration cannot be negative, clip it up h⁺ = max(zero(h⁺), h⁺) # Thickness cannot be negative, clip it up - ht, ℵt = conservative_adjustment(h⁺, h⁻, ℵ⁺) - - ℵ[i, j, k] = ℵt - h[i, j, k] = ht + # Ridging and rafting caused by the advection step + V⁺ = h⁺ * ℵ⁺ + + ℵ[i, j, k] = ifelse(ℵ⁺ > 1, one(ℵ⁺), ℵ⁺) + h[i, j, k] = ifelse(ℵ⁺ > 1, V⁺, h⁺) end end -# If h < hmin we reset the thickness to h⁻ and adjust the concentration accordingly -# to maintain a constant ice volume. -# A no ice condition is represented by h = hmin and ℵ = 0 since ice_volume = (h * ℵ) -# The thickness should _NEVER_ be zero! -@inline function conservative_adjustment(h⁺, h⁻, ℵ⁺) - - # Remove ice if h⁺ == 0 - thin_ice = (0 < h⁺ < h⁻) # Thin ice condition - - ht = ifelse(thin_ice, h⁻, h⁺) # Non conservative adjustement of thickness - ht = ifelse(ℵ⁺ > 1, ht * ℵ⁺, ht) - ℵt = ifelse(ht == 0, zero(ℵ⁺), ℵ⁺) - ht = ifelse(ht == 0, h⁻, ht) - ℵt = ifelse(ℵt > 1, one(ℵt), ℵt) - - return ht, ℵt -end - function update_state!(model::SIM) foreach(prognostic_fields(model)) do field diff --git a/src/tracer_tendency_kernel_functions.jl b/src/tracer_tendency_kernel_functions.jl index ec82a990..f68d609a 100644 --- a/src/tracer_tendency_kernel_functions.jl +++ b/src/tracer_tendency_kernel_functions.jl @@ -1,5 +1,4 @@ using Oceananigans.Advection -using ClimaSeaIce.SeaIceThermodynamics: thickness_growth, bottom_ice_formation using ClimaSeaIce.SeaIceMomentumEquations: compute_momentum_tendencies! function compute_tendencies!(model::SIM, Δt) @@ -13,60 +12,36 @@ function compute_tracer_tendencies!(model::SIM) arch = architecture(grid) launch!(arch, grid, :xy, - _compute_tracer_tendencies!, + _compute_dynamic_tracer_tendencies!, model.timestepper.Gⁿ, - model.ice_thickness, grid, - model.clock, model.velocities, model.advection, + model.ice_thickness, model.ice_concentration, - model.ice_consolidation_thickness, - model.ice_thermodynamics, - model.external_heat_fluxes.top, - model.external_heat_fluxes.bottom, - model.forcing.h, - fields(model)) + model.tracers) return nothing end -@kernel function _compute_tracer_tendencies!(Gⁿ, ice_thickness, - grid, - clock, - velocities, - advection, - ice_concentration, - ice_consolidation_thickness, - thermodynamics, - top_external_heat_flux, - bottom_external_heat_flux, - h_forcing, - model_fields) +@kernel function _compute_dynamic_tracer_tendencies!(Gⁿ, + grid, + velocities, + advection, + ice_thickness, + ice_concentration, + tracers) i, j = @index(Global, NTuple) - @inbounds hᵢ = ice_thickness[i, j, 1] - @inbounds ℵᵢ = ice_concentration[i, j, 1] - - Gh⁺ = thickness_growth(i, j, 1, grid, - thermodynamics, - ice_thickness, - ice_concentration, - ice_consolidation_thickness, - top_external_heat_flux, - bottom_external_heat_flux, - clock, model_fields) - - GV⁺ = bottom_ice_formation(i, j, 1, grid, thermodynamics, bottom_external_heat_flux, clock, model_fields) - - Gℵ = GV⁺ / hᵢ - Gh = Gh⁺ * ℵᵢ - - Guh = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_thickness) - Guℵ = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_concentration) - - @inbounds Gⁿ.h[i, j, 1] = Gh + Guh - @inbounds Gⁿ.ℵ[i, j, 1] = Gℵ + Guℵ + @inbounds begin + Gⁿ.h[i, j, 1] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_thickness) + Gⁿ.ℵ[i, j, 1] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_concentration) + + for (n, θ) in enumerate(tracers) + @inbounds Gⁿ[n] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, θ) + end + end end + diff --git a/test/test_sea_ice_advection.jl b/test/test_sea_ice_advection.jl index 6f129106..225feddf 100644 --- a/test/test_sea_ice_advection.jl +++ b/test/test_sea_ice_advection.jl @@ -26,7 +26,7 @@ end grid = RectilinearGrid(size=(10, 10), x=(0, 1), y=(0, 1), topology=(Bounded, Bounded, Flat)) dynamics = SeaIceMomentumEquation(grid, rheology=ViscousRheology(ν=1000)) - model = SeaIceModel(grid; dynamics, ice_thermodynamics=nothing, advection=WENO()) + model = SeaIceModel(grid; dynamics, thermodynamics=nothing, advection=WENO()) @test !(model.velocities.u isa Nothing) @test !(model.velocities.v isa Nothing) diff --git a/test/test_time_stepping.jl b/test/test_time_stepping.jl index 202e2d0e..33e07b7c 100644 --- a/test/test_time_stepping.jl +++ b/test/test_time_stepping.jl @@ -1,9 +1,9 @@ function time_step_sea_ice_model_works(grid; dynamics = nothing, - ice_thermodynamics = nothing, + thermodynamics = nothing, advection = nothing) - model = SeaIceModel(grid; dynamics, ice_thermodynamics, advection) + model = SeaIceModel(grid; dynamics, thermodynamics, advection) simulation = Simulation(model, Δt=1.0, stop_iteration=1) run!(simulation) @@ -21,17 +21,17 @@ end rheologies = (ElastoViscoPlasticRheology(), ViscousRheology(ν=1000)) advections = (WENO(), UpwindBiased(order=5)) - ice_thermodynamics = (nothing, SlabSeaIceThermodynamics(grid)) + thermodynamics = (nothing, SlabSeaIceThermodynamics(grid)) coriolises = (nothing, FPlane(latitude=45), BetaPlane(latitude=45)) solvers = (ExplicitSolver(), SplitExplicitSolver()) - for coriolis in coriolises, advection in advections, rheology in rheologies, thermodynamics in ice_thermodynamics, solver in solvers + for coriolis in coriolises, advection in advections, rheology in rheologies, thermodynamics in thermodynamics, solver in solvers dynamics = SeaIceMomentumEquation(grid; coriolis, rheology, solver) @test time_step_sea_ice_model_works(grid; dynamics, - ice_thermodynamics=thermodynamics, + thermodynamics=thermodynamics, advection=advection) end end From 35af7219a6018792fb77632d9c9dade478edeb73 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 17:47:22 +0100 Subject: [PATCH 016/108] will this work? --- .../thermodynamic_time_step.jl | 82 +++++++++++-------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index 44ba3c37..0a5f245e 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -9,7 +9,7 @@ function thermodynamic_step!(model, ::SlabSeaIceThermodynamics, Δt) _slab_thermodynamic_step!, model.ice_thickness, model.ice_concentration, - grid, + grid, Δt, model.clock, model.ice_consolidation_thickness, model.thermodynamics, @@ -25,30 +25,17 @@ end # # ∂t_V = ∂t_h * ℵ + h * ∂t_ℵ # -# We consider the lateral growth as increasing ℵ and the vertical growth as increasing h. -# This equation is solved semi-implicitly to avoid NaNs coming from the division by quantities that can go to zero. # Therefore: # -# hⁿ⁺¹ - hⁿ ℵⁿ⁺¹ - ℵⁿ -# ∂t_V = Gᴸ + Gⱽ = --------- ⋅ ℵⁿ⁺¹ + --------- ⋅ hⁿ⁺¹ -# Δt Δt +# h⁺ * ℵ⁺ - hⁿ * ℵⁿ +# ∂t_V = Gᴸ + Gⱽ = ------------------- +# Δt # -# Leading to: -# -# ℵⁿ⁺¹ - ℵⁿ Gᴸ -# --------- = ----- -# Δt hⁿ⁺¹ -# -# And -# -# hⁿ⁺¹ - hⁿ hⁿ⁺¹ - 1 1 -# --------- = [Gⱽ + Gᴸ --------] ----- -# Δt hⁿ⁺¹ ℵⁿ⁺¹ -# # The two will be adjusted conservatively after the thermodynamic step to ensure that ℵ ≤ 1. @kernel function _slab_thermodynamic_step!(ice_thickness, ice_concentration, grid, + Δt, clock, ice_consolidation_thickness, thermodynamics, @@ -58,26 +45,51 @@ end i, j = @index(Global, NTuple) - @inbounds hᵢ = ice_thickness[i, j, 1] - @inbounds ℵᵢ = ice_concentration[i, j, 1] + @inbounds hⁿ = ice_thickness[i, j, 1] + @inbounds ℵⁿ = ice_concentration[i, j, 1] + + Gⱽ = vertical_growth(i, j, 1, grid, + thermodynamics, + ice_thickness, + ice_concentration, + ice_consolidation_thickness, + top_external_heat_flux, + bottom_external_heat_flux, + clock, model_fields) + + Gᴸ = lateral_growth(i, j, 1, grid, thermodynamics, bottom_external_heat_flux, clock, model_fields) + + # Total volume tendency + ∂t_V = Gᴸ + Gⱽ - Gh⁺ = lateral_growth(i, j, 1, grid, - thermodynamics, - ice_thickness, - ice_concentration, - ice_consolidation_thickness, - top_external_heat_flux, - bottom_external_heat_flux, - clock, model_fields) + # ice volume at timestep n+1 + Vⁿ⁺¹ = hⁿ * ℵⁿ + Δt * ∂t_V - GV⁺ = vertical_growth(i, j, 1, grid, thermodynamics, bottom_external_heat_flux, clock, model_fields) + # Adjust the ice volume to zero + Vⁿ⁺¹ = max(zero(Vⁿ⁺¹), Vⁿ⁺¹) - Gℵ = GV⁺ / hᵢ - Gh = Gh⁺ * ℵᵢ + # If Vⁿ⁺¹ == 0 the ice has melted completely, and we set both hⁿ⁺¹ and ℵⁿ⁺¹ to zero + # Otherwise, if the volume is positive, we adjust the thickness and concentration conservatively + # To account for this, we recalculate the actual volume derivative + ∂t_V = (Vⁿ⁺¹ - hⁿ * ℵⁿ) / Δt - Guh = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_thickness) - Guℵ = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_concentration) + # Simple explicit step, we assume lateral growth + # (at the beginning) contributes only to the ice concentration + ℵ⁺ = ℵⁿ + Δt * Gᴸ / hⁿ * (hⁿ > 0) + ℵ⁺ = max(zero(ℵ⁺), ℵ⁺) # Concentration cannot be negative, clip it up - @inbounds Gⁿ.h[i, j, 1] = Gh + Guh - @inbounds Gⁿ.ℵ[i, j, 1] = Gℵ + Guℵ + # The concentration derivative + ∂t_ℵ = (ℵ⁺ - ℵⁿ) / Δt + + # Adjust the thickness accordingly + h⁺ = hⁿ + Δt * (GV - hⁿ * ∂t_ℵ) / ℵ⁺ * (ℵ⁺ > 0) + + # Ridging and rafting caused by the thermodynamic step + h⁺ = max(zero(h⁺), h⁺) # Thickness cannot be negative, clip it up + + # Ridging and rafting caused by the advection step + V⁺ = h⁺ * ℵ⁺ + + @inbounds ℵ[i, j, k] = ifelse(ℵ⁺ > 1, one(ℵ⁺), ℵ⁺) + @inbounds h[i, j, k] = ifelse(ℵ⁺ > 1, V⁺, h⁺) end \ No newline at end of file From 57fd25dacc8dcaa0687c493ec17fb7bde71f363e Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 17:47:37 +0100 Subject: [PATCH 017/108] comment --- src/SeaIceThermodynamics/thermodynamic_time_step.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index 0a5f245e..57e88879 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -86,8 +86,6 @@ end # Ridging and rafting caused by the thermodynamic step h⁺ = max(zero(h⁺), h⁺) # Thickness cannot be negative, clip it up - - # Ridging and rafting caused by the advection step V⁺ = h⁺ * ℵ⁺ @inbounds ℵ[i, j, k] = ifelse(ℵ⁺ > 1, one(ℵ⁺), ℵ⁺) From e3031d45555814e981b12616cf8cfede75cde193 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 17:48:10 +0100 Subject: [PATCH 018/108] completely melt the ice --- src/SeaIceThermodynamics/thermodynamic_time_step.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index 57e88879..be40876b 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -87,6 +87,9 @@ end # Ridging and rafting caused by the thermodynamic step h⁺ = max(zero(h⁺), h⁺) # Thickness cannot be negative, clip it up V⁺ = h⁺ * ℵ⁺ + + ℵ⁺ = ifelse(Vⁿ⁺¹ == 0, zero(ℵ⁺), ℵ⁺) + h⁺ = ifelse(Vⁿ⁺¹ == 0, zero(h⁺), h⁺) @inbounds ℵ[i, j, k] = ifelse(ℵ⁺ > 1, one(ℵ⁺), ℵ⁺) @inbounds h[i, j, k] = ifelse(ℵ⁺ > 1, V⁺, h⁺) From 74b07c0d0f249f2911d7cd8c7079e801729c80ce Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 17:48:49 +0100 Subject: [PATCH 019/108] adjust the thermodynamic step --- src/SeaIceThermodynamics/thermodynamic_time_step.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index be40876b..cf4f1582 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -82,7 +82,7 @@ end ∂t_ℵ = (ℵ⁺ - ℵⁿ) / Δt # Adjust the thickness accordingly - h⁺ = hⁿ + Δt * (GV - hⁿ * ∂t_ℵ) / ℵ⁺ * (ℵ⁺ > 0) + h⁺ = hⁿ + Δt * (∂t_V - hⁿ * ∂t_ℵ) / ℵ⁺ * (ℵ⁺ > 0) # Ridging and rafting caused by the thermodynamic step h⁺ = max(zero(h⁺), h⁺) # Thickness cannot be negative, clip it up From 4cc60e6e140ed0a6c4eb8ac0bfda98bfce8b8c52 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 17:57:23 +0100 Subject: [PATCH 020/108] will this work? --- .../SeaIceThermodynamics.jl | 2 +- .../thermodynamic_time_step.jl | 7 ++++--- src/sea_ice_model.jl | 21 ------------------- 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/SeaIceThermodynamics/SeaIceThermodynamics.jl b/src/SeaIceThermodynamics/SeaIceThermodynamics.jl index 264de3f3..35a2b0b2 100644 --- a/src/SeaIceThermodynamics/SeaIceThermodynamics.jl +++ b/src/SeaIceThermodynamics/SeaIceThermodynamics.jl @@ -150,7 +150,7 @@ import Oceananigans.Utils: prettytime # TODO: Fix this after this PR # include("EnthalpyMethodThermodynamics.jl") -include("slab_sea_thermodynamics.jl") +include("slab_sea_ice_thermodynamics.jl") include("slab_heat_and_tracer_fluxes.jl") include("slab_thermodynamics_tendencies.jl") include("thermodynamic_time_step.jl") diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index cf4f1582..5423094a 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -1,3 +1,6 @@ +using Oceananigans.Architectures: architecture +using Oceananigans.Utils +using KernelAbstractions: @kernel, @index thermodynamic_step!(model, ::Nothing, Δt) = nothing @@ -86,11 +89,9 @@ end # Ridging and rafting caused by the thermodynamic step h⁺ = max(zero(h⁺), h⁺) # Thickness cannot be negative, clip it up - V⁺ = h⁺ * ℵ⁺ - ℵ⁺ = ifelse(Vⁿ⁺¹ == 0, zero(ℵ⁺), ℵ⁺) h⁺ = ifelse(Vⁿ⁺¹ == 0, zero(h⁺), h⁺) @inbounds ℵ[i, j, k] = ifelse(ℵ⁺ > 1, one(ℵ⁺), ℵ⁺) - @inbounds h[i, j, k] = ifelse(ℵ⁺ > 1, V⁺, h⁺) + @inbounds h[i, j, k] = ifelse(ℵ⁺ > 1, h⁺ * ℵ⁺, h⁺) end \ No newline at end of file diff --git a/src/sea_ice_model.jl b/src/sea_ice_model.jl index d504845b..42d2c4bb 100644 --- a/src/sea_ice_model.jl +++ b/src/sea_ice_model.jl @@ -133,21 +133,6 @@ end const SIM = SeaIceModel -@kernel function _set_minium_ice_thickness!(h, ℵ, hmin) - i, j = @index(Global, NTuple) - - @inbounds begin - h⁺ = h[i, j, 1] - ℵ⁺ = ℵ[i, j, 1] - h⁻ = hmin[i, j, 1] - - ht, ℵt = conservative_adjustment(h⁺, h⁻, ℵ⁺) - - ℵ[i, j, 1] = ℵt - h[i, j, 1] = ht - end -end - function set!(model::SIM; h=nothing, ℵ=nothing) grid = model.grid arch = architecture(model) @@ -155,12 +140,6 @@ function set!(model::SIM; h=nothing, ℵ=nothing) !isnothing(h) && set!(model.ice_thickness, h) !isnothing(ℵ) && set!(model.ice_concentration, ℵ) - #We cap the ice to the consolidation thickness - launch!(arch, grid, :xy, _set_minium_ice_thickness!, - model.ice_thickness, - model.ice_concentration, - model.ice_consolidation_thickness) - return nothing end From 2cd8cc207d735fe2d530b80d28db2ea4b5ff1cd6 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 17:59:45 +0100 Subject: [PATCH 021/108] some bugfixes --- src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl | 2 +- src/SeaIceMomentumEquations/explicit_momentum_equations.jl | 2 +- .../split_explicit_momentum_equations.jl | 4 ++-- src/SeaIceThermodynamics/thermodynamic_time_step.jl | 4 ++-- src/sea_ice_time_stepping.jl | 2 +- src/tracer_tendency_kernel_functions.jl | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl b/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl index ae5cc4d1..2a149964 100644 --- a/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl +++ b/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl @@ -43,7 +43,7 @@ import Oceananigans: fields ## - ocean dynamic surface # Fallbacks for `nothing` ice dynamics -step_momentum!(model, dynamics, Δt, stage) = nothing +step_momentum!(model, dynamics, Δt) = nothing compute_momentum_tendencies!(model, dynamics, Δt) = nothing include("sea_ice_momentum_equations.jl") diff --git a/src/SeaIceMomentumEquations/explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/explicit_momentum_equations.jl index e35b6132..0d953df5 100644 --- a/src/SeaIceMomentumEquations/explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/explicit_momentum_equations.jl @@ -3,7 +3,7 @@ using Oceananigans.Utils const ExplicitMomentumEquation = SeaIceMomentumEquation{<:ExplicitSolver} # Simple explicit stepping of the momentum equations -function step_momentum!(model, ::ExplicitMomentumEquation, Δt, args...) +function step_momentum!(model, ::ExplicitMomentumEquation, Δt) grid = model.grid arch = architecture(grid) diff --git a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl index 6ba70c54..1ef58f70 100644 --- a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl @@ -21,14 +21,14 @@ SplitExplicitSolver(; substeps=120) = SplitExplicitSolver(substeps) const SplitExplicitMomentumEquation = SeaIceMomentumEquation{<:SplitExplicitSolver} """ - step_momentum!(model, rheology::AbstractExplicitRheology, Δt, χ) + step_momentum!(model, rheology::AbstractExplicitRheology, Δt) function for stepping u and v in the case of _explicit_ solvers. The sea-ice momentum equations are characterized by smaller time-scale than sea-ice thermodynamics and sea-ice tracer advection, therefore explicit rheologies require substepping over a set number of substeps. """ -function step_momentum!(model, dynamics::SplitExplicitMomentumEquation, Δt, args...) +function step_momentum!(model, dynamics::SplitExplicitMomentumEquation, Δt) grid = model.grid arch = architecture(grid) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index 5423094a..20e3fdb7 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -92,6 +92,6 @@ end ℵ⁺ = ifelse(Vⁿ⁺¹ == 0, zero(ℵ⁺), ℵ⁺) h⁺ = ifelse(Vⁿ⁺¹ == 0, zero(h⁺), h⁺) - @inbounds ℵ[i, j, k] = ifelse(ℵ⁺ > 1, one(ℵ⁺), ℵ⁺) - @inbounds h[i, j, k] = ifelse(ℵ⁺ > 1, h⁺ * ℵ⁺, h⁺) + @inbounds ice_concentration[i, j, 1] = ifelse(ℵ⁺ > 1, one(ℵ⁺), ℵ⁺) + @inbounds ice_thickness[i, j, 1] = ifelse(ℵ⁺ > 1, h⁺ * ℵ⁺, h⁺) end \ No newline at end of file diff --git a/src/sea_ice_time_stepping.jl b/src/sea_ice_time_stepping.jl index 3de4a1ef..80e13756 100644 --- a/src/sea_ice_time_stepping.jl +++ b/src/sea_ice_time_stepping.jl @@ -44,7 +44,7 @@ function step_tracers!(model::SIM, Δt) Gⁿ = model.timestepper.Gⁿ - launch!(arch, grid, :xy, _step_tracers!, h, ℵ, hmin, tracers, Gⁿ, Δt) + launch!(arch, grid, :xy, _step_tracers!, h, ℵ, tracers, Gⁿ, Δt) return nothing end diff --git a/src/tracer_tendency_kernel_functions.jl b/src/tracer_tendency_kernel_functions.jl index f68d609a..7be4ecf5 100644 --- a/src/tracer_tendency_kernel_functions.jl +++ b/src/tracer_tendency_kernel_functions.jl @@ -38,9 +38,9 @@ end Gⁿ.h[i, j, 1] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_thickness) Gⁿ.ℵ[i, j, 1] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_concentration) - for (n, θ) in enumerate(tracers) - @inbounds Gⁿ[n] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, θ) - end + # for (n, θ) in enumerate(tracers) + # @inbounds Gⁿ[n] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, θ) + # end end end From 70746a1b5ee55d1e9ed01fb998ccb587137bec79 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 18:37:06 +0100 Subject: [PATCH 022/108] add freezing bucket --- examples/freezing_bucket.jl | 33 +++++++++++-------- .../thermodynamic_time_step.jl | 9 +++-- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/examples/freezing_bucket.jl b/examples/freezing_bucket.jl index 7df35b09..ab55956a 100644 --- a/examples/freezing_bucket.jl +++ b/examples/freezing_bucket.jl @@ -53,15 +53,17 @@ top_heat_boundary_condition = PrescribedTemperature(-10) # slab sea ice representation of thermodynamics thermodynamics = SlabSeaIceThermodynamics(grid; - internal_heat_flux, - phase_transitions, - top_heat_boundary_condition) + internal_heat_flux, + phase_transitions, + top_heat_boundary_condition) -# Then we assemble it all into a model, -model = SeaIceModel(grid; thermodynamics) +# We also prescribe a frazil ice heat flux that stops +# when the ice has reached a concentration of 1. +@inline frazil_ice_formation(i, j, grid, Tuᵢ, clock, fields) = - (1 - fields.ℵ[i, j, 1]) # W m⁻² +bottom_heat_flux = FluxFunction(frazil_ice_formation) -# The ice concentration is set to 1 everywhere, otherwise the ice cannot grow! -fill!(model.ice_concentration, 1) +# Then we assemble it all into a model, +model = SeaIceModel(grid; thermodynamics, bottom_heat_flux) # Note that the default bottom heat boundary condition for `SlabSeaIceThermodynamics` is # `IceWaterThermalEquilibrium` with freshwater. That's what we want! @@ -73,8 +75,6 @@ model.thermodynamics.heat_boundary_conditions.bottom simulation = Simulation(model, Δt=10minute, stop_time=10days) -set!(model, h=0.01) - # # Collecting data and running the simulation # # Before simulating the freezing bucket, we set up a `Callback` to create @@ -86,7 +86,8 @@ timeseries = [] ## Callback function to collect the data from the `sim`ulation function accumulate_timeseries(sim) h = sim.model.ice_thickness - push!(timeseries, (time(sim), first(h))) + ℵ = sim.model.ice_concentration + push!(timeseries, (time(sim), first(h), first(ℵ))) end ## Add the callback to `simulation` @@ -106,20 +107,24 @@ using CairoMakie # to build `Vector`s of time `t` and thickness `h`. It's not much work though: t = [datum[1] for datum in timeseries] h = [datum[2] for datum in timeseries] +ℵ = [datum[3] for datum in timeseries] +V = h .* ℵ # Just for fun, we also compute the velocity of the ice-water interface: -dhdt = @. (h[2:end] - h[1:end-1]) / simulation.Δt +dVdt = @. (h[2:end] .* ℵ[2:end] - h[1:end-1] .* ℵ[1:end-1]) / simulation.Δt # All that's left, really, is to put those `lines!` in an `Axis`: set_theme!(Theme(fontsize=24, linewidth=4)) -fig = Figure(size=(1200, 600)) +fig = Figure(size=(1600, 700)) axh = Axis(fig[1, 1], xlabel="Time (days)", ylabel="Ice thickness (cm)") -axd = Axis(fig[1, 2], xlabel="Ice thickness (cm)", ylabel="Freezing rate (μm s⁻¹)") +axℵ = Axis(fig[1, 2], xlabel="Time (days)", ylabel="Ice concentration (-)") +axV = Axis(fig[1, 3], xlabel="Ice Volume (cm)", ylabel="Freezing rate (μm s⁻¹)") lines!(axh, t ./ day, 1e2 .* h) -lines!(axd, 1e2 .* h[1:end-1], 1e6 .* dhdt) +lines!(axℵ, t ./ day, ℵ) +lines!(axV, 1e2 .* V[1:end-1], 1e6 .* dhdt) current_figure() # hide fig diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index 20e3fdb7..75fa8cef 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -50,6 +50,7 @@ end @inbounds hⁿ = ice_thickness[i, j, 1] @inbounds ℵⁿ = ice_concentration[i, j, 1] + @inbounds hᶜ = ice_consolidation_thickness[i, j, 1] Gⱽ = vertical_growth(i, j, 1, grid, thermodynamics, @@ -78,7 +79,11 @@ end # Simple explicit step, we assume lateral growth # (at the beginning) contributes only to the ice concentration - ℵ⁺ = ℵⁿ + Δt * Gᴸ / hⁿ * (hⁿ > 0) + # This ice grows following the + ℵ⁺ = ℵⁿ + Δt * Gᴸ / max(hⁿ, hᶜ) + + @show Gᴸ, Gⱽ, hⁿ * ℵⁿ, Vⁿ⁺¹, ℵ⁺ + ℵ⁺ = max(zero(ℵ⁺), ℵ⁺) # Concentration cannot be negative, clip it up # The concentration derivative @@ -86,7 +91,7 @@ end # Adjust the thickness accordingly h⁺ = hⁿ + Δt * (∂t_V - hⁿ * ∂t_ℵ) / ℵ⁺ * (ℵ⁺ > 0) - + # Ridging and rafting caused by the thermodynamic step h⁺ = max(zero(h⁺), h⁺) # Thickness cannot be negative, clip it up ℵ⁺ = ifelse(Vⁿ⁺¹ == 0, zero(ℵ⁺), ℵ⁺) From 5807494e8d4ae429af234eec3301bbcb42b6594a Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 18:38:14 +0100 Subject: [PATCH 023/108] remove the show --- src/SeaIceThermodynamics/thermodynamic_time_step.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index 75fa8cef..c0b823c0 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -81,9 +81,6 @@ end # (at the beginning) contributes only to the ice concentration # This ice grows following the ℵ⁺ = ℵⁿ + Δt * Gᴸ / max(hⁿ, hᶜ) - - @show Gᴸ, Gⱽ, hⁿ * ℵⁿ, Vⁿ⁺¹, ℵ⁺ - ℵ⁺ = max(zero(ℵ⁺), ℵ⁺) # Concentration cannot be negative, clip it up # The concentration derivative From 5317586552398373f19822ab6b1f3904a566c338 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 18:41:44 +0100 Subject: [PATCH 024/108] adapt freezing bucket --- examples/freezing_bucket.jl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/examples/freezing_bucket.jl b/examples/freezing_bucket.jl index ab55956a..de75c68a 100644 --- a/examples/freezing_bucket.jl +++ b/examples/freezing_bucket.jl @@ -57,12 +57,15 @@ thermodynamics = SlabSeaIceThermodynamics(grid; phase_transitions, top_heat_boundary_condition) -# We also prescribe a frazil ice heat flux that stops -# when the ice has reached a concentration of 1. +# We also prescribe a frazil ice heat flux that stops when the ice has reached a concentration of 1. +# This heat flux represents the initial ice formation from a liquid bucket. + @inline frazil_ice_formation(i, j, grid, Tuᵢ, clock, fields) = - (1 - fields.ℵ[i, j, 1]) # W m⁻² + bottom_heat_flux = FluxFunction(frazil_ice_formation) -# Then we assemble it all into a model, +# Then we assemble it all into a model. + model = SeaIceModel(grid; thermodynamics, bottom_heat_flux) # Note that the default bottom heat boundary condition for `SlabSeaIceThermodynamics` is @@ -70,8 +73,9 @@ model = SeaIceModel(grid; thermodynamics, bottom_heat_flux) model.thermodynamics.heat_boundary_conditions.bottom -# Ok, we're ready to freeze the bucket for 10 straight days with an initial ice -# thickness of 1 cm, +# Ok, we're ready to freeze the bucket for 10 straight days. +# The ice will start forming suddenly due to the frazil ice heat flux and then eventually +# grow more slowly. simulation = Simulation(model, Δt=10minute, stop_time=10days) @@ -124,7 +128,7 @@ axV = Axis(fig[1, 3], xlabel="Ice Volume (cm)", ylabel="Freezing rate (μm s⁻ lines!(axh, t ./ day, 1e2 .* h) lines!(axℵ, t ./ day, ℵ) -lines!(axV, 1e2 .* V[1:end-1], 1e6 .* dhdt) +lines!(axV, 1e2 .* V[1:end-1], 1e6 .* dVdt) current_figure() # hide fig From a9040d19ea972af29deb3c80596185b37138d6d5 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 19:16:27 +0100 Subject: [PATCH 025/108] comment --- src/SeaIceThermodynamics/thermodynamic_time_step.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index c0b823c0..5c319933 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -79,7 +79,6 @@ end # Simple explicit step, we assume lateral growth # (at the beginning) contributes only to the ice concentration - # This ice grows following the ℵ⁺ = ℵⁿ + Δt * Gᴸ / max(hⁿ, hᶜ) ℵ⁺ = max(zero(ℵ⁺), ℵ⁺) # Concentration cannot be negative, clip it up From 23c15766b39cedfda8c2ebdb24847fd071ab774c Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 4 Mar 2025 19:22:41 +0100 Subject: [PATCH 026/108] add a comment for tomorrow --- src/SeaIceThermodynamics/thermodynamic_time_step.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index 5c319933..10374bfe 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -77,6 +77,12 @@ end # To account for this, we recalculate the actual volume derivative ∂t_V = (Vⁿ⁺¹ - hⁿ * ℵⁿ) / Δt + # Probably need to change this: + # - If the ice is growing this is good + # - If the ice is melting, this is bad because we just remove concentration, + # but we should probably start by removing thickness until we reach the consolidation thickness + # and then remove concentration. + # Simple explicit step, we assume lateral growth # (at the beginning) contributes only to the ice concentration ℵ⁺ = ℵⁿ + Δt * Gᴸ / max(hⁿ, hᶜ) From 4f3b3c96112cc5d8ffefbcaea8d35fb2088e760f Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 5 Mar 2025 12:26:10 +0100 Subject: [PATCH 027/108] this should work --- .../thermodynamic_time_step.jl | 57 +++++++++++-------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index 10374bfe..a980f33f 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -72,33 +72,42 @@ end # Adjust the ice volume to zero Vⁿ⁺¹ = max(zero(Vⁿ⁺¹), Vⁿ⁺¹) - # If Vⁿ⁺¹ == 0 the ice has melted completely, and we set both hⁿ⁺¹ and ℵⁿ⁺¹ to zero - # Otherwise, if the volume is positive, we adjust the thickness and concentration conservatively - # To account for this, we recalculate the actual volume derivative + # We recalculate the actual volume derivative, after accounting for the + # volume adjustment (the ice cannot produce more melt than its actual volume!) ∂t_V = (Vⁿ⁺¹ - hⁿ * ℵⁿ) / Δt - # Probably need to change this: - # - If the ice is growing this is good - # - If the ice is melting, this is bad because we just remove concentration, - # but we should probably start by removing thickness until we reach the consolidation thickness - # and then remove concentration. - - # Simple explicit step, we assume lateral growth - # (at the beginning) contributes only to the ice concentration - ℵ⁺ = ℵⁿ + Δt * Gᴸ / max(hⁿ, hᶜ) - ℵ⁺ = max(zero(ℵ⁺), ℵ⁺) # Concentration cannot be negative, clip it up - - # The concentration derivative - ∂t_ℵ = (ℵ⁺ - ℵⁿ) / Δt - - # Adjust the thickness accordingly - h⁺ = hⁿ + Δt * (∂t_V - hⁿ * ∂t_ℵ) / ℵ⁺ * (ℵ⁺ > 0) - - # Ridging and rafting caused by the thermodynamic step - h⁺ = max(zero(h⁺), h⁺) # Thickness cannot be negative, clip it up - ℵ⁺ = ifelse(Vⁿ⁺¹ == 0, zero(ℵ⁺), ℵ⁺) - h⁺ = ifelse(Vⁿ⁺¹ == 0, zero(h⁺), h⁺) + # There are 6 typical cases depending on the sign of ∂t_V, ℵⁿ and hⁿ + # + # - ∂t_V ≥ 0 : the ice is growing * easy case to handle * + # ├── ℵⁿ == 0 -> new ice is forming (we only increase ℵⁿ, post increase ridging will adjust h) + # └── ℵⁿ > 0 -> ice is growing (we increase both ℵ and h based on ℵⁿ) + # + # - ∂t_V < 0 : the ice is melting * tricky case to handle * + # ├── ℵⁿ == 0 -> not possible! (there is no ice to begin with) + # ├── h > hᶜ -> ice is melting (we decrease both ℵ and h based on ℵⁿ) + # └── h ≤ hᶜ -> unconsolidated ice is melting (we only decrease ℵⁿ) + + freezing = ∂t_V > 0 + consolidated = hⁿ > hᶜ + open_ocean = ℵⁿ == 0 + + # Freezing and melting cases: + hᶠ = hⁿ + Δt * ∂t_V * ℵⁿ * !open_ocean + hᵐ = hⁿ + Δt * ∂t_V * ℵⁿ * consolidated + hᵐ = max(hᵐ, zero(hᵐ)) + + h⁺ = ifelse(freezing, hᶠ, hᵐ) + + # There is also a very particular case when the ice is nucleating corresponding + # h⁺ == 0 and freezing. In this case, we set h = hᶜ and advance ℵ accordingly + h⁺ = ifelse(freezing & (h⁺ == 0), hᶜ, h⁺) + ℵ⁺ = Vⁿ⁺¹ / h⁺ + # No volume change + ℵ⁺ = ifelse(∂t_V == 0, ℵⁿ, ℵ⁺) + h⁺ = ifelse(∂t_V == 0, hⁿ, h⁺) + + # Ridging caused by the thermodynamic step @inbounds ice_concentration[i, j, 1] = ifelse(ℵ⁺ > 1, one(ℵ⁺), ℵ⁺) @inbounds ice_thickness[i, j, 1] = ifelse(ℵ⁺ > 1, h⁺ * ℵ⁺, h⁺) end \ No newline at end of file From e92886444af507e7aaf9e0d434b8c0872d48c697 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 5 Mar 2025 13:32:23 +0100 Subject: [PATCH 028/108] this works! --- examples/melting_in_march.jl | 57 ++++++++++++------- .../thermodynamic_time_step.jl | 3 +- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/examples/melting_in_march.jl b/examples/melting_in_march.jl index ea64c04a..3495b033 100644 --- a/examples/melting_in_march.jl +++ b/examples/melting_in_march.jl @@ -4,15 +4,18 @@ using ClimaSeaIce using GLMakie # Generate a 0D grid for a single column slab model + grid = RectilinearGrid(size=4, x=(0, 1), topology=(Periodic, Flat, Flat)) # Build a model of an ice slab that has internal conductive fluxes # and that emits radiation from its top surface. + solar_insolation = [-600, -800, -1000, -1200] # W m⁻² solar_insolation = reshape(solar_insolation, (4, 1, 1)) outgoing_radiation = RadiativeEmission() # Define a FluxFunction representing a sensible heat flux + parameters = ( transfer_coefficient = 1e-3, # Unitless atmosphere_density = 1.225, # kg m⁻³ @@ -34,26 +37,31 @@ parameters = ( end aerodynamic_flux = FluxFunction(sensible_heat_flux; parameters) - top_heat_flux = (outgoing_radiation, solar_insolation, aerodynamic_flux) -model = SlabSeaIceModel(grid; - ice_consolidation_thickness = 0.01, # m - top_heat_flux) -set!(model, h=1) + +model = SeaIceModel(grid; + ice_consolidation_thickness = 0.05, # m + top_heat_flux) + +# We initialize the model with a 1 m thick slab of ice with 100% ice concentration. + +set!(model, h=1, ℵ=1) simulation = Simulation(model, Δt=10minute, stop_time=30days) # Accumulate data + timeseries = [] function accumulate_timeseries(sim) - T = model.top_surface_temperature + T = model.thermodynamics.top_surface_temperature h = model.ice_thickness + ℵ = model.ice_concentration push!(timeseries, (time(sim), - h[1, 1, 1], T[1, 1, 1], - h[2, 1, 1], T[2, 1, 1], - h[3, 1, 1], T[3, 1, 1], - h[4, 1, 1], T[4, 1, 1])) + h[1, 1, 1], ℵ[1, 1, 1], T[1, 1, 1], + h[2, 1, 1], ℵ[2, 1, 1], T[2, 1, 1], + h[3, 1, 1], ℵ[3, 1, 1], T[3, 1, 1], + h[4, 1, 1], ℵ[4, 1, 1], T[4, 1, 1])) end simulation.callbacks[:save] = Callback(accumulate_timeseries) @@ -62,33 +70,42 @@ run!(simulation) # Extract and visualize data -t = [datum[1] for datum in timeseries] -h1 = [datum[2] for datum in timeseries] -T1 = [datum[3] for datum in timeseries] -h2 = [datum[4] for datum in timeseries] -T2 = [datum[5] for datum in timeseries] -h3 = [datum[6] for datum in timeseries] -T3 = [datum[7] for datum in timeseries] -h4 = [datum[8] for datum in timeseries] -T4 = [datum[9] for datum in timeseries] +t = [datum[1] for datum in timeseries] +h1 = [datum[2] for datum in timeseries] +ℵ1 = [datum[3] for datum in timeseries] +T1 = [datum[4] for datum in timeseries] +h2 = [datum[5] for datum in timeseries] +ℵ2 = [datum[6] for datum in timeseries] +T2 = [datum[7] for datum in timeseries] +h3 = [datum[8] for datum in timeseries] +ℵ3 = [datum[9] for datum in timeseries] +T3 = [datum[10] for datum in timeseries] +h4 = [datum[11] for datum in timeseries] +ℵ4 = [datum[12] for datum in timeseries] +T4 = [datum[13] for datum in timeseries] set_theme!(Theme(fontsize=24, linewidth=4)) fig = Figure(size=(1000, 800)) axT = Axis(fig[1, 1], xlabel="Time (days)", ylabel="Top temperature (ᵒC)") -axh = Axis(fig[2, 1], xlabel="Time (days)", ylabel="Ice thickness (m)") +axℵ = Axis(fig[2, 1], xlabel="Time (days)", ylabel="Ice concentration (-)") +axh = Axis(fig[3, 1], xlabel="Time (days)", ylabel="Ice thickness (m)") lines!(axT, t / day, T1) +lines!(axℵ, t / day, ℵ1) lines!(axh, t / day, h1) lines!(axT, t / day, T2) +lines!(axℵ, t / day, ℵ2) lines!(axh, t / day, h2) lines!(axT, t / day, T3) +lines!(axℵ, t / day, ℵ3) lines!(axh, t / day, h3) lines!(axT, t / day, T4) +lines!(axℵ, t / day, ℵ4) lines!(axh, t / day, h4) display(fig) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index a980f33f..902d6ff3 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -95,7 +95,6 @@ end hᶠ = hⁿ + Δt * ∂t_V * ℵⁿ * !open_ocean hᵐ = hⁿ + Δt * ∂t_V * ℵⁿ * consolidated hᵐ = max(hᵐ, zero(hᵐ)) - h⁺ = ifelse(freezing, hᶠ, hᵐ) # There is also a very particular case when the ice is nucleating corresponding @@ -110,4 +109,6 @@ end # Ridging caused by the thermodynamic step @inbounds ice_concentration[i, j, 1] = ifelse(ℵ⁺ > 1, one(ℵ⁺), ℵ⁺) @inbounds ice_thickness[i, j, 1] = ifelse(ℵ⁺ > 1, h⁺ * ℵ⁺, h⁺) + + @show i, ℵ⁺ end \ No newline at end of file From c9859bab219c151b9e6c88615873a76e3ba6803a Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 5 Mar 2025 13:34:52 +0100 Subject: [PATCH 029/108] remove the show methods --- src/SeaIceThermodynamics/thermodynamic_time_step.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index 902d6ff3..9adbd362 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -109,6 +109,4 @@ end # Ridging caused by the thermodynamic step @inbounds ice_concentration[i, j, 1] = ifelse(ℵ⁺ > 1, one(ℵ⁺), ℵ⁺) @inbounds ice_thickness[i, j, 1] = ifelse(ℵ⁺ > 1, h⁺ * ℵ⁺, h⁺) - - @show i, ℵ⁺ end \ No newline at end of file From af21933972c9701ed542cd2c2ab13027b6a956ad Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 5 Mar 2025 13:43:44 +0100 Subject: [PATCH 030/108] fix examples --- docs/make.jl | 2 ++ examples/freezing_bucket.jl | 8 +++--- ...lting_in_march.jl => melting_in_spring.jl} | 25 ++++++++++++++----- 3 files changed, 26 insertions(+), 9 deletions(-) rename examples/{melting_in_march.jl => melting_in_spring.jl} (78%) diff --git a/docs/make.jl b/docs/make.jl index 72498328..329d96ea 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -14,6 +14,7 @@ const OUTPUT_DIR = joinpath(@__DIR__, "src/literated") example_scripts = [ "freezing_bucket.jl", + "melting_in_spring.jl", "ice_advected_by_anticyclone.jl", "ice_advected_on_coastline.jl", ] @@ -25,6 +26,7 @@ end example_pages = [ "Freezing bucket" => "literated/freezing_bucket.md", + "Melting in Spring" => "literated/melting_in_spring.md", "Ice advected by anticyclone" => "literated/ice_advected_by_anticyclone.md", "Ice advected on coastline" => "literated/ice_advected_on_coastline.md", ] diff --git a/examples/freezing_bucket.jl b/examples/freezing_bucket.jl index de75c68a..3ae00175 100644 --- a/examples/freezing_bucket.jl +++ b/examples/freezing_bucket.jl @@ -3,7 +3,7 @@ # A common laboratory experiment freezes an insultated bucket of water # from the top down, using a metal lid to keep the top of the bucket # at some constant, very cold temperature. In this example, we simulate such -# a scenario using `SlabSeaIceModel`. Here, the bucket is perfectly insulated +# a scenario using the `SeaIceModel`. Here, the bucket is perfectly insulated # and infinitely deep, like many buckets are: if the `Simulation` is run for longer, # the ice will keep freezing, and freezing, and will never run out of water. # Also, the water in the infinite bucket is (somehow) all at the same temperature, @@ -130,8 +130,10 @@ lines!(axh, t ./ day, 1e2 .* h) lines!(axℵ, t ./ day, ℵ) lines!(axV, 1e2 .* V[1:end-1], 1e6 .* dVdt) -current_figure() # hide -fig +save("freezing_bucket.png", fig) +nothing # hide + +# ![](freezing_bucket.png) # If you want more ice, you can increase `simulation.stop_time` and # `run!(simulation)` again (or just re-run the whole script). diff --git a/examples/melting_in_march.jl b/examples/melting_in_spring.jl similarity index 78% rename from examples/melting_in_march.jl rename to examples/melting_in_spring.jl index 3495b033..753a301b 100644 --- a/examples/melting_in_march.jl +++ b/examples/melting_in_spring.jl @@ -1,9 +1,19 @@ +# # Melting in Spring +# +# A simulation that mimicks the melting of (relatively thick) sea ice in the spring +# when the sun is shining. The ice is subject to solar insolation and sensible heat +# fluxes from the atmosphere. Different cells show how the ice melts at different rates +# depending on the amount of solar insolation they receive. +# +# We start by `using Oceananigans` to bring in functions for building grids +# and `Simulation`s and the like. + using Oceananigans using Oceananigans.Units using ClimaSeaIce -using GLMakie +using CairoMakie -# Generate a 0D grid for a single column slab model +# Generate a 1D grid for difference ice columns subject to different solar insolation grid = RectilinearGrid(size=4, x=(0, 1), topology=(Periodic, Flat, Flat)) @@ -14,7 +24,7 @@ solar_insolation = [-600, -800, -1000, -1200] # W m⁻² solar_insolation = reshape(solar_insolation, (4, 1, 1)) outgoing_radiation = RadiativeEmission() -# Define a FluxFunction representing a sensible heat flux +# The sensible heat flux from the atmosphere is represented by a `FluxFunction`. parameters = ( transfer_coefficient = 1e-3, # Unitless @@ -43,13 +53,13 @@ model = SeaIceModel(grid; ice_consolidation_thickness = 0.05, # m top_heat_flux) -# We initialize the model with a 1 m thick slab of ice with 100% ice concentration. +# We initialize all the columns with a 1 m thick slab of ice with 100% ice concentration. set!(model, h=1, ℵ=1) simulation = Simulation(model, Δt=10minute, stop_time=30days) -# Accumulate data +# The data is accumulated in a timeseries for visualization. timeseries = [] @@ -108,5 +118,8 @@ lines!(axT, t / day, T4) lines!(axℵ, t / day, ℵ4) lines!(axh, t / day, h4) -display(fig) +save("melting_in_march.png", fig) +nothing # hide + +# ![](melting_in_march.png) From 0db4a5f7eb9a02e1154f8ba70442061115292d27 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 5 Mar 2025 14:21:35 +0100 Subject: [PATCH 031/108] add freezing in winter --- docs/make.jl | 2 + examples/freezing_in_winter.jl | 170 +++++++++++++++++++++++++++++++++ examples/melting_in_spring.jl | 4 +- 3 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 examples/freezing_in_winter.jl diff --git a/docs/make.jl b/docs/make.jl index 329d96ea..8c06b55c 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -15,6 +15,7 @@ const OUTPUT_DIR = joinpath(@__DIR__, "src/literated") example_scripts = [ "freezing_bucket.jl", "melting_in_spring.jl", + "freezing_in_winter.jl", "ice_advected_by_anticyclone.jl", "ice_advected_on_coastline.jl", ] @@ -27,6 +28,7 @@ end example_pages = [ "Freezing bucket" => "literated/freezing_bucket.md", "Melting in Spring" => "literated/melting_in_spring.md", + "Freezing in Winter" => "literated/freezing_in_winter.md", "Ice advected by anticyclone" => "literated/ice_advected_by_anticyclone.md", "Ice advected on coastline" => "literated/ice_advected_on_coastline.md", ] diff --git a/examples/freezing_in_winter.jl b/examples/freezing_in_winter.jl new file mode 100644 index 00000000..6d50ac41 --- /dev/null +++ b/examples/freezing_in_winter.jl @@ -0,0 +1,170 @@ +# # Freezing in Winter +# +# A simulation that mimicks the melting of (relatively thick) sea ice in the spring +# when the sun is shining. The ice is subject to solar insolation and sensible heat +# fluxes from the atmosphere. Different cells show how the ice melts at different rates +# depending on the amount of solar insolation they receive. +# +# We start by `using Oceananigans` to bring in functions for building grids +# and `Simulation`s and the like. + +using Oceananigans +using Oceananigans.Units +using ClimaSeaIce +using CairoMakie + +# Generate a 1D grid for difference ice columns subject to different solar insolation + +grid = RectilinearGrid(size=4, x=(0, 1), topology=(Periodic, Flat, Flat)) + +# The sensible heat flux from the atmosphere is represented by a `FluxFunction`. + +parameters = ( + transfer_coefficient = 1e-3, # Unitless + atmosphere_density = 1.225, # kg m⁻³ + atmosphere_heat_capacity = 1004, # + atmosphere_temperature = [-20, -10, -1, -0.1], # ᵒC + atmosphere_wind_speed = 5 # m s⁻¹ +) + +@inline function sensible_heat_flux(i, j, grid, Tᵤ, clock, fields, parameters) + Cₛ = parameters.transfer_coefficient + ρₐ = parameters.atmosphere_density + cₐ = parameters.atmosphere_heat_capacity + Tₐ = parameters.atmosphere_temperature[i] + uₐ = parameters.atmosphere_wind_speed + + # Flux is positive (cooling by fluxing heat up away from upper surface) + # when Tₐ < Tᵤ: + return Cₛ * ρₐ * cₐ * uₐ * (Tᵤ - Tₐ) +end + +# We also evolve a bucket freshwater lake that cools down and freezes the ice from below. + +lake = ( + lake_density = 1000, # kg m⁻³ + lake_heat_capacity = 4000, # + lake_temperature = [1.0, 1.0, 1.0, 1.0], # ᵒC + lake_depth = 10, # m + atmosphere = parameters +) + +@inline function advance_ocean_and_bottom_heat_flux(i, j, grid, Tuᵢ, clock, fields, parameters) + # First we calculate the heat flux between the atmosphere and the ocean + atmos = parameters.atmosphere + + Cₛ = atmos.transfer_coefficient + ρₐ = atmos.atmosphere_density + cₐ = atmos.atmosphere_heat_capacity + Tₐ = atmos.atmosphere_temperature[i] + uₐ = atmos.atmosphere_wind_speed + Tₒ = parameters.lake_temperature + cₒ = parameters.lake_heat_capacity + ρₒ = parameters.lake_density + Δ = parameters.lake_depth + ℵ = fields.ℵ[i, j, 1] + + Qₐ = Cₛ * ρₐ * cₐ * uₐ * (Tₐ - Tₒ[i]) * (1 - ℵ) + + # Cool down the ocean + Tₒ[i] = Tₒ[i] + Qₐ / (ρₒ * cₒ) * 10minute + + # If the ocean temperature is low enough, we freeze the ice from below + # and add the heat flux to the bottom of the ice + Qᵢ = ρₒ * cₒ * (Tₒ[i] - 0) / 10minute * Δ # W m⁻² + Qᵢ = min(Qᵢ, zero(Qᵢ)) # We only freeze, not melt + + Tₒ[i] = ifelse(Qᵢ == 0, Tₒ[i], zero(Qᵢ)) + + return Qᵢ +end + +aerodynamic_flux = FluxFunction(sensible_heat_flux; parameters) +top_heat_flux = (aerodynamic_flux) +bottom_heat_flux = FluxFunction(advance_ocean_and_bottom_heat_flux; parameters=lake) + +model = SeaIceModel(grid; + ice_consolidation_thickness = 0.05, # m + top_heat_flux, + bottom_heat_flux) + +# We initialize all the columns with open ocean (0 thickness and 0 concentration) +set!(model, h=0, ℵ=0) + +simulation = Simulation(model, Δt=10minute, stop_time=30days) + +# The data is accumulated in a timeseries for visualization. + +timeseries = [] + +function accumulate_timeseries(sim) + T = model.thermodynamics.top_surface_temperature + h = model.ice_thickness + ℵ = model.ice_concentration + To = lake.lake_temperature + push!(timeseries, (time(sim), + h[1, 1, 1], ℵ[1, 1, 1], T[1, 1, 1], + h[2, 1, 1], ℵ[2, 1, 1], T[2, 1, 1], + h[3, 1, 1], ℵ[3, 1, 1], T[3, 1, 1], + h[4, 1, 1], ℵ[4, 1, 1], T[4, 1, 1], + To[1], To[2], To[3], To[4])) +end + +simulation.callbacks[:save] = Callback(accumulate_timeseries) + +run!(simulation) + +# Extract and visualize data + +t = [datum[1] for datum in timeseries] +h1 = [datum[2] for datum in timeseries] +ℵ1 = [datum[3] for datum in timeseries] +T1 = [datum[4] for datum in timeseries] +h2 = [datum[5] for datum in timeseries] +ℵ2 = [datum[6] for datum in timeseries] +T2 = [datum[7] for datum in timeseries] +h3 = [datum[8] for datum in timeseries] +ℵ3 = [datum[9] for datum in timeseries] +T3 = [datum[10] for datum in timeseries] +h4 = [datum[11] for datum in timeseries] +ℵ4 = [datum[12] for datum in timeseries] +T4 = [datum[13] for datum in timeseries] +L1 = [datum[14] for datum in timeseries] +L2 = [datum[15] for datum in timeseries] +L3 = [datum[16] for datum in timeseries] +L4 = [datum[17] for datum in timeseries] + +set_theme!(Theme(fontsize=24, linewidth=4)) + +fig = Figure(size=(1000, 900)) + +axT = Axis(fig[1, 1], xlabel="Time (days)", ylabel="Top temperature (ᵒC)") +axℵ = Axis(fig[2, 1], xlabel="Time (days)", ylabel="Ice concentration (-)") +axh = Axis(fig[3, 1], xlabel="Time (days)", ylabel="Ice thickness (m)") +axL = Axis(fig[4, 1], xlabel="Time (days)", ylabel="Lake temperature (ᵒC)") + +lines!(axT, t / day, T1) +lines!(axℵ, t / day, ℵ1) +lines!(axh, t / day, h1) +lines!(axL, t / day, L1) + +lines!(axT, t / day, T2) +lines!(axℵ, t / day, ℵ2) +lines!(axh, t / day, h2) +lines!(axL, t / day, L2) + +lines!(axT, t / day, T3) +lines!(axℵ, t / day, ℵ3) +lines!(axh, t / day, h3) +lines!(axL, t / day, L3) + +lines!(axT, t / day, T4) +lines!(axℵ, t / day, ℵ4) +lines!(axh, t / day, h4) +lines!(axL, t / day, L4) + +save("freezing_in_winter.png", fig) +nothing # hide + +# ![](freezing_in_winter.png) + diff --git a/examples/melting_in_spring.jl b/examples/melting_in_spring.jl index 753a301b..bbceead6 100644 --- a/examples/melting_in_spring.jl +++ b/examples/melting_in_spring.jl @@ -118,8 +118,8 @@ lines!(axT, t / day, T4) lines!(axℵ, t / day, ℵ4) lines!(axh, t / day, h4) -save("melting_in_march.png", fig) +save("melting_in_spring.png", fig) nothing # hide -# ![](melting_in_march.png) +# ![](melting_in_spring.png) From f4ab9e70579ce033badc4875a27a4f0af715bbde Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 5 Mar 2025 14:28:16 +0100 Subject: [PATCH 032/108] new example --- docs/make.jl | 2 +- ...ing_in_winter.jl => freezing_of_a_lake.jl} | 30 +++++++++++-------- 2 files changed, 19 insertions(+), 13 deletions(-) rename examples/{freezing_in_winter.jl => freezing_of_a_lake.jl} (82%) diff --git a/docs/make.jl b/docs/make.jl index 8c06b55c..6f7a0894 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -28,7 +28,7 @@ end example_pages = [ "Freezing bucket" => "literated/freezing_bucket.md", "Melting in Spring" => "literated/melting_in_spring.md", - "Freezing in Winter" => "literated/freezing_in_winter.md", + "Freezing of a Lake" => "literated/freezing_of_a_lake.md", "Ice advected by anticyclone" => "literated/ice_advected_by_anticyclone.md", "Ice advected on coastline" => "literated/ice_advected_on_coastline.md", ] diff --git a/examples/freezing_in_winter.jl b/examples/freezing_of_a_lake.jl similarity index 82% rename from examples/freezing_in_winter.jl rename to examples/freezing_of_a_lake.jl index 6d50ac41..7b90fe57 100644 --- a/examples/freezing_in_winter.jl +++ b/examples/freezing_of_a_lake.jl @@ -1,9 +1,9 @@ -# # Freezing in Winter +# # Freezing of a lake # -# A simulation that mimicks the melting of (relatively thick) sea ice in the spring -# when the sun is shining. The ice is subject to solar insolation and sensible heat -# fluxes from the atmosphere. Different cells show how the ice melts at different rates -# depending on the amount of solar insolation they receive. +# In this example we simulate the freezing of a lake in winter. The lake is +# represented by four points that start at 1ᵒC and are cooled down by an atmosphere with +# temperatures of -20, -10, -1, and -0.1ᵒC. The lake is 10 m deep and not subjected to +# radiative transfer (this lake is covered in a wind tunnel under which we blow some cold air). # # We start by `using Oceananigans` to bring in functions for building grids # and `Simulation`s and the like. @@ -39,7 +39,10 @@ parameters = ( return Cₛ * ρₐ * cₐ * uₐ * (Tᵤ - Tₐ) end -# We also evolve a bucket freshwater lake that cools down and freezes the ice from below. +# We also evolve a bucket freshwater lake that cools down and freezes from below +# generating fresh sea-ice (or lake-ice in this case?). +# We set the Δt of the lake to 10 minutes. This time step will be used to also for the sea-ice +# model. lake = ( lake_density = 1000, # kg m⁻³ @@ -47,9 +50,10 @@ lake = ( lake_temperature = [1.0, 1.0, 1.0, 1.0], # ᵒC lake_depth = 10, # m atmosphere = parameters + Δt = 10minutes ) -@inline function advance_ocean_and_bottom_heat_flux(i, j, grid, Tuᵢ, clock, fields, parameters) +@inline function advance_lake_and_frazil_flux(i, j, grid, Tuᵢ, clock, fields, parameters) # First we calculate the heat flux between the atmosphere and the ocean atmos = parameters.atmosphere @@ -63,15 +67,16 @@ lake = ( ρₒ = parameters.lake_density Δ = parameters.lake_depth ℵ = fields.ℵ[i, j, 1] + Δt = parameters.Δt Qₐ = Cₛ * ρₐ * cₐ * uₐ * (Tₐ - Tₒ[i]) * (1 - ℵ) # Cool down the ocean - Tₒ[i] = Tₒ[i] + Qₐ / (ρₒ * cₒ) * 10minute + Tₒ[i] = Tₒ[i] + Qₐ / (ρₒ * cₒ) * Δt # If the ocean temperature is low enough, we freeze the ice from below # and add the heat flux to the bottom of the ice - Qᵢ = ρₒ * cₒ * (Tₒ[i] - 0) / 10minute * Δ # W m⁻² + Qᵢ = ρₒ * cₒ * (Tₒ[i] - 0) / Δt * Δ # W m⁻² Qᵢ = min(Qᵢ, zero(Qᵢ)) # We only freeze, not melt Tₒ[i] = ifelse(Qᵢ == 0, Tₒ[i], zero(Qᵢ)) @@ -81,17 +86,18 @@ end aerodynamic_flux = FluxFunction(sensible_heat_flux; parameters) top_heat_flux = (aerodynamic_flux) -bottom_heat_flux = FluxFunction(advance_ocean_and_bottom_heat_flux; parameters=lake) +bottom_heat_flux = FluxFunction(advance_lake_and_frazil_flux; parameters=lake) model = SeaIceModel(grid; ice_consolidation_thickness = 0.05, # m top_heat_flux, bottom_heat_flux) -# We initialize all the columns with open ocean (0 thickness and 0 concentration) +# We initialize all the columns with open water (0 thickness and 0 concentration) + set!(model, h=0, ℵ=0) -simulation = Simulation(model, Δt=10minute, stop_time=30days) +simulation = Simulation(model, Δt=lake.Δt, stop_time=30days) # The data is accumulated in a timeseries for visualization. From 632bd81da43424e3f9ac0cc9684cc467ce59e021 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 5 Mar 2025 14:30:42 +0100 Subject: [PATCH 033/108] better --- examples/freezing_of_a_lake.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/freezing_of_a_lake.jl b/examples/freezing_of_a_lake.jl index 7b90fe57..2e69dc30 100644 --- a/examples/freezing_of_a_lake.jl +++ b/examples/freezing_of_a_lake.jl @@ -23,7 +23,7 @@ parameters = ( transfer_coefficient = 1e-3, # Unitless atmosphere_density = 1.225, # kg m⁻³ atmosphere_heat_capacity = 1004, # - atmosphere_temperature = [-20, -10, -1, -0.1], # ᵒC + atmosphere_temperature = [-20, -10, -5, 0], # ᵒC atmosphere_wind_speed = 5 # m s⁻¹ ) @@ -49,7 +49,7 @@ lake = ( lake_heat_capacity = 4000, # lake_temperature = [1.0, 1.0, 1.0, 1.0], # ᵒC lake_depth = 10, # m - atmosphere = parameters + atmosphere = parameters, Δt = 10minutes ) @@ -140,7 +140,7 @@ L2 = [datum[15] for datum in timeseries] L3 = [datum[16] for datum in timeseries] L4 = [datum[17] for datum in timeseries] -set_theme!(Theme(fontsize=24, linewidth=4)) +set_theme!(Theme(fontsize=18, linewidth=3)) fig = Figure(size=(1000, 900)) From e70f38a5c4455ce286ce3a595cbf90d09c2ed466 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 5 Mar 2025 14:32:52 +0100 Subject: [PATCH 034/108] only 10 days --- examples/freezing_of_a_lake.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/freezing_of_a_lake.jl b/examples/freezing_of_a_lake.jl index 2e69dc30..6655a870 100644 --- a/examples/freezing_of_a_lake.jl +++ b/examples/freezing_of_a_lake.jl @@ -97,7 +97,7 @@ model = SeaIceModel(grid; set!(model, h=0, ℵ=0) -simulation = Simulation(model, Δt=lake.Δt, stop_time=30days) +simulation = Simulation(model, Δt=lake.Δt, stop_time=10days) # The data is accumulated in a timeseries for visualization. From d6113baf0b61f613a42980b7b0bfee128a178a4b Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 5 Mar 2025 14:43:01 +0100 Subject: [PATCH 035/108] make --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index 6f7a0894..491f5f1b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -15,7 +15,7 @@ const OUTPUT_DIR = joinpath(@__DIR__, "src/literated") example_scripts = [ "freezing_bucket.jl", "melting_in_spring.jl", - "freezing_in_winter.jl", + "freezing_of_a_lake.jl", "ice_advected_by_anticyclone.jl", "ice_advected_on_coastline.jl", ] From d633392873ca17273a342ac5671488418674ca3f Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 5 Mar 2025 14:58:39 +0100 Subject: [PATCH 036/108] keep a "thermodynamic_tendency" --- .../SeaIceThermodynamics.jl | 3 +- .../slab_thermodynamics_tendencies.jl | 51 +++++++------------ .../thermodynamic_time_step.jl | 20 +++----- 3 files changed, 26 insertions(+), 48 deletions(-) diff --git a/src/SeaIceThermodynamics/SeaIceThermodynamics.jl b/src/SeaIceThermodynamics/SeaIceThermodynamics.jl index 35a2b0b2..424bf979 100644 --- a/src/SeaIceThermodynamics/SeaIceThermodynamics.jl +++ b/src/SeaIceThermodynamics/SeaIceThermodynamics.jl @@ -121,8 +121,7 @@ end end # Fallback for no thermodynamics -@inline vertical_growth(i, j, k, grid, ::Nothing, args...) = zero(grid) -@inline lateral_growth(i, j, k, grid, ::Nothing, args...) = zero(grid) +@inline thermodynamic_tendency(i, j, k, grid, ::Nothing, args...) = zero(grid) include("HeatBoundaryConditions/HeatBoundaryConditions.jl") diff --git a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl index d20a5f02..143e2114 100644 --- a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl +++ b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl @@ -1,38 +1,14 @@ using ClimaSeaIce.SeaIceThermodynamics.HeatBoundaryConditions: bottom_temperature, top_surface_temperature # Frazil ice formation -@inline function lateral_growth(i, j, k, grid, - thermodynamics::SlabSeaIceThermodynamics, - bottom_external_heat_flux, - clock, model_fields) - - phase_transitions = thermodynamics.phase_transitions - bottom_heat_bc = thermodynamics.heat_boundary_conditions.bottom - liquidus = phase_transitions.liquidus - Tu = thermodynamics.top_surface_temperature - @inbounds Tuᵢ = Tu[i, j, k] - - Qb = bottom_external_heat_flux - - Tbᵢ = bottom_temperature(i, j, grid, bottom_heat_bc, liquidus) - ℰb = latent_heat(phase_transitions, Tbᵢ) - - # Retrieve bottom flux - Qbᵢ = getflux(Qb, i, j, grid, Tuᵢ, clock, model_fields) - - # If ice is consolidated, compute tendency for an ice slab; otherwise - # just add ocean fluxes from frazil ice formation or melting - return - Qbᵢ / ℰb -end - -@inline function vertical_growth(i, j, k, grid, - thermodynamics::SlabSeaIceThermodynamics, - ice_thickness, - ice_concentration, - ice_consolidation_thickness, - top_external_heat_flux, - bottom_external_heat_flux, - clock, model_fields) +@inline function thermodynamic_tendency(i, j, k, grid, + thermodynamics::SlabSeaIceThermodynamics, + ice_thickness, + ice_concentration, + ice_consolidation_thickness, + top_external_heat_flux, + bottom_external_heat_flux, + clock, model_fields) phase_transitions = thermodynamics.phase_transitions @@ -42,6 +18,7 @@ end Qi = thermodynamics.internal_heat_flux Qu = top_external_heat_flux + Qb = bottom_external_heat_flux Tu = thermodynamics.top_surface_temperature @inbounds begin @@ -50,6 +27,8 @@ end ℵᵢ = ice_concentration[i, j, k] end + @inbounds Tuᵢ = Tu[i, j, k] + consolidated_ice = hᵢ > hc # Determine top surface temperature. @@ -75,12 +54,16 @@ end # Retrieve fluxes Quᵢ = getflux(Qu, i, j, grid, Tuᵢ, clock, model_fields) Qiᵢ = getflux(Qi, i, j, grid, Tuᵢ, clock, model_fields) + Qbᵢ = getflux(Qb, i, j, grid, Tuᵢ, clock, model_fields) # Upper (top) and bottom interface velocities wu = (Quᵢ - Qiᵢ) / ℰu * ℵᵢ # < 0 => melting wb = + Qiᵢ / ℰb * ℵᵢ # < 0 => freezing - slabby_Gh = wu + wb + # Ice forming at the bottom. + # it applies to the whole area, so it need + # not be multiplied by the concentration + wf = - Qbᵢ / ℰb - return slabby_Gh + return wu + wb + wf end \ No newline at end of file diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index 9adbd362..e36e25ce 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -52,19 +52,15 @@ end @inbounds ℵⁿ = ice_concentration[i, j, 1] @inbounds hᶜ = ice_consolidation_thickness[i, j, 1] - Gⱽ = vertical_growth(i, j, 1, grid, - thermodynamics, - ice_thickness, - ice_concentration, - ice_consolidation_thickness, - top_external_heat_flux, - bottom_external_heat_flux, - clock, model_fields) - - Gᴸ = lateral_growth(i, j, 1, grid, thermodynamics, bottom_external_heat_flux, clock, model_fields) - # Total volume tendency - ∂t_V = Gᴸ + Gⱽ + ∂t_V = thermodynamic_tendency(i, j, 1, grid, + thermodynamics, + ice_thickness, + ice_concentration, + ice_consolidation_thickness, + top_external_heat_flux, + bottom_external_heat_flux, + clock, model_fields) # ice volume at timestep n+1 Vⁿ⁺¹ = hⁿ * ℵⁿ + Δt * ∂t_V From 522656f07c24f71993f6d3f9c2d5a5a21e08f496 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 5 Mar 2025 16:49:35 +0100 Subject: [PATCH 037/108] maybe a bit smaller font --- examples/melting_in_spring.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/melting_in_spring.jl b/examples/melting_in_spring.jl index bbceead6..f0b77683 100644 --- a/examples/melting_in_spring.jl +++ b/examples/melting_in_spring.jl @@ -94,7 +94,7 @@ h4 = [datum[11] for datum in timeseries] ℵ4 = [datum[12] for datum in timeseries] T4 = [datum[13] for datum in timeseries] -set_theme!(Theme(fontsize=24, linewidth=4)) +set_theme!(Theme(fontsize=18, linewidth=3)) fig = Figure(size=(1000, 800)) From 81c672b3d8da3684e63db127e5dbb4d9b04439d7 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 5 Mar 2025 17:20:08 +0100 Subject: [PATCH 038/108] fix examples --- examples/freezing_of_a_lake.jl | 22 ++++++++++++---------- examples/melting_in_spring.jl | 4 ++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/examples/freezing_of_a_lake.jl b/examples/freezing_of_a_lake.jl index 6655a870..bcba9ca6 100644 --- a/examples/freezing_of_a_lake.jl +++ b/examples/freezing_of_a_lake.jl @@ -53,29 +53,31 @@ lake = ( Δt = 10minutes ) +# We build a flux function that serves three purposes: +# 1. computing the cooling of the lake caused by the atmosphere +# 2. If the lake temperature is low enough, freezing the lake from above +# 3. and adding the heat flux to the bottom of the ice + @inline function advance_lake_and_frazil_flux(i, j, grid, Tuᵢ, clock, fields, parameters) - # First we calculate the heat flux between the atmosphere and the ocean atmos = parameters.atmosphere + lake = parameters - Cₛ = atmos.transfer_coefficient + Cₛ = atmos.transfer_coefficient ρₐ = atmos.atmosphere_density cₐ = atmos.atmosphere_heat_capacity Tₐ = atmos.atmosphere_temperature[i] uₐ = atmos.atmosphere_wind_speed - Tₒ = parameters.lake_temperature - cₒ = parameters.lake_heat_capacity - ρₒ = parameters.lake_density - Δ = parameters.lake_depth + Tₒ = lake.lake_temperature + cₒ = lake.lake_heat_capacity + ρₒ = lake.lake_density + Δ = lake.lake_depth ℵ = fields.ℵ[i, j, 1] - Δt = parameters.Δt + Δt = lake.Δt Qₐ = Cₛ * ρₐ * cₐ * uₐ * (Tₐ - Tₒ[i]) * (1 - ℵ) - # Cool down the ocean Tₒ[i] = Tₒ[i] + Qₐ / (ρₒ * cₒ) * Δt - # If the ocean temperature is low enough, we freeze the ice from below - # and add the heat flux to the bottom of the ice Qᵢ = ρₒ * cₒ * (Tₒ[i] - 0) / Δt * Δ # W m⁻² Qᵢ = min(Qᵢ, zero(Qᵢ)) # We only freeze, not melt diff --git a/examples/melting_in_spring.jl b/examples/melting_in_spring.jl index f0b77683..e87b7e36 100644 --- a/examples/melting_in_spring.jl +++ b/examples/melting_in_spring.jl @@ -34,6 +34,8 @@ parameters = ( atmosphere_wind_speed = 5 # m s⁻¹ ) +# Flux is positive (cooling by fluxing heat up away from upper surface) +# when Tₐ < Tᵤ: @inline function sensible_heat_flux(i, j, grid, Tᵤ, clock, fields, parameters) Cₛ = parameters.transfer_coefficient ρₐ = parameters.atmosphere_density @@ -41,8 +43,6 @@ parameters = ( Tₐ = parameters.atmosphere_temperature uₐ = parameters.atmosphere_wind_speed - # Flux is positive (cooling by fluxing heat up away from upper surface) - # when Tₐ < Tᵤ: return Cₛ * ρₐ * cₐ * uₐ * (Tᵤ - Tₐ) end From 331f198506b284e09553b6c5b86dab0fb0b229ea Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 5 Mar 2025 17:32:29 +0100 Subject: [PATCH 039/108] correct examples --- examples/freezing_of_a_lake.jl | 7 ++++--- examples/melting_in_spring.jl | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/freezing_of_a_lake.jl b/examples/freezing_of_a_lake.jl index bcba9ca6..f0d64b53 100644 --- a/examples/freezing_of_a_lake.jl +++ b/examples/freezing_of_a_lake.jl @@ -26,6 +26,9 @@ parameters = ( atmosphere_temperature = [-20, -10, -5, 0], # ᵒC atmosphere_wind_speed = 5 # m s⁻¹ ) + +# Flux is positive (cooling by fluxing heat up away from upper surface) +# when Tₐ < Tᵤ: @inline function sensible_heat_flux(i, j, grid, Tᵤ, clock, fields, parameters) Cₛ = parameters.transfer_coefficient @@ -33,9 +36,7 @@ parameters = ( cₐ = parameters.atmosphere_heat_capacity Tₐ = parameters.atmosphere_temperature[i] uₐ = parameters.atmosphere_wind_speed - - # Flux is positive (cooling by fluxing heat up away from upper surface) - # when Tₐ < Tᵤ: + return Cₛ * ρₐ * cₐ * uₐ * (Tᵤ - Tₐ) end diff --git a/examples/melting_in_spring.jl b/examples/melting_in_spring.jl index e87b7e36..2908b666 100644 --- a/examples/melting_in_spring.jl +++ b/examples/melting_in_spring.jl @@ -36,6 +36,7 @@ parameters = ( # Flux is positive (cooling by fluxing heat up away from upper surface) # when Tₐ < Tᵤ: + @inline function sensible_heat_flux(i, j, grid, Tᵤ, clock, fields, parameters) Cₛ = parameters.transfer_coefficient ρₐ = parameters.atmosphere_density From 28698152742bf65ba6f18b17ec2016ab88448f50 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 6 Mar 2025 11:39:16 +0100 Subject: [PATCH 040/108] add this --- examples/ice_advected_by_anticyclone.jl | 13 ++- src/Rheologies/Rheologies.jl | 9 +- .../brittle_bingham_maxwell_rheology.jl | 97 ++++++------------- .../elasto_visco_plastic_rheology.jl | 2 +- .../SeaIceMomentumEquations.jl | 3 +- .../sea_ice_momentum_equations.jl | 4 +- src/sea_ice_model.jl | 7 +- src/sea_ice_time_stepping.jl | 11 ++- src/tracer_tendency_kernel_functions.jl | 11 ++- 9 files changed, 70 insertions(+), 87 deletions(-) diff --git a/examples/ice_advected_by_anticyclone.jl b/examples/ice_advected_by_anticyclone.jl index b1f74490..448c6be6 100644 --- a/examples/ice_advected_by_anticyclone.jl +++ b/examples/ice_advected_by_anticyclone.jl @@ -35,11 +35,15 @@ grid = RectilinearGrid(arch; ##### Value boundary conditions for velocities ##### -u_bcs = FieldBoundaryConditions(north=ValueBoundaryCondition(0), - south=ValueBoundaryCondition(0)) +u_bcs = FieldBoundaryConditions(north=OpenBoundaryCondition(0), + south=OpenBoundaryCondition(0), + west=OpenBoundaryCondition(0), + east=OpenBoundaryCondition(0)) -v_bcs = FieldBoundaryConditions(west=ValueBoundaryCondition(0), - east=ValueBoundaryCondition(0)) +v_bcs = FieldBoundaryConditions(north=OpenBoundaryCondition(0), + south=OpenBoundaryCondition(0), + west=OpenBoundaryCondition(0), + east=OpenBoundaryCondition(0)) ##### ##### Ocean sea-ice stress @@ -99,7 +103,6 @@ model = SeaIceModel(grid; dynamics = momentum_equations, thermodynamics = nothing, # No thermodynamics here advection = WENO(order=7), - tracers = :d, boundary_conditions = (u=u_bcs, v=v_bcs)) # We start with a concentration of ℵ = 1 and an diff --git a/src/Rheologies/Rheologies.jl b/src/Rheologies/Rheologies.jl index 31305ca2..f30a10cf 100644 --- a/src/Rheologies/Rheologies.jl +++ b/src/Rheologies/Rheologies.jl @@ -1,14 +1,13 @@ module Rheologies export ViscousRheology, ElastoViscoPlasticRheology, BrittleBinghamMaxwellRheology -export ∂ⱼ_σ₁ⱼ, ∂ⱼ_σ₂ⱼ, required_auxiliary_fields +export ∂ⱼ_σ₁ⱼ, ∂ⱼ_σ₂ⱼ, rheology_auxiliary_fields using Oceananigans using Oceananigans.Operators using ClimaSeaIce: ice_mass # Nothing rheology -required_auxiliary_fields(rheology, grid) = NamedTuple() initialize_rheology!(model, rheology) = nothing compute_stresses!(model, dynamics, rheology, Δt) = nothing @@ -19,6 +18,12 @@ compute_stresses!(model, dynamics, rheology, Δt) = nothing @inline sum_of_forcing_u(i, j, k, grid, rheology, u_forcing, fields, Δt) = u_forcing(i, j, k, grid, fields) @inline sum_of_forcing_v(i, j, k, grid, rheology, v_forcing, fields, Δt) = v_forcing(i, j, k, grid, fields) +# No needed extra tracers +rheology_prognostic_tracers(rheology) = () + +# No needed auxiliary fields +rheology_auxiliary_fields(rheology, grid) = NamedTuple() + include("ice_stress_divergence.jl") include("viscous_rheology.jl") include("elasto_visco_plastic_rheology.jl") diff --git a/src/Rheologies/brittle_bingham_maxwell_rheology.jl b/src/Rheologies/brittle_bingham_maxwell_rheology.jl index 03567a31..72dd32c6 100644 --- a/src/Rheologies/brittle_bingham_maxwell_rheology.jl +++ b/src/Rheologies/brittle_bingham_maxwell_rheology.jl @@ -42,26 +42,16 @@ function BrittleBinghamMaxwellRheology(FT::DataType = Float64; nothing) end -required_prognostic_tracers(::BrittleBinghamMaxwellRheology, grid) = - (; d = Field{Center, Center, Nothing}(grid)) # damage tracer +rheology_prognostic_tracers(::BrittleBinghamMaxwellRheology) = (:d, :σ₁₁, :σ₁₂, :σ₂₂) -function required_auxiliary_fields(::BrittleBinghamMaxwellRheology, grid) +function rheology_auxiliary_fields(::BrittleBinghamMaxwellRheology, grid) # TODO: What about boundary conditions? P = Field{Center, Center, Nothing}(grid) E = Field{Center, Center, Nothing}(grid) λ = Field{Center, Center, Nothing}(grid) - σ₁₁ = Field{Center, Center, Nothing}(grid) - σ₂₂ = Field{Center, Center, Nothing}(grid) - σ₁₂ = Field{Center, Center, Nothing}(grid) - σ₁₁ₙ = Field{Center, Center, Nothing}(grid) - σ₂₂ₙ = Field{Center, Center, Nothing}(grid) - σ₁₂ₙ = Field{Center, Center, Nothing}(grid) - uₙ = Field{Face, Face, Center}(grid) - vₙ = Field{Face, Face, Center}(grid) - dₙ = Field{Center, Center, Nothing}(grid) - return (; σ₁₁, σ₂₂, σ₁₂, σ₁₁ₙ, σ₂₂ₙ, σ₁₂ₙ, dₙ, P, E, λ, uₙ, vₙ) + return (; P, E, λ) end # Extend the `adapt_structure` function for the ElastoViscoPlasticRheology @@ -99,7 +89,7 @@ function initialize_rheology!(model, rheology::BrittleBinghamMaxwellRheology) # compute on the whole grid including halos parameters = KernelParameters(size(fields.P.data)[1:2], fields.P.data.offsets[1:2]) - launch!(architecture(model.grid), model.grid, parameters, _initialize_bbm_rheology!, fields, model.grid, P★, E★, λ★, h★, α, C, h, ℵ) + launch!(architecture(model.grid), model.grid, parameters, _initialize_bbm_rheology!, fields, d, model.grid, P★, E★, λ★, h★, α, C, h, ℵ) return nothing end @@ -113,8 +103,8 @@ end fields.E[i, j, 1] = E★ * ecc fields.λ[i, j, 1] = λ★ * ecc^(α - 1) # Clamp the damage between 0 and a value close to 1 - dᵢ = d[i, j, k] - d[i, j, k] = clamp(dᵢ, zero(dᵢ), 99999 * one(dᵢ) / 100000) + dᵢ = d[i, j, 1] + d[i, j, 1] = clamp(dᵢ, zero(dᵢ), 99999 * one(dᵢ) / 100000) end end @@ -142,14 +132,8 @@ function compute_stresses!(model, dynamics, rheology::BrittleBinghamMaxwellRheol return nothing end -@inline σᴵ(i, j, k, grid, fields) = @inbounds (fields.σ₁₁[i, j, k] + fields.σ₂₂[i, j, k]) / 2 -@inline σᴵᴵ(i, j, k, grid, fields) = @inbounds sqrt((fields.σ₁₁[i, j, k] - fields.σ₂₂[i, j, k])^2 / 4 + fields.σ₁₂[i, j, k]^2) - -@inline function dcritical(i, j, k, grid, N, c, μ, fields) - σI = σᴵ(i, j, k, grid, fields) - σII = σᴵᴵ(i, j, k, grid, fields) - return one(grid) - ifelse(σI > - N, c / (σII + μ * σI), - N / σI) -end +@inline σᴵ(i, j, k, grid, tracers) = @inbounds (tracers.σ₁₁[i, j, k] + tracers.σ₂₂[i, j, k]) / 2 +@inline σᴵᴵ(i, j, k, grid, tracers) = @inbounds sqrt((tracers.σ₁₁[i, j, k] - tracers.σ₂₂[i, j, k])^2 / 4 + tracers.σ₁₂[i, j, k]^2) @inline function reconstruction_2d(i, j, k, grid, f, args...) fij = f(i, j, k, grid, args...) @@ -186,10 +170,16 @@ end return (fij + fmj + fpj + fim + fip + fmm + fmp + fpm + fpp) end -@inline function dcrit2(i, j, k, grid, N, c, μ, fields) +@inline function dcritical(i, j, k, grid, N, c, μ, tracers) + σI = σᴵ(i, j, k, grid, tracers) + σII = σᴵᴵ(i, j, k, grid, tracers) + return one(grid) - ifelse(σI > - N, c / (σII + μ * σI), - N / σI) +end + +@inline function dcrit2(i, j, k, grid, N, c, μ, tracers) - σI = σᴵ(i, j, k, grid, fields) - σII = σᴵᴵ(i, j, k, grid, fields) + σI = σᴵ(i, j, k, grid, tracers) + σII = σᴵᴵ(i, j, k, grid, tracers) m = tand(90 - atand(μ)) q = σII - m * σI @@ -232,9 +222,9 @@ end # Implicit diagonal operator Ω = 1 / (1 + Δτ * (1 + P̃) / λ) - @inbounds tracers.σ₁₁[i, j, 1] = Ω * (fields.σ₁₁ₙ[i, j, 1] + Δτ * E * Kϵ₁₁) - @inbounds tracers.σ₂₂[i, j, 1] = Ω * (fields.σ₂₂ₙ[i, j, 1] + Δτ * E * Kϵ₂₂) - @inbounds tracers.σ₁₂[i, j, 1] = Ω * (fields.σ₁₂ₙ[i, j, 1] + Δτ * E * Kϵ₁₂) + @inbounds tracers.σ₁₁[i, j, 1] = Ω * (tracers.σ₁₁[i, j, 1] + Δτ * E * Kϵ₁₁) + @inbounds tracers.σ₂₂[i, j, 1] = Ω * (tracers.σ₂₂[i, j, 1] + Δτ * E * Kϵ₂₂) + @inbounds tracers.σ₁₂[i, j, 1] = Ω * (tracers.σ₁₂[i, j, 1] + Δτ * E * Kϵ₁₂) end @kernel function _advance_stresses!(fields, grid, rheology, tracers, u, v, ρᵢ, Δτ) @@ -248,46 +238,23 @@ end ρ = @inbounds ρᵢ[i, j, 1] dᵢ = @inbounds tracers.d[i, j, 1] - P = @inbounds fields.P[i, j, 1] E₀ = @inbounds fields.E[i, j, 1] - λ₀ = @inbounds fields.λ[i, j, 1] - - E = E₀ * (1 - dᵢ) - dcrit = reconstruction_2d(i, j, 1, grid, dcrit2, N, c, μ, tracers) - σI = σᴵ(i, j, 1, grid, tracers) - σII = σᴵᴵ(i, j, 1, grid, tracers) - - # Relaxation time (constant) - td = sqrt(2 * (1 + ν) * ρ / E * Azᶜᶜᶜ(i, j, 1, grid)) + # The relaxation time is time-dependent + E = E₀ * (1 - dᵢ) + td = sqrt(2 * (1 + ν) * ρ / E * Azᶜᶜᶜ(i, j, 1, grid)) + # damage tendency + Gd = reconstruction_2d(i, j, 1, grid, dcrit2, N, c, μ, tracers) / td + # Damage update - @inbounds dᵢ += dcrit * Δτ / td * (1 - dᵢ) - - # Clamp damage between 0 and a value close to 1 (cannot do 1 because of the relaxation time) - dᵢ = clamp(dᵢ, zero(grid), 99999 * one(grid) / 100000) - Δd = @inbounds (dᵢ - d[i, j, 1]) - - # Now we readvance the stresses with the new information - ϵ̇₁₁ = strain_rate_xx(i, j, 1, grid, u, v) - ϵ̇₂₂ = strain_rate_yy(i, j, 1, grid, u, v) - ϵ̇₁₂ = strain_rate_xy(i, j, 1, grid, u, v) - - Kϵ₁₁ = (ϵ̇₁₁ + ν * ϵ̇₂₂) / (1 - ν^2) - Kϵ₂₂ = (ϵ̇₂₂ + ν * ϵ̇₁₁) / (1 - ν^2) - Kϵ₁₂ = (1 - ν) * ϵ̇₁₂ / (1 - ν^2) - - E = E₀ * (1 - dᵢ) - λ = λ₀ * (1 - dᵢ)^(α - 1) - P̃ = zero(grid) - - # Implicit diagonal operator - Ω = @inbounds 1 / (1 + Δτ * (1 + P̃) / λ + Δd / (1 - dᵢ)) + dᵢ += Gd * (1 - dᵢ) * Δτ - @inbounds fields.σ₁₁[i, j, 1] = Ω * (fields.σ₁₁ₙ[i, j, 1] + Δτ * E * Kϵ₁₁) - @inbounds fields.σ₂₂[i, j, 1] = Ω * (fields.σ₂₂ₙ[i, j, 1] + Δτ * E * Kϵ₂₂) - @inbounds fields.σ₁₂[i, j, 1] = Ω * (fields.σ₁₂ₙ[i, j, 1] + Δτ * E * Kϵ₁₂) - @inbounds tracers.d[i, j, 1] = dᵢ + # Advance stresses and clamp damage + @inbounds tracers.σ₁₁[i, j, 1] -= Gd * Δτ * tracers.σ₁₁[i, j, 1] + @inbounds tracers.σ₂₂[i, j, 1] -= Gd * Δτ * tracers.σ₂₂[i, j, 1] + @inbounds tracers.σ₁₂[i, j, 1] -= Gd * Δτ * tracers.σ₁₂[i, j, 1] + @inbounds tracers.d[i, j, 1] = clamp(dᵢ, zero(grid), 99999 * one(grid) / 100000) end ##### diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index db90e54f..45d105b0 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -90,7 +90,7 @@ function ElastoViscoPlasticRheology(FT::DataType = Float64; convert(FT, max_relaxation_parameter)) end -function required_auxiliary_fields(r::ElastoViscoPlasticRheology, grid) +function rheology_auxiliary_fields(r::ElastoViscoPlasticRheology, grid) # TODO: What about boundary conditions? σ₁₁ = Field{Center, Center, Nothing}(grid) diff --git a/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl b/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl index 9765d025..1cb8d7da 100644 --- a/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl +++ b/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl @@ -18,7 +18,7 @@ using ClimaSeaIce.Rheologies: ∂ⱼ_σ₁ⱼ, ∂ⱼ_σ₂ⱼ, immersed_∂ⱼ_σ₁ⱼ, immersed_∂ⱼ_σ₂ⱼ, - required_auxiliary_fields, + rheology_auxiliary_fields, compute_stresses!, initialize_rheology!, compute_substep_Δtᶠᶠᶜ, @@ -26,6 +26,7 @@ using ClimaSeaIce.Rheologies: ∂ⱼ_σ₁ⱼ, sum_of_forcing_v import Oceananigans: fields +import ClimaSeaIce.Rheologies: rheology_prognostic_tracers ## A Framework to solve for the ice momentum equation, in the form: ## diff --git a/src/SeaIceMomentumEquations/sea_ice_momentum_equations.jl b/src/SeaIceMomentumEquations/sea_ice_momentum_equations.jl index 842c3627..941a9e21 100644 --- a/src/SeaIceMomentumEquations/sea_ice_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/sea_ice_momentum_equations.jl @@ -63,7 +63,7 @@ function SeaIceMomentumEquation(grid; minimum_concentration = 1e-3, minimum_mass = 1.0) - auxiliary_fields = merge(auxiliary_fields, required_auxiliary_fields(rheology, grid)) + auxiliary_fields = merge(auxiliary_fields, rheology_auxiliary_fields(rheology, grid)) external_momentum_stresses = (top = top_momentum_stress, bottom = bottom_momentum_stress) @@ -88,3 +88,5 @@ fields(mom::SeaIceMomentumEquation) = mom.auxiliary_fields # Passing no velocities @inline free_drift_u(i, j, k, grid, ::Nothing) = zero(grid) @inline free_drift_v(i, j, k, grid, ::Nothing) = zero(grid) + +rheology_prognostic_tracers(m::SeaIceMomentumEquation) = rheology_prognostic_tracers(m.rheology) diff --git a/src/sea_ice_model.jl b/src/sea_ice_model.jl index 2956ef4c..52b14748 100644 --- a/src/sea_ice_model.jl +++ b/src/sea_ice_model.jl @@ -6,8 +6,9 @@ using Oceananigans: tupleit, tracernames using Oceananigans.BoundaryConditions: regularize_field_boundary_conditions using Oceananigans.Forcings: model_forcing using ClimaSeaIce.SeaIceThermodynamics.HeatBoundaryConditions: flux_summary +using ClimaSeaIce.Rheologies: rheology_prognostic_tracers -struct SeaIceModel{GR, TD, D, TS, CL, U, T, IT, IC, ID, CT, STF, A, F} <: AbstractModel{TS} +struct SeaIceModel{GR, TD, D, TS, CL, U, T, IT, IC, IS, ID, CT, STF, A, F} <: AbstractModel{TS} grid :: GR clock :: CL forcing :: F @@ -16,6 +17,7 @@ struct SeaIceModel{GR, TD, D, TS, CL, U, T, IT, IC, ID, CT, STF, A, F} <: Abstra tracers :: T ice_thickness :: IT ice_concentration :: IC + ice_salinity :: IS ice_density :: ID ice_consolidation_thickness :: CT # Thermodynamics @@ -53,6 +55,7 @@ function SeaIceModel(grid; ice_consolidation_thickness = field((Center, Center, Nothing), ice_consolidation_thickness, grid) tracers = tupleit(tracers) # supports tracers=:c keyword argument (for example) + tracers = (tracers..., rheology_prognostic_tracers(dynamics)...) # add prognostic tracers # Next, we form a list of default boundary conditions: field_names = (:u, :v, :h, :ℵ, :S, tracernames(tracers)...) @@ -94,7 +97,6 @@ function SeaIceModel(grid; # TODO: should we have ice thickness and concentration as part of the tracers or # just additional fields of the sea ice model? - tracers = merge(tracers, (; S = ice_salinity)) timestepper = ForwardEulerTimeStepper(grid, prognostic_fields) if !isnothing(thermodynamics) @@ -122,6 +124,7 @@ function SeaIceModel(grid; tracers, ice_thickness, ice_concentration, + ice_salinity, ice_density, ice_consolidation_thickness, thermodynamics, diff --git a/src/sea_ice_time_stepping.jl b/src/sea_ice_time_stepping.jl index 965db5cf..00a714fc 100644 --- a/src/sea_ice_time_stepping.jl +++ b/src/sea_ice_time_stepping.jl @@ -74,15 +74,16 @@ end ℵ[i, j, k] = ifelse(ℵ⁺ > 1, one(ℵ⁺), ℵ⁺) h[i, j, k] = ifelse(ℵ⁺ > 1, V⁺, h⁺) - advance_tracers!(tracers, i, j, k, grid, Gⁿ, Δt) + advance_tracers!(tracers, i, j, k, Gⁿ, Δt) end end -advance_tracer_tendencies!(::EmptyTuples, args...) = nothing +advance_tracers!(::EmptyTuples, args...) = nothing -function advance_tracer_tendencies!(tracers, i, j, k, grid, G, Δt) - for (name, tracer) in tracers - tracer += Δt * G[name][i, j, k] +function advance_tracers!(tracers, i, j, k, G, Δt) + # Assumption! The tracer tendencies are the first ones + for n in eachindex(tracers) + @inbounds tracers[n][i, j, 1] += Δt * G[n][i, j, k] end end diff --git a/src/tracer_tendency_kernel_functions.jl b/src/tracer_tendency_kernel_functions.jl index 70cd1da6..b5c7a420 100644 --- a/src/tracer_tendency_kernel_functions.jl +++ b/src/tracer_tendency_kernel_functions.jl @@ -44,10 +44,11 @@ end const EmptyTuples = Union{NamedTuple{(), Tuple{}}, Tuple{}} -compute_tracer_tendencies!(Gⁿ, i, j, grid, advection, velocities, ::EmptyTuples) = nothing +compute_tracer_tendencies!(G, i, j, grid, advection, velocities, ::EmptyTuples) = nothing -function compute_tracer_tendencies!(Gⁿ, i, j, grid, advection, velocities, tracers) - for (name, tracer) in tracers - Gⁿ[name][i, j, 1] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, tracer) +function compute_tracer_tendencies!(G, i, j, grid, advection, velocities, tracers) + # Assumption! The tracer tendencies are the first ones + for n in eachindex(tracers) + @inbounds G[n][i, j, 1] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, tracers[n]) end -end +end \ No newline at end of file From 796459ef2a7321f55f107757440beae944f39a67 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 6 Mar 2025 11:57:42 +0100 Subject: [PATCH 041/108] add it --- .../brittle_bingham_maxwell_rheology.jl | 153 ++++++++++-------- 1 file changed, 83 insertions(+), 70 deletions(-) diff --git a/src/Rheologies/brittle_bingham_maxwell_rheology.jl b/src/Rheologies/brittle_bingham_maxwell_rheology.jl index 72dd32c6..aad2c892 100644 --- a/src/Rheologies/brittle_bingham_maxwell_rheology.jl +++ b/src/Rheologies/brittle_bingham_maxwell_rheology.jl @@ -47,11 +47,12 @@ rheology_prognostic_tracers(::BrittleBinghamMaxwellRheology) = (:d, :σ₁₁, function rheology_auxiliary_fields(::BrittleBinghamMaxwellRheology, grid) # TODO: What about boundary conditions? - P = Field{Center, Center, Nothing}(grid) - E = Field{Center, Center, Nothing}(grid) - λ = Field{Center, Center, Nothing}(grid) - - return (; P, E, λ) + P = Field{Center, Center, Nothing}(grid) + E = Field{Center, Center, Nothing}(grid) + λ = Field{Center, Center, Nothing}(grid) + σ¹ = Field{Center, Center, Nothing}(grid) + σ² = Field{Center, Center, Nothing}(grid) + return (; P, E, λ, σ¹, σ²) end # Extend the `adapt_structure` function for the ElastoViscoPlasticRheology @@ -117,6 +118,7 @@ function compute_stresses!(model, dynamics, rheology::BrittleBinghamMaxwellRheol ρᵢ = model.ice_density u, v = model.velocities fields = dynamics.auxiliary_fields + tracers = model.tracers Nx, Ny, _ = size(grid) Hx, Hy, _ = halo_size(grid) @@ -126,72 +128,18 @@ function compute_stresses!(model, dynamics, rheology::BrittleBinghamMaxwellRheol # Pretty simple timestepping Δτ = Δt / Ns - launch!(arch, grid, parameters, _compute_stress_predictors!, fields, grid, rheology, model.tracers, u, v, ρᵢ, Δτ) - launch!(arch, grid, parameters, _advance_stresses!, fields, grid, rheology, model.tracers, u, v, ρᵢ, Δτ) + launch!(arch, grid, parameters, _compute_stress_predictors!, fields, grid, rheology, tracers, u, v, ρᵢ, Δτ) + launch!(arch, grid, parameters, _advance_stresses_and_damage!, tracers, grid, rheology, fields, ρᵢ, Δτ) return nothing end -@inline σᴵ(i, j, k, grid, tracers) = @inbounds (tracers.σ₁₁[i, j, k] + tracers.σ₂₂[i, j, k]) / 2 -@inline σᴵᴵ(i, j, k, grid, tracers) = @inbounds sqrt((tracers.σ₁₁[i, j, k] - tracers.σ₂₂[i, j, k])^2 / 4 + tracers.σ₁₂[i, j, k]^2) - -@inline function reconstruction_2d(i, j, k, grid, f, args...) - fij = f(i, j, k, grid, args...) - fmj = f(i-1, j, k, grid, args...) - fpj = f(i+1, j, k, grid, args...) - fim = f(i, j-1, k, grid, args...) - fip = f(i, j+1, k, grid, args...) - fmm = f(i-1, j-1, k, grid, args...) - fmp = f(i-1, j+1, k, grid, args...) - fpm = f(i+1, j-1, k, grid, args...) - fpp = f(i+1, j+1, k, grid, args...) - - # remove NaNs - isnanij = isnan(fij) - isnanmj = isnan(fmj) - isnanpj = isnan(fpj) - isnanim = isnan(fim) - isnanip = isnan(fip) - isnanmm = isnan(fmm) - isnanmp = isnan(fmp) - isnanpm = isnan(fpm) - isnanpp = isnan(fpp) - - fij = ifelse(isnanij, zero(grid), fij) / 4 - fmj = ifelse(isnanmj, zero(grid), fmj) / 8 - fpj = ifelse(isnanpj, zero(grid), fpj) / 8 - fim = ifelse(isnanim, zero(grid), fim) / 8 - fip = ifelse(isnanip, zero(grid), fip) / 8 - fmm = ifelse(isnanmm, zero(grid), fmm) / 16 - fmp = ifelse(isnanmp, zero(grid), fmp) / 16 - fpm = ifelse(isnanpm, zero(grid), fpm) / 16 - fpp = ifelse(isnanpp, zero(grid), fpp) / 16 - - return (fij + fmj + fpj + fim + fip + fmm + fmp + fpm + fpp) -end - -@inline function dcritical(i, j, k, grid, N, c, μ, tracers) - σI = σᴵ(i, j, k, grid, tracers) - σII = σᴵᴵ(i, j, k, grid, tracers) - return one(grid) - ifelse(σI > - N, c / (σII + μ * σI), - N / σI) -end - -@inline function dcrit2(i, j, k, grid, N, c, μ, tracers) - - σI = σᴵ(i, j, k, grid, tracers) - σII = σᴵᴵ(i, j, k, grid, tracers) - - m = tand(90 - atand(μ)) - q = σII - m * σI - - # # Move towards the yield curve in a perpendicular fashion - σIf = (c - q) / (m + μ) - σIIf = m * σIf + q - - dcrit = one(grid) - sqrt(σIf^2 + σIIf^2) / sqrt(σI^2 + σII^2) - dcrit = ifelse(isnan(dcrit), zero(grid), dcrit) - - return dcrit * (σII > c - μ * σI) +@inline function critical_damage(i, j, k, grid, N, c, μ, fields) + σ¹ = @inbounds fields.σ¹[i, j, k] + σ² = @inbounds fields.σ²[i, j, k] + dc = one(grid) - ifelse(σ¹ > - N, c / (σ² + μ * σ¹), - N / σ¹) + dc = ifelse(isnan(dc), zero(grid), dc) + return dc * (σ² > c - μ * σ¹) end @kernel function _compute_stress_predictors!(fields, grid, rheology, tracers, u, v, ρᵢ, Δτ) @@ -225,9 +173,17 @@ end @inbounds tracers.σ₁₁[i, j, 1] = Ω * (tracers.σ₁₁[i, j, 1] + Δτ * E * Kϵ₁₁) @inbounds tracers.σ₂₂[i, j, 1] = Ω * (tracers.σ₂₂[i, j, 1] + Δτ * E * Kϵ₂₂) @inbounds tracers.σ₁₂[i, j, 1] = Ω * (tracers.σ₁₂[i, j, 1] + Δτ * E * Kϵ₁₂) + + σ₁₁ = @inbounds tracers.σ₁₁[i, j, 1] + σ₂₂ = @inbounds tracers.σ₂₂[i, j, 1] + σ₁₂ = @inbounds tracers.σ₁₂[i, j, 1] + + # Principal stress components + @inbounds fields.σ¹[i, j, 1] = (σ₁₁ + σ₂₂) / 2 + @inbounds fields.σ²[i, j, 1] = sqrt((σ₁₁ - σ₂₂)^2 / 4 + σ₁₂^2) end -@kernel function _advance_stresses!(fields, grid, rheology, tracers, u, v, ρᵢ, Δτ) +@kernel function _advance_stresses_and_damage!(tracers, grid, rheology, fields, ρᵢ, Δτ) i, j = @index(Global, NTuple) α = rheology.damage_parameter @@ -245,7 +201,7 @@ end td = sqrt(2 * (1 + ν) * ρ / E * Azᶜᶜᶜ(i, j, 1, grid)) # damage tendency - Gd = reconstruction_2d(i, j, 1, grid, dcrit2, N, c, μ, tracers) / td + Gd = critical_damage(i, j, 1, grid, N, c, μ, fields) / td # Damage update dᵢ += Gd * (1 - dᵢ) * Δτ @@ -270,4 +226,61 @@ end @inline ice_stress_ux(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, hσ₁₁, fields) @inline ice_stress_uy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₁₂, fields) @inline ice_stress_vx(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, hσ₁₂, fields) -@inline ice_stress_vy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₂₂, fields) \ No newline at end of file +@inline ice_stress_vy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₂₂, fields) + +# Another formulation of the critical Damage + +# @inline function dc_perpendicular(i, j, k, grid, N, c, μ, fields) + +# σ¹ = @inbounds fields.σ¹[i, j, k] +# σ² = @inbounds fields.σ²[i, j, k] + +# m = tand(90 - atand(μ)) +# q = σ² - m * σ¹ + +# # # Move towards the yield curve in a perpendicular fashion +# σᵪ¹ = (c - q) / (m + μ) +# σᵪ² = m * σᵪ¹ + q + +# dc = one(grid) - sqrt(σᵪ¹^2 + σᵪ²^2) / sqrt(σ¹^2 + σ²^2) +# dc = ifelse(isnan(dc), zero(grid), dc) + +# return dc * (σ² > c - μ * σ¹) +# end +# +# Dcrit needs to be reconstructed +# +# @inline function reconstruction_2d(i, j, k, grid, f, args...) +# fij = f(i, j, k, grid, args...) +# fmj = f(i-1, j, k, grid, args...) +# fpj = f(i+1, j, k, grid, args...) +# fim = f(i, j-1, k, grid, args...) +# fip = f(i, j+1, k, grid, args...) +# fmm = f(i-1, j-1, k, grid, args...) +# fmp = f(i-1, j+1, k, grid, args...) +# fpm = f(i+1, j-1, k, grid, args...) +# fpp = f(i+1, j+1, k, grid, args...) + +# # remove NaNs +# isnanij = isnan(fij) +# isnanmj = isnan(fmj) +# isnanpj = isnan(fpj) +# isnanim = isnan(fim) +# isnanip = isnan(fip) +# isnanmm = isnan(fmm) +# isnanmp = isnan(fmp) +# isnanpm = isnan(fpm) +# isnanpp = isnan(fpp) + +# fij = ifelse(isnanij, zero(grid), fij) / 4 +# fmj = ifelse(isnanmj, zero(grid), fmj) / 8 +# fpj = ifelse(isnanpj, zero(grid), fpj) / 8 +# fim = ifelse(isnanim, zero(grid), fim) / 8 +# fip = ifelse(isnanip, zero(grid), fip) / 8 +# fmm = ifelse(isnanmm, zero(grid), fmm) / 16 +# fmp = ifelse(isnanmp, zero(grid), fmp) / 16 +# fpm = ifelse(isnanpm, zero(grid), fpm) / 16 +# fpp = ifelse(isnanpp, zero(grid), fpp) / 16 + +# return (fij + fmj + fpj + fim + fip + fmm + fmp + fpm + fpp) +# end \ No newline at end of file From f02692cfcf3ac7d28dda8a53673ef9008078fba6 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 6 Mar 2025 11:59:01 +0100 Subject: [PATCH 042/108] add it --- examples/ice_advected_by_anticyclone.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ice_advected_by_anticyclone.jl b/examples/ice_advected_by_anticyclone.jl index 448c6be6..a7ff7c47 100644 --- a/examples/ice_advected_by_anticyclone.jl +++ b/examples/ice_advected_by_anticyclone.jl @@ -94,7 +94,7 @@ momentum_equations = SeaIceMomentumEquation(grid; bottom_momentum_stress = τₒ, coriolis = FPlane(f=1e-4), ocean_velocities = (u = Uₒ, v = Vₒ), - rheology = BrittleBinghamMaxwellRheology(), + rheology = ElastoViscoPlasticRheology(), solver = SplitExplicitSolver(substeps=150)) # Define the model! From 274b8d9c308493ae7ea44b6a9559bd2a80639b71 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 6 Mar 2025 13:04:40 +0100 Subject: [PATCH 043/108] improve --- examples/ice_advected_on_coastline.jl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/ice_advected_on_coastline.jl b/examples/ice_advected_on_coastline.jl index 5bb2aa72..20da1e7f 100644 --- a/examples/ice_advected_on_coastline.jl +++ b/examples/ice_advected_on_coastline.jl @@ -64,15 +64,10 @@ dynamics = SeaIceMomentumEquation(grid; rheology = ElastoViscoPlasticRheology(), solver = SplitExplicitSolver(substeps=150)) -u_bcs = FieldBoundaryConditions(top = nothing, bottom = nothing, - north = ValueBoundaryCondition(0), - south = ValueBoundaryCondition(0)) - #Define the model! model = SeaIceModel(grid; advection = WENO(order=7), dynamics = dynamics, - boundary_conditions = (; u=u_bcs), thermodynamics = nothing) # We start with a concentration of ℵ = 1 everywhere From 82e3a7c9cd56a8eb0ed642d54d57ec54c0ad2fa7 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Fri, 7 Mar 2025 11:42:25 +0100 Subject: [PATCH 044/108] add this --- examples/freezing_of_a_lake.jl | 4 ++-- examples/melting_in_spring.jl | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/freezing_of_a_lake.jl b/examples/freezing_of_a_lake.jl index 28178468..34d6b93f 100644 --- a/examples/freezing_of_a_lake.jl +++ b/examples/freezing_of_a_lake.jl @@ -36,8 +36,9 @@ parameters = ( cₐ = parameters.atmosphere_heat_capacity Tₐ = parameters.atmosphere_temperature[i] uₐ = parameters.atmosphere_wind_speed + ℵ = fields.ℵ[i, j, 1] - return Cₛ * ρₐ * cₐ * uₐ * (Tᵤ - Tₐ) + return Cₛ * ρₐ * cₐ * uₐ * (Tᵤ - Tₐ) * ℵ end # We also evolve a bucket freshwater lake that cools down and freezes from below @@ -72,7 +73,6 @@ lake = ( cₒ = lake.lake_heat_capacity ρₒ = lake.lake_density Δ = lake.lake_depth - ℵ = fields.ℵ[i, j, 1] Δt = lake.Δt Qₐ = Cₛ * ρₐ * cₐ * uₐ * (Tₐ - Tₒ[i]) * (1 - ℵ) diff --git a/examples/melting_in_spring.jl b/examples/melting_in_spring.jl index 1957a0e6..8f3c7673 100644 --- a/examples/melting_in_spring.jl +++ b/examples/melting_in_spring.jl @@ -43,8 +43,9 @@ parameters = ( cₐ = parameters.atmosphere_heat_capacity Tₐ = parameters.atmosphere_temperature uₐ = parameters.atmosphere_wind_speed + ℵ = fields.ℵ[i, j, 1] - return Cₛ * ρₐ * cₐ * uₐ * (Tᵤ - Tₐ) + return Cₛ * ρₐ * cₐ * uₐ * (Tᵤ - Tₐ) * ℵ end aerodynamic_flux = FluxFunction(sensible_heat_flux; parameters) From e2d7bea7a12e14d0ef4e705ee78e26f2b269207b Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Fri, 7 Mar 2025 14:20:05 +0100 Subject: [PATCH 045/108] Update freezing_of_a_lake.jl --- examples/freezing_of_a_lake.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/freezing_of_a_lake.jl b/examples/freezing_of_a_lake.jl index 34d6b93f..287357b5 100644 --- a/examples/freezing_of_a_lake.jl +++ b/examples/freezing_of_a_lake.jl @@ -74,7 +74,8 @@ lake = ( ρₒ = lake.lake_density Δ = lake.lake_depth Δt = lake.Δt - + ℵ = fields.ℵ[i, j, 1] + Qₐ = Cₛ * ρₐ * cₐ * uₐ * (Tₐ - Tₒ[i]) * (1 - ℵ) Tₒ[i] = Tₒ[i] + Qₐ / (ρₒ * cₒ) * Δt From b935dffd622c6a243b3c2cc4865ee3baca278f02 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Sat, 8 Mar 2025 10:01:55 +0100 Subject: [PATCH 046/108] formatting --- examples/freezing_of_a_lake.jl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/freezing_of_a_lake.jl b/examples/freezing_of_a_lake.jl index 287357b5..dd416386 100644 --- a/examples/freezing_of_a_lake.jl +++ b/examples/freezing_of_a_lake.jl @@ -51,7 +51,6 @@ lake = ( lake_heat_capacity = 4000, # lake_temperature = [1.0, 1.0, 1.0, 1.0], # ᵒC lake_depth = 10, # m - atmosphere = parameters, Δt = 10minutes ) @@ -62,9 +61,9 @@ lake = ( @inline function advance_lake_and_frazil_flux(i, j, grid, Tuᵢ, clock, fields, parameters) atmos = parameters.atmosphere - lake = parameters + lake = parameters.lake - Cₛ = atmos.transfer_coefficient + Cₛ = atmos.transfer_coefficient ρₐ = atmos.atmosphere_density cₐ = atmos.atmosphere_heat_capacity Tₐ = atmos.atmosphere_temperature[i] @@ -88,9 +87,8 @@ lake = ( return Qᵢ end -aerodynamic_flux = FluxFunction(sensible_heat_flux; parameters) -top_heat_flux = (aerodynamic_flux) -bottom_heat_flux = FluxFunction(advance_lake_and_frazil_flux; parameters=lake) +top_heat_flux = FluxFunction(sensible_heat_flux; parameters) +bottom_heat_flux = FluxFunction(advance_lake_and_frazil_flux; parameters=(; lake, atmosphere)) model = SeaIceModel(grid; ice_consolidation_thickness = 0.05, # m From a2477ce7197cea3f7c925e5984c512159a9d313c Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Mon, 10 Mar 2025 10:18:46 +0100 Subject: [PATCH 047/108] some fixes --- examples/freezing_of_a_lake.jl | 50 ++++++++++++++++++++++++++++------ src/sea_ice_model.jl | 2 +- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/examples/freezing_of_a_lake.jl b/examples/freezing_of_a_lake.jl index dd416386..be235009 100644 --- a/examples/freezing_of_a_lake.jl +++ b/examples/freezing_of_a_lake.jl @@ -19,26 +19,30 @@ grid = RectilinearGrid(size=4, x=(0, 1), topology=(Periodic, Flat, Flat)) # The sensible heat flux from the atmosphere is represented by a `FluxFunction`. -parameters = ( +atmosphere = ( transfer_coefficient = 1e-3, # Unitless atmosphere_density = 1.225, # kg m⁻³ atmosphere_heat_capacity = 1004, # atmosphere_temperature = [-20, -10, -5, 0], # ᵒC - atmosphere_wind_speed = 5 # m s⁻¹ + atmosphere_wind_speed = 5, # m s⁻¹ + atmosphere_ice_flux = [0.0, 0.0, 0.0, 0.0], # W m⁻² ) # Flux is positive (cooling by fluxing heat up away from upper surface) # when Tₐ < Tᵤ: -@inline function sensible_heat_flux(i, j, grid, Tᵤ, clock, fields, parameters) - Cₛ = parameters.transfer_coefficient - ρₐ = parameters.atmosphere_density - cₐ = parameters.atmosphere_heat_capacity - Tₐ = parameters.atmosphere_temperature[i] - uₐ = parameters.atmosphere_wind_speed +@inline function sensible_heat_flux(i, j, grid, Tᵤ, clock, fields, atmosphere) + Cₛ = atmosphere.transfer_coefficient + ρₐ = atmosphere.atmosphere_density + cₐ = atmosphere.atmosphere_heat_capacity + Tₐ = atmosphere.atmosphere_temperature[i] + uₐ = atmosphere.atmosphere_wind_speed ℵ = fields.ℵ[i, j, 1] + Qₐ = atmosphere.atmosphere_ice_flux - return Cₛ * ρₐ * cₐ * uₐ * (Tᵤ - Tₐ) * ℵ + Qₐ[i] = Cₛ * ρₐ * cₐ * uₐ * (Tᵤ - Tₐ) * ℵ + + return Qₐ[i] end # We also evolve a bucket freshwater lake that cools down and freezes from below @@ -51,6 +55,8 @@ lake = ( lake_heat_capacity = 4000, # lake_temperature = [1.0, 1.0, 1.0, 1.0], # ᵒC lake_depth = 10, # m + lake_ice_flux = [0.0, 0.0, 0.0, 0.0], # W m⁻² + atmosphere_lake_flux = [0.0, 0.0, 0.0, 0.0], # W m⁻² Δt = 10minutes ) @@ -75,6 +81,9 @@ lake = ( Δt = lake.Δt ℵ = fields.ℵ[i, j, 1] + Qᵗ = lake.atmosphere_lake_flux + Qᵇ = lake.lake_ice_flux + Qₐ = Cₛ * ρₐ * cₐ * uₐ * (Tₐ - Tₒ[i]) * (1 - ℵ) Tₒ[i] = Tₒ[i] + Qₐ / (ρₒ * cₒ) * Δt @@ -84,6 +93,9 @@ lake = ( Tₒ[i] = ifelse(Qᵢ == 0, Tₒ[i], zero(Qᵢ)) + Qᵗ[i] = Qₐ + Qᵇ[i] = Qᵢ + return Qᵢ end @@ -120,6 +132,26 @@ end simulation.callbacks[:save] = Callback(accumulate_timeseries) +# accumulate energy +Ei = [] +Qa = [] +Ql = [] + +function accumulate_energy(sim) + T = sim.model.ice_thermodynamics.top_surface_temperature + h = sim.model.ice_thickness + ℵ = sim.model.ice_concentration + ρ = sim.model.ice_density + + # TODO: Need to subtract the latent energy here + push!(Ei, @. ρ * T * h * ℵ) + push!(Qa, atmosphere.atmosphere_ice_flux) + push!(Ql, lake.lake_ice_flux) +end + +simulation.callbacks[:save] = Callback(accumulate_timeseries) +simulation.callbacks[:energy] = Callback(accumulate_energy) + run!(simulation) # Extract and visualize data diff --git a/src/sea_ice_model.jl b/src/sea_ice_model.jl index 33b1ea86..2b9e5936 100644 --- a/src/sea_ice_model.jl +++ b/src/sea_ice_model.jl @@ -44,7 +44,7 @@ function SeaIceModel(grid; advection = nothing, tracers = (), boundary_conditions = NamedTuple(), - ice_thermodynamics = SlabSeaIceThermodynamics(grid), + ice_thermodynamics = SlabSeaIceThermodynamics(grid), dynamics = nothing, forcing = NamedTuple()) From d543278f303cdab072f33d2f03c517358e2fd2bd Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Mon, 10 Mar 2025 10:39:01 +0100 Subject: [PATCH 048/108] hmm, not sure heat capacity is consistent here --- examples/freezing_of_a_lake.jl | 63 ++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/examples/freezing_of_a_lake.jl b/examples/freezing_of_a_lake.jl index be235009..20b7338c 100644 --- a/examples/freezing_of_a_lake.jl +++ b/examples/freezing_of_a_lake.jl @@ -99,7 +99,7 @@ lake = ( return Qᵢ end -top_heat_flux = FluxFunction(sensible_heat_flux; parameters) +top_heat_flux = FluxFunction(sensible_heat_flux; parameters=atmosphere) bottom_heat_flux = FluxFunction(advance_lake_and_frazil_flux; parameters=(; lake, atmosphere)) model = SeaIceModel(grid; @@ -141,12 +141,15 @@ function accumulate_energy(sim) T = sim.model.ice_thermodynamics.top_surface_temperature h = sim.model.ice_thickness ℵ = sim.model.ice_concentration - ρ = sim.model.ice_density + ρ = sim.model.ice_density[1, 1, 1] + c = sim.model.ice_thermodynamics.phase_transitions.ice_heat_capacity + PT = sim.model.ice_thermodynamics.phase_transitions + + ℰ = latent_heat.(Ref(PT), T) - # TODO: Need to subtract the latent energy here - push!(Ei, @. ρ * T * h * ℵ) - push!(Qa, atmosphere.atmosphere_ice_flux) - push!(Ql, lake.lake_ice_flux) + push!(Ei, deepcopy(@. h * ℵ * ρ * c * (T + 273.15) - ℰ)) + push!(Qa, deepcopy(atmosphere.atmosphere_ice_flux)) + push!(Ql, deepcopy(lake.lake_ice_flux)) end simulation.callbacks[:save] = Callback(accumulate_timeseries) @@ -208,3 +211,51 @@ nothing # hide # ![](freezing_in_winter.png) +# Extract and visualize energy +Ei1 = [datum[1] for datum in Ei] +Qa1 = [datum[1] for datum in Qa] +Ql1 = [datum[1] for datum in Ql] +Ei2 = [datum[2] for datum in Ei] +Qa2 = [datum[2] for datum in Qa] +Ql2 = [datum[2] for datum in Ql] +Ei3 = [datum[3] for datum in Ei] +Qa3 = [datum[3] for datum in Qa] +Ql3 = [datum[3] for datum in Ql] +Ei4 = [datum[4] for datum in Ei] +Qa4 = [datum[4] for datum in Qa] +Ql4 = [datum[4] for datum in Ql] + +fig = Figure(size=(1000, 900)) + +axE = Axis(fig[1, 1], xlabel="Time (days)", ylabel="Sea Ice energy (J)") +axA = Axis(fig[2, 1], xlabel="Time (days)", ylabel="Atmosphere HF") +axL = Axis(fig[3, 1], xlabel="Time (days)", ylabel="Lake HF") +axB = Axis(fig[4, 1], xlabel="Time (days)", ylabel="Heat budget", yscale=log10) + +pEt1 = (Ei1[2:end] - Ei1[1:end-1]) ./ 10minutes +pEt2 = (Ei2[2:end] - Ei2[1:end-1]) ./ 10minutes +pEt3 = (Ei3[2:end] - Ei3[1:end-1]) ./ 10minutes +pEt4 = (Ei4[2:end] - Ei4[1:end-1]) ./ 10minutes +thalf = (t[2:end] .+ t[1:end-1]) ./ 2 + +lines!(axE, t / day, Ei1) +lines!(axA, t / day, Qa1) +lines!(axL, t / day, Ql1) + +lines!(axE, t / day, Ei2) +lines!(axA, t / day, Qa2) +lines!(axL, t / day, Ql2) + +lines!(axE, t / day, Ei3) +lines!(axA, t / day, Qa3) +lines!(axL, t / day, Ql3) + +lines!(axE, t / day, Ei4) +lines!(axA, t / day, Qa4) +lines!(axL, t / day, Ql4) + +lines!(axB, thalf / day, pEt1) +lines!(axB, t / day, 2 .* (Qa1 - Ql1)) + +save("freezing_in_winter.png", fig) +nothing # hide \ No newline at end of file From 6aa378cb8086ca924bdf1cb4a4f08f48af08ecc9 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 13 Mar 2025 10:42:59 +0100 Subject: [PATCH 049/108] add to examples? --- docs/make.jl | 2 ++ .../arctic_basin_seasonal_cycle.jl | 31 +++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) rename {validation/reproduce_semtner => examples}/arctic_basin_seasonal_cycle.jl (71%) diff --git a/docs/make.jl b/docs/make.jl index 491f5f1b..3d533a35 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -18,6 +18,7 @@ example_scripts = [ "freezing_of_a_lake.jl", "ice_advected_by_anticyclone.jl", "ice_advected_on_coastline.jl", + "arctic_basin_seasonal_cycle.jl" ] for filename in example_scripts @@ -31,6 +32,7 @@ example_pages = [ "Freezing of a Lake" => "literated/freezing_of_a_lake.md", "Ice advected by anticyclone" => "literated/ice_advected_by_anticyclone.md", "Ice advected on coastline" => "literated/ice_advected_on_coastline.md", + "Arctic basin seasonal cycle" => "literated/arctic_basin_seasonal_cycle.md" ] ##### diff --git a/validation/reproduce_semtner/arctic_basin_seasonal_cycle.jl b/examples/arctic_basin_seasonal_cycle.jl similarity index 71% rename from validation/reproduce_semtner/arctic_basin_seasonal_cycle.jl rename to examples/arctic_basin_seasonal_cycle.jl index b93283e3..895b1cbf 100644 --- a/validation/reproduce_semtner/arctic_basin_seasonal_cycle.jl +++ b/examples/arctic_basin_seasonal_cycle.jl @@ -1,21 +1,20 @@ ##### ##### The purpose of this script is to reproduce some results from Semtner (1976) -##### Note that this script probably doesn't work and is a work in progress. -##### Don't hestitate to update this script in a PR! ##### using Oceananigans using Oceananigans.Units +using Oceananigans.Fields: index_binary_search using ClimaSeaIce # Forcings (Semtner 1976, table 1), originally tabulated by Fletcher (1965) # Note: these are in kcal, which was deprecated in the ninth General Conference on Weights and # Measures in 1948. We convert these to Joules (and then to Watts) below. # Month: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec -tabulated_shortwave = [ 0 0 1.9 9.9 17.7 19.2 13.6 9.0 3.7 0.4 0 0] -tabulated_longwave = [10.4 10.3 10.3 11.6 15.1 18.0 19.1 18.7 16.5 13.9 11.2 10.9] -tabulated_sensible = [1.18 0.76 0.72 0.29 -0.45 -0.39 -0.30 -0.40 -0.17 0.1 0.56 0.79] -tabulated_latent = [ 0 -0.02 -0.03 -0.46 -0.70 -0.64 -0.66 -0.39 -0.19 -0.01 -0.01 -3.20] +tabulated_shortwave = [ 0, 0, 1.9, 9.9, 17.7, 19.2, 13.6, 9.0, 3.7, 0.4, 0, 0] .* 1e4 # to convert to kcal / m² +tabulated_longwave = [10.4, 10.3, 10.3, 11.6, 15.1, 18.0, 19.1, 18.7, 16.5, 13.9, 11.2, 10.9] .* 1e4 # to convert to kcal / m² +tabulated_sensible = [1.18, 0.76, 0.72, 0.29, -0.45, -0.39, -0.30, -0.40, -0.17, 0.1, 0.56, 0.79] .* 1e4 # to convert to kcal / m² +tabulated_latent = [ 0, -0.02, -0.03, -0.46, -0.70, -0.64, -0.66, -0.39, -0.19, -0.01, -0.01, -3.20] .* 1e4 # to convert to kcal / m² # Pretend every month is just 30 days Nmonths = 12 @@ -46,7 +45,7 @@ display(fig) times = parameters.times Q = parameters.flux Nt = length(times) - t = mod(clock.time, 360days) + t = mod(clock.time, 360days) n₁, n₂ = index_binary_search(times, t, Nt) Q₁ = @inbounds Q[n₁] @@ -67,18 +66,18 @@ Q_emission = RadiativeEmission() grid = RectilinearGrid(size=(), topology=(Flat, Flat, Flat)) top_heat_flux = (Q_shortwave, Q_longwave, Q_sensible, Q_latent, Q_emission) -model = SlabSeaIceModel(grid; top_heat_flux) -set!(model, h=1) +model = SeaIceModel(grid; top_heat_flux) +set!(model, h=0.3, ℵ=1) # We start from 300cm of ice and full concentration -simulation = Simulation(model, Δt=8hours, stop_time=4 * 360days) +simulation = Simulation(model, Δt=10minutes, stop_time=4 * 360days) # Accumulate data -timeseries = [] +series = [] function accumulate_timeseries(sim) - T = model.top_temperature + T = model.ice_thermodynamics.top_surface_temperature h = model.ice_thickness - push!(timeseries, (time(sim), first(h), first(T))) + push!(series, (time(sim), first(h), first(T))) end simulation.callbacks[:save] = Callback(accumulate_timeseries) @@ -87,9 +86,9 @@ run!(simulation) # Extract and visualize data -t = [datum[1] for datum in timeseries] -h = [datum[2] for datum in timeseries] -T = [datum[3] for datum in timeseries] +t = [datum[1] for datum in series] +h = [datum[2] for datum in series] +T = [datum[3] for datum in series] set_theme!(Theme(fontsize=24, linewidth=4)) From e169ecaf9cfbbebd3cf4ce438fabea5051033295 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 13 Mar 2025 10:50:37 +0100 Subject: [PATCH 050/108] the seasonal cycle is not that well predicted I guess --- examples/arctic_basin_seasonal_cycle.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/arctic_basin_seasonal_cycle.jl b/examples/arctic_basin_seasonal_cycle.jl index 895b1cbf..70a8b08c 100644 --- a/examples/arctic_basin_seasonal_cycle.jl +++ b/examples/arctic_basin_seasonal_cycle.jl @@ -51,7 +51,11 @@ display(fig) Q₁ = @inbounds Q[n₁] Q₂ = @inbounds Q[n₂] - ñ = @inbounds (t - times[n₁]) * (n₂ - n₁) / (times[n₂] - times[n₁]) + ñ = if n₁ == n₂ + n₁ + else + @inbounds (t - times[n₁]) * (n₂ - n₁) / (times[n₂] - times[n₁]) + end return Q₁ * (ñ - n₁) + Q₂ * (n₂ - ñ) end @@ -69,7 +73,7 @@ top_heat_flux = (Q_shortwave, Q_longwave, Q_sensible, Q_latent, Q_emission) model = SeaIceModel(grid; top_heat_flux) set!(model, h=0.3, ℵ=1) # We start from 300cm of ice and full concentration -simulation = Simulation(model, Δt=10minutes, stop_time=4 * 360days) +simulation = Simulation(model, Δt=8hours, stop_time=4 * 360days) # Accumulate data series = [] From 499e4ff0dcb285ccee1967e77860143f51bc578e Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 13 Mar 2025 11:00:34 +0100 Subject: [PATCH 051/108] add this --- examples/arctic_basin_seasonal_cycle.jl | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/examples/arctic_basin_seasonal_cycle.jl b/examples/arctic_basin_seasonal_cycle.jl index 70a8b08c..ab66f5f0 100644 --- a/examples/arctic_basin_seasonal_cycle.jl +++ b/examples/arctic_basin_seasonal_cycle.jl @@ -11,10 +11,10 @@ using ClimaSeaIce # Note: these are in kcal, which was deprecated in the ninth General Conference on Weights and # Measures in 1948. We convert these to Joules (and then to Watts) below. # Month: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec -tabulated_shortwave = [ 0, 0, 1.9, 9.9, 17.7, 19.2, 13.6, 9.0, 3.7, 0.4, 0, 0] .* 1e4 # to convert to kcal / m² -tabulated_longwave = [10.4, 10.3, 10.3, 11.6, 15.1, 18.0, 19.1, 18.7, 16.5, 13.9, 11.2, 10.9] .* 1e4 # to convert to kcal / m² -tabulated_sensible = [1.18, 0.76, 0.72, 0.29, -0.45, -0.39, -0.30, -0.40, -0.17, 0.1, 0.56, 0.79] .* 1e4 # to convert to kcal / m² -tabulated_latent = [ 0, -0.02, -0.03, -0.46, -0.70, -0.64, -0.66, -0.39, -0.19, -0.01, -0.01, -3.20] .* 1e4 # to convert to kcal / m² +tabulated_shortwave = [ 0, 0, 1.9, 9.9, 17.7, 19.2, 13.6, 9.0, 3.7, 0.4, 0, 0] # .* 1e4 # to convert to kcal / m² +tabulated_longwave = [10.4, 10.3, 10.3, 11.6, 15.1, 18.0, 19.1, 18.7, 16.5, 13.9, 11.2, 10.9] # .* 1e4 # to convert to kcal / m² +tabulated_sensible = [1.18, 0.76, 0.72, 0.29, -0.45, -0.39, -0.30, -0.40, -0.17, 0.1, 0.56, 0.79] # .* 1e4 # to convert to kcal / m² +tabulated_latent = [ 0, -0.02, -0.03, -0.46, -0.70, -0.64, -0.66, -0.39, -0.19, -0.01, -0.01, -3.20] # .* 1e4 # to convert to kcal / m² # Pretend every month is just 30 days Nmonths = 12 @@ -81,7 +81,8 @@ series = [] function accumulate_timeseries(sim) T = model.ice_thermodynamics.top_surface_temperature h = model.ice_thickness - push!(series, (time(sim), first(h), first(T))) + ℵ = model.ice_concentration + push!(series, (time(sim), first(h), first(T), first(ℵ))) end simulation.callbacks[:save] = Callback(accumulate_timeseries) @@ -93,6 +94,7 @@ run!(simulation) t = [datum[1] for datum in series] h = [datum[2] for datum in series] T = [datum[3] for datum in series] +ℵ = [datum[4] for datum in series] set_theme!(Theme(fontsize=24, linewidth=4)) @@ -100,9 +102,11 @@ fig = Figure(size=(1000, 800)) axT = Axis(fig[1, 1], xlabel="Time (days)", ylabel="Top temperature (ᵒC)") axh = Axis(fig[2, 1], xlabel="Time (days)", ylabel="Ice thickness (m)") +axℵ = Axis(fig[3, 1], xlabel="Time (days)", ylabel="Ice concentration (-)") lines!(axT, t / day, T) lines!(axh, t / day, h) +lines!(axℵ, t / day, ℵ) display(fig) From fd8e294fc572f7e46ada60934f34372f3c32d8f1 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 13 Mar 2025 11:23:34 +0100 Subject: [PATCH 052/108] the fluxes have wrong signs --- examples/arctic_basin_seasonal_cycle.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/arctic_basin_seasonal_cycle.jl b/examples/arctic_basin_seasonal_cycle.jl index ab66f5f0..1bdb7a52 100644 --- a/examples/arctic_basin_seasonal_cycle.jl +++ b/examples/arctic_basin_seasonal_cycle.jl @@ -11,10 +11,10 @@ using ClimaSeaIce # Note: these are in kcal, which was deprecated in the ninth General Conference on Weights and # Measures in 1948. We convert these to Joules (and then to Watts) below. # Month: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec -tabulated_shortwave = [ 0, 0, 1.9, 9.9, 17.7, 19.2, 13.6, 9.0, 3.7, 0.4, 0, 0] # .* 1e4 # to convert to kcal / m² -tabulated_longwave = [10.4, 10.3, 10.3, 11.6, 15.1, 18.0, 19.1, 18.7, 16.5, 13.9, 11.2, 10.9] # .* 1e4 # to convert to kcal / m² -tabulated_sensible = [1.18, 0.76, 0.72, 0.29, -0.45, -0.39, -0.30, -0.40, -0.17, 0.1, 0.56, 0.79] # .* 1e4 # to convert to kcal / m² -tabulated_latent = [ 0, -0.02, -0.03, -0.46, -0.70, -0.64, -0.66, -0.39, -0.19, -0.01, -0.01, -3.20] # .* 1e4 # to convert to kcal / m² +tabulated_shortwave = - [ 0, 0, 1.9, 9.9, 17.7, 19.2, 13.6, 9.0, 3.7, 0.4, 0, 0] # .* 1e4 # to convert to kcal / m² +tabulated_longwave = - [10.4, 10.3, 10.3, 11.6, 15.1, 18.0, 19.1, 18.7, 16.5, 13.9, 11.2, 10.9] # .* 1e4 # to convert to kcal / m² +tabulated_sensible = - [1.18, 0.76, 0.72, 0.29, -0.45, -0.39, -0.30, -0.40, -0.17, 0.1, 0.56, 0.79] # .* 1e4 # to convert to kcal / m² +tabulated_latent = - [ 0, -0.02, -0.03, -0.46, -0.70, -0.64, -0.66, -0.39, -0.19, -0.01, -0.01, -3.20] # .* 1e4 # to convert to kcal / m² # Pretend every month is just 30 days Nmonths = 12 From 876c2f4e6bdbd305606d22cdfecf578f4b940642 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 13 Mar 2025 11:27:27 +0100 Subject: [PATCH 053/108] missing a minus and a conversion factor --- examples/arctic_basin_seasonal_cycle.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/arctic_basin_seasonal_cycle.jl b/examples/arctic_basin_seasonal_cycle.jl index 1bdb7a52..40f38f38 100644 --- a/examples/arctic_basin_seasonal_cycle.jl +++ b/examples/arctic_basin_seasonal_cycle.jl @@ -11,10 +11,10 @@ using ClimaSeaIce # Note: these are in kcal, which was deprecated in the ninth General Conference on Weights and # Measures in 1948. We convert these to Joules (and then to Watts) below. # Month: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec -tabulated_shortwave = - [ 0, 0, 1.9, 9.9, 17.7, 19.2, 13.6, 9.0, 3.7, 0.4, 0, 0] # .* 1e4 # to convert to kcal / m² -tabulated_longwave = - [10.4, 10.3, 10.3, 11.6, 15.1, 18.0, 19.1, 18.7, 16.5, 13.9, 11.2, 10.9] # .* 1e4 # to convert to kcal / m² -tabulated_sensible = - [1.18, 0.76, 0.72, 0.29, -0.45, -0.39, -0.30, -0.40, -0.17, 0.1, 0.56, 0.79] # .* 1e4 # to convert to kcal / m² -tabulated_latent = - [ 0, -0.02, -0.03, -0.46, -0.70, -0.64, -0.66, -0.39, -0.19, -0.01, -0.01, -3.20] # .* 1e4 # to convert to kcal / m² +tabulated_shortwave = - [ 0, 0, 1.9, 9.9, 17.7, 19.2, 13.6, 9.0, 3.7, 0.4, 0, 0] .* 1e4 # to convert to kcal / m² +tabulated_longwave = - [10.4, 10.3, 10.3, 11.6, 15.1, 18.0, 19.1, 18.7, 16.5, 13.9, 11.2, 10.9] .* 1e4 # to convert to kcal / m² +tabulated_sensible = - [1.18, 0.76, 0.72, 0.29, -0.45, -0.39, -0.30, -0.40, -0.17, 0.1, 0.56, 0.79] .* 1e4 # to convert to kcal / m² +tabulated_latent = - [ 0, -0.02, -0.03, -0.46, -0.70, -0.64, -0.66, -0.39, -0.19, -0.01, -0.01, -3.20] .* 1e4 # to convert to kcal / m² # Pretend every month is just 30 days Nmonths = 12 @@ -25,7 +25,7 @@ times = times_days .* day # times in seconds # Convert fluxes to the right units kcal_to_joules = 4184 -tabulated_shortwave .*= kcal_to_joules / (month_days * days) +tabulated_shortwave .*= kcal_to_joules / (month_days * days) tabulated_longwave .*= kcal_to_joules / (month_days * days) tabulated_sensible .*= kcal_to_joules / (month_days * days) tabulated_latent .*= kcal_to_joules / (month_days * days) From b56dca3db5749ea1f9ce326b0656fd3fefb043a9 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 13 Mar 2025 11:38:00 +0100 Subject: [PATCH 054/108] some error in the table --- examples/arctic_basin_seasonal_cycle.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/arctic_basin_seasonal_cycle.jl b/examples/arctic_basin_seasonal_cycle.jl index 40f38f38..e3d278a9 100644 --- a/examples/arctic_basin_seasonal_cycle.jl +++ b/examples/arctic_basin_seasonal_cycle.jl @@ -14,7 +14,7 @@ using ClimaSeaIce tabulated_shortwave = - [ 0, 0, 1.9, 9.9, 17.7, 19.2, 13.6, 9.0, 3.7, 0.4, 0, 0] .* 1e4 # to convert to kcal / m² tabulated_longwave = - [10.4, 10.3, 10.3, 11.6, 15.1, 18.0, 19.1, 18.7, 16.5, 13.9, 11.2, 10.9] .* 1e4 # to convert to kcal / m² tabulated_sensible = - [1.18, 0.76, 0.72, 0.29, -0.45, -0.39, -0.30, -0.40, -0.17, 0.1, 0.56, 0.79] .* 1e4 # to convert to kcal / m² -tabulated_latent = - [ 0, -0.02, -0.03, -0.46, -0.70, -0.64, -0.66, -0.39, -0.19, -0.01, -0.01, -3.20] .* 1e4 # to convert to kcal / m² +tabulated_latent = - [ 0, -0.02, -0.03, -0.09, -0.46, -0.70, -0.64, -0.66, -0.39, -0.19, -0.01, -0.01] .* 1e4 # to convert to kcal / m² # Pretend every month is just 30 days Nmonths = 12 @@ -23,9 +23,11 @@ year_days = month_days * Nmonths times_days = 15:30:(year_days - 15) times = times_days .* day # times in seconds +albedo = 0.8 # Almost like in Semtner (1976) + # Convert fluxes to the right units kcal_to_joules = 4184 -tabulated_shortwave .*= kcal_to_joules / (month_days * days) +tabulated_shortwave .*= kcal_to_joules / (month_days * days) .* (1 - albedo) tabulated_longwave .*= kcal_to_joules / (month_days * days) tabulated_sensible .*= kcal_to_joules / (month_days * days) tabulated_latent .*= kcal_to_joules / (month_days * days) From c033058d6f8b67cd46234530ebecf81c515b1847 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 13 Mar 2025 13:05:51 +0100 Subject: [PATCH 055/108] muuuch better --- examples/arctic_basin_seasonal_cycle.jl | 66 ++++++++++++++----------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/examples/arctic_basin_seasonal_cycle.jl b/examples/arctic_basin_seasonal_cycle.jl index e3d278a9..5f04a45f 100644 --- a/examples/arctic_basin_seasonal_cycle.jl +++ b/examples/arctic_basin_seasonal_cycle.jl @@ -4,9 +4,12 @@ using Oceananigans using Oceananigans.Units -using Oceananigans.Fields: index_binary_search +using Oceananigans.Utils: Time using ClimaSeaIce +# Generate a 0D grid for a single column slab model +grid = RectilinearGrid(size=(), topology=(Flat, Flat, Flat)) + # Forcings (Semtner 1976, table 1), originally tabulated by Fletcher (1965) # Note: these are in kcal, which was deprecated in the ninth General Conference on Weights and # Measures in 1948. We convert these to Joules (and then to Watts) below. @@ -23,11 +26,9 @@ year_days = month_days * Nmonths times_days = 15:30:(year_days - 15) times = times_days .* day # times in seconds -albedo = 0.8 # Almost like in Semtner (1976) - # Convert fluxes to the right units kcal_to_joules = 4184 -tabulated_shortwave .*= kcal_to_joules / (month_days * days) .* (1 - albedo) +tabulated_shortwave .*= kcal_to_joules / (month_days * days) tabulated_longwave .*= kcal_to_joules / (month_days * days) tabulated_sensible .*= kcal_to_joules / (month_days * days) tabulated_latent .*= kcal_to_joules / (month_days * days) @@ -43,39 +44,43 @@ lines!(ax, times, tabulated_latent) display(fig) -@inline function linearly_interpolate_flux(i, j, grid, Tₛ, clock, model_fields, parameters) - times = parameters.times - Q = parameters.flux - Nt = length(times) - t = mod(clock.time, 360days) - n₁, n₂ = index_binary_search(times, t, Nt) +# Make them into a FieldTimeSeries for better manipulation - Q₁ = @inbounds Q[n₁] - Q₂ = @inbounds Q[n₂] +Rs = FieldTimeSeries{Nothing, Nothing, Nothing}(grid, times; time_indexing = Cyclical) +Rl = FieldTimeSeries{Nothing, Nothing, Nothing}(grid, times; time_indexing = Cyclical) +Qs = FieldTimeSeries{Nothing, Nothing, Nothing}(grid, times; time_indexing = Cyclical) +Ql = FieldTimeSeries{Nothing, Nothing, Nothing}(grid, times; time_indexing = Cyclical) - ñ = if n₁ == n₂ - n₁ - else - @inbounds (t - times[n₁]) * (n₂ - n₁) / (times[n₂] - times[n₁]) - end +for (i, time) in enumerate(times) + set!(Rs, tabulated_shortwave[i]) + set!(Rl, tabulated_longwave[i]) + set!(Qs, tabulated_sensible[i]) + set!(Ql, tabulated_latent[i]) +end - return Q₁ * (ñ - n₁) + Q₂ * (n₂ - ñ) +@inline function linearly_interpolate_flux(i, j, grid, Tₛ, clock, model_fields, flux) + t = Time(clock.time) + return flux[i, j, 1, t] end -Q_shortwave = FluxFunction(linearly_interpolate_flux, parameters=(; times, flux=tabulated_shortwave)) -Q_longwave = FluxFunction(linearly_interpolate_flux, parameters=(; times, flux=tabulated_longwave)) -Q_sensible = FluxFunction(linearly_interpolate_flux, parameters=(; times, flux=tabulated_sensible)) -Q_latent = FluxFunction(linearly_interpolate_flux, parameters=(; times, flux=tabulated_latent)) -Q_emission = RadiativeEmission() +@inline function linearly_interpolate_solar_flux(i, j, grid, Tₛ, clock, model_fields, flux) + Q = linearly_interpolate_flux(i, j, grid, Tₛ, clock, model_fields, flux) + α = ifelse(Tₛ < -0.1, 0.75, 0.64) + return Q * (1 - α) +end -# Generate a 0D grid for a single column slab model -grid = RectilinearGrid(size=(), topology=(Flat, Flat, Flat)) +Q_shortwave = FluxFunction(linearly_interpolate_solar_flux, parameters=Rs) +Q_longwave = FluxFunction(linearly_interpolate_flux, parameters=Rl) +Q_sensible = FluxFunction(linearly_interpolate_flux, parameters=Qs) +Q_latent = FluxFunction(linearly_interpolate_flux, parameters=Ql) +Q_emission = RadiativeEmission() top_heat_flux = (Q_shortwave, Q_longwave, Q_sensible, Q_latent, Q_emission) + model = SeaIceModel(grid; top_heat_flux) set!(model, h=0.3, ℵ=1) # We start from 300cm of ice and full concentration -simulation = Simulation(model, Δt=8hours, stop_time=4 * 360days) +simulation = Simulation(model, Δt=1hours, stop_time=2* 360days) # Accumulate data series = [] @@ -84,7 +89,9 @@ function accumulate_timeseries(sim) T = model.ice_thermodynamics.top_surface_temperature h = model.ice_thickness ℵ = model.ice_concentration - push!(series, (time(sim), first(h), first(T), first(ℵ))) + Qe = model.external_heat_fluxes.top + Qe = ClimaSeaIce.SeaIceThermodynamics.HeatBoundaryConditions.getflux(Qe, 1, 1, grid, first(T), model.clock, model.ice_thickness) + push!(series, (time(sim), first(h), first(T), first(ℵ), Qe)) end simulation.callbacks[:save] = Callback(accumulate_timeseries) @@ -97,18 +104,21 @@ t = [datum[1] for datum in series] h = [datum[2] for datum in series] T = [datum[3] for datum in series] ℵ = [datum[4] for datum in series] +Q = [datum[5] for datum in series] set_theme!(Theme(fontsize=24, linewidth=4)) -fig = Figure(size=(1000, 800)) +fig = Figure(size=(1000, 1200)) axT = Axis(fig[1, 1], xlabel="Time (days)", ylabel="Top temperature (ᵒC)") axh = Axis(fig[2, 1], xlabel="Time (days)", ylabel="Ice thickness (m)") axℵ = Axis(fig[3, 1], xlabel="Time (days)", ylabel="Ice concentration (-)") +axQ = Axis(fig[4, 1], xlabel="Time (days)", ylabel="Top heat flux (W/m²)") lines!(axT, t / day, T) lines!(axh, t / day, h) lines!(axℵ, t / day, ℵ) +lines!(axQ, t / day, Q) display(fig) From b85744561ddabdc4ffcb839ea012de3e971c6582 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 13 Mar 2025 13:08:20 +0100 Subject: [PATCH 056/108] much better --- examples/arctic_basin_seasonal_cycle.jl | 17 +++++++++-------- .../slab_thermodynamics_tendencies.jl | 4 ++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/examples/arctic_basin_seasonal_cycle.jl b/examples/arctic_basin_seasonal_cycle.jl index 5f04a45f..3a76c1e4 100644 --- a/examples/arctic_basin_seasonal_cycle.jl +++ b/examples/arctic_basin_seasonal_cycle.jl @@ -5,6 +5,7 @@ using Oceananigans using Oceananigans.Units using Oceananigans.Utils: Time +using Oceananigans.OutputReaders using ClimaSeaIce # Generate a 0D grid for a single column slab model @@ -46,16 +47,16 @@ display(fig) # Make them into a FieldTimeSeries for better manipulation -Rs = FieldTimeSeries{Nothing, Nothing, Nothing}(grid, times; time_indexing = Cyclical) -Rl = FieldTimeSeries{Nothing, Nothing, Nothing}(grid, times; time_indexing = Cyclical) -Qs = FieldTimeSeries{Nothing, Nothing, Nothing}(grid, times; time_indexing = Cyclical) -Ql = FieldTimeSeries{Nothing, Nothing, Nothing}(grid, times; time_indexing = Cyclical) +Rs = FieldTimeSeries{Nothing, Nothing, Nothing}(grid, times; time_indexing = Cyclical()) +Rl = FieldTimeSeries{Nothing, Nothing, Nothing}(grid, times; time_indexing = Cyclical()) +Qs = FieldTimeSeries{Nothing, Nothing, Nothing}(grid, times; time_indexing = Cyclical()) +Ql = FieldTimeSeries{Nothing, Nothing, Nothing}(grid, times; time_indexing = Cyclical()) for (i, time) in enumerate(times) - set!(Rs, tabulated_shortwave[i]) - set!(Rl, tabulated_longwave[i]) - set!(Qs, tabulated_sensible[i]) - set!(Ql, tabulated_latent[i]) + set!(Rs[i], tabulated_shortwave[i:i]) + set!(Rl[i], tabulated_longwave[i:i]) + set!(Qs[i], tabulated_sensible[i:i]) + set!(Ql[i], tabulated_latent[i:i]) end @inline function linearly_interpolate_flux(i, j, grid, Tₛ, clock, model_fields, flux) diff --git a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl index a3757526..82e004cf 100644 --- a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl +++ b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl @@ -1,4 +1,5 @@ using ClimaSeaIce.SeaIceThermodynamics.HeatBoundaryConditions: bottom_temperature, top_surface_temperature +using Oceananigans # Frazil ice formation @inline function thermodynamic_tendency(i, j, k, grid, @@ -38,6 +39,9 @@ using ClimaSeaIce.SeaIceThermodynamics.HeatBoundaryConditions: bottom_temperatur if consolidated_ice # slab is consolidated and has an independent surface temperature Tu⁻ = @inbounds Tu[i, j, k] Tuⁿ = top_surface_temperature(i, j, grid, top_heat_bc, Tu⁻, Qi, Qu, clock, model_fields) + # We ca the temperature with the melting temperature + Tuₘ = melting_temperature(liquidus, 0) + Tuⁿ = min(Tuⁿ, Tuₘ) else # slab is unconsolidated and does not have an independent surface temperature Tuⁿ = bottom_temperature(i, j, grid, bottom_heat_bc, liquidus) end From 1b05402cdf5201e9aae79aeae949345ec5cfb693 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 13 Mar 2025 13:10:00 +0100 Subject: [PATCH 057/108] fix examples --- examples/freezing_of_a_lake.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/freezing_of_a_lake.jl b/examples/freezing_of_a_lake.jl index 20b7338c..f81a8539 100644 --- a/examples/freezing_of_a_lake.jl +++ b/examples/freezing_of_a_lake.jl @@ -11,6 +11,7 @@ using Oceananigans using Oceananigans.Units using ClimaSeaIce +using ClimaSeaIce.SeaIceThermodynamics: latent_heat using CairoMakie # Generate a 1D grid for difference ice columns subject to different solar insolation From fdeab12b314d4e0490661fd9b1f6350d82e16edd Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 13 Mar 2025 13:13:22 +0100 Subject: [PATCH 058/108] add ice salinity --- src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl index 82e004cf..8c1264fa 100644 --- a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl +++ b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl @@ -26,6 +26,7 @@ using Oceananigans hᵢ = ice_thickness[i, j, k] hc = ice_consolidation_thickness[i, j, k] ℵᵢ = ice_concentration[i, j, k] + Sᵢ = model_fields.S[i, j, k] end @inbounds Tuᵢ = Tu[i, j, k] @@ -40,7 +41,7 @@ using Oceananigans Tu⁻ = @inbounds Tu[i, j, k] Tuⁿ = top_surface_temperature(i, j, grid, top_heat_bc, Tu⁻, Qi, Qu, clock, model_fields) # We ca the temperature with the melting temperature - Tuₘ = melting_temperature(liquidus, 0) + Tuₘ = melting_temperature(liquidus, Sᵢ) Tuⁿ = min(Tuⁿ, Tuₘ) else # slab is unconsolidated and does not have an independent surface temperature Tuⁿ = bottom_temperature(i, j, grid, bottom_heat_bc, liquidus) From a209c010edd81c2f0caab7dd76d79c4cf7c3174f Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 13 Mar 2025 13:33:57 +0100 Subject: [PATCH 059/108] ok this works --- examples/arctic_basin_seasonal_cycle.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/arctic_basin_seasonal_cycle.jl b/examples/arctic_basin_seasonal_cycle.jl index 3a76c1e4..3727ec07 100644 --- a/examples/arctic_basin_seasonal_cycle.jl +++ b/examples/arctic_basin_seasonal_cycle.jl @@ -27,10 +27,13 @@ year_days = month_days * Nmonths times_days = 15:30:(year_days - 15) times = times_days .* day # times in seconds +# Sea ice emissivity +ϵ = 1 + # Convert fluxes to the right units kcal_to_joules = 4184 tabulated_shortwave .*= kcal_to_joules / (month_days * days) -tabulated_longwave .*= kcal_to_joules / (month_days * days) +tabulated_longwave .*= kcal_to_joules / (month_days * days) .* ϵ tabulated_sensible .*= kcal_to_joules / (month_days * days) tabulated_latent .*= kcal_to_joules / (month_days * days) @@ -74,14 +77,14 @@ Q_shortwave = FluxFunction(linearly_interpolate_solar_flux, parameters=Rs) Q_longwave = FluxFunction(linearly_interpolate_flux, parameters=Rl) Q_sensible = FluxFunction(linearly_interpolate_flux, parameters=Qs) Q_latent = FluxFunction(linearly_interpolate_flux, parameters=Ql) -Q_emission = RadiativeEmission() +Q_emission = RadiativeEmission(emissivity=ϵ) top_heat_flux = (Q_shortwave, Q_longwave, Q_sensible, Q_latent, Q_emission) model = SeaIceModel(grid; top_heat_flux) set!(model, h=0.3, ℵ=1) # We start from 300cm of ice and full concentration -simulation = Simulation(model, Δt=1hours, stop_time=2* 360days) +simulation = Simulation(model, Δt=8hours, stop_time= 30 * 360days) # Accumulate data series = [] From bc167716874dd4ee5b3dd217268875729de7f9a3 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 13 Mar 2025 13:43:09 +0100 Subject: [PATCH 060/108] small fix --- examples/arctic_basin_seasonal_cycle.jl | 6 +++++- .../HeatBoundaryConditions/boundary_fluxes.jl | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/arctic_basin_seasonal_cycle.jl b/examples/arctic_basin_seasonal_cycle.jl index 3727ec07..d890afb5 100644 --- a/examples/arctic_basin_seasonal_cycle.jl +++ b/examples/arctic_basin_seasonal_cycle.jl @@ -73,11 +73,15 @@ end return Q * (1 - α) end +# NOTE: Semtner (1976) uses a wrong value for the Stefan-Boltzmann constant (roughly 2% higher) to match +# the results of Maykut & Unterstainer (196(5)). We use the same value here for comparison purposes. +σ = 5.67e-8 * 1.02 # Wrong value!! + Q_shortwave = FluxFunction(linearly_interpolate_solar_flux, parameters=Rs) Q_longwave = FluxFunction(linearly_interpolate_flux, parameters=Rl) Q_sensible = FluxFunction(linearly_interpolate_flux, parameters=Qs) Q_latent = FluxFunction(linearly_interpolate_flux, parameters=Ql) -Q_emission = RadiativeEmission(emissivity=ϵ) +Q_emission = RadiativeEmission(emissivity=ϵ, stefan_boltzmann_constant=σ) top_heat_flux = (Q_shortwave, Q_longwave, Q_sensible, Q_latent, Q_emission) diff --git a/src/SeaIceThermodynamics/HeatBoundaryConditions/boundary_fluxes.jl b/src/SeaIceThermodynamics/HeatBoundaryConditions/boundary_fluxes.jl index 93d5b34a..e1eb5544 100644 --- a/src/SeaIceThermodynamics/HeatBoundaryConditions/boundary_fluxes.jl +++ b/src/SeaIceThermodynamics/HeatBoundaryConditions/boundary_fluxes.jl @@ -108,7 +108,7 @@ end ϵ = emission.emissivity σ = emission.stefan_boltzmann_constant Tᵣ = emission.reference_temperature - return ϵ * σ * (T + Tᵣ)^4 + return ϵ * σ * (T + Tᵣ)^4 end function Base.summary(flux::RadiativeEmission) From 6cb53a5bd1acfdfc6bc2f9b4b43cd2b2d74ab67c Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 20 Mar 2025 08:24:55 +0100 Subject: [PATCH 061/108] cairomakie --- examples/arctic_basin_seasonal_cycle.jl | 2 +- examples/diffusive_ice_column_model.jl | 2 +- examples/perpetual_night.jl | 2 +- validation/ice_ocean_model/cooling_then_warming_ocean.jl | 2 +- validation/ice_ocean_model/melting_baroclinicity.jl | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/arctic_basin_seasonal_cycle.jl b/examples/arctic_basin_seasonal_cycle.jl index d890afb5..bd508d3f 100644 --- a/examples/arctic_basin_seasonal_cycle.jl +++ b/examples/arctic_basin_seasonal_cycle.jl @@ -37,7 +37,7 @@ tabulated_longwave .*= kcal_to_joules / (month_days * days) .* ϵ tabulated_sensible .*= kcal_to_joules / (month_days * days) tabulated_latent .*= kcal_to_joules / (month_days * days) -using GLMakie +using CairoMakie fig = Figure() ax = Axis(fig[1, 1]) diff --git a/examples/diffusive_ice_column_model.jl b/examples/diffusive_ice_column_model.jl index 18e399b8..93a298b5 100644 --- a/examples/diffusive_ice_column_model.jl +++ b/examples/diffusive_ice_column_model.jl @@ -3,7 +3,7 @@ using Oceananigans using Oceananigans.Units using Oceananigans.Operators: Δzᶜᶜᶠ using Oceananigans.Grids: znode -using GLMakie +using CairoMakie ##### ##### Set up a EnthalpyMethodSeaIceModel diff --git a/examples/perpetual_night.jl b/examples/perpetual_night.jl index d797569e..6c004670 100644 --- a/examples/perpetual_night.jl +++ b/examples/perpetual_night.jl @@ -2,7 +2,7 @@ using Oceananigans using Oceananigans.Units using ClimaSeaIce using ClimaSeaIce.HeatBoundaryConditions: RadiativeEmission -using GLMakie +using CairoMakie # Generate a zero-dimensional grid for a single column slab model grid = RectilinearGrid(size=(), topology=(Flat, Flat, Flat)) diff --git a/validation/ice_ocean_model/cooling_then_warming_ocean.jl b/validation/ice_ocean_model/cooling_then_warming_ocean.jl index a4579401..0c09aaee 100644 --- a/validation/ice_ocean_model/cooling_then_warming_ocean.jl +++ b/validation/ice_ocean_model/cooling_then_warming_ocean.jl @@ -6,7 +6,7 @@ using ClimaSeaIce using ClimaSeaIce.SeaIceThermodynamics: melting_temperature using SeawaterPolynomials: TEOS10EquationOfState using ClimaSeaIce.HeatBoundaryConditions: RadiativeEmission, IceWaterThermalEquilibrium -using GLMakie +using CairoMakie import Oceananigans.Simulations: time_step!, time diff --git a/validation/ice_ocean_model/melting_baroclinicity.jl b/validation/ice_ocean_model/melting_baroclinicity.jl index 405a89f6..02cc97ae 100644 --- a/validation/ice_ocean_model/melting_baroclinicity.jl +++ b/validation/ice_ocean_model/melting_baroclinicity.jl @@ -12,7 +12,7 @@ using ClimaSeaIce: melting_temperature using ClimaSeaIce.HeatBoundaryConditions: RadiativeEmission, IceWaterThermalEquilibrium using Printf -using GLMakie +using CairoMakie using Statistics include("ice_ocean_model.jl") From 6414056700f5d63adf1c5d9f593262267c1d90c3 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 20 Mar 2025 09:48:41 +0100 Subject: [PATCH 062/108] fix examples --- examples/ice_advected_by_anticyclone.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/ice_advected_by_anticyclone.jl b/examples/ice_advected_by_anticyclone.jl index ce2b1a7e..0b35fddc 100644 --- a/examples/ice_advected_by_anticyclone.jl +++ b/examples/ice_advected_by_anticyclone.jl @@ -139,10 +139,10 @@ u, v = model.velocities outputs = (; h, u, v, ℵ) -simulation.output_writers[:sea_ice] = JLD2OutputWriter(model, outputs; - filename = "sea_ice_advected_by_anticyclone.jld2", - schedule = IterationInterval(5), - overwrite_existing = true) +simulation.output_writers[:sea_ice] = JLD2Writer(model, outputs; + filename = "sea_ice_advected_by_anticyclone.jld2", + schedule = IterationInterval(5), + overwrite_existing = true) wall_time = [time_ns()] From 07656ab361f7c20b0a9de27c3c60d131f2702b2b Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 20 Mar 2025 11:34:50 +0100 Subject: [PATCH 063/108] energy budget --- examples/freezing_of_a_lake.jl | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/examples/freezing_of_a_lake.jl b/examples/freezing_of_a_lake.jl index f81a8539..a0c08775 100644 --- a/examples/freezing_of_a_lake.jl +++ b/examples/freezing_of_a_lake.jl @@ -142,13 +142,12 @@ function accumulate_energy(sim) T = sim.model.ice_thermodynamics.top_surface_temperature h = sim.model.ice_thickness ℵ = sim.model.ice_concentration - ρ = sim.model.ice_density[1, 1, 1] - c = sim.model.ice_thermodynamics.phase_transitions.ice_heat_capacity PT = sim.model.ice_thermodynamics.phase_transitions - - ℰ = latent_heat.(Ref(PT), T) + Tb = 0 + + ℰ = latent_heat.(Ref(PT), (T .+ Tb) / 2) - push!(Ei, deepcopy(@. h * ℵ * ρ * c * (T + 273.15) - ℰ)) + push!(Ei, deepcopy(@. h * ℵ * ℰ)) push!(Qa, deepcopy(atmosphere.atmosphere_ice_flux)) push!(Ql, deepcopy(lake.lake_ice_flux)) end @@ -231,13 +230,13 @@ fig = Figure(size=(1000, 900)) axE = Axis(fig[1, 1], xlabel="Time (days)", ylabel="Sea Ice energy (J)") axA = Axis(fig[2, 1], xlabel="Time (days)", ylabel="Atmosphere HF") axL = Axis(fig[3, 1], xlabel="Time (days)", ylabel="Lake HF") -axB = Axis(fig[4, 1], xlabel="Time (days)", ylabel="Heat budget", yscale=log10) +axB = Axis(fig[4, 1], xlabel="Time (days)", ylabel="Heat budget") pEt1 = (Ei1[2:end] - Ei1[1:end-1]) ./ 10minutes pEt2 = (Ei2[2:end] - Ei2[1:end-1]) ./ 10minutes pEt3 = (Ei3[2:end] - Ei3[1:end-1]) ./ 10minutes pEt4 = (Ei4[2:end] - Ei4[1:end-1]) ./ 10minutes -thalf = (t[2:end] .+ t[1:end-1]) ./ 2 +thalf = t[2:end] lines!(axE, t / day, Ei1) lines!(axA, t / day, Qa1) @@ -256,7 +255,7 @@ lines!(axA, t / day, Qa4) lines!(axL, t / day, Ql4) lines!(axB, thalf / day, pEt1) -lines!(axB, t / day, 2 .* (Qa1 - Ql1)) +lines!(axB, t / day, Qa1 - Ql1) -save("freezing_in_winter.png", fig) +save("energy_budget.png", fig) nothing # hide \ No newline at end of file From e95488b285d791143eb1519301f2f1ae8d4e78ad Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 20 Mar 2025 11:39:42 +0100 Subject: [PATCH 064/108] add an energy budgets --- examples/freezing_of_a_lake.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/freezing_of_a_lake.jl b/examples/freezing_of_a_lake.jl index a0c08775..3e7aea83 100644 --- a/examples/freezing_of_a_lake.jl +++ b/examples/freezing_of_a_lake.jl @@ -145,7 +145,7 @@ function accumulate_energy(sim) PT = sim.model.ice_thermodynamics.phase_transitions Tb = 0 - ℰ = latent_heat.(Ref(PT), (T .+ Tb) / 2) + ℰ = latent_heat.(Ref(PT), T) push!(Ei, deepcopy(@. h * ℵ * ℰ)) push!(Qa, deepcopy(atmosphere.atmosphere_ice_flux)) @@ -236,7 +236,7 @@ pEt1 = (Ei1[2:end] - Ei1[1:end-1]) ./ 10minutes pEt2 = (Ei2[2:end] - Ei2[1:end-1]) ./ 10minutes pEt3 = (Ei3[2:end] - Ei3[1:end-1]) ./ 10minutes pEt4 = (Ei4[2:end] - Ei4[1:end-1]) ./ 10minutes -thalf = t[2:end] +tpE = t[2:end] lines!(axE, t / day, Ei1) lines!(axA, t / day, Qa1) @@ -254,8 +254,8 @@ lines!(axE, t / day, Ei4) lines!(axA, t / day, Qa4) lines!(axL, t / day, Ql4) -lines!(axB, thalf / day, pEt1) -lines!(axB, t / day, Qa1 - Ql1) +lines!(axB, tpE / day, pEt1) +lines!(axB, t / day, Qa1 - Ql1) save("energy_budget.png", fig) nothing # hide \ No newline at end of file From cf5a04bbe2b33b32c7b1be9fe7da22d4eb0600b8 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Mon, 24 Mar 2025 13:18:37 +0100 Subject: [PATCH 065/108] Update src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl Co-authored-by: Gregory L. Wagner --- src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl index 8c1264fa..58377a78 100644 --- a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl +++ b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl @@ -40,7 +40,7 @@ using Oceananigans if consolidated_ice # slab is consolidated and has an independent surface temperature Tu⁻ = @inbounds Tu[i, j, k] Tuⁿ = top_surface_temperature(i, j, grid, top_heat_bc, Tu⁻, Qi, Qu, clock, model_fields) - # We ca the temperature with the melting temperature + # We cap by melting temperature Tuₘ = melting_temperature(liquidus, Sᵢ) Tuⁿ = min(Tuⁿ, Tuₘ) else # slab is unconsolidated and does not have an independent surface temperature From bde6e0475a054e62b7537a2a3ddea968305019b3 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 25 Mar 2025 16:46:13 +0100 Subject: [PATCH 066/108] this should work --- .../thermodynamic_time_step.jl | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index 4108aced..ef6d7121 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -70,33 +70,19 @@ end # We recalculate the actual volume derivative, after accounting for the # volume adjustment (the ice cannot produce more melt than its actual volume!) ∂t_V = (Vⁿ⁺¹ - hⁿ * ℵⁿ) / Δt + freezing = ∂t_V ≥ 0 - # There are 6 typical cases depending on the sign of ∂t_V, ℵⁿ and hⁿ - # - # - ∂t_V ≥ 0 : the ice is growing * easy case to handle * - # ├── ℵⁿ == 0 -> new ice is forming (we only increase ℵⁿ, post increase ridging will adjust h) - # └── ℵⁿ > 0 -> ice is growing (we increase both ℵ and h based on ℵⁿ) - # - # - ∂t_V < 0 : the ice is melting * tricky case to handle * - # ├── ℵⁿ == 0 -> not possible! (there is no ice to begin with) - # ├── h > hᶜ -> ice is melting (we decrease both ℵ and h based on ℵⁿ) - # └── h ≤ hᶜ -> unconsolidated ice is melting (we only decrease ℵⁿ) - - freezing = ∂t_V > 0 - consolidated = hⁿ ≥ hᶜ - open_ocean = ℵⁿ == 0 + # The lateral vs vertical growth is parameterized as in Hibler 1979. + ∂t_ℵᶠ = (1 - ℵⁿ) * ∂t_V / hᶜ * freezing + ∂t_ℵᵐ = ℵⁿ * min(∂t_V, zero(ℵⁿ)) / 2hⁿ * !(freezing) # Freezing and melting cases: - hᶠ = hⁿ + Δt * ∂t_V * ℵⁿ * !open_ocean - hᵐ = hⁿ + Δt * ∂t_V * ℵⁿ * consolidated - hᵐ = max(hᵐ, zero(hᵐ)) - h⁺ = ifelse(freezing, hᶠ, hᵐ) + ℵ⁺ = ℵⁿ + Δt * (∂t_ℵᶠ + ∂t_ℵᵐ) + h⁺ = Vⁿ⁺¹ / ℵ⁺ + + ℵ⁺ = max(zero(ℵ⁺), ℵ⁺) + h⁺ = ifelse(ℵ⁺ ≤ 0, zero(h⁺), h⁺) - # There is also a very particular case when the ice is nucleating corresponding - # h⁺ == 0 and freezing. In this case, we set h = hᶜ and advance ℵ accordingly - h⁺ = ifelse(freezing & (h⁺ == 0), hᶜ, h⁺) - ℵ⁺ = Vⁿ⁺¹ / h⁺ - # No volume change ℵ⁺ = ifelse(∂t_V == 0, ℵⁿ, ℵ⁺) h⁺ = ifelse(∂t_V == 0, hⁿ, h⁺) From 2d3b1c78ec004a4daf31f018839bf0c66327ec01 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 25 Mar 2025 17:58:23 +0100 Subject: [PATCH 067/108] add a change to conc --- src/SeaIceThermodynamics/thermodynamic_time_step.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index ef6d7121..c087df6f 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -86,6 +86,7 @@ end # No volume change ℵ⁺ = ifelse(∂t_V == 0, ℵⁿ, ℵ⁺) h⁺ = ifelse(∂t_V == 0, hⁿ, h⁺) + ℵ⁺ = ifelse(h⁺ == 0, zero(ℵ⁺), ℵ⁺) # Ridging caused by the thermodynamic step @inbounds ice_concentration[i, j, 1] = ifelse(ℵ⁺ > 1, one(ℵ⁺), ℵ⁺) From 946fca4340673c3a374b6192235914fc32971a66 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 10:00:39 +0100 Subject: [PATCH 068/108] remove ice salinity from there --- src/sea_ice_model.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sea_ice_model.jl b/src/sea_ice_model.jl index a82866d9..ee98bc95 100644 --- a/src/sea_ice_model.jl +++ b/src/sea_ice_model.jl @@ -19,7 +19,6 @@ struct SeaIceModel{GR, TD, D, TS, CL, U, T, IT, IC, ID, CT, STF, A, F, Arch} <: tracers :: T ice_thickness :: IT ice_concentration :: IC - ice_salinity :: IS ice_density :: ID ice_consolidation_thickness :: CT # Thermodynamics From 13a69d0f2ad066328a222c29591877dce5be9a93 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 10:02:16 +0100 Subject: [PATCH 069/108] fix the examples --- examples/freezing_bucket.jl | 6 +++--- examples/ice_advected_on_coastline.jl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/freezing_bucket.jl b/examples/freezing_bucket.jl index d5864a5c..d69554cf 100644 --- a/examples/freezing_bucket.jl +++ b/examples/freezing_bucket.jl @@ -53,9 +53,9 @@ top_heat_boundary_condition = PrescribedTemperature(-10) # slab sea ice representation of ice_thermodynamics ice_thermodynamics = SlabSeaIceThermodynamics(grid; - internal_heat_flux, - phase_transitions, - top_heat_boundary_condition) + internal_heat_flux, + phase_transitions, + top_heat_boundary_condition) # We also prescribe a frazil ice heat flux that stops when the ice has reached a concentration of 1. # This heat flux represents the initial ice formation from a liquid bucket. diff --git a/examples/ice_advected_on_coastline.jl b/examples/ice_advected_on_coastline.jl index 20da1e7f..34db20aa 100644 --- a/examples/ice_advected_on_coastline.jl +++ b/examples/ice_advected_on_coastline.jl @@ -68,7 +68,7 @@ dynamics = SeaIceMomentumEquation(grid; model = SeaIceModel(grid; advection = WENO(order=7), dynamics = dynamics, - thermodynamics = nothing) + ice_thermodynamics = nothing) # We start with a concentration of ℵ = 1 everywhere set!(model, h = 1) From b41e3384088aa336db2c195ff1d908157022bd1d Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 13:16:05 +0100 Subject: [PATCH 070/108] improve --- src/Rheologies/elasto_visco_plastic_rheology.jl | 6 ++---- src/Rheologies/ice_stress_divergence.jl | 10 +++++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index 9e0771f0..7459f860 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -94,7 +94,7 @@ function rheology_auxiliary_fields(r::ElastoViscoPlasticRheology, grid) # TODO: What about boundary conditions? σ₁₁ = Field{Center, Center, Nothing}(grid) σ₂₂ = Field{Center, Center, Nothing}(grid) - σ₁₂ = Field{Face, Face, Nothing}(grid) + σ₁₂ = Field{Center, Center, Nothing}(grid) P = Field{Center, Center, Nothing}(grid) ζ = Field{Center, Center, Nothing}(grid) @@ -177,9 +177,7 @@ end @inline strain_rate_xx(i, j, k, grid, u, v) = ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, u) / Azᶜᶜᶜ(i, j, k, grid) @inline strain_rate_yy(i, j, k, grid, u, v) = ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, v) / Azᶜᶜᶜ(i, j, k, grid) -@inline strain_rate_xy(i, j, k, grid, u, v) = - (ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, v) + - ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, u)) / Azᶜᶜᶜ(i, j, k, grid) / 2 +@inline strain_rate_xy(i, j, k, grid, u, v) = (ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, v) + ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, u)) / Azᶜᶜᶜ(i, j, k, grid) / 2 @kernel function _compute_evp_viscosities!(fields, grid, rheology, u, v, h, ℵ, ρᵢ, Δt) i, j = @index(Global, NTuple) diff --git a/src/Rheologies/ice_stress_divergence.jl b/src/Rheologies/ice_stress_divergence.jl index 75506449..98bee8e4 100644 --- a/src/Rheologies/ice_stress_divergence.jl +++ b/src/Rheologies/ice_stress_divergence.jl @@ -29,17 +29,17 @@ const f = Face() ##### @inline function ∂ⱼ_σ₁ⱼ(i, j, k, grid, rheology, clock, fields) - return 1 / Azᶠᶠᶜ(i, j, k, grid) * (δxᶠᵃᵃ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_ux, rheology, clock, fields) + - δyᵃᶠᵃ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_uy, rheology, clock, fields)) + return 1 / Azᶠᶠᶜ(i, j, k, grid) * (δxᶠᶠᶜ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_ux, rheology, clock, fields) + + δyᶠᶠᶜ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_uy, rheology, clock, fields)) end @inline function ∂ⱼ_σ₂ⱼ(i, j, k, grid, rheology, clock, fields) - return 1 / Azᶠᶠᶜ(i, j, k, grid) * (δxᶠᵃᵃ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_vx, rheology, clock, fields) + - δyᵃᶠᵃ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_vy, rheology, clock, fields)) + return 1 / Azᶠᶠᶜ(i, j, k, grid) * (δxᶠᶠᶜ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_vx, rheology, clock, fields) + + δyᶠᶠᶜ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_vy, rheology, clock, fields)) end ##### -##### Immersed Stress divergence +##### Immersed Stress divergence (Nothing on B-grid!) ##### @inline immersed_∂ⱼ_σ₁ⱼ(i, j, k, grid, args...) = zero(grid) From a0bc65f920ca1923ac06fde9cb4468d50e3c97a6 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 13:17:04 +0100 Subject: [PATCH 071/108] change name --- src/sea_ice_advection.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sea_ice_advection.jl b/src/sea_ice_advection.jl index 0a37d4f9..945d3fff 100644 --- a/src/sea_ice_advection.jl +++ b/src/sea_ice_advection.jl @@ -17,13 +17,13 @@ using Oceananigans.Advection: FluxFormAdvection, using Oceananigans.Advection: _biased_interpolate_xᶠᵃᵃ, _biased_interpolate_yᵃᶠᵃ, bias -@inline function advective_new_tracer_flux_x(i, j, k, grid, advection, u, c) +@inline function advective_bgrid_tracer_flux_x(i, j, k, grid, advection, u, c) ũ = ℑyᵃᶜᵃ(i, j, k, grid, u) cᴿ = _biased_interpolate_xᶠᵃᵃ(i, j, k, grid, advection, bias(ũ), c) return Axᶠᶜᶜ(i, j, k, grid) * ũ * cᴿ end -@inline function advective_new_tracer_flux_y(i, j, k, grid, advection, v, c) +@inline function advective_bgrid_tracer_flux_y(i, j, k, grid, advection, v, c) ṽ = ℑxᶜᵃᵃ(i, j, k, grid, v) cᴿ = _biased_interpolate_yᵃᶠᵃ(i, j, k, grid, advection, bias(ṽ), c) return Ayᶜᶠᶜ(i, j, k, grid) * ṽ * cᴿ @@ -31,10 +31,10 @@ end @inline horizontal_div_Uc(i, j, k, grid, ::Nothing, U, c) = zero(grid) @inline horizontal_div_Uc(i, j, k, grid, advection, U, c) = - 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, advective_new_tracer_flux_x, advection, U.u, c) + - δyᵃᶜᵃ(i, j, k, grid, advective_new_tracer_flux_y, advection, U.v, c)) + 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, advective_bgrid_tracer_flux_x, advection, U.u, c) + + δyᵃᶜᵃ(i, j, k, grid, advective_bgrid_tracer_flux_y, advection, U.v, c)) @inline horizontal_div_Uc(i, j, k, grid, advection::FluxFormAdvection, U, c) = - 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, advective_new_tracer_flux_x, advection.x, U.u, c) + - δyᵃᶜᵃ(i, j, k, grid, advective_new_tracer_flux_y, advection.y, U.v, c)) + 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, advective_bgrid_tracer_flux_x, advection.x, U.u, c) + + δyᵃᶜᵃ(i, j, k, grid, advective_bgrid_tracer_flux_y, advection.y, U.v, c)) From 855b721577e8078afe68098e07702c03a47fa3db Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 13:18:52 +0100 Subject: [PATCH 072/108] try it out --- src/sea_ice_model.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sea_ice_model.jl b/src/sea_ice_model.jl index ee98bc95..d1a3dc89 100644 --- a/src/sea_ice_model.jl +++ b/src/sea_ice_model.jl @@ -9,7 +9,7 @@ using Oceananigans.Forcings: model_forcing using ClimaSeaIce.SeaIceThermodynamics.HeatBoundaryConditions: flux_summary using ClimaSeaIce.Rheologies: rheology_prognostic_tracers -struct SeaIceModel{GR, TD, D, TS, CL, U, T, IT, IC, ID, CT, STF, A, F, Arch} <: AbstractModel{TS, Arch} +struct SeaIceModel{GR, TD, D, TS, CL, U, T, IT, IC, ID, IS, CT, STF, A, F, Arch} <: AbstractModel{TS, Arch} architecture :: Arch grid :: GR clock :: CL @@ -20,6 +20,7 @@ struct SeaIceModel{GR, TD, D, TS, CL, U, T, IT, IC, ID, CT, STF, A, F, Arch} <: ice_thickness :: IT ice_concentration :: IC ice_density :: ID + ice_salinity :: IS ice_consolidation_thickness :: CT # Thermodynamics thermodynamics :: TD @@ -129,8 +130,8 @@ function SeaIceModel(grid; tracers, ice_thickness, ice_concentration, - ice_salinity, ice_density, + ice_salinity, ice_consolidation_thickness, ice_thermodynamics, dynamics, From aeab0f44d675207bf0af370278251dc4337ab60c Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 13:23:37 +0100 Subject: [PATCH 073/108] fixes --- examples/freezing_bucket.jl | 2 +- src/sea_ice_model.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/freezing_bucket.jl b/examples/freezing_bucket.jl index d69554cf..102b0ae3 100644 --- a/examples/freezing_bucket.jl +++ b/examples/freezing_bucket.jl @@ -71,7 +71,7 @@ model = SeaIceModel(grid; ice_thermodynamics, bottom_heat_flux) # Note that the default bottom heat boundary condition for `SlabSeaIceThermodynamics` is # `IceWaterThermalEquilibrium` with freshwater. That's what we want! -model.thermodynamics.heat_boundary_conditions.bottom +model.ice_thermodynamics.heat_boundary_conditions.bottom # Ok, we're ready to freeze the bucket for 10 straight days. # The ice will start forming suddenly due to the frazil ice heat flux and then eventually diff --git a/src/sea_ice_model.jl b/src/sea_ice_model.jl index d1a3dc89..8dd6261e 100644 --- a/src/sea_ice_model.jl +++ b/src/sea_ice_model.jl @@ -23,7 +23,7 @@ struct SeaIceModel{GR, TD, D, TS, CL, U, T, IT, IC, ID, IS, CT, STF, A, F, Arch} ice_salinity :: IS ice_consolidation_thickness :: CT # Thermodynamics - thermodynamics :: TD + ice_thermodynamics :: TD # Dynamics dynamics :: D # External boundary conditions From fac6c5e77c90ac22208219c9bb9557ce92405ee7 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 13:24:26 +0100 Subject: [PATCH 074/108] step tracers --- src/sea_ice_time_stepping.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sea_ice_time_stepping.jl b/src/sea_ice_time_stepping.jl index 91059e2d..f0bf6910 100644 --- a/src/sea_ice_time_stepping.jl +++ b/src/sea_ice_time_stepping.jl @@ -44,7 +44,6 @@ function dynamic_time_step!(model::SIM, Δt) Gⁿ = model.timestepper.Gⁿ - launch!(arch, grid, :xy, _step_tracers!, h, ℵ, tracers, Gⁿ, Δt) launch!(arch, grid, :xy, _dynamic_step_tracers!, h, ℵ, tracers, Gⁿ, Δt) return nothing From 589bdefe4fef836ad9c7fa106dc9b87f5630dca3 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 13:44:59 +0100 Subject: [PATCH 075/108] adding ice_salinity --- src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl | 3 ++- src/SeaIceThermodynamics/thermodynamic_time_step.jl | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl index 58377a78..062cdaa2 100644 --- a/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl +++ b/src/SeaIceThermodynamics/slab_thermodynamics_tendencies.jl @@ -7,6 +7,7 @@ using Oceananigans ice_thickness, ice_concentration, ice_consolidation_thickness, + ice_salinity, top_external_heat_flux, bottom_external_heat_flux, clock, model_fields) @@ -26,7 +27,7 @@ using Oceananigans hᵢ = ice_thickness[i, j, k] hc = ice_consolidation_thickness[i, j, k] ℵᵢ = ice_concentration[i, j, k] - Sᵢ = model_fields.S[i, j, k] + Sᵢ = ice_salinity[i, j, k] end @inbounds Tuᵢ = Tu[i, j, k] diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index f79f5eaa..63f39856 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -15,6 +15,7 @@ function thermodynamic_time_step!(model, ::SlabSeaIceThermodynamics, Δt) grid, Δt, model.clock, model.ice_consolidation_thickness, + model.ice_salinity, model.ice_thermodynamics, model.external_heat_fluxes.top, model.external_heat_fluxes.bottom, @@ -40,6 +41,7 @@ end Δt, clock, ice_consolidation_thickness, + ice_salinity, ice_thermodynamics, top_external_heat_flux, bottom_external_heat_flux, @@ -57,6 +59,7 @@ end ice_thickness, ice_concentration, ice_consolidation_thickness, + ice_salinity, top_external_heat_flux, bottom_external_heat_flux, clock, model_fields) From 7ae099f4c1eeae3949d33b95256064217c3b25c1 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 14:14:56 +0100 Subject: [PATCH 076/108] take off the salinity from here --- src/SeaIceThermodynamics/thermodynamic_time_step.jl | 2 +- src/sea_ice_model.jl | 3 +-- src/sea_ice_time_stepping.jl | 8 +++++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index 63f39856..b438aff3 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -15,7 +15,7 @@ function thermodynamic_time_step!(model, ::SlabSeaIceThermodynamics, Δt) grid, Δt, model.clock, model.ice_consolidation_thickness, - model.ice_salinity, + model.tracers.S, model.ice_thermodynamics, model.external_heat_fluxes.top, model.external_heat_fluxes.bottom, diff --git a/src/sea_ice_model.jl b/src/sea_ice_model.jl index 8dd6261e..66fb3163 100644 --- a/src/sea_ice_model.jl +++ b/src/sea_ice_model.jl @@ -20,7 +20,6 @@ struct SeaIceModel{GR, TD, D, TS, CL, U, T, IT, IC, ID, IS, CT, STF, A, F, Arch} ice_thickness :: IT ice_concentration :: IC ice_density :: ID - ice_salinity :: IS ice_consolidation_thickness :: CT # Thermodynamics ice_thermodynamics :: TD @@ -83,6 +82,7 @@ function SeaIceModel(grid; # Wrap ice_salinity in a field ice_salinity = field((Center, Center, Nothing), ice_salinity, grid) ice_density = field((Center, Center, Nothing), ice_density, grid) + tracers = merge(tracers, (; S = ice_salinity)) # Construct prognostic fields if not provided ice_thickness = Field{Center, Center, Nothing}(grid, boundary_conditions=boundary_conditions.h) @@ -131,7 +131,6 @@ function SeaIceModel(grid; ice_thickness, ice_concentration, ice_density, - ice_salinity, ice_consolidation_thickness, ice_thermodynamics, dynamics, diff --git a/src/sea_ice_time_stepping.jl b/src/sea_ice_time_stepping.jl index f0bf6910..793b4195 100644 --- a/src/sea_ice_time_stepping.jl +++ b/src/sea_ice_time_stepping.jl @@ -83,10 +83,16 @@ advance_tracers!(::EmptyTuples, args...) = nothing function advance_tracers!(tracers, i, j, k, G, Δt) # Assumption! The tracer tendencies are the first ones for n in eachindex(tracers) - @inbounds tracers[n][i, j, 1] += Δt * G[n][i, j, k] + _advance_tracer!(tracers[n], i, j, k, G[n], Δt) end end +_advance_tracer!(tracer, i, j, k, G, Δt) = @inbounds tracer[i, j, 1] += Δt * G[i, j, k] +_advance_tracer!(::ConstantField, i, j, k, G, Δt) = nothing +_advance_tracer!(::ZeroField, i, j, k, G, Δt) = nothing +_advance_tracer!(::OneField, i, j, k, G, Δt) = nothing +_advance_tracer!(::Nothing, i, j, k, G, Δt) = nothing + function update_state!(model::SIM) foreach(prognostic_fields(model)) do field From 7ada6a487a2ef61f50d4b7a9ace436c78177c267 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 14:41:46 +0100 Subject: [PATCH 077/108] this should work --- src/sea_ice_time_stepping.jl | 8 ++------ src/tracer_tendency_kernel_functions.jl | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sea_ice_time_stepping.jl b/src/sea_ice_time_stepping.jl index 793b4195..7c7da257 100644 --- a/src/sea_ice_time_stepping.jl +++ b/src/sea_ice_time_stepping.jl @@ -1,5 +1,5 @@ using Oceananigans.Utils: Time -using Oceananigans.Fields: flattened_unique_values +using Oceananigans.Fields: flattened_unique_values, ConstantField, ZeroField using Oceananigans.OutputReaders: extract_field_time_series, update_field_time_series! using Oceananigans.ImmersedBoundaries: mask_immersed_field_xy! @@ -82,16 +82,12 @@ advance_tracers!(::EmptyTuples, args...) = nothing function advance_tracers!(tracers, i, j, k, G, Δt) # Assumption! The tracer tendencies are the first ones - for n in eachindex(tracers) + for n in eachindex(G) _advance_tracer!(tracers[n], i, j, k, G[n], Δt) end end _advance_tracer!(tracer, i, j, k, G, Δt) = @inbounds tracer[i, j, 1] += Δt * G[i, j, k] -_advance_tracer!(::ConstantField, i, j, k, G, Δt) = nothing -_advance_tracer!(::ZeroField, i, j, k, G, Δt) = nothing -_advance_tracer!(::OneField, i, j, k, G, Δt) = nothing -_advance_tracer!(::Nothing, i, j, k, G, Δt) = nothing function update_state!(model::SIM) diff --git a/src/tracer_tendency_kernel_functions.jl b/src/tracer_tendency_kernel_functions.jl index fb71ae44..c7864a8c 100644 --- a/src/tracer_tendency_kernel_functions.jl +++ b/src/tracer_tendency_kernel_functions.jl @@ -48,7 +48,7 @@ compute_tracer_tendencies!(G, i, j, grid, advection, velocities, ::EmptyTuples) function compute_tracer_tendencies!(G, i, j, grid, advection, velocities, tracers) # Assumption! The tracer tendencies are the first ones - for n in eachindex(tracers) + for n in eachindex(G) @inbounds G[n][i, j, 1] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, tracers[n]) end end From 7bd550f75b1fdb9cc8430cd1ef8ed58e7262b334 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 14:50:49 +0100 Subject: [PATCH 078/108] merge here --- src/sea_ice_model.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sea_ice_model.jl b/src/sea_ice_model.jl index 66fb3163..59cfc036 100644 --- a/src/sea_ice_model.jl +++ b/src/sea_ice_model.jl @@ -82,7 +82,6 @@ function SeaIceModel(grid; # Wrap ice_salinity in a field ice_salinity = field((Center, Center, Nothing), ice_salinity, grid) ice_density = field((Center, Center, Nothing), ice_density, grid) - tracers = merge(tracers, (; S = ice_salinity)) # Construct prognostic fields if not provided ice_thickness = Field{Center, Center, Nothing}(grid, boundary_conditions=boundary_conditions.h) @@ -90,7 +89,7 @@ function SeaIceModel(grid; # Adding thickness and concentration if not there prognostic_fields = merge(tracers, (; h = ice_thickness, ℵ = ice_concentration)) - prognostic_fields = if ice_salinity isa ConstantField + prognostic_fields = if ice_salinity isa ConstantField prognostic_fields else merge(prognostic_fields, (; S = ice_salinity)) @@ -101,6 +100,7 @@ function SeaIceModel(grid; # TODO: should we have ice thickness and concentration as part of the tracers or # just additional fields of the sea ice model? timestepper = ForwardEulerTimeStepper(grid, prognostic_fields) + tracers = merge(tracers, (; S = ice_salinity)) if !isnothing(ice_thermodynamics) if isnothing(top_heat_flux) From b4a4933ca2a51a1108bd0d410e1b82c5d6488951 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 14:56:19 +0100 Subject: [PATCH 079/108] remove IS --- src/sea_ice_model.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sea_ice_model.jl b/src/sea_ice_model.jl index 59cfc036..2ea34ca3 100644 --- a/src/sea_ice_model.jl +++ b/src/sea_ice_model.jl @@ -9,7 +9,7 @@ using Oceananigans.Forcings: model_forcing using ClimaSeaIce.SeaIceThermodynamics.HeatBoundaryConditions: flux_summary using ClimaSeaIce.Rheologies: rheology_prognostic_tracers -struct SeaIceModel{GR, TD, D, TS, CL, U, T, IT, IC, ID, IS, CT, STF, A, F, Arch} <: AbstractModel{TS, Arch} +struct SeaIceModel{GR, TD, D, TS, CL, U, T, IT, IC, ID, CT, STF, A, F, Arch} <: AbstractModel{TS, Arch} architecture :: Arch grid :: GR clock :: CL From 1908e9a432293de7de12e149743d11702ec61327 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 15:00:10 +0100 Subject: [PATCH 080/108] just remove it for now --- src/sea_ice_time_stepping.jl | 14 ++------------ src/tracer_tendency_kernel_functions.jl | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/sea_ice_time_stepping.jl b/src/sea_ice_time_stepping.jl index 7c7da257..eaca4f42 100644 --- a/src/sea_ice_time_stepping.jl +++ b/src/sea_ice_time_stepping.jl @@ -74,21 +74,11 @@ end ℵ[i, j, k] = ifelse(ℵ⁺ > 1, one(ℵ⁺), ℵ⁺) h[i, j, k] = ifelse(ℵ⁺ > 1, V⁺, h⁺) - advance_tracers!(tracers, i, j, k, Gⁿ, Δt) + # TODO: BBM rheology needs this! + # advance_tracers!(tracers, i, j, k, Gⁿ, Δt) end end -advance_tracers!(::EmptyTuples, args...) = nothing - -function advance_tracers!(tracers, i, j, k, G, Δt) - # Assumption! The tracer tendencies are the first ones - for n in eachindex(G) - _advance_tracer!(tracers[n], i, j, k, G[n], Δt) - end -end - -_advance_tracer!(tracer, i, j, k, G, Δt) = @inbounds tracer[i, j, 1] += Δt * G[i, j, k] - function update_state!(model::SIM) foreach(prognostic_fields(model)) do field diff --git a/src/tracer_tendency_kernel_functions.jl b/src/tracer_tendency_kernel_functions.jl index c7864a8c..51f30a6a 100644 --- a/src/tracer_tendency_kernel_functions.jl +++ b/src/tracer_tendency_kernel_functions.jl @@ -38,17 +38,18 @@ end Gⁿ.h[i, j, 1] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_thickness) Gⁿ.ℵ[i, j, 1] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, ice_concentration) - compute_tracer_tendencies!(Gⁿ, i, j, grid, advection, velocities, tracers) + # TODO: BBM rheology needs this! + # compute_tracer_tendencies!(Gⁿ, i, j, grid, advection, velocities, tracers) end end -const EmptyTuples = Union{NamedTuple{(), Tuple{}}, Tuple{}} +# const EmptyTuples = Union{NamedTuple{(), Tuple{}}, Tuple{}} -compute_tracer_tendencies!(G, i, j, grid, advection, velocities, ::EmptyTuples) = nothing +# compute_tracer_tendencies!(G, i, j, grid, advection, velocities, ::EmptyTuples) = nothing -function compute_tracer_tendencies!(G, i, j, grid, advection, velocities, tracers) - # Assumption! The tracer tendencies are the first ones - for n in eachindex(G) - @inbounds G[n][i, j, 1] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, tracers[n]) - end -end +# function compute_tracer_tendencies!(G, i, j, grid, advection, velocities, tracers) +# # Assumption! The tracer tendencies are the first ones +# for n in eachindex(G) +# @inbounds G[n][i, j, 1] = - horizontal_div_Uc(i, j, 1, grid, advection, velocities, tracers[n]) +# end +# end From 99279ba55d2cc1d22f44ac4560db42256fa200ed Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 17:38:54 +0100 Subject: [PATCH 081/108] full tau --- src/SeaIceMomentumEquations/sea_ice_external_stress.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/SeaIceMomentumEquations/sea_ice_external_stress.jl b/src/SeaIceMomentumEquations/sea_ice_external_stress.jl index bd4f0061..87e61b16 100644 --- a/src/SeaIceMomentumEquations/sea_ice_external_stress.jl +++ b/src/SeaIceMomentumEquations/sea_ice_external_stress.jl @@ -22,6 +22,14 @@ using Oceananigans.Fields: ZeroField @inline explicit_τx(i, j, k, grid, stress::NamedTuple, clock, fields) = explicit_τx(i, j, k, grid, stress.u, clock, fields) @inline explicit_τy(i, j, k, grid, stress::NamedTuple, clock, fields) = explicit_τx(i, j, k, grid, stress.v, clock, fields) +# Convenience functions to compute the full stress +@inline full_τx(i, j, k, grid, τ, clock, fields) = + @inbounds explicit_τx(i, j, k, grid, τ, clock, fields) - implicit_τx_coefficient(i, j, k, grid, τ, clock, fields) * fields.u[i, j, k] + +# Convenience functions to compute the full stress +@inline full_τy(i, j, k, grid, τ, c, f) = + @inbounds explicit_τy(i, j, k, grid, τ, clock, fields) - implicit_τy_coefficient(i, j, k, grid, τ, clock, fields) * fields.v[i, j, k] + ##### ##### SemiImplicitStress ##### From 981d0f09379d1f81775175cdbd03a6abd55bdbcf Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 26 Mar 2025 18:52:57 +0100 Subject: [PATCH 082/108] change name --- src/SeaIceMomentumEquations/sea_ice_external_stress.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SeaIceMomentumEquations/sea_ice_external_stress.jl b/src/SeaIceMomentumEquations/sea_ice_external_stress.jl index 87e61b16..1a6a8dfd 100644 --- a/src/SeaIceMomentumEquations/sea_ice_external_stress.jl +++ b/src/SeaIceMomentumEquations/sea_ice_external_stress.jl @@ -23,11 +23,11 @@ using Oceananigans.Fields: ZeroField @inline explicit_τy(i, j, k, grid, stress::NamedTuple, clock, fields) = explicit_τx(i, j, k, grid, stress.v, clock, fields) # Convenience functions to compute the full stress -@inline full_τx(i, j, k, grid, τ, clock, fields) = +@inline x_momentum_stress(i, j, k, grid, τ, clock, fields) = @inbounds explicit_τx(i, j, k, grid, τ, clock, fields) - implicit_τx_coefficient(i, j, k, grid, τ, clock, fields) * fields.u[i, j, k] # Convenience functions to compute the full stress -@inline full_τy(i, j, k, grid, τ, c, f) = +@inline y_momentum_stress(i, j, k, grid, τ, c, f) = @inbounds explicit_τy(i, j, k, grid, τ, clock, fields) - implicit_τy_coefficient(i, j, k, grid, τ, clock, fields) * fields.v[i, j, k] ##### From 11a963ace26ae31ff601835585a9a9d5ceb653aa Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 27 Mar 2025 12:40:08 +0100 Subject: [PATCH 083/108] bugfix --- src/SeaIceMomentumEquations/sea_ice_external_stress.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SeaIceMomentumEquations/sea_ice_external_stress.jl b/src/SeaIceMomentumEquations/sea_ice_external_stress.jl index 1a6a8dfd..95360c02 100644 --- a/src/SeaIceMomentumEquations/sea_ice_external_stress.jl +++ b/src/SeaIceMomentumEquations/sea_ice_external_stress.jl @@ -27,7 +27,7 @@ using Oceananigans.Fields: ZeroField @inbounds explicit_τx(i, j, k, grid, τ, clock, fields) - implicit_τx_coefficient(i, j, k, grid, τ, clock, fields) * fields.u[i, j, k] # Convenience functions to compute the full stress -@inline y_momentum_stress(i, j, k, grid, τ, c, f) = +@inline y_momentum_stress(i, j, k, grid, τ, clock, fields) = @inbounds explicit_τy(i, j, k, grid, τ, clock, fields) - implicit_τy_coefficient(i, j, k, grid, τ, clock, fields) * fields.v[i, j, k] ##### From c56616a7dbc39e8650aedc136839e83dc144dc2e Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 1 May 2025 13:47:49 +0200 Subject: [PATCH 084/108] space --- src/Rheologies/elasto_visco_plastic_rheology.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index de289d11..26509554 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -175,8 +175,8 @@ function compute_stresses!(model, dynamics, rheology::ElastoViscoPlasticRheology return nothing end -@inline strain_rate_xx(i, j, k, grid, u, v) = ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, u) / Azᶜᶜᶜ(i, j, k, grid) -@inline strain_rate_yy(i, j, k, grid, u, v) = ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, v) / Azᶜᶜᶜ(i, j, k, grid) +@inline strain_rate_xx(i, j, k, grid, u, v) = ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, u) / Azᶜᶜᶜ(i, j, k, grid) +@inline strain_rate_yy(i, j, k, grid, u, v) = ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, v) / Azᶜᶜᶜ(i, j, k, grid) @inline strain_rate_xy(i, j, k, grid, u, v) = (ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, v) + ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, u)) / Azᶜᶜᶜ(i, j, k, grid) / 2 @kernel function _compute_evp_viscosities!(fields, grid, rheology, u, v, h, ℵ, ρᵢ, Δt) From 73e0f466002b47f7b31d68473cc4c17dc4f3b2dc Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 1 May 2025 13:48:21 +0200 Subject: [PATCH 085/108] more changes --- .../elasto_visco_plastic_rheology.jl | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index 26509554..aaf06b14 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -190,9 +190,9 @@ end P = fields.P # Strain rates - ϵ̇₁₁ = strain_rate_xx(i, j, 1, grid, u, v) - ϵ̇₂₂ = strain_rate_yy(i, j, 1, grid, u, v) - ϵ̇₁₂ = strain_rate_xy(i, j, 1, grid, u, v) + ϵ̇₁₁ = strain_rate_xx(i, j, kᴺ, grid, u, v) + ϵ̇₂₂ = strain_rate_yy(i, j, kᴺ, grid, u, v) + ϵ̇₁₂ = strain_rate_xy(i, j, kᴺ, grid, u, v) # Ice divergence δ = ϵ̇₁₁ + ϵ̇₂₂ @@ -204,11 +204,11 @@ end # if Δ is very small we assume a linear viscous response # adding a minimum Δ_min (at Centers) Δ = max(sqrt(δ^2 + s^2 * e⁻²), Δm) - P = @inbounds P[i, j, 1] + P = @inbounds P[i, j, kᴺ] ζ = P / 2Δ - @inbounds fields.ζ[i, j, 1] = ζ - @inbounds fields.Δ[i, j, 1] = Δ + @inbounds fields.ζ[i, j, kᴺ] = ζ + @inbounds fields.Δ[i, j, kᴺ] = Δ e⁻² = rheology.yield_curve_eccentricity^(-2) Δm = rheology.minimum_plastic_stress @@ -234,22 +234,22 @@ end # Update coefficients for substepping using dynamic substepping # with spatially varying coefficients as in Kimmritz et al (2016) - γ = ζ * π^2 * Δt / mᵢ / Azᶜᶜᶜ(i, j, 1, grid) + γ = ζ * π^2 * Δt / mᵢ / Azᶜᶜᶜ(i, j, kᴺ, grid) α = clamp(sqrt(γ), α⁻, α⁺) α = ifelse(isnan(α), α⁺, α) @inbounds begin # Compute the new stresses and store the value of the # dynamic substepping coefficient α - σ₁₁★ = (σ₁₁ᵖ⁺¹ - σ₁₁[i, j, 1]) / α - σ₂₂★ = (σ₂₂ᵖ⁺¹ - σ₂₂[i, j, 1]) / α - σ₁₂★ = (σ₁₂ᵖ⁺¹ - σ₁₂[i, j, 1]) / α + σ₁₁★ = (σ₁₁ᵖ⁺¹ - σ₁₁[i, j, kᴺ]) / α + σ₂₂★ = (σ₂₂ᵖ⁺¹ - σ₂₂[i, j, kᴺ]) / α + σ₁₂★ = (σ₁₂ᵖ⁺¹ - σ₁₂[i, j, kᴺ]) / α - σ₁₁[i, j, 1] += ifelse(mᵢ > 0, σ₁₁★, zero(grid)) - σ₂₂[i, j, 1] += ifelse(mᵢ > 0, σ₂₂★, zero(grid)) - σ₁₂[i, j, 1] += ifelse(mᵢ > 0, σ₁₂★, zero(grid)) + σ₁₁[i, j, kᴺ] += ifelse(mᵢ > 0, σ₁₁★, zero(grid)) + σ₂₂[i, j, kᴺ] += ifelse(mᵢ > 0, σ₂₂★, zero(grid)) + σ₁₂[i, j, kᴺ] += ifelse(mᵢ > 0, σ₁₂★, zero(grid)) - fields.α[i, j, 1] = α + fields.α[i, j, kᴺ] = α end end From 4b9d9ca07ee6edd41cf6b48d49d555c385c2407c Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 1 May 2025 13:50:37 +0200 Subject: [PATCH 086/108] we can remove all this --- src/Rheologies/ice_stress_divergence.jl | 66 ++----------------------- 1 file changed, 5 insertions(+), 61 deletions(-) diff --git a/src/Rheologies/ice_stress_divergence.jl b/src/Rheologies/ice_stress_divergence.jl index 98bee8e4..c673d4e0 100644 --- a/src/Rheologies/ice_stress_divergence.jl +++ b/src/Rheologies/ice_stress_divergence.jl @@ -19,10 +19,10 @@ const f = Face() @inline _ice_stress_vx(args...) = ice_stress_vx(args...) @inline _ice_stress_vy(args...) = ice_stress_vy(args...) -@inline _ice_stress_ux(i, j, k, ibg::IBG, args...) = conditional_flux_ccc(i, j, k, ibg, zero(ibg), ice_stress_ux(i, j, k, ibg, args...)) -@inline _ice_stress_uy(i, j, k, ibg::IBG, args...) = conditional_flux_ffc(i, j, k, ibg, zero(ibg), ice_stress_uy(i, j, k, ibg, args...)) -@inline _ice_stress_vx(i, j, k, ibg::IBG, args...) = conditional_flux_ffc(i, j, k, ibg, zero(ibg), ice_stress_vx(i, j, k, ibg, args...)) -@inline _ice_stress_vy(i, j, k, ibg::IBG, args...) = conditional_flux_ccc(i, j, k, ibg, zero(ibg), ice_stress_vy(i, j, k, ibg, args...)) +@inline _ice_stress_ux(i, j, k, ibg::IBG, args...) = conditional_flux_cfc(i, j, k, ibg, zero(ibg), ice_stress_ux(i, j, k, ibg, args...)) +@inline _ice_stress_uy(i, j, k, ibg::IBG, args...) = conditional_flux_fcc(i, j, k, ibg, zero(ibg), ice_stress_uy(i, j, k, ibg, args...)) +@inline _ice_stress_vx(i, j, k, ibg::IBG, args...) = conditional_flux_cfc(i, j, k, ibg, zero(ibg), ice_stress_vx(i, j, k, ibg, args...)) +@inline _ice_stress_vy(i, j, k, ibg::IBG, args...) = conditional_flux_cfc(i, j, k, ibg, zero(ibg), ice_stress_vy(i, j, k, ibg, args...)) ##### ##### Stress divergence @@ -36,60 +36,4 @@ end @inline function ∂ⱼ_σ₂ⱼ(i, j, k, grid, rheology, clock, fields) return 1 / Azᶠᶠᶜ(i, j, k, grid) * (δxᶠᶠᶜ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_vx, rheology, clock, fields) + δyᶠᶠᶜ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_vy, rheology, clock, fields)) -end - -##### -##### Immersed Stress divergence (Nothing on B-grid!) -##### - -@inline immersed_∂ⱼ_σ₁ⱼ(i, j, k, grid, args...) = zero(grid) -@inline immersed_∂ⱼ_σ₂ⱼ(i, j, k, grid, args...) = zero(grid) - -@inline flip(::Type{Center}) = Face -@inline flip(::Type{Face}) = Center -@inline flip(::Center) = Face() -@inline flip(::Face) = Center() - -@inline function immersed_∂ⱼ_σ₁ⱼ(i, j, k, ibg::IBG, u_bc::IBC, rheology, clock, fields) - # Fetch fluxes across immersed boundary - q̃ᵂ = ib_ice_stress_ux(i, j, k, ibg, u_bc.west, rheology, clock, fields) - q̃ᴱ = ib_ice_stress_ux(i+1, j, k, ibg, u_bc.east, rheology, clock, fields) - q̃ˢ = ib_ice_stress_uy(i, j-1, k, ibg, u_bc.south, rheology, clock, fields) - q̃ᴺ = ib_ice_stress_uy(i, j, k, ibg, u_bc.north, rheology, clock, fields) - - iᵂ, jˢ, _ = map(index_left, (i, j, k), (c, c, c)) # Broadcast instead of map causes inference failure - iᴱ, jᴺ, _ = map(index_right, (i, j, k), (f, f, c)) - - # Impose i) immersed fluxes if we're on an immersed boundary or ii) zero otherwise. - qᵂ = conditional_flux_ccc(iᵂ, j, k, ibg, q̃ᵂ, zero(ibg)) * Axᶜᶜᶜ(iᵂ, j, k, grid) - qᴱ = conditional_flux_ccc(iᴱ, j, k, ibg, q̃ᴱ, zero(ibg)) * Axᶜᶜᶜ(iᴱ, j, k, grid) - qˢ = conditional_flux_ffc(i, jˢ, k, ibg, q̃ˢ, zero(ibg)) * Ayᶠᶠᶜ(i, jˢ, k, grid) - qᴺ = conditional_flux_ffc(i, jᴺ, k, ibg, q̃ᴺ, zero(ibg)) * Ayᶠᶠᶜ(i, jᴺ, k, grid) - - return (qᴱ - qᵂ + qᴺ - qˢ) / Vᶠᶜᶜ(i, j, k, grid) -end - -@inline function immersed_∂ⱼ_σ₂ⱼ(i, j, k, ibg::IBG, v_bc::IBC, rheology, clock, fields) - # Fetch fluxes across immersed boundary - q̃ᵂ = ib_ice_stress_vx(i-1, j, k, ibg, v_bc.west, rheology, clock, fields) - q̃ᴱ = ib_ice_stress_vx(i, j, k, ibg, v_bc.east, rheology, clock, fields) - q̃ˢ = ib_ice_stress_vy(i, j, k, ibg, v_bc.south, rheology, clock, fields) - q̃ᴺ = ib_ice_stress_vy(i, j+1, k, ibg, v_bc.north, rheology, clock, fields) - - iᵂ, jˢ, _ = map(index_left, (i, j, k), (f, f, c)) # Broadcast instead of map causes inference failure - iᴱ, jᴺ, _ = map(index_right, (i, j, k), (c, c, c)) - - # Impose i) immersed fluxes if we're on an immersed boundary or ii) zero otherwise. - qᵂ = conditional_flux_ffc(iᵂ, j, k, ibg, q̃ᵂ, zero(ibg)) * Axᶠᶠᶜ(iᵂ, j, k, grid) - qᴱ = conditional_flux_ffc(iᴱ, j, k, ibg, q̃ᴱ, zero(ibg)) * Axᶠᶠᶜ(iᴱ, j, k, grid) - qˢ = conditional_flux_ccc(i, jˢ, k, ibg, q̃ˢ, zero(ibg)) * Ayᶜᶜᶜ(i, jˢ, k, grid) - qᴺ = conditional_flux_ccc(i, jᴺ, k, ibg, q̃ᴺ, zero(ibg)) * Ayᶜᶜᶜ(i, jᴺ, k, grid) - - return (qᴱ - qᵂ + qᴺ - qˢ) / Vᶜᶠᶜ(i, j, k, grid) -end - -# TODO: Implement immersed fluxes (0 for the moment) -@inline ib_ice_stress_ux(i, j, k, grid, args...) = zero(grid) -@inline ib_ice_stress_vx(i, j, k, grid, args...) = zero(grid) -@inline ib_ice_stress_uy(i, j, k, grid, args...) = zero(grid) -@inline ib_ice_stress_vy(i, j, k, grid, args...) = zero(grid) \ No newline at end of file +end \ No newline at end of file From 6f8237d02e50ddaff41e5afc0cbf795dd15cd86c Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 1 May 2025 14:47:45 +0200 Subject: [PATCH 087/108] no more immersed stresses --- .../momentum_tendencies_kernel_functions.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl b/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl index 3c688fec..49cc778d 100644 --- a/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl +++ b/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl @@ -24,7 +24,6 @@ using Oceananigans.Coriolis: fᶠᶠᵃ + explicit_τx(i, j, kᴺ, grid, u_top_stress, clock, model_fields) / mᵢ * ℵᵢ + explicit_τx(i, j, kᴺ, grid, u_bottom_stress, clock, model_fields) / mᵢ * ℵᵢ + ∂ⱼ_σ₁ⱼ(i, j, kᴺ, grid, rheology, clock, model_fields) / mᵢ - + immersed_∂ⱼ_σ₁ⱼ(i, j, kᴺ, grid, u_immersed_bc, rheology, clock, model_fields) / mᵢ + sum_of_forcing_u(i, j, kᴺ, grid, rheology, u_forcing, model_fields, Δt)) # sum of user defined forcing and possibly other forcing terms that are rheology-dependent Gᵁ = ifelse(mᵢ ≤ 0, zero(grid), Gᵁ) @@ -56,7 +55,6 @@ end + explicit_τy(i, j, kᴺ, grid, v_top_stress, clock, model_fields) / mᵢ * ℵᵢ + explicit_τy(i, j, kᴺ, grid, v_bottom_stress, clock, model_fields) / mᵢ * ℵᵢ + ∂ⱼ_σ₂ⱼ(i, j, kᴺ, grid, rheology, clock, model_fields) / mᵢ - + immersed_∂ⱼ_σ₂ⱼ(i, j, kᴺ, grid, v_immersed_bc, rheology, clock, model_fields) / mᵢ + sum_of_forcing_v(i, j, kᴺ, grid, rheology, v_forcing, model_fields, Δt)) # sum of user defined forcing and possibly other forcing terms that are rheology-dependent Gⱽ = ifelse(mᵢ ≤ 0, zero(grid), Gⱽ) From 77712d6715d79eafdfe2f82c0a72645c488d3404 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 1 May 2025 15:18:19 +0200 Subject: [PATCH 088/108] better --- src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl b/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl index 32931c2a..f03e028e 100644 --- a/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl +++ b/src/SeaIceMomentumEquations/SeaIceMomentumEquations.jl @@ -16,8 +16,6 @@ using Oceananigans.Grids: architecture using ClimaSeaIce: ice_mass using ClimaSeaIce.Rheologies: ∂ⱼ_σ₁ⱼ, ∂ⱼ_σ₂ⱼ, - immersed_∂ⱼ_σ₁ⱼ, - immersed_∂ⱼ_σ₂ⱼ, rheology_auxiliary_fields, compute_stresses!, initialize_rheology!, From d5241604460737258c7b2c66e964f4c5c999a09e Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 1 May 2025 15:42:07 +0200 Subject: [PATCH 089/108] add ice salinity --- src/SeaIceThermodynamics/thermodynamic_time_step.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SeaIceThermodynamics/thermodynamic_time_step.jl b/src/SeaIceThermodynamics/thermodynamic_time_step.jl index 2398d9d7..149c1ef4 100644 --- a/src/SeaIceThermodynamics/thermodynamic_time_step.jl +++ b/src/SeaIceThermodynamics/thermodynamic_time_step.jl @@ -41,6 +41,7 @@ end Δt, clock, ice_consolidation_thickness, + ice_salinity, ice_thermodynamics, top_external_heat_flux, bottom_external_heat_flux, From a9731ab38dd813041f3fc87736e8a168cccb9e22 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Thu, 1 May 2025 17:20:58 +0200 Subject: [PATCH 090/108] bugfix --- .../split_explicit_momentum_equations.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl index aa8ac746..bce1b53a 100644 --- a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl @@ -145,7 +145,8 @@ end v_immersed_bc, v_top_stress, v_bottom_stress, v_forcing) i, j = @index(Global, NTuple) - + kᴺ = size(grid, 3) + mᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, ice_mass, model_fields.h, model_fields.ℵ, model_fields.ρ) ℵᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, model_fields.ℵ) From a3c8db556d3476c4f21c3f3f056ce3fd81adf213 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Mon, 5 May 2025 09:45:36 +0200 Subject: [PATCH 091/108] adding --- .../elasto_visco_plastic_rheology.jl | 14 +++++++------- src/Rheologies/ice_stress_divergence.jl | 5 ----- .../sea_ice_external_stress.jl | 18 ++++-------------- 3 files changed, 11 insertions(+), 26 deletions(-) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index aaf06b14..8bd495d7 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -230,7 +230,7 @@ end σ₂₂ᵖ⁺¹ = 2 * η * ϵ̇₂₂ + ((ζ - η) * (ϵ̇₁₁ + ϵ̇₂₂) - Pᵣ / 2) σ₁₂ᵖ⁺¹ = 2 * η * ϵ̇₁₂ - mᵢ = ice_mass(i, j, 1, grid, h, ℵ, ρᵢ) + mᵢ = ice_mass(i, j, kᴺ, grid, h, ℵ, ρᵢ) # Update coefficients for substepping using dynamic substepping # with spatially varying coefficients as in Kimmritz et al (2016) @@ -258,10 +258,10 @@ end ##### # Here we extend all the functions that a rheology model needs to support: -@inline ice_stress_ux(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, fields.σ₁₁) -@inline ice_stress_uy(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, fields.σ₁₂) -@inline ice_stress_vx(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, fields.σ₁₂) -@inline ice_stress_vy(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, fields.σ₂₂) +@inline ice_stress_ux(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, k, grid, fields.σ₁₁) +@inline ice_stress_uy(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, k, grid, fields.σ₁₂) +@inline ice_stress_vx(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, k, grid, fields.σ₁₂) +@inline ice_stress_vy(i, j, k, grid, ::ElastoViscoPlasticRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, k, grid, fields.σ₂₂) # To help convergence to the right velocities @inline compute_substep_Δtᶠᶠᶜ(i, j, grid, Δt, ::ElastoViscoPlasticRheology, substeps, fields) = @inbounds Δt / ℑxyᶠᶠᵃ(i, j, 1, grid, fields.α) @@ -272,12 +272,12 @@ end @inline function sum_of_forcing_u(i, j, k, grid, ::ElastoViscoPlasticRheology, u_forcing, fields, Δt) user_forcing = u_forcing(i, j, k, grid, fields) - rheology_forcing = @inbounds (fields.uⁿ[i, j, k] - fields.u[i, j, k]) / Δt / ℑxyᶠᶠᵃ(i, j, 1, grid, fields.α) + rheology_forcing = @inbounds (fields.uⁿ[i, j, k] - fields.u[i, j, k]) / Δt / ℑxyᶠᶠᵃ(i, j, k, grid, fields.α) return user_forcing + rheology_forcing end @inline function sum_of_forcing_v(i, j, k, grid, ::ElastoViscoPlasticRheology, v_forcing, fields, Δt) user_forcing = v_forcing(i, j, k, grid, fields) - rheology_forcing = @inbounds (fields.vⁿ[i, j, k] - fields.v[i, j, k]) / Δt / ℑxyᶠᶠᵃ(i, j, 1, grid, fields.α) + rheology_forcing = @inbounds (fields.vⁿ[i, j, k] - fields.v[i, j, k]) / Δt / ℑxyᶠᶠᵃ(i, j, k, grid, fields.α) return user_forcing + rheology_forcing end \ No newline at end of file diff --git a/src/Rheologies/ice_stress_divergence.jl b/src/Rheologies/ice_stress_divergence.jl index c673d4e0..6a68a721 100644 --- a/src/Rheologies/ice_stress_divergence.jl +++ b/src/Rheologies/ice_stress_divergence.jl @@ -19,11 +19,6 @@ const f = Face() @inline _ice_stress_vx(args...) = ice_stress_vx(args...) @inline _ice_stress_vy(args...) = ice_stress_vy(args...) -@inline _ice_stress_ux(i, j, k, ibg::IBG, args...) = conditional_flux_cfc(i, j, k, ibg, zero(ibg), ice_stress_ux(i, j, k, ibg, args...)) -@inline _ice_stress_uy(i, j, k, ibg::IBG, args...) = conditional_flux_fcc(i, j, k, ibg, zero(ibg), ice_stress_uy(i, j, k, ibg, args...)) -@inline _ice_stress_vx(i, j, k, ibg::IBG, args...) = conditional_flux_cfc(i, j, k, ibg, zero(ibg), ice_stress_vx(i, j, k, ibg, args...)) -@inline _ice_stress_vy(i, j, k, ibg::IBG, args...) = conditional_flux_cfc(i, j, k, ibg, zero(ibg), ice_stress_vy(i, j, k, ibg, args...)) - ##### ##### Stress divergence ##### diff --git a/src/SeaIceMomentumEquations/sea_ice_external_stress.jl b/src/SeaIceMomentumEquations/sea_ice_external_stress.jl index 12822ab3..5f9e43ef 100644 --- a/src/SeaIceMomentumEquations/sea_ice_external_stress.jl +++ b/src/SeaIceMomentumEquations/sea_ice_external_stress.jl @@ -22,14 +22,6 @@ using Oceananigans.Fields: ZeroField @inline explicit_τx(i, j, k, grid, stress::NamedTuple, clock, fields) = explicit_τx(i, j, k, grid, stress.u, clock, fields) @inline explicit_τy(i, j, k, grid, stress::NamedTuple, clock, fields) = explicit_τx(i, j, k, grid, stress.v, clock, fields) -# Convenience functions to compute the full stress -@inline x_momentum_stress(i, j, k, grid, τ, clock, fields) = - @inbounds explicit_τx(i, j, k, grid, τ, clock, fields) - implicit_τx_coefficient(i, j, k, grid, τ, clock, fields) * fields.u[i, j, k] - -# Convenience functions to compute the full stress -@inline y_momentum_stress(i, j, k, grid, τ, clock, fields) = - @inbounds explicit_τy(i, j, k, grid, τ, clock, fields) - implicit_τy_coefficient(i, j, k, grid, τ, clock, fields) * fields.v[i, j, k] - ##### ##### Utility for computing the total stress ##### @@ -98,16 +90,14 @@ Adapt.adapt_structure(to, τ::SemiImplicitStress) = @inline function explicit_τx(i, j, k, grid, τ::SemiImplicitStress, clock, fields) uₑ = @inbounds τ.uₑ[i, j, k] - Δu = @inbounds τ.uₑ[i, j, k] - fields.u[i, j, k] - Δv = @inbounds τ.vₑ[i, j, k] - fields.v[i, j, k] - return τ.ρₑ * τ.Cᴰ * sqrt(Δu^2 + Δv^2) * uₑ + τi = implicit_τx_coefficient(i, j, k, grid, τ, clock, fields) + return τi * uₑ end @inline function explicit_τy(i, j, k, grid, τ::SemiImplicitStress, clock, fields) vₑ = @inbounds τ.vₑ[i, j, k] - Δv = @inbounds τ.vₑ[i, j, k] - fields.v[i, j, k] - Δu = @inbounds τ.uₑ[i, j, k] - fields.u[i, j, k] - return τ.ρₑ * τ.Cᴰ * sqrt(Δu^2 + Δv^2) * vₑ + τi = implicit_τy_coefficient(i, j, k, grid, τ, clock, fields) + return τi * vₑ end @inline function implicit_τx_coefficient(i, j, k, grid, τ::SemiImplicitStress, clock, fields) From 90a0499eb99ff3afcd12583680fde030edca4aa3 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Mon, 5 May 2025 10:44:31 +0200 Subject: [PATCH 092/108] ice_thermodynamics --- .../explicit_momentum_equations.jl | 4 +- src/sea_ice_advection.jl | 62 +++++++++++-------- test/test_sea_ice_advection.jl | 2 +- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/SeaIceMomentumEquations/explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/explicit_momentum_equations.jl index c2eaf4be..3455c433 100644 --- a/src/SeaIceMomentumEquations/explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/explicit_momentum_equations.jl @@ -44,10 +44,10 @@ end # Implicit part of the stress that depends linearly on the velocity τuᵢ = ( implicit_τx_coefficient(i, j, kᴺ, grid, bottom_stress, clock, fields) - - implicit_τx_coefficient(i, j, kᴺ, grid, top_stress, clock, fields)) / mᶠᶜ * ℵᶠᶜ + - implicit_τx_coefficient(i, j, kᴺ, grid, top_stress, clock, fields)) / mᶠᶠ * ℵᶠᶠ τvᵢ = ( implicit_τy_coefficient(i, j, kᴺ, grid, bottom_stress, clock, fields) - - implicit_τy_coefficient(i, j, kᴺ, grid, top_stress, clock, fields)) / mᶜᶠ * ℵᶜᶠ + - implicit_τy_coefficient(i, j, kᴺ, grid, top_stress, clock, fields)) / mᶠᶠ * ℵᶠᶠ @inbounds begin uᴰ = (u[i, j, 1] + Δt * Gⁿ.u[i, j, 1]) / (1 + Δt * τuᵢ) diff --git a/src/sea_ice_advection.jl b/src/sea_ice_advection.jl index 945d3fff..160500d9 100644 --- a/src/sea_ice_advection.jl +++ b/src/sea_ice_advection.jl @@ -1,40 +1,52 @@ using Oceananigans.Operators -using Oceananigans.ImmersedBoundaries +using Oceananigans.ImmersedBoundaries: IBG using Oceananigans.Advection: FluxFormAdvection, _advective_tracer_flux_x, _advective_tracer_flux_y, conditional_flux_fcc, - conditional_flux_cfc - -# To obtain better numerical properties, the ice thickness is advected together -# with the concentration, i.e.: -# -# A = ℵ⁻¹ ∇ ⋅ (uℵh) -# -# instead of the classical -# -# A = ∇ ⋅ (uh) + conditional_flux_cfc, + UpwindScheme, + CenteredScheme using Oceananigans.Advection: _biased_interpolate_xᶠᵃᵃ, _biased_interpolate_yᵃᶠᵃ, bias -@inline function advective_bgrid_tracer_flux_x(i, j, k, grid, advection, u, c) - ũ = ℑyᵃᶜᵃ(i, j, k, grid, u) - cᴿ = _biased_interpolate_xᶠᵃᵃ(i, j, k, grid, advection, bias(ũ), c) - return Axᶠᶜᶜ(i, j, k, grid) * ũ * cᴿ +@inline _advective_sea_ice_tracer_flux_x(i, j, k, grid, scheme, u, c) = advective_sea_ice_tracer_flux_x(i, j, k, grid, scheme, u, c) +@inline _advective_sea_ice_tracer_flux_y(i, j, k, grid, scheme, v, c) = advective_sea_ice_tracer_flux_y(i, j, k, grid, scheme, v, c) + +@inline _advective_sea_ice_tracer_flux_x(i, j, k, ibg::IBG, scheme, u, c) = conditional_flux_fcc(i, j, k, ibg, zero(ibg), advective_sea_ice_tracer_flux_x(i, j, k, grid, scheme, u, c)) +@inline _advective_sea_ice_tracer_flux_y(i, j, k, ibg::IBG, scheme, v, c) = conditional_flux_cfc(i, j, k, ibg, zero(ibg), advective_sea_ice_tracer_flux_y(i, j, k, grid, scheme, v, c)) + +@inline function advective_sea_ice_tracer_flux_x(i, j, k, grid, scheme::UpwindScheme, u, c) + ũ = ℑyᵃᶜᵃ(i, j, k, grid, Ax_qᶠᶠᶜ, u) + cᴿ = _biased_interpolate_xᶠᵃᵃ(i, j, k, grid, scheme, bias(ũ), c) + return ũ * cᴿ +end + +@inline function advective_sea_ice_tracer_flux_y(i, j, k, grid, scheme::UpwindScheme, v, c) + ṽ = ℑxᶜᵃᵃ(i, j, k, grid, Ay_qᶠᶠᶜ, v) + cᴿ = _biased_interpolate_yᵃᶠᵃ(i, j, k, grid, scheme, bias(ṽ), c) + return ṽ * cᴿ +end + +@inline function advective_sea_ice_tracer_flux_x(i, j, k, grid, scheme::CenteredScheme, u, c) + ũ = ℑyᵃᶜᵃ(i, j, k, grid, Ax_qᶠᶠᶜ, u) + cᴿ = _symmetric_interpolate_xᶠᵃᵃ(i, j, k, grid, scheme, bias(ũ), c) + return ũ * cᴿ end -@inline function advective_bgrid_tracer_flux_y(i, j, k, grid, advection, v, c) - ṽ = ℑxᶜᵃᵃ(i, j, k, grid, v) - cᴿ = _biased_interpolate_yᵃᶠᵃ(i, j, k, grid, advection, bias(ṽ), c) - return Ayᶜᶠᶜ(i, j, k, grid) * ṽ * cᴿ +@inline function advective_sea_ice_tracer_flux_y(i, j, k, grid, scheme::CenteredScheme, v, c) + ṽ = ℑxᶜᵃᵃ(i, j, k, grid, Ay_qᶠᶠᶜ, v) + cᴿ = _symmetric_interpolate_yᵃᶠᵃ(i, j, k, grid, scheme, bias(ṽ), c) + return ṽ * cᴿ end @inline horizontal_div_Uc(i, j, k, grid, ::Nothing, U, c) = zero(grid) -@inline horizontal_div_Uc(i, j, k, grid, advection, U, c) = - 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, advective_bgrid_tracer_flux_x, advection, U.u, c) + - δyᵃᶜᵃ(i, j, k, grid, advective_bgrid_tracer_flux_y, advection, U.v, c)) + +@inline horizontal_div_Uc(i, j, k, grid, scheme, U, c) = + 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, _advective_sea_ice_tracer_flux_x, scheme, U.u, c) + + δyᵃᶜᵃ(i, j, k, grid, _advective_sea_ice_tracer_flux_y, scheme, U.v, c)) -@inline horizontal_div_Uc(i, j, k, grid, advection::FluxFormAdvection, U, c) = - 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, advective_bgrid_tracer_flux_x, advection.x, U.u, c) + - δyᵃᶜᵃ(i, j, k, grid, advective_bgrid_tracer_flux_y, advection.y, U.v, c)) +@inline horizontal_div_Uc(i, j, k, grid, scheme::FluxFormAdvection, U, c) = + 1 / Vᶜᶜᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, _advective_sea_ice_tracer_flux_x, scheme.x, U.u, c) + + δyᵃᶜᵃ(i, j, k, grid, _advective_sea_ice_tracer_flux_y, scheme.y, U.v, c)) diff --git a/test/test_sea_ice_advection.jl b/test/test_sea_ice_advection.jl index 225feddf..6f129106 100644 --- a/test/test_sea_ice_advection.jl +++ b/test/test_sea_ice_advection.jl @@ -26,7 +26,7 @@ end grid = RectilinearGrid(size=(10, 10), x=(0, 1), y=(0, 1), topology=(Bounded, Bounded, Flat)) dynamics = SeaIceMomentumEquation(grid, rheology=ViscousRheology(ν=1000)) - model = SeaIceModel(grid; dynamics, thermodynamics=nothing, advection=WENO()) + model = SeaIceModel(grid; dynamics, ice_thermodynamics=nothing, advection=WENO()) @test !(model.velocities.u isa Nothing) @test !(model.velocities.v isa Nothing) From 64e37f570c235adf497e2cda0f0697dd9f13dc31 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Mon, 5 May 2025 11:35:23 +0200 Subject: [PATCH 093/108] do not use max(d, dm) --- src/Rheologies/elasto_visco_plastic_rheology.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index 8bd495d7..1a7a5477 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -203,7 +203,7 @@ end # Visco - Plastic parameter # if Δ is very small we assume a linear viscous response # adding a minimum Δ_min (at Centers) - Δ = max(sqrt(δ^2 + s^2 * e⁻²), Δm) + Δ = sqrt(δ^2 + s^2 * e⁻²) P = @inbounds P[i, j, kᴺ] ζ = P / 2Δ @@ -234,7 +234,7 @@ end # Update coefficients for substepping using dynamic substepping # with spatially varying coefficients as in Kimmritz et al (2016) - γ = ζ * π^2 * Δt / mᵢ / Azᶜᶜᶜ(i, j, kᴺ, grid) + γ = ζ * π^2 * Δt / mᵢ * Az⁻¹ᶜᶜᶜ(i, j, kᴺ, grid) α = clamp(sqrt(γ), α⁻, α⁺) α = ifelse(isnan(α), α⁺, α) From d4aec31f43e08fcdeb21600707e64d1b78937025 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Mon, 5 May 2025 11:42:43 +0200 Subject: [PATCH 094/108] remove BBM for the moment --- src/Rheologies/Rheologies.jl | 2 +- .../brittle_bingham_maxwell_rheology.jl | 286 ------------------ 2 files changed, 1 insertion(+), 287 deletions(-) delete mode 100644 src/Rheologies/brittle_bingham_maxwell_rheology.jl diff --git a/src/Rheologies/Rheologies.jl b/src/Rheologies/Rheologies.jl index f30a10cf..d4228b00 100644 --- a/src/Rheologies/Rheologies.jl +++ b/src/Rheologies/Rheologies.jl @@ -1,6 +1,6 @@ module Rheologies -export ViscousRheology, ElastoViscoPlasticRheology, BrittleBinghamMaxwellRheology +export ViscousRheology, ElastoViscoPlasticRheology export ∂ⱼ_σ₁ⱼ, ∂ⱼ_σ₂ⱼ, rheology_auxiliary_fields using Oceananigans diff --git a/src/Rheologies/brittle_bingham_maxwell_rheology.jl b/src/Rheologies/brittle_bingham_maxwell_rheology.jl deleted file mode 100644 index aad2c892..00000000 --- a/src/Rheologies/brittle_bingham_maxwell_rheology.jl +++ /dev/null @@ -1,286 +0,0 @@ -using Oceananigans.Grids: halo_size - -struct BrittleBinghamMaxwellRheology{FT, A} - ice_ridging_strength :: FT # compressive strength - ice_compaction_hardening :: FT # compaction hardening - ice_cohesion :: FT # cohesion - undamaged_elastic_modulus :: FT # minimum plastic parameter (transitions to viscous behaviour) - undamaged_viscous_relaxation_time :: FT # minimum number of substeps expressed as the dynamic coefficient - ridging_ice_thickness :: FT # maximum number of substeps expressed as the dynamic coefficient - poisson_ratio :: FT - friction_coefficient :: FT - maximum_compressive_strength :: FT - healing_constant :: FT - damage_parameter :: FT - interpolation_scheme :: A # Interpolation of cc variables to faces -end - -function BrittleBinghamMaxwellRheology(FT::DataType = Float64; - ice_ridging_strength = 1e4, - ice_compaction_hardening = 20, - ice_cohesion = 5.7e3, - undamaged_elastic_modulus = 5.96e8, - undamaged_viscous_relaxation_time = 1e7, - ridging_ice_thickness = 1, - poisson_ratio = 1 / 3, - friction_coefficient = 0.7, - maximum_compressive_strength = 2.9e7, - healing_constant = 26, # Ks - damage_parameter = 5) - - return BrittleBinghamMaxwellRheology(convert(FT, ice_ridging_strength), - convert(FT, ice_compaction_hardening), - convert(FT, ice_cohesion), - convert(FT, undamaged_elastic_modulus), - convert(FT, undamaged_viscous_relaxation_time), - convert(FT, ridging_ice_thickness), - convert(FT, poisson_ratio), - convert(FT, friction_coefficient), - convert(FT, maximum_compressive_strength), - convert(FT, healing_constant), - convert(FT, damage_parameter), - nothing) -end - -rheology_prognostic_tracers(::BrittleBinghamMaxwellRheology) = (:d, :σ₁₁, :σ₁₂, :σ₂₂) - -function rheology_auxiliary_fields(::BrittleBinghamMaxwellRheology, grid) - - # TODO: What about boundary conditions? - P = Field{Center, Center, Nothing}(grid) - E = Field{Center, Center, Nothing}(grid) - λ = Field{Center, Center, Nothing}(grid) - σ¹ = Field{Center, Center, Nothing}(grid) - σ² = Field{Center, Center, Nothing}(grid) - return (; P, E, λ, σ¹, σ²) -end - -# Extend the `adapt_structure` function for the ElastoViscoPlasticRheology -Adapt.adapt_structure(to, r::BrittleBinghamMaxwellRheology) = - BrittleBinghamMaxwellRheology(Adapt.adapt(to, r.ice_ridging_strength), - Adapt.adapt(to, r.ice_compaction_hardening), - Adapt.adapt(to, r.ice_cohesion), - Adapt.adapt(to, r.undamaged_elastic_modulus), - Adapt.adapt(to, r.undamaged_viscous_relaxation_time), - Adapt.adapt(to, r.ridging_ice_thickness), - Adapt.adapt(to, r.poisson_ratio), - Adapt.adapt(to, r.friction_coefficient), - Adapt.adapt(to, r.maximum_compressive_strength), - Adapt.adapt(to, r.healing_constant), - Adapt.adapt(to, r.damage_parameter), - Adapt.adapt(to, r.interpolation_scheme)) - -##### -##### Computation of the stresses -##### - -function initialize_rheology!(model, rheology::BrittleBinghamMaxwellRheology) - h = model.ice_thickness - ℵ = model.ice_concentration - - fields = model.dynamics.auxiliary_fields - - P★ = rheology.ice_ridging_strength - E★ = rheology.undamaged_elastic_modulus - λ★ = rheology.undamaged_viscous_relaxation_time - h★ = rheology.ridging_ice_thickness - α = rheology.damage_parameter - C = rheology.ice_compaction_hardening - d = model.tracers.d - - # compute on the whole grid including halos - parameters = KernelParameters(size(fields.P.data)[1:2], fields.P.data.offsets[1:2]) - launch!(architecture(model.grid), model.grid, parameters, _initialize_bbm_rheology!, fields, d, model.grid, P★, E★, λ★, h★, α, C, h, ℵ) - - return nothing -end - -@kernel function _initialize_bbm_rheology!(fields, d, grid, P★, E★, λ★, h★, α, C, h, ℵ) - i, j = @index(Global, NTuple) - @inbounds begin - ecc = exp(- C * (1 - ℵ[i, j, 1])) - # Center - Center fields - fields.P[i, j, 1] = P★ * (h[i, j, 1] / h★)^(3/2) * ecc - fields.E[i, j, 1] = E★ * ecc - fields.λ[i, j, 1] = λ★ * ecc^(α - 1) - # Clamp the damage between 0 and a value close to 1 - dᵢ = d[i, j, 1] - d[i, j, 1] = clamp(dᵢ, zero(dᵢ), 99999 * one(dᵢ) / 100000) - end -end - -# Specific compute stresses for the EVP rheology -function compute_stresses!(model, dynamics, rheology::BrittleBinghamMaxwellRheology, Δt, Ns) - - grid = model.grid - arch = architecture(grid) - - ρᵢ = model.ice_density - u, v = model.velocities - fields = dynamics.auxiliary_fields - tracers = model.tracers - - Nx, Ny, _ = size(grid) - Hx, Hy, _ = halo_size(grid) - - parameters = KernelParameters(-Hx+2:Nx+Hx-1, -Hy+2:Ny+Hy-1) - - # Pretty simple timestepping - Δτ = Δt / Ns - - launch!(arch, grid, parameters, _compute_stress_predictors!, fields, grid, rheology, tracers, u, v, ρᵢ, Δτ) - launch!(arch, grid, parameters, _advance_stresses_and_damage!, tracers, grid, rheology, fields, ρᵢ, Δτ) - - return nothing -end - -@inline function critical_damage(i, j, k, grid, N, c, μ, fields) - σ¹ = @inbounds fields.σ¹[i, j, k] - σ² = @inbounds fields.σ²[i, j, k] - dc = one(grid) - ifelse(σ¹ > - N, c / (σ² + μ * σ¹), - N / σ¹) - dc = ifelse(isnan(dc), zero(grid), dc) - return dc * (σ² > c - μ * σ¹) -end - -@kernel function _compute_stress_predictors!(fields, grid, rheology, tracers, u, v, ρᵢ, Δτ) - i, j = @index(Global, NTuple) - - α = rheology.damage_parameter - ν = rheology.poisson_ratio - - ϵ̇₁₁ = strain_rate_xx(i, j, 1, grid, u, v) - ϵ̇₂₂ = strain_rate_yy(i, j, 1, grid, u, v) - ϵ̇₁₂ = strain_rate_xy(i, j, 1, grid, u, v) - - Kϵ₁₁ = (ϵ̇₁₁ + ν * ϵ̇₂₂) / (1 - ν^2) - Kϵ₂₂ = (ϵ̇₂₂ + ν * ϵ̇₁₁) / (1 - ν^2) - Kϵ₁₂ = (1 - ν) * ϵ̇₁₂ / (1 - ν^2) - - dᵢ = @inbounds tracers.d[i, j, 1] - P = @inbounds fields.P[i, j, 1] - E₀ = @inbounds fields.E[i, j, 1] - λ₀ = @inbounds fields.λ[i, j, 1] - - E = E₀ * (1 - dᵢ) - λ = λ₀ * (1 - dᵢ)^(α - 1) - - # Test which isotropic stress to use - P̃ = zero(grid) - - # Implicit diagonal operator - Ω = 1 / (1 + Δτ * (1 + P̃) / λ) - - @inbounds tracers.σ₁₁[i, j, 1] = Ω * (tracers.σ₁₁[i, j, 1] + Δτ * E * Kϵ₁₁) - @inbounds tracers.σ₂₂[i, j, 1] = Ω * (tracers.σ₂₂[i, j, 1] + Δτ * E * Kϵ₂₂) - @inbounds tracers.σ₁₂[i, j, 1] = Ω * (tracers.σ₁₂[i, j, 1] + Δτ * E * Kϵ₁₂) - - σ₁₁ = @inbounds tracers.σ₁₁[i, j, 1] - σ₂₂ = @inbounds tracers.σ₂₂[i, j, 1] - σ₁₂ = @inbounds tracers.σ₁₂[i, j, 1] - - # Principal stress components - @inbounds fields.σ¹[i, j, 1] = (σ₁₁ + σ₂₂) / 2 - @inbounds fields.σ²[i, j, 1] = sqrt((σ₁₁ - σ₂₂)^2 / 4 + σ₁₂^2) -end - -@kernel function _advance_stresses_and_damage!(tracers, grid, rheology, fields, ρᵢ, Δτ) - i, j = @index(Global, NTuple) - - α = rheology.damage_parameter - ν = rheology.poisson_ratio - N = rheology.maximum_compressive_strength - c = rheology.ice_cohesion - μ = rheology.friction_coefficient - - ρ = @inbounds ρᵢ[i, j, 1] - dᵢ = @inbounds tracers.d[i, j, 1] - E₀ = @inbounds fields.E[i, j, 1] - - # The relaxation time is time-dependent - E = E₀ * (1 - dᵢ) - td = sqrt(2 * (1 + ν) * ρ / E * Azᶜᶜᶜ(i, j, 1, grid)) - - # damage tendency - Gd = critical_damage(i, j, 1, grid, N, c, μ, fields) / td - - # Damage update - dᵢ += Gd * (1 - dᵢ) * Δτ - - # Advance stresses and clamp damage - @inbounds tracers.σ₁₁[i, j, 1] -= Gd * Δτ * tracers.σ₁₁[i, j, 1] - @inbounds tracers.σ₂₂[i, j, 1] -= Gd * Δτ * tracers.σ₂₂[i, j, 1] - @inbounds tracers.σ₁₂[i, j, 1] -= Gd * Δτ * tracers.σ₁₂[i, j, 1] - @inbounds tracers.d[i, j, 1] = clamp(dᵢ, zero(grid), 99999 * one(grid) / 100000) -end - -##### -##### Methods for the BBM rheology -##### - -# In the BBM rheology, the stresses need to be vertically integrated -@inline hσ₁₁(i, j, k, grid, fields) = @inbounds fields.σ₁₁[i, j, k] * fields.h[i, j, k] -@inline hσ₂₂(i, j, k, grid, fields) = @inbounds fields.σ₂₂[i, j, k] * fields.h[i, j, k] -@inline hσ₁₂(i, j, k, grid, fields) = @inbounds fields.σ₁₂[i, j, k] * fields.h[i, j, k] - -# Here we extend all the functions that a rheology model needs to support: -@inline ice_stress_ux(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, hσ₁₁, fields) -@inline ice_stress_uy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₁₂, fields) -@inline ice_stress_vx(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, hσ₁₂, fields) -@inline ice_stress_vy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₂₂, fields) - -# Another formulation of the critical Damage - -# @inline function dc_perpendicular(i, j, k, grid, N, c, μ, fields) - -# σ¹ = @inbounds fields.σ¹[i, j, k] -# σ² = @inbounds fields.σ²[i, j, k] - -# m = tand(90 - atand(μ)) -# q = σ² - m * σ¹ - -# # # Move towards the yield curve in a perpendicular fashion -# σᵪ¹ = (c - q) / (m + μ) -# σᵪ² = m * σᵪ¹ + q - -# dc = one(grid) - sqrt(σᵪ¹^2 + σᵪ²^2) / sqrt(σ¹^2 + σ²^2) -# dc = ifelse(isnan(dc), zero(grid), dc) - -# return dc * (σ² > c - μ * σ¹) -# end -# -# Dcrit needs to be reconstructed -# -# @inline function reconstruction_2d(i, j, k, grid, f, args...) -# fij = f(i, j, k, grid, args...) -# fmj = f(i-1, j, k, grid, args...) -# fpj = f(i+1, j, k, grid, args...) -# fim = f(i, j-1, k, grid, args...) -# fip = f(i, j+1, k, grid, args...) -# fmm = f(i-1, j-1, k, grid, args...) -# fmp = f(i-1, j+1, k, grid, args...) -# fpm = f(i+1, j-1, k, grid, args...) -# fpp = f(i+1, j+1, k, grid, args...) - -# # remove NaNs -# isnanij = isnan(fij) -# isnanmj = isnan(fmj) -# isnanpj = isnan(fpj) -# isnanim = isnan(fim) -# isnanip = isnan(fip) -# isnanmm = isnan(fmm) -# isnanmp = isnan(fmp) -# isnanpm = isnan(fpm) -# isnanpp = isnan(fpp) - -# fij = ifelse(isnanij, zero(grid), fij) / 4 -# fmj = ifelse(isnanmj, zero(grid), fmj) / 8 -# fpj = ifelse(isnanpj, zero(grid), fpj) / 8 -# fim = ifelse(isnanim, zero(grid), fim) / 8 -# fip = ifelse(isnanip, zero(grid), fip) / 8 -# fmm = ifelse(isnanmm, zero(grid), fmm) / 16 -# fmp = ifelse(isnanmp, zero(grid), fmp) / 16 -# fpm = ifelse(isnanpm, zero(grid), fpm) / 16 -# fpp = ifelse(isnanpp, zero(grid), fpp) / 16 - -# return (fij + fmj + fpj + fim + fip + fmm + fmp + fpm + fpp) -# end \ No newline at end of file From 7c2c06f8b53b65efe0f9842bf96fc4b9b32e61e8 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Mon, 5 May 2025 11:46:25 +0200 Subject: [PATCH 095/108] actually need this regularization --- src/Rheologies/elasto_visco_plastic_rheology.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index 1a7a5477..fcec1b14 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -203,7 +203,7 @@ end # Visco - Plastic parameter # if Δ is very small we assume a linear viscous response # adding a minimum Δ_min (at Centers) - Δ = sqrt(δ^2 + s^2 * e⁻²) + Δ = max(sqrt(δ^2 + s^2 * e⁻²), Δm) # (at Centers) P = @inbounds P[i, j, kᴺ] ζ = P / 2Δ From f558138cb8a7849ab77831b0dbc42570d79e136c Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 6 May 2025 09:04:47 +0200 Subject: [PATCH 096/108] som fixes --- .../elasto_visco_plastic_rheology.jl | 7 ++++--- .../momentum_tendencies_kernel_functions.jl | 12 +++++------- .../split_explicit_momentum_equations.jl | 18 ++++++++---------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index fcec1b14..0d003ef5 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -99,8 +99,8 @@ function rheology_auxiliary_fields(r::ElastoViscoPlasticRheology, grid) P = Field{Center, Center, Nothing}(grid) ζ = Field{Center, Center, Nothing}(grid) Δ = Field{Center, Center, Nothing}(grid) - - α = Field{Face, Face, Nothing}(grid) # Dynamic substeps a la Kimmritz et al (2016) + α = Field{Center, Center, Nothing}(grid) # Dynamic substeps a la Kimmritz et al (2016) + uⁿ = Field{Face, Face, Nothing}(grid) vⁿ = Field{Face, Face, Nothing}(grid) @@ -203,10 +203,11 @@ end # Visco - Plastic parameter # if Δ is very small we assume a linear viscous response # adding a minimum Δ_min (at Centers) - Δ = max(sqrt(δ^2 + s^2 * e⁻²), Δm) # (at Centers) + Δ = max(sqrt(δ^2 + s^2 * e⁻²), Δm) P = @inbounds P[i, j, kᴺ] ζ = P / 2Δ + # Store viscosity and the deformation for analysis purposes @inbounds fields.ζ[i, j, kᴺ] = ζ @inbounds fields.Δ[i, j, kᴺ] = Δ diff --git a/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl b/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl index 49cc778d..d7133477 100644 --- a/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl +++ b/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl @@ -6,7 +6,6 @@ using Oceananigans.Coriolis: fᶠᶠᵃ model_fields, clock, coriolis, - u_immersed_bc, u_top_stress, u_bottom_stress, u_forcing) @@ -37,7 +36,6 @@ end model_fields, clock, coriolis, - v_immersed_bc, v_top_stress, v_bottom_stress, v_forcing) @@ -51,11 +49,11 @@ end ℵᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, ℵ) mᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, ice_mass, h, ℵ, ρ) - Gⱽ = ( - fᶠᶠᵃ(i, j, kᴺ, grid, coriolis) * model_fields.u[i, j, kᴺ] - + explicit_τy(i, j, kᴺ, grid, v_top_stress, clock, model_fields) / mᵢ * ℵᵢ - + explicit_τy(i, j, kᴺ, grid, v_bottom_stress, clock, model_fields) / mᵢ * ℵᵢ - + ∂ⱼ_σ₂ⱼ(i, j, kᴺ, grid, rheology, clock, model_fields) / mᵢ - + sum_of_forcing_v(i, j, kᴺ, grid, rheology, v_forcing, model_fields, Δt)) # sum of user defined forcing and possibly other forcing terms that are rheology-dependent + @inbounds Gⱽ = ( - fᶠᶠᵃ(i, j, kᴺ, grid, coriolis) * model_fields.u[i, j, kᴺ] + + explicit_τy(i, j, kᴺ, grid, v_top_stress, clock, model_fields) / mᵢ * ℵᵢ + + explicit_τy(i, j, kᴺ, grid, v_bottom_stress, clock, model_fields) / mᵢ * ℵᵢ + + ∂ⱼ_σ₂ⱼ(i, j, kᴺ, grid, rheology, clock, model_fields) / mᵢ + + sum_of_forcing_v(i, j, kᴺ, grid, rheology, v_forcing, model_fields, Δt)) # sum of user defined forcing and possibly other forcing terms that are rheology-dependent Gⱽ = ifelse(mᵢ ≤ 0, zero(grid), Gⱽ) diff --git a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl index bce1b53a..9c5d0aae 100644 --- a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl @@ -47,8 +47,6 @@ function time_step_momentum!(model, dynamics::SplitExplicitMomentumEquation, Δt u_forcing = model.forcing.u v_forcing = model.forcing.v - u_immersed_bc = u.boundary_conditions.immersed - v_immersed_bc = v.boundary_conditions.immersed model_fields = merge(dynamics.auxiliary_fields, model.velocities, (; h = model.ice_thickness, @@ -76,24 +74,24 @@ function time_step_momentum!(model, dynamics::SplitExplicitMomentumEquation, Δt u_velocity_kernel!(u, grid, Δt, substeps, rheology, model_fields, ocean_velocities, clock, coriolis, minimum_mass, minimum_concentration, - u_immersed_bc, top_stress, bottom_stress, u_forcing) + top_stress, bottom_stress, u_forcing) v_velocity_kernel!(v, grid, Δt, substeps, rheology, model_fields, ocean_velocities, clock, coriolis, minimum_mass, minimum_concentration, - v_immersed_bc, top_stress, bottom_stress, v_forcing) + top_stress, bottom_stress, v_forcing) else v_velocity_kernel!(v, grid, Δt, substeps, rheology, model_fields, ocean_velocities, clock, coriolis, minimum_mass, minimum_concentration, - v_immersed_bc, top_stress, bottom_stress, v_forcing) + top_stress, bottom_stress, v_forcing) u_velocity_kernel!(u, grid, Δt, substeps, rheology, model_fields, ocean_velocities, clock, coriolis, minimum_mass, minimum_concentration, - u_immersed_bc, top_stress, bottom_stress, u_forcing) + top_stress, bottom_stress, u_forcing) end # TODO: This needs to be removed in some way! @@ -111,7 +109,7 @@ end model_fields, ocean_velocities, clock, coriolis, minimum_mass, minimum_concentration, - u_immersed_bc, u_top_stress, u_bottom_stress, u_forcing) + u_top_stress, u_bottom_stress, u_forcing) i, j = @index(Global, NTuple) kᴺ = size(grid, 3) @@ -120,7 +118,7 @@ end ℵᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, model_fields.ℵ) Δτ = compute_substep_Δtᶠᶠᶜ(i, j, grid, Δt, rheology, substeps, model_fields) - Gu = u_velocity_tendency(i, j, grid, Δτ, rheology, model_fields, clock, coriolis, u_immersed_bc, u_top_stress, u_bottom_stress, u_forcing) + Gu = u_velocity_tendency(i, j, grid, Δτ, rheology, model_fields, clock, coriolis, u_top_stress, u_bottom_stress, u_forcing) # Implicit part of the stress that depends linearly on the velocity τuᵢ = ( implicit_τx_coefficient(i, j, kᴺ, grid, u_bottom_stress, clock, model_fields) @@ -142,7 +140,7 @@ end model_fields, ocean_velocities, clock, coriolis, minimum_mass, minimum_concentration, - v_immersed_bc, v_top_stress, v_bottom_stress, v_forcing) + v_top_stress, v_bottom_stress, v_forcing) i, j = @index(Global, NTuple) kᴺ = size(grid, 3) @@ -151,7 +149,7 @@ end ℵᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, model_fields.ℵ) Δτ = compute_substep_Δtᶠᶠᶜ(i, j, grid, Δt, rheology, substeps, model_fields) - Gv = v_velocity_tendency(i, j, grid, Δτ, rheology, model_fields, clock, coriolis, v_immersed_bc, v_top_stress, v_bottom_stress, v_forcing) + Gv = v_velocity_tendency(i, j, grid, Δτ, rheology, model_fields, clock, coriolis, v_top_stress, v_bottom_stress, v_forcing) # Implicit part of the stress that depends linearly on the velocity τvᵢ = ( implicit_τy_coefficient(i, j, kᴺ, grid, v_bottom_stress, clock, model_fields) From 1fce55292467626fb30e8ed8538f8de4557722d4 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 6 May 2025 09:05:30 +0200 Subject: [PATCH 097/108] not here for the moment --- src/Rheologies/Rheologies.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Rheologies/Rheologies.jl b/src/Rheologies/Rheologies.jl index d4228b00..e9f44652 100644 --- a/src/Rheologies/Rheologies.jl +++ b/src/Rheologies/Rheologies.jl @@ -27,6 +27,5 @@ rheology_auxiliary_fields(rheology, grid) = NamedTuple() include("ice_stress_divergence.jl") include("viscous_rheology.jl") include("elasto_visco_plastic_rheology.jl") -include("brittle_bingham_maxwell_rheology.jl") end \ No newline at end of file From 879674819b163d71c276439dd0983489d7985385 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 6 May 2025 09:08:48 +0200 Subject: [PATCH 098/108] fix tests --- test/test_time_stepping.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_time_stepping.jl b/test/test_time_stepping.jl index 967c0e37..440b734b 100644 --- a/test/test_time_stepping.jl +++ b/test/test_time_stepping.jl @@ -1,9 +1,9 @@ function time_step_sea_ice_model_works(grid; dynamics = nothing, - thermodynamics = nothing, + ice_thermodynamics = nothing, advection = nothing) - model = SeaIceModel(grid; dynamics, thermodynamics, advection) + model = SeaIceModel(grid; dynamics, ice_thermodynamics, advection) simulation = Simulation(model, Δt=1.0, stop_iteration=1) run!(simulation) @@ -26,7 +26,7 @@ end coriolises = (nothing, FPlane(latitude=45), BetaPlane(latitude=45)) solvers = (ExplicitSolver(), SplitExplicitSolver()) - for coriolis in coriolises, advection in advections, rheology in rheologies, ice_thermodynamics in ice_thermodynamics, solver in solvers + for coriolis in coriolises, advection in advections, rheology in rheologies, ice_thermodynamics in thermodynamics, solver in solvers dynamics = SeaIceMomentumEquation(grid; coriolis, rheology, solver) @test time_step_sea_ice_model_works(grid; From fe391962a71e2c0555a00d56855f86c45306b335 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 6 May 2025 09:15:35 +0200 Subject: [PATCH 099/108] no need for these --- .../explicit_momentum_equations.jl | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/SeaIceMomentumEquations/explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/explicit_momentum_equations.jl index 3455c433..9dfdfc2f 100644 --- a/src/SeaIceMomentumEquations/explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/explicit_momentum_equations.jl @@ -81,15 +81,11 @@ function compute_momentum_tendencies!(model, ::ExplicitMomentumEquation, Δt) top_stress = dynamics.external_momentum_stresses.top bottom_stress = dynamics.external_momentum_stresses.bottom - u_immersed_bc = model_fields.u.boundary_conditions.immersed - v_immersed_bc = model_fields.v.boundary_conditions.immersed - Gu = model.timestepper.Gⁿ.u Gv = model.timestepper.Gⁿ.v launch!(architecture(grid), grid, :xy, _compute_velocity_tendencies!, Gu, Gv, grid, Δt, rheology, model_fields, clock, coriolis, - u_immersed_bc, v_immersed_bc, top_stress, bottom_stress, model.forcing) return nothing @@ -97,14 +93,13 @@ end @kernel function _compute_velocity_tendencies!(Gu, Gv, grid, Δt, rheology, model_fields, clock, coriolis, - u_immersed_bc, v_immersed_bc, top_stress, bottom_stress, forcing) i, j = @index(Global, NTuple) @inbounds Gu[i, j, 1] = u_velocity_tendency(i, j, grid, Δt, rheology, model_fields, clock, coriolis, - u_immersed_bc, top_stress, bottom_stress, forcing.u) + top_stress, bottom_stress, forcing.u) @inbounds Gv[i, j, 1] = v_velocity_tendency(i, j, grid, Δt, rheology, model_fields, clock, coriolis, - v_immersed_bc, top_stress, bottom_stress, forcing.v) + top_stress, bottom_stress, forcing.v) end \ No newline at end of file From 17837a59b4a8e9716671ca7660fe24e62f5153bd Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 6 May 2025 09:56:54 +0200 Subject: [PATCH 100/108] some more fixes --- .../brittle_bingham_maxwell_rheology.jl | 288 ++++++++++++++++++ src/Rheologies/ice_stress_divergence.jl | 9 +- src/Rheologies/viscous_rheology.jl | 8 +- 3 files changed, 297 insertions(+), 8 deletions(-) create mode 100644 src/Rheologies/brittle_bingham_maxwell_rheology.jl diff --git a/src/Rheologies/brittle_bingham_maxwell_rheology.jl b/src/Rheologies/brittle_bingham_maxwell_rheology.jl new file mode 100644 index 00000000..63b861f2 --- /dev/null +++ b/src/Rheologies/brittle_bingham_maxwell_rheology.jl @@ -0,0 +1,288 @@ +using Oceananigans.Grids: halo_size + +struct BrittleBinghamMaxwellRheology{FT, A} + ice_ridging_strength :: FT # compressive strength + ice_compaction_hardening :: FT # compaction hardening + ice_cohesion :: FT # cohesion + undamaged_elastic_modulus :: FT # minimum plastic parameter (transitions to viscous behaviour) + undamaged_viscous_relaxation_time :: FT # minimum number of substeps expressed as the dynamic coefficient + ridging_ice_thickness :: FT # maximum number of substeps expressed as the dynamic coefficient + poisson_ratio :: FT + friction_coefficient :: FT + maximum_compressive_strength :: FT + healing_constant :: FT + damage_parameter :: FT + interpolation_scheme :: A # Interpolation of cc variables to faces +end + +function BrittleBinghamMaxwellRheology(FT::DataType = Float64; + ice_ridging_strength = 1e4, + ice_compaction_hardening = 20, + ice_cohesion = 5.7e3, + undamaged_elastic_modulus = 5.96e8, + undamaged_viscous_relaxation_time = 1e7, + ridging_ice_thickness = 1, + poisson_ratio = 1 / 3, + friction_coefficient = 0.7, + maximum_compressive_strength = 2.9e7, + healing_constant = 26, # Ks + damage_parameter = 5) + + return BrittleBinghamMaxwellRheology(convert(FT, ice_ridging_strength), + convert(FT, ice_compaction_hardening), + convert(FT, ice_cohesion), + convert(FT, undamaged_elastic_modulus), + convert(FT, undamaged_viscous_relaxation_time), + convert(FT, ridging_ice_thickness), + convert(FT, poisson_ratio), + convert(FT, friction_coefficient), + convert(FT, maximum_compressive_strength), + convert(FT, healing_constant), + convert(FT, damage_parameter), + nothing) +end + +rheology_prognostic_tracers(::BrittleBinghamMaxwellRheology) = (:d, :σ₁₁, :σ₁₂, :σ₂₂) + +function rheology_auxiliary_fields(::BrittleBinghamMaxwellRheology, grid) + + # TODO: What about boundary conditions? + P = Field{Center, Center, Nothing}(grid) + E = Field{Center, Center, Nothing}(grid) + λ = Field{Center, Center, Nothing}(grid) + σ¹ = Field{Center, Center, Nothing}(grid) + σ² = Field{Center, Center, Nothing}(grid) + return (; P, E, λ, σ¹, σ²) +end + +# Extend the `adapt_structure` function for the ElastoViscoPlasticRheology +Adapt.adapt_structure(to, r::BrittleBinghamMaxwellRheology) = + BrittleBinghamMaxwellRheology(Adapt.adapt(to, r.ice_ridging_strength), + Adapt.adapt(to, r.ice_compaction_hardening), + Adapt.adapt(to, r.ice_cohesion), + Adapt.adapt(to, r.undamaged_elastic_modulus), + Adapt.adapt(to, r.undamaged_viscous_relaxation_time), + Adapt.adapt(to, r.ridging_ice_thickness), + Adapt.adapt(to, r.poisson_ratio), + Adapt.adapt(to, r.friction_coefficient), + Adapt.adapt(to, r.maximum_compressive_strength), + Adapt.adapt(to, r.healing_constant), + Adapt.adapt(to, r.damage_parameter), + Adapt.adapt(to, r.interpolation_scheme)) + +##### +##### Computation of the stresses +##### + +function initialize_rheology!(model, rheology::BrittleBinghamMaxwellRheology) + h = model.ice_thickness + ℵ = model.ice_concentration + + fields = model.dynamics.auxiliary_fields + + P★ = rheology.ice_ridging_strength + E★ = rheology.undamaged_elastic_modulus + λ★ = rheology.undamaged_viscous_relaxation_time + h★ = rheology.ridging_ice_thickness + α = rheology.damage_parameter + C = rheology.ice_compaction_hardening + d = model.tracers.d + + # compute on the whole grid including halos + parameters = KernelParameters(size(fields.P.data)[1:2], fields.P.data.offsets[1:2]) + launch!(architecture(model.grid), model.grid, parameters, _initialize_bbm_rheology!, fields, d, model.grid, P★, E★, λ★, h★, α, C, h, ℵ) + + return nothing +end + +@kernel function _initialize_bbm_rheology!(fields, d, grid, P★, E★, λ★, h★, α, C, h, ℵ) + i, j = @index(Global, NTuple) + @inbounds begin + ecc = exp(- C * (1 - ℵ[i, j, 1])) + # Center - Center fields + fields.P[i, j, 1] = P★ * (h[i, j, 1] / h★)^(3/2) * ecc + fields.E[i, j, 1] = E★ * ecc + fields.λ[i, j, 1] = λ★ * ecc^(α - 1) + # Heal the damage with a healing constant + + # Clamp the damage between 0 and a value close to 1 + dᵢ = d[i, j, 1] + d[i, j, 1] = clamp(dᵢ, zero(dᵢ), 99999 * one(dᵢ) / 100000) + end +end + +# Specific compute stresses for the EVP rheology +function compute_stresses!(model, dynamics, rheology::BrittleBinghamMaxwellRheology, Δt, Ns) + + grid = model.grid + arch = architecture(grid) + + ρᵢ = model.ice_density + u, v = model.velocities + fields = dynamics.auxiliary_fields + tracers = model.tracers + + Nx, Ny, _ = size(grid) + Hx, Hy, _ = halo_size(grid) + + parameters = KernelParameters(-Hx+2:Nx+Hx-1, -Hy+2:Ny+Hy-1) + + # Pretty simple timestepping + Δτ = Δt / Ns + + launch!(arch, grid, parameters, _compute_stress_predictors!, fields, grid, rheology, tracers, u, v, ρᵢ, Δτ) + launch!(arch, grid, parameters, _advance_stresses_and_damage!, tracers, grid, rheology, fields, ρᵢ, Δτ) + + return nothing +end + +@inline function critical_damage(i, j, k, grid, N, c, μ, fields) + σ¹ = @inbounds fields.σ¹[i, j, k] + σ² = @inbounds fields.σ²[i, j, k] + dc = one(grid) - ifelse(σ¹ > - N, c / (σ² + μ * σ¹), - N / σ¹) + dc = ifelse(isnan(dc), zero(grid), dc) + return dc * (σ² > c - μ * σ¹) +end + +@kernel function _compute_stress_predictors!(fields, grid, rheology, tracers, u, v, ρᵢ, Δτ) + i, j = @index(Global, NTuple) + + α = rheology.damage_parameter + ν = rheology.poisson_ratio + + ϵ̇₁₁ = strain_rate_xx(i, j, 1, grid, u, v) + ϵ̇₂₂ = strain_rate_yy(i, j, 1, grid, u, v) + ϵ̇₁₂ = strain_rate_xy(i, j, 1, grid, u, v) + + Kϵ₁₁ = (ϵ̇₁₁ + ν * ϵ̇₂₂) / (1 - ν^2) + Kϵ₂₂ = (ϵ̇₂₂ + ν * ϵ̇₁₁) / (1 - ν^2) + Kϵ₁₂ = (1 - ν) * ϵ̇₁₂ / (1 - ν^2) + + dᵢ = @inbounds tracers.d[i, j, 1] + P = @inbounds fields.P[i, j, 1] + E₀ = @inbounds fields.E[i, j, 1] + λ₀ = @inbounds fields.λ[i, j, 1] + + E = E₀ * (1 - dᵢ) + λ = λ₀ * (1 - dᵢ)^(α - 1) + + # Test which isotropic stress to use + P̃ = zero(grid) + + # Implicit diagonal operator + Ω = 1 / (1 + Δτ * (1 + P̃) / λ) + + @inbounds tracers.σ₁₁[i, j, 1] = Ω * (tracers.σ₁₁[i, j, 1] + Δτ * E * Kϵ₁₁) + @inbounds tracers.σ₂₂[i, j, 1] = Ω * (tracers.σ₂₂[i, j, 1] + Δτ * E * Kϵ₂₂) + @inbounds tracers.σ₁₂[i, j, 1] = Ω * (tracers.σ₁₂[i, j, 1] + Δτ * E * Kϵ₁₂) + + σ₁₁ = @inbounds tracers.σ₁₁[i, j, 1] + σ₂₂ = @inbounds tracers.σ₂₂[i, j, 1] + σ₁₂ = @inbounds tracers.σ₁₂[i, j, 1] + + # Principal stress components + @inbounds fields.σ¹[i, j, 1] = (σ₁₁ + σ₂₂) / 2 + @inbounds fields.σ²[i, j, 1] = sqrt((σ₁₁ - σ₂₂)^2 / 4 + σ₁₂^2) +end + +@kernel function _advance_stresses_and_damage!(tracers, grid, rheology, fields, ρᵢ, Δτ) + i, j = @index(Global, NTuple) + + α = rheology.damage_parameter + ν = rheology.poisson_ratio + N = rheology.maximum_compressive_strength + c = rheology.ice_cohesion + μ = rheology.friction_coefficient + + ρ = @inbounds ρᵢ[i, j, 1] + dᵢ = @inbounds tracers.d[i, j, 1] + E₀ = @inbounds fields.E[i, j, 1] + + # The relaxation time is time-dependent + E = E₀ * (1 - dᵢ) + td = sqrt(2 * (1 + ν) * ρ / E * Azᶜᶜᶜ(i, j, 1, grid)) + + # damage tendency + Gd = reconstruction_2d(i, j, 1, grid, critical_damage, N, c, μ, fields) / td + + # Damage update + dᵢ += Gd * (1 - dᵢ) * Δτ + + # Advance stresses and clamp damage + @inbounds tracers.σ₁₁[i, j, 1] -= Gd * Δτ * tracers.σ₁₁[i, j, 1] + @inbounds tracers.σ₂₂[i, j, 1] -= Gd * Δτ * tracers.σ₂₂[i, j, 1] + @inbounds tracers.σ₁₂[i, j, 1] -= Gd * Δτ * tracers.σ₁₂[i, j, 1] + @inbounds tracers.d[i, j, 1] = clamp(dᵢ, zero(grid), 99999 * one(grid) / 100000) +end + +##### +##### Methods for the BBM rheology +##### + +# In the BBM rheology, the stresses need to be vertically integrated +@inline hσ₁₁(i, j, k, grid, fields) = @inbounds fields.σ₁₁[i, j, k] * fields.h[i, j, k] +@inline hσ₂₂(i, j, k, grid, fields) = @inbounds fields.σ₂₂[i, j, k] * fields.h[i, j, k] +@inline hσ₁₂(i, j, k, grid, fields) = @inbounds fields.σ₁₂[i, j, k] * fields.h[i, j, k] + +# Here we extend all the functions that a rheology model needs to support: +@inline ice_stress_ux(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, hσ₁₁, fields) +@inline ice_stress_uy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₁₂, fields) +@inline ice_stress_vx(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, hσ₁₂, fields) +@inline ice_stress_vy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₂₂, fields) + +# Another formulation of the critical Damage + +# @inline function dc_perpendicular(i, j, k, grid, N, c, μ, fields) + +# σ¹ = @inbounds fields.σ¹[i, j, k] +# σ² = @inbounds fields.σ²[i, j, k] + +# m = tand(90 - atand(μ)) +# q = σ² - m * σ¹ + +# # # Move towards the yield curve in a perpendicular fashion +# σᵪ¹ = (c - q) / (m + μ) +# σᵪ² = m * σᵪ¹ + q + +# dc = one(grid) - sqrt(σᵪ¹^2 + σᵪ²^2) / sqrt(σ¹^2 + σ²^2) +# dc = ifelse(isnan(dc), zero(grid), dc) + +# return dc * (σ² > c - μ * σ¹) +# end +# +# Dcrit needs to be reconstructed +# +@inline function reconstruction_2d(i, j, k, grid, f, args...) + fij = f(i, j, k, grid, args...) + fmj = f(i-1, j, k, grid, args...) + fpj = f(i+1, j, k, grid, args...) + fim = f(i, j-1, k, grid, args...) + fip = f(i, j+1, k, grid, args...) + fmm = f(i-1, j-1, k, grid, args...) + fmp = f(i-1, j+1, k, grid, args...) + fpm = f(i+1, j-1, k, grid, args...) + fpp = f(i+1, j+1, k, grid, args...) + + # remove NaNs + isnanij = isnan(fij) + isnanmj = isnan(fmj) + isnanpj = isnan(fpj) + isnanim = isnan(fim) + isnanip = isnan(fip) + isnanmm = isnan(fmm) + isnanmp = isnan(fmp) + isnanpm = isnan(fpm) + isnanpp = isnan(fpp) + + fij = ifelse(isnanij, zero(grid), fij) / 4 + fmj = ifelse(isnanmj, zero(grid), fmj) / 8 + fpj = ifelse(isnanpj, zero(grid), fpj) / 8 + fim = ifelse(isnanim, zero(grid), fim) / 8 + fip = ifelse(isnanip, zero(grid), fip) / 8 + fmm = ifelse(isnanmm, zero(grid), fmm) / 16 + fmp = ifelse(isnanmp, zero(grid), fmp) / 16 + fpm = ifelse(isnanpm, zero(grid), fpm) / 16 + fpp = ifelse(isnanpp, zero(grid), fpp) / 16 + + return (fij + fmj + fpj + fim + fip + fmm + fmp + fpm + fpp) +end \ No newline at end of file diff --git a/src/Rheologies/ice_stress_divergence.jl b/src/Rheologies/ice_stress_divergence.jl index 6a68a721..ffb7ba2c 100644 --- a/src/Rheologies/ice_stress_divergence.jl +++ b/src/Rheologies/ice_stress_divergence.jl @@ -2,6 +2,7 @@ using Oceananigans.ImmersedBoundaries: ImmersedBoundaryGrid, ImmersedBoundaryCondition using Oceananigans.Advection:conditional_flux_ccc, conditional_flux_ffc +using Oceananigans.Operators using Oceananigans.Operators: index_left, index_right const IBG = ImmersedBoundaryGrid @@ -24,11 +25,11 @@ const f = Face() ##### @inline function ∂ⱼ_σ₁ⱼ(i, j, k, grid, rheology, clock, fields) - return 1 / Azᶠᶠᶜ(i, j, k, grid) * (δxᶠᶠᶜ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_ux, rheology, clock, fields) + - δyᶠᶠᶜ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_uy, rheology, clock, fields)) + return Az⁻¹ᶠᶠᶜ(i, j, k, grid) * (δxᶠᶠᶜ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_ux, rheology, clock, fields) + + δyᶠᶠᶜ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_uy, rheology, clock, fields)) end @inline function ∂ⱼ_σ₂ⱼ(i, j, k, grid, rheology, clock, fields) - return 1 / Azᶠᶠᶜ(i, j, k, grid) * (δxᶠᶠᶜ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_vx, rheology, clock, fields) + - δyᶠᶠᶜ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_vy, rheology, clock, fields)) + return Az⁻¹ᶠᶠᶜ(i, j, k, grid) * (δxᶠᶠᶜ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_vx, rheology, clock, fields) + + δyᶠᶠᶜ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_vy, rheology, clock, fields)) end \ No newline at end of file diff --git a/src/Rheologies/viscous_rheology.jl b/src/Rheologies/viscous_rheology.jl index c011f9eb..47c31bc9 100644 --- a/src/Rheologies/viscous_rheology.jl +++ b/src/Rheologies/viscous_rheology.jl @@ -10,7 +10,7 @@ ViscousRheology(FT::DataType=Float64; ν = 1000.0) = ViscousRheology(convert_dif @inline viscosity_location(ν) = (Center(), Center(), Center()) @inline viscosity_location(ν::AbstractField) = location(ν) -@inline ice_stress_ux(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶜᶜᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δxᶜᶜᶜ(i, j, k, grid, fields.u) -@inline ice_stress_vx(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶠᶠᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δxᶠᶠᶜ(i, j, k, grid, fields.v) -@inline ice_stress_uy(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶠᶠᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δyᶠᶠᶜ(i, j, k, grid, fields.u) -@inline ice_stress_vy(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶜᶜᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δyᶜᶜᶜ(i, j, k, grid, fields.v) +@inline ice_stress_ux(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶜᶠᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δxᶜᶠᶜ(i, j, k, grid, fields.u) +@inline ice_stress_vx(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶜᶠᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δxᶜᶠᶜ(i, j, k, grid, fields.v) +@inline ice_stress_uy(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶜᶠᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δyᶠᶜᶜ(i, j, k, grid, fields.u) +@inline ice_stress_vy(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶜᶠᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δyᶠᶜᶜ(i, j, k, grid, fields.v) From 28e91a785ee4e2f813a3f8dcc3e4b7a5b18bc54f Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 6 May 2025 10:11:48 +0200 Subject: [PATCH 101/108] fixes for viscous rheology --- src/Rheologies/viscous_rheology.jl | 21 ++++++++++++++++--- .../explicit_momentum_equations.jl | 3 ++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Rheologies/viscous_rheology.jl b/src/Rheologies/viscous_rheology.jl index 47c31bc9..7ae0facb 100644 --- a/src/Rheologies/viscous_rheology.jl +++ b/src/Rheologies/viscous_rheology.jl @@ -1,10 +1,25 @@ +using Base: Callable using Oceananigans.Fields: AbstractField, location -using Oceananigans.TurbulenceClosures: νᶜᶜᶜ, νᶠᶜᶠ, νᶜᶠᶠ, νᶠᶠᶜ, convert_diffusivity +using Oceananigans.TurbulenceClosures: convert_diffusivity struct ViscousRheology{N} ν :: N end +const c = Center() +const f = Face() + +@inline νᶠᶜᶜ(i, j, k, grid, loc, ν::Number, args...) = ν +@inline νᶜᶠᶠ(i, j, k, grid, loc, ν::Number, args...) = ν + +# Array / Field at `Center, Center, Center` +const Lᶜᶜᵃ = Tuple{Center, Center, <:Any} +@inline νᶠᶜᶜ(i, j, k, grid, ::Lᶜᶜᵃ, ν::AbstractArray, args...) = ℑxᵃᶠᵃ(i, j, k, grid, ν) +@inline νᶜᶠᶜ(i, j, k, grid, ::Lᶜᶜᵃ, ν::AbstractArray, args...) = ℑyᵃᶠᵃ(i, j, k, grid, ν) + +@inline νᶠᶜᶜ(i, j, k, grid, loc, ν::Callable, clock, args...) = ν(node(i, j, k, grid, f, c, c)..., clock.time) +@inline νᶜᶠᶜ(i, j, k, grid, loc, ν::Callable, clock, args...) = ν(node(i, j, k, grid, c, f, c)..., clock.time) + ViscousRheology(FT::DataType=Float64; ν = 1000.0) = ViscousRheology(convert_diffusivity(FT, ν)) @inline viscosity_location(ν) = (Center(), Center(), Center()) @@ -12,5 +27,5 @@ ViscousRheology(FT::DataType=Float64; ν = 1000.0) = ViscousRheology(convert_dif @inline ice_stress_ux(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶜᶠᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δxᶜᶠᶜ(i, j, k, grid, fields.u) @inline ice_stress_vx(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶜᶠᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δxᶜᶠᶜ(i, j, k, grid, fields.v) -@inline ice_stress_uy(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶜᶠᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δyᶠᶜᶜ(i, j, k, grid, fields.u) -@inline ice_stress_vy(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶜᶠᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δyᶠᶜᶜ(i, j, k, grid, fields.v) +@inline ice_stress_uy(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶠᶜᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δyᶠᶜᶜ(i, j, k, grid, fields.u) +@inline ice_stress_vy(i, j, k, grid, vr::ViscousRheology, clock, fields) = νᶠᶜᶜ(i, j, k, grid, viscosity_location(vr.ν), vr.ν, clock, fields) * δyᶠᶜᶜ(i, j, k, grid, fields.v) diff --git a/src/SeaIceMomentumEquations/explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/explicit_momentum_equations.jl index 9dfdfc2f..72760cf3 100644 --- a/src/SeaIceMomentumEquations/explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/explicit_momentum_equations.jl @@ -38,7 +38,8 @@ end ocean_velocities, minimum_mass, minimum_concentration, clock, fields) i, j = @index(Global, NTuple) - + kᴺ = size(grid, 3) + ℵᶠᶠ = ℑxyᶠᶠᵃ(i, j, 1, grid, fields.ℵ) mᶠᶠ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, fields.h, fields.ℵ, fields.ρ) From e97332dad7a264c1ab49788a85798de1d09f6a15 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 6 May 2025 10:12:28 +0200 Subject: [PATCH 102/108] bugfix --- src/Rheologies/viscous_rheology.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rheologies/viscous_rheology.jl b/src/Rheologies/viscous_rheology.jl index 7ae0facb..1d192e96 100644 --- a/src/Rheologies/viscous_rheology.jl +++ b/src/Rheologies/viscous_rheology.jl @@ -10,7 +10,7 @@ const c = Center() const f = Face() @inline νᶠᶜᶜ(i, j, k, grid, loc, ν::Number, args...) = ν -@inline νᶜᶠᶠ(i, j, k, grid, loc, ν::Number, args...) = ν +@inline νᶜᶠᶜ(i, j, k, grid, loc, ν::Number, args...) = ν # Array / Field at `Center, Center, Center` const Lᶜᶜᵃ = Tuple{Center, Center, <:Any} From 0ba55eb49fe141e95bf9e8f9013673e46880d688 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Tue, 6 May 2025 10:42:56 +0200 Subject: [PATCH 103/108] some corrections --- src/Rheologies/Rheologies.jl | 2 +- src/Rheologies/ice_stress_divergence.jl | 8 ++++---- .../momentum_tendencies_kernel_functions.jl | 16 ++++++++-------- .../split_explicit_momentum_equations.jl | 16 ++++++++-------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Rheologies/Rheologies.jl b/src/Rheologies/Rheologies.jl index e9f44652..13b95308 100644 --- a/src/Rheologies/Rheologies.jl +++ b/src/Rheologies/Rheologies.jl @@ -9,7 +9,7 @@ using ClimaSeaIce: ice_mass # Nothing rheology initialize_rheology!(model, rheology) = nothing -compute_stresses!(model, dynamics, rheology, Δt) = nothing +compute_stresses!(model, dynamics, rheology, Δt, Ns) = nothing # Nothing rheology or viscous rheology @inline compute_substep_Δtᶠᶠᶜ(i, j, grid, Δt, rheology, substeps, fields) = Δt / substeps diff --git a/src/Rheologies/ice_stress_divergence.jl b/src/Rheologies/ice_stress_divergence.jl index ffb7ba2c..9f923b11 100644 --- a/src/Rheologies/ice_stress_divergence.jl +++ b/src/Rheologies/ice_stress_divergence.jl @@ -25,11 +25,11 @@ const f = Face() ##### @inline function ∂ⱼ_σ₁ⱼ(i, j, k, grid, rheology, clock, fields) - return Az⁻¹ᶠᶠᶜ(i, j, k, grid) * (δxᶠᶠᶜ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_ux, rheology, clock, fields) + - δyᶠᶠᶜ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_uy, rheology, clock, fields)) + return Az⁻¹ᶠᶠᶜ(i, j, k, grid) * (δxᶠᵃᵃ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_ux, rheology, clock, fields) + + δyᵃᶠᵃ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_uy, rheology, clock, fields)) end @inline function ∂ⱼ_σ₂ⱼ(i, j, k, grid, rheology, clock, fields) - return Az⁻¹ᶠᶠᶜ(i, j, k, grid) * (δxᶠᶠᶜ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_vx, rheology, clock, fields) + - δyᶠᶠᶜ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_vy, rheology, clock, fields)) + return Az⁻¹ᶠᶠᶜ(i, j, k, grid) * (δxᶠᵃᵃ(i, j, k, grid, Δy_qᶜᶠᶜ, _ice_stress_vx, rheology, clock, fields) + + δyᵃᶠᵃ(i, j, k, grid, Δx_qᶠᶜᶜ, _ice_stress_vy, rheology, clock, fields)) end \ No newline at end of file diff --git a/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl b/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl index d7133477..46b75690 100644 --- a/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl +++ b/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl @@ -16,13 +16,13 @@ using Oceananigans.Coriolis: fᶠᶠᵃ ρ = model_fields.ρ # Ice mass (per unit area) interpolated on u points - ℵᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, ℵ) - mᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, ice_mass, h, ℵ, ρ) + ℵᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ℵ) + mᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, h, ℵ, ρ) - @inbounds Gᵁ = ( + fᶠᶠᵃ(i, j, kᴺ, grid, coriolis) * model_fields.v[i, j, kᴺ] + @inbounds Gᵁ = ( + fᶠᶠᵃ(i, j, 1, grid, coriolis) * model_fields.v[i, j, 1] + explicit_τx(i, j, kᴺ, grid, u_top_stress, clock, model_fields) / mᵢ * ℵᵢ + explicit_τx(i, j, kᴺ, grid, u_bottom_stress, clock, model_fields) / mᵢ * ℵᵢ - + ∂ⱼ_σ₁ⱼ(i, j, kᴺ, grid, rheology, clock, model_fields) / mᵢ + + ∂ⱼ_σ₁ⱼ(i, j, 1, grid, rheology, clock, model_fields) / mᵢ + sum_of_forcing_u(i, j, kᴺ, grid, rheology, u_forcing, model_fields, Δt)) # sum of user defined forcing and possibly other forcing terms that are rheology-dependent Gᵁ = ifelse(mᵢ ≤ 0, zero(grid), Gᵁ) @@ -46,13 +46,13 @@ end ρ = model_fields.ρ # Ice mass (per unit area) interpolated on v points - ℵᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, ℵ) - mᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, ice_mass, h, ℵ, ρ) + ℵᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ℵ) + mᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, h, ℵ, ρ) - @inbounds Gⱽ = ( - fᶠᶠᵃ(i, j, kᴺ, grid, coriolis) * model_fields.u[i, j, kᴺ] + @inbounds Gⱽ = ( - fᶠᶠᵃ(i, j, 1, grid, coriolis) * model_fields.u[i, j, 1] + explicit_τy(i, j, kᴺ, grid, v_top_stress, clock, model_fields) / mᵢ * ℵᵢ + explicit_τy(i, j, kᴺ, grid, v_bottom_stress, clock, model_fields) / mᵢ * ℵᵢ - + ∂ⱼ_σ₂ⱼ(i, j, kᴺ, grid, rheology, clock, model_fields) / mᵢ + + ∂ⱼ_σ₂ⱼ(i, j, 1, grid, rheology, clock, model_fields) / mᵢ + sum_of_forcing_v(i, j, kᴺ, grid, rheology, v_forcing, model_fields, Δt)) # sum of user defined forcing and possibly other forcing terms that are rheology-dependent Gⱽ = ifelse(mᵢ ≤ 0, zero(grid), Gⱽ) diff --git a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl index 9c5d0aae..e495cd3e 100644 --- a/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl +++ b/src/SeaIceMomentumEquations/split_explicit_momentum_equations.jl @@ -114,8 +114,8 @@ end i, j = @index(Global, NTuple) kᴺ = size(grid, 3) - mᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, ice_mass, model_fields.h, model_fields.ℵ, model_fields.ρ) - ℵᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, model_fields.ℵ) + mᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, model_fields.h, model_fields.ℵ, model_fields.ρ) + ℵᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, model_fields.ℵ) Δτ = compute_substep_Δtᶠᶠᶜ(i, j, grid, Δt, rheology, substeps, model_fields) Gu = u_velocity_tendency(i, j, grid, Δτ, rheology, model_fields, clock, coriolis, u_top_stress, u_bottom_stress, u_forcing) @@ -125,14 +125,14 @@ end - implicit_τx_coefficient(i, j, kᴺ, grid, u_top_stress, clock, model_fields)) / mᵢ * ℵᵢ τuᵢ = ifelse(mᵢ ≤ 0, zero(grid), τuᵢ) - uᴰ = @inbounds (u[i, j, kᴺ] + Δτ * Gu) / (1 + Δτ * τuᵢ) # dynamical velocity + uᴰ = @inbounds (u[i, j, 1] + Δτ * Gu) / (1 + Δτ * τuᵢ) # dynamical velocity uᶠ = free_drift_u(i, j, kᴺ, grid, ocean_velocities) # free drift velocity # If the ice mass or the ice concentration are below a certain threshold, # the sea ice velocity is set to the free drift velocity sea_ice = (mᵢ ≥ minimum_mass) & (ℵᵢ ≥ minimum_concentration) - @inbounds u[i, j, kᴺ] = ifelse(sea_ice, uᴰ, uᶠ) + @inbounds u[i, j, 1] = ifelse(sea_ice, uᴰ, uᶠ) end @kernel function _v_velocity_step!(v, grid, Δt, @@ -145,8 +145,8 @@ end i, j = @index(Global, NTuple) kᴺ = size(grid, 3) - mᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, ice_mass, model_fields.h, model_fields.ℵ, model_fields.ρ) - ℵᵢ = ℑxyᶠᶠᵃ(i, j, kᴺ, grid, model_fields.ℵ) + mᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, model_fields.h, model_fields.ℵ, model_fields.ρ) + ℵᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, model_fields.ℵ) Δτ = compute_substep_Δtᶠᶠᶜ(i, j, grid, Δt, rheology, substeps, model_fields) Gv = v_velocity_tendency(i, j, grid, Δτ, rheology, model_fields, clock, coriolis, v_top_stress, v_bottom_stress, v_forcing) @@ -157,12 +157,12 @@ end τvᵢ = ifelse(mᵢ ≤ 0, zero(grid), τvᵢ) - vᴰ = @inbounds (v[i, j, kᴺ] + Δτ * Gv) / (1 + Δτ * τvᵢ)# dynamical velocity + vᴰ = @inbounds (v[i, j, 1] + Δτ * Gv) / (1 + Δτ * τvᵢ)# dynamical velocity vᶠ = free_drift_v(i, j, kᴺ, grid, ocean_velocities) # free drift velocity # If the ice mass or the ice concentration are below a certain threshold, # the sea ice velocity is set to the free drift velocity sea_ice = (mᵢ ≥ minimum_mass) & (ℵᵢ ≥ minimum_concentration) - @inbounds v[i, j, kᴺ] = ifelse(sea_ice, vᴰ, vᶠ) + @inbounds v[i, j, 1] = ifelse(sea_ice, vᴰ, vᶠ) end From dddf06db3c46944683873453dcc1785eaf42a8e3 Mon Sep 17 00:00:00 2001 From: simone-silvestri Date: Tue, 6 May 2025 04:59:10 -0400 Subject: [PATCH 104/108] fix the symmetric advection --- src/sea_ice_advection.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sea_ice_advection.jl b/src/sea_ice_advection.jl index 160500d9..c088a3de 100644 --- a/src/sea_ice_advection.jl +++ b/src/sea_ice_advection.jl @@ -30,13 +30,13 @@ end @inline function advective_sea_ice_tracer_flux_x(i, j, k, grid, scheme::CenteredScheme, u, c) ũ = ℑyᵃᶜᵃ(i, j, k, grid, Ax_qᶠᶠᶜ, u) - cᴿ = _symmetric_interpolate_xᶠᵃᵃ(i, j, k, grid, scheme, bias(ũ), c) + cᴿ = _symmetric_interpolate_xᶠᵃᵃ(i, j, k, grid, scheme, c) return ũ * cᴿ end @inline function advective_sea_ice_tracer_flux_y(i, j, k, grid, scheme::CenteredScheme, v, c) ṽ = ℑxᶜᵃᵃ(i, j, k, grid, Ay_qᶠᶠᶜ, v) - cᴿ = _symmetric_interpolate_yᵃᶠᵃ(i, j, k, grid, scheme, bias(ṽ), c) + cᴿ = _symmetric_interpolate_yᵃᶠᵃ(i, j, k, grid, scheme, c) return ṽ * cᴿ end From 89f729758254986f7a761c7c75eb7ff24dc1362a Mon Sep 17 00:00:00 2001 From: simone-silvestri Date: Tue, 6 May 2025 06:12:46 -0400 Subject: [PATCH 105/108] fix docs --- src/sea_ice_advection.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sea_ice_advection.jl b/src/sea_ice_advection.jl index c088a3de..6314dd83 100644 --- a/src/sea_ice_advection.jl +++ b/src/sea_ice_advection.jl @@ -13,8 +13,8 @@ using Oceananigans.Advection: _biased_interpolate_xᶠᵃᵃ, _biased_interpolat @inline _advective_sea_ice_tracer_flux_x(i, j, k, grid, scheme, u, c) = advective_sea_ice_tracer_flux_x(i, j, k, grid, scheme, u, c) @inline _advective_sea_ice_tracer_flux_y(i, j, k, grid, scheme, v, c) = advective_sea_ice_tracer_flux_y(i, j, k, grid, scheme, v, c) -@inline _advective_sea_ice_tracer_flux_x(i, j, k, ibg::IBG, scheme, u, c) = conditional_flux_fcc(i, j, k, ibg, zero(ibg), advective_sea_ice_tracer_flux_x(i, j, k, grid, scheme, u, c)) -@inline _advective_sea_ice_tracer_flux_y(i, j, k, ibg::IBG, scheme, v, c) = conditional_flux_cfc(i, j, k, ibg, zero(ibg), advective_sea_ice_tracer_flux_y(i, j, k, grid, scheme, v, c)) +@inline _advective_sea_ice_tracer_flux_x(i, j, k, ibg::IBG, scheme, u, c) = conditional_flux_fcc(i, j, k, ibg, zero(ibg), advective_sea_ice_tracer_flux_x(i, j, k, ibg, scheme, u, c)) +@inline _advective_sea_ice_tracer_flux_y(i, j, k, ibg::IBG, scheme, v, c) = conditional_flux_cfc(i, j, k, ibg, zero(ibg), advective_sea_ice_tracer_flux_y(i, j, k, ibg, scheme, v, c)) @inline function advective_sea_ice_tracer_flux_x(i, j, k, grid, scheme::UpwindScheme, u, c) ũ = ℑyᵃᶜᵃ(i, j, k, grid, Ax_qᶠᶠᶜ, u) From 459be185d6446fd4dc78e1f693cd517b7b599da8 Mon Sep 17 00:00:00 2001 From: simone-silvestri Date: Tue, 6 May 2025 06:16:04 -0400 Subject: [PATCH 106/108] fix the momentum tendencies --- src/Rheologies/elasto_visco_plastic_rheology.jl | 6 +++--- .../momentum_tendencies_kernel_functions.jl | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index 0d003ef5..17a7f801 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -175,9 +175,9 @@ function compute_stresses!(model, dynamics, rheology::ElastoViscoPlasticRheology return nothing end -@inline strain_rate_xx(i, j, k, grid, u, v) = ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, u) / Azᶜᶜᶜ(i, j, k, grid) -@inline strain_rate_yy(i, j, k, grid, u, v) = ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, v) / Azᶜᶜᶜ(i, j, k, grid) -@inline strain_rate_xy(i, j, k, grid, u, v) = (ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, v) + ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, u)) / Azᶜᶜᶜ(i, j, k, grid) / 2 +@inline strain_rate_xx(i, j, k, grid, u, v) = ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, u) * Az⁻¹ᶜᶜᶜ(i, j, k, grid) +@inline strain_rate_yy(i, j, k, grid, u, v) = ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, v) * Az⁻¹ᶜᶜᶜ(i, j, k, grid) +@inline strain_rate_xy(i, j, k, grid, u, v) = (ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, v) + ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, u)) * Az⁻¹ᶜᶜᶜ(i, j, k, grid) / 2 @kernel function _compute_evp_viscosities!(fields, grid, rheology, u, v, h, ℵ, ρᵢ, Δt) i, j = @index(Global, NTuple) diff --git a/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl b/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl index 46b75690..9d14330a 100644 --- a/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl +++ b/src/SeaIceMomentumEquations/momentum_tendencies_kernel_functions.jl @@ -20,7 +20,7 @@ using Oceananigans.Coriolis: fᶠᶠᵃ mᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, h, ℵ, ρ) @inbounds Gᵁ = ( + fᶠᶠᵃ(i, j, 1, grid, coriolis) * model_fields.v[i, j, 1] - + explicit_τx(i, j, kᴺ, grid, u_top_stress, clock, model_fields) / mᵢ * ℵᵢ + - explicit_τx(i, j, kᴺ, grid, u_top_stress, clock, model_fields) / mᵢ * ℵᵢ + explicit_τx(i, j, kᴺ, grid, u_bottom_stress, clock, model_fields) / mᵢ * ℵᵢ + ∂ⱼ_σ₁ⱼ(i, j, 1, grid, rheology, clock, model_fields) / mᵢ + sum_of_forcing_u(i, j, kᴺ, grid, rheology, u_forcing, model_fields, Δt)) # sum of user defined forcing and possibly other forcing terms that are rheology-dependent @@ -50,7 +50,7 @@ end mᵢ = ℑxyᶠᶠᵃ(i, j, 1, grid, ice_mass, h, ℵ, ρ) @inbounds Gⱽ = ( - fᶠᶠᵃ(i, j, 1, grid, coriolis) * model_fields.u[i, j, 1] - + explicit_τy(i, j, kᴺ, grid, v_top_stress, clock, model_fields) / mᵢ * ℵᵢ + - explicit_τy(i, j, kᴺ, grid, v_top_stress, clock, model_fields) / mᵢ * ℵᵢ + explicit_τy(i, j, kᴺ, grid, v_bottom_stress, clock, model_fields) / mᵢ * ℵᵢ + ∂ⱼ_σ₂ⱼ(i, j, 1, grid, rheology, clock, model_fields) / mᵢ + sum_of_forcing_v(i, j, kᴺ, grid, rheology, v_forcing, model_fields, Δt)) # sum of user defined forcing and possibly other forcing terms that are rheology-dependent From eb8a3f94dc84a4e2e7ec28ae1255f64e08715cc4 Mon Sep 17 00:00:00 2001 From: simone-silvestri Date: Tue, 6 May 2025 06:33:34 -0400 Subject: [PATCH 107/108] remove bbm for now --- .../brittle_bingham_maxwell_rheology.jl | 288 ------------------ 1 file changed, 288 deletions(-) delete mode 100644 src/Rheologies/brittle_bingham_maxwell_rheology.jl diff --git a/src/Rheologies/brittle_bingham_maxwell_rheology.jl b/src/Rheologies/brittle_bingham_maxwell_rheology.jl deleted file mode 100644 index 63b861f2..00000000 --- a/src/Rheologies/brittle_bingham_maxwell_rheology.jl +++ /dev/null @@ -1,288 +0,0 @@ -using Oceananigans.Grids: halo_size - -struct BrittleBinghamMaxwellRheology{FT, A} - ice_ridging_strength :: FT # compressive strength - ice_compaction_hardening :: FT # compaction hardening - ice_cohesion :: FT # cohesion - undamaged_elastic_modulus :: FT # minimum plastic parameter (transitions to viscous behaviour) - undamaged_viscous_relaxation_time :: FT # minimum number of substeps expressed as the dynamic coefficient - ridging_ice_thickness :: FT # maximum number of substeps expressed as the dynamic coefficient - poisson_ratio :: FT - friction_coefficient :: FT - maximum_compressive_strength :: FT - healing_constant :: FT - damage_parameter :: FT - interpolation_scheme :: A # Interpolation of cc variables to faces -end - -function BrittleBinghamMaxwellRheology(FT::DataType = Float64; - ice_ridging_strength = 1e4, - ice_compaction_hardening = 20, - ice_cohesion = 5.7e3, - undamaged_elastic_modulus = 5.96e8, - undamaged_viscous_relaxation_time = 1e7, - ridging_ice_thickness = 1, - poisson_ratio = 1 / 3, - friction_coefficient = 0.7, - maximum_compressive_strength = 2.9e7, - healing_constant = 26, # Ks - damage_parameter = 5) - - return BrittleBinghamMaxwellRheology(convert(FT, ice_ridging_strength), - convert(FT, ice_compaction_hardening), - convert(FT, ice_cohesion), - convert(FT, undamaged_elastic_modulus), - convert(FT, undamaged_viscous_relaxation_time), - convert(FT, ridging_ice_thickness), - convert(FT, poisson_ratio), - convert(FT, friction_coefficient), - convert(FT, maximum_compressive_strength), - convert(FT, healing_constant), - convert(FT, damage_parameter), - nothing) -end - -rheology_prognostic_tracers(::BrittleBinghamMaxwellRheology) = (:d, :σ₁₁, :σ₁₂, :σ₂₂) - -function rheology_auxiliary_fields(::BrittleBinghamMaxwellRheology, grid) - - # TODO: What about boundary conditions? - P = Field{Center, Center, Nothing}(grid) - E = Field{Center, Center, Nothing}(grid) - λ = Field{Center, Center, Nothing}(grid) - σ¹ = Field{Center, Center, Nothing}(grid) - σ² = Field{Center, Center, Nothing}(grid) - return (; P, E, λ, σ¹, σ²) -end - -# Extend the `adapt_structure` function for the ElastoViscoPlasticRheology -Adapt.adapt_structure(to, r::BrittleBinghamMaxwellRheology) = - BrittleBinghamMaxwellRheology(Adapt.adapt(to, r.ice_ridging_strength), - Adapt.adapt(to, r.ice_compaction_hardening), - Adapt.adapt(to, r.ice_cohesion), - Adapt.adapt(to, r.undamaged_elastic_modulus), - Adapt.adapt(to, r.undamaged_viscous_relaxation_time), - Adapt.adapt(to, r.ridging_ice_thickness), - Adapt.adapt(to, r.poisson_ratio), - Adapt.adapt(to, r.friction_coefficient), - Adapt.adapt(to, r.maximum_compressive_strength), - Adapt.adapt(to, r.healing_constant), - Adapt.adapt(to, r.damage_parameter), - Adapt.adapt(to, r.interpolation_scheme)) - -##### -##### Computation of the stresses -##### - -function initialize_rheology!(model, rheology::BrittleBinghamMaxwellRheology) - h = model.ice_thickness - ℵ = model.ice_concentration - - fields = model.dynamics.auxiliary_fields - - P★ = rheology.ice_ridging_strength - E★ = rheology.undamaged_elastic_modulus - λ★ = rheology.undamaged_viscous_relaxation_time - h★ = rheology.ridging_ice_thickness - α = rheology.damage_parameter - C = rheology.ice_compaction_hardening - d = model.tracers.d - - # compute on the whole grid including halos - parameters = KernelParameters(size(fields.P.data)[1:2], fields.P.data.offsets[1:2]) - launch!(architecture(model.grid), model.grid, parameters, _initialize_bbm_rheology!, fields, d, model.grid, P★, E★, λ★, h★, α, C, h, ℵ) - - return nothing -end - -@kernel function _initialize_bbm_rheology!(fields, d, grid, P★, E★, λ★, h★, α, C, h, ℵ) - i, j = @index(Global, NTuple) - @inbounds begin - ecc = exp(- C * (1 - ℵ[i, j, 1])) - # Center - Center fields - fields.P[i, j, 1] = P★ * (h[i, j, 1] / h★)^(3/2) * ecc - fields.E[i, j, 1] = E★ * ecc - fields.λ[i, j, 1] = λ★ * ecc^(α - 1) - # Heal the damage with a healing constant - - # Clamp the damage between 0 and a value close to 1 - dᵢ = d[i, j, 1] - d[i, j, 1] = clamp(dᵢ, zero(dᵢ), 99999 * one(dᵢ) / 100000) - end -end - -# Specific compute stresses for the EVP rheology -function compute_stresses!(model, dynamics, rheology::BrittleBinghamMaxwellRheology, Δt, Ns) - - grid = model.grid - arch = architecture(grid) - - ρᵢ = model.ice_density - u, v = model.velocities - fields = dynamics.auxiliary_fields - tracers = model.tracers - - Nx, Ny, _ = size(grid) - Hx, Hy, _ = halo_size(grid) - - parameters = KernelParameters(-Hx+2:Nx+Hx-1, -Hy+2:Ny+Hy-1) - - # Pretty simple timestepping - Δτ = Δt / Ns - - launch!(arch, grid, parameters, _compute_stress_predictors!, fields, grid, rheology, tracers, u, v, ρᵢ, Δτ) - launch!(arch, grid, parameters, _advance_stresses_and_damage!, tracers, grid, rheology, fields, ρᵢ, Δτ) - - return nothing -end - -@inline function critical_damage(i, j, k, grid, N, c, μ, fields) - σ¹ = @inbounds fields.σ¹[i, j, k] - σ² = @inbounds fields.σ²[i, j, k] - dc = one(grid) - ifelse(σ¹ > - N, c / (σ² + μ * σ¹), - N / σ¹) - dc = ifelse(isnan(dc), zero(grid), dc) - return dc * (σ² > c - μ * σ¹) -end - -@kernel function _compute_stress_predictors!(fields, grid, rheology, tracers, u, v, ρᵢ, Δτ) - i, j = @index(Global, NTuple) - - α = rheology.damage_parameter - ν = rheology.poisson_ratio - - ϵ̇₁₁ = strain_rate_xx(i, j, 1, grid, u, v) - ϵ̇₂₂ = strain_rate_yy(i, j, 1, grid, u, v) - ϵ̇₁₂ = strain_rate_xy(i, j, 1, grid, u, v) - - Kϵ₁₁ = (ϵ̇₁₁ + ν * ϵ̇₂₂) / (1 - ν^2) - Kϵ₂₂ = (ϵ̇₂₂ + ν * ϵ̇₁₁) / (1 - ν^2) - Kϵ₁₂ = (1 - ν) * ϵ̇₁₂ / (1 - ν^2) - - dᵢ = @inbounds tracers.d[i, j, 1] - P = @inbounds fields.P[i, j, 1] - E₀ = @inbounds fields.E[i, j, 1] - λ₀ = @inbounds fields.λ[i, j, 1] - - E = E₀ * (1 - dᵢ) - λ = λ₀ * (1 - dᵢ)^(α - 1) - - # Test which isotropic stress to use - P̃ = zero(grid) - - # Implicit diagonal operator - Ω = 1 / (1 + Δτ * (1 + P̃) / λ) - - @inbounds tracers.σ₁₁[i, j, 1] = Ω * (tracers.σ₁₁[i, j, 1] + Δτ * E * Kϵ₁₁) - @inbounds tracers.σ₂₂[i, j, 1] = Ω * (tracers.σ₂₂[i, j, 1] + Δτ * E * Kϵ₂₂) - @inbounds tracers.σ₁₂[i, j, 1] = Ω * (tracers.σ₁₂[i, j, 1] + Δτ * E * Kϵ₁₂) - - σ₁₁ = @inbounds tracers.σ₁₁[i, j, 1] - σ₂₂ = @inbounds tracers.σ₂₂[i, j, 1] - σ₁₂ = @inbounds tracers.σ₁₂[i, j, 1] - - # Principal stress components - @inbounds fields.σ¹[i, j, 1] = (σ₁₁ + σ₂₂) / 2 - @inbounds fields.σ²[i, j, 1] = sqrt((σ₁₁ - σ₂₂)^2 / 4 + σ₁₂^2) -end - -@kernel function _advance_stresses_and_damage!(tracers, grid, rheology, fields, ρᵢ, Δτ) - i, j = @index(Global, NTuple) - - α = rheology.damage_parameter - ν = rheology.poisson_ratio - N = rheology.maximum_compressive_strength - c = rheology.ice_cohesion - μ = rheology.friction_coefficient - - ρ = @inbounds ρᵢ[i, j, 1] - dᵢ = @inbounds tracers.d[i, j, 1] - E₀ = @inbounds fields.E[i, j, 1] - - # The relaxation time is time-dependent - E = E₀ * (1 - dᵢ) - td = sqrt(2 * (1 + ν) * ρ / E * Azᶜᶜᶜ(i, j, 1, grid)) - - # damage tendency - Gd = reconstruction_2d(i, j, 1, grid, critical_damage, N, c, μ, fields) / td - - # Damage update - dᵢ += Gd * (1 - dᵢ) * Δτ - - # Advance stresses and clamp damage - @inbounds tracers.σ₁₁[i, j, 1] -= Gd * Δτ * tracers.σ₁₁[i, j, 1] - @inbounds tracers.σ₂₂[i, j, 1] -= Gd * Δτ * tracers.σ₂₂[i, j, 1] - @inbounds tracers.σ₁₂[i, j, 1] -= Gd * Δτ * tracers.σ₁₂[i, j, 1] - @inbounds tracers.d[i, j, 1] = clamp(dᵢ, zero(grid), 99999 * one(grid) / 100000) -end - -##### -##### Methods for the BBM rheology -##### - -# In the BBM rheology, the stresses need to be vertically integrated -@inline hσ₁₁(i, j, k, grid, fields) = @inbounds fields.σ₁₁[i, j, k] * fields.h[i, j, k] -@inline hσ₂₂(i, j, k, grid, fields) = @inbounds fields.σ₂₂[i, j, k] * fields.h[i, j, k] -@inline hσ₁₂(i, j, k, grid, fields) = @inbounds fields.σ₁₂[i, j, k] * fields.h[i, j, k] - -# Here we extend all the functions that a rheology model needs to support: -@inline ice_stress_ux(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, hσ₁₁, fields) -@inline ice_stress_uy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₁₂, fields) -@inline ice_stress_vx(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑyᵃᶠᵃ(i, j, 1, grid, hσ₁₂, fields) -@inline ice_stress_vy(i, j, k, grid, ::BrittleBinghamMaxwellRheology, clock, fields) = ℑxᶠᵃᵃ(i, j, 1, grid, hσ₂₂, fields) - -# Another formulation of the critical Damage - -# @inline function dc_perpendicular(i, j, k, grid, N, c, μ, fields) - -# σ¹ = @inbounds fields.σ¹[i, j, k] -# σ² = @inbounds fields.σ²[i, j, k] - -# m = tand(90 - atand(μ)) -# q = σ² - m * σ¹ - -# # # Move towards the yield curve in a perpendicular fashion -# σᵪ¹ = (c - q) / (m + μ) -# σᵪ² = m * σᵪ¹ + q - -# dc = one(grid) - sqrt(σᵪ¹^2 + σᵪ²^2) / sqrt(σ¹^2 + σ²^2) -# dc = ifelse(isnan(dc), zero(grid), dc) - -# return dc * (σ² > c - μ * σ¹) -# end -# -# Dcrit needs to be reconstructed -# -@inline function reconstruction_2d(i, j, k, grid, f, args...) - fij = f(i, j, k, grid, args...) - fmj = f(i-1, j, k, grid, args...) - fpj = f(i+1, j, k, grid, args...) - fim = f(i, j-1, k, grid, args...) - fip = f(i, j+1, k, grid, args...) - fmm = f(i-1, j-1, k, grid, args...) - fmp = f(i-1, j+1, k, grid, args...) - fpm = f(i+1, j-1, k, grid, args...) - fpp = f(i+1, j+1, k, grid, args...) - - # remove NaNs - isnanij = isnan(fij) - isnanmj = isnan(fmj) - isnanpj = isnan(fpj) - isnanim = isnan(fim) - isnanip = isnan(fip) - isnanmm = isnan(fmm) - isnanmp = isnan(fmp) - isnanpm = isnan(fpm) - isnanpp = isnan(fpp) - - fij = ifelse(isnanij, zero(grid), fij) / 4 - fmj = ifelse(isnanmj, zero(grid), fmj) / 8 - fpj = ifelse(isnanpj, zero(grid), fpj) / 8 - fim = ifelse(isnanim, zero(grid), fim) / 8 - fip = ifelse(isnanip, zero(grid), fip) / 8 - fmm = ifelse(isnanmm, zero(grid), fmm) / 16 - fmp = ifelse(isnanmp, zero(grid), fmp) / 16 - fpm = ifelse(isnanpm, zero(grid), fpm) / 16 - fpp = ifelse(isnanpp, zero(grid), fpp) / 16 - - return (fij + fmj + fpj + fim + fip + fmm + fmp + fpm + fpp) -end \ No newline at end of file From 92639cf89fe6de6aeda9d5ab16fff6a2527710c4 Mon Sep 17 00:00:00 2001 From: Simone Silvestri Date: Wed, 14 May 2025 09:04:15 +0200 Subject: [PATCH 108/108] correct the tendencies --- src/Rheologies/elasto_visco_plastic_rheology.jl | 2 +- src/forward_euler_timestepper.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Rheologies/elasto_visco_plastic_rheology.jl b/src/Rheologies/elasto_visco_plastic_rheology.jl index 17a7f801..502f6e27 100644 --- a/src/Rheologies/elasto_visco_plastic_rheology.jl +++ b/src/Rheologies/elasto_visco_plastic_rheology.jl @@ -177,7 +177,7 @@ end @inline strain_rate_xx(i, j, k, grid, u, v) = ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, u) * Az⁻¹ᶜᶜᶜ(i, j, k, grid) @inline strain_rate_yy(i, j, k, grid, u, v) = ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, v) * Az⁻¹ᶜᶜᶜ(i, j, k, grid) -@inline strain_rate_xy(i, j, k, grid, u, v) = (ℑyᵃᶜᵃ(i, j, k, grid, δxᶜᵃᵃ, Δy_qᶠᶠᶜ, v) + ℑxᶜᵃᵃ(i, j, k, grid, δyᵃᶜᵃ, Δx_qᶠᶠᶜ, u)) * Az⁻¹ᶜᶜᶜ(i, j, k, grid) / 2 +@inline strain_rate_xy(i, j, k, grid, u, v) = (δxᶜᵃᵃ(i, j, k, grid, ℑyᵃᶜᵃ, Δy_qᶠᶠᶜ, v) + δyᵃᶜᵃ(i, j, k, grid, ℑxᶜᵃᵃ, Δx_qᶠᶠᶜ, u)) * Az⁻¹ᶜᶜᶜ(i, j, k, grid) / 2 @kernel function _compute_evp_viscosities!(fields, grid, rheology, u, v, h, ℵ, ρᵢ, Δt) i, j = @index(Global, NTuple) diff --git a/src/forward_euler_timestepper.jl b/src/forward_euler_timestepper.jl index 8fe7b17f..4b7e7242 100644 --- a/src/forward_euler_timestepper.jl +++ b/src/forward_euler_timestepper.jl @@ -11,7 +11,7 @@ end """ ForwardEulerTimeStepper(grid, prognostic_fields; implicit_solver = nothing, - Gⁿ = map(similar, prognostic_fields)) + Gⁿ = map(deepcopy, prognostic_fields)) Return a 1st-order Forward-Euler (FE) time stepper (`ForwardEulerTimeStepper`) on `grid`, with `tracers`. The tendency fields `Gⁿ`, usually equal to @@ -27,7 +27,7 @@ at the ``n``-th timestep. """ function ForwardEulerTimeStepper(grid, prognostic_fields; implicit_solver::IT = nothing, - Gⁿ = map(similar, prognostic_fields)) where IT + Gⁿ = map(deepcopy, prognostic_fields)) where IT FT = eltype(grid) GT = typeof(Gⁿ)