### Persefone.jl - a model of agricultural landscapes and ecosystems in Europe. ### ### This file contains structs and functions for implementing Dynamic Energy Budgets. ### ## XXX THE CODE IN THIS FILE IS CURRENTLY NOT USED AND LIKELY WON'T BE IN FUTURE. ## HOWEVER, I PREFER TO KEEP IT FOR NOW IN CASE WE EVER DO. (DV, 29th July 2024) #TODO add units ## 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 - *compare with:* 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