Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
235ecb4
initial container instructions
May 13, 2026
981be36
initial docker container
May 13, 2026
c7aa767
fix julia setup and hide suggestions
May 14, 2026
ec70db5
fancy vs code settings
May 14, 2026
ecc8b22
docs and initial test script
May 14, 2026
b16f629
fix UID
May 14, 2026
78d17ba
add gitignore
May 14, 2026
fba27ab
rename folder
May 14, 2026
f59dc7e
naming
May 14, 2026
f720e45
update to ignore model outputs and plots
May 16, 2026
593b8c2
add oceanBiome and Oceananigans.jl to deps
May 16, 2026
c101294
toml for docs
May 16, 2026
59949d4
install julia packages from project.toml
May 16, 2026
bed1c61
initial diffusivity example
May 16, 2026
c852c71
more realistic diffusivity
May 16, 2026
7d0d70a
initial sensitivity example
May 16, 2026
ab0c3e4
some diagnostic plots
May 17, 2026
5c63748
Merge remote-tracking branch 'origin/main' into exercises
May 18, 2026
1e8a86f
updates from main
May 18, 2026
39bbe0b
match main
May 18, 2026
31e7505
initial schedule
May 18, 2026
371a3a7
wording
May 18, 2026
1a37a86
bump agate version
May 18, 2026
0b2db3a
initial allometric scaling example
May 18, 2026
e679617
change schedule
May 19, 2026
e5ad016
brain storming
May 19, 2026
63a58d9
initial allometric examples
May 19, 2026
6cf5a21
re-org
May 19, 2026
29b948b
start adding slides
May 19, 2026
c06dd35
add background to title slide
May 19, 2026
a12ee16
text and image alignment
May 19, 2026
d07930b
rename
May 19, 2026
48fd771
update to ignore quarto outputs
May 19, 2026
4542418
place holders
May 19, 2026
03784c7
some workshop tools
May 19, 2026
f415af9
old page
May 19, 2026
c7cd9a1
WIP
May 19, 2026
8c845fa
placeholders plus re=org
May 19, 2026
112dcce
set julia version to 1.10
May 19, 2026
b7df290
WIP
May 19, 2026
5df6017
add captions and improve formatting
May 20, 2026
1872f5f
initial WIP slides
May 20, 2026
651fea8
background image
May 20, 2026
af1871b
embed quarto slides into docs
May 20, 2026
4cf8fb9
add welcome slide
May 20, 2026
839047f
update gitignore for quarto
May 20, 2026
ec1815a
fix paths
May 20, 2026
4973c5a
figure for welcome slide
May 20, 2026
35940ec
add slides to index
May 20, 2026
357bf74
update to ignore .quarto
May 20, 2026
8d2daf3
wip figures
May 20, 2026
b5f69fd
wip text
May 20, 2026
1c7475c
papers
May 20, 2026
d05771e
update diags
May 20, 2026
665f545
ignore generated slides
May 20, 2026
5313406
re-org figures
May 20, 2026
5f23bff
include figs in doc page
May 20, 2026
8eb9f16
fix links
May 20, 2026
a577d03
update
May 20, 2026
6ad6300
wip slides
May 20, 2026
e0f7900
outline ideas
May 20, 2026
f1ddbed
add living vs dead contributions + pretty cbars
May 20, 2026
f79b078
fix diffusivity warning (?)
May 20, 2026
9e38210
include figure save
May 21, 2026
8c763e0
only export tracers (fix turbulence bug?)
May 21, 2026
f651ee9
.tracers not supported for boxmodel
May 21, 2026
dc1aca6
maybe?
May 21, 2026
0cfaf49
strip warnings from docs
May 21, 2026
c1fdfe6
re-org examples
May 21, 2026
05b1ee2
WIP slide images
May 21, 2026
77af661
start embedding images
May 21, 2026
714c2f3
add cbars
May 21, 2026
fd7866b
add column plots for irradiance
May 21, 2026
3fc29e9
update make for new format
May 21, 2026
806e182
fix typo
May 21, 2026
08defe2
re-org
May 21, 2026
fc16068
WIP slides
May 21, 2026
cf2a667
improve cbars
May 21, 2026
c658f46
update tools
May 21, 2026
ea27191
tidy helpers
May 21, 2026
2c361a1
new diagnostics
May 21, 2026
7ab1c36
pretty stacked plot
May 21, 2026
ec5110a
one generic time series plotting function
May 21, 2026
90c895e
generic cwm size plot that also works for comparison
May 21, 2026
282ffff
shared legends
May 21, 2026
fc1b6b9
simplify contribution plots
May 21, 2026
611127a
pretty colors
May 21, 2026
0bc565c
fix page titles
May 21, 2026
e311dd2
add palatability example
May 21, 2026
1ca45ee
add default initial conditions function
May 21, 2026
7b81b27
update examples
May 21, 2026
2e5eb9c
nicer colors
May 21, 2026
764f61e
add closure term example
May 21, 2026
a74aabb
use helper plots
May 21, 2026
86b7222
initial docker build with AgateWorkshop package
May 21, 2026
6394cff
fix compat
May 22, 2026
71fcb43
manually define sizes
May 22, 2026
b6b4fb1
add missing import
May 22, 2026
ff7102f
final fig
May 22, 2026
1f4d901
polished intro slides
May 22, 2026
54f5838
tidy
May 22, 2026
8b629bb
remove broken slides
May 22, 2026
9a6d615
tidy examples
May 22, 2026
40c12c7
fix irradiance box example
May 22, 2026
8208313
fix ugly outputs
May 22, 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
9 changes: 8 additions & 1 deletion .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ jobs:
- name: Set up Julia
uses: julia-actions/setup-julia@v2
with:
version: '1'
version: '1.10'

