Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
239ab13
agents: limit use of pydantic to data validation
lrntct Sep 25, 2026
93b85b8
first draft
lrntct Sep 26, 2026
d9ed06c
split ensemble in three python files
lrntct Sep 26, 2026
874e679
simplify CSV import
lrntct Sep 26, 2026
22f90e5
fix imports, and remove TypedDict in providers
lrntct Sep 26, 2026
5a0bb05
use get() in configreader
lrntct Sep 26, 2026
c86464f
remove unused hotstart, infiltration label, and ensemble transport
lrntct Sep 26, 2026
2707981
simplify ConfigReader
lrntct Sep 26, 2026
7067f39
remove unused paths
lrntct Sep 26, 2026
6fc6743
use HH:MM:SS for durations
lrntct Sep 27, 2026
e405df3
make sure stat file names are rendered, and reduce digest size
lrntct Sep 27, 2026
18db9f4
resolve ensemble per ensemble
lrntct Sep 27, 2026
3d61ab0
resolve ensemble per ensemble
lrntct Sep 27, 2026
518b7e3
fix grass env for exec
lrntct Sep 27, 2026
a98ed7d
change manifest file name and save it to yaml
lrntct Sep 27, 2026
593e032
order manifest
lrntct Sep 27, 2026
3eb0203
add datetime to manifest
lrntct Sep 27, 2026
f956b71
move manifest functions into their own file
lrntct Sep 27, 2026
ba993ea
simplify ensemble validation
lrntct Sep 27, 2026
405660f
simplify argument parsing in tests
lrntct Sep 27, 2026
d8714bb
simplify ensemble resolution and manifest update
lrntct Sep 27, 2026
c184523
simplify grass input and interface
lrntct Sep 27, 2026
9179243
use ProcessPoolExecutor instead of manual processes
lrntct Sep 27, 2026
274bab2
remove Any and object type annotations
lrntct Sep 27, 2026
590cb95
ConfigReader: build SimulationConfig directly
lrntct Sep 27, 2026
105abfe
more explicit dry-run cli argument
lrntct Sep 27, 2026
0becaec
add copyright notice to csv writer
lrntct Sep 27, 2026
f8761b4
AGENTS: clearer mapset change description
lrntct Sep 27, 2026
26cd4ad
improve bmi class definitions
lrntct Sep 28, 2026
9f194d7
use itzi_core imports in configreader
lrntct Sep 28, 2026
af360a1
reorder imports in __init__
lrntct Sep 28, 2026
f5c62b8
use itzi_core imports in enememble_schema and _models
lrntct Sep 28, 2026
4ea231b
add preflight checks
lrntct Sep 28, 2026
4f42946
make all preflight checks in a single subprocess per ensemble
lrntct Sep 28, 2026
7a6a724
agents: immutable objects and pygrass
lrntct Sep 28, 2026
b7e3d5f
split out one ensemble run in its own function
lrntct Sep 28, 2026
5a17d93
display 2 digits for percents
lrntct Sep 29, 2026
8b409b5
rename functions
lrntct Sep 29, 2026
3eebcf8
add integration tests
lrntct Sep 29, 2026
042fd10
add tests for python 3.14 and 3.15
lrntct Sep 29, 2026
2fa8c73
finalize bmi object in the tests
lrntct Sep 29, 2026
fae5d72
fix test tutorial
lrntct Sep 29, 2026
76ee4a8
fix the init of ensemble and input tests
lrntct Sep 29, 2026
1a2706d
remove 3.15 from test matrix
lrntct Sep 29, 2026
4d7d209
move grass code into grass_interface
lrntct Sep 29, 2026
e2ad592
move grass fucntions out of GrassInterface, and simplify them
lrntct Sep 29, 2026
3390cf6
reorganize imports
lrntct Sep 29, 2026
f89df38
add copyright notice
lrntct Sep 29, 2026
bb6d045
update ty
lrntct Sep 29, 2026
6e79a6b
refactor: move grass and ensemble into their own submodules
lrntct Sep 29, 2026
aad3a73
move colortable data in the grass submodule
lrntct Sep 29, 2026
162e6d1
remove nullable dict entries
lrntct Sep 29, 2026
bea7055
stronger typing in GrassSession
lrntct Sep 29, 2026
a0b9214
organize imports
lrntct Sep 29, 2026
6518323
various simplifications
lrntct Sep 29, 2026
b59d004
check map keys at input and output
lrntct Sep 29, 2026
bb6d2e1
Simplify validation
lrntct Sep 30, 2026
1b9bcfb
fix providers
lrntct Sep 30, 2026
c325fcf
use only one worker for preflight. Remove background raster writer
lrntct Sep 30, 2026
2a2f747
fix type checking error
lrntct Sep 30, 2026
a87d5bc
simplify code
lrntct Sep 30, 2026
f0a0289
remove exception caching from input resolution and remove has_mask
lrntct Sep 30, 2026
7c6f619
fix cli tests
lrntct Sep 30, 2026
d93941f
fix typing errors in test, and init tgis in conftest
lrntct Sep 30, 2026
847176b
fix tutorial tests
lrntct Sep 30, 2026
fc92781
put validation in GrassParams
lrntct Sep 30, 2026
5de9d97
input_kinds should be supplied by the caller, simplify cache, and use…
lrntct Sep 30, 2026
e094429
pass EffectiveMask directly, remove format_id
lrntct Sep 30, 2026
b023595
remove unused init re-exports
lrntct Sep 30, 2026
fb1611d
rename preflight to run_validation
lrntct Sep 30, 2026
206f49d
consolidate ensemble tests
lrntct Sep 30, 2026
c7d0126
assert that green ampt keys are part of input keys
lrntct Sep 30, 2026
21f6352
rename options to parameters
lrntct Sep 30, 2026
05ecced
print an error message instead of raw traceback
lrntct Sep 30, 2026
9ecc5df
rename ensemble.name to ensemble.description. Limit ensemble.id to 80…
lrntct Sep 30, 2026
67de8b6
always display elapsed_seconds after status in the manifest. Remove t…
lrntct Sep 30, 2026
8335eb9
fold DomainConfig and GrassContextConfig under GrassConfig
lrntct Sep 30, 2026
e5895a3
remove unused digest calculations
lrntct Sep 30, 2026
ee10cdb
in case of simulation failing, finalize() si called within the grass env
lrntct Sep 30, 2026
18a8a8f
update documentation
lrntct Oct 1, 2026
d871d4e
drainage coefficients are optional
lrntct Oct 1, 2026
35d490b
AGETNS: do no duplicate pydantic declarative validation
lrntct Oct 1, 2026
e2600db
remove exogenous doc
lrntct Oct 1, 2026
13f3e8a
reorganize imports, and fold write_raster_map_blocking into write_ras…
lrntct Oct 1, 2026
faf1627
fix test after removing write_raster_map_blocking
lrntct Oct 1, 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
13 changes: 7 additions & 6 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
test:
strategy:
matrix:
python-version: ["3.12", "3.13"]
python-version: ["3.12", "3.13", "3.14"]

