Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
339f9ed
this should work?
simone-silvestri Jul 30, 2025
21e6feb
Merge remote-tracking branch 'origin/main' into ss/no-heat-flux-when-…
simone-silvestri Jul 30, 2025
ddfdcca
launch this
simone-silvestri Jul 31, 2025
2c98f69
remove this
simone-silvestri Aug 5, 2025
ef87337
go like this
simone-silvestri Aug 5, 2025
c643d1e
make it work
simone-silvestri Aug 7, 2025
df016c4
Merge remote-tracking branch 'origin/main' into ss/no-heat-flux-when-…
simone-silvestri Aug 8, 2025
ef9a3ab
try it out
simone-silvestri Aug 8, 2025
56a57bc
let's go!
simone-silvestri Aug 8, 2025
1a5e2ff
Merge branch 'main' into ss/no-heat-flux-when-unconsolidated
simone-silvestri Oct 13, 2025
1def747
Update sea_ice_time_stepping.jl
simone-silvestri Oct 13, 2025
e222a0f
Update sea_ice_model.jl
simone-silvestri Oct 13, 2025
bb87c8b
Optimize halo region filling for momentum equations
simone-silvestri Oct 16, 2025
bed7f64
go ahead
simone-silvestri Oct 27, 2025
72b8f9e
add this
simone-silvestri Oct 27, 2025
20d164a
go back
simone-silvestri Oct 27, 2025
5508c80
Update sea_ice_model.jl
simone-silvestri Oct 27, 2025
9199c44
assume
simone-silvestri Oct 27, 2025
df1dace
Update runtests.jl
simone-silvestri Oct 27, 2025
055f89e
Adjust halo size calculation in sea ice model
simone-silvestri Nov 3, 2025
b03d502
add a test
simone-silvestri Nov 6, 2025
f35150f
separate tests
simone-silvestri Nov 6, 2025
90ba288
remove ext
simone-silvestri Nov 6, 2025
c9bc9ce
ensure the correct group
simone-silvestri Nov 6, 2025
992afb6
fix distributed tests
simone-silvestri Nov 6, 2025
56619c6
add the dynamics
simone-silvestri Nov 6, 2025
abbd164
fix tests
simone-silvestri Nov 6, 2025
fb51b14
sea ice model
simone-silvestri Nov 6, 2025
3dffd81
another bugfix
simone-silvestri Nov 6, 2025
cba00d7
add stuff
simone-silvestri Nov 6, 2025
f92c1d3
add Oceananigans
simone-silvestri Nov 6, 2025
4bc0e60
fix keyword
simone-silvestri Nov 6, 2025
bcc35c2
fix these tests
simone-silvestri Nov 6, 2025
8e74f4e
fix stuff
simone-silvestri Nov 6, 2025
b87ca1e
add with _halo
simone-silvestri Nov 6, 2025
9639f34
fix
simone-silvestri Nov 6, 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
158 changes: 127 additions & 31 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,54 +1,150 @@
name: CI

on:
push:
branches: [main]
tags: ["*"]
pull_request:
paths:
- '.github/workflows/ci.yml'
- 'src/**'
- 'test/**'
- 'Project.toml'
push:
branches:
- main
tags: '*'
paths:
- '.github/workflows/ci.yml'
- 'src/**'
- 'test/**'
- 'Project.toml'

concurrency:
# Skip intermediate builds: always.
# Cancel intermediate builds: only if it is a pull request build.
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}

jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
checkpointing_tests:
name: Checkpointing Tests - Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
version:
- '1.10'
os:
- ubuntu-latest
arch:
- x64
steps:
- uses: actions/checkout@v5
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
env:
TEST_GROUP: "checkpointing"

distributed:
name: Distributed Tests - Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
version:
- '1.10'
os:
- ubuntu-latest
arch:
- x64
steps:
- uses: actions/checkout@v5
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
env:
TEST_GROUP: "distributed"

