Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
119 commits
Select commit Hold shift + click to select a range
21265dc
Record the decision to move the network topology layer to mikeio1d
jpalm3r Aug 13, 2026
9d3d1c6
Note the proposed move to mikeio1d in the network roadmap entry
jpalm3r Aug 13, 2026
4c8e92d
Accept ADR-013 now that the upstream module exists
jpalm3r Aug 25, 2026
c9dc5b1
Locate network observations with a MIKE+ database
jpalm3r Aug 25, 2026
34d3aad
Document locating observations from a MIKE+ database
jpalm3r Aug 11, 2026
62486c7
Identify a network timeseries by name, not by graph integer
jpalm3r Aug 25, 2026
72ec69d
Take the network topology layer from mikeio1d
jpalm3r Aug 25, 2026
f6765b6
Build the network model result on mikeio1d's topology layer
jpalm3r Aug 25, 2026
dfc9fe4
Drop the integer form of NodeObservation's at=
jpalm3r Aug 25, 2026
7f9efc6
Point the docs and the records at the upstream module
jpalm3r Aug 25, 2026
c81faf3
Keep a 1.4.0a3 comparer loading
jpalm3r Aug 25, 2026
c9ef75b
Say plainly what the ADRs now record
jpalm3r Aug 25, 2026
6999d61
Cut the flourishes from the network ADRs
jpalm3r Aug 25, 2026
181b2b5
merge latest changes from main
jpalm3r Sep 3, 2026
a178762
Potential fix for pull request finding 'Statement has no effect'
jpalm3r Sep 3, 2026
8294657
Potential fix for pull request finding 'Statement has no effect'
jpalm3r Sep 3, 2026
64bb87a
Potential fix for pull request finding 'Statement has no effect'
jpalm3r Sep 3, 2026
27babc8
Merge branch 'network-phase-2' of https://github.com/DHI/modelskill i…
jpalm3r Sep 3, 2026
62df504
Add reach branch to Comparer.to_dataframe
jpalm3r Sep 3, 2026
904e5f0
Guard node extraction against locations with no data
jpalm3r Sep 3, 2026
ec14d7b
Fix Option B breakpoint example in the network guide
jpalm3r Sep 3, 2026
38c120e
Match MIKE+ source on the file name, not a substring
jpalm3r Sep 3, 2026
1123857
Keep unclassifiable MIKE+ stations from aborting a resolve
jpalm3r Sep 3, 2026
04e93ad
Treat a Quantity passed with db= as metadata only
jpalm3r Sep 3, 2026
71be06c
Rewrite ADR-013 to the point
jpalm3r Sep 3, 2026
137dfaf
Move the network location reader next to NodeObservation
jpalm3r Sep 3, 2026
3fcea06
Read a NodeObservation's location through one function
jpalm3r Sep 3, 2026
f5d4711
State the network-coordinate rule once, on GeometryType
jpalm3r Sep 3, 2026
52f0711
Read a coordinate off a dataset through one function
jpalm3r Sep 3, 2026
525e876
Take the network vocabulary off the TimeSeries base
jpalm3r Sep 3, 2026
a5b0ae2
Give the network parsers a module of their own
jpalm3r Sep 3, 2026
4a50c0d
Name the network scope in from_network_coords
jpalm3r Sep 4, 2026
c6beb58
Keep the coordinate reader in the coordinate module
jpalm3r Sep 4, 2026
50fe132
Read a network location from the coordinate module
jpalm3r Sep 4, 2026
aac8db1
Test the network module through its public surface
jpalm3r Sep 4, 2026
a8ba234
Reach the network comparers through modelskill's own names
jpalm3r Sep 4, 2026
bc6e446
Match a reach observation end to end
jpalm3r Sep 4, 2026
dffb909
Run the doctests that state the network-coordinate rule
jpalm3r Sep 4, 2026
61babd0
Moving constant
jpalm3r Sep 4, 2026
9b5d164
Fix network plotting in example notebook
jpalm3r Sep 4, 2026
0be65c2
Include tests for network values passed to the comparer
jpalm3r Sep 4, 2026
d2995a1
Reusing existing fixtures
jpalm3r Sep 4, 2026
173fe46
Move function to network_helpers
jpalm3r Sep 4, 2026
77545a1
Assert the network values survive item selection and the file path
jpalm3r Sep 4, 2026
5f4d838
Remove NetworkModelResult.nodes
jpalm3r Sep 4, 2026
95e9ce7
Remove the MIKE+ station lookup
jpalm3r Sep 21, 2026
ebcede0
Drop the MIKE+ section from the network guide
jpalm3r Sep 21, 2026
57f5199
Take the MIKE+ station resolver off ADR-013's ownership table
jpalm3r Sep 21, 2026
eae79e0
Read the network quantity off the attributes directly
jpalm3r Sep 21, 2026
4a8a14c
pin mikeio1d to alpha release
jpalm3r Sep 22, 2026
fb885ba
pin mikeio1d to alpha release
jpalm3r Sep 22, 2026
9e885c3
Run the file-backed network tests on 3.14
jpalm3r Sep 22, 2026
7681792
Run the network notebook again
jpalm3r Sep 22, 2026
1107be3
Build the docs on 3.14
jpalm3r Sep 22, 2026
86eee4f
Guard the network fixtures on mikeio1d, not networkx
jpalm3r Sep 22, 2026
4df5a6d
Stop handing Comparer.load a name it ignores
jpalm3r Sep 22, 2026
aeafce2
Build NodeModelResult only from data that knows where it is
jpalm3r Sep 22, 2026
52ef693
Drop the NodeModelResult _create_new_instance override
jpalm3r Sep 22, 2026
86aaa5f
Describe NetworkModelResult by what it takes
jpalm3r Sep 22, 2026
ee60c4a
Seed the synthetic sensor data in the network notebook
jpalm3r Sep 22, 2026
945b8ac
Test NodeModelResult and its recorded graph integer
jpalm3r Sep 22, 2026
0e9ebdf
Run the file-backed network tests on EPANET too
jpalm3r Sep 22, 2026
af1a59b
Carry an observation's aux items into the comparer
jpalm3r Sep 22, 2026
f66fe83
Say what a reach with no values is missing
jpalm3r Sep 22, 2026
208abad
Copy the dataset before marking it a model result
jpalm3r Sep 22, 2026
b11c068
Refuse a node location the data contradicts
jpalm3r Sep 22, 2026
831db3a
Refuse a reach location the data contradicts
jpalm3r Sep 22, 2026
bf77e67
Build the test networks from result files
jpalm3r Sep 23, 2026
e08b109
Pin mikeio1d to the issue-250 branch
jpalm3r Sep 24, 2026
1408277
Read a network location only when it is extracted
jpalm3r Sep 24, 2026
f4e6691
Point the integer location error at the graph alias
jpalm3r Sep 24, 2026
73850f0
Describe the lazy network model result in the guide
jpalm3r Sep 24, 2026
163d0b2
Use resolve and graph aliases in the network notebook
jpalm3r Sep 24, 2026
d095fb5
Read a resolved location's fields as attributes
jpalm3r Sep 25, 2026
a5a9f39
Name the graph attribute address, as mikeio1d now does
jpalm3r Sep 25, 2026
994560d
Run the network notebook on the Location surface
jpalm3r Sep 25, 2026
dac0f53
Plot the temporal coverage of a comparer saved by 1.4.0a3
jpalm3r Sep 25, 2026
5bcb6cd
Build a comparer's observation at a node name it can take
jpalm3r Sep 25, 2026
3ed2a74
Build a NetworkModelResult from model_result()
jpalm3r Sep 25, 2026
baffb46
Give a network model result the at its observation has
jpalm3r Sep 25, 2026
3926f0e
Give a comparer the at its observation has
jpalm3r Sep 25, 2026
8278f4b
Merge the node and reach coords into one NetworkCoords
jpalm3r Sep 25, 2026
4344e22
Build network timeseries through _parse_point_input directly
jpalm3r Sep 25, 2026
da66d0a
Drop the node_index coordinate
jpalm3r Sep 25, 2026
ad21400
Fold _from_network into _read_at
jpalm3r Sep 25, 2026
f1648f0
Say a location is missing without rebuilding mikeio1d's search
jpalm3r Sep 25, 2026
70ea520
Read a reach observation's break points once
jpalm3r Sep 25, 2026
3485b79
introducing network_gtype function
jpalm3r Sep 25, 2026
6175d80
Follow mikeio1d main, where the network PR merged
jpalm3r Sep 30, 2026
f97e8eb
Point network hints at the surface mikeio1d now has
jpalm3r Sep 30, 2026
4d73572
Say that EPANET pipe lengths come from the .res
jpalm3r Sep 30, 2026
482874c
Run the network notebook on mikeio1d main
jpalm3r Sep 30, 2026
c28b9a4
Read node_series nodes from network.addresses
jpalm3r Sep 30, 2026
6b7f085
Drop epanet.inp, which nothing reads
jpalm3r Sep 30, 2026
806cc80
Rename the network distance coordinate to position
jpalm3r Sep 30, 2026
3115d78
Rerun collection systems network with new mikeio1d
jpalm3r Sep 30, 2026
7c4fc69
Pin mikeio1d 1.4.0
jpalm3r Oct 1, 2026
2c807ae
Drop the 1.4.0a3 comparer fixture
jpalm3r Oct 1, 2026
2228945
Describe the network layer against mikeio1d 1.4.0
jpalm3r Oct 1, 2026
6638045
Drop graph integers from the network notebook
jpalm3r Oct 1, 2026
9e31f0f
Generate a discharge sensor on reach 116l1
jpalm3r Oct 1, 2026
ab1d0b2
Match the guide's sensors where they were generated
jpalm3r Oct 1, 2026
bd1e854
Describe network extraction as it happens
jpalm3r Oct 1, 2026
4157140
Say ADRs 012 and 013 read no EPANET .inp
jpalm3r Oct 1, 2026
cf51e7d
Describe NetworkModelResult's contents and name default correctly
jpalm3r Oct 1, 2026
7da099f
Show a comparer's location in its repr
jpalm3r Oct 1, 2026
0f8a163
Print an undefined quantity as Undefined, and drop empty unit brackets
jpalm3r Oct 1, 2026
a81a778
Reserve network coordinate names
jpalm3r Oct 1, 2026
44544ae
Resolve a node observation with the model's item
jpalm3r Oct 1, 2026
982a276
Let a node observation set its chainage tolerance
jpalm3r Oct 1, 2026
4a8ed33
Explain position_tol in the network user guide
jpalm3r Oct 1, 2026
64b8fbc
Keep the reach observation's location on a reach comparer
jpalm3r Oct 1, 2026
ccc5ca8
Give reach observations and comparers an .at
jpalm3r Oct 1, 2026
1314a00
Say where a reach comparer sits in the network user guide
jpalm3r Oct 1, 2026
152c8df
Drop the Python 3.15 skips from the network tests
jpalm3r Oct 1, 2026
2310812
Delete network tests a stronger test already covers
jpalm3r Oct 1, 2026
52289d7
Fold the reach location test into the reach comparer test
jpalm3r Oct 1, 2026
bc943a7
Place a comparer's raw model results at the observation
jpalm3r Oct 1, 2026
28c0780
Read the break point a model used from the model in the network guide
jpalm3r 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
4 changes: 2 additions & 2 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
python-version: "3.13" # Need to use 3.13 for docs build due to pythonnet dependency not yet supporting 3.14
python-version: "3.14" # mikeio1d needs Python < 3.15, so the docs build cannot go higher
enable-cache: true