runs-on: ubuntu-latest
container: osgeo/grass-gis:releasebranch_8_4-ubuntu
Expand All @@ -31,11 +31,12 @@ jobs:

- name: Pytest
run: |
$HOME/.local/bin/uv run --all-extras --python ${{ matrix.python-version }} pytest --cov=src --junitxml=junit/test-cli-results.xml --cov-report=xml --cov-report=html tests/cli tests/test_grass_session.py
$HOME/.local/bin/uv run --all-extras --python ${{ matrix.python-version }} pytest --cov=src --junitxml=junit/test-itzi-results.xml --cov-report=xml --cov-report=html tests/grass/test_itzi.py
$HOME/.local/bin/uv run --all-extras --python ${{ matrix.python-version }} pytest --cov=src --junitxml=junit/test-bmi-results.xml --cov-report=xml --cov-report=html tests/grass/test_bmi.py
$HOME/.local/bin/uv run --all-extras --python ${{ matrix.python-version }} pytest --cov=src --junitxml=junit/test-tutorial-results.xml --cov-report=xml --cov-report=html tests/grass/test_tutorial.py
$HOME/.local/bin/uv run --all-extras --python ${{ matrix.python-version }} pytest --cov=src --junitxml=junit/test-temporal-overwrite-results.xml --cov-report=xml --cov-report=html tests/grass/test_temporal_overwrite.py
$HOME/.local/bin/uv run --all-extras --python ${{ matrix.python-version }} pytest --cov=src --cov-report= --junitxml=junit/test-cli-results.xml tests/cli tests/test_grass_session.py
for test_file in tests/grass/test_*.py; do
$HOME/.local/bin/uv run --all-extras --python ${{ matrix.python-version }} pytest --cov=src --cov-append --cov-report= --junitxml="junit/$(basename "$test_file" .py)-results.xml" "$test_file"
done
$HOME/.local/bin/uv run --all-extras --python ${{ matrix.python-version }} coverage xml
$HOME/.local/bin/uv run --all-extras --python ${{ matrix.python-version }} coverage html

