Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
d9bb2ca
Add restart MVP integration tests and fixtures
dlebauer Feb 22, 2026
3d8243c
Implement strict full-state restart checkpoint MVP
dlebauer Feb 22, 2026
17c609d
Fix CLI argument parsing for file-name and required option args
dlebauer Feb 22, 2026
336c3fa
Refactor restart logic into dedicated module
dlebauer Feb 22, 2026
5194b07
Harden restart load checks and checkpoint failure tests
dlebauer Feb 22, 2026
2b6e8e1
Split restart docs between user and developer guides
dlebauer Feb 22, 2026
70bf334
Switch restart checkpoints to ASCII schema 1.0
dlebauer Feb 23, 2026
aa19995
Make restart schema fully named and update SIPNET_RESTART header
dlebauer Feb 23, 2026
ccea1e9
Format restart text schema sections and accept blank separator lines
dlebauer Feb 23, 2026
0006d1b
Relax restart boundary validation to timestamp keys
dlebauer Feb 23, 2026
7206a51
Switch restart resume contract to post-boundary timestamp
dlebauer Feb 23, 2026
2eda169
Polish restart formatting and align developer guide sequence
dlebauer Feb 23, 2026
a0e37a6
Clarify user-guide restart boundary timestamp wording
dlebauer Feb 23, 2026
84ebdf7
doc fix: dump-config default is off
dlebauer Feb 23, 2026
fca2ef1
Merge branch 'master' into codex/restart-mvp-master
dlebauer Feb 23, 2026
43a4397
Update docs/user-guide/model-inputs.md
dlebauer Feb 24, 2026
8978ab3
Add minimal changelog entry for restart MVP
dlebauer Feb 24, 2026
dfbd746
Remove restart strict flag, add events-file option, and include resta…
dlebauer Feb 27, 2026
625a9aa
Complete Step 1 restart parser robustness
dlebauer Feb 27, 2026
321322e
Add restart build-info mismatch warning acceptance test
dlebauer Feb 27, 2026
27abb02
Enforce midnight restart boundaries
dlebauer Feb 27, 2026
64a1822
Remove restart event cursor/hash state
dlebauer Feb 28, 2026
594a8dc
Move cumulative GDD tracking to trackers for restart continuity
dlebauer Mar 2, 2026
d2b2ad2
Cleanup + Step 6: tests, GDD consolidation, event validation, and sch…
dlebauer Mar 3, 2026
b385710
Step 7: trim restart boundary metadata to timestamp-only fields
dlebauer Mar 3, 2026
8944baa
Step 8: rename mean restart namespace to mean.npp and harden schema l…
dlebauer Mar 3, 2026
719b69f
Docs: align restart and runtime guides with current behavior
dlebauer Mar 3, 2026
b672b81
Step 9: drop balance restart state and reject legacy balance keys
dlebauer Mar 3, 2026
410704e
Harden restart validation and align docs/tests
dlebauer Mar 4, 2026
9b7373a
Merge origin/master and resolve tracker conflicts
dlebauer Mar 4, 2026
421cdfe
Merge branch 'master' into codex/restart-mvp-master
dlebauer Mar 5, 2026
936556d
conditionally call restartNoteProcessedClimateStep
dlebauer Mar 7, 2026
b7a7e61
Apply suggestions from code review
dlebauer Mar 7, 2026
9b290a7
Harden restart checkpoint validation and regression tests
dlebauer Mar 4, 2026
e1ee530
Address Mike's PR 276 restart and events feedback
dlebauer Mar 8, 2026
3200032
Merge origin/master into codex/restart-mvp-master
dlebauer Mar 8, 2026
9ad90a9
Update smoke config baselines for restart defaults
dlebauer Mar 8, 2026
4a33317
Fix broken restart doc example reference
dlebauer Mar 8, 2026
3d1f6f1
Fix merged harvest test expectations
dlebauer Mar 8, 2026
5fb5fee
Fix cpp-linter failure step
dlebauer Mar 8, 2026
c19f5a4
Guard frontend output file close
dlebauer Mar 8, 2026
419a100
Preserve frontend cleanup semantics
dlebauer Mar 8, 2026
788b8b1
Test updates for restart redo
Alomir Mar 12, 2026
1e7423f
Updates for restart
Alomir Mar 12, 2026
56dc673
Added restart exit code
Alomir Mar 12, 2026
b73610e
Move some function from test_restart here
Alomir Mar 12, 2026
3cb1d37
Rearranged tracker vars
Alomir Mar 12, 2026
234225b
Added #def for num model flags
Alomir Mar 12, 2026
c9addc8
Major redo of restart code
Alomir Mar 12, 2026
595db63
Test fixes
Alomir Mar 13, 2026
6daa25a
Added NOLINT to copied header
Alomir Mar 13, 2026
79eccea
Tweaks/reorg for clang-tidy happiness
Alomir Mar 13, 2026
9737e1f
Update error messages for linux static_assert
Alomir Mar 13, 2026
532363c
Attempt to silence clang-tidy
Alomir Mar 13, 2026
3dd5b8e
Merge branch 'master' into codex/restart-mvp-master
Alomir Mar 13, 2026
2b49f44
Add check for no events before first climate
Alomir Mar 13, 2026
3dcbcf5
Add function to check first event vs. climate year/day
Alomir Mar 13, 2026
984dd86
Convert several errors to warnings
Alomir Mar 13, 2026
99f7ae8
Add docs/html
Alomir Mar 13, 2026
e8a92d3
Cleanup
Alomir Mar 13, 2026
8fe00bd
Add no-events guard to isFirstEventBefore
Alomir Mar 13, 2026
3556616
Restart doc updates
Alomir Mar 13, 2026
47a212d
Minor restart doc updates
Alomir Mar 13, 2026
c8cfdf2
Apply minor tweaks, copilot feedback
Alomir Mar 16, 2026
ff2b6dc
Update restart file keys
Alomir Mar 16, 2026
db84146
Merge branch 'master' into codex/restart-mvp-master
Alomir Mar 16, 2026
72ea8e0
Reorg for more clarity
Alomir Mar 16, 2026
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
4 changes: 2 additions & 2 deletions .github/workflows/cpp-linter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ jobs:
- name: analysis
if: steps.linter.outputs.checks-failed > 0
run: |
echo "${{ steps.linter.outputs.checks-failed }} linter checks failed" |

