Skip to content

Commit ae04351

Browse files
committed
add ocean sea ice fluxes fields [skip ci]
1 parent 5ca7048 commit ae04351

File tree

4 files changed

+145
-26
lines changed

4 files changed

+145
-26
lines changed

experiments/ClimaEarth/components/ocean/clima_seaice.jl

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ include("climaocean_helpers.jl")
1212
"""
1313
ClimaSeaIceSimulation{SIM, A, OPROP, REMAP}
1414
15-
The ClimaCoupler simulation object used to run with Oceananigans.
15+
The ClimaCoupler simulation object used to run with ClimaSeaIce.
1616
This type is used by the coupler to indicate that this simulation
1717
is an surface/ocean simulation for dispatch.
1818
@@ -21,15 +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
- `melting_speed::MS`: An constant characteristic speed for melting/freezing.
2323
- `remapping::REMAP`: Objects needed to remap from the exchange (spectral) grid to Oceananigans spaces.
24-
TODO add skin_temperature field, for now set skin temperature to top layer sea ice temperature
25-
alt: compute skin temp from bulk temp and ocean surface temp, assuming linear profile in the top layer,
26-
might need to limit min sea ice thickness to avoid crazy gradients
24+
- `ocean_sea_ice_fluxes::NT`: A NamedTuple of fluxes between the ocean and sea ice, computed at each coupling step.
2725
"""
28-
struct ClimaSeaIceSimulation{SIM, A, MS, REMAP} <: Interfacer.SeaIceModelSimulation
26+
struct ClimaSeaIceSimulation{SIM, A, MS, REMAP, NT} <: Interfacer.SeaIceModelSimulation
2927
sea_ice::SIM
3028
area_fraction::A
3129
melting_speed::MS
3230
remapping::REMAP
31+
ocean_sea_ice_fluxes::NT
3332
end
3433

