Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e408000
Switches events to flux model, ensures smoke test output has not changed
Alomir Aug 25, 2025
3ef5df4
Updates to keep output unchanged
Alomir Aug 25, 2025
3b3f85e
Updates event handler for proper flux handling
Alomir Aug 26, 2025
340e870
Updates event handler for proper flux handling
Alomir Aug 26, 2025
d5d16a5
Updates for tests for retool-events-as-fluxes
Alomir Aug 26, 2025
8c71672
Enables exclusion check for events/microbes
Alomir Aug 26, 2025
4f101d5
Minor comment updates
Alomir Aug 27, 2025
dc358d8
Expand irrigation and water dynamics documentation; clarify event pro…
dlebauer Aug 28, 2025
9252451
Merge branch 'master' into SIP147-retool-events-as-fluxes
dlebauer Aug 28, 2025
528f511
Merge branch 'SIP147-retool-events-as-fluxes' into 148_doc_updates
dlebauer Aug 28, 2025
ac331b7
formatting
dlebauer Aug 28, 2025
30061dd
more formatting
dlebauer Aug 28, 2025
1d99aac
Merge branch 'master' into 148_doc_updates
dlebauer Aug 28, 2025
8e0ccb8
Update docs/.alternate-model-structure-ideas.md
dlebauer Aug 28, 2025
d227753
fixed typos and updated doc of drainage
dlebauer Aug 28, 2025
d59b5db
Merge branch '148_doc_updates' of github.com:pecanproject/sipnet into…
dlebauer Aug 28, 2025
f68674f
update typo F_vol
dlebauer Aug 28, 2025
fae1509
typos
dlebauer Sep 15, 2025
4e69876
Add DOI badge to README
dlebauer Sep 17, 2025
dcb7636
Add documentation for model outputs, parameters, logging; update navi…
dlebauer Sep 22, 2025
da00346
Merge branch '148_doc_updates' of github.com:pecanproject/sipnet into…
dlebauer Sep 22, 2025
1186237
Merge branch 'master' into 148_doc_updates
dlebauer Sep 22, 2025
a80f449
148 even more doc updates (#159)
dlebauer Oct 11, 2025
0136fa8
Tweaks and updates
Alomir Oct 14, 2025
b003664
Removes duplicate doc
Alomir Oct 16, 2025
820e067
Minor tweaks
Alomir Oct 16, 2025
589100d
Minor tweaks
Alomir Oct 16, 2025
0801395
Merge branch 'master' into 148_doc_updates
Alomir Oct 16, 2025
cd3fec2
Make turnover params consistently per-year
Alomir Oct 17, 2025
542812c
Merge branch 'master' into 148_doc_updates
Alomir Oct 17, 2025
b838c27
Dummy action when linter not triggered
Alomir Oct 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 28 additions & 19 deletions docs/.alternate-model-structure-ideas.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,16 @@ $$

Heterotrophic respiration ($R_h$) is a function of soil carbon content ($C_S$), a scaling factor ($K_h$), the $Q_{10}$ factor, and the ratio of soil moisture ($W$) to water holding capacity ($W_e$).

## Flooding
## Irrigation

### Moisture based irrigation

Currently, irrigation is specified as an amount.

Additional approach based on farmer behavior:
To represent an irrigation program that sets a moisture range and turns irrigation on at a lower moisture threshold and of at an upper threshold. This could be done internally by directly changing the soil moisture content - while keeping track of the amount of water added to report in events.out.

### Flooding

There are multiple options for representing flooding.

Expand All @@ -42,17 +51,17 @@ K could be a fixed value like 2 or 10. Or a site level parameter if warranted (e

### Variables (Pools, Fluxes, and Parameters)

| Symbol | Description |
|----------------|-----------------------------------------------------------------------------|
| $$ R_{\text{leach,NO3}} $$ | Rate of nitrate leaching |
| $$ R_{\text{NH4,fert}} $$ | Rate of ammonium fertilization input |
| $$ R_{\text{NO3,fert}} $$ | Rate of nitrate fertilization input
| Symbol | Description |
| -------------------------- | ------------------------------------ |
| $$ R_{\text{leach,NO3}} $$ | Rate of nitrate leaching |
| $$ R_{\text{NH4,fert}} $$ | Rate of ammonium fertilization input |
| $$ R_{\text{NO3,fert}} $$ | Rate of nitrate fertilization input |

### Subscripts (Temporal, Spatial, or Contextual Identifiers)
| Subscript | Description |
|--------------- |----------------------------------------------------------------------------- |
| $$ _\text{nitr} $$ | nitrification |
| $$ _\text{denitr} $$ | denitrification
| Subscript | Description |
| -------------------- | --------------- |
| $$ _\text{nitr} $$ | nitrification |
| $$ _\text{denitr} $$ | denitrification |


### Soil Ammonium
Expand Down Expand Up @@ -363,15 +372,15 @@ Some options considered:
- Adapt the parabolic $D_\text{temp, A}$ from SIPNET (Braswell et al 2005)


| Function | Formula | Parameters | Notes |
|-------------------------------------|-----------------------------------------------------------------------------------------|-------------------------------------|------------------------------------------------------------------------------------------------|
| **Beta Function** | $\left( \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)^\beta \cdot \left( 1 - \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)^\gamma$ | 4 ($\beta, \gamma, f_{\text{WHC,min}}, f_{\text{WHC,max}}$) | Scaled to [0, 1] within $f_{\text{WHC,min}}$ and $f_{\text{WHC,max}}$. Peak shape controlled by $\beta$ and $\gamma$. |
| **Gaussian Function** | $e^{-\frac{\left( \frac{f_{\text{WHC}} - \mu}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)^2}{2\sigma^2}}$ | 3 ($\mu, \sigma, f_{\text{WHC,max}}, f_{\text{WHC,min}}$) | Fixed maximum of 1. Centered at $\mu$, scaled to $f_{\text{WHC,min}}$ and $f_{\text{WHC,max}}$. Symmetric around $\mu$. |
| **Piecewise Linear Function** | $\begin{cases} m_1 \left( \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right) + c_1, & f_{\text{WHC}} \leq f_{\text{WHC,mid}} \\ m_2 \left( \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right) + c_2, & f_{\text{WHC}} > f_{\text{WHC,mid}} \end{cases}$ | 6 ($m_1, c_1, m_2, c_2, f_{\text{WHC,min}}, f_{\text{WHC,max}}$) | Two linear segments split at $f_{\text{WHC,mid}}$. Scaled to $f_{\text{WHC,min}}$ and $f_{\text{WHC,max}}$. |
| **Double Exponential Function** | $a_1 e^{b_1 \left( \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)} + a_2 e^{b_2 \left( \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)}$ | 4 ($a_1, b_1, a_2, b_2$) | Scaled exponential components to $f_{\text{WHC,min}}$ and $f_{\text{WHC,max}}$. |
| **Piecewise Double Exponential** | $\begin{cases} (1 - e^{-k_1(f_{\text{WHC}} - f_{\text{WHC,min}})}), & f_{\text{WHC}} \leq f_{\text{WHC,opt}} \\ e^{-k_2(f_{\text{WHC}} - f_{\text{WHC,opt}})}, & f_{\text{WHC}} > f_{\text{WHC,opt}} \end{cases}$ | 4 ($k_1, k_2, f_{\text{WHC,opt}}, f_{\text{WHC,min}}$) | Piecewise rise (left of $f_{\text{WHC,opt}}$) and asymmetric exponential decay (right). Scaled to range. |
| **Double Logistic Function** | $\frac{1}{1 + e^{-k_1 \left( \frac{f_{\text{WHC}} - x_1}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)}} + \frac{1}{1 + e^{-k_2 \left( \frac{f_{\text{WHC}} - x_2}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)}}$ | 6 ($k_1, x_1, k_2, x_2, f_{\text{WHC,min}}, f_{\text{WHC,max}}$) | Scaled logistic transitions split at $x_1$ and $x_2$. Scaled to $f_{\text{WHC,min}}$ and $f_{\text{WHC,max}}$. |
| **Parabolic (SIPNET)** | $a \left( \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} - b \right)^2 + c$ | 5 ($a, b, c, f_{\text{WHC,min}}, f_{\text{WHC,max}}$) | Scaled to $f_{\text{WHC,min}}$ and $f_{\text{WHC,max}}$. Derived from SIPNET temperature-response function. |
| Function | Formula | Parameters | Notes |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| **Beta Function** | $\left( \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)^\beta \cdot \left( 1 - \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)^\gamma$ | 4 ($\beta, \gamma, f_{\text{WHC,min}}, f_{\text{WHC,max}}$) | Scaled to [0, 1] within $f_{\text{WHC,min}}$ and $f_{\text{WHC,max}}$. Peak shape controlled by $\beta$ and $\gamma$. |
| **Gaussian Function** | $e^{-\frac{\left( \frac{f_{\text{WHC}} - \mu}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)^2}{2\sigma^2}}$ | 3 ($\mu, \sigma, f_{\text{WHC,max}}, f_{\text{WHC,min}}$) | Fixed maximum of 1. Centered at $\mu$, scaled to $f_{\text{WHC,min}}$ and $f_{\text{WHC,max}}$. Symmetric around $\mu$. |
| **Piecewise Linear Function** | $\begin{cases} m_1 \left( \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right) + c_1, & f_{\text{WHC}} \leq f_{\text{WHC,mid}} \\ m_2 \left( \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right) + c_2, & f_{\text{WHC}} > f_{\text{WHC,mid}} \end{cases}$ | 6 ($m_1, c_1, m_2, c_2, f_{\text{WHC,min}}, f_{\text{WHC,max}}$) | Two linear segments split at $f_{\text{WHC,mid}}$. Scaled to $f_{\text{WHC,min}}$ and $f_{\text{WHC,max}}$. |
| **Double Exponential Function** | $a_1 e^{b_1 \left( \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)} + a_2 e^{b_2 \left( \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)}$ | 4 ($a_1, b_1, a_2, b_2$) | Scaled exponential components to $f_{\text{WHC,min}}$ and $f_{\text{WHC,max}}$. |
| **Piecewise Double Exponential** | $\begin{cases} (1 - e^{-k_1(f_{\text{WHC}} - f_{\text{WHC,min}})}), & f_{\text{WHC}} \leq f_{\text{WHC,opt}} \\ e^{-k_2(f_{\text{WHC}} - f_{\text{WHC,opt}})}, & f_{\text{WHC}} > f_{\text{WHC,opt}} \end{cases}$ | 4 ($k_1, k_2, f_{\text{WHC,opt}}, f_{\text{WHC,min}}$) | Piecewise rise (left of $f_{\text{WHC,opt}}$) and asymmetric exponential decay (right). Scaled to range. |
| **Double Logistic Function** | $\frac{1}{1 + e^{-k_1 \left( \frac{f_{\text{WHC}} - x_1}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)}} + \frac{1}{1 + e^{-k_2 \left( \frac{f_{\text{WHC}} - x_2}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} \right)}}$ | 6 ($k_1, x_1, k_2, x_2, f_{\text{WHC,min}}, f_{\text{WHC,max}}$) | Scaled logistic transitions split at $x_1$ and $x_2$. Scaled to $f_{\text{WHC,min}}$ and $f_{\text{WHC,max}}$. |
| **Parabolic (SIPNET)** | $a \left( \frac{f_{\text{WHC}} - f_{\text{WHC,min}}}{f_{\text{WHC,max}} - f_{\text{WHC,min}}} - b \right)^2 + c$ | 5 ($a, b, c, f_{\text{WHC,min}}, f_{\text{WHC,max}}$) | Scaled to $f_{\text{WHC,min}}$ and $f_{\text{WHC,max}}$. Derived from SIPNET temperature-response function. |

