Skip to content

Commit 26d38c1

Browse files
committed
isolate area fraction logic to update_surface_fractions
1 parent b8e151a commit 26d38c1

File tree

7 files changed

+53
-110
lines changed

7 files changed

+53
-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 & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +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))
128-
ice_fraction = ifelse.(ice_fraction .> FT(0.5), FT(1), FT(0))
122+
ice_fraction = CC.Fields.zeros(space)
123+
evaluate!(ice_fraction, SIC_timevaryinginput, tspan[1])
129124

130125
params = IceSlabParameters{FT}()
131126

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: 26 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -324,16 +324,6 @@ function CoupledSimulation(config_dict::AbstractDict)
324324
# Determine whether to use a shared surface space
325325
shared_surface_space = share_surface_space ? boundary_space : nothing
326326
if land_model == "bucket"
327-
328-
# TODO move this into resolve_area_fractions and call after init, think about restarts
329-
polar_mask = CC.Fields.zeros(boundary_space)
330-
lat = CC.Fields.coordinate_field(boundary_space).lat
331-
polar_mask .= abs.(lat) .>= FT(80)
332-
333-
# Set land fraction to 1 where polar_mask is 1
334-
@. land_fraction = ifelse.(polar_mask == FT(1), FT(1), land_fraction)
335-
336-
337327
land_sim = BucketSimulation(
338328
FT;
339329
dt = component_dt_dict["dt_land"],
@@ -372,97 +362,51 @@ function CoupledSimulation(config_dict::AbstractDict)
372362
error("Invalid land model specified: $(land_model)")
373363
end
374364

375-
# TODO separate drivers to clean this up
376-
## sea ice model
377-
if 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-
ice_fraction = Interfacer.get_field(ice_sim, Val(:area_fraction))
391-
elseif ice_model == "clima_seaice"
392-
@assert sim_mode <: CMIPMode
393-
394-
# TODO how should we initialize ocean fraction when using ClimaSeaIce?
395-
# TODO init everything with AF=1, then resolve after getting sea ice
396-
sic_data = try
397-
joinpath(
398-
@clima_artifact("historical_sst_sic", comms_ctx),
399-
"MODEL.ICE.HAD187001-198110.OI198111-202206.nc",
400-
)
401-
catch error
402-
@warn "Using lowres SIC. If you want the higher resolution version, you have to obtain it from ClimaArtifacts"
403-
joinpath(
404-
@clima_artifact("historical_sst_sic_lowres", comms_ctx),
405-
"MODEL.ICE.HAD187001-198110.OI198111-202206_lowres.nc",
406-
)
407-
end
408-
@info "Using initial condition prescribed SIC file: " sic_data
409-
410-
SIC_timevaryinginput = TimeVaryingInput(
411-
sic_data,
412-
"SEAICE",
413-
boundary_space,
414-
reference_date = start_date,
415-
file_reader_kwargs = (; preprocess_func = (data) -> data / 100,), ## convert to fraction
416-
)
417-
418-
# Get initial SIC values and use them to calculate ice fraction
419-
ice_fraction = CC.Fields.zeros(boundary_space)
420-
evaluate!(ice_fraction, SIC_timevaryinginput, tspan[1])
421-
else
422-
error("Invalid ice model specified: $(ice_model)")
423-
end
424-
425-
ice_fraction = ifelse.(ice_fraction .> FT(0.5), FT(1), FT(0))
426365
## ocean model
427-
ocean_fraction = FT(1) .- ice_fraction .- land_fraction
428-
429366
if sim_mode <: CMIPMode
430367
stop_date = date(tspan[end] - tspan[begin])
431368
ocean_sim = OceananigansSimulation(
432-
ocean_fraction,
369+
boundary_space,
433370
start_date,
434371
stop_date;
435372
output_dir = ocean_output_dir,
436373
comms_ctx,
437374
)
438-
439-
if ice_model == "clima_seaice"
440-
ice_sim = ClimaSeaIceSimulation(
441-
ice_fraction,
442-
ocean_sim;
443-
output_dir = ice_output_dir,
444-
start_date,
445-
)
446-
# TODO don't need to initialize ocean fraction correctly if we do this
447-
# TODO can rename to `resolve_area_fractions!` and also make land_fraction cover poles here instead of in driver
448-
FieldExchanger.resolve_ocean_ice_fractions!(
449-
ocean_sim,
450-
ice_sim,
451-
land_fraction,
452-
)
453-
end
454375
else
455376
ocean_sim = PrescribedOceanSimulation(
456377
FT,
457378
boundary_space,
458379
start_date,
459380
t_start,
460-
ocean_fraction,
461381
thermo_params,
462382
comms_ctx;
463383
sst_path = subseasonal_sst,
464384
)
465385
end
386+
## sea ice model
387+
if ice_model == "clima_seaice"
388+
ice_sim = ClimaSeaIceSimulation(
389+
ice_fraction,
390+
ocean_sim;
391+
output_dir = ice_output_dir,
392+
start_date,
393+
)
394+
elseif ice_model == "prescribed"
395+
ice_sim = PrescribedIceSimulation(
396+
FT;
397+
tspan = tspan,
398+
dt = component_dt_dict["dt_seaice"],
399+
saveat = saveat,
400+
space = boundary_space,
401+
thermo_params = thermo_params,
402+
comms_ctx,
403+
start_date,
404+
land_fraction,
405+
sic_path = subseasonal_sic,
406+
)
407+
else
408+
error("Invalid ice model specified: $(ice_model)")
409+
end
466410

467411
elseif (sim_mode <: AbstractSlabplanetSimulationMode)
468412

@@ -494,7 +438,6 @@ function CoupledSimulation(config_dict::AbstractDict)
494438
dt = component_dt_dict["dt_ocean"],
495439
space = boundary_space,
496440
saveat = saveat,
497-
area_fraction = (FT(1) .- land_fraction), ## NB: this ocean fraction includes areas covered by sea ice (unlike the one contained in the cs)
498441
thermo_params = thermo_params,
499442
evolving = evolving_ocean,
500443
)
@@ -618,8 +561,8 @@ function CoupledSimulation(config_dict::AbstractDict)
618561
619562
The concrete steps for proper initialization are:
620563
=#
621-
622564
# 1. Make sure surface model area fractions sum to 1 everywhere.
565+
# Note that ocean and ice fractions are not accurate until after this call.
623566
FieldExchanger.update_surface_fractions!(cs)
624567

625568
# 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)