3534
"""
@@ -73,7 +72,28 @@ function ClimaSeaIceSimulation(area_fraction, ocean; output_dir)
7372
# sea_ice.output_writers[:diagnostics] = netcdf_writer
7473
# end
7574

76-
sim = ClimaSeaIceSimulation(sea_ice, area_fraction, melting_speed, remapping)
75+
# Allocate space for the sea ice-ocean (io) fluxes
76+
io_bottom_heat_flux = OC.Field{Center, Center, Nothing}(grid)
77+
io_frazil_heat_flux = OC.Field{Center, Center, Nothing}(grid)
78+
io_salt_flux = OC.Field{Center, Center, Nothing}(grid)
79+
x_momentum = OC.Field{Face, Center, Nothing}(grid)
80+
y_momentum = OC.Field{Center, Face, Nothing}(grid)
81+
82+
ocean_sea_ice_fluxes = (
83+
interface_heat = io_bottom_heat_flux,
84+
frazil_heat = io_frazil_heat_flux,
85+
salt = io_salt_flux,
86+
x_momentum = x_momentum,
87+
y_momentum = y_momentum,
88+
)
89+
90+
sim = ClimaSeaIceSimulation(
91+
sea_ice,
92+
area_fraction,
93+
melting_speed,
94+
remapping,
95+
ocean_sea_ice_fluxes,
96+
)
7797
return sim
7898
end
7999

@@ -164,6 +184,7 @@ function FluxCalculator.update_turbulent_fluxes!(sim::ClimaSeaIceSimulation, fie
164184
remapped_F_sh = sim.remapping.scratch_arr2
165185

166186

187+
# TODO update this for sea ice
167188
# TODO what is sim.sea_ice.model.ice_thermodynamics.top_surface_temperature? where is it set?
168189
# TODO ocean_reference_density -> sea_ice.model.ice_density ?
169190
oc_flux_T = surface_flux(sim.ocean.model.tracers.T)
@@ -186,28 +207,29 @@ function FluxCalculator.update_turbulent_fluxes!(sim::ClimaSeaIceSimulation, fie
186207
return nothing
187208
end
188209

189-
function Interfacer.update_field!(sim::OceananigansSimulation, ::Val{:area_fraction}, field)
210+
function Interfacer.update_field!(sim::ClimaSeaIceSimulation, ::Val{:area_fraction}, field)
190211
sim.area_fraction .= field
191212
return nothing
192213
end
193214

194215
"""
195-
FieldExchanger.update_sim!(sim::OceananigansSimulation, csf, area_fraction)
216+
FieldExchanger.update_sim!(sim::ClimaSeaIceSimulation, csf, area_fraction)
196217
197-
Update the ocean simulation with the provided fields, which have been filled in
218+
Update the sea ice simulation with the provided fields, which have been filled in
198219
by the coupler.
199220
200221
Update the portion of the surface_fluxes for T and S that is due to radiation and
201222
precipitation. The rest will be updated in `update_turbulent_fluxes!`.
202223
203224
A note on sign conventions:
204-
ClimaAtmos and Oceananigans both use the convention that a positive flux is an upward flux.
225+
ClimaAtmos and ClimaSeaIce both use the convention that a positive flux is an upward flux.
205226
No sign change is needed during the exchange, except for precipitation/salinity fluxes.
206227
ClimaAtmos provides precipitation as a negative flux at the surface, and
207-
Oceananigans represents precipitation as a positive salinity flux,
228+
ClimaSeaIce represents precipitation as a positive salinity flux,
208229
so a sign change is needed when we convert from precipitation to salinity flux.
209230
"""
210-
function FieldExchanger.update_sim!(sim::OceananigansSimulation, csf, area_fraction)
231+
function FieldExchanger.update_sim!(sim::ClimaSeaIceSimulation, csf, area_fraction)
232+
# TODO update this for sea ice
211233
(; ocean_reference_density, ocean_heat_capacity, ocean_fresh_water_density) =
212234
sim.ocean_properties
213235

@@ -249,28 +271,35 @@ function FieldExchanger.update_sim!(sim::OceananigansSimulation, csf, area_fract
249271
return nothing
250272
end
251273

252-
# TODO add stub to FluxCalculator
253-
# TODO fill this in using ClimaOcean: https://github.com/CliMA/ClimaOcean.jl/pull/627
254-
# TODO instead of cs we can take in the ocean and sea ice sims, dispatch on them to do nothing for other sea ice models
255-
# TODO docstring
256-
function ocean_seaice_fluxes!(cs)
257-
seaice_sim = cs.models.sea_ice
258-
ocean_sim = cs.models.ocean
274+
"""
275+
ocean_seaice_fluxes!(ocean_sim, ice_sim)
259276
260-
melting_speed = seaice_sim.melting_speed
277+
Compute the fluxes between the ocean and sea ice, storing them in the `ocean_sea_ice_fluxes`
278+
fields of the ocean and sea ice simulations.
279+
"""
280+
function FluxCalculator.ocean_seaice_fluxes!(
281+
ocean_sim::OceananigansSimulation,
282+
ice_sim::ClimaSeaIceSimulation,
283+
)
284+
# TODO unify sea_ice vs ice naming convention in this file
285+
melting_speed = ice_sim.melting_speed
261286
ocean_properties = ocean_sim.ocean_properties
262287

263-
# TODO what should fluxes be here?
288+
# Compute the fluxes and store them in the both simulations
264289
OC.compute_sea_ice_ocean_fluxes!(
265-
fluxes,
290+
ice_sim.ocean_sea_ice_fluxes,
266291
ocean_sim.ocean,
267-
seaice_sim.sea_ice,
292+
ice_sim.sea_ice,
268293
melting_speed,
269294
ocean_properties,
270295
)
296+
ocean_sim.ocean_sea_ice_fluxes = ice_sim.ocean_sea_ice_fluxes
297+
298+
# TODO what do we do with these fluxes now? They need to be passed to the component sims somehow
271299
return nothing
272300
end
273301

302+
274303
"""
275304
get_model_prog_state(sim::ClimaSeaIceSimulation)
276305