echo "${{ steps.linter.outputs.checks-failed }} linter checks failed"
exit 1
16 changes: 9 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
# build artifacts and executables
# /sipnet instead of just sipnet to NOT ignore test dirs
*.o
*.a
**/*.o
**/*.a
/sipnet
subsetData
transpose
.doxygen.stamp
.mkdocs.stamp
site/*
site_preview/*
_codeql_build_dir/
_codeql_detected_source_root

# documentation
docs/api
docs/latex
docs/html

# Test files
tests/sipnet/*/*
!tests/sipnet/*/*/
!tests/sipnet/*/*.c
!tests/sipnet/*/*.h
!tests/sipnet/*/*.in
!tests/sipnet/*/*.out
!tests/sipnet/*/*.clim
!tests/sipnet/*/*.param
!**/.clang-tidy
# But exclude generated test outputs
tests/sipnet/*/events.out
tests/sipnet/*/sipnet.out
Expand All @@ -34,8 +33,11 @@ tests/sipnet/*/sipnet.config
.Rproj.user
.Rhistory
src/sipnet/.vscode/*
cmake-build-debug-gcc/
.vscode/

# Temporary and backup files
*.bak
*~
*.tmp
*.tmp
/tmp/
9 changes: 8 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ add_library(commonlib
)

add_library(sipnetlib
src/sipnet/balance.c
src/sipnet/cli.c
src/sipnet/events.c
src/sipnet/frontend.c
src/sipnet/outputItems.c
src/sipnet/restart.c
src/sipnet/runmean.c
src/sipnet/sipnet.c
src/sipnet/state.c
src/sipnet/balance.c
)

add_library(tests
Expand All @@ -55,4 +56,10 @@ add_library(tests
tests/sipnet/test_modeling/testNitrogenCycle.c
tests/sipnet/test_modeling/testDependencyFunctions.c
tests/sipnet/test_modeling/testBalance.c
tests/sipnet/test_restart_infrastructure/testRestartMVP.c
tests/sipnet/test_restart_infrastructure/testRestartMissedEnvi.c
tests/sipnet/test_restart_infrastructure/testRestartMissedCtx.c
tests/sipnet/test_restart_infrastructure/mock_state.c
tests/sipnet/test_restart_infrastructure/bad_code/ctx_fail.c
tests/sipnet/test_restart_infrastructure/bad_code/envi_fail.c
)
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ COMMON_CFILES:=context.c logging.c modelParams.c util.c
COMMON_CFILES:=$(addprefix src/common/, $(COMMON_CFILES))
COMMON_OFILES=$(COMMON_CFILES:.c=.o)

SIPNET_CFILES:=sipnet.c cli.c events.c frontend.c outputItems.c runmean.c state.c balance.c
SIPNET_CFILES:=sipnet.c cli.c events.c frontend.c outputItems.c restart.c runmean.c state.c balance.c
SIPNET_CFILES:=$(addprefix src/sipnet/, $(SIPNET_CFILES))
SIPNET_OFILES=$(SIPNET_CFILES:.c=.o)
SIPNET_LIBS=-lsipnet_common
Expand Down
2 changes: 2 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ sections to include in release notes:
### Added

- Build and release binaries for MacOS and Windows on release (in addition to existing Linux builds)
- MVP restart checkpoints for segmented runs (`RESTART_IN` / `RESTART_OUT`) (#279)
- Configurable events prefix for `<prefix>.in` / `<prefix>.out`
- Support for tillage events (#158)
- `woodCreation` as output (#161)
- Soil mineral pool (#170)
Expand Down
94 changes: 94 additions & 0 deletions docs/developer-guide/restart-checkpoint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Restart Checkpoint Spec

This page documents SIPNET's restart checkpoint implementation.

## Scope and Intent

SIPNET restart is designed for segmented orchestration:

- stop at end of one climate segment
- write full runtime state at segment end (`RESTART_OUT`)
- restore full runtime state at next segment start (`RESTART_IN`)
- fail fast on incompatible restart/configuration inputs

SIPNET itself does not stitch outputs across segments.

## Runtime Sequence

On resume, SIPNET executes:

1. Normal setup (`setupModel`, `setupEvents`)
2. Load checkpoint and overwrite runtime state
3. Validate compatibility checks and restart boundary checks
4. Continue run from resumed climate input

## Restart Schema v1.0 Overview

Checkpoint format is ASCII text with one key/value per line:

- header: `SIPNET_RESTART 1.0`
- metadata: `meta_info.model_version`, `meta_info.build_info`, `meta_info.checkpoint_utc_epoch`, `meta_info.processed_steps`
- schema layout guard metadata: `schema_layout.envi_size`, `schema_layout.trackers_size`, `schema_layout.phenology_trackers_size`, `schema_layout.event_trackers_size`
- mode flags: `flags.*`
- boundary metadata: `boundary.year`, `boundary.day`, `boundary.time`, `boundary.length`
- mean tracker metadata: `mean.npp.*`
- full runtime state: `envi.*`, `trackers.*`, `phenology.*`, `event_trackers.*`
- mean ring buffers: `mean.npp.values.<idx>`, `mean.npp.weights.<idx>`
- end marker: `end_restart 1`

Example checkpoint content is exercised in
`tests/sipnet/test_restart_infrastructure/testRestartMVP.c`.

## Validation Contract

On load, SIPNET enforces the following. Lines that start with (warning) log a warning and do not error.

- magic header match
- schema version match
- model numeric version match
- `schema_layout.*` values exactly match the expected struct sizes for the running build
- (warning) build info mismatch
- context flag compatibility
- first-row climate timestamp strictly after checkpoint boundary (`year`, `day`, `time`)
- (warning) resumed segment starts on the midnight-following day and within one timestep after midnight
- mean tracker shape/cursor validity
- All lines appearing after `end_restart` are ignored
- integer values must fit in signed 32-bit range
- floating-point values must be finite (`nan`/`inf` are rejected)

All mismatches above are hard errors except as indicated.

## Climate and Event Boundaries

Restart writes always emit a checkpoint. If the last processed climate step is
more than one timestep before midnight, SIPNET logs a warning, as there will be a time gap in any resumption from that
file.

Resumed climate segments must begin on the day after the checkpoint boundary. If they start more than one timestep
after midnight (using the first resumed climate row's timestep length) SIPNET logs a warning.

Event files must be segmented to the same time boundaries as climate segments.

## When Saved State Changes

If you add saved state or change an existing saved payload:

1. Update the serialized payload type and restart read/write logic in `src/sipnet/restart.c`.
2. Update the `RESTART_SCHEMA_LAYOUT_*` constants, static asserts, and runtime schema-layout validation.
3. Update restart docs/tests and bump `RESTART_SCHEMA_VERSION`.

## Struct Drift Guards

Restart schema v1.0 includes compile-time and runtime drift guards so struct layout changes cannot silently pass:

- Compile-time guards: `_Static_assert` checks in `src/sipnet/restart.c` for `Envi`, `Trackers`, `PhenologyTrackers`, `EventTrackers`, and expected number of model flags in `Context`.
- Runtime guards: `schema_layout.*` fields in each checkpoint are validated on load.
- Test guardrails: `tests/sipnet/test_restart_infrastructure/testRestartMVP.c` verifies schema layout keys are present and rejects tampered values.

## Schema Bump Checklist

When intentionally changing the restart schema version:

1. Update `src/sipnet/restart.c` in all schema touchpoints: `RESTART_SCHEMA_VERSION`, `RESTART_SCHEMA_LAYOUT_*`, `_Static_assert` layout guards, and checkpoint read/write + required-key validation logic.
2. Update restart examples/fixtures to the new header and key set, including the restart fixtures in `tests/sipnet/test_restart_infrastructure/testRestartMVP.c`.
3. Update docs that name schema version or key expectations: `docs/developer-guide/restart-checkpoint.md` and `docs/user-guide/running-sipnet.md`.
28 changes: 17 additions & 11 deletions docs/user-guide/model-inputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ loc year day time length tair tsoil par precip vpd vpdSoil vPress wspd

## Agronomic Events

Agronomic (management) events are read from an `events.in` file. This file specifies one event per line:
Agronomic (management) events are read from `events.in` by default, or from
`<EVENTS_PREFIX>.in` when `EVENTS_PREFIX` / `--events-prefix` is set. This
file specifies one event per line:

| col | parameter | description | units | notes |
| --- | ----------- | ------------------------------------ | ------ | ------------------------------------------------ |
Expand Down Expand Up @@ -207,21 +209,25 @@ Thus, command-line arguments override settings in the configuration file, and co

### Input / Output Options

| Option | Default | Description |
| ------------ | --------- | ------------------------------------- |
| `input-file` | sipnet.in | Name of input config file |
| `file-name` | sipnet | Prefix of climate and parameter files |
| Option | Default | Description |
| --------------- | --------- | -------------------------------------------------- |
| `input-file` | sipnet.in | Name of input config file |
| `file-name` | sipnet | Prefix of climate and parameter files |
| `events-prefix` | events | Prefix for events input/output files (`<name>.in`, `<name>.out`) |
| `restart-in` | unset | Path to restart checkpoint to load |
| `restart-out` | unset | Path to restart checkpoint to write |

### Output Flags

| Option | Default | Description |
|---------------------|---------|----------------------------------------------------------------|
| `do-main-output` | on | Print time series of all output variables to `<file-name>.out` |
| `do-single-outputs` | off | Print outputs one variable per file (e.g. `<file-name>.NEE`) |
| `dump-config` | on | Print final config to `<file-name>.config` |
| `do-single-outputs` | off | Print selected outputs only (`NEE`, `NEE_cum`, `GPP`, `GPP_cum`) one variable per file (e.g. `<file-name>.NEE`) |
| `dump-config` | off | Print final config to `<file-name>.config` |
| `print-header` | on | Whether to print header row in output files |
| `quiet` | off | Suppress info and warning message |


### Model Flags

| Option | Default | Description |
Expand Down Expand Up @@ -260,24 +266,24 @@ See `sipnet --help` for a full list of available command-line options.
SIPNET reads a configuration file that specifies run-time options without using command-line arguments. By default, SIPNET looks for a file named `sipnet.in` in the current directory. These will be overwritten by command-line arguments if specified.

The configuration file uses a simple key-value format, `option = value`,
with one option per line; comments follow `#`. Flags are specified as 0 for off and 1 for on.
with one option per line; comments follow `!`. Flags are specified as 0 for off and 1 for on.

#### Example Configuration File

Note that case is ignored for parameter names, as well as dashes and underscores.

```
# Base filename (used for derived filenames)
! Base filename (used for derived filenames)
FILE_NAME = mysite

# Output options
! Output options
DO_MAIN_OUTPUT = 1
DO_SINGLE_OUTPUTS = 0
DUMP_CONFIG = 1
PRINT_HEADER = 1
QUIET = 0

# Model options
! Model options
EVENTS = 1
GDD = 1
GROWTH_RESP = 0
Expand Down
11 changes: 7 additions & 4 deletions docs/user-guide/model-outputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

There are two main output files generated by SIPNET:
1. `sipnet.out`: Model state variables and fluxes at each timestep.
2. `events.out`: Contains a record of agronomic events processed during the simulation (if event handling is enabled).
2. `events.out` (name configurable): Contains a record of events processed during the simulation if event handling is enabled.

## Model Outputs

Expand Down Expand Up @@ -59,18 +59,21 @@ year day time plantWoodC plantLeafC woodCreation soil coarseRootC fineRoot

## Events output

When event handling is enabled, SIPNET will create a file named `events.out`.
When event handling is enabled, SIPNET will create `events.out` by default, or
`<EVENTS_PREFIX>.out` when a custom events prefix is configured.

This file is designed primarily for _testing and debugging_.
It contains one row for each agronomic event that is processed.
Each row lists the year, day, event type, and parameter name/value pairs.
The name/value pairs represent the state variables that are directly changed by an event, recording the change (delta) applied to each.

Information in `events.out` can, in principle, be reconstructed or inferred from `events.in` and `sipnet.out` though this may be confounded if simultaneous events affect the same variable.
Information in the events output file can, in principle, be reconstructed or
inferred from the corresponding events input file and `sipnet.out` though this
may be confounded if simultaneous events affect the same variable.

Still, _`sipnet.out` is the authoritative source_ for information about system state and evolution in time, including responses to events.

Below is an example `events.out`, with header enabled for clarity.
Below is an example events output file, with header enabled for clarity.
Note the delimiters: spaces separate columns, commas separate name/value pairs, and `=` map names with their values (deltas).

```
Expand Down
Loading
Loading