Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
806be1e
Added a run_coupled! function to implement checkpointers in coupled s…
taimoorsohail Mar 11, 2025
46b7bb2
Added a run_coupled! function to implement checkpointers in coupled s…
taimoorsohail Mar 11, 2025
75b8182
Simplified the near_global_ocean example to see why the checkpointing…
taimoorsohail Mar 12, 2025
55106ba
Merge remote-tracking branch 'origin/main' into ts/checkpointer-for-c…
taimoorsohail Mar 12, 2025
61f91f5
merge main;
taimoorsohail Mar 13, 2025
fb5d35d
Testing the set! function
navidcy Mar 13, 2025
e39c87a
merge main
navidcy Mar 13, 2025
d15439f
managed to pick up!
navidcy Mar 13, 2025
a0dca49
Changed checkointer_mwe.jl
taimoorsohail Mar 13, 2025
8ff3748
Cleaning up checkpointer_mwe.jl file
taimoorsohail Mar 13, 2025
3fdf785
extends methods to work with OSIM and OSIMSIM
navidcy Mar 13, 2025
2328bb5
mwe
navidcy Mar 13, 2025
9ab0df1
simplify
navidcy Mar 13, 2025
5bd34f8
tidying up
navidcy Mar 13, 2025
9e2af01
bit cleaner
navidcy Mar 13, 2025
4f5ff2e
set!(sim::OSIMSIM{PrescribedAtmosphere})
navidcy Mar 13, 2025
ba2521a
cleaner mwe
navidcy Mar 13, 2025
b75d6e0
Changed the function to set!
taimoorsohail Mar 13, 2025
f1cae4f
Merge NCC changes
taimoorsohail Mar 13, 2025
dfb57fa
Merge NCC changes
taimoorsohail Mar 14, 2025
76dd720
reverting near_global_ocean.jl example
taimoorsohail Mar 14, 2025
3177cf2
Update near_global_ocean_simulation.jl
taimoorsohail Mar 14, 2025
868c870
Update Project.toml
taimoorsohail Mar 14, 2025
5f02123
Added checkpointing test; integrated checkpointer into one_degree exa…
taimoorsohail Mar 14, 2025
f9087aa
set clock method for each simulation type
navidcy Mar 14, 2025
4b0ebeb
Apply suggestions from code review
navidcy Mar 14, 2025
3f0b9d5
Apply suggestions from code review
navidcy Mar 14, 2025
f4aa21e
don't pickup by default; add explanation
navidcy Mar 14, 2025
0e0a168
merge
navidcy Mar 14, 2025
f56ca8e
move set_clock! for PrescribedAtmosphere to where it belongs
navidcy Mar 14, 2025
f8c1444
move set_clock! for PrescribedAtmosphere to where it belongs
navidcy Mar 14, 2025
cfba574
only pickup in the second time
navidcy Mar 14, 2025
9f811df
Merge branch 'main' into ts/checkpointer-for-coupled-model
navidcy Mar 14, 2025
4110aca
extend set!(model::OSIM,...) instead of set!(sim:OSIMSIM,...)
navidcy Mar 16, 2025
e56515f
Merge branch 'main' into ts/checkpointer-for-coupled-model
navidcy Mar 16, 2025
a9be97c
use default radiation
navidcy Mar 16, 2025
afd7a66
add set!(::PrescribedAtmospher, checkopoint_file_path)
navidcy Mar 16, 2025
1fddbf2
use set!(::PrescribedAtmosphere,...)
navidcy Mar 16, 2025
8699207
check all clocks are aligned
navidcy Mar 16, 2025
9f9ca89
drop unused aliases
navidcy Mar 16, 2025
ba80b56
Merge branch 'ts/checkpointer-for-coupled-model' of github.com:CliMA/…
taimoorsohail Mar 19, 2025
90abea4
Removed bottom line
taimoorsohail Mar 19, 2025
9efd6b8
try to generalize
navidcy Mar 19, 2025
006574f
Merge branch 'ts/checkpointer-for-coupled-model' of github.com:CliMA/…
navidcy Mar 19, 2025
e3029cb
Merge branch 'main' into ts/checkpointer-for-coupled-model
navidcy Mar 19, 2025
1e8d638
don't assume ocean component is special
navidcy Mar 20, 2025
5a69a52
bump Oceanigans compat
navidcy Mar 20, 2025
a74499f
remove commented code
navidcy Mar 20, 2025
0a46067
properties in write_output! is kwarg
navidcy Mar 20, 2025
98ec18b
undo changes
navidcy Mar 20, 2025
f9e9e60
Delete examples/generate_atmos_dataset.jl
navidcy Mar 20, 2025
39c347a
Delete src/CoupledSimulation.jl
navidcy Mar 20, 2025
5281eab
Delete test/test_ocean_sea_ice_model_parameter_space.jl
navidcy Mar 20, 2025
368358e
Delete test/test_simulations.jl
navidcy Mar 20, 2025
021c306
Delete src/DataWrangling/JRA55.jl
navidcy Mar 20, 2025
48dd1b1
Delete src/DistributedUtils.jl
navidcy Mar 20, 2025
fdf45d7
don't import things we don't need
navidcy Mar 20, 2025
875d045
cleanup
navidcy Mar 20, 2025
1e34bb5
validate_properties -> validate_checkpointed_properties
navidcy Mar 20, 2025
1f3a46b
no need to duplicate validation
navidcy Mar 20, 2025
4f9425a
fix initialize! and update_state! + add set_clock!
navidcy Mar 25, 2025
bf96083
reorganize imports
navidcy Mar 25, 2025
cd7f0c5
wip
navidcy Apr 24, 2025
47d1121
merge main and resolve conflicts
navidcy Apr 24, 2025
8bd0d36
Update ClimaOcean.jl
navidcy Apr 25, 2025
ed8c6df
Merge branch 'main' into ts/checkpointer-for-coupled-model
navidcy Apr 28, 2025
72efbc4
Merge branch 'main' into ts/checkpointer-for-coupled-model
taimoorsohail Apr 30, 2025
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 Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ JLD2 = "0.4, 0.5"
KernelAbstractions = "0.9"
MPI = "0.20"
NCDatasets = "0.12, 0.13, 0.14"
Oceananigans = "0.96 - 0.99"
Oceananigans = "0.96.1 - 0.99"
OffsetArrays = "1.14"
PrecompileTools = "1"
Reactant = "0.2.45"
Expand Down
72 changes: 72 additions & 0 deletions examples/checkpointer_mwe.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using ClimaOcean
using Oceananigans
using CFTime
using Dates
using Printf

