Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f417cc9
Revise logic test for whether to step atmos and land models
daverumph Mar 10, 2026
e95665d
Changes based on Julia's informal review.
daverumph Mar 10, 2026
9cba02d
Update coupler dt in CMIP configs to step at same rate as land
daverumph Mar 10, 2026
7f59c43
Merge branch 'main' into dr/coupler_step_logic
daverumph Mar 10, 2026
8750dd5
Merge branch 'main' into dr/coupler_step_logic
daverumph Mar 16, 2026
0a257cb
Initial implementation of support for ITime types when stepping.
daverumph Mar 18, 2026
41a7fae
Merge branch 'main' into dr/coupler_step_logic
daverumph Mar 23, 2026
828c177
Bug fixes
daverumph Mar 30, 2026
428a6cc
Fix stepping when using ITimes
daverumph Mar 31, 2026
e7b4c59
Merge branch 'main' into dr/coupler_step_logic
daverumph Apr 6, 2026
afeb5a5
Add Dates to include list two places
daverumph Apr 6, 2026
e015b4a
Change back ECCO data dependence to ECCO4MONTHLY, remove @info debugging
daverumph Apr 7, 2026
ffe0cec
Merge branch 'main' into dr/coupler_step_logic
daverumph Apr 7, 2026
a26c176
Support ITime as well as Float64 model clocks.
daverumph Apr 7, 2026
31987bd
Ran JuliaFormatter
daverumph Apr 7, 2026
496e21a
Update compat to require ClimaOcean v0.9.5
daverumph Apr 7, 2026
e4ccf56
Return nothing at end of each implementation of step!
daverumph Apr 7, 2026
724303e
Change how dt is calculated when using ITime / Dates.DateTime clocks
daverumph Apr 8, 2026
bc0fdd7
Resolve two issues raised by reviewers
daverumph Apr 8, 2026
3a9f648
Change calculation of delta_t when using ITime/DateTime model clock
daverumph Apr 9, 2026
1e4cd9a
Merge branch 'main' into dr/coupler_step_logic
daverumph Apr 13, 2026
61c914e
Update test to match changed assertion text
daverumph Apr 14, 2026
b0757b9
Run JuliaFormatter
daverumph Apr 14, 2026
668ab29
Merge branch 'main' into dr/coupler_step_logic
daverumph Apr 15, 2026
45272d4
Add tolerance to isapprox in step!{Float64} calls
daverumph Apr 15, 2026
b74c828
Run formatter
daverumph Apr 15, 2026
1a5fb22
Add time incrementing test
imreddyTeja Apr 20, 2026
6769935
Merge branch 'tr/ttm' into dr/coupler_step_logic
daverumph Apr 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/ci_configs/cmip_oceananigans_climaseaice.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ albedo_model: "CouplerAlbedo"
atmos_config_file: "config/atmos_configs/climaatmos_edonly.yml"
coupler_toml: ["toml/amip_edonly.toml"]
dt_atmos: "120secs" # 2 minutes
dt_cpl: "1800secs" # 30 minutes
dt_cpl: "360secs" # 6 minutes
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you revert this to keep the previous behavior?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point of this PR is to improve the behavior, not keep it. The previous coupler time step is less accurate because land/atmos flux exchange only occurs every 30 minutes.