- name: Set up Quarto
uses: quarto-dev/quarto-actions/setup@v2

- name: Cache Julia artifacts
uses: julia-actions/cache@v2
Expand All @@ -37,8 +40,12 @@ jobs:
shell: julia --color=yes --project=2026-05/docs {0}
run: |
using Pkg
Pkg.develop(Pkg.PackageSpec(path="2026-05"))
Pkg.instantiate()

- name: Render Quarto slides
run: quarto render 2026-05/slides

- name: Build and deploy documentation
run: julia --color=yes --project=2026-05/docs 2026-05/docs/make.jl
env:
Expand Down
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@
2026-05/docs/build/
2026-05/docs/Manifest.toml
2026-05/docs/src/generated
2026-05/docs/src/slides

# Quarto slides
2026-05/slides/01_intro_files/
2026-05/slides/_site/
2026-05/slides/.quarto

*.html

# Model output
*.jld
*.jld2

# Plots
*.png
*.svg
*.gif
*.mp4
18 changes: 18 additions & 0 deletions 2026-05/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name = "AgateWorkshop"
uuid = "63d0df89-dfc1-4d5a-a86d-28cde1db1234"
version = "0.1.0"

[deps]
Agate = "41889b7c-c35c-4f16-abd7-94b299d9fd29"
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
OceanBioME = "a49af516-9db8-4be4-be45-1dad61c5a376"
Oceananigans = "9e8cae18-63c1-5223-a75c-80ca9d6e9a09"

[compat]
Agate = "0.5.3"
CairoMakie = "0.12, 0.13, 0.14, 0.15"
JLD2 = "0.5"
OceanBioME = "0.16"
Oceananigans = "0.101.1, 0.102"
julia = "1.10"
158 changes: 158 additions & 0 deletions 2026-05/advances_excercises/06_diffusivity_sensitivity.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# # [Exercise 06 supplement: Diffusivity sensitivity] (@id diffusivity_sensitivity_exercise)

# !!! info
# This example uses [Oceananigans.jl](https://clima.github.io/OceananigansDocumentation/stable/) and [OceanBioME.jl](https://oceanbiome.github.io/OceanBioME.jl/stable/).
# We recommend familiarizing yourself with their user interface if you intend to make changes to the physical model setup.

# In this example we run a default Agate.jl-NiPiZD model inside a 2 layer column model.
# The simulation is repeated across a range of vertical diffusivities, then the top-layer
# total plankton biomass is summarized as a function of κ_max.

# ## Loading dependencies

using Agate
using Agate.Library.Light
using OceanBioME
using OceanBioME: Biogeochemistry
using Oceananigans
using Oceananigans.Units
using CairoMakie
using Statistics

stop_time = 3*365day
Δt = 1hour
output_interval = 1day
LAYER_INTERFACE = -100meters
PAR_SURFACE_MAX = 80

κ_values = [
3e-5,
1e-4,
3e-4,
1e-3,
]

# ## Forcings

struct LayeredDiffusivity{T}
κ_max::T
layer_interface::T
end

@inline function (diffusivity::LayeredDiffusivity)(x, y, z, t)
return ifelse(z >= diffusivity.layer_interface, diffusivity.κ_max, zero(diffusivity.κ_max))
end

function surface_irradiance(x, y, z, t)
return ifelse(z >= LAYER_INTERFACE, PAR_SURFACE_MAX, 0.0)
end

# ## Physical and ecosystem model