arch = CPU()

Nx = 120
Ny = 50
Nz = 20

grid = LatitudeLongitudeGrid(arch;
size = (Nx, Ny, Nz),
halo = (7, 7, 7),
z = (-6000, 0),
latitude = (-75, 75),
longitude = (0, 360))


function testbed_coupled_simulation(grid; stop_iteration=8)
ocean = ocean_simulation(grid)

radiation = Radiation(arch)

atmosphere = JRA55PrescribedAtmosphere(arch; backend=JRA55NetCDFBackend(4))

coupled_model = OceanSeaIceModel(ocean; atmosphere, radiation)

return Simulation(coupled_model; Δt=10, stop_iteration)
end

simulation = testbed_coupled_simulation(grid; stop_iteration=8)

wall_time = Ref(time_ns())

function progress(sim)
ocean = sim.model.ocean
atmosphere = sim.model.atmosphere

step_time = 1e-9 * (time_ns() - wall_time[])

msg = @sprintf("iteration: %d, sim time: %s, atmos time: %s, ocean time: %s, wall time: %s",
iteration(sim), sim.model.clock.time, atmosphere.clock.time, ocean.model.clock.time, prettytime(step_time))

@info msg

wall_time[] = time_ns()
end

simulation.callbacks[:progress] = Callback(progress, IterationInterval(1))

simulation.output_writers[:checkpoint] = Checkpointer(ocean.model;
schedule = IterationInterval(3),
prefix = "checkpointer",
dir = ".",
verbose = true,
overwrite_existing = true)

run!(simulation)

new_simulation = testbed_coupled_simulation(grid; stop_iteration=14)

new_simulation.output_writers[:checkpoint] = Checkpointer(ocean.model;
schedule = IterationInterval(3),
prefix = "checkpointer",
dir = ".",
verbose = true,
overwrite_existing = true)


