Skip to content

SIP208, SIP209 Add carbon balance check#248

Merged
Alomir merged 23 commits intomasterfrom
add_carbon_balance_check
Feb 6, 2026
Merged

SIP208, SIP209 Add carbon balance check#248
Alomir merged 23 commits intomasterfrom
add_carbon_balance_check

Conversation

@Alomir
Copy link
Collaborator

@Alomir Alomir commented Feb 3, 2026

Summary

  • What: Add carbon and nitrogen balance checks, fix discovered issues

Buckle up...

General

balance.c|h:

  • added infrastructure to calculate total system C and N and system inputs and outputs for each. Also calculates deltas for each time step.

sipnet.c:

  • now writes a line to events.out when leaf-on or leaf-off is triggered
  • created an updatePoolsAndBalance function for all of updateXPools subfunctions plus new balance calcs-n-checks
  • added output columns for balance-check deltas for C and N
  • splits leafCreation flux into two parts to track creation from leaf-on event separately
  • leaf-on creation flux now subtracted from wood pool

state.h:

  • added new NPP storage pool
  • added explicit fluxes for tracking system input and output C and N caused by events

Carbon balance

  • fixes Check (and fix) mass balance of woodCreation #208 by formalizing the NPP storage concept in wood C, as described in our current model doc. Note that this shuffles some terms around , most notable root respiration terms. But, note below for further changes here

Nitrogen balance

  • Realized that having the "NPP storage" included in woodC was guaranteed to throw off N balance; split NPP storage into it's own pool.
  • NOTE: currently, we are outputting the same plantWoodC values as before (i.e., nppStorage + plantWoodC), but calculations involving plantWoodC are NOT including nppStorage (wood resp and wood litter)
  • Nitrogen balance is not finished, as there are two known issues:
  • leaf on/leaf off magically adds/removes N; this is a real issue [UPDATE: this should be fine now]
  • plant N uptake is not implemented yet (this should be fixed when it is)

How was this change tested?

I ran smoke tests, reviewed output for general logical sense, and verified that the balance checks worked (modulo known issues with N)

Steps to reproduce and verify the change locally:

make smoke

See sipnet.out in any smoke test directory

Smoke test check results:
smoke_check.txt

Related issues

Reproduction steps

If appropriate, list steps to reproduce the change locally

Related issues

  • Fixes # (or "Relates to #" if this is not a resolution of that ticket)

Checklist

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

For model structure changes:

  • Removed \fraktur font formatting from docs/model-structure.md for implemented features

@Alomir Alomir marked this pull request as ready for review February 3, 2026 21:18
Copy link
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 pull request implements carbon and nitrogen balance checking infrastructure to address issues #208 and #209. The changes separate the NPP storage concept into its own pool (distinct from plantWoodC) to enable proper nitrogen balance tracking, add comprehensive mass balance verification, and implement event tracking for leaf-on and leaf-off phenology.

Changes:

  • New balance checking infrastructure (balance.c/h) that calculates and verifies total system carbon and nitrogen at each timestep
  • Separation of nppStorage from plantWoodC pool to enable nitrogen balance calculations
  • Addition of explicit event flux tracking for mass balance (eventInputC, eventOutputC, eventInputN, eventOutputN)
  • Enhanced event output to include computed leaf-on and leaf-off events
  • New test suite (testBalance.c) and smoke test improvements

Reviewed changes

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

