Skip to content

Commit aa879f7

Browse files
committed
the code runs
1 parent 68ec04e commit aa879f7

File tree

7 files changed

+136
-90
lines changed

7 files changed

+136
-90
lines changed

docs/src/fieldexchanger.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,17 @@ the atmosphere and each surface model.
2828
## FieldExchanger API
2929

3030
```@docs
31+
ClimaCoupler.FieldExchanger.exchange!
3132
ClimaCoupler.FieldExchanger.update_sim!
3233
ClimaCoupler.FieldExchanger.step_model_sims!
3334
ClimaCoupler.FieldExchanger.update_surface_fractions!
34-
ClimaCoupler.FieldExchanger.exchange!
3535
ClimaCoupler.FieldExchanger.set_caches!
3636
```
3737

3838
## FieldExchanger Internal Functions
3939

4040
```@docs
4141
ClimaCoupler.FieldExchanger.combine_surfaces!
42+
ClimaCoupler.FieldExchanger.resolve_ocean_ice_fractions!
43+
ClimaCoupler.FieldExchanger.import_atmos_fields!
4244
```

experiments/ClimaEarth/Manifest-v1.11.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
julia_version = "1.11.6"
44
manifest_format = "2.0"
5-
project_hash = "bf902fbe4cdfe69d6bdc0c99b12a9c00c5aa28ee"
5+
project_hash = "205aaadcd07952f66e0fa3e53c74e23c6fca069d"
66

77
[[deps.ADTypes]]
88
git-tree-sha1 = "27cecae79e5cc9935255f90c53bb831cc3c870d7"

experiments/ClimaEarth/components/ocean/clima_seaice.jl

Lines changed: 84 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Oceananigans as OC
2+
import ClimaSeaIce as CSI
23
import ClimaOcean as CO
34
import ClimaCoupler: Checkpointer, FieldExchanger, FluxCalculator, Interfacer, Utilities
45
import ClimaComms
@@ -51,12 +52,12 @@ can be found in the documentation for `ClimaOcean.ocean_simulation`.
5152
"""
5253
function 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

112125
Interfacer.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}) =
122135
Interfacer.get_field(sim::ClimaSeaIceSimulation, ::Val{:roughness_momentum}) =
123136
Float32(5.8e-5)
124137
Interfacer.get_field(sim::ClimaSeaIceSimulation, ::Val{:beta}) = Float32(1)
138+
Interfacer.get_field(sim::ClimaSeaIceSimulation, ::Val{:emissivity}) = Float32(1)
125139
Interfacer.get_field(sim::ClimaSeaIceSimulation, ::Val{:surface_direct_albedo}) =
126140
Float32(0.7)
127141
Interfacer.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.
132146
Interfacer.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
248262
Compute the fluxes between the ocean and sea ice, storing them in the `ocean_ice_fluxes`
249263
fields 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
"""
251270
function 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
290337
end
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)

experiments/ClimaEarth/components/ocean/climaocean_helpers.jl

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ fields, rotate them onto the target grid and remap to `Face, Center` and
7171
`Center, Face` fields, respectively.
7272
"""
7373
function set_from_extrinsic_vectors!(vectors, grid, u_cc, v_cc)
74-
arch = grid.architecture
74+
arch = OC.Architectures.architecture(grid)
7575

7676
# Rotate vectors onto the grid
77-
OC.Utils.launch!(arch, grid, :xy, _rotate_velocities!, u_cc, v_cc, grid)
77+
OC.Utils.launch!(arch, grid, :xy, _rotate_vectors!, u_cc, v_cc, grid)
7878

7979
# Fill halo regions with the rotated vectors so we can use them to interpolate
8080
OC.fill_halo_regions!(u_cc)
@@ -85,7 +85,7 @@ function set_from_extrinsic_vectors!(vectors, grid, u_cc, v_cc)
8585
arch,
8686
grid,
8787
:xy,
88-
_interpolate_velocities!,
88+
_interpolate_vectors!,
8989
vectors.u,
9090
vectors.v,
9191
grid,
@@ -96,33 +96,33 @@ function set_from_extrinsic_vectors!(vectors, grid, u_cc, v_cc)
9696
end
9797

9898
"""
99-
_rotate_velocities!(u, v, grid)
99+
_rotate_vectors!(τx, τy, grid)
100100
101101
Rotate the velocities from the extrinsic coordinate system to the intrinsic
102102
coordinate system.
103103
"""
104-
@kernel function _rotate_velocities!(u, v, grid)
104+
@kernel function _rotate_vectors!(τx, τy, grid)
105105
# Use `k = 1` to index into the reduced Fields
106106
i, j = @index(Global, NTuple)
107107
# Rotate u, v from extrinsic to intrinsic coordinate system
108-
ur, vr = OC.Operators.intrinsic_vector(i, j, 1, grid, u, v)
108+
τxr, τyr = OC.Operators.intrinsic_vector(i, j, 1, grid, τx, τy)
109109
@inbounds begin
110-
u[i, j, 1] = ur
111-
v[i, j, 1] = vr
110+
τx[i, j, 1] = τxr
111+
τy[i, j, 1] = τyr
112112
end
113113
end
114114

115115
"""
116-
_interpolate_velocities!(u, v, grid, u_cc, v_cc)
116+
_interpolate_vectors!(τx, τy, grid, τx_cc, τy_cc)
117117
118-
Interpolate the input velocities `u_cc` and `v_cc`, which are Center/Center
118+
Interpolate the input fluxes `τx_cc` and `τy_cc`, which are Center/Center
119119
Fields to Face/Center and Center/Face coordinates, respectively.
120120
"""
121-
@kernel function _interpolate_velocities!(u, v, grid, u_cc, v_cc)
121+
@kernel function _interpolate_vectors!(τx, τy, grid, τx_cc, τy_cc)
122122
# Use `k = 1` to index into the reduced Fields
123123
i, j = @index(Global, NTuple)
124124
@inbounds begin
125-
u[i, j, 1] = OC.Operators.ℑxyᶠᶜᵃ(i, j, 1, grid, u_cc)
126-
v[i, j, 1] = OC.Operators.ℑxyᶜᶠᵃ(i, j, 1, grid, v_cc)
125+
τx[i, j, 1] = OC.Operators.ℑxᶠᵃᵃ(i, j, 1, grid, τx_cc)
126+
τy[i, j, 1] = OC.Operators.ℑyᵃᶠᵃ(i, j, 1, grid, τy_cc)
127127
end
128128
end

experiments/ClimaEarth/components/ocean/oceananigans.jl

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,12 @@ 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_ice_fluxes::NT`: A NamedTuple of fluxes between the ocean and sea ice, computed at each coupling step.
2524
"""
26-
struct OceananigansSimulation{SIM, A, OPROP, REMAP, NT, SIC} <:
27-
Interfacer.OceanModelSimulation
25+
struct OceananigansSimulation{SIM, A, OPROP, REMAP} <: Interfacer.OceanModelSimulation
2826
ocean::SIM
2927
area_fraction::A
3028
ocean_properties::OPROP
3129
remapping::REMAP
32-
ocean_ice_fluxes::NT
3330
end
3431