function build_model(κ_max)
grid = RectilinearGrid(; size=(1, 1, 2), extent=(20meters, 20meters, 200meters))
diffusivity = LayeredDiffusivity(κ_max, LAYER_INTERFACE)

bgc = Agate.Models.NiPiZD.construct()
bgc_model = Biogeochemistry(
bgc; light_attenuation=FunctionFieldPAR(; grid, PAR_f=surface_irradiance)
)

model = NonhydrostaticModel(;
grid,
clock=Clock(; time=0.0),
timestepper=:QuasiAdamsBashforth2,
closure=ScalarDiffusivity(
VerticallyImplicitTimeDiscretization(); ν=diffusivity, κ=diffusivity
),
biogeochemistry=bgc_model,
)

set!(model; N=7.0, P1=0.01, P2=0.01, Z1=0.05, Z2=0.05, D=0.0)

return model
end

function output_filename(κ_max)
κ_label = replace(string(κ_max), "." => "p", "-" => "m")
return "N2P2ZD_column_k$(κ_label).jld2"
end

function run_column(κ_max)
model = build_model(κ_max)
filename = output_filename(κ_max)

simulation = Simulation(model; Δt, stop_time)

simulation.output_writers[:profiles] = JLD2Writer(
model,
model.tracers;
filename,
schedule=TimeInterval(output_interval),
overwrite_existing=true,
)

run!(simulation)

return filename, model
end

function top_layer_index(timeseries_field)
_, _, z_nodes = nodes(timeseries_field)
z_vals = collect(z_nodes)
return argmax(z_vals)
end

function top_layer_plankton_biomass(filename)
P1 = FieldTimeSeries(filename, "P1")
P2 = FieldTimeSeries(filename, "P2")
Z1 = FieldTimeSeries(filename, "Z1")
Z2 = FieldTimeSeries(filename, "Z2")

k_top = top_layer_index(P1)
times = collect(P1.times ./ days)

biomass = vec(
interior(P1, 1, 1, k_top, :) .+
interior(P2, 1, 1, k_top, :) .+
interior(Z1, 1, 1, k_top, :) .+
interior(Z2, 1, 1, k_top, :)
)

final_biomass = biomass[end]
final_year = times .>= maximum(times) - 365
mean_final_year_biomass = mean(biomass[final_year])

return final_biomass, mean_final_year_biomass
end

# ## Sensitivity runs

final_top_layer_biomass = Float64[]
mean_final_year_top_layer_biomass = Float64[]

for κ_max in κ_values
@info "Running diffusivity sensitivity" κ_max
filename, _ = run_column(κ_max)
final_biomass, mean_final_year_biomass = top_layer_plankton_biomass(filename)
push!(final_top_layer_biomass, final_biomass)
push!(mean_final_year_top_layer_biomass, mean_final_year_biomass)
end

# ## Plot top-layer total plankton biomass against κ_max

fig = Figure(; size=(850, 550), fontsize=16)
ax = Axis(
fig[1, 1];
xlabel="κ_max (m² s⁻¹)",
ylabel="Top-layer total plankton biomass (mmol N m⁻³)",
title="Diffusivity sensitivity",
xscale=log10,
)

scatterlines!(ax, κ_values, mean_final_year_top_layer_biomass; label="Final-year mean")
axislegend(ax; position=:rb)

save("top_layer_plankton_biomass_vs_kmax.png", fig)

fig
4 changes: 3 additions & 1 deletion 2026-05/docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[deps]
AgateWorkshop = "63d0df89-dfc1-4d5a-a86d-28cde1db1234"
Agate = "41889b7c-c35c-4f16-abd7-94b299d9fd29"
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
Expand All @@ -10,7 +11,8 @@ OceanBioME = "a49af516-9db8-4be4-be45-1dad61c5a376"
Oceananigans = "9e8cae18-63c1-5223-a75c-80ca9d6e9a09"

[compat]
Agate = "0.5.2"
AgateWorkshop = "0.1"
Agate = "0.5.3"
CairoMakie = "0.12, 0.13, 0.14, 0.15"
CSV = "0.10"
DataFrames = "1"
Expand Down
93 changes: 75 additions & 18 deletions 2026-05/docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,21 +1,43 @@
using Pkg

Pkg.activate(@__DIR__)

workshop_root = normpath(joinpath(@__DIR__, ".."))
Pkg.develop(Pkg.PackageSpec(path=workshop_root))
Pkg.instantiate()

using Literate
using Documenter

workshop_root = normpath(joinpath(@__DIR__, ".."))