## Effect of SOM on Soil Water Dynamics

Expand Down
3 changes: 3 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ SIPNET (Simplified Photosynthesis and Evapotranspiration Model) is an ecosystem
carbon and water dynamics. Originally developed for assimilation of eddy covariance flux data in forest ecosystems,
current development is focused on representing carbon balance and GHG fluxes and agricultural management practices.

[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.17148669.svg)](https://doi.org/10.5281/zenodo.17148669)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice!



## Quick Start

1. Clone the repository:
Expand Down
87 changes: 87 additions & 0 deletions docs/developer-guide/code-structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Code Structure

This guide documents how state is advanced each timestep and the conventions that keep flux calculations pure and pool updates centralized.

## Timestep Phases (in `updateState()`)

1) Initialize fluxes
- Zero all `fluxes.*` and `fluxes.event*`.

2) Compute fluxes (pure calculations)
- `calculateFluxes()` computes photosynthesis, respiration, water/snow, etc.
- `processEvents()` converts scheduled/instant events to `fluxes.event*` deltas (no pool mutation).
- `soilDegradation()` and other biogeochemical modules compute additional flux rates only (no pool mutation).
- No function in this phase mutates `envi.*` or `trackers.*`.

3) Apply pool updates (single place)
- `applyPoolUpdates()` is the only code that changes `envi.*`.
- For each pool P: ΔP = (sum of rate fluxes to P) * climate.length + (sum of `fluxes.event*` deltas to P).
- Apply bounds, conservation, and cross-pool constraints here.