- name: Set up Quarto
Expand All @@ -36,7 +36,7 @@ jobs:
version: "1.8.27"

- name: Install dependencies
run: uv sync --group dev --group docs --group networks
run: uv sync --group dev --group docs --group network

- name: Build documentation
run: just docs
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/full_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
enable-cache: true

- name: Install dependencies
run: uv sync --group test --group networks --no-dev
run: uv sync --group test --group network --no-dev

- name: Install pandas 2.x
if: matrix.pandas-version == 'pandas2'
Expand All @@ -57,7 +57,7 @@ jobs:
- name: Test
run: just test

build-no-networks:
build-no-network:
runs-on: ubuntu-latest

steps:
Expand All @@ -71,7 +71,7 @@ jobs:
python-version: "3.12"
enable-cache: true

- name: Install dependencies (without networks)
- name: Install dependencies (without network)
run: uv sync --group test --no-dev

- name: Test
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/notebooks_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
python-version: "3.14"
enable-cache: true
- name: Install dependencies
run: uv sync --group test --group notebooks --group networks --no-dev
run: uv sync --group test --group notebooks --group network --no-dev
- name: Test notebooks
run: |
uv run pytest tests/notebooks/
5 changes: 4 additions & 1 deletion adr/010-optional-domain-dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ Installation: `pip install modelskill modelskill-network`

