What is fb4package?
fb4package is an R implementation of Fish Bioenergetics 4.0 (Deslauriers et al. 2017). Bioenergetics models answer a deceptively simple question: how much food does a fish need to grow from weight A to weight B under these environmental conditions?
The model partitions consumed energy among metabolism, waste, and growth to produce daily estimates of fish consumption and weight gain. It includes a built-in species database with 105 parameterizations for 73 fish species, so you can get started without hunting down literature values.
You can explore the full documentation at hansttito.github.io/fb4package.
Installation
Install the development version from GitHub:
# install.packages("devtools")
devtools::install_github("HansTtito/fb4package")library(fb4package)Core workflow
The package is built around two functions:
Bioenergetic()— builds the model object (species, temperature, diet, initial conditions)run_fb4()— runs the simulation or fits parameters to observed data
Every analysis follows the same three steps:
- Gather species parameters, temperature data, and diet composition
- Build a
Bioenergeticobject - Run
run_fb4()with the strategy of your choice
Step 1: Species parameters
The package ships with a built-in database you can explore immediately:
data(fish4_parameters)
# List all available species
names(fish4_parameters)
# Load Chinook salmon
chinook_db <- fish4_parameters[["Oncorhynchus tshawytscha"]]
# Pick a life stage
stage <- "juvenile"
sp_params <- chinook_db$life_stages[[stage]]
sp_info <- chinook_db$species_info
sp_info$life_stage <- stage
cat("Consumption equation (CEQ):", sp_params$consumption$CEQ, "\n")
cat("Respiration equation (REQ):", sp_params$respiration$REQ, "\n")Step 2: Environmental data (temperature)
Temperature drives virtually every rate in the bioenergetics model, so you need a daily time series:
set.seed(42)
days <- 1:180
base_temp <- 9 + 7 * sin(pi * (days - 20) / 180) # sinusoidal season
temp_vec <- pmax(2, base_temp + rnorm(180, 0, 0.4)) # small daily noise
temp_data <- data.frame(
Day = days,
Temperature = round(temp_vec, 2)
)
cat(sprintf("Temperature range: %.1f – %.1f °C\n",
min(temp_data$Temperature), max(temp_data$Temperature)))This mimics a Pacific Northwest lake warming from ~2°C in early spring to a peak of ~14°C in midsummer, then cooling again — a 180-day growing season.
Step 3: Diet composition
The model needs daily prey proportions and energy densities (J/g wet weight):
alewife <- pmax(0, 0.55 + 0.25 * sin(pi * (days - 30) / 180))
shrimp <- pmax(0, 0.28 - 0.10 * sin(pi * (days - 30) / 180))
inverts <- pmax(0, 1 - alewife - shrimp)
total <- alewife + shrimp + inverts
diet_props <- data.frame(
Day = days,
Alewife = round(alewife / total, 4),
Shrimp = round(shrimp / total, 4),
Inverts = round(inverts / total, 4)
)
prey_energy <- data.frame(
Day = days,
Alewife = 4900, # J/g
Shrimp = 3200,
Inverts = 2600
)Diet shifts seasonally: alewife dominates in summer while shrimp and invertebrates fill in spring and autumn.
Step 4: Building the Bioenergetic object
Now assemble everything into a single model object:
bio <- Bioenergetic(
species_params = sp_params,
species_info = sp_info,
environmental_data = list(temperature = temp_data),
diet_data = list(
proportions = diet_props,
prey_names = c("Alewife", "Shrimp", "Inverts"),
energies = prey_energy
),
simulation_settings = list(
initial_weight = 5, # grams (post-emergence juvenile)
duration = 180
)
)
# Set predator energy density (increases as fish accumulates lipids)
bio$species_params$predator$ED_ini <- 4200 # J/g at start
bio$species_params$predator$ED_end <- 5000 # J/g at end
print(bio)You can visualize the inputs before running any simulation:
plot(bio, type = "dashboard")
plot(bio, type = "temperature")
plot(bio, type = "diet")Estimation strategies
run_fb4() supports four strategies. The right choice depends on what data you have.
Binary search — “what p produces my target weight?”
This is the most common starting point. You have a target final weight and want to find the feeding level p (proportion of maximum ration) that achieves it:
res_bs <- run_fb4(
x = bio,
fit_to = "Weight",
fit_value = 40, # target: 40 g after 180 days
strategy = "binary_search",
verbose = FALSE
)
cat(sprintf("Estimated p : %.4f\n", res_bs$summary$p_value))
cat(sprintf("Final weight: %.1f g\n", res_bs$summary$final_weight))
cat(sprintf("Converged : %s\n", res_bs$summary$converged))A p ≈ 0.62 means the fish consumed roughly 62% of their bioenergetically predicted maximum ration to reach 40 g in 180 days.
plot(res_bs, type = "growth")
plot(res_bs, type = "energy")
plot(res_bs, type = "dashboard")Direct simulation — fixed p, observe growth
If you already know (or want to explore) a specific feeding level:
res_direct <- run_fb4(
x = bio,
fit_to = "p_value",
fit_value = 0.75,
strategy = "direct_p_value",
verbose = FALSE
)
cat(sprintf("Final weight at p = 0.75: %.1f g\n", res_direct$summary$final_weight))Bootstrap — uncertainty from observed weights
When you have a sample of observed final weights and want to propagate measurement uncertainty into your p estimate:
set.seed(123)
obs_weights <- rnorm(25, mean = 40, sd = 40 * 0.08) # 25 fish, CV = 8%
res_boot <- run_fb4(
x = bio,
fit_to = "Weight",
observed_weights = obs_weights,
strategy = "bootstrap",
n_bootstrap = 100,
confidence_level = 0.95,
parallel = FALSE,
verbose = FALSE
)
cat(sprintf("p mean : %.4f\n", res_boot$summary$p_mean))
cat(sprintf("p SD : %.4f\n", res_boot$summary$p_sd))
cat(sprintf("95%% CI : [%.4f, %.4f]\n",
res_boot$method_data$confidence_intervals$p_ci_lower,
res_boot$method_data$confidence_intervals$p_ci_upper))
plot(res_boot, type = "uncertainty")MLE — likelihood-based estimation
Maximizes the likelihood of your observed weights under a log-normal model, returning a standard error and confidence interval via profile likelihood:
res_mle <- run_fb4(
x = bio,
strategy = "mle",
fit_to = "Weight",
observed_weights = obs_weights
)
cat("p estimate:", round(res_mle$summary$p_value, 4), "\n")
cat("SE :", round(res_mle$summary$p_se, 4), "\n")
cat("Converged :", res_mle$summary$converged, "\n")Analyzing results
Once you have a result object, extract ecologically meaningful metrics:
growth_stats <- analyze_growth_patterns(res_bs)
feeding_stats <- analyze_feeding_performance(res_bs)
energy_budget <- analyze_energy_budget(res_bs)
# Growth
cat(sprintf("Final weight : %.1f g\n", growth_stats$final_weight$estimate))
cat(sprintf("Total growth : %.1f g\n", growth_stats$total_growth$estimate))
cat(sprintf("Specific growth rate: %.4f g/g/day\n",
growth_stats$specific_growth_rate$estimate))
# Feeding performance
cat(sprintf("Total consumption : %.1f g\n", feeding_stats$total_consumption$estimate))
cat(sprintf("Gross conv. eff. : %.3f\n",
feeding_stats$gross_conversion_efficiency$estimate))Interpreting the energy budget
A typical juvenile Chinook energy budget over a 180-day summer season:
| Component | % of consumed energy |
|---|---|
| Respiration (metabolism) | ~55–60% |
| Egestion + Excretion (waste) | ~20% |
| Somatic growth | ~20–25% |
This breakdown tells you how efficiently the fish converts food into body mass under the simulated conditions.
Sensitivity analysis
A useful diagnostic: how does final weight respond to different temperatures and feeding levels?
# Grid of temperature offsets × p values
sensitivity <- expand.grid(
temp_offset = seq(-2, 4, by = 1),
p_value = seq(0.3, 1.0, by = 0.1)
)
# For each combination, run a direct simulation and extract final weight
# (see full vignette for the loop implementation)This produces a sensitivity surface that reveals whether growth is more limited by temperature or by food availability — critical for climate impact assessments.
Why bioenergetics?
Bioenergetics models are workhorses in fisheries science for good reason:
- Consumption estimation — infer how much a fish population eats without direct diet sampling
- Climate impact — project how warming temperatures change energy demand and growth
- Ecosystem modeling — link individual-level physiology to population and ecosystem dynamics
- Management — evaluate stocking scenarios, prey availability, or habitat quality
fb4package makes this framework accessible in R, with a tidy interface and a species database that covers most commercially and ecologically important taxa.
References
Deslauriers D, Chipps SR, Breck JE, Rice JA, Madenjian CP (2017). Fish Bioenergetics 4.0: An R-Based Modeling Application. Fisheries 42(11):586–596.
Stewart DJ, Ibarra M (1991). Predation and production by salmonine fishes in Lake Michigan, 1978–88. Canadian Journal of Fisheries and Aquatic Sciences 48(5):909–922.
Chipps SR, Wahl DH (2008). Bioenergetics modeling in the 21st century: Reviewing new insights and revisiting old constraints. Transactions of the American Fisheries Society 137(1):298–313.