R version: 4.5.1
Course: MSc: Fisheries Technology and Stock Assessment (Size Selectivity)

Goals

Libraries

library(ggplot2)
library(dplyr)
## 
## 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
library(tidyr)

1. Helper functions for selectivity curves

We will work with selectivity curves scaled between 0 and 1.
For some dome-shaped curves (normal, double-normal), we scale so the maximum equals 1, which makes it easier to compare shapes across gears.

# 1.1 Logistic selectivity
#     Typical for trawl codend, increasing to an asymptote at 1
#     Parameters:
#       L50 = length at 50% retention
#       SR  = selection range = L75 - L25
logistic_sel <- function(L, L50, SR) {
  # For the logistic curve:
  #   SR = 2*log(3) / b   =>   b = 2*log(3) / SR
  b <- 2 * log(3) / SR
  a <- -b * L50
  1 / (1 + exp(-(a + b * L)))
}

# 1.2 Normal dome-shaped selectivity
#     Typical for gillnets/hooks: catches peak at an optimal size, then drop
#     Parameters:
#       mu    = mode (peak length)
#       sigma = spread (standard deviation)
normal_sel <- function(L, mu, sigma) {
  s <- exp(-0.5 * ((L - mu) / sigma)^2)
  s / max(s)  # scale so max = 1 for easier comparison
}

# 1.3 Double-normal (bi-normal) dome-shaped selectivity
#     More flexible dome-shaped form (can be skewed, bimodal, etc.)
#     Parameters:
#       mu1, sigma1 = first mode - first component (main peak)
#       mu2, sigma2 = second mode - second component (secondary/shoulder)
#       w           = weight of first mode (0–1)
double_normal_sel <- function(L, mu1, sigma1, mu2, sigma2, w = 0.5) {
  s1 <- exp(-0.5 * ((L - mu1) / sigma1)^2)
  s2 <- exp(-0.5 * ((L - mu2) / sigma2)^2)
  s  <- w * s1 + (1 - w) * s2
  s / max(s)  # scale so max = 1 for easier comparison
}

# Length grid used for all plots
L_grid <- seq(20, 180, by = 1)

2. Logistic selectivity – trawl/codend type curves

Example 1: Different L50, same SR

Interpretation: gears that shift selectivity to larger fish (e.g., larger mesh) will have higher L50.

codends <- data.frame(
  gear = c("Codend A", "Codend B", "Codend C"),
  L50  = c(40, 55, 70),  # increases
  SR   = c(8, 8, 8)      # same SR
)

# Generate predicted selectivity for every combination of:
#   - each codend (with its L50 and SR)
#   - each length in L_grid
logistic_df <- codends |>
  crossing(length = L_grid) |>
  mutate(selectivity = logistic_sel(length, L50, SR))

ggplot(logistic_df, aes(x = length, y = selectivity, colour = gear)) +
  geom_line(linewidth = 1.1) +
  labs(
    x = "Length (cm)",
    y = "Retention probability",
    colour = "Codend",
    title = "Logistic selectivity – different L50, same SR"
  ) +
  theme_minimal(base_size = 13)

Example 2: Same L50, different SR

  • Narrow SR → steeper curve (more abrupt transition)
  • Wide SR → more gradual transition
codends_SR <- data.frame(
  gear = c("Narrow SR", "Medium SR", "Wide SR"),
  L50  = c(90, 90, 90),       # same L50
  SR   = c(10, 25, 40)        # different SR
)

logistic_SR_df <- codends_SR |>
  crossing(length = L_grid) |>
  mutate(selectivity = logistic_sel(length, L50, SR))

ggplot(logistic_SR_df, aes(x = length, y = selectivity, colour = gear)) +
  geom_line(linewidth = 1.1) +
  labs(
    x = "Length (cm)",
    y = "Retention probability",
    colour = "Codend",
    title = "Logistic selectivity – same L50, different SR"
  ) +
  theme_minimal(base_size = 13)

3. Dome-shaped selectivity – normal model (gillnets/hooks)

Think of mesh size (or hook size) as controlling the “target-optimum” fish size.
A simplified assumption:

\[ \mu \approx k \times \text{mesh} \]

where k is a scaling factor.

mesh  <- c(5, 7.5, 10, 12.5, 15)  # example mesh/hook sizes
k_mu  <- 6                        # scaling factor from mesh to peak length
sigmaN <- 10                      # common spread for all curves

normal_df <- data.frame(mesh = mesh) |>
  crossing(length = L_grid) |>
  mutate(
    mu = k_mu * mesh,
    selectivity = normal_sel(length, mu, sigmaN)
  )

ggplot(normal_df, aes(x = length, y = selectivity, colour = factor(mesh))) +
  geom_line(linewidth = 1.1) +
  labs(
    x = "Length (cm)",
    y = "Relative selectivity (scaled to 1)",
    colour = "Mesh size",
    title = "Normal dome-shaped selectivity – multi-mesh gears"
  ) +
  theme_minimal(base_size = 13)

4. Dome-shaped selectivity – double-normal (bi-normal)

Here we allow a more flexible shape with a #secondary peak” at larger sizes.
This can mimic skewness, shoulder, or (sometimes) bi-modality.

We first define a parameter table (one row per mesh), then expand over lengths and compute selectivity.

mesh_par <- data.frame(
  mesh   = mesh,
  mu1    = k_mu * mesh,       # main peak
  mu2    = k_mu * mesh + 30,  # secondary peak (shoulder) at larger sizes
  sigma1 = 10,
  sigma2 = 20,
  w      = 0.7                # 70% weight on first mode
)

double_df <- mesh_par |>
  crossing(length = L_grid) |>
  mutate(selectivity = double_normal_sel(length, mu1, sigma1, mu2, sigma2, w))

ggplot(double_df, aes(x = length, y = selectivity, colour = factor(mesh))) +
  geom_line(linewidth = 1.1) +
  labs(
    x = "Length (cm)",
    y = "Relative selectivity (scaled to 1)",
    colour = "Mesh size",
    title = "Double-normal dome-shaped selectivity – flexible curves"
  ) +
  theme_minimal(base_size = 13)

5. Students exercises

These are tasks (you can do them during class or as homework):


cat("\nWELL DONE! You know understand what size selectivity means\n")
## 
## WELL DONE! You know understand what size selectivity means