run!(new_simulation, pickup=true)
18 changes: 18 additions & 0 deletions examples/one_degree_simulation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,14 @@ ocean.output_writers[:surface] = JLD2Writer(ocean.model, outputs;
indices = (:, :, grid.Nz),
overwrite_existing = true)

# We also save a series of Checkpointers that will enable us to restart the simulation
# from a later point; ideal for longer runs.

simulation.output_writers[:checkpoint] = Checkpointer(ocean.model;
schedule = TimeInterval(5days),
prefix = "one_degree_checkpointer",
overwrite_existing = true)

# ### Ready to run

# We are ready to press the big red button and run the simulation.
Expand All @@ -170,6 +178,16 @@ simulation.Δt = 20minutes
simulation.stop_time = 360days
run!(simulation)

# After we had run long-enough to produce at least one checkpointer file, if we want to restart the
# simulation we call
#
# ```julia
# run!(simulation, pickup=true)
# ```
#
# Doing so, simulation will be picked up from the latest checkpointer.


# ### A pretty movie
#
# We load the saved output and make a pretty movie of the simulation. First we plot a snapshot:
Expand Down
1 change: 0 additions & 1 deletion src/ClimaOcean.jl
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,3 @@ using PrecompileTools: @setup_workload, @compile_workload
end

end # module

Original file line number Diff line number Diff line change
Expand Up @@ -418,4 +418,3 @@ function sea_ice_similarity_theory(sea_ice::SeaIceSimulation)
interface_temperature_type = SkinTemperature(internal_flux)
return SimilarityTheoryFluxes(; interface_temperature_type)
end

6 changes: 6 additions & 0 deletions src/OceanSeaIceModels/OceanSeaIceModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ using ClimaOcean: stateindex
using KernelAbstractions: @kernel, @index
using KernelAbstractions.Extras.LoopInfo: @unroll

import Oceananigans.OutputWriters: checkpointer_address,
required_checkpointed_properties,
default_checkpointed_properties,
prognostic_fields

function downwelling_radiation end
function freshwater_flux end
function reference_density end
Expand Down Expand Up @@ -69,6 +74,7 @@ import .InterfaceComputations:
compute_sea_ice_ocean_fluxes!

include("ocean_sea_ice_model.jl")
include("output_writers.jl")
include("freezing_limited_ocean_temperature.jl")
include("time_step_ocean_sea_ice_model.jl")

Expand Down
50 changes: 43 additions & 7 deletions src/OceanSeaIceModels/PrescribedAtmospheres.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ using Oceananigans.OutputReaders: FieldTimeSeries, update_field_time_series!, ex
using Oceananigans.TimeSteppers: Clock, tick!

using Adapt
using JLD2
using Thermodynamics.Parameters: AbstractThermodynamicsParameters

import Oceananigans.TimeSteppers: time_step!
import Oceananigans: initialize!, prognostic_fields
import Oceananigans.Fields: set!
import Oceananigans.TimeSteppers: time_step!, set_clock!
import Oceananigans.OutputWriters: checkpointer_address,
required_checkpointed_properties,
default_checkpointed_properties

import Thermodynamics.Parameters:
gas_constant, #
Expand All @@ -22,15 +28,15 @@ import Thermodynamics.Parameters:
T_0, # Enthalpy reference temperature
LH_v0, # Vaporization enthalpy at the reference temperature
LH_s0, # Sublimation enthalpy at the reference temperature
LH_f0, # Fusionn enthalpy at the reference temperature
LH_f0, # Fusion enthalpy at the reference temperature
cp_d, # Heat capacity of dry air at constant pressure
cp_v, # Isobaric specific heat capacity of gaseous water vapor
cp_l, # Isobaric specific heat capacity of liquid water
cp_i, # Isobaric specific heat capacity of water ice
cv_v, # Heat capacity of dry air at constant volume
cv_l, # Isobaric specific heat capacity of liquid water
cv_i, # Isobaric specific heat capacity of liquid water
e_int_v0, # what? someting about reference internal energy of water vapor
e_int_v0, # what? something about reference internal energy of water vapor
T_freeze, # Freezing temperature of _pure_ water
T_triple, # Triple point temperature of _pure_ water
press_triple, # Triple point pressure of pure water
Expand Down Expand Up @@ -75,7 +81,7 @@ Construct a set of parameters that define the density of moist air,
```

where ``p`` is pressure, ``T`` is temperature, ``q`` defines the partition
of total mass into vapor, liqiud, and ice mass fractions, and
of total mass into vapor, liquid, and ice mass fractions, and
``Rᵐ`` is the effective specific gas constant for the mixture,

```math
Expand Down Expand Up @@ -318,6 +324,29 @@ function Base.show(io::IO, pa::PrescribedAtmosphere)
print(io, "└── boundary_layer_height: ", prettysummary(pa.boundary_layer_height))
end

