Skip to content

Commit 84e3514

Browse files
jpalm3rclaude
andcommitted
Document loading a network model result from a path
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent f434b5b commit 84e3514

2 files changed

Lines changed: 18 additions & 6 deletions

File tree

adr/012-network-format-constructors.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Name constructors after the product that writes the file, and ship one only wher
1919
| `Network.from_mike` | `.res1d`, `.res11` |
2020
| `Network.from_epanet` | `.res`, plus optional `.resx` and `.inp` companions |
2121

22+
`NetworkModelResult` is exempt: it accepts a path and reads it with the constructor its extension is mapped to, since every other model result class already takes a path.
23+
2224
A product's companion files are arguments rather than constructors of their own. A companion describes a network defined elsewhere and cannot stand alone, so `from_epanet(res, resx=..., inp=...)` and not a `from_resx()`. Each companion is validated against the main file — same time axis, no unknown IDs — because two unrelated runs would otherwise merge silently.
2325

2426
Every extension mikeio1d reads is accounted for in one of three module-level tables in `network.py`: readable by `from_mike`, readable by `from_epanet`, or refused with a reason that names the file or method which would lift it. A test asserts the tables cover exactly `Res1D.get_supported_file_extensions()`, so a mikeio1d release adding a tenth format fails CI instead of leaving that format silently unreachable. `from_res1d` is removed without a deprecation shim: it shipped only in the 1.4.0a3 alpha, and the network module is opt-in and absent from the API reference.
@@ -37,5 +39,6 @@ Every extension mikeio1d reads is accounted for in one of three module-level tab
3739

3840
- The method list is the format list: `Network.from_<TAB>` answers "which formats does this read", and passing a file the other constructor handles raises a `ValueError` naming that constructor.
3941
- EPANET's degenerate geometry is stated in the `from_epanet` docstring and the user guide and asserted in tests, rather than warned about at runtime. A warning would fire on correct usage, and both consequences already raise where they bite.
42+
- `NetworkModelResult(path)` reads the extension table rather than asking the caller, which is the one place the guessing objection above does not bite: the tables map each extension to exactly one product, and the answer is reported in `mr.network`. It picks up an EPANET file's `.resx` and `.inp` siblings for the same reason, since a network built without the `.inp` has no reach lengths at all. Anything needing named companions or selective loading still goes through `Network.from_*`.
4043
- MOUSE and Water Hammer are refused even though mikeio1d may well read them correctly. Refusing with a reason is recoverable; a method that silently builds a wrong graph is not. Each becomes a six-line addition once a redistributable fixture exists.
4144
- The `.inp` reader (`model/adapters/_inp.py`) is ours to maintain, since mikeio1d does not read `.inp` and pulling in `wntr` or `swmmio` for two sections would weigh more than the parser does (ADR-010). SWMM support will reuse it, as the two products share the layout.

docs/user-guide/network.qmd

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -391,13 +391,23 @@ print(ids)
391391

392392
## Skill assessment workflow
393393

394-
### 1. Wrap the Network in a NetworkModelResult
394+
### 1. Wrap the network in a NetworkModelResult
395+
396+
`NetworkModelResult` takes the path to a result file, so an assessment can start from one without loading the `Network` yourself:
395397

396398
```{python}
397399
import modelskill as ms
398-
from modelskill.model.network import NetworkModelResult
399400
400-
mr = NetworkModelResult(network, name="MyModel", item="WaterLevel")
401+
mr = ms.NetworkModelResult(path_to_res1d, name="MyModel", item="WaterLevel")
402+
mr
403+
```
404+
405+
The extension picks the constructor, following the same table as [Building a Network](#building-a-network) above, and an EPANET `.res` also reads the `.resx` and `.inp` that share its folder and stem.
406+
407+
Pass a `Network` when you need to name the companions yourself, or to keep memory down with [selective loading](#selective-loading):
408+
409+
```{python}
410+
mr = ms.NetworkModelResult(network, name="MyModel", item="WaterLevel")
401411
mr
402412
```
403413

@@ -461,7 +471,7 @@ obs_q
461471
Pass the observation to `ms.match()` exactly as you would a `NodeObservation`. modelskill resolves which breakpoint to use automatically:
462472

463473
```{python}
464-
mr_q = NetworkModelResult(network, name="MyModel", item="Discharge")
474+
mr_q = ms.NetworkModelResult(network, name="MyModel", item="Discharge")
465475
cc_q = ms.match(obs=obs_q, mod=mr_q)
466476
cc_q.skill()
467477
```
@@ -479,8 +489,7 @@ Pass that database as `db` and modelskill does the lookup for you:
479489
```python
480490
quantity = "Pressure"
481491

482-
network = Network.from_epanet("model.res", quantities=quantity)
483-
network_model = ms.NetworkModelResult(network, item=quantity)
492+
network_model = ms.NetworkModelResult("model.res", item=quantity)
484493

485494
obs = ms.NodeObservation.from_multiple(
486495
data="calibration.dfs0",

0 commit comments

Comments
 (0)