- name: Upload coverage
uses: actions/upload-artifact@v7
Expand Down
16 changes: 10 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

## Common commands
- Run a single test: `uv run pytest tests/my_test.py`
- Due to a bug in GRASS, tests will fails if the mapset is changed between tests. Run each test file independently to prevent this. Tests that need to run in a separate process are marked with `@pytest.mark.forked`.
- Enforce code formatting: `uvx ruff format .`
- Due to a bug in GRASS<8.5, a change of mapset cannot occur within the same process when using the temporal framework. Each test file must therefore be run independently. Tests that need to run in a separate process are marked with `@pytest.mark.forked`.
- Enforce code formatting: `uv run ruff format .`

## Code style
- Use python type hints. When a function that does not yet use hints is substantially edited, take the opportunity to add type hints.
- Do not quote class names in hints. Use `from __future__ import annotations` when necessary.
- Since the arguments types and return types are already documented by the hints, there's no need to duplicate this information in the docstrings.
- Apart from particular cases, use pydantic BaseModel instead of dataclass
- Use python type annotation. When a function that does not yet use hints is substantially edited, take the opportunity to add type hints.
- Do not quote class names in hints. Use `from __futue__ import annotations` when necessary.
- Do not use the `Any` type, and avoid the use of the general `object` type. Define the actual type instead.
- Since the arguments types and return types are already documented by the annotations, there's no need to duplicate this information in the docstrings.
- Use pydantic BaseModel when validation is needed, and dataclass for internal data exchange.
- No need to write tests to check if declarative Pydantic validation is working.
- Place imports at the top of the file. Only break this rule to prevent heavy imports in a rarely used function (for example, CLI options).
- When passing data structures between functions, prefer immutable objects instead of equivalent mutable ones, unless immutability is desired.
- For GRASS interactions, prefer C functions calls through `pygrass` when available, instead of sub-process tool calls through `grass.script`

## General comments
- The project uses `uv`. To run a command in the correct environment, use `uv run`
Expand Down
22 changes: 22 additions & 0 deletions docs/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,28 @@ Run a simulation
:path: run
:nodefault:

YAML ensembles
~~~~~~~~~~~~~~

Each YAML document is one ensemble. Use ``--dry-run`` (or ``-d``) to perform parsing,
expansion, GRASS input resolution, member-ID calculation, and artifact
validation without writing manifests or model outputs:

.. code-block:: bash

itzi run --dry-run studies.yaml

Select resolved members with a qualified ensemble and simulation ID. The
unqualified ID form is accepted only when the batch contains one ensemble.

.. code-block:: bash

itzi run studies.yaml --member central-city#sim-c1f52e9a

Stage 1 provides basic YAML ensemble execution. YAML hotstart resume mapping
and checkpoint provenance are introduced in Stage 2; the retained INI resume
syntax below continues to apply to legacy INI-only invocations.


Hotstart usage
~~~~~~~~~~~~~~
Expand Down
4 changes: 2 additions & 2 deletions docs/conf_file.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

Configuration file
==================
INI configuration file
======================

