diff --git a/src/nature/energy.jl b/src/nature/energy.jl
index 74937b9bf8ab6dd1f56576e236205743be3b6066..e6e9c844b1463cf87861988b081f15c627773d64 100644
--- a/src/nature/energy.jl
+++ b/src/nature/energy.jl
@@ -3,6 +3,9 @@
 ### This file contains structs and functions for implementing Dynamic Energy Budgets.
 ###
 
+
+## STRUCTS
+
 """
     DEBparameters
 
@@ -10,13 +13,18 @@ An immutable struct to save the parameter list for a species' Dynamic Energy Bud
 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)
-    juvenilesize::Float64 # structure size at which an embryo becomes a juvenile (MHb)
-    adultsize::Float64 # structure size at which a juvenile becomes an adult (MHp)
-    maintenance::Float64 # daily percentage of body size needed per update for somatic and maturity maintenance
-    reproduction::Float64 # energy needed to produce an egg (= initial amount of reserve)
-    v::Float64 # energy conductance (XXX ???)
+    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
 
 """
@@ -50,32 +58,111 @@ Ecology, 85(7), 1771–1789. https://doi.org/10.1890/03-9000
 """
 mutable struct EnergyBudget
     params::DEBparameters
-    reserve::Float64
-    structure::Float64
-    offspring::Float64
+    reserve::Float64    # M_E
+    structure::Float64  # M_V
+    #maturity::Float64  # M_H
+    offspring::Float64  # M_ER
 end
 
+
+## INTERNAL CALCULATIONS
+
 """
-    biomass(energybudget)
+    volumetriclength(energybudget)
 
-Calculate the total biomass of an individual based on its energy budget.
-(Sum of the reserve, structure, and offspring buffers; Sibly et al. 2013.)
+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 biomass(deb::EnergyBudget)
-    deb.reserve + deb.structure + deb.offspring
+function volumetriclength(deb::EnergyBudget)
+    #TODO is cm the right unit?
+    weight = deb.reserve + deb.structure + deb.offspring
+    vlength = weight ^ (1/3)
+    vlength
 end
 
 """
-    feed!(biomass, energybudget)
+    maturitymaintenance(energybudget)
 
-Consume a given quantity of food. Expands the energy reserve by an
-amount determined by the assimilation rate.
+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 feed!(biomass::Float64, deb::EnergyBudget)
-    deb.reserve += biomass * deb.params.assimilation
-    #TODO reserve doesn't grow infinitely
+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)
 
@@ -86,25 +173,76 @@ Return `true` if the individual has enough energy to survive, or `false` if the
 reserve is empty and it starves.
 """
 function update!(deb::EnergyBudget)
-    #TODO
-    mobilisation = 0
-    somaticmaintenance = deb.params.maintenance # J_ES
-    maturitymaintenance = deb.params.maintenance # J_EJ
+    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
-    maturityreproduction = (mobilisation*(1-deb.params.kappa)) - maturitymaintenance
-    if deb.structure >= adultsize
-        deb.offspring += maturityreproduction
+    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
-    
-    deb.reserve -= mobilisation
+
+    #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)
 
@@ -113,9 +251,14 @@ embryo/egg, reducing the parent energy in the process. Returns the embryo's ener
 budget, or `nothing` if the conditions are not met.
 """
 function reproduce!(deb::EnergyBudget)
-    if deb.structure >= deb.params.adultsize && deb.offspring >= deb.params.reproduction
-        deb.offspring -= deb.params.reproduction
-        return EnergyBudget(deb.params, deb.params.reproduction, 0.0, 0.0)
+    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