checkpointer_address(::PrescribedAtmosphere) = "PrescribedAtmosphere"
default_checkpointed_properties(::PrescribedAtmosphere) = [:clock]
required_checkpointed_properties(::PrescribedAtmosphere) = [:clock]
prognostic_fields(::PrescribedAtmosphere) = ()

function set!(model::PrescribedAtmosphere, checkpoint_file_path::AbstractString)
addr = checkpointer_address(model)

checkpointed_clock = nothing
jldopen(checkpoint_file_path, "r") do file
checkpointed_clock = file["$addr/clock"]
end

# Update model clock
set_clock!(model, checkpointed_clock)

@info "hi, I set the clock of the atmosphere"
return nothing
end

set_clock!(atmos::PrescribedAtmosphere, new_clock) =
set_clock!(atmos.clock, new_clock)

function default_atmosphere_velocities(grid, times)
ua = FieldTimeSeries{Center, Center, Nothing}(grid, times)
va = FieldTimeSeries{Center, Center, Nothing}(grid, times)
Expand All @@ -327,7 +356,7 @@ end
function default_atmosphere_tracers(grid, times)
Ta = FieldTimeSeries{Center, Center, Nothing}(grid, times)
qa = FieldTimeSeries{Center, Center, Nothing}(grid, times)
parent(Ta) .= 273.15 + 20
parent(Ta) .= 273.15 + 20 # ᵒK
return (T=Ta, q=qa)
end

Expand All @@ -347,13 +376,20 @@ end
This is approximately equal to the mean sea-level atmospheric pressure on Earth. """
function default_atmosphere_pressure(grid, times)
pa = FieldTimeSeries{Center, Center, Nothing}(grid, times)
parent(pa) .= 101325
parent(pa) .= 101325 # Pa
return pa
end

@inline function time_step!(atmos::PrescribedAtmosphere, Δt)
tick!(atmos.clock, Δt)
update_state!(atmos)

return nothing
end

initialize!(atmos::PrescribedAtmosphere) = update_state!(atmos)

function update_state!(atmos::PrescribedAtmosphere)
time = Time(atmos.clock.time)
ftses = extract_field_time_series(atmos)

Expand Down Expand Up @@ -422,7 +458,7 @@ struct TwoBandDownwellingRadiation{SW, LW}
end

"""
TwoBandDownwellingRadiation(shortwave=nothing, longwave=nothing)
TwoBandDownwellingRadiation(; shortwave=nothing, longwave=nothing)

Return a two-band model for downwelling radiation (split into a shortwave band
and a longwave band) that passes through the atmosphere and arrives at the surface of ocean
Expand Down
69 changes: 45 additions & 24 deletions src/OceanSeaIceModels/ocean_sea_ice_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@ using Oceananigans.TimeSteppers: Clock
using Oceananigans: SeawaterBuoyancy
using ClimaSeaIce.SeaIceThermodynamics: melting_temperature
using KernelAbstractions: @kernel, @index

using SeawaterPolynomials: TEOS10EquationOfState

import Thermodynamics as AtmosphericThermodynamics

# Simulations interface
import Oceananigans: fields, prognostic_fields
import Oceananigans: fields,
prognostic_fields
import Oceananigans.Architectures: architecture
import Oceananigans.Fields: set!
import Oceananigans.Models: timestepper, NaNChecker, default_nan_checker
import Oceananigans.OutputWriters: default_included_properties
import Oceananigans.Simulations: reset!, initialize!, iteration
import Oceananigans.TimeSteppers: time_step!, update_state!, time
import Oceananigans.Utils: prettytime
import Oceananigans.Models: timestepper, NaNChecker, default_nan_checker, initialization_update_state!
import Oceananigans.OutputWriters: checkpointer_address,
required_checkpointed_properties,
default_checkpointed_properties

