Skip to content

Commit 740c779

Browse files
Implement sea-ice ocean stress (#453)
* add sea-ice ocean stress * last index * add this * add a clock * add the arctic * add the metadatum * thos works! * bugfix * Update experiments/arctic_simulation.jl * Update src/OceanSeaIceModels/InterfaceComputations/sea_ice_ocean_fluxes.jl * add a test * remember the allowscalar * more testing * fix test * fix tests * Update test/test_surface_fluxes.jl * Update test/test_surface_fluxes.jl --------- Co-authored-by: Navid C. Constantinou <[email protected]>
1 parent 5a7ea23 commit 740c779

File tree

5 files changed

+141
-65
lines changed

5 files changed

+141
-65
lines changed

experiments/arctic_simulation.jl

+18-28
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ ocean = ocean_simulation(grid;
5252

5353
dataset = ECCO4Monthly()
5454

55-
set!(ocean.model, T=Metadata(:temperature; dataset),
56-
S=Metadata(:salinity; dataset))
55+
set!(ocean.model, T=Metadatum(:temperature; dataset),
56+
S=Metadatum(:salinity; dataset))
5757

5858
#####
5959
##### A Prognostic Sea-ice model
@@ -63,12 +63,11 @@ using ClimaSeaIce.SeaIceMomentumEquations
6363
using ClimaSeaIce.Rheologies
6464

6565
# Remember to pass the SSS as a bottom bc to the sea ice!
66-
SSS = view(ocean.model.tracers.S, :, :, grid.Nz)
66+
SSS = view(ocean.model.tracers.S.data, :, :, grid.Nz)
6767
bottom_heat_boundary_condition = IceWaterThermalEquilibrium(SSS)
6868

6969
SSU = view(ocean.model.velocities.u, :, :, grid.Nz)
7070
SSV = view(ocean.model.velocities.u, :, :, grid.Nz)
71-
7271
τo = SemiImplicitStress(uₑ=SSU, vₑ=SSV)
7372
τua = Field{Face, Center, Nothing}(grid)
7473
τva = Field{Center, Face, Nothing}(grid)
@@ -77,14 +76,14 @@ dynamics = SeaIceMomentumEquation(grid;
7776
coriolis = ocean.model.coriolis,
7877
top_momentum_stress = (u=τua, v=τva),
7978
bottom_momentum_stress = τo,
80-
ocean_velocities = (u=SSU, v=SSV),
79+
ocean_velocities = (u=0.1*SSU, v=0.1*SSV),
8180
rheology = ElastoViscoPlasticRheology(),
8281
solver = SplitExplicitSolver(120))
8382

8483
sea_ice = sea_ice_simulation(grid; bottom_heat_boundary_condition, dynamics, advection=WENO(order=7))
8584

86-
set!(sea_ice.model, h=Metadata(:sea_ice_thickness; dataset),
87-
=Metadata(:sea_ice_concentration; dataset))
85+
set!(sea_ice.model, h=Metadatum(:sea_ice_thickness; dataset),
86+
=Metadatum(:sea_ice_concentration; dataset))
8887

8988
#####
9089
##### A Prescribed Atmosphere model
@@ -134,19 +133,20 @@ using Statistics
134133

135134
function progress(sim)
136135
sea_ice = sim.model.sea_ice
137-
hmax = maximum(sea_ice.model.ice_thickness)
138-
ℵmax = maximum(sea_ice.model.ice_concentration)
139-
hmean = mean(sea_ice.model.ice_thickness)
140-
ℵmean = mean(sea_ice.model.ice_concentration)
141-
Tmax = maximum(sim.model.interfaces.atmosphere_sea_ice_interface.temperature)
142-
Tmin = minimum(sim.model.interfaces.atmosphere_sea_ice_interface.temperature)
136+
ocean = sim.model.ocean
137+
hmax = maximum(sea_ice.model.ice_thickness)
138+
ℵmax = maximum(sea_ice.model.ice_concentration)
139+
uimax = maximum(abs, sea_ice.model.velocities.u)
140+
vimax = maximum(abs, sea_ice.model.velocities.v)
141+
uomax = maximum(abs, ocean.model.velocities.u)
142+
vomax = maximum(abs, ocean.model.velocities.v)
143143

144144
step_time = 1e-9 * (time_ns() - wall_time[])
145145

146146
msg1 = @sprintf("time: %s, iteration: %d, Δt: %s, ", prettytime(sim), iteration(sim), prettytime(sim.Δt))
147147
msg2 = @sprintf("max(h): %.2e m, max(ℵ): %.2e ", hmax, ℵmax)
148-
msg3 = @sprintf("mean(h): %.2e m, mean(ℵ): %.2e ", hmean, ℵmean)
149-
msg4 = @sprintf("extrema(T): (%.2f, %.2f) ᵒC, ", Tmax, Tmin)
148+
msg3 = @sprintf("max uᵢ: (%.2f, %.2f) m s⁻¹, ", uimax, vimax)
149+
msg4 = @sprintf("max uₒ: (%.2f, %.2f) m s⁻¹, ", uomax, vomax)
150150
msg5 = @sprintf("wall time: %s \n", prettytime(step_time))
151151

152152
@info msg1 * msg2 * msg3 * msg4 * msg5
@@ -183,16 +183,6 @@ h = FieldTimeSeries("averaged_sea_ice_quantities.jld2", "h")
183183
hm = FieldTimeSeries{Center, Center, Nothing}(grid, hE.times; backend=InMemory())
184184
ℵm = FieldTimeSeries{Center, Center, Nothing}(grid, hE.times; backend=InMemory())
185185

186-
for (i, time) in enumerate(hm.times)
187-
counter = 0
188-
for (j, t) in enumerate(h.times)
189-
if t time
190-
hm[i] .+= h[j]
191-
ℵm[i] .+= ℵ[j]
192-
counter += 1
193-
end
194-
end
195-
@show counter
196-
hm[i] ./= counter
197-
ℵm[i] ./= counter
198-
end
186+
hMe = [mean(h[t]) for t in 1:length(h.times)]
187+
ℵMe = [mean(ℵ[t]) for t in 1:length(ℵ.times)]
188+

src/OceanSeaIceModels/InterfaceComputations/assemble_net_fluxes.jl

+23-14
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ using Oceananigans.Operators: ℑxᶠᵃᵃ, ℑyᵃᶠᵃ
44
using ClimaOcean.OceanSeaIceModels: sea_ice_concentration
55

66
@inline computed_sea_ice_ocean_fluxes(interface) = interface.fluxes
7-
@inline computed_sea_ice_ocean_fluxes(::Nothing) = (interface_heat = ZeroField(), frazil_heat = ZeroField(), salt = ZeroField())
7+
@inline computed_sea_ice_ocean_fluxes(::Nothing) = (interface_heat = ZeroField(),
8+
frazil_heat = ZeroField(),
9+
salt = ZeroField(),
10+
x_momentum = ZeroField(),
11+
y_momentum = ZeroField())
812

913
function compute_net_ocean_fluxes!(coupled_model)
1014
ocean = coupled_model.ocean
@@ -76,8 +80,10 @@ end
7680
i, j = @index(Global, NTuple)
7781
kᴺ = size(grid, 3)
7882
time = Time(clock.time)
79-
ρτx = atmos_ocean_fluxes.x_momentum # zonal momentum flux
80-
ρτy = atmos_ocean_fluxes.y_momentum # meridional momentum flux
83+
ρτxao = atmos_ocean_fluxes.x_momentum # atmosphere - ocean zonal momentum flux
84+
ρτyao = atmos_ocean_fluxes.y_momentum # atmosphere - ocean meridional momentum flux
85+
ρτxio = sea_ice_ocean_fluxes.x_momentum # sea_ice - ocean zonal momentum flux
86+
ρτyio = sea_ice_ocean_fluxes.y_momentum # sea_ice - ocean meridional momentum flux
8187

8288
@inbounds begin
8389
Sₒ = ocean_salinity[i, j, kᴺ]
@@ -121,22 +127,25 @@ end
121127
ρₒ⁻¹ = 1 / ocean_properties.reference_density
122128
cₒ = ocean_properties.heat_capacity
123129

124-
τxao = ℑxᶠᵃᵃ(i, j, 1, grid, τᶜᶜᶜ, ρₒ⁻¹, ℵ, ρτx)
125-
τyao = ℑyᵃᶠᵃ(i, j, 1, grid, τᶜᶜᶜ, ρₒ⁻¹, ℵ, ρτy)
126-
Jᵀao = ΣQao * ρₒ⁻¹ / cₒ
127-
Jˢao = - Sₒ * ΣFao
128-
129-
ρₒ⁻¹ = 1 / ocean_properties.reference_density
130-
cₒ = ocean_properties.heat_capacity
131-
132130
@inbounds begin
133131
ℵᵢ = ℵ[i, j, 1]
134132
Qio = sea_ice_ocean_fluxes.interface_heat[i, j, 1]
135-
Jˢio = sea_ice_ocean_fluxes.salt[i, j, 1]
133+
134+
Jᵀao = ΣQao * ρₒ⁻¹ / cₒ
135+
Jˢao = - Sₒ * ΣFao
136136
Jᵀio = Qio * ρₒ⁻¹ / cₒ
137+
Jˢio = sea_ice_ocean_fluxes.salt[i, j, 1]
138+
139+
τxao = ℑxᶠᵃᵃ(i, j, 1, grid, τᶜᶜᶜ, ρₒ⁻¹, ℵ, ρτxao)
140+
τyao = ℑyᵃᶠᵃ(i, j, 1, grid, τᶜᶜᶜ, ρₒ⁻¹, ℵ, ρτyao)
141+
τxio = ρτxio[i, j, 1] * ρₒ⁻¹ * ℑxᶠᵃᵃ(i, j, 1, grid, ℵ)
142+
τyio = ρτyio[i, j, 1] * ρₒ⁻¹ * ℑyᵃᶠᵃ(i, j, 1, grid, ℵ)
143+
144+
# Stresses
145+
τx[i, j, 1] = τxao + τxio
146+
τy[i, j, 1] = τyao + τyio
137147

138-
τx[i, j, 1] = τxao
139-
τy[i, j, 1] = τyao
148+
# Tracer fluxes
140149
Jᵀ[i, j, 1] = (1 - ℵᵢ) * Jᵀao + Jᵀio
141150
Jˢ[i, j, 1] = (1 - ℵᵢ) * Jˢao + Jˢio
142151
end

src/OceanSeaIceModels/InterfaceComputations/component_interfaces.jl

+5-1
Original file line numberDiff line numberDiff line change
@@ -247,14 +247,18 @@ function sea_ice_ocean_interface(sea_ice::SeaIceSimulation, ocean;
247247
io_bottom_heat_flux = Field{Center, Center, Nothing}(ocean.model.grid)
248248
io_frazil_heat_flux = Field{Center, Center, Nothing}(ocean.model.grid)
249249
io_salt_flux = Field{Center, Center, Nothing}(ocean.model.grid)
250+
x_momentum = Field{Face, Center, Nothing}(ocean.model.grid)
251+
y_momentum = Field{Center, Face, Nothing}(ocean.model.grid)
250252

251253
@assert io_frazil_heat_flux isa Field{Center, Center, Nothing}
252254
@assert io_bottom_heat_flux isa Field{Center, Center, Nothing}
253255
@assert io_salt_flux isa Field{Center, Center, Nothing}
254256

255257
io_fluxes = (interface_heat=io_bottom_heat_flux,
256258
frazil_heat=io_frazil_heat_flux,
257-
salt=io_salt_flux)
259+
salt=io_salt_flux,
260+
x_momentum=x_momentum,
261+
y_momentum=y_momentum)
258262

259263
io_properties = (; characteristic_melting_speed)
260264

src/OceanSeaIceModels/InterfaceComputations/sea_ice_ocean_fluxes.jl

+47-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Oceananigans.Operators: Δzᶜᶜᶜ
22
using ClimaSeaIce.SeaIceThermodynamics: melting_temperature
3+
using ClimaSeaIce.SeaIceMomentumEquations: x_momentum_stress, y_momentum_stress
34

45
function compute_sea_ice_ocean_fluxes!(coupled_model)
56
ocean = coupled_model.ocean
@@ -18,46 +19,65 @@ function compute_sea_ice_ocean_fluxes!(coupled_model)
1819

1920
ocean_properties = coupled_model.interfaces.ocean_properties
2021
liquidus = sea_ice.model.ice_thermodynamics.phase_transitions.liquidus
21-
grid = ocean.model.grid
22-
arch = architecture(grid)
22+
grid = ocean.model.grid
23+
clock = ocean.model.clock
24+
arch = architecture(grid)
25+
26+
uᵢ, vᵢ = sea_ice.model.velocities
27+
dynamics = sea_ice.model.dynamics
28+
29+
τs = if isnothing(dynamics)
30+
nothing
31+
else
32+
dynamics.external_momentum_stresses.bottom
33+
end
2334

2435
# What about the latent heat removed from the ocean when ice forms?
2536
# Is it immediately removed from the ocean? Or is it stored in the ice?
26-
launch!(arch, grid, :xy, _compute_sea_ice_ocean_latent_heat_flux!,
27-
sea_ice_ocean_fluxes, grid, hᵢ, h⁻, ℵᵢ, Sᵢ, Tₒ, Sₒ, liquidus, ocean_properties, interface_properties, Δt)
37+
launch!(arch, grid, :xy, _compute_sea_ice_ocean_fluxes!,
38+
sea_ice_ocean_fluxes, grid, clock, hᵢ, h⁻, ℵᵢ, Sᵢ, Tₒ, Sₒ, uᵢ, vᵢ,
39+
τs, liquidus, ocean_properties, interface_properties, Δt)
2840

2941
return nothing
3042
end
3143

32-
@kernel function _compute_sea_ice_ocean_latent_heat_flux!(sea_ice_ocean_fluxes,
33-
grid,
34-
ice_thickness,
35-
previous_ice_thickness,
36-
ice_concentration,
37-
ice_salinity,
38-
ocean_temperature,
39-
ocean_salinity,
40-
liquidus,
41-
ocean_properties,
42-
interface_properties,
43-
Δt)
44-
44+
@kernel function _compute_sea_ice_ocean_fluxes!(sea_ice_ocean_fluxes,
45+
grid,
46+
clock,
47+
ice_thickness,
48+
previous_ice_thickness,
49+
ice_concentration,
50+
ice_salinity,
51+
ocean_temperature,
52+
ocean_salinity,
53+
sea_ice_u_velocity,
54+
sea_ice_v_velocity,
55+
sea_ice_ocean_stresses,
56+
liquidus,
57+
ocean_properties,
58+
interface_properties,
59+
Δt)
60+
4561
i, j = @index(Global, NTuple)
4662

4763
Nz = size(grid, 3)
4864
Qᶠₒ = sea_ice_ocean_fluxes.frazil_heat
4965
Qᵢₒ = sea_ice_ocean_fluxes.interface_heat
5066
= sea_ice_ocean_fluxes.salt
67+
τx = sea_ice_ocean_fluxes.x_momentum
68+
τy = sea_ice_ocean_fluxes.y_momentum
69+
uᵢ = sea_ice_u_velocity
70+
vᵢ = sea_ice_v_velocity
5171
Tₒ = ocean_temperature
5272
Sₒ = ocean_salinity
5373
Sᵢ = ice_salinity
5474
hᵢ = ice_thickness
75+
ℵᵢ = ice_concentration
5576
h⁻ = previous_ice_thickness
5677
ρₒ = ocean_properties.reference_density
5778
cₒ = ocean_properties.heat_capacity
5879
uₘ★ = interface_properties.characteristic_melting_speed
5980

60-
= @inbounds ice_concentration[i, j, 1]
6181
δQ_frazil = zero(grid)
6282

6383
for k = Nz:-1:1
@@ -94,6 +114,7 @@ end
94114
@inbounds begin
95115
Tᴺ = Tₒ[i, j, Nz]
96116
Sᴺ = Sₒ[i, j, Nz]
117+
= ℵᵢ[i, j, 1]
97118
end
98119

99120
# Compute total heat associated with temperature adjustment
@@ -111,6 +132,9 @@ end
111132
@inbounds Qᶠₒ[i, j, 1] = δQ_frazil
112133
@inbounds Qᵢₒ[i, j, 1] = δQ_melting *# Melting depends on concentration
113134

135+
sea_ice_fields = (; u = uᵢ, v = vᵢ, h = hᵢ, ℵ = ℵᵢ)
136+
τₒᵢ = sea_ice_ocean_stresses
137+
114138
@inbounds begin
115139
# Change in thickness
116140
Δh = hᵢ[i, j, 1] - h⁻[i, j, 1]
@@ -122,5 +146,9 @@ end
122146

123147
# Update previous ice thickness
124148
h⁻[i, j, 1] = hᵢ[i, j, 1]
149+
150+
# Momentum stresses
151+
τx[i, j, 1] = x_momentum_stress(i, j, Nz, grid, τₒᵢ, clock, sea_ice_fields)
152+
τy[i, j, 1] = y_momentum_stress(i, j, Nz, grid, τₒᵢ, clock, sea_ice_fields)
125153
end
126-
end
154+
end

test/test_surface_fluxes.jl

+48-3
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,10 @@ end
186186
topology = (Bounded, Bounded, Bounded))
187187

188188
ocean = ocean_simulation(grid; momentum_advection = nothing,
189-
tracer_advection = nothing,
190-
closure = nothing,
191-
bottom_drag_coefficient = 0.0)
189+
tracer_advection = nothing,
190+
coriolis = nothing,
191+
closure = nothing,
192+
bottom_drag_coefficient = 0.0)
192193

