Skip to content

Commit dab0ae5

Browse files
committed
isolate area fraction logic to update_surface_fractions
1 parent b24315d commit dab0ae5

File tree

7 files changed

+54
-110
lines changed

7 files changed

+54
-110
lines changed

docs/src/fieldexchanger.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ the atmosphere and each surface model.
3939

4040
```@docs
4141
ClimaCoupler.FieldExchanger.combine_surfaces!
42-
ClimaCoupler.FieldExchanger.resolve_ocean_ice_fractions!
42+
ClimaCoupler.FieldExchanger.resolve_area_fractions!
4343
ClimaCoupler.FieldExchanger.import_atmos_fields!
4444
```

experiments/ClimaEarth/components/ocean/oceananigans.jl

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Specific details about the default model configuration
4343
can be found in the documentation for `ClimaOcean.ocean_simulation`.
4444
"""
4545
function OceananigansSimulation(
46-
area_fraction,
46+
boundary_space,
4747
start_date,
4848
stop_date;
4949
output_dir,
@@ -141,7 +141,7 @@ function OceananigansSimulation(
141141
lat_cc = reshape(lat_cc, 1, length(lat_cc))
142142
target_points_cc = @. CC.Geometry.LatLongPoint(lat_cc, long_cc)
143143
# TODO: We can remove the `nothing` after CC > 0.14.33
144-
remapper_cc = CC.Remapping.Remapper(axes(area_fraction), target_points_cc, nothing)
144+
remapper_cc = CC.Remapping.Remapper(boundary_space, target_points_cc, nothing)
145145

146146
# Construct two 2D Center/Center fields to use as scratch space while remapping
147147
scratch_cc1 = OC.Field{OC.Center, OC.Center, Nothing}(grid)
@@ -178,10 +178,13 @@ function OceananigansSimulation(
178178
ocean.output_writers[:diagnostics] = netcdf_writer
179179
end
180180

181-
# Initialize with 0 ice concentration; this will be updated in `resolve_ocean_ice_fractions!`
181+
# Initialize with 0 ice concentration; this will be updated in `resolve_area_fractions!`
182182
# if the ocean is coupled to a non-prescribed sea ice model.
183183
ice_concentration = OC.Field{OC.Center, OC.Center, Nothing}(grid)
184184

185+
# Create a dummy area fraction that will get overwritten in `update_surface_fractions!`
186+
area_fraction = ones(boundary_space)
187+
185188
return OceananigansSimulation(
186189
ocean,
187190
area_fraction,
@@ -192,7 +195,7 @@ function OceananigansSimulation(
192195
end
193196

194197
"""
195-
FieldExchanger.resolve_ocean_ice_fractions!(ocean_sim, ice_sim, land_fraction)
198+
FieldExchanger.resolve_area_fractions!(ocean_sim, ice_sim, land_fraction)
196199
197200
Ensure the ocean and ice area fractions are consistent with each other.
198201
This matters in the case of a LatitudeLongitudeGrid, which is only
@@ -206,7 +209,7 @@ and doesn't need to be set again since its fraction is static.
206209
This function also updates the ice concentration field in the ocean simulation
207210
so that it can be used for weighting flux updates.
208211
"""
209-
function FieldExchanger.resolve_ocean_ice_fractions!(
212+
function FieldExchanger.resolve_area_fractions!(
210213
ocean_sim::OceananigansSimulation,
211214
ice_sim,
212215
land_fraction,
@@ -222,9 +225,9 @@ function FieldExchanger.resolve_ocean_ice_fractions!(
222225
polar_mask = CC.Fields.zeros(boundary_space)
223226
polar_mask .= abs.(lat) .>= FT(80)
224227

225-
# TODO do we want both to be 0 since we use capped lat/lon?
226-
# Set ice fraction to 1 - land_fraction and ocean fraction to 0 where polar_mask is 1
227-
@. ice_fraction = ifelse.(polar_mask == FT(1), FT(1) - land_fraction, ice_fraction)
228+
# Set land fraction to 1 and ice/ocean fraction to 0 where polar_mask is 1
229+
@. land_fraction = ifelse.(polar_mask == FT(1), FT(1), land_fraction)
230+
@. ice_fraction = ifelse.(polar_mask == FT(1), FT(0), ice_fraction)
228231
@. ocean_fraction = ifelse.(polar_mask == FT(1), FT(0), ocean_fraction)
229232
end
230233

experiments/ClimaEarth/components/ocean/prescr_ocean.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ function PrescribedOceanSimulation(
6565
space,
6666
start_date,
6767
t_start,
68-
area_fraction,
6968
thermo_params,
7069
comms_ctx;
7170
z0m = FT(5.8e-5),
@@ -113,7 +112,7 @@ function PrescribedOceanSimulation(
113112
α_diffuse = ones(space) .* α_diffuse_val,
114113
u_int = zeros(space),
115114
v_int = zeros(space),
116-
area_fraction = area_fraction,
115+
area_fraction = ones(space),
117116
phase = TD.Liquid(),
118117
thermo_params = thermo_params,
119118
SST_timevaryinginput = SST_timevaryinginput,

experiments/ClimaEarth/components/ocean/prescr_seaice.jl

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,8 @@ function PrescribedIceSimulation(
119119
)
120120

121121
# Get initial SIC values and use them to calculate ice fraction
122-
SIC_init = CC.Fields.zeros(space)
123-
evaluate!(SIC_init, SIC_timevaryinginput, tspan[1])
124-
125-
# Overwrite ice fraction with the static land area fraction anywhere we have nonzero land area
126-
# max needed to avoid Float32 errors (see issue #271; Heisenbug on HPC)
127-
ice_fraction = @. max(min(SIC_init, FT(1) - land_fraction), FT(0))
122+
ice_fraction = CC.Fields.zeros(space)
123+
evaluate!(ice_fraction, SIC_timevaryinginput, tspan[1])
128124

129125
params = IceSlabParameters{FT}()
130126

experiments/ClimaEarth/components/ocean/slab_ocean.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function slab_ocean_space_init(space, params)
5555
end
5656

5757
"""
58-
SlabOceanSimulation(::Type{FT}; tspan, dt, saveat, space, area_fraction, stepper = CTS.RK4()) where {FT}
58+
SlabOceanSimulation(::Type{FT}; tspan, dt, saveat, space, stepper = CTS.RK4()) where {FT}
5959
6060
Initializes the `DiffEq` problem, and creates a Simulation-type object containing the necessary information for `step!` in the coupling loop.
6161
"""
@@ -65,7 +65,6 @@ function SlabOceanSimulation(
6565
dt,
6666
saveat,
6767
space,
68-
area_fraction,
6968
thermo_params,
7069
stepper = CTS.RK4(),
7170
evolving = true,
@@ -80,7 +79,7 @@ function SlabOceanSimulation(
8079
F_turb_energy = CC.Fields.zeros(space),
8180
SW_d = CC.Fields.zeros(space),
8281
LW_d = CC.Fields.zeros(space),
83-
area_fraction = area_fraction,
82+
area_fraction = CC.Fields.ones(space),
8483
thermo_params = thermo_params,
8584
α_direct = CC.Fields.ones(space) .* params.α,
8685
α_diffuse = CC.Fields.ones(space) .* params.α,

experiments/ClimaEarth/setup_run.jl

Lines changed: 27 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -307,16 +307,6 @@ function CoupledSimulation(config_dict::AbstractDict)
307307
# Determine whether to use a shared surface space
308308
shared_surface_space = share_surface_space ? boundary_space : nothing
309309
if land_model == "bucket"
310-
311-
# TODO move this into resolve_area_fractions and call after init, think about restarts
312-
polar_mask = CC.Fields.zeros(boundary_space)
313-
lat = CC.Fields.coordinate_field(boundary_space).lat
314-
polar_mask .= abs.(lat) .>= FT(80)
315-
316-
# Set land fraction to 1 where polar_mask is 1
317-
@. land_fraction = ifelse.(polar_mask == FT(1), FT(1), land_fraction)
318-
319-
320310
land_sim = BucketSimulation(
321311
FT;
322312
dt = component_dt_dict["dt_land"],
@@ -355,97 +345,51 @@ function CoupledSimulation(config_dict::AbstractDict)
355345
error("Invalid land model specified: $(land_model)")
356346
end
357347

358-
# TODO separate drivers to clean this up
359-
## sea ice model
360-
if ice_model == "prescribed"
361-
ice_sim = PrescribedIceSimulation(
362-
FT;
363-
tspan = tspan,
364-
dt = component_dt_dict["dt_seaice"],
365-
saveat = saveat,
366-
space = boundary_space,
367-
thermo_params = thermo_params,
368-
comms_ctx,
369-
start_date,
370-
land_fraction,
371-
sic_path = subseasonal_sic,
372-
)
373-
ice_fraction = Interfacer.get_field(ice_sim, Val(:area_fraction))
374-
elseif ice_model == "clima_seaice"
375-
@assert sim_mode <: CMIPMode
376-
377-
# TODO how should we initialize ocean fraction when using ClimaSeaIce?
378-
# TODO init everything with AF=1, then resolve after getting sea ice
379-
sic_data = try
380-
joinpath(
381-
@clima_artifact("historical_sst_sic", comms_ctx),
382-
"MODEL.ICE.HAD187001-198110.OI198111-202206.nc",
383-
)
384-
catch error
385-
@warn "Using lowres SIC. If you want the higher resolution version, you have to obtain it from ClimaArtifacts"
386-
joinpath(
387-
@clima_artifact("historical_sst_sic_lowres", comms_ctx),
388-
"MODEL.ICE.HAD187001-198110.OI198111-202206_lowres.nc",
389-
)
390-
end
391-
@info "Using initial condition prescribed SIC file: " sic_data
392-
393-
SIC_timevaryinginput = TimeVaryingInput(
394-
sic_data,
395-
"SEAICE",
396-
boundary_space,
397-
reference_date = start_date,
398-
file_reader_kwargs = (; preprocess_func = (data) -> data / 100,), ## convert to fraction
399-
)
400-
401-
# Get initial SIC values and use them to calculate ice fraction
402-
ice_fraction = CC.Fields.zeros(boundary_space)
403-
evaluate!(ice_fraction, SIC_timevaryinginput, tspan[1])
404-
else
405-
error("Invalid ice model specified: $(ice_model)")
406-
end
407-
408-
## ocean model using prescribed data
409-
ice_fraction = Interfacer.get_field(ice_sim, Val(:area_fraction))
410-
ocean_fraction = FT(1) .- ice_fraction .- land_fraction
411-
348+
## ocean model
412349
if sim_mode <: CMIPMode
413350
stop_date = date(tspan[end] - tspan[begin])
414351
ocean_sim = OceananigansSimulation(
415-
ocean_fraction,
352+
boundary_space,
416353
start_date,
417354
stop_date;
418355
output_dir = dir_paths.ocean_output_dir,
419356
comms_ctx,
420357
)
421-
422-
if ice_model == "clima_seaice"
423-
ice_sim = ClimaSeaIceSimulation(
424-
ice_fraction,
425-
ocean_sim;
426-
output_dir = dir_paths.ice_output_dir,
427-
start_date,
428-
)
429-
# TODO don't need to initialize ocean fraction correctly if we do this
430-
# TODO can rename to `resolve_area_fractions!` and also make land_fraction cover poles here instead of in driver
431-
FieldExchanger.resolve_ocean_ice_fractions!(
432-
ocean_sim,
433-
ice_sim,
434-
land_fraction,
435-
)
436-
end
437358
else
438359
ocean_sim = PrescribedOceanSimulation(
439360
FT,
440361
boundary_space,
441362
start_date,
442363
t_start,
443-
ocean_fraction,
444364
thermo_params,
445365
comms_ctx;
446366
sst_path = subseasonal_sst,
447367
)
448368
end
369+
## sea ice model
370+
if ice_model == "clima_seaice"
371+
ice_sim = ClimaSeaIceSimulation(
372+
ice_fraction,
373+
ocean_sim;
374+
output_dir = dir_paths.ice_output_dir,
375+
start_date,
376+
)
377+
elseif ice_model == "prescribed"
378+
ice_sim = PrescribedIceSimulation(
379+
FT;
380+
tspan = tspan,
381+
dt = component_dt_dict["dt_seaice"],
382+
saveat = saveat,
383+
space = boundary_space,
384+
thermo_params = thermo_params,
385+
comms_ctx,
386+
start_date,
387+
land_fraction,
388+
sic_path = subseasonal_sic,
389+
)
390+
else
391+
error("Invalid ice model specified: $(ice_model)")
392+
end
449393

450394
elseif (sim_mode <: AbstractSlabplanetSimulationMode)
451395

@@ -477,7 +421,6 @@ function CoupledSimulation(config_dict::AbstractDict)
477421
dt = component_dt_dict["dt_ocean"],
478422
space = boundary_space,
479423
saveat = saveat,
480-
area_fraction = (FT(1) .- land_fraction), ## NB: this ocean fraction includes areas covered by sea ice (unlike the one contained in the cs)
481424
thermo_params = thermo_params,
482425
evolving = evolving_ocean,
483426
)
@@ -598,8 +541,8 @@ function CoupledSimulation(config_dict::AbstractDict)
598541
599542
The concrete steps for proper initialization are:
600543
=#
601-
602544
# 1. Make sure surface model area fractions sum to 1 everywhere.
545+
# Note that ocean and ice fractions are not accurate until after this call.
603546
FieldExchanger.update_surface_fractions!(cs)
604547

605548
# 2. Import atmospheric and surface fields into the coupler fields,

src/FieldExchanger.jl

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export update_sim!,
1616
exchange!,
1717
set_caches!,
1818
update_surface_fractions!,
19-
resolve_ocean_ice_fractions!
19+
resolve_area_fractions!
2020

2121
"""
2222
update_surface_fractions!(cs::Interfacer.CoupledSimulation)
@@ -69,9 +69,9 @@ function update_surface_fractions!(cs::Interfacer.CoupledSimulation)
6969
)
7070
ocean_fraction = Interfacer.get_field(ocean_sim, Val(:area_fraction))
7171