advection:
name: Sea Ice Advection - Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
version:
- '1.10'
- '1.12'
os:
- ubuntu-latest
arch:
- x64
- x86
include:
# test macOS and Windows with latest Julia only
- os: macOS-latest
arch: x64
version: '1.10'
- os: windows-latest
arch: x64
version: '1.10'
- os: windows-latest
arch: x86
version: '1.10'
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@latest
- uses: actions/checkout@v5
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: actions/cache@v3
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
env:
cache-name: cache-artifacts
TEST_GROUP: "advection"

timestepping:
name: Time Stepping - Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
version:
- '1.10'
os:
- ubuntu-latest
arch:
- x64
steps:
- uses: actions/checkout@v5
- uses: julia-actions/setup-julia@v2
with:
path: ~/.julia/artifacts
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
restore-keys: |
${{ runner.os }}-test-${{ env.cache-name }}-
${{ runner.os }}-test-
${{ runner.os }}-
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v2
env:
TEST_GROUP: "timestepping"

enthalpy:
name: Enthalpy Model - Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
version:
- '1.10'
os:
- ubuntu-latest
arch:
- x64
steps:
- uses: actions/checkout@v5
- uses: julia-actions/setup-julia@v2
with:
file: lcov.info
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
env:
TEST_GROUP: "enthalpy"
2 changes: 1 addition & 1 deletion src/SeaIceDynamics/sea_ice_momentum_equations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ function SeaIceMomentumEquation(grid;
top_momentum_stress = nothing,
bottom_momentum_stress = nothing,
free_drift = nothing,
solver = SplitExplicitSolver(150),
solver = SplitExplicitSolver(grid; substeps=150),
Copy link
Member

Choose a reason for hiding this comment

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

Thought --- should we distinguish between the halo size and number of substeps? This would allow adaptive time-stepping; users just have to select the maximum number of substeps they expect to need. This could be combined with a max time step option in the TimeStepWizard

Copy link
Member

Choose a reason for hiding this comment

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

In other words the abstraction here is baking in an equivalence between substeps and halo points, but it need not be

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah this is kind of the downside of this method. We could think about giving a "maximum halo", which would limit the amount of substeps (and in turn the maximum allowable timestep for the TimeStepWizard).

minimum_concentration = 1e-3,
minimum_mass = 1.0)

Expand Down
38 changes: 27 additions & 11 deletions src/SeaIceDynamics/split_explicit_momentum_equations.jl
Original file line number Diff line number Diff line change
@@ -1,25 +1,41 @@
using Oceananigans.Grids: AbstractGrid, architecture, halo_size
using Oceananigans.BoundaryConditions: fill_halo_regions!, fill_halo_size, fill_halo_offset
using Oceananigans.Utils: configure_kernel
using Oceananigans.Architectures: convert_to_device
using Oceananigans.Fields: instantiated_location, boundary_conditions
using Oceananigans.DistributedComputations: DistributedGrid
using Oceananigans.ImmersedBoundaries: peripheral_node
using Oceananigans.BoundaryConditions: fill_halo_regions!, fill_halo_size, fill_halo_offset

using Oceananigans.Models.HydrostaticFreeSurfaceModels.SplitExplicitFreeSurfaces: split_explicit_kernel_size

struct SplitExplicitSolver
substeps :: Int
struct SplitExplicitSolver{I, K}
substeps :: I
kernel_parameters :: K
end

"""
SplitExplicitSolver(; substeps=120)
SplitExplicitSolver(grid; substeps=120)

Creates a `SplitExplicitSolver` that controls the dynamical evolution of sea-ice momentum
by subcycling `substeps` times in between each ice_thermodynamics / tracer advection time step.

The default number of substeps is 120.
"""
SplitExplicitSolver(; substeps=120) = SplitExplicitSolver(substeps)
SplitExplicitSolver(grid; substeps=120) = SplitExplicitSolver(substeps, :xy)