193194
dates = all_dates(RepeatYearJRA55(), :temperature)
194195
atmosphere = JRA55PrescribedAtmosphere(arch; end_date=dates[2], backend = InMemory())
@@ -210,6 +211,50 @@ end
210211
# Test that the temperature has snapped up to freezing
211212
@test minimum(ocean.model.tracers.T) == 0
212213
end
214+
215+
@info "Testing Surface Fluxes with sea ice..."
216+
217+
grid = RectilinearGrid(arch;
218+
size = (2, 2, 2),
219+
extent = (1, 1, 1),
220+
topology = (Periodic, Periodic, Bounded))
221+
222+
ocean = ocean_simulation(grid; momentum_advection = nothing,
223+
tracer_advection = nothing,
224+
coriolis = nothing,
225+
closure = nothing,
226+
bottom_drag_coefficient = 0.0)
227+
228+
SSU = view(ocean.model.velocities.u, :, :, grid.Nz)
229+
SSV = view(ocean.model.velocities.v, :, :, grid.Nz)
230+
231+
τo = SemiImplicitStress(uₑ=SSU, vₑ=SSV, Cᴰ=0.001, ρₑ=1000.0)
232+
τua = Field{Face, Center, Nothing}(grid)
233+
τva = Field{Center, Face, Nothing}(grid)
234+
235+
dynamics = SeaIceMomentumEquation(grid;
236+
top_momentum_stress = (u=τua, v=τva),
237+
bottom_momentum_stress = τo,
238+
rheology = nothing,
239+
solver = ExplicitSolver())
240+
241+
sea_ice = sea_ice_simulation(grid; dynamics, advection=Centered())
242+
243+
# Set a velocity for the ocean
244+
fill!(ocean.model.velocities.u, 0.1)
245+
fill!(ocean.model.velocities.v, 0.2)
246+
fill!(ocean.model.tracers.T, -2.0)
247+
248+
# Test that we populate the sea-ice ocean stress
249+
earth = OceanSeaIceModel(ocean, sea_ice; atmosphere, radiation=Radiation())
250+
251+
τx = earth.interfaces.sea_ice_ocean_interface.fluxes.x_momentum
252+
τy = earth.interfaces.sea_ice_ocean_interface.fluxes.y_momentum
253+
254+
CUDA.@allowscalar begin
255+
@test τx[1, 1, 1] == sqrt(0.1^2 + 0.2^2) * 0.1
256+
@test τy[1, 1, 1] == sqrt(0.1^2 + 0.2^2) * 0.2
257+
end
213258
end
214259
end
215260

0 commit comments

Comments
 (0)