Nutrients, plankton, bacteria, detritus

This example illustrates how to use ClimaOceanBiogeochemistry's NutrientsPlanktonBacteriaDetrius model in a single column context.

using ClimaOceanBiogeochemistry: NutrientsPlanktonBacteriaDetritus

using Oceananigans
using Oceananigans.Units

using Printf
using CairoMakie

A single column grid

We set up a single column grid whose depth is H and with Nz points

H = 1000meters
z = (-H, 0)
Nz = 100

grid = RectilinearGrid(size = Nz; z, topology = (Flat, Flat, Bounded))
1×1×100 RectilinearGrid{Float64, Oceananigans.Grids.Flat, Oceananigans.Grids.Flat, Oceananigans.Grids.Bounded} on Oceananigans.Architectures.CPU with 0×0×3 halo
├── Flat x
├── Flat y
└── Bounded  z ∈ [-1000.0, 0.0]   regularly spaced with Δz=10.0

A prescribed vertical tracer diffusivity

We define a tracer diffusivity that mixes a lot near the surface (in the top 50 m), and less down below.

@inline κ(z, t) = 1e-4 + 1e-2 * exp(z / 25) + 1e-2 * exp(-(z + 1000) / 50)
vertical_diffusion = VerticalScalarDiffusivity(; κ)
VerticalScalarDiffusivity{ExplicitTimeDiscretization}(ν=0.0, κ=κ (generic function with 1 method))

We put the pieces together. The important line here is biogeochemistry = NutrientsPlanktonBacteriaDetritus(grid). We use all default parameters.

model = HydrostaticFreeSurfaceModel(; grid,
                                    velocities = PrescribedVelocityFields(),
                                    biogeochemistry = NutrientsPlanktonBacteriaDetritus(grid),
                                    tracers = (:N, :P, :Z, :B, :D),
                                    tracer_advection = WENO(),
                                    buoyancy = nothing,
                                    closure = vertical_diffusion)
HydrostaticFreeSurfaceModel{CPU, RectilinearGrid}(time = 0 seconds, iteration = 0)
├── grid: 1×1×100 RectilinearGrid{Float64, Oceananigans.Grids.Flat, Oceananigans.Grids.Flat, Oceananigans.Grids.Bounded} on Oceananigans.Architectures.CPU with 0×0×3 halo
├── timestepper: QuasiAdamsBashforth2TimeStepper
├── tracers: (N, P, Z, B, D)
├── closure: VerticalScalarDiffusivity{ExplicitTimeDiscretization}(ν=0.0, κ=(N=κ (generic function with 1 method), P=κ (generic function with 1 method), Z=κ (generic function with 1 method), B=κ (generic function with 1 method), D=κ (generic function with 1 method)))
├── buoyancy: Nothing
├── advection scheme: 
│   ├── momentum: Centered reconstruction order 2
│   ├── N: WENO reconstruction order 5
│   ├── P: WENO reconstruction order 5
│   ├── Z: WENO reconstruction order 5
│   ├── B: WENO reconstruction order 5
│   └── D: WENO reconstruction order 5
└── coriolis: Nothing

Initial conditions

We initialize the model with reasonable nutrients, detritus, and a nutrient mixed layer.

set!(model, N=3, P=1e-1, Z=1e-1, B=1e-1, D=1e-1)

simulation = Simulation(model, Δt=30minutes, stop_time=10days)

function progress(sim)
    @printf("Iteration: %d, time: %s, total(N): %.2e \n",
            iteration(sim), prettytime(sim),
            sum(model.tracers.N) + sum(model.tracers.P) + sum(model.tracers.B) + sum(model.tracers.D))
    return nothing
end

simulation.callbacks[:progress] = Callback(progress, IterationInterval(10))
Callback of progress on IterationInterval(10)

Let's see the initial condition

N = model.tracers.N
P = model.tracers.P
Z = model.tracers.Z
B = model.tracers.B
D = model.tracers.D

z = znodes(N)

fig = Figure(size=(1200, 600))

axN  = Axis(fig[1, 1], xlabel="Nutrient concentration (N)", ylabel="z (m)")
axP  = Axis(fig[1, 2], xlabel="Phytoplankton concentration (P)")
axZ  = Axis(fig[1, 3], xlabel="Zooplankton concentration (Z)")
axB  = Axis(fig[1, 4], xlabel="Bacteria concentration (B)")
axD = Axis(fig[1, 5], xlabel="Detritus concentration (D)")

