# Case study: brown-shrimp lethal TDT

This article is a focused walk-through of one dataset: the brown shrimp (Crangon crangon) lethal thermal-death-time (TDT) assay vendored from `bayesTLS`. It is written for an applied thermal-biology reader who wants to see the whole `freqTLS` workflow end to end on a single, well-behaved lethal dataset — fit, visualise, derive critical temperatures, and place the result beside the Bayesian and classical two-stage estimates.

`shrimp_lethal` has 148 rows at seven nominal assay temperatures (30–33 °C in 0.5 °C steps) crossed with exposure durations from about five minutes to six hours. The survival counts are reconstructed from the source CSV mortality proportions: `freqTLS` multiplies each proportion by the trial size and rounds, so `deaths = round(mortality_prop * total)` and `survived = total - deaths` (the “R-SHRIMP” reconstruction; see `?shrimp_lethal`).

The benchmark configuration is fixed throughout: reference time `tref = 1` hour, the relative mortality threshold, a `beta_binomial` family, and a constant 4PL shape (`low`, `up`, `k` shared across temperatures). This is the matched configuration for the two model-based fits. The classical two-stage estimate is an absolute-LT50 approximation for this near-0/near-1 lethal curve (see `vignette("comparing-to-bayesTLS")`).

This article builds without Stan and renders fast. The `freqTLS` fit runs live; the Bayesian and classical two-stage numbers are read from a version-stamped cache shipped with the package. The two chunks copied from `vignette("comparing-to-bayesTLS")` — the live shrimp fit and the three-way table — render Stan-free using that cache.

    library(freqTLS)

## The live `freqTLS` fit

The `freqTLS` fit needs nothing beyond this package. We standardise the raw assay table with `standardize_data()`, fit the ungrouped shrimp 4PL by maximum likelihood with `fit_4pl()`, and read profile-likelihood confidence intervals for `CTmax` and `z` with `tls()`.

    data(shrimp_lethal)
    shrimp_std <- standardize_data(
      shrimp_lethal,
      temp = "Temperature_assay", duration = "Duration_exposure_hours",
      n_total = "N_individuals_after_trial", mortality = "Mortality_after_trial",
      duration_unit = "hours"
    )
    shrimp_fit <- fit_4pl(shrimp_std, t_ref = 1, family = "beta_binomial", quiet = TRUE)
    tls(shrimp_fit, method = "profile")$summary
    #> # A tibble: 2 × 4
    #>   quantity median lower upper
    #>   <chr>     <dbl> <dbl> <dbl>
    #> 1 CTmax     31.8  31.6  31.9
    #> 2 z          2.19  1.96  2.46

`CTmax` lands near 31.8 °C and `z` near 2.2 °C, each with a narrow asymmetry- respecting profile interval. Biologically, `z` ≈ 2.2 °C is the temperature change that scales the tolerated exposure tenfold: an exposure that is lethal in about an hour at 31.8 °C would be lethal in ~6 minutes at 34.0 °C, or take ~10 hours at 29.6 °C. A larger `z` would mean tolerance declines more gradually with temperature; a smaller `z`, more steeply.

## Seeing the fit

### The Confidence Eye

The default `freqTLS` uncertainty visual is the Confidence Eye: a confidence lens with a hollow point estimate. It carries no prior and makes no probability statement about the parameter — it is a likelihood confidence display, not a posterior density.

    plot_confidence_eye(shrimp_fit, parm = c("CTmax", "z"), method = "profile")

### Survival curves

The fitted survival surface, drawn as one curve per assay temperature against exposure duration:

    plot_survival_curves(shrimp_fit)

### The thermal-death-time line

Collapsing the survival surface to the threshold gives the classical TDT line — log exposure time at the mortality threshold against temperature. Its slope is `z` and its position fixes `CTmax`:

    plot_tdt_curve(shrimp_fit)

## Deriving critical temperatures

Because shrimp lethal TDT measures death, both critical-temperature derivations are meaningful here. `derive_ctmax()` reads the absolute temperature giving 50% survival at a one-hour exposure; `derive_tcrit()` returns the rate-multiplier `T_crit` (valid because this is a lethal endpoint).

    c(
      CTmax_50pct_1h = round(derive_ctmax(shrimp_fit, surv = 0.5, duration = 1), 2),
      T_crit_rate1   = round(derive_tcrit(shrimp_fit, rate = 1), 2)
    )
    #> `T_crit` assumes a lethal endpoint; for sublethal data its steeper `z` makes it
    #> implausibly low.
    #> CTmax_50pct_1h   T_crit_rate1
    #>          31.73          27.39

The 50%-survival critical temperature at one hour is about 31.7 °C; the rate-multiplier `T_crit` is about 27.4 °C. These are deterministic transforms of the fitted `CTmax` / `z`, not new fits. (`derive_tcrit()` prints an explicit lethal-endpoint caveat: on a sublethal endpoint the `z` is estimated from a functional decline rather than death, so feeding it into a lethal-damage accumulator can drive `T_crit` to implausible values. Shrimp lethal TDT is a lethal endpoint, so the value stands.)

## The three-way comparison, with real numbers

The cache holds the maintainer-built `bayesTLS` (posterior) and classical two-stage summaries; the `freqTLS` column is computed live as this page renders. The two model fits use the matched beta-binomial, relative-threshold, constant-shape configuration at `tref = 1` hour. The classical two-stage column uses absolute LT50 and is an approximate comparator because the fitted lethal asymptotes are near zero and one.

Shrimp CTmax and z shown side by side: the two model fits use the relative midpoint; the classical estimator uses absolute LT50.
Quantity | Two-stage (delta CI) | bayesTLS (95% CrI) | freqTLS (profile CI)
--- | --- | --- | ---
CTmax (°C) | 31.62 [31.34, 31.89] | 31.72 [31.60, 31.85] | 31.77 [31.63, 31.92]
z (°C / decade) | 2.04 [1.49, 2.60] | 2.17 [1.95, 2.43] | 2.19 [1.96, 2.46]

The headline is the agreement between the two interval-bearing model fits: the `freqTLS` profile confidence interval and the `bayesTLS` posterior credible interval nearly coincide — and the `freqTLS` side is produced live, in milliseconds, with no Stan. That is the complementary framing made concrete on one dataset: under the matched configuration the likelihood and the posterior summarise the same fitted curve, one prior-free and by optimisation, the other with a prior and MCMC.

## Boundary: what this case study does not cover

The shrimp assay in `bayesTLS` also includes a sublethal time-to-knockdown endpoint — time until loss of righting response. That is a time-to-event quantity with a different likelihood, and it is a deliberate non-goal for `freqTLS`, which fits the single binomial / beta-binomial survival-count 4PL. For the sublethal knockdown analysis, see `bayesTLS`.

Where to next: for a grouped fit, see the zebrafish or aphid case study; to derive heat-injury / `T_crit` from a fit, see `vignette("heat-injury")`.