Something went wrong on our end
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
energy.jl 8.39 KiB
### Persefone - a socio-economic-ecological model of European agricultural landscapes.
###
### This file contains structs and functions for implementing Dynamic Energy Budgets.
###
## STRUCTS
"""
DEBparameters
An immutable struct to save the parameter list for a species' Dynamic Energy Budget
model. (See Sousa et al., 2010.)
"""
struct DEBparameters
maxsearchrate::Float64 # maximum amount of food handled per day (F_m)
assimilation::Float64 # percentage of food biomass assimilated (y_EX)
growthefficiency::Float64 # yield of structure on reserve (y_VE)
v::Float64 # energy conductance
surfacesomaticmaintenance::Float64 # surface-specific somatic maintenance (J_ET)
volumesomaticmaintenance::Float64 # volume-specific somatic maintenance (J_EM)
#maturitymaintenance::Float64 # specific maturity maintenance (k_J)
kappa::Float64 # percentage allocated to somatic maintenance and growth (k)
reproductionefficiency::Float64 # overhead cost of reproduction (k_R)
eggsize::Float64 # weight of an egg/embryo, = initial amount of reserve (M_E0)
juvenilesize::Float64 # structure size at which an embryo becomes a juvenile (M_Hb)
adultsize::Float64 # structure size at which a juvenile becomes an adult (M_Hp)
end
"""
EnergyBudget
This struct represents an individual's energy balance, as conceptualised by the
Dynamic Energy Budget theory. Upon assimilation, energy is first stored as biomass
in a reserve buffer, before being used for maintenance, growth, and reproduction.
(Note that this is a simplified model form which ignores maturity as a separate buffer.)
**Sources:**
- Malishev & Kramer-Schadt (2021). Movement, models, and metabolism:
Individual-based energy budget models as next-generation extensions
for predicting animal movement outcomes across scales. Ecological
Modelling, 441, 109413. https://doi.org/10.1016/j.ecolmodel.2020.109413
- Marques et al. (2018). The AmP project: Comparing species on the basis
of dynamic energy budget parameters. PLOS Computational Biology,
14(5), e1006100. https://doi.org/10.1371/journal.pcbi.1006100
- Sibly et al. (2013). Representing the acquisition and use of energy
by individuals in agent-based models of animal populations. Methods in
Ecology and Evolution, 4(2), 151–161. https://doi.org/10.1111/2041-210x.12002
- Sousa et al. (2010). Dynamic energy budget theory restores coherence
in biology. Philosophical Transactions of the Royal Society B: Biological
Sciences, 365(1557), 3413–3428. https://doi.org/10.1098/rstb.2010.0166
- Kooijman, S. A. L. M. (2009). Dynamic energy and mass budgets
in biological systems (3rd ed). Cambridge University Press.
https://www.researchgate.net/profile/Edgar-Meza-3/post/Is_there_a_toxicokinetic_model_for_daphnia_magna_or_other_zooplankton/attachment/59d62cf579197b807798b396/AS%3A348547653357569%401460111644286/download/Dynamic+Energy+Budget+theory+-+Kooijman.pdf
- *see also:* Brown et al. (2004). Toward a metabolic theory of ecology.
Ecology, 85(7), 1771–1789. https://doi.org/10.1890/03-9000
"""
mutable struct EnergyBudget
params::DEBparameters
reserve::Float64 # M_E
structure::Float64 # M_V
#maturity::Float64 # M_H
offspring::Float64 # M_ER
end
## INTERNAL CALCULATIONS
"""
volumetriclength(energybudget)
Calculate the structural length in cm based on an individual's weight
(assuming a density of 1 g/cm³ to calculate volume, see Kooijman 2009).
"""
function volumetriclength(deb::EnergyBudget)
#TODO is cm the right unit?
weight = deb.reserve + deb.structure + deb.offspring
vlength = weight ^ (1/3)
vlength
end
"""
maturitymaintenance(energybudget)
Calculate the specific maturity maintenance k_J.
(Internal function.)
"""
function maturitymaintenance(deb::EnergyBudget)
y_VE = deb.params.growthefficiency
J_EM = deb.params.volumesomaticmaintenance
M_V = deb.structure
k_J = (y_VE * J_EM) / M_V
k_J
end
"""
scaledreservedensity(energybudget)
Calculate the scaled reserve density e.
(Internal function.)
"""
function scaledreservedensity(deb::EnergyBudget)
J_EAm = deb.params.assimilation * deb.params.maxsearchrate
m_E = deb.reserve / deb.structure
m_Em = J_EAm / (deb.params.v * deb.structure)
e = m_E / m_Em
e
end
"""
investmentratio(energybudget)
Calculate the investment ratio g.
(Internal function.)
"""
function investmentratio(deb::EnergyBudget)
J_EAm = deb.params.assimilation * deb.params.maxsearchrate
y_VE = deb.params.growthefficiency
k = deb.params.kappa
v = deb.params.v
M_V = deb.structure
g = (v * M_V) / (k * J_EAm * y_VE)
g
end
"""
growthrate(energybudget)
Calculate the specific growth rate r.
(Internal function.)
"""
function growthrate(deb::EnergyBudget)
J_EAm = deb.params.assimilation * deb.params.maxsearchrate
J_ET = deb.params.surfacesomaticmaintenance
J_EM = deb.params.volumesomaticmaintenance
k = deb.params.kappa
v = deb.params.v
e = scaledreservedensity(deb)
g = investmentratio(deb)
L = volumetriclength(deb)
L_m = k * (J_EAm / J_EM) # max length
L_T = J_ET / J_EM # heating length
r = v * (e/L - (1+L_T/L)/L_m) / (e+g)
r
end
"""
mobilisation(energybudget)
Calculate the mobilisation rate J_EC.
(Internal function.)
"""
function mobilisation(deb::EnergyBudget)
M_E = deb.reserve
r = growthrate(deb)
v = deb.params.v
L = volumetriclength(deb)
J_EC = M_E * (v/L - r)
J_EC
end
## PUBLIC INTERFACE
"""
update!(energybudget)
Carry out a daily update of the energy budget. Mobilises reserves and allocates
these to maintenance (prioritised), growth, and reproduction.
Return `true` if the individual has enough energy to survive, or `false` if the
reserve is empty and it starves.
"""
function update!(deb::EnergyBudget)
J_ET = deb.params.surfacesomaticmaintenance
J_EM = deb.params.volumesomaticmaintenance
k_R = deb.params.reproductionefficiency
y_VE = deb.params.growthefficiency
#k_J = deb.params.maturitymaintenance
k_J = maturitymaintenance(deb)
L = volumetriclength(deb)
k = deb.params.kappa
M_V = deb.structure
#M_H = deb.maturity
M_E = deb.reserve
J_EC = mobilisation(deb)
## Somatic maintenance and growth
J_ES = J_EM * (L^3) + J_ET * (L^2) # somatic maintenance
J_EG = J_EC * k - J_ES # energy devoted to growth
J_VG = J_EG * y_VE # actual growth
#J_VG = growthrate(deb) * deb.structure # r*M_V
#XXX check if J_VG == J_EG * y_VE == r * M_V
deb.structure += J_VG
# juveniles grow until they reach adult size
#XXX growth should stop automatically at M_V == M_Hp
if deb.structure < adultsize
growth = (mobilisation*deb.params.kappa) - somaticmaintenance
deb.structure += growth
end
# juveniles increase in maturity, adults invest into reproduction
J_EJ = k_J * M_H # maturity maintenance
J_ER = J_EC*(1-k) - J_EJ # resources for reproduction
if M_V >= deb.params.adultsize
deb.offspring += J_ER * k_R
else
#deb.maturity += J_ER #XXX I'm ignoring the maturity buffer for now
end
#XXX Check that J_EC == J_ES + J_EG + J_EJ + J_ER
deb.reserve -= J_EC
return deb.reserve > 0
end
"""
feed!(quantity, energybudget)
Consume a given quantity of food. Expands the energy reserve by an
amount determined by the assimilation rate. Returns `true` if successful,
`false` if the reserve is already full.
"""
function feed!(quantity::Float64, deb::EnergyBudget)
F_m = deb.params.maxsearchrate
y_EX = deb.params.assimilation
M_V = deb.structure
M_E = deb.reserve
v = deb.params.v
J_EAm = y_EX * F_m # max assimilation rate
m_E = M_E / M_V # reserve density
m_Em = J_EAm / (v * M_V) # max reserve density
# only feed if the reserve is not yet at maximum capacity
if m_E < m_Em
deb.reserve += quantity * y_EX
return true
else
return false
end
end
"""
reproduce!(energybudget)
If there is sufficient energy in the `offspring` buffer of an adult, produce an
embryo/egg, reducing the parent energy in the process. Returns the embryo's energy
budget, or `nothing` if the conditions are not met.
"""
function reproduce!(deb::EnergyBudget)
M_V = deb.structure
M_ER = deb.offspring
M_Hp = deb.params.adultsize
M_E0 = deb.params.eggsize
if M_V >= M_Hp && M_ER >= M_E0
deb.offspring -= M_E0
return EnergyBudget(deb.params, M_E0, 0.0, 0.0)
else
return nothing
end
end