Skip to content

Bug/i86 annual reset catch - #88

Draft
jacobkasper wants to merge 1 commit into
devfrom
bug/i86-annual-reset-catch
Draft

jacobkasper wants to merge 1 commit into
devfrom
bug/i86-annual-reset-catch

Conversation

@jacobkasper

@jacobkasper jacobkasper commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Justification

Fixes #86.

Problem. OutCatchPerFishery.txt under-reports catch for every fished species by one timestep's catch per year, so it disagrees with OutCatch.txt, OutCATCH.nc and OutTOTCATCH.nc, which agree with each other exactly. For cod (FCD) in our model the shortfall is 384–416 t per year, about 0.15% of annual catch. Anyone reconciling catch across these outputs, or using the per-fishery file for fleet-level analyses, gets figures that are systematically low.

Cause. bm->Catch[box][sp][nf][layer] is a per-step buffer. Catch is deposited during ecology in Calculate_Catch (atprocess.c), then drained into TotCumCatch (plus CumCatch, TripCatch and TotCumBiCatch) and zeroed at the start of the next step's layer processing, in Harvest_Init_Layer_Arrays (atHarvest.c). Between timesteps the buffer therefore always holds the most recent step's catch.

At the year boundary, Harvest_Annual_Reset (atHarvestAnnual.c) zeroes bm->Catch without draining it, destroying the final step of each year. CatchSum (→ OutCatch.txt) and the catch tracers (→ NetCDF) are incremented in the step the catch is taken, so they already include it; TotCumCatch (→ OutCatchPerFishery.txt) never receives it.

Fix. This PR adds Harvest_Account_Catch_Buffer to atHarvest.c, next to the per-step drain it mirrors (same isFished and > 0.0 guards, same accumulators). It is called in Report_Annual_Harvest immediately before Harvest_Report_Annual_Stats, and adds the step still held in the buffer to the completed year, TotCumCatch[..][bm->thisyear - 1]; by that point thisyear has already been incremented in the annual block of atlantismain.c.

The function deliberately does not zero bm->Catch. Harvest_Annual_Reset still does that later in the same annual block, which stops the next per-step drain from counting the step again. As a result, every other routine that reads bm->Catch (management, economics) sees exactly the same values as before, and model dynamics are unaffected.

The call is placed in Report_Annual_Harvest rather than inside Harvest_Report_Annual_Stats because the latter is also called from Textfile_Dump at the end of a run, where adding the in-flight step would file it in the wrong year.

Evidence

Symptom on unmodified code (main, ab323c7), FCD, with OutCatchPerFishery.txt summed over fisheries:

Day OutCatch.txt OutCatchPerFishery Gap (t)
365 261,975.9 261,582.1 393.8
730 269,001.1 268,604.1 397.0
1095 263,658.7 263,274.8 383.9
1460 264,518.8 264,128.7 390.1
1825 277,378.5 276,962.3 416.2

Instrumented counters on unmodified code, FCD, in tonnes. Catch deposited into bm->Catch matched CatchSum to 7 significant figures, and catch drained matched TotCumCatch exactly (the > 0.0 gate in the drain discards nothing):

Period (days) Deposited into bm->Catch Drained into TotCumCatch Shortfall Cumulative shortfall
0–365 262,886.4 262,491.4 395.0 395.0
365–730 269,405.1 269,010.0 395.1 790.1
730–799.5 (run end) 54,395.9 54,101.2 294.7 1,084.8

Each year, the final timestep's catch is still in the buffer at the annual write, and the reset then destroys it, so every year is short by one step and the losses accumulate. The last row is a partial year: its 294.7 t is the step still in flight when the run ended.

At each annual write, the year's final step is still in the buffer; the reset then destroys it, so the gap grows by one timestep's catch per year. The day-799.5 value also includes the step in flight when the run ended.

With this PR (dev, d693861, 800-day run), OutCatchPerFishery.txt summed over fisheries matches OutCatch.txt in every year, including the first:

Day FCD, OutCatch.txt FCD, OutCatchPerFishery
365 262,863.3 262,863.3
730 269,339.3 269,339.3

Differences for all fished species, OutCatch.txt minus OutCatchPerFishery.txt (t):

Day FCD FHA FSA FRF FGH FFF FHE FCA FOC FDC
365 0.01 0 0 0.05 0 0 0 0.00 0 0
730 0.04 0 0 0.05 0 0 0 0.01 0 0

The largest relative difference for any species is 4.6e-7, the print precision of OutCatchPerFishery.txt (%e, 7 significant figures).

Related, not changed here

  • In Harvest_Annual_Reset, bm->TotOldCumCatch[k][n] += TotCumCatch[k][n][bm->thisyear]; runs after bm->thisyear++, so it reads the new, empty year's bucket. The adjacent comment suggests thisyear - 1 was intended; this would affect bulkTAC accounting.
  • In Harvest_Skip_biology, the epibenthic block indexes bm->Catch[ij][sp][nf][k] with k left over from the preceding layer loop.
  • I do not know why/how but when the length of the run seems to impact the catch per year. Another issue for another day.

@jacobkasper
jacobkasper force-pushed the bug/i86-annual-reset-catch branch from 2ae6a73 to ce219b9 Compare September 29, 2026 16:05
@jacobkasper
jacobkasper changed the base branch from main to dev September 29, 2026 16:07
@jacobkasper
jacobkasper marked this pull request as draft September 29, 2026 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug Report]: OutCatchPerFishery.txt under-reports catch by one timestep per year: Harvest_Annual_Reset zeroes undrained bm->Catch

2 participants