lines!(axN, interior(N, 1, 1, :), z)
lines!(axP, interior(P, 1, 1, :), z)
lines!(axZ, interior(Z, 1, 1, :), z)
lines!(axB, interior(B, 1, 1, :), z)
lines!(axD, interior(D, 1, 1, :), z)

current_figure()
Example block output

Now we add an output writer to the simulation and run the simulation.

filename = "nutrients_plankton_bacteria_detritus.jld2"

simulation.output_writers[:fields] = JLD2OutputWriter(model, model.tracers;
                                                      filename,
                                                      schedule = TimeInterval(1day),
                                                      overwrite_existing = true)

run!(simulation)
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
[ Info: Initializing simulation...
Iteration: 0, time: 0 seconds, total(N): 3.30e+02
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
[ Info:     ... simulation initialization complete (1.376 seconds)
[ Info: Executing initial time step...
[ Info:     ... initial time step complete (2.414 seconds).
Iteration: 10, time: 5 hours, total(N): 3.30e+02
Iteration: 20, time: 10 hours, total(N): 3.30e+02
Iteration: 30, time: 15 hours, total(N): 3.30e+02
Iteration: 40, time: 20 hours, total(N): 3.30e+02
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
Iteration: 50, time: 1.042 days, total(N): 3.31e+02
Iteration: 60, time: 1.250 days, total(N): 3.31e+02
Iteration: 70, time: 1.458 days, total(N): 3.31e+02
Iteration: 80, time: 1.667 days, total(N): 3.31e+02
Iteration: 90, time: 1.875 days, total(N): 3.31e+02
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
Iteration: 100, time: 2.083 days, total(N): 3.31e+02
Iteration: 110, time: 2.292 days, total(N): 3.31e+02
Iteration: 120, time: 2.500 days, total(N): 3.31e+02
Iteration: 130, time: 2.708 days, total(N): 3.31e+02
Iteration: 140, time: 2.917 days, total(N): 3.31e+02
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
Iteration: 150, time: 3.125 days, total(N): 3.31e+02
Iteration: 160, time: 3.333 days, total(N): 3.31e+02
Iteration: 170, time: 3.542 days, total(N): 3.31e+02
Iteration: 180, time: 3.750 days, total(N): 3.31e+02
Iteration: 190, time: 3.958 days, total(N): 3.31e+02
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
Iteration: 200, time: 4.167 days, total(N): 3.31e+02
Iteration: 210, time: 4.375 days, total(N): 3.31e+02
Iteration: 220, time: 4.583 days, total(N): 3.31e+02
Iteration: 230, time: 4.792 days, total(N): 3.31e+02
Iteration: 240, time: 5 days, total(N): 3.31e+02
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
Iteration: 250, time: 5.208 days, total(N): 3.31e+02
Iteration: 260, time: 5.417 days, total(N): 3.31e+02
Iteration: 270, time: 5.625 days, total(N): 3.31e+02
Iteration: 280, time: 5.833 days, total(N): 3.31e+02
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
Iteration: 290, time: 6.042 days, total(N): 3.31e+02
Iteration: 300, time: 6.250 days, total(N): 3.31e+02
Iteration: 310, time: 6.458 days, total(N): 3.31e+02
Iteration: 320, time: 6.667 days, total(N): 3.31e+02
Iteration: 330, time: 6.875 days, total(N): 3.31e+02
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
Iteration: 340, time: 7.083 days, total(N): 3.31e+02
Iteration: 350, time: 7.292 days, total(N): 3.31e+02
Iteration: 360, time: 7.500 days, total(N): 3.31e+02
Iteration: 370, time: 7.708 days, total(N): 3.31e+02
Iteration: 380, time: 7.917 days, total(N): 3.31e+02
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
Iteration: 390, time: 8.125 days, total(N): 3.31e+02
Iteration: 400, time: 8.333 days, total(N): 3.31e+02
Iteration: 410, time: 8.542 days, total(N): 3.31e+02
Iteration: 420, time: 8.750 days, total(N): 3.31e+02
Iteration: 430, time: 8.958 days, total(N): 3.31e+02
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618
Iteration: 440, time: 9.167 days, total(N): 3.31e+02
Iteration: 450, time: 9.375 days, total(N): 3.31e+02
Iteration: 460, time: 9.583 days, total(N): 3.31e+02
Iteration: 470, time: 9.792 days, total(N): 3.31e+02
[ Info: Simulation is stopping after running for 4.143 seconds.
[ Info: Simulation time 10 days equals or exceeds stop time 10 days.
Iteration: 480, time: 10 days, total(N): 3.31e+02
┌ Warning: some parameters could not be resolved for type Oceananigans.TurbulenceClosures.ScalarDiffusivity{Oceananigans.TurbulenceClosures.ExplicitTimeDiscretization,Oceananigans.TurbulenceClosures.VerticalFormulation,Float64,NamedTuple{(:N, :P, :Z, :B, :D),Tuple{Main.#κ,Main.#κ,Main.#κ,Main.#κ,Main.#κ}},Float64}; reconstructing
@ JLD2 ~/.julia/packages/JLD2/OP0XX/src/data/reconstructing_datatypes.jl:618

Visualization

All that's left is to visualize the results.

Pt = FieldTimeSeries(filename, "P")
Zt = FieldTimeSeries(filename, "Z")
Bt = FieldTimeSeries(filename, "B")
Dt = FieldTimeSeries(filename, "D")
Nt = FieldTimeSeries(filename, "N")

t = Pt.times
nt = length(t)
z = znodes(Pt)

fig = Figure(size=(1200, 600))

axN  = Axis(fig[1, 1], xlabel="[Nutrient] (mmol m⁻³)", ylabel="z (m)")
axP  = Axis(fig[1, 2], xlabel="[Phytoplankton] (mmol m⁻³)")
axZ  = Axis(fig[1, 3], xlabel="[Zooplankton] (mmol m⁻³)")
axB  = Axis(fig[1, 4], xlabel="[Bacteria] (mmol m⁻³)")
axD = Axis(fig[1, 5], xlabel="[Detritus] (mmol m⁻³)")

slider = Slider(fig[2, 1:5], range=1:nt, startvalue=1)
n = slider.value

title = @lift @sprintf("Equilibrium biogeochemistry at t = %d days", t[$n] / day)
Label(fig[0, 1:5], title)

Nn  = @lift interior(Nt[$n], 1, 1, :)
Pn  = @lift interior(Pt[$n], 1, 1, :)
Zn  = @lift interior(Zt[$n], 1, 1, :)
Bn  = @lift interior(Bt[$n], 1, 1, :)
Dn = @lift interior(Dt[$n], 1, 1, :)

lines!(axP, Pn, z)
lines!(axZ, Zn, z)
lines!(axD, Dn, z)
lines!(axB, Bn, z)
lines!(axN, Nn, z)

record(fig, "nutrients_plankton_bacteria_detritus.mp4", 1:nt, framerate=24) do nn
    n[] = nn
end

Let's plot a snapshot of the last frame.

Nn_last  = interior(Nt[end], 1, 1, :)
Pn_last  = interior(Pt[end], 1, 1, :)
Zn_last  = interior(Zt[end], 1, 1, :)
Bn_last  = interior(Bt[end], 1, 1, :)
Dn_last = interior(Dt[end], 1, 1, :)

last_frame = Figure(size=(1200, 600))
axN  = Axis(last_frame[1, 1], xlabel="[N] (mmol m⁻³)", ylabel="z (m)")
axP  = Axis(last_frame[1, 2], xlabel="[P] (mmol m⁻³)")
axZ  = Axis(last_frame[1, 3], xlabel="[Z] (mmol m⁻³)")
axB  = Axis(last_frame[1, 4], xlabel="[B] (mmol m⁻³)")
axD = Axis(last_frame[1, 5], xlabel="[D] (mmol m⁻³)")

lines!(axP, Pn_last, z)
lines!(axZ, Zn_last, z)
lines!(axD, Dn_last, z)
lines!(axB, Bn_last, z)
lines!(axN, Nn_last, z)

save("NPZDB.png", last_frame)

Another figure: we plot the sum of each variable against time.

N_time  = zeros(1:nt)
P_time  = zeros(1:nt)
Z_time  = zeros(1:nt)
B_time  = zeros(1:nt)
D_time = zeros(1:nt)

for times = 1:nt
    N_time[times] = sum(Nt[:, :, :, times])
    P_time[times] = sum(Pt[:, :, :, times])
    Z_time[times] = sum(Zt[:, :, :, times])
    B_time[times] = sum(Bt[:, :, :, times])
    D_time[times] = sum(Dt[:, :, :, times])
end

TimeVar = Figure()
ax2 = Axis(TimeVar[1, 1], title="Nutrients evolution", ylabel="Variable (mmol m⁻³)", xlabel="Time (days)")
lines!(ax2, 1:nt, N_time, label="N")
lines!(ax2, 1:nt, P_time, label="P")
lines!(ax2, 1:nt, Z_time, label="Z")
lines!(ax2, 1:nt, B_time, label="B")
lines!(ax2, 1:nt, D_time, label="dD")

axislegend()

save("TimeVariations.png", TimeVar)


This page was generated using Literate.jl.