**Open Questions:**
- Should `modelskill[all]` install all optional model types?
- How to handle version constraints for optional dependencies?
- How to handle version constraints for optional dependencies? Answered for network
support by [ADR-013](013-network-topology-in-mikeio1d.md): the `network` extra names a
minimum mikeio1d, because the topology layer ships there. Network support requires
whatever Python that release requires.
- Should optional dependencies be tested in CI for every commit or separately?

## Status Notes
Expand Down
14 changes: 13 additions & 1 deletion adr/012-network-format-constructors.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
# ADR-012: One Network Constructor per Modelling Product

**Status**: Draft
**Status**: Accepted, narrowed by [ADR-013](013-network-topology-in-mikeio1d.md)

**Date**: 2026-08

## Narrowed by ADR-013

The constructors, the companion arguments, the extension tables and the coverage test are
mikeio1d's. It replaced `from_mike` and `from_epanet` with one `Network.open` that reads
the extension. Naming a constructor after the product that wrote the file is still the
rule, and mikeio1d applies it.

EPANET reach lengths now come from the `.res`, so no `.inp` is read.

`NetworkModelResult` hands a path to mikeio1d. The refusal messages for `.out`, `.resx`
and the formats without a fixture are written there.

## Context

`Network` is built from result files read through mikeio1d, whose single `Res1D` class opens nine extensions across five products — MIKE 1D (`.res1d`), MIKE 11 (`.res11`), MOUSE (`.prf`, `.crf`, `.xrf`), EPANET (`.res`), SWMM (`.out`), Water Hammer (`.whr`), and `.resx`, which is shared by the last three. There is no per-format reader and no per-format constructor argument, so from mikeio1d's side all nine look alike. modelskill's constructor was named `from_res1d`, and its extension guard was briefly widened to accept everything mikeio1d could read — making the name promise one format while reading nine.
Expand Down
60 changes: 60 additions & 0 deletions adr/013-network-topology-in-mikeio1d.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# ADR-013: The Network Topology Layer Belongs to mikeio1d