examples_dir = joinpath(workshop_root, "examples")
docs_src_dir = joinpath(@__DIR__, "src")
docs_exercises_dir = joinpath(docs_src_dir, "exercises")
docs_exercises_dir = joinpath(docs_src_dir, "generated")
mkpath(docs_exercises_dir)


example_files = [
"00_setup_check.jl",
"01_quick_start.jl",
"02_diagnostics.jl",
"03_allometric_scaling.jl",
"04_number_size_classes.jl",
"05_size_range.jl",
"06_palatability.jl",
"07_assimilation_efficiency.jl",
"08_closure_terms.jl",
"09_irradiance_box.jl",
"10_irradiance_column.jl",
"11_diffusivity_stratified.jl",
"12_diffusivity_seasonal.jl",
]

function strip_jld2_warnings(content)
return replace(
content,
r"(?ms)^┌ Warning:.*?^└ @ JLD2 .*?/(writing_datatypes|reconstructing_datatypes)\.jl:\d+\n" => "",
)
end

# Run from the workshop root so relative paths in examples are predictable.
cd(workshop_root) do
mkpath("figures")
Expand All @@ -25,37 +47,72 @@ cd(workshop_root) do
source = joinpath(examples_dir, file)

# Generate and execute the rendered documentation page.
#
# Executing here lets CI pre-run the examples and include generated figures
# Executing here lets CI pre-run examples and include generated figures
# in the deployed documentation.
Literate.markdown(
source,
docs_exercises_dir;
documenter = true,
execute = true,
credit = false,
documenter=true,
execute=true,
credit=false,
postprocess=strip_jld2_warnings,
)
end
end

# Quarto renders slide decks into slides/_site before this script runs in CI.
# Copy that site into docs/src before makedocs so Documenter can validate
# local links to the rendered slide HTML.
slides_site_src = joinpath(workshop_root, "slides", "_site")
slides_docs_src = joinpath(docs_src_dir, "slides")

if isdir(slides_site_src)
isdir(slides_docs_src) && rm(slides_docs_src; recursive=true, force=true)
cp(slides_site_src, slides_docs_src)
else
@warn "Quarto slide output not found before makedocs; slide links may be unavailable" slides_site_src
end

makedocs(
sitename = "Agate.jl workshop 2026-05",
format = Documenter.HTML(
prettyurls = get(ENV, "CI", "false") == "true",
assets = String[],
sitename="Agate.jl workshop 2026-05",
format=Documenter.HTML(
prettyurls=get(ENV, "CI", "false") == "true",
assets=String[],
size_threshold_warn=1_000_000,
size_threshold=2_000_000,
),
modules = Module[],
pages = [
modules=Module[],
pages=[
"Home" => "index.md",
"Setup" => "setup.md",
"Workshop slides" => "lectures.md",
"Examples" => [
"00 Setup Check" => "exercises/00_setup_check.md",
"00 Setup Check" => "generated/00_setup_check.md",
"01 Quick Start" => "generated/01_quick_start.md",
"02 Diagnostics" => "generated/02_diagnostics.md",
"03 Allometric Scaling" => "generated/03_allometric_scaling.md",
"04 Number of Size Classes" => "generated/04_number_size_classes.md",
"05 Size Range" => "generated/05_size_range.md",
"06 Palatability" => "generated/06_palatability.md",
"07 Assimilation Efficiency" => "generated/07_assimilation_efficiency.md",
"08 Closure Terms" => "generated/08_closure_terms.md",
"09 Irradiance Box" => "generated/09_irradiance_box.md",
"10 Irradiance Column" => "generated/10_irradiance_column.md",
"11 Diffusivity Stratified" => "generated/11_diffusivity_stratified.md",
"12 Diffusivity Seasonal" => "generated/12_diffusivity_seasonal.md",
],
],
)
# Ensure rendered slides are present in the final build output even if a
# future Documenter version changes how non-markdown files are copied.
slides_build_dst = joinpath(@__DIR__, "build", "slides")
if isdir(slides_site_src)
isdir(slides_build_dst) && rm(slides_build_dst; recursive=true, force=true)
cp(slides_site_src, slides_build_dst)
end

deploydocs(
repo = "github.com/agate-model/workshops.git",
devbranch = "main",
push_preview = true,
repo="github.com/agate-model/workshops.git",
devbranch="main",
push_preview=true,
)
8 changes: 8 additions & 0 deletions 2026-05/docs/src/advanced.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
| Example | Theory |
|---|---|
| sinking + remin | POC attenuation |
| exponential mortality | viral shunt |
| kN vs umax | r/K trade-off |
| specificity vs gmax | generalist-specialist trade-off |
| alpha vs umax | LL vs HL prochlorococcus |
| interaction roles | cannibalism |
Loading
Loading