3532
"""
@@ -44,19 +41,14 @@ Specific details about the default model configuration
4441
can be found in the documentation for `ClimaOcean.ocean_simulation`.
4542
"""
4643
function OceananigansSimulation(
47-
ice_fraction,
44+
area_fraction,
4845
start_date,
4946
stop_date;
5047
output_dir,
5148
comms_ctx = ClimaComms.context(),
5249
)
5350
arch = comms_ctx.device isa ClimaComms.CUDADevice ? OC.GPU() : OC.CPU()
5451

55-
# Initialize the area fraction, making sure no overlap between ice and ocean
56-
boundary_space = axes(ice_fraction)
57-
area_fraction = CC.Fields.zeros(boundary_space)
58-
@. area_fraction = max(min(area_fraction, FT(1) - ice_fraction), FT(0))
59-
6052
# Retrieve EN4 data (monthly)
6153
# (It requires username and password)
6254
dates = range(start_date, step = Dates.Month(1), stop = stop_date)
@@ -72,10 +64,6 @@ function OceananigansSimulation(
7264
z = OC.ExponentialDiscretization(Nz, -depth, 0; scale = 0.85 * depth)
7365

7466
# Regular LatLong because we know how to do interpolation there
75-
76-
# TODO: When moving to TripolarGrid, note that we need to be careful about
77-
# ensuring the coordinate systems align (ie, rotate vectors on the OC grid)
78-
7967
underlying_grid = OC.LatitudeLongitudeGrid(
8068
arch;
8169
size = resolution_points,
@@ -88,7 +76,7 @@ function OceananigansSimulation(
8876
bottom_height = CO.regrid_bathymetry(
8977
underlying_grid;
9078
minimum_depth = 30,
91-
interpolation_passes = 20,
79+
interpolation_passes = 1, # TODO revert
9280
major_basins = 1,
9381
)
9482

@@ -189,29 +177,7 @@ function OceananigansSimulation(
189177
ocean.output_writers[:diagnostics] = netcdf_writer
190178
end
191179

192-
# TODO make this depend on sea ice model type
193-
# Allocate space for the sea ice-ocean (io) fluxes
194-
io_bottom_heat_flux = OC.Field{OC.Center, OC.Center, Nothing}(grid)
195-
io_frazil_heat_flux = OC.Field{OC.Center, OC.Center, Nothing}(grid)
196-
io_salt_flux = OC.Field{OC.Center, OC.Center, Nothing}(grid)
197-
x_momentum = OC.Field{OC.Face, OC.Center, Nothing}(grid)
198-
y_momentum = OC.Field{OC.Center, OC.Face, Nothing}(grid)
199-
200-
ocean_ice_fluxes = (
201-
interface_heat = io_bottom_heat_flux,
202-
frazil_heat = io_frazil_heat_flux,
203-
salt = io_salt_flux,
204-
x_momentum = x_momentum,
205-
y_momentum = y_momentum,
206-
)
207-
208-
sim = OceananigansSimulation(
209-
ocean,
210-
area_fraction,
211-
ocean_properties,
212-
remapping,
213-
ocean_ice_fluxes,
214-
)
180+
sim = OceananigansSimulation(ocean, area_fraction, ocean_properties, remapping)
215181
return sim
216182
end
217183

0 commit comments

Comments
 (0)