Skip to content

Account for stock clamping in mass balance check#282

Merged
Alomir merged 6 commits into
PecanProject:masterfrom
divine7022:fix-balance-check-stock-clamping
Mar 3, 2026
Merged

Account for stock clamping in mass balance check#282
Alomir merged 6 commits into
PecanProject:masterfrom
divine7022:fix-balance-check-stock-clamping

Conversation

@divine7022

Copy link
Copy Markdown
Member

Summary

  • when ensureNonNegativeStocks() clamps negative pool to zero, the balance checker now accounts for added mass instead of treating it as an error. A warning is logged when clamping occurs.
  • Motivation:
    the balance check compared pool deltas against flux predicted deltas without accounting for mass added by stock clamping. This caused a fatal error any time a pool went slightly negative -- common during harvest events with extreme parameter combinations (e.g. SA or ensemble runs); had existing TODO acknowledging this problem

How was this change tested?

  • tested on a real failing case
make smoke
python tools/smoke_check.py run verbose russell_2

Run python tools/smoke_check.py help for more info.

Reproduction steps

run sipnet with a parameter set and events file that causes a pool to go slightly negative (e.g. high turnover rate + full harvest). The balance check will fail with a non zero delta equal to the clamped amount

If appropriate, list steps to reproduce the change locally

Related issues

  • Fixes #

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)
  • docs/CHANGELOG.md updated with noteworthy changes
  • Code formatted with clang-format (run git clang-format if needed)

Note: See CONTRIBUTING.md for additional guidance. This repository uses automated formatting checks; if the pre-commit hook blocks your commit, run git clang-format to format staged changes.

Copilot AI review requested due to automatic review settings March 2, 2026 23:36

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

Updates SIPNET’s mass balance checking to account for mass introduced when negative stocks are clamped to zero, preventing false fatal balance errors during edge-case runs (e.g., harvest + extreme parameters).

Changes:

  • Track C/N totals before ensureNonNegativeStocks() and compute per-timestep “clamped mass” adjustments.
  • Subtract clamped adjustments from balance deltas and emit a warning when clamping occurs.
  • Update smoke-test golden output and add a changelog entry.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/sipnet/sipnet.c Captures pre-clamp mass totals, computes clamp adjustments, and logs a warning when clamping happens.
src/sipnet/balance.h Extends BalanceTracker with clampedC/clampedN and exposes getMassTotals().
src/sipnet/balance.c Initializes clamp fields and adjusts delta calculations to account for clamped mass.
tests/smoke/russell_2/sipnet.out Updates expected bcdeltaC/bcdeltaN values to reflect the corrected balance logic.
docs/CHANGELOG.md Notes the balance-check behavior change under “Fixed”.

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

Comment thread src/sipnet/sipnet.c Outdated
Comment thread docs/CHANGELOG.md Outdated

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 5 out of 5 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/sipnet/balance.c:160

  • checkBalance() now logs warnings but never sets err = 1 (and the internal-error calls are commented out), so the model will never exit on balance failure. This is a significant behavioral change that isn’t described in the PR summary, and the current mix of commented-out code + dead if (err) block is confusing. Please either restore the intended fatal behavior (set err when deltas exceed tolerance) or remove the err/exit path and update the PR description to reflect that balance failures are non-fatal.
  int err = 0;
  if (fabs(balanceTracker.deltaC) > 0.0) {
    // err = 1;
    //  logInternalError(  someday
    logWarning(
        "Carbon balance check failed (delta=%8.4f, Y: %d D: %d T: %4.2f)\n",
        balanceTracker.deltaC, climate->year, climate->day, climate->time);
  }
  if (fabs(balanceTracker.deltaN) > 0.0) {
    // err = 1;
    // logInternalError(  someday
    logWarning(
        "Nitrogen balance check failed (delta=%8.4f, Y: %d D: %d T: %4.2f)\n",
        balanceTracker.deltaN, climate->year, climate->day, climate->time);
    logWarning("preTot %8.5f postTot %8.5f input %8.5f output %8.5f clamped "
               "%8.5f delta %8.5f\n",
               balanceTracker.preTotalN, balanceTracker.postTotalN,
               balanceTracker.inputsN, balanceTracker.outputsN,
               balanceTracker.clampedN, balanceTracker.deltaN);
  }
  if (err) {
    logInternalError("Exiting\n");
    exit(EXIT_CODE_INTERNAL_ERROR);
  }

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

Comment thread src/sipnet/balance.c Outdated
Comment thread src/sipnet/sipnet.c Outdated
logWarning(
"Non-negative stock constraint applied for %s (value %8.4f set "
"to %8.4f)\n",
label, *var, minVal);

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

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

The clamping warning message says the value is set to minVal, but the code always assigns *var = 0.. This makes the log output misleading (e.g., for snow where minVal is TINY). Consider either setting *var = minVal or updating the message to report the actual value being assigned (0.0).

Suggested change
label, *var, minVal);
label, *var, 0.0);

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yep, I was thinking it actually set it to TINY, but that's not the case.

Comment thread src/sipnet/sipnet.c Outdated

@Alomir Alomir left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good!

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.

3 participants