import Oceananigans.Simulations: reset!, initialize!, iteration, run!
import Oceananigans.TimeSteppers: time_step!, update_state!, time, set_clock!
import Oceananigans.Utils: prettytime

import .PrescribedAtmospheres: set_clock!

mutable struct OceanSeaIceModel{I, A, O, F, C, Arch} <: AbstractModel{Nothing, Arch}
architecture :: Arch
Expand All @@ -29,6 +33,7 @@ mutable struct OceanSeaIceModel{I, A, O, F, C, Arch} <: AbstractModel{Nothing, A
end

const OSIM = OceanSeaIceModel
const OSIMPA = OceanSeaIceModel{<:Any, <:PrescribedAtmosphere}

function Base.summary(model::OSIM)
A = nameof(typeof(architecture(model)))
Expand All @@ -53,20 +58,23 @@ function Base.show(io::IO, cm::OSIM)
end

# Assumption: We have an ocean!
architecture(model::OSIM) = architecture(model.ocean.model)
Base.eltype(model::OSIM) = Base.eltype(model.ocean.model)
prettytime(model::OSIM) = prettytime(model.clock.time)
iteration(model::OSIM) = model.clock.iteration
timestepper(::OSIM) = nothing
default_included_properties(::OSIM) = tuple()
prognostic_fields(cm::OSIM) = nothing
fields(::OSIM) = NamedTuple()
default_clock(TT) = Oceananigans.TimeSteppers.Clock{TT}(0, 0, 1)

function reset!(model::OSIM)
reset!(model.ocean)
return nothing
end
architecture(model::OSIM) = architecture(model.ocean.model)
Base.eltype(model::OSIM) = Base.eltype(model.ocean.model)
prettytime(model::OSIM) = prettytime(model.clock.time)
iteration(model::OSIM) = model.clock.iteration
timestepper(::OSIM) = nothing
default_included_properties(::OSIM) = tuple()
prognostic_fields(::OSIM) = tuple()
fields(::OSIM) = NamedTuple()
default_clock(TT) = Oceananigans.TimeSteppers.Clock{TT}(0, 0, 1)
time(model::OSIM) = model.clock.time
required_checkpointed_properties(::OSIM) = [:clock]
default_checkpointed_properties(::OSIM) = [:clock]
checkpointer_address(::OSIM) = "OceanSeaIceModel"

reset!(model::OSIM) = reset!(model.ocean)

components(model::OSIM) = (model.atmosphere, model.ocean.model)

# Make sure to initialize the exchanger here
function initialization_update_state!(model::OSIM)
Expand All @@ -77,10 +85,25 @@ end

function initialize!(model::OSIM)
initialize!(model.ocean)
initialize!(model.atmosphere)
initialize!(model.interfaces.exchanger, model.atmosphere)
return nothing
end

function set_clock!(model::OSIM, clock)

set_clock!(model.clock, clock)

# set the component clocks
atmos, ocean = model.atmosphere, model.ocean
set_clock!(atmos, clock)
set_clock!(ocean, clock)

return nothing
end

set_clock!(sim::Simulation, new_clock) = set_clock!(sim.model, new_clock)

reference_density(unsupported) =
throw(ArgumentError("Cannot extract reference density from $(typeof(unsupported))"))

Expand Down Expand Up @@ -159,10 +182,8 @@ function OceanSeaIceModel(ocean, sea_ice=FreezingLimitedOceanTemperature(eltype(
return ocean_sea_ice_model
end

time(coupled_model::OceanSeaIceModel) = coupled_model.clock.time

# Check for NaNs in the first prognostic field (generalizes to prescribed velocities).
function default_nan_checker(model::OceanSeaIceModel)
function default_nan_checker(model::OSIM)
u_ocean = model.ocean.model.velocities.u
nan_checker = NaNChecker((; u_ocean))
return nan_checker
Expand Down
Loading
Loading