Grow macroalgae
Tormey Reimer
2025-10-30
grow-macroalgae.RmdThis space will be used to detail how to use the main model function,
grow_macroalgae().
## Warning: package 'magrittr' was built under R version 4.4.3
## Warning: package 'units' was built under R version 4.4.3
## udunits database from C:/Program Files/R/R-4.4.2/library/units/share/udunits/udunits2.xml
## Warning: package 'lubridate' was built under R version 4.4.3
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
## Warning: package 'ggplot2' was built under R version 4.4.3
## Warning: package 'cowplot' was built under R version 4.4.3
##
## Attaching package: 'cowplot'
## The following object is masked from 'package:lubridate':
##
## stamp
## Warning: package 'dplyr' was built under R version 4.4.3
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
## Warning: package 'tidyr' was built under R version 4.4.3
##
## Attaching package: 'tidyr'
## The following object is masked from 'package:magrittr':
##
## extract
remove_unit("gN")
remove_unit("molN")
install_unit(name = "gN")
install_unit(name = "molN", def = "14.067 gN")
dir.create("pngs")## Warning in dir.create("pngs"): 'pngs' already exists
Load datasets
Within this package, there are three default datasets:
-
envis a datafrae of default environmental variables -
a_armatais a named vector of parameters for Asparagopsis armata -
site_paramsis a named vector of site-specific parameters (culture conditions)
env$nitrate_uM <- env$nitrate %>% set_units("mgN m-3") %>% set_units("umolN L-1") %>% drop_units()
env$ammonium_uM <- env$ammonium %>% set_units("mgN m-3") %>% set_units("umolN L-1") %>% drop_units()
my_species <- a_armataCheck environmental data
First let’s check to make sure our environmental inputs look reasonable. You should also check that they are within the species tolerance limits or the macroalgae will not grow (or maybe that’s what you want).
p_temperature <- ggplot(env, aes(x = t, y = temperature)) +
geom_line(linewidth = 0.75) +
theme_classic() +
labs(x = "Day of year", y = "Temperature (°C)") +
ggtitle("Surface temperature")
p_light <- ggplot(env, aes(x = t, y = (light*exp(-(kW*site_params['d_top']))))) +
geom_line(linewidth = 0.75) +
theme_classic() +
labs(x = "Day of year", y = "Light (μmol m⁻² s⁻¹)") +
ggtitle("Light at top of canopy")
p_salinity <- ggplot(env, aes(x = t, y = salinity)) +
geom_line(linewidth = 0.75) +
theme_classic() +
labs(x = "Day of year", y = "Salinity (g L⁻¹)") +
ggtitle("Salinity")
p_velocity <- ggplot(env, aes(x = t, y = velocity)) +
geom_line(linewidth = 0.75) +
theme_classic() +
labs(x = "Day of year", y = "Velocity (m s⁻¹)") +
ggtitle("Water Velocity")
p_nitrate <- ggplot(env, aes(x = t, y = nitrate)) +
geom_line(linewidth = 0.75) +
theme_classic() +
labs(x = "Day of year", y = "Nitrate (mg m⁻³)") +
ggtitle("Ambient nitrate concentration")
p_ammonium <- ggplot(env, aes(x = t, y = ammonium)) +
geom_line(linewidth = 0.75) +
theme_classic() +
labs(x = "Day of year", y = "Ammonium (mg m⁻³)") +
ggtitle("Ambient ammonium concentration")
p_nitrate_2 <- ggplot(env, aes(x = t, y = nitrate_uM)) +
geom_line(linewidth = 0.75) +
theme_classic() +
labs(x = "Day of year", y = expression("Nitrate ("*mu*"M)")) +
ggtitle("Ambient nitrate concentration (uM)")
p_ammonium_2 <- ggplot(env, aes(x = t, y = ammonium_uM)) +
geom_line(linewidth = 0.75) +
theme_classic() +
labs(x = "Day of year", y = expression("Ammonium ("*mu*"M)")) +
ggtitle("Ambient ammonium concentration (uM)")
plot_grid(
p_temperature + theme(axis.title.x = element_blank(), axis.text.x = element_blank()),
p_light + theme(axis.title.x = element_blank(), axis.text.x = element_blank()),
p_salinity + theme(axis.title.x = element_blank(), axis.text.x = element_blank()),
p_velocity + theme(axis.title.x = element_blank(), axis.text.x = element_blank()),
p_nitrate + theme(axis.title.x = element_blank(), axis.text.x = element_blank()),
p_ammonium + theme(axis.title.x = element_blank(), axis.text.x = element_blank()),
p_nitrate_2,
p_ammonium_2,
ncol = 2
)
Get initial macroalgae stat
Three values are required for macroalgae initialisation: \(N_f\), \(N_s\), and \(Q_{rel}\). \(N_f\), \(N_s\) must be in mg m\(^{-3}\) and can be calculated from an
initial biomass using biomass_to_Nf().
init_biomass <- 0.005 %>% set_units("g L-1") %>% set_units("mg m-3") %>% drop_units()
init_NfNs <- biomass_to_Nf(
biomass = init_biomass,
Q_rel = 0.5,
spec_params = my_species,
dry = T
)
init_state <- c(init_NfNs, Q_rel = 0.5)Check all inputs
Since the grow_macroalgae() function has so many inputs
with very specific requirements, the check_grow() function
is designed to take the exact same inputs as
grow_macroalgae() and provide a report.
check_grow(
t = 1:60,
temperature = env$temperature[1:60],
salinity = env$salinity[1:60],
light = env$light[1:60],
kW = env$kW[1:60],
velocity = env$velocity[1:60],
nitrate = env$nitrate[1:60],
ammonium = env$ammonium[1:60],
ni_uptake = NA,
am_uptake = NA,
site_params = site_params,
spec_params = my_species,
initials = init_state,
# sparse_output = T,
# other_constants = c(s = 0.0045, gam = 1.13, a2 = 0.2^2, Cb = 0.0025)
)## Starting all checks...
## ✔ Timeseries looks good.
## ✔ Variable 'temperature' looks good.
## ✔ Variable 'salinity' looks good.
## ✔ Variable 'light' looks good.
## ✔ Variable 'kW' looks good.
## ✔ Variable 'light' looks good.
## ✔ Variable 'velocity' looks good.
## ✔ Variable 'nitrate' looks good.
## ✔ Variable 'ammonium' looks good.
## ✔ Site params looks good.
## ✔ Variable Nf in initials looks good.
## ✔ Variable Q_int/Q_rel in initials looks good.
## → spec_params has provided linear parameters for nitrate. Uptake will use linear kinetics.
## → spec_params has provided parameters for both Michaelis-Menton and linear uptake for ammonium. Uptake will default to Michaelis-Menton kinetics.
Grow macroalgae
gro <- grow_macroalgae(
t = 1:60,
temperature = env$temperature[1:60],
salinity = env$salinity[1:60],
light = env$light[1:60],
kW = env$kW[1:60],
velocity = env$velocity[1:60],
nitrate = env$nitrate[1:60],
ammonium = env$ammonium[1:60],
ni_uptake = NA,
am_uptake = NA,
site_params = c(site_params, turbulence = NA),
spec_params = my_species,
initials = init_state,
# sparse_output = T,
# other_constants = c(s = 0.0045, gam = 1.13, a2 = 0.2^2, Cb = 0.0025)
)
gro <- as.data.frame(gro)Biomass, Nf and Ns outputs
ggplot(gro, aes(t, B_dw.mg * hm)) +
geom_line(linewidth = 0.75) +
theme_classic() +
ggtitle(expression("Dry biomass (mg m"^-2*")"))
ggsave("pngs/biomass.png")## Saving 7.29 x 4.51 in image
ggplot(gro, aes(t, (Nf * Ns))) +
geom_line(linewidth = 0.75) +
theme_classic() +
ggtitle(expression("Total macroalgae nitrogen (mg m"^-3*")"))
ggsave("pngs/nitrogen.png")## Saving 7.29 x 4.51 in image
ggplot(gro, aes(t, hm)) +
geom_line(linewidth = 0.75) +
theme_classic() +
ggtitle(expression("Macroalgae height (m)"))
ggsave("pngs/height.png")## Saving 7.29 x 4.51 in image
ggplot(gro, aes(t, up_Ni)) +
geom_line(linewidth = 0.75) +
theme_classic() +
ggtitle(expression("Nitrate taken up (mg m-3)"))
ggsave("pngs/nitrate_uptake.png")## Saving 7.29 x 4.51 in image
ggplot(gro, aes(t, up_Am)) +
geom_line(linewidth = 0.75) +
theme_classic() +
ggtitle(expression("Ammonium taken up (mg m-3)"))
ggsave("pngs/ammonium_uptake.png")## Saving 7.29 x 4.51 in image
ggplot(gro, aes(t, Q_lim)) +
geom_line(linewidth = 0.75) +
scale_y_continuous(limits = c(0,1)) +
theme_classic() +
ggtitle(expression("Q limitation"))
ggsave("pngs/Q_lim.png")## Saving 7.29 x 4.51 in image
ggplot(gro, aes(t, T_lim)) +
geom_line(linewidth = 0.75) +
scale_y_continuous(limits = c(0,1)) +
theme_classic() +
ggtitle(expression("Temperature limitation"))
ggsave("pngs/T_lim.png")## Saving 7.29 x 4.51 in image
ggplot(gro, aes(t, I_lim)) +
geom_line(linewidth = 0.75) +
scale_y_continuous(limits = c(0,1)) +
theme_classic() +
ggtitle(expression("Light limitation"))
ggsave("pngs/I_lim.png")## Saving 7.29 x 4.51 in image
ggplot(gro, aes(t, S_lim)) +
geom_line(linewidth = 0.75) +
scale_y_continuous(limits = c(0,1)) +
theme_classic() +
ggtitle(expression("Salinity limitation"))
ggsave("pngs/S_lim.png")## Saving 7.29 x 4.51 in image
ggplot(gro, aes(t, u_c)) +
geom_line(linewidth = 0.75) +
scale_y_continuous(limits = c(0,1)) +
theme_classic() +
ggtitle(expression("Water attenuation"))
ggsave("pngs/u_c.png")## Saving 7.29 x 4.51 in image
ggplot(gro, aes(t, growth_rate)) +
geom_line(linewidth = 0.75) +
geom_hline(yintercept = my_species["mu"], linetype = "dashed") +
theme_classic() +
ggtitle(expression("Growth rate (d-1)"))
ggsave("pngs/growth_rate.png")## Saving 7.29 x 4.51 in image
ggplot(gro, aes(t, Nf_loss)) +
geom_line(linewidth = 0.75) +
theme_classic() +
ggtitle(expression("Nf loss (mg m-3)"))
ggsave("pngs/Nf_loss.png")## Saving 7.29 x 4.51 in image
ggplot(gro, aes(t, Ns_loss)) +
geom_line(linewidth = 0.75) +
theme_classic() +
ggtitle(expression("Ns loss (mg m-3)"))
ggsave("pngs/Ns_loss.png")## Saving 7.29 x 4.51 in image
Sensitivity to parameter changes
params <- names(my_species)
params <- params[!params %in% c("M_am", "C_am", "V_ni", "K_ni", "D_lo", "D_mi", "D_hi", "T_opt", "T_min", "T_max", "S_opt", "S_min", "S_max")]
factors <- c(0.9, 1, 1.1)
res <- data.frame(
param = rep(params, each = length(factors)),
factor = rep(factors, times = length(params))
)
params_adj <- purrr::map(1:nrow(res), function(r) {
my_species_adj <- my_species
my_species_adj[res$param[r]] <- my_species_adj[res$param[r]] * res$factor[r]
my_species_adj
})
res_1 <- purrr::map(params_adj, function(pa) {
gro <- grow_macroalgae(
t = 1:60,
temperature = env$temperature[1:60],
salinity = env$salinity[1:60],
light = env$light[1:60],
kW = env$kW[1:60],
velocity = env$velocity[1:60],
nitrate = env$nitrate[1:60],
ammonium = env$ammonium[1:60],
ni_uptake = NA,
am_uptake = NA,
site_params = c(site_params, turbulence = NA),
spec_params = pa,
initials = init_state
)
data.frame(
B_dw = unname(gro[nrow(gro),"B_dw.mg"]),
total_N = unname((gro[nrow(gro),"Nf"] + gro[nrow(gro),"Ns"])*gro[nrow(gro),"hm"])
)
})
res_2 <- res_1 %>%
bind_rows() %>%
cbind(res) %>%
pivot_wider(
names_from = factor,
values_from = c(B_dw, total_N)
) %>%
mutate(sens.total_N = (total_N_1.1-total_N_0.9)/(0.2*total_N_1),
sens.B_dw = (B_dw_1.1-B_dw_0.9)/(0.2*B_dw_1)) %>%
dplyr::select(c(param, contains("sens")))
ggplot(res_2, aes(x = param, y = sens.total_N)) +
geom_col() +
coord_flip()