4) Trackers and running means
- `updateTrackers()` uses timestep-integrated values (rate * climate.length) plus event deltas.

5) Output
- `outputState()` and any optional diagnostics/logging.

### Pseudocode outline

- updateState():
- zeroFluxes()
- calculateFluxes() // pure rates
- processEvents() // sets fluxes.event* deltas only
- soilDegradation() // pure rates
- applyPoolUpdates() // the only place that mutates envi.*
- updateTrackers()
- outputState()

## Mutability Rules (must-follow)

- Only `applyPoolUpdates()` may change `envi.*`.
- Flux calculators:
- May read `envi.*`, `params.*`, `ctx.*`, `climate.*`.
- May write `fluxes.*` (rates) and `fluxes.event*` (event deltas).
- Must not mutate `envi.*`, `trackers.*`, or perform I/O as logic side-effects.
- Events never change pools directly; they only add to `fluxes.event*`.

## Units and Integration

- Rate fluxes in `fluxes.*` are per-day rates (pool units per day).
- Event deltas in `fluxes.event*` are direct pool deltas (same units as pools), not rates.
- Integration per pool each timestep:
- Δpool_from_rates = (sum of relevant `fluxes.*`) * climate.length
- Δpool_from_events = (sum of relevant `fluxes.event*`)
- pool += Δpool_from_rates + Δpool_from_events