dt_land: "360secs" # 6 minutes
dt_ocean: "1800secs" # 30 minutes
dt_seaice: "1800secs" # 30 minutes
Expand Down
2 changes: 1 addition & 1 deletion config/ci_configs/cmip_oceananigans_climaseaice_bucket.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ atmos_config_file: "config/atmos_configs/climaatmos_edonly.yml"
bucket_albedo_type: "map_temporal"
coupler_toml: ["toml/amip_edonly.toml"]
dt_atmos: "120secs" # 2 minutes
dt_cpl: "1800secs" # 30 minutes
dt_cpl: "360secs" # 6 minutes
dt_land: "360secs" # 6 minutes
dt_ocean: "1800secs" # 30 minutes
dt_seaice: "1800secs" # 30 minutes
Expand Down
16 changes: 13 additions & 3 deletions ext/ClimaCouplerCMIPExt/clima_seaice.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ It contains the following objects:
- `ice_properties::IP`: A NamedTuple of sea ice properties, including melting speed, Stefan-Boltzmann constant,
and the Celsius to Kelvin conversion constant.
"""
struct ClimaSeaIceSimulation{SIM, A, REMAP, NT, IP} <: Interfacer.AbstractSeaIceSimulation
struct ClimaSeaIceSimulation{SIM, A, REMAP, NT, IP, MDT} <:
Interfacer.AbstractSeaIceSimulation
ice::SIM
area_fraction::A
remapping::REMAP
ocean_ice_interface::NT
ice_properties::IP
model_Δt::MDT
end

"""
Expand Down Expand Up @@ -130,6 +132,9 @@ function ClimaSeaIceSimulation(
interface_temperature = OC.Field{OC.Center, OC.Center, Nothing}(grid)
interface_salinity = OC.Field{OC.Center, OC.Center, Nothing}(grid)

# Initialize model_Δt so that time stepping works properly
model_Δt = float(dt)

# Initialize nonzero sea ice if start date provided
if !isnothing(start_date)
sic_metadata = CO.DataWrangling.Metadatum(
Expand Down Expand Up @@ -207,6 +212,7 @@ function ClimaSeaIceSimulation(
remapping,
ocean_ice_interface,
ice_properties,
model_Δt,
)

# Ensure ocean temperature is above freezing where there is sea ice
Expand Down Expand Up @@ -270,8 +276,12 @@ end
###############################################################################

# Timestep the simulation forward to time `t`
Interfacer.step!(sim::ClimaSeaIceSimulation, t) =
OC.time_step!(sim.ice, float(t) - sim.ice.model.clock.time)
function Interfacer.step!(sim::ClimaSeaIceSimulation, t)
Δt = float(t) - sim.ice.model.clock.time
Comment thread
daverumph marked this conversation as resolved.
Outdated
if isapprox(Δt, sim.model_Δt) || Δt > sim.model_Δt
OC.time_step!(sim.ice, Δt)
end
end

Interfacer.get_field(sim::ClimaSeaIceSimulation, ::Val{:area_fraction}) = sim.area_fraction
Interfacer.get_field(sim::ClimaSeaIceSimulation, ::Val{:ice_concentration}) =
Expand Down
20 changes: 13 additions & 7 deletions ext/ClimaCouplerCMIPExt/oceananigans.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ It contains the following objects:
- `remapping::REMAP`: Objects needed to remap from the exchange (spectral) grid to Oceananigans spaces.
- `ice_concentration::SIC`: An Oceananigans Field representing the sea ice concentration on the ocean/sea ice grid.
"""
struct OceananigansSimulation{SIM, A, OPROP, REMAP, SIC} <:
struct OceananigansSimulation{SIM, A, OPROP, REMAP, SIC, MDT} <:
Interfacer.AbstractOceanSimulation
ocean::SIM
area_fraction::A
ocean_properties::OPROP
remapping::REMAP
ice_concentration::SIC
model_Δt::MDT
end

"""
Expand Down Expand Up @@ -67,7 +68,7 @@ function OceananigansSimulation(
tspan,
output_dir,
simple_ocean = false,
dt = nothing,
dt = 1800.0, # 30 minutes
comms_ctx = ClimaComms.context(),
coupled_param_dict = CP.create_toml_dict(FT),
extra_kwargs...,
Expand Down Expand Up @@ -156,10 +157,10 @@ function OceananigansSimulation(
closure = (horizontal_viscosity, vertical_mixing)
end

Δt = isnothing(dt) ? CO.OceanSimulations.estimate_maximum_Δt(grid) : float(dt)
model_Δt = float(dt)
ocean = CO.ocean_simulation(
grid;
Δt,
Δt = model_Δt,
timestepper = :SplitRungeKutta3,
momentum_advection,
tracer_advection,
Expand Down Expand Up @@ -287,6 +288,7 @@ function OceananigansSimulation(
ocean_properties,
remapping,
ice_concentration,
model_Δt,
)
end

Expand Down Expand Up @@ -335,9 +337,13 @@ end
### Functions required by ClimaCoupler.jl for a AbstractSurfaceSimulation
###############################################################################

# Timestep the simulation forward to time `t`
Interfacer.step!(sim::OceananigansSimulation, t) =
OC.time_step!(sim.ocean, float(t) - sim.ocean.model.clock.time)
# Timestep the simulation forward to time `t`. This may not actually do anything.
function Interfacer.step!(sim::OceananigansSimulation, t)
Δt = float(t) - sim.ocean.model.clock.time
if isapprox(Δt, sim.model_Δt) || Δt > sim.model_Δt
OC.time_step!(sim.ocean, Δt)
end
end
Comment thread
daverumph marked this conversation as resolved.

Interfacer.get_field(sim::OceananigansSimulation, ::Val{:area_fraction}) = sim.area_fraction

Expand Down
20 changes: 16 additions & 4 deletions ext/ClimaCouplerClimaAtmosExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -523,11 +523,23 @@ function FluxCalculator.update_turbulent_fluxes!(sim::ClimaAtmosSimulation, fiel
end

# extensions required by FieldExchanger
Interfacer.step!(sim::ClimaAtmosSimulation, t::Real) =
Interfacer.step!(sim.integrator, t - sim.integrator.t, true)
function Interfacer.step!(sim::ClimaAtmosSimulation, t::Real)
Δt = t - sim.integrator.t
if Δt < sim.integrator.dt # don't step if we haven't reached a step boundary
# (can happen if the coupler dt is less than this model's)
return nothing
end
Interfacer.step!(sim.integrator, Δt, true)
Comment thread
juliasloan25 marked this conversation as resolved.
Outdated
end

function Interfacer.step!(sim::ClimaAtmosSimulation, t::ITime)
while sim.integrator.t < t
Interfacer.step!(sim.integrator)
# Don't step if we haven't reached a step boundary
# (This can happen if the coupler dt is less than this model's)
Δt = float(t) - float(sim.integrator.t)
if isapprox(Δt, sim.integrator.dt) || Δt > sim.integrator.dt
while sim.integrator.t < t
Interfacer.step!(sim.integrator)
end
end
return nothing
Comment thread
daverumph marked this conversation as resolved.
end
Expand Down
10 changes: 7 additions & 3 deletions ext/ClimaCouplerClimaLandExt/climaland_bucket.jl
Original file line number Diff line number Diff line change
Expand Up @@ -316,10 +316,14 @@ function Interfacer.update_field!(
end

function Interfacer.step!(sim::BucketSimulation, t)
while float(sim.integrator.t) < float(t)
Interfacer.step!(sim.integrator)
# Don't step if we haven't reached a step boundary
# (This can happen if the coupler dt is less than this model's)
Δt = float(t) - float(sim.integrator.t)
if isapprox(Δt, sim.integrator.dt) || Δt > sim.integrator.dt
while float(sim.integrator.t) < float(t)
Interfacer.step!(sim.integrator)
end
end
return nothing
Comment thread
daverumph marked this conversation as resolved.
end
Interfacer.close_output_writers(sim::BucketSimulation) =
isnothing(sim.output_writer) || close(sim.output_writer)
Expand Down
9 changes: 7 additions & 2 deletions ext/ClimaCouplerClimaLandExt/climaland_integrated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,13 @@ function Interfacer.update_field!(
end

function Interfacer.step!(sim::ClimaLandSimulation, t)
while float(sim.integrator.t) < float(t)
Interfacer.step!(sim.integrator)
# Don't step if we haven't reached a step boundary
# (This can happen if the coupler dt is less than this model's)
Δt = float(t) - float(sim.integrator.t)
if isapprox(Δt, sim.integrator.dt) || Δt > sim.integrator.dt
while sim.integrator.t < t
Interfacer.step!(sim.integrator)
end
end
return nothing
end
Expand Down
8 changes: 7 additions & 1 deletion src/Input.jl
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,13 @@ function parse_component_dts!(config_dict)
end
for key in component_dt_names
component_dt = Float64(Utilities.time_to_seconds(config_dict[key]))
@assert isapprox(Δt_cpl % component_dt, 0.0) "Coupler dt must be divisible by all component dt's\n dt_cpl = $Δt_cpl\n $key = $component_dt"
if key == "dt_atmos"
Comment thread
juliasloan25 marked this conversation as resolved.
Outdated
# ensure that the coupler dt is an integer multiple of the atmos dt
@assert isapprox(Δt_cpl % component_dt, 0.0) "Coupler time step must be an integer multiple of the atmos dt\n dt_cpl = $Δt_cpl\n $key = $component_dt"
else
# all other (surface) model dts must be divisible by the coupler dt
@assert isapprox(component_dt % Δt_cpl, 0.0) "All surface component dts must be divisible by the coupler dt\n $key = $component_dt\n dt_cpl = $Δt_cpl"
end
component_dt_dict[key] = component_dt
end
else
Expand Down
Loading