-
Notifications
You must be signed in to change notification settings - Fork 18
SIP182b Add litter and soil org n fluxes #199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
2380796
640bb2e
5ac872d
37663f3
7137e15
f8fb01a
4c96eb7
896f99d
6f77a31
cc2ef42
789f143
d58dbad
ddfa75e
73fa60c
a6a08ef
48a547f
0fd073d
07dd270
309ea45
2bf2ea1
71b240b
fb91e8d
d540a76
d2fae26
068021e
0d09b86
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -236,7 +236,7 @@ $$ | |||||
| R_{H} = f_{R_H} \cdot | ||||||
| \left(\sum_j K_j \cdot C_j | ||||||
| \right) \cdot | ||||||
| D_{\text{temp}} \cdot D_{\text{water,}R_H} \cdot D_{CN} \mathfrak{\cdot D_{\text{tillage}}} | ||||||
| D_{\text{temp}} \cdot D_{\text{water,}R_H} \cdot D_{CN} \cdot D_{\text{tillage}} | ||||||
| \tag{7}\label{eq:rh} | ||||||
| $$ | ||||||
|
|
||||||
|
|
@@ -261,7 +261,7 @@ The calculation of methane flux $(F^C_{CH_4})$ is analagous to to that of $R_H$ | |||||
|
|
||||||
| The carbon and nitrogen cycle are tightly coupled by the C:N ratios of plant and organic matter pools. The C:N ratio of plant biomass pools is fixed, while the C:N ratio of soil organic matter and litter pools is dynamic. | ||||||
|
|
||||||
| ### $\frak{Fixed \ Plant \ C:N \ Ratios}$ | ||||||
| ### Fixed Plant C:N Ratios | ||||||
|
|
||||||
| Plant biomass pools have a fixed CN ratio and are thus stoichiometrically coupled to carbon: | ||||||
|
|
||||||
|
|
@@ -275,15 +275,15 @@ Where $i$ is the leaf, wood, fine root, or coarse root pool. This relationship a | |||||
|
|
||||||
| Soil organic matter and litter pools have dynamic CN that is determined below. | ||||||
|
|
||||||
| ### $\frak{Dynamic \ Soil \ Organic \ Matter \ and \ Litter \ C:N \ Ratios}$ | ||||||
| ### Dynamic Soil Organic Matter and Litter C:N Ratios | ||||||
|
|
||||||
| The change in the soil C:N ratio over time of soil and litter pools depends on the rate of change of carbon and nitrogen in the pool, normalized by the total nitrogen in the pool. This makes sense as it captures how changes in carbon and nitrogen affect their ratio. | ||||||
| In SIPNET, the C:N ratio of soil and litter pools is calculated directly from the carbon and nitrogen pools. | ||||||
|
|
||||||
| $$ | ||||||
| \frac{dCN_{\text{j}}}{dt} = \frac{1}{N_{\text{j}}} \left( \frac{dC_{\text{j}}}{dt} - CN_{\text{j}} \cdot \frac{dN_{\text{j}}}{dt} \right) \tag{10}\label{eq:cn} | ||||||
| CN_j = \frac{C_j}{N_j}, \qquad j \in \{\text{soil, litter}\}. | ||||||
| $$ | ||||||
|
|
||||||
| $$\small j \in \{\text{soil, litter}\}$$ | ||||||
| This is used to calculate C:N-dependency $D_{CN}$ used in Eq. \eqref{eq:cn_dep}. | ||||||
|
|
||||||
| ### $\frak{C:N \ Dependency \ Function \ (D_{CN})}$ | ||||||
|
||||||
| ### $\frak{C:N \ Dependency \ Function \ (D_{CN})}$ | |
| ### C:N Dependency Function ($D_{CN}$) |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -199,6 +199,11 @@ void validateContext(void) { | |||||
| hasError = 1; | ||||||
| } | ||||||
|
|
||||||
| if (ctx.nitrogenCycle && !ctx.litterPool) { | ||||||
| logError("nitrogen-cycle require litter-pool to be turned on\n"); | ||||||
|
||||||
| logError("nitrogen-cycle require litter-pool to be turned on\n"); | |
| logError("nitrogen-cycle requires litter-pool to be turned on\n"); |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -36,6 +36,7 @@ EventNode *createEventNode(int year, int day, int eventType, | |||||
| newEvent->year = year; | ||||||
| newEvent->day = day; | ||||||
| newEvent->type = eventType; | ||||||
| static int nitrogenWarned = 0; | ||||||
|
||||||
|
|
||||||
| switch (eventType) { | ||||||
| case HARVEST: { | ||||||
|
|
@@ -97,6 +98,12 @@ EventNode *createEventNode(int year, int day, int eventType, | |||||
| fParams->minN = minN; | ||||||
| // params->nh4_no3_frac = nh4_nos_frac; | ||||||
| newEvent->eventParams = fParams; | ||||||
|
|
||||||
| if (!ctx.nitrogenCycle && (orgN > 0.0 || minN > 0.0) && !nitrogenWarned) { | ||||||
| logWarning("Fertilization nitrogen quantities are being ignored since " | ||||||
| "nitrogen cycle modeling is off\n"); | ||||||
| nitrogenWarned = 1; | ||||||
| } | ||||||
| } break; | ||||||
| case PLANTING: { | ||||||
| double leafC, woodC, fineRootC, coarseRootC; | ||||||
|
|
@@ -440,9 +447,16 @@ void processEvents(void) { | |||||
| const double orgC = fertParams->orgC; | ||||||
| const double minN = fertParams->minN; | ||||||
|
|
||||||
| fluxes.eventOrgN += orgN / climLen; | ||||||
| fluxes.eventLitterC += orgC / climLen; | ||||||
| fluxes.eventMinN += minN / climLen; | ||||||
| if (ctx.nitrogenCycle) { | ||||||
| fluxes.eventOrgN += orgN / climLen; | ||||||
| fluxes.eventMinN += minN / climLen; | ||||||
| } else { | ||||||
| // As the warning says in readEventData(), we ignore N when the | ||||||
| // nitrogen cycle model is off | ||||||
| fluxes.eventOrgN += 0; | ||||||
| fluxes.eventMinN += 0; | ||||||
|
Comment on lines
+457
to
+458
|
||||||
| fluxes.eventOrgN += 0; | |
| fluxes.eventMinN += 0; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -392,6 +392,9 @@ void readParamData(ModelParams **modelParamsPtr, const char *paramFile) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initializeOneModelParam(modelParams, "litterOrgNInit", &(params.litterOrgNInit), ctx.nitrogenCycle); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initializeOneModelParam(modelParams, "nVolatilizationFrac", &(params.nVolatilizationFrac), ctx.nitrogenCycle); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initializeOneModelParam(modelParams, "nLeachingFrac", &(params.nLeachingFrac), ctx.nitrogenCycle); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initializeOneModelParam(modelParams, "leafCNRatio", &(params.leafCNRatio), ctx.nitrogenCycle); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initializeOneModelParam(modelParams, "woodCNRatio", &(params.woodCNRatio), ctx.nitrogenCycle); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initializeOneModelParam(modelParams, "rootCNRatio", &(params.rootCNRatio), ctx.nitrogenCycle); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // NOLINTEND | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // clang-format on | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -409,12 +412,13 @@ void readParamData(ModelParams **modelParamsPtr, const char *paramFile) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| void outputHeader(FILE *out) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fprintf(out, "Notes: (PlantWoodC, PlantLeafC, Soil and Litter in g C/m^2; " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Water and Snow in cm; SoilWetness is fraction of WHC;\n"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fprintf(out, "year day time plantWoodC plantLeafC woodCreation "); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fprintf(out, "soil microbeC coarseRootC fineRootC "); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fprintf(out, "litter soilWater soilWetnessFrac snow "); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fprintf(out, "npp nee cumNEE gpp rAboveground rSoil rRoot ra rh rtot " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "evapotranspiration fluxestranspiration minN soilOrgN " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "litterOrgN n2oFlux nLeachFlux\n"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fprintf(out, "year day time plantWoodC plantLeafC woodCreation "); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fprintf(out, "soil microbeC coarseRootC fineRootC "); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fprintf(out, "litter soilWater soilWetnessFrac snow "); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fprintf(out, "npp nee cumNEE gpp rAboveground rSoil " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "rRoot ra rh rtot evapotranspiration "); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fprintf(out, "fluxestranspiration minN soilOrgN litterOrgN n2oFlux " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fprintf(out, "fluxestranspiration minN soilOrgN litterOrgN n2oFlux " | |
| fprintf(out, "fluxesTranspiration minN soilOrgN litterOrgN n2oFlux " |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential division by zero: If envi.litterOrgN is zero or very close to zero, the calculation litterCN = envi.litter / envi.litterOrgN on line 1263 will result in division by zero or numerical instability. Similarly for envi.soilOrgN on line 1272. Consider adding validation to ensure these values are positive before performing the division.
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The calculation appears to have an error in the nitrogen flux accounting. The term (fluxes.litterToSoil - fluxes.rSoil) / soilCN incorrectly divides the litter-to-soil carbon flux by the soil C:N ratio. The nitrogen flux from litter to soil should use the litter C:N ratio (litterCN), while only the soil mineralization term (fluxes.rSoil) should use the soil C:N ratio. The expression should be: fluxes.litterToSoil / litterCN - fluxes.rSoil / soilCN.
| fluxes.nOrgSoil = (fluxes.litterToSoil - fluxes.rSoil) / soilCN + | |
| fluxes.nOrgSoil = fluxes.litterToSoil / litterCN - fluxes.rSoil / soilCN + |
Copilot
AI
Dec 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential division by zero: If envi.soilOrgN is zero or very close to zero, the calculation soilCN = envi.soil / envi.soilOrgN will result in division by zero or numerical instability. Consider adding validation to ensure this value is positive before performing the division.
| litterCN = envi.litter / envi.litterOrgN; | |
| fluxes.nOrgLitter = fluxes.leafLitter / params.leafCNRatio + | |
| fluxes.woodLitter / params.woodCNRatio - | |
| fluxes.rLitter / litterCN; | |
| // soil | |
| // The soil org N flux is determined by the carbon flux from the litter pool, | |
| // carbon fluxes from roots, and N loss due to mineralization | |
| // (Note: woodCNRatio is used for coarse roots) | |
| soilCN = envi.soil / envi.soilOrgN; | |
| fluxes.nOrgSoil = (fluxes.litterToSoil - fluxes.rSoil) / soilCN + | |
| fluxes.fineRootLoss / params.rootCNRatio + | |
| fluxes.coarseRootLoss / params.woodCNRatio; | |
| if (fabs(envi.litterOrgN) >= TINY) { | |
| litterCN = envi.litter / envi.litterOrgN; | |
| fluxes.nOrgLitter = fluxes.leafLitter / params.leafCNRatio + | |
| fluxes.woodLitter / params.woodCNRatio - | |
| fluxes.rLitter / litterCN; | |
| } else { | |
| // Avoid division by zero or near-zero litterOrgN; omit mineralization term | |
| fluxes.nOrgLitter = fluxes.leafLitter / params.leafCNRatio + | |
| fluxes.woodLitter / params.woodCNRatio; | |
| } | |
| // soil | |
| // The soil org N flux is determined by the carbon flux from the litter pool, | |
| // carbon fluxes from roots, and N loss due to mineralization | |
| // (Note: woodCNRatio is used for coarse roots) | |
| if (fabs(envi.soilOrgN) >= TINY) { | |
| soilCN = envi.soil / envi.soilOrgN; | |
| fluxes.nOrgSoil = (fluxes.litterToSoil - fluxes.rSoil) / soilCN + | |
| fluxes.fineRootLoss / params.rootCNRatio + | |
| fluxes.coarseRootLoss / params.woodCNRatio; | |
| } else { | |
| // Avoid division by zero or near-zero soilOrgN; omit mineralization term | |
| fluxes.nOrgSoil = fluxes.fineRootLoss / params.rootCNRatio + | |
| fluxes.coarseRootLoss / params.woodCNRatio; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The equation reference is incorrect. The text says "used in Eq. \eqref{eq:cn_dep}" but should reference "\eqref{eq:rh}" (Equation 7) which is where$D_{CN}$ is actually used in the heterotrophic respiration formula.