Show a summary per file
File Description
src/sipnet/balance.c New file implementing mass balance tracking and verification logic
src/sipnet/balance.h New file defining balance tracker structure and interface
src/sipnet/state.h Added nppStorage pool and event mass balance flux fields
src/sipnet/sipnet.c Refactored NPP storage model, added balance checking to pool updates, enhanced output
src/sipnet/events.c Added mass balance tracking for events, new writeComputedEventOut function
src/sipnet/events.h Added writeComputedEventOut function declaration
src/sipnet/cli.c Minor documentation clarification for do-single-outputs
tools/smoke_check.py Added 'base' comparison option and updated column list for new outputs
tests/smoke/*/events.out Updated with new leaf-on and leaf-off event outputs
tests/sipnet/test_modeling/testBalance.c New comprehensive balance checking test
tests/sipnet/test_modeling/balance.param Test parameter file
tests/sipnet/test_modeling/balance.clim Test climate file
tests/sipnet/test_modeling/Makefile Added testBalance to build
Makefile Added balance.c to SIPNET_CFILES
CMakeLists.txt Added balance.c and testBalance.c to builds

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

Copy link
Member

@dlebauer dlebauer left a comment

Choose a reason for hiding this comment

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

Looks good. It is reassuring to have these checks (I think title should be C and N checks?).

I just discussed this with @Alomir online, and am noting down a few key decisions and rationale.

  1. The N demand associated with leafOn can be met by the soil mineral N pool, constrained by the N limitation dynamics.

    • If this causes the model to behave in unexpected ways (e.g. N limitation is too strong to meet demand for leafOn, and then plants are unable to grow realistically), we can revisit this assumption.
    • A more physiologically realistic approach would be to have N translocated from leaves to a N storage pool prior to leafOff, then use that storage to support leafOn. We decided against that because plant biomass has fixed C:N in the current version of SIPNET.
  2. Rename envi.nppStorage to better reflect that this is a carbon storage or non-structural carbohydrate delta (without N). We discussed using woodNSCDelta but I'm leaning toward 'storage' since it is an abstract bookkeeping term, while NSC is directly measurable. e.g. woodCStorageDelta.

  3. Currently, plantWoodC implicitly includes the nppStorage storage delta. To explicitly track the storage delta and maintain existing behavior, any fluxes that use total wood C need to replace plantWoodC with (plantWoodC + nppStorage) where it is meant to represent total woodC e.g. calculations of flux and allocation. But only use plantWoodC in N accounting.

// litter (organic) nitrogen pool (g N m^-2 ground area)
double litterN;

///// New to SIPNET
Copy link
Member

Choose a reason for hiding this comment

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

maybe time to bump a version and say 'added to SIPNET in v2.1'

balanceTracker.deltaN = 0.0;
}

// TBD: warn if balance off?
Copy link
Member

Choose a reason for hiding this comment

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

Any reason not to error? Seems like this is a failure here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nope, error added!

Copy link
Contributor

Copilot AI commented Feb 5, 2026

@dlebauer I've opened a new pull request, #252, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Member

@dlebauer dlebauer left a comment

Choose a reason for hiding this comment

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

Looks good to me. I fixed one typo, made one suggestion to comments for clarity (but I am not certain it is correct), and asked whether we should write out event fluxes that are 0.

@copilot please suggest updates to the documentation (model-structure.md, parameters.md) and CHANGELOG.md.

}

if (ctx.nitrogenCycle) {
// Note: this is the one place where we ust plantWoodC by itself; it's the
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Note: this is the one place where we ust plantWoodC by itself; it's the
// Note: this is the one place where we use plantWoodC by itself; it's the

@@ -1,14 +1,14 @@
1999 184 leafon fluxes.leafCreation=0.00
1999 184 leafon fluxes.leafOnCreation=0.00
Copy link
Member

Choose a reason for hiding this comment

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

should these be 0? should they be written out if they are 0?

Comment on lines +436 to +439
// here that must be tracked. So, we split the wood pool into plantWoodC
// (above) and a new pool to track non-nitrogen-affecting changes over time.
// As this is a delta, it can be negative. Note that the actual "wood carbon"
// is the sum of these two pools.
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering is if there is a more clear way to define "non-nitrogen-affecting changes". My first thought is that this is separating out NPP + allocation as C-only processes? However, N affects both, and both of these eventually affect N (e.g. through demand and allocation).

Suggested change
// here that must be tracked. So, we split the wood pool into plantWoodC
// (above) and a new pool to track non-nitrogen-affecting changes over time.
// As this is a delta, it can be negative. Note that the actual "wood carbon"
// is the sum of these two pools.
// here that must be tracked. So, we split the wood pool into plantWoodC
// (above) and a new pool, plantWoodCStorage, to track non-nitrogen-affecting
// changes over time.
// As this is a delta, it can be negative. Note that the actual "wood carbon"
// is the sum of these two pools.
// plantWoodCStorage term was previously named nppStorage and tracked implicitly;
// tracking it explicitly allows SIPNET to handle NPP and allocation prior to
// accounting for N dynamics.

Copy link
Contributor

Copilot AI commented Feb 6, 2026

@dlebauer I've opened a new pull request, #253, to work on those changes. Once the pull request is ready, I'll request review from you.

@dlebauer
Copy link
Member

dlebauer commented Feb 6, 2026

@copilot Can you provide a final review of this PR?

Copy link
Contributor

Copilot AI commented Feb 6, 2026

@dlebauer I've opened a new pull request, #254, to work on those changes. Once the pull request is ready, I'll request review from you.

@Alomir Alomir merged commit da427c9 into master Feb 6, 2026
12 checks passed
@Alomir Alomir deleted the add_carbon_balance_check branch February 6, 2026 17:02
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.

Test C balance closure Check (and fix) mass balance of woodCreation

3 participants