The parameters of a simulation are given through a configuration file in
a format similar to Microsoft Windows INI files.
Expand Down
2 changes: 2 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ Please see the Itzï's `home page <https://www.itzi.org/>`__.
cli
tutorial
conf_file
yaml_file
manifest
faq
prog_manual
102 changes: 102 additions & 0 deletions docs/manifest.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
Ensemble manifest
=================

Each YAML ensemble run writes a YAML manifest describing its resolved members,
their planned outputs, and their run status. By default, it is saved
as ``results/<ensemble-id>.manifest.yaml`` beside the source YAML file. Set
``outputs.manifest.file`` in the ensemble configuration to choose another file:

.. code-block:: yaml

outputs:
manifest:
file: "results/{ensemble}.yaml"

The manifest path may be absolute or relative to the source YAML file; only
``{ensemble}`` is allowed as a template placeholder (not ``{simulation}``).
An existing manifest requires ``itzi run -o`` to be replaced; if the manifest
destination fails validation, no manifest is written. A dry run validates the
destination but does not write a manifest. An ensemble with no selected resolved
members does not write one, unless it contains unresolved candidates in a run
without ``--member``.

Format
------

The manifest is a YAML mapping with these top-level fields:

* ``manifest_version``: manifest format version, currently ``1``. This is
distinct from the configuration's ``schema_version``.
* ``last_updated_at``: last write time as an ISO 8601 string with a local
timezone offset. It changes when the manifest is updated.
* ``ensemble``: ``id`` and ``description`` from the configuration;
``description`` is ``null`` when omitted.
* ``source``: absolute ``path`` to the source YAML file and zero-based
``document_index`` within that YAML stream.
* ``members``: resolved members in sweep expansion order, followed by
unresolved candidates in sweep expansion order.

For example, a completed member might appear as follows (names and timings
are illustrative):

.. code-block:: yaml

manifest_version: 1
last_updated_at: '2026-09-30T10:20:30+02:00'
ensemble:
id: central-city
description: Central city rainfall study
source:
path: /srv/studies/city.yaml
document_index: 0
members:
- simulation_id: sim-1234abcd
selected: true
status: completed
elapsed_seconds: 12.5
coordinates:
parameters.cfl: 0.5
artifacts:
rasters:
water_depth: central_city_sim-1234abcd_water_depth@itzi
drainage: null
statistics: /srv/studies/results/sim-1234abcd.csv

Member fields
-------------

``simulation_id`` is the resolved member ID (``sim-...``), or ``null`` if
input resolution failed before an ID could be assigned. ``selected`` indicates
whether this member was selected to run. ``coordinates`` maps swept configuration
paths (for example, ``parameters.cfl`` or ``input.rainfall_rate``) to their
chosen values; it is ``{}`` without sweeps. An ``input.infiltration`` sweep
value is a mapping containing its ``type`` and applicable fields.

Resolved members also have ``artifacts``: ``rasters`` maps requested output
variables to GRASS space-time raster dataset IDs (``name@mapset``),
``drainage`` is a GRASS space-time vector dataset ID or ``null``, and
``statistics`` is an absolute CSV file path or ``null``. ``rasters`` is ``{}``
when no raster outputs are requested. These are intended destinations; a
failed or unfinished member may not have produced them. Unresolved candidates
have no ``artifacts`` field.

``elapsed_seconds`` is ``null`` until execution finishes, then records the
elapsed wall-clock time in seconds, including when execution fails. The
``status`` field can be:

* ``not_selected``: excluded by ``--member``.
* ``planned``: selected, but not yet started.
* ``running``: execution started; the manifest is updated before and after
each member runs.
* ``completed``: execution finished successfully.
* ``validation_failed``: the member could not be run.
* ``execution_failed``: execution raised an error.

Failures also have a ``failure`` mapping with ``phase`` and ``detail``.
Possible phases are ``input_resolution`` (no resolved ID or artifacts),
``artifact_validation`` (conflicting or invalid outputs),
``run_validation`` (pre-run checks), and ``execution``. Unresolved candidates
are marked ``validation_failed`` when running without ``--member``; with
selectors they appear as ``not_selected`` but still carry their failure
detail. A failed YAML document (parse, schema, or expansion error) has no
manifest entry because no ensemble was expanded from it.
Loading
Loading