Skip to content

Update neg growth handling to be an actual limitation check - #368

Merged
Alomir merged 42 commits into
masterfrom
SIPxxx-formalize-negative-growth-handling-and-limitation-checks
Aug 19, 2026
Merged

Update neg growth handling to be an actual limitation check#368
Alomir merged 42 commits into
masterfrom
SIPxxx-formalize-negative-growth-handling-and-limitation-checks

Conversation

@Alomir

@Alomir Alomir commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • What: Bring negative growth handling in line with SIPNET methodology
  • Motivation: Better model design

Details

  • The wood/leaf and root/root checks for inappropriate allocation of negative growth have been moved to limitations.c, in a proper limitation check
  • nResorptionFlux handling moved into nitrogen.c, which is more appropriate than sipnet.c (better modularity)
    • Note this also removes the signed weirdness required in the old updatedNResorptionFlux function
  • Unit tests updated as appropriate for the new code locations/flow

How was this change tested?

This should be a zero-functional-change PR, with one minor exception - leaf-off events no longer report resorbed N (but this can be calculated, so no real loss). Some unit tests needed to be updated (and were), but the smoke tests pass without modification.

Related issues

  • Fixes #N/A

Checklist

  • Related issues are listed above. PRs without an approved, related issue may not get reviewed.
  • PR title has the issue number in it ("[#] <concise description of proposed change>")
  • Tests added/updated for new features (if applicable)
  • Documentation updated (if applicable) (not applicable)
  • docs/CHANGELOG.md updated with noteworthy changes (None)
  • Code formatted with clang-format (run git clang-format if needed)

Alomir and others added 30 commits August 1, 2026 10:23
* Initial plan

* Add unit tests for carbon accounting and plant mortality changes

* Update for clang-format

* Update context handling

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Mike Longfritz <Mike.Longfritz@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: dlebauer <464871+dlebauer@users.noreply.github.com>
Co-authored-by: dlebauer <464871+dlebauer@users.noreply.github.com>
Base automatically changed from SIP350-Redux-Revamp-C-N-Handling to master August 14, 2026 21:06
@Alomir
Alomir marked this pull request as ready for review August 18, 2026 19:01
Copilot AI lite review requested due to automatic review settings August 18, 2026 19:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 refactors negative-growth handling by moving “can’t drive pools negative” allocation adjustments into the limitations layer, and consolidates reduction-driven nitrogen resorption accounting into the nitrogen module. This fits the SIPNET execution model by separating flux calculation, limitation checks, and pool updates more cleanly.

Changes:

  • Moved negative-growth allocation correction (leaf↔wood and fine↔coarse root) out of sipnet.c into limitations.c via a new checkCarbonLimitations() step.
  • Replaced incremental updateNResorptionFlux() updates with a centralized calcReductionResorptionFlux() calculation in nitrogen.c.
  • Updated unit tests to reflect the new call order/locations for carbon limitation checks and reduction resorption.

Reviewed changes

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

Show a summary per file
File Description
tests/sipnet/test_modeling/testNitrogenCycle.c Removes the unit test for the deleted updateNResorptionFlux() helper.
tests/sipnet/test_modeling/testFluxCalculations.c Adjusts flux tests to call checkCarbonLimitations() and calcReductionResorptionFlux() explicitly and updates expected resorption values.
src/sipnet/sipnet.c Removes in-function negative-creation adjustments and adds a centralized checkCarbonLimitations() call before nitrogen flux calculations.
src/sipnet/nitrogen.h Removes the old resorption update API and exposes calcReductionResorptionFlux() (used by tests).
src/sipnet/nitrogen.c Implements centralized reduction-driven resorption and changes nitrogen flux wrapper behavior.
src/sipnet/limitations.h Adds checkCarbonLimitations() to the limitations interface.
src/sipnet/limitations.c Implements carbon limitation logic previously embedded in wood/leaf and root flux calculators.
Suppressed comments (1)

src/sipnet/nitrogen.h:64

  • The calcNitrogenFluxes() header comment says it computes all nitrogen fluxes "except N resorption", but calcNitrogenFluxes() now calls calcReductionResorptionFlux(). The comment should be updated so it matches the current behavior.