**Status**: Accepted

**Date**: 2026-08

## Context

`modelskill.network` had grown to roughly 630 lines of topology: the abstract node/reach/breakpoint types, a `Res1D` adapter, one constructor per modelling product, the `.resx` and `.inp` companions,
tables of which extensions we refuse and why, a networkx graph carrying reach lengths and boundary edges, an alias map, and `find`/`recall`/`to_dataset` on top. `NetworkModelResult` uses five members
of `Network`, two of them private, and never traverses the graph. mikeio1d's `experimental.to_networkx` converts the same files in 25 lines and ignores gridpoints.

That leaves us on the far side of the line ADR-001 drew for mikeio, where we call `mikeio.read()` and stop, modelling no dfsu geometry and policing no format list. `Res1D` reads nine extensions across
five products. Our tables decide which of the nine we accept, and a test fails our CI when a mikeio1d release adds a tenth. The fixtures those tables are checked against are copies of mikeio1d's own:
`network.res1d`, `network_cali.res11`, `epanet.res/.resx/.inp`.

## Decision

mikeio1d gains an optional network module that builds and owns `Network`. modelskill requires it and consumes what it produces.

| Owner | Pieces |
|---|---|
| mikeio1d | abstract types and `BasicNode`/`BasicReach`, the `Res1D` adapter, `Network.open`, the `.resx` companion, the extension policy tables, graph construction with its length and boundary semantics, the alias map, `find`, `recall`, `to_dataframe`, `to_dataset` |
| modelskill | `NetworkModelResult`, `NodeModelResult`, `NodeObservation`, `ReachObservation`, matching |