function SplitExplicitSolver(grid::DistributedGrid; substeps=120)
Nx, Ny, _ = size(grid)
Hx, Hy, _ = halo_size(grid)
TX, TY, _ = topology(grid)
kernel_sizes = map(split_explicit_kernel_size, (TX, TY), (Nx, Ny), (Hx, Hy))
return SplitExplicitSolver(substeps, KernelParameters(kernel_sizes...))
end

# When no grid is provided, we assume a serial grid with default kernel parameters
SplitExplicitSolver(; substeps=120) = SplitExplicitSolver(substeps, :xy)

const SplitExplicitMomentumEquation = SeaIceMomentumEquation{<:SplitExplicitSolver}
const ExtendedSplitExplicitMomentumEquation = SeaIceMomentumEquation{<:SplitExplicitSolver{<:Any, <:KernelParameters}}

"""
time_step_momentum!(model, rheology::AbstractExplicitRheology, Δt)
Expand Down Expand Up @@ -58,10 +74,10 @@ function time_step_momentum!(model, dynamics::SplitExplicitMomentumEquation, Δt
ℵ = model.ice_concentration,
ρ = model.ice_density))

active_cells_map = Oceananigans.Grids.get_active_column_map(grid)
params = dynamics.solver.kernel_parameters

u_velocity_kernel!, _ = configure_kernel(arch, grid, :xy, _u_velocity_step!; active_cells_map)
v_velocity_kernel!, _ = configure_kernel(arch, grid, :xy, _v_velocity_step!; active_cells_map)
u_velocity_kernel!, _ = configure_kernel(arch, grid, params, _u_velocity_step!)
v_velocity_kernel!, _ = configure_kernel(arch, grid, params, _v_velocity_step!)

substeps = dynamics.solver.substeps
initialize_rheology!(model, dynamics.rheology)
Expand Down Expand Up @@ -101,6 +117,9 @@ function time_step_momentum!(model, dynamics::SplitExplicitMomentumEquation, Δt
converted_stresses_args = convert_to_device(arch, stresses_args)

for substep in 1 : substeps
fill_halo_regions!(converted_u_halo...; only_local_halos = true)
fill_halo_regions!(converted_v_halo...; only_local_halos = true)

# Compute stresses! depending on the particular rheology implementation
compute_stresses!(dynamics, converted_stresses_args...)

Expand All @@ -115,9 +134,6 @@ function time_step_momentum!(model, dynamics::SplitExplicitMomentumEquation, Δt
v_velocity_kernel!(converted_v_args...)
u_velocity_kernel!(converted_u_args...)
end

fill_halo_regions!(converted_u_halo...)
fill_halo_regions!(converted_v_halo...)
end
end

Expand Down
25 changes: 23 additions & 2 deletions src/sea_ice_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@ using Oceananigans.TimeSteppers: TimeStepper
using Oceananigans.BoundaryConditions: regularize_field_boundary_conditions
using Oceananigans: tupleit, tracernames
using Oceananigans.Forcings: model_forcing
using Oceananigans.Grids: halo_size, topology, with_halo
using Oceananigans.Grids: LeftConnected, RightConnected, FullyConnected

using ClimaSeaIce.SeaIceDynamics: ExtendedSplitExplicitMomentumEquation
using ClimaSeaIce.SeaIceThermodynamics: PrescribedTemperature
using ClimaSeaIce.SeaIceThermodynamics.HeatBoundaryConditions: flux_summary

@inline instantiate(T::DataType) = T()
@inline instantiate(T) = T

const ConnectedTopology = Union{LeftConnected, RightConnected, FullyConnected}

struct SeaIceModel{GR, TD, D, TS, CL, U, T, IT, IC, ID, CT, STF, A, F, Arch} <: AbstractModel{TS, Arch}
architecture :: Arch
grid :: GR
Expand Down Expand Up @@ -72,8 +77,24 @@ function SeaIceModel(grid;
boundary_conditions = regularize_field_boundary_conditions(boundary_conditions, grid, field_names)

if isnothing(velocities)
u = Field{Face, Center, Nothing}(grid, boundary_conditions=boundary_conditions.u)
v = Field{Center, Face, Nothing}(grid, boundary_conditions=boundary_conditions.v)

# Extend the halos for the velocity fields if the dynamics is
# an extended split explicit momentum equation
if dynamics isa ExtendedSplitExplicitMomentumEquation
old_halos = halo_size(grid)
Nsubsteps = length(dynamics.solver.substeps)
TX, TY = topology(grid)
Hx = TX() isa ConnectedTopology ? Nsubsteps + old_halos[1] : old_halos[1]
Hy = TY() isa ConnectedTopology ? Nsubsteps + old_halos[2] : old_halos[2]

new_halos = (Hx, Hy, old_halos[3])
velocity_grid = with_halo(new_halos, grid)
else
velocity_grid = grid
end

u = Field{Face, Center, Nothing}(velocity_grid, boundary_conditions=boundary_conditions.u)
v = Field{Center, Face, Nothing}(velocity_grid, boundary_conditions=boundary_conditions.v)
velocities = (; u, v)
end

Expand Down
5 changes: 2 additions & 3 deletions src/sea_ice_time_stepping.jl
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,9 @@ function update_state!(model::SIM)

foreach(prognostic_fields(model)) do field
mask_immersed_field_xy!(field, k=size(model.grid, 3))
fill_halo_regions!(field, model.clock, fields(model))
end

fill_halo_regions!(prognostic_fields(model), model.clock, fields(model))

update_model_field_time_series!(model, model.clock)

return nothing
Expand All @@ -104,4 +103,4 @@ function update_model_field_time_series!(model::SeaIceModel, clock::Clock)
end

return nothing
end
end
57 changes: 57 additions & 0 deletions test/distributed_tests_utils.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using JLD2
using MPI
using Oceananigans
using Oceananigans.DistributedComputations: reconstruct_global_field, reconstruct_global_grid
using Oceananigans.Units
using ClimaSeaIce
using ClimaSeaIce.SeaIceDynamics: SeaIceMomentumEquation, ElastoViscoPlasticRheology, SemiImplicitStress

# Run the distributed grid simulation and save down reconstructed results
function run_distributed_sea_ice(arch, filename)
distributed_grid = RectilinearGrid(arch;
size = (100, 100, 1),
x = (-10kilometers, 10kilometers),
y = (-10kilometers, 10kilometers),
z = (-1, 0),
halo = (5, 5, 5))

model = run_distributed_simulation(distributed_grid)

u = reconstruct_global_field(model.velocities.u)
v = reconstruct_global_field(model.velocities.v)

if arch.local_rank == 0
jldsave(filename; u = Array(interior(u, :, :, 1)),
v = Array(interior(v, :, :, 1)))
end

MPI.Barrier(MPI.COMM_WORLD)
MPI.Finalize()

return nothing
end

# Just a random simulation on a rectilinear grid
function run_distributed_simulation(grid)

τᵤ = 0.01
τᵥ = 0.01
τₒ = SemiImplicitStress()

# We use an elasto-visco-plastic rheology and WENO seventh order
# for advection of h and ℵ
dynamics = SeaIceMomentumEquation(grid;
top_momentum_stress = (u=τᵤ, v=τᵥ),
bottom_momentum_stress = τₒ,
rheology = ElastoViscoPlasticRheology(),
solver = SplitExplicitSolver(grid, substeps=50))

model = SeaIceModel(grid; ice_thermodynamics = nothing, dynamics, advection = WENO(order=7))

for N in 1:100
time_step!(model, 1minutes)
end

return model
end

Loading
Loading