/*!
 * Calculate all nitrogen fluxes (except N resorption)
 *
 * This is the general flux calculation wrapper for sipnet.c
 */

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/sipnet/nitrogen.c Outdated
Comment on lines +174 to +176
if (fluxes.woodCreation < 0.0) {
// Note: we want these negative fluxes to INCREASE N resorption
fluxes.reductionNResorption -=

@Alomir Alomir Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I agree that this is a weak check, but:

  • if mean NPP < 0, ALL creation terms are <= 0; likewise, if mean NPP >=0, all creation terms are >= 0
  • this only fails if the wood allocation fraction parameter is zero, which I think is nonsense?

However...

  • better commenting would be good, and
  • given that they are all non-positive-or-not, checking the sum is stronger

Will update.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

Comment thread src/sipnet/limitations.c Outdated
Comment on lines +136 to +138
// If leafCreation is too negative, we need to deduct from wood instead
double leafDeficit = envi.plantLeafC / climate->length + fluxes.leafCreation -
fluxes.leafLitter;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch! In the long run, I think it's better to consider the leaf-off litter too, but let's stick with "no functional change" for this PR. I'll make a ticket to consider leaf-off - and events - in this limitation check.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ticket #372 has been created

Comment thread src/sipnet/limitations.c Outdated
@Alomir

Alomir commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

Note - a discovered TODO: N resorption from leaf-off is still in sipnet.c, and should be moved to nitrogen.c with the rest of it.

Done.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

Suppressed comments (2)

src/sipnet/nitrogen.c:193

  • This introduces an additional model-state change beyond the event-output change described in the PR. fluxes.leafLitter includes calculated phenology leaf-off litter regardless of ctx.events, whereas the previous resorption update for that litter was inside if (leafOff > TINY && ctx.events). Consequently, nitrogen-enabled runs with events disabled now resorb phenology leaf N when they previously did not. Either preserve the old guard/flux split for this zero-functional-change PR, or explicitly document and regression-test this behavior change.
  // Leaf litter resorption; at this point, fluxes.leafLitter counts both normal
  // turnover and leaf-off calcs. Note that event leaf off is handled in
  // events.c
  double nResorp =
      params.leafNResorptionFrac * fluxes.leafLitter / params.leafCN;
  fluxes.leafOffNResorption += nResorp;

src/sipnet/limitations.c:161

  • The relocated below-ground limitation has no test exercising either redistribution branch. The existing negative-root test leaves both root pools solvent and does not call checkCarbonLimitations(), so a regression in this core moved logic would pass. Add cases where only the fine-root pool and only the coarse-root pool would go negative, and verify that creation is transferred to the other root pool.
  if ((fineRootDeficit < 0.0) != (coarseRootDeficit < 0.0)) {
    // If neither are negative, nothing to do
    // If both are negative, the plant will die in checkForMortality()
    if (fineRootDeficit < 0.0) {
      fluxes.coarseRootCreation += fineRootDeficit;
      fluxes.fineRootCreation -= fineRootDeficit;
    }
    if (coarseRootDeficit < 0.0) {
      fluxes.fineRootCreation += coarseRootDeficit;
      fluxes.coarseRootCreation -= coarseRootDeficit;

@dlebauer dlebauer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM
Please update changelog

@Alomir
Alomir enabled auto-merge (squash) August 19, 2026 16:18
@Alomir

Alomir commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator Author

linter check appears to be hung; close and reopen to restart.

@Alomir Alomir closed this Aug 19, 2026
auto-merge was automatically disabled August 19, 2026 16:23

Pull request was closed

@Alomir Alomir reopened this Aug 19, 2026
@Alomir
Alomir enabled auto-merge (squash) August 19, 2026 16:25
@Alomir
Alomir merged commit 41fa853 into master Aug 19, 2026
27 of 30 checks passed
@Alomir
Alomir deleted the SIPxxx-formalize-negative-growth-handling-and-limitation-checks branch August 19, 2026 16:33
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.

4 participants