72-
# ensure that ocean and ice fractions are consistent
72+
# Apply any additional constraints on the ocean and ice fractions if necessary
7373
if haskey(cs.model_sims, :ice_sim)
74-
resolve_ocean_ice_fractions!(ocean_sim, cs.model_sims.ice_sim, land_fraction)
74+
resolve_area_fractions!(ocean_sim, cs.model_sims.ice_sim, land_fraction)
7575
end
7676
else
7777
cs.fields.scalar_temp1 .= 0
@@ -84,15 +84,15 @@ function update_surface_fractions!(cs::Interfacer.CoupledSimulation)
8484
end
8585

8686
"""
87-
resolve_ocean_ice_fractions!(ocean_sim, ice_sim, land_fraction)
87+
resolve_area_fractions!(ocean_sim, ice_sim, land_fraction)
8888
8989
Ensure that the ocean and ice fractions are consistent with each other.
9090
For most ocean and ice models, this does nothing since the ocean fraction is
9191
defined as `1 - ice_fraction - land_fraction`. However, some models may have
9292
additional constraints on the ice and ocean fractions that need to be enforced.
9393
This function can be extended for such models.
9494
"""
95-
function resolve_ocean_ice_fractions!(ocean_sim, ice_sim, land_fraction)
95+
function resolve_area_fractions!(ocean_sim, ice_sim, land_fraction)
9696
return nothing
9797
end
9898

@@ -209,6 +209,11 @@ end
209209
Updates the surface component model cache with the current coupler fields
210210
*besides turbulent fluxes*, which are updated in `update_turbulent_fluxes`.
211211
212+
Note that upwelling longwave and shortwave radiation are not computed here,
213+
and are expected to be computed internally by the surface model.
214+
Some component models extend this function and compute the upwelling longwave
215+
and shortwave radiation in their methods of `update_sim!`.
216+
212217
# Arguments
213218
- `sim`: [Interfacer.SurfaceModelSimulation] containing a surface model simulation object.
214219
- `csf`: [NamedTuple] containing coupler fields.
@@ -217,7 +222,6 @@ function update_sim!(sim::Interfacer.SurfaceModelSimulation, csf)
217222
# radiative fluxes
218223
Interfacer.update_field!(sim, Val(:SW_d), csf.SW_d)
219224
Interfacer.update_field!(sim, Val(:LW_d), csf.LW_d)
220-
# TODO need to compute SWU, LWU here too
221225

222226
# precipitation
223227
Interfacer.update_field!(sim, Val(:liquid_precipitation), csf.P_liq)

0 commit comments

Comments
 (0)