Predator-prey palatability
This example compares two ways of configuring predator-prey palatability in the Agate.jl-Onyx model. By default, the model computes the palatability matrix from allometric traits based on the preferred predator:prey size ratio (optimum_predator_prey_ratio, abbreviated here as Vopt). We will change this default value to use a different optimum. Additionally, we will show how the allometric calculation can be bypassed by supplying an explicit custom palatability_matrix.
After inspecting the resulting interaction matrices, each configuration is run in a 0D box-model setup so their tracer trajectories can be compared.
Loading dependencies
The example uses Agate.jl, Oceananigans.jl, and OceanBioME.jl for the ecosystem simulation. CairoMakie.jl is used for plotting.
using Agate
using Onyx
using Agate.Introspection: interaction_matrix, tracer_names
using Agate.Library.Light
using OceanBioME
using OceanBioME: Biogeochemistry
using Oceananigans
using Oceananigans.Fields: FunctionField
using Oceananigans.Units
using CairoMakie
Construct the ecosystem configurations
default_bgc = Onyx.construct()
default_pal = interaction_matrix(default_bgc, :palatability_matrix)The default configuration uses the model's allometric palatability calculation. By default, predators prefer prey that are 10 times smaller than themselves (Vopt = 10). Palatability contributes to the consumer's total effective food concentration and to allocation of its shared ingestion capacity among prey. Onyx uses proportional allocation (switching_exponent = 1) by default.
Instead, we will change that optimum to Vopt = 5 for the two zooplankton consumers. Vector parameters can be overridden by plankton SizeClass name, so only the relevant entries need to be supplied. Omitted entries are filled from the model defaults.
vopt_bgc = Onyx.construct(;
parameters=(; optimum_predator_prey_ratio=(Z_1=5.0, Z_2=5.0))
)
vopt_pal = interaction_matrix(vopt_bgc, :palatability_matrix)Another option is to provide the palatability matrix directly. This is useful for experiments where the intended interaction structure is known explicitly, rather than derived from size traits.
custom_bgc = Onyx.construct(;
parameters=(palatability_matrix=[0.0 1.0; 1.0 0.0],)
)
custom_pal = interaction_matrix(custom_bgc, :palatability_matrix)
Helper for labelled matrix plots
function plot_matrix!(fig, position, table; title)
mat = Matrix(table.matrix)
nrows, ncols = size(mat)
ax = Axis(
fig[position...];
title,
xlabel=string(table.column_axis),
ylabel=string(table.row_axis),
xticks=(1:ncols, string.(table.columns)),
yticks=(1:nrows, string.(table.rows)),
yreversed=true,
aspect=DataAspect(),
)
hm = heatmap!(ax, 1:ncols, 1:nrows, mat'; colorrange=(0, 1))
for row in 1:nrows, col in 1:ncols
text!(
ax,
col,
row;
text=string(round(mat[row, col]; digits=2)),
align=(:center, :center),
fontsize=12,
)
end
return hm
end
Compare the palatability matrices
matrix_fig = Figure(; size=(1100, 380), fontsize=16)
hm_pal_default = plot_matrix!(matrix_fig, (1, 1), default_pal; title="Vopt = 10 (default)")
plot_matrix!(matrix_fig, (1, 2), vopt_pal; title="Vopt = 5")
plot_matrix!(matrix_fig, (1, 3), custom_pal; title="Custom")
Colorbar(matrix_fig[1, 4], hm_pal_default; label="palatability")
Label(matrix_fig[0, 1:4], "Predator-prey palatability"; fontsize=22)
save("predator_prey_palatability_matrices.png", matrix_fig; px_per_unit=1)
matrix_figRun each configuration in a box model
function run_box_model(bgc, filename)
grid = BoxModelGrid()
clock = Clock(; time=zero(grid))
PAR = FunctionField{Center,Center,Center}(CyclicalPAR(-10), grid; clock)
light_attenuation = PrescribedPhotosyntheticallyActiveRadiation(PAR)
bgc_model = Biogeochemistry(bgc; light_attenuation)
full_model = BoxModel(; grid, clock, biogeochemistry=bgc_model)
set!(full_model; N=7.0, P_1=0.01, Z_1=0.01, P_2=0.1, Z_2=0.01, D=0.01)
simulation = Simulation(full_model; Δt=240minutes, stop_time=1095days)
simulation.output_writers[:fields] = JLD2Writer(
full_model,
full_model.fields;
filename,
schedule=TimeInterval(1day),
overwrite_files=true,
)
run!(simulation)
return filename
end
default_filename = run_box_model(default_bgc, "predator_prey_palatability_default.jld2")
vopt_filename = run_box_model(vopt_bgc, "predator_prey_palatability_vopt5.jld2")
custom_filename = run_box_model(custom_bgc, "predator_prey_palatability_custom.jld2")
[ Info: Initializing simulation...
[ Info: ... simulation initialization complete (1.700 seconds)
[ Info: Executing initial time step...
[ Info: ... initial time step complete (581.252 ms).
[ Info: Simulation is stopping after running for 5.849 seconds.
[ Info: Simulation time 1095 days equals or exceeds stop time 1095 days.
[ Info: Initializing simulation...
[ Info: ... simulation initialization complete (9.872 ms)
[ Info: Executing initial time step...
[ Info: ... initial time step complete (120.735 μs).
[ Info: Simulation is stopping after running for 3.575 seconds.
[ Info: Simulation time 1095 days equals or exceeds stop time 1095 days.
[ Info: Initializing simulation...
[ Info: ... simulation initialization complete (9.401 ms)
[ Info: Executing initial time step...
[ Info: ... initial time step complete (120.074 μs).
[ Info: Simulation is stopping after running for 3.578 seconds.
[ Info: Simulation time 1095 days equals or exceeds stop time 1095 days.
Load the output time series
tracer_syms = tracer_names(default_bgc)
default_timeseries = (;
(s => FieldTimeSeries(default_filename, string(s))[1, 1, 1, :] for s in tracer_syms)...
)
vopt_timeseries = (;
(s => FieldTimeSeries(vopt_filename, string(s))[1, 1, 1, :] for s in tracer_syms)...
)
custom_timeseries = (;
(s => FieldTimeSeries(custom_filename, string(s))[1, 1, 1, :] for s in tracer_syms)...
)
times = FieldTimeSeries(default_filename, string(first(tracer_syms))).times
Compare the box-model output
simulation_fig = Figure(; size=(900, 900), fontsize=16)
Label(simulation_fig[0, 1:2], "Box-model response"; fontsize=22)
for (idx, sym) in enumerate(tracer_syms)
row = 1 + div(idx - 1, 2)
col = 1 + mod(idx - 1, 2)
ax = Axis(
simulation_fig[row, col];
ylabel=string(sym),
xlabel="Days",
title="$(sym) concentration (mmol N / m³)",
)
lines!(
ax,
times / day,
getproperty(default_timeseries, sym);
label="Vopt = 10",
linewidth=2,
)
lines!(
ax,
times / day,
getproperty(vopt_timeseries, sym);
label="Vopt = 5",
linewidth=2,
)
lines!(
ax,
times / day,
getproperty(custom_timeseries, sym);
label="custom",
linewidth=2,
)
axislegend(ax; position=:rt)
end
save("predator_prey_palatability_simulation.png", simulation_fig; px_per_unit=1)
simulation_figThis page was generated using Literate.jl.