experiments/ClimaEarth/components/ocean/oceananigans.jl

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
- `ocean_sea_ice_fluxes::NT`: A NamedTuple of fluxes between the ocean and sea ice, computed at each coupling step.
2425
"""
25-
struct OceananigansSimulation{SIM, A, OPROP, REMAP} <: Interfacer.OceanModelSimulation
26+
struct OceananigansSimulation{SIM, A, OPROP, REMAP, NT} <: Interfacer.OceanModelSimulation
2627
ocean::SIM
2728
area_fraction::A
2829
ocean_properties::OPROP
2930
remapping::REMAP
31+
ocean_sea_ice_fluxes::NT
3032
end
3133

3234
"""
@@ -181,7 +183,29 @@ function OceananigansSimulation(
181183
ocean.output_writers[:diagnostics] = netcdf_writer
182184
end
183185

184-
sim = OceananigansSimulation(ocean, area_fraction, ocean_properties, remapping)
186+
# TODO make this depend on sea ice model type
187+
# Allocate space for the sea ice-ocean (io) fluxes
188+
io_bottom_heat_flux = OC.Field{Center, Center, Nothing}(grid)
189+
io_frazil_heat_flux = OC.Field{Center, Center, Nothing}(grid)
190+
io_salt_flux = OC.Field{Center, Center, Nothing}(grid)
191+
x_momentum = OC.Field{Face, Center, Nothing}(grid)
192+
y_momentum = OC.Field{Center, Face, Nothing}(grid)
193+
194+
ocean_sea_ice_fluxes = (
195+
interface_heat = io_bottom_heat_flux,
196+
frazil_heat = io_frazil_heat_flux,
197+
salt = io_salt_flux,
198+
x_momentum = x_momentum,
199+
y_momentum = y_momentum,
200+
)
201+
202+
sim = OceananigansSimulation(
203+
ocean,
204+
area_fraction,
205+
ocean_properties,
206+
remapping,
207+
ocean_sea_ice_fluxes,
208+
)
185209
return sim
186210
end
187211

experiments/ClimaEarth/setup_run.jl

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ include("components/ocean/slab_ocean.jl")
7676
include("components/ocean/prescr_ocean.jl")
7777
include("components/ocean/prescr_seaice.jl")
7878
include("components/ocean/oceananigans.jl")
79+
include("components/ocean/clima_seaice.jl")
7980

8081
#=
8182
### Configuration Dictionaries
@@ -162,6 +163,8 @@ function CoupledSimulation(config_dict::AbstractDict)
162163
isdir(land_output_dir) || mkpath(land_output_dir)
163164
ocean_output_dir = joinpath(dir_paths.output, "clima_ocean")
164165
isdir(ocean_output_dir) || mkpath(ocean_output_dir)
166+
ice_output_dir = joinpath(dir_paths.output, "clima_seaice")
167+
isdir(ice_output_dir) || mkpath(ice_output_dir)
165168

166169

167170
## get component model dictionaries (if applicable)
@@ -357,6 +360,7 @@ function CoupledSimulation(config_dict::AbstractDict)
357360
error("Invalid land model specified: $(land_model)")
358361
end
359362

363+
# TODO separate drivers to clean this up
360364
## sea ice model
361365
ice_sim = PrescribedIceSimulation(
362366
FT;
@@ -375,7 +379,40 @@ function CoupledSimulation(config_dict::AbstractDict)
375379
ice_fraction = Interfacer.get_field(ice_sim, Val(:area_fraction))
376380
ocean_fraction = FT(1) .- ice_fraction .- land_fraction
377381

382+
378383
if sim_mode <: CMIPMode
384+
########### TODO CLEAN UP ############
385+
# TODO how should we initialize ocean fraction when using ClimaSeaIce?
386+
# Set up prescribed sea ice concentration object
387+
sic_data = try
388+
joinpath(
389+
@clima_artifact("historical_sst_sic", comms_ctx),
390+
"MODEL.ICE.HAD187001-198110.OI198111-202206.nc",
391+
)
392+
catch error
393+
@warn "Using lowres SIC. If you want the higher resolution version, you have to obtain it from ClimaArtifacts"
394+
joinpath(
395+
@clima_artifact("historical_sst_sic_lowres", comms_ctx),
396+
"MODEL.ICE.HAD187001-198110.OI198111-202206_lowres.nc",
397+
)
398+
end
399+
@info "Using initial condition prescribed SIC file: " sic_data
400+
401+
SIC_timevaryinginput = TimeVaryingInput(
402+
sic_data,
403+
"SEAICE",
404+
boundary_space,
405+
reference_date = start_date,
406+
file_reader_kwargs = (; preprocess_func = (data) -> data / 100,), ## convert to fraction
407+
)
408+
409+
# Get initial SIC values and use them to calculate ice fraction
410+
ice_fraction = CC.Fields.zeros(space)
411+
evaluate!(ice_fraction, SIC_timevaryinginput, tspan[1])
412+
########### TODO CLEAN UP ############
413+
414+
ocean_fraction = FT(1) .- ice_fraction .- land_fraction
415+
379416
stop_date = date(tspan[end] - tspan[begin])
380417
ocean_sim = OceananigansSimulation(
381418
ocean_fraction,
@@ -384,6 +421,9 @@ function CoupledSimulation(config_dict::AbstractDict)
384421
output_dir = ocean_output_dir,
385422
comms_ctx,
386423
)
424+
425+
ice_sim = ClimaSeaIceSimulation(ice_fraction, ocean_sim; output_dir)
426+
387427
else
388428
ocean_sim = PrescribedOceanSimulation(
389429
FT,
@@ -562,6 +602,9 @@ function CoupledSimulation(config_dict::AbstractDict)
562602
# 3. Calculate and update turbulent fluxes for each surface model,
563603
# and save the weighted average in coupler fields
564604
FluxCalculator.turbulent_fluxes!(cs)
605+
606+
# 4. Compute any ocean-sea ice fluxes
607+
FluxCalculator.ocean_seaice_fluxes!(cs)
565608
end
566609
Utilities.show_memory_usage()
567610
return cs

src/FluxCalculator.jl

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import ClimaCore as CC
1313
import ..Interfacer, ..Utilities
1414

1515
export extrapolate_ρ_to_sfc,
16-
turbulent_fluxes!, get_surface_params, update_turbulent_fluxes!, compute_surface_fluxes!
16+
turbulent_fluxes!,
17+
get_surface_params,
18+
update_turbulent_fluxes!,
19+
compute_surface_fluxes!,
20+
ocean_seaice_fluxes!
1721

1822
function turbulent_fluxes!(cs::Interfacer.CoupledSimulation)
1923
return turbulent_fluxes!(cs.fields, cs.model_sims, cs.thermo_params)
@@ -339,4 +343,23 @@ function compute_surface_fluxes!(
339343
return nothing
340344
end
341345

346+
"""
347+
ocean_seaice_fluxes!(cs::CoupledSimulation)
348+
ocean_seaice_fluxes!(ocean_sim, ice_sim)
349+
350+
Compute the fluxes between the ocean and sea ice simulations.
351+
This function does nothing by default - it should be extended
352+
for any ocean and sea ice models that support flux calculations.
353+
"""
354+
function ocean_seaice_fluxes!(cs::Interfacer.CoupledSimulation)
355+
ocean_seaice_fluxes!(cs.model_sims.ocean_sim, cs.model_sims.ice_sim)
356+
return nothing
357+
end
358+
function ocean_seaice_fluxes!(
359+
ocean_sim::Interfacer.OceanModelSimulation,
360+
ice_sim::Interfacer.SeaIceModelSimulation,
361+
)
362+
return nothing
363+
end
364+
342365
end # module

0 commit comments

Comments
 (0)