@@ -10,7 +10,7 @@ using KernelAbstractions: @kernel, @index, @inbounds
1010include (" climaocean_helpers.jl" )
1111
1212"""
13- OceananigansSimulation{SIM, A, OPROP, REMAP}
13+ OceananigansSimulation{SIM, A, OPROP, REMAP, SIC }
1414
1515The ClimaCoupler simulation object used to run with Oceananigans.
1616This type is used by the coupler to indicate that this simulation
@@ -21,12 +21,14 @@ It contains the following objects:
2121- `area_fraction::A`: A ClimaCore Field representing the surface area fraction of this component model on the exchange grid.
2222- `ocean_properties::OPROP`: A NamedTuple of ocean properties and parameters
2323- `remapping::REMAP`: Objects needed to remap from the exchange (spectral) grid to Oceananigans spaces.
24+ - `ice_concentration::SIC`: An Oceananigans Field representing the sea ice concentration on the ocean/sea ice grid.
2425"""
25- struct OceananigansSimulation{SIM, A, OPROP, REMAP} <: Interfacer.OceanModelSimulation
26+ struct OceananigansSimulation{SIM, A, OPROP, REMAP, SIC } <: Interfacer.OceanModelSimulation
2627 ocean:: SIM
2728 area_fraction:: A
2829 ocean_properties:: OPROP
2930 remapping:: REMAP
31+ ice_concentration:: SIC
3032end
3133
3234"""
@@ -154,13 +156,11 @@ function OceananigansSimulation(
154156 scratch_arr2 = ArrayType (zeros (FT, interpolated_values_dim... ))
155157 scratch_arr3 = ArrayType (zeros (FT, interpolated_values_dim... ))
156158
157- remapping = (; remapper_cc, scratch_cc1, scratch_cc2, scratch_arr1, scratch_arr2, scratch_arr3)
159+ remapping =
160+ (; remapper_cc, scratch_cc1, scratch_cc2, scratch_arr1, scratch_arr2, scratch_arr3)
158161
159- ocean_properties = (;
160- ocean_reference_density = 1020 ,
161- ocean_heat_capacity = 3991 ,
162- ocean_fresh_water_density = 999.8 ,
163- )
162+ ocean_properties =
163+ (; reference_density = 1020 , heat_capacity = 3991 , fresh_water_density = 999.8 )
164164
165165 # Before version 0.96.22, the NetCDFWriter was broken on GPU
166166 if arch isa OC. CPU || pkgversion (OC) >= v " 0.96.22"
@@ -178,8 +178,17 @@ function OceananigansSimulation(
178178 ocean. output_writers[:diagnostics ] = netcdf_writer
179179 end
180180
181- sim = OceananigansSimulation (ocean, area_fraction, ocean_properties, remapping)
182- return sim
181+ # Initialize with 0 ice concentration; this will be updated in `resolve_ocean_ice_fractions!`
182+ # if the ocean is coupled to a non-prescribed sea ice model.
183+ ice_concentration = OC. Field {OC.Center, OC.Center, Nothing} (grid)
184+
185+ return OceananigansSimulation (
186+ ocean,
187+ area_fraction,
188+ ocean_properties,
189+ remapping,
190+ ice_concentration,
191+ )
183192end
184193
185194"""
@@ -193,6 +202,9 @@ degrees latitude, and make sure the ocean fraction is 0 there.
193202
194203The land fraction is expected to be set to 1 at the poles before calling this function,
195204and doesn't need to be set again since its fraction is static.
205+
206+ This function also updates the ice concentration field in the ocean simulation
207+ so that it can be used for weighting flux updates.
196208"""
197209function FieldExchanger. resolve_ocean_ice_fractions! (
198210 ocean_sim:: OceananigansSimulation ,
@@ -215,6 +227,9 @@ function FieldExchanger.resolve_ocean_ice_fractions!(
215227 @. ice_fraction = ifelse .(polar_mask == FT (1 ), FT (1 ) - land_fraction, ice_fraction)
216228 @. ocean_fraction = ifelse .(polar_mask == FT (1 ), FT (0 ), ocean_fraction)
217229 end
230+
231+ # Update the ice concentration field in the ocean simulation
232+ ocean_sim. ice_concentration .= Interfacer. get_field (ice_sim, Val (:ice_concentration ))
218233 return nothing
219234end
220235
@@ -254,7 +269,11 @@ Interfacer.get_field(sim::OceananigansSimulation, ::Val{:surface_temperature}) =
254269Update the turbulent fluxes in the simulation using the values computed at this time step.
255270These include latent heat flux, sensible heat flux, momentum fluxes, and moisture flux.
256271
257- The input `fields` are already area-weighted, so there's no need to weight them again.
272+ Rather than setting the surface fluxes and overwriting previous values, this function adds only
273+ the contributions from the turbulent fluxes. `update_sim!` sets the surface fluxes due to
274+ radiation and precipitation. Additional contributions may be made in `ocean_seaice_fluxes!`.
275+ An exception is the momentum fluxes, which are set directly here since they are not updated
276+ in `update_sim!`.
258277
259278A note on sign conventions:
260279SurfaceFluxes and Oceananigans both use the convention that a positive flux is an upward flux.
@@ -264,18 +283,9 @@ and Oceananigans represents moisture moving from atmosphere to ocean as a positi
264283so a sign change is needed when we convert from moisture to salinity flux.
265284"""
266285function FluxCalculator. update_turbulent_fluxes! (sim:: OceananigansSimulation , fields)
267- # TODO multiply all fluxes by 1 - SIC
268- # TODO clarify where we need to add and where we set fluxes directly
269286 (; F_lh, F_sh, F_turb_ρτxz, F_turb_ρτyz, F_turb_moisture) = fields
270287 grid = sim. ocean. model. grid
271-
272- # Remap the area fraction from the boundary space to the Oceananigans grid
273- CC. Remapping. interpolate! (
274- sim. remapping. scratch_arr3,
275- sim. remapping. remapper_cc,
276- sim. area_fraction,
277- )
278- area_fraction = sim. remapping. scratch_arr3
288+ ice_concentration = sim. ice_concentration
279289
280290 # Remap momentum fluxes onto reduced 2D Center, Center fields using scratch arrays and fields
281291 CC. Remapping. interpolate! (
@@ -295,6 +305,13 @@ function FluxCalculator.update_turbulent_fluxes!(sim::OceananigansSimulation, fi
295305 F_turb_ρτxz_cc = sim. remapping. scratch_cc1
296306 F_turb_ρτyz_cc = sim. remapping. scratch_cc2
297307
308+ # Weight by (1 - sea ice concentration)
309+ # TODO does this work with OC fields?
310+ OC. interior (F_turb_ρτxz_cc, :, :, 1 ) .=
311+ OC. interior (F_turb_ρτxz_cc, :, :, 1 ) .* (1.0 .- ice_concentration)
312+ OC. interior (F_turb_ρτyz_cc, :, :, 1 ) .=
313+ OC. interior (F_turb_ρτyz_cc, :, :, 1 ) .* (1.0 .- ice_concentration)
314+
298315 # Set the momentum flux BCs at the correct locations using the remapped scratch fields
299316 oc_flux_u = surface_flux (sim. ocean. model. velocities. u)
300317 oc_flux_v = surface_flux (sim. ocean. model. velocities. v)
@@ -303,10 +320,9 @@ function FluxCalculator.update_turbulent_fluxes!(sim::OceananigansSimulation, fi
303320 grid,
304321 F_turb_ρτxz_cc,
305322 F_turb_ρτyz_cc,
306- ) # TODO multiply by area_fraction?
323+ )
307324
308- (; ocean_reference_density, ocean_heat_capacity, ocean_fresh_water_density) =
309- sim. ocean_properties
325+ (; reference_density, heat_capacity, fresh_water_density) = sim. ocean_properties
310326
311327 # Remap the latent and sensible heat fluxes using scratch arrays
312328 CC. Remapping. interpolate! (sim. remapping. scratch_arr1, sim. remapping. remapper_cc, F_lh) # latent heat flux
@@ -322,8 +338,8 @@ function FluxCalculator.update_turbulent_fluxes!(sim::OceananigansSimulation, fi
322338 oc_flux_T = surface_flux (sim. ocean. model. tracers. T)
323339 OC. interior (oc_flux_T, :, :, 1 ) .=
324340 OC. interior (oc_flux_T, :, :, 1 ) .+
325- area_fraction .* (remapped_F_lh .+ remapped_F_sh) ./
326- (ocean_reference_density * ocean_heat_capacity )
341+ ( 1.0 .- ice_concentration) .* (remapped_F_lh .+ remapped_F_sh) ./
342+ (reference_density * heat_capacity )
327343
328344 # Add the part of the salinity flux that comes from the moisture flux, we also need to
329345 # add the component due to precipitation (that was done with the radiative fluxes)
@@ -332,12 +348,12 @@ function FluxCalculator.update_turbulent_fluxes!(sim::OceananigansSimulation, fi
332348 sim. remapping. remapper_cc,
333349 F_turb_moisture,
334350 )
335- moisture_fresh_water_flux = sim. remapping. scratch_arr1 ./ ocean_fresh_water_density
351+ moisture_fresh_water_flux = sim. remapping. scratch_arr1 ./ fresh_water_density
336352 oc_flux_S = surface_flux (sim. ocean. model. tracers. S)
337353 surface_salinity = OC. interior (sim. ocean. model. tracers. S, :, :, 1 )
338354 OC. interior (oc_flux_S, :, :, 1 ) .=
339355 OC. interior (oc_flux_S, :, :, 1 ) .-
340- area_fraction .* surface_salinity .* moisture_fresh_water_flux
356+ ( 1.0 .- ice_concentration) .* surface_salinity .* moisture_fresh_water_flux
341357 return nothing
342358end
343359
@@ -355,9 +371,8 @@ by the coupler.
355371Update the portion of the surface_fluxes for T and S that is due to radiation and
356372precipitation. The rest will be updated in `update_turbulent_fluxes!`.
357373
358- Unlike the turbulent fluxes, the radiative and precipitation fluxes need to be
359- weighted by the ocean area fraction, since they provided from the atmosphere
360- without any weighting.
374+ This function sets the surface fluxes directly, overwriting any previous values.
375+ Additional contributions will be made in `update_turbulent_fluxes!` and `ocean_seaice_fluxes!`.
361376
362377A note on sign conventions:
363378ClimaAtmos and Oceananigans both use the convention that a positive flux is an upward flux.
@@ -367,16 +382,8 @@ Oceananigans represents precipitation as a positive salinity flux,
367382so a sign change is needed when we convert from precipitation to salinity flux.
368383"""
369384function FieldExchanger. update_sim! (sim:: OceananigansSimulation , csf)
370- (; ocean_reference_density, ocean_heat_capacity, ocean_fresh_water_density) =
371- sim. ocean_properties
372- # TODO use SIC instead?
373- # Remap the area fraction from the boundary space to the Oceananigans grid
374- CC. Remapping. interpolate! (
375- sim. remapping. scratch_arr3,
376- sim. remapping. remapper_cc,
377- sim. area_fraction,
378- )
379- area_fraction = sim. remapping. scratch_arr3
385+ (; reference_density, heat_capacity, fresh_water_density) = sim. ocean_properties
386+ ice_concentration = sim. ice_concentration
380387
381388 # Remap radiative flux onto scratch array; rename for clarity
382389 CC. Remapping. interpolate! (
@@ -401,36 +408,34 @@ function FieldExchanger.update_sim!(sim::OceananigansSimulation, csf)
401408 α = Interfacer. get_field (sim, Val (:surface_direct_albedo )) # scalar
402409 ϵ = Interfacer. get_field (sim, Val (:emissivity )) # scalar
403410 OC. interior (oc_flux_T, :, :, 1 ) .=
404- area_fraction .* (
411+ ( 1.0 .- ice_concentration) .* (
405412 - (1 - α) .* remapped_SW_d .-
406413 ϵ * (
407414 remapped_LW_d .-
408415 σ .* (273.15 .+ OC. interior (sim. ocean. model. tracers. T, :, :, 1 )) .^ 4
409416 )
410- ) ./ (ocean_reference_density * ocean_heat_capacity )
417+ ) ./ (reference_density * heat_capacity )
411418
412419 # Remap precipitation fields onto scratch arrays; rename for clarity
413420 CC. Remapping. interpolate! (
414421 sim. remapping. scratch_arr1,
415422 sim. remapping. remapper_cc,
416- sim . area_fraction .* csf. P_liq,
423+ csf. P_liq,
417424 )
418425 CC. Remapping. interpolate! (
419426 sim. remapping. scratch_arr2,
420427 sim. remapping. remapper_cc,
421- sim . area_fraction .* csf. P_snow,
428+ csf. P_snow,
422429 )
423430 remapped_P_liq = sim. remapping. scratch_arr1
424431 remapped_P_snow = sim. remapping. scratch_arr2
425432
426433 # Virtual salt flux
427434 oc_flux_S = surface_flux (sim. ocean. model. tracers. S)
428- precipitating_fresh_water_flux =
429- area_fraction .* (remapped_P_liq .+ remapped_P_snow) ./ ocean_fresh_water_density
430- surface_salinity_flux =
431- OC. interior (sim. ocean. model. tracers. S, :, :, 1 ) .* precipitating_fresh_water_flux
432435 OC. interior (oc_flux_S, :, :, 1 ) .=
433- OC. interior (oc_flux_S, :, :, 1 ) .- surface_salinity_flux
436+ OC. interior (oc_flux_S, :, :, 1 ) .-
437+ OC. interior (sim. ocean. model. tracers. S, :, :, 1 ) .* (1.0 .- ice_concentration) .*
438+ (remapped_P_liq .+ remapped_P_snow) ./ fresh_water_density
434439 return nothing
435440end
436441
0 commit comments