Skip to content

SIP287 Add C Saturation - #301

Merged
Alomir merged 26 commits into
masterfrom
SIP287-Implement-Carbon-Saturation
Jun 12, 2026
Merged

SIP287 Add C Saturation#301
Alomir merged 26 commits into
masterfrom
SIP287-Implement-Carbon-Saturation

Conversation

@mswilburn

@mswilburn mswilburn commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Implementing soil carbon saturation by creating a flag, adding a new parameter, modifying fluxes into soil organic carbon pool, adding new flux from soil organic carbon pool to litter pool, and updating documentation. Motivation for this change includes evaluating model soil carbon dynamics with and without an explicit maximum limit for long-term storage.

How was this change tested?

List steps taken to test this change, with appropriate outputs if applicable

If changes are needed for any sipnet.out files in the test/smoke subdirectories, then include output from
tools/smoke_check.py by running the commands below and pasting the output at the end of this PR. Note that
this must be run BEFORE submitting changes to any sipnet.out files.

make smoke
python tools/smoke_check.py run verbose <list tests/smoke subdirectories with changed outputs>

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

Related issues

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.

@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.

New flag option looks good! (Other than the one issue in restart.c)

Comment thread src/sipnet/restart.c Outdated
@mswilburn

Copy link
Copy Markdown
Contributor Author

Smoke and Unit test output.

PR301 Smoke Test Output.txt
PR301 Unit Test Output.txt

@mswilburn
mswilburn marked this pull request as ready for review May 22, 2026 12:43
@Alomir
Alomir requested a review from Copilot May 27, 2026 16:34

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

Implements optional soil carbon saturation (issue #287) by adding a new carbonSaturation model flag, a soilCSaturation parameter, and a saturation-fraction-based rerouting of root-loss and litter-to-soil carbon back into the litter pool in updatePoolsForSoil(). The flag is wired through Context, CLI, restart serialization, and validation (it requires litterPool), and is exercised by a new unit test and existing smoke runs.

Changes:

  • Adds carbon-saturation CLI/model flag (Context, CLI map, restart schema, validation) and registers a new optional soilCSaturation parameter.
  • Modifies updatePoolsForSoil() to partition soil-bound carbon inputs by envi.soilC / params.soilCSaturation between soil and litter pools when the flag is on.
  • Adds testCarbonSaturation.c unit test plus smoke-test config/param updates.

Reviewed changes

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

Show a summary per file
File Description
src/sipnet/sipnet.c New saturation branch in updatePoolsForSoil() and parameter registration.
src/sipnet/state.h Adds soilCSaturation param field (and a stray blank line in FluxVars).
src/sipnet/cli.c, cli.h Adds --carbon-saturation flag, arg map entry, usage text, bumps NUM_FLAG_OPTIONS.
src/common/context.h, context.c Adds carbonSaturation Context field, initializer, and validateContext() dependency on litterPool.
src/sipnet/restart.c Adds the flag to restart schema, serialization, and compatibility check.
tests/sipnet/test_modeling/testCarbonSaturation.c, Makefile New unit test covering four input/saturation combinations.
tests/smoke/{niwot,russell_*}/sipnet.config, sipnet.param Adds default-off CARBON_SATURATION line and soilCSaturation 1.0 param entry.

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

Comment thread src/sipnet/sipnet.c Outdated
Comment thread src/sipnet/sipnet.c Outdated
Comment on lines +1604 to +1615
double saturationFraction = envi.soilC / params.soilCSaturation;
double soilInputs =
fluxes.coarseRootLoss + fluxes.fineRootLoss + fluxes.litterToSoil;

envi.litterC += (fluxes.woodLitter + fluxes.leafLitter +
(soilInputs * saturationFraction) - fluxes.litterToSoil -
fluxes.rLitter - fluxes.litterMethane) *
climate->length;

envi.soilC += (soilInputs * (1 - saturationFraction) - fluxes.rSoil -
fluxes.soilMethane) *
climate->length;

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.

This sounds like something to address

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed, saturationFraction has been clamped to 0-1

Comment thread src/sipnet/sipnet.c Outdated
Comment on lines +79 to +94
soilInputs =
fluxes.coarseRootLoss + fluxes.fineRootLoss + fluxes.litterToSoil;
saturationFraction = expSoilC / params.soilCSaturation;

expLitterC += (fluxes.woodLitter + fluxes.leafLitter +
(soilInputs * saturationFraction) - fluxes.litterToSoil -
fluxes.rLitter - fluxes.litterMethane) *
climate->length;

expSoilC += (soilInputs * (1 - saturationFraction) - fluxes.rSoil -
fluxes.soilMethane) *
climate->length;

updatePoolsForSoil();
status |= checkSoil(expSoilC);
status |= checkLitter(expLitterC);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Revised and included test for envi.soilC > params.soilCSaturation scenario

Comment thread src/sipnet/cli.c
Comment thread src/sipnet/state.h Outdated

@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.

This looks great, glad to have this feature and look forward to seeing whether and when it improves model performance.

I'm about to start a code review but before merging, could you please update the documentation?

@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.

Thanks for adding the docs. I made a few comments related to improving clarity.

Adding the clamp to the code as requested by Mike and Copilot will make code consistent with docs, and avoid negative fluxes.

Comment thread docs/model-structure.md Outdated
Comment thread docs/parameters.md Outdated
| $Q_{10s}$ | soilRespQ10 | Soil respiration Q10 | unitless | scalar determining effect of temp on soil respiration |
| $D_{\text{moisture}}$ | soilRespMoistEffect | scalar determining effect of moisture on soil resp. | unitless | |
| $f_{\text{till}}$ | tillageEff | Effect of tillage on decomposition that exponentially decays over time | fraction | Documented in model structure; event-level term in `events.in` |
| $f_{\text{C,soil,saturation}}$ | soilCSaturation | Maximum amount of carbon that can be stabilized in the soil determined by soil texture | $\text{g C} \cdot \text{m}^{-2} \text{ ground area}$ | |

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.

f is reserved for unitless "fraction" parameters.

Something like C_soil,sat, or C^*_soil

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Revised to C_soil,saturation

Comment thread docs/model-structure.md Outdated
If soil carbon saturation is enabled, only a fraction of the total carbon input will be added to the soil. The amount added to the soil carbon pool is a function of the proximity of the pool to the soil carbon saturation limit. As the soil carbon pool aqcuires more carbon and approaches its saturation limit, the amount of carbon stored in the pool decreases. This represents the physical and chemical constraints of the mineralogical surfaces of soil to stabilize carbon for long-term storage. The remaining carbon that is not stabilized in the soil carbon pool due to saturation constraints will be returned to the litter carbon pool to act as fast-turnover carbon \eqref{eq:soil_carbon_to_litter}.

\begin{equation}
\frac{dC_\text{soil}}{dt} = F^C_{\text{soil}} \cdot (1 - \frac{C_{\text{soil}}}{f_{\text{C,soil,saturation}}}) - R_{\text{soil}} - F^C_{\text{CH}_4\text{,soil}}

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.

F^C_{\text{soil}} it is used earlier to represent “total carbon input to soil pool,” not as an unstabilized return flux, please confirm that it is the correct quantity to use here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is the correct term. The inputs did not change, they are just getting multiplied by (1-saturationFraction). Therefore, F^C_{\text{soil}} remains the same. The term is also correct in the envi.litterC calculation because it is the remainder of the fraction that gets redirected to the litter pool. If F^C_{\text{soil}} * (1-saturationFraction) C goes to the soil pool, then F^C_{\text{soil}} * (saturationFraction) C goes to the litter pool.

@mswilburn

Copy link
Copy Markdown
Contributor Author

All comments have been addressed, ready for another review.

mswilburn and others added 2 commits June 3, 2026 14:03
Add restriction for 'carbon-saturation' requiring 'litter-pool'.

@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.

Thanks, this looks great. I reviewed the documentation and made a few suggestions.

Comment thread docs/parameters.md Outdated
Comment thread docs/model-structure.md
Comment thread docs/model-structure.md
Comment thread docs/model-structure.md Outdated
Comment thread docs/user-guide/running-sipnet.md

@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 great - only a couple of minor doc comments.

@dlebauer - this is good to go from the code perspective, I'll leave the doc approval to you

Comment thread docs/user-guide/running-sipnet.md
Comment thread docs/CHANGELOG.md Outdated
Comment thread src/sipnet/state.h Outdated
@Alomir

Alomir commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

As @dlebauer is away for several weeks, I'm going to merge this in. I believe all of the doc comments have been addressed; any remaining tweaks can be done in a separate PR

@Alomir
Alomir merged commit af5f3b8 into master Jun 12, 2026
12 checks passed
@Alomir
Alomir deleted the SIP287-Implement-Carbon-Saturation branch June 12, 2026 16:10
@Alomir

Alomir commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

This PR is not showing up in the closed PRs list - hoping this comment forces a reindex.

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.

Implement Carbon Saturation in SIPNET

4 participants