Skip to content

SIP328 fix wood storage bookkeeping so handling of plantWoodCStorageDelta is consistent#330

Open
dlebauer wants to merge 2 commits intoPecanProject:masterfrom
dlebauer:woodfix
Open

SIP328 fix wood storage bookkeeping so handling of plantWoodCStorageDelta is consistent#330
dlebauer wants to merge 2 commits intoPecanProject:masterfrom
dlebauer:woodfix

Conversation

@dlebauer
Copy link
Copy Markdown
Member

Summary

  • What: Fixes wood-storage bookkeeping so harvest, wood respiration, and wood turnover handle plantWoodCStorageDelta consistently.
  • Motivation:
    • Make wood carbon storage bookkeeping internally consistent, satisfying mass-balance.
    • Harvest, respiration, turnover, and output reporting should handle structural wood and storage delta with explicit semantics so SIPNET does not produce misleading reported wood stocks or nonphysical wood fluxes.
    • The current storage behavior can leave reported wood after harvest or create nonphysical negative wood fluxes.

Without this fix, I was unable to find plausible parameterization that produced sensible AGB values following double counting of woodstorage that was fixed in #248.

How was this change tested?

make libs/libsipnet.a
make -C tests/sipnet/test_modeling tests run

Added focused tests for:

  • sipnet.out reporting plantWoodC = structural wood + storage delta
  • nppStorage = plantWoodCStorageDelta
  • negative storage not reducing wood respiration or wood turnover below structural wood
  • harvest removing positive wood storage
  • harvest leaving negative storage debt visible while preserving carbon balance

Still useful follow-up coverage:

  • event-type tests that exercise harvest storage routing through the public event test fixtures
  • restart tests with positive and negative plantWoodCStorageDelta
  • smoke/reference-output review if this changes expected outputs in configured scenarios

Still TODO

  • Update Docs
  • Update related issues 324,325

Reproduction steps

make libs/libsipnet.a
make -C tests/sipnet/test_modeling tests run

Related issues

For #324, this fixes

  • Harvest no longer uses a negative plantWoodC + plantWoodCStorageDelta value to create negative litter/event fluxes.
  • Wood respiration and wood turnover no longer become negative when storage debt makes reported total wood negative.
  • Positive storage is explicitly routed during harvest, so full harvest removes positive storage instead of leaving reported wood behind.
  • It does not implement a joint clamp/floor on plantWoodC + plantWoodCStorageDelta, which could invent carbon to erase carbon debt.

For #325, this ensures that negative wood storage does not make wood-based process fluxes negative.

Checklist

  • Related issues are listed above.
  • PR title has the issue number in it ("[#] ")
  • Tests added/updated for new features (if applicable)
  • Documentation updated (if applicable)
    • Remaining docs work: clarify in docs/user-guide/model-outputs.md that text plantWoodC is reported total wood (plantWoodC + nppStorage) and structural wood is plantWoodC - nppStorage.
  • docs/CHANGELOG.md updated with noteworthy changes
  • Code formatted with clang-format (run git clang-format if needed)

Route harvest removals through structural wood and positive wood-storage pools separately. Harvest now updates plantWoodCStorageDelta through a dedicated eventWoodStorageDelta flux so a full aboveground harvest removes positive storage instead of leaving reported plantWoodC behind.

Add plant wood helper functions for total reported wood and storage-backed process carbon. Output reporting now uses the total helper, while wood maintenance respiration and wood turnover use storage-backed carbon so negative storage debt does not create negative process fluxes.

Add focused modeling tests for output semantics, negative-storage respiration and turnover, positive-storage harvest removal, negative-storage harvest debt handling, and carbon balance across the event update.
Copilot AI review requested due to automatic review settings April 27, 2026 19:03
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes SIPNET’s wood carbon bookkeeping consistent when plantWoodCStorageDelta is present, aligning harvest handling, wood respiration/turnover calculations, and sipnet.out reporting semantics to better satisfy mass-balance and avoid nonphysical negative wood flux behavior.

