11import Oceananigans as OC
2+ import ClimaSeaIce as CSI
23import ClimaOcean as CO
34import ClimaCoupler: Checkpointer, FieldExchanger, FluxCalculator, Interfacer, Utilities
45import ClimaComms
@@ -51,12 +52,12 @@ can be found in the documentation for `ClimaOcean.ocean_simulation`.
5152"""
5253function ClimaSeaIceSimulation (land_fraction, ocean; output_dir)
5354 # Initialize the sea ice with the same grid as the ocean
54- grid = ocean. ocean. model. grid # TODO can't use lat/lon grid at poles for ice
55- arch = grid . architecture
55+ grid = ocean. ocean. model. grid # TODO can't use lat/lon grid at poles for ice, need to fill with bucket
56+ arch = OC . Architectures . architecture (grid)
5657 advection = ocean. ocean. model. advection. T
57- top_heat_boundary_condition = CO . MeltingConstrainedFluxBalance ()
58+ top_heat_boundary_condition = CSI . MeltingConstrainedFluxBalance ()
5859
59- # TODO use branch ss-js/top-heat-bc for this constructor
60+ # TODO use ClimaOcean 0.8.6
6061 ice = CO. sea_ice_simulation (grid, ocean. ocean; advection, top_heat_boundary_condition)
6162
6263 melting_speed = 1e-4
@@ -68,7 +69,7 @@ function ClimaSeaIceSimulation(land_fraction, ocean; output_dir)
6869 if arch isa OC. CPU || pkgversion (OC) >= v " 0.96.22"
6970 # Save all tracers and velocities to a NetCDF file at daily frequency
7071 outputs = OC. prognostic_fields (ice. model)
71- jld_writer = OC. JLDWriter (
72+ jld_writer = OC. JLD2Writer (
7273 ice. model,
7374 outputs;
7475 schedule = OC. TimeInterval (86400 ), # Daily output
@@ -80,7 +81,19 @@ function ClimaSeaIceSimulation(land_fraction, ocean; output_dir)
8081 end
8182
8283 # Allocate space for the sea ice-ocean (io) fluxes
83- ocean_ice_fluxes = ocean. ocean_ice_fluxes
84+ io_bottom_heat_flux = OC. Field {OC.Center, OC.Center, Nothing} (grid)
85+ io_frazil_heat_flux = OC. Field {OC.Center, OC.Center, Nothing} (grid)
86+ io_salt_flux = OC. Field {OC.Center, OC.Center, Nothing} (grid)
87+ x_momentum = OC. Field {OC.Face, OC.Center, Nothing} (grid)
88+ y_momentum = OC. Field {OC.Center, OC.Face, Nothing} (grid)
89+
90+ ocean_ice_fluxes = (
91+ interface_heat = io_bottom_heat_flux,
92+ frazil_heat = io_frazil_heat_flux,
93+ salt = io_salt_flux,
94+ x_momentum = x_momentum,
95+ y_momentum = y_momentum,
96+ )
8497
8598 # Get the initial area fraction from the fractional ice concentration
8699 boundary_space = axes (ocean. area_fraction)
@@ -110,7 +123,7 @@ Interfacer.step!(sim::ClimaSeaIceSimulation, t) =
110123 OC. time_step! (sim. ice, float (t) - sim. ice. model. clock. time)
111124
112125Interfacer. get_field (sim:: ClimaSeaIceSimulation , :: Val{:area_fraction} ) = sim. area_fraction
113- Interfacer. get_field (sim:: ClimaSeaIceSimulation , :: Val ( :ice_concentration ) ) =
126+ Interfacer. get_field (sim:: ClimaSeaIceSimulation , :: Val{ :ice_concentration} ) =
114127 sim. ice. model. ice_concentration
115128
116129# At the moment, we return always Float32. This is because we always want to run
@@ -122,6 +135,7 @@ Interfacer.get_field(sim::ClimaSeaIceSimulation, ::Val{:roughness_buoyancy}) =
122135Interfacer. get_field (sim:: ClimaSeaIceSimulation , :: Val{:roughness_momentum} ) =
123136 Float32 (5.8e-5 )
124137Interfacer. get_field (sim:: ClimaSeaIceSimulation , :: Val{:beta} ) = Float32 (1 )
138+ Interfacer. get_field (sim:: ClimaSeaIceSimulation , :: Val{:emissivity} ) = Float32 (1 )
125139Interfacer. get_field (sim:: ClimaSeaIceSimulation , :: Val{:surface_direct_albedo} ) =
126140 Float32 (0.7 )
127141Interfacer. get_field (sim:: ClimaSeaIceSimulation , :: Val{:surface_diffuse_albedo} ) =
@@ -130,7 +144,7 @@ Interfacer.get_field(sim::ClimaSeaIceSimulation, ::Val{:surface_diffuse_albedo})
130144# Approximate the sea ice surface temperature as the temperature computed from the
131145# fluxes at the previous timestep.
132146Interfacer. get_field (sim:: ClimaSeaIceSimulation , :: Val{:surface_temperature} ) =
133- 273.15 .+ OC . interior ( sim. ice. model. ice_thermodynamics. top_surface_temperature, :, :, 1 )
147+ 273.15 + sim. ice. model. ice_thermodynamics. top_surface_temperature
134148
135149"""
136150 FluxCalculator.update_turbulent_fluxes!(sim::ClimaSeaIceSimulation, fields)
@@ -176,8 +190,8 @@ function FluxCalculator.update_turbulent_fluxes!(sim::ClimaSeaIceSimulation, fie
176190
177191 # Set the momentum flux BCs at the correct locations using the remapped scratch fields
178192 # Note that this requires the sea ice model to always be run with dynamics turned on
179- si_flux_u = sim. ice. model. dynamics. external_stresses . top. u
180- si_flux_v = sim. ice. model. dynamics. external_stresses . top. v
193+ si_flux_u = sim. ice. model. dynamics. external_momentum_stresses . top. u
194+ si_flux_v = sim. ice. model. dynamics. external_momentum_stresses . top. v
181195 set_from_extrinsic_vectors! (
182196 (; u = si_flux_u, v = si_flux_v),
183197 grid,
@@ -247,23 +261,32 @@ end
247261
248262Compute the fluxes between the ocean and sea ice, storing them in the `ocean_ice_fluxes`
249263fields of the ocean and sea ice simulations.
264+
265+ !!! note
266+ This function must be called after the turbulent fluxes have been updated in both
267+ simulations. Here only the contributions from the sea ice/ocean interactions
268+ are added to the fluxes.
250269"""
251270function FluxCalculator. ocean_seaice_fluxes! (
252271 ocean_sim:: OceananigansSimulation ,
253272 ice_sim:: ClimaSeaIceSimulation ,
254273)
255274 melting_speed = ice_sim. melting_speed
256275 ocean_properties = ocean_sim. ocean_properties
276+ ice_concentration = Interfacer. get_field (ice_sim, Val (:ice_concentration ))
257277
258278 # Compute the fluxes and store them in the both simulations
259- CO. compute_sea_ice_ocean_fluxes! (
279+ ocean_properties = (;
280+ reference_density = ocean_properties. ocean_reference_density,
281+ heat_capacity = ocean_properties. ocean_heat_capacity,
282+ ) # TODO rename in constructor
283+ CO. OceanSeaIceModels. InterfaceComputations. compute_sea_ice_ocean_fluxes! (
260284 ice_sim. ocean_ice_fluxes,
261285 ocean_sim. ocean,
262286 ice_sim. ice,
263287 melting_speed,
264288 ocean_properties,
265289 )
266- ocean_sim. ocean_ice_fluxes = ice_sim. ocean_ice_fluxes
267290
268291 # # Update the internals of the sea ice model
269292 # Set the bottom heat flux to the sum of the frazil and interface heat fluxes
@@ -274,21 +297,62 @@ function FluxCalculator.ocean_seaice_fluxes!(
274297 bottom_heat_flux .= Qf .+ Qi
275298
276299 # # Update the internals of the ocean model
277- ρₒ⁻¹ = 1 / ocean_sim. ocean_properties. reference_density
278- cₒ = ocean_sim. ocean_properties. heat_capacity
300+ ρₒ⁻¹ = 1 / ocean_sim. ocean_properties. ocean_reference_density
301+ cₒ = ocean_sim. ocean_properties. ocean_heat_capacity
279302
280- Jᵀio = Qio * ρₒ⁻¹ / cₒ
281- Jˢio = ice_sim. ocean_ice_fluxes. salt[i, j, 1 ] * ℵᵢ
303+ # Compute fluxes for u, v, T, and S from momentum, heat, and freshwater fluxes
304+ oc_flux_u = surface_flux (ocean_sim. ocean. model. velocities. u)
305+ oc_flux_v = surface_flux (ocean_sim. ocean. model. velocities. v)
282306
283- # ℑxᶠᵃᵃ: interpolate faces to centers
284- τxio = ρτxio[i, j, 1 ] * ρₒ⁻¹ * ℑxᶠᵃᵃ (i, j, 1 , grid, ℵ)
285- τyio = ρτyio[i, j, 1 ] * ρₒ⁻¹ * ℑyᵃᶠᵃ (i, j, 1 , grid, ℵ)
307+ ρτxio = ice_sim. ocean_ice_fluxes. x_momentum # sea_ice - ocean zonal momentum flux
308+ ρτyio = ice_sim. ocean_ice_fluxes. y_momentum # sea_ice - ocean meridional momentum flux
309+
310+ # Update the momentum flux contributions from ocean/sea ice fluxes
311+ grid = ocean_sim. ocean. model. grid
312+ arch = OC. Architectures. architecture (grid)
313+ OC. Utils. launch! (
314+ arch,
315+ grid,
316+ :xy ,
317+ _add_ocean_ice_stress!,
318+ oc_flux_u,
319+ oc_flux_v,
320+ grid,
321+ ρτxio,
322+ ρτyio,
323+ ρₒ⁻¹,
324+ ice_concentration,
325+ )
326+
327+ oc_flux_T = surface_flux (ocean_sim. ocean. model. tracers. T)
328+ OC. interior (oc_flux_T, :, :, 1 ) .+ =
329+ OC. interior (ice_concentration, :, :, 1 ) .* OC. interior (Qi, :, :, 1 ) .* ρₒ⁻¹ ./ cₒ
286330
287- # TODO finish this
331+ oc_flux_S = surface_flux (ocean_sim. ocean. model. tracers. S)
332+ OC. interior (oc_flux_S, :, :, 1 ) .+ =
333+ OC. interior (ice_concentration, :, :, 1 ) .*
334+ OC. interior (ice_sim. ocean_ice_fluxes. salt, :, :, 1 )
288335
289336 return nothing
290337end
291338
339+ @kernel function _add_ocean_ice_stress! (
340+ oc_flux_u,
341+ oc_flux_v,
342+ grid,
343+ ρτxio,
344+ ρτyio,
345+ ρₒ⁻¹,
346+ ice_concentration,
347+ )
348+ i, j = @index (Global, NTuple)
349+
350+ # ℑxᶠᵃᵃ: interpolate faces to centers
351+ oc_flux_u +=
352+ ρτxio[i, j, 1 ] * ρₒ⁻¹ * OC. Operators. ℑxᶠᵃᵃ (i, j, 1 , grid, ice_concentration)
353+ oc_flux_v +=
354+ ρτyio[i, j, 1 ] * ρₒ⁻¹ * OC. Operators. ℑyᵃᶠᵃ (i, j, 1 , grid, ice_concentration)
355+ end
292356
293357"""
294358 get_model_prog_state(sim::ClimaSeaIceSimulation)
0 commit comments