Skip to content

ImmersedBoundaryCondition for drag, take 2 #137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6f81571
take 2
simone-silvestri Aug 14, 2024
2548528
forse restart build
simone-silvestri Aug 14, 2024
2ae2f6a
Update src/OceanSimulations/OceanSimulations.jl
simone-silvestri Aug 14, 2024
5209b3f
bugfix
simone-silvestri Aug 14, 2024
13e8d91
Merge branch 'ss/immersed-boundary-conditions-take-2' of github.com:C…
simone-silvestri Aug 14, 2024
41d5b19
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Aug 15, 2024
9bf8862
adapt to new syntax
simone-silvestri Aug 15, 2024
9653b2c
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Aug 19, 2024
14c9e94
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Aug 20, 2024
50f2046
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Aug 27, 2024
18b1ed8
update project
simone-silvestri Aug 28, 2024
cca28de
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Sep 6, 2024
fe7faf7
update Oceananigans
simone-silvestri Sep 16, 2024
43379eb
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Sep 18, 2024
3b60770
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Sep 24, 2024
3d48ce0
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Oct 29, 2024
ebcadb1
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Nov 4, 2024
d34eea6
Update OceanSimulations.jl
simone-silvestri Nov 4, 2024
a69b71e
add also generate fluxes
simone-silvestri Nov 5, 2024
637c9f5
this should work now?
simone-silvestri Nov 5, 2024
2094ae4
try adding manifest
simone-silvestri Nov 5, 2024
8a833fa
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Nov 6, 2024
4d80a81
remove the manifest
simone-silvestri Nov 6, 2024
0fd6607
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Nov 6, 2024
bfe46cf
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Nov 8, 2024
56edade
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Nov 9, 2024
7f4217e
Merge branch 'main' into ss/immersed-boundary-conditions-take-2
simone-silvestri Nov 10, 2024
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
4 changes: 2 additions & 2 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const OUTPUT_DIR = joinpath(@__DIR__, "src/literated")
to_be_literated = [
"inspect_ecco_data.jl",
"generate_bathymetry.jl",
# "generate_surface_fluxes.jl",
"generate_surface_fluxes.jl",
"single_column_os_papa_simulation.jl",
# "mediterranean_simulation_with_ecco_restoring.jl",
"near_global_ocean_simulation.jl"
Expand Down Expand Up @@ -50,7 +50,7 @@ pages = [
"Examples" => [
"Inspect ECCO2 data" => "literated/inspect_ecco_data.md",
"Generate bathymetry" => "literated/generate_bathymetry.md",
# "Surface fluxes" => "literated/generate_surface_fluxes.md",
"Surface fluxes" => "literated/generate_surface_fluxes.md",
"Single column simulation" => "literated/single_column_os_papa_simulation.md",
# "Mediterranean simulation with ECCO restoring" => "literated/mediterranean_simulation_with_ecco_restoring.md",
"Near-global Ocean simulation" => "literated/near_global_ocean_simulation.md",
Expand Down
11 changes: 4 additions & 7 deletions examples/generate_bathymetry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ h_one_basin = regrid_bathymetry(grid; major_basins = 1)
nothing #hide

# Finally, we visualize the generated bathymetry data for the Mediterranean Sea using CairoMakie.

λ, φ, z = nodes(h_smooth)

land_smooth = interior(h_smooth) .>= 0
interior(h_smooth)[land_smooth] .= NaN

Expand All @@ -63,13 +60,13 @@ interior(h_one_basin)[land_one_basin] .= NaN
fig = Figure(size=(850, 1150))

ax = Axis(fig[1, 1], title = "Rough bathymetry", xlabel = "Longitude", ylabel = "Latitude")
hm = heatmap!(ax, λ, φ, - interior(h_rough, :, :, 1), nan_color=:white, colormap = Reverse(:deep))
hm = heatmap!(ax, h_rough, nan_color=:grey, colormap = Reverse(:deep))

ax = Axis(fig[2, 1], title = "Smooth bathymetry", xlabel = "Longitude", ylabel = "Latitude")
hm = heatmap!(ax, λ, φ, - interior(h_smooth, :, :, 1), nan_color=:white, colormap = Reverse(:deep))
hm = heatmap!(ax, h_smooth, nan_color=:grey, colormap = Reverse(:deep))

ax = Axis(fig[3, 1], title = "Bathymetry without only one basin", xlabel = "Longitude", ylabel = "Latitude")
hm = heatmap!(ax, λ, φ, - interior(h_one_basin, :, :, 1), nan_color=:white, colormap = Reverse(:deep))
ax = Axis(fig[3, 1], title = "Bathymetry with only one basin", xlabel = "Longitude", ylabel = "Latitude")
hm = heatmap!(ax, h_one_basin, nan_color=:grey, colormap = Reverse(:deep))

cb = Colorbar(fig[1:3, 2], hm, height = Relative(3/4), label = "Depth (m)")

Expand Down
32 changes: 15 additions & 17 deletions examples/generate_surface_fluxes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,13 @@ using CairoMakie

# # Computing fluxes on the ECCO2 grid
#
# We start by building the ECCO2 grid, using `ECCO_mask`
# to identify missing cells.
# We start by building the ECCO2 grid, using `ECCO_bottom_height` to identify the bottom height.

mask = ECCO_mask()
grid = mask.grid
grid = ImmersedBoundaryGrid(grid, GridFittedBoundary(mask))
grid = ECCO_immersed_grid()

fig = Figure()
ax = Axis(fig[1, 1])
heatmap!(ax, interior(grid.immersed_boundary.mask, :, :, grid.Nz))
heatmap!(ax, interior(grid.immersed_boundary.bottom_height, :, :, 1))
save("ECCO_continents.png", fig) #hide

# ![](ECCO_continents.png)
Expand Down Expand Up @@ -71,24 +68,25 @@ coupled_model = OceanSeaIceModel(ocean; atmosphere, radiation=Radiation())
# Now that the surface fluxes are computed, we can extract and visualize them.
# The turbulent fluxes are stored in `coupled_model.fluxes.turbulent`.

fluxes = coupled_model.fluxes.turbulent.fields
fluxes = coupled_model.fluxes.turbulent.fields
λ, φ, z = nodes(fluxes.sensible_heat)

fig = Figure(size = (800, 400))
fig = Figure(size = (800, 800), fontsize = 15)

ax = Axis(fig[1, 1], title = "Sensible heat flux (W m⁻²)")
heatmap!(ax, fluxes.sensible_heat; colormap = :bwr)
ax = Axis(fig[1, 1], title = "Sensible heat flux (W m⁻²)", ylabel = "Latitude")
heatmap!(ax, λ, φ, interior(fluxes.sensible_heat, :, :, 1); colormap = :bwr)

ax = Axis(fig[1, 2], title = "Latent heat flux (W m⁻²)")
heatmap!(ax, fluxes.latent_heat; colormap = :bwr)
heatmap!(ax, λ, φ, interior(fluxes.latent_heat, :, :, 1); colormap = :bwr)

ax = Axis(fig[2, 1], title = "Zonal wind stress (N m)")
heatmap!(ax, fluxes.x_momentum; colormap = :bwr)
ax = Axis(fig[2, 1], title = "Zonal wind stress (N m)", ylabel = "Latitude")
heatmap!(ax, λ, φ, interior(fluxes.x_momentum, :, :, 1); colormap = :bwr)

ax = Axis(fig[2, 2], title = "Meridional wind stress (N m)")
heatmap!(ax, fluxes.y_momentum; colormap = :bwr)
ax = Axis(fig[2, 2], title = "Meridional wind stress (N m)", xlabel = "Longitude")
heatmap!(ax, λ, φ, interior(fluxes.y_momentum, :, :, 1); colormap = :bwr)

ax = Axis(fig[3, 1], title = "Water vapor flux (kg m⁻² s⁻¹)")
heatmap!(ax, Mv; colormap = :bwr)
ax = Axis(fig[3, 1], title = "Water vapor flux (kg m⁻² s⁻¹)", xlabel = "Longitude", ylabel = "Latitude")
heatmap!(ax, λ, φ, interior(fluxes.water_vapor, :, :, 1); colormap = :bwr)

save("fluxes.png", fig)
# ![](fluxes.png)
5 changes: 1 addition & 4 deletions examples/single_column_os_papa_simulation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,7 @@ last_time = simulation_days * snapshots_per_day
atmosphere = JRA55_prescribed_atmosphere(1:last_time;
longitude = λ★,
latitude = φ★,
#longitude = (λ★ - 1/4, λ★ + 1/4),
#latitude = (φ★ - 1/4, φ★ + 1/4),
backend = InMemory(),
include_rivers_and_icebergs = false)
backend = InMemory())

# This builds a representation of the atmosphere on the small grid

Expand Down
6 changes: 3 additions & 3 deletions src/DataWrangling/ECCO/ECCO.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module ECCO

export ECCOMetadata, ECCO_field, ECCO_mask, adjusted_ECCO_tracers, initialize!
export ECCOMetadata, ECCO_field, ECCO_mask, ECCO_immersed_grid, adjusted_ECCO_tracers, initialize!
export ECCO2Monthly, ECCO4Monthly, ECCO2Daily
export ECCORestoring, LinearlyTaperedPolarMask

Expand Down Expand Up @@ -88,7 +88,7 @@ empty_ECCO_field(variable_name::Symbol; kw...) = empty_ECCO_field(ECCOMetadata(v

function empty_ECCO_field(metadata::ECCOMetadata;
architecture = CPU(),
horizontal_halo = (3, 3))
horizontal_halo = (7, 7))

Nx, Ny, Nz, _ = size(metadata)
loc = location(metadata)
Expand Down Expand Up @@ -128,7 +128,7 @@ in the x and y direction. The halo in the z-direction is one.
"""
function ECCO_field(metadata::ECCOMetadata;
architecture = CPU(),
horizontal_halo = (3, 3))
horizontal_halo = (7, 7))

download_dataset!(metadata)
path = metadata_path(metadata)
Expand Down
37 changes: 36 additions & 1 deletion src/DataWrangling/ECCO/ECCO_mask.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Oceananigans.Architectures: AbstractArchitecture
using Oceananigans.Grids: znode
import ClimaOcean: stateindex

"""
Expand Down Expand Up @@ -40,6 +41,40 @@ end
@inbounds mask[i, j, k] = (Tᵢ[i, j, k] == 0)
end

"""
ECCO_immersed_grid(metadata, architecture = CPU())

Compute the `ImmersedBoundaryGrid` for `metadata` with a bottom height field that is defined
by the first non-missing value from the bottom up.
"""
function ECCO_immersed_grid(metadata, architecture = CPU())

mask = ECCO_mask(metadata, architecture)
grid = mask.grid
bottom = Field{Center, Center, Nothing}(grid)

# Set the mask with zeros where field is defined
launch!(architecture, grid, :xy, _set_height_from_mask!, bottom, grid, mask)

return ImmersedBoundaryGrid(grid, GridFittedBottom(bottom))
end

# Default
ECCO_immersed_grid(arch::AbstractArchitecture=CPU()) = ECCO_immersed_grid(ECCOMetadata(:temperature), arch)

@kernel function _set_height_from_mask!(bottom, grid, mask)
i, j = @index(Global, NTuple)

# Starting from the bottom
@inbounds bottom[i, j, 1] = znode(i, j, 1, grid, Center(), Center(), Face())

# Sweep up
for k in 1:grid.Nz
z⁺ = znode(i, j, k+1, grid, Center(), Center(), Face())
@inbounds bottom[i, j, k] = ifelse(mask[i, j, k], z⁺, bottom[i, j, k])
end
end

struct LinearlyTaperedPolarMask{N, S, Z}
northern :: N
southern :: S
Expand Down Expand Up @@ -88,4 +123,4 @@ end
LX, LY, LZ = loc
λ, φ, z = node(i, j, k, grid, LX(), LY(), LZ())
return mask(φ, z)
end
end
43 changes: 27 additions & 16 deletions src/OceanSimulations/OceanSimulations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,9 @@ default_tracer_advection() = FluxFormAdvection(WENO(order=7),
@inline u_quadratic_bottom_drag(i, j, grid, c, Φ, μ) = @inbounds - μ * Φ.u[i, j, 1] * spᶠᶜᶜ(i, j, 1, grid, Φ)
@inline v_quadratic_bottom_drag(i, j, grid, c, Φ, μ) = @inbounds - μ * Φ.v[i, j, 1] * spᶜᶠᶜ(i, j, 1, grid, Φ)

@inline is_immersed_drag_u(i, j, k, grid) = Int(immersed_peripheral_node(i, j, k-1, grid, Face(), Center(), Center()) & !inactive_node(i, j, k, grid, Face(), Center(), Center()))
@inline is_immersed_drag_v(i, j, k, grid) = Int(immersed_peripheral_node(i, j, k-1, grid, Center(), Face(), Center()) & !inactive_node(i, j, k, grid, Center(), Face(), Center()))

# Keep a constant linear drag parameter independent on vertical level
@inline u_immersed_bottom_drag(i, j, k, grid, clock, fields, μ) = @inbounds - μ * fields.u[i, j, k] * is_immersed_drag_u(i, j, k, grid) * spᶠᶜᶜ(i, j, k, grid, fields) / Δzᶠᶜᶜ(i, j, k, grid)
@inline v_immersed_bottom_drag(i, j, k, grid, clock, fields, μ) = @inbounds - μ * fields.v[i, j, k] * is_immersed_drag_v(i, j, k, grid) * spᶜᶠᶜ(i, j, k, grid, fields) / Δzᶜᶠᶜ(i, j, k, grid)
@inline u_immersed_bottom_drag(i, j, k, grid, clock, fields, μ) = @inbounds - μ * fields.u[i, j, k] * spᶠᶜᶜ(i, j, k, grid, fields)
@inline v_immersed_bottom_drag(i, j, k, grid, clock, fields, μ) = @inbounds - μ * fields.v[i, j, k] * spᶜᶠᶜ(i, j, k, grid, fields)

# TODO: Specify the grid to a grid on the sphere; otherwise we can provide a different
# function that requires latitude and longitude etc for computing coriolis=FPlane...
Expand Down Expand Up @@ -105,8 +102,18 @@ function ocean_simulation(grid;
# Don't let users use advection in a single column model
tracer_advection = nothing
momentum_advection = nothing

# No immersed boundaries in a single column grid
u_immersed_bc = nothing
v_immersed_bc = nothing
else
bottom_drag_coefficient = default_or_override(bottom_drag_coefficient)

u_immersed_drag = FluxBoundaryCondition(u_immersed_bottom_drag, discrete_form=true, parameters=bottom_drag_coefficient)
v_immersed_drag = FluxBoundaryCondition(v_immersed_bottom_drag, discrete_form=true, parameters=bottom_drag_coefficient)

u_immersed_bc = ImmersedBoundaryCondition(bottom = u_immersed_drag)
v_immersed_bc = ImmersedBoundaryCondition(bottom = v_immersed_drag)
end

bottom_drag_coefficient = convert(FT, bottom_drag_coefficient)
Expand All @@ -117,19 +124,23 @@ function ocean_simulation(grid;
top_ocean_heat_flux = Jᵀ = Field{Center, Center, Nothing}(grid)
top_salt_flux = Jˢ = Field{Center, Center, Nothing}(grid)

# Construct ocean boundary conditions including surface forcing and bottom drag
u_top_bc = FluxBoundaryCondition(τx)
v_top_bc = FluxBoundaryCondition(τy)
T_top_bc = FluxBoundaryCondition(Jᵀ)
S_top_bc = FluxBoundaryCondition(Jˢ)

u_bot_bc = FluxBoundaryCondition(u_quadratic_bottom_drag, discrete_form=true, parameters=bottom_drag_coefficient)
v_bot_bc = FluxBoundaryCondition(v_quadratic_bottom_drag, discrete_form=true, parameters=bottom_drag_coefficient)

ocean_boundary_conditions = (u = FieldBoundaryConditions(top = FluxBoundaryCondition(τx), bottom = u_bot_bc),
v = FieldBoundaryConditions(top = FluxBoundaryCondition(τy), bottom = v_bot_bc),
T = FieldBoundaryConditions(top = FluxBoundaryCondition(Jᵀ)),
S = FieldBoundaryConditions(top = FluxBoundaryCondition(Jˢ)))

if grid isa ImmersedBoundaryGrid
Fu = Forcing(u_immersed_bottom_drag, discrete_form=true, parameters=bottom_drag_coefficient)
Fv = Forcing(v_immersed_bottom_drag, discrete_form=true, parameters=bottom_drag_coefficient)
forcing = merge(forcing, (u=Fu, v=Fv))
end

ocean_boundary_conditions = (u = FieldBoundaryConditions(top = u_top_bc, bottom = u_bot_bc, immersed = u_immersed_bc),
v = FieldBoundaryConditions(top = v_top_bc, bottom = v_bot_bc, immersed = v_immersed_bc),
T = FieldBoundaryConditions(top = T_top_bc),
S = FieldBoundaryConditions(top = S_top_bc))

# Use the TEOS10 equation of state
teos10 = TEOS10EquationOfState(; reference_density)
buoyancy = SeawaterBuoyancy(; gravitational_acceleration, equation_of_state=teos10)

buoyancy = SeawaterBuoyancy(; gravitational_acceleration, equation_of_state)

Expand Down
Loading