Changes:

  • Add helper accessors for “total wood” and “storage-backed wood” and use them in sipnet.out, wood respiration, and wood turnover.
  • Update HARVEST event logic to explicitly remove positive plantWoodCStorageDelta (while leaving negative storage debt visible) and apply storage-delta updates to the state.
  • Add focused unit tests covering output semantics and harvest scenarios with positive/negative storage.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/sipnet/test_modeling/testWoodStorage.c New unit tests for output semantics, flux behavior under negative storage, and harvest routing of positive/negative storage.
tests/sipnet/test_modeling/Makefile Adds the new wood storage test to the modeling test suite and cleans its artifacts.
src/sipnet/state.h Declares new helper functions and adds an event flux for storage-delta routing.
src/sipnet/state.c Implements helper functions for total wood and “storage-backed” wood carbon.
src/sipnet/sipnet.c Uses helpers in sipnet.out reporting and in wood respiration/turnover flux calculations.
src/sipnet/events.c Updates harvest to split structural wood vs. positive storage removal and applies storage-delta event flux to state.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/sipnet/events.c
Comment on lines 505 to 509
fluxes.eventSoilC += soilAdd / climLen;
fluxes.eventLeafC += leafDelta / climLen;
fluxes.eventWoodC += woodDelta / climLen;
fluxes.eventWoodStorageDelta += storageWoodDelta / climLen;
fluxes.eventFineRootC += fineDelta / climLen;
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fluxes.eventWoodStorageDelta is introduced/updated for harvest, but resetEventFluxes() doesn’t currently reset it. If processEvents() is ever called without a preceding resetFluxes() (several unit tests call processEvents() directly), the storage-delta event flux can carry over between timesteps/tests and corrupt pool updates. Add fluxes.eventWoodStorageDelta = 0.0; to resetEventFluxes() alongside the other event flux resets.

Copilot uses AI. Check for mistakes.
Comment thread src/sipnet/events.c
Comment on lines 506 to 509
fluxes.eventLeafC += leafDelta / climLen;
fluxes.eventWoodC += woodDelta / climLen;
fluxes.eventWoodStorageDelta += storageWoodDelta / climLen;
fluxes.eventFineRootC += fineDelta / climLen;
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Harvest now updates plantWoodCStorageDelta via fluxes.eventWoodStorageDelta, but the writeEventOut() call for HARVEST doesn’t include this delta. Since events.out is used for debugging/testing and is intended to list all state deltas applied by an event, this omission makes harvest rows incomplete/misleading (especially when positive storage is removed). Include fluxes.eventWoodStorageDelta in the parameters written for harvest events.

Copilot uses AI. Check for mistakes.
Comment on lines +47 to +66
params.baseVegResp = 1.0;
params.vegRespQ10 = 1.0;
params.woodTurnoverRate = 0.5;
params.coarseRootTurnoverRate = 0.0;
params.fineRootTurnoverRate = 0.0;
params.coarseRootAllocation = 0.0;
params.fineRootAllocation = 0.0;
params.woodAllocation = 0.0;
params.leafCN = 20.0;
params.woodCN = 10.0;
params.fineRootCN = 30.0;

envi = (Envi){0};
envi.soilC = 10.0;
envi.soilWater = 5.0;

initTrackers();
resetFluxes();
initBalanceTracker();
}
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initTrackers() computes trackers.soilWetnessFrac = envi.soilWater / params.soilWHC, but this test setup never initializes params.soilWHC. If it remains 0 (common in isolated unit tests), this produces Inf/NaN and can make the test output nondeterministic. Initialize params.soilWHC (and any other params used by initTrackers() in this test) to a non-zero value before calling initTrackers().

Copilot uses AI. Check for mistakes.
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.

Clarify SIPNET wood-storage bookkeeping around harvest and outputs

2 participants