## Naming Conventions

- envi.* State variables (pools, water, snow, canopy, soil layers).
- fluxes.* Per-day flux rates computed in the flux phase.
- fluxes.event* Instantaneous/event deltas to be applied during pool update.
- trackers.* Integrated timestep values, cumulative sums, yearly aggregates.
- params.* Fixed run parameters (immutable during a run).
- ctx.* Feature flags / configuration switches.
- climate.* Forcing for the current timestep (e.g., length, met drivers).
- diag.* Optional transient diagnostics (no side effects on state).

Name fluxes by direction and target, e.g., `fluxes.NPP`, `fluxes.soilRespiration`, `fluxes.leafLitterToSoil`, `fluxes.eventHarvestC`. Prefer “to/from” clarity for transfers.

## Pool Update Responsibilities

- Apply all additions/removals in a consistent order if constraints require it (e.g., water first if it bounds biochemical rates next step).
- Enforce invariants:
- No negative pools; clamp with tracked deficits and warnings if needed.
- Mass conservation across linked pools (e.g., C/N stoichiometry) with balanced cross-pool transfers.
- Centralize any event-specific application here (e.g., harvest removing biomass, adding residues).

## Adding a New Flux or Event

- Rates: add a `fluxes.*` variable, compute it in a flux function, and integrate it in `applyPoolUpdates()`.
- Events: add a `fluxes.event*` delta, accumulate in `processEvents()`, apply it in `applyPoolUpdates()`.
- Do not mutate `envi.*` in calculators or event processors.

## Logging & Errors

- Use `logError()` and `logWarning()` (not printf) so tests can capture output.
- Messages should include timestep context: year, day, event type (if relevant), and the offending value(s).
- Emit warnings on clamping, conservation corrections, or unexpected negative fluxes.
54 changes: 34 additions & 20 deletions docs/developer-guide/logging.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,46 @@
# Logging

SIPNET's logger is defined in `common/logger.h` and implemented in `common/logger.c`.
SIPNET's logger is a small wrapper around `printf` that adds standard prefixes and, for internal errors, the source file and line number. It is defined in `common/logging.h` and implemented in `common/logging.c`.

It provides a simple interface for logging messages at different levels (e.g., debug, info, warning, error).
## Levels

The use of logger functions is preferred over `printf` because ...
It is appropriate to use printf when ...

## Logging Levels

- **logDebug**: Information useful during development or debugging.
- **logInfo**: General information about the program's execution, such as successful initialization or key milestones.
- **logWarning**: Non-critical issues that might require attention but do not stop execution. Example: deprecated parameters or ignored input.
- **logError**: Critical issues that prevent the program from continuing correctly. Example: missing required parameters or internal errors.
- 0: Quiet-able (suppressed by `--quiet`).
- 1: Always on (not suppressed).
- 2: Always on and includes `file:line`.


## Usage

1. Include the logger header in your source file:
- logInfo: Level 0; routine progress, configuration summaries, expected state changes.
- logWarning: Level 0; Recoverable issues or surprises; fallbacks, deprecated/ignored inputs.
- logTest: Level 1; Deterøinistic messages for tests/CI; not user-facing.
- logError: Level 1; Non-recoverable problems preventing correct operation; abort/exit or skip major task.
- logInternalError: Level 2; Errors that should never happen; include details and ask to report.

1. Include the header:
```c
#include "common/logger.h"
#include "common/logging.h"
```

2. Use the logging functions to log messages at different levels:
2. Log messages:
```c
// Log messages at different levels
logDebug("This is a debug message");
logInfo("This is an info message");
logWarning("This is a warning message");
logError("This is an error message");
logInfo("Initialized OK\n");
logWarning("Deprecated parameter: %s\n", name);
logTest("Iteration %d\n", i);
logError("Missing required parameter: %s\n", key);
logInternalError("Unexpected state: %d\n", code);
```
3. Example outputs:
```
[INFO ] Initialized OK
[WARNING] Deprecated parameter: foo
[TEST ] Iteration 12
[ERROR ] Missing required parameter: bar
[ERROR (INTERNAL)] (myfile.c:123) Unexpected state: 5
```


## Notes:

- Each log prints a fixed prefix (e.g., `[INFO ]`, `[WARNING]`, `[ERROR ]`).
- Messages use `printf`-style formatting. Include `\n` yourself if you want a newline.
- Level 2 (`logInternalError`) prints file:line; levels 0–1 print just the prefix.
Loading