`NetworkModelResult` takes a `Network` the upstream module built, or a path it hands to that module. The module is an extra there, carrying networkx and xarray, so `to_dataset()` ships with the class.
modelskill's `network` extra requires a mikeio1d release new enough to contain it.

Original IDs become the only identifier a user handles: `NodeObservation.at` takes a node name or a `(reach, position)` pair, not an integer. The alias integers stay an internal index,
because the ID space mixes names and break points and a tuple cannot be an xarray coordinate value. A saved comparer records only the original ID, so reloading
does not depend on the numbering the installed mikeio1d handed out.

The loader's output over six fixture loads was recorded before anything moved — graph edges with their lengths and boundary flags, the alias map, the dataframe, and every answer `find` and `recall`
give. Those snapshots are the upstream module's acceptance test. Phase 1 landed as mikeio1d [#247](https://github.com/DHI/mikeio1d/pull/247), merged 2026-08-19. The snapshots pass there unchanged
twice: against the code moved verbatim, and again after the two product constructors collapsed into `Network.open`.

mikeio1d 1.4.0 is the first release carrying the module, and modelskill 1.4.0 requires it.

## Alternatives Considered

**Keep the layer here.** Defensible while the API is private. Costs a format matrix, an EPANET `.inp` parser and a graph contract for traversals we never perform.

**Move only the constructors and companions**, leaving the graph and the abstract types here. Splits the format knowledge from the topology it produces, and leaves `Res1DReach` here as the single
adapter for a plug point with no second implementation.

**A separate `modelskill-network` package.** Rejected in ADR-010 for fragmenting the install. It would still own format knowledge that belongs with mikeio1d.

**Ask mikeio1d to guarantee stable node numbering** instead of keeping integers out of our API. Puts a promise on someone else's release process, to protect a number users should not be handling.

## Consequences

- ADR-012 is narrowed: the constructors, the companion arguments, the extension tables and the coverage test become mikeio1d's. Naming a constructor after the product that wrote the file is still the
rule, and mikeio1d applies it.
- The EPANET `.inp` companion and its parser did not move. mikeio1d reads EPANET reach lengths from the `.res`, so no `.inp` is read.
- ADR-010's open question about version constraints for optional dependencies is answered for this feature: the `network` extra pins a minimum mikeio1d, and network support requires whatever Python
that release requires.
- A hand-built network needs mikeio1d installed, since `BasicNode`/`BasicReach` move too. That costs a .NET dependency for users who touch no MIKE file, which only matters for tests and for a backend
nobody has written.
- `at` refuses an integer with an error that points to the name. Accepting one in 1.4.0 would make the internal index part of the API, and hard to withdraw later.
- Releases become coupled in one direction: a fix to network file reading ships on mikeio1d's schedule. A format mikeio1d adds no longer breaks our CI.
3 changes: 2 additions & 1 deletion adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ Each ADR follows this structure:
- [ADR-009](009-factory-pattern.md) - Factory pattern for type detection
- [ADR-010](010-optional-domain-dependencies.md) - Optional dependencies for domain-specific model types (Draft)
- [ADR-011](011-vertical-pre-extracted-columns.md) - VerticalModelResult ingests pre-extracted columns
- [ADR-012](012-network-format-constructors.md) - One Network constructor per modelling product (Draft)
- [ADR-012](012-network-format-constructors.md) - One Network constructor per modelling product (narrowed by ADR-013)
- [ADR-013](013-network-topology-in-mikeio1d.md) - The network topology layer belongs to mikeio1d

## Contributing

Expand Down
Binary file removed docs/images/res1d_network_mapping.png